//------------------------------------------------------------------------------
// <copyright file="HttpRequest.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------

/*
 * Request intrinsic
 *
 * Copyright (c) 1998 Microsoft Corporation
 */

namespace System.Web {
    using System;
    using System.Collections;
    using System.Collections.Specialized;
    using System.Configuration.Assemblies;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Security.Authentication.ExtendedProtection;
    using System.Security.Permissions;
    using System.Security.Principal;
    using System.Text;
    using System.Threading;
    using System.Web.Configuration;
    using System.Web.Hosting;
    using System.Web.Management;
    using System.Web.Routing;
    using System.Web.Security;
    using System.Web.SessionState;
    using System.Web.Util;

    // enumeration of dynamic server variables
    internal enum DynamicServerVariable {
        AUTH_TYPE = 1,
        AUTH_USER = 2,
        PATH_INFO = 3,
        PATH_TRANSLATED = 4,
        QUERY_STRING = 5,
        SCRIPT_NAME = 6
    };

    internal enum HttpVerb {
        Unparsed = 0,   // must be 0 so that it's zero-init value is Unparsed
        Unknown,
        GET,
        PUT,
        HEAD,
        POST,
        DEBUG,
        DELETE,
    }


    /// <devdoc>
    ///    <para>
    ///       Enables
    ///       type-safe browser to server communication. Used to gain access to HTTP request data
    ///       elements supplied by a client.
    ///    </para>
    /// </devdoc>
    public sealed class HttpRequest {
        // worker request
        [DoNotReset]
        private HttpWorkerRequest _wr;

        // context
        [DoNotReset]
        private HttpContext _context;

        // properties
        private String _httpMethod;
        private HttpVerb _httpVerb;
        private String _requestType;
        private VirtualPath _path;
        private String _rewrittenUrl;
        private bool   _computePathInfo;
        private VirtualPath _filePath;
        private VirtualPath _currentExecutionFilePath;
        private VirtualPath _pathInfo;
        private String _queryStringText;
        private bool   _queryStringOverriden;
        private byte[] _queryStringBytes;
        private String _pathTranslated;
        private String _contentType;
        private int    _contentLength = -1;
        private String _clientTarget;
        private String[] _acceptTypes;
        private String[] _userLanguages;
        private HttpBrowserCapabilities _browsercaps;
        private Uri _url;
        private Uri _referrer;
        private HttpInputStream _inputStream;
        private HttpClientCertificate _clientCertificate;
        private WindowsIdentity _logonUserIdentity;
        [DoNotReset]
        private RequestContext _requestContext;
        private string _rawUrl;
        private Stream _readEntityBodyStream;
        private ReadEntityBodyMode _readEntityBodyMode;

        // collections
        private UnvalidatedRequestValues _unvalidatedRequestValues;
        private HttpValueCollection _params;
        private HttpValueCollection _queryString;
        private HttpValueCollection _form;
        private HttpHeaderCollection _headers;
        private HttpServerVarsCollection _serverVariables;
        private HttpCookieCollection _cookies;
        [DoNotReset] // we can't reset this field when transitioning to WebSockets because it's our only remaining reference to the response cookies collection
        private HttpCookieCollection _storedResponseCookies;
        private HttpFileCollection _files;

        // content (to be read once)
        private HttpRawUploadedContent _rawContent;
        private bool _needToInsertEntityBody;
        private MultipartContentElement[] _multipartContentElements;

        // encoding (for content and query string)
        private Encoding _encoding;

        // content filtering
        private HttpInputStreamFilterSource _filterSource;
        private Stream _installedFilter;
        private bool _filterApplied;

        // Input validation
        #pragma warning disable 0649
        private SimpleBitVector32 _flags;
        #pragma warning restore 0649
        // const masks into the BitVector32
        private const int needToValidateQueryString     = 0x0001;
        private const int needToValidateForm            = 0x0002;
        private const int needToValidateCookies         = 0x0004;
        private const int needToValidateHeaders         = 0x0008;
        private const int needToValidateServerVariables = 0x0010;
        private const int contentEncodingResolved       = 0x0020;
        private const int needToValidatePostedFiles     = 0x0040;
        private const int needToValidateRawUrl          = 0x0080;
        private const int needToValidatePath            = 0x0100;
        private const int needToValidatePathInfo        = 0x0200;
        private const int hasValidateInputBeenCalled    = 0x8000;
        private const int needToValidateCookielessHeader = 0x10000;

        // True if granular request validation is enabled (validationmode >= 4.5); false if all collections validated eagerly.
        private bool _enableGranularValidation;

        // Browser caps one-time evaluator objects
        internal static object s_browserLock = new object();
        internal static bool s_browserCapsEvaled = false;

        /*
         * Internal constructor to create requests
         * that have associated HttpWorkerRequest
         *
         * @param wr HttpWorkerRequest
         */
        internal HttpRequest(HttpWorkerRequest wr, HttpContext context) {
            _wr = wr;
            _context = context;
        }

        /*
         * Public constructor for request that come from arbitrary place
         *
         * @param filename physical file name
         * @param queryString query string
         */

        /// <devdoc>
        ///    <para>
        ///       Initializes an HttpRequest object.
        ///    </para>
        /// </devdoc>
        public HttpRequest(String filename, String url, String queryString) {
            _wr = null;
            _pathTranslated = filename;
            _httpMethod = "GET";
            _url = new Uri(url);
            _path = VirtualPath.CreateAbsolute(_url.AbsolutePath);
            _queryStringText = queryString;
            _queryStringOverriden = true;
            _queryString = new HttpValueCollection(_queryStringText, true, true, Encoding.Default);

            PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_EXECUTING);
        }

