WebHeaderCollection.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Net / System / Net / WebHeaderCollection.cs / 1305376 / WebHeaderCollection.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

namespace System.Net { 
    using System.Net.Cache; 
    using System.Collections;
    using System.Collections.Specialized; 
    using System.Text;
    using System.Runtime.InteropServices;
    using System.Runtime.Serialization;
    using System.Globalization; 
    using System.Security.Permissions;
    using System.Diagnostics.CodeAnalysis; 
 
    internal enum WebHeaderCollectionType : ushort {
        Unknown, 
        WebRequest,
        WebResponse,
        HttpWebRequest,
        HttpWebResponse, 
        HttpListenerRequest,
        HttpListenerResponse, 
        FtpWebRequest, 
        FtpWebResponse,
        FileWebRequest, 
        FileWebResponse,
    }

    // 
    // HttpHeaders - this is our main HttpHeaders object,
    //  which is a simple collection of name-value pairs, 
    //  along with additional methods that provide HTTP parsing 
    //  collection to sendable buffer capablities and other enhansments
    //  We also provide validation of what headers are allowed to be added. 
    //

    /// 
    ///     
    ///       Contains protocol headers associated with a
    ///       request or response. 
    ///     
    /// 
    [ComVisible(true), Serializable] 
    public class WebHeaderCollection : NameValueCollection, ISerializable {
        //
        // Data and Constants
        // 
        private const int ApproxAveHeaderLineSize = 30;
        private const int ApproxHighAvgNumHeaders = 16; 
        private static readonly HeaderInfoTable HInfo = new HeaderInfoTable(); 

        // 
        // Common Headers - used only when receiving a response, and internally.  If the user ever requests a header,
        // all the common headers are moved into the hashtable.
        //
        private string[] m_CommonHeaders; 
        private int m_NumCommonHeaders;
 
        // Grouped by first character, so lookup is faster.  The table s_CommonHeaderHints maps first letters to indexes in this array. 
        // After first character, sort by decreasing length.  It's ok if two headers have the same first character and length.
        private static readonly string[] s_CommonHeaderNames = new string[] { 
            HttpKnownHeaderNames.AcceptRanges,      // "Accept-Ranges"       13
            HttpKnownHeaderNames.ContentLength,     // "Content-Length"      14
            HttpKnownHeaderNames.CacheControl,      // "Cache-Control"       13
            HttpKnownHeaderNames.ContentType,       // "Content-Type"        12 
            HttpKnownHeaderNames.Date,              // "Date"                 4
            HttpKnownHeaderNames.Expires,           // "Expires"              7 
            HttpKnownHeaderNames.ETag,              // "ETag"                 4 
            HttpKnownHeaderNames.LastModified,      // "Last-Modified"       13
            HttpKnownHeaderNames.Location,          // "Location"             8 
            HttpKnownHeaderNames.ProxyAuthenticate, // "Proxy-Authenticate"  18
            HttpKnownHeaderNames.P3P,               // "P3P"                  3
            HttpKnownHeaderNames.SetCookie2,        // "Set-Cookie2"         11
            HttpKnownHeaderNames.SetCookie,         // "Set-Cookie"          10 
            HttpKnownHeaderNames.Server,            // "Server"               6
            HttpKnownHeaderNames.Via,               // "Via"                  3 
            HttpKnownHeaderNames.WWWAuthenticate,   // "WWW-Authenticate"    16 
            HttpKnownHeaderNames.XAspNetVersion,    // "X-AspNet-Version"    16
            HttpKnownHeaderNames.XPoweredBy,        // "X-Powered-By"        12 
            "[" };  // This sentinel will never match.  (This character isn't in the hint table.)

        // Mask off all but the bottom five bits, and look up in this array.
        private static readonly sbyte[] s_CommonHeaderHints = new sbyte[] { 
            -1,  0, -1,  1,  4,  5, -1, -1,   // - a b c d e f g
            -1, -1, -1, -1,  7, -1, -1, -1,   // h i j k l m n o 
             9, -1, -1, 11, -1, -1, 14, 15,   // p q r s t u v w 
            16, -1, -1, -1, -1, -1, -1, -1 }; // x y z [ - - - -
 
        private const int c_AcceptRanges      =  0;
        private const int c_ContentLength     =  1;
        private const int c_CacheControl      =  2;
        private const int c_ContentType       =  3; 
        private const int c_Date              =  4;
        private const int c_Expires           =  5; 
        private const int c_ETag              =  6; 
        private const int c_LastModified      =  7;
        private const int c_Location          =  8; 
        private const int c_ProxyAuthenticate =  9;
        private const int c_P3P               = 10;
        private const int c_SetCookie2        = 11;
        private const int c_SetCookie         = 12; 
        private const int c_Server            = 13;
        private const int c_Via               = 14; 
        private const int c_WwwAuthenticate   = 15; 
        private const int c_XAspNetVersion    = 16;
        private const int c_XPoweredBy        = 17; 

        // Easy fast lookups for common headers.  More can be added.
        internal string ContentLength
        { 
            get
            { 
                return m_CommonHeaders != null ? m_CommonHeaders[c_ContentLength] : Get(s_CommonHeaderNames[c_ContentLength]); 
            }
        } 

        internal string CacheControl
        {
            get 
            {
                return m_CommonHeaders != null ? m_CommonHeaders[c_CacheControl] : Get(s_CommonHeaderNames[c_CacheControl]); 
            } 
        }
 
        internal string ContentType
        {
            get
            { 
                return m_CommonHeaders != null ? m_CommonHeaders[c_ContentType] : Get(s_CommonHeaderNames[c_ContentType]);
            } 
        } 

        internal string Date 
        {
            get
            {
                return m_CommonHeaders != null ? m_CommonHeaders[c_Date] : Get(s_CommonHeaderNames[c_Date]); 
            }
        } 
 
        internal string Expires
        { 
            get
            {
                return m_CommonHeaders != null ? m_CommonHeaders[c_Expires] : Get(s_CommonHeaderNames[c_Expires]);
            } 
        }
 
        internal string ETag 
        {
            get 
            {
                return m_CommonHeaders != null ? m_CommonHeaders[c_ETag] : Get(s_CommonHeaderNames[c_ETag]);
            }
        } 

        internal string LastModified 
        { 
            get
            { 
                return m_CommonHeaders != null ? m_CommonHeaders[c_LastModified] : Get(s_CommonHeaderNames[c_LastModified]);
            }
        }
 
        internal string Location
        { 
            get 
            {
                return m_CommonHeaders != null ? m_CommonHeaders[c_Location] : Get(s_CommonHeaderNames[c_Location]); 
            }
        }

        internal string ProxyAuthenticate 
        {
            get 
            { 
                return m_CommonHeaders != null ? m_CommonHeaders[c_ProxyAuthenticate] : Get(s_CommonHeaderNames[c_ProxyAuthenticate]);
            } 
        }

        internal string SetCookie2
        { 
            get
            { 
                return m_CommonHeaders != null ? m_CommonHeaders[c_SetCookie2] : Get(s_CommonHeaderNames[c_SetCookie2]); 
            }
        } 

        internal string SetCookie
        {
            get 
            {
                return m_CommonHeaders != null ? m_CommonHeaders[c_SetCookie] : Get(s_CommonHeaderNames[c_SetCookie]); 
            } 
        }
 
        internal string Server
        {
            get
            { 
                return m_CommonHeaders != null ? m_CommonHeaders[c_Server] : Get(s_CommonHeaderNames[c_Server]);
            } 
        } 

        internal string Via 
        {
            get
            {
                return m_CommonHeaders != null ? m_CommonHeaders[c_Via] : Get(s_CommonHeaderNames[c_Via]); 
            }
        } 
 
        private void NormalizeCommonHeaders()
        { 
            if (m_CommonHeaders == null)
                return;
            for (int i = 0; i < m_CommonHeaders.Length; i++)
                if (m_CommonHeaders[i] != null) 
                    InnerCollection.Add(s_CommonHeaderNames[i], m_CommonHeaders[i]);
 
            m_CommonHeaders = null; 
            m_NumCommonHeaders = 0;
        } 

        //
        // To ensure C++ and IL callers can't pollute the underlying collection by calling overridden base members directly, we
        // will use a member collection instead. 
        private NameValueCollection m_InnerCollection;
 
        private NameValueCollection InnerCollection 
        {
            get 
            {
                if (m_InnerCollection == null)
                    m_InnerCollection = new NameValueCollection(ApproxHighAvgNumHeaders, CaseInsensitiveAscii.StaticInstance);
                return m_InnerCollection; 
            }
        } 
 
        // this is the object that created the header collection.
        private WebHeaderCollectionType m_Type; 

#if !FEATURE_PAL
        private bool AllowHttpRequestHeader {
            get { 
                if (m_Type==WebHeaderCollectionType.Unknown) {
                    m_Type = WebHeaderCollectionType.WebRequest; 
                } 
                return m_Type==WebHeaderCollectionType.WebRequest || m_Type==WebHeaderCollectionType.HttpWebRequest || m_Type==WebHeaderCollectionType.HttpListenerRequest;
            } 
        }

        internal bool AllowHttpResponseHeader {
            get { 
                if (m_Type==WebHeaderCollectionType.Unknown) {
                    m_Type = WebHeaderCollectionType.WebResponse; 
                } 
                return m_Type==WebHeaderCollectionType.WebResponse || m_Type==WebHeaderCollectionType.HttpWebResponse || m_Type==WebHeaderCollectionType.HttpListenerResponse;
            } 
        }

        public string this[HttpRequestHeader header] {
            get { 
                if (!AllowHttpRequestHeader) {
                    throw new InvalidOperationException(SR.GetString(SR.net_headers_req)); 
                } 
                return this[UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_HEADER_ID.ToString((int)header)];
            } 
            set {
                if (!AllowHttpRequestHeader) {
                    throw new InvalidOperationException(SR.GetString(SR.net_headers_req));
                } 
                this[UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_HEADER_ID.ToString((int)header)] = value;
            } 
        } 
        public string this[HttpResponseHeader header] {
            get { 
                if (!AllowHttpResponseHeader) {
                    throw new InvalidOperationException(SR.GetString(SR.net_headers_rsp));
                }
 
                // Some of these can be mapped to Common Headers.  Other cases can be added as needed for perf.
                if (m_CommonHeaders != null) 
                { 
                    switch (header)
                    { 
                        case HttpResponseHeader.ProxyAuthenticate:
                            return m_CommonHeaders[c_ProxyAuthenticate];

                        case HttpResponseHeader.WwwAuthenticate: 
                            return m_CommonHeaders[c_WwwAuthenticate];
                    } 
                } 

                return this[UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_HEADER_ID.ToString((int)header)]; 
            }
            set {
                if (!AllowHttpResponseHeader) {
                    throw new InvalidOperationException(SR.GetString(SR.net_headers_rsp)); 
                }
                if (m_Type==WebHeaderCollectionType.HttpListenerResponse) { 
                    if (value!=null && value.Length>ushort.MaxValue) { 
                        throw new ArgumentOutOfRangeException("value", value, SR.GetString(SR.net_headers_toolong, ushort.MaxValue));
                    } 
                }
                this[UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_HEADER_ID.ToString((int)header)] = value;
            }
        } 

        public void Add(HttpRequestHeader header, string value) { 
            if (!AllowHttpRequestHeader) { 
                throw new InvalidOperationException(SR.GetString(SR.net_headers_req));
            } 
            this.Add(UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_HEADER_ID.ToString((int)header), value);
        }

        public void Add(HttpResponseHeader header, string value) { 
            if (!AllowHttpResponseHeader) {
                throw new InvalidOperationException(SR.GetString(SR.net_headers_rsp)); 
            } 
            if (m_Type==WebHeaderCollectionType.HttpListenerResponse) {
                if (value!=null && value.Length>ushort.MaxValue) { 
                    throw new ArgumentOutOfRangeException("value", value, SR.GetString(SR.net_headers_toolong, ushort.MaxValue));
                }
            }
            this.Add(UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_HEADER_ID.ToString((int)header), value); 
        }
 
        public void Set(HttpRequestHeader header, string value) { 
            if (!AllowHttpRequestHeader) {
                throw new InvalidOperationException(SR.GetString(SR.net_headers_req)); 
            }
            this.Set(UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_HEADER_ID.ToString((int)header), value);
        }
 
        public void Set(HttpResponseHeader header, string value) {
            if (!AllowHttpResponseHeader) { 
                throw new InvalidOperationException(SR.GetString(SR.net_headers_rsp)); 
            }
            if (m_Type==WebHeaderCollectionType.HttpListenerResponse) { 
                if (value!=null && value.Length>ushort.MaxValue) {
                    throw new ArgumentOutOfRangeException("value", value, SR.GetString(SR.net_headers_toolong, ushort.MaxValue));
                }
            } 
            this.Set(UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_HEADER_ID.ToString((int)header), value);
        } 
 

        internal void SetInternal(HttpResponseHeader header, string value) { 
            if (!AllowHttpResponseHeader) {
                throw new InvalidOperationException(SR.GetString(SR.net_headers_rsp));
            }
            if (m_Type==WebHeaderCollectionType.HttpListenerResponse) { 
                if (value!=null && value.Length>ushort.MaxValue) {
                    throw new ArgumentOutOfRangeException("value", value, SR.GetString(SR.net_headers_toolong, ushort.MaxValue)); 
                } 
            }
            this.SetInternal(UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_HEADER_ID.ToString((int)header), value); 
        }


        public void Remove(HttpRequestHeader header) { 
            if (!AllowHttpRequestHeader) {
                throw new InvalidOperationException(SR.GetString(SR.net_headers_req)); 
            } 
            this.Remove(UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_HEADER_ID.ToString((int)header));
        } 

        public void Remove(HttpResponseHeader header) {
            if (!AllowHttpResponseHeader) {
                throw new InvalidOperationException(SR.GetString(SR.net_headers_rsp)); 
            }
            this.Remove(UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_HEADER_ID.ToString((int)header)); 
        } 
#endif // !FEATURE_PAL
 
        // In general, HttpWebResponse headers aren't modified, so these methods don't support common headers.

        /// 
        ///    [To be supplied.] 
        /// 
        protected void AddWithoutValidate(string headerName, string headerValue) { 
            headerName = CheckBadChars(headerName, false); 
            headerValue = CheckBadChars(headerValue, true);
            GlobalLog.Print("WebHeaderCollection::AddWithoutValidate() calling InnerCollection.Add() key:[" + headerName + "], value:[" + headerValue + "]"); 
            if (m_Type==WebHeaderCollectionType.HttpListenerResponse) {
                if (headerValue!=null && headerValue.Length>ushort.MaxValue) {
                    throw new ArgumentOutOfRangeException("headerValue", headerValue, SR.GetString(SR.net_headers_toolong, ushort.MaxValue));
                } 
            }
            NormalizeCommonHeaders(); 
            InvalidateCachedArrays(); 
            InnerCollection.Add(headerName, headerValue);
        } 

        internal void SetAddVerified(string name, string value) {
            if(HInfo[name].AllowMultiValues) {
                GlobalLog.Print("WebHeaderCollection::SetAddVerified() calling InnerCollection.Add() key:[" + name + "], value:[" + value + "]"); 
                NormalizeCommonHeaders();
                InvalidateCachedArrays(); 
                InnerCollection.Add(name, value); 
            }
            else { 
                GlobalLog.Print("WebHeaderCollection::SetAddVerified() calling InnerCollection.Set() key:[" + name + "], value:[" + value + "]");
                NormalizeCommonHeaders();
                InvalidateCachedArrays();
                InnerCollection.Set(name, value); 
            }
        } 
 
        // Below three methods are for fast headers manipulation, bypassing all the checks
        internal void AddInternal(string name, string value) { 
            GlobalLog.Print("WebHeaderCollection::AddInternal() calling InnerCollection.Add() key:[" + name + "], value:[" + value + "]");
            NormalizeCommonHeaders();
            InvalidateCachedArrays();
            InnerCollection.Add(name, value); 
        }
 
        internal void ChangeInternal(string name, string value) { 
            GlobalLog.Print("WebHeaderCollection::ChangeInternal() calling InnerCollection.Set() key:[" + name + "], value:[" + value + "]");
            NormalizeCommonHeaders(); 
            InvalidateCachedArrays();
            InnerCollection.Set(name, value);
        }
 

        internal void RemoveInternal(string name) { 
            GlobalLog.Print("WebHeaderCollection::RemoveInternal() calling InnerCollection.Remove() key:[" + name + "]"); 
            NormalizeCommonHeaders();
            if (m_InnerCollection != null) 
            {
                InvalidateCachedArrays();
                m_InnerCollection.Remove(name);
            } 
        }
 
