Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / xsp / System / Web / HttpInputStream.cs / 1305376 / HttpInputStream.cs

    //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

/* 
 * Input stream used in response and uploaded file objects 
 *
 * Copyright (c) 1998 Microsoft Corporation 
 */

namespace System.Web {
 
    using System.IO;
    using System.CodeDom.Compiler; // needed for TempFilesCollection 
    using System.Security; 
    using System.Security.Permissions;
    using System.Web.Hosting; 


    /*
     * Wrapper around temporary file or byte[] for input stream 
     *
     * Pattern of use: 
     *      ctor 
     *      AddBytes
     *      ... 
     *      DoneAddingBytes
     *      access bytes: [] / CopyBytes / WriteBytes / GetAsByteArray
     *      Dispose
     */ 
    internal class HttpRawUploadedContent : IDisposable {
        private int _fileThreshold; // for sizes over this use file 
        private int _expectedLength;// content-length 
        private bool _completed;    // true when all data's in
        private int _length;        // length of the data 
        private byte[] _data;       // contains data (either all of it or part read from file)
        private TempFile _file;     // temporary file with content (null when using byte[])
        private int _chunkOffset;   // which part of file is cached in data - offset
        private int _chunkLength;   // which part of file is cached in data - length 

        internal HttpRawUploadedContent(int fileThreshold, int expectedLength) { 
            _fileThreshold = fileThreshold; 
            _expectedLength = expectedLength;
 
            if (_expectedLength >= 0 && _expectedLength < _fileThreshold)
                _data = new byte[_expectedLength];
            else
                _data = new byte[_fileThreshold]; 
        }
 
        public void Dispose() { 
            if (_file != null)
                _file.Dispose(); 
        }

        internal void AddBytes(byte[] data, int offset, int length) {
            if (_completed) 
                throw new InvalidOperationException();
 
            if (length <= 0) 
                return;
 
            if (_file == null) {
                // fits in the existing _data
                if (_length + length <= _data.Length) {
                    Array.Copy(data, offset, _data, _length, length); 
                    _length += length;
                    return; 
                } 

                // doesn't fit in _data but still under threshold 
                // possible if content-length is -1, or when filtering
                if (_length + length <= _fileThreshold) {
                    byte[] newData = new byte[_fileThreshold];
                    if (_length > 0) 
                        Array.Copy(_data, 0, newData, 0, _length);
                    Array.Copy(data, offset, newData, _length, length); 
 
                    _data = newData;
                    _length += length; 
                    return;
                }

                // need to convert to file 
                _file = new TempFile();
                _file.AddBytes(_data, 0, _length); 
            } 

            // using file 
            _file.AddBytes(data, offset, length);
            _length += length;
        }
 
        internal void DoneAddingBytes() {
            if (_data == null) 
                _data = new byte[0]; 

            if (_file != null) 
                _file.DoneAddingBytes();

            _completed = true;
        } 

        internal int Length { 
            get { return _length; } 
        }
 
        internal byte this[int index] {
            get {
                if (!_completed)
                    throw new InvalidOperationException(); 

                // all data in memory 
                if (_file == null) 
                    return _data[index];
 
                // index in the chunk already read
                if (index >= _chunkOffset && index < _chunkOffset + _chunkLength)
                    return _data[index - _chunkOffset];
 
                // check bounds
                if (index < 0 || index >= _length) 
                    throw new ArgumentOutOfRangeException("index"); 

                // read from file 
                _chunkLength = _file.GetBytes(index, _data.Length, _data, 0);
                _chunkOffset = index;
                return _data[0];
            } 
        }
 
