256b52387b
Reviewed-by: jpai
677 lines
26 KiB
Java
677 lines
26 KiB
Java
/*
|
|
* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
import com.sun.net.httpserver.Filter;
|
|
import com.sun.net.httpserver.Headers;
|
|
import com.sun.net.httpserver.HttpContext;
|
|
import com.sun.net.httpserver.HttpExchange;
|
|
import com.sun.net.httpserver.HttpHandler;
|
|
import com.sun.net.httpserver.HttpServer;
|
|
import jdk.internal.net.http.common.HttpHeadersBuilder;
|
|
|
|
import java.net.InetAddress;
|
|
import java.io.ByteArrayInputStream;
|
|
import java.net.http.HttpClient.Version;
|
|
import java.io.ByteArrayOutputStream;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.OutputStream;
|
|
import java.io.PrintStream;
|
|
import java.io.UncheckedIOException;
|
|
import java.math.BigInteger;
|
|
import java.net.InetSocketAddress;
|
|
import java.net.URI;
|
|
import java.net.http.HttpHeaders;
|
|
import java.util.List;
|
|
import java.util.ListIterator;
|
|
import java.util.Map;
|
|
import java.util.Objects;
|
|
import java.util.Optional;
|
|
import java.util.Set;
|
|
import java.util.concurrent.CopyOnWriteArrayList;
|
|
import java.util.concurrent.ExecutorService;
|
|
import java.util.logging.Level;
|
|
import java.util.logging.Logger;
|
|
import java.util.stream.Stream;
|
|
|
|
/**
|
|
* Defines an adaptation layers so that a test server handlers and filters
|
|
* can be implemented independently of the underlying server version.
|
|
* <p>
|
|
* For instance:
|
|
* <pre>{@code
|
|
*
|
|
* URI http1URI, http2URI;
|
|
*
|
|
* InetSocketAddress sa = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
|
|
* HttpTestServer server1 = HttpTestServer.of(HttpServer.create(sa, 0));
|
|
* HttpTestContext context = server.addHandler(new HttpTestEchoHandler(), "/http1/echo");
|
|
* http2URI = "http://localhost:" + server1.getAddress().getPort() + "/http1/echo";
|
|
*
|
|
* Http2TestServer http2TestServer = new Http2TestServer("localhost", false, 0);
|
|
* HttpTestServer server2 = HttpTestServer.of(http2TestServer);
|
|
* server2.addHandler(new HttpTestEchoHandler(), "/http2/echo");
|
|
* http1URI = "http://localhost:" + server2.getAddress().getPort() + "/http2/echo";
|
|
*
|
|
* }</pre>
|
|
*/
|
|
public interface HttpServerAdapters {
|
|
|
|
static final boolean PRINTSTACK =
|
|
Boolean.getBoolean("jdk.internal.httpclient.debug");
|
|
|
|
static void uncheckedWrite(ByteArrayOutputStream baos, byte[] ba) {
|
|
try {
|
|
baos.write(ba);
|
|
} catch (IOException e) {
|
|
throw new UncheckedIOException(e);
|
|
}
|
|
}
|
|
|
|
static void printBytes(PrintStream out, String prefix, byte[] bytes) {
|
|
int padding = 4 + 4 - (bytes.length % 4);
|
|
padding = padding > 4 ? padding - 4 : 4;
|
|
byte[] bigbytes = new byte[bytes.length + padding];
|
|
System.arraycopy(bytes, 0, bigbytes, padding, bytes.length);
|
|
out.println(prefix + bytes.length + " "
|
|
+ new BigInteger(bigbytes).toString(16));
|
|
}
|
|
|
|
/**
|
|
* A version agnostic adapter class for HTTP request Headers.
|
|
*/
|
|
public static abstract class HttpTestRequestHeaders {
|
|
public abstract Optional<String> firstValue(String name);
|
|
public abstract Set<String> keySet();
|
|
public abstract Set<Map.Entry<String, List<String>>> entrySet();
|
|
public abstract List<String> get(String name);
|
|
public abstract boolean containsKey(String name);
|
|
@Override
|
|
public boolean equals(Object o) {
|
|
if (this == o) return true;
|
|
if (!(o instanceof HttpTestRequestHeaders other)) return false;
|
|
return Objects.equals(entrySet(), other.entrySet());
|
|
}
|
|
@Override
|
|
public int hashCode() {
|
|
return Objects.hashCode(entrySet());
|
|
}
|
|
|
|
public static HttpTestRequestHeaders of(Headers headers) {
|
|
return new Http1TestRequestHeaders(headers);
|
|
}
|
|
|
|
public static HttpTestRequestHeaders of(HttpHeaders headers) {
|
|
return new Http2TestRequestHeaders(headers);
|
|
}
|
|
|
|
private static final class Http1TestRequestHeaders extends HttpTestRequestHeaders {
|
|
private final Headers headers;
|
|
Http1TestRequestHeaders(Headers h) { this.headers = h; }
|
|
@Override
|
|
public Optional<String> firstValue(String name) {
|
|
if (headers.containsKey(name)) {
|
|
return Optional.ofNullable(headers.getFirst(name));
|
|
}
|
|
return Optional.empty();
|
|
}
|
|
@Override
|
|
public Set<String> keySet() { return headers.keySet(); }
|
|
@Override
|
|
public Set<Map.Entry<String, List<String>>> entrySet() {
|
|
return headers.entrySet();
|
|
}
|
|
@Override
|
|
public List<String> get(String name) {
|
|
return headers.get(name);
|
|
}
|
|
@Override
|
|
public boolean containsKey(String name) {
|
|
return headers.containsKey(name);
|
|
}
|
|
@Override
|
|
public String toString() {
|
|
return String.valueOf(headers);
|
|
}
|
|
}
|
|
private static final class Http2TestRequestHeaders extends HttpTestRequestHeaders {
|
|
private final HttpHeaders headers;
|
|
Http2TestRequestHeaders(HttpHeaders h) { this.headers = h; }
|
|
@Override
|
|
public Optional<String> firstValue(String name) {
|
|
return headers.firstValue(name);
|
|
}
|
|
@Override
|
|
public Set<String> keySet() { return headers.map().keySet(); }
|
|
@Override
|
|
public Set<Map.Entry<String, List<String>>> entrySet() {
|
|
return headers.map().entrySet();
|
|
}
|
|
@Override
|
|
public List<String> get(String name) {
|
|
return headers.allValues(name);
|
|
}
|
|
@Override
|
|
public boolean containsKey(String name) {
|
|
return headers.firstValue(name).isPresent();
|
|
}
|
|
@Override
|
|
public String toString() {
|
|
return String.valueOf(headers);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A version agnostic adapter class for HTTP response Headers.
|
|
*/
|
|
public static abstract class HttpTestResponseHeaders {
|
|
public abstract void addHeader(String name, String value);
|
|
|
|
public static HttpTestResponseHeaders of(Headers headers) {
|
|
return new Http1TestResponseHeaders(headers);
|
|
}
|
|
public static HttpTestResponseHeaders of(HttpHeadersBuilder headersBuilder) {
|
|
return new Http2TestResponseHeaders(headersBuilder);
|
|
}
|
|
|
|
private final static class Http1TestResponseHeaders extends HttpTestResponseHeaders {
|
|
private final Headers headers;
|
|
Http1TestResponseHeaders(Headers h) { this.headers = h; }
|
|
@Override
|
|
public void addHeader(String name, String value) {
|
|
headers.add(name, value);
|
|
}
|
|
}
|
|
private final static class Http2TestResponseHeaders extends HttpTestResponseHeaders {
|
|
private final HttpHeadersBuilder headersBuilder;
|
|
Http2TestResponseHeaders(HttpHeadersBuilder hb) { this.headersBuilder = hb; }
|
|
@Override
|
|
public void addHeader(String name, String value) {
|
|
headersBuilder.addHeader(name, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A version agnostic adapter class for HTTP Server Exchange.
|
|
*/
|
|
public static abstract class HttpTestExchange implements AutoCloseable {
|
|
public abstract Version getServerVersion();
|
|
public abstract Version getExchangeVersion();
|
|
public abstract InputStream getRequestBody();
|
|
public abstract OutputStream getResponseBody();
|
|
public abstract HttpTestRequestHeaders getRequestHeaders();
|
|
public abstract HttpTestResponseHeaders getResponseHeaders();
|
|
public abstract void sendResponseHeaders(int code, int contentLength) throws IOException;
|
|
public abstract URI getRequestURI();
|
|
public abstract String getRequestMethod();
|
|
public abstract void close();
|
|
public abstract InetSocketAddress getRemoteAddress();
|
|
public void serverPush(URI uri, HttpHeaders headers, byte[] body) {
|
|
ByteArrayInputStream bais = new ByteArrayInputStream(body);
|
|
serverPush(uri, headers, bais);
|
|
}
|
|
public void serverPush(URI uri, HttpHeaders headers, InputStream body) {
|
|
throw new UnsupportedOperationException("serverPush with " + getExchangeVersion());
|
|
}
|
|
public boolean serverPushAllowed() {
|
|
return false;
|
|
}
|
|
public static HttpTestExchange of(HttpExchange exchange) {
|
|
return new Http1TestExchange(exchange);
|
|
}
|
|
public static HttpTestExchange of(Http2TestExchange exchange) {
|
|
return new Http2TestExchangeImpl(exchange);
|
|
}
|
|
|
|
abstract void doFilter(Filter.Chain chain) throws IOException;
|
|
|
|
// implementations...
|
|
private static final class Http1TestExchange extends HttpTestExchange {
|
|
private final HttpExchange exchange;
|
|
Http1TestExchange(HttpExchange exch) {
|
|
this.exchange = exch;
|
|
}
|
|
@Override
|
|
public Version getServerVersion() { return Version.HTTP_1_1; }
|
|
@Override
|
|
public Version getExchangeVersion() { return Version.HTTP_1_1; }
|
|
@Override
|
|
public InputStream getRequestBody() {
|
|
return exchange.getRequestBody();
|
|
}
|
|
@Override
|
|
public OutputStream getResponseBody() {
|
|
return exchange.getResponseBody();
|
|
}
|
|
@Override
|
|
public HttpTestRequestHeaders getRequestHeaders() {
|
|
return HttpTestRequestHeaders.of(exchange.getRequestHeaders());
|
|
}
|
|
@Override
|
|
public HttpTestResponseHeaders getResponseHeaders() {
|
|
return HttpTestResponseHeaders.of(exchange.getResponseHeaders());
|
|
}
|
|
@Override
|
|
public void sendResponseHeaders(int code, int contentLength) throws IOException {
|
|
if (contentLength == 0) contentLength = -1;
|
|
else if (contentLength < 0) contentLength = 0;
|
|
exchange.sendResponseHeaders(code, contentLength);
|
|
}
|
|
@Override
|
|
void doFilter(Filter.Chain chain) throws IOException {
|
|
chain.doFilter(exchange);
|
|
}
|
|
@Override
|
|
public void close() { exchange.close(); }
|
|
|
|
@Override
|
|
public InetSocketAddress getRemoteAddress() {
|
|
return exchange.getRemoteAddress();
|
|
}
|
|
|
|
@Override
|
|
public URI getRequestURI() { return exchange.getRequestURI(); }
|
|
@Override
|
|
public String getRequestMethod() { return exchange.getRequestMethod(); }
|
|
@Override
|
|
public String toString() {
|
|
return this.getClass().getSimpleName() + ": " + exchange.toString();
|
|
}
|
|
}
|
|
|
|
private static final class Http2TestExchangeImpl extends HttpTestExchange {
|
|
private final Http2TestExchange exchange;
|
|
Http2TestExchangeImpl(Http2TestExchange exch) {
|
|
this.exchange = exch;
|
|
}
|
|
@Override
|
|
public Version getServerVersion() { return Version.HTTP_2; }
|
|
@Override
|
|
public Version getExchangeVersion() { return Version.HTTP_2; }
|
|
@Override
|
|
public InputStream getRequestBody() {
|
|
return exchange.getRequestBody();
|
|
}
|
|
@Override
|
|
public OutputStream getResponseBody() {
|
|
return exchange.getResponseBody();
|
|
}
|
|
@Override
|
|
public HttpTestRequestHeaders getRequestHeaders() {
|
|
return HttpTestRequestHeaders.of(exchange.getRequestHeaders());
|
|
}
|
|
|
|
@Override
|
|
public HttpTestResponseHeaders getResponseHeaders() {
|
|
return HttpTestResponseHeaders.of(exchange.getResponseHeaders());
|
|
}
|
|
@Override
|
|
public void sendResponseHeaders(int code, int contentLength) throws IOException {
|
|
if (contentLength == 0) contentLength = -1;
|
|
else if (contentLength < 0) contentLength = 0;
|
|
exchange.sendResponseHeaders(code, contentLength);
|
|
}
|
|
@Override
|
|
public boolean serverPushAllowed() {
|
|
return exchange.serverPushAllowed();
|
|
}
|
|
@Override
|
|
public void serverPush(URI uri, HttpHeaders headers, InputStream body) {
|
|
exchange.serverPush(uri, headers, body);
|
|
}
|
|
void doFilter(Filter.Chain filter) throws IOException {
|
|
throw new IOException("cannot use HTTP/1.1 filter with HTTP/2 server");
|
|
}
|
|
@Override
|
|
public void close() { exchange.close();}
|
|
|
|
@Override
|
|
public InetSocketAddress getRemoteAddress() {
|
|
return exchange.getRemoteAddress();
|
|
}
|
|
|
|
@Override
|
|
public URI getRequestURI() { return exchange.getRequestURI(); }
|
|
@Override
|
|
public String getRequestMethod() { return exchange.getRequestMethod(); }
|
|
@Override
|
|
public String toString() {
|
|
return this.getClass().getSimpleName() + ": " + exchange.toString();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* A version agnostic adapter class for HTTP Server Handlers.
|
|
*/
|
|
public interface HttpTestHandler {
|
|
void handle(HttpTestExchange t) throws IOException;
|
|
|
|
default HttpHandler toHttpHandler() {
|
|
return (t) -> doHandle(HttpTestExchange.of(t));
|
|
}
|
|
default Http2Handler toHttp2Handler() {
|
|
return (t) -> doHandle(HttpTestExchange.of(t));
|
|
}
|
|
private void doHandle(HttpTestExchange t) throws IOException {
|
|
try {
|
|
handle(t);
|
|
} catch (Throwable x) {
|
|
System.out.println("WARNING: exception caught in HttpTestHandler::handle " + x);
|
|
System.err.println("WARNING: exception caught in HttpTestHandler::handle " + x);
|
|
if (PRINTSTACK && !expectException(t)) x.printStackTrace(System.out);
|
|
throw x;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public static class HttpTestEchoHandler implements HttpTestHandler {
|
|
@Override
|
|
public void handle(HttpTestExchange t) throws IOException {
|
|
try (InputStream is = t.getRequestBody();
|
|
OutputStream os = t.getResponseBody()) {
|
|
byte[] bytes = is.readAllBytes();
|
|
printBytes(System.out,"Echo server got "
|
|
+ t.getExchangeVersion() + " bytes: ", bytes);
|
|
if (t.getRequestHeaders().firstValue("Content-type").isPresent()) {
|
|
t.getResponseHeaders().addHeader("Content-type",
|
|
t.getRequestHeaders().firstValue("Content-type").get());
|
|
}
|
|
t.sendResponseHeaders(200, bytes.length);
|
|
os.write(bytes);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static boolean expectException(HttpTestExchange e) {
|
|
HttpTestRequestHeaders h = e.getRequestHeaders();
|
|
Optional<String> expectException = h.firstValue("X-expect-exception");
|
|
if (expectException.isPresent()) {
|
|
return expectException.get().equalsIgnoreCase("true");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* A version agnostic adapter class for HTTP Server Filter Chains.
|
|
*/
|
|
public abstract class HttpChain {
|
|
|
|
public abstract void doFilter(HttpTestExchange exchange) throws IOException;
|
|
public static HttpChain of(Filter.Chain chain) {
|
|
return new Http1Chain(chain);
|
|
}
|
|
|
|
public static HttpChain of(List<HttpTestFilter> filters, HttpTestHandler handler) {
|
|
return new Http2Chain(filters, handler);
|
|
}
|
|
|
|
private static class Http1Chain extends HttpChain {
|
|
final Filter.Chain chain;
|
|
Http1Chain(Filter.Chain chain) {
|
|
this.chain = chain;
|
|
}
|
|
@Override
|
|
public void doFilter(HttpTestExchange exchange) throws IOException {
|
|
try {
|
|
exchange.doFilter(chain);
|
|
} catch (Throwable t) {
|
|
System.out.println("WARNING: exception caught in Http1Chain::doFilter " + t);
|
|
System.err.println("WARNING: exception caught in Http1Chain::doFilter " + t);
|
|
if (PRINTSTACK && !expectException(exchange)) t.printStackTrace(System.out);
|
|
throw t;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static class Http2Chain extends HttpChain {
|
|
ListIterator<HttpTestFilter> iter;
|
|
HttpTestHandler handler;
|
|
Http2Chain(List<HttpTestFilter> filters, HttpTestHandler handler) {
|
|
this.iter = filters.listIterator();
|
|
this.handler = handler;
|
|
}
|
|
@Override
|
|
public void doFilter(HttpTestExchange exchange) throws IOException {
|
|
try {
|
|
if (iter.hasNext()) {
|
|
iter.next().doFilter(exchange, this);
|
|
} else {
|
|
handler.handle(exchange);
|
|
}
|
|
} catch (Throwable t) {
|
|
System.out.println("WARNING: exception caught in Http2Chain::doFilter " + t);
|
|
System.err.println("WARNING: exception caught in Http2Chain::doFilter " + t);
|
|
if (PRINTSTACK && !expectException(exchange)) t.printStackTrace(System.out);
|
|
throw t;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* A version agnostic adapter class for HTTP Server Filters.
|
|
*/
|
|
public abstract class HttpTestFilter {
|
|
|
|
public abstract String description();
|
|
|
|
public abstract void doFilter(HttpTestExchange exchange, HttpChain chain) throws IOException;
|
|
|
|
public Filter toFilter() {
|
|
return new Filter() {
|
|
@Override
|
|
public void doFilter(HttpExchange exchange, Chain chain) throws IOException {
|
|
HttpTestFilter.this.doFilter(HttpTestExchange.of(exchange), HttpChain.of(chain));
|
|
}
|
|
@Override
|
|
public String description() {
|
|
return HttpTestFilter.this.description();
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A version agnostic adapter class for HTTP Server Context.
|
|
*/
|
|
public static abstract class HttpTestContext {
|
|
public abstract String getPath();
|
|
public abstract void addFilter(HttpTestFilter filter);
|
|
public abstract Version getVersion();
|
|
|
|
// will throw UOE if the server is HTTP/2
|
|
public abstract void setAuthenticator(com.sun.net.httpserver.Authenticator authenticator);
|
|
}
|
|
|
|
/**
|
|
* A version agnostic adapter class for HTTP Servers.
|
|
*/
|
|
public static abstract class HttpTestServer {
|
|
private static final class ServerLogging {
|
|
private static final Logger logger = Logger.getLogger("com.sun.net.httpserver");
|
|
static void enableLogging() {
|
|
logger.setLevel(Level.FINE);
|
|
Stream.of(Logger.getLogger("").getHandlers())
|
|
.forEach(h -> h.setLevel(Level.ALL));
|
|
}
|
|
}
|
|
|
|
public abstract void start();
|
|
public abstract void stop();
|
|
public abstract HttpTestContext addHandler(HttpTestHandler handler, String root);
|
|
public abstract InetSocketAddress getAddress();
|
|
public abstract Version getVersion();
|
|
|
|
public String serverAuthority() {
|
|
return InetAddress.getLoopbackAddress().getHostName() + ":"
|
|
+ getAddress().getPort();
|
|
}
|
|
|
|
public static HttpTestServer of(HttpServer server) {
|
|
return new Http1TestServer(server);
|
|
}
|
|
|
|
public static HttpTestServer of(HttpServer server, ExecutorService executor) {
|
|
return new Http1TestServer(server, executor);
|
|
}
|
|
|
|
public static HttpTestServer of(Http2TestServer server) {
|
|
return new Http2TestServerImpl(server);
|
|
}
|
|
|
|
private static class Http1TestServer extends HttpTestServer {
|
|
private final HttpServer impl;
|
|
private final ExecutorService executor;
|
|
Http1TestServer(HttpServer server) {
|
|
this(server, null);
|
|
}
|
|
Http1TestServer(HttpServer server, ExecutorService executor) {
|
|
if (executor != null) server.setExecutor(executor);
|
|
this.executor = executor;
|
|
this.impl = server;
|
|
}
|
|
@Override
|
|
public void start() {
|
|
System.out.println("Http1TestServer: start");
|
|
impl.start();
|
|
}
|
|
@Override
|
|
public void stop() {
|
|
System.out.println("Http1TestServer: stop");
|
|
try {
|
|
impl.stop(0);
|
|
} finally {
|
|
if (executor != null) {
|
|
executor.shutdownNow();
|
|
}
|
|
}
|
|
}
|
|
@Override
|
|
public HttpTestContext addHandler(HttpTestHandler handler, String path) {
|
|
System.out.println("Http1TestServer[" + getAddress()
|
|
+ "]::addHandler " + handler + ", " + path);
|
|
return new Http1TestContext(impl.createContext(path, handler.toHttpHandler()));
|
|
}
|
|
@Override
|
|
public InetSocketAddress getAddress() {
|
|
return new InetSocketAddress(InetAddress.getLoopbackAddress(),
|
|
impl.getAddress().getPort());
|
|
}
|
|
public Version getVersion() { return Version.HTTP_1_1; }
|
|
}
|
|
|
|
private static class Http1TestContext extends HttpTestContext {
|
|
private final HttpContext context;
|
|
Http1TestContext(HttpContext ctxt) {
|
|
this.context = ctxt;
|
|
}
|
|
@Override public String getPath() {
|
|
return context.getPath();
|
|
}
|
|
@Override
|
|
public void addFilter(HttpTestFilter filter) {
|
|
System.out.println("Http1TestContext::addFilter " + filter.description());
|
|
context.getFilters().add(filter.toFilter());
|
|
}
|
|
@Override
|
|
public void setAuthenticator(com.sun.net.httpserver.Authenticator authenticator) {
|
|
context.setAuthenticator(authenticator);
|
|
}
|
|
@Override public Version getVersion() { return Version.HTTP_1_1; }
|
|
}
|
|
|
|
private static class Http2TestServerImpl extends HttpTestServer {
|
|
private final Http2TestServer impl;
|
|
Http2TestServerImpl(Http2TestServer server) {
|
|
this.impl = server;
|
|
}
|
|
@Override
|
|
public void start() {
|
|
System.out.println("Http2TestServerImpl: start");
|
|
impl.start();
|
|
}
|
|
@Override
|
|
public void stop() {
|
|
System.out.println("Http2TestServerImpl: stop");
|
|
impl.stop();
|
|
}
|
|
@Override
|
|
public HttpTestContext addHandler(HttpTestHandler handler, String path) {
|
|
System.out.println("Http2TestServerImpl[" + getAddress()
|
|
+ "]::addHandler " + handler + ", " + path);
|
|
Http2TestContext context = new Http2TestContext(handler, path);
|
|
impl.addHandler(context.toHttp2Handler(), path);
|
|
return context;
|
|
}
|
|
@Override
|
|
public InetSocketAddress getAddress() {
|
|
return new InetSocketAddress(InetAddress.getLoopbackAddress(),
|
|
impl.getAddress().getPort());
|
|
}
|
|
public Version getVersion() { return Version.HTTP_2; }
|
|
}
|
|
|
|
private static class Http2TestContext
|
|
extends HttpTestContext implements HttpTestHandler {
|
|
private final HttpTestHandler handler;
|
|
private final String path;
|
|
private final List<HttpTestFilter> filters = new CopyOnWriteArrayList<>();
|
|
Http2TestContext(HttpTestHandler hdl, String path) {
|
|
this.handler = hdl;
|
|
this.path = path;
|
|
}
|
|
@Override
|
|
public String getPath() { return path; }
|
|
@Override
|
|
public void addFilter(HttpTestFilter filter) {
|
|
System.out.println("Http2TestContext::addFilter " + filter.description());
|
|
filters.add(filter);
|
|
}
|
|
@Override
|
|
public void handle(HttpTestExchange exchange) throws IOException {
|
|
System.out.println("Http2TestContext::handle " + exchange);
|
|
HttpChain.of(filters, handler).doFilter(exchange);
|
|
}
|
|
@Override
|
|
public void setAuthenticator(com.sun.net.httpserver.Authenticator authenticator) {
|
|
throw new UnsupportedOperationException("Can't set HTTP/1.1 authenticator on HTTP/2 context");
|
|
}
|
|
@Override public Version getVersion() { return Version.HTTP_2; }
|
|
}
|
|
}
|
|
|
|
public static void enableServerLogging() {
|
|
System.setProperty("java.util.logging.SimpleFormatter.format",
|
|
"%4$s [%1$tb %1$td, %1$tl:%1$tM:%1$tS.%1$tN] %2$s: %5$s%6$s%n");
|
|
HttpTestServer.ServerLogging.enableLogging();
|
|
}
|
|
|
|
}
|