        internal void CheckUpdate(string name, string value) { 
            value = CheckBadChars(value, true);
            ChangeInternal(name, value); 
        }

        // This even faster one can be used to add headers when it's known not to be a common header or that common headers aren't active.
        private void AddInternalNotCommon(string name, string value) 
        {
            GlobalLog.Print("WebHeaderCollection::AddInternalNotCommon() calling InnerCollection.Add() key:[" + name + "], value:[" + value + "]"); 
            InvalidateCachedArrays(); 
            InnerCollection.Add(name, value);
        } 


        private static readonly char[] HttpTrimCharacters = new char[]{(char)0x09,(char)0xA,(char)0xB,(char)0xC,(char)0xD,(char)0x20};
 
        //
        // CheckBadChars - throws on invalid chars to be not found in header name/value 
        // 
        internal static string CheckBadChars(string name, bool isHeaderValue) {
 
            if (name == null || name.Length == 0) {
                // emtpy name is invlaid
                if (!isHeaderValue) {
                    throw name == null ? new ArgumentNullException("name") : 
                        new ArgumentException(SR.GetString(SR.net_emptystringcall, "name"), "name");
                } 
                //empty value is OK 
                return string.Empty;
            } 

            if (isHeaderValue) {
                // VALUE check
                //Trim spaces from both ends 
                name = name.Trim(HttpTrimCharacters);
 
                //First, check for correctly formed multi-line value 
                //Second, check for absenece of CTL characters
                int crlf = 0; 
                for(int i = 0; i < name.Length; ++i) {
                    char c = (char) (0x000000ff & (uint) name[i]);
                    switch (crlf)
                    { 
                        case 0:
                            if (c == '\r') 
                            { 
                                crlf = 1;
                            } 
                            else if (c == '\n')
                            {
                                // Technically this is bad HTTP.  But it would be a breaking change to throw here.
                                // Is there an exploit? 
                                crlf = 2;
                            } 
                            else if (c == 127 || (c < ' ' && c != '\t')) 
                            {
                                throw new ArgumentException(SR.GetString(SR.net_WebHeaderInvalidControlChars), "value"); 
                            }
                            break;

                        case 1: 
                            if (c == '\n')
                            { 
                                crlf = 2; 
                                break;
                            } 
                            throw new ArgumentException(SR.GetString(SR.net_WebHeaderInvalidCRLFChars), "value");

                        case 2:
                            if (c == ' ' || c == '\t') 
                            {
                                crlf = 0; 
                                break; 
                            }
                            throw new ArgumentException(SR.GetString(SR.net_WebHeaderInvalidCRLFChars), "value"); 
                    }
                }
                if (crlf != 0)
                { 
                    throw new ArgumentException(SR.GetString(SR.net_WebHeaderInvalidCRLFChars), "value");
                } 
            } 
            else {
                // NAME check 
                //First, check for absence of separators and spaces
                if (name.IndexOfAny(ValidationHelper.InvalidParamChars) != -1) {
                    throw new ArgumentException(SR.GetString(SR.net_WebHeaderInvalidHeaderChars), "name");
                } 

                //Second, check for non CTL ASCII-7 characters (32-126) 
                if (ContainsNonAsciiChars(name)) { 
                    throw new ArgumentException(SR.GetString(SR.net_WebHeaderInvalidNonAsciiChars), "name");
                } 
            }
            return name;
        }
 
        internal static bool IsValidToken(string token) {
            return (token.Length > 0) 
                && (token.IndexOfAny(ValidationHelper.InvalidParamChars) == -1) 
                && !ContainsNonAsciiChars(token);
        } 

        internal static bool ContainsNonAsciiChars(string token) {
            for (int i = 0; i < token.Length; ++i) {
                if ((token[i] < 0x20) || (token[i] > 0x7e)) { 
                    return true;
                } 
            } 
            return false;
        } 

        //
        // ThrowOnRestrictedHeader - generates an error if the user,
        //  passed in a reserved string as the header name 
        //
        internal void ThrowOnRestrictedHeader(string headerName) 
        { 
            if (m_Type == WebHeaderCollectionType.HttpWebRequest)
            { 
                if (HInfo[headerName].IsRequestRestricted)
                {
                    throw new ArgumentException(!Equals(headerName, HttpKnownHeaderNames.Host) ? SR.GetString(SR.net_headerrestrict) :
                        SR.GetString(SR.net_headerrestrict_resp, HttpKnownHeaderNames.Host), "name"); 
                }
            } 
            else if (m_Type == WebHeaderCollectionType.HttpListenerResponse) 
            {
                if (HInfo[headerName].IsResponseRestricted) 
                {
                    throw new ArgumentException(SR.GetString(SR.net_headerrestrict_resp, headerName), "name");
                }
            } 
        }
 
        // 
        // Our Public METHOD set, most are inherited from NameValueCollection,
        //  not all methods from NameValueCollection are listed, even though usable - 
        //
        //  this includes
        //  Add(name, value)
        //  Add(header) 
        //  this[name] {set, get}
        //  Remove(name), returns bool 
        //  Remove(name), returns void 
        //  Set(name, value)
        //  ToString() 
        //
        //  SplitValue(name, value)
        //  ToByteArray()
        //  ParseHeaders(char [], ...) 
        //  ParseHeaders(byte [], ...)
        // 
 
        // Add more headers; if "name" already exists it will
        // add concatenated value 


        // Add -
        //  Routine Description: 
        //      Adds headers with validation to see if they are "proper" headers.
        //      Will cause header to be concat to existing if already found. 
        //      If the header is a special header, listed in RestrictedHeaders object, 
        //      then this call will cause an exception indication as such.
        //  Arguments: 
        //      name - header-name to add
        //      value - header-value to add, a header is already there, will concat this value
        //  Return Value:
        //      None 

        ///  
        ///     
        ///       Adds a new header with the indicated name and value.
        ///     
        /// 
        public override void Add(string name, string value) {
            name = CheckBadChars(name, false);
            ThrowOnRestrictedHeader(name); 
            value = CheckBadChars(value, true);
            GlobalLog.Print("WebHeaderCollection::Add() calling InnerCollection.Add() key:[" + name + "], value:[" + value + "]"); 
            if (m_Type==WebHeaderCollectionType.HttpListenerResponse) { 
                if (value!=null && value.Length>ushort.MaxValue) {
                    throw new ArgumentOutOfRangeException("value", value, SR.GetString(SR.net_headers_toolong, ushort.MaxValue)); 
                }
            }
            NormalizeCommonHeaders();
            InvalidateCachedArrays(); 
            InnerCollection.Add(name, value);
        } 
 

        // Add - 
        // Routine Description:
        //     Adds headers with validation to see if they are "proper" headers.
        //     Assumes a combined a "Name: Value" string, and parses the two parts out.
        //     Will cause header to be concat to existing if already found. 
        //     If the header is a speical header, listed in RestrictedHeaders object,
        //     then this call will cause an exception indication as such. 
        // Arguments: 
        //     header - header name: value pair
        // Return Value: 
        //     None

        /// 
        ///     
        ///       Adds the indicated header.
        ///     
        ///  
        public void Add(string header) {
            if ( ValidationHelper.IsBlankString(header) ) { 
                throw new ArgumentNullException("header");
            }
            int colpos = header.IndexOf(':');
            // check for badly formed header passed in 
            if (colpos<0) {
                throw new ArgumentException(SR.GetString(SR.net_WebHeaderMissingColon), "header"); 
            } 
            string name = header.Substring(0, colpos);
            string value = header.Substring(colpos+1); 
            name = CheckBadChars(name, false);
            ThrowOnRestrictedHeader(name);
            value = CheckBadChars(value, true);
            GlobalLog.Print("WebHeaderCollection::Add(" + header + ") calling InnerCollection.Add() key:[" + name + "], value:[" + value + "]"); 
            if (m_Type==WebHeaderCollectionType.HttpListenerResponse) {
                if (value!=null && value.Length>ushort.MaxValue) { 
                    throw new ArgumentOutOfRangeException("value", value, SR.GetString(SR.net_headers_toolong, ushort.MaxValue)); 
                }
            } 
            NormalizeCommonHeaders();
            InvalidateCachedArrays();
            InnerCollection.Add(name, value);
        } 

        // Set - 
        // Routine Description: 
        //     Sets headers with validation to see if they are "proper" headers.
        //     If the header is a special header, listed in RestrictedHeaders object, 
        //     then this call will cause an exception indication as such.
        // Arguments:
        //     name - header-name to set
        //     value - header-value to set 
        // Return Value:
        //     None 
 
        /// 
        ///     
        ///       Sets the specified header to the specified value.
        ///    
        /// 
        public override void Set(string name, string value) { 
            if (ValidationHelper.IsBlankString(name)) {
                throw new ArgumentNullException("name"); 
            } 
            name = CheckBadChars(name, false);
            ThrowOnRestrictedHeader(name); 
            value = CheckBadChars(value, true);
            GlobalLog.Print("WebHeaderCollection::Set() calling InnerCollection.Set() key:[" + name + "], value:[" + value + "]");
            if (m_Type==WebHeaderCollectionType.HttpListenerResponse) {
                if (value!=null && value.Length>ushort.MaxValue) { 
                    throw new ArgumentOutOfRangeException("value", value, SR.GetString(SR.net_headers_toolong, ushort.MaxValue));
                } 
            } 
            NormalizeCommonHeaders();
            InvalidateCachedArrays(); 
            InnerCollection.Set(name, value);
        }

 
        internal void SetInternal(string name, string value) {
            if (ValidationHelper.IsBlankString(name)) { 
                throw new ArgumentNullException("name"); 
            }
            name = CheckBadChars(name, false); 
            value = CheckBadChars(value, true);
            GlobalLog.Print("WebHeaderCollection::Set() calling InnerCollection.Set() key:[" + name + "], value:[" + value + "]");
            if (m_Type==WebHeaderCollectionType.HttpListenerResponse) {
                if (value!=null && value.Length>ushort.MaxValue) { 
                    throw new ArgumentOutOfRangeException("value", value, SR.GetString(SR.net_headers_toolong, ushort.MaxValue));
                } 
            } 
            NormalizeCommonHeaders();
            InvalidateCachedArrays(); 
            InnerCollection.Set(name, value);
        }

 
        // Remove -
        // Routine Description: 
        //     Removes give header with validation to see if they are "proper" headers. 
        //     If the header is a speical header, listed in RestrictedHeaders object,
        //     then this call will cause an exception indication as such. 
        // Arguments:
        //     name - header-name to remove
        // Return Value:
        //     None 

        ///  
        ///    Removes the specified header. 
        /// 
        public override void Remove(string name) { 
            if ( ValidationHelper.IsBlankString(name) ) {
                throw new ArgumentNullException("name");
            }
            ThrowOnRestrictedHeader(name); 
            name = CheckBadChars(name,  false);
            GlobalLog.Print("WebHeaderCollection::Remove() calling InnerCollection.Remove() key:[" + name + "]"); 
            NormalizeCommonHeaders(); 
            if (m_InnerCollection != null)
            { 
                InvalidateCachedArrays();
                m_InnerCollection.Remove(name);
            }
        } 

 
        // GetValues 
        // Routine Description:
        //     This method takes a header name and returns a string array representing 
        //     the individual values for that headers. For example, if the headers
        //     contained the line Accept: text/plain, text/html then
        //     GetValues("Accept") would return an array of two strings: "text/plain"
        //     and "text/html". 
        // Arguments:
        //     header      - Name of the header. 
        // Return Value: 
        //     string[] - array of parsed string objects
 
        /// 
        ///    
        ///       Gets an array of header values stored in a
        ///       header. 
        ///    
        ///  
        public override string[] GetValues(string header) { 
            // This method doesn't work with common headers.  Dump common headers into the pool.
            NormalizeCommonHeaders(); 

            // First get the information about the header and the values for
            // the header.
            HeaderInfo Info = HInfo[header]; 
            string[] Values = InnerCollection.GetValues(header);
            // If we have no information about the header or it doesn't allow 
            // multiple values, just return the values. 
            if (Info == null || Values == null || !Info.AllowMultiValues) {
                return Values; 
            }
            // Here we have a multi value header. We need to go through
            // each entry in the multi values array, and if an entry itself
            // has multiple values we'll need to combine those in. 
            //
            // We do some optimazation here, where we try not to copy the 
            // values unless there really is one that have multiple values. 
            string[] TempValues;
            ArrayList ValueList = null; 
            int i;
            for (i = 0; i < Values.Length; i++) {
                // Parse this value header.
                TempValues = Info.Parser(Values[i]); 
                // If we don't have an array list yet, see if this
                // value has multiple values. 
                if (ValueList == null) { 
                    // See if it has multiple values.
                    if (TempValues.Length > 1) { 
                        // It does, so we need to create an array list that
                        // represents the Values, then trim out this one and
                        // the ones after it that haven't been parsed yet.
                        ValueList = new ArrayList(Values); 
                        ValueList.RemoveRange(i, Values.Length - i);
                        ValueList.AddRange(TempValues); 
                    } 
                }
                else { 
                    // We already have an ArrayList, so just add the values.
                    ValueList.AddRange(TempValues);
                }
            } 
            // See if we have an ArrayList. If we don't, just return the values.
            // Otherwise convert the ArrayList to a string array and return that. 
            if (ValueList != null) { 
                string[] ReturnArray = new string[ValueList.Count];
                ValueList.CopyTo(ReturnArray); 
                return ReturnArray;
            }
            return Values;
        } 

 
        // ToString()  - 
        // Routine Description:
        //     Generates a string representation of the headers, that is ready to be sent except for it being in string format: 
        //     the format looks like:
        //
        //     Header-Name: Header-Value\r\n
        //     Header-Name2: Header-Value2\r\n 
        //     ...
        //     Header-NameN: Header-ValueN\r\n 
        //     \r\n 
        //
        //     Uses the string builder class to Append the elements together. 
        // Arguments:
        //     None.
        // Return Value:
        //     string 

        ///  
        ///  
        ///    
        ///       Obsolete. 
        ///    
        /// 
        public override string ToString() {
            string result = GetAsString(this, false, false); 
            GlobalLog.Print("WebHeaderCollection::ToString: \r\n" + result);
            return result; 
        } 

        internal string ToString(bool forTrace) 
        {
            return GetAsString(this, false, true);
        }
 

        // 
        // if winInetCompat = true then it will not insert spaces after ':' 
        // and it will output "~U" header first
        // 
        internal static string GetAsString(NameValueCollection cc,
                                           bool                winInetCompat,
                                           bool                forTrace) {
#if FEATURE_PAL 
            if (winInetCompat) {
                throw new InvalidOperationException(); 
            } 
#endif // FEATURE_PAL
 
            if (cc == null || cc.Count == 0) {
                return "\r\n";
            }
            StringBuilder sb = new StringBuilder(ApproxAveHeaderLineSize*cc.Count); 
            string statusLine;
            statusLine = cc[string.Empty]; 
            if (statusLine != null) { 
                sb.Append(statusLine).Append("\r\n");
            } 
            for (int i = 0; i < cc.Count ; i++) {
                string key = cc.GetKey(i) as string;
                string val = cc.Get(i) as string;
                /* 
                if (forTrace)
                { 
                    // Put a condition here that if we are using basic auth, 
                    // we shouldn't put the authorization header. Otherwise
                    // the password will get saved in the trace. 
                    if (using basic)
                        continue;
                }
                */ 
                if (ValidationHelper.IsBlankString(key)) {
                    continue; 
                } 
                sb.Append(key);
                if (winInetCompat) { 
                    sb.Append(':');
                }
                else {
                    sb.Append(": "); 
                }
                sb.Append(val).Append("\r\n"); 
            } 
            if (!forTrace)
                sb.Append("\r\n"); 
            return sb.ToString();
        }

 
        // ToByteArray()  -
        // Routine Description: 
        //     Generates a byte array representation of the headers, that is ready to be sent. 
        //     So it Serializes our headers into a byte array suitable for sending over the net.
        // 
        //     the format looks like:
        //
        //     Header-Name1: Header-Value1\r\n
        //     Header-Name2: Header-Value2\r\n 
        //     ...
        //     Header-NameN: Header-ValueN\r\n 
        //     \r\n 
        //
        //     Uses the ToString() method to generate, and then performs conversion. 
        //
        //     Performance Note:  Why are we not doing a single copy/covert run?
        //     As the code before used to know the size of the output!
        //     Because according to Demitry, its cheaper to copy the headers twice, 
        //     then it is to call the UNICODE to ANSI conversion code many times.
        // Arguments: 
        //     None. 
        // Return Value:
        //     byte [] - array of bytes values 

