Esempio n. 1
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);
    }
Esempio n. 2
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);
    }
Esempio n. 3
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);
        }