        internal void CopyBytes(int offset, byte[] buffer, int bufferOffset, int length) { 
            if (!_completed)
                throw new InvalidOperationException(); 

            if (_file != null) {
                if (offset >= _chunkOffset && offset+length < _chunkOffset + _chunkLength) {
                    // preloaded 
                    Array.Copy(_data, offset - _chunkOffset, buffer, bufferOffset, length);
                } 
                else { 
                    if (length <= _data.Length) {
                        // read from file and remember the chunk 
                        _chunkLength = _file.GetBytes(offset, _data.Length, _data, 0);
                        _chunkOffset = offset;
                        Array.Copy(_data, offset - _chunkOffset, buffer, bufferOffset, length);
                    } 
                    else {
                        // read from file 
                        _file.GetBytes(offset, length, buffer, bufferOffset); 
                    }
                } 
            }
            else {
                Array.Copy(_data, offset, buffer, bufferOffset, length);
            } 
        }
 
        internal void WriteBytes(int offset, int length, Stream stream) { 
            if (!_completed)
                throw new InvalidOperationException(); 

            if (_file != null) {
                int readPosition = offset;
                int bytesRemaining = length; 
                byte[] buf = new byte[bytesRemaining > _fileThreshold ? _fileThreshold : bytesRemaining];
 
                while (bytesRemaining > 0) { 
                    int bytesToRead = bytesRemaining > _fileThreshold ? _fileThreshold : bytesRemaining;
                    int bytesRead = _file.GetBytes(readPosition, bytesToRead, buf, 0); 
                    if (bytesRead == 0)
                        break;

                    stream.Write(buf, 0, bytesRead); 

                    readPosition += bytesRead; 
                    bytesRemaining -= bytesRead; 
                }
            } 
            else {
                stream.Write(_data, offset, length);
            }
        } 

        internal byte[] GetAsByteArray() { 
            // If the request is chunked, _data can be much larger than 
            // the actual number of bytes read, and FillInFormCollection
            // will call FillFromEncodedBytes and incorrectly append a 
            // bunch of zeros to the last form value.  Therefore, we copy
            // the data into a smaller array if _length < _data.Length
            if (_file == null && _length == _data.Length) {
                return _data; 
            }
            return GetAsByteArray(0, _length); 
        } 

        internal byte[] GetAsByteArray(int offset, int length) { 
            if (!_completed)
                throw new InvalidOperationException();

            if (length == 0) 
                return new byte[0];
 
            byte[] result = new byte[length]; 
            CopyBytes(offset, result, 0, length);
            return result; 
        }

        // helper class for a temp file for large posted data
        class TempFile : IDisposable { 
            TempFileCollection _tempFiles;
            String _filename; 
            Stream _filestream; 

            internal TempFile() { 
                // suspend the impersonation for the file creation
                using (new ApplicationImpersonationContext()) {
                    String tempDir = Path.Combine(HttpRuntime.CodegenDirInternal, "uploads");
 
                    // Assert IO access to the temporary directory
                    new FileIOPermission(FileIOPermissionAccess.AllAccess, tempDir).Assert(); 
 
                    if (!Directory.Exists(tempDir)) {
                        try { 
                            Directory.CreateDirectory(tempDir);
                        }
                        catch {
                        } 
                    }
 
                    _tempFiles = new TempFileCollection(tempDir, false /*keepFiles*/); 
                    _filename = _tempFiles.AddExtension("post", false /*keepFiles*/);
                    //using 4096 as the buffer size, same as the BCL 
                    _filestream = new FileStream(_filename, FileMode.Create, FileAccess.ReadWrite, FileShare.None, 4096, FileOptions.DeleteOnClose);
                }
            }
 
            public void Dispose() {
                // suspend the impersonation for the file creation 
                using (new ApplicationImpersonationContext()) { 
                    try {
                        // force filestream handle to close 
                        // since we're using FILE_FLAG_DELETE_ON_CLOSE
                        // this will delete it from disk as well
                        if (_filestream != null) {
                            _filestream.Close(); 
                        }
 
                        _tempFiles.Delete(); 
                        ((IDisposable)_tempFiles).Dispose();
                    } 
                    catch {
                    }
                }
            } 

            internal void AddBytes(byte[] data, int offset, int length) { 
                if (_filestream == null) 
                    throw new InvalidOperationException();
 
                _filestream.Write(data, offset, length);
            }

            internal void DoneAddingBytes() { 
                if (_filestream == null)
                    throw new InvalidOperationException(); 
 
                _filestream.Flush();
                _filestream.Seek(0, SeekOrigin.Begin); 
            }

            internal int GetBytes(int offset, int length, byte[] buffer, int bufferOffset) {
                if (_filestream == null) 
                    throw new InvalidOperationException();
 
                _filestream.Seek(offset, SeekOrigin.Begin); 
                return _filestream.Read(buffer, bufferOffset, length);
            } 
        }
    }

    /* 
     * Stream object over HttpRawUploadedContent
     * Not a publc class - used internally, returned as Stream 
     */ 
    internal class HttpInputStream : Stream {
        private HttpRawUploadedContent _data; // the buffer with the content 
        private int _offset;        // offset to the start of this stream
        private int _length;        // length of this stream
        private int _pos;           // current reader posision
 
        //
        // Internal access (from this package) 
        // 

        internal HttpInputStream(HttpRawUploadedContent data, int offset, int length) { 
            Init(data, offset, length);
        }

        protected void Init(HttpRawUploadedContent data, int offset, int length) { 
            _data = data;
            _offset = offset; 
            _length = length; 
            _pos = 0;
        } 

        protected void Uninit() {
            _data = null;
            _offset = 0; 
            _length = 0;
            _pos = 0; 
        } 

        internal byte[] GetAsByteArray() { 
            if (_length == 0)
                return null;

            return _data.GetAsByteArray(_offset, _length); 
        }
 
        internal void WriteTo(Stream s) { 
            if (_data != null && _length > 0)
                _data.WriteBytes(_offset, _length, s); 
        }

        //
        // BufferedStream implementation 
        //
 
        public override bool CanRead { 
            get {return true;}
        } 

        public override bool CanSeek {
            get {return true;}
        } 

        public override bool CanWrite { 
            get {return false;} 
        }
 
        public override long Length {
            get {return _length;}
        }
 
        public override long Position {
            get {return _pos;} 
 
            set {
                Seek(value, SeekOrigin.Begin); 
            }
        }

        protected override void Dispose(bool disposing) { 
            try {
                if (disposing) 
                    Uninit(); 
            }
            finally { 
                base.Dispose(disposing);
            }
        }
 
        public override void Flush() {
        } 
 
        public override long Seek(long offset, SeekOrigin origin) {
            int newpos = _pos; 
            int offs = (int)offset;

            switch (origin) {
                case SeekOrigin.Begin: 
                    newpos = offs;
                    break; 
                case SeekOrigin.Current: 
                    newpos = _pos + offs;
                    break; 
                case SeekOrigin.End:
                    newpos = _length + offs;
                    break;
                default: 
                    throw new ArgumentOutOfRangeException("origin");
            } 
 
            if (newpos < 0 || newpos > _length)
                throw new ArgumentOutOfRangeException("offset"); 

            _pos = newpos;
            return _pos;
        } 

        public override void SetLength(long length) { 
            throw new NotSupportedException(); 
        }
 
        public override int Read(byte[] buffer, int offset, int count) {
            // find the number of bytes to copy
            int numBytes = _length - _pos;
            if (count < numBytes) 
                numBytes = count;
 
            // copy the bytes 
            if (numBytes > 0)
                _data.CopyBytes(_offset + _pos, buffer, offset, numBytes); 

            // adjust the position
            _pos += numBytes;
            return numBytes; 
        }
 
        public override void Write(byte[] buffer, int offset, int count) { 
            throw new NotSupportedException();
        } 
    }

    /*
     * Stream used as the source for input filtering 
     */
 
    internal class HttpInputStreamFilterSource : HttpInputStream { 
        internal HttpInputStreamFilterSource() : base(null, 0, 0) {
        } 

        internal void SetContent(HttpRawUploadedContent data) {
            if (data != null)
                base.Init(data, 0, data.Length); 
            else
                base.Uninit(); 
        } 
    }
 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.