        /// 
        /// 
        ///     
        ///       Obsolete.
        ///     
        ///  
        public byte[] ToByteArray() {
            // Make sure the buffer is big enough. 
            string tempStr = ToString();
            //
            // Use the string of headers, convert to Char Array,
            //  then convert to Bytes, 
            //  serializing finally into the buffer, along the way.
            // 
            byte[] buffer = HeaderEncoding.GetBytes(tempStr); 
            return buffer;
        } 

        /// 
        ///    Tests if access to the HTTP header with the provided name is accessible for setting.
        ///  
        public static bool IsRestricted(string headerName)
        { 
            return IsRestricted(headerName, false); 
        }
 
        public static bool IsRestricted(string headerName, bool response)
        {
            return response ? HInfo[CheckBadChars(headerName, false)].IsResponseRestricted : HInfo[CheckBadChars(headerName, false)].IsRequestRestricted;
        } 

 
        ///  
        ///    
        ///       Initializes a new instance of the  
        ///       class.
        ///    
        /// 
        public WebHeaderCollection() : base(DBNull.Value) 
        {
        } 
 
        internal WebHeaderCollection(WebHeaderCollectionType type) : base(DBNull.Value)
        { 
            m_Type = type;
            if (type == WebHeaderCollectionType.HttpWebResponse)
                m_CommonHeaders = new string[s_CommonHeaderNames.Length - 1];  // Minus one for the sentinel.
        } 

        //This is for Cache 
        internal WebHeaderCollection(NameValueCollection cc): base(DBNull.Value) 
        {
            m_InnerCollection = new NameValueCollection(cc.Count + 2, CaseInsensitiveAscii.StaticInstance); 
            int len = cc.Count;
            for (int i = 0; i < len; ++i) {
                String key = cc.GetKey(i);
                String[] values = cc.GetValues(i); 
                if (values != null) {
                    for (int j = 0; j < values.Length; j++) { 
                        InnerCollection.Add(key, values[j]); 
                    }
                } 
                else {
                    InnerCollection.Add(key, null);
                }
            } 
        }
 
        // 
        // ISerializable constructor
        // 
        /// 
        ///    [To be supplied.]
        /// 
        protected WebHeaderCollection(SerializationInfo serializationInfo, StreamingContext streamingContext) : 
            base(DBNull.Value)
        { 
            int count = serializationInfo.GetInt32("Count"); 
            m_InnerCollection = new NameValueCollection(count + 2, CaseInsensitiveAscii.StaticInstance);
            for (int i = 0; i < count; i++) { 
                string headerName = serializationInfo.GetString(i.ToString(NumberFormatInfo.InvariantInfo));
                string headerValue = serializationInfo.GetString((i+count).ToString(NumberFormatInfo.InvariantInfo));
                GlobalLog.Print("WebHeaderCollection::.ctor(ISerializable) calling InnerCollection.Add() key:[" + headerName + "], value:[" + headerValue + "]");
                InnerCollection.Add(headerName, headerValue); 
            }
        } 
 
        public override void OnDeserialization(object sender) {
            // 


        }
 
        //
        // ISerializable method 
        // 
        /// 
        [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.SerializationFormatter)] 		 
        public override void GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext) {
            //
            // for now disregard streamingContext.
            // 
            NormalizeCommonHeaders();
            serializationInfo.AddValue("Count", Count); 
            for (int i = 0; i < Count; i++) 
            {
                serializationInfo.AddValue(i.ToString(NumberFormatInfo.InvariantInfo), GetKey(i)); 
                serializationInfo.AddValue((i + Count).ToString(NumberFormatInfo.InvariantInfo), Get(i));
            }
        }
 

        // we use this static class as a helper class to encode/decode HTTP headers. 
        // what we need is a 1-1 correspondence between a char in the range U+0000-U+00FF 
        // and a byte in the range 0x00-0xFF (which is the range that can hit the network).
        // The Latin-1 encoding (ISO-88591-1) (GetEncoding(28591)) works for byte[] to string, but is a little slow. 
        // It doesn't work for string -> byte[] because of best-fit-mapping problems.
        internal static class HeaderEncoding
        {
            internal static unsafe string GetString(byte[] bytes, int byteIndex, int byteCount) 
            {
                fixed(byte* pBytes = bytes) 
                    return GetString(pBytes + byteIndex, byteCount); 
            }
 
            internal static unsafe string GetString(byte* pBytes, int byteCount)
            {
                if (byteCount < 1)
                    return ""; 

                string s = new String('\0', byteCount); 
 
                fixed (char* pStr = s)
                { 
                    char* pString = pStr;
                    while (byteCount >= 8)
                    {
                        pString[0] = (char) pBytes[0]; 
                        pString[1] = (char) pBytes[1];
                        pString[2] = (char) pBytes[2]; 
                        pString[3] = (char) pBytes[3]; 
                        pString[4] = (char) pBytes[4];
                        pString[5] = (char) pBytes[5]; 
                        pString[6] = (char) pBytes[6];
                        pString[7] = (char) pBytes[7];
                        pString += 8;
                        pBytes += 8; 
                        byteCount -= 8;
                    } 
                    for (int i = 0; i < byteCount; i++) 
                    {
                        pString[i] = (char) pBytes[i]; 
                    }
                }

                return s; 
            }
 
            internal static int GetByteCount(string myString) { 
                return myString.Length;
            } 
            internal unsafe static void GetBytes(string myString, int charIndex, int charCount, byte[] bytes, int byteIndex) {
                if (myString.Length==0) {
                    return;
                } 
                fixed (byte *bufferPointer = bytes) {
                    byte* newBufferPointer = bufferPointer + byteIndex; 
                    int finalIndex = charIndex + charCount; 
                    while (charIndex
            // TEXT           =  
            // CTL            = 
            // SP             = 
            // HT             = 
            // CR             =  
            // LF             = 
            // LWS            = [CR LF] 1*( SP | HT ) 
            // CHAR           =  
            // token          = 1*
            // separators     = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <"> | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT 
            // quoted-string  = ( <"> *(qdtext | quoted-pair ) <"> )
            // qdtext         = >
            // quoted-pair    = "\" CHAR
            // 

            // 
            // At each iteration of the following loop we expect to parse a single HTTP header entirely. 
            //
            for (;;) { 
                //
                // trim leading whitespaces (LWS) just for extra robustness, in fact if there are leading white spaces then:
                // 1) it could be that after the status line we might have spaces. handle this.
                // 2) this should have been detected to be a multiline header so there'll be no spaces and we'll spend some time here. 
                //
                headerName = string.Empty; 
                headerValue = string.Empty; 
                spaceAfterLf = false;
                headerMultiLineValue = null; 

                if (Count == 0) {
                    //
                    // so, restrict this extra trimming only on the first header line 
                    //
                    while (index < size) { 
                         ch = (char) byteBuffer[index]; 
                         if (ch == ' ' || ch == '\t') {
                             ++index; 
                            if (maximumResponseHeadersLength>=0 && ++localTotalResponseHeadersLength>=maximumResponseHeadersLength) {
                                parseStatus = DataParseStatus.DataTooBig;
                                goto quit;
                            } 
                        }
                        else { 
                            break; 
                        }
                    } 

                    if (index==size) {
                        //
                        // we reached the end of the buffer. ask for more data. 
                        //
                        parseStatus = DataParseStatus.NeedMoreData; 
                        goto quit; 
                    }
                } 

                //
                // what we have here is the beginning of a new header
                // 
                headerNameStartOffset = index;
 
                while (index < size) { 
                    ch = (char) byteBuffer[index];
                    if (ch != ':' && ch != '\n') { 
                        if (ch > ' ') {
                            //
                            // if there's an illegal character we should return DataParseStatus.Invalid
                            // instead we choose to be flexible, try to trim it, but include it in the string 
                            //
                            headerNameEndOffset = index; 
                        } 
                        ++index;
                        if (maximumResponseHeadersLength>=0 && ++localTotalResponseHeadersLength>=maximumResponseHeadersLength) { 
                            parseStatus = DataParseStatus.DataTooBig;
                            goto quit;
                        }
                    } 
                    else {
                        if (ch == ':') { 
                            ++index; 
                            if (maximumResponseHeadersLength>=0 && ++localTotalResponseHeadersLength>=maximumResponseHeadersLength) {
                                parseStatus = DataParseStatus.DataTooBig; 
                                goto quit;
                            }
                        }
                        break; 
                    }
                } 
                if (index==size) { 
                    //
                    // we reached the end of the buffer. ask for more data. 
                    //
                    parseStatus = DataParseStatus.NeedMoreData;
                    goto quit;
                } 

startOfValue: 
                // 
                // skip all [' ','\t','\r','\n'] characters until HeaderValue starts
                // if we didn't find any headers yet, we set numberOfLf to 1 
                // so that we take the '\n' from the status line into account
                //

                numberOfLf = (Count == 0 && headerNameEndOffset < 0) ? 1 : 0; 
                while (index=0 && ++localTotalResponseHeadersLength>=maximumResponseHeadersLength) {
                            parseStatus = DataParseStatus.DataTooBig; 
                            goto quit;
                        }
                    }
                    else { 
                        break;
                    } 
                } 
                if (numberOfLf==2 || (numberOfLf==1 && !spaceAfterLf)) {
                    // 
                    // if we've counted two '\n' we got at the end of the headers even if we're past the end of the buffer
                    // if we've counted one '\n' and the first character after that was a ' ' or a '\t'
                    // no matter if we found a ':' or not, treat this as an empty header name.
                    // 
                    goto addHeader;
                } 
                if (index==size) { 
                    //
                    // we reached the end of the buffer. ask for more data. 
                    //
                    parseStatus = DataParseStatus.NeedMoreData;
                    goto quit;
                } 

                headerValueStartOffset = index; 
 
                while (index ' ') {
                            headerValueEndOffset = index;
                        } 
                        ++index;
                        if (maximumResponseHeadersLength>=0 && ++localTotalResponseHeadersLength>=maximumResponseHeadersLength) { 
                            parseStatus = DataParseStatus.DataTooBig; 
                            goto quit;
                        } 
                    }
                    else {
                        break;
                    } 
                }
                if (index==size) { 
                    // 
                    // we reached the end of the buffer. ask for more data.
                    // 
                    parseStatus = DataParseStatus.NeedMoreData;
                    goto quit;
                }
 
                //
                // at this point we found either a '\n' or the end of the headers 
                // hence we are at the end of the Header Line. 4 options: 
                // 1) need more data
                // 2) if we find two '\n' => end of headers 
                // 3) if we find one '\n' and a ' ' or a '\t' => multiline header
                // 4) if we find one '\n' and a valid char => next header
                //
                numberOfLf = 0; 
                while (index=0 && ++localTotalResponseHeadersLength>=maximumResponseHeadersLength) {
                            parseStatus = DataParseStatus.DataTooBig; 
                            goto quit;
                        } 
                    } 
                    else {
                        break; 
                    }
                }
                if (index==size && numberOfLf<2) {
                    // 
                    // we reached the end of the buffer but not of the headers. ask for more data.
                    // 
                    parseStatus = DataParseStatus.NeedMoreData; 
                    goto quit;
                } 

addHeader:
                if (headerValueStartOffset>=0 && headerValueStartOffset>headerNameEndOffset && headerValueEndOffset>=headerValueStartOffset) {
                    // 
                    // Encoding fastest way to build the UNICODE string off the byte[]
                    // 
                    headerValue = HeaderEncoding.GetString(byteBuffer + headerValueStartOffset, headerValueEndOffset - headerValueStartOffset + 1); 
                }
 
                //
                // if we got here from the beginning of the for loop, headerMultiLineValue will be null
                // otherwise it will contain the headerValue constructed for the multiline header
                // add this line as well to it, separated by a single space 
                //
                headerMultiLineValue = (headerMultiLineValue==null ? headerValue : headerMultiLineValue + " " + headerValue); 
 
                if (index < size && numberOfLf == 1) {
                    ch = (char) byteBuffer[index]; 
                    if (ch == ' ' || ch == '\t') {
                        //
                        // since we found only one Lf and the next header line begins with a Lws,
                        // this is the beginning of a multiline header. 
                        // parse the next line into headerValue later it will be added to headerMultiLineValue
                        // 
                        ++index; 
                        if (maximumResponseHeadersLength>=0 && ++localTotalResponseHeadersLength>=maximumResponseHeadersLength) {
                            parseStatus = DataParseStatus.DataTooBig; 
                            goto quit;
                        }
                        goto startOfValue;
                    } 
                }
 
                if (headerNameStartOffset>=0 && headerNameEndOffset>=headerNameStartOffset) { 
                    //
                    // Encoding is the fastest way to build the UNICODE string off the byte[] 
                    //
                    headerName = HeaderEncoding.GetString(byteBuffer + headerNameStartOffset, headerNameEndOffset - headerNameStartOffset + 1);
                }
 
                //
                // now it's finally safe to add the header if we have a name for it 
                // 
                if (headerName.Length>0) {
                    // 
                    // the base clasee will check for pre-existing headerValue and append
                    // it using commas as indicated in the RFC
                    //
                    GlobalLog.Print("WebHeaderCollection::ParseHeaders() calling AddInternal() key:[" + headerName + "], value:[" + headerMultiLineValue + "]"); 
                    AddInternal(headerName, headerMultiLineValue);
                } 
 
                //
                // and update unparsed 
                //
                totalResponseHeadersLength = localTotalResponseHeadersLength;
                unparsed = index;
 
                if (numberOfLf==2) {
                    parseStatus = DataParseStatus.Done; 
                    goto quit; 
                }
 
            } // for (;;)

