|
1 /* |
|
2 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. |
|
8 * |
|
9 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
12 * version 2 for more details (a copy is included in the LICENSE file that |
|
13 * accompanied this code). |
|
14 * |
|
15 * You should have received a copy of the GNU General Public License version |
|
16 * 2 along with this work; if not, write to the Free Software Foundation, |
|
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
18 * |
|
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
20 * or visit www.oracle.com if you need additional information or have any |
|
21 * questions. |
|
22 */ |
|
23 |
|
24 import com.sun.net.httpserver.Filter; |
|
25 import com.sun.net.httpserver.Headers; |
|
26 import com.sun.net.httpserver.HttpContext; |
|
27 import com.sun.net.httpserver.HttpExchange; |
|
28 import com.sun.net.httpserver.HttpHandler; |
|
29 import com.sun.net.httpserver.HttpServer; |
|
30 |
|
31 import java.net.InetAddress; |
|
32 import java.io.ByteArrayInputStream; |
|
33 import java.net.http.HttpClient.Version; |
|
34 import jdk.internal.net.http.common.HttpHeadersImpl; |
|
35 import java.io.ByteArrayOutputStream; |
|
36 import java.io.IOException; |
|
37 import java.io.InputStream; |
|
38 import java.io.OutputStream; |
|
39 import java.io.PrintStream; |
|
40 import java.io.UncheckedIOException; |
|
41 import java.math.BigInteger; |
|
42 import java.net.InetSocketAddress; |
|
43 import java.net.URI; |
|
44 import java.util.List; |
|
45 import java.util.ListIterator; |
|
46 import java.util.Map; |
|
47 import java.util.Optional; |
|
48 import java.util.Set; |
|
49 import java.util.concurrent.CopyOnWriteArrayList; |
|
50 import java.util.logging.Level; |
|
51 import java.util.logging.Logger; |
|
52 import java.util.stream.Stream; |
|
53 |
|
54 /** |
|
55 * Defines an adaptation layers so that a test server handlers and filters |
|
56 * can be implemented independently of the underlying server version. |
|
57 * <p> |
|
58 * For instance: |
|
59 * <pre>{@code |
|
60 * |
|
61 * URI http1URI, http2URI; |
|
62 * |
|
63 * InetSocketAddress sa = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); |
|
64 * HttpTestServer server1 = HttpTestServer.of(HttpServer.create(sa, 0)); |
|
65 * HttpTestContext context = server.addHandler(new HttpTestEchoHandler(), "/http1/echo"); |
|
66 * http2URI = "http://localhost:" + server1.getAddress().getPort() + "/http1/echo"; |
|
67 * |
|
68 * Http2TestServer http2TestServer = new Http2TestServer("localhost", false, 0); |
|
69 * HttpTestServer server2 = HttpTestServer.of(http2TestServer); |
|
70 * server2.addHandler(new HttpTestEchoHandler(), "/http2/echo"); |
|
71 * http1URI = "http://localhost:" + server2.getAddress().getPort() + "/http2/echo"; |
|
72 * |
|
73 * }</pre> |
|
74 */ |
|
75 public interface HttpServerAdapters { |
|
76 |
|
77 static final boolean PRINTSTACK = |
|
78 Boolean.getBoolean("jdk.internal.httpclient.debug"); |
|
79 |
|
80 static void uncheckedWrite(ByteArrayOutputStream baos, byte[] ba) { |
|
81 try { |
|
82 baos.write(ba); |
|
83 } catch (IOException e) { |
|
84 throw new UncheckedIOException(e); |
|
85 } |
|
86 } |
|
87 |
|
88 static void printBytes(PrintStream out, String prefix, byte[] bytes) { |
|
89 int padding = 4 + 4 - (bytes.length % 4); |
|
90 padding = padding > 4 ? padding - 4 : 4; |
|
91 byte[] bigbytes = new byte[bytes.length + padding]; |
|
92 System.arraycopy(bytes, 0, bigbytes, padding, bytes.length); |
|
93 out.println(prefix + bytes.length + " " |
|
94 + new BigInteger(bigbytes).toString(16)); |
|
95 } |
|
96 |
|
97 /** |
|
98 * A version agnostic adapter class for HTTP Headers. |
|
99 */ |
|
100 public static abstract class HttpTestHeaders { |
|
101 public abstract Optional<String> firstValue(String name); |
|
102 public abstract void addHeader(String name, String value); |
|
103 public abstract Set<String> keySet(); |
|
104 public abstract Set<Map.Entry<String, List<String>>> entrySet(); |
|
105 public abstract List<String> get(String name); |
|
106 public abstract boolean containsKey(String name); |
|
107 |
|
108 public static HttpTestHeaders of(Headers headers) { |
|
109 return new Http1TestHeaders(headers); |
|
110 } |
|
111 public static HttpTestHeaders of(HttpHeadersImpl headers) { |
|
112 return new Http2TestHeaders(headers); |
|
113 } |
|
114 |
|
115 private final static class Http1TestHeaders extends HttpTestHeaders { |
|
116 private final Headers headers; |
|
117 Http1TestHeaders(Headers h) { this.headers = h; } |
|
118 @Override |
|
119 public Optional<String> firstValue(String name) { |
|
120 if (headers.containsKey(name)) { |
|
121 return Optional.ofNullable(headers.getFirst(name)); |
|
122 } |
|
123 return Optional.empty(); |
|
124 } |
|
125 @Override |
|
126 public void addHeader(String name, String value) { |
|
127 headers.add(name, value); |
|
128 } |
|
129 |
|
130 @Override |
|
131 public Set<String> keySet() { return headers.keySet(); } |
|
132 @Override |
|
133 public Set<Map.Entry<String, List<String>>> entrySet() { |
|
134 return headers.entrySet(); |
|
135 } |
|
136 @Override |
|
137 public List<String> get(String name) { |
|
138 return headers.get(name); |
|
139 } |
|
140 @Override |
|
141 public boolean containsKey(String name) { |
|
142 return headers.containsKey(name); |
|
143 } |
|
144 } |
|
145 private final static class Http2TestHeaders extends HttpTestHeaders { |
|
146 private final HttpHeadersImpl headers; |
|
147 Http2TestHeaders(HttpHeadersImpl h) { this.headers = h; } |
|
148 @Override |
|
149 public Optional<String> firstValue(String name) { |
|
150 return headers.firstValue(name); |
|
151 } |
|
152 @Override |
|
153 public void addHeader(String name, String value) { |
|
154 headers.addHeader(name, value); |
|
155 } |
|
156 public Set<String> keySet() { return headers.map().keySet(); } |
|
157 @Override |
|
158 public Set<Map.Entry<String, List<String>>> entrySet() { |
|
159 return headers.map().entrySet(); |
|
160 } |
|
161 @Override |
|
162 public List<String> get(String name) { |
|
163 return headers.allValues(name); |
|
164 } |
|
165 @Override |
|
166 public boolean containsKey(String name) { |
|
167 return headers.firstValue(name).isPresent(); |
|
168 } |
|
169 } |
|
170 } |
|
171 |
|
172 /** |
|
173 * A version agnostic adapter class for HTTP Server Exchange. |
|
174 */ |
|
175 public static abstract class HttpTestExchange { |
|
176 public abstract Version getServerVersion(); |
|
177 public abstract Version getExchangeVersion(); |
|
178 public abstract InputStream getRequestBody(); |
|
179 public abstract OutputStream getResponseBody(); |
|
180 public abstract HttpTestHeaders getRequestHeaders(); |
|
181 public abstract HttpTestHeaders getResponseHeaders(); |
|
182 public abstract void sendResponseHeaders(int code, int contentLength) throws IOException; |
|
183 public abstract URI getRequestURI(); |
|
184 public abstract String getRequestMethod(); |
|
185 public abstract void close(); |
|
186 public void serverPush(URI uri, HttpTestHeaders headers, byte[] body) { |
|
187 ByteArrayInputStream bais = new ByteArrayInputStream(body); |
|
188 serverPush(uri, headers, bais); |
|
189 } |
|
190 public void serverPush(URI uri, HttpTestHeaders headers, InputStream body) { |
|
191 throw new UnsupportedOperationException("serverPush with " + getExchangeVersion()); |
|
192 } |
|
193 public boolean serverPushAllowed() { |
|
194 return false; |
|
195 } |
|
196 public static HttpTestExchange of(HttpExchange exchange) { |
|
197 return new Http1TestExchange(exchange); |
|
198 } |
|
199 public static HttpTestExchange of(Http2TestExchange exchange) { |
|
200 return new Http2TestExchangeImpl(exchange); |
|
201 } |
|
202 |
|
203 abstract void doFilter(Filter.Chain chain) throws IOException; |
|
204 |
|
205 // implementations... |
|
206 private static final class Http1TestExchange extends HttpTestExchange { |
|
207 private final HttpExchange exchange; |
|
208 Http1TestExchange(HttpExchange exch) { |
|
209 this.exchange = exch; |
|
210 } |
|
211 @Override |
|
212 public Version getServerVersion() { return Version.HTTP_1_1; } |
|
213 @Override |
|
214 public Version getExchangeVersion() { return Version.HTTP_1_1; } |
|
215 @Override |
|
216 public InputStream getRequestBody() { |
|
217 return exchange.getRequestBody(); |
|
218 } |
|
219 @Override |
|
220 public OutputStream getResponseBody() { |
|
221 return exchange.getResponseBody(); |
|
222 } |
|
223 @Override |
|
224 public HttpTestHeaders getRequestHeaders() { |
|
225 return HttpTestHeaders.of(exchange.getRequestHeaders()); |
|
226 } |
|
227 @Override |
|
228 public HttpTestHeaders getResponseHeaders() { |
|
229 return HttpTestHeaders.of(exchange.getResponseHeaders()); |
|
230 } |
|
231 @Override |
|
232 public void sendResponseHeaders(int code, int contentLength) throws IOException { |
|
233 if (contentLength == 0) contentLength = -1; |
|
234 else if (contentLength < 0) contentLength = 0; |
|
235 exchange.sendResponseHeaders(code, contentLength); |
|
236 } |
|
237 @Override |
|
238 void doFilter(Filter.Chain chain) throws IOException { |
|
239 chain.doFilter(exchange); |
|
240 } |
|
241 @Override |
|
242 public void close() { exchange.close(); } |
|
243 @Override |
|
244 public URI getRequestURI() { return exchange.getRequestURI(); } |
|
245 @Override |
|
246 public String getRequestMethod() { return exchange.getRequestMethod(); } |
|
247 @Override |
|
248 public String toString() { |
|
249 return this.getClass().getSimpleName() + ": " + exchange.toString(); |
|
250 } |
|
251 } |
|
252 |
|
253 private static final class Http2TestExchangeImpl extends HttpTestExchange { |
|
254 private final Http2TestExchange exchange; |
|
255 Http2TestExchangeImpl(Http2TestExchange exch) { |
|
256 this.exchange = exch; |
|
257 } |
|
258 @Override |
|
259 public Version getServerVersion() { return Version.HTTP_2; } |
|
260 @Override |
|
261 public Version getExchangeVersion() { return Version.HTTP_2; } |
|
262 @Override |
|
263 public InputStream getRequestBody() { |
|
264 return exchange.getRequestBody(); |
|
265 } |
|
266 @Override |
|
267 public OutputStream getResponseBody() { |
|
268 return exchange.getResponseBody(); |
|
269 } |
|
270 @Override |
|
271 public HttpTestHeaders getRequestHeaders() { |
|
272 return HttpTestHeaders.of(exchange.getRequestHeaders()); |
|
273 } |
|
274 @Override |
|
275 public HttpTestHeaders getResponseHeaders() { |
|
276 return HttpTestHeaders.of(exchange.getResponseHeaders()); |
|
277 } |
|
278 @Override |
|
279 public void sendResponseHeaders(int code, int contentLength) throws IOException { |
|
280 if (contentLength == 0) contentLength = -1; |
|
281 else if (contentLength < 0) contentLength = 0; |
|
282 exchange.sendResponseHeaders(code, contentLength); |
|
283 } |
|
284 @Override |
|
285 public boolean serverPushAllowed() { |
|
286 return exchange.serverPushAllowed(); |
|
287 } |
|
288 @Override |
|
289 public void serverPush(URI uri, HttpTestHeaders headers, InputStream body) { |
|
290 HttpHeadersImpl headersImpl; |
|
291 if (headers instanceof HttpTestHeaders.Http2TestHeaders) { |
|
292 headersImpl = ((HttpTestHeaders.Http2TestHeaders)headers).headers.deepCopy(); |
|
293 } else { |
|
294 headersImpl = new HttpHeadersImpl(); |
|
295 for (Map.Entry<String, List<String>> e : headers.entrySet()) { |
|
296 String name = e.getKey(); |
|
297 for (String v : e.getValue()) { |
|
298 headersImpl.addHeader(name, v); |
|
299 } |
|
300 } |
|
301 } |
|
302 exchange.serverPush(uri, headersImpl, body); |
|
303 } |
|
304 void doFilter(Filter.Chain filter) throws IOException { |
|
305 throw new IOException("cannot use HTTP/1.1 filter with HTTP/2 server"); |
|
306 } |
|
307 @Override |
|
308 public void close() { exchange.close();} |
|
309 @Override |
|
310 public URI getRequestURI() { return exchange.getRequestURI(); } |
|
311 @Override |
|
312 public String getRequestMethod() { return exchange.getRequestMethod(); } |
|
313 @Override |
|
314 public String toString() { |
|
315 return this.getClass().getSimpleName() + ": " + exchange.toString(); |
|
316 } |
|
317 } |
|
318 |
|
319 } |
|
320 |
|
321 |
|
322 /** |
|
323 * A version agnostic adapter class for HTTP Server Handlers. |
|
324 */ |
|
325 public interface HttpTestHandler { |
|
326 void handle(HttpTestExchange t) throws IOException; |
|
327 |
|
328 default HttpHandler toHttpHandler() { |
|
329 return (t) -> doHandle(HttpTestExchange.of(t)); |
|
330 } |
|
331 default Http2Handler toHttp2Handler() { |
|
332 return (t) -> doHandle(HttpTestExchange.of(t)); |
|
333 } |
|
334 private void doHandle(HttpTestExchange t) throws IOException { |
|
335 try { |
|
336 handle(t); |
|
337 } catch (Throwable x) { |
|
338 System.out.println("WARNING: exception caught in HttpTestHandler::handle " + x); |
|
339 System.err.println("WARNING: exception caught in HttpTestHandler::handle " + x); |
|
340 if (PRINTSTACK && !expectException(t)) x.printStackTrace(System.out); |
|
341 throw x; |
|
342 } |
|
343 } |
|
344 } |
|
345 |
|
346 |
|
347 public static class HttpTestEchoHandler implements HttpTestHandler { |
|
348 @Override |
|
349 public void handle(HttpTestExchange t) throws IOException { |
|
350 try (InputStream is = t.getRequestBody(); |
|
351 OutputStream os = t.getResponseBody()) { |
|
352 byte[] bytes = is.readAllBytes(); |
|
353 printBytes(System.out,"Echo server got " |
|
354 + t.getExchangeVersion() + " bytes: ", bytes); |
|
355 if (t.getRequestHeaders().firstValue("Content-type").isPresent()) { |
|
356 t.getResponseHeaders().addHeader("Content-type", |
|
357 t.getRequestHeaders().firstValue("Content-type").get()); |
|
358 } |
|
359 t.sendResponseHeaders(200, bytes.length); |
|
360 os.write(bytes); |
|
361 } |
|
362 } |
|
363 } |
|
364 |
|
365 public static boolean expectException(HttpTestExchange e) { |
|
366 HttpTestHeaders h = e.getRequestHeaders(); |
|
367 Optional<String> expectException = h.firstValue("X-expect-exception"); |
|
368 if (expectException.isPresent()) { |
|
369 return expectException.get().equalsIgnoreCase("true"); |
|
370 } |
|
371 return false; |
|
372 } |
|
373 |
|
374 /** |
|
375 * A version agnostic adapter class for HTTP Server Filter Chains. |
|
376 */ |
|
377 public abstract class HttpChain { |
|
378 |
|
379 public abstract void doFilter(HttpTestExchange exchange) throws IOException; |
|
380 public static HttpChain of(Filter.Chain chain) { |
|
381 return new Http1Chain(chain); |
|
382 } |
|
383 |
|
384 public static HttpChain of(List<HttpTestFilter> filters, HttpTestHandler handler) { |
|
385 return new Http2Chain(filters, handler); |
|
386 } |
|
387 |
|
388 private static class Http1Chain extends HttpChain { |
|
389 final Filter.Chain chain; |
|
390 Http1Chain(Filter.Chain chain) { |
|
391 this.chain = chain; |
|
392 } |
|
393 @Override |
|
394 public void doFilter(HttpTestExchange exchange) throws IOException { |
|
395 try { |
|
396 exchange.doFilter(chain); |
|
397 } catch (Throwable t) { |
|
398 System.out.println("WARNING: exception caught in Http1Chain::doFilter " + t); |
|
399 System.err.println("WARNING: exception caught in Http1Chain::doFilter " + t); |
|
400 if (PRINTSTACK && !expectException(exchange)) t.printStackTrace(System.out); |
|
401 throw t; |
|
402 } |
|
403 } |
|
404 } |
|
405 |
|
406 private static class Http2Chain extends HttpChain { |
|
407 ListIterator<HttpTestFilter> iter; |
|
408 HttpTestHandler handler; |
|
409 Http2Chain(List<HttpTestFilter> filters, HttpTestHandler handler) { |
|
410 this.iter = filters.listIterator(); |
|
411 this.handler = handler; |
|
412 } |
|
413 @Override |
|
414 public void doFilter(HttpTestExchange exchange) throws IOException { |
|
415 try { |
|
416 if (iter.hasNext()) { |
|
417 iter.next().doFilter(exchange, this); |
|
418 } else { |
|
419 handler.handle(exchange); |
|
420 } |
|
421 } catch (Throwable t) { |
|
422 System.out.println("WARNING: exception caught in Http2Chain::doFilter " + t); |
|
423 System.err.println("WARNING: exception caught in Http2Chain::doFilter " + t); |
|
424 if (PRINTSTACK && !expectException(exchange)) t.printStackTrace(System.out); |
|
425 throw t; |
|
426 } |
|
427 } |
|
428 } |
|
429 |
|
430 } |
|
431 |
|
432 /** |
|
433 * A version agnostic adapter class for HTTP Server Filters. |
|
434 */ |
|
435 public abstract class HttpTestFilter { |
|
436 |
|
437 public abstract String description(); |
|
438 |
|
439 public abstract void doFilter(HttpTestExchange exchange, HttpChain chain) throws IOException; |
|
440 |
|
441 public Filter toFilter() { |
|
442 return new Filter() { |
|
443 @Override |
|
444 public void doFilter(HttpExchange exchange, Chain chain) throws IOException { |
|
445 HttpTestFilter.this.doFilter(HttpTestExchange.of(exchange), HttpChain.of(chain)); |
|
446 } |
|
447 @Override |
|
448 public String description() { |
|
449 return HttpTestFilter.this.description(); |
|
450 } |
|
451 }; |
|
452 } |
|
453 } |
|
454 |
|
455 /** |
|
456 * A version agnostic adapter class for HTTP Server Context. |
|
457 */ |
|
458 public static abstract class HttpTestContext { |
|
459 public abstract String getPath(); |
|
460 public abstract void addFilter(HttpTestFilter filter); |
|
461 public abstract Version getVersion(); |
|
462 |
|
463 // will throw UOE if the server is HTTP/2 |
|
464 public abstract void setAuthenticator(com.sun.net.httpserver.Authenticator authenticator); |
|
465 } |
|
466 |
|
467 /** |
|
468 * A version agnostic adapter class for HTTP Servers. |
|
469 */ |
|
470 public static abstract class HttpTestServer { |
|
471 private static final class ServerLogging { |
|
472 private static final Logger logger = Logger.getLogger("com.sun.net.httpserver"); |
|
473 static void enableLogging() { |
|
474 logger.setLevel(Level.FINE); |
|
475 Stream.of(Logger.getLogger("").getHandlers()) |
|
476 .forEach(h -> h.setLevel(Level.ALL)); |
|
477 } |
|
478 } |
|
479 |
|
480 public abstract void start(); |
|
481 public abstract void stop(); |
|
482 public abstract HttpTestContext addHandler(HttpTestHandler handler, String root); |
|
483 public abstract InetSocketAddress getAddress(); |
|
484 public abstract Version getVersion(); |
|
485 |
|
486 public String serverAuthority() { |
|
487 return InetAddress.getLoopbackAddress().getHostName() + ":" |
|
488 + getAddress().getPort(); |
|
489 } |
|
490 |
|
491 public static HttpTestServer of(HttpServer server) { |
|
492 return new Http1TestServer(server); |
|
493 } |
|
494 |
|
495 public static HttpTestServer of(Http2TestServer server) { |
|
496 return new Http2TestServerImpl(server); |
|
497 } |
|
498 |
|
499 private static class Http1TestServer extends HttpTestServer { |
|
500 private final HttpServer impl; |
|
501 Http1TestServer(HttpServer server) { |
|
502 this.impl = server; |
|
503 } |
|
504 @Override |
|
505 public void start() { |
|
506 System.out.println("Http1TestServer: start"); |
|
507 impl.start(); |
|
508 } |
|
509 @Override |
|
510 public void stop() { |
|
511 System.out.println("Http1TestServer: stop"); |
|
512 impl.stop(0); |
|
513 } |
|
514 @Override |
|
515 public HttpTestContext addHandler(HttpTestHandler handler, String path) { |
|
516 System.out.println("Http1TestServer[" + getAddress() |
|
517 + "]::addHandler " + handler + ", " + path); |
|
518 return new Http1TestContext(impl.createContext(path, handler.toHttpHandler())); |
|
519 } |
|
520 @Override |
|
521 public InetSocketAddress getAddress() { |
|
522 return new InetSocketAddress(InetAddress.getLoopbackAddress(), |
|
523 impl.getAddress().getPort()); |
|
524 } |
|
525 public Version getVersion() { return Version.HTTP_1_1; } |
|
526 } |
|
527 |
|
528 private static class Http1TestContext extends HttpTestContext { |
|
529 private final HttpContext context; |
|
530 Http1TestContext(HttpContext ctxt) { |
|
531 this.context = ctxt; |
|
532 } |
|
533 @Override public String getPath() { |
|
534 return context.getPath(); |
|
535 } |
|
536 @Override |
|
537 public void addFilter(HttpTestFilter filter) { |
|
538 System.out.println("Http1TestContext::addFilter " + filter.description()); |
|
539 context.getFilters().add(filter.toFilter()); |
|
540 } |
|
541 @Override |
|
542 public void setAuthenticator(com.sun.net.httpserver.Authenticator authenticator) { |
|
543 context.setAuthenticator(authenticator); |
|
544 } |
|
545 @Override public Version getVersion() { return Version.HTTP_1_1; } |
|
546 } |
|
547 |
|
548 private static class Http2TestServerImpl extends HttpTestServer { |
|
549 private final Http2TestServer impl; |
|
550 Http2TestServerImpl(Http2TestServer server) { |
|
551 this.impl = server; |
|
552 } |
|
553 @Override |
|
554 public void start() { |
|
555 System.out.println("Http2TestServerImpl: start"); |
|
556 impl.start(); |
|
557 } |
|
558 @Override |
|
559 public void stop() { |
|
560 System.out.println("Http2TestServerImpl: stop"); |
|
561 impl.stop(); |
|
562 } |
|
563 @Override |
|
564 public HttpTestContext addHandler(HttpTestHandler handler, String path) { |
|
565 System.out.println("Http2TestServerImpl[" + getAddress() |
|
566 + "]::addHandler " + handler + ", " + path); |
|
567 Http2TestContext context = new Http2TestContext(handler, path); |
|
568 impl.addHandler(context.toHttp2Handler(), path); |
|
569 return context; |
|
570 } |
|
571 @Override |
|
572 public InetSocketAddress getAddress() { |
|
573 return new InetSocketAddress(InetAddress.getLoopbackAddress(), |
|
574 impl.getAddress().getPort()); |
|
575 } |
|
576 public Version getVersion() { return Version.HTTP_2; } |
|
577 } |
|
578 |
|
579 private static class Http2TestContext |
|
580 extends HttpTestContext implements HttpTestHandler { |
|
581 private final HttpTestHandler handler; |
|
582 private final String path; |
|
583 private final List<HttpTestFilter> filters = new CopyOnWriteArrayList<>(); |
|
584 Http2TestContext(HttpTestHandler hdl, String path) { |
|
585 this.handler = hdl; |
|
586 this.path = path; |
|
587 } |
|
588 @Override |
|
589 public String getPath() { return path; } |
|
590 @Override |
|
591 public void addFilter(HttpTestFilter filter) { |
|
592 System.out.println("Http2TestContext::addFilter " + filter.description()); |
|
593 filters.add(filter); |
|
594 } |
|
595 @Override |
|
596 public void handle(HttpTestExchange exchange) throws IOException { |
|
597 System.out.println("Http2TestContext::handle " + exchange); |
|
598 HttpChain.of(filters, handler).doFilter(exchange); |
|
599 } |
|
600 @Override |
|
601 public void setAuthenticator(com.sun.net.httpserver.Authenticator authenticator) { |
|
602 throw new UnsupportedOperationException("Can't set HTTP/1.1 authenticator on HTTP/2 context"); |
|
603 } |
|
604 @Override public Version getVersion() { return Version.HTTP_2; } |
|
605 } |
|
606 } |
|
607 |
|
608 public static void enableServerLogging() { |
|
609 System.setProperty("java.util.logging.SimpleFormatter.format", |
|
610 "%4$s [%1$tb %1$td, %1$tl:%1$tM:%1$tS.%1$tN] %2$s: %5$s%6$s%n"); |
|
611 HttpTestServer.ServerLogging.enableLogging(); |
|
612 } |
|
613 |
|
614 } |