diff options
author | Stephen Toub <stoub@microsoft.com> | 2017-10-26 05:43:54 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-10-26 05:43:54 +0300 |
commit | 4b1137f8d46955904462acff7b2da34a9aa96d39 (patch) | |
tree | cb91221f14fb2379e1db7a4c4a27e39ac63d0d66 /src | |
parent | 789035424aa1d3940baa3acde7b78fd78641b5e4 (diff) | |
parent | 5ecea8ff47c03a073a77916a9c7d1d806f8c7ce3 (diff) |
Merge pull request #24820 from stephentoub/fix_http_issues
Fix a few ManagedHandler issues
Diffstat (limited to 'src')
7 files changed, 139 insertions, 42 deletions
diff --git a/src/System.Net.Http/src/Resources/Strings.resx b/src/System.Net.Http/src/Resources/Strings.resx index 90a5d97b9c..129ef9fd5f 100644 --- a/src/System.Net.Http/src/Resources/Strings.resx +++ b/src/System.Net.Http/src/Resources/Strings.resx @@ -363,9 +363,6 @@ <data name="net_http_feature_UWPClientCertSupportRequiresCertInPersonalCertificateStore" xml:space="preserve"> <value>Client certificate was not found in the personal (\"MY\") certificate store. In UWP, client certificates are only supported if they have been added to that certificate store.</value> </data> - <data name="net_http_headers_missing_location" xml:space="preserve"> - <value>The response code indicates a redirect, but the 'Location' header is missing.</value> - </data> <data name="net_http_max_redirects" xml:space="preserve"> <value>The maximum number of redirects was exceeded.</value> </data> @@ -387,4 +384,4 @@ <data name="net_ssl_app_protocols_invalid" xml:space="preserve"> <value>The application protocol list is invalid.</value> </data> -</root>
\ No newline at end of file +</root> diff --git a/src/System.Net.Http/src/System/Net/Http/Managed/AutoRedirectHandler.cs b/src/System.Net.Http/src/System/Net/Http/Managed/AutoRedirectHandler.cs index a35175acaa..7605616781 100644 --- a/src/System.Net.Http/src/System/Net/Http/Managed/AutoRedirectHandler.cs +++ b/src/System.Net.Http/src/System/Net/Http/Managed/AutoRedirectHandler.cs @@ -72,7 +72,8 @@ namespace System.Net.Http Uri location = response.Headers.Location; if (location == null) { - throw new HttpRequestException(SR.net_http_headers_missing_location); + // No location header. Nothing to redirect to. + break; } if (!location.IsAbsoluteUri) diff --git a/src/System.Net.Http/src/System/Net/Http/Managed/DecompressionHandler.cs b/src/System.Net.Http/src/System/Net/Http/Managed/DecompressionHandler.cs index cd72dd3293..261ae26af7 100644 --- a/src/System.Net.Http/src/System/Net/Http/Managed/DecompressionHandler.cs +++ b/src/System.Net.Http/src/System/Net/Http/Managed/DecompressionHandler.cs @@ -47,30 +47,22 @@ namespace System.Net.Http HttpResponseMessage response = await _innerHandler.SendAsync(request, cancellationToken).ConfigureAwait(false); - while (true) + ICollection<string> contentEncodings = response.Content.Headers.ContentEncoding; + if (contentEncodings.Count > 0) { - // Get first encoding - using (IEnumerator<string> e = response.Content.Headers.ContentEncoding.GetEnumerator()) + string last = null; + foreach (string encoding in contentEncodings) { - if (!e.MoveNext()) - { - break; - } + last = encoding; + } - string encoding = e.Current; - if (GZipEnabled && encoding == s_gzip) - { - response.Content = new GZipDecompressedContent(response.Content); - } - else if (DeflateEnabled && encoding == s_deflate) - { - response.Content = new DeflateDecompressedContent(response.Content); - } - else - { - // Unknown content encoding. Stop processing. - break; - } + if (GZipEnabled && last == s_gzip) + { + response.Content = new GZipDecompressedContent(response.Content); + } + else if (DeflateEnabled && last == s_deflate) + { + response.Content = new DeflateDecompressedContent(response.Content); } } @@ -99,21 +91,18 @@ namespace System.Net.Http // Copy original response headers, but with the following changes: // Content-Length is removed, since it no longer applies to the decompressed content - // The first Content-Encoding is removed, since we are processing that here + // The last Content-Encoding is removed, since we are processing that here. Headers.AddHeaders(originalContent.Headers); Headers.ContentLength = null; Headers.ContentEncoding.Clear(); - bool first = true; - foreach (var encoding in originalContent.Headers.ContentEncoding) + string prevEncoding = null; + foreach (string encoding in originalContent.Headers.ContentEncoding) { - if (first) - { - first = false; - } - else + if (prevEncoding != null) { - Headers.ContentEncoding.Add(encoding); + Headers.ContentEncoding.Add(prevEncoding); } + prevEncoding = encoding; } } diff --git a/src/System.Net.Http/src/System/Net/Http/Managed/HttpConnectionHandler.cs b/src/System.Net.Http/src/System/Net/Http/Managed/HttpConnectionHandler.cs index c0635480de..6a5a0723cf 100644 --- a/src/System.Net.Http/src/System/Net/Http/Managed/HttpConnectionHandler.cs +++ b/src/System.Net.Http/src/System/Net/Http/Managed/HttpConnectionHandler.cs @@ -91,7 +91,42 @@ namespace System.Net.Http if (HttpUtilities.IsSupportedSecureScheme(uri.Scheme)) { - SslStream sslStream = await EstablishSslConnection(uri.IdnHost, request, stream).ConfigureAwait(false); + // Get the appropriate host name to use for the SSL connection, allowing a host header to override. + string host = request.Headers.Host; + if (host == null) + { + // No host header, use the host from the Uri. + host = uri.IdnHost; + } + else + { + // There is a host header. Use it, but first see if we need to trim off a port. + int colonPos = host.IndexOf(':'); + if (colonPos >= 0) + { + // There is colon, which could either be a port separator or a separator in + // an IPv6 address. See if this is an IPv6 address; if it's not, use everything + // before the colon as the host name, and if it is, use everything before the last + // colon iff the last colon is after the end of the IPv6 address (otherwise it's a + // part of the address). + int ipV6AddressEnd = host.IndexOf(']'); + if (ipV6AddressEnd == -1) + { + host = host.Substring(0, colonPos); + } + else + { + colonPos = host.LastIndexOf(':'); + if (colonPos > ipV6AddressEnd) + { + host = host.Substring(0, colonPos); + } + } + } + } + + // Establish the connection using the parsed host name. + SslStream sslStream = await EstablishSslConnection(host, request, stream).ConfigureAwait(false); stream = sslStream; transportContext = sslStream.TransportContext; } diff --git a/src/System.Net.Http/src/System/Net/Http/Managed/ManagedHandler.cs b/src/System.Net.Http/src/System/Net/Http/Managed/ManagedHandler.cs index cf1a1798f3..0945f400b5 100644 --- a/src/System.Net.Http/src/System/Net/Http/Managed/ManagedHandler.cs +++ b/src/System.Net.Http/src/System/Net/Http/Managed/ManagedHandler.cs @@ -269,14 +269,14 @@ namespace System.Net.Http handler = new HttpProxyConnectionHandler(_settings, handler); } - if (_settings._credentials != null) + if (_settings._useCookies) { - handler = new AuthenticationHandler(_settings._preAuthenticate, _settings._credentials, handler); + handler = new CookieHandler(CookieContainer, handler); } - if (_settings._useCookies) + if (_settings._credentials != null) { - handler = new CookieHandler(CookieContainer, handler); + handler = new AuthenticationHandler(_settings._preAuthenticate, _settings._credentials, handler); } if (_settings._allowAutoRedirect) diff --git a/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.cs b/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.cs index 9f2aa8db2f..3e3993ca53 100644 --- a/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.cs +++ b/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.cs @@ -285,6 +285,49 @@ namespace System.Net.Http.Functional.Tests } } + [OuterLoop] // TODO: Issue #11345 + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task SendAsync_GetWithValidHostHeader_Success(bool withPort) + { + var m = new HttpRequestMessage(HttpMethod.Get, Configuration.Http.SecureRemoteEchoServer); + m.Headers.Host = withPort ? Configuration.Http.SecureHost + ":123" : Configuration.Http.SecureHost; + + using (HttpClient client = CreateHttpClient()) + using (HttpResponseMessage response = await client.SendAsync(m)) + { + string responseContent = await response.Content.ReadAsStringAsync(); + _output.WriteLine(responseContent); + TestHelper.VerifyResponseBody( + responseContent, + response.Content.Headers.ContentMD5, + false, + null); + } + } + + [OuterLoop] // TODO: Issue #11345 + [Fact] + public async Task SendAsync_GetWithInvalidHostHeader_ThrowsException() + { + if (PlatformDetection.IsNetCore && !UseManagedHandler) + { + // [ActiveIssue(24862)] + // WinHttpHandler and CurlHandler do not use the Host header to influence the SSL auth. + // .NET Framework and ManagedHandler do. + return; + } + + var m = new HttpRequestMessage(HttpMethod.Get, Configuration.Http.SecureRemoteEchoServer); + m.Headers.Host = "hostheaderthatdoesnotmatch"; + + using (HttpClient client = CreateHttpClient()) + { + await Assert.ThrowsAsync<HttpRequestException>(() => client.SendAsync(m)); + } + } + [ActiveIssue(22158, TargetFrameworkMonikers.Uap)] [OuterLoop] // TODO: Issue #11345 [Fact] @@ -635,6 +678,36 @@ namespace System.Net.Http.Functional.Tests } } + [Fact] + public async Task GetAsync_AllowAutoRedirectTrue_RedirectWithoutLocation_ReturnsOriginalResponse() + { + // [ActiveIssue(24819, TestPlatforms.Windows)] + if (PlatformDetection.IsWindows && PlatformDetection.IsNetCore && !UseManagedHandler) + { + return; + } + + HttpClientHandler handler = CreateHttpClientHandler(); + handler.AllowAutoRedirect = true; + using (var client = new HttpClient(handler)) + { + await LoopbackServer.CreateServerAsync(async (server, url) => + { + Task<HttpResponseMessage> getTask = client.GetAsync(url); + Task<List<string>> serverTask = LoopbackServer.ReadRequestAndSendResponseAsync(server, + $"HTTP/1.1 302 OK\r\n" + + $"Date: {DateTimeOffset.UtcNow:R}\r\n" + + "\r\n"); + await TestHelper.WhenAllCompletedOrAnyFailed(getTask, serverTask); + + using (HttpResponseMessage response = await getTask) + { + Assert.Equal(302, (int)response.StatusCode); + } + }); + } + } + [OuterLoop] // TODO: Issue #11345 [Fact] public async Task GetAsync_AllowAutoRedirectTrue_RedirectToUriWithParams_RequestMsgUriSet() diff --git a/src/System.Net.WebSockets.Client/tests/ConnectTest.cs b/src/System.Net.WebSockets.Client/tests/ConnectTest.cs index cde6c660c5..0c5645f000 100644 --- a/src/System.Net.WebSockets.Client/tests/ConnectTest.cs +++ b/src/System.Net.WebSockets.Client/tests/ConnectTest.cs @@ -86,9 +86,11 @@ namespace System.Net.WebSockets.Client.Tests [ActiveIssue(18784, TargetFrameworkMonikers.NetFramework)] [OuterLoop] - [ConditionalTheory(nameof(WebSocketsSupported)), MemberData(nameof(EchoHeadersServers))] - public async Task ConnectAsync_AddHostHeader_Success(Uri server) + [ConditionalTheory(nameof(WebSocketsSupported))] + public async Task ConnectAsync_AddHostHeader_Success() { + Uri server = System.Net.Test.Common.Configuration.WebSockets.RemoteEchoServer; + // Send via the physical address such as "corefx-net.cloudapp.net" // Set the Host header to logical address like "subdomain.corefx-net.cloudapp.net" // Verify the scenario works and the remote server received "Host: subdomain.corefx-net.cloudapp.net" |