Exemple #1
0
        byte [] GetHeaders()
        {
            var basicHeaders = new StringBuilder();

            basicHeaders.Append(protocol);
            if (statusCode == 200)
            {
                basicHeaders.Append(" 200 ");
            }
            else
            {
                basicHeaders.Append(' ');
                basicHeaders.Append(statusCode.ToString(CultureInfo.InvariantCulture));
                basicHeaders.Append(' ');
            }
            basicHeaders.Append(statusDescription);
            basicHeaders.Append("\r\nDate: ");
            basicHeaders.Append(DateTime.UtcNow.ToString("r", CultureInfo.InvariantCulture));
            basicHeaders.Append(serverHeader);
            responseHeaders.Insert(0, basicHeaders.ToString());

            if (!sentConnection)
            {
                if (!haveContentLength)
                {
                    keepAlive = false;
                }

                AddConnectionHeader();
            }

            responseHeaders.Append("\r\n");
            return(HeaderEncoding.GetBytes(responseHeaders.ToString()));
        }
Exemple #2
0
        /// <devdoc>
        ///    <para>Dumps a byte array to the log</para>
        /// </devdoc>
        internal static void Dump(TraceSource traceSource, object obj, string method, byte[] buffer, int offset, int length)
        {
            if (!ValidateSettings(traceSource, TraceEventType.Verbose))
            {
                return;
            }
            if (buffer == null)
            {
                PrintLine(traceSource, TraceEventType.Verbose, 0, "(null)");
                return;
            }
            if (offset > buffer.Length)
            {
                PrintLine(traceSource, TraceEventType.Verbose, 0, "(offset out of range)");
                return;
            }
            PrintLine(traceSource, TraceEventType.Verbose, 0, "Data from " + GetObjectName(obj) + "#" + HashString(obj) + "::" + method);
            int maxDumpSize = GetMaxDumpSizeSetting(traceSource);

            if (length > maxDumpSize)
            {
                PrintLine(traceSource, TraceEventType.Verbose, 0, "(printing " + maxDumpSize.ToString(NumberFormatInfo.InvariantInfo) + " out of " + length.ToString(NumberFormatInfo.InvariantInfo) + ")");
                length = maxDumpSize;
            }
            if ((length < 0) || (length > buffer.Length - offset))
            {
                length = buffer.Length - offset;
            }
            if (GetUseProtocolTextSetting(traceSource))
            {
                string output = "<<" + HeaderEncoding.GetString(buffer, offset, length) + ">>";
                PrintLine(traceSource, TraceEventType.Verbose, 0, output);
                return;
            }
            do
            {
                int    n    = Math.Min(length, 16);
                string disp = String.Format(CultureInfo.CurrentCulture, "{0:X8} : ", offset);
                for (int i = 0; i < n; ++i)
                {
                    disp += String.Format(CultureInfo.CurrentCulture, "{0:X2}", buffer[offset + i]) + ((i == 7) ? '-' : ' ');
                }
                for (int i = n; i < 16; ++i)
                {
                    disp += "   ";
                }
                disp += ": ";
                for (int i = 0; i < n; ++i)
                {
                    disp += ((buffer[offset + i] < 0x20) || (buffer[offset + i] > 0x7e))
                                ? '.'
                                : (char)(buffer[offset + i]);
                }
                PrintLine(traceSource, TraceEventType.Verbose, 0, disp);
                offset += n;
                length -= n;
            } while (length > 0);
        }
        public override SecurityToken ReadToken(string tokenString)
        {
            // unbase64 header if necessary
            if (HeaderEncoding.IsBase64Encoded(tokenString))
            {
                tokenString = HeaderEncoding.DecodeBase64(tokenString);
            }

            return(ReadToken(new XmlTextReader(new StringReader(tokenString))));
        }
 private void EnsureHeadersSent()
 {
     if (headers != null)
     {
         headers.Append("\r\n");
         string str  = headers.ToString();
         byte[] data = HeaderEncoding.GetBytes(str);
         transport.SendOutput(requestId, requestNumber, data, data.Length);
         headers = null;
     }
 }
Exemple #5
0
 internal static string ParseHeaderEncoding(HeaderEncoding encoding)
 {
     if (encoding == HeaderEncoding.QuotedPrintable)
     {
         return("Q");
     }
     else
     {
         return("B");
     }
 }
        /*++
         *
         * 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
         *
         * --*/
        /// <include file='doc\WebHeaders.uex' path='docs/doc[@for="WebHeaderCollection.ToByteArray"]/*' />
        /// <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);
        }
        public override bool CanReadToken(string tokenString)
        {
            // unbase64 header if necessary
            if (HeaderEncoding.IsBase64Encoded(tokenString))
            {
                tokenString = HeaderEncoding.DecodeBase64(tokenString);
            }

            if (tokenString.StartsWith("<"))
            {
                return(base.CanReadToken(new XmlTextReader(new StringReader(tokenString))));
            }

            return(base.CanReadToken(tokenString));
        }
Exemple #8
0
        byte [] GetHeaders()
        {
            responseHeaders.Insert(0, status);
            if (!sentConnection)
            {
                if (!haveContentLength)
                {
                    keepAlive = false;
                }

                AddConnectionHeader();
            }

            responseHeaders.Append("\r\n");
            return(HeaderEncoding.GetBytes(responseHeaders.ToString()));
        }
Exemple #9
0
    // These methods require the HTTP_REQUEST to still be pinned in its original location.

    internal string?GetVerb()
    {
        var verb = NativeRequest->Verb;

        if (verb > HttpApiTypes.HTTP_VERB.HttpVerbUnknown && verb < HttpApiTypes.HTTP_VERB.HttpVerbMaximum)
        {
            return(HttpApiTypes.HttpVerbs[(int)verb]);
        }
        else if (verb == HttpApiTypes.HTTP_VERB.HttpVerbUnknown && NativeRequest->pUnknownVerb != null)
        {
            // Never use Latin1 for the VERB
            return(HeaderEncoding.GetString(NativeRequest->pUnknownVerb, NativeRequest->UnknownVerbLength, useLatin1: false));
        }

        return(null);
    }
        public override SecurityToken ReadToken(string tokenString)
        {
            // unbase64 header if necessary
            if (HeaderEncoding.IsBase64Encoded(tokenString))
            {
                tokenString = HeaderEncoding.DecodeBase64(tokenString);
            }

            // check containing collection (mainly useful for chained EncryptedSecurityTokenHandler)
            if (ContainingCollection != null)
            {
                return(ContainingCollection.ReadToken(new XmlTextReader(new StringReader(tokenString))));
            }
            else
            {
                return(ReadToken(new XmlTextReader(new StringReader(tokenString))));
            }
        }
        /// <summary>
        /// Get message body
        /// </summary>
        /// <returns></returns>
        public List <byte> GetBody()
        {
            var builder = new List <byte>(1024);

            // headers
            builder.AddRange(header.Encode(headerEncoding));

            // delimeter
            builder.AddRange(HeaderEncoding.GetBytes(Util.CRLF));

            if (IsMultipart)
            {
                builder.AddRange(
                    Encoding.GetBytes("This is a multi-part message in MIME format." + Util.CRLF + Util.CRLF));
            }

            builder.AddRange(GetBodyContent());
            return(builder);
        }
Exemple #12
0
    internal unsafe void SerializeTrailers(HttpApiTypes.HTTP_DATA_CHUNK[] dataChunks, int currentChunk, List <GCHandle> pins)
    {
        Debug.Assert(currentChunk == dataChunks.Length - 1);
        Debug.Assert(HasTrailers);
        MakeTrailersReadOnly();
        var trailerCount = 0;

        foreach (var trailerPair in Trailers)
        {
            trailerCount += trailerPair.Value.Count;
        }

        var pinnedHeaders = new List <GCHandle>();

        var unknownHeaders = new HttpApiTypes.HTTP_UNKNOWN_HEADER[trailerCount];
        var gcHandle       = GCHandle.Alloc(unknownHeaders, GCHandleType.Pinned);

        pinnedHeaders.Add(gcHandle);
        dataChunks[currentChunk].DataChunkType         = HttpApiTypes.HTTP_DATA_CHUNK_TYPE.HttpDataChunkTrailers;
        dataChunks[currentChunk].trailers.trailerCount = (ushort)trailerCount;
        dataChunks[currentChunk].trailers.pTrailers    = gcHandle.AddrOfPinnedObject();

        try
        {
            var unknownHeadersOffset = 0;

            foreach (var headerPair in Trailers)
            {
                if (headerPair.Value.Count == 0)
                {
                    continue;
                }

                var headerName   = headerPair.Key;
                var headerValues = headerPair.Value;

                for (int headerValueIndex = 0; headerValueIndex < headerValues.Count; headerValueIndex++)
                {
                    // Add Name
                    var bytes = HeaderEncoding.GetBytes(headerName);
                    unknownHeaders[unknownHeadersOffset].NameLength = (ushort)bytes.Length;
                    gcHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
                    pinnedHeaders.Add(gcHandle);
                    unknownHeaders[unknownHeadersOffset].pName = (byte *)gcHandle.AddrOfPinnedObject();

                    // Add Value
                    var headerValue = headerValues[headerValueIndex] ?? string.Empty;
                    bytes = HeaderEncoding.GetBytes(headerValue);
                    unknownHeaders[unknownHeadersOffset].RawValueLength = (ushort)bytes.Length;
                    gcHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
                    pinnedHeaders.Add(gcHandle);
                    unknownHeaders[unknownHeadersOffset].pRawValue = (byte *)gcHandle.AddrOfPinnedObject();
                    unknownHeadersOffset++;
                }
            }

            Debug.Assert(unknownHeadersOffset == trailerCount);
        }
        catch
        {
            FreePinnedHeaders(pinnedHeaders);
            throw;
        }

        // Success, keep the pins.
        pins.AddRange(pinnedHeaders);
    }
Exemple #13
0
        internal unsafe void SendError(ulong requestId, int httpStatusCode, IList <string>?authChallenges = null)
        {
            HttpApiTypes.HTTP_RESPONSE_V2 httpResponse = new HttpApiTypes.HTTP_RESPONSE_V2();
            httpResponse.Response_V1.Version = new HttpApiTypes.HTTP_VERSION();
            httpResponse.Response_V1.Version.MajorVersion = (ushort)1;
            httpResponse.Response_V1.Version.MinorVersion = (ushort)1;

            List <GCHandle>?pinnedHeaders = null;
            GCHandle        gcHandle;

            try
            {
                // Copied from the multi-value headers section of SerializeHeaders
                if (authChallenges != null && authChallenges.Count > 0)
                {
                    pinnedHeaders = new List <GCHandle>(authChallenges.Count + 3);

                    HttpApiTypes.HTTP_RESPONSE_INFO[] knownHeaderInfo = new HttpApiTypes.HTTP_RESPONSE_INFO[1];
                    gcHandle = GCHandle.Alloc(knownHeaderInfo, GCHandleType.Pinned);
                    pinnedHeaders.Add(gcHandle);
                    httpResponse.pResponseInfo = (HttpApiTypes.HTTP_RESPONSE_INFO *)gcHandle.AddrOfPinnedObject();

                    knownHeaderInfo[httpResponse.ResponseInfoCount].Type   = HttpApiTypes.HTTP_RESPONSE_INFO_TYPE.HttpResponseInfoTypeMultipleKnownHeaders;
                    knownHeaderInfo[httpResponse.ResponseInfoCount].Length =
                        (uint)Marshal.SizeOf <HttpApiTypes.HTTP_MULTIPLE_KNOWN_HEADERS>();

                    HttpApiTypes.HTTP_MULTIPLE_KNOWN_HEADERS header = new HttpApiTypes.HTTP_MULTIPLE_KNOWN_HEADERS();

                    header.HeaderId = HttpApiTypes.HTTP_RESPONSE_HEADER_ID.Enum.HttpHeaderWwwAuthenticate;
                    header.Flags    = HttpApiTypes.HTTP_RESPONSE_INFO_FLAGS.PreserveOrder; // The docs say this is for www-auth only.

                    HttpApiTypes.HTTP_KNOWN_HEADER[] nativeHeaderValues = new HttpApiTypes.HTTP_KNOWN_HEADER[authChallenges.Count];
                    gcHandle = GCHandle.Alloc(nativeHeaderValues, GCHandleType.Pinned);
                    pinnedHeaders.Add(gcHandle);
                    header.KnownHeaders = (HttpApiTypes.HTTP_KNOWN_HEADER *)gcHandle.AddrOfPinnedObject();

                    for (int headerValueIndex = 0; headerValueIndex < authChallenges.Count; headerValueIndex++)
                    {
                        // Add Value
                        string headerValue = authChallenges[headerValueIndex];
                        byte[] bytes       = HeaderEncoding.GetBytes(headerValue);
                        nativeHeaderValues[header.KnownHeaderCount].RawValueLength = (ushort)bytes.Length;
                        gcHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
                        pinnedHeaders.Add(gcHandle);
                        nativeHeaderValues[header.KnownHeaderCount].pRawValue = (byte *)gcHandle.AddrOfPinnedObject();
                        header.KnownHeaderCount++;
                    }

                    // This type is a struct, not an object, so pinning it causes a boxed copy to be created. We can't do that until after all the fields are set.
                    gcHandle = GCHandle.Alloc(header, GCHandleType.Pinned);
                    pinnedHeaders.Add(gcHandle);
                    knownHeaderInfo[0].pInfo = (HttpApiTypes.HTTP_MULTIPLE_KNOWN_HEADERS *)gcHandle.AddrOfPinnedObject();

                    httpResponse.ResponseInfoCount = 1;
                }

                httpResponse.Response_V1.StatusCode = (ushort)httpStatusCode;
                string?statusDescription = HttpReasonPhrase.Get(httpStatusCode);
                uint   dataWritten       = 0;
                uint   statusCode;
                byte[] byteReason = statusDescription != null?HeaderEncoding.GetBytes(statusDescription) : Array.Empty <byte>();
                fixed(byte *pReason = byteReason)
                {
                    httpResponse.Response_V1.pReason      = (byte *)pReason;
                    httpResponse.Response_V1.ReasonLength = (ushort)byteReason.Length;

                    byte[] byteContentLength = new byte[] { (byte)'0' };
                    fixed(byte *pContentLength = byteContentLength)
                    {
                        (&httpResponse.Response_V1.Headers.KnownHeaders)[(int)HttpSysResponseHeader.ContentLength].pRawValue      = (byte *)pContentLength;
                        (&httpResponse.Response_V1.Headers.KnownHeaders)[(int)HttpSysResponseHeader.ContentLength].RawValueLength = (ushort)byteContentLength.Length;
                        httpResponse.Response_V1.Headers.UnknownHeaderCount = 0;

                        statusCode =
                            HttpApi.HttpSendHttpResponse(
                                _requestQueue.Handle,
                                requestId,
                                0,
                                &httpResponse,
                                null,
                                &dataWritten,
                                IntPtr.Zero,
                                0,
                                SafeNativeOverlapped.Zero,
                                IntPtr.Zero);
                    }
                }
                if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS)
                {
                    // if we fail to send a 401 something's seriously wrong, abort the request
                    HttpApi.HttpCancelHttpRequest(_requestQueue.Handle, requestId, IntPtr.Zero);
                }
            }
            finally
            {
                if (pinnedHeaders != null)
                {
                    foreach (GCHandle handle in pinnedHeaders)
                    {
                        if (handle.IsAllocated)
                        {
                            handle.Free();
                        }
                    }
                }
            }
        }
Exemple #14
0
    /*
     * 12.3
     * HttpSendHttpResponse() and HttpSendResponseEntityBody() Flag Values.
     * The following flags can be used on calls to HttpSendHttpResponse() and HttpSendResponseEntityBody() API calls:
     *
     #define HTTP_SEND_RESPONSE_FLAG_DISCONNECT          0x00000001
     #define HTTP_SEND_RESPONSE_FLAG_MORE_DATA           0x00000002
     #define HTTP_SEND_RESPONSE_FLAG_RAW_HEADER          0x00000004
     #define HTTP_SEND_RESPONSE_FLAG_VALID               0x00000007
     *
     * HTTP_SEND_RESPONSE_FLAG_DISCONNECT:
     *  specifies that the network connection should be disconnected immediately after
     *  sending the response, overriding the HTTP protocol's persistent connection features.
     * HTTP_SEND_RESPONSE_FLAG_MORE_DATA:
     *  specifies that additional entity body data will be sent by the caller. Thus,
     *  the last call HttpSendResponseEntityBody for a RequestId, will have this flag reset.
     * HTTP_SEND_RESPONSE_RAW_HEADER:
     *  specifies that a caller of HttpSendResponseEntityBody() is intentionally omitting
     *  a call to HttpSendHttpResponse() in order to bypass normal header processing. The
     *  actual HTTP header will be generated by the application and sent as entity body.
     *  This flag should be passed on the first call to HttpSendResponseEntityBody, and
     *  not after. Thus, flag is not applicable to HttpSendHttpResponse.
     */

    // TODO: Consider using HTTP_SEND_RESPONSE_RAW_HEADER with HttpSendResponseEntityBody instead of calling HttpSendHttpResponse.
    // This will give us more control of the bytes that hit the wire, including encodings, HTTP 1.0, etc..
    // It may also be faster to do this work in managed code and then pass down only one buffer.
    // What would we loose by bypassing HttpSendHttpResponse?
    //
    // TODO: Consider using the HTTP_SEND_RESPONSE_FLAG_BUFFER_DATA flag for most/all responses rather than just Opaque.
    internal unsafe uint SendHeaders(HttpApiTypes.HTTP_DATA_CHUNK[]?dataChunks,
                                     ResponseStreamAsyncResult?asyncResult,
                                     HttpApiTypes.HTTP_FLAGS flags,
                                     bool isOpaqueUpgrade)
    {
        Debug.Assert(!HasStarted, "HttpListenerResponse::SendHeaders()|SentHeaders is true.");

        _responseState = ResponseState.Started;
        var reasonPhrase = GetReasonPhrase(StatusCode);

        uint            statusCode;
        uint            bytesSent;
        List <GCHandle>?pinnedHeaders = SerializeHeaders(isOpaqueUpgrade);

        try
        {
            if (dataChunks != null)
            {
                if (pinnedHeaders == null)
                {
                    pinnedHeaders = new List <GCHandle>();
                }
                var handle = GCHandle.Alloc(dataChunks, GCHandleType.Pinned);
                pinnedHeaders.Add(handle);
                _nativeResponse.Response_V1.EntityChunkCount = (ushort)dataChunks.Length;
                _nativeResponse.Response_V1.pEntityChunks    = (HttpApiTypes.HTTP_DATA_CHUNK *)handle.AddrOfPinnedObject();
            }
            else if (asyncResult != null && asyncResult.DataChunks != null)
            {
                _nativeResponse.Response_V1.EntityChunkCount = asyncResult.DataChunkCount;
                _nativeResponse.Response_V1.pEntityChunks    = asyncResult.DataChunks;
            }
            else
            {
                _nativeResponse.Response_V1.EntityChunkCount = 0;
                _nativeResponse.Response_V1.pEntityChunks    = null;
            }

            var cachePolicy = new HttpApiTypes.HTTP_CACHE_POLICY();
            if (_cacheTtl.HasValue && _cacheTtl.Value > TimeSpan.Zero)
            {
                cachePolicy.Policy        = HttpApiTypes.HTTP_CACHE_POLICY_TYPE.HttpCachePolicyTimeToLive;
                cachePolicy.SecondsToLive = (uint)Math.Min(_cacheTtl.Value.Ticks / TimeSpan.TicksPerSecond, Int32.MaxValue);
            }

            byte[] reasonPhraseBytes = HeaderEncoding.GetBytes(reasonPhrase);
            fixed(byte *pReasonPhrase = reasonPhraseBytes)
            {
                _nativeResponse.Response_V1.ReasonLength = (ushort)reasonPhraseBytes.Length;
                _nativeResponse.Response_V1.pReason      = (byte *)pReasonPhrase;
                fixed(HttpApiTypes.HTTP_RESPONSE_V2 *pResponse = &_nativeResponse)
                {
                    statusCode =
                        HttpApi.HttpSendHttpResponse(
                            RequestContext.Server.RequestQueue.Handle,
                            Request.RequestId,
                            (uint)flags,
                            pResponse,
                            &cachePolicy,
                            &bytesSent,
                            IntPtr.Zero,
                            0,
                            asyncResult == null ? SafeNativeOverlapped.Zero : asyncResult.NativeOverlapped !,
                            IntPtr.Zero);

                    // GoAway is only supported on later versions. Retry.
                    if (statusCode == ErrorCodes.ERROR_INVALID_PARAMETER &&
                        (flags & HttpApiTypes.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_GOAWAY) != 0)
                    {
                        flags     &= ~HttpApiTypes.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_GOAWAY;
                        statusCode =
                            HttpApi.HttpSendHttpResponse(
                                RequestContext.Server.RequestQueue.Handle,
                                Request.RequestId,
                                (uint)flags,
                                pResponse,
                                &cachePolicy,
                                &bytesSent,
                                IntPtr.Zero,
                                0,
                                asyncResult == null ? SafeNativeOverlapped.Zero : asyncResult.NativeOverlapped !,
                                IntPtr.Zero);

                        // Succeeded without GoAway, disable them.
                        if (statusCode != ErrorCodes.ERROR_INVALID_PARAMETER)
                        {
                            SupportsGoAway = false;
                        }
                    }

                    if (asyncResult != null &&
                        statusCode == ErrorCodes.ERROR_SUCCESS &&
                        HttpSysListener.SkipIOCPCallbackOnSuccess)
                    {
                        asyncResult.BytesSent = bytesSent;
                        // The caller will invoke IOCompleted
                    }
                }
            }
        }
        finally
        {
            FreePinnedHeaders(pinnedHeaders);
        }
        return(statusCode);
    }
Exemple #15
0
    private unsafe List <GCHandle>?SerializeHeaders(bool isOpaqueUpgrade)
    {
        Headers.IsReadOnly = true; // Prohibit further modifications.
        HttpApiTypes.HTTP_UNKNOWN_HEADER[]? unknownHeaders = null;
        HttpApiTypes.HTTP_RESPONSE_INFO[]? knownHeaderInfo = null;
        List <GCHandle> pinnedHeaders;
        GCHandle        gcHandle;

        if (Headers.Count == 0)
        {
            return(null);
        }
        string headerName;
        string headerValue;
        int    lookup;

        byte[]? bytes = null;
        pinnedHeaders = new List <GCHandle>();

        int numUnknownHeaders    = 0;
        int numKnownMultiHeaders = 0;

        foreach (var headerPair in Headers)
        {
            if (headerPair.Value.Count == 0)
            {
                continue;
            }
            // See if this is an unknown header
            lookup = HttpApiTypes.HTTP_RESPONSE_HEADER_ID.IndexOfKnownHeader(headerPair.Key);

            // Http.Sys doesn't let us send the Connection: Upgrade header as a Known header.
            if (lookup == -1 ||
                (isOpaqueUpgrade && lookup == (int)HttpApiTypes.HTTP_RESPONSE_HEADER_ID.Enum.HttpHeaderConnection))
            {
                numUnknownHeaders += headerPair.Value.Count;
            }
            else if (headerPair.Value.Count > 1)
            {
                numKnownMultiHeaders++;
            }
            // else known single-value header.
        }

        try
        {
            fixed(HttpApiTypes.HTTP_KNOWN_HEADER *pKnownHeaders = &_nativeResponse.Response_V1.Headers.KnownHeaders)
            {
                foreach (var headerPair in Headers)
                {
                    if (headerPair.Value.Count == 0)
                    {
                        continue;
                    }
                    headerName = headerPair.Key;
                    StringValues headerValues = headerPair.Value;
                    lookup = HttpApiTypes.HTTP_RESPONSE_HEADER_ID.IndexOfKnownHeader(headerName);

                    // Http.Sys doesn't let us send the Connection: Upgrade header as a Known header.
                    if (lookup == -1 ||
                        (isOpaqueUpgrade && lookup == (int)HttpApiTypes.HTTP_RESPONSE_HEADER_ID.Enum.HttpHeaderConnection))
                    {
                        if (unknownHeaders == null)
                        {
                            unknownHeaders = new HttpApiTypes.HTTP_UNKNOWN_HEADER[numUnknownHeaders];
                            gcHandle       = GCHandle.Alloc(unknownHeaders, GCHandleType.Pinned);
                            pinnedHeaders.Add(gcHandle);
                            _nativeResponse.Response_V1.Headers.pUnknownHeaders = (HttpApiTypes.HTTP_UNKNOWN_HEADER *)gcHandle.AddrOfPinnedObject();
                        }

                        for (int headerValueIndex = 0; headerValueIndex < headerValues.Count; headerValueIndex++)
                        {
                            // Add Name
                            bytes = HeaderEncoding.GetBytes(headerName);
                            unknownHeaders[_nativeResponse.Response_V1.Headers.UnknownHeaderCount].NameLength = (ushort)bytes.Length;
                            gcHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
                            pinnedHeaders.Add(gcHandle);
                            unknownHeaders[_nativeResponse.Response_V1.Headers.UnknownHeaderCount].pName = (byte *)gcHandle.AddrOfPinnedObject();

                            // Add Value
                            headerValue = headerValues[headerValueIndex] ?? string.Empty;
                            bytes       = HeaderEncoding.GetBytes(headerValue);
                            unknownHeaders[_nativeResponse.Response_V1.Headers.UnknownHeaderCount].RawValueLength = (ushort)bytes.Length;
                            gcHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
                            pinnedHeaders.Add(gcHandle);
                            unknownHeaders[_nativeResponse.Response_V1.Headers.UnknownHeaderCount].pRawValue = (byte *)gcHandle.AddrOfPinnedObject();
                            _nativeResponse.Response_V1.Headers.UnknownHeaderCount++;
                        }
                    }
                    else if (headerPair.Value.Count == 1)
                    {
                        headerValue = headerValues[0] ?? string.Empty;
                        bytes       = HeaderEncoding.GetBytes(headerValue);
                        pKnownHeaders[lookup].RawValueLength = (ushort)bytes.Length;
                        gcHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
                        pinnedHeaders.Add(gcHandle);
                        pKnownHeaders[lookup].pRawValue = (byte *)gcHandle.AddrOfPinnedObject();
                    }
                    else
                    {
                        if (knownHeaderInfo == null)
                        {
                            knownHeaderInfo = new HttpApiTypes.HTTP_RESPONSE_INFO[numKnownMultiHeaders];
                            gcHandle        = GCHandle.Alloc(knownHeaderInfo, GCHandleType.Pinned);
                            pinnedHeaders.Add(gcHandle);
                            _nativeResponse.pResponseInfo = (HttpApiTypes.HTTP_RESPONSE_INFO *)gcHandle.AddrOfPinnedObject();
                        }

                        knownHeaderInfo[_nativeResponse.ResponseInfoCount].Type   = HttpApiTypes.HTTP_RESPONSE_INFO_TYPE.HttpResponseInfoTypeMultipleKnownHeaders;
                        knownHeaderInfo[_nativeResponse.ResponseInfoCount].Length = (uint)Marshal.SizeOf <HttpApiTypes.HTTP_MULTIPLE_KNOWN_HEADERS>();

                        HttpApiTypes.HTTP_MULTIPLE_KNOWN_HEADERS header = new HttpApiTypes.HTTP_MULTIPLE_KNOWN_HEADERS();

                        header.HeaderId = (HttpApiTypes.HTTP_RESPONSE_HEADER_ID.Enum)lookup;
                        header.Flags    = HttpApiTypes.HTTP_RESPONSE_INFO_FLAGS.PreserveOrder; // TODO: The docs say this is for www-auth only.

                        HttpApiTypes.HTTP_KNOWN_HEADER[] nativeHeaderValues = new HttpApiTypes.HTTP_KNOWN_HEADER[headerValues.Count];
                        gcHandle = GCHandle.Alloc(nativeHeaderValues, GCHandleType.Pinned);
                        pinnedHeaders.Add(gcHandle);
                        header.KnownHeaders = (HttpApiTypes.HTTP_KNOWN_HEADER *)gcHandle.AddrOfPinnedObject();

                        for (int headerValueIndex = 0; headerValueIndex < headerValues.Count; headerValueIndex++)
                        {
                            // Add Value
                            headerValue = headerValues[headerValueIndex] ?? string.Empty;
                            bytes       = HeaderEncoding.GetBytes(headerValue);
                            nativeHeaderValues[header.KnownHeaderCount].RawValueLength = (ushort)bytes.Length;
                            gcHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
                            pinnedHeaders.Add(gcHandle);
                            nativeHeaderValues[header.KnownHeaderCount].pRawValue = (byte *)gcHandle.AddrOfPinnedObject();
                            header.KnownHeaderCount++;
                        }

                        // This type is a struct, not an object, so pinning it causes a boxed copy to be created. We can't do that until after all the fields are set.
                        gcHandle = GCHandle.Alloc(header, GCHandleType.Pinned);
                        pinnedHeaders.Add(gcHandle);
                        knownHeaderInfo[_nativeResponse.ResponseInfoCount].pInfo = (HttpApiTypes.HTTP_MULTIPLE_KNOWN_HEADERS *)gcHandle.AddrOfPinnedObject();

                        _nativeResponse.ResponseInfoCount++;
                    }
                }
            }
        }
        catch
        {
            FreePinnedHeaders(pinnedHeaders);
            throw;
        }
        return(pinnedHeaders);
    }
        internal DataParseStatus ParseHeaders(byte[] buffer, int size, ref int unparsed, ref int totalResponseHeadersLength, int maximumResponseHeadersLength)
        {
            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;

            DataParseStatus parseStatus = DataParseStatus.Invalid;

            GlobalLog.Enter("WebHeaderCollection::ParseHeaders(): size:" + size.ToString() + ", unparsed:" + unparsed.ToString() + " buffer:[" + Encoding.ASCII.GetString(buffer, unparsed, Math.Min(256, size - unparsed)) + "]");
            //GlobalLog.Print("WebHeaderCollection::ParseHeaders(): advancing. index=" + index.ToString() + " current character='" + ((char)buffer[index]).ToString() + "':" + ((int)buffer[index]).ToString() + " next character='" + ((char)buffer[index+1]).ToString() + "':" + ((int)buffer[index+1]).ToString());
            //GlobalLog.Print("WebHeaderCollection::ParseHeaders(): headerName:[" + headerName + "], headerValue:[" + headerValue + "], headerMultiLineValue:[" + headerMultiLineValue + "] numberOfLf=" + numberOfLf.ToString() + " index=" + index.ToString());
            //GlobalLog.Print("WebHeaderCollection::ParseHeaders(): headerNameStartOffset=" + headerNameStartOffset + " headerNameEndOffset=" + headerNameEndOffset + " headerValueStartOffset=" + headerValueStartOffset + " headerValueEndOffset=" + headerValueEndOffset);

            //
            // 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 && (buffer[index] == ' ' || buffer[index] == '\t'))
                    {
                        index++;
                        if (maximumResponseHeadersLength >= 0 && ++localTotalResponseHeadersLength >= maximumResponseHeadersLength)
                        {
                            parseStatus = DataParseStatus.DataTooBig;
                            goto quit;
                        }
                    }
                    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 && (buffer[index] != ':' && buffer[index] != '\n'))
                {
                    if (buffer[index] > ' ')
                    {
                        //
                        // if thre'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;
                    }
                }
                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 && (buffer[index] <= ' ' || buffer[index] == ':'))
                {
                    if (buffer[index] == '\n')
                    {
                        numberOfLf++;
                        spaceAfterLf = index + 1 < size && (buffer[index + 1] == ' ' || buffer[index + 1] == '\t');
                    }
                    index++;
                    if (maximumResponseHeadersLength >= 0 && ++localTotalResponseHeadersLength >= maximumResponseHeadersLength)
                    {
                        parseStatus = DataParseStatus.DataTooBig;
                        goto quit;
                    }
                }
                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 && buffer[index] != '\n')
                {
                    if (buffer[index] > ' ')
                    {
                        headerValueEndOffset = index;
                    }
                    index++;
                    if (maximumResponseHeadersLength >= 0 && ++localTotalResponseHeadersLength >= maximumResponseHeadersLength)
                    {
                        parseStatus = DataParseStatus.DataTooBig;
                        goto quit;
                    }
                }
                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 && (buffer[index] == '\r' || buffer[index] == '\n'))
                {
                    if (buffer[index] == '\n')
                    {
                        numberOfLf++;
                    }
                    index++;
                    if (maximumResponseHeadersLength >= 0 && ++localTotalResponseHeadersLength >= maximumResponseHeadersLength)
                    {
                        parseStatus = DataParseStatus.DataTooBig;
                        goto quit;
                    }
                }
                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(buffer, 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 && (buffer[index] == ' ' || buffer[index] == '\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(buffer, 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 base.Add() key:[" + headerName + "], value:[" + headerMultiLineValue + "]");
                    base.Add(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());
            return(parseStatus);
        }
        internal static string EncodeHeader(string header, string charset, HeaderEncoding headerEncoding)
        {
            Encoding encoding = null;

            try
            {
                encoding = Encoding.GetEncoding(charset);
            }
            catch
            {
                encoding = Encoding.UTF8;
                charset  = "utf-8";
            }

            string encodedHeader = "";

            if (headerEncoding == HeaderEncoding.QuotedPrintable)
            {
                string[] lines = SplitToLinesToEncode(header, 68);

                for (int i = 0; i < lines.Length; i++)
                {
                    if (HasToEncodeHeader(lines[i]))
                    {
                        string encodedLine = EncodeHeaderQuotedPrintable(lines[i], encoding);

                        encodedLine = "=?" + charset + "?" + EnumUtil.ParseHeaderEncoding(headerEncoding) + "?" + encodedLine + "?=";

                        if (encodedHeader.EndsWith(" ") || encodedHeader.EndsWith("\t"))
                        {
                            encodedHeader += encodedLine;
                        }
                        else
                        {
                            encodedHeader += " " + encodedLine;
                        }
                    }
                    else
                    {
                        if (encodedHeader.EndsWith(" ") || encodedHeader.EndsWith("\t"))
                        {
                            encodedHeader += lines[i];
                        }
                        else
                        {
                            encodedHeader += " " + lines[i];
                        }
                    }
                }
            }
            else if (headerEncoding == HeaderEncoding.Binary)
            {
                string[] lines = SplitToLinesToEncode(header, 68);

                for (int i = 0; i < lines.Length; i++)
                {
                    if (HasToEncodeHeader(lines[i]))
                    {
                        byte[] headerBuffer = encoding.GetBytes(lines[i]);
                        string encodedLine  = Convert.ToBase64String(headerBuffer, 0, headerBuffer.Length);

                        encodedLine = "=?" + charset + "?" + EnumUtil.ParseHeaderEncoding(headerEncoding) + "?" + encodedLine + "?=";

                        if (encodedHeader.EndsWith(" ") || encodedHeader.EndsWith("\t"))
                        {
                            encodedHeader += encodedLine;
                        }
                        else
                        {
                            encodedHeader += " " + encodedLine;
                        }
                    }
                    else
                    {
                        if (encodedHeader.EndsWith(" ") || encodedHeader.EndsWith("\t"))
                        {
                            encodedHeader += lines[i];
                        }
                        else
                        {
                            encodedHeader += " " + lines[i];
                        }
                    }
                }
            }

            return(encodedHeader);
        }
        internal static string DecodeHeader(string encodedWord, ref HeaderEncoding headerEncoding)
        {
            int encodedWordStartIndex = encodedWord.IndexOf("=?");

            if (encodedWordStartIndex == -1)
            {
                return(encodedWord);
            }

            int encodedWordEndIndex = encodedWord.IndexOf("?=", encodedWordStartIndex);

            if (encodedWordEndIndex == -1)
            {
                return(encodedWord);
            }

            if (encodedWordEndIndex < encodedWordStartIndex)
            {
                return(encodedWord);
            }

            //remove SPACE between encoded words
            encodedWord = encodedWord.Replace("?= =?", "?==?");
            encodedWord = encodedWord.Replace("?=  =?", "?==?");
            encodedWord = encodedWord.Replace("?=   =?", "?==?");
            encodedWord = encodedWord.Replace("?=    =?", "?==?");
            encodedWord = encodedWord.Replace("?=     =?", "?==?");
            encodedWord = encodedWord.Replace("?=      =?", "?==?");
            encodedWord = encodedWord.Replace("?=       =?", "?==?");
            encodedWord = encodedWord.Replace("?=        =?", "?==?");

            while (encodedWord.IndexOf("?==?") > 0)
            {
                bool replaced = false;

                int startIndex = encodedWord.IndexOf("?==?");

                int firstQuestionMarkIndex = encodedWord.IndexOf("?", startIndex + 4);

                if (firstQuestionMarkIndex > startIndex)
                {
                    int secondQuestionMarkIndex = encodedWord.IndexOf("?", firstQuestionMarkIndex + 1);

                    if (secondQuestionMarkIndex > firstQuestionMarkIndex)
                    {
                        encodedWord = encodedWord.Substring(0, startIndex) + encodedWord.Substring(secondQuestionMarkIndex + 1);
                        replaced    = true;
                    }
                }

                if (!replaced)
                {
                    break;
                }
            }

            string decodedHeader = encodedWord.Substring(0, encodedWordStartIndex);

            while (true)
            {
                int firstQuestionMarkIndex  = encodedWord.IndexOf("?", encodedWordStartIndex + 2);
                int secondQuestionMarkIndex = encodedWord.IndexOf("?", firstQuestionMarkIndex + 1);

                if (firstQuestionMarkIndex > -1 && secondQuestionMarkIndex > -1)
                {
                    encodedWordEndIndex = encodedWord.IndexOf("?=", secondQuestionMarkIndex + 1);

                    if (encodedWordEndIndex == -1)
                    {
                        encodedWordEndIndex = encodedWord.Length - 2;
                    }

                    String headerCharSet  = encodedWord.Substring(encodedWordStartIndex + 2, firstQuestionMarkIndex - encodedWordStartIndex - 2);
                    string encodingMethod = encodedWord.Substring(firstQuestionMarkIndex + 1, 1);

                    string encodedText = null;

                    if (encodedWordEndIndex > -1)
                    {
                        encodedText = encodedWord.Substring(secondQuestionMarkIndex + 1, encodedWordEndIndex - secondQuestionMarkIndex - 1);
                    }
                    else
                    {
                        encodedText = encodedWord.Substring(secondQuestionMarkIndex + 1);
                    }


                    if (encodingMethod == "Q" || encodingMethod == "q")
                    {
                        headerEncoding = HeaderEncoding.QuotedPrintable;
                        Encoding encoding = null;

                        try
                        {
                            encoding = Encoding.GetEncoding(headerCharSet);
                        }
                        catch
                        {
                            encoding      = Encoding.UTF8;
                            headerCharSet = "utf-8";
                        }

                        string decodedWord = DecodeQuotedPrintable(encodedText, encoding);

                        decodedWord = decodedWord.Replace("_", " ");

                        decodedHeader += decodedWord;
                    }
                    else if (encodingMethod == "B" || encodingMethod == "b")
                    {
                        headerEncoding = HeaderEncoding.Binary;
                        Encoding encoding = null;

                        try
                        {
                            encoding = Encoding.GetEncoding(headerCharSet);
                        }
                        catch
                        {
                            encoding      = Encoding.UTF8;
                            headerCharSet = "utf-8";
                        }

                        string decodedWord = DecodeBase64(encodedText, encoding);

                        decodedWord = decodedWord.Replace("_", " ");

                        decodedHeader += decodedWord;
                    }
                }

                encodedWordStartIndex = encodedWord.IndexOf("=?", encodedWordEndIndex + 2);

                if (encodedWordStartIndex == -1)
                {
                    decodedHeader += encodedWord.Substring(encodedWordEndIndex + 2, encodedWord.Length - encodedWordEndIndex - 2);
                    break;
                }

                int lastEncodingWordEndIndex = encodedWordEndIndex;

                encodedWordEndIndex = encodedWord.IndexOf("?=", encodedWordStartIndex);

                if (encodedWordEndIndex == -1)
                {
                    decodedHeader += encodedWord.Substring(lastEncodingWordEndIndex + 2, encodedWordStartIndex - lastEncodingWordEndIndex - 2);
                    break;
                }
                else
                {
                    decodedHeader += encodedWord.Substring(lastEncodingWordEndIndex + 2, encodedWordStartIndex - lastEncodingWordEndIndex - 2);
                }
            }

            return(decodedHeader);
        }
Exemple #19
0
        // From HttpSys, except does not write to response
        private unsafe List <GCHandle> SerializeHeaders(HttpApiTypes.HTTP_RESPONSE_V2 *pHttpResponse)
        {
            HttpResponseHeaders.IsReadOnly = true;
            HttpApiTypes.HTTP_UNKNOWN_HEADER[] unknownHeaders  = null;
            HttpApiTypes.HTTP_RESPONSE_INFO[]  knownHeaderInfo = null;
            var      pinnedHeaders = new List <GCHandle>();
            GCHandle gcHandle;

            if (HttpResponseHeaders.Count == 0)
            {
                return(null);
            }
            string headerName;
            string headerValue;
            int    lookup;
            var    numUnknownHeaders    = 0;
            int    numKnownMultiHeaders = 0;

            byte[] bytes = null;

            foreach (var headerPair in HttpResponseHeaders)
            {
                if (headerPair.Value.Count == 0)
                {
                    continue;
                }
                lookup = HttpApiTypes.HTTP_RESPONSE_HEADER_ID.IndexOfKnownHeader(headerPair.Key);
                if (lookup == -1) // TODO handle opaque stream upgrade?
                {
                    numUnknownHeaders++;
                }
                else if (headerPair.Value.Count > 1)
                {
                    numKnownMultiHeaders++;
                }
            }

            try
            {
                var pKnownHeaders = &pHttpResponse->Response_V1.Headers.KnownHeaders;
                foreach (var headerPair in HttpResponseHeaders)
                {
                    if (headerPair.Value.Count == 0)
                    {
                        continue;
                    }
                    headerName = headerPair.Key;
                    StringValues headerValues = headerPair.Value;
                    lookup = HttpApiTypes.HTTP_RESPONSE_HEADER_ID.IndexOfKnownHeader(headerName);
                    if (lookup == -1)
                    {
                        if (unknownHeaders == null)
                        {
                            unknownHeaders = new HttpApiTypes.HTTP_UNKNOWN_HEADER[numUnknownHeaders];
                            gcHandle       = GCHandle.Alloc(unknownHeaders, GCHandleType.Pinned);
                            pinnedHeaders.Add(gcHandle);
                            pHttpResponse->Response_V1.Headers.pUnknownHeaders    = (HttpApiTypes.HTTP_UNKNOWN_HEADER *)gcHandle.AddrOfPinnedObject();
                            pHttpResponse->Response_V1.Headers.UnknownHeaderCount = 0; // to remove the iis header for server=...
                        }

                        for (var headerValueIndex = 0; headerValueIndex < headerValues.Count; headerValueIndex++)
                        {
                            // Add Name
                            bytes = HeaderEncoding.GetBytes(headerName);
                            unknownHeaders[pHttpResponse->Response_V1.Headers.UnknownHeaderCount].NameLength = (ushort)bytes.Length;
                            gcHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
                            pinnedHeaders.Add(gcHandle);
                            unknownHeaders[pHttpResponse->Response_V1.Headers.UnknownHeaderCount].pName = (byte *)gcHandle.AddrOfPinnedObject();

                            // Add Value
                            headerValue = headerValues[headerValueIndex] ?? string.Empty;
                            bytes       = HeaderEncoding.GetBytes(headerValue);
                            unknownHeaders[pHttpResponse->Response_V1.Headers.UnknownHeaderCount].RawValueLength = (ushort)bytes.Length;
                            gcHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
                            pinnedHeaders.Add(gcHandle);
                            unknownHeaders[pHttpResponse->Response_V1.Headers.UnknownHeaderCount].pRawValue = (byte *)gcHandle.AddrOfPinnedObject();
                            pHttpResponse->Response_V1.Headers.UnknownHeaderCount++;
                        }
                    }
                    else if (headerPair.Value.Count == 1)
                    {
                        headerValue = headerValues[0] ?? string.Empty;
                        bytes       = HeaderEncoding.GetBytes(headerValue);
                        pKnownHeaders[lookup].RawValueLength = (ushort)bytes.Length;
                        gcHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
                        pinnedHeaders.Add(gcHandle);
                        pKnownHeaders[lookup].pRawValue = (byte *)gcHandle.AddrOfPinnedObject();
                    }
                    else
                    {
                        if (knownHeaderInfo == null)
                        {
                            knownHeaderInfo = new HttpApiTypes.HTTP_RESPONSE_INFO[numKnownMultiHeaders];
                            gcHandle        = GCHandle.Alloc(knownHeaderInfo, GCHandleType.Pinned);
                            pinnedHeaders.Add(gcHandle);
                            pHttpResponse->pResponseInfo = (HttpApiTypes.HTTP_RESPONSE_INFO *)gcHandle.AddrOfPinnedObject();
                        }

                        knownHeaderInfo[pHttpResponse->ResponseInfoCount].Type   = HttpApiTypes.HTTP_RESPONSE_INFO_TYPE.HttpResponseInfoTypeMultipleKnownHeaders;
                        knownHeaderInfo[pHttpResponse->ResponseInfoCount].Length = (uint)Marshal.SizeOf <HttpApiTypes.HTTP_MULTIPLE_KNOWN_HEADERS>();

                        var header = new HttpApiTypes.HTTP_MULTIPLE_KNOWN_HEADERS();

                        header.HeaderId = (HttpApiTypes.HTTP_RESPONSE_HEADER_ID.Enum)lookup;
                        header.Flags    = HttpApiTypes.HTTP_RESPONSE_INFO_FLAGS.PreserveOrder; // TODO: The docs say this is for www-auth only.

                        var nativeHeaderValues = new HttpApiTypes.HTTP_KNOWN_HEADER[headerValues.Count];
                        gcHandle = GCHandle.Alloc(nativeHeaderValues, GCHandleType.Pinned);
                        pinnedHeaders.Add(gcHandle);
                        header.KnownHeaders = (HttpApiTypes.HTTP_KNOWN_HEADER *)gcHandle.AddrOfPinnedObject();

                        for (int headerValueIndex = 0; headerValueIndex < headerValues.Count; headerValueIndex++)
                        {
                            // Add Value
                            headerValue = headerValues[headerValueIndex] ?? string.Empty;
                            bytes       = HeaderEncoding.GetBytes(headerValue);
                            nativeHeaderValues[header.KnownHeaderCount].RawValueLength = (ushort)bytes.Length;
                            gcHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
                            pinnedHeaders.Add(gcHandle);
                            nativeHeaderValues[header.KnownHeaderCount].pRawValue = (byte *)gcHandle.AddrOfPinnedObject();
                            header.KnownHeaderCount++;
                        }

                        // This type is a struct, not an object, so pinning it causes a boxed copy to be created. We can't do that until after all the fields are set.
                        gcHandle = GCHandle.Alloc(header, GCHandleType.Pinned);
                        pinnedHeaders.Add(gcHandle);
                        knownHeaderInfo[pHttpResponse->ResponseInfoCount].pInfo = (HttpApiTypes.HTTP_MULTIPLE_KNOWN_HEADERS *)gcHandle.AddrOfPinnedObject();

                        pHttpResponse->ResponseInfoCount++;
                    }
                }
            }
            catch (Exception)
            {
                FreePinnedHeaders(pinnedHeaders);
                throw;
            }
            return(pinnedHeaders);
        }
 private static IDictionary<string, IHeaderFieldParser> CreateHeaderFieldList()
 {
     // NOTE: Header fields not mentioned here are treated as unstructured
       fieldMap = new Dictionary<string,
     IHeaderFieldParser>();
     fieldMap["content-return"] = new
     HeaderX400ContentReturn();
     fieldMap["x400-content-return"] = new HeaderX400ContentReturn();
     fieldMap["delivery-date"] = new HeaderDeliveryDate();
     fieldMap["priority"] = new HeaderPriority();
     fieldMap["importance"] = new HeaderImportance();
     fieldMap["sensitivity"] = new HeaderSensitivity();
     fieldMap["reply-by"] = new HeaderDate();
     fieldMap["x400-content-identifier"] = new HeaderX400ContentIdentifier();
     fieldMap["x400-received"] = new HeaderX400Received();
     fieldMap["x400-mts-identifier"] = new HeaderX400MtsIdentifier();
     fieldMap["x400-trace"] = new HeaderX400Received();
     fieldMap["x400-originator"] = new HeaderX400Originator();
     fieldMap["x400-recipients"] = new HeaderX400Recipients();
     fieldMap["conversion"] = new HeaderConversion();
     fieldMap["conversion-with-loss"] = new HeaderConversionWithLoss();
     fieldMap["supersedes"] = new HeaderSupersedes();
     fieldMap["expires"] = new HeaderDate();
     fieldMap["autoforwarded"] = new HeaderAutoforwarded();
     fieldMap["generate-delivery-report"] = new HeaderGenerateDeliveryReport();
     fieldMap["incomplete-copy"] = new HeaderIncompleteCopy();
     fieldMap["prevent-nondelivery-report"] = new HeaderPreventNondeliveryReport();
     fieldMap["alternate-recipient"] = new HeaderAlternateRecipient();
     fieldMap["disclose-recipients"] = new HeaderDiscloseRecipients();
     fieldMap["expanded-date"] = new HeaderExpandedDate();
     fieldMap["newsgroups"] = new HeaderNewsgroups();
     fieldMap["path"] = new HeaderPath();
     fieldMap["archive"] = new HeaderArchive();
     fieldMap["control"] = new HeaderControl();
     fieldMap["distribution"] = new HeaderDistribution();
     fieldMap["followup-to"] = new HeaderFollowupTo();
     fieldMap["injection-date"] = new HeaderInjectionDate();
     fieldMap["injection-info"] = new HeaderInjectionInfo();
     fieldMap["user-agent"] = new HeaderUserAgent();
     fieldMap["xref"] = new HeaderXref();
     fieldMap["nntp-posting-date"] = new HeaderInjectionDate();
     fieldMap["nntp-posting-host"] = new HeaderNntpPostingHost();
     fieldMap["accept-language"] = new HeaderAcceptLanguage();
     fieldMap["archived-at"] = new HeaderArchivedAt();
     fieldMap["authentication-results"] = new HeaderAuthenticationResults();
     fieldMap["auto-submitted"] = new HeaderAutoSubmitted();
     fieldMap["base"] = new HeaderContentBase();
     fieldMap["bcc"] = new HeaderBcc();
     fieldMap["cc"] = new HeaderTo();
     fieldMap["content-base"] = new HeaderContentBase();
     fieldMap["content-disposition"] = new HeaderContentDisposition();
     fieldMap["content-duration"] = new HeaderContentDuration();
     fieldMap["content-id"] = new HeaderContentId();
     fieldMap["content-language"] = new HeaderContentLanguage();
     fieldMap["content-location"] = new HeaderContentLocation();
     fieldMap["content-md5"] = new HeaderContentMd5();
     fieldMap["content-transfer-encoding"] = new HeaderContentTransferEncoding();
     fieldMap["content-type"] = new HeaderContentType();
     fieldMap["date"] = new HeaderDate();
     fieldMap["deferred-delivery"] = new HeaderDeferredDelivery();
     fieldMap["disposition-notification-options"] = new
       HeaderDispositionNotificationOptions();
     fieldMap["disposition-notification-to"] = new HeaderDispositionNotificationTo();
     fieldMap["mmhs-authorizing-users"] = new HeaderMmhsAuthorizingUsers();
     fieldMap["dkim-signature"] = new HeaderDkimSignature();
     fieldMap["ediint-features"] = new HeaderEdiintFeatures();
     fieldMap["eesst-version"] = new HeaderEesstVersion();
     fieldMap["encoding"] = new HeaderEncoding();
     fieldMap["encrypted"] = new HeaderEncrypted();
     fieldMap["expiry-date"] = new HeaderDate();
     fieldMap["from"] = new HeaderFrom();
     fieldMap["in-reply-to"] = new HeaderInReplyTo();
     fieldMap["jabber-id"] = new HeaderJabberId();
     fieldMap["keywords"] = new HeaderKeywords();
     fieldMap["language"] = new HeaderLanguage();
     fieldMap["latest-delivery-time"] = new HeaderLatestDeliveryTime();
     fieldMap["list-id"] = new HeaderListId();
     fieldMap["message-context"] = new HeaderMessageContext();
     fieldMap["message-id"] = new HeaderMessageId();
     fieldMap["mime-version"] = new HeaderMimeVersion();
     fieldMap["mmhs-acp127-message-identifier"] = new
       HeaderMmhsAcp127MessageIdentifier();
     fieldMap["mmhs-codress-message-indicator"] = new
       HeaderMmhsCodressMessageIndicator();
     fieldMap["mmhs-copy-precedence"] = new HeaderMmhsCopyPrecedence();
     fieldMap["mmhs-exempted-address"] = new HeaderMmhsExemptedAddress();
     fieldMap["mmhs-extended-authorisation-info"] = new
       HeaderMmhsExtendedAuthorisationInfo();
     fieldMap["mmhs-handling-instructions"] = new HeaderMmhsHandlingInstructions();
     fieldMap["mmhs-message-instructions"] = new HeaderMmhsMessageInstructions();
     fieldMap["mmhs-message-type"] = new HeaderMmhsMessageType();
     fieldMap["mmhs-originator-plad"] = new HeaderMmhsOriginatorPlad();
     fieldMap["mmhs-originator-reference"] = new HeaderMmhsOriginatorReference();
     fieldMap["mmhs-other-recipients-indicator-cc"] = new
       HeaderMmhsOtherRecipientsIndicatorCc();
     fieldMap["mmhs-other-recipients-indicator-to"] = new
       HeaderMmhsOtherRecipientsIndicatorTo();
     fieldMap["mmhs-primary-precedence"] = new HeaderMmhsPrimaryPrecedence();
     fieldMap["mmhs-subject-indicator-codes"] = new
       HeaderMmhsSubjectIndicatorCodes();
     fieldMap["mt-priority"] = new HeaderMtPriority();
     fieldMap["obsoletes"] = new HeaderObsoletes();
     fieldMap["original-from"] = new HeaderFrom();
     fieldMap["original-message-id"] = new HeaderMessageId();
     fieldMap["original-recipient"] = new HeaderOriginalRecipient();
     fieldMap["received"] = new HeaderReceived();
     fieldMap["received-spf"] = new HeaderReceivedSpf();
     fieldMap["references"] = new HeaderInReplyTo();
     fieldMap["reply-to"] = new HeaderResentTo();
     fieldMap["require-recipient-valid-since"] = new
       HeaderRequireRecipientValidSince();
     fieldMap["resent-bcc"] = new HeaderBcc();
     fieldMap["resent-cc"] = new HeaderResentTo();
     fieldMap["resent-date"] = new HeaderDate();
     fieldMap["resent-from"] = new HeaderFrom();
     fieldMap["resent-message-id"] = new HeaderMessageId();
     fieldMap["resent-reply-to"] = new HeaderResentTo();
     fieldMap["resent-sender"] = new HeaderSender();
     fieldMap["resent-to"] = new HeaderResentTo();
     fieldMap["return-path"] = new HeaderReturnPath();
     fieldMap["sender"] = new HeaderSender();
     fieldMap["sio-label"] = new HeaderSioLabel();
     fieldMap["sio-label-history"] = new HeaderSioLabel();
     fieldMap["solicitation"] = new HeaderSolicitation();
     fieldMap["to"] = new HeaderTo();
     fieldMap["vbr-info"] = new HeaderVbrInfo();
     fieldMap["x-archived-at"] = new HeaderXArchivedAt();
     fieldMap["x-mittente"] = new HeaderSender();
     fieldMap["x-ricevuta"] = new HeaderXRicevuta();
     fieldMap["x-riferimento-message-id"] = new HeaderMessageId();
     fieldMap["x-tiporicevuta"] = new HeaderXTiporicevuta();
     fieldMap["x-trasporto"] = new HeaderXTrasporto();
     fieldMap["x-verificasicurezza"] = new HeaderXVerificasicurezza();
       return fieldMap;
 }