quit:
            GlobalLog.Leave("WebHeaderCollection::ParseHeaders() returning parseStatus:" + parseStatus.ToString()); 
            if (parseStatus == DataParseStatus.Invalid) {
                parseError.Section = WebParseErrorSection.ResponseHeader; 
                parseError.Code    = parseErrorCode; 
            }
 
            return parseStatus;
            }
        }
 
        //
        // Alternative parsing that follows RFC2616.  Like the above, this trims both sides of the header value and replaces 
        // folding with a single space. 
        //
        private enum RfcChar : byte 
        {
            High = 0,
            Reg,
            Ctl, 
            CR,
            LF, 
            WS, 
            Colon,
            Delim 
        }

        private static RfcChar[] RfcCharMap = new RfcChar[128]
        { 
            RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,
            RfcChar.Ctl,   RfcChar.WS,    RfcChar.LF,    RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.CR,    RfcChar.Ctl,   RfcChar.Ctl, 
            RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl, 
            RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,
            RfcChar.WS,    RfcChar.Reg,   RfcChar.Delim, RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg, 
            RfcChar.Delim, RfcChar.Delim, RfcChar.Reg,   RfcChar.Reg,   RfcChar.Delim, RfcChar.Reg,   RfcChar.Reg,   RfcChar.Delim,
            RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,
            RfcChar.Reg,   RfcChar.Reg,   RfcChar.Colon, RfcChar.Delim, RfcChar.Delim, RfcChar.Delim, RfcChar.Delim, RfcChar.Delim,
            RfcChar.Delim, RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg, 
            RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,
            RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg, 
            RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Delim, RfcChar.Delim, RfcChar.Delim, RfcChar.Reg,   RfcChar.Reg, 
            RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,
            RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg, 
            RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,
            RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Delim, RfcChar.Reg,   RfcChar.Delim, RfcChar.Reg,   RfcChar.Ctl,
        };
 
        internal unsafe DataParseStatus ParseHeadersStrict(
                byte[] buffer, 
                int size, 
                ref int unparsed,
                ref int totalResponseHeadersLength, 
                int maximumResponseHeadersLength,
                ref WebParseError parseError)
        {
            GlobalLog.Enter("WebHeaderCollection::ParseHeadersStrict(): size:" + size.ToString() + ", unparsed:" + unparsed.ToString() + " buffer:[" + Encoding.ASCII.GetString(buffer, unparsed, Math.Min(256, size-unparsed)) + "]"); 

            WebParseErrorCode parseErrorCode = WebParseErrorCode.Generic; 
            DataParseStatus parseStatus = DataParseStatus.Invalid; 

            int i = unparsed; 
            RfcChar ch;
            int effectiveSize = maximumResponseHeadersLength <= 0 ? Int32.MaxValue : maximumResponseHeadersLength - totalResponseHeadersLength + i;
            DataParseStatus sizeError = DataParseStatus.DataTooBig;
            if (size < effectiveSize) 
            {
                effectiveSize = size; 
                sizeError = DataParseStatus.NeedMoreData; 
            }
 
            // Verify the size.
            if (i >= effectiveSize)
            {
                parseStatus = sizeError; 
                goto quit;
            } 
 
            fixed (byte* byteBuffer = buffer)
            { 
                while (true)
                {
                    // If this is CRLF, actually we're done.
                    if (byteBuffer[i] == '\r') 
                    {
                        if (++i == effectiveSize) 
                        { 
                            parseStatus = sizeError;
                            goto quit; 
                        }

                        if (byteBuffer[i++] == '\n')
                        { 
                            totalResponseHeadersLength += i - unparsed;
                            unparsed = i; 
                            parseStatus = DataParseStatus.Done; 
                            goto quit;
                        } 

                        parseStatus = DataParseStatus.Invalid;
                        parseErrorCode = WebParseErrorCode.CrLfError;
                        goto quit; 
                    }
 
                    // Find the header name; only regular characters allowed. 
                    int iBeginName = i;
                    for (; i < effectiveSize && (ch = byteBuffer[i] > 127 ? RfcChar.High : RfcCharMap[byteBuffer[i]]) == RfcChar.Reg; i++); 
                    if (i == effectiveSize)
                    {
                        parseStatus = sizeError;
                        goto quit; 
                    }
                    if (i == iBeginName) 
                    { 
                        parseStatus = DataParseStatus.Invalid;
                        parseErrorCode = WebParseErrorCode.InvalidHeaderName; 
                        goto quit;
                    }

                    // Read to a colon. 
                    int iEndName = i - 1;
                    int crlf = 0;  // 1 = cr, 2 = crlf 
                    for (; i < effectiveSize && (ch = byteBuffer[i] > 127 ? RfcChar.High : RfcCharMap[byteBuffer[i]]) != RfcChar.Colon; i++) 
                    {
                        switch (ch) 
                        {
                            case RfcChar.WS:
                                if (crlf == 1)
                                { 
                                    break;
                                } 
                                crlf = 0; 
                                continue;
 
                            case RfcChar.CR:
                                if (crlf == 0)
                                {
                                    crlf = 1; 
                                    continue;
                                } 
                                break; 

                            case RfcChar.LF: 
                                if (crlf == 1)
                                {
                                    crlf = 2;
                                    continue; 
                                }
                                break; 
                        } 
                        parseStatus = DataParseStatus.Invalid;
                        parseErrorCode = WebParseErrorCode.CrLfError; 
                        goto quit;
                    }
                    if (i == effectiveSize)
                    { 
                        parseStatus = sizeError;
                        goto quit; 
                    } 
                    if (crlf != 0)
                    { 
                        parseStatus = DataParseStatus.Invalid;
                        parseErrorCode = WebParseErrorCode.IncompleteHeaderLine;
                        goto quit;
                    } 

                    // Skip the colon. 
                    if (++i == effectiveSize) 
                    {
                        parseStatus = sizeError; 
                        goto quit;
                    }

                    // Read the value.  crlf = 3 means in the whitespace after a CRLF 
                    int iBeginValue = -1;
                    int iEndValue = -1; 
                    StringBuilder valueAccumulator = null; 
                    for (; i < effectiveSize && ((ch = byteBuffer[i] > 127 ? RfcChar.High : RfcCharMap[byteBuffer[i]]) == RfcChar.WS || crlf != 2); i++)
                    { 
                        switch (ch)
                        {
                            case RfcChar.WS:
                                if (crlf == 1) 
                                {
                                    break; 
                                } 
                                if (crlf == 2)
                                { 
                                    crlf = 3;
                                }
                                continue;
 
                            case RfcChar.CR:
                                if (crlf == 0) 
                                { 
                                    crlf = 1;
                                    continue; 
                                }
                                break;

                            case RfcChar.LF: 
                                if (crlf == 1)
                                { 
                                    crlf = 2; 
                                    continue;
                                } 
                                break;

                            case RfcChar.High:
                            case RfcChar.Colon: 
                            case RfcChar.Delim:
                            case RfcChar.Reg: 
                                if (crlf == 1) 
                                {
                                    break; 
                                }
                                if (crlf == 3)
                                {
                                    crlf = 0; 
                                    if (iBeginValue != -1)
                                    { 
                                        string s = HeaderEncoding.GetString(byteBuffer + iBeginValue, iEndValue - iBeginValue + 1); 
                                        if (valueAccumulator == null)
                                        { 
                                            valueAccumulator = new StringBuilder(s, s.Length * 5);
                                        }
                                        else
                                        { 
                                            valueAccumulator.Append(" ");
                                            valueAccumulator.Append(s); 
                                        } 
                                    }
                                    iBeginValue = -1; 
                                }
                                if (iBeginValue == -1)
                                {
                                    iBeginValue = i; 
                                }
                                iEndValue = i; 
                                continue; 
                        }
                        parseStatus = DataParseStatus.Invalid; 
                        parseErrorCode = WebParseErrorCode.CrLfError;
                        goto quit;
                    }
                    if (i == effectiveSize) 
                    {
                        parseStatus = sizeError; 
                        goto quit; 
                    }
 
                    // Make the value.
                    string sValue = iBeginValue == -1 ? "" : HeaderEncoding.GetString(byteBuffer + iBeginValue, iEndValue - iBeginValue + 1);
                    if (valueAccumulator != null)
                    { 
                        if (sValue.Length != 0)
                        { 
                            valueAccumulator.Append(" "); 
                            valueAccumulator.Append(sValue);
                        } 
                        sValue = valueAccumulator.ToString();
                    }

                    // Make the name.  See if it's a common header first. 
                    string sName = null;
                    int headerNameLength = iEndName - iBeginName + 1; 
                    if (m_CommonHeaders != null) 
                    {
                        int iHeader = s_CommonHeaderHints[byteBuffer[iBeginName] & 0x1f]; 
                        if (iHeader >= 0)
                        {
                            while (true)
                            { 
                                string s = s_CommonHeaderNames[iHeader++];
 
                                // Not found if we get to a shorter header or one with a different first character. 
                                if (s.Length < headerNameLength || CaseInsensitiveAscii.AsciiToLower[byteBuffer[iBeginName]] != CaseInsensitiveAscii.AsciiToLower[s[0]])
                                    break; 

                                // Keep looking if the common header is too long.
                                if (s.Length > headerNameLength)
                                    continue; 

                                int j; 
                                byte* pBuffer = byteBuffer + iBeginName + 1; 
                                for (j = 1; j < s.Length; j++)
                                { 
                                    // Avoid the case-insensitive compare in the common case where they match.
                                    if (*(pBuffer++) != s[j] && CaseInsensitiveAscii.AsciiToLower[*(pBuffer - 1)] != CaseInsensitiveAscii.AsciiToLower[s[j]])
                                        break;
                                } 
                                if (j == s.Length)
                                { 
                                    // Set it to the appropriate index. 
                                    m_NumCommonHeaders++;
                                    iHeader--; 
                                    if (m_CommonHeaders[iHeader] == null)
                                    {
                                        m_CommonHeaders[iHeader] = sValue;
                                    } 
                                    else
                                    { 
                                        // Don't currently handle combining multiple header instances in the common header case. 
                                        // Nothing to do but punt them all to the NameValueCollection.
                                        NormalizeCommonHeaders(); 
                                        AddInternalNotCommon(s, sValue);
                                    }

                                    sName = s; 
                                    break;
                                } 
                            } 
                        }
                    } 

                    // If it wasn't a common header, add it to the hash.
                    if (sName == null)
                    { 
                        sName = HeaderEncoding.GetString(byteBuffer + iBeginName, headerNameLength);
                        AddInternalNotCommon(sName, sValue); 
                    } 

                    totalResponseHeadersLength += i - unparsed; 
                    unparsed = i;
                }
            }
 
quit:
            GlobalLog.Leave("WebHeaderCollection::ParseHeadersStrict() returning parseStatus:" + parseStatus.ToString()); 
 
            if (parseStatus == DataParseStatus.Invalid) {
                parseError.Section = WebParseErrorSection.ResponseHeader; 
                parseError.Code    = parseErrorCode;
            }

            return parseStatus; 
        }
 
        // 
        // Keeping this version for backwards compatibility (mostly with reflection).  Remove some day, along with the interface
        // explicit reimplementation. 
        //
        /// 
        [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase", Justification = "System.dll is still using pre-v4 security model and needs this demand")]
        [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.SerializationFormatter, SerializationFormatter=true)] 
        void ISerializable.GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext)
        { 
            GetObjectData(serializationInfo, streamingContext); 
        }
 
        // Override Get() to check the common headers.
        public override string Get(string name)
        {
            // In this case, need to make sure name doesn't have any Unicode since it's being used as an index into tables. 
            if (m_CommonHeaders != null && name != null && name.Length > 0 && name[0] < 256)
            { 
                int iHeader = s_CommonHeaderHints[name[0] & 0x1f]; 
                if (iHeader >= 0)
                { 
                    while (true)
                    {
                        string s = s_CommonHeaderNames[iHeader++];
 
                        // Not found if we get to a shorter header or one with a different first character.
                        if (s.Length < name.Length || CaseInsensitiveAscii.AsciiToLower[name[0]] != CaseInsensitiveAscii.AsciiToLower[s[0]]) 
                            break; 

                        // Keep looking if the common header is too long. 
                        if (s.Length > name.Length)
                            continue;

                        int j; 
                        for (j = 1; j < s.Length; j++)
                        { 
                            // Avoid the case-insensitive compare in the common case where they match. 
                            if (name[j] != s[j] && (name[j] > 255 || CaseInsensitiveAscii.AsciiToLower[name[j]] != CaseInsensitiveAscii.AsciiToLower[s[j]]))
                                break; 
                        }
                        if (j == s.Length)
                        {
                            // Get the appropriate header. 
                            return m_CommonHeaders[iHeader - 1];
                        } 
                    } 
                }
            } 

            // Fall back to normal lookup.
            if (m_InnerCollection == null)
                return null; 
            return m_InnerCollection.Get(name);
        } 
 

        // 
        // Additional overrides required to fully orphan the base implementation.
        //
        public override IEnumerator GetEnumerator()
        { 
            NormalizeCommonHeaders();
            return new NameObjectKeysEnumerator(InnerCollection); 
        } 

        public override int Count 
        {
            get
            {
                return (m_InnerCollection == null ? 0 : m_InnerCollection.Count) + m_NumCommonHeaders; 
            }
        } 
 
        public override KeysCollection Keys
        { 
            get
            {
                NormalizeCommonHeaders();
                return InnerCollection.Keys; 
            }
        } 
 
        internal override bool InternalHasKeys()
        { 
            NormalizeCommonHeaders();
            if (m_InnerCollection == null)
                return false;
            return m_InnerCollection.HasKeys(); 
        }
 
        public override string Get(int index) 
        {
            NormalizeCommonHeaders(); 
            return InnerCollection.Get(index);
        }

        public override string[] GetValues(int index) 
        {
            NormalizeCommonHeaders(); 
            return InnerCollection.GetValues(index); 
        }
 
        public override string GetKey(int index)
        {
            NormalizeCommonHeaders();
            return InnerCollection.GetKey(index); 
        }
 
        public override string[] AllKeys 
        {
            get 
            {
                NormalizeCommonHeaders();
                return InnerCollection.AllKeys;
            } 
        }
 
        public override void Clear() 
        {
            m_CommonHeaders = null; 
            m_NumCommonHeaders = 0;
            InvalidateCachedArrays();
            if (m_InnerCollection != null)
                m_InnerCollection.Clear(); 
        }
    } // class WebHeaderCollection 
 

    internal class CaseInsensitiveAscii : IEqualityComparer, IComparer{ 
        // ASCII char ToLower table
        internal static readonly CaseInsensitiveAscii StaticInstance = new CaseInsensitiveAscii();
        internal static readonly byte[] AsciiToLower = new byte[] {
              0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 
             10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
             20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 
             30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 
             40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
             50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 
             60, 61, 62, 63, 64, 97, 98, 99,100,101, //  60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
            102,103,104,105,106,107,108,109,110,111, //  70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
            112,113,114,115,116,117,118,119,120,121, //  80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
            122, 91, 92, 93, 94, 95, 96, 97, 98, 99, //  90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 
            100,101,102,103,104,105,106,107,108,109,
            110,111,112,113,114,115,116,117,118,119, 
            120,121,122,123,124,125,126,127,128,129, 
            130,131,132,133,134,135,136,137,138,139,
            140,141,142,143,144,145,146,147,148,149, 
            150,151,152,153,154,155,156,157,158,159,
            160,161,162,163,164,165,166,167,168,169,
            170,171,172,173,174,175,176,177,178,179,
            180,181,182,183,184,185,186,187,188,189, 
            190,191,192,193,194,195,196,197,198,199,
            200,201,202,203,204,205,206,207,208,209, 
            210,211,212,213,214,215,216,217,218,219, 
            220,221,222,223,224,225,226,227,228,229,
            230,231,232,233,234,235,236,237,238,239, 
            240,241,242,243,244,245,246,247,248,249,
            250,251,252,253,254,255
        };
 
        // ASCII string case insensitive hash function
        public int GetHashCode(object myObject) { 
            string myString = myObject as string; 
            if (myObject == null) {
                return 0; 
            }
            int myHashCode = myString.Length;
            if (myHashCode == 0) {
                return 0; 
            }
            myHashCode ^= AsciiToLower[(byte)myString[0]]<<24 ^ AsciiToLower[(byte)myString[myHashCode-1]]<<16; 
            return myHashCode; 
        }
 
        // ASCII string case insensitive comparer
        public int Compare(object firstObject, object secondObject) {
            string firstString = firstObject as string;
            string secondString = secondObject as string; 
            if (firstString==null) {
                return secondString == null ? 0 : -1; 
            } 
            if (secondString == null) {
                return 1; 
            }
            int result = firstString.Length - secondString.Length;
            int comparisons = result > 0 ? secondString.Length : firstString.Length;
            int difference, index = 0; 
            while ( index < comparisons ) {
                difference = (int)(AsciiToLower[ firstString[index] ] - AsciiToLower[ secondString[index] ]); 
                if ( difference != 0 ) { 
                    result = difference;
                    break; 
                }
                index++;
            }
            return result; 
        }
 
        // ASCII string case insensitive hash function 
        int FastGetHashCode(string myString) {
            int myHashCode = myString.Length; 
            if (myHashCode!=0) {
                myHashCode ^= AsciiToLower[(byte)myString[0]]<<24 ^ AsciiToLower[(byte)myString[myHashCode-1]]<<16;
            }
            return myHashCode; 
        }
 
        // ASCII string case insensitive comparer 
        public new bool Equals(object firstObject, object secondObject) {
            string firstString = firstObject as string; 
            string secondString = secondObject as string;
            if (firstString==null) {
                return secondString==null;
            } 
            if (secondString!=null) {
                int index = firstString.Length; 
                if (index==secondString.Length) { 
                    if (FastGetHashCode(firstString)==FastGetHashCode(secondString)) {
                        int comparisons = firstString.Length; 
                        while (index>0) {
                            index--;
                            if (AsciiToLower[firstString[index]]!=AsciiToLower[secondString[index]]) {
                                return false; 
                            }
                        } 
                        return true; 
                    }
                } 
            }
            return false;
        }
    } 

    internal class HostHeaderString { 
 
        private bool m_Converted;
        private string m_String; 
        private byte[] m_Bytes;

        internal HostHeaderString() {
            Init(null); 
        }
 
        internal HostHeaderString(string s) { 
            Init(s);
        } 

        private void Init(string s) {
            m_String = s;
            m_Converted = false; 
            m_Bytes = null;
        } 
 
        private void Convert() {
            if (m_String != null && !m_Converted) { 
                m_Bytes = Encoding.Default.GetBytes(m_String);
                string copy = Encoding.Default.GetString(m_Bytes);
                if (!(string.Compare(m_String, copy, StringComparison.Ordinal) == 0)) {
                    m_Bytes = Encoding.UTF8.GetBytes(m_String); 
                }
            } 
        } 

        internal string String { 
            get { return m_String; }
            set {
                Init(value);
            } 
        }
 
        internal int ByteCount { 
            get {
                Convert(); 
                return m_Bytes.Length;
            }
        }
 
        internal byte[] Bytes {
            get { 
                Convert(); 
                return m_Bytes;
            } 
        }

        internal void Copy(byte[] destBytes, int destByteIndex) {
            Convert(); 
            Array.Copy(m_Bytes, 0, destBytes, destByteIndex, m_Bytes.Length);
        } 
 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

namespace System.Net { 
    using System.Net.Cache; 
    using System.Collections;
    using System.Collections.Specialized; 
    using System.Text;
    using System.Runtime.InteropServices;
    using System.Runtime.Serialization;
    using System.Globalization; 
    using System.Security.Permissions;
    using System.Diagnostics.CodeAnalysis; 
 
    internal enum WebHeaderCollectionType : ushort {
        Unknown, 
        WebRequest,
        WebResponse,
        HttpWebRequest,
        HttpWebResponse, 
        HttpListenerRequest,
        HttpListenerResponse, 
        FtpWebRequest, 
        FtpWebResponse,
        FileWebRequest, 
        FileWebResponse,
    }

    // 
    // HttpHeaders - this is our main HttpHeaders object,
    //  which is a simple collection of name-value pairs, 
    //  along with additional methods that provide HTTP parsing 
    //  collection to sendable buffer capablities and other enhansments
    //  We also provide validation of what headers are allowed to be added. 
    //

    /// 
    ///     
    ///       Contains protocol headers associated with a
    ///       request or response. 
    ///     
    /// 
    [ComVisible(true), Serializable] 
    public class WebHeaderCollection : NameValueCollection, ISerializable {
        //
        // Data and Constants
        // 
        private const int ApproxAveHeaderLineSize = 30;
        private const int ApproxHighAvgNumHeaders = 16; 
        private static readonly HeaderInfoTable HInfo = new HeaderInfoTable(); 

        // 
        // Common Headers - used only when receiving a response, and internally.  If the user ever requests a header,
        // all the common headers are moved into the hashtable.
        //
        private string[] m_CommonHeaders; 
        private int m_NumCommonHeaders;
 
        // Grouped by first character, so lookup is faster.  The table s_CommonHeaderHints maps first letters to indexes in this array. 
        // After first character, sort by decreasing length.  It's ok if two headers have the same first character and length.
        private static readonly string[] s_CommonHeaderNames = new string[] { 
            HttpKnownHeaderNames.AcceptRanges,      // "Accept-Ranges"       13
            HttpKnownHeaderNames.ContentLength,     // "Content-Length"      14
            HttpKnownHeaderNames.CacheControl,      // "Cache-Control"       13
            HttpKnownHeaderNames.ContentType,       // "Content-Type"        12 
            HttpKnownHeaderNames.Date,              // "Date"                 4
            HttpKnownHeaderNames.Expires,           // "Expires"              7 
            HttpKnownHeaderNames.ETag,              // "ETag"                 4 
            HttpKnownHeaderNames.LastModified,      // "Last-Modified"       13
            HttpKnownHeaderNames.Location,          // "Location"             8 
            HttpKnownHeaderNames.ProxyAuthenticate, // "Proxy-Authenticate"  18
            HttpKnownHeaderNames.P3P,               // "P3P"                  3
            HttpKnownHeaderNames.SetCookie2,        // "Set-Cookie2"         11
            HttpKnownHeaderNames.SetCookie,         // "Set-Cookie"          10 
            HttpKnownHeaderNames.Server,            // "Server"               6
            HttpKnownHeaderNames.Via,               // "Via"                  3 
            HttpKnownHeaderNames.WWWAuthenticate,   // "WWW-Authenticate"    16 
            HttpKnownHeaderNames.XAspNetVersion,    // "X-AspNet-Version"    16
            HttpKnownHeaderNames.XPoweredBy,        // "X-Powered-By"        12 
            "[" };  // This sentinel will never match.  (This character isn't in the hint table.)

        // Mask off all but the bottom five bits, and look up in this array.
        private static readonly sbyte[] s_CommonHeaderHints = new sbyte[] { 
            -1,  0, -1,  1,  4,  5, -1, -1,   // - a b c d e f g
            -1, -1, -1, -1,  7, -1, -1, -1,   // h i j k l m n o 
             9, -1, -1, 11, -1, -1, 14, 15,   // p q r s t u v w 
            16, -1, -1, -1, -1, -1, -1, -1 }; // x y z [ - - - -
 
        private const int c_AcceptRanges      =  0;
        private const int c_ContentLength     =  1;
        private const int c_CacheControl      =  2;
        private const int c_ContentType       =  3; 
        private const int c_Date              =  4;
        private const int c_Expires           =  5; 
        private const int c_ETag              =  6; 
        private const int c_LastModified      =  7;
        private const int c_Location          =  8; 
        private const int c_ProxyAuthenticate =  9;
        private const int c_P3P               = 10;
        private const int c_SetCookie2        = 11;
        private const int c_SetCookie         = 12; 
        private const int c_Server            = 13;
        private const int c_Via               = 14; 
        private const int c_WwwAuthenticate   = 15; 
        private const int c_XAspNetVersion    = 16;
        private const int c_XPoweredBy        = 17; 

        // Easy fast lookups for common headers.  More can be added.
        internal string ContentLength
        { 
            get
            { 
                return m_CommonHeaders != null ? m_CommonHeaders[c_ContentLength] : Get(s_CommonHeaderNames[c_ContentLength]); 
            }
        } 

        internal string CacheControl
        {
            get 
            {
                return m_CommonHeaders != null ? m_CommonHeaders[c_CacheControl] : Get(s_CommonHeaderNames[c_CacheControl]); 
            } 
        }
 
        internal string ContentType
        {
            get
            { 
                return m_CommonHeaders != null ? m_CommonHeaders[c_ContentType] : Get(s_CommonHeaderNames[c_ContentType]);
            } 
        } 

        internal string Date 
        {
            get
            {
                return m_CommonHeaders != null ? m_CommonHeaders[c_Date] : Get(s_CommonHeaderNames[c_Date]); 
            }
        } 
 
        internal string Expires
        { 
            get
            {
                return m_CommonHeaders != null ? m_CommonHeaders[c_Expires] : Get(s_CommonHeaderNames[c_Expires]);
            } 
        }
 
        internal string ETag 
        {
            get 
            {
                return m_CommonHeaders != null ? m_CommonHeaders[c_ETag] : Get(s_CommonHeaderNames[c_ETag]);
            }
        } 

        internal string LastModified 
        { 
            get
            { 
                return m_CommonHeaders != null ? m_CommonHeaders[c_LastModified] : Get(s_CommonHeaderNames[c_LastModified]);
            }
        }
 
        internal string Location
        { 
            get 
            {
                return m_CommonHeaders != null ? m_CommonHeaders[c_Location] : Get(s_CommonHeaderNames[c_Location]); 
            }
        }

        internal string ProxyAuthenticate 
        {
            get 
            { 
                return m_CommonHeaders != null ? m_CommonHeaders[c_ProxyAuthenticate] : Get(s_CommonHeaderNames[c_ProxyAuthenticate]);
            } 
        }

        internal string SetCookie2
        { 
            get
            { 
                return m_CommonHeaders != null ? m_CommonHeaders[c_SetCookie2] : Get(s_CommonHeaderNames[c_SetCookie2]); 
            }
        } 

        internal string SetCookie
        {
            get 
            {
                return m_CommonHeaders != null ? m_CommonHeaders[c_SetCookie] : Get(s_CommonHeaderNames[c_SetCookie]); 
            } 
        }
 
        internal string Server
        {
            get
            { 
                return m_CommonHeaders != null ? m_CommonHeaders[c_Server] : Get(s_CommonHeaderNames[c_Server]);
            } 
        } 

        internal string Via 
        {
            get
            {
                return m_CommonHeaders != null ? m_CommonHeaders[c_Via] : Get(s_CommonHeaderNames[c_Via]); 
            }
        } 
 
        private void NormalizeCommonHeaders()
        { 
            if (m_CommonHeaders == null)
                return;
            for (int i = 0; i < m_CommonHeaders.Length; i++)
                if (m_CommonHeaders[i] != null) 
                    InnerCollection.Add(s_CommonHeaderNames[i], m_CommonHeaders[i]);
 
            m_CommonHeaders = null; 
            m_NumCommonHeaders = 0;
        } 

        //
        // To ensure C++ and IL callers can't pollute the underlying collection by calling overridden base members directly, we
        // will use a member collection instead. 
        private NameValueCollection m_InnerCollection;
 
        private NameValueCollection InnerCollection 
        {
            get 
            {
                if (m_InnerCollection == null)
                    m_InnerCollection = new NameValueCollection(ApproxHighAvgNumHeaders, CaseInsensitiveAscii.StaticInstance);
                return m_InnerCollection; 
            }
        } 
 
        // this is the object that created the header collection.
        private WebHeaderCollectionType m_Type; 

#if !FEATURE_PAL
        private bool AllowHttpRequestHeader {
            get { 
                if (m_Type==WebHeaderCollectionType.Unknown) {
                    m_Type = WebHeaderCollectionType.WebRequest; 
                } 
                return m_Type==WebHeaderCollectionType.WebRequest || m_Type==WebHeaderCollectionType.HttpWebRequest || m_Type==WebHeaderCollectionType.HttpListenerRequest;
            } 
        }

        internal bool AllowHttpResponseHeader {
            get { 
                if (m_Type==WebHeaderCollectionType.Unknown) {
                    m_Type = WebHeaderCollectionType.WebResponse; 
                } 
                return m_Type==WebHeaderCollectionType.WebResponse || m_Type==WebHeaderCollectionType.HttpWebResponse || m_Type==WebHeaderCollectionType.HttpListenerResponse;
            } 
        }

        public string this[HttpRequestHeader header] {
            get { 
                if (!AllowHttpRequestHeader) {
                    throw new InvalidOperationException(SR.GetString(SR.net_headers_req)); 
                } 
                return this[UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_HEADER_ID.ToString((int)header)];
            } 
            set {
                if (!AllowHttpRequestHeader) {
                    throw new InvalidOperationException(SR.GetString(SR.net_headers_req));
                } 
                this[UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_HEADER_ID.ToString((int)header)] = value;
            } 
        } 
        public string this[HttpResponseHeader header] {
            get { 
                if (!AllowHttpResponseHeader) {
                    throw new InvalidOperationException(SR.GetString(SR.net_headers_rsp));
                }
 
                // Some of these can be mapped to Common Headers.  Other cases can be added as needed for perf.
                if (m_CommonHeaders != null) 
                { 
                    switch (header)
                    { 
                        case HttpResponseHeader.ProxyAuthenticate:
                            return m_CommonHeaders[c_ProxyAuthenticate];

                        case HttpResponseHeader.WwwAuthenticate: 
                            return m_CommonHeaders[c_WwwAuthenticate];
                    } 
                } 

                return this[UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_HEADER_ID.ToString((int)header)]; 
            }
            set {
                if (!AllowHttpResponseHeader) {
                    throw new InvalidOperationException(SR.GetString(SR.net_headers_rsp)); 
                }
                if (m_Type==WebHeaderCollectionType.HttpListenerResponse) { 
                    if (value!=null && value.Length>ushort.MaxValue) { 
                        throw new ArgumentOutOfRangeException("value", value, SR.GetString(SR.net_headers_toolong, ushort.MaxValue));
                    } 
                }
                this[UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_HEADER_ID.ToString((int)header)] = value;
            }
        } 

        public void Add(HttpRequestHeader header, string value) { 
            if (!AllowHttpRequestHeader) { 
                throw new InvalidOperationException(SR.GetString(SR.net_headers_req));
            } 
            this.Add(UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_HEADER_ID.ToString((int)header), value);
        }

        public void Add(HttpResponseHeader header, string value) { 
            if (!AllowHttpResponseHeader) {
                throw new InvalidOperationException(SR.GetString(SR.net_headers_rsp)); 
            } 
            if (m_Type==WebHeaderCollectionType.HttpListenerResponse) {
                if (value!=null && value.Length>ushort.MaxValue) { 
                    throw new ArgumentOutOfRangeException("value", value, SR.GetString(SR.net_headers_toolong, ushort.MaxValue));
                }
            }
            this.Add(UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_HEADER_ID.ToString((int)header), value); 
        }
 
        public void Set(HttpRequestHeader header, string value) { 
            if (!AllowHttpRequestHeader) {
                throw new InvalidOperationException(SR.GetString(SR.net_headers_req)); 
            }
            this.Set(UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_HEADER_ID.ToString((int)header), value);
        }
 
        public void Set(HttpResponseHeader header, string value) {
            if (!AllowHttpResponseHeader) { 
                throw new InvalidOperationException(SR.GetString(SR.net_headers_rsp)); 
            }
            if (m_Type==WebHeaderCollectionType.HttpListenerResponse) { 
                if (value!=null && value.Length>ushort.MaxValue) {
                    throw new ArgumentOutOfRangeException("value", value, SR.GetString(SR.net_headers_toolong, ushort.MaxValue));
                }
            } 
            this.Set(UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_HEADER_ID.ToString((int)header), value);
        } 
 

        internal void SetInternal(HttpResponseHeader header, string value) { 
            if (!AllowHttpResponseHeader) {
                throw new InvalidOperationException(SR.GetString(SR.net_headers_rsp));
            }
            if (m_Type==WebHeaderCollectionType.HttpListenerResponse) { 
                if (value!=null && value.Length>ushort.MaxValue) {
                    throw new ArgumentOutOfRangeException("value", value, SR.GetString(SR.net_headers_toolong, ushort.MaxValue)); 
                } 
            }
            this.SetInternal(UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_HEADER_ID.ToString((int)header), value); 
        }


        public void Remove(HttpRequestHeader header) { 
            if (!AllowHttpRequestHeader) {
                throw new InvalidOperationException(SR.GetString(SR.net_headers_req)); 
            } 
            this.Remove(UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_HEADER_ID.ToString((int)header));
        } 

        public void Remove(HttpResponseHeader header) {
            if (!AllowHttpResponseHeader) {
                throw new InvalidOperationException(SR.GetString(SR.net_headers_rsp)); 
            }
            this.Remove(UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_HEADER_ID.ToString((int)header)); 
        } 
#endif // !FEATURE_PAL
 
        // In general, HttpWebResponse headers aren't modified, so these methods don't support common headers.

        /// 
        ///    [To be supplied.] 
        /// 
        protected void AddWithoutValidate(string headerName, string headerValue) { 
            headerName = CheckBadChars(headerName, false); 
            headerValue = CheckBadChars(headerValue, true);
            GlobalLog.Print("WebHeaderCollection::AddWithoutValidate() calling InnerCollection.Add() key:[" + headerName + "], value:[" + headerValue + "]"); 
            if (m_Type==WebHeaderCollectionType.HttpListenerResponse) {
                if (headerValue!=null && headerValue.Length>ushort.MaxValue) {
                    throw new ArgumentOutOfRangeException("headerValue", headerValue, SR.GetString(SR.net_headers_toolong, ushort.MaxValue));
                } 
            }
            NormalizeCommonHeaders(); 
            InvalidateCachedArrays(); 
            InnerCollection.Add(headerName, headerValue);
        } 

        internal void SetAddVerified(string name, string value) {
            if(HInfo[name].AllowMultiValues) {
                GlobalLog.Print("WebHeaderCollection::SetAddVerified() calling InnerCollection.Add() key:[" + name + "], value:[" + value + "]"); 
                NormalizeCommonHeaders();
                InvalidateCachedArrays(); 
                InnerCollection.Add(name, value); 
            }
            else { 
                GlobalLog.Print("WebHeaderCollection::SetAddVerified() calling InnerCollection.Set() key:[" + name + "], value:[" + value + "]");
                NormalizeCommonHeaders();
                InvalidateCachedArrays();
                InnerCollection.Set(name, value); 
            }
        } 
 
        // Below three methods are for fast headers manipulation, bypassing all the checks
        internal void AddInternal(string name, string value) { 
            GlobalLog.Print("WebHeaderCollection::AddInternal() calling InnerCollection.Add() key:[" + name + "], value:[" + value + "]");
            NormalizeCommonHeaders();
            InvalidateCachedArrays();
            InnerCollection.Add(name, value); 
        }
 
        internal void ChangeInternal(string name, string value) { 
            GlobalLog.Print("WebHeaderCollection::ChangeInternal() calling InnerCollection.Set() key:[" + name + "], value:[" + value + "]");
            NormalizeCommonHeaders(); 
            InvalidateCachedArrays();
            InnerCollection.Set(name, value);
        }
 

        internal void RemoveInternal(string name) { 
            GlobalLog.Print("WebHeaderCollection::RemoveInternal() calling InnerCollection.Remove() key:[" + name + "]"); 
            NormalizeCommonHeaders();
            if (m_InnerCollection != null) 
            {
                InvalidateCachedArrays();
                m_InnerCollection.Remove(name);
            } 
        }
 
        internal void CheckUpdate(string name, string value) { 
            value = CheckBadChars(value, true);
            ChangeInternal(name, value); 
        }

        // This even faster one can be used to add headers when it's known not to be a common header or that common headers aren't active.
        private void AddInternalNotCommon(string name, string value) 
        {
            GlobalLog.Print("WebHeaderCollection::AddInternalNotCommon() calling InnerCollection.Add() key:[" + name + "], value:[" + value + "]"); 
            InvalidateCachedArrays(); 
            InnerCollection.Add(name, value);
        } 


        private static readonly char[] HttpTrimCharacters = new char[]{(char)0x09,(char)0xA,(char)0xB,(char)0xC,(char)0xD,(char)0x20};
 
        //
        // CheckBadChars - throws on invalid chars to be not found in header name/value 
        // 
        internal static string CheckBadChars(string name, bool isHeaderValue) {
 
            if (name == null || name.Length == 0) {
                // emtpy name is invlaid
                if (!isHeaderValue) {
                    throw name == null ? new ArgumentNullException("name") : 
                        new ArgumentException(SR.GetString(SR.net_emptystringcall, "name"), "name");
                } 
                //empty value is OK 
                return string.Empty;
            } 

            if (isHeaderValue) {
                // VALUE check
                //Trim spaces from both ends 
                name = name.Trim(HttpTrimCharacters);
 
                //First, check for correctly formed multi-line value 
                //Second, check for absenece of CTL characters
                int crlf = 0; 
                for(int i = 0; i < name.Length; ++i) {
                    char c = (char) (0x000000ff & (uint) name[i]);
                    switch (crlf)
                    { 
                        case 0:
                            if (c == '\r') 
                            { 
                                crlf = 1;
                            } 
                            else if (c == '\n')
                            {
                                // Technically this is bad HTTP.  But it would be a breaking change to throw here.
                                // Is there an exploit? 
                                crlf = 2;
                            } 
                            else if (c == 127 || (c < ' ' && c != '\t')) 
                            {
                                throw new ArgumentException(SR.GetString(SR.net_WebHeaderInvalidControlChars), "value"); 
                            }
                            break;

                        case 1: 
                            if (c == '\n')
                            { 
                                crlf = 2; 
                                break;
                            } 
                            throw new ArgumentException(SR.GetString(SR.net_WebHeaderInvalidCRLFChars), "value");

                        case 2:
                            if (c == ' ' || c == '\t') 
                            {
                                crlf = 0; 
                                break; 
                            }
                            throw new ArgumentException(SR.GetString(SR.net_WebHeaderInvalidCRLFChars), "value"); 
                    }
                }
                if (crlf != 0)
                { 
                    throw new ArgumentException(SR.GetString(SR.net_WebHeaderInvalidCRLFChars), "value");
                } 
            } 
            else {
                // NAME check 
                //First, check for absence of separators and spaces
                if (name.IndexOfAny(ValidationHelper.InvalidParamChars) != -1) {
                    throw new ArgumentException(SR.GetString(SR.net_WebHeaderInvalidHeaderChars), "name");
                } 

                //Second, check for non CTL ASCII-7 characters (32-126) 
                if (ContainsNonAsciiChars(name)) { 
                    throw new ArgumentException(SR.GetString(SR.net_WebHeaderInvalidNonAsciiChars), "name");
                } 
            }
            return name;
        }
 
        internal static bool IsValidToken(string token) {
            return (token.Length > 0) 
                && (token.IndexOfAny(ValidationHelper.InvalidParamChars) == -1) 
                && !ContainsNonAsciiChars(token);
        } 

        internal static bool ContainsNonAsciiChars(string token) {
            for (int i = 0; i < token.Length; ++i) {
                if ((token[i] < 0x20) || (token[i] > 0x7e)) { 
                    return true;
                } 
            } 
            return false;
        } 

        //
        // ThrowOnRestrictedHeader - generates an error if the user,
        //  passed in a reserved string as the header name 
        //
        internal void ThrowOnRestrictedHeader(string headerName) 
        { 
            if (m_Type == WebHeaderCollectionType.HttpWebRequest)
            { 
                if (HInfo[headerName].IsRequestRestricted)
                {
                    throw new ArgumentException(!Equals(headerName, HttpKnownHeaderNames.Host) ? SR.GetString(SR.net_headerrestrict) :
                        SR.GetString(SR.net_headerrestrict_resp, HttpKnownHeaderNames.Host), "name"); 
                }
            } 
            else if (m_Type == WebHeaderCollectionType.HttpListenerResponse) 
            {
                if (HInfo[headerName].IsResponseRestricted) 
                {
                    throw new ArgumentException(SR.GetString(SR.net_headerrestrict_resp, headerName), "name");
                }
            } 
        }
 
        // 
        // Our Public METHOD set, most are inherited from NameValueCollection,
        //  not all methods from NameValueCollection are listed, even though usable - 
        //
        //  this includes
        //  Add(name, value)
        //  Add(header) 
        //  this[name] {set, get}
        //  Remove(name), returns bool 
        //  Remove(name), returns void 
        //  Set(name, value)
        //  ToString() 
        //
        //  SplitValue(name, value)
        //  ToByteArray()
        //  ParseHeaders(char [], ...) 
        //  ParseHeaders(byte [], ...)
        // 
 
        // Add more headers; if "name" already exists it will
        // add concatenated value 


        // Add -
        //  Routine Description: 
        //      Adds headers with validation to see if they are "proper" headers.
        //      Will cause header to be concat to existing if already found. 
        //      If the header is a special header, listed in RestrictedHeaders object, 
        //      then this call will cause an exception indication as such.
        //  Arguments: 
        //      name - header-name to add
        //      value - header-value to add, a header is already there, will concat this value
        //  Return Value:
        //      None 

        ///  
        ///     
        ///       Adds a new header with the indicated name and value.
        ///     
        /// 
        public override void Add(string name, string value) {
            name = CheckBadChars(name, false);
            ThrowOnRestrictedHeader(name); 
            value = CheckBadChars(value, true);
            GlobalLog.Print("WebHeaderCollection::Add() calling InnerCollection.Add() key:[" + name + "], value:[" + value + "]"); 
            if (m_Type==WebHeaderCollectionType.HttpListenerResponse) { 
                if (value!=null && value.Length>ushort.MaxValue) {
                    throw new ArgumentOutOfRangeException("value", value, SR.GetString(SR.net_headers_toolong, ushort.MaxValue)); 
                }
            }
            NormalizeCommonHeaders();
            InvalidateCachedArrays(); 
            InnerCollection.Add(name, value);
        } 
 

        // Add - 
        // Routine Description:
        //     Adds headers with validation to see if they are "proper" headers.
        //     Assumes a combined a "Name: Value" string, and parses the two parts out.
        //     Will cause header to be concat to existing if already found. 
        //     If the header is a speical header, listed in RestrictedHeaders object,
        //     then this call will cause an exception indication as such. 
        // Arguments: 
        //     header - header name: value pair
        // Return Value: 
        //     None

        /// 
        ///     
        ///       Adds the indicated header.
        ///     
        ///  
        public void Add(string header) {
            if ( ValidationHelper.IsBlankString(header) ) { 
                throw new ArgumentNullException("header");
            }
            int colpos = header.IndexOf(':');
            // check for badly formed header passed in 
            if (colpos<0) {
                throw new ArgumentException(SR.GetString(SR.net_WebHeaderMissingColon), "header"); 
            } 
            string name = header.Substring(0, colpos);
            string value = header.Substring(colpos+1); 
            name = CheckBadChars(name, false);
            ThrowOnRestrictedHeader(name);
            value = CheckBadChars(value, true);
            GlobalLog.Print("WebHeaderCollection::Add(" + header + ") calling InnerCollection.Add() key:[" + name + "], value:[" + value + "]"); 
            if (m_Type==WebHeaderCollectionType.HttpListenerResponse) {
                if (value!=null && value.Length>ushort.MaxValue) { 
                    throw new ArgumentOutOfRangeException("value", value, SR.GetString(SR.net_headers_toolong, ushort.MaxValue)); 
                }
            } 
            NormalizeCommonHeaders();
            InvalidateCachedArrays();
            InnerCollection.Add(name, value);
        } 

        // Set - 
        // Routine Description: 
        //     Sets headers with validation to see if they are "proper" headers.
        //     If the header is a special header, listed in RestrictedHeaders object, 
        //     then this call will cause an exception indication as such.
        // Arguments:
        //     name - header-name to set
        //     value - header-value to set 
        // Return Value:
        //     None 
 
        /// 
        ///     
        ///       Sets the specified header to the specified value.
        ///    
        /// 
        public override void Set(string name, string value) { 
            if (ValidationHelper.IsBlankString(name)) {
                throw new ArgumentNullException("name"); 
            } 
            name = CheckBadChars(name, false);
            ThrowOnRestrictedHeader(name); 
            value = CheckBadChars(value, true);
            GlobalLog.Print("WebHeaderCollection::Set() calling InnerCollection.Set() key:[" + name + "], value:[" + value + "]");
            if (m_Type==WebHeaderCollectionType.HttpListenerResponse) {
                if (value!=null && value.Length>ushort.MaxValue) { 
                    throw new ArgumentOutOfRangeException("value", value, SR.GetString(SR.net_headers_toolong, ushort.MaxValue));
                } 
            } 
            NormalizeCommonHeaders();
            InvalidateCachedArrays(); 
            InnerCollection.Set(name, value);
        }

 
        internal void SetInternal(string name, string value) {
            if (ValidationHelper.IsBlankString(name)) { 
                throw new ArgumentNullException("name"); 
            }
            name = CheckBadChars(name, false); 
            value = CheckBadChars(value, true);
            GlobalLog.Print("WebHeaderCollection::Set() calling InnerCollection.Set() key:[" + name + "], value:[" + value + "]");
            if (m_Type==WebHeaderCollectionType.HttpListenerResponse) {
                if (value!=null && value.Length>ushort.MaxValue) { 
                    throw new ArgumentOutOfRangeException("value", value, SR.GetString(SR.net_headers_toolong, ushort.MaxValue));
                } 
            } 
            NormalizeCommonHeaders();
            InvalidateCachedArrays(); 
            InnerCollection.Set(name, value);
        }

 
        // Remove -
        // Routine Description: 
        //     Removes give header with validation to see if they are "proper" headers. 
        //     If the header is a speical header, listed in RestrictedHeaders object,
        //     then this call will cause an exception indication as such. 
        // Arguments:
        //     name - header-name to remove
        // Return Value:
        //     None 

        ///  
        ///    Removes the specified header. 
        /// 
        public override void Remove(string name) { 
            if ( ValidationHelper.IsBlankString(name) ) {
                throw new ArgumentNullException("name");
            }
            ThrowOnRestrictedHeader(name); 
            name = CheckBadChars(name,  false);
            GlobalLog.Print("WebHeaderCollection::Remove() calling InnerCollection.Remove() key:[" + name + "]"); 
            NormalizeCommonHeaders(); 
            if (m_InnerCollection != null)
            { 
                InvalidateCachedArrays();
                m_InnerCollection.Remove(name);
            }
        } 

 
        // GetValues 
        // Routine Description:
        //     This method takes a header name and returns a string array representing 
        //     the individual values for that headers. For example, if the headers
        //     contained the line Accept: text/plain, text/html then
        //     GetValues("Accept") would return an array of two strings: "text/plain"
        //     and "text/html". 
        // Arguments:
        //     header      - Name of the header. 
        // Return Value: 
        //     string[] - array of parsed string objects
 
        /// 
        ///    
        ///       Gets an array of header values stored in a
        ///       header. 
        ///    
        ///  
        public override string[] GetValues(string header) { 
            // This method doesn't work with common headers.  Dump common headers into the pool.
            NormalizeCommonHeaders(); 

            // First get the information about the header and the values for
            // the header.
            HeaderInfo Info = HInfo[header]; 
            string[] Values = InnerCollection.GetValues(header);
            // If we have no information about the header or it doesn't allow 
            // multiple values, just return the values. 
            if (Info == null || Values == null || !Info.AllowMultiValues) {
                return Values; 
            }
            // Here we have a multi value header. We need to go through
            // each entry in the multi values array, and if an entry itself
            // has multiple values we'll need to combine those in. 
            //
            // We do some optimazation here, where we try not to copy the 
            // values unless there really is one that have multiple values. 
            string[] TempValues;
            ArrayList ValueList = null; 
            int i;
            for (i = 0; i < Values.Length; i++) {
                // Parse this value header.
                TempValues = Info.Parser(Values[i]); 
                // If we don't have an array list yet, see if this
                // value has multiple values. 
                if (ValueList == null) { 
                    // See if it has multiple values.
                    if (TempValues.Length > 1) { 
                        // It does, so we need to create an array list that
                        // represents the Values, then trim out this one and
                        // the ones after it that haven't been parsed yet.
                        ValueList = new ArrayList(Values); 
                        ValueList.RemoveRange(i, Values.Length - i);
                        ValueList.AddRange(TempValues); 
                    } 
                }
                else { 
                    // We already have an ArrayList, so just add the values.
                    ValueList.AddRange(TempValues);
                }
            } 
            // See if we have an ArrayList. If we don't, just return the values.
            // Otherwise convert the ArrayList to a string array and return that. 
            if (ValueList != null) { 
                string[] ReturnArray = new string[ValueList.Count];
                ValueList.CopyTo(ReturnArray); 
                return ReturnArray;
            }
            return Values;
        } 

 
        // ToString()  - 
        // Routine Description:
        //     Generates a string representation of the headers, that is ready to be sent except for it being in string format: 
        //     the format looks like:
        //
        //     Header-Name: Header-Value\r\n
        //     Header-Name2: Header-Value2\r\n 
        //     ...
        //     Header-NameN: Header-ValueN\r\n 
        //     \r\n 
        //
        //     Uses the string builder class to Append the elements together. 
        // Arguments:
        //     None.
        // Return Value:
        //     string 

        ///  
        ///  
        ///    
        ///       Obsolete. 
        ///    
        /// 
        public override string ToString() {
            string result = GetAsString(this, false, false); 
            GlobalLog.Print("WebHeaderCollection::ToString: \r\n" + result);
            return result; 
        } 

        internal string ToString(bool forTrace) 
        {
            return GetAsString(this, false, true);
        }
 

        // 
        // if winInetCompat = true then it will not insert spaces after ':' 
        // and it will output "~U" header first
        // 
        internal static string GetAsString(NameValueCollection cc,
                                           bool                winInetCompat,
                                           bool                forTrace) {
#if FEATURE_PAL 
            if (winInetCompat) {
                throw new InvalidOperationException(); 
            } 
#endif // FEATURE_PAL
 
            if (cc == null || cc.Count == 0) {
                return "\r\n";
            }
            StringBuilder sb = new StringBuilder(ApproxAveHeaderLineSize*cc.Count); 
            string statusLine;
            statusLine = cc[string.Empty]; 
            if (statusLine != null) { 
                sb.Append(statusLine).Append("\r\n");
            } 
            for (int i = 0; i < cc.Count ; i++) {
                string key = cc.GetKey(i) as string;
                string val = cc.Get(i) as string;
                /* 
                if (forTrace)
                { 
                    // Put a condition here that if we are using basic auth, 
                    // we shouldn't put the authorization header. Otherwise
                    // the password will get saved in the trace. 
                    if (using basic)
                        continue;
                }
                */ 
                if (ValidationHelper.IsBlankString(key)) {
                    continue; 
                } 
                sb.Append(key);
                if (winInetCompat) { 
                    sb.Append(':');
                }
                else {
                    sb.Append(": "); 
                }
                sb.Append(val).Append("\r\n"); 
            } 
            if (!forTrace)
                sb.Append("\r\n"); 
            return sb.ToString();
        }

 
        // ToByteArray()  -
        // Routine Description: 
        //     Generates a byte array representation of the headers, that is ready to be sent. 
        //     So it Serializes our headers into a byte array suitable for sending over the net.
        // 
        //     the format looks like:
        //
        //     Header-Name1: Header-Value1\r\n
        //     Header-Name2: Header-Value2\r\n 
        //     ...
        //     Header-NameN: Header-ValueN\r\n 
        //     \r\n 
        //
        //     Uses the ToString() method to generate, and then performs conversion. 
        //
        //     Performance Note:  Why are we not doing a single copy/covert run?
        //     As the code before used to know the size of the output!
        //     Because according to Demitry, its cheaper to copy the headers twice, 
        //     then it is to call the UNICODE to ANSI conversion code many times.
        // Arguments: 
        //     None. 
        // Return Value:
        //     byte [] - array of bytes values 

        /// 
        /// 
        ///     
        ///       Obsolete.
        ///     
        ///  
        public byte[] ToByteArray() {
            // Make sure the buffer is big enough. 
            string tempStr = ToString();
            //
            // Use the string of headers, convert to Char Array,
            //  then convert to Bytes, 
            //  serializing finally into the buffer, along the way.
            // 
            byte[] buffer = HeaderEncoding.GetBytes(tempStr); 
            return buffer;
        } 

        /// 
        ///    Tests if access to the HTTP header with the provided name is accessible for setting.
        ///  
        public static bool IsRestricted(string headerName)
        { 
            return IsRestricted(headerName, false); 
        }
 
        public static bool IsRestricted(string headerName, bool response)
        {
            return response ? HInfo[CheckBadChars(headerName, false)].IsResponseRestricted : HInfo[CheckBadChars(headerName, false)].IsRequestRestricted;
        } 

 
        ///  
        ///    
        ///       Initializes a new instance of the  
        ///       class.
        ///    
        /// 
        public WebHeaderCollection() : base(DBNull.Value) 
        {
        } 
 
        internal WebHeaderCollection(WebHeaderCollectionType type) : base(DBNull.Value)
        { 
            m_Type = type;
            if (type == WebHeaderCollectionType.HttpWebResponse)
                m_CommonHeaders = new string[s_CommonHeaderNames.Length - 1];  // Minus one for the sentinel.
        } 

        //This is for Cache 
        internal WebHeaderCollection(NameValueCollection cc): base(DBNull.Value) 
        {
            m_InnerCollection = new NameValueCollection(cc.Count + 2, CaseInsensitiveAscii.StaticInstance); 
            int len = cc.Count;
            for (int i = 0; i < len; ++i) {
                String key = cc.GetKey(i);
                String[] values = cc.GetValues(i); 
                if (values != null) {
                    for (int j = 0; j < values.Length; j++) { 
                        InnerCollection.Add(key, values[j]); 
                    }
                } 
                else {
                    InnerCollection.Add(key, null);
                }
            } 
        }
 
        // 
        // ISerializable constructor
        // 
        /// 
        ///    [To be supplied.]
        /// 
        protected WebHeaderCollection(SerializationInfo serializationInfo, StreamingContext streamingContext) : 
            base(DBNull.Value)
        { 
            int count = serializationInfo.GetInt32("Count"); 
            m_InnerCollection = new NameValueCollection(count + 2, CaseInsensitiveAscii.StaticInstance);
            for (int i = 0; i < count; i++) { 
                string headerName = serializationInfo.GetString(i.ToString(NumberFormatInfo.InvariantInfo));
                string headerValue = serializationInfo.GetString((i+count).ToString(NumberFormatInfo.InvariantInfo));
                GlobalLog.Print("WebHeaderCollection::.ctor(ISerializable) calling InnerCollection.Add() key:[" + headerName + "], value:[" + headerValue + "]");
                InnerCollection.Add(headerName, headerValue); 
            }
        } 
 
        public override void OnDeserialization(object sender) {
            // 


        }
 
        //
        // ISerializable method 
        // 
        /// 
        [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.SerializationFormatter)] 		 
        public override void GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext) {
            //
            // for now disregard streamingContext.
            // 
            NormalizeCommonHeaders();
            serializationInfo.AddValue("Count", Count); 
            for (int i = 0; i < Count; i++) 
            {
                serializationInfo.AddValue(i.ToString(NumberFormatInfo.InvariantInfo), GetKey(i)); 
                serializationInfo.AddValue((i + Count).ToString(NumberFormatInfo.InvariantInfo), Get(i));
            }
        }
 

        // we use this static class as a helper class to encode/decode HTTP headers. 
        // what we need is a 1-1 correspondence between a char in the range U+0000-U+00FF 
        // and a byte in the range 0x00-0xFF (which is the range that can hit the network).
        // The Latin-1 encoding (ISO-88591-1) (GetEncoding(28591)) works for byte[] to string, but is a little slow. 
        // It doesn't work for string -> byte[] because of best-fit-mapping problems.
        internal static class HeaderEncoding
        {
            internal static unsafe string GetString(byte[] bytes, int byteIndex, int byteCount) 
            {
                fixed(byte* pBytes = bytes) 
                    return GetString(pBytes + byteIndex, byteCount); 
            }
 
            internal static unsafe string GetString(byte* pBytes, int byteCount)
            {
                if (byteCount < 1)
                    return ""; 

                string s = new String('\0', byteCount); 
 
                fixed (char* pStr = s)
                { 
                    char* pString = pStr;
                    while (byteCount >= 8)
                    {
                        pString[0] = (char) pBytes[0]; 
                        pString[1] = (char) pBytes[1];
                        pString[2] = (char) pBytes[2]; 
                        pString[3] = (char) pBytes[3]; 
                        pString[4] = (char) pBytes[4];
                        pString[5] = (char) pBytes[5]; 
                        pString[6] = (char) pBytes[6];
                        pString[7] = (char) pBytes[7];
                        pString += 8;
                        pBytes += 8; 
                        byteCount -= 8;
                    } 
                    for (int i = 0; i < byteCount; i++) 
                    {
                        pString[i] = (char) pBytes[i]; 
                    }
                }

                return s; 
            }
 
            internal static int GetByteCount(string myString) { 
                return myString.Length;
            } 
            internal unsafe static void GetBytes(string myString, int charIndex, int charCount, byte[] bytes, int byteIndex) {
                if (myString.Length==0) {
                    return;
                } 
                fixed (byte *bufferPointer = bytes) {
                    byte* newBufferPointer = bufferPointer + byteIndex; 
                    int finalIndex = charIndex + charCount; 
                    while (charIndex
            // TEXT           =  
            // CTL            = 
            // SP             = 
            // HT             = 
            // CR             =  
            // LF             = 
            // LWS            = [CR LF] 1*( SP | HT ) 
            // CHAR           =  
            // token          = 1*
            // separators     = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <"> | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT 
            // quoted-string  = ( <"> *(qdtext | quoted-pair ) <"> )
            // qdtext         = >
            // quoted-pair    = "\" CHAR
            // 

            // 
            // At each iteration of the following loop we expect to parse a single HTTP header entirely. 
            //
            for (;;) { 
                //
                // trim leading whitespaces (LWS) just for extra robustness, in fact if there are leading white spaces then:
                // 1) it could be that after the status line we might have spaces. handle this.
                // 2) this should have been detected to be a multiline header so there'll be no spaces and we'll spend some time here. 
                //
                headerName = string.Empty; 
                headerValue = string.Empty; 
                spaceAfterLf = false;
                headerMultiLineValue = null; 

                if (Count == 0) {
                    //
                    // so, restrict this extra trimming only on the first header line 
                    //
                    while (index < size) { 
                         ch = (char) byteBuffer[index]; 
                         if (ch == ' ' || ch == '\t') {
                             ++index; 
                            if (maximumResponseHeadersLength>=0 && ++localTotalResponseHeadersLength>=maximumResponseHeadersLength) {
                                parseStatus = DataParseStatus.DataTooBig;
                                goto quit;
                            } 
                        }
                        else { 
                            break; 
                        }
                    } 

                    if (index==size) {
                        //
                        // we reached the end of the buffer. ask for more data. 
                        //
                        parseStatus = DataParseStatus.NeedMoreData; 
                        goto quit; 
                    }
                } 

                //
                // what we have here is the beginning of a new header
                // 
                headerNameStartOffset = index;
 
                while (index < size) { 
                    ch = (char) byteBuffer[index];
                    if (ch != ':' && ch != '\n') { 
                        if (ch > ' ') {
                            //
                            // if there's an illegal character we should return DataParseStatus.Invalid
                            // instead we choose to be flexible, try to trim it, but include it in the string 
                            //
                            headerNameEndOffset = index; 
                        } 
                        ++index;
                        if (maximumResponseHeadersLength>=0 && ++localTotalResponseHeadersLength>=maximumResponseHeadersLength) { 
                            parseStatus = DataParseStatus.DataTooBig;
                            goto quit;
                        }
                    } 
                    else {
                        if (ch == ':') { 
                            ++index; 
                            if (maximumResponseHeadersLength>=0 && ++localTotalResponseHeadersLength>=maximumResponseHeadersLength) {
                                parseStatus = DataParseStatus.DataTooBig; 
                                goto quit;
                            }
                        }
                        break; 
                    }
                } 
                if (index==size) { 
                    //
                    // we reached the end of the buffer. ask for more data. 
                    //
                    parseStatus = DataParseStatus.NeedMoreData;
                    goto quit;
                } 

startOfValue: 
                // 
                // skip all [' ','\t','\r','\n'] characters until HeaderValue starts
                // if we didn't find any headers yet, we set numberOfLf to 1 
                // so that we take the '\n' from the status line into account
                //

                numberOfLf = (Count == 0 && headerNameEndOffset < 0) ? 1 : 0; 
                while (index=0 && ++localTotalResponseHeadersLength>=maximumResponseHeadersLength) {
                            parseStatus = DataParseStatus.DataTooBig; 
                            goto quit;
                        }
                    }
                    else { 
                        break;
                    } 
                } 
                if (numberOfLf==2 || (numberOfLf==1 && !spaceAfterLf)) {
                    // 
                    // if we've counted two '\n' we got at the end of the headers even if we're past the end of the buffer
                    // if we've counted one '\n' and the first character after that was a ' ' or a '\t'
                    // no matter if we found a ':' or not, treat this as an empty header name.
                    // 
                    goto addHeader;
                } 
                if (index==size) { 
                    //
                    // we reached the end of the buffer. ask for more data. 
                    //
                    parseStatus = DataParseStatus.NeedMoreData;
                    goto quit;
                } 

                headerValueStartOffset = index; 
 
                while (index ' ') {
                            headerValueEndOffset = index;
                        } 
                        ++index;
                        if (maximumResponseHeadersLength>=0 && ++localTotalResponseHeadersLength>=maximumResponseHeadersLength) { 
                            parseStatus = DataParseStatus.DataTooBig; 
                            goto quit;
                        } 
                    }
                    else {
                        break;
                    } 
                }
                if (index==size) { 
                    // 
                    // we reached the end of the buffer. ask for more data.
                    // 
                    parseStatus = DataParseStatus.NeedMoreData;
                    goto quit;
                }
 
                //
                // at this point we found either a '\n' or the end of the headers 
                // hence we are at the end of the Header Line. 4 options: 
                // 1) need more data
                // 2) if we find two '\n' => end of headers 
                // 3) if we find one '\n' and a ' ' or a '\t' => multiline header
                // 4) if we find one '\n' and a valid char => next header
                //
                numberOfLf = 0; 
                while (index=0 && ++localTotalResponseHeadersLength>=maximumResponseHeadersLength) {
                            parseStatus = DataParseStatus.DataTooBig; 
                            goto quit;
                        } 
                    } 
                    else {
                        break; 
                    }
                }
                if (index==size && numberOfLf<2) {
                    // 
                    // we reached the end of the buffer but not of the headers. ask for more data.
                    // 
                    parseStatus = DataParseStatus.NeedMoreData; 
                    goto quit;
                } 

