File: net\System\Net\WebHeaderCollection.cs
Project: ndp\fx\src\System.csproj (System)
//------------------------------------------------------------------------------
// <copyright file="WebHeaderCollection.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
 
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.
    //
 
    /// <devdoc>
    ///    <para>
    ///       Contains protocol headers associated with a
    ///       request or response.
    ///    </para>
    /// </devdoc>
    [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
            {
                string location = m_CommonHeaders != null
                    ? m_CommonHeaders[c_Location] : Get(s_CommonHeaderNames[c_Location]);
                // The normal header parser just casts bytes to chars. Check if there is a UTF8 host name.
                return HeaderEncoding.DecodeUtf8FromString(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.
 
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        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(SR.GetString(SR.net_headerrestrict, headerName), "name");
                }
            }
            else if (m_Type == WebHeaderCollectionType.HttpListenerResponse)
            {
                if (HInfo[headerName].IsResponseRestricted)
                {
                    throw new ArgumentException(SR.GetString(SR.net_headerrestrict, 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
 
        /// <devdoc>
        ///    <para>
        ///       Adds a new header with the indicated name and value.
        ///    </para>
        /// </devdoc>
        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
 
        /// <devdoc>
        ///    <para>
        ///       Adds the indicated header.
        ///    </para>
        /// </devdoc>
        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
 
        /// <devdoc>
        ///    <para>
        ///       Sets the specified header to the specified value.
        ///    </para>
        /// </devdoc>
        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
 
        /// <devdoc>
        ///    <para>Removes the specified header.</para>
        /// </devdoc>
        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
 
        /// <devdoc>
        ///    <para>
        ///       Gets an array of header values stored in a
        ///       header.
        ///    </para>
        /// </devdoc>
        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
 
        /// <internalonly/>
        /// <devdoc>
        ///    <para>
        ///       Obsolete.
        ///    </para>
        /// </devdoc>
        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
 
        /// <internalonly/>
        /// <devdoc>
        ///    <para>
        ///       Obsolete.
        ///    </para>
        /// </devdoc>
        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;
        }
 
        /// <devdoc>
        ///    <para>Tests if access to the HTTP header with the provided name is accessible for setting.</para>
        /// </devdoc>
        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;
        }
 
 
        /// <devdoc>
        ///    <para>
        ///       Initializes a new instance of the <see cref='System.Net.WebHeaderCollection'/>
        ///       class.
        ///    </para>
        /// </devdoc>
        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
        //
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        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
        //
        /// <internalonly/>
        [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<finalIndex) {
                        *newBufferPointer++ = (byte)myString[charIndex++];
                    }
                }
            }
            internal unsafe static byte[] GetBytes(string myString) {
                byte[] bytes = new byte[myString.Length];
                if (myString.Length!=0) {
                    GetBytes(myString, 0, myString.Length, bytes, 0);
                }
                return bytes;
            }
 
            // The normal client header parser just casts bytes to chars (see GetString).
            // Check if those bytes were actually utf-8 instead of ASCII.
            // If not, just return the input value.
            [System.Runtime.CompilerServices.FriendAccessAllowed]
            internal static string DecodeUtf8FromString(string input) {
                if (string.IsNullOrWhiteSpace(input)) {
                    return input;
                }
 
                bool possibleUtf8 = false;
                for (int i = 0; i < input.Length; i++) {
                    if (input[i] > (char)255) {
                        return input; // This couldn't have come from the wire, someone assigned it directly.
                    }
                    else if (input[i] > (char)127) {
                        possibleUtf8 = true;
                        break;
                    }
                }
                if (possibleUtf8) {
                    byte[] rawBytes = new byte[input.Length];
                    for (int i = 0; i < input.Length; i++) {
                        if (input[i] > (char)255) {
                            return input; // This couldn't have come from the wire, someone assigned it directly.
                        }
                        rawBytes[i] = (byte)input[i];
                    }
                    try {
                        // We don't want '?' replacement characters, just fail.
                        Encoding decoder = Encoding.GetEncoding("utf-8", EncoderFallback.ExceptionFallback,
                            DecoderFallback.ExceptionFallback);
                        return decoder.GetString(rawBytes);
                    }
                    catch (ArgumentException) { } // Not actually Utf-8
                }
                return input;
            }
        }
 
 
        // ParseHeaders -
        // Routine Description:
        //
        //     This code is optimized for the case in which all the headers fit in the buffer.
        //     we support multiple re-entrance, but we won't save intermediate
        //     state, we will just roll back all the parsing done for the current header if we can't
        //     parse a whole one (including multiline) or decide something else ("invalid data" or "done parsing").
        //
        //     we're going to cycle through the loop until we
        //
        //     1) find an HTTP violation (in this case we return DataParseStatus.Invalid)
        //     2) we need more data (in this case we return DataParseStatus.NeedMoreData)
        //     3) we found the end of the headers and the beginning of the entity body (in this case we return DataParseStatus.Done)
        //
        //
        // Arguments:
        //
        //     buffer      - buffer containing the data to be parsed
        //     size        - size of the buffer
        //     unparsed    - offset of data yet to be parsed
        //
        // Return Value:
        //
        //     DataParseStatus - status of parsing
        //
        // Revision:
        //
        //     02/13/2001 rewrote the method from scratch.
        //
        // BreakPoint:
        //
        //     b system.dll!System.Net.WebHeaderCollection::ParseHeaders
        internal unsafe DataParseStatus ParseHeaders(
                byte[] buffer, 
                int size, 
                ref int unparsed, 
                ref int totalResponseHeadersLength, 
                int maximumResponseHeadersLength, 
                ref WebParseError parseError) {
 
            fixed (byte * byteBuffer = buffer) {
 
            char ch;
 
            // quick check in the boundaries (as we use unsafe pointer)
            if (buffer.Length < size) {
                return DataParseStatus.NeedMoreData;
            }
 
            int headerNameStartOffset = -1;
            int headerNameEndOffset = -1;
            int headerValueStartOffset = -1;
            int headerValueEndOffset = -1;
            int numberOfLf = -1;
            int index = unparsed;
            bool spaceAfterLf;
            string headerMultiLineValue;
            string headerName;
            string headerValue;
 
            // we need this because this method is entered multiple times.
            int localTotalResponseHeadersLength = totalResponseHeadersLength;
 
            WebParseErrorCode parseErrorCode = WebParseErrorCode.Generic;
            DataParseStatus parseStatus = DataParseStatus.Invalid;
#if TRAVE
            GlobalLog.Enter("WebHeaderCollection::ParseHeaders(): ANSI size:" + size.ToString() + ", unparsed:" + unparsed.ToString() + " buffer:[" + Encoding.ASCII.GetString(buffer, unparsed, Math.Min(256, size-unparsed)) + "]");
#endif
 
            //
            // according to RFC216 a header can have the following syntax:
            //
            // message-header = field-name ":" [ field-value ]
            // field-name     = token
            // field-value    = *( field-content | LWS )
            // field-content  = <the OCTETs making up the field-value and consisting of either *TEXT or combinations of token, separators, and quoted-string>
            // TEXT           = <any OCTET except CTLs, but including LWS>
            // CTL            = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
            // SP             = <US-ASCII SP, space (32)>
            // HT             = <US-ASCII HT, horizontal-tab (9)>
            // CR             = <US-ASCII CR, carriage return (13)>
            // LF             = <US-ASCII LF, linefeed (10)>
            // LWS            = [CR LF] 1*( SP | HT )
            // CHAR           = <any US-ASCII character (octets 0 - 127)>
            // token          = 1*<any CHAR except CTLs or separators>
            // separators     = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <"> | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT
            // quoted-string  = ( <"> *(qdtext | quoted-pair ) <"> )
            // qdtext         = <any TEXT except <">>
            // 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<size && numberOfLf<2) {
                    ch = (char) byteBuffer[index];
                    if (ch <= ' ') {
                        if (ch=='\n') {
                            numberOfLf++;
                            // In this case, need to check for a space.
                            if (numberOfLf == 1)
                            {
                                if (index + 1 == size)
                                {
                                    //
                                    // we reached the end of the buffer. ask for more data.
                                    // need to be able to peek after the \n and see if there's some space.
                                    //
                                    parseStatus = DataParseStatus.NeedMoreData;
                                    goto quit;
                                }
                                spaceAfterLf = (char) byteBuffer[index + 1] == ' ' || (char) byteBuffer[index + 1] == '\t';
                            }
                        }
                        ++index;
                        if (maximumResponseHeadersLength>=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<size) {
                    ch = (char) byteBuffer[index];
                    if (ch != '\n') {
                        if (ch > ' ') {
                            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<size && numberOfLf<2) {
                    ch = (char) byteBuffer[index];
                    if (ch =='\r' || ch == '\n') {
                        if (ch == '\n') {
                            numberOfLf++;
                        }
                        ++index;
                        if (maximumResponseHeadersLength>=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.
        //
        /// <internalonly/>
        [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);
        }
 
    }
}