diff --git a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/TLSIntegrationTest.java b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/TLSIntegrationTest.java index e84581832..e38f4f7c2 100644 --- a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/TLSIntegrationTest.java +++ b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/TLSIntegrationTest.java @@ -451,4 +451,52 @@ public boolean upgrade( } + @Test + void testHttpsProxyWithConnectAndSecondTls() throws Exception { + final TlsStrategy targetTlsStrategy = new BasicServerTlsStrategy( + SSLTestContexts.createServerSSLContext()); + server = createServer(targetTlsStrategy); + server.start(); + final ListenerEndpoint targetListener = server + .listen(new InetSocketAddress("localhost", 0), URIScheme.HTTPS) + .get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); + final InetSocketAddress targetAddress = (InetSocketAddress) targetListener.getAddress(); + + final HttpAsyncServer proxyServer = createServer( + new BasicServerTlsStrategy(SSLTestContexts.createServerSSLContext())); + proxyServer.start(); + final ListenerEndpoint proxyListener = proxyServer + .listen(new InetSocketAddress("localhost", 0), URIScheme.HTTPS) + .get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); + final InetSocketAddress proxyAddress = (InetSocketAddress) proxyListener.getAddress(); + + final TlsStrategy clientTlsStrategy = new BasicClientTlsStrategy( + SSLTestContexts.createClientSSLContext()); + client = createClient(clientTlsStrategy); + client.start(); + + final HttpHost proxy = new HttpHost("https", "localhost", proxyAddress.getPort()); + final HttpHost target = new HttpHost("https", "localhost", targetAddress.getPort()); + + final Future> future = client.execute( + proxy, + new BasicRequestProducer( + Method.POST, + target, + "/test", + new StringAsyncEntityProducer("ping", ContentType.TEXT_PLAIN) + ), + new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), + TIMEOUT, null + ); + + final Message response = future + .get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); + Assertions.assertNotNull(response); + Assertions.assertEquals(200, response.getHead().getCode()); + Assertions.assertEquals("ping", response.getBody()); + + proxyServer.close(CloseMode.IMMEDIATE); + } + } diff --git a/httpcore5/src/main/java/org/apache/hc/core5/reactor/InternalDataChannel.java b/httpcore5/src/main/java/org/apache/hc/core5/reactor/InternalDataChannel.java index d290939b4..f2e244fb1 100644 --- a/httpcore5/src/main/java/org/apache/hc/core5/reactor/InternalDataChannel.java +++ b/httpcore5/src/main/java/org/apache/hc/core5/reactor/InternalDataChannel.java @@ -226,6 +226,19 @@ public void startTls( final SSLSessionVerifier verifier, final Timeout handshakeTimeout, final FutureCallback callback) { + + // If we already performed TLS (e.g. to the HTTPS proxy), clear out the old session + if (tlsSessionRef.get() != null) { + // Drop the previous SSLIOSession so we can start fresh + tlsSessionRef.set(null); + // Revert to raw TCP I/O before installing the new TLS layer + currentSessionRef.set( + ioSessionDecorator != null + ? ioSessionDecorator.decorate(ioSession) + : ioSession + ); + } + final SSLIOSession sslioSession = new SSLIOSession( endpoint != null ? endpoint : initialEndpoint, ioSession, @@ -247,11 +260,8 @@ public void completed(final SSLSession sslSession) { } }); - if (tlsSessionRef.compareAndSet(null, sslioSession)) { - currentSessionRef.set(ioSessionDecorator != null ? ioSessionDecorator.decorate(sslioSession) : sslioSession); - } else { - throw new IllegalStateException("TLS already activated"); - } + tlsSessionRef.set(sslioSession); + currentSessionRef.set(ioSessionDecorator != null ? ioSessionDecorator.decorate(sslioSession) : sslioSession); try { if (sessionListener != null) { sessionListener.startTls(sslioSession);