Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Web / HttpBufferlessInputStream.cs
blobdc3174c8fa899c5f948b6701dd25771f56247cbe
1 //------------------------------------------------------------------------------
2 // <copyright file="HttpBufferlessInputStream.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
7 /*
8 * Bufferless Input stream used in response and uploaded file objects
10 * Copyright (c) 2009 Microsoft Corporation
13 namespace System.Web {
15 using System.IO;
16 using System.Security;
17 using System.Security.Permissions;
18 using System.Threading;
19 using System.Web.Hosting;
20 using System.Web.Configuration;
21 using System.Web.Management;
22 using System.Web.Util;
24 internal class HttpBufferlessInputStream : Stream {
25 private long _position;
26 private long _length;
27 private long _maxRequestLength;
28 private bool _disableMaxRequestLength;
29 private int _fileThreshold;
30 private bool _preloadedContentRead;
31 private HttpContext _context;
32 private int _preloadedBytesRead;
33 private bool _persistEntityBody;
34 private HttpRawUploadedContent _rawContent;
35 private byte[] _buffer;
36 private int _offset;
37 private int _count;
38 private int _remainingBytes;
40 internal HttpBufferlessInputStream(HttpContext context, bool persistEntityBody, bool disableMaxRequestLength) {
41 _context = context;
42 _persistEntityBody = persistEntityBody;
43 _disableMaxRequestLength = disableMaxRequestLength;
45 // Check max-request-length for preloaded content
46 HttpRuntimeSection section = RuntimeConfig.GetConfig(_context).HttpRuntime;
47 _maxRequestLength = section.MaxRequestLengthBytes;
48 _fileThreshold = section.RequestLengthDiskThresholdBytes;
50 if (_persistEntityBody) {
51 _rawContent = new HttpRawUploadedContent(_fileThreshold, _context.Request.ContentLength);
54 int contentLength = _context.Request.ContentLength;
55 _remainingBytes = (contentLength > 0) ? contentLength : Int32.MaxValue;
56 _length = contentLength;
59 internal bool PersistEntityBody {
60 get {
61 return _persistEntityBody;
65 /////////////////////////////////////////////////////////////////////////////
66 /////////////////////////////////////////////////////////////////////////////
67 /////////////////////////////////////////////////////////////////////////////
69 // A call to Close is required for proper operation of a stream. Normally the
70 // raw content will have already been set, but if it was not, we set it now,
71 // even if the user of GetBufferedInputStream did not read the entire request
72 // entity body. Since HttpRequest.Dispose will dispose the raw content, this
73 // helps to ensure that any temporary files are deleted.
74 protected override void Dispose(bool disposing) {
75 if (disposing && _persistEntityBody) {
76 SetRawContentOnce();
78 base.Dispose(disposing);
81 public override bool CanRead {
82 get {
83 return true;
87 public override bool CanSeek {
88 get {
89 return false;
93 public override bool CanWrite {
94 get {
95 return false;
99 public override long Length {
100 get {
101 return _length;
105 public override long Position {
106 get {
107 return _position;
109 set {
110 throw new NotSupportedException();
114 public override void Flush() {
117 public override void SetLength(long length) {
118 throw new NotSupportedException();
120 public override long Seek(long offset, SeekOrigin origin) {
121 throw new NotSupportedException();
124 public override void Write(byte[] buffer, int offset, int count) {
125 throw new NotSupportedException();
128 // Caller may invoke this repeatedly until EndRead returns zero, at which point the entire entity has been read.
129 public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, Object state) {
130 HttpWorkerRequest wr = _context.WorkerRequest;
131 // Only perform an async read if the worker request supports it and we're not in a cancellable period.
132 // If we were to allow async read in a cancellable period, the timeout manager could raise a ThreadAbortEx and
133 // corrupt the state of the request.
134 if (wr != null && wr.SupportsAsyncRead && !_context.IsInCancellablePeriod) {
135 if (!_preloadedContentRead) {
136 if (buffer == null)
137 throw new ArgumentNullException("buffer");
138 if (offset < 0)
139 throw new ArgumentOutOfRangeException("offset");
140 if (count < 0)
141 throw new ArgumentOutOfRangeException("count");
142 if (buffer.Length - offset < count)
143 throw new ArgumentException(SR.GetString(SR.InvalidOffsetOrCount, "offset", "count"));
144 _preloadedBytesRead = GetPreloadedContent(buffer, ref offset, ref count);
146 if (_remainingBytes == 0) {
147 // set count to zero and invoke BeginRead to return an async result
148 count = 0;
150 if (_persistEntityBody) {
151 // hold a reference so we can add bytes to _rawContent when EndRead is called
152 _buffer = buffer;
153 _offset = offset;
154 _count = count;
156 try {
157 return wr.BeginRead(buffer, offset, count, callback, state);
159 catch(HttpException) {
160 if (_persistEntityBody) {
161 SetRawContentOnce();
163 throw;
166 else {
167 // perform a sync read
168 return base.BeginRead(buffer, offset, count, callback, state);
172 // When this returns zero, the entire entity has been read.
173 public override int EndRead(IAsyncResult asyncResult) {
174 HttpWorkerRequest wr = _context.WorkerRequest;
175 if (wr != null && wr.SupportsAsyncRead && !_context.IsInCancellablePeriod) {
176 int totalBytesRead = _preloadedBytesRead;
177 if (_preloadedBytesRead > 0) {
178 _preloadedBytesRead = 0;
180 int bytesRead = 0;
181 try {
182 bytesRead = wr.EndRead(asyncResult);
184 catch(HttpException) {
185 if (_persistEntityBody) {
186 SetRawContentOnce();
188 throw;
190 totalBytesRead += bytesRead;
191 if (bytesRead > 0) {
192 if (_persistEntityBody) {
193 if (_rawContent != null) {
194 _rawContent.AddBytes(_buffer, _offset, bytesRead);
196 _buffer = null;
197 _offset = 0;
198 _count = 0;
200 int dummy1 = 0, dummy2 = 0, dummy3 = 0;
201 UpdateCounters(bytesRead, ref dummy1, ref dummy2, ref dummy3);
203 if (_persistEntityBody
204 // we might attempt a read with count == 0, in which case bytesRead will
205 // be zero but we may not be done reading the entity body. Don't set raw
206 // content until bytesRead is 0 and count is not 0 or _remainingBytes is 0
207 && ((bytesRead == 0 && _count != 0) || _remainingBytes == 0)) {
208 SetRawContentOnce();
210 return totalBytesRead;
212 else {
213 return base.EndRead(asyncResult);
217 // Caller may invoke this repeatedly until it returns zero, at which point the entire entity has been read.
218 public override int Read(byte[] buffer, int offset, int count) {
219 HttpWorkerRequest wr = _context.WorkerRequest;
220 if (wr == null || count == 0)
221 return 0;
222 if (buffer == null)
223 throw new ArgumentNullException("buffer");
224 if (offset < 0 || offset + count > buffer.Length)
225 throw new ArgumentException(null, "offset");
226 if (count < 0)
227 throw new ArgumentException(null, "count");
229 int totalBytesRead = GetPreloadedContent(buffer, ref offset, ref count);
230 int bytesRead = 0;
231 // We are done if the count == 0 or there is no more content
232 while (count > 0 && _remainingBytes != 0) {
233 // Do the actual read
234 bytesRead = wr.ReadEntityBody(buffer, offset, count);
235 if (bytesRead <= 0) {
236 if (!_context.Response.IsClientConnected) {
237 if (_persistEntityBody) {
238 SetRawContentOnce();
240 throw new HttpException(SR.GetString(SR.HttpBufferlessInputStream_ClientDisconnected));
242 break;
244 if (_persistEntityBody) {
245 if (_rawContent != null) {
246 _rawContent.AddBytes(buffer, offset, bytesRead);
249 UpdateCounters(bytesRead, ref offset, ref count, ref totalBytesRead);
251 if (_persistEntityBody
252 // we might attempt a read with count == 0, in which case bytesRead will
253 // be zero but we may not be done reading the entity body. Don't set raw
254 // content until bytesRead is 0 and count is not 0 or _remainingBytes is 0
255 && ((bytesRead == 0 && count != 0) || _remainingBytes == 0)) {
256 SetRawContentOnce();
258 return totalBytesRead;
261 private int GetPreloadedContent(byte[] buffer, ref int offset, ref int count) {
262 if (_preloadedContentRead) {
263 return 0;
266 // validate once before reading preloaded bytes
267 if (_position == 0) {
268 ValidateRequestEntityLength();
271 int totalBytesRead = 0;
272 int preloadedRemaining = 0;
273 byte [] preloadedContent = _context.WorkerRequest.GetPreloadedEntityBody();
274 if (preloadedContent != null) {
275 // Read preloaded content
276 preloadedRemaining = preloadedContent.Length - (int) _position;
277 int bytesRead = Math.Min(count, preloadedRemaining);
278 Buffer.BlockCopy(preloadedContent, (int) _position, buffer, offset, bytesRead);
279 if (_persistEntityBody) {
280 if (_rawContent != null) {
281 _rawContent.AddBytes(preloadedContent, (int) _position, bytesRead);
284 UpdateCounters(bytesRead, ref offset, ref count, ref totalBytesRead);
286 // are we done reading preloaded content
287 if (totalBytesRead == preloadedRemaining) {
288 _preloadedContentRead = true;
289 if (_context.WorkerRequest.IsEntireEntityBodyIsPreloaded()) {
290 _remainingBytes = 0;
293 return totalBytesRead;
296 /////////////////////////////////////////////////////////////////////////////
297 /////////////////////////////////////////////////////////////////////////////
298 // Helper function to increment variables in Read API
299 private void UpdateCounters(int bytesRead, ref int offset, ref int count, ref int totalBytesRead) {
300 _context.WorkerRequest.UpdateRequestCounters(bytesRead);
301 count -= bytesRead;
302 offset += bytesRead;
303 _position += bytesRead;
304 _remainingBytes -= bytesRead;
305 totalBytesRead += bytesRead;
306 if (_length < _position)
307 _length = _position;
308 ValidateRequestEntityLength();
311 private void ValidateRequestEntityLength() {
312 if (!_disableMaxRequestLength && Length > _maxRequestLength) {
313 if ( !(_context.WorkerRequest is IIS7WorkerRequest) ) {
314 _context.Response.CloseConnectionAfterError();
316 throw new HttpException(SR.GetString(SR.Max_request_length_exceeded), null, WebEventCodes.RuntimeErrorPostTooLarge);
320 private void SetRawContentOnce() {
321 if (_persistEntityBody && _rawContent != null) {
322 _rawContent.DoneAddingBytes();
323 _context.Request.SetRawContent(_rawContent);
324 _rawContent = null;