Merge pull request #3562 from BrzVlad/fix-reverse-wbarrier
[mono.git] / mcs / class / System.Web / System.Web / HttpRequest.cs
old mode 100644 (file)
new mode 100755 (executable)
index e839650..8e9359a
@@ -39,16 +39,15 @@ using System.Runtime.InteropServices;
 using System.Security;
 using System.Security.Permissions;
 using System.Security.Principal;
+using System.Threading;
 using System.Web.Configuration;
 using System.Web.Management;
 using System.Web.UI;
 using System.Web.Util;
 using System.Globalization;
 
-#if NET_4_0
 using System.Security.Authentication.ExtendedProtection;
 using System.Web.Routing;
-#endif
 
 namespace System.Web
 {      
@@ -80,13 +79,17 @@ namespace System.Web
                string unescaped_path;
                string original_path;
                string path_info;
+               string path_info_unvalidated;
                string raw_url;
+               string raw_url_unvalidated;
                WebROCollection all_params;
-               WebROCollection headers;
+               NameValueCollection headers;
+               WebROCollection headers_unvalidated;
                Stream input_stream;
                InputFilterStream input_filter;
                Stream filter;
                HttpCookieCollection cookies;
+               HttpCookieCollection cookies_unvalidated;
                string http_method;
 
                WebROCollection form;
@@ -108,11 +111,11 @@ namespace System.Web
                bool checked_cookies, checked_query_string, checked_form;
                static readonly UrlMappingCollection urlMappings;
                readonly static char [] queryTrimChars = {'?'};
-#if NET_4_0
                bool lazyFormValidation;
                bool lazyQueryStringValidation;
                bool inputValidationEnabled;
                RequestContext requestContext;
+               BufferlessInputStream bufferlessInputStream;
                
                static bool validateRequestNewMode;
                internal static bool ValidateRequestNewMode {
@@ -149,7 +152,6 @@ namespace System.Web
 
                        return chars;
                }
-#endif
 
                static HttpRequest ()
                {
@@ -161,7 +163,6 @@ namespace System.Web
                                                urlMappings = null;
                                }
 
-#if NET_4_0
                                Version validationMode = HttpRuntime.Section.RequestValidationMode;
 
                                if (validationMode >= new Version (4, 0)) {
@@ -170,7 +171,6 @@ namespace System.Web
                                        if (!String.IsNullOrEmpty (invalidChars))
                                                RequestPathInvalidCharacters = CharsFromList (invalidChars);
                                }
-#endif
                        } catch {
                                // unlikely to happen
                        }
@@ -288,11 +288,9 @@ namespace System.Web
                        }
                }
 
-#if !TARGET_JVM
                public WindowsIdentity LogonUserIdentity {
                        get { throw new NotImplementedException (); }
                }
-#endif
                
                string anonymous_id;
                public string AnonymousID {
@@ -315,8 +313,7 @@ namespace System.Web
                public HttpBrowserCapabilities Browser {
                        get {
                                if (browser_capabilities == null)
-                                       browser_capabilities = (HttpBrowserCapabilities)
-                                               HttpCapabilitiesBase.GetConfigCapabilities (null, this);
+                                       browser_capabilities = HttpCapabilitiesBase.BrowserCapabilitiesProvider.GetBrowserCapabilities (this);
 
                                return browser_capabilities;
                        }
@@ -437,25 +434,29 @@ namespace System.Web
                        }
                }
 
