From d17f17012879c18815211d745631c2d56fe05c83 Mon Sep 17 00:00:00 2001 From: John Gardiner Myers Date: Sat, 22 Feb 2025 12:59:56 -0800 Subject: [PATCH] Enable HTTP/2 over TLS in http-client --- CHANGES | 3 ++ .../http/client/jetty/JettyHttpClient.java | 31 ++++++++++++------- .../http/client/AbstractHttpClientTest.java | 16 +++++++--- .../jetty/TestJettyHttpsClientHttp2.java | 5 --- 4 files changed, 34 insertions(+), 21 deletions(-) diff --git a/CHANGES b/CHANGES index c9661a95b..173eb8266 100644 --- a/CHANGES +++ b/CHANGES @@ -19,6 +19,9 @@ Platform 3.28 We now handle the Retry-After: header of 429 HTTP status codes. + HTTP/2 now works over TLS, negotiating with the server using ALPN. + HTTP/2 may be enabled using the "http-client.http2.enabled" configuration property. + * Maven plugin upgrades - maven-deploy-plugin to 3.1.3 (was 2.8.2) diff --git a/http-client/src/main/java/com/proofpoint/http/client/jetty/JettyHttpClient.java b/http-client/src/main/java/com/proofpoint/http/client/jetty/JettyHttpClient.java index a25e66dba..f8d2c3ed4 100644 --- a/http-client/src/main/java/com/proofpoint/http/client/jetty/JettyHttpClient.java +++ b/http-client/src/main/java/com/proofpoint/http/client/jetty/JettyHttpClient.java @@ -24,15 +24,17 @@ import org.eclipse.jetty.client.InputStreamResponseListener; import org.eclipse.jetty.client.Response; import org.eclipse.jetty.client.Socks4Proxy; -import org.eclipse.jetty.client.transport.HttpClientTransportOverHTTP; +import org.eclipse.jetty.client.transport.HttpClientConnectionFactory; +import org.eclipse.jetty.client.transport.HttpClientTransportDynamic; import org.eclipse.jetty.client.transport.HttpDestination; import org.eclipse.jetty.client.transport.HttpExchange; import org.eclipse.jetty.client.transport.HttpRequest; import org.eclipse.jetty.client.transport.internal.HttpConnectionOverHTTP; import org.eclipse.jetty.http.HttpCookieStore; import org.eclipse.jetty.http2.client.HTTP2Client; -import org.eclipse.jetty.http2.client.transport.HttpClientTransportOverHTTP2; +import org.eclipse.jetty.http2.client.transport.ClientConnectionFactoryOverHTTP2; import org.eclipse.jetty.io.ArrayByteBufferPool; +import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.ClientConnector; import org.eclipse.jetty.util.component.LifeCycle; import org.eclipse.jetty.util.ssl.SslContextFactory; @@ -179,22 +181,27 @@ public JettyHttpClient( sslContextFactory.setTrustAll(true); } - HttpClientTransport transport; + ClientConnector clientConnector = new ClientConnector(); + clientConnector.setSelectors(config.getSelectorCount()); + clientConnector.setSslContextFactory(sslContextFactory); + + ImmutableList.Builder protocols = ImmutableList.builder(); if (config.isHttp2Enabled()) { - HTTP2Client client = new HTTP2Client(); + HTTP2Client client = new HTTP2Client(clientConnector); client.setInitialSessionRecvWindow(toIntExact(config.getHttp2InitialSessionReceiveWindowSize().toBytes())); client.setInitialStreamRecvWindow(toIntExact(config.getHttp2InitialStreamReceiveWindowSize().toBytes())); client.setInputBufferSize(toIntExact(config.getHttp2InputBufferSize().toBytes())); - client.setSelectors(config.getSelectorCount()); - transport = new HttpClientTransportOverHTTP2(client); - } - else { - ClientConnector clientConnector = new ClientConnector(); - clientConnector.setSelectors(config.getSelectorCount()); - clientConnector.setSslContextFactory(sslContextFactory); - transport = new HttpClientTransportOverHTTP(clientConnector); + client.setStreamIdleTimeout(idleTimeoutMillis); + protocols.add(new ClientConnectionFactoryOverHTTP2.HTTP2(client)); } + protocols.add(HttpClientConnectionFactory.HTTP11); + + // The order of the protocols indicates the client's preference. + // The first is the most preferred, the last is the least preferred, but + // the protocol version to use can be explicitly specified in the request. + HttpClientTransport transport = new HttpClientTransportDynamic(clientConnector, protocols.build().toArray(new ClientConnectionFactory.Info[0])); + httpClient = new AuthorizationPreservingHttpClient(transport); // request and response buffer size diff --git a/http-client/src/test/java/com/proofpoint/http/client/AbstractHttpClientTest.java b/http-client/src/test/java/com/proofpoint/http/client/AbstractHttpClientTest.java index 3dec840db..f5078e217 100644 --- a/http-client/src/test/java/com/proofpoint/http/client/AbstractHttpClientTest.java +++ b/http-client/src/test/java/com/proofpoint/http/client/AbstractHttpClientTest.java @@ -12,6 +12,8 @@ import com.proofpoint.tracetoken.TraceToken; import com.proofpoint.units.Duration; import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory; import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory; import org.eclipse.jetty.server.ConnectionFactory; @@ -23,8 +25,6 @@ import org.eclipse.jetty.server.SslConnectionFactory; import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.server.handler.gzip.GzipHandler; -import org.eclipse.jetty.ee10.servlet.ServletContextHandler; -import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.testng.Assert; import org.testng.SkipException; @@ -1292,7 +1292,15 @@ public void testConnectReadRequestWriteJunkHangup() public void testDynamicBodySourceConnectWriteRequestClose() throws Exception { - try (FakeServer fakeServer = new FakeServer(scheme, host, 1024, null, true)) { + int bodySize; + if (createClientConfig().isHttp2Enabled()) { + // HTTP/2 over TLS has smaller buffers for this + bodySize = 256; + } else { + bodySize = 1024; + } + + try (FakeServer fakeServer = new FakeServer(scheme, host, bodySize, null, true)) { HttpClientConfig config = createClientConfig(); config.setConnectTimeout(new Duration(5, SECONDS)); config.setIdleTimeout(new Duration(5, SECONDS)); @@ -1308,7 +1316,7 @@ public void testDynamicBodySourceConnectWriteRequestClose() .setUri(fakeServer.getUri()) .setBodySource((DynamicBodySource) out -> () -> { if (invocation.getAndIncrement() < 100) { - out.write(new byte[1024]); + out.write(new byte[bodySize]); } else { out.close(); diff --git a/http-client/src/test/java/com/proofpoint/http/client/jetty/TestJettyHttpsClientHttp2.java b/http-client/src/test/java/com/proofpoint/http/client/jetty/TestJettyHttpsClientHttp2.java index c8ca7eb7e..20112ef7a 100644 --- a/http-client/src/test/java/com/proofpoint/http/client/jetty/TestJettyHttpsClientHttp2.java +++ b/http-client/src/test/java/com/proofpoint/http/client/jetty/TestJettyHttpsClientHttp2.java @@ -13,9 +13,4 @@ protected HttpClientConfig createClientConfig() return super.createClientConfig() .setHttp2Enabled(true); } - - @BeforeMethod - public void checkValidConfiguration(){ - throw new SkipException("Https is not supported for Http/2"); - } }