You've already forked linux-packaging-mono
Imported Upstream version 4.6.0.125
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
This commit is contained in:
parent
a569aebcfd
commit
e79aa3c0ed
434
mcs/class/referencesource/System.Web/HttpInputStream.cs
Normal file
434
mcs/class/referencesource/System.Web/HttpInputStream.cs
Normal file
@ -0,0 +1,434 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="HttpInputStream.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user