Beispiel #1
0
        public void Trim_VariousInputs_ReturnsExpectedSubstring(string valueString, string expected)
        {
            char[] value      = valueString.ToCharArray();
            int    startIndex = 0;
            int    length     = value.Length;

            CharArrayHelpers.Trim(value, ref startIndex, ref length);

            Assert.Equal(expected, new string(value, startIndex, length));
        }
Beispiel #2
0
        /// <summary>
        /// Reads a header line.
        /// Empty header lines are skipped, as are malformed header lines that are missing a colon character.
        /// </summary>
        /// <returns>true if the next header was read successfully, or false if all characters have been read.</returns>
        public bool ReadHeader([NotNullWhen(true)] out string?name, [NotNullWhen(true)] out string?value)
        {
            int startIndex;
            int length;

            while (ReadLine(out startIndex, out length))
            {
                // Skip empty lines.
                if (length == 0)
                {
                    continue;
                }

                int colonIndex = Array.IndexOf(_buffer, ':', startIndex, length);

                // Skip malformed header lines that are missing the colon character.
                if (colonIndex == -1)
                {
                    continue;
                }

                int nameLength = colonIndex - startIndex;

                // If it's a known header name, use the known name instead of allocating a new string.
                if (!HttpKnownHeaderNames.TryGetHeaderName(_buffer, startIndex, nameLength, out name))
                {
                    name = new string(_buffer, startIndex, nameLength);
                }

                // Normalize header value by trimming whitespace.
                int valueStartIndex = colonIndex + 1;
                int valueLength     = startIndex + length - colonIndex - 1;
                CharArrayHelpers.Trim(_buffer, ref valueStartIndex, ref valueLength);

                value = HttpKnownHeaderNames.GetHeaderValue(name, _buffer, valueStartIndex, valueLength);

                return(true);
            }

            name  = null;
            value = null;
            return(false);
        }
Beispiel #3
0
        public static HttpResponseMessage CreateResponseMessage(
            WinHttpRequestState state,
            bool doManualDecompressionCheck)
        {
            HttpRequestMessage request         = state.RequestMessage;
            SafeWinHttpHandle  requestHandle   = state.RequestHandle;
            CookieUsePolicy    cookieUsePolicy = state.Handler.CookieUsePolicy;
            CookieContainer    cookieContainer = state.Handler.CookieContainer;
            var  response             = new HttpResponseMessage();
            bool stripEncodingHeaders = false;

            // Create a single buffer to use for all subsequent WinHttpQueryHeaders string interop calls.
            // This buffer is the length needed for WINHTTP_QUERY_RAW_HEADERS_CRLF, which includes the status line
            // and all headers separated by CRLF, so it should be large enough for any individual status line or header queries.
            int bufferLength = GetResponseHeaderCharBufferLength(requestHandle, Interop.WinHttp.WINHTTP_QUERY_RAW_HEADERS_CRLF);

            char[] buffer = ArrayPool <char> .Shared.Rent(bufferLength);

            try
            {
                // Get HTTP version, status code, reason phrase from the response headers.

                if (IsResponseHttp2(requestHandle))
                {
                    response.Version = WinHttpHandler.HttpVersion20;
                }
                else
                {
                    int versionLength = GetResponseHeader(requestHandle, Interop.WinHttp.WINHTTP_QUERY_VERSION, buffer);
                    response.Version =
                        CharArrayHelpers.EqualsOrdinalAsciiIgnoreCase("HTTP/1.1", buffer, 0, versionLength) ? HttpVersionInternal.Version11 :
                        CharArrayHelpers.EqualsOrdinalAsciiIgnoreCase("HTTP/1.0", buffer, 0, versionLength) ? HttpVersionInternal.Version10 :
                        WinHttpHandler.HttpVersionUnknown;
                }

                response.StatusCode = (HttpStatusCode)GetResponseHeaderNumberInfo(
                    requestHandle,
                    Interop.WinHttp.WINHTTP_QUERY_STATUS_CODE);

                int reasonPhraseLength = GetResponseHeader(requestHandle, Interop.WinHttp.WINHTTP_QUERY_STATUS_TEXT, buffer);
                response.ReasonPhrase = reasonPhraseLength > 0 ?
                                        GetReasonPhrase(response.StatusCode, buffer, reasonPhraseLength) :
                                        string.Empty;

                // Create response stream and wrap it in a StreamContent object.
                var responseStream = new WinHttpResponseStream(requestHandle, state);
                state.RequestHandle = null; // ownership successfully transfered to WinHttpResponseStram.
                Stream decompressedStream = responseStream;

                if (doManualDecompressionCheck)
                {
                    int contentEncodingStartIndex = 0;
                    int contentEncodingLength     = GetResponseHeader(
                        requestHandle,
                        Interop.WinHttp.WINHTTP_QUERY_CONTENT_ENCODING,
                        buffer);

                    CharArrayHelpers.Trim(buffer, ref contentEncodingStartIndex, ref contentEncodingLength);

                    if (contentEncodingLength > 0)
                    {
                        if (CharArrayHelpers.EqualsOrdinalAsciiIgnoreCase(
                                EncodingNameGzip, buffer, contentEncodingStartIndex, contentEncodingLength))
                        {
                            decompressedStream   = new GZipStream(responseStream, CompressionMode.Decompress);
                            stripEncodingHeaders = true;
                        }
                        else if (CharArrayHelpers.EqualsOrdinalAsciiIgnoreCase(
                                     EncodingNameDeflate, buffer, contentEncodingStartIndex, contentEncodingLength))
                        {
                            decompressedStream   = new DeflateStream(responseStream, CompressionMode.Decompress);
                            stripEncodingHeaders = true;
                        }
                    }
                }

                response.Content        = new NoWriteNoSeekStreamContent(decompressedStream);
                response.RequestMessage = request;

                // Parse raw response headers and place them into response message.
                ParseResponseHeaders(requestHandle, response, buffer, stripEncodingHeaders);

                if (response.RequestMessage.Method != HttpMethod.Head)
                {
                    state.ExpectedBytesToRead = response.Content.Headers.ContentLength;
                }

                return(response);
            }
            finally
            {
                ArrayPool <char> .Shared.Return(buffer);
            }
        }
        public static HttpResponseMessage CreateResponseMessage(
            WinHttpRequestState state,
            bool doManualDecompressionCheck)
        {
            HttpRequestMessage request         = state.RequestMessage;
            SafeWinHttpHandle  requestHandle   = state.RequestHandle;
            CookieUsePolicy    cookieUsePolicy = state.Handler.CookieUsePolicy;
            CookieContainer    cookieContainer = state.Handler.CookieContainer;
            var  response             = new HttpResponseMessage();
            bool stripEncodingHeaders = false;

            // Create a single buffer to use for all subsequent WinHttpQueryHeaders string interop calls.
            // This buffer is the length needed for WINHTTP_QUERY_RAW_HEADERS_CRLF, which includes the status line
            // and all headers separated by CRLF, so it should be large enough for any individual status line or header queries.
            int bufferLength = GetResponseHeaderCharBufferLength(requestHandle, Interop.WinHttp.WINHTTP_QUERY_RAW_HEADERS_CRLF);

            char[] buffer = new char[bufferLength];

            // Get HTTP version, status code, reason phrase from the response headers.

            int versionLength = GetResponseHeader(requestHandle, Interop.WinHttp.WINHTTP_QUERY_VERSION, buffer);

            response.Version =
                CharArrayHelpers.EqualsOrdinalAsciiIgnoreCase("HTTP/1.1", buffer, 0, versionLength) ? HttpVersion.Version11 :
                CharArrayHelpers.EqualsOrdinalAsciiIgnoreCase("HTTP/1.0", buffer, 0, versionLength) ? HttpVersion.Version10 :
                HttpVersion.Unknown;

            response.StatusCode = (HttpStatusCode)GetResponseHeaderNumberInfo(
                requestHandle,
                Interop.WinHttp.WINHTTP_QUERY_STATUS_CODE);

            int reasonPhraseLength = GetResponseHeader(requestHandle, Interop.WinHttp.WINHTTP_QUERY_STATUS_TEXT, buffer);

            response.ReasonPhrase = reasonPhraseLength > 0 ?
                                    GetReasonPhrase(response.StatusCode, buffer, reasonPhraseLength) :
                                    string.Empty;

            // Create response stream and wrap it in a StreamContent object.
            var responseStream = new WinHttpResponseStream(requestHandle, state);

            state.RequestHandle = null; // ownership successfully transfered to WinHttpResponseStram.
            Stream decompressedStream = responseStream;

            if (doManualDecompressionCheck)
            {
                int contentEncodingStartIndex = 0;
                int contentEncodingLength     = GetResponseHeader(
                    requestHandle,
                    Interop.WinHttp.WINHTTP_QUERY_CONTENT_ENCODING,
                    buffer);

                CharArrayHelpers.Trim(buffer, ref contentEncodingStartIndex, ref contentEncodingLength);

                if (contentEncodingLength > 0)
                {
                    if (CharArrayHelpers.EqualsOrdinalAsciiIgnoreCase(
                            EncodingNameGzip, buffer, contentEncodingStartIndex, contentEncodingLength))
                    {
                        decompressedStream   = new GZipStream(responseStream, CompressionMode.Decompress);
                        stripEncodingHeaders = true;
                    }
                    else if (CharArrayHelpers.EqualsOrdinalAsciiIgnoreCase(
                                 EncodingNameDeflate, buffer, contentEncodingStartIndex, contentEncodingLength))
                    {
                        decompressedStream   = new DeflateStream(responseStream, CompressionMode.Decompress);
                        stripEncodingHeaders = true;
                    }
                }
            }

#if HTTP_DLL
            var content = new StreamContent(decompressedStream, state.CancellationToken);
#else
            // TODO: Issue https://github.com/dotnet/corefx/issues/9071
            // We'd like to be able to pass state.CancellationToken into the StreamContent so that its
            // SerializeToStreamAsync method can use it, but that ctor isn't public, nor is there a
            // SerializeToStreamAsync override that takes a CancellationToken.
            var content = new StreamContent(decompressedStream);
#endif

            response.Content        = content;
            response.RequestMessage = request;

            // Parse raw response headers and place them into response message.
            ParseResponseHeaders(requestHandle, response, buffer, stripEncodingHeaders);

            if (response.RequestMessage.Method != HttpMethod.Head)
            {
                state.ExpectedBytesToRead = response.Content.Headers.ContentLength;
            }

            return(response);
        }