System.Web: Fixed bugs in HttpRequest.GetBufferlessInputStream()
authorAlexander Köplinger <alex.koeplinger@gmail.com>
Wed, 13 Feb 2013 13:14:37 +0000 (05:14 -0800)
committerAlexander Köplinger <alex.koeplinger@gmail.com>
Wed, 13 Feb 2013 13:18:11 +0000 (05:18 -0800)
mcs/class/System.Web/System.Web/HttpRequest.cs [changed mode: 0644->0755]
mcs/class/System.Web/System.Web/HttpWorkerRequest.cs [changed mode: 0644->0755]

old mode 100644 (file)
new mode 100755 (executable)
index 6ea9d04..de51fc8
@@ -113,6 +113,7 @@ namespace System.Web
                bool lazyQueryStringValidation;
                bool inputValidationEnabled;
                RequestContext requestContext;
+               BufferlessInputStream bufferlessInputStream;
                
                static bool validateRequestNewMode;
                internal static bool ValidateRequestNewMode {
@@ -989,50 +990,17 @@ namespace System.Web
                        }
                }
 
-               //
-               // TODO:
-               //   * I no longer remember what worker_request = null meant.
-               //   * Hook up the Filter?
-               // 
-               Stream MakeBufferlessInputStream ()
+               public Stream GetBufferlessInputStream ()
                {
-                       int content_length = ContentLength;
-                       int content_length_kb = content_length / 1024;
-                       HttpRuntimeSection config = HttpRuntime.Section;
-                       if (content_length_kb > config.MaxRequestLength)
-                               throw HttpException.NewWithCode (400, "Upload size exceeds httpRuntime limit.", WebEventCodes.RuntimeErrorPostTooLarge);
+                       if (bufferlessInputStream == null) {
+                               if (input_stream != null)
+                                       throw new HttpException ("Input stream has already been created");
 
-                       int total = 0;
-                       byte [] buffer;
-                       buffer = worker_request.GetPreloadedEntityBody ();
-                       // we check the instance field 'content_length' here, not the local var.
-                       if (this.content_length <= 0 || worker_request.IsEntireEntityBodyIsPreloaded ()) {
-                               if (buffer == null || content_length == 0) {
-                                       input_stream = new MemoryStream (new byte [0], 0, 0, false, true);
-                               } else {
-                                       input_stream = new MemoryStream (buffer, 0, buffer.Length, false, true);
-                               }
-                               DoFilter (new byte [1024]);
-                               return input_stream;
+                               // we don't need to hook up the filter here, because the raw stream should be returned
+                               bufferlessInputStream = new BufferlessInputStream (this);
                        }
 
-                       if (buffer != null)
-                               total = buffer.Length;
-
-                       return new BufferlessInputStream (this, buffer);
-               }
-
-
-               public Stream GetBufferlessInputStream ()
-               {
-                       if (input_stream != null)
-                               throw new HttpException ("Input stream has already been created");
-                       input_stream = MakeBufferlessInputStream ();
-                       if (input_filter != null){
-                               input_filter.BaseStream = input_stream;
-                               return input_filter;
-                       }
-                       return input_stream;
+                       return bufferlessInputStream;
                }
 
                //
@@ -1040,33 +1008,27 @@ namespace System.Web
                //
                class BufferlessInputStream : Stream {
                        HttpRequest request;
-                       
 
                        // cached, the request content-length
                        int content_length;
 
-                       // the buffer that we read from
+                       // buffer that holds preloaded data
                        byte [] preloadedBuffer;
 
-                       // number of valid bytes in buffer.
-                       int bytes_in_buffer;
+                       // indicates if we already served the whole preloaded buffer
+                       bool preloaded_served;
 
-                       // how many bytes we have returned so far
-                       int total;
+                       // indicates if we already checked the request content-length against httpRuntime limit
+                       bool checked_maxRequestLength;
 
                        // our stream position
                        long position;
-                       int buffer_start;
 
                        //
                        // @request: the containing request that created us, used to find out content length
-                       // @buffer: the byte buffer that we start serving (may be null).
-                       // @total: number of valid bytes in buffer.
-                       public BufferlessInputStream (HttpRequest request, byte [] preloadedBuffer)
+                       public BufferlessInputStream (HttpRequest request)
                        {
                                this.request = request;
-                               this.preloadedBuffer = preloadedBuffer;
-                               bytes_in_buffer = preloadedBuffer == null ? 0 : preloadedBuffer.Length;
                                content_length = request.ContentLength;
                        }
 
@@ -1105,28 +1067,60 @@ namespace System.Web
                        {
                                if (buffer == null)
                                        throw new ArgumentNullException ("buffer");
-                               
+
                                if (offset < 0 || count < 0)
                                        throw new ArgumentOutOfRangeException ("offset or count less than zero.");
-                               
+
                                if (buffer.Length - offset < count )
                                        throw new ArgumentException ("offset+count",
                                                                     "The size of the buffer is less than offset + count.");
 
+                               if (count == 0 || request.worker_request == null)
+                                       return 0;
+
+                               if (!checked_maxRequestLength) {
+                                       int content_length_kb = content_length / 1024;
+                                       HttpRuntimeSection config = HttpRuntime.Section;
+                                       if (content_length_kb > config.MaxRequestLength)
+                                               throw HttpException.NewWithCode (400, "Upload size exceeds httpRuntime limit.", WebEventCodes.RuntimeErrorPostTooLarge);
+                                       else
+                                               checked_maxRequestLength = true;
+                               }
+
                                // Serve the bytes we might have preloaded already.
-                               if (preloadedBuffer != null){
-                                       int bytes_left = bytes_in_buffer-buffer_start;
-                                       if (bytes_left != 0){
-                                               int n = Math.Min (count, bytes_left);
-                                               Array.Copy (preloadedBuffer, buffer_start, buffer, offset, n);
-                                               buffer_start += n;
-                                               if (buffer_start == bytes_in_buffer)
-                                                       preloadedBuffer = null;
+                               if (!preloaded_served) {
+                                       if (preloadedBuffer == null)
+                                               preloadedBuffer = request.worker_request.GetPreloadedEntityBody ();
+
+                                       if (preloadedBuffer != null) {
+                                               long bytes_left = preloadedBuffer.Length-position;
+                                               int n = (int) Math.Min (count, bytes_left);
+                                               Array.Copy (preloadedBuffer, position, buffer, offset, n);
+                                               position += n;
+
+                                               if (n == bytes_left)
+                                                       preloaded_served = true;
+
                                                return n;
                                        }
+                                       else
+                                               preloaded_served = true;
+                               }
+
+                               // serve bytes from worker request if available
+                               if (position < content_length) {
+                                       long bytes_left = content_length-position;
+                                       int n = count;
+
+                                       if (bytes_left < count)
+                                               n = (int) bytes_left;
+
+                                       int bytes_read = request.worker_request.ReadEntityBody (buffer, offset, n);
+                                       position += bytes_read;
+                                       return bytes_read;
                                }
 
-                               return request.worker_request.ReadEntityBody (buffer, offset, count);
+                               return 0;
                        }
 
                        public override long Seek (long offset, SeekOrigin origin)
old mode 100644 (file)
new mode 100755 (executable)
index 21e8e94..9598012
@@ -298,7 +298,13 @@ namespace System.Web
 
                public virtual int ReadEntityBody (byte [] buffer, int offset, int size)
                {
-                       return 0;
+                       byte[] temp = new byte [size];
+                       int n = ReadEntityBody (temp, size);
+
+                       if(n > 0)
+                               Array.Copy (temp, 0, buffer, offset, n);
+
+                       return n;
                }
 
                public virtual void SendCalculatedContentLength (long contentLength)