From 31de746c6e2de0a5fecf52f39b3a74fb5e2fbdd6 Mon Sep 17 00:00:00 2001 From: Geoff Kizer Date: Fri, 25 Jun 2021 21:10:32 -0700 Subject: [PATCH] Move remote server redirect and decompression tests to HttpClientHandlerTest.RemoteServer.cs (#54738) * move remote redirect and decompression tests to HttpClientHandler_RemoteServerTest Co-authored-by: Geoffrey Kizer --- .../Net/Http/HttpClientHandlerTest.AutoRedirect.cs | 321 +---------------- .../Http/HttpClientHandlerTest.Decompression.cs | 90 ----- .../Net/Http/HttpClientHandlerTest.RemoteServer.cs | 398 +++++++++++++++++++++ 3 files changed, 400 insertions(+), 409 deletions(-) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.AutoRedirect.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.AutoRedirect.cs index 7521484..c9bbdbc 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.AutoRedirect.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.AutoRedirect.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; -using System.Net.Http.Headers; using System.Net.Sockets; using System.Net.Test.Common; using System.Threading.Tasks; @@ -12,32 +11,13 @@ using Xunit.Abstractions; namespace System.Net.Http.Functional.Tests { - using Configuration = System.Net.Test.Common.Configuration; - #if WINHTTPHANDLER_TEST using HttpClientHandler = System.Net.Http.WinHttpClientHandler; #endif public abstract class HttpClientHandlerTest_AutoRedirect : HttpClientHandlerTestBase { - private const string ExpectedContent = "Test content"; - private const string Username = "testuser"; - private const string Password = "password"; - - private readonly NetworkCredential _credential = new NetworkCredential(Username, Password); - - public static IEnumerable RemoteServersAndRedirectStatusCodes() - { - foreach (Configuration.Http.RemoteServer remoteServer in Configuration.Http.RemoteServers) - { - yield return new object[] { remoteServer, 300 }; - yield return new object[] { remoteServer, 301 }; - yield return new object[] { remoteServer, 302 }; - yield return new object[] { remoteServer, 303 }; - yield return new object[] { remoteServer, 307 }; - yield return new object[] { remoteServer, 308 }; - } - } + public HttpClientHandlerTest_AutoRedirect(ITestOutputHelper output) : base(output) { } public static IEnumerable RedirectStatusCodesOldMethodsNewMethods() { @@ -55,34 +35,6 @@ namespace System.Net.Http.Functional.Tests yield return new object[] { statusCode, "MYCUSTOMMETHOD", statusCode == 303 ? "GET" : "MYCUSTOMMETHOD" }; } } - public HttpClientHandlerTest_AutoRedirect(ITestOutputHelper output) : base(output) { } - - [OuterLoop("Uses external servers")] - [Theory, MemberData(nameof(RemoteServersAndRedirectStatusCodes))] - public async Task GetAsync_AllowAutoRedirectFalse_RedirectFromHttpToHttp_StatusCodeRedirect(Configuration.Http.RemoteServer remoteServer, int statusCode) - { - if (statusCode == 308 && (IsWinHttpHandler && PlatformDetection.WindowsVersion < 10)) - { - // 308 redirects are not supported on old versions of WinHttp, or on .NET Framework. - return; - } - - HttpClientHandler handler = CreateHttpClientHandler(); - handler.AllowAutoRedirect = false; - using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) - { - Uri uri = remoteServer.RedirectUriForDestinationUri( - statusCode: statusCode, - destinationUri: remoteServer.EchoUri, - hops: 1); - _output.WriteLine("Uri: {0}", uri); - using (HttpResponseMessage response = await client.GetAsync(uri)) - { - Assert.Equal(statusCode, (int)response.StatusCode); - Assert.Equal(uri, response.RequestMessage.RequestUri); - } - } - } [Theory, MemberData(nameof(RedirectStatusCodesOldMethodsNewMethods))] public async Task AllowAutoRedirect_True_ValidateNewMethodUsedOnRedirection( @@ -143,7 +95,7 @@ namespace System.Net.Http.Functional.Tests await LoopbackServer.CreateServerAsync(async (origServer, origUrl) => { var request = new HttpRequestMessage(HttpMethod.Post, origUrl) { Version = UseVersion }; - request.Content = new StringContent(ExpectedContent); + request.Content = new StringContent("hello world"); request.Headers.TransferEncodingChunked = true; Task getResponseTask = client.SendAsync(TestAsync, request); @@ -192,76 +144,6 @@ namespace System.Net.Http.Functional.Tests } } - [OuterLoop("Uses external servers")] - [Theory, MemberData(nameof(RemoteServersAndRedirectStatusCodes))] - public async Task GetAsync_AllowAutoRedirectTrue_RedirectFromHttpToHttp_StatusCodeOK(Configuration.Http.RemoteServer remoteServer, int statusCode) - { - if (statusCode == 308 && (IsWinHttpHandler && PlatformDetection.WindowsVersion < 10)) - { - // 308 redirects are not supported on old versions of WinHttp, or on .NET Framework. - return; - } - - HttpClientHandler handler = CreateHttpClientHandler(); - handler.AllowAutoRedirect = true; - using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) - { - Uri uri = remoteServer.RedirectUriForDestinationUri( - statusCode: statusCode, - destinationUri: remoteServer.EchoUri, - hops: 1); - _output.WriteLine("Uri: {0}", uri); - using (HttpResponseMessage response = await client.GetAsync(uri)) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(remoteServer.EchoUri, response.RequestMessage.RequestUri); - } - } - } - - [OuterLoop("Uses external servers")] - [Fact] - public async Task GetAsync_AllowAutoRedirectTrue_RedirectFromHttpToHttps_StatusCodeOK() - { - HttpClientHandler handler = CreateHttpClientHandler(); - handler.AllowAutoRedirect = true; - using (HttpClient client = CreateHttpClient(handler)) - { - Uri uri = Configuration.Http.RemoteHttp11Server.RedirectUriForDestinationUri( - statusCode: 302, - destinationUri: Configuration.Http.RemoteSecureHttp11Server.EchoUri, - hops: 1); - _output.WriteLine("Uri: {0}", uri); - using (HttpResponseMessage response = await client.GetAsync(uri)) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(Configuration.Http.SecureRemoteEchoServer, response.RequestMessage.RequestUri); - } - } - } - - [OuterLoop("Uses external servers")] - [Fact] - public async Task GetAsync_AllowAutoRedirectTrue_RedirectFromHttpsToHttp_StatusCodeRedirect() - { - HttpClientHandler handler = CreateHttpClientHandler(); - handler.AllowAutoRedirect = true; - using (HttpClient client = CreateHttpClient(handler)) - { - Uri uri = Configuration.Http.RemoteSecureHttp11Server.RedirectUriForDestinationUri( - statusCode: 302, - destinationUri: Configuration.Http.RemoteHttp11Server.EchoUri, - hops: 1); - _output.WriteLine("Uri: {0}", uri); - - using (HttpResponseMessage response = await client.GetAsync(uri)) - { - Assert.Equal(HttpStatusCode.Redirect, response.StatusCode); - Assert.Equal(uri, response.RequestMessage.RequestUri); - } - } - } - [Fact] public async Task GetAsync_AllowAutoRedirectTrue_RedirectWithoutLocation_ReturnsOriginalResponse() { @@ -288,99 +170,6 @@ namespace System.Net.Http.Functional.Tests } } - [OuterLoop("Uses external servers")] - [Theory, MemberData(nameof(RemoteServersMemberData))] - public async Task GetAsync_AllowAutoRedirectTrue_RedirectToUriWithParams_RequestMsgUriSet(Configuration.Http.RemoteServer remoteServer) - { - HttpClientHandler handler = CreateHttpClientHandler(); - handler.AllowAutoRedirect = true; - Uri targetUri = remoteServer.BasicAuthUriForCreds(userName: Username, password: Password); - using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) - { - Uri uri = remoteServer.RedirectUriForDestinationUri( - statusCode: 302, - destinationUri: targetUri, - hops: 1); - _output.WriteLine("Uri: {0}", uri); - using (HttpResponseMessage response = await client.GetAsync(uri)) - { - Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); - Assert.Equal(targetUri, response.RequestMessage.RequestUri); - } - } - } - - [OuterLoop("Uses external servers")] - [Theory] - [InlineData(3, 2)] - [InlineData(3, 3)] - [InlineData(3, 4)] - public async Task GetAsync_MaxAutomaticRedirectionsNServerHops_ThrowsIfTooMany(int maxHops, int hops) - { - if (IsWinHttpHandler && !PlatformDetection.IsWindows10Version1703OrGreater) - { - // Skip this test if using WinHttpHandler but on a release prior to Windows 10 Creators Update. - _output.WriteLine("Skipping test due to Windows 10 version prior to Version 1703."); - return; - } - - HttpClientHandler handler = CreateHttpClientHandler(); - handler.MaxAutomaticRedirections = maxHops; - using (HttpClient client = CreateHttpClient(handler)) - { - Task t = client.GetAsync(Configuration.Http.RemoteHttp11Server.RedirectUriForDestinationUri( - statusCode: 302, - destinationUri: Configuration.Http.RemoteHttp11Server.EchoUri, - hops: hops)); - - if (hops <= maxHops) - { - using (HttpResponseMessage response = await t) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(Configuration.Http.RemoteEchoServer, response.RequestMessage.RequestUri); - } - } - else - { - if (!IsWinHttpHandler) - { - using (HttpResponseMessage response = await t) - { - Assert.Equal(HttpStatusCode.Redirect, response.StatusCode); - } - } - else - { - await Assert.ThrowsAsync(() => t); - } - } - } - } - - [OuterLoop("Uses external servers")] - [Theory, MemberData(nameof(RemoteServersMemberData))] - public async Task GetAsync_AllowAutoRedirectTrue_RedirectWithRelativeLocation(Configuration.Http.RemoteServer remoteServer) - { - HttpClientHandler handler = CreateHttpClientHandler(); - handler.AllowAutoRedirect = true; - using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) - { - Uri uri = remoteServer.RedirectUriForDestinationUri( - statusCode: 302, - destinationUri: remoteServer.EchoUri, - hops: 1, - relative: true); - _output.WriteLine("Uri: {0}", uri); - - using (HttpResponseMessage response = await client.GetAsync(uri)) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(remoteServer.EchoUri, response.RequestMessage.RequestUri); - } - } - } - [Theory] [InlineData(200)] [InlineData(201)] @@ -476,111 +265,5 @@ namespace System.Net.Http.Functional.Tests }); } } - - [Theory, MemberData(nameof(RemoteServersMemberData))] - [OuterLoop("Uses external servers")] - public async Task GetAsync_CredentialIsNetworkCredentialUriRedirect_StatusCodeUnauthorized(Configuration.Http.RemoteServer remoteServer) - { - HttpClientHandler handler = CreateHttpClientHandler(); - handler.Credentials = _credential; - using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) - { - Uri redirectUri = remoteServer.RedirectUriForCreds( - statusCode: 302, - userName: Username, - password: Password); - using (HttpResponseMessage unAuthResponse = await client.GetAsync(redirectUri)) - { - Assert.Equal(HttpStatusCode.Unauthorized, unAuthResponse.StatusCode); - } - } - } - - [Theory, MemberData(nameof(RemoteServersMemberData))] - [OuterLoop("Uses external servers")] - public async Task HttpClientHandler_CredentialIsNotCredentialCacheAfterRedirect_StatusCodeOK(Configuration.Http.RemoteServer remoteServer) - { - HttpClientHandler handler = CreateHttpClientHandler(); - handler.Credentials = _credential; - using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) - { - Uri redirectUri = remoteServer.RedirectUriForCreds( - statusCode: 302, - userName: Username, - password: Password); - using (HttpResponseMessage unAuthResponse = await client.GetAsync(redirectUri)) - { - Assert.Equal(HttpStatusCode.Unauthorized, unAuthResponse.StatusCode); - } - - // Use the same handler to perform get request, authentication should succeed after redirect. - Uri uri = remoteServer.BasicAuthUriForCreds(userName: Username, password: Password); - using (HttpResponseMessage authResponse = await client.GetAsync(uri)) - { - Assert.Equal(HttpStatusCode.OK, authResponse.StatusCode); - } - } - } - - [OuterLoop("Uses external servers")] - [Theory, MemberData(nameof(RemoteServersAndRedirectStatusCodes))] - public async Task GetAsync_CredentialIsCredentialCacheUriRedirect_StatusCodeOK(Configuration.Http.RemoteServer remoteServer, int statusCode) - { - if (statusCode == 308 && (IsWinHttpHandler && PlatformDetection.WindowsVersion < 10)) - { - // 308 redirects are not supported on old versions of WinHttp, or on .NET Framework. - return; - } - - Uri uri = remoteServer.BasicAuthUriForCreds(userName: Username, password: Password); - Uri redirectUri = remoteServer.RedirectUriForCreds( - statusCode: statusCode, - userName: Username, - password: Password); - _output.WriteLine(uri.AbsoluteUri); - _output.WriteLine(redirectUri.AbsoluteUri); - var credentialCache = new CredentialCache(); - credentialCache.Add(uri, "Basic", _credential); - - HttpClientHandler handler = CreateHttpClientHandler(); - handler.Credentials = credentialCache; - using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) - { - using (HttpResponseMessage response = await client.GetAsync(redirectUri)) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(uri, response.RequestMessage.RequestUri); - } - } - } - - [OuterLoop("Uses external servers")] - [Theory, MemberData(nameof(RemoteServersAndRedirectStatusCodes))] - public async Task DefaultHeaders_SetCredentials_ClearedOnRedirect(Configuration.Http.RemoteServer remoteServer, int statusCode) - { - if (statusCode == 308 && (IsWinHttpHandler && PlatformDetection.WindowsVersion < 10)) - { - // 308 redirects are not supported on old versions of WinHttp, or on .NET Framework. - return; - } - - HttpClientHandler handler = CreateHttpClientHandler(); - using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) - { - string credentialString = _credential.UserName + ":" + _credential.Password; - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", credentialString); - Uri uri = remoteServer.RedirectUriForDestinationUri( - statusCode: statusCode, - destinationUri: remoteServer.EchoUri, - hops: 1); - _output.WriteLine("Uri: {0}", uri); - using (HttpResponseMessage response = await client.GetAsync(uri)) - { - string responseText = await response.Content.ReadAsStringAsync(); - _output.WriteLine(responseText); - Assert.False(TestHelper.JsonMessageContainsKey(responseText, "Authorization")); - } - } - } } } diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Decompression.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Decompression.cs index f53b6fc..88ac334 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Decompression.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Decompression.cs @@ -13,8 +13,6 @@ using Xunit.Abstractions; namespace System.Net.Http.Functional.Tests { - using Configuration = System.Net.Test.Common.Configuration; - #if WINHTTPHANDLER_TEST using HttpClientHandler = System.Net.Http.WinHttpClientHandler; #endif @@ -28,17 +26,6 @@ namespace System.Net.Http.Functional.Tests #endif public HttpClientHandler_Decompression_Test(ITestOutputHelper output) : base(output) { } - public static IEnumerable RemoteServersAndCompressionUris() - { - foreach (Configuration.Http.RemoteServer remoteServer in Configuration.Http.RemoteServers) - { - yield return new object[] { remoteServer, remoteServer.GZipUri }; - - // Remote deflate endpoint isn't correctly following the deflate protocol. - //yield return new object[] { remoteServer, remoteServer.DeflateUri }; - } - } - [Theory] [InlineData("gzip", false)] [InlineData("gzip", true)] @@ -169,83 +156,6 @@ namespace System.Net.Http.Functional.Tests }); } - [OuterLoop("Uses external servers")] - [SkipOnPlatform(TestPlatforms.Browser, "AutomaticDecompression not supported on Browser")] - [Theory, MemberData(nameof(RemoteServersAndCompressionUris))] - public async Task GetAsync_SetAutomaticDecompression_ContentDecompressed_GZip(Configuration.Http.RemoteServer remoteServer, Uri uri) - { - // Sync API supported only up to HTTP/1.1 - if (!TestAsync && remoteServer.HttpVersion.Major >= 2) - { - return; - } - - HttpClientHandler handler = CreateHttpClientHandler(); - handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; - using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) - { - using (HttpResponseMessage response = await client.SendAsync(TestAsync, CreateRequest(HttpMethod.Get, uri, remoteServer.HttpVersion))) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - string responseContent = await response.Content.ReadAsStringAsync(); - _output.WriteLine(responseContent); - TestHelper.VerifyResponseBody( - responseContent, - response.Content.Headers.ContentMD5, - false, - null); - } - } - } - - // The remote server endpoint was written to use DeflateStream, which isn't actually a correct - // implementation of the deflate protocol (the deflate protocol requires the zlib wrapper around - // deflate). Until we can get that updated (and deal with previous releases still testing it - // via a DeflateStream-based implementation), we utilize httpbin.org to help validate behavior. - [OuterLoop("Uses external servers")] - [Theory] - [InlineData("http://httpbin.org/deflate", "\"deflated\": true")] - [InlineData("https://httpbin.org/deflate", "\"deflated\": true")] - [SkipOnPlatform(TestPlatforms.Browser, "AutomaticDecompression not supported on Browser")] - public async Task GetAsync_SetAutomaticDecompression_ContentDecompressed_Deflate(string uri, string expectedContent) - { - if (IsWinHttpHandler) - { - // WinHttpHandler targets netstandard2.0 and still erroneously uses DeflateStream rather than ZlibStream for deflate. - return; - } - - HttpClientHandler handler = CreateHttpClientHandler(); - handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; - using (HttpClient client = CreateHttpClient(handler)) - { - Assert.Contains(expectedContent, await client.GetStringAsync(uri)); - } - } - - [OuterLoop("Uses external servers")] - [Theory, MemberData(nameof(RemoteServersAndCompressionUris))] - [SkipOnPlatform(TestPlatforms.Browser, "AutomaticDecompression not supported on Browser")] - public async Task GetAsync_SetAutomaticDecompression_HeadersRemoved(Configuration.Http.RemoteServer remoteServer, Uri uri) - { - // Sync API supported only up to HTTP/1.1 - if (!TestAsync && remoteServer.HttpVersion.Major >= 2) - { - return; - } - - HttpClientHandler handler = CreateHttpClientHandler(); - handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; - using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) - using (HttpResponseMessage response = await client.SendAsync(TestAsync, CreateRequest(HttpMethod.Get, uri, remoteServer.HttpVersion), HttpCompletionOption.ResponseHeadersRead)) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - - Assert.False(response.Content.Headers.Contains("Content-Encoding"), "Content-Encoding unexpectedly found"); - Assert.False(response.Content.Headers.Contains("Content-Length"), "Content-Length unexpectedly found"); - } - } - [Theory] #if NETCOREAPP [InlineData(DecompressionMethods.Brotli, "br", "")] diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.RemoteServer.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.RemoteServer.cs index 732384e..36d732d 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.RemoteServer.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.RemoteServer.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net.Http.Headers; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -912,6 +913,403 @@ namespace System.Net.Http.Functional.Tests } } + public static IEnumerable RemoteServersAndRedirectStatusCodes() + { + foreach (Configuration.Http.RemoteServer remoteServer in Configuration.Http.RemoteServers) + { + yield return new object[] { remoteServer, 300 }; + yield return new object[] { remoteServer, 301 }; + yield return new object[] { remoteServer, 302 }; + yield return new object[] { remoteServer, 303 }; + yield return new object[] { remoteServer, 307 }; + yield return new object[] { remoteServer, 308 }; + } + } + + [OuterLoop("Uses external servers")] + [Theory, MemberData(nameof(RemoteServersAndRedirectStatusCodes))] + public async Task GetAsync_AllowAutoRedirectFalse_RedirectFromHttpToHttp_StatusCodeRedirect(Configuration.Http.RemoteServer remoteServer, int statusCode) + { + if (statusCode == 308 && (IsWinHttpHandler && PlatformDetection.WindowsVersion < 10)) + { + // 308 redirects are not supported on old versions of WinHttp, or on .NET Framework. + return; + } + + HttpClientHandler handler = CreateHttpClientHandler(); + handler.AllowAutoRedirect = false; + using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) + { + Uri uri = remoteServer.RedirectUriForDestinationUri( + statusCode: statusCode, + destinationUri: remoteServer.EchoUri, + hops: 1); + _output.WriteLine("Uri: {0}", uri); + using (HttpResponseMessage response = await client.GetAsync(uri)) + { + Assert.Equal(statusCode, (int)response.StatusCode); + Assert.Equal(uri, response.RequestMessage.RequestUri); + } + } + } + + [OuterLoop("Uses external servers")] + [Theory, MemberData(nameof(RemoteServersAndRedirectStatusCodes))] + public async Task GetAsync_AllowAutoRedirectTrue_RedirectFromHttpToHttp_StatusCodeOK(Configuration.Http.RemoteServer remoteServer, int statusCode) + { + if (statusCode == 308 && (IsWinHttpHandler && PlatformDetection.WindowsVersion < 10)) + { + // 308 redirects are not supported on old versions of WinHttp, or on .NET Framework. + return; + } + + HttpClientHandler handler = CreateHttpClientHandler(); + handler.AllowAutoRedirect = true; + using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) + { + Uri uri = remoteServer.RedirectUriForDestinationUri( + statusCode: statusCode, + destinationUri: remoteServer.EchoUri, + hops: 1); + _output.WriteLine("Uri: {0}", uri); + using (HttpResponseMessage response = await client.GetAsync(uri)) + { + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(remoteServer.EchoUri, response.RequestMessage.RequestUri); + } + } + } + + [OuterLoop("Uses external servers")] + [Fact] + public async Task GetAsync_AllowAutoRedirectTrue_RedirectFromHttpToHttps_StatusCodeOK() + { + HttpClientHandler handler = CreateHttpClientHandler(); + handler.AllowAutoRedirect = true; + using (HttpClient client = CreateHttpClient(handler)) + { + Uri uri = Configuration.Http.RemoteHttp11Server.RedirectUriForDestinationUri( + statusCode: 302, + destinationUri: Configuration.Http.RemoteSecureHttp11Server.EchoUri, + hops: 1); + _output.WriteLine("Uri: {0}", uri); + using (HttpResponseMessage response = await client.GetAsync(uri)) + { + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(Configuration.Http.SecureRemoteEchoServer, response.RequestMessage.RequestUri); + } + } + } + + [OuterLoop("Uses external servers")] + [Fact] + public async Task GetAsync_AllowAutoRedirectTrue_RedirectFromHttpsToHttp_StatusCodeRedirect() + { + HttpClientHandler handler = CreateHttpClientHandler(); + handler.AllowAutoRedirect = true; + using (HttpClient client = CreateHttpClient(handler)) + { + Uri uri = Configuration.Http.RemoteSecureHttp11Server.RedirectUriForDestinationUri( + statusCode: 302, + destinationUri: Configuration.Http.RemoteHttp11Server.EchoUri, + hops: 1); + _output.WriteLine("Uri: {0}", uri); + + using (HttpResponseMessage response = await client.GetAsync(uri)) + { + Assert.Equal(HttpStatusCode.Redirect, response.StatusCode); + Assert.Equal(uri, response.RequestMessage.RequestUri); + } + } + } + + [OuterLoop("Uses external servers")] + [Theory, MemberData(nameof(RemoteServersMemberData))] + public async Task GetAsync_AllowAutoRedirectTrue_RedirectToUriWithParams_RequestMsgUriSet(Configuration.Http.RemoteServer remoteServer) + { + HttpClientHandler handler = CreateHttpClientHandler(); + handler.AllowAutoRedirect = true; + Uri targetUri = remoteServer.BasicAuthUriForCreds(userName: Username, password: Password); + using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) + { + Uri uri = remoteServer.RedirectUriForDestinationUri( + statusCode: 302, + destinationUri: targetUri, + hops: 1); + _output.WriteLine("Uri: {0}", uri); + using (HttpResponseMessage response = await client.GetAsync(uri)) + { + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); + Assert.Equal(targetUri, response.RequestMessage.RequestUri); + } + } + } + + [OuterLoop("Uses external servers")] + [Theory] + [InlineData(3, 2)] + [InlineData(3, 3)] + [InlineData(3, 4)] + public async Task GetAsync_MaxAutomaticRedirectionsNServerHops_ThrowsIfTooMany(int maxHops, int hops) + { + if (IsWinHttpHandler && !PlatformDetection.IsWindows10Version1703OrGreater) + { + // Skip this test if using WinHttpHandler but on a release prior to Windows 10 Creators Update. + _output.WriteLine("Skipping test due to Windows 10 version prior to Version 1703."); + return; + } + + HttpClientHandler handler = CreateHttpClientHandler(); + handler.MaxAutomaticRedirections = maxHops; + using (HttpClient client = CreateHttpClient(handler)) + { + Task t = client.GetAsync(Configuration.Http.RemoteHttp11Server.RedirectUriForDestinationUri( + statusCode: 302, + destinationUri: Configuration.Http.RemoteHttp11Server.EchoUri, + hops: hops)); + + if (hops <= maxHops) + { + using (HttpResponseMessage response = await t) + { + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(Configuration.Http.RemoteEchoServer, response.RequestMessage.RequestUri); + } + } + else + { + if (!IsWinHttpHandler) + { + using (HttpResponseMessage response = await t) + { + Assert.Equal(HttpStatusCode.Redirect, response.StatusCode); + } + } + else + { + await Assert.ThrowsAsync(() => t); + } + } + } + } + + [OuterLoop("Uses external servers")] + [Theory, MemberData(nameof(RemoteServersMemberData))] + public async Task GetAsync_AllowAutoRedirectTrue_RedirectWithRelativeLocation(Configuration.Http.RemoteServer remoteServer) + { + HttpClientHandler handler = CreateHttpClientHandler(); + handler.AllowAutoRedirect = true; + using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) + { + Uri uri = remoteServer.RedirectUriForDestinationUri( + statusCode: 302, + destinationUri: remoteServer.EchoUri, + hops: 1, + relative: true); + _output.WriteLine("Uri: {0}", uri); + + using (HttpResponseMessage response = await client.GetAsync(uri)) + { + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(remoteServer.EchoUri, response.RequestMessage.RequestUri); + } + } + } + + [Theory, MemberData(nameof(RemoteServersMemberData))] + [OuterLoop("Uses external servers")] + public async Task GetAsync_CredentialIsNetworkCredentialUriRedirect_StatusCodeUnauthorized(Configuration.Http.RemoteServer remoteServer) + { + HttpClientHandler handler = CreateHttpClientHandler(); + handler.Credentials = _credential; + using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) + { + Uri redirectUri = remoteServer.RedirectUriForCreds( + statusCode: 302, + userName: Username, + password: Password); + using (HttpResponseMessage unAuthResponse = await client.GetAsync(redirectUri)) + { + Assert.Equal(HttpStatusCode.Unauthorized, unAuthResponse.StatusCode); + } + } + } + + [Theory, MemberData(nameof(RemoteServersMemberData))] + [OuterLoop("Uses external servers")] + public async Task HttpClientHandler_CredentialIsNotCredentialCacheAfterRedirect_StatusCodeOK(Configuration.Http.RemoteServer remoteServer) + { + HttpClientHandler handler = CreateHttpClientHandler(); + handler.Credentials = _credential; + using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) + { + Uri redirectUri = remoteServer.RedirectUriForCreds( + statusCode: 302, + userName: Username, + password: Password); + using (HttpResponseMessage unAuthResponse = await client.GetAsync(redirectUri)) + { + Assert.Equal(HttpStatusCode.Unauthorized, unAuthResponse.StatusCode); + } + + // Use the same handler to perform get request, authentication should succeed after redirect. + Uri uri = remoteServer.BasicAuthUriForCreds(userName: Username, password: Password); + using (HttpResponseMessage authResponse = await client.GetAsync(uri)) + { + Assert.Equal(HttpStatusCode.OK, authResponse.StatusCode); + } + } + } + + [OuterLoop("Uses external servers")] + [Theory, MemberData(nameof(RemoteServersAndRedirectStatusCodes))] + public async Task GetAsync_CredentialIsCredentialCacheUriRedirect_StatusCodeOK(Configuration.Http.RemoteServer remoteServer, int statusCode) + { + if (statusCode == 308 && (IsWinHttpHandler && PlatformDetection.WindowsVersion < 10)) + { + // 308 redirects are not supported on old versions of WinHttp, or on .NET Framework. + return; + } + + Uri uri = remoteServer.BasicAuthUriForCreds(userName: Username, password: Password); + Uri redirectUri = remoteServer.RedirectUriForCreds( + statusCode: statusCode, + userName: Username, + password: Password); + _output.WriteLine(uri.AbsoluteUri); + _output.WriteLine(redirectUri.AbsoluteUri); + var credentialCache = new CredentialCache(); + credentialCache.Add(uri, "Basic", _credential); + + HttpClientHandler handler = CreateHttpClientHandler(); + handler.Credentials = credentialCache; + using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) + { + using (HttpResponseMessage response = await client.GetAsync(redirectUri)) + { + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(uri, response.RequestMessage.RequestUri); + } + } + } + + [OuterLoop("Uses external servers")] + [Theory, MemberData(nameof(RemoteServersAndRedirectStatusCodes))] + public async Task DefaultHeaders_SetCredentials_ClearedOnRedirect(Configuration.Http.RemoteServer remoteServer, int statusCode) + { + if (statusCode == 308 && (IsWinHttpHandler && PlatformDetection.WindowsVersion < 10)) + { + // 308 redirects are not supported on old versions of WinHttp, or on .NET Framework. + return; + } + + HttpClientHandler handler = CreateHttpClientHandler(); + using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) + { + string credentialString = _credential.UserName + ":" + _credential.Password; + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", credentialString); + Uri uri = remoteServer.RedirectUriForDestinationUri( + statusCode: statusCode, + destinationUri: remoteServer.EchoUri, + hops: 1); + _output.WriteLine("Uri: {0}", uri); + using (HttpResponseMessage response = await client.GetAsync(uri)) + { + string responseText = await response.Content.ReadAsStringAsync(); + _output.WriteLine(responseText); + Assert.False(TestHelper.JsonMessageContainsKey(responseText, "Authorization")); + } + } + } + + public static IEnumerable RemoteServersAndCompressionUris() + { + foreach (Configuration.Http.RemoteServer remoteServer in Configuration.Http.RemoteServers) + { + yield return new object[] { remoteServer, remoteServer.GZipUri }; + + // Remote deflate endpoint isn't correctly following the deflate protocol. + //yield return new object[] { remoteServer, remoteServer.DeflateUri }; + } + } + + [OuterLoop("Uses external servers")] + [SkipOnPlatform(TestPlatforms.Browser, "AutomaticDecompression not supported on Browser")] + [Theory, MemberData(nameof(RemoteServersAndCompressionUris))] + public async Task GetAsync_SetAutomaticDecompression_ContentDecompressed_GZip(Configuration.Http.RemoteServer remoteServer, Uri uri) + { + // Sync API supported only up to HTTP/1.1 + if (!TestAsync && remoteServer.HttpVersion.Major >= 2) + { + return; + } + + HttpClientHandler handler = CreateHttpClientHandler(); + handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; + using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) + { + using (HttpResponseMessage response = await client.SendAsync(TestAsync, CreateRequest(HttpMethod.Get, uri, remoteServer.HttpVersion))) + { + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + string responseContent = await response.Content.ReadAsStringAsync(); + _output.WriteLine(responseContent); + TestHelper.VerifyResponseBody( + responseContent, + response.Content.Headers.ContentMD5, + false, + null); + } + } + } + + // The remote server endpoint was written to use DeflateStream, which isn't actually a correct + // implementation of the deflate protocol (the deflate protocol requires the zlib wrapper around + // deflate). Until we can get that updated (and deal with previous releases still testing it + // via a DeflateStream-based implementation), we utilize httpbin.org to help validate behavior. + [OuterLoop("Uses external servers")] + [Theory] + [InlineData("http://httpbin.org/deflate", "\"deflated\": true")] + [InlineData("https://httpbin.org/deflate", "\"deflated\": true")] + [SkipOnPlatform(TestPlatforms.Browser, "AutomaticDecompression not supported on Browser")] + public async Task GetAsync_SetAutomaticDecompression_ContentDecompressed_Deflate(string uri, string expectedContent) + { + if (IsWinHttpHandler) + { + // WinHttpHandler targets netstandard2.0 and still erroneously uses DeflateStream rather than ZlibStream for deflate. + return; + } + + HttpClientHandler handler = CreateHttpClientHandler(); + handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; + using (HttpClient client = CreateHttpClient(handler)) + { + Assert.Contains(expectedContent, await client.GetStringAsync(uri)); + } + } + + [OuterLoop("Uses external servers")] + [Theory, MemberData(nameof(RemoteServersAndCompressionUris))] + [SkipOnPlatform(TestPlatforms.Browser, "AutomaticDecompression not supported on Browser")] + public async Task GetAsync_SetAutomaticDecompression_HeadersRemoved(Configuration.Http.RemoteServer remoteServer, Uri uri) + { + // Sync API supported only up to HTTP/1.1 + if (!TestAsync && remoteServer.HttpVersion.Major >= 2) + { + return; + } + + HttpClientHandler handler = CreateHttpClientHandler(); + handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; + using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) + using (HttpResponseMessage response = await client.SendAsync(TestAsync, CreateRequest(HttpMethod.Get, uri, remoteServer.HttpVersion), HttpCompletionOption.ResponseHeadersRead)) + { + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + + Assert.False(response.Content.Headers.Contains("Content-Encoding"), "Content-Encoding unexpectedly found"); + Assert.False(response.Content.Headers.Contains("Content-Length"), "Content-Length unexpectedly found"); + } + } + [OuterLoop("Uses external servers", typeof(PlatformDetection), nameof(PlatformDetection.LocalEchoServerIsNotAvailable))] [Theory] [MemberData(nameof(Http2Servers))] -- 2.7.4