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 java.net.InetSocketAddress;
31 import java.net.URI;
32 import java.net.URISyntaxException;
33 import java.net.URL;
34 import java.security.Provider;
35 import java.security.SecureRandom;
36 import java.security.Security;
37 import java.util.concurrent.Future;
38
39 import org.apache.hc.core5.http.HttpHeaders;
40 import org.apache.hc.core5.http.HttpResponse;
41 import org.apache.hc.core5.http.Message;
42 import org.apache.hc.core5.http.Method;
43 import org.apache.hc.core5.http.config.Http1Config;
44 import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer;
45 import org.apache.hc.core5.http.nio.support.AsyncRequestBuilder;
46 import org.apache.hc.core5.http.nio.support.BasicRequestProducer;
47 import org.apache.hc.core5.http.nio.support.BasicResponseConsumer;
48 import org.apache.hc.core5.http.protocol.DefaultHttpProcessor;
49 import org.apache.hc.core5.http.protocol.HttpProcessor;
50 import org.apache.hc.core5.http.protocol.RequestValidateHost;
51 import org.apache.hc.core5.reactor.IOReactorConfig;
52 import org.apache.hc.core5.ssl.SSLContextBuilder;
53 import org.apache.hc.core5.util.TimeValue;
54 import org.apache.hc.core5.util.Timeout;
55 import org.conscrypt.Conscrypt;
56 import org.junit.jupiter.api.Assertions;
57 import org.junit.jupiter.api.Order;
58 import org.junit.jupiter.api.Test;
59 import org.junit.jupiter.api.extension.AfterEachCallback;
60 import org.junit.jupiter.api.extension.BeforeEachCallback;
61 import org.junit.jupiter.api.extension.ExtensionContext;
62 import org.junit.jupiter.api.extension.RegisterExtension;
63
64
65
66
67
68
69
70
71
72
73
74
75 public abstract class JSSEProviderIntegrationTest {
76
77 private final String securityProviderName;
78 private final String protocolVersion;
79
80 public JSSEProviderIntegrationTest(final String securityProviderName, final String protocolVersion) {
81 super();
82 this.securityProviderName = securityProviderName;
83 this.protocolVersion = protocolVersion;
84 }
85
86 private static final Timeout TIMEOUT = Timeout.ofMinutes(1);
87 private static final int REQ_NUM = 25;
88
89 private Provider securityProvider;
90
91 class SecurityProviderResource implements BeforeEachCallback, AfterEachCallback {
92
93 @Override
94 public void beforeEach(final ExtensionContext context) throws Exception {
95 if ("Conscrypt".equalsIgnoreCase(securityProviderName)) {
96 try {
97 securityProvider = Conscrypt.newProviderBuilder().provideTrustManager(true).build();
98 } catch (final UnsatisfiedLinkError e) {
99 Assertions.fail("Conscrypt provider failed to be loaded: " + e.getMessage());
100 }
101 } else {
102 securityProvider = null;
103 }
104 if (securityProvider != null) {
105 Security.insertProviderAt(securityProvider, 1);
106 }
107 }
108
109 @Override
110 public void afterEach(final ExtensionContext context) throws Exception {
111 if (securityProvider != null) {
112 Security.removeProvider(securityProvider.getName());
113 securityProvider = null;
114 }
115 }
116
117 }
118
119 @RegisterExtension
120 @Order(1)
121 private final SecurityProviderResource securityProviderResource = new SecurityProviderResource();
122
123 private Http1TestServer server;
124
125 class ServerResource implements BeforeEachCallback, AfterEachCallback {
126
127 @Override
128 public void beforeEach(final ExtensionContext context) throws Exception {
129 final URL keyStoreURL = getClass().getResource("/test-server.p12");
130 final String storePassword = "nopassword";
131
132 server = new Http1TestServer(
133 IOReactorConfig.custom()
134 .setSoTimeout(TIMEOUT)
135 .build(),
136 SSLContextBuilder.create()
137 .setProvider(securityProvider)
138 .setKeyStoreType("pkcs12")
139 .loadTrustMaterial(keyStoreURL, storePassword.toCharArray())
140 .loadKeyMaterial(keyStoreURL, storePassword.toCharArray(), storePassword.toCharArray())
141 .setSecureRandom(new SecureRandom())
142 .build(),
143 (endpoint, sslEngine) -> {
144 if (protocolVersion != null) {
145 sslEngine.setEnabledProtocols(new String[]{protocolVersion});
146 }
147 },
148 null);
149 }
150
151 @Override
152 public void afterEach(final ExtensionContext context) throws Exception {
153 if (server != null) {
154 server.shutdown(TimeValue.ofSeconds(5));
155 }
156 }
157
158 }
159
160 @RegisterExtension
161 @Order(2)
162 private final ServerResource serverResource = new ServerResource();
163
164 private Http1TestClient client;
165
166 class ClientResource implements BeforeEachCallback, AfterEachCallback {
167
168 @Override
169 public void beforeEach(final ExtensionContext context) throws Exception {
170 final URL keyStoreURL = getClass().getResource("/test-client.p12");
171 final String storePassword = "nopassword";
172
173 client = new Http1TestClient(
174 IOReactorConfig.custom()
175 .setSoTimeout(TIMEOUT)
176 .build(),
177 SSLContextBuilder.create()
178 .setProvider(securityProvider)
179 .setKeyStoreType("pkcs12")
180 .loadTrustMaterial(keyStoreURL, storePassword.toCharArray())
181 .setSecureRandom(new SecureRandom())
182 .build(),
183 (endpoint, sslEngine) -> {
184 if (protocolVersion != null) {
185 sslEngine.setEnabledProtocols(new String[]{protocolVersion});
186 }
187 },
188 null);
189 }
190
191 @Override
192 public void afterEach(final ExtensionContext context) throws Exception {
193 if (client != null) {
194 client.shutdown(TimeValue.ofSeconds(5));
195 }
196 }
197
198 }
199
200 @RegisterExtension
201 @Order(3)
202 private final ClientResource clientResource = new ClientResource();
203
204 private URI createRequestURI(final InetSocketAddress serverEndpoint, final String path) {
205 try {
206 return new URI("https", null, "localhost", serverEndpoint.getPort(), path, null, null);
207 } catch (final URISyntaxException e) {
208 throw new IllegalStateException();
209 }
210 }
211
212 @Test
213 public void testSimpleGet() throws Exception {
214 server.register("/hello", () -> new SingleLineResponseHandler("Hi there"));
215 final InetSocketAddress serverEndpoint = server.start();
216
217 client.start();
218 final Future<ClientSessionEndpoint> connectFuture = client.connect(
219 "localhost", serverEndpoint.getPort(), TIMEOUT);
220 final ClientSessionEndpoint streamEndpoint = connectFuture.get();
221
222 for (int i = 0; i < REQ_NUM; i++) {
223 final Future<Message<HttpResponse, String>> future = streamEndpoint.execute(
224 new BasicRequestProducer(Method.GET, createRequestURI(serverEndpoint, "/hello")),
225 new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null);
226 final Message<HttpResponse, String> result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
227 Assertions.assertNotNull(result);
228 final HttpResponse response1 = result.getHead();
229 final String entity1 = result.getBody();
230 Assertions.assertNotNull(response1);
231 Assertions.assertEquals(200, response1.getCode());
232 Assertions.assertEquals("Hi there", entity1);
233 }
234 }
235
236 @Test
237 public void testSimpleGetConnectionClose() throws Exception {
238 server.register("/hello", () -> new SingleLineResponseHandler("Hi there"));
239 final InetSocketAddress serverEndpoint = server.start();
240
241 client.start();
242 final URI requestURI = createRequestURI(serverEndpoint, "/hello");
243 for (int i = 0; i < REQ_NUM; i++) {
244 final Future<ClientSessionEndpoint> connectFuture = client.connect(
245 "localhost", serverEndpoint.getPort(), TIMEOUT);
246 try (final ClientSessionEndpoint streamEndpoint = connectFuture.get()) {
247 final Future<Message<HttpResponse, String>> future = streamEndpoint.execute(
248 AsyncRequestBuilder.get(requestURI)
249 .addHeader(HttpHeaders.CONNECTION, "close")
250 .build(),
251 new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null);
252 final Message<HttpResponse, String> result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
253 Assertions.assertNotNull(result);
254 final HttpResponse response1 = result.getHead();
255 final String entity1 = result.getBody();
256 Assertions.assertNotNull(response1);
257 Assertions.assertEquals(200, response1.getCode());
258 Assertions.assertEquals("Hi there", entity1);
259 }
260 }
261 }
262
263 @Test
264 public void testSimpleGetIdentityTransfer() throws Exception {
265 server.register("/hello", () -> new SingleLineResponseHandler("Hi there"));
266 final HttpProcessor httpProcessor = new DefaultHttpProcessor(new RequestValidateHost());
267 final InetSocketAddress serverEndpoint = server.start(httpProcessor, Http1Config.DEFAULT);
268
269 client.start();
270
271 for (int i = 0; i < REQ_NUM; i++) {
272 final Future<ClientSessionEndpoint> connectFuture = client.connect(
273 "localhost", serverEndpoint.getPort(), TIMEOUT);
274 try (final ClientSessionEndpoint streamEndpoint = connectFuture.get()) {
275 final Future<Message<HttpResponse, String>> future = streamEndpoint.execute(
276 new BasicRequestProducer(Method.GET, createRequestURI(serverEndpoint, "/hello")),
277 new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null);
278 final Message<HttpResponse, String> result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
279 Assertions.assertNotNull(result);
280 final HttpResponse response = result.getHead();
281 final String entity = result.getBody();
282 Assertions.assertNotNull(response);
283 Assertions.assertEquals(200, response.getCode());
284 Assertions.assertEquals("Hi there", entity);
285 }
286 }
287 }
288
289 }