-               public HttpCookieCollection Cookies {
+               internal HttpCookieCollection CookiesNoValidation {
                        get {
-                               if (cookies == null) {
+                               if (cookies_unvalidated == null) {
                                        if (worker_request == null) {
-                                               cookies = new HttpCookieCollection ();
+                                               cookies_unvalidated = new HttpCookieCollection ();
                                        } else {
                                                string cookie_hv = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderCookie);
-                                               cookies = new HttpCookieCollection (cookie_hv);
+                                               cookies_unvalidated = new HttpCookieCollection (cookie_hv);
                                        }
                                }
 
-#if TARGET_J2EE
-                               // For J2EE portal support we emulate cookies using the session.
-                               GetSessionCookiesForPortal (cookies);
-#endif
+                               return cookies_unvalidated;
+                       }
+               }
+
+               public HttpCookieCollection Cookies {
+                       get {
+                               if (cookies == null) {
+                                       cookies = CookiesNoValidation;
+                               }
+
                                bool needValidation = validate_cookies;
-#if NET_4_0
                                needValidation |= validateRequestNewMode;
-#endif
                                if (needValidation && !checked_cookies) {
                                        // Setting this before calling the validator prevents
                                        // possible endless recursion
@@ -476,11 +477,9 @@ namespace System.Web
                                return FilePath;
                        }
                }
-#if NET_4_0
                public string CurrentExecutionFilePathExtension {
                        get { return global::System.IO.Path.GetExtension (CurrentExecutionFilePath); }
                }
-#endif
                public string AppRelativeCurrentExecutionFilePath {
                        get {
                                return VirtualPathUtility.ToAppRelative (CurrentExecutionFilePath);
@@ -575,10 +574,8 @@ namespace System.Web
                // GetSubStream returns a 'copy' of the InputStream with Position set to 0.
                static Stream GetSubStream (Stream stream)
                {
-#if !TARGET_JVM
                        if (stream is IntPtrStream)
                                return new IntPtrStream (stream);
-#endif
 
                        if (stream is MemoryStream) {
                                MemoryStream other = (MemoryStream) stream;
@@ -648,11 +645,7 @@ namespace System.Web
                //
                // Loads the form data from on a application/x-www-form-urlencoded post
                // 
-#if TARGET_J2EE
-               void RawLoadWwwForm ()
-#else
                void LoadWwwForm ()
-#endif
                {
                        using (Stream input = GetSubStream (InputStream)) {
                                using (StreamReader s = new StreamReader (input, ContentEncoding)) {
@@ -717,7 +710,6 @@ namespace System.Web
                public NameValueCollection Form {
                        get {
                                NameValueCollection form = FormUnvalidated;
-#if NET_4_0
                                if (validateRequestNewMode && !checked_form) {
                                        if (!lazyFormValidation) {
                                                // Setting this before calling the validator prevents
@@ -726,7 +718,6 @@ namespace System.Web
                                                ValidateNameValueCollection ("Form", form, RequestValidationSource.Form);
                                        }
                                } else
-#endif
                                        if (validate_form && !checked_form){
                                                checked_form = true;
                                                ValidateNameValueCollection ("Form", form);
@@ -736,11 +727,20 @@ namespace System.Web
                        }
                }
 
+               internal NameValueCollection HeadersNoValidation {
+                       get {
+                               if (headers_unvalidated == null) {
+                                       headers_unvalidated = new HeadersCollection (this);
+                               }
+                       
+                               return headers_unvalidated;
+                       }
+               }
+
                public NameValueCollection Headers {
                        get {
                                if (headers == null) {
-                                       headers = new HeadersCollection (this);
-#if NET_4_0
+                                       headers = HeadersNoValidation;
                                        if (validateRequestNewMode) {
                                                RequestValidator validator = RequestValidator.Current;
                                                int validationFailureIndex;
@@ -752,7 +752,6 @@ namespace System.Web
                                                                ThrowValidationException ("Headers", hkey, value);
                                                }
                                        }
-#endif
                                }
                                
                                return headers;
@@ -792,7 +791,6 @@ namespace System.Web
                        input_stream = new MemoryStream (ms.GetBuffer (), 0, (int) ms.Length, false, true);
                }
 
-#if !TARGET_JVM
                const int INPUT_BUFFER_SIZE = 32*1024;
 
                TempFileStream GetTempStream ()
@@ -946,7 +944,6 @@ namespace System.Web
                        if (total < content_length)
                                throw HttpException.NewWithCode (411, "The request body is incomplete.", WebEventCodes.WebErrorOtherError);
                }
-#endif
 
                internal void ReleaseResources ()
                {
@@ -967,7 +964,6 @@ namespace System.Web
                                } catch {}
                        }
                }
-#if NET_4_0
                public RequestContext RequestContext {
                        get {
                                if (requestContext == null)
@@ -984,7 +980,169 @@ namespace System.Web
                                throw new PlatformNotSupportedException ("This property is not supported.");
                        }
                }
-#endif
+
+               public Stream GetBufferedInputStream ()
+               {
+                       return input_stream;
+               }
+
+               public Stream GetBufferlessInputStream ()
+               {
+                       if (bufferlessInputStream == null) {
+                               if (input_stream != null)
+                                       throw new HttpException ("Input stream has already been created");
+
+                               // we don't need to hook up the filter here, because the raw stream should be returned
+                               bufferlessInputStream = new BufferlessInputStream (this);
+                       }
+
+                       return bufferlessInputStream;
+               }
+
+               public Stream GetBufferlessInputStream (bool disableMaxRequestLength)
+               {
+                       return GetBufferlessInputStream ();
+               }
+
+               //
+               // Stream that returns the data as it is read, without buffering
+               //
+               class BufferlessInputStream : Stream {
+                       HttpRequest request;
+
+                       // cached, the request content-length
+                       int content_length;
+
+                       // buffer that holds preloaded data
+                       byte [] preloadedBuffer;
+
+                       // indicates if we already served the whole preloaded buffer
+                       bool preloaded_served;
+
+                       // indicates if we already checked the request content-length against httpRuntime limit
+                       bool checked_maxRequestLength;
+
+                       // our stream position
+                       long position;
+
+                       //
+                       // @request: the containing request that created us, used to find out content length
+                       public BufferlessInputStream (HttpRequest request)
+                       {
+                               this.request = request;
+                               content_length = request.ContentLength;
+                       }
+
+                       public override bool CanRead {
+                               get { return true; }
+                       }
+
+                       public override bool CanSeek {
+                               get { return false; }
+                       }
+
+                       public override bool CanWrite {
+                               get { return false; }
+                       }
+
+                       public override long Length {
+                               get {
+                                       return content_length;
+                               }
+                       }
+
+                       public override long Position {
+                               get {
+                                       return position;
+                               }
+                               set {
+                                       throw new NotSupportedException ("This is a readonly stream");
+                               }
+                       }
+
+                       public override void Flush ()
+                       {
+                       }
+
+                       public override int Read (byte [] buffer, int offset, int count)
+                       {
+                               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 (!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 0;
+                       }
+
+                       public override long Seek (long offset, SeekOrigin origin)
+                       {
+                               throw new NotSupportedException ("Can not seek on the HttpRequest.BufferlessInputStream");
+                       }
+
+                       public override void SetLength (long value)
+                       {
+                               throw new NotSupportedException ("Can not set length on the HttpRequest.BufferlessInputStream");
+                       }
+
+                       public override void Write (byte [] buffer, int offset, int count)
+                       {
+                               throw new NotSupportedException ("Can not write on the HttpRequest.BufferlessInputStream");
+                       }
+
+                       //
+                       // TODO: explicitly support the async methods if there is a convenient way of doing it
+                       //
+               }
                public Stream InputStream {
                        get {
                                if (input_stream == null)
@@ -1059,7 +1217,6 @@ namespace System.Web
                        get {
                                if (unescaped_path == null) {
                                        unescaped_path = PathNoValidation;
-#if NET_4_0
                                        if (validateRequestNewMode) {
                                                RequestValidator validator = RequestValidator.Current;
                                                int validationFailureIndex;
@@ -1067,20 +1224,29 @@ namespace System.Web
                                                if (!validator.IsValidRequestString (HttpContext.Current, unescaped_path, RequestValidationSource.Path, null, out validationFailureIndex))
                                                        ThrowValidationException ("Path", "Path", unescaped_path);
                                        }
-#endif
                                }
                                
                                return unescaped_path;
                        }
                }
 
+               internal string PathInfoNoValidation {
+                       get {
+                               if (path_info_unvalidated == null) {
+                                       if (worker_request == null)
+                                               return String.Empty;
+
+                                       path_info_unvalidated = worker_request.GetPathInfo () ?? String.Empty;
+                               }
+
+                               return path_info_unvalidated;
+                       }
+               }
+
                public string PathInfo {
                        get {
                                if (path_info == null) {
-                                       if (worker_request == null)
-                                               return String.Empty;
-                                       path_info = worker_request.GetPathInfo () ?? String.Empty;
-#if NET_4_0
+                                       path_info = PathInfoNoValidation;
                                        if (validateRequestNewMode) {
                                                RequestValidator validator = RequestValidator.Current;
                                                int validationFailureIndex;
@@ -1088,7 +1254,6 @@ namespace System.Web
                                                if (!validator.IsValidRequestString (HttpContext.Current, path_info, RequestValidationSource.PathInfo, null, out validationFailureIndex))
                                                        ThrowValidationException ("PathInfo", "PathInfo", path_info);
                                        }
-#endif
                                }
 
                                return path_info;
@@ -1162,7 +1327,6 @@ namespace System.Web
                public NameValueCollection QueryString {
                        get {
                                NameValueCollection query_string_nvc = QueryStringUnvalidated;
-#if NET_4_0
                                if (validateRequestNewMode && !checked_query_string) {
                                        if (!lazyQueryStringValidation) {
                                                // Setting this before calling the validator prevents
@@ -1171,7 +1335,6 @@ namespace System.Web
                                                ValidateNameValueCollection ("QueryString", query_string_nvc, RequestValidationSource.QueryString);
                                        }
                                } else
-#endif
                                        if (validate_query_string && !checked_query_string) {
                                                // Setting this before calling the validator prevents
                                                // possible endless recursion
@@ -1183,17 +1346,26 @@ namespace System.Web
                        }
                }
 
-               public string RawUrl {
+               internal string RawUrlUnvalidated {
                        get {
-                               if (raw_url == null) {
+                               if (raw_url_unvalidated == null) {
                                        if (worker_request != null)
-                                               raw_url = worker_request.GetRawUrl ();
+                                               raw_url_unvalidated = worker_request.GetRawUrl ();
                                        else
-                                               raw_url = UrlComponents.Path + UrlComponents.Query;
+                                               raw_url_unvalidated = UrlComponents.Path + UrlComponents.Query;
                                        
-                                       if (raw_url == null)
-                                               raw_url = String.Empty;
-#if NET_4_0
+                                       if (raw_url_unvalidated == null)
+                                               raw_url_unvalidated = String.Empty;
+                               }
+
+                               return raw_url_unvalidated;
+                       }
+               }
+
+               public string RawUrl {
+                       get {
+                               if (raw_url == null) {
+                                       raw_url = RawUrlUnvalidated;
                                        if (validateRequestNewMode) {
                                                RequestValidator validator = RequestValidator.Current;
                                                int validationFailureIndex;
@@ -1201,7 +1373,6 @@ namespace System.Web
                                                if (!validator.IsValidRequestString (HttpContext.Current, raw_url, RequestValidationSource.RawUrl, null, out validationFailureIndex))
                                                        ThrowValidationException ("RawUrl", "RawUrl", raw_url);
                                        }
-#endif
                                }
                                
                                return raw_url;
@@ -1239,6 +1410,12 @@ namespace System.Web
                        }
                }
 
+               public CancellationToken TimedOutToken {
+                       get {
+                               throw new NotImplementedException ();
+                       }
+               }
+
                public int TotalBytes {
                        get {
                                Stream ins = InputStream;
@@ -1246,6 +1423,24 @@ namespace System.Web
                        }
                }
 
+               public UnvalidatedRequestValues Unvalidated { 
+                       get {
+                               var vals = new UnvalidatedRequestValues ();
+                               
+                               vals.Cookies = CookiesNoValidation;
+                               vals.Files = Files;
+                               vals.Form = FormUnvalidated;
+                               vals.Headers = HeadersNoValidation;
+                               vals.Path = PathNoValidation;
+                               vals.PathInfo = PathInfoNoValidation;
+                               vals.QueryString = QueryStringUnvalidated;
+                               vals.RawUrl = RawUrlUnvalidated;
+                               vals.Url = Url;
+                               
+                               return vals;
+                       }
+               }
+
                public Uri Url {
                        get {
                                if (cached_url == null) {
@@ -1330,6 +1525,38 @@ namespace System.Web
                }
 
                public int [] MapImageCoordinates (string imageFieldName)
+               {
+                       string[] parameters = GetImageCoordinatesParameters (imageFieldName);
+                       if (parameters == null)
+                               return null;
+                       int [] result = new int [2];
+                       try {
+                               result [0] = Int32.Parse (parameters [0]);
+                               result [1] = Int32.Parse (parameters [1]);
+                       } catch {
+                               return null;
+                       }
+
+                       return result;
+               }
+
+               public double [] MapRawImageCoordinates (string imageFieldName)
+               {
+                       string[] parameters = GetImageCoordinatesParameters (imageFieldName);
+                       if (parameters == null)
+                               return null;
+                       double [] result = new double [2];
+                       try {
+                               result [0] = Double.Parse (parameters [0]);
+                               result [1] = Double.Parse (parameters [1]);
+                       } catch {
+                               return null;
+                       }
+
+                       return result;
+               }
+
+               string [] GetImageCoordinatesParameters (string imageFieldName)
                {
                        string method = HttpMethod;
                        NameValueCollection coll = null;
@@ -1348,14 +1575,7 @@ namespace System.Web
                        string y = coll [imageFieldName + ".y"];
                        if (y == null || y == "")
                                return null;
-
-                       int [] result = new int [2];
-                       try {
-                               result [0] = Int32.Parse (x);
-                               result [1] = Int32.Parse (y);
-                       } catch {
-                               return null;
-                       }
+                       string[] result = new string [] { x, y };
 
                        return result;
                }
@@ -1405,14 +1625,10 @@ namespace System.Web
                        
                        if (!isAppVirtualPath && !virtualPath.StartsWith (appVirtualPath, RuntimeHelpers.StringComparison))
                                throw new InvalidOperationException (String.Format ("Failed to map path '{0}'", virtualPath));
-#if TARGET_JVM
-                       return worker_request.MapPath (virtualPath);
-#else
                        string path = worker_request.MapPath (virtualPath);
                        if (virtualPath [virtualPath.Length - 1] != '/' && path [path.Length - 1] == System.IO.Path.DirectorySeparatorChar)
                                path = path.TrimEnd (System.IO.Path.DirectorySeparatorChar);
                        return path;
-#endif
                }
 
                public void SaveAs (string filename, bool includeHeaders)
@@ -1466,11 +1682,8 @@ namespace System.Web
                        validate_cookies = true;
                        validate_query_string = true;
                        validate_form = true;
-#if NET_4_0
                        inputValidationEnabled = true;
-#endif
                }
-#if NET_4_0
                internal void Validate ()
                {
                        var cfg = HttpRuntime.Section;
@@ -1498,7 +1711,6 @@ namespace System.Web
                        if (validateRequestNewMode)
                                ValidateInput ();
                }
-#endif
 #region internal routines
                internal string ClientTarget {
                        get {
@@ -1561,7 +1773,6 @@ namespace System.Web
                        string path = UrlComponents.Path;
                        UrlComponents.Path = path + PathInfo;
                }
-#if NET_4_0
                internal void SetFormCollection (WebROCollection coll, bool lazyValidation)
                {
                        if (coll == null)
@@ -1577,7 +1788,6 @@ namespace System.Web
                        query_string_nvc = coll;
                        lazyQueryStringValidation = lazyValidation;
                }
-#endif
                // Headers is ReadOnly, so we need this hack for cookie-less sessions.
                internal void SetHeader (string name, string value)
                {
@@ -1642,7 +1852,6 @@ namespace System.Web
                                        ThrowValidationException (name, key, val);
                        }
                }
-#if NET_4_0
                static void ValidateNameValueCollection (string name, NameValueCollection coll, RequestValidationSource source)
                {
                        if (coll == null)
@@ -1670,7 +1879,6 @@ namespace System.Web
                {
                        throw new PlatformNotSupportedException ("This method is not supported.");
                }
-#endif
                static void ValidateCookieCollection (HttpCookieCollection cookies)
                {
                        if (cookies == null)
@@ -1678,11 +1886,9 @@ namespace System.Web
                
                        int size = cookies.Count;
                        HttpCookie cookie;
-#if NET_4_0
                        RequestValidator validator = RequestValidator.Current;
                        int validationFailureIndex;
                        HttpContext context = HttpContext.Current;
-#endif
                        bool invalid;
                        
                        for (int i = 0 ; i < size ; i++) {
@@ -1694,11 +1900,9 @@ namespace System.Web
                                string name = cookie.Name;
 
                                if (!String.IsNullOrEmpty (value)) {
-#if NET_4_0
                                        if (validateRequestNewMode)
                                                invalid = !validator.IsValidRequestString (context, value, RequestValidationSource.Cookies, name, out validationFailureIndex);
                                        else
-#endif
                                                invalid = IsInvalidString (value);
 
                                        if (invalid)
@@ -1718,7 +1922,6 @@ namespace System.Web
                
                        throw new HttpRequestValidationException (msg);
                }
-#if NET_4_0
                internal static void ValidateString (string key, string value, RequestValidationSource source)
                {
                        if (String.IsNullOrEmpty (value))
@@ -1729,7 +1932,6 @@ namespace System.Web
                        if (IsInvalidString (value, out ignore))
                                ThrowValidationException (source.ToString (), key, value);
                }
-#endif
                internal static bool IsInvalidString (string val)
                {
 #pragma warning disable 219
@@ -1782,7 +1984,7 @@ namespace System.Web
 #endregion
 
 #region Helper classes
-       
+
        //
        // Stream-based multipart handling.
        //