addHeader:
                if (headerValueStartOffset>=0 && headerValueStartOffset>headerNameEndOffset && headerValueEndOffset>=headerValueStartOffset) {
                    // 
                    // Encoding fastest way to build the UNICODE string off the byte[]
                    // 
                    headerValue = HeaderEncoding.GetString(byteBuffer + headerValueStartOffset, headerValueEndOffset - headerValueStartOffset + 1); 
                }
 
                //
                // if we got here from the beginning of the for loop, headerMultiLineValue will be null
                // otherwise it will contain the headerValue constructed for the multiline header
                // add this line as well to it, separated by a single space 
                //
                headerMultiLineValue = (headerMultiLineValue==null ? headerValue : headerMultiLineValue + " " + headerValue); 
 
                if (index < size && numberOfLf == 1) {
                    ch = (char) byteBuffer[index]; 
                    if (ch == ' ' || ch == '\t') {
                        //
                        // since we found only one Lf and the next header line begins with a Lws,
                        // this is the beginning of a multiline header. 
                        // parse the next line into headerValue later it will be added to headerMultiLineValue
                        // 
                        ++index; 
                        if (maximumResponseHeadersLength>=0 && ++localTotalResponseHeadersLength>=maximumResponseHeadersLength) {
                            parseStatus = DataParseStatus.DataTooBig; 
                            goto quit;
                        }
                        goto startOfValue;
                    } 
                }
 
                if (headerNameStartOffset>=0 && headerNameEndOffset>=headerNameStartOffset) { 
                    //
                    // Encoding is the fastest way to build the UNICODE string off the byte[] 
                    //
                    headerName = HeaderEncoding.GetString(byteBuffer + headerNameStartOffset, headerNameEndOffset - headerNameStartOffset + 1);
                }
 
                //
                // now it's finally safe to add the header if we have a name for it 
                // 
                if (headerName.Length>0) {
                    // 
                    // the base clasee will check for pre-existing headerValue and append
                    // it using commas as indicated in the RFC
                    //
                    GlobalLog.Print("WebHeaderCollection::ParseHeaders() calling AddInternal() key:[" + headerName + "], value:[" + headerMultiLineValue + "]"); 
                    AddInternal(headerName, headerMultiLineValue);
                } 
 
                //
                // and update unparsed 
                //
                totalResponseHeadersLength = localTotalResponseHeadersLength;
                unparsed = index;
 
                if (numberOfLf==2) {
                    parseStatus = DataParseStatus.Done; 
                    goto quit; 
                }
 
            } // for (;;)

