add code to establish proxy tunnel for SSL
authorGeoff Kizer <geoffrek>
Thu, 15 Feb 2018 02:43:15 +0000 (18:43 -0800)
committerGeoff Kizer <geoffrek>
Thu, 15 Feb 2018 02:43:15 +0000 (18:43 -0800)
Commit migrated from https://github.com/dotnet/corefx/commit/d84a28844502bcf30538a18e30e33118ca879467

src/libraries/System.Net.Http/src/Resources/Strings.resx
src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs
src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpProxyConnectionHandler.cs

index eda5a3b..af92019 100644 (file)
   <data name="IO_PathTooLong_Path" xml:space="preserve">
     <value>The path '{0}' is too long, or a component of the specified path is too long.</value>
   </data>
+  <data name="net_http_proxy_tunnel_failed" xml:space="preserve">
+    <value>Unable to establish a tunnel though proxy {0}.  The proxy returned status code {1}.</value>
+  </data>
 </root>
index 14728ba..96ae7af 100644 (file)
@@ -203,7 +203,7 @@ namespace System.Net.Http
             Stream stream =
                 _proxyUri != null ?
                     (_sslOptions != null ?
-                        throw new NotSupportedException("SSL Proxy tunneling not currently supported") :
+                        await EstablishProxyTunnel(cancellationToken) :
                         await ConnectHelper.ConnectAsync(_proxyUri.IdnHost, _proxyUri.Port, cancellationToken)) :
                     await ConnectHelper.ConnectAsync(_host, _port, cancellationToken);
 
@@ -220,6 +220,24 @@ namespace System.Net.Http
                 new HttpConnectionWithFinalizer(this, stream, transportContext); // finalizer needed to signal the pool when a connection is dropped
         }
 
+        private async ValueTask<Stream> EstablishProxyTunnel(CancellationToken cancellationToken)
+        {
+            // Send a CONNECT request to the proxy server to establish a tunnel.
+            HttpRequestMessage tunnelRequest = new HttpRequestMessage(new HttpMethod("CONNECT"), _proxyUri);
+            tunnelRequest.Headers.Host = $"{_host}:{_port}";    // This specifies destination host/port to connect to
+
+            // TODO: For now, we don't support proxy authentication in this scenario.
+            // This will get fixed when we refactor proxy auth handling.
+
+            HttpResponseMessage tunnelResponse = await _poolManager.SendAsync(tunnelRequest, null, cancellationToken);
+            if (tunnelResponse.StatusCode != HttpStatusCode.OK)
+            {
+                throw new HttpRequestException(SR.Format(SR.net_http_proxy_tunnel_failed, _proxyUri, tunnelResponse.StatusCode));
+            }
+
+            return await tunnelResponse.Content.ReadAsStreamAsync();
+        }
+
         /// <summary>Enqueues a waiter to the waiters list.</summary>
         /// <param name="waiter">The waiter to add.</param>
         private void EnqueueWaiter(ConnectionWaiter waiter)
index 5af611d..a1fd08e 100644 (file)
@@ -61,12 +61,6 @@ namespace System.Net.Http
                 throw new InvalidOperationException(SR.net_http_invalid_proxy_scheme);
             }
 
-            if (!HttpUtilities.IsSupportedNonSecureScheme(request.RequestUri.Scheme))
-            {
-                // TODO #23136: Implement SSL tunneling through proxy
-                throw new NotImplementedException("no support for SSL tunneling through proxy");
-            }
-
             HttpResponseMessage response = await GetConnectionAndSendAsync(request, proxyUri, cancellationToken).ConfigureAwait(false);
 
             // Handle proxy authentication