Skip to content

Commit 802d32d

Browse files
committed
Fixes #6207 - Make ALPN optional in HTTP2Client over TLS
Introduced HTTP2Client.setUseALPN(boolean) to configure whether to use ALPN. Signed-off-by: Simone Bordet <[email protected]>
1 parent 455e798 commit 802d32d

File tree

4 files changed

+174
-3
lines changed

4 files changed

+174
-3
lines changed

jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2Client.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ public class HTTP2Client extends ContainerLifeCycle
115115
private long streamIdleTimeout;
116116
private boolean useInputDirectByteBuffers = true;
117117
private boolean useOutputDirectByteBuffers = true;
118+
private boolean isUseALPN = true;
118119

119120
public HTTP2Client()
120121
{
@@ -358,6 +359,17 @@ public void setUseOutputDirectByteBuffers(boolean useOutputDirectByteBuffers)
358359
this.useOutputDirectByteBuffers = useOutputDirectByteBuffers;
359360
}
360361

362+
@ManagedAttribute(value = "Whether ALPN should be used when establishing connections")
363+
public boolean isUseALPN()
364+
{
365+
return isUseALPN;
366+
}
367+
368+
public void setUseALPN(boolean useALPN)
369+
{
370+
isUseALPN = useALPN;
371+
}
372+
361373
public CompletableFuture<Session> connect(SocketAddress address, Session.Listener listener)
362374
{
363375
return connect(null, address, listener);
@@ -423,8 +435,9 @@ private ClientConnectionFactory newClientConnectionFactory(SslContextFactory ssl
423435
ClientConnectionFactory factory = new HTTP2ClientConnectionFactory();
424436
if (sslContextFactory != null)
425437
{
426-
ALPNClientConnectionFactory alpn = new ALPNClientConnectionFactory(getExecutor(), factory, getProtocols());
427-
factory = new SslClientConnectionFactory(sslContextFactory, getByteBufferPool(), getExecutor(), alpn);
438+
if (isUseALPN())
439+
factory = new ALPNClientConnectionFactory(getExecutor(), factory, getProtocols());
440+
factory = new SslClientConnectionFactory(sslContextFactory, getByteBufferPool(), getExecutor(), factory);
428441
}
429442
return factory;
430443
}
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
//
2+
// ========================================================================
3+
// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
4+
//
5+
// This program and the accompanying materials are made available under the
6+
// terms of the Eclipse Public License v. 2.0 which is available at
7+
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
8+
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
9+
//
10+
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
11+
// ========================================================================
12+
//
13+
14+
package org.eclipse.jetty.http2.client;
15+
16+
import java.net.InetSocketAddress;
17+
import java.util.concurrent.CompletableFuture;
18+
import java.util.concurrent.TimeUnit;
19+
import javax.servlet.http.HttpServletRequest;
20+
import javax.servlet.http.HttpServletResponse;
21+
22+
import org.eclipse.jetty.http.HttpFields;
23+
import org.eclipse.jetty.http.HttpStatus;
24+
import org.eclipse.jetty.http.HttpURI;
25+
import org.eclipse.jetty.http.HttpVersion;
26+
import org.eclipse.jetty.http.MetaData;
27+
import org.eclipse.jetty.http2.HTTP2Cipher;
28+
import org.eclipse.jetty.http2.api.Session;
29+
import org.eclipse.jetty.http2.api.Stream;
30+
import org.eclipse.jetty.http2.frames.HeadersFrame;
31+
import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
32+
import org.eclipse.jetty.io.ClientConnector;
33+
import org.eclipse.jetty.server.ConnectionFactory;
34+
import org.eclipse.jetty.server.Handler;
35+
import org.eclipse.jetty.server.HttpConfiguration;
36+
import org.eclipse.jetty.server.Request;
37+
import org.eclipse.jetty.server.SecureRequestCustomizer;
38+
import org.eclipse.jetty.server.Server;
39+
import org.eclipse.jetty.server.ServerConnector;
40+
import org.eclipse.jetty.server.SslConnectionFactory;
41+
import org.eclipse.jetty.server.handler.AbstractHandler;
42+
import org.eclipse.jetty.util.component.LifeCycle;
43+
import org.eclipse.jetty.util.ssl.SslContextFactory;
44+
import org.eclipse.jetty.util.thread.QueuedThreadPool;
45+
import org.junit.jupiter.api.AfterEach;
46+
import org.junit.jupiter.api.Test;
47+
48+
import static org.junit.jupiter.api.Assertions.assertEquals;
49+
import static org.junit.jupiter.api.Assertions.assertTrue;
50+
51+
public class PriorKnowledgeHTTP2OverTLSTest
52+
{
53+
private Server server;
54+
private ServerConnector connector;
55+
private HTTP2Client client;
56+
57+
private void start(Handler handler) throws Exception
58+
{
59+
startServer(handler);
60+
startClient();
61+
}
62+
63+
private void startServer(Handler handler) throws Exception
64+
{
65+
QueuedThreadPool serverThreads = new QueuedThreadPool();
66+
serverThreads.setName("server");
67+
server = new Server(serverThreads);
68+
HttpConfiguration httpsConfig = new HttpConfiguration();
69+
httpsConfig.addCustomizer(new SecureRequestCustomizer());
70+
ConnectionFactory h2 = new HTTP2ServerConnectionFactory(httpsConfig);
71+
ConnectionFactory ssl = new SslConnectionFactory(newServerSslContextFactory(), h2.getProtocol());
72+
connector = new ServerConnector(server, 1, 1, ssl, h2);
73+
server.addConnector(connector);
74+
server.setHandler(handler);
75+
server.start();
76+
}
77+
78+
private void startClient() throws Exception
79+
{
80+
ClientConnector clientConnector = new ClientConnector();
81+
QueuedThreadPool clientThreads = new QueuedThreadPool();
82+
clientThreads.setName("client");
83+
clientConnector.setExecutor(clientThreads);
84+
clientConnector.setSslContextFactory(newClientSslContextFactory());
85+
client = new HTTP2Client(clientConnector);
86+
client.setUseALPN(false);
87+
client.start();
88+
}
89+
90+
@AfterEach
91+
public void dispose() throws Exception
92+
{
93+
LifeCycle.stop(client);
94+
LifeCycle.stop(server);
95+
}
96+
97+
private SslContextFactory.Server newServerSslContextFactory()
98+
{
99+
SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
100+
configureSslContextFactory(sslContextFactory);
101+
return sslContextFactory;
102+
}
103+
104+
private SslContextFactory.Client newClientSslContextFactory()
105+
{
106+
SslContextFactory.Client sslContextFactory = new SslContextFactory.Client();
107+
configureSslContextFactory(sslContextFactory);
108+
// sslContextFactory.setEndpointIdentificationAlgorithm(null);
109+
return sslContextFactory;
110+
}
111+
112+
private void configureSslContextFactory(SslContextFactory sslContextFactory)
113+
{
114+
sslContextFactory.setKeyStorePath("src/test/resources/keystore.p12");
115+
sslContextFactory.setKeyStorePassword("storepwd");
116+
sslContextFactory.setUseCipherSuitesOrder(true);
117+
sslContextFactory.setCipherComparator(HTTP2Cipher.COMPARATOR);
118+
}
119+
120+
@Test
121+
public void testDirectHTTP2OverTLS() throws Exception
122+
{
123+
// The client knows a priori that the server speaks h2 on a particular port.
124+
125+
start(new AbstractHandler()
126+
{
127+
@Override
128+
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
129+
{
130+
baseRequest.setHandled(true);
131+
}
132+
});
133+
134+
int port = connector.getLocalPort();
135+
MetaData.Response response = client.connect(client.getClientConnector().getSslContextFactory(), new InetSocketAddress("localhost", port), new Session.Listener.Adapter())
136+
.thenCompose(session ->
137+
{
138+
CompletableFuture<MetaData.Response> responsePromise = new CompletableFuture<>();
139+
HttpURI.Mutable uri = HttpURI.build("https://localhost:" + port + "/path");
140+
MetaData.Request request = new MetaData.Request("GET", uri, HttpVersion.HTTP_2, HttpFields.EMPTY);
141+
return session.newStream(new HeadersFrame(request, null, true), new Stream.Listener.Adapter()
142+
{
143+
@Override
144+
public void onHeaders(Stream stream, HeadersFrame frame)
145+
{
146+
assertTrue(frame.isEndStream());
147+
MetaData metaData = frame.getMetaData();
148+
assertTrue(metaData.isResponse());
149+
MetaData.Response response = (MetaData.Response)metaData;
150+
responsePromise.complete(response);
151+
}
152+
}).thenCompose(stream -> responsePromise);
153+
})
154+
.get(5, TimeUnit.SECONDS);
155+
156+
assertEquals(HttpStatus.OK_200, response.getStatus());
157+
}
158+
}
2.54 KB
Binary file not shown.

jetty-http2/http2-http-client-transport/src/test/java/org/eclipse/jetty/http2/client/http/DirectHTTP2OverTLSTest.java renamed to jetty-http2/http2-http-client-transport/src/test/java/org/eclipse/jetty/http2/client/http/PriorKnowledgeHTTP2OverTLSTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141

4242
import static org.junit.jupiter.api.Assertions.assertEquals;
4343

44-
public class DirectHTTP2OverTLSTest
44+
public class PriorKnowledgeHTTP2OverTLSTest
4545
{
4646
private Server server;
4747
private ServerConnector connector;

0 commit comments

Comments
 (0)