quit:
            GlobalLog.Leave("WebHeaderCollection::ParseHeaders() returning parseStatus:" + parseStatus.ToString()); 
            if (parseStatus == DataParseStatus.Invalid) {
                parseError.Section = WebParseErrorSection.ResponseHeader; 
                parseError.Code    = parseErrorCode; 
            }
 
            return parseStatus;
            }
        }
 
        //
        // Alternative parsing that follows RFC2616.  Like the above, this trims both sides of the header value and replaces 
        // folding with a single space. 
        //
        private enum RfcChar : byte 
        {
            High = 0,
            Reg,
            Ctl, 
            CR,
            LF, 
            WS, 
            Colon,
            Delim 
        }

        private static RfcChar[] RfcCharMap = new RfcChar[128]
        { 
            RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,
            RfcChar.Ctl,   RfcChar.WS,    RfcChar.LF,    RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.CR,    RfcChar.Ctl,   RfcChar.Ctl, 
            RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl, 
            RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,   RfcChar.Ctl,
            RfcChar.WS,    RfcChar.Reg,   RfcChar.Delim, RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg, 
            RfcChar.Delim, RfcChar.Delim, RfcChar.Reg,   RfcChar.Reg,   RfcChar.Delim, RfcChar.Reg,   RfcChar.Reg,   RfcChar.Delim,
            RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,
            RfcChar.Reg,   RfcChar.Reg,   RfcChar.Colon, RfcChar.Delim, RfcChar.Delim, RfcChar.Delim, RfcChar.Delim, RfcChar.Delim,
            RfcChar.Delim, RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg, 
            RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,
            RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg, 
            RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Delim, RfcChar.Delim, RfcChar.Delim, RfcChar.Reg,   RfcChar.Reg, 
            RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,
            RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg, 
            RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,
            RfcChar.Reg,   RfcChar.Reg,   RfcChar.Reg,   RfcChar.Delim, RfcChar.Reg,   RfcChar.Delim, RfcChar.Reg,   RfcChar.Ctl,
        };
 
        internal unsafe DataParseStatus ParseHeadersStrict(
                byte[] buffer, 
                int size, 
                ref int unparsed,
                ref int totalResponseHeadersLength, 
                int maximumResponseHeadersLength,
                ref WebParseError parseError)
        {
            GlobalLog.Enter("WebHeaderCollection::ParseHeadersStrict(): size:" + size.ToString() + ", unparsed:" + unparsed.ToString() + " buffer:[" + Encoding.ASCII.GetString(buffer, unparsed, Math.Min(256, size-unparsed)) + "]"); 

            WebParseErrorCode parseErrorCode = WebParseErrorCode.Generic; 
            DataParseStatus parseStatus = DataParseStatus.Invalid; 

            int i = unparsed; 
            RfcChar ch;
            int effectiveSize = maximumResponseHeadersLength <= 0 ? Int32.MaxValue : maximumResponseHeadersLength - totalResponseHeadersLength + i;
            DataParseStatus sizeError = DataParseStatus.DataTooBig;
            if (size < effectiveSize) 
            {
                effectiveSize = size; 
                sizeError = DataParseStatus.NeedMoreData; 
            }
 
            // Verify the size.
            if (i >= effectiveSize)
            {
                parseStatus = sizeError; 
                goto quit;
            } 
 
            fixed (byte* byteBuffer = buffer)
            { 
                while (true)
                {
                    // If this is CRLF, actually we're done.
                    if (byteBuffer[i] == '\r') 
                    {
                        if (++i == effectiveSize) 
                        { 
                            parseStatus = sizeError;
                            goto quit; 
                        }

                        if (byteBuffer[i++] == '\n')
                        { 
                            totalResponseHeadersLength += i - unparsed;
                            unparsed = i; 
                            parseStatus = DataParseStatus.Done; 
                            goto quit;
                        } 

                        parseStatus = DataParseStatus.Invalid;
                        parseErrorCode = WebParseErrorCode.CrLfError;
                        goto quit; 
                    }
 
                    // Find the header name; only regular characters allowed. 
                    int iBeginName = i;
                    for (; i < effectiveSize && (ch = byteBuffer[i] > 127 ? RfcChar.High : RfcCharMap[byteBuffer[i]]) == RfcChar.Reg; i++); 
                    if (i == effectiveSize)
                    {
                        parseStatus = sizeError;
                        goto quit; 
                    }
                    if (i == iBeginName) 
                    { 
                        parseStatus = DataParseStatus.Invalid;
                        parseErrorCode = WebParseErrorCode.InvalidHeaderName; 
                        goto quit;
                    }

                    // Read to a colon. 
                    int iEndName = i - 1;
                    int crlf = 0;  // 1 = cr, 2 = crlf 
                    for (; i < effectiveSize && (ch = byteBuffer[i] > 127 ? RfcChar.High : RfcCharMap[byteBuffer[i]]) != RfcChar.Colon; i++) 
                    {
                        switch (ch) 
                        {
                            case RfcChar.WS:
                                if (crlf == 1)
                                { 
                                    break;
                                } 
                                crlf = 0; 
                                continue;
 
                            case RfcChar.CR:
                                if (crlf == 0)
                                {
                                    crlf = 1; 
                                    continue;
                                } 
                                break; 

                            case RfcChar.LF: 
                                if (crlf == 1)
                                {
                                    crlf = 2;
                                    continue; 
                                }
                                break; 
                        } 
                        parseStatus = DataParseStatus.Invalid;
                        parseErrorCode = WebParseErrorCode.CrLfError; 
                        goto quit;
                    }
                    if (i == effectiveSize)
                    { 
                        parseStatus = sizeError;
                        goto quit; 
                    } 
                    if (crlf != 0)
                    { 
                        parseStatus = DataParseStatus.Invalid;
                        parseErrorCode = WebParseErrorCode.IncompleteHeaderLine;
                        goto quit;
                    } 

                    // Skip the colon. 
                    if (++i == effectiveSize) 
                    {
                        parseStatus = sizeError; 
                        goto quit;
                    }

                    // Read the value.  crlf = 3 means in the whitespace after a CRLF 
                    int iBeginValue = -1;
                    int iEndValue = -1; 
                    StringBuilder valueAccumulator = null; 
                    for (; i < effectiveSize && ((ch = byteBuffer[i] > 127 ? RfcChar.High : RfcCharMap[byteBuffer[i]]) == RfcChar.WS || crlf != 2); i++)
                    { 
                        switch (ch)
                        {
                            case RfcChar.WS:
                                if (crlf == 1) 
                                {
                                    break; 
                                } 
                                if (crlf == 2)
                                { 
                                    crlf = 3;
                                }
                                continue;
 
                            case RfcChar.CR:
                                if (crlf == 0) 
                                { 
                                    crlf = 1;
                                    continue; 
                                }
                                break;

                            case RfcChar.LF: 
                                if (crlf == 1)
                                { 
                                    crlf = 2; 
                                    continue;
                                } 
                                break;

                            case RfcChar.High:
                            case RfcChar.Colon: 
                            case RfcChar.Delim:
                            case RfcChar.Reg: 
                                if (crlf == 1) 
                                {
                                    break; 
                                }
                                if (crlf == 3)
                                {
                                    crlf = 0; 
                                    if (iBeginValue != -1)
                                    { 
                                        string s = HeaderEncoding.GetString(byteBuffer + iBeginValue, iEndValue - iBeginValue + 1); 
                                        if (valueAccumulator == null)
                                        { 
                                            valueAccumulator = new StringBuilder(s, s.Length * 5);
                                        }
                                        else
                                        { 
                                            valueAccumulator.Append(" ");
                                            valueAccumulator.Append(s); 
                                        } 
                                    }
                                    iBeginValue = -1; 
                                }
                                if (iBeginValue == -1)
                                {
                                    iBeginValue = i; 
                                }
                                iEndValue = i; 
                                continue; 
                        }
                        parseStatus = DataParseStatus.Invalid; 
                        parseErrorCode = WebParseErrorCode.CrLfError;
                        goto quit;
                    }
                    if (i == effectiveSize) 
                    {
                        parseStatus = sizeError; 
                        goto quit; 
                    }
 
                    // Make the value.
                    string sValue = iBeginValue == -1 ? "" : HeaderEncoding.GetString(byteBuffer + iBeginValue, iEndValue - iBeginValue + 1);
                    if (valueAccumulator != null)
                    { 
                        if (sValue.Length != 0)
                        { 
                            valueAccumulator.Append(" "); 
                            valueAccumulator.Append(sValue);
                        } 
                        sValue = valueAccumulator.ToString();
                    }

                    // Make the name.  See if it's a common header first. 
                    string sName = null;
                    int headerNameLength = iEndName - iBeginName + 1; 
                    if (m_CommonHeaders != null) 
                    {
                        int iHeader = s_CommonHeaderHints[byteBuffer[iBeginName] & 0x1f]; 
                        if (iHeader >= 0)
                        {
                            while (true)
                            { 
                                string s = s_CommonHeaderNames[iHeader++];
 
                                // Not found if we get to a shorter header or one with a different first character. 
                                if (s.Length < headerNameLength || CaseInsensitiveAscii.AsciiToLower[byteBuffer[iBeginName]] != CaseInsensitiveAscii.AsciiToLower[s[0]])
                                    break; 

                                // Keep looking if the common header is too long.
                                if (s.Length > headerNameLength)
                                    continue; 

                                int j; 
                                byte* pBuffer = byteBuffer + iBeginName + 1; 
                                for (j = 1; j < s.Length; j++)
                                { 
                                    // Avoid the case-insensitive compare in the common case where they match.
                                    if (*(pBuffer++) != s[j] && CaseInsensitiveAscii.AsciiToLower[*(pBuffer - 1)] != CaseInsensitiveAscii.AsciiToLower[s[j]])
                                        break;
                                } 
                                if (j == s.Length)
                                { 
                                    // Set it to the appropriate index. 
                                    m_NumCommonHeaders++;
                                    iHeader--; 
                                    if (m_CommonHeaders[iHeader] == null)
                                    {
                                        m_CommonHeaders[iHeader] = sValue;
                                    } 
                                    else
                                    { 
                                        // Don't currently handle combining multiple header instances in the common header case. 
                                        // Nothing to do but punt them all to the NameValueCollection.
                                        NormalizeCommonHeaders(); 
                                        AddInternalNotCommon(s, sValue);
                                    }

                                    sName = s; 
                                    break;
                                } 
                            } 
                        }
                    } 

                    // If it wasn't a common header, add it to the hash.
                    if (sName == null)
                    { 
                        sName = HeaderEncoding.GetString(byteBuffer + iBeginName, headerNameLength);
                        AddInternalNotCommon(sName, sValue); 
                    } 

                    totalResponseHeadersLength += i - unparsed; 
                    unparsed = i;
                }
            }
 
quit:
            GlobalLog.Leave("WebHeaderCollection::ParseHeadersStrict() returning parseStatus:" + parseStatus.ToString()); 
 
            if (parseStatus == DataParseStatus.Invalid) {
                parseError.Section = WebParseErrorSection.ResponseHeader; 
                parseError.Code    = parseErrorCode;
            }

            return parseStatus; 
        }
 
        // 
        // Keeping this version for backwards compatibility (mostly with reflection).  Remove some day, along with the interface
        // explicit reimplementation. 
        //
        /// 
        [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase", Justification = "System.dll is still using pre-v4 security model and needs this demand")]
        [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.SerializationFormatter, SerializationFormatter=true)] 
        void ISerializable.GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext)
        { 
            GetObjectData(serializationInfo, streamingContext); 
        }
 
        // Override Get() to check the common headers.
        public override string Get(string name)
        {
            // In this case, need to make sure name doesn't have any Unicode since it's being used as an index into tables. 
            if (m_CommonHeaders != null && name != null && name.Length > 0 && name[0] < 256)
            { 
                int iHeader = s_CommonHeaderHints[name[0] & 0x1f]; 
                if (iHeader >= 0)
                { 
                    while (true)
                    {
                        string s = s_CommonHeaderNames[iHeader++];
 
                        // Not found if we get to a shorter header or one with a different first character.
                        if (s.Length < name.Length || CaseInsensitiveAscii.AsciiToLower[name[0]] != CaseInsensitiveAscii.AsciiToLower[s[0]]) 
                            break; 

                        // Keep looking if the common header is too long. 
                        if (s.Length > name.Length)
                            continue;

                        int j; 
                        for (j = 1; j < s.Length; j++)
                        { 
                            // Avoid the case-insensitive compare in the common case where they match. 
                            if (name[j] != s[j] && (name[j] > 255 || CaseInsensitiveAscii.AsciiToLower[name[j]] != CaseInsensitiveAscii.AsciiToLower[s[j]]))
                                break; 
                        }
                        if (j == s.Length)
                        {
                            // Get the appropriate header. 
                            return m_CommonHeaders[iHeader - 1];
                        } 
                    } 
                }
            } 

            // Fall back to normal lookup.
            if (m_InnerCollection == null)
                return null; 
            return m_InnerCollection.Get(name);
        } 
 

        // 
        // Additional overrides required to fully orphan the base implementation.
        //
        public override IEnumerator GetEnumerator()
        { 
            NormalizeCommonHeaders();
            return new NameObjectKeysEnumerator(InnerCollection); 
        } 

        public override int Count 
        {
            get
            {
                return (m_InnerCollection == null ? 0 : m_InnerCollection.Count) + m_NumCommonHeaders; 
            }
        } 
 
        public override KeysCollection Keys
        { 
            get
            {
                NormalizeCommonHeaders();
                return InnerCollection.Keys; 
            }
        } 
 
        internal override bool InternalHasKeys()
        { 
            NormalizeCommonHeaders();
            if (m_InnerCollection == null)
                return false;
            return m_InnerCollection.HasKeys(); 
        }
 
        public override string Get(int index) 
        {
            NormalizeCommonHeaders(); 
            return InnerCollection.Get(index);
        }

        public override string[] GetValues(int index) 
        {
            NormalizeCommonHeaders(); 
            return InnerCollection.GetValues(index); 
        }
 
        public override string GetKey(int index)
        {
            NormalizeCommonHeaders();
            return InnerCollection.GetKey(index); 
        }
 
        public override string[] AllKeys 
        {
            get 
            {
                NormalizeCommonHeaders();
                return InnerCollection.AllKeys;
            } 
        }
 
        public override void Clear() 
        {
            m_CommonHeaders = null; 
            m_NumCommonHeaders = 0;
            InvalidateCachedArrays();
            if (m_InnerCollection != null)
                m_InnerCollection.Clear(); 
        }
    } // class WebHeaderCollection 
 

    internal class CaseInsensitiveAscii : IEqualityComparer, IComparer{ 
        // ASCII char ToLower table
        internal static readonly CaseInsensitiveAscii StaticInstance = new CaseInsensitiveAscii();
        internal static readonly byte[] AsciiToLower = new byte[] {
              0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 
             10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
             20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 
             30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 
             40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
             50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 
             60, 61, 62, 63, 64, 97, 98, 99,100,101, //  60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
            102,103,104,105,106,107,108,109,110,111, //  70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
            112,113,114,115,116,117,118,119,120,121, //  80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
            122, 91, 92, 93, 94, 95, 96, 97, 98, 99, //  90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 
            100,101,102,103,104,105,106,107,108,109,
            110,111,112,113,114,115,116,117,118,119, 
            120,121,122,123,124,125,126,127,128,129, 
            130,131,132,133,134,135,136,137,138,139,
            140,141,142,143,144,145,146,147,148,149, 
            150,151,152,153,154,155,156,157,158,159,
            160,161,162,163,164,165,166,167,168,169,
            170,171,172,173,174,175,176,177,178,179,
            180,181,182,183,184,185,186,187,188,189, 
            190,191,192,193,194,195,196,197,198,199,
            200,201,202,203,204,205,206,207,208,209, 
            210,211,212,213,214,215,216,217,218,219, 
            220,221,222,223,224,225,226,227,228,229,
            230,231,232,233,234,235,236,237,238,239, 
            240,241,242,243,244,245,246,247,248,249,
            250,251,252,253,254,255
        };
 
        // ASCII string case insensitive hash function
        public int GetHashCode(object myObject) { 
            string myString = myObject as string; 
            if (myObject == null) {
                return 0; 
            }
            int myHashCode = myString.Length;
            if (myHashCode == 0) {
                return 0; 
            }
            myHashCode ^= AsciiToLower[(byte)myString[0]]<<24 ^ AsciiToLower[(byte)myString[myHashCode-1]]<<16; 
            return myHashCode; 
        }
 
        // ASCII string case insensitive comparer
        public int Compare(object firstObject, object secondObject) {
            string firstString = firstObject as string;
            string secondString = secondObject as string; 
            if (firstString==null) {
                return secondString == null ? 0 : -1; 
            } 
            if (secondString == null) {
                return 1; 
            }
            int result = firstString.Length - secondString.Length;
            int comparisons = result > 0 ? secondString.Length : firstString.Length;
            int difference, index = 0; 
            while ( index < comparisons ) {
                difference = (int)(AsciiToLower[ firstString[index] ] - AsciiToLower[ secondString[index] ]); 
                if ( difference != 0 ) { 
                    result = difference;
                    break; 
                }
                index++;
            }
            return result; 
        }
 
        // ASCII string case insensitive hash function 
        int FastGetHashCode(string myString) {
            int myHashCode = myString.Length; 
            if (myHashCode!=0) {
                myHashCode ^= AsciiToLower[(byte)myString[0]]<<24 ^ AsciiToLower[(byte)myString[myHashCode-1]]<<16;
            }
            return myHashCode; 
        }
 
        // ASCII string case insensitive comparer 
        public new bool Equals(object firstObject, object secondObject) {
            string firstString = firstObject as string; 
            string secondString = secondObject as string;
            if (firstString==null) {
                return secondString==null;
            } 
            if (secondString!=null) {
                int index = firstString.Length; 
                if (index==secondString.Length) { 
                    if (FastGetHashCode(firstString)==FastGetHashCode(secondString)) {
                        int comparisons = firstString.Length; 
                        while (index>0) {
                            index--;
                            if (AsciiToLower[firstString[index]]!=AsciiToLower[secondString[index]]) {
                                return false; 
                            }
                        } 
                        return true; 
                    }
                } 
            }
            return false;
        }
    } 

    internal class HostHeaderString { 
 
        private bool m_Converted;
        private string m_String; 
        private byte[] m_Bytes;

        internal HostHeaderString() {
            Init(null); 
        }
 
        internal HostHeaderString(string s) { 
            Init(s);
        } 

        private void Init(string s) {
            m_String = s;
            m_Converted = false; 
            m_Bytes = null;
        } 
 
        private void Convert() {
            if (m_String != null && !m_Converted) { 
                m_Bytes = Encoding.Default.GetBytes(m_String);
                string copy = Encoding.Default.GetString(m_Bytes);
                if (!(string.Compare(m_String, copy, StringComparison.Ordinal) == 0)) {
                    m_Bytes = Encoding.UTF8.GetBytes(m_String); 
                }
            } 
        } 

        internal string String { 
            get { return m_String; }
            set {
                Init(value);
            } 
        }
 
        internal int ByteCount { 
            get {
                Convert(); 
                return m_Bytes.Length;
            }
        }
 
        internal byte[] Bytes {
            get { 
                Convert(); 
                return m_Bytes;
            } 
        }

        internal void Copy(byte[] destBytes, int destByteIndex) {
            Convert(); 
            Array.Copy(m_Bytes, 0, destBytes, destByteIndex, m_Bytes.Length);
        } 
 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK