diff --git a/src/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpAuthHelper.cs b/src/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpAuthHelper.cs index 5b38ac1f5f15..87e1964973f1 100644 --- a/src/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpAuthHelper.cs +++ b/src/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpAuthHelper.cs @@ -122,6 +122,15 @@ public void CheckResponseForAuthentication( state.LastStatusCode = statusCode; + // If we don't have any proxy credentials to try, then we end up with 407. + ICredentials proxyCreds = state.Proxy == null ? + state.DefaultProxyCredentials : + state.Proxy.Credentials; + if (proxyCreds == null) + { + break; + } + // Determine authorization scheme to use. We ignore the firstScheme // parameter which is included in the supportedSchemes flags already. // We pass the schemes to ChooseAuthScheme which will pick the scheme diff --git a/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.cs b/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.cs index eb26604967be..717ead6181b6 100644 --- a/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.cs +++ b/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.cs @@ -953,7 +953,10 @@ public async Task GetAsync_SupportedSSLVersion_Succeeds(string name, string url) public void Proxy_BypassFalse_GetRequestGoesThroughCustomProxy(ICredentials creds) { int port; - Task proxyTask = LoopbackGetRequestHttpProxy.StartAsync(out port, requireAuth: creds != null && creds != CredentialCache.DefaultCredentials); + Task proxyTask = LoopbackGetRequestHttpProxy.StartAsync( + out port, + requireAuth: creds != null && creds != CredentialCache.DefaultCredentials, + expectCreds: true); Uri proxyUrl = new Uri($"http://localhost:{port}"); using (var handler = new HttpClientHandler() { Proxy = new UseSpecifiedUriWebProxy(proxyUrl, creds) }) @@ -990,6 +993,26 @@ await response.Content.ReadAsStringAsync(), } } + [Fact] + public void Proxy_HaveNoCredsAndUseAuthenticatedCustomProxy_ProxyAuthenticationRequiredStatusCode() + { + int port; + Task proxyTask = LoopbackGetRequestHttpProxy.StartAsync( + out port, + requireAuth: true, + expectCreds: false); + Uri proxyUrl = new Uri($"http://localhost:{port}"); + + using (var handler = new HttpClientHandler() { Proxy = new UseSpecifiedUriWebProxy(proxyUrl, null) }) + using (var client = new HttpClient(handler)) + { + Task responseTask = client.GetAsync(HttpTestServers.RemoteEchoServer); + Task.WaitAll(proxyTask, responseTask); + + Assert.Equal(HttpStatusCode.ProxyAuthenticationRequired, responseTask.Result.StatusCode); + } + } + private sealed class UseSpecifiedUriWebProxy : IWebProxy { private readonly Uri _uri; diff --git a/src/System.Net.Http/tests/FunctionalTests/LoopbackGetRequestHttpProxy.cs b/src/System.Net.Http/tests/FunctionalTests/LoopbackGetRequestHttpProxy.cs index cd7fdfa79621..cb9e3ae8c724 100644 --- a/src/System.Net.Http/tests/FunctionalTests/LoopbackGetRequestHttpProxy.cs +++ b/src/System.Net.Http/tests/FunctionalTests/LoopbackGetRequestHttpProxy.cs @@ -24,15 +24,15 @@ public struct ProxyResult public string AuthenticationHeaderValue; } - public static Task StartAsync(out int port, bool requireAuth) + public static Task StartAsync(out int port, bool requireAuth, bool expectCreds) { var listener = new TcpListener(IPAddress.Loopback, 0); listener.Start(); port = ((IPEndPoint)listener.LocalEndpoint).Port; - return StartAsync(listener, requireAuth); + return StartAsync(listener, requireAuth, expectCreds); } - private static async Task StartAsync(TcpListener listener, bool requireAuth) + private static async Task StartAsync(TcpListener listener, bool requireAuth, bool expectCreds) { ProxyResult result = new ProxyResult(); var headers = new Dictionary(); @@ -60,11 +60,9 @@ private static async Task StartAsync(TcpListener listener, bool req await getAndReadRequest().ConfigureAwait(false); // If we're expecting credentials, look for them, and if we didn't get them, send back - // a response and process a new request. + // a 407 response. Optionally, process a new request that would expect credentials. if (requireAuth && !headers.ContainsKey("Proxy-Authorization")) { - Task secondListen = listener.AcceptSocketAsync(); - // Send back a 407 await clientSocket.SendAsync( new ArraySegment(Encoding.ASCII.GetBytes("HTTP/1.1 407 Proxy Auth Required\r\nProxy-Authenticate: Basic\r\n\r\n")), @@ -72,8 +70,16 @@ await clientSocket.SendAsync( clientSocket.Shutdown(SocketShutdown.Send); clientSocket.Dispose(); - // Wait for a new connection that should have an auth header this time and parse it. - await getAndReadRequest().ConfigureAwait(false); + if (expectCreds) + { + // Wait for a new connection that should have an auth header this time and parse it. + await getAndReadRequest().ConfigureAwait(false); + } + else + { + // No credentials will be coming in a subsequent request. + return default(ProxyResult); + } } // Store any auth header we may have for later comparison.