        internal HttpRequest(VirtualPath virtualPath, String queryString) {
            _wr = null;
            _pathTranslated = virtualPath.MapPath();
            _httpMethod = "GET";
            _url = new Uri("http://localhost" + virtualPath.VirtualPathString);
            _path = virtualPath;
            _queryStringText = queryString;
            _queryStringOverriden = true;
            _queryString = new HttpValueCollection(_queryStringText, true, true, Encoding.Default);

            PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_EXECUTING);
        }

        internal bool NeedToInsertEntityBody {
            get { return _needToInsertEntityBody; }
            set { _needToInsertEntityBody = value; }
        }

        internal void SetRawContent(HttpRawUploadedContent rawContent) {
            Debug.Assert(rawContent != null);
            if (rawContent.Length > 0) {
                NeedToInsertEntityBody = true;
            }
            _rawContent = rawContent;
        }

        internal byte[] EntityBody { get { return NeedToInsertEntityBody ? _rawContent.GetAsByteArray() : null; } }

        internal string ClientTarget {
            get {
                return (_clientTarget == null) ? String.Empty : _clientTarget;
            }
            set {
                _clientTarget = value;
                // force re-create of browser caps
                _browsercaps = null;
            }
        }

        internal HttpContext Context {
            get { return _context; }
            set { _context = value; }
        }

        public RequestContext RequestContext {
            get {
                // Create an empty request context if we don't have one set
                if (_requestContext == null) {
                    HttpContext context = Context ?? HttpContext.Current;
                    _requestContext = new RequestContext(new HttpContextWrapper(context), new RouteData());
                }
                return _requestContext;
            }
            set {
                _requestContext = value;
            }
        }

        private bool HasTransitionedToWebSocketRequest {
            get {
                return (Context != null && Context.HasWebSocketRequestTransitionCompleted);
            }
        }

        /*
         * internal response object
         */
        internal HttpResponse Response {
            get {
                if (_context == null)
                    return null;
                return _context.Response;
            }
        }

        /*
         * Public property to determine if request is local
         */

        public bool IsLocal {
            get {
                if (_wr != null) {
                    return _wr.IsLocal();
                }
                else {
                    return false;
                }
            }
        }

        /*
         *  Cleanup code
         */
        internal void Dispose() {
            if (_serverVariables != null)
                _serverVariables.Dispose();  // disconnect from request

            if (_rawContent != null)
                _rawContent.Dispose();  // remove temp file with uploaded content

	    // 






        }

        //
        // Misc private methods to fill in collections from HttpWorkerRequest
        // properties
        //

        internal static String[] ParseMultivalueHeader(String s) {
            int l = (s != null) ? s.Length : 0;
            if (l == 0)
                return null;

            // collect comma-separated values into list

            ArrayList values = new ArrayList();
            int i = 0;

            while (i < l) {
                // find next ,
                int ci = s.IndexOf(',', i);
                if (ci < 0)
                    ci = l;

                // append corresponding server value
                values.Add(s.Substring(i, ci-i));

                // move to next
                i = ci+1;

                // skip leading space
                if (i < l && s[i] == ' ')
                    i++;
            }

            // return list as array of strings

            int n = values.Count;
            if (n == 0)
                return null;

            String[] strings = new String[n];
            values.CopyTo(0, strings, 0, n);
            return strings;
        }

        //
        // Query string collection support
        //

        private void FillInQueryStringCollection() {
            // try from raw bytes when available (better for globalization)

            byte[] rawQueryString = this.QueryStringBytes;

            if (rawQueryString != null) {
                if (rawQueryString.Length != 0)
                    _queryString.FillFromEncodedBytes(rawQueryString, QueryStringEncoding);
            }
            else if (!(String.IsNullOrEmpty(this.QueryStringText))) {
                _queryString.FillFromString(this.QueryStringText, true, QueryStringEncoding);
            }
        }

        //
        // Form collection support
        //

        private void FillInFormCollection() {
            if (_wr == null)
                return;

            if (!_wr.HasEntityBody())
                return;

            String contentType = this.ContentType;
            if (contentType == null)
                return;

            if (_readEntityBodyMode == ReadEntityBodyMode.Bufferless) {
                return;
            }

            if (StringUtil.StringStartsWithIgnoreCase(contentType, "application/x-www-form-urlencoded")) {
                // regular urlencoded form

                byte[] formBytes = null;
                HttpRawUploadedContent content = GetEntireRawContent();

                if (content != null)
                    formBytes = content.GetAsByteArray();

                if (formBytes != null) {
                    try {
                        _form.FillFromEncodedBytes(formBytes, ContentEncoding);
                    }
                    catch (Exception e) {
                        // could be thrown because of malformed data
                        throw new HttpException(SR.GetString(SR.Invalid_urlencoded_form_data), e);
                    }
                }
            }
            else if (StringUtil.StringStartsWithIgnoreCase(contentType, "multipart/form-data")) {
                // multipart form

                MultipartContentElement[] elements = GetMultipartContent();

                if (elements != null) {
                    for (int i = 0; i < elements.Length; i++) {
                        if (elements[i].IsFormItem) {
                            _form.ThrowIfMaxHttpCollectionKeysExceeded();
                            _form.Add(elements[i].Name, elements[i].GetAsString(ContentEncoding));
                        }
                    }
                }
            }
        }

        //
        // Headers collection support
        //

        private void FillInHeadersCollection() {
            if (_wr == null)
                return;

            // known headers

            for (int i = 0; i < HttpWorkerRequest.RequestHeaderMaximum; i++) {
                String h = _wr.GetKnownRequestHeader(i);

                if (!String.IsNullOrEmpty(h)) {
                    String name = HttpWorkerRequest.GetKnownRequestHeaderName(i);
                    _headers.SynchronizeHeader(name, h);
                }
            }

            // unknown headers

            String[][] hh = _wr.GetUnknownRequestHeaders();

            if (hh != null) {
                for (int i = 0; i < hh.Length; i++)
                    _headers.SynchronizeHeader(hh[i][0], hh[i][1]);
            }
        }

        //
        // Server variables collection support
        //

        private static String ServerVariableNameFromHeader(String header) {
            return("HTTP_" + header.ToUpper(CultureInfo.InvariantCulture).Replace('-', '_'));
        }

        private String CombineAllHeaders(bool asRaw) {
            if (_wr == null)
                return String.Empty;

            StringBuilder sb = new StringBuilder(256);

            // known headers

            for (int i = 0; i < HttpWorkerRequest.RequestHeaderMaximum; i++) {
                String h = _wr.GetKnownRequestHeader(i);

                if (!String.IsNullOrEmpty(h)) {
                    String name;
                    if (!asRaw)
                        name = HttpWorkerRequest.GetServerVariableNameFromKnownRequestHeaderIndex(i);
                    else
                        name = HttpWorkerRequest.GetKnownRequestHeaderName(i);

                    if (name != null) {
                        sb.Append(name);
                        sb.Append(asRaw ? ": " : ":");  // for ASP compat don't add space
                        sb.Append(h);
                        sb.Append("\r\n");
                    }
                }
            }

            // unknown headers

            String[][] hh = _wr.GetUnknownRequestHeaders();

            if (hh != null) {
                for (int i = 0; i < hh.Length; i++) {
                    String name = hh[i][0];

                    if (!asRaw)
                        name = ServerVariableNameFromHeader(name);

                    sb.Append(name);
                    sb.Append(asRaw ? ": " : ":");  // for ASP compat don't add space
                    sb.Append(hh[i][1]);
                    sb.Append("\r\n");
                }
            }

            return sb.ToString();
        }

        // callback to calculate dynamic server variable
        internal String CalcDynamicServerVariable(DynamicServerVariable var) {
            String value = null;

            switch (var) {
                case DynamicServerVariable.AUTH_TYPE:
                    if (_context.User != null && _context.User.Identity.IsAuthenticated)
                        value = _context.User.Identity.AuthenticationType;
                    else
                        value = String.Empty;
                    break;
                case DynamicServerVariable.AUTH_USER:
                    if (_context.User != null && _context.User.Identity.IsAuthenticated)
                        value = _context.User.Identity.Name;
                    else
                        value = String.Empty;
                    break;
                case DynamicServerVariable.PATH_INFO:
                    value = this.Path;
                    break;
                case DynamicServerVariable.PATH_TRANSLATED:
                    value = this.PhysicalPathInternal;
                    break;
                case DynamicServerVariable.QUERY_STRING:
                    value = this.QueryStringText;
                    break;
                case DynamicServerVariable.SCRIPT_NAME:
                    value = this.FilePath;
                    break;
            }

            return value;
        }

        [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
        private void AddServerVariableToCollection(String name, DynamicServerVariable var) {
            // dynamic server var
            _serverVariables.AddDynamic(name, var);
        }

        private void AddServerVariableToCollection(String name, String value) {
            if (value == null)
                value = String.Empty;
            // static server var
            _serverVariables.AddStatic(name, value);
        }

        private void AddServerVariableToCollection(String name) {
            // static server var from worker request
            _serverVariables.AddStatic(name, _wr.GetServerVariable(name));
        }

        internal void FillInServerVariablesCollection() {
            if (_wr == null)
                return;

            //  Add from hardcoded list

            AddServerVariableToCollection("ALL_HTTP",           CombineAllHeaders(false));
            AddServerVariableToCollection("ALL_RAW",            CombineAllHeaders(true));

            AddServerVariableToCollection("APPL_MD_PATH");

            AddServerVariableToCollection("APPL_PHYSICAL_PATH", _wr.GetAppPathTranslated());

            AddServerVariableToCollection("AUTH_TYPE",          DynamicServerVariable.AUTH_TYPE);
            AddServerVariableToCollection("AUTH_USER",          DynamicServerVariable.AUTH_USER);

            AddServerVariableToCollection("AUTH_PASSWORD");

            AddServerVariableToCollection("LOGON_USER");
            AddServerVariableToCollection("REMOTE_USER",        DynamicServerVariable.AUTH_USER);

            AddServerVariableToCollection("CERT_COOKIE");
            AddServerVariableToCollection("CERT_FLAGS");
            AddServerVariableToCollection("CERT_ISSUER");
            AddServerVariableToCollection("CERT_KEYSIZE");
            AddServerVariableToCollection("CERT_SECRETKEYSIZE");
            AddServerVariableToCollection("CERT_SERIALNUMBER");
            AddServerVariableToCollection("CERT_SERVER_ISSUER");
            AddServerVariableToCollection("CERT_SERVER_SUBJECT");
            AddServerVariableToCollection("CERT_SUBJECT");

            String clString = _wr.GetKnownRequestHeader(HttpWorkerRequest.HeaderContentLength);
            AddServerVariableToCollection("CONTENT_LENGTH",     (clString != null) ? clString : "0");

            AddServerVariableToCollection("CONTENT_TYPE",       this.ContentType);

            AddServerVariableToCollection("GATEWAY_INTERFACE");

            AddServerVariableToCollection("HTTPS");
            AddServerVariableToCollection("HTTPS_KEYSIZE");
            AddServerVariableToCollection("HTTPS_SECRETKEYSIZE");
            AddServerVariableToCollection("HTTPS_SERVER_ISSUER");
            AddServerVariableToCollection("HTTPS_SERVER_SUBJECT");

            AddServerVariableToCollection("INSTANCE_ID");
            AddServerVariableToCollection("INSTANCE_META_PATH");

            AddServerVariableToCollection("LOCAL_ADDR",         _wr.GetLocalAddress());

            AddServerVariableToCollection("PATH_INFO",          DynamicServerVariable.PATH_INFO);
            AddServerVariableToCollection("PATH_TRANSLATED",    DynamicServerVariable.PATH_TRANSLATED);

            AddServerVariableToCollection("QUERY_STRING",       DynamicServerVariable.QUERY_STRING);

            AddServerVariableToCollection("REMOTE_ADDR",        this.UserHostAddress);
            AddServerVariableToCollection("REMOTE_HOST",        this.UserHostName);

            AddServerVariableToCollection("REMOTE_PORT");

            AddServerVariableToCollection("REQUEST_METHOD",     this.HttpMethod);

            AddServerVariableToCollection("SCRIPT_NAME",        DynamicServerVariable.SCRIPT_NAME);

            AddServerVariableToCollection("SERVER_NAME",        _wr.GetServerName());
            AddServerVariableToCollection("SERVER_PORT",        _wr.GetLocalPortAsString());

            AddServerVariableToCollection("SERVER_PORT_SECURE", _wr.IsSecure() ? "1" : "0");

            AddServerVariableToCollection("SERVER_PROTOCOL",    _wr.GetHttpVersion());
            AddServerVariableToCollection("SERVER_SOFTWARE");

            AddServerVariableToCollection("URL",                DynamicServerVariable.SCRIPT_NAME);

            // Add all headers in HTTP_XXX format

            for (int i = 0; i < HttpWorkerRequest.RequestHeaderMaximum; i++) {
                String h = _wr.GetKnownRequestHeader(i);
                if (!String.IsNullOrEmpty(h))
                    AddServerVariableToCollection(HttpWorkerRequest.GetServerVariableNameFromKnownRequestHeaderIndex(i), h);
            }

            String[][] hh = _wr.GetUnknownRequestHeaders();

            if (hh != null) {
                for (int i = 0; i < hh.Length; i++)
                    AddServerVariableToCollection(ServerVariableNameFromHeader(hh[i][0]), hh[i][1]);
            }
        }

        //
        // Cookies collection support
        //

        internal static HttpCookie CreateCookieFromString(String s) {
            HttpCookie c = new HttpCookie();

            int l = (s != null) ? s.Length : 0;
            int i = 0;
            int ai, ei;
            bool firstValue = true;
            int numValues = 1;

            // Format: cookiename[=key1=val2&key2=val2&...]

            while (i < l) {
                //  find next &
                ai = s.IndexOf('&', i);
                if (ai < 0)
                    ai = l;

                // first value might contain cookie name before =
                if (firstValue) {
                    ei = s.IndexOf('=', i);

                    if (ei >= 0 && ei < ai) {
                        c.Name = s.Substring(i, ei-i);
                        i = ei+1;
                    }
                    else if (ai == l) {
                        // the whole cookie is just a name
                        c.Name = s;
                        break;
                    }

                    firstValue = false;
                }

                // find '='
                ei = s.IndexOf('=', i);

                if (ei < 0 && ai == l && numValues == 0) {
                    // simple cookie with simple value
                    c.Value = s.Substring(i, l-i);
                }
                else if (ei >= 0 && ei < ai) {
                    // key=value
                    c.Values.Add(s.Substring(i, ei-i), s.Substring(ei+1, ai-ei-1));
                    numValues++;
                }
                else {
                    // value without key
                    c.Values.Add(null, s.Substring(i, ai-i));
                    numValues++;
                }

                i = ai+1;
            }

            return c;
        }

        internal void FillInCookiesCollection(HttpCookieCollection cookieCollection, bool includeResponse) {
            if (_wr == null)
                return;

            String s = _wr.GetKnownRequestHeader(HttpWorkerRequest.HeaderCookie);

            // Parse the cookie server variable.
            // Format: c1=k1=v1&k2=v2; c2=...

            int l = (s != null) ? s.Length : 0;
            int i = 0;
            int j;
            char ch;

            HttpCookie lastCookie = null;

            while (i < l) {
                // find next ';' (don't look to ',' as per 91884)
                j = i;
                while (j < l) {
                    ch = s[j];
                    if (ch == ';')
                        break;
                    j++;
                }

                // create cookie form string
                String cookieString = s.Substring(i, j-i).Trim();
                i = j+1; // next cookie start

                if (cookieString.Length == 0)
                    continue;

                HttpCookie cookie = CreateCookieFromString(cookieString);

                // some cookies starting with '$' are really attributes of the last cookie
                if (lastCookie != null) {
                    String name = cookie.Name;

                    // add known attribute to the last cookie (if any)
                    if (name != null && name.Length > 0 && name[0] == '$') {
                        if (StringUtil.EqualsIgnoreCase(name, "$Path"))
                            lastCookie.Path = cookie.Value;
                        else if (StringUtil.EqualsIgnoreCase(name, "$Domain"))
                            lastCookie.Domain = cookie.Value;

                        continue;
                    }
                }

                // regular cookie
                cookieCollection.AddCookie(cookie, true);
                lastCookie = cookie;

                // goto next cookie
            }

            // Append response cookies
            if (includeResponse) {
                // If we have a reference to the response cookies collection, use it directly
                // rather than going through the Response object (which might not be available, e.g.
                // if we have already transitioned to a WebSockets request).
                HttpCookieCollection storedResponseCookies = _storedResponseCookies;
                if (storedResponseCookies == null && !HasTransitionedToWebSocketRequest && Response != null) {
                    storedResponseCookies = Response.GetCookiesNoCreate();
                }

                if (storedResponseCookies != null && storedResponseCookies.Count > 0) {
                    HttpCookie[] responseCookieArray = new HttpCookie[storedResponseCookies.Count];
                    storedResponseCookies.CopyTo(responseCookieArray, 0);
                    for (int iCookie = 0; iCookie < responseCookieArray.Length; iCookie++)
                        cookieCollection.AddCookie(responseCookieArray[iCookie], append: true);
                }

                // release any stored reference to the response cookie collection
                _storedResponseCookies = null;
            }
        }

        internal void StoreReferenceToResponseCookies(HttpCookieCollection responseCookies) {
            _storedResponseCookies = responseCookies;
        }

        // Params collection support
        private void FillInParamsCollection() {
            _params.Add(this.QueryString);
            _params.Add(this.Form);
            _params.Add(this.Cookies);
            _params.Add(this.ServerVariables);
        }

        //
        // Files collection support
        //

        private void FillInFilesCollection() {
            if (_wr == null)
                return;

            if (!StringUtil.StringStartsWithIgnoreCase(ContentType, "multipart/form-data"))
                return;

            MultipartContentElement[] elements = GetMultipartContent();
            if (elements == null)
                return;

            for (int i = 0; i < elements.Length; i++) {
                if (elements[i].IsFile) {
                    HttpPostedFile p = elements[i].GetAsPostedFile();
                    _files.AddFile(elements[i].Name, p);
                }
            }
        }

        //
        // Reading posted content ...
        //

        /*
         * Get attribute off header value
         */
        private static String GetAttributeFromHeader(String headerValue, String attrName) {
            if (headerValue == null)
                return null;

            int l = headerValue.Length;
            int k = attrName.Length;

            // find properly separated attribute name
            int i = 1; // start searching from 1

            while (i < l) {
                i = CultureInfo.InvariantCulture.CompareInfo.IndexOf(headerValue, attrName, i, CompareOptions.IgnoreCase);
                if (i < 0)
                    break;
                if (i+k >= l)
                    break;

                char chPrev = headerValue[i-1];
                char chNext = headerValue[i+k];
                if ((chPrev == ';' || chPrev == ',' || Char.IsWhiteSpace(chPrev)) && (chNext == '=' || Char.IsWhiteSpace(chNext)))
                    break;

                i += k;
            }

            if (i < 0 || i >= l)
                return null;

            // skip to '=' and the following whitespaces
            i += k;
            while (i < l && Char.IsWhiteSpace(headerValue[i]))
                i++;
            if (i >= l || headerValue[i] != '=')
                return null;
            i++;
            while (i < l && Char.IsWhiteSpace(headerValue[i]))
                i++;
            if (i >= l)
                return null;

            // parse the value
            String attrValue = null;

            int j;

            if (i < l && headerValue[i] == '"') {
                if (i == l-1)
                    return null;
                j = headerValue.IndexOf('"', i+1);
                if (j < 0 || j == i+1)
                    return null;

                attrValue = headerValue.Substring(i+1, j-i-1).Trim();
            }
            else {
                for (j = i; j < l; j++) {
                    if (headerValue[j] == ' ' || headerValue[j] == ',')
                        break;
                }

                if (j == i)
                    return null;

                attrValue = headerValue.Substring(i, j-i).Trim();
            }

            return attrValue;
        }

        /*
         * In case content-type header contains encoding it should override the config
         */
        private Encoding GetEncodingFromHeaders() {

            if (UserAgent != null && CultureInfo.InvariantCulture.CompareInfo.IsPrefix(UserAgent, "UP")) {
                String postDataCharset = Headers["x-up-devcap-post-charset"];
                if (!String.IsNullOrEmpty(postDataCharset)) {
                    try {
                        return Encoding.GetEncoding(postDataCharset);
                    }
                    catch {
                        // Exception may be thrown when charset is not valid.
                        // In this case, do nothing, and let the framework
                        // use the configured RequestEncoding setting.
                    }
                }
            }


            if (!_wr.HasEntityBody())
                return null;

            String contentType = this.ContentType;
            if (contentType == null)
                return null;

            String charSet = GetAttributeFromHeader(contentType, "charset");
            if (charSet == null)
                return null;

            Encoding encoding = null;

            try {
                encoding = Encoding.GetEncoding(charSet);
            }
            catch {
                // bad encoding string throws an exception that needs to be consumed
            }

            return encoding;
        }

        /*
         * Read entire raw content as byte array
         */
        private HttpRawUploadedContent GetEntireRawContent() {
            if (_wr == null)
                return null;

            if (_rawContent != null) {
                // if _rawContent was set by HttpBufferlessInputStream, then we will apply the filter here
                if (_installedFilter != null && !_filterApplied) {
                    ApplyFilter(ref _rawContent, RuntimeConfig.GetConfig(_context).HttpRuntime.RequestLengthDiskThresholdBytes);
                }
                return _rawContent;
            }

            if (_readEntityBodyMode == ReadEntityBodyMode.None) {
                _readEntityBodyMode = ReadEntityBodyMode.Classic;
            }
            else if (_readEntityBodyMode == ReadEntityBodyMode.Buffered) {
                // _rawContent should have been set already
                throw new InvalidOperationException(SR.GetString(SR.Invalid_operation_with_get_buffered_input_stream));
            }
            else if (_readEntityBodyMode == ReadEntityBodyMode.Bufferless) {
                throw new HttpException(SR.GetString(SR.Incompatible_with_get_bufferless_input_stream));
            }

            // enforce the limit
            HttpRuntimeSection cfg = RuntimeConfig.GetConfig(_context).HttpRuntime;
            int limit = cfg.MaxRequestLengthBytes;
            if (ContentLength > limit) {
                if ( !(_wr is IIS7WorkerRequest) ) {
                    Response.CloseConnectionAfterError();
                }
                throw new HttpException(SR.GetString(SR.Max_request_length_exceeded),
                                        null, WebEventCodes.RuntimeErrorPostTooLarge);
            }

            // threshold to go to file

            int fileThreshold = cfg.RequestLengthDiskThresholdBytes;

            // read the preloaded content

            HttpRawUploadedContent rawContent = new HttpRawUploadedContent(fileThreshold, ContentLength);

            byte[] preloadedContent = _wr.GetPreloadedEntityBody();

            if (preloadedContent != null) {
                _wr.UpdateRequestCounters(preloadedContent.Length);
                rawContent.AddBytes(preloadedContent, 0, preloadedContent.Length);
            }

            // read the remaing content

            if (!_wr.IsEntireEntityBodyIsPreloaded()) {
                int remainingBytes = (ContentLength > 0) ? ContentLength - rawContent.Length : Int32.MaxValue;

                HttpApplication app = _context.ApplicationInstance;
                byte[] buf = (app != null) ? app.EntityBuffer : new byte[8 * 1024];
                int numBytesRead = rawContent.Length;

                while (remainingBytes > 0) {
                    int bytesToRead = buf.Length;
                    if (bytesToRead > remainingBytes)
                        bytesToRead = remainingBytes;

                    int bytesRead = _wr.ReadEntityBody(buf, bytesToRead);
                    if (bytesRead <= 0)
                        break;

                    _wr.UpdateRequestCounters(bytesRead);

                    rawContent.AddBytes(buf, 0, bytesRead);

                    remainingBytes -= bytesRead;
                    numBytesRead += bytesRead;

                    if (numBytesRead > limit) {
                        throw new HttpException(SR.GetString(SR.Max_request_length_exceeded),
                                    null, WebEventCodes.RuntimeErrorPostTooLarge);
                    }

                    // Fail synchrously if receiving the request content takes too long
                    // RequestTimeoutManager is not efficient in case of ThreadPool starvation
                    // as the timer callback doing Thread.Abort may not trigger for a long time
                    if (remainingBytes > 0 && _context.HasTimeoutExpired) {
                        throw new HttpException(SR.GetString(SR.Request_timed_out));
                    }
                }
            }

            rawContent.DoneAddingBytes();

            // filter content
            if (_installedFilter != null) {
                ApplyFilter(ref rawContent, fileThreshold);
            }

            SetRawContent(rawContent);
            return _rawContent;
        }

        private void ApplyFilter(ref HttpRawUploadedContent rawContent, int fileThreshold) {
            if (_installedFilter != null) {
                _filterApplied = true;
                if (rawContent.Length > 0) {
                    try {
                        try {
                            _filterSource.SetContent(rawContent);
                            
                            HttpRawUploadedContent filteredRawContent = new HttpRawUploadedContent(fileThreshold, rawContent.Length);
                            HttpApplication app = _context.ApplicationInstance;
                            byte[] buf = (app != null) ? app.EntityBuffer : new byte[8 * 1024];
                            
                            for (;;) {
                                int bytesRead = _installedFilter.Read(buf, 0, buf.Length);
                                if (bytesRead == 0)
                                    break;
                                filteredRawContent.AddBytes(buf, 0, bytesRead);
                            }
                            
                            filteredRawContent.DoneAddingBytes();
                            rawContent = filteredRawContent;
                        }
                        finally {
                            _filterSource.SetContent(null);
                        }
                    }
                    catch { // Protect against exception filters
                        throw;
                    }
                }
            }
        }

        /*
         * Get multipart posted content as array of elements
         */
        private MultipartContentElement[] GetMultipartContent() {
            // already parsed
            if (_multipartContentElements != null)
                return _multipartContentElements;

            // check the boundary
            byte[] boundary = GetMultipartBoundary();
            if (boundary == null)
                return new MultipartContentElement[0];

            // read the content if not read already
            HttpRawUploadedContent content = GetEntireRawContent();
            if (content == null)
                return new MultipartContentElement[0];

            // do the parsing
            _multipartContentElements = HttpMultipartContentTemplateParser.Parse(content, content.Length, boundary, ContentEncoding);
            return _multipartContentElements;
        }

        /*
         * Get boundary for the posted multipart content as byte array
         */

        private byte[] GetMultipartBoundary() {
            // extract boundary value
            String b = GetAttributeFromHeader(ContentType, "boundary");
            if (b == null)
                return null;

            // prepend with "--" and convert to byte array
            b = "--" + b;
            return Encoding.ASCII.GetBytes(b.ToCharArray());
        }

        //
        // Request cookies sometimes are populated from Response
        // Here are helper methods to do that.
        //

        /*
         * Add response cookie to request collection (can override existing)
         */
        internal void AddResponseCookie(HttpCookie cookie) {
            // cookies collection

            if (_cookies != null)
                _cookies.AddCookie(cookie, true);

            // cookies also go to parameters collection

            if (_params != null) {
                _params.MakeReadWrite();
                _params.Add(cookie.Name, cookie.Value);
                _params.MakeReadOnly();
            }
        }

        /*
         * Clear any cookies response might've added
         */
        internal void ResetCookies() {
            // cookies collection

            if (_cookies != null) {
                _cookies.Reset();
                FillInCookiesCollection(_cookies, true /*includeResponse*/);
            }

            // cookies also go to parameters collection

            if (_params != null) {
                _params.MakeReadWrite();
                _params.Reset();
                FillInParamsCollection();
                _params.MakeReadOnly();
            }
        }

        /*
         * Http method (verb) associated with the current request
         */

        /// <devdoc>
        ///    <para>Indicates the HTTP data transfer method used by client (GET, POST). This property is read-only.</para>
        /// </devdoc>
        public String HttpMethod {
            get {
                // Directly from worker request
                if (_httpMethod == null) {
                    Debug.Assert(_wr != null);
                    _httpMethod = _wr.GetHttpVerbName();
                }

                return _httpMethod;
            }
        }

        internal HttpVerb HttpVerb {
            get {
                if (_httpVerb == HttpVerb.Unparsed) {
                    _httpVerb = HttpVerb.Unknown;
                    string method = HttpMethod;
                    if (method != null) {
                        switch (method.Length) {
                            case 3:
                                if (method == "GET") {
                                    _httpVerb = HttpVerb.GET;
                                }
                                else if (method == "PUT") {
                                    _httpVerb = HttpVerb.PUT;
                                }
                                break;

                            case 4:
                                if (method == "POST") {
                                    _httpVerb = HttpVerb.POST;
                                }
                                else if (method == "HEAD") {
                                    _httpVerb = HttpVerb.HEAD;
                                }
                                break;

                            case 5:
                                if (method == "DEBUG") {
                                    _httpVerb = HttpVerb.DEBUG;
                                }
                                break;

                            case 6:
                                if (method == "DELETE") {
                                    _httpVerb = HttpVerb.DELETE;
                                }
                                break;
                        }
                    }
                }

                return _httpVerb;
            }
        }

        // Check whether this is a DEBUG verb request
        internal bool IsDebuggingRequest {
            get {
                return (HttpVerb == HttpVerb.DEBUG);
            }
        }

        /*
         * RequestType default to verb, but can be changed
         */

        /// <devdoc>
        ///    Indicates the HTTP data transfer method used by client
        ///    (GET, POST).
        /// </devdoc>
        public String RequestType {
            get {
                return(_requestType != null) ? _requestType : this.HttpMethod;
            }

            set {
                _requestType = value;
            }
        }

        /*
          * Content-type of the content posted with the current request
          */

        /// <devdoc>
        ///    <para>Indicates the MIME content type of incoming request. This property is read-only.</para>
        /// </devdoc>
        public String ContentType {
            get {
                if (_contentType == null) {
                    if (_wr != null)
                        _contentType = _wr.GetKnownRequestHeader(HttpWorkerRequest.HeaderContentType);

                    if (_contentType == null)
                        _contentType = String.Empty;
                }

                return _contentType;
            }

            set {
                _contentType = value;
            }
        }



        /// <devdoc>
        ///    <para>Indicates the content length of incoming request. This property is read-only.</para>
        /// </devdoc>
        public int ContentLength {
            get {
                if (_contentLength == -1) {
                    if (_wr != null) {
                        String s = _wr.GetKnownRequestHeader(HttpWorkerRequest.HeaderContentLength);

                        if (s != null) {
                            try {
                                _contentLength = Int32.Parse(s, CultureInfo.InvariantCulture);
                            }
                            catch {
                            }
                        }
                        else {
                            // no content-length header, but there is data
                            if (_wr.IsEntireEntityBodyIsPreloaded()) {
                                byte[] preloadedContent = _wr.GetPreloadedEntityBody();

                                if (preloadedContent != null)
                                    _contentLength = preloadedContent.Length;
                            }
                        }
                    }
                }

                return (_contentLength >= 0) ? _contentLength : 0;
            }
        }

        /*
         * Encoding to read posted text content
         */

        /// <devdoc>
        ///    <para>Indicates the character set of data supplied by client. This property is read-only.</para>
        /// </devdoc>
        public Encoding ContentEncoding {
            get {
                if(_flags[contentEncodingResolved] && _encoding != null) {
                    return _encoding;
                }

                _encoding = GetEncodingFromHeaders();

                // DevDiv #351560 - UTF-7 is dangerous and should be forbidden by default.
                // The application developer can choose to allow it if desired.
                if (_encoding is UTF7Encoding && !AppSettings.AllowUtf7RequestContentEncoding) {
                    _encoding = null;
                }

                if (_encoding == null) {
                    // WOS 1953542: No Event Is Logged When App Config is Corrupt
                    GlobalizationSection globConfig = RuntimeConfig.GetLKGConfig(_context).Globalization;
                    _encoding = globConfig.RequestEncoding;
                }

                _flags.Set(contentEncodingResolved);
                return _encoding;
            }

            set {
                _encoding = value;
                _flags.Set(contentEncodingResolved);
            }
        }

        internal Encoding QueryStringEncoding {
            get {
                Encoding e = ContentEncoding;
                // query string is never unicode - use utf-8 if instead
                return e.Equals(Encoding.Unicode) ? Encoding.UTF8 : e;
            }
        }

        /*
         * Parsed Accept header as array of strings
         */

        /// <devdoc>
        ///    <para>Returns a string array of client-supported MIME accept types. This property is read-only.</para>
        /// </devdoc>
        public String[] AcceptTypes {
            get {
                if (_acceptTypes == null) {
                    if (_wr != null)
                        _acceptTypes = ParseMultivalueHeader(_wr.GetKnownRequestHeader(HttpWorkerRequest.HeaderAccept));
                }

                return _acceptTypes;
            }
        }

        // Is the request authenticated?
        public bool IsAuthenticated {
            get {
                return(_context.User != null && _context.User.Identity != null && _context.User.Identity.IsAuthenticated);
            }
        }

        // Is using HTTPS?
        //    Indicates whether the HTTP connection is secure (that is, HTTPS). This property is read-only.
        public bool IsSecureConnection {
            get {
                if (_wr != null)
                    return _wr.IsSecure();
                else
                    return false;
            }
        }


        /*
         * Virtual path corresponding to the requested Url
         */

        /// <devdoc>
        ///    <para>Indicates the virtual path of the current
        ///       request, including the path PathInfo. This property is read-only.</para>
        /// </devdoc>
        public String Path {
            get {
                string path = GetUnvalidatedPath();
                if (_flags[needToValidatePath]) {
                    _flags.Clear(needToValidatePath);
                    ValidateString(path, null, RequestValidationSource.Path);
                }
                return path;
            }
        }

        internal VirtualPath PathObject {
            get {
                if (_path == null) {
                    // Directly from worker request

                    Debug.Assert(_wr != null);

                    // Don't allow malformed paths for security reasons
                    _path = VirtualPath.Create(_wr.GetUriPath(),
                        VirtualPathOptions.AllowAbsolutePath);
                }

                return _path;
            }
        }

        // Gets the Path property but does not hook up validation.
        internal string GetUnvalidatedPath() {
            return PathObject.VirtualPathString;
        }

        [DoNotReset]
        private string _anonymousId;

        public string AnonymousID {
            get { return _anonymousId; }
            internal set { _anonymousId = value; }
        }

        internal String PathWithQueryString {
            get {
                String qs = QueryStringText;
                return (!String.IsNullOrEmpty(qs)) ? (Path + "?" + qs) : Path;
            }
        }

        // The virtual file path where the client browsers think we are.
        // However, in the case of cookieless session, ClientFilePath does *not* include the session id.
        private VirtualPath _clientFilePath;
        internal VirtualPath ClientFilePath {
            get {
                if (_clientFilePath == null) {
                    string uri = RawUrl;

                    // remove query string if it exists
                    int qsIndex = uri.IndexOf('?');
                    if (qsIndex > -1) {
                        uri = uri.Substring(0, qsIndex);
                    }
                    _clientFilePath = VirtualPath.Create(uri, VirtualPathOptions.AllowAbsolutePath);
                }

                Debug.Trace("ClientUrl", "*** ClientFilePath --> " + _clientFilePath + " ***");
                return _clientFilePath;
            }
            set {
                _clientFilePath = value;
            }
        }

        // The base dir of the client virtual file path.
        // However, in the case of cookieless session, ClientFilePath does *not* include the session id.
        private VirtualPath _clientBaseDir;

        // VSWhidbey 560283 : The ClientBaseDir represents the directory of the current
        // request to the client.
        //
        // the request is   ClientBaseDir   FilePathObject (FilePathObject.Parent)
        // 1. /app/sub/     /app/sub/       /app/
        // 2. /app/sub      /app/           /app/
        internal VirtualPath ClientBaseDir {
            get {
                if (_clientBaseDir == null) {
                    // client virtual path before the last '/'
                    if (ClientFilePath.HasTrailingSlash) {
                        _clientBaseDir = ClientFilePath;
                    }
                    else {
                        _clientBaseDir = ClientFilePath.Parent;
                    }
                }

                return _clientBaseDir;
            }
        }

        /*
         * File path corresponding to the requested Url
         */

        /// <devdoc>
        ///    <para>Indicates the virtual path of the current request, but without the PathInfo.
        ///         This property is read-only.</para>
        /// </devdoc>
        public String FilePath {
            get {
                return VirtualPath.GetVirtualPathString(FilePathObject);
            }
        }

        internal VirtualPath FilePathObject {
            get {
                if (_filePath != null) {
                    return _filePath;
                }

                if (!_computePathInfo) {
                    // Directly from worker request

                    if (_wr != null) {
                        _filePath = _wr.GetFilePathObject();
                    }
                    else {
                        _filePath = PathObject;
                    }
                }
                else if (_context != null) {
                    // From config
                    //
                    //          RAID#93378
                    //          Config system relies on FilePath for lookups so we should not
                    //          be calling it while _filePath is null or it will lead to
                    //          infinite recursion.
                    //
                    //          It is safe to set _filePath to Path as longer path would still
                    //          yield correct configuration, just a little slower.

                    _filePath = PathObject;

                    int filePathLen = _context.GetFilePathData().Path.VirtualPathStringNoTrailingSlash.Length;

                    // case could be wrong in config (_path has the correct case)
                    string path = Path;
                    int pathLength = path.Length;
                    // If path is extensionless, _filePath should be equal to path--trailing slash should not be removed.
                    if (pathLength != filePathLen
                        && (pathLength - filePathLen != 1
                            || path[pathLength-1] != '/'
                            || path.IndexOf('.') > -1)
                        )
                        _filePath = VirtualPath.CreateAbsolute(Path.Substring(0, filePathLen));
                }

                return _filePath;
            }
        }

        /*
         * Normally the same as ClientFilePath.  The difference is that when doing a
         * Server.Execute, ClientFilePath doesn't change, while this changes to the
         * currently executing virtual path
         */

        public string CurrentExecutionFilePath {
            get {
                return CurrentExecutionFilePathObject.VirtualPathString;
            }
        }

        public string CurrentExecutionFilePathExtension {
            get {
                return UrlPath.GetExtension(CurrentExecutionFilePathObject.VirtualPathString);
            }
        }

        internal VirtualPath CurrentExecutionFilePathObject {
            get {
                if (_currentExecutionFilePath != null)
                    return _currentExecutionFilePath;

                return FilePathObject;
            }
        }

        internal VirtualPath SwitchCurrentExecutionFilePath(VirtualPath path) {
            VirtualPath oldPath = _currentExecutionFilePath;
            _currentExecutionFilePath = path;
            return oldPath;
        }


        // Same as CurrentExecutionFilePath, but made relative to the application root,
        // so it is application-agnostic.
        public string AppRelativeCurrentExecutionFilePath {
            get {
                return UrlPath.MakeVirtualPathAppRelative(CurrentExecutionFilePath);
            }
        }

        // Path-info corresponding to the requested Url
        //    Indicates additional path information for a resource with a URL extension. i.e. for the URL
        //       /virdir/page.html/tail, the PathInfo value is /tail. This property is read-only.</para>
        public String PathInfo {
            get {
                string pathInfo = GetUnvalidatedPathInfo();
                if (_flags[needToValidatePathInfo]) {
                    _flags.Clear(needToValidatePathInfo);
                    ValidateString(pathInfo, null, RequestValidationSource.PathInfo);
                }
                return pathInfo;
            }
        }

        internal VirtualPath PathInfoObject {
            get {
                if (_pathInfo != null) {
                    return _pathInfo;
                }

                if (!_computePathInfo) {
                    // Directly from worker request

                    if (_wr != null) {
                        _pathInfo = VirtualPath.CreateAbsoluteAllowNull(_wr.GetPathInfo());
                    }
                }

                if (_pathInfo == null && _context != null) {
                    VirtualPath path = PathObject;
                    int pathLength = path.VirtualPathString.Length;
                    VirtualPath filePath = FilePathObject;
                    int filePathLength = filePath.VirtualPathString.Length;

                    if (filePath == null)
                        _pathInfo = path;
                    else if (path == null || pathLength <= filePathLength )
                        _pathInfo = null;
                    else {
                        string pathInfoString = path.VirtualPathString.Substring(filePathLength, pathLength - filePathLength);
                        _pathInfo = VirtualPath.CreateAbsolute(pathInfoString);
                    }
                }

                return _pathInfo;
            }
        }

        // Gets the PathInfo property but does not hook up validation.
        internal string GetUnvalidatedPathInfo() {
            VirtualPath pathInfoObject = PathInfoObject;
            return (pathInfoObject == null) ? String.Empty : pathInfoObject.VirtualPathString;
        }

        /*
         * Physical path corresponding to the requested Url
         */

        /// <devdoc>
        ///    <para>Gets the physical file system path corresponding
        ///       to
        ///       the requested URL. This property is read-only.</para>
        /// </devdoc>
        public String PhysicalPath {
            get {
                String path = PhysicalPathInternal;
                InternalSecurityPermissions.PathDiscovery(path).Demand();
                return path;
            }
        }

        internal String PhysicalPathInternal {
            get {
                if (_pathTranslated == null) {
                    if (!_computePathInfo) {
                        // Directly from worker request
                        Debug.Assert(_wr != null);
                        _pathTranslated = _wr.GetFilePathTranslated();
                        if (HttpRuntime.IsMapPathRelaxed)
                            _pathTranslated = HttpRuntime.GetRelaxedMapPathResult(_pathTranslated);
                    }

                    if (_pathTranslated == null && _wr != null) {
                        // Compute after rewrite
                        _pathTranslated = HostingEnvironment.MapPathInternal(FilePath);
                    }
                }

                return _pathTranslated;
            }
        }

        /*
         * Virtual path to the application root
         */

        /// <devdoc>
        ///    <para>Gets the
        ///       virtual path to the currently executing server application.</para>
        /// </devdoc>
        public String ApplicationPath {
            get {
                return HttpRuntime.AppDomainAppVirtualPath;
            }
        }

        internal VirtualPath ApplicationPathObject {
            get {
                return HttpRuntime.AppDomainAppVirtualPathObject;
            }
        }

        /*
         * Physical path to the application root
         */

        /// <devdoc>
        ///    <para>Gets the physical
        ///       file system path of currently executing server application.</para>
        /// </devdoc>
        public String PhysicalApplicationPath {
            get {
                InternalSecurityPermissions.AppPathDiscovery.Demand();

                if (_wr != null)
                    return _wr.GetAppPathTranslated();
                else
                    return null;
            }
        }

        /*
         * User agent string
         */

        /// <devdoc>
        ///    <para>Gets the client
        ///       browser's raw User Agent String.</para>
        /// </devdoc>
        public String UserAgent {
            get {
                if (_wr != null)
                    return _wr.GetKnownRequestHeader(HttpWorkerRequest.HeaderUserAgent);
                else
                    return null;
            }
        }

        /*
         * Accepted user languages
         */

        /// <devdoc>
        ///    <para>Gets a
        ///       sorted array of client language preferences.</para>
        /// </devdoc>
        public String[] UserLanguages {
            get {
                if (_userLanguages == null) {
                    if (_wr != null)
                        _userLanguages = ParseMultivalueHeader(_wr.GetKnownRequestHeader(HttpWorkerRequest.HeaderAcceptLanguage));
                }

                return _userLanguages;
            }
        }

        // Browser caps
        //    Provides information about incoming client's browser capabilities.
        public HttpBrowserCapabilities Browser {
            get {
                if(_browsercaps != null) {
                    return _browsercaps;
                }

                if (! s_browserCapsEvaled) {
                    lock (s_browserLock) {
                        if (! s_browserCapsEvaled) {
                            HttpCapabilitiesBase.GetBrowserCapabilities(this);
                        }
                        s_browserCapsEvaled = true;
                    }
                }

                _browsercaps = (HttpBrowserCapabilities)HttpCapabilitiesBase.GetBrowserCapabilities(this);
                return _browsercaps;
            }

            set {
                _browsercaps = value;
            }
        }

        /*
         * Client's host name
         */

        /// <devdoc>
        ///    <para>Gets the
        ///       DNS name of remote client.</para>
        /// </devdoc>
        public String UserHostName {
            get {
                String s = (_wr != null) ? _wr.GetRemoteName() : null;
                if (String.IsNullOrEmpty(s))
                    s = UserHostAddress;
                return s;
            }
        }

        /*
         * Client's host address
         */

        /// <devdoc>
        ///    <para>Gets the
        ///       IP host address of remote client.</para>
        /// </devdoc>
        public String UserHostAddress {
            get {
                if (_wr != null)
                    return _wr.GetRemoteAddress();
                else
                    return null;
            }
        }

        /*
         * The current request's RAW Url (as supplied by worker request)
         */

        /// <devdoc>
        ///    <para>Gets the URI requsted by the client, which may include PathInfo and QueryString if it exists.
        ///    This value is unaffected by any URL rewriting or routing that may occur on the server.</para>
        /// </devdoc>
        public String RawUrl {
            get {
                EnsureRawUrl();

                if (_flags[needToValidateRawUrl]) {
                    _flags.Clear(needToValidateRawUrl);
                    ValidateString(_rawUrl, null, RequestValidationSource.RawUrl);
                }
                return _rawUrl;
            }
            internal set {
                _rawUrl = value;
            }
        }

        // Populates the RawUrl property but does not hook up validation.
        internal string EnsureRawUrl() {
            if (_rawUrl == null) {
                String url;

                if (_wr != null) {
                    url = _wr.GetRawUrl();
                }
                else {
                    String p = this.GetUnvalidatedPath();
                    String qs = this.QueryStringText;

                    if (!String.IsNullOrEmpty(qs))
                        url = p + "?" + qs;
                    else
                        url = p;
                }
                _rawUrl = url;
            }

            return _rawUrl;
        }

        // WOS 1953542: No Event Is Logged When App Config is Corrupt
        // This should never throw.  Since Uri.ctor can throw if config
        // is bad, we cannot use the public Url property.
        //
        // Security note: This property has not been sanitized and should not
        // be used for making decisions at runtime. It should only be used for
        // logging or other innocuous things.
        internal String UrlInternal {
            get {
                string q = QueryStringText;
                if (!String.IsNullOrEmpty(q))
                    q = "?" + HttpEncoder.CollapsePercentUFromStringInternal(q, QueryStringEncoding);

                // Get server name and port from Host header?  Or the old way?
                if (AppSettings.UseHostHeaderForRequestUrl) {
                    string serverAndPort = _wr.GetKnownRequestHeader(HttpWorkerRequest.HeaderHost);
                    try {
                        if (!String.IsNullOrEmpty(serverAndPort)) {
                            // RFC 2732 (Section 2 for format, section 3 for update of RFC 2396 [HTTP 1.1]) mandates that
                            // IPv6 addresses in the host header be enclosed in []'s already.  Se we don't need to do the
                            // same check for a ---- IPv6 address as we do with _wr.GetServerName() below.
                            string u = _wr.GetProtocol() + "://" + serverAndPort + Path + q;
                            _url = new Uri(u);
                            return u;
                        }
                    } catch (UriFormatException) { /* Do nothing, leave _url null.  Backup plan will kick in below. */ }
                }

                // If for some reason the Host Header failed to produce a valid Url, fall back on the old way of doing it.
                String serverName = _wr.GetServerName();
                if (serverName.IndexOf(':') >= 0 && serverName[0] != '[')
                    serverName = "[" + serverName + "]"; // IPv6
                if (_wr.GetLocalPortAsString() == "80") {
                    return _wr.GetProtocol() + "://" + serverName + Path + q;
                }
                else {
                    return _wr.GetProtocol() + "://" + serverName + ":" + _wr.GetLocalPortAsString() + Path + q;
                }
            }
        }

        // The current request's Url
        //    Gets Information regarding URL of current request.
        public Uri Url {
            get {
                if (_url == null && _wr != null) {
                    // The Path is accessed in a deferred way to preserve the execution order that existed
                    // before the code in BuildUrl was factored out of this property.
                    // While evaluating the Path immediately would probably not have an impact on regular execution
                    // it might impact error cases. Consider a situation in which some method in workerRequest throws.
                    // If we evaluate Path early, then some other method might throw, thus producing a different
                    // error behavior for the same conditions. Passing in a Func preserves the old ordering.
                    _url = BuildUrl(() => Path);
                }

                return _url;
            }
        }

        internal Uri BuildUrl(Func<string> pathAccessor) {
            Uri url = null;
            string q = QueryStringText;
            if (!String.IsNullOrEmpty(q))
                q = "?" + HttpEncoder.CollapsePercentUFromStringInternal(q, QueryStringEncoding);

            // Get server name and port from Host header?  Or the old way?
            if (AppSettings.UseHostHeaderForRequestUrl) {
                string serverAndPort = _wr.GetKnownRequestHeader(HttpWorkerRequest.HeaderHost);
                try {
                    if (!String.IsNullOrEmpty(serverAndPort)) {
                        // RFC 2732 (Section 2 for format, section 3 for update of RFC 2396 [HTTP 1.1]) mandates that
                        // IPv6 addresses in the host header be enclosed in []'s already.  Se we don't need to do the
                        // same check for a ---- IPv6 address as we do with _wr.GetServerName() below.
                        url = UriUtil.BuildUri(_wr.GetProtocol(), Uri.UnescapeDataString(serverAndPort), null /* port */, pathAccessor(), q);
                    }
                }
                catch (UriFormatException) { /* Do nothing, leave _url null.  Backup plan will kick in below. */ }
            }

            // If for some reason the Host Header failed to produce a valid Url, fall back on the old way of doing it.
            if (url == null) {
                String serverName = _wr.GetServerName();
                if (serverName.IndexOf(':') >= 0 && serverName[0] != '[')
                    serverName = "[" + serverName + "]"; // IPv6

                url = UriUtil.BuildUri(_wr.GetProtocol(), Uri.UnescapeDataString(serverName), _wr.GetLocalPortAsString(), pathAccessor(), q);
            }
            return url;
        }

        // Url of the Http referrer
        /// Gets information regarding the URL of the client's previous request that linked to the current URL.
        public Uri UrlReferrer {
            get {
                if (_referrer == null) {
                    if (_wr != null) {
                        String r = _wr.GetKnownRequestHeader(HttpWorkerRequest.HeaderReferer);

                        if (!String.IsNullOrEmpty(r)) {
                            try {
                                if (r.IndexOf("://", StringComparison.Ordinal) >= 0)
                                    _referrer = new Uri(r);
                                 else
                                    _referrer = new Uri(this.Url, r);
                            }
                            catch (HttpException) {
                                // malformed referrer shouldn't crash the request
                                _referrer = null;
                            }
                        }
                    }
                }

                return _referrer;
            }
        }

        // special case for perf in output cache module
        internal String IfModifiedSince {
            get {
                if (_wr == null)
                    return null;
                return _wr.GetKnownRequestHeader(HttpWorkerRequest.HeaderIfModifiedSince);
            }
        }

        // special case for perf in output cache module
        internal String IfNoneMatch {
            get {
                if (_wr == null)
                    return null;
                return _wr.GetKnownRequestHeader(HttpWorkerRequest.HeaderIfNoneMatch);
            }
        }

        // Params collection - combination of query string, form, server vars
        //    Gets a combined collection of QueryString+Form+ ServerVariable+Cookies.
        public NameValueCollection Params {
            get {
                if (HttpRuntime.HasAspNetHostingPermission(AspNetHostingPermissionLevel.Low))
                    return GetParams();
                else
                    return GetParamsWithDemand();
            }
        }

        // Used in integrated pipeline mode to invalidate the params collection
        // after a change is made to the headers or server variables
        internal void InvalidateParams() {
            _params = null;
        }

        private NameValueCollection GetParams() {
            if (_params == null) {
                _params = new HttpValueCollection(64);
                FillInParamsCollection();
                _params.MakeReadOnly();
            }
            return _params;
        }

        [AspNetHostingPermission(SecurityAction.Demand, Level=AspNetHostingPermissionLevel.Low)]
        private NameValueCollection GetParamsWithDemand()
        {
            return GetParams();
        }


        // Default property that goes through the collections
        //      QueryString, Form, Cookies, ClientCertificate and ServerVariables
        public String this[String key] {
            get {
                String s;

                s = QueryString[key];
                if (s != null)
                    return s;

                s = Form[key];
                if (s != null)
                    return s;

                HttpCookie c = Cookies[key];
                if (c != null)
                    return c.Value;

                s = ServerVariables[key];
                if (s != null)
                    return s;

                return null;
            }
        }

        // Query string as String (private)
        internal String QueryStringText {
            get {
                if (_queryStringText == null) {
                    if (_wr != null) {
                        // if raw bytes available use them
                        byte[] rawQueryString = this.QueryStringBytes;

                        if (rawQueryString != null) {
                            if (rawQueryString.Length > 0)
                                _queryStringText = QueryStringEncoding.GetString(rawQueryString);
                            else
                                _queryStringText = String.Empty;
                        }
                        else {
                            _queryStringText = _wr.GetQueryString();
                        }
                    }

                    if (_queryStringText == null)
                        _queryStringText = String.Empty;

                    if (_queryStringText.Length > 0 && !AppSettings.UseLegacyRequestUrlGeneration)
                        _queryStringText = _queryStringText.Replace("#", "%23"); // we don't want consumers to perform string concatenation and end up with an unintended fragment
                }

                return _queryStringText;
            }

            set {
                // override the query string
                _queryStringText = value;
                _queryStringOverriden = true;

                if (_queryString != null) {
                    _params=null;
                    _queryString.MakeReadWrite();
                    _queryString.Reset();
                    FillInQueryStringCollection();
                    _queryString.MakeReadOnly();
                    Unvalidated.InvalidateQueryString();
                }
            }
        }

        // Query string as byte[] (private) -- for parsing
        internal byte[] QueryStringBytes {
            get {
                if (_queryStringOverriden)
                    return null;

                if (_queryStringBytes == null) {
                    if (_wr != null)
                        _queryStringBytes = _wr.GetQueryStringRawBytes();
                }

                return _queryStringBytes;
            }
        }

        // Query string collection
        //    <para>Gets the collection of QueryString variables.</para>
        //
        public NameValueCollection QueryString {
            get {
                EnsureQueryString();

                if (_flags[needToValidateQueryString]) {
                    _flags.Clear(needToValidateQueryString);
                    ValidateHttpValueCollection(_queryString, RequestValidationSource.QueryString);
                }

                return _queryString;
            }
        }

        // Populates the QueryString property but does not hook up validation.
        internal HttpValueCollection EnsureQueryString() {
            if (_queryString == null) {
                _queryString = new HttpValueCollection();

                if (_wr != null)
                    FillInQueryStringCollection();

                _queryString.MakeReadOnly();
            }

            return _queryString;
        }

        internal bool HasQueryString {
            get {
                if (_queryString != null)
                    return (_queryString.Count > 0);

                byte[] rawQueryString = this.QueryStringBytes;

                if (rawQueryString != null) {
                    return (rawQueryString.Length > 0);
                }
                else {
                    return (QueryStringText.Length > 0);
                }
            }
        }

        // Form collection
        ///    Gets a collection of Form variables.
        public NameValueCollection Form {
            get {
                EnsureForm();

                if (_flags[needToValidateForm]) {
                    _flags.Clear(needToValidateForm);
                    ValidateHttpValueCollection(_form, RequestValidationSource.Form);
                }

                return _form;
            }
        }

        // Populates the Form property but does not hook up validation.
        internal HttpValueCollection EnsureForm() {
            if (_form == null) {
                _form = new HttpValueCollection();

                if (_wr != null)
                    FillInFormCollection();

                _form.MakeReadOnly();
            }

            return _form;
        }

        internal bool HasForm {
            get {
                if (_form != null) {
                    return (_form.Count > 0);
                }
                else {
                    if (_wr != null && !_wr.HasEntityBody()) {
                        return false;
                    }
                    else {
                        return (Form.Count > 0);
                    }
                }
            }
        }


        internal HttpValueCollection SwitchForm(HttpValueCollection form) {
            HttpValueCollection oldForm = _form;
            _form = form;
            Unvalidated.InvalidateForm();
            return oldForm;
        }

        // Headers collection
        //    Gets a collection of HTTP headers.
        public NameValueCollection Headers {
            get {
                EnsureHeaders();

                if (_flags[needToValidateHeaders]) {
                    _flags.Clear(needToValidateHeaders);
                    ValidateHttpValueCollection(_headers, RequestValidationSource.Headers);
                }
                if (_flags[needToValidateCookielessHeader]) {
                    _flags.Clear(needToValidateCookielessHeader);
                    ValidateCookielessHeaderIfRequiredByConfig(_headers[CookielessHelperClass.COOKIELESS_SESSION_FILTER_HEADER]);
                }
                return _headers;
            }
        }

        // Populates the Headers property but does not hook up validation.
        internal HttpHeaderCollection EnsureHeaders() {
            if (_headers == null) {
                _headers = new HttpHeaderCollection(_wr, this, 8);

                if (_wr != null)
                    FillInHeadersCollection();

                if (!(_wr is IIS7WorkerRequest)) {
                    _headers.MakeReadOnly();
                }
            }

            return _headers;
        }

        // Allows access to request collections that have not gone through request validation
        public UnvalidatedRequestValues Unvalidated {
            get {
                if (_unvalidatedRequestValues == null) {
                    _unvalidatedRequestValues = new UnvalidatedRequestValues(this);
                }
                return _unvalidatedRequestValues;
            }
        }

        // Server vars collection
        // Gets a collection of web server variables.
        public NameValueCollection ServerVariables {
            get {
                if (HttpRuntime.HasAspNetHostingPermission(AspNetHostingPermissionLevel.Low))
                    return GetServerVars();
                else
                    return GetServerVarsWithDemand();
            }
        }

        internal NameValueCollection GetServerVarsWithoutDemand() {
            return GetServerVars();
        }

        [AspNetHostingPermission(SecurityAction.Demand, Level=AspNetHostingPermissionLevel.Low)]
        private NameValueCollection GetServerVarsWithDemand()
        {
            return GetServerVars();
        }

        private NameValueCollection GetServerVars()
        {
            if (_serverVariables == null) {
                _serverVariables = new HttpServerVarsCollection(_wr, this);

                if ( !(_wr is IIS7WorkerRequest) ) {
                    _serverVariables.MakeReadOnly();
                }
            }
            return _serverVariables;
        }

        internal void SetSkipAuthorization(bool value) {
            IIS7WorkerRequest wr = _wr as IIS7WorkerRequest;
            if (wr == null) {
                return;
            }

            // If value is true, set server variable to "1".
            // If value is false, remove server variable by setting value to null.
            // Don't create server variable collection if it's not created yet.

            if (_serverVariables == null) {
                wr.SetServerVariable("IS_LOGIN_PAGE", value ? "1" : null);
            }
            else {
                _serverVariables.SetNoDemand("IS_LOGIN_PAGE", value ? "1" : null);
            }
        }

        internal void SetDynamicCompression(bool enable) {
            IIS7WorkerRequest wr = _wr as IIS7WorkerRequest;
            if (wr == null) {
                return;
            }

            // If "enable" is true, remove server variable by setting value to null.
            // If "enable" is false, set server variable to "0" to disable.
            // Don't create server variable collection if it's not created yet.

            if (_serverVariables == null) {
                wr.SetServerVariable("IIS_EnableDynamicCompression", enable ? null : "0");
            }
            else {
                _serverVariables.SetNoDemand("IIS_EnableDynamicCompression", enable ? null : "0");
            }
        }

        // WOS 1526602: ASP.Net v2.0: Response.AppendToLog does not work properly in integrated mode
        internal void AppendToLogQueryString(string logData) {
            IIS7WorkerRequest wr = _wr as IIS7WorkerRequest;
            if (wr == null ||  String.IsNullOrEmpty(logData)) {
                return;
            }

            // Don't create server variable collection if it's not created yet.
            if (_serverVariables == null) {
                string currentLogData = wr.GetServerVariable("LOG_QUERY_STRING");
                if (String.IsNullOrEmpty(currentLogData)) {
                    wr.SetServerVariable("LOG_QUERY_STRING", QueryStringText + logData);
                }
                else {
                    wr.SetServerVariable("LOG_QUERY_STRING", currentLogData + logData);
                }
            }
            else {
                string currentLogData = _serverVariables.Get("LOG_QUERY_STRING");
                if (String.IsNullOrEmpty(currentLogData)) {
                    _serverVariables.SetNoDemand("LOG_QUERY_STRING", QueryStringText + logData);
                }
                else {
                    _serverVariables.SetNoDemand("LOG_QUERY_STRING", currentLogData + logData);
                }
            }
        }

        // Cookie collection associated with current request
        //    Gets a collection of client's cookie variables.
        public HttpCookieCollection Cookies {
            get {
                EnsureCookies();

                if (_flags[needToValidateCookies]) {
                    _flags.Clear(needToValidateCookies);
                    ValidateCookieCollection(_cookies);
                }

                return _cookies;
            }
        }

        // Populates the Cookies property but does not hook up validation.
        internal HttpCookieCollection EnsureCookies() {
            if (_cookies == null) {
                _cookies = new HttpCookieCollection(null, false);

                if (_wr != null)
                    FillInCookiesCollection(_cookies, true /*includeResponse*/);

                if (HasTransitionedToWebSocketRequest) // cookies can't be modified after the WebSocket handshake is complete
                    _cookies.MakeReadOnly();
            }

            return _cookies;
        }

        // File collection associated with current request
        // Gets the collection of client-uploaded files (Multipart MIME format).
        public HttpFileCollection Files {
            get {
                EnsureFiles();

                if (_flags[needToValidatePostedFiles]) {
                    _flags.Clear(needToValidatePostedFiles);
                    ValidatePostedFileCollection(_files);
                }

                return _files;
            }
        }

        // Populates the Files property but does not hook up validation.
        internal HttpFileCollection EnsureFiles() {
            if (_files == null) {
                if (_readEntityBodyMode == ReadEntityBodyMode.Bufferless)
                    throw new HttpException(SR.GetString(SR.Incompatible_with_get_bufferless_input_stream));

                _files = new HttpFileCollection();

                if (_wr != null)
                    FillInFilesCollection();
            }

            return _files;
        }

        // Stream to read raw content
        //   Provides access to the raw contents of the incoming HTTP entity body.
        public Stream InputStream {
            get {
                if (_inputStream == null) {
                    if (_readEntityBodyMode == ReadEntityBodyMode.Bufferless)
                        throw new HttpException(SR.GetString(SR.Incompatible_with_get_bufferless_input_stream));

                    HttpRawUploadedContent rawContent = null;

                    if (_wr != null)
                        rawContent = GetEntireRawContent();

                    if (rawContent != null) {
                        _inputStream = new HttpInputStream(
                                                          rawContent,
                                                          0,
                                                          rawContent.Length
                                                          );
                    }
                    else {
                        _inputStream = new HttpInputStream(null, 0, 0);
                    }
                }

                return _inputStream;
            }
        }

        // ASP classic compat
        //       Gets the number of bytes in the current input stream.
        public int TotalBytes {
            get {
                Stream s = (_readEntityBodyStream != null) ? _readEntityBodyStream : InputStream;
                return(s != null) ? (int)s.Length : 0;
            }
        }

        // ASP classic compat
        //  Performs a binary read of a specified number of bytes from the current input stream.
        public byte[] BinaryRead(int count) {
            if (_readEntityBodyMode == ReadEntityBodyMode.Bufferless)
                throw new HttpException(SR.GetString(SR.Incompatible_with_get_bufferless_input_stream));

            if (count < 0 || count > TotalBytes)
                throw new ArgumentOutOfRangeException("count");

            if (count == 0)
                return new byte[0];

            byte[] buffer = new byte[count];
            int c = InputStream.Read(buffer, 0, count);

            if (c != count) {
                byte[] b2 = new byte[c];
                if (c > 0)
                    Array.Copy(buffer, b2, c);
                buffer = b2;
            }

            return buffer;
        }

        // Filtering of the input
        //   Gets or sets a filter to use when reading the current input stream.
        public Stream Filter {
            get {
                if (_installedFilter != null)
                    return _installedFilter;

                if (_filterSource == null)
                    _filterSource = new HttpInputStreamFilterSource();

                return _filterSource;
            }

            set {
                if (_filterSource == null)  // have to use the source -- null means source wasn't ever asked for
                    throw new HttpException(SR.GetString(SR.Invalid_request_filter));

                _installedFilter = value;
            }
        }


        // Client Certificate
        //    Gets information on the current request's client security certificate.
        public HttpClientCertificate ClientCertificate {
            [AspNetHostingPermission(SecurityAction.Demand, Level=AspNetHostingPermissionLevel.Low)]
            get {
                if (_clientCertificate == null) {
                    _clientCertificate = CreateHttpClientCertificateWithAssert();
                }

                return _clientCertificate;
            }
        }

        [PermissionSet(SecurityAction.Assert, Unrestricted=true)]
        HttpClientCertificate CreateHttpClientCertificateWithAssert() {
            return new HttpClientCertificate(_context);
        }


        //    Gets LOGON_USER as WindowsIdentity
        public WindowsIdentity LogonUserIdentity {
            [AspNetHostingPermission(SecurityAction.Demand, Level=AspNetHostingPermissionLevel.Medium)]
            get {
                if (_logonUserIdentity == null) {
                    if (_wr != null) {
                        if (_wr is IIS7WorkerRequest && _context.NotificationContext != null &&
                            ((_context.NotificationContext.CurrentNotification == RequestNotification.AuthenticateRequest && !_context.NotificationContext.IsPostNotification)
                             || (_context.NotificationContext.CurrentNotification < RequestNotification.AuthenticateRequest))) {
                            throw new InvalidOperationException(SR.GetString(SR.Invalid_before_authentication));
                        }

                        _logonUserIdentity = _wr.GetLogonUserIdentity();
                    }
                }

                return _logonUserIdentity;
            }
        }

        //  Validate that the input from the browser is safe.
        public void ValidateInput() {
            // it doesn't make sense to call this multiple times per request
            if (ValidateInputWasCalled) {
                return;
            }

            _flags.Set(hasValidateInputBeenCalled);

            // This is to prevent some XSS (cross site scripting) attacks (ASURT 122278)
            _flags.Set(needToValidateQueryString);
            _flags.Set(needToValidateForm);
            _flags.Set(needToValidateCookies);
            _flags.Set(needToValidatePostedFiles);
            _flags.Set(needToValidateRawUrl);
            _flags.Set(needToValidatePath);
            _flags.Set(needToValidatePathInfo);
            _flags.Set(needToValidateHeaders);
        }

        internal bool ValidateInputWasCalled {
            get {
                return _flags[hasValidateInputBeenCalled];
            }
        }

        // There are a few situations where we do not want to validate the request ever:
        private bool CanValidateRequest() {
            if (_wr == null) {
                return false;
            }

            // 1. State server requests
            if (_wr is StateHttpWorkerRequest) {
                return false;
            }

            // 2. DevDiv2 162442: When IIS has already rejected the request and we are inside logrequest or end request
            if (_wr is IIS7WorkerRequest &&
                (_context.Response.StatusCode == 404 || _context.Response.StatusCode == 400) &&
                (_context.NotificationContext != null) &&
                (_context.NotificationContext.CurrentNotification == RequestNotification.LogRequest ||
                _context.NotificationContext.CurrentNotification == RequestNotification.EndRequest)) {
                return false;
            }

            return true;
        }

        internal void ValidateInputIfRequiredByConfig() {
            // Do we need to enable request validation?
            RuntimeConfig config = RuntimeConfig.GetConfig(Context);
            HttpRuntimeSection runtimeSection = config.HttpRuntime;

            //////////////////////////////////////////////////////////////////////
            // Perform Path & QueryString validation checks for non-state_server requests
            if (CanValidateRequest()) {
                string requestUrl = Path;

                //////////////////////////////////////////////////////////////////
                // Verify the URL & QS lengths
                if (requestUrl.Length > runtimeSection.MaxUrlLength) {
                    throw new HttpException(400, SR.GetString(SR.Url_too_long));
                }
                if (QueryStringText.Length > runtimeSection.MaxQueryStringLength) {
                    throw new HttpException(400, SR.GetString(SR.QueryString_too_long));
                }

                //////////////////////////////////////////////////////////////////
                // Verify that the URL does not contain invalid chars
                char [] invalidChars = runtimeSection.RequestPathInvalidCharactersArray;
                if (invalidChars != null && invalidChars.Length > 0) {
                    int index = requestUrl.IndexOfAny(invalidChars);
                    if (index >= 0) {
                        string invalidString = new string(requestUrl[index], 1);
                        throw new HttpException(400, SR.GetString(SR.Dangerous_input_detected,
                                                                  "Request.Path", invalidString));
                    }
                    _flags.Set(needToValidateCookielessHeader);
                }
            }

            // only enable request validation for the entire pipeline in v4.0+ of the framework
            Version targetVersion = runtimeSection.RequestValidationMode;
            if (targetVersion >= VersionUtil.Framework40) {
                ValidateInput();

                // Mode v4.5+ implies granular request validation
                if (targetVersion >= VersionUtil.Framework45) {
                    EnableGranularRequestValidation();
                }
            }
        }

        internal void ValidateCookielessHeaderIfRequiredByConfig(string header) {
            if (string.IsNullOrEmpty(header))
                return;
            if (!CanValidateRequest())
                return;
            // Verify that the header does not contain invalid chars
            char [] invalidChars = RuntimeConfig.GetConfig(Context).HttpRuntime.RequestPathInvalidCharactersArray;
            if (invalidChars != null && invalidChars.Length > 0) {
                int index = header.IndexOfAny(invalidChars);
                if (index >= 0) {
                    string invalidString = new string(header[index], 1);
                    throw new HttpException(400, SR.GetString(SR.Dangerous_input_detected, "Request.Path", invalidString));
                }
            }
        }

        private static string RemoveNullCharacters(string s) {
            if (s == null)
                return null;

            // Ignore null characters to prevent attacks (VSWhidbey 85016)
            if (s.IndexOf('\0') > -1)
                return s.Replace("\0", String.Empty);

            return s;
        }

        private void ValidateString(string value, string collectionKey, RequestValidationSource requestCollection) {

            value = RemoveNullCharacters(value);

            // Only provide the HttpContext if this is an actual HttpRequest; pass null
            // if this is simply a shell that exists for WebSockets.
            HttpContext contextToProvide = (HasTransitionedToWebSocketRequest) ? null : Context;

            int validationFailureIndex;
            if (!RequestValidator.Current.IsValidRequestString(contextToProvide, value, requestCollection, collectionKey, out validationFailureIndex)) {
                // Display only the piece of the string that caused the problem, padded by on each side
                string detectedString = collectionKey + "=\"";
                int startIndex = validationFailureIndex - 10;
                if (startIndex <= 0) {
                    startIndex = 0;
                }
                else {
                    // Start with "..." to show that this is not the beginning
                    detectedString += "...";
                }
                int endIndex = validationFailureIndex + 20;
                if (endIndex >= value.Length) {
                    endIndex = value.Length;
                    detectedString += value.Substring(startIndex, endIndex - startIndex) + "\"";
                }
                else {
                    detectedString += value.Substring(startIndex, endIndex - startIndex) + "...\"";
                }

                string collectionName = GetRequestValidationSourceName(requestCollection);
                throw new HttpRequestValidationException(SR.GetString(SR.Dangerous_input_detected,
                    collectionName, detectedString));
            }
        }

        internal void EnableGranularRequestValidation() {
            _enableGranularValidation = true;
        }

        private static string GetRequestValidationSourceName(RequestValidationSource requestCollection) {
            switch (requestCollection) {
                case RequestValidationSource.Cookies: return "Request.Cookies";
                case RequestValidationSource.Files: return "Request.Files";
                case RequestValidationSource.Form: return "Request.Form";
                case RequestValidationSource.Headers: return "Request.Headers";
                case RequestValidationSource.Path: return "Request.Path";
                case RequestValidationSource.PathInfo: return "Request.PathInfo";
                case RequestValidationSource.QueryString: return "Request.QueryString";
                case RequestValidationSource.RawUrl: return "Request.RawUrl";

                default:
                    return "Request." + requestCollection.ToString();
            }
        }

        private void ValidateHttpValueCollection(HttpValueCollection collection, RequestValidationSource requestCollection) {
            if (_enableGranularValidation) {
                // Granular request validation is enabled - validate collection entries only as they're accessed.
                collection.EnableGranularValidation((key, value) => ValidateString(value, key, requestCollection));
            }
            else {
                // Granular request validation is disabled - eagerly validate all collection entries.
                int c = collection.Count;

                for (int i = 0; i < c; i++) {
                    String key = collection.GetKey(i);

                    // Certain fields shouldn't go through validation - see comments in KeyIsCandidateForValidation for more information.
                    if (!HttpValueCollection.KeyIsCandidateForValidation(key)) {
                        continue;
                    }

                    String val = collection.Get(i);

                    if (!String.IsNullOrEmpty(val))
                        ValidateString(val, key, requestCollection);
                }
            }
        }

        private void ValidateCookieCollection(HttpCookieCollection cc) {
            if (_enableGranularValidation) {
                // Granular request validation is enabled - validate collection entries only as they're accessed.
                cc.EnableGranularValidation((key, value) => ValidateString(value, key, RequestValidationSource.Cookies));
            }
            else {
                // Granular request validation is disabled - eagerly validate all collection entries.
                int c = cc.Count;

                for (int i = 0; i < c; i++) {
                    String key = cc.GetKey(i);
                    String val = cc.Get(i).Value;

                    if (!String.IsNullOrEmpty(val))
                        ValidateString(val, key, RequestValidationSource.Cookies);
                }
            }
        }

        private void ValidatePostedFileCollection(HttpFileCollection col) {
            if (_enableGranularValidation) {
                // Granular request validation is enabled - validate collection entries only as they're accessed.
                col.EnableGranularValidation((key, value) => ValidateString(value, "filename", RequestValidationSource.Files));
            }
            else {
                // Granular request validation is disabled - eagerly validate all collection entries.
                for (int i = 0; i < col.Count; i++) {
                    string filename = col[i].FileName;
                    ValidateString(filename, "filename", RequestValidationSource.Files);
                }
            }
        }

        internal void ClearReferencesForWebSocketProcessing() {
            bool needToRevalidateInputs = ValidateInputWasCalled;

            // everything not marked [DoNotReset] should be eligible for garbage collection
            ReflectionUtil.Reset(this);

            if (needToRevalidateInputs) {
                ValidateInput();
            }
        }

        /*
         * Get coordinates of the clicked image send as name.x=&name.y=
         * in the form or in the query string
         * @param imageFieldName name of the image field
         * @return x,y as int[2] or null if not found
         */

        /// <devdoc>
        ///    <para>
        ///       Maps an incoming image field form parameter into appropriate x/y
        ///       coordinate values.
        ///    </para>
        /// </devdoc>
        public int[] MapImageCoordinates(String imageFieldName) {
            var coords = MapImageCoordinatatesInternal(imageFieldName, HttpVerb, QueryString, Form);
            if (coords != null) {
                return new[] { (int)coords[0], (int)coords[1] };
            }
            return null;
        }

        /*
         * Get coordinates of the clicked image send as name.x=&name.y=
         * in the form or in the query string
         * @param imageFieldName name of the image field
         * @return x,y as double[2] or null if not found
         */

        /// <devdoc>
        ///    <para>
        ///       Maps an incoming image field form parameter into appropriate x/y
        ///       coordinate values.
        ///    </para>
        /// </devdoc>
        public double[] MapRawImageCoordinates(String imageFieldName) {
            return MapImageCoordinatatesInternal(imageFieldName, HttpVerb, QueryString, Form);
        }

        internal static double[] MapImageCoordinatatesInternal(string imageFieldName, HttpVerb verb, NameValueCollection queryString, NameValueCollection form) {
            // Select collection where to look according to verb

            NameValueCollection c = null;

            switch (verb) {
                case HttpVerb.GET:
                case HttpVerb.HEAD:
                    c = queryString;
                    break;

                case HttpVerb.POST:
                    c = form;
                    break;

                default:
                    return null;
            }

            // Look for .x and .y values in the collection

            double[] ret = null;

            try {
                string x = c[imageFieldName + ".x"];
                string y = c[imageFieldName + ".y"];

                double xVal;
                double yVal;

                if (x != null && y != null && HttpUtility.TryParseCoordinates(x, out xVal) && HttpUtility.TryParseCoordinates(y, out yVal)) {
                    ret = new[] { xVal, yVal };
                }
            }
            catch {
                // eat parsing exceptions
            }

            return ret;
        }

        /*
         * Save contents of request into a file
         * @param filename where to save
         * @param includeHeaders flag to request inclusion of Http headers
         */

        /// <devdoc>
        ///    <para>Saves an HTTP request to disk.</para>
        /// </devdoc>
        public void SaveAs(String filename, bool includeHeaders) {
            // NDPWhidbey 14376
            if (!System.IO.Path.IsPathRooted(filename)) {
                HttpRuntimeSection config = RuntimeConfig.GetConfig(_context).HttpRuntime;
                if (config.RequireRootedSaveAsPath) {
                    throw new HttpException(SR.GetString(SR.SaveAs_requires_rooted_path, filename));
                }
            }

            FileStream f = new FileStream(filename, FileMode.Create);

            try {
                // headers

                if (includeHeaders) {
                    TextWriter w = new StreamWriter(f);

                    w.Write(this.HttpMethod + " " + this.Path);

                    String qs = this.QueryStringText;
                    if (!String.IsNullOrEmpty(qs))
                        w.Write("?" + qs);

                    if (_wr != null) {
                        // real request -- add protocol
                        w.Write(" " + _wr.GetHttpVersion() + "\r\n");

                        // headers
                        w.Write(CombineAllHeaders(true));
                    }
                    else {
                        // manufactured request
                        w.Write("\r\n");
                    }

                    w.Write("\r\n");
                    w.Flush();
                }

                // entity body

                HttpInputStream s = (HttpInputStream)this.InputStream;
                s.WriteTo(f);
                f.Flush();
            }
            finally {
                f.Close();
            }
        }

        /*
         * Map virtual path to physical path relative to current request
         * @param virtualPath virtual path (absolute or relative)
         * @return physical path
         */

        /// <devdoc>
        ///    <para>
        ///       Maps the given virtual path to a physical path.
        ///    </para>
        /// </devdoc>
        public String MapPath(String virtualPath) {
            return MapPath(VirtualPath.CreateAllowNull(virtualPath));
        }

        internal String MapPath(VirtualPath virtualPath) {
            if (_wr != null) {
                return MapPath(virtualPath, FilePathObject, true/*allowCrossAppMapping*/);
            }
            else {
                return virtualPath.MapPath();
            }
        }


        /// <devdoc>
        ///    <para>
        ///       Maps the given virtual path to a physical path.
        ///    </para>
        /// </devdoc>
        public String MapPath(string virtualPath, string baseVirtualDir, bool allowCrossAppMapping) {
            VirtualPath baseVirtualDirObject;
            if (String.IsNullOrEmpty(baseVirtualDir)) {
                // If no base is passed in, use the request's base dir (VSWhidbey 539063)
                baseVirtualDirObject = FilePathObject;
            }
            else {
                // We need to ensure a trailing slash to match v1.x behavior (VSWhidbey 539063)
                baseVirtualDirObject = VirtualPath.CreateTrailingSlash(baseVirtualDir);
            }

            return MapPath(VirtualPath.CreateAllowNull(virtualPath),
                baseVirtualDirObject, allowCrossAppMapping);
        }

        internal String MapPath(VirtualPath virtualPath, VirtualPath baseVirtualDir, bool allowCrossAppMapping) {
            if (_wr == null)
                throw new HttpException(SR.GetString(SR.Cannot_map_path_without_context));

            // treat null as "."

            // 
            if (virtualPath == null)
                virtualPath = VirtualPath.Create(".");

            VirtualPath originalVirtualPath = virtualPath; // remember for patch-up at the end

            // Combine it with the base if one was passed in
            if (baseVirtualDir != null) {
                virtualPath = baseVirtualDir.Combine(virtualPath);
            }

            if (!allowCrossAppMapping)
                virtualPath.FailIfNotWithinAppRoot();

            string realPath = virtualPath.MapPathInternal();

            // patch up the result for Everett combatibility (VSWhidbey 319826)
            if (virtualPath.VirtualPathString == "/" &&
                originalVirtualPath.VirtualPathString != "/" &&
                !originalVirtualPath.HasTrailingSlash &&
                UrlPath.PathEndsWithExtraSlash(realPath)) {
                realPath = realPath.Substring(0, realPath.Length - 1);
            }

            InternalSecurityPermissions.PathDiscovery(realPath).Demand();
            return realPath;
        }

        internal void InternalRewritePath(VirtualPath newPath, String newQueryString, bool rebaseClientPath) {
            // clear things that depend on path
            _pathTranslated = null;
            _pathInfo = null;
            _filePath = null;
            _url = null;
            Unvalidated.InvalidateUrl();

            // DevDiv Bug 164390: calling the worker request's RawUrl method here
            // to ensure we cache the original request Url in Url Mapping scenarios.
            string temp = RawUrl;

            // remember the new path
            _path = newPath;

            if (rebaseClientPath) {
                _clientBaseDir = null;
                _clientFilePath = newPath;
            }

            // set a flag so we compute things that depend on path by hand
            _computePathInfo = true;

            // parse the new query string (might require config)
            if (newQueryString != null)
                this.QueryStringText = newQueryString;

            // remember the rewritten url
            _rewrittenUrl = _path.VirtualPathString;
            string q = QueryStringText;
            if (!String.IsNullOrEmpty(q))
                _rewrittenUrl += "?" + q;

            IIS7WorkerRequest iis7WorkerRequest = _wr as IIS7WorkerRequest;
            if (iis7WorkerRequest != null) {
                iis7WorkerRequest.RewriteNotifyPipeline(_path.VirtualPathString, newQueryString, rebaseClientPath);
            }
        }

        internal void InternalRewritePath(VirtualPath newFilePath, VirtualPath newPathInfo,
            String newQueryString, bool setClientFilePath) {
            // clear things that depend on path
            _pathTranslated = (_wr != null) ? newFilePath.MapPathInternal() : null;
            _pathInfo = newPathInfo;
            _filePath = newFilePath;
            _url = null;
            Unvalidated.InvalidateUrl();

            // DevDiv Bug 164390: calling the worker request's RawUrl method here
            // to ensure we cache the original request Url in Url Mapping scenarios.
            string temp = RawUrl;

            if (newPathInfo == null) {
                _path = newFilePath;
            }
            else {
                // Combine the file path and the pathInfo to get the path.  Note that we can't call
                // newFilePath.Combine here, since the rules are very different here (VSWhidbey 498926, 528055)
                string newFullPathString = newFilePath.VirtualPathStringWhicheverAvailable + "/" + newPathInfo.VirtualPathString;
                _path = VirtualPath.Create(newFullPathString);
            }

            if (newQueryString != null)
                this.QueryStringText = newQueryString;

            // remember the rewritten url
            _rewrittenUrl = _path.VirtualPathString;
            string q = QueryStringText;
            if (!String.IsNullOrEmpty(q))
                _rewrittenUrl += "?" + q;

            // no need to calculate any paths
            _computePathInfo = false;

            if (setClientFilePath) {
                _clientFilePath = newFilePath;
            }

            IIS7WorkerRequest iis7WorkerRequest = _wr as IIS7WorkerRequest;
            if (iis7WorkerRequest != null) {
                String newPath = (_path != null && _path.VirtualPathString != null) ? _path.VirtualPathString : String.Empty;
                iis7WorkerRequest.RewriteNotifyPipeline(newPath, newQueryString, setClientFilePath);
            }
        }

        internal String RewrittenUrl {
            get { return _rewrittenUrl; }
        }

        internal string FetchServerVariable(string variable) {
            return _wr.GetServerVariable(variable);
        }

        // Used by IIS7WorkerRequest.SynchronizeServerVariables to update server variables in the collection
        internal void SynchronizeServerVariable(String name, String value) {
            if (name == "IS_LOGIN_PAGE") {
                bool skipAuth = (value != null && value != "0") ? true : false;
                _context.SetSkipAuthorizationNoDemand(skipAuth, true /*managedOnly*/);
            }
            // populate the server variables collection if necessary
            HttpServerVarsCollection serverVars = ServerVariables as HttpServerVarsCollection;
            if (serverVars != null) {
                serverVars.SynchronizeServerVariable(name, value);
            }
        }

        // Used by IIS7WorkerRequest.SynchronizeServerVariables to update server variables in the collection
        internal void SynchronizeHeader(String name, String value) {

            // populate the headers collection if necessary
            HttpHeaderCollection headers = Headers as HttpHeaderCollection;
            if (headers != null) {
                headers.SynchronizeHeader(name, value);
            }

            // if a header changes, the server variable also needs to be updated
            // populate the server variables collection if necessary
            HttpServerVarsCollection serverVars = ServerVariables as HttpServerVarsCollection;
            if (serverVars != null) {
                string svName = "HTTP_" + name.ToUpper(CultureInfo.InvariantCulture).Replace('-', '_');
                serverVars.SynchronizeServerVariable(svName, value);
            }
        }

        public ChannelBinding HttpChannelBinding {
            [PermissionSet(SecurityAction.Demand, Unrestricted = true)]
            get {
                if (_wr is IIS7WorkerRequest)
                    return ((IIS7WorkerRequest)_wr).HttpChannelBindingToken;
                else if (_wr is ISAPIWorkerRequestInProc)
                    return ((ISAPIWorkerRequestInProc)_wr).HttpChannelBindingToken;
                throw new PlatformNotSupportedException();
            }
        }

        // Only supported on IIS7 and later.
        // This is a wrapper for the IIS 7.0 IHttpRequest::InsertEntityBody method.
        // If you want to provide IIS with a copy of the request entity previously read by ASP.NET,
        // call the InsertEntityBody overload that takes no arguments.
        [AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.High)]
        public void InsertEntityBody(byte[] buffer, int offset, int count) {
            EnsureHasNotTransitionedToWebSocket();

            IIS7WorkerRequest wr = _wr as IIS7WorkerRequest;
            if (wr == null)
                throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
            if (buffer == null)
                throw new ArgumentNullException("buffer");
            if (offset < 0)
                throw new ArgumentOutOfRangeException("offset");
            if (count < 0)
                throw new ArgumentOutOfRangeException("count");
            if (buffer.Length - offset < count)
                throw new ArgumentException(SR.GetString(SR.InvalidOffsetOrCount, "offset", "count"));

            wr.InsertEntityBody(buffer, offset, count);
            NeedToInsertEntityBody = false;
        }

        // Only supported on IIS7 and later.  IIS does not maintain a copy of the request entity
        // after it has been read.  For this reason, it is recommended that only the handler
        // read the request entity.  This method provides IIS with a copy of the request entity that ASP.NET
        // previously read.  For example, this is useful for scenarios where a native handler may need to access the
        // request entity after it has been read by ASP.NET.
        public void InsertEntityBody() {
            IIS7WorkerRequest wr = _wr as IIS7WorkerRequest;
            if (wr == null)
                throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
            byte[] buffer = EntityBody;
            if (buffer == null)
                return;
            wr.InsertEntityBody(buffer, 0, buffer.Length);
            NeedToInsertEntityBody = false;
        }

        // Determines if the request entity body has been read, and if so, how it was
        // read.  Used to avoid the exception thrown by GetBufferlessInputStream,
        // GetBufferedInputStream, Form, Files, InputStream, and BinaryRead when the
        // entity has already been read by an incompatible method.
        public ReadEntityBodyMode ReadEntityBodyMode { get { return _readEntityBodyMode; } }

        // GetBufferlessInputStream allows the caller to read the request entity bytes directly off the wire, either
        // synchronously or asynchronously.  The bytes are not buffered, and once read, neither ASP.NET nor IIS have a
        // copy of the request entity.  To read the entire entity, call Read or BeginRead/EndRead repeatedly until zero
        // bytes are returned.  The stream returned keeps track of the total bytes read, and for non-chunked entity bodies
        // it will not read more bytes than indicated by the Content-Length header.  If the client disconnects while the
        // entity is being read, the Read and BeginRead/EndRead methods will throw an exception.
        //
        // Throws HttpException if the entity has already been read and stored via Form, Files, InputStream, or GetBufferedInputStream.
        // To avoid this exception, first call Request.ReadEntityBodyMode.
        // Throws HttpException from Read/BeginRead/EndRead if the client disconnects while the entity is being read.
        [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId="Bufferless", Justification = "Name is from the spec")]
        [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
          Justification = "This method does additional work which causes very big side-effects")]        
        public Stream GetBufferlessInputStream() {
            return GetInputStream(persistEntityBody: false);
        }

        // Same as GetBufferlessInputStream, but with the option of disabling system.web/httpRuntime/maxRequestLength.
        [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId="Bufferless", Justification = "Name is from the spec")]
        [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
          Justification = "This method does additional work which causes very big side-effects")]        
        public Stream GetBufferlessInputStream(bool disableMaxRequestLength) {
            return GetInputStream(false, disableMaxRequestLength);
        }

        // GetBufferedInputStream is identical to GetBufferlessInputStream except that it also copies the bytes read
        // to internal storage used by ASP.NET to populate HttpRequest.Form, HttpRequest.Files, and HttpRequest.InputStream.
        //
        // Throws HttpException if the entity has already been read by Form, Files, InputStream, or GetBufferlessInputStream.
        // To avoid this exception, first call Request.ReadEntityBodyMode.
        // Throws HttpException from Read/BeginRead/EndRead if the client disconnects while the entity is being read.
        [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId="Buffered", Justification = "Name is from the spec")]
        [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
          Justification = "This method does additional work which causes very big side-effects")]
        public Stream GetBufferedInputStream() {
            return GetInputStream(persistEntityBody: true);
        }
        
        private Stream GetInputStream(bool persistEntityBody, bool disableMaxRequestLength = false) {
            EnsureHasNotTransitionedToWebSocket();

            ReadEntityBodyMode requestedMode = (persistEntityBody) ? ReadEntityBodyMode.Buffered : ReadEntityBodyMode.Bufferless;
            ReadEntityBodyMode currentMode = _readEntityBodyMode;
            if (currentMode == ReadEntityBodyMode.None) {
                _readEntityBodyMode = requestedMode;
                _readEntityBodyStream = new HttpBufferlessInputStream(_context, persistEntityBody, disableMaxRequestLength);                
            }
            else if (currentMode == ReadEntityBodyMode.Classic) {
                throw new HttpException(SR.GetString(SR.Incompatible_with_input_stream));
            }
            else if (currentMode != requestedMode) {
                throw new HttpException((persistEntityBody) ? SR.GetString(SR.Incompatible_with_get_bufferless_input_stream) : SR.GetString(SR.Incompatible_with_get_buffered_input_stream));
            }
            return _readEntityBodyStream;
        }

        /// <summary>
        /// Forcibly terminates the underlying TCP connection to the client, causing any outstanding I/O to fail.
        /// </summary>
        /// <remarks>
        /// This method requires that the application be using the IIS integrated mode pipeline.
        /// 
        /// This method is thread-safe. Any thread may call it at any time.
        /// </remarks>
        public void Abort() {
            IIS7WorkerRequest wr = _wr as IIS7WorkerRequest;
            if (wr != null) {
                wr.AbortConnection();
            }
            else {
                throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
            }
        }

        // helper that throws an exception if we have transitioned the current request to a WebSocket request
        internal void EnsureHasNotTransitionedToWebSocket() {
            if (Context != null) {
                Context.EnsureHasNotTransitionedToWebSocket();
            }
        }

        /// <summary>
        /// Retrieves a CancellationToken that is signaled when a request times out.
        /// </summary>
        /// <remarks>
        /// The timeout period can be specified via config (httpRuntime/executionTimeout) or programmatically via
        /// Server.ScriptTimeout. The timeout period is measured from the time the request comes in. If using the
        /// default timeout of 110 seconds, the TimedOutToken will be tripped no earlier than 110 seconds after we
        /// started processing the request. The application developer can change the ScriptTimeout property if he
        /// so chooses, and as long as we haven't yet tripped this token we will respect the new timeout value.
        /// 
        /// Currently we only provide 15 second granularity on this token, so using the default timeout period of
        /// 110 seconds this means that we'll actually trip the token sometime between 110 - 125 seconds after we
        /// started processing the request. We may improve this resolution in the future.
        /// 
        /// Even though this property is thread-safe, there are restrictions on its use. Please see the remarks
        /// on HttpResponse.ClientDisconnectToken for caveats and best practices when consuming CancellationToken
        /// properties provided by ASP.NET intrinsics.
        /// 
        /// This property is meaningless once WebSockets request processing has started.
        /// </remarks>
        public CancellationToken TimedOutToken {
            get {
                EnsureHasNotTransitionedToWebSocket();

                HttpContext context = Context;
                return (context != null) ? context.TimedOutToken : default(CancellationToken);
            }
        }

    }
}
