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)); }
/// <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); }
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); }