1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package org.apache.hc.core5.testing.nio;
29
30 import static org.hamcrest.MatcherAssert.assertThat;
31 import static org.junit.jupiter.api.Assertions.assertEquals;
32 import static org.junit.jupiter.api.Assertions.assertFalse;
33 import static org.junit.jupiter.api.Assertions.assertTrue;
34
35 import java.net.InetSocketAddress;
36 import java.util.concurrent.ExecutionException;
37 import java.util.concurrent.Future;
38
39 import org.apache.hc.core5.http.ContentType;
40 import org.apache.hc.core5.http.HttpHost;
41 import org.apache.hc.core5.http.HttpResponse;
42 import org.apache.hc.core5.http.HttpStatus;
43 import org.apache.hc.core5.http.Message;
44 import org.apache.hc.core5.http.Method;
45 import org.apache.hc.core5.http.URIScheme;
46 import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer;
47 import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer;
48 import org.apache.hc.core5.http.nio.entity.StringAsyncEntityProducer;
49 import org.apache.hc.core5.http.nio.ssl.BasicServerTlsStrategy;
50 import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
51 import org.apache.hc.core5.http.nio.support.BasicRequestProducer;
52 import org.apache.hc.core5.http.nio.support.BasicResponseConsumer;
53 import org.apache.hc.core5.http2.impl.nio.ProtocolNegotiationException;
54 import org.apache.hc.core5.http2.impl.nio.bootstrap.H2MultiplexingRequester;
55 import org.apache.hc.core5.http2.ssl.H2ClientTlsStrategy;
56 import org.apache.hc.core5.http2.ssl.H2ServerTlsStrategy;
57 import org.apache.hc.core5.reactor.IOReactorConfig;
58 import org.apache.hc.core5.reactor.ListenerEndpoint;
59 import org.apache.hc.core5.testing.SSLTestContexts;
60 import org.apache.hc.core5.testing.nio.extension.H2AsyncServerResource;
61 import org.apache.hc.core5.testing.nio.extension.H2MultiplexingRequesterResource;
62 import org.apache.hc.core5.util.Timeout;
63 import org.hamcrest.CoreMatchers;
64 import org.junit.jupiter.api.Test;
65 import org.junit.jupiter.api.extension.RegisterExtension;
66
67 public abstract class H2AlpnTest {
68
69 private static final Timeout TIMEOUT = Timeout.ofMinutes(1);
70
71 private final boolean strictALPN;
72 private final boolean h2Allowed;
73
74 @RegisterExtension
75 private final H2AsyncServerResource serverResource;
76 @RegisterExtension
77 private final H2MultiplexingRequesterResource clientResource;
78
79 public H2AlpnTest(final boolean strictALPN, final boolean h2Allowed) throws Exception {
80 this.strictALPN = strictALPN;
81 this.h2Allowed = h2Allowed;
82
83 final TlsStrategy serverTlsStrategy = h2Allowed ?
84 new H2ServerTlsStrategy(SSLTestContexts.createServerSSLContext()) :
85 new BasicServerTlsStrategy(SSLTestContexts.createServerSSLContext());
86
87 this.serverResource = new H2AsyncServerResource(bootstrap -> bootstrap
88 .setIOReactorConfig(
89 IOReactorConfig.custom()
90 .setSoTimeout(TIMEOUT)
91 .build())
92 .setTlsStrategy(serverTlsStrategy)
93 .register("*", () -> new EchoHandler(2048))
94 );
95
96 final TlsStrategy clientTlsStrategy = new H2ClientTlsStrategy(SSLTestContexts.createClientSSLContext());
97
98 this.clientResource = new H2MultiplexingRequesterResource(bootstrap -> bootstrap
99 .setIOReactorConfig(IOReactorConfig.custom()
100 .setSoTimeout(TIMEOUT)
101 .build())
102 .setTlsStrategy(clientTlsStrategy)
103 .setStrictALPNHandshake(strictALPN)
104 );
105 }
106
107 @Test
108 public void testALPN() throws Exception {
109 final HttpAsyncServer server = serverResource.start();
110 final Future<ListenerEndpoint> future = server.listen(new InetSocketAddress(0), URIScheme.HTTPS);
111 final ListenerEndpoint listener = future.get();
112 final InetSocketAddress address = (InetSocketAddress) listener.getAddress();
113 final H2MultiplexingRequester requester = clientResource.start();
114
115 final HttpHost target = new HttpHost(URIScheme.HTTPS.id, "localhost", address.getPort());
116 final Future<Message<HttpResponse, String>> resultFuture1 = requester.execute(
117 new BasicRequestProducer(Method.POST, target, "/stuff",
118 new StringAsyncEntityProducer("some stuff", ContentType.TEXT_PLAIN)),
119 new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null);
120 final Message<HttpResponse, String> message1;
121 try {
122 message1 = resultFuture1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
123 } catch (final ExecutionException e) {
124 final Throwable cause = e.getCause();
125 assertFalse(h2Allowed, "h2 negotiation was enabled, but h2 was not negotiated");
126 assertTrue(cause instanceof ProtocolNegotiationException);
127 assertEquals("ALPN: missing application protocol", cause.getMessage());
128 assertTrue(strictALPN, "strict ALPN mode was not enabled, but the client negotiator still threw");
129 return;
130 }
131
132 assertTrue(h2Allowed, "h2 negotiation was disabled, but h2 was negotiated");
133 assertThat(message1, CoreMatchers.notNullValue());
134 final HttpResponse response1 = message1.getHead();
135 assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK));
136 final String body1 = message1.getBody();
137 assertThat(body1, CoreMatchers.equalTo("some stuff"));
138 }
139
140 }