diff --git a/vertx-core/src/main/asciidoc/http.adoc b/vertx-core/src/main/asciidoc/http.adoc index 29c39569b09..383e6e85c2b 100644 --- a/vertx-core/src/main/asciidoc/http.adoc +++ b/vertx-core/src/main/asciidoc/http.adoc @@ -2029,64 +2029,31 @@ on the same port value will share this random port. Vert.x http servers and clients can be configured to use HTTPS in exactly the same way as net servers. -==== HTTPS on the server +Please see <> for more information. -HTTPS is enabled with the `HttpServerOptions` {@link io.vertx.core.http.HttpServerOptions#setSsl(boolean) ssl} setting. - -By default, it is disabled. - -[source,$lang] ----- -{@link examples.HTTPExamples#sslServerConfiguration} ----- - -You can read more about <> - -==== HTTPS on the client - -Client SSL/TLS is enabled with the `HttpClientOptions` {@link io.vertx.core.http.HttpClientOptions#setSsl(boolean) ssl} property or {@link io.vertx.core.http.RequestOptions#setSsl(java.lang.Boolean) ssl} property. - -{@link io.vertx.core.http.HttpClientOptions#setSsl(boolean)} setting acts as the default client setting. +SSL can also be enabled/disabled per request with {@link io.vertx.core.http.RequestOptions} or when +specifying a scheme with {@link io.vertx.core.http.RequestOptions#setAbsoluteURI(java.lang.String)} +method. [source,$lang] ---- -{@link examples.HTTPExamples#sslClientConfiguration} +{@link examples.HTTPExamples#setSSLPerRequest(io.vertx.core.http.HttpClient)} ---- -SSL can also be enabled/disabled per request with {@link io.vertx.core.http.RequestOptions} or when -specifying a scheme with {@link io.vertx.core.http.RequestOptions#setAbsoluteURI(java.lang.String)} -method. +The {@link io.vertx.core.http.HttpClientOptions#setSsl(boolean)} setting acts as the default client setting. -{@link io.vertx.core.http.RequestOptions#setSsl(Boolean)} overrides the default client setting. +The {@link io.vertx.core.http.RequestOptions#setSsl(Boolean)} overrides the default client setting * setting the value to `false` will disable SSL/TLS even if the client is configured to use SSL/TLS -* setting the value to `true` will enable SSL/TLS even if the client is configured to not use SSL/TLS, the actual client SSL/TLS (such as trust, key/certificate, ciphers, ALPN, ...) will be reused +* setting the value to `true` will enable SSL/TLS even if the client is configured to not use SSL/TLS, the actual +client SSL/TLS (such as trust, key/certificate, ciphers, ALPN, ...) will be reused Likewise {@link io.vertx.core.http.RequestOptions#setAbsoluteURI(java.lang.String)} scheme also overrides the default client setting. -[source,$lang] ----- -{@link examples.HTTPExamples#sslClientRequestConfiguration} ----- - -You can also set {@link io.vertx.core.net.ClientSSLOptions} at request time. - -[source,$lang] ----- -{@link examples.HTTPExamples#sslClientRequestConfiguration2} ----- - -You can read more about <>. - ==== Server Name Indication (SNI) -Vert.x http servers can be configured to use SNI. - -[source,$lang] ----- -{@link examples.HTTPExamples#serverSNIConfig} ----- +Vert.x http servers can be configured to use SNI in exactly the same way as {@linkplain io.vertx.core.net net servers}. Vert.x http client will present the actual hostname as _server name_ during the TLS handshake. diff --git a/vertx-core/src/main/java/examples/HTTPExamples.java b/vertx-core/src/main/java/examples/HTTPExamples.java index 4963d798f96..f31b3902807 100644 --- a/vertx-core/src/main/java/examples/HTTPExamples.java +++ b/vertx-core/src/main/java/examples/HTTPExamples.java @@ -51,17 +51,9 @@ import io.vertx.core.http.WebSocketConnectOptions; import io.vertx.core.http.WebSocketFrame; import io.vertx.core.json.JsonObject; -import io.vertx.core.net.ClientSSLOptions; -import io.vertx.core.net.ConnectOptions; -import io.vertx.core.net.JksOptions; -import io.vertx.core.net.NetClient; -import io.vertx.core.net.NetClientOptions; -import io.vertx.core.net.NetServer; -import io.vertx.core.net.NetServerOptions; import io.vertx.core.net.NetSocket; import io.vertx.core.net.ProxyOptions; import io.vertx.core.net.ProxyType; -import io.vertx.core.net.ServerSSLOptions; import io.vertx.core.net.endpoint.LoadBalancer; import io.vertx.core.net.endpoint.ServerEndpoint; import io.vertx.core.streams.Pipe; @@ -1301,54 +1293,7 @@ public void randomServersharing(Vertx vertx) { }).listen(-1); } - public void sslServerConfiguration(Vertx vertx) { - ServerSSLOptions sslOptions = new ServerSSLOptions() - .setKeyCertOptions( - new JksOptions(). - setPath("/path/to/your/server-keystore.jks"). - setPassword("password-of-your-keystore") - ); - - HttpServerOptions options = new HttpServerOptions() - .setSsl(true) - .setSslOptions(sslOptions); - - HttpServer server = vertx.createHttpServer(options); - } - - public void sslClientConfiguration(Vertx vertx) { - ClientSSLOptions sslOptions = new ClientSSLOptions() - .setTrustOptions(new JksOptions(). - setPath("/path/to/your/truststore.jks"). - setPassword("password-of-your-truststore") - ); - - HttpClientOptions options = new HttpClientOptions() - .setSsl(true) - .setSslOptions(sslOptions); - - HttpClientAgent client = vertx.createHttpClient(options); - } - - public void serverSNIConfig(Vertx vertx) { - ServerSSLOptions sslOptions = new ServerSSLOptions() - .setKeyCertOptions(new JksOptions(). - setPath("/path/to/your/server-keystore.jks"). - setPassword("password-of-your-keystore")) - .setSni(true); - } - - public void sslClientRequestConfiguration(Vertx vertx, int port, String host) { - ClientSSLOptions sslOptions = new ClientSSLOptions() - .setTrustOptions(new JksOptions(). - setPath("/path/to/your/truststore.jks"). - setPassword("password-of-your-truststore") - ); - - HttpClientOptions options = new HttpClientOptions().setSslOptions(sslOptions); - - HttpClientAgent client = vertx.createHttpClient(options); - + public void setSSLPerRequest(HttpClient client) { client .request(new RequestOptions() .setHost("localhost") @@ -1361,28 +1306,6 @@ public void sslClientRequestConfiguration(Vertx vertx, int port, String host) { }); } - public void sslClientRequestConfiguration2(Vertx vertx, int port, String host) { - HttpClientAgent client = vertx.createHttpClient(); - - ClientSSLOptions sslOptions = new ClientSSLOptions() - .setTrustOptions(new JksOptions(). - setPath("/path/to/your/truststore.jks"). - setPassword("password-of-your-truststore") - ); - - client - .request(new RequestOptions() - .setHost("localhost") - .setPort(8080) - .setURI("/") - .setSsl(true) - .setSslOptions(sslOptions)) - .compose(request -> request.send()) - .onSuccess(response -> { - System.out.println("Received response with status code " + response.statusCode()); - }); - } - public static void setIdentityContentEncodingHeader(HttpServerRequest request) { // Disable compression and send an image request.response() diff --git a/vertx-core/src/main/java/io/vertx/core/http/Http2ServerConfig.java b/vertx-core/src/main/java/io/vertx/core/http/Http2ServerConfig.java index a16eceec417..3766ea3274b 100644 --- a/vertx-core/src/main/java/io/vertx/core/http/Http2ServerConfig.java +++ b/vertx-core/src/main/java/io/vertx/core/http/Http2ServerConfig.java @@ -25,7 +25,6 @@ public class Http2ServerConfig { private Http2Settings initialSettings; - private boolean clearTextEnabled; private int connectionWindowSize; private boolean multiplexImplementation; private int rstFloodMaxRstFramePerWindow; @@ -33,7 +32,6 @@ public class Http2ServerConfig { public Http2ServerConfig() { initialSettings = new Http2Settings().setMaxConcurrentStreams(DEFAULT_INITIAL_SETTINGS_MAX_CONCURRENT_STREAMS); - clearTextEnabled = DEFAULT_HTTP2_CLEAR_TEXT_ENABLED; connectionWindowSize = DEFAULT_HTTP2_CONNECTION_WINDOW_SIZE; rstFloodMaxRstFramePerWindow = DEFAULT_HTTP2_RST_FLOOD_MAX_RST_FRAME_PER_WINDOW; rstFloodWindowDuration = Duration.of(DEFAULT_HTTP2_RST_FLOOD_WINDOW_DURATION, DEFAULT_HTTP2_RST_FLOOD_WINDOW_DURATION_TIME_UNIT.toChronoUnit()); @@ -42,7 +40,6 @@ public Http2ServerConfig() { public Http2ServerConfig(Http2ServerConfig other) { this.initialSettings = other.initialSettings != null ? new Http2Settings(other.initialSettings) : null; - this.clearTextEnabled = other.clearTextEnabled; this.connectionWindowSize = other.connectionWindowSize; this.rstFloodMaxRstFramePerWindow = other.rstFloodMaxRstFramePerWindow; this.rstFloodWindowDuration = other.rstFloodWindowDuration; @@ -87,24 +84,6 @@ public Http2ServerConfig setRstFloodWindowDuration(Duration rstFloodWindowDurati return this; } - /** - * @return whether the server accepts HTTP/2 over clear text connections - */ - public boolean isClearTextEnabled() { - return clearTextEnabled; - } - - /** - * Set whether HTTP/2 over clear text is enabled or disabled, default is enabled. - * - * @param clearTextEnabled whether to accept HTTP/2 over clear text - * @return a reference to this, so the API can be used fluently - */ - public Http2ServerConfig setClearTextEnabled(boolean clearTextEnabled) { - this.clearTextEnabled = clearTextEnabled; - return this; - } - /** * @return the default HTTP/2 connection window size */ diff --git a/vertx-core/src/main/java/io/vertx/core/http/HttpClientConfig.java b/vertx-core/src/main/java/io/vertx/core/http/HttpClientConfig.java index df575397965..62bc7861e65 100644 --- a/vertx-core/src/main/java/io/vertx/core/http/HttpClientConfig.java +++ b/vertx-core/src/main/java/io/vertx/core/http/HttpClientConfig.java @@ -34,7 +34,7 @@ private static List toSupportedVersion(HttpVersion version) { case HTTP_1_0: return List.of(HttpVersion.HTTP_1_0); case HTTP_1_1: - return List.of(HttpVersion.HTTP_1_1, HttpVersion.HTTP_2); + return List.of(HttpVersion.HTTP_1_1); case HTTP_2: return List.of(HttpVersion.HTTP_2, HttpVersion.HTTP_1_1); default: diff --git a/vertx-core/src/main/java/io/vertx/core/http/HttpClientOptions.java b/vertx-core/src/main/java/io/vertx/core/http/HttpClientOptions.java index 5d21de20ca0..4c0b3a17ba9 100755 --- a/vertx-core/src/main/java/io/vertx/core/http/HttpClientOptions.java +++ b/vertx-core/src/main/java/io/vertx/core/http/HttpClientOptions.java @@ -276,11 +276,6 @@ protected ClientSSLOptions createSSLOptions() { return super.createSSLOptions().setApplicationLayerProtocols(HttpUtils.fromHttpAlpnVersions(DEFAULT_ALPN_VERSIONS)); } - @Override - public HttpClientOptions setSslOptions(ClientSSLOptions sslOptions) { - return (HttpClientOptions) super.setSslOptions(sslOptions); - } - @Override public HttpClientOptions setSendBufferSize(int sendBufferSize) { super.setSendBufferSize(sendBufferSize); @@ -812,21 +807,9 @@ public HttpClientOptions setInitialSettings(Http2Settings settings) { return this; } - @Override - public boolean isUseAlpn() { - return protocolVersion == HttpVersion.HTTP_2; - } - - /** - * Alpn supported is automatically managed by the HTTP client depending on the client supported protocols. - * - * @param useAlpn ignored - * @return this object - */ - @Deprecated(forRemoval = true) @Override public HttpClientOptions setUseAlpn(boolean useAlpn) { - return this; + return (HttpClientOptions) super.setUseAlpn(useAlpn); } @Override @@ -835,22 +818,33 @@ public HttpClientOptions setSslEngineOptions(SSLEngineOptions sslEngineOptions) } /** - * @return {@code null} + * @return the list of protocol versions to provide during the Application-Layer Protocol Negotiation. When + * the list is empty, the client provides a best effort list according to {@link #setProtocolVersion} */ public List getAlpnVersions() { - return null; + List applicationLayerProtocols = getOrCreateSSLOptions().getApplicationLayerProtocols(); + return applicationLayerProtocols != null ? HttpUtils.toHttpAlpnVersions(applicationLayerProtocols ) : null; } /** - * Does nothing, the list of supported alpn versions is managed by the HTTP client depending on the - * client supported HTTP versions. + * Set the list of protocol versions to provide to the server during the Application-Layer Protocol Negotiation. + * When the list is empty, the client makes a best effort list according to {@link #setProtocolVersion}: + * + *
    + *
  • {@link HttpVersion#HTTP_2}: [ "h2", "http/1.1" ]
  • + *
  • otherwise: [{@link #getProtocolVersion()}]
  • + *
* - * @param alpnVersions ignored + * @param alpnVersions the versions * @return a reference to this, so the API can be used fluently - * @deprecated this should not be used anymore */ - @Deprecated(forRemoval = true) public HttpClientOptions setAlpnVersions(List alpnVersions) { + ClientSSLOptions sslOptions = getOrCreateSSLOptions(); + if (alpnVersions != null) { + sslOptions.setApplicationLayerProtocols(HttpUtils.fromHttpAlpnVersions(alpnVersions)); + } else { + sslOptions.setApplicationLayerProtocols(null); + } return this; } diff --git a/vertx-core/src/main/java/io/vertx/core/http/HttpServerConfig.java b/vertx-core/src/main/java/io/vertx/core/http/HttpServerConfig.java index 1e388605304..1f071d9269e 100644 --- a/vertx-core/src/main/java/io/vertx/core/http/HttpServerConfig.java +++ b/vertx-core/src/main/java/io/vertx/core/http/HttpServerConfig.java @@ -99,7 +99,20 @@ public HttpServerConfig(HttpServerOptions options) { compression = null; } - this.versions = EnumSet.copyOf(DEFAULT_VERSIONS); + Set versions; + if (options.isSsl()) { + if (options.isUseAlpn()) { + versions = EnumSet.copyOf(options.getAlpnVersions()); + } else { + versions = EnumSet.of(HttpVersion.HTTP_1_1); + } + } else if (options.isHttp2ClearTextEnabled()) { + versions = EnumSet.copyOf(DEFAULT_VERSIONS); + } else { + versions = EnumSet.of(HttpVersion.HTTP_1_1); + } + + this.versions = versions; this.maxFormAttributeSize = options.getMaxFormAttributeSize(); this.maxFormFields = options.getMaxFormFields(); this.maxFormBufferedBytes = options.getMaxFormBufferedBytes(); @@ -150,9 +163,9 @@ public HttpServerConfig(HttpServerConfig other) { this.strictThreadMode = other.strictThreadMode; this.metricsName = other.metricsName; this.tracingPolicy = other.tracingPolicy; - this.http1Config = other.http1Config != null ? new Http1ServerConfig(other.http1Config) : new Http1ServerConfig(); - this.http2Config = other.http2Config != null ? new Http2ServerConfig(other.http2Config) : new Http2ServerConfig(); - this.http3Config = other.http3Config != null ? new Http3ServerConfig(other.http3Config) : new Http3ServerConfig(); + this.http1Config = other.http1Config != null ? new Http1ServerConfig(other.http1Config) : null; + this.http2Config = other.http2Config != null ? new Http2ServerConfig(other.http2Config) : null; + this.http3Config = other.http3Config != null ? new Http3ServerConfig(other.http3Config) : null; this.webSocketConfig = other.webSocketConfig != null ? new WebSocketServerConfig(other.webSocketConfig) : new WebSocketServerConfig(); this.compression = other.compression != null ? new HttpCompressionConfig(other.compression) : new HttpCompressionConfig(); this.tcpConfig = other.tcpConfig != null ? new TcpServerConfig(other.tcpConfig) : defaultTcpServerConfig(); diff --git a/vertx-core/src/main/java/io/vertx/core/http/HttpServerOptions.java b/vertx-core/src/main/java/io/vertx/core/http/HttpServerOptions.java index 67fed88b80b..c6fddb07927 100755 --- a/vertx-core/src/main/java/io/vertx/core/http/HttpServerOptions.java +++ b/vertx-core/src/main/java/io/vertx/core/http/HttpServerOptions.java @@ -225,6 +225,7 @@ public class HttpServerOptions extends NetServerOptions { private boolean registerWebSocketWriteHandlers; private TimeUnit http2RstFloodWindowDurationTimeUnit; private boolean strictThreadMode; + private boolean http2ClearTextEnabled; /** * Default constructor @@ -256,6 +257,7 @@ public HttpServerOptions(HttpServerOptions other) { this.registerWebSocketWriteHandlers = other.registerWebSocketWriteHandlers; this.http2RstFloodWindowDurationTimeUnit = other.http2RstFloodWindowDurationTimeUnit; this.strictThreadMode = other.strictThreadMode; + this.http2ClearTextEnabled = other.http2ClearTextEnabled; } /** @@ -295,6 +297,7 @@ private void init() { tracingPolicy = DEFAULT_TRACING_POLICY; registerWebSocketWriteHandlers = DEFAULT_REGISTER_WEBSOCKET_WRITE_HANDLERS; http2RstFloodWindowDurationTimeUnit = DEFAULT_HTTP2_RST_FLOOD_WINDOW_DURATION_TIME_UNIT; + http2ClearTextEnabled = DEFAULT_HTTP2_CLEAR_TEXT_ENABLED; } /** @@ -332,11 +335,6 @@ protected ServerSSLOptions createSSLOptions() { return super.createSSLOptions().setApplicationLayerProtocols(HttpUtils.fromHttpAlpnVersions(DEFAULT_ALPN_VERSIONS)); } - @Override - public HttpServerOptions setSslOptions(ServerSSLOptions sslOptions) { - return (HttpServerOptions) super.setSslOptions(sslOptions); - } - @Override public HttpServerOptions setSendBufferSize(int sendBufferSize) { super.setSendBufferSize(sendBufferSize); @@ -415,20 +413,9 @@ public HttpServerOptions setSsl(boolean ssl) { return this; } - @Override - public boolean isUseAlpn() { - return true; - } - - /** - * Alpn supported is automatically managed by the HTTP server depending on the client supported protocols. - * - * @param useAlpn ignored - * @return this object - */ - @Deprecated(forRemoval = true) @Override public HttpServerOptions setUseAlpn(boolean useAlpn) { + super.setUseAlpn(useAlpn); return this; } @@ -893,21 +880,26 @@ public HttpServerOptions setInitialSettings(Http2Settings settings) { } /** - * @return {@code null} + * @return the list of protocol versions to provide during the Application-Layer Protocol Negotiation */ public List getAlpnVersions() { - return null; + List applicationLayerProtocols = getOrCreateSSLOptions().getApplicationLayerProtocols(); + return applicationLayerProtocols != null ? HttpUtils.toHttpAlpnVersions(applicationLayerProtocols ) : null; } /** - * Does nothing, the list of supported alpn versions is managed by the HTTP server. + * Set the list of protocol versions to provide to the server during the Application-Layer Protocol Negotiation. * - * @param alpnVersions ignored + * @param alpnVersions the versions * @return a reference to this, so the API can be used fluently - * @deprecated this should not be used anymore */ - @Deprecated(forRemoval = true) public HttpServerOptions setAlpnVersions(List alpnVersions) { + ServerSSLOptions sslOptions = getOrCreateSSLOptions(); + if (alpnVersions != null) { + sslOptions.setApplicationLayerProtocols(HttpUtils.fromHttpAlpnVersions(alpnVersions)); + } else { + sslOptions.setApplicationLayerProtocols(null); + } return this; } @@ -915,7 +907,7 @@ public HttpServerOptions setAlpnVersions(List alpnVersions) { * @return whether the server accepts HTTP/2 over clear text connections */ public boolean isHttp2ClearTextEnabled() { - return http2Config.isClearTextEnabled(); + return http2ClearTextEnabled; } /** @@ -925,7 +917,7 @@ public boolean isHttp2ClearTextEnabled() { * @return a reference to this, so the API can be used fluently */ public HttpServerOptions setHttp2ClearTextEnabled(boolean http2ClearTextEnabled) { - http2Config.setClearTextEnabled(http2ClearTextEnabled); + this.http2ClearTextEnabled = http2ClearTextEnabled; return this; } diff --git a/vertx-core/src/main/java/io/vertx/core/http/impl/EndpointKey.java b/vertx-core/src/main/java/io/vertx/core/http/impl/EndpointKey.java index 718f23d044a..b6460451994 100644 --- a/vertx-core/src/main/java/io/vertx/core/http/impl/EndpointKey.java +++ b/vertx-core/src/main/java/io/vertx/core/http/impl/EndpointKey.java @@ -29,9 +29,6 @@ public final class EndpointKey { final ClientSSLOptions sslOptions; public EndpointKey(boolean ssl, HttpVersion protocol, ClientSSLOptions sslOptions, ProxyOptions proxyOptions, SocketAddress server, HostAndPort authority) { - if (protocol == null) { - throw new NullPointerException("No null protocol"); - } if (server == null) { throw new NullPointerException("No null server address"); } @@ -50,7 +47,7 @@ public boolean equals(Object o) { } if (o instanceof EndpointKey) { EndpointKey that = (EndpointKey) o; - return ssl == that.ssl && protocol == that.protocol && server.equals(that.server) && Objects.equals(authority, that.authority) && Objects.equals(sslOptions, that.sslOptions) && equals(proxyOptions, that.proxyOptions); + return ssl == that.ssl && Objects.equals(protocol, that.protocol) && server.equals(that.server) && Objects.equals(authority, that.authority) && Objects.equals(sslOptions, that.sslOptions) && equals(proxyOptions, that.proxyOptions); } return false; } @@ -58,7 +55,7 @@ public boolean equals(Object o) { @Override public int hashCode() { int result = ssl ? 1 : 0; - result = 31 * result + protocol.hashCode(); + result = 31 * result + (protocol == null ? 0 : protocol.hashCode()); result = 31 * result + server.hashCode(); if (authority != null) { result = 31 * result + authority.hashCode(); diff --git a/vertx-core/src/main/java/io/vertx/core/http/impl/HttpClientBase.java b/vertx-core/src/main/java/io/vertx/core/http/impl/HttpClientBase.java index a44b8e5b687..bb9e49de08d 100644 --- a/vertx-core/src/main/java/io/vertx/core/http/impl/HttpClientBase.java +++ b/vertx-core/src/main/java/io/vertx/core/http/impl/HttpClientBase.java @@ -47,11 +47,10 @@ public HttpClientBase(VertxInternal vertx, this.proxyFilter = nonProxyHosts != null ? ProxyFilter.nonProxyHosts(nonProxyHosts) : ProxyFilter.DEFAULT_PROXY_FILTER; } - static void configureSSLOptions(boolean verifyHost, boolean useAlpn, ClientSSLOptions sslOptions) { + static void configureSSLOptions(boolean verifyHost, ClientSSLOptions sslOptions) { if (sslOptions.getHostnameVerificationAlgorithm() == null) { sslOptions.setHostnameVerificationAlgorithm(verifyHost ? "HTTPS" : ""); } - sslOptions.setUseAlpn(useAlpn); } public Future closeFuture() { @@ -79,11 +78,11 @@ protected ProxyOptions computeProxyOptions(ProxyOptions proxyOptions, SocketAddr return proxyOptions; } - protected static ClientSSLOptions sslOptions(boolean verifyHost, boolean useAlpn, HttpConnectOptions connectOptions, ClientSSLOptions defaultSslOptions) { + protected static ClientSSLOptions sslOptions(boolean verifyHost, HttpConnectOptions connectOptions, ClientSSLOptions defaultSslOptions) { ClientSSLOptions sslOptions = connectOptions.getSslOptions(); if (sslOptions != null) { sslOptions = sslOptions.copy(); - configureSSLOptions(verifyHost, useAlpn, sslOptions); + configureSSLOptions(verifyHost, sslOptions); } else { sslOptions = defaultSslOptions; } diff --git a/vertx-core/src/main/java/io/vertx/core/http/impl/HttpClientBuilderInternal.java b/vertx-core/src/main/java/io/vertx/core/http/impl/HttpClientBuilderInternal.java index 5dc7b830d96..b260b345096 100644 --- a/vertx-core/src/main/java/io/vertx/core/http/impl/HttpClientBuilderInternal.java +++ b/vertx-core/src/main/java/io/vertx/core/http/impl/HttpClientBuilderInternal.java @@ -146,8 +146,7 @@ private HttpClientImpl createHttpClientImpl(HttpClientConfig config, po = poolOptions != null ? poolOptions : new PoolOptions(); HttpClientOptions options = HttpClientBuilderInternal.this.clientOptions; Handler connectHandler = connectionHandler(config); - HttpVersion defaultVersion = config.getVersions().isEmpty() ? null : config.getVersions().get(0); - boolean useAlpn = config.getVersions().contains(HttpVersion.HTTP_2); + List versions = config.getVersions(); return new HttpClientImpl( vertx, resolver, @@ -160,12 +159,11 @@ private HttpClientImpl createHttpClientImpl(HttpClientConfig config, followAlternativeServices, resolverKeepAlive, config.isVerifyHost(), - useAlpn, config.isSsl(), config.getDefaultHost(), config.getDefaultPort(), config.getMaxRedirects(), - defaultVersion, + versions, sslOptions, connectHandler, tcpTransport, diff --git a/vertx-core/src/main/java/io/vertx/core/http/impl/HttpClientImpl.java b/vertx-core/src/main/java/io/vertx/core/http/impl/HttpClientImpl.java index 53cdeb8622d..1348e2d960c 100644 --- a/vertx-core/src/main/java/io/vertx/core/http/impl/HttpClientImpl.java +++ b/vertx-core/src/main/java/io/vertx/core/http/impl/HttpClientImpl.java @@ -62,12 +62,11 @@ public class HttpClientImpl extends HttpClientBase implements HttpClientInternal private final EndpointResolverInternal originResolver; private final boolean followAlternativeServices; private final boolean verifyHost; - private final boolean useAlpn; private final boolean defaultSsl; private final String defaultHost; private final int defaultPort; private final int maxRedirects; - private final HttpVersion defaultProtocol; + private final List versions; private final Handler connectHandler; private volatile ClientSSLOptions sslOptions; @@ -82,12 +81,11 @@ public class HttpClientImpl extends HttpClientBase implements HttpClientInternal boolean followAlternativeServices, Duration resolverKeepAlive, boolean verifyHost, - boolean useAlpn, boolean defaultSsl, String defaultHost, int defaultPort, int maxRedirects, - HttpVersion defaultProtocol, + List versions, ClientSSLOptions sslOptions, Handler connectHandler, HttpClientTransport tcpTransport, @@ -95,7 +93,7 @@ public class HttpClientImpl extends HttpClientBase implements HttpClientInternal super(vertx, httpMetrics, defaultProxyOptions, nonProxyHosts); if (sslOptions != null) { - configureSSLOptions(verifyHost, useAlpn, sslOptions); + configureSSLOptions(verifyHost, sslOptions); } boolean resolveAll = loadBalancer != null; @@ -111,12 +109,11 @@ public class HttpClientImpl extends HttpClientBase implements HttpClientInternal this.redirectHandler = redirectHandler != null ? redirectHandler : DEFAULT_REDIRECT_HANDLER; this.followAlternativeServices = followAlternativeServices; this.verifyHost = verifyHost; - this.useAlpn = useAlpn; this.defaultSsl = defaultSsl; this.defaultHost = defaultHost; this.defaultPort = defaultPort; this.maxRedirects = maxRedirects; - this.defaultProtocol = defaultProtocol; + this.versions = versions; this.sslOptions = sslOptions; this.connectHandler = connectHandler; int eventLoopSize = poolOptions.getEventLoopSize(); @@ -196,12 +193,18 @@ private Function httpEndpointProvi proxyOptions = null; } HttpVersion protocol = key.protocol; - HttpConnectParams params = new HttpConnectParams(key.protocol, key.sslOptions, proxyOptions, key.ssl); + List protocols; + if (protocol == null) { + protocols = versions; + } else { + protocols = List.of(protocol); + } + HttpConnectParams params = new HttpConnectParams(protocols, key.sslOptions, proxyOptions, key.ssl); Function p = group -> { int queueMaxSize = poolOptions.getMaxWaitQueueSize(); int http1MaxSize = poolOptions.getHttp1MaxSize(); int http2MaxSize = poolOptions.getHttp2MaxSize(); - int initialPoolKind = (protocol == HttpVersion.HTTP_1_1 || protocol == HttpVersion.HTTP_1_0) ? 0 : 1; + int initialPoolKind = protocol == HttpVersion.HTTP_1_1 || protocol == HttpVersion.HTTP_1_0 ? 0 : 1; return new SharedHttpClientConnectionGroup.Pool(group, transport, queueMaxSize, http1MaxSize, http2MaxSize, maxLifetime, initialPoolKind, params, contextProvider); }; return new SharedHttpClientConnectionGroup( @@ -240,7 +243,7 @@ public HttpClientTransport quicTransport() { } protected void setDefaultSslOptions(ClientSSLOptions options) { - configureSSLOptions(verifyHost, useAlpn, options); + configureSSLOptions(verifyHost, options); this.sslOptions = options; } @@ -326,17 +329,20 @@ private Future connect(HttpClientTransp throw new IllegalArgumentException("Only socket address are currently supported"); } HttpVersion protocol = connect.getProtocolVersion(); + List protocols; if (protocol == null) { - protocol = defaultProtocol; + protocols = versions; + } else { + protocols = List.of(protocol); } HostAndPort authority = HostAndPort.create(host, port); - ClientSSLOptions sslOptions = sslOptions(verifyHost, useAlpn, connect, this.sslOptions); + ClientSSLOptions sslOptions = sslOptions(verifyHost, connect, this.sslOptions); ProxyOptions proxyOptions = computeProxyOptions(connect.getProxyOptions(), server); ClientMetrics clientMetrics = httpMetrics != null ? httpMetrics.createEndpointMetrics(server, 1) : null; Boolean ssl = connect.isSsl(); boolean useSSL = ssl != null ? ssl : defaultSsl; checkClosed(); - HttpConnectParams params = new HttpConnectParams(protocol, sslOptions, proxyOptions, useSSL); + HttpConnectParams params = new HttpConnectParams(protocols, sslOptions, proxyOptions, useSSL); return transport.connect(vertx.getOrCreateContext(), server, authority, params, clientMetrics) .map(conn -> new UnpooledHttpClientConnection(conn).init()); } @@ -348,10 +354,8 @@ public Future request(RequestOptions request) { if (version == null) { if (tcpTransport != null) { transport = tcpTransport; - version = defaultProtocol; } else { transport = quicTransport; - version = HttpVersion.HTTP_3; } } else { switch (version) { @@ -435,10 +439,10 @@ private Future doRequest(HttpClientTransport transport, Addre authority = null; } HttpVersion protocolVersion = request.getProtocolVersion(); - if (protocolVersion == null) { - protocolVersion = defaultProtocol; + if (protocolVersion == null && versions.size() == 1) { + protocolVersion = versions.get(0); } - ClientSSLOptions sslOptions = sslOptions(verifyHost, useAlpn, request, this.sslOptions); + ClientSSLOptions sslOptions = sslOptions(verifyHost, request, this.sslOptions); if (server instanceof SocketAddress) { SocketAddress serverSocketAddress = (SocketAddress) server; ProxyOptions proxyOptions = computeProxyOptions(request.getProxyOptions(), serverSocketAddress); @@ -450,25 +454,30 @@ private Future doRequest(HttpClientTransport transport, Addre } boolean useSSL; HttpProtocol protocol; - switch (protocolVersion) { - case HTTP_1_0: - protocol = HttpProtocol.HTTP_1_0; - useSSL = ssl != null ? ssl : defaultSsl; - break; - case HTTP_1_1: - protocol = HttpProtocol.HTTP_1_1; - useSSL = ssl != null ? ssl : defaultSsl; - break; - case HTTP_2: - useSSL = ssl != null ? ssl : defaultSsl; - protocol = useSSL ? HttpProtocol.H2 : HttpProtocol.H2C; - break; - case HTTP_3: - useSSL = true; - protocol = HttpProtocol.H3; - break; - default: - throw new AssertionError(); + if (protocolVersion == null) { + protocol = null; + useSSL = ssl != null ? ssl : defaultSsl; + } else { + switch (protocolVersion) { + case HTTP_1_0: + useSSL = ssl != null ? ssl : defaultSsl; + protocol = HttpProtocol.HTTP_1_0; + break; + case HTTP_1_1: + useSSL = ssl != null ? ssl : defaultSsl; + protocol = HttpProtocol.HTTP_1_1; + break; + case HTTP_2: + useSSL = ssl != null ? ssl : defaultSsl; + protocol = useSSL ? HttpProtocol.H2 : HttpProtocol.H2C; + break; + case HTTP_3: + useSSL = true; + protocol = HttpProtocol.H3; + break; + default: + throw new AssertionError(); + } } return doRequest(transport, protocol, method, authority, server, useSSL, requestURI, headers, request.getTraceOperation(), request.getRoutingKey(), connectTimeout, idleTimeout, followRedirects, sslOptions); } @@ -606,7 +615,7 @@ private Future doRequest( throw new IllegalStateException("No results for " + server); } SocketAddress address = lookup2.address(); - EndpointKey key = new EndpointKey(useSSL, protocol.version(), sslOptions, null, address, authority != null ? authority : HostAndPort.create(address.host(), address.port())); + EndpointKey key = new EndpointKey(useSSL, protocol != null ? protocol.version() : null, sslOptions, null, address, authority != null ? authority : HostAndPort.create(address.host(), address.port())); return resourceManager.withResourceAsync(key, httpEndpointProvider(followAlternativeServices && useSSL, transport), (e, created) -> { Future> fut2 = e.requestConnection(streamCtx, connectTimeout); ServerInteraction endpointRequest = lookup2.newInteraction(); diff --git a/vertx-core/src/main/java/io/vertx/core/http/impl/HttpConnectParams.java b/vertx-core/src/main/java/io/vertx/core/http/impl/HttpConnectParams.java index 28a4281c5d0..807563b3d65 100644 --- a/vertx-core/src/main/java/io/vertx/core/http/impl/HttpConnectParams.java +++ b/vertx-core/src/main/java/io/vertx/core/http/impl/HttpConnectParams.java @@ -4,16 +4,21 @@ import io.vertx.core.net.ClientSSLOptions; import io.vertx.core.net.ProxyOptions; +import java.util.List; + public class HttpConnectParams { - public HttpConnectParams(HttpVersion protocol, ClientSSLOptions sslOptions, ProxyOptions proxyOptions, boolean ssl) { - this.protocol = protocol; + public HttpConnectParams(List protocols, + ClientSSLOptions sslOptions, + ProxyOptions proxyOptions, + boolean ssl) { + this.protocols = protocols; this.sslOptions = sslOptions; this.proxyOptions = proxyOptions; this.ssl = ssl; } - public final HttpVersion protocol; + public final List protocols; public final ClientSSLOptions sslOptions; public final ProxyOptions proxyOptions; public final boolean ssl; diff --git a/vertx-core/src/main/java/io/vertx/core/http/impl/WebSocketClientImpl.java b/vertx-core/src/main/java/io/vertx/core/http/impl/WebSocketClientImpl.java index 442f3a8738c..0ed8e148fa8 100644 --- a/vertx-core/src/main/java/io/vertx/core/http/impl/WebSocketClientImpl.java +++ b/vertx-core/src/main/java/io/vertx/core/http/impl/WebSocketClientImpl.java @@ -51,7 +51,7 @@ public WebSocketClientImpl(VertxInternal vertx, ClientSSLOptions sslOptions = options.getSslOptions(); if (sslOptions != null) { - configureSSLOptions(options.isVerifyHost(), false, sslOptions); + configureSSLOptions(options.isVerifyHost(), sslOptions); } this.options = wsOptions; @@ -83,7 +83,7 @@ public Future connect(WebSocketConnectOptions options) { @Override protected void setDefaultSslOptions(ClientSSLOptions options) { - configureSSLOptions(this.options.isVerifyHost(), false, options); + configureSSLOptions(this.options.isVerifyHost(), options); this.defaultSslOptions = options; } @@ -93,14 +93,14 @@ public void webSocket(ContextInternal ctx, WebSocketConnectOptions connectOption SocketAddress addr = SocketAddress.inetSocketAddress(port, host); HostAndPort peer = HostAndPort.create(host, port); ProxyOptions proxyOptions = computeProxyOptions(connectOptions.getProxyOptions(), addr); - ClientSSLOptions sslOptions = sslOptions(options.isVerifyHost(), false, connectOptions, defaultSslOptions); + ClientSSLOptions sslOptions = sslOptions(options.isVerifyHost(), connectOptions, defaultSslOptions); EndpointKey key = new EndpointKey(connectOptions.isSsl() != null ? connectOptions.isSsl() : options.isSsl(), HttpVersion.HTTP_1_1, sslOptions, proxyOptions, addr, peer); // todo: cache Function provider = (key_) -> { int maxPoolSize = options.getMaxConnections(); ClientMetrics clientMetrics = WebSocketClientImpl.this.httpMetrics != null ? WebSocketClientImpl.this.httpMetrics.createEndpointMetrics(key_.server, maxPoolSize) : null; PoolMetrics queueMetrics = WebSocketClientImpl.this.httpMetrics != null ? vertx.metrics().createPoolMetrics("ws", key_.server.toString(), maxPoolSize) : null; - HttpConnectParams params = new HttpConnectParams(HttpVersion.HTTP_1_1, sslOptions, key_.proxyOptions, key_.ssl); + HttpConnectParams params = new HttpConnectParams(List.of(HttpVersion.HTTP_1_1), sslOptions, key_.proxyOptions, key_.ssl); return new WebSocketGroup(key_.server, httpMetrics, clientMetrics, queueMetrics, options, maxPoolSize, connector, params, key_.authority, 0L); }; webSocketCM diff --git a/vertx-core/src/main/java/io/vertx/core/http/impl/tcp/HttpServerConnectionInitializer.java b/vertx-core/src/main/java/io/vertx/core/http/impl/tcp/HttpServerConnectionInitializer.java index 1b207025d1b..adadd2ecc34 100644 --- a/vertx-core/src/main/java/io/vertx/core/http/impl/tcp/HttpServerConnectionInitializer.java +++ b/vertx-core/src/main/java/io/vertx/core/http/impl/tcp/HttpServerConnectionInitializer.java @@ -56,7 +56,6 @@ public class HttpServerConnectionInitializer { private final Supplier streamContextSupplier; private final TcpHttpServer server; private final String serverOrigin; - private final boolean disableH2C; private final Handler connectionHandler; private final Handler exceptionHandler; private final HttpServerMetrics httpMetrics; @@ -112,36 +111,40 @@ public HttpServerConnectionInitializer(ContextInternal context, compressionManager = null; } - Http2ServerChannelInitializer http2ChannelInitalizer; - if (http2Config.getMultiplexImplementation()) { - http2ChannelInitalizer = new Http2MultiplexServerChannelInitializer( - context, - compressionManager, - useDecompression, - server.getMetrics(), - transportMetrics, - metric, - streamContextSupplier, - connectionHandler, - HttpUtils.fromVertxInitialSettings(true, http2Config.getInitialSettings()), - http2Config.getRstFloodMaxRstFramePerWindow(), - (int)http2Config.getRstFloodWindowDuration().toSeconds(), - logEnabled); + Http2ServerChannelInitializer http2ChannelInitializer; + if (http2Config != null) { + if (http2Config.getMultiplexImplementation()) { + http2ChannelInitializer = new Http2MultiplexServerChannelInitializer( + context, + compressionManager, + useDecompression, + server.getMetrics(), + transportMetrics, + metric, + streamContextSupplier, + connectionHandler, + HttpUtils.fromVertxInitialSettings(true, http2Config.getInitialSettings()), + http2Config.getRstFloodMaxRstFramePerWindow(), + (int)http2Config.getRstFloodWindowDuration().toSeconds(), + logEnabled); + } else { + http2ChannelInitializer = new Http2CodecServerChannelInitializer( + this, + tracingPolicy, + httpMetrics, + transportMetrics, + useDecompression, + useCompression, + http2Config, + compressionManager, + streamContextSupplier, + connectionHandler, + metric, + logEnabled + ); + } } else { - http2ChannelInitalizer = new Http2CodecServerChannelInitializer( - this, - tracingPolicy, - httpMetrics, - transportMetrics, - useDecompression, - useCompression, - http2Config, - compressionManager, - streamContextSupplier, - connectionHandler, - metric, - logEnabled - ); + http2ChannelInitializer = null; } this.context = context; @@ -157,7 +160,6 @@ public HttpServerConnectionInitializer(ContextInternal context, this.maxFormBufferedBytes = maxFormBufferedBytes; this.http1Config = http1Config; this.http2Config = http2Config; - this.disableH2C = !http2Config.isClearTextEnabled(); this.connectionHandler = connectionHandler; this.exceptionHandler = exceptionHandler; this.metric = metric; @@ -170,7 +172,7 @@ public HttpServerConnectionInitializer(ContextInternal context, this.compressionContentSizeThreshold = compressionContentSizeThreshold; this.httpMetrics = httpMetrics; this.transportMetrics = transportMetrics; - this.http2ChannelInitializer = http2ChannelInitalizer; + this.http2ChannelInitializer = http2ChannelInitializer; } public void configurePipeline(Channel ch, SslChannelProvider sslChannelProvider, SslContextManager sslContextManager, TransportMetrics transportMetrics) { @@ -186,22 +188,28 @@ public void configurePipeline(Channel ch, SslChannelProvider sslChannelProvider, break; case "http/1.1": case "http/1.0": - configureHttp1Pipeline(ch.pipeline(), sslChannelProvider, sslContextManager); + configureHttp1Pipeline(ch.pipeline()); configureHttp1Handler(ch.pipeline(), sslContextManager); break; + default: + throw new AssertionError(); } } else { // No alpn presented or OpenSSL - configureHttp1Pipeline(ch.pipeline(), sslChannelProvider, sslContextManager); - configureHttp1Handler(ch.pipeline(), sslContextManager); + if (http1Config != null) { + configureHttp1Pipeline(ch.pipeline()); + configureHttp1Handler(ch.pipeline(), sslContextManager); + } else { + ch.close(); + } } } else { - configureHttp1Pipeline(ch.pipeline(), sslChannelProvider, sslContextManager); + configureHttp1Pipeline(ch.pipeline()); configureHttp1Handler(ch.pipeline(), sslContextManager); } } else { - if (disableH2C) { - configureHttp1Pipeline(ch.pipeline(), sslChannelProvider, sslContextManager); + if (http2Config == null) { + configureHttp1Pipeline(ch.pipeline()); configureHttp1Handler(ch.pipeline(), sslContextManager); } else { Http1xOrH2CHandler handler = new Http1xOrH2CHandler() { @@ -210,7 +218,7 @@ protected void configure(ChannelHandlerContext ctx, boolean h2c) { if (h2c) { configureHttp2(ctx.pipeline(), false); } else { - configureHttp1Pipeline(ctx.pipeline(), sslChannelProvider, sslContextManager); + configureHttp1Pipeline(ctx.pipeline()); http2ChannelInitializer.configureHttp1OrH2CUpgradeHandler(context, ctx.pipeline(), sslChannelProvider, sslContextManager); } } @@ -270,7 +278,7 @@ private static String computeChannelName(ChannelPipeline pipeline) { } } - private void configureHttp1Pipeline(ChannelPipeline pipeline, SslChannelProvider sslChannelProvider, SslContextManager sslContextManager) { + private void configureHttp1Pipeline(ChannelPipeline pipeline) { String name = computeChannelName(pipeline); pipeline.addBefore(name, "httpDecoder", new VertxHttpRequestDecoder(http1Config)); pipeline.addBefore(name, "httpEncoder", new VertxHttpResponseEncoder()); diff --git a/vertx-core/src/main/java/io/vertx/core/http/impl/tcp/TcpHttpClientTransport.java b/vertx-core/src/main/java/io/vertx/core/http/impl/tcp/TcpHttpClientTransport.java index 4f039be66a1..105d63aff67 100644 --- a/vertx-core/src/main/java/io/vertx/core/http/impl/tcp/TcpHttpClientTransport.java +++ b/vertx-core/src/main/java/io/vertx/core/http/impl/tcp/TcpHttpClientTransport.java @@ -51,6 +51,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import static io.vertx.core.http.HttpMethod.OPTIONS; @@ -157,12 +158,15 @@ private void connect(ContextInternal context, HttpConnectParams params, HostAndP // We might end up using javax.net.ssl.trustStore copy = new ClientSSLOptions().setHostnameVerificationAlgorithm("HTTPS"); } - if (params.protocol == HttpVersion.HTTP_2) { + boolean useAlpn = params.protocols.contains(HttpVersion.HTTP_2); + copy.setUseAlpn(useAlpn); + if (useAlpn) { + List list = params.protocols + .stream() + .map(HttpVersion::alpnName) + .collect(Collectors.toList()); copy - .setUseAlpn(true) - .setApplicationLayerProtocols(List.of(HttpVersion.HTTP_2.alpnName(), HttpVersion.HTTP_1_1.alpnName())); - } else { - copy.setApplicationLayerProtocols(List.of(HttpVersion.HTTP_1_1.alpnName())); + .setApplicationLayerProtocols(list); } connectOptions.setSslOptions(copy); } @@ -191,26 +195,48 @@ public Future wrap(ContextInternal context, HttpConnectPar Channel ch = so.channelHandlerContext().channel(); if (params.ssl) { String protocol = so.applicationLayerProtocol(); - if ("h2".equals(protocol)) { - applyHttp2ConnectionOptions(ch.pipeline()); - http2Connected(context, authority, transportMetrics, metric, ch, clientMetrics, promise); - } else { - applyHttp1xConnectionOptions(ch.pipeline()); - HttpVersion fallbackProtocol = "http/1.0".equals(protocol) ? HttpVersion.HTTP_1_0 : HttpVersion.HTTP_1_1; - http1xConnected(fallbackProtocol, server, authority, true, context, transportMetrics, metric, ch, clientMetrics, promise); + if (protocol == null) { + protocol = ""; + } + switch (protocol) { + case "h2": + if (http2Config != null) { + applyHttp2ConnectionOptions(ch.pipeline()); + http2Connected(context, authority, transportMetrics, metric, ch, clientMetrics, promise); + } else { + so.close(); + promise.tryFail(new IllegalStateException("HTTP/2 not supported")); + } + break; + case "http/1.1": + case "http/1.0": + case "": + if (http1Config != null) { + applyHttp1xConnectionOptions(ch.pipeline()); + HttpVersion version = "http/1.0".equals(protocol) ? HttpVersion.HTTP_1_0 : HttpVersion.HTTP_1_1; + http1xConnected(version, server, authority, true, context, transportMetrics, metric, ch, clientMetrics, promise); + } else { + so.close(); + promise.tryFail(new IllegalStateException("HTTP/1.1 not supported")); + } + break; + default: + so.close(); + promise.tryFail(new IllegalStateException("Unsupported protocol <" + protocol + ">")); + break; } } else { - if (params.protocol == HttpVersion.HTTP_2) { + if (params.protocols.get(0) == HttpVersion.HTTP_2) { if (http2Config.isClearTextUpgrade()) { applyHttp1xConnectionOptions(pipeline); - http1xConnected(params.protocol, server, authority, false, context, transportMetrics, metric, ch, clientMetrics, promise); + http1xConnected(HttpVersion.HTTP_2, server, authority, false, context, transportMetrics, metric, ch, clientMetrics, promise); } else { applyHttp2ConnectionOptions(pipeline); http2Connected(context, authority, transportMetrics, metric, ch, clientMetrics, promise); } } else { applyHttp1xConnectionOptions(pipeline); - http1xConnected(params.protocol, server, authority, false, context, transportMetrics, metric, ch, clientMetrics, promise); + http1xConnected(params.protocols.get(0), server, authority, false, context, transportMetrics, metric, ch, clientMetrics, promise); } } return promise.future(); @@ -226,13 +252,24 @@ private void http2Connected(ContextInternal context, HostAndPort authority, Tran public Future connect(ContextInternal context, SocketAddress server, HostAndPort authority, HttpConnectParams params, ClientMetrics clientMetrics) { - if (params.sslOptions != null && !params.sslOptions.isUseAlpn() && params.ssl && params.protocol == HttpVersion.HTTP_2) { - return context.failedFuture("Must enable ALPN when using H2"); - } - - if (!params.ssl && params.protocol == HttpVersion.HTTP_2) { - if (http2Config.isClearTextUpgrade() && http1Config == null) { - return context.failedFuture("Must enable HTTP/1.1 when using H2C with upgrade"); + if (params.protocols.size() == 1) { + switch (params.protocols.get(0)) { + case HTTP_1_0: + case HTTP_1_1: + if (http1Config == null) { + return context.failedFuture(new IllegalStateException("The client does not support HTTP/1.x")); + } + break; + case HTTP_2: + if (http2Config == null) { + return context.failedFuture(new IllegalStateException("The client does not support HTTP/2")); + } + if (!params.ssl && http2Config.isClearTextUpgrade() && http1Config == null) { + return context.failedFuture(new IllegalStateException("Must enable HTTP/1.1 when using H2C with upgrade")); + } + break; + case HTTP_3: + return context.failedFuture(new IllegalStateException("Cannot handle HTTP/3")); } } diff --git a/vertx-core/src/main/java/io/vertx/core/http/impl/tcp/TcpHttpServer.java b/vertx-core/src/main/java/io/vertx/core/http/impl/tcp/TcpHttpServer.java index 423d9b1f3cf..36b9248aa75 100644 --- a/vertx-core/src/main/java/io/vertx/core/http/impl/tcp/TcpHttpServer.java +++ b/vertx-core/src/main/java/io/vertx/core/http/impl/tcp/TcpHttpServer.java @@ -213,6 +213,8 @@ public synchronized Future listen(ContextInternal context, SocketAdd Handler h = exceptionHandler; Handler exceptionHandler = h != null ? h : DEFAULT_EXCEPTION_HANDLER; server.exceptionHandler(exceptionHandler); + Http1ServerConfig http1Config = config.getVersions().contains(HttpVersion.HTTP_1_0) || config.getVersions().contains(HttpVersion.HTTP_1_1) ? config.getHttp1Config() != null ? config.getHttp1Config() : new Http1ServerConfig() : null; + Http2ServerConfig http2Config = config.getVersions().contains(HttpVersion.HTTP_2) ? config.getHttp2Config() != null ? config.getHttp2Config() : new Http2ServerConfig() : null; server.connectHandler(so -> { NetSocketImpl soi = (NetSocketImpl) so; Supplier streamContextSupplier = context::duplicate; @@ -228,7 +230,7 @@ public synchronized Future listen(ContextInternal context, SocketAdd webSocketHandhakeHandler, connectionHandler, exceptionHandler, - config.getHttp2Config().getConnectionWindowSize()); + http2Config != null ? http2Config.getConnectionWindowSize() : HttpServerOptions.DEFAULT_HTTP2_CONNECTION_WINDOW_SIZE); List compressors = compression != null ? compression.getCompressors() : null; HttpServerConnectionInitializer initializer = new HttpServerConnectionInitializer( @@ -247,8 +249,8 @@ public synchronized Future listen(ContextInternal context, SocketAdd config.getMaxFormAttributeSize(), config.getMaxFormFields(), config.getMaxFormBufferedBytes(), - config.getHttp1Config(), - config.getHttp2Config(), + http1Config, + http2Config, registerWebSocketWriteHandlers, config.getWebSocketConfig(), config.isSsl() ? sslOptions : null, diff --git a/vertx-core/src/main/java/io/vertx/core/net/ClientOptionsBase.java b/vertx-core/src/main/java/io/vertx/core/net/ClientOptionsBase.java index dd3b23b202a..76cae3829ca 100755 --- a/vertx-core/src/main/java/io/vertx/core/net/ClientOptionsBase.java +++ b/vertx-core/src/main/java/io/vertx/core/net/ClientOptionsBase.java @@ -104,17 +104,6 @@ public ClientSSLOptions getSslOptions() { return (ClientSSLOptions) super.getSslOptions(); } - /** - * Set the SSL options to use. - * - * @param sslOptions the ssl options to use - * @return a reference to this, so the API can be used fluently - */ - @GenIgnore - public ClientOptionsBase setSslOptions(ClientSSLOptions sslOptions) { - return (ClientOptionsBase) super.setSslOptions(sslOptions); - } - @Override protected ClientSSLOptions getOrCreateSSLOptions() { return (ClientSSLOptions) super.getOrCreateSSLOptions(); diff --git a/vertx-core/src/main/java/io/vertx/core/net/NetClientOptions.java b/vertx-core/src/main/java/io/vertx/core/net/NetClientOptions.java index 563af9edd6a..1ee8c7b78d5 100755 --- a/vertx-core/src/main/java/io/vertx/core/net/NetClientOptions.java +++ b/vertx-core/src/main/java/io/vertx/core/net/NetClientOptions.java @@ -272,11 +272,6 @@ public NetClientOptions setMetricsName(String metricsName) { return (NetClientOptions) super.setMetricsName(metricsName); } - @Override - public NetClientOptions setSslOptions(ClientSSLOptions sslOptions) { - return (NetClientOptions) super.setSslOptions(sslOptions); - } - /** * Set the value of reconnect attempts * diff --git a/vertx-core/src/main/java/io/vertx/core/net/NetServerOptions.java b/vertx-core/src/main/java/io/vertx/core/net/NetServerOptions.java index 184c139d9e9..6f2f7495532 100755 --- a/vertx-core/src/main/java/io/vertx/core/net/NetServerOptions.java +++ b/vertx-core/src/main/java/io/vertx/core/net/NetServerOptions.java @@ -141,17 +141,6 @@ public ServerSSLOptions getSslOptions() { return (ServerSSLOptions) super.getSslOptions(); } - /** - * Set the SSL options to use. - * - * @param sslOptions the ssl options to use - * @return a reference to this, so the API can be used fluently - */ - @GenIgnore - public NetServerOptions setSslOptions(ServerSSLOptions sslOptions) { - return (NetServerOptions) super.setSslOptions(sslOptions); - } - @Override protected ServerSSLOptions getOrCreateSSLOptions() { return (ServerSSLOptions) super.getOrCreateSSLOptions(); diff --git a/vertx-core/src/main/java/io/vertx/core/net/SSLOptions.java b/vertx-core/src/main/java/io/vertx/core/net/SSLOptions.java index ec9feb6338e..e2123d72971 100644 --- a/vertx-core/src/main/java/io/vertx/core/net/SSLOptions.java +++ b/vertx-core/src/main/java/io/vertx/core/net/SSLOptions.java @@ -258,7 +258,7 @@ public SSLOptions setUseAlpn(boolean useAlpn) { * @return the enabled protocols */ public Set getEnabledSecureTransportProtocols() { - return enabledSecureTransportProtocols; + return new LinkedHashSet<>(enabledSecureTransportProtocols); } /** diff --git a/vertx-core/src/main/java/io/vertx/core/net/TCPSSLOptions.java b/vertx-core/src/main/java/io/vertx/core/net/TCPSSLOptions.java index c482716f7d9..92e3457bfe0 100755 --- a/vertx-core/src/main/java/io/vertx/core/net/TCPSSLOptions.java +++ b/vertx-core/src/main/java/io/vertx/core/net/TCPSSLOptions.java @@ -105,6 +105,10 @@ public abstract class TCPSSLOptions extends NetworkOptions { private SSLEngineOptions sslEngineOptions; private SSLOptions sslOptions; + private Set enabledCipherSuites; + private List crlPaths; + private List crlValues; + /** * Default constructor */ @@ -120,9 +124,6 @@ public TCPSSLOptions() { */ public TCPSSLOptions(TCPSSLOptions other) { super(other); - - SSLOptions sslOptions = other.sslOptions; - this.idleTimeout = other.getIdleTimeout(); this.idleTimeoutUnit = other.getIdleTimeoutUnit() != null ? other.getIdleTimeoutUnit() : DEFAULT_IDLE_TIMEOUT_TIME_UNIT; this.readIdleTimeout = other.getReadIdleTimeout(); @@ -130,7 +131,16 @@ public TCPSSLOptions(TCPSSLOptions other) { this.ssl = other.isSsl(); this.sslEngineOptions = other.sslEngineOptions != null ? other.sslEngineOptions.copy() : null; this.transportOptions = other.transportOptions != null ? other.transportOptions.copy() : new TcpConfig(); - this.sslOptions = sslOptions != null ? sslOptions.copy() : null; + + SSLOptions sslOptions = other.sslOptions; + if (sslOptions != null) { + this.sslOptions = sslOptions.copy(); + if (this.sslOptions != null) { + enabledCipherSuites = this.sslOptions.enabledCipherSuites; + crlPaths = this.sslOptions.crlPaths; + crlValues = this.sslOptions.crlValues; + } + } } /** @@ -217,6 +227,22 @@ private void init() { protected SSLOptions getOrCreateSSLOptions() { if (sslOptions == null) { sslOptions = createSSLOptions(); + // Necessary hacks because we return lazy created collections so we need to care about that + if (enabledCipherSuites != null) { + sslOptions.enabledCipherSuites = enabledCipherSuites; + } else { + enabledCipherSuites = sslOptions.enabledCipherSuites; + } + if (crlPaths != null) { + sslOptions.crlPaths = crlPaths; + } else { + crlPaths = sslOptions.crlPaths; + } + if (crlValues != null) { + sslOptions.crlValues = crlValues; + } else { + crlValues = sslOptions.crlValues; + } } return sslOptions; } @@ -235,11 +261,6 @@ public SSLOptions getSslOptions() { return sslOptions; } - TCPSSLOptions setSslOptions(SSLOptions sslOptions) { - this.sslOptions = sslOptions; - return this; - } - @Override public int getSendBufferSize() { return transportOptions.getSendBufferSize(); @@ -535,7 +556,10 @@ public TCPSSLOptions removeEnabledCipherSuite(String suite) { * @return the enabled cipher suites */ public Set getEnabledCipherSuites() { - return getOrCreateSSLOptions().getEnabledCipherSuites(); + if (enabledCipherSuites == null) { + enabledCipherSuites = new LinkedHashSet<>(); + } + return enabledCipherSuites; } /** @@ -543,13 +567,17 @@ public Set getEnabledCipherSuites() { * @return the CRL (Certificate revocation list) paths */ public List getCrlPaths() { - return getOrCreateSSLOptions().getCrlPaths(); + if (crlPaths == null) { + crlPaths = new ArrayList<>(); + } + return crlPaths; } /** * Add a CRL path * @param crlPath the path * @return a reference to this, so the API can be used fluently + * @throws NullPointerException */ public TCPSSLOptions addCrlPath(String crlPath) throws NullPointerException { getOrCreateSSLOptions().addCrlPath(crlPath); @@ -562,7 +590,10 @@ public TCPSSLOptions addCrlPath(String crlPath) throws NullPointerException { * @return the list of values */ public List getCrlValues() { - return getOrCreateSSLOptions().getCrlValues(); + if (crlValues == null) { + crlValues = new ArrayList<>(); + } + return crlValues; } /** diff --git a/vertx-core/src/test/java/io/vertx/it/tls/ConnectToTLSTrustedServerTest.java b/vertx-core/src/test/java/io/vertx/it/tls/ConnectToTLSTrustedServerTest.java index 3f53fef7f31..37f605501c5 100644 --- a/vertx-core/src/test/java/io/vertx/it/tls/ConnectToTLSTrustedServerTest.java +++ b/vertx-core/src/test/java/io/vertx/it/tls/ConnectToTLSTrustedServerTest.java @@ -23,7 +23,7 @@ public void testHTTP1x() throws Exception { @Test public void testHTTP2() throws Exception { String val = testHTTP( - new HttpServerOptions().setSsl(true).setKeyCertOptions(Cert.SERVER_JKS_ROOT_CA.get()), + new HttpServerOptions().setSsl(true).setUseAlpn(true).setKeyCertOptions(Cert.SERVER_JKS_ROOT_CA.get()), new HttpClientOptions().setProtocolVersion(HttpVersion.HTTP_2) ); assertEquals("true/HTTP_2", val); @@ -35,7 +35,7 @@ public String testHTTP(HttpServerOptions serverOptions, HttpClientOptions client HttpServer server = vertx.createHttpServer(serverOptions) .requestHandler(req -> req.response().end(req.isSSL() + "/" + req.version())); server.listen(8443, "localhost").await(); - HttpClient client = vertx.createHttpClient(clientOptions); + HttpClient client = vertx.createHttpClient(new HttpClientOptions().setUseAlpn(true).setProtocolVersion(HttpVersion.HTTP_2)); Future fut = client.request(new RequestOptions().setAbsoluteURI("https://localhost:8443")); Future buff = fut.compose(req -> req.send().compose(HttpClientResponse::body)); return buff.await().toString(); diff --git a/vertx-core/src/test/java/io/vertx/tests/http/Http1xTest.java b/vertx-core/src/test/java/io/vertx/tests/http/Http1xTest.java index 9043e3562a1..5f5270dcbd0 100644 --- a/vertx-core/src/test/java/io/vertx/tests/http/Http1xTest.java +++ b/vertx-core/src/test/java/io/vertx/tests/http/Http1xTest.java @@ -243,16 +243,16 @@ public void testClientOptions() { assertEquals(false, options.isUseAlpn()); assertEquals(options, options.setUseAlpn(true)); - assertEquals(options.getProtocolVersion() == HttpVersion.HTTP_2, options.isUseAlpn()); + assertEquals(true, options.isUseAlpn()); assertNull(options.getSslEngineOptions()); assertEquals(options, options.setSslEngineOptions(new JdkSSLEngineOptions())); assertTrue(options.getSslEngineOptions() instanceof JdkSSLEngineOptions); List alpnVersions = Collections.singletonList(HttpVersion.HTTP_1_1); - assertEquals(null, options.getAlpnVersions()); + assertEquals(HttpClientOptions.DEFAULT_ALPN_VERSIONS, options.getAlpnVersions()); assertEquals(options, options.setAlpnVersions(alpnVersions)); - assertEquals(null, options.getAlpnVersions()); + assertEquals(alpnVersions, options.getAlpnVersions()); assertEquals(true, options.isHttp2ClearTextUpgrade()); assertEquals(options, options.setHttp2ClearTextUpgrade(false)); @@ -389,7 +389,7 @@ public void testServerOptions() { assertEquals(options, options.setHandle100ContinueAutomatically(true)); assertTrue(options.isHandle100ContinueAutomatically()); - assertEquals(true, options.isUseAlpn()); + assertEquals(false, options.isUseAlpn()); assertEquals(options, options.setUseAlpn(true)); assertEquals(true, options.isUseAlpn()); @@ -403,9 +403,9 @@ public void testServerOptions() { assertEquals(initialSettings, options.getInitialSettings()); List alpnVersions = Collections.singletonList(HttpVersion.HTTP_1_1); - assertEquals(null, options.getAlpnVersions()); + assertEquals(HttpServerOptions.DEFAULT_ALPN_VERSIONS, options.getAlpnVersions()); assertEquals(options, options.setAlpnVersions(alpnVersions)); - assertEquals(null, options.getAlpnVersions()); + assertEquals(alpnVersions, options.getAlpnVersions()); assertEquals(HttpClientOptions.DEFAULT_HTTP2_CONNECTION_WINDOW_SIZE, options.getHttp2ConnectionWindowSize()); rand = TestUtils.randomPositiveInt(); @@ -615,7 +615,9 @@ public void testClientOptionsJson() { int maxHeaderSize = TestUtils.randomPositiveInt(); int maxWaitQueueSize = TestUtils.randomPositiveInt(); Http2Settings initialSettings = randomHttp2Settings(); + boolean useAlpn = TestUtils.randomBoolean(); String sslEngine = TestUtils.randomBoolean() ? "jdkSslEngineOptions" : "openSslEngineOptions"; + List alpnVersions = Collections.singletonList(HttpVersion.values()[TestUtils.randomPositiveInt() % 3]); boolean h2cUpgrade = rand.nextBoolean(); boolean openSslSessionCacheEnabled = rand.nextBoolean(); String localAddress = TestUtils.randomAlphaString(10); @@ -662,7 +664,9 @@ public void testClientOptionsJson() { .put("maxConcurrentStreams", initialSettings.getMaxConcurrentStreams()) .put("initialWindowSize", initialSettings.getInitialWindowSize()) .put("maxFrameSize", initialSettings.getMaxFrameSize())) + .put("useAlpn", useAlpn) .put(sslEngine, new JsonObject()) + .put("alpnVersions", new JsonArray().add(alpnVersions.get(0).name())) .put("http2ClearTextUpgrade", h2cUpgrade) .put("openSslSessionCacheEnabled", openSslSessionCacheEnabled) .put("localAddress", localAddress) @@ -706,7 +710,7 @@ public void testClientOptionsJson() { assertEquals(maxInitialLineLength, options.getMaxInitialLineLength()); assertEquals(maxHeaderSize, options.getMaxHeaderSize()); assertEquals(initialSettings, options.getInitialSettings()); - assertEquals(options.getProtocolVersion() == HttpVersion.HTTP_2, options.isUseAlpn()); + assertEquals(useAlpn, options.isUseAlpn()); switch (sslEngine) { case "jdkSslEngineOptions": assertTrue(options.getSslEngineOptions() instanceof JdkSSLEngineOptions); @@ -718,7 +722,7 @@ public void testClientOptionsJson() { fail(); break; } - assertEquals(null, options.getAlpnVersions()); + assertEquals(alpnVersions, options.getAlpnVersions()); assertEquals(h2cUpgrade, options.isHttp2ClearTextUpgrade()); assertEquals(localAddress, options.getLocalAddress()); assertEquals(decoderInitialBufferSize, options.getDecoderInitialBufferSize()); @@ -910,8 +914,10 @@ public void testServerOptionsJson() { int maxHeaderSize = rand.nextInt(10000); HttpVersion enabledProtocol = HttpVersion.values()[rand.nextInt(HttpVersion.values().length)]; Http2Settings initialSettings = TestUtils.randomHttp2Settings(); + boolean useAlpn = TestUtils.randomBoolean(); int http2ConnectionWindowSize = TestUtils.randomInt(); String sslEngine = TestUtils.randomBoolean() ? "jdkSslEngineOptions" : "openSslEngineOptions"; + List alpnVersions = Collections.singletonList(HttpVersion.values()[TestUtils.randomPositiveInt() % 3]); boolean openSslSessionCacheEnabled = TestUtils.randomBoolean(); boolean decompressionSupported = TestUtils.randomBoolean(); boolean acceptUnmaskedFrames = TestUtils.randomBoolean(); @@ -950,8 +956,10 @@ public void testServerOptionsJson() { .put("maxConcurrentStreams", initialSettings.getMaxConcurrentStreams()) .put("initialWindowSize", initialSettings.getInitialWindowSize()) .put("maxFrameSize", initialSettings.getMaxFrameSize())) + .put("useAlpn", useAlpn) .put("http2ConnectionWindowSize", http2ConnectionWindowSize) .put(sslEngine, new JsonObject()) + .put("alpnVersions", new JsonArray().add(alpnVersions.get(0).name())) .put("openSslSessionCacheEnabled", openSslSessionCacheEnabled) .put("decompressionSupported", decompressionSupported) .put("acceptUnmaskedFrames", acceptUnmaskedFrames) @@ -989,7 +997,7 @@ public void testServerOptionsJson() { assertEquals(maxInitialLineLength, options.getMaxInitialLineLength()); assertEquals(maxHeaderSize, options.getMaxHeaderSize()); assertEquals(initialSettings, options.getInitialSettings()); - assertEquals(true, options.isUseAlpn()); + assertEquals(useAlpn, options.isUseAlpn()); assertEquals(http2ConnectionWindowSize, options.getHttp2ConnectionWindowSize()); switch (sslEngine) { case "jdkSslEngineOptions": @@ -1002,7 +1010,7 @@ public void testServerOptionsJson() { fail(); break; } - assertEquals(null, options.getAlpnVersions()); + assertEquals(alpnVersions, options.getAlpnVersions()); assertEquals(decompressionSupported, options.isDecompressionSupported()); assertEquals(acceptUnmaskedFrames, options.isAcceptUnmaskedFrames()); assertEquals(decoderInitialBufferSize, options.getDecoderInitialBufferSize()); @@ -1462,16 +1470,14 @@ private void testKeepAlive(boolean keepAlive, int poolSize, int numServers, int // We make sure we execute all the requests on the same context otherwise some responses can come beack when there // are no waiters resulting in it being closed so a a new connection is made for the next request resulting in the // number of total connections being > pool size (which is correct) - vertx.runOnContext(v -> { - for (int count = 0; count < requests; count++) { - client.request(requestOptions) - .compose(HttpClientRequest::send) - .onComplete(onSuccess(resp -> { - assertEquals(200, resp.statusCode()); - reqLatch.countDown(); - })); - } - }); + for (int count = 0; count < requests; count++) { + client.request(new RequestOptions(requestOptions)) + .compose(HttpClientRequest::send) + .onComplete(onSuccess(resp -> { + assertEquals(200, resp.statusCode()); + reqLatch.countDown(); + })); + } awaitLatch(reqLatch); diff --git a/vertx-core/src/test/java/io/vertx/tests/http/Http2ClientTest.java b/vertx-core/src/test/java/io/vertx/tests/http/Http2ClientTest.java index fc39e3eb117..6bd8c34cd1c 100644 --- a/vertx-core/src/test/java/io/vertx/tests/http/Http2ClientTest.java +++ b/vertx-core/src/test/java/io/vertx/tests/http/Http2ClientTest.java @@ -704,7 +704,7 @@ public void testConnectionFailed() throws Exception { @Test public void testFallbackOnHttp1() throws Exception { server.close(); - server = vertx.createHttpServer(new HttpServerConfig(serverOptions).setVersions(HttpVersion.HTTP_1_1), serverOptions.getSslOptions()); + server = vertx.createHttpServer(serverOptions.setUseAlpn(false)); server.requestHandler(req -> { assertEquals(HttpVersion.HTTP_1_1, req.version()); req.response().end(); diff --git a/vertx-core/src/test/java/io/vertx/tests/http/Http2Test.java b/vertx-core/src/test/java/io/vertx/tests/http/Http2Test.java index 6e6c14597a0..2474c3a1d54 100644 --- a/vertx-core/src/test/java/io/vertx/tests/http/Http2Test.java +++ b/vertx-core/src/test/java/io/vertx/tests/http/Http2Test.java @@ -279,9 +279,7 @@ public void testClientDoesNotSupportAlpn() throws Exception { public void testServerDoesNotSupportAlpn() throws Exception { waitFor(2); server.close(); - server = vertx.createHttpServer( - new HttpServerConfig(Http2TestBase.createHttp2ServerOptions()).setVersions(HttpVersion.HTTP_1_1), - Http2TestBase.createHttp2ServerOptions().getSslOptions()); + server = vertx.createHttpServer(Http2TestBase.createHttp2ServerOptions().setUseAlpn(false)); server.requestHandler(req -> { assertEquals(HttpVersion.HTTP_1_1, req.version()); req.response().end(); @@ -958,45 +956,36 @@ public void testStreamResetErrorMapping() throws Exception { @Test public void testUnsupportedAlpnVersion() throws Exception { - testUnsupportedAlpnVersion(new JdkSSLEngineOptions(), false); + testUnsupportedAlpnVersion(new JdkSSLEngineOptions()); } @Test public void testUnsupportedAlpnVersionOpenSSL() throws Exception { - testUnsupportedAlpnVersion(new OpenSSLEngineOptions(), true); + testUnsupportedAlpnVersion(new OpenSSLEngineOptions()); } - private void testUnsupportedAlpnVersion(SSLEngineOptions engine, boolean accept) throws Exception { + private void testUnsupportedAlpnVersion(SSLEngineOptions engine) throws Exception { server.close(); - server = vertx.createHttpServer( - new HttpServerConfig(Http2TestBase.createHttp2ServerOptions() - .setSslEngineOptions(engine)) - .setVersions(HttpVersion.HTTP_2), - Http2TestBase.createHttp2ServerOptions().getSslOptions()); + server = vertx.createHttpServer(Http2TestBase.createHttp2ServerOptions() + .setSslEngineOptions(engine) + .setAlpnVersions(Collections.singletonList(HttpVersion.HTTP_2)) + ); server.requestHandler(request -> { request.response().end(); }); startServer(testAddress); client.close(); client = vertx.createHttpClient(Http2TestBase.createHttp2ClientOptions().setProtocolVersion(HttpVersion.HTTP_1_1)); - client.request(requestOptions).onComplete(ar -> { - if (ar.succeeded()) { - if (accept) { - ar.result().send().onComplete(onSuccess(resp -> { - testComplete(); - })); - } else { - fail(); - } - } else { - if (accept) { - fail(); - } else { - testComplete(); - } - } - }); - await(); + try { + client.request(requestOptions).compose(request -> request.send() + .expecting(HttpResponseExpectation.SC_OK) + .compose(HttpClientResponse::end) + .map(request.version())) + .await(); + fail(); + } catch (Exception ignore) { + // Expected + } } @Test diff --git a/vertx-core/src/test/java/io/vertx/tests/http/HttpAlternativesTest.java b/vertx-core/src/test/java/io/vertx/tests/http/HttpAlternativesTest.java index b8bcf9c9915..e0af5d3af36 100644 --- a/vertx-core/src/test/java/io/vertx/tests/http/HttpAlternativesTest.java +++ b/vertx-core/src/test/java/io/vertx/tests/http/HttpAlternativesTest.java @@ -28,6 +28,7 @@ import io.vertx.test.core.VertxTestBase; import io.vertx.test.proxy.Proxy; import io.vertx.test.tls.Cert; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; @@ -37,7 +38,6 @@ import java.util.IdentityHashMap; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.function.Supplier; @@ -65,16 +65,15 @@ protected VertxOptions getOptions() { private Consumer> startServer(int port, Cert cert, HttpVersion... versions) { AtomicReference> handler = new AtomicReference<>(); - Set tcpVersions = Stream.of(versions).filter(v -> v != HttpVersion.HTTP_3).collect(Collectors.toSet()); + List tcpVersions = Stream.of(versions).filter(v -> v != HttpVersion.HTTP_3).collect(Collectors.toList()); List quicVersions = Stream.of(versions).filter(v -> v == HttpVersion.HTTP_3).collect(Collectors.toList()); if (!tcpVersions.isEmpty()) { - HttpServer server = vertx.createHttpServer(new HttpServerConfig() + HttpServer server = vertx.createHttpServer(new HttpServerOptions() .setSsl(true) - .setVersions(tcpVersions), - new ServerSSLOptions() - .setSni(true) - .setKeyCertOptions(cert.get()) - ); + .setUseAlpn(true) + .setSni(true) + .setAlpnVersions(tcpVersions) + .setKeyCertOptions(cert.get())); server.requestHandler(request -> { Handler h = handler.get(); if (h != null) { @@ -104,36 +103,43 @@ protected void tearDown() throws Exception { client = null; } + @Ignore @Test public void testHttp1ToHttp2Protocol() { testFollowProtocol(HttpVersion.HTTP_1_1, HttpVersion.HTTP_2, "h2=\"localhost:4044\"", "localhost:4044"); } + @Ignore @Test public void testHttp1ToHttp2ProtocolSameHost() { testFollowProtocol(HttpVersion.HTTP_1_1, HttpVersion.HTTP_2, "h2=\":4044\"", "host2.com:4044"); } + @Ignore @Test public void testHttp1ToHttp3Protocol() { testFollowProtocol(HttpVersion.HTTP_1_1, HttpVersion.HTTP_3, "h3=\"host2.com:4044\"", "host2.com:4044"); } + @Ignore @Test public void testHttp1ToHttp3ProtocolSameHost() { testFollowProtocol(HttpVersion.HTTP_1_1, HttpVersion.HTTP_3, "h3=\":4044\"", "host2.com:4044"); } + @Ignore @Test public void testHttp2ToHttp3Protocol() { testFollowProtocol(HttpVersion.HTTP_2, HttpVersion.HTTP_3, "h3=\"host2.com:4044\"", "host2.com:4044"); } + @Ignore @Test public void testHttp2ToHttp3ProtocolSameHost() { testFollowProtocol(HttpVersion.HTTP_2, HttpVersion.HTTP_3, "h3=\":4044\"", "host2.com:4044"); } + @Ignore @Test public void testExpiration() throws Exception { testFollowProtocol(HttpVersion.HTTP_1_1, HttpVersion.HTTP_2, "h2=\"host2.com:4044\";ma=1", "host2.com:4044"); @@ -147,16 +153,19 @@ public void testExpiration() throws Exception { assertEquals("host2.com:4043", body.toString()); } + @Ignore @Test public void testOverwriteInvalidation() { testInvalidation("h2=\"host2.com:4045\""); } + @Ignore @Test public void testClearInvalidation() { testInvalidation("clear"); } + @Ignore @Test public void testInvalidProtocolInvalidation() { testInvalidation("h2c=\"host2.com:4044\""); @@ -222,6 +231,7 @@ private void testFollowProtocol(HttpVersion initialProtocol, HttpVersion upgrade assertEquals("host2.com:4043", body.toString()); } + @Ignore @Test public void testEvictInvalidAlternative() { startServer(4043, Cert.SNI_JKS, HttpVersion.HTTP_1_1) @@ -254,6 +264,7 @@ public void testEvictInvalidAlternative() { ).await(); } + @Ignore @Test public void testIgnoreAlternativeWithoutSNI() { startServer(4043, Cert.SNI_JKS, HttpVersion.HTTP_1_1) @@ -280,6 +291,7 @@ public void testIgnoreAlternativeWithoutSNI() { assertEquals("localhost:4043", body.toString()); } + @Ignore @Test public void testCertificateValidation() { startServer(4043, Cert.SNI_JKS, HttpVersion.HTTP_1_1) @@ -321,6 +333,7 @@ public void testCertificateValidation() { } } + @Ignore @Test public void testIgnoreAlternativeServicesAdvertisements() { startServer(4043, Cert.SNI_JKS, HttpVersion.HTTP_1_1) @@ -347,6 +360,7 @@ public void testIgnoreAlternativeServicesAdvertisements() { assertEquals("host2.com:4043", body.toString()); } + @Ignore @Test public void testIgnoreAlternativeServicesAdvertisements2() { HttpServer server = vertx.createHttpServer(new HttpServerOptions().setHttp2ClearTextEnabled(false)); @@ -374,6 +388,7 @@ public void testIgnoreAlternativeServicesAdvertisements2() { assertEquals("host2.com:8080", body.toString()); } + @Ignore @Test public void testAlternativeCaching1() throws Exception { // Test that we maintain the information although the server will an advertisement on each request @@ -381,6 +396,7 @@ public void testAlternativeCaching1() throws Exception { assertEquals(1, times); } + @Ignore @Test public void testAlternativeCaching2() throws Exception { // Test that we refresh information when expiration time is short and the server provides identical advertisements diff --git a/vertx-core/src/test/java/io/vertx/tests/http/HttpClientConfigTest.java b/vertx-core/src/test/java/io/vertx/tests/http/HttpClientConfigTest.java index 119c81f556a..0544b27c48e 100644 --- a/vertx-core/src/test/java/io/vertx/tests/http/HttpClientConfigTest.java +++ b/vertx-core/src/test/java/io/vertx/tests/http/HttpClientConfigTest.java @@ -38,7 +38,7 @@ public void testFromDefaultOptions() { assertNotNull(config.getHttp2Config()); assertNotNull(config.getHttp3Config()); assertFalse(config.isSsl()); - assertEquals(List.of(HTTP_1_1, HTTP_2), config.getVersions()); + assertEquals(List.of(HTTP_1_1), config.getVersions()); } @Test diff --git a/vertx-core/src/test/java/io/vertx/tests/http/HttpProtocolTest.java b/vertx-core/src/test/java/io/vertx/tests/http/HttpProtocolTest.java deleted file mode 100644 index 3e7ac702fca..00000000000 --- a/vertx-core/src/test/java/io/vertx/tests/http/HttpProtocolTest.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2011-2025 Contributors to the Eclipse Foundation - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 - * which is available at https://www.apache.org/licenses/LICENSE-2.0. - * - * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 - */ -package io.vertx.tests.http; - -import io.vertx.core.http.*; -import io.vertx.test.http.HttpConfig; -import io.vertx.test.http.HttpTestBase; -import io.vertx.test.http.SimpleHttpTest; -import org.junit.Test; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -public class HttpProtocolTest extends SimpleHttpTest { - - public HttpProtocolTest() { - super(HttpConfig.Http1x.DEFAULT); - } - - @Test - public void testDefaultProtocol() throws Exception { - server.requestHandler(request -> { - request.response().send("" + request.version()); - }); - startServer(testAddress); - - String protocol = client.request(requestOptions) - .compose(request -> request - .send() - .expecting(HttpResponseExpectation.SC_OK) - .compose(HttpClientResponse::body)).await().toString(); - - assertEquals("HTTP_1_1", protocol); - } - - @Test - public void testProtocolSelection() throws Exception { - testProtocols("HTTP_2"); - } - - @Test - public void testUnsupportedProtocol() throws Exception { - server.close(); - server = vertx.createHttpServer(new HttpServerOptions().setHttp2ClearTextEnabled(false)); - testProtocols("HTTP_1_1"); - } - - private void testProtocols(String expectedProtocol) throws Exception { - List connections = Collections.synchronizedList(new ArrayList<>()); - server.connectionHandler(connections::add); - server.requestHandler(request -> { - request.response().send("" + request.version()); - }); - startServer(testAddress); - - for (int i = 0;i < 8;i++) { - String protocol = client.request(new RequestOptions(requestOptions).setProtocolVersion(HttpVersion.HTTP_2)) - .compose(request -> request - .send() - .expecting(HttpResponseExpectation.SC_OK) - .compose(HttpClientResponse::body)).await().toString(); - assertEquals(expectedProtocol, protocol); - } - - for (int i = 0;i < 8;i++) { - String protocol = client.request(requestOptions) - .compose(request -> request - .send() - .expecting(HttpResponseExpectation.SC_OK) - .compose(HttpClientResponse::body)).await().toString(); - assertEquals("HTTP_1_1", protocol); - } - - assertEquals(2, connections.size()); - } -} diff --git a/vertx-core/src/test/java/io/vertx/tests/http/SupportedVersionsTest.java b/vertx-core/src/test/java/io/vertx/tests/http/SupportedVersionsTest.java index 3a06657552c..24a4adf39fc 100644 --- a/vertx-core/src/test/java/io/vertx/tests/http/SupportedVersionsTest.java +++ b/vertx-core/src/test/java/io/vertx/tests/http/SupportedVersionsTest.java @@ -11,7 +11,6 @@ package io.vertx.tests.http; import io.vertx.core.http.*; -import io.vertx.core.http.Http2ClientConfig; import io.vertx.core.http.HttpClientConfig; import io.vertx.core.net.ClientSSLOptions; import io.vertx.core.net.ServerSSLOptions; @@ -21,8 +20,10 @@ import org.junit.Test; import org.junit.runner.RunWith; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; -import java.util.Set; +import java.util.function.Supplier; import static io.vertx.test.http.AbstractHttpTest.DEFAULT_HTTPS_PORT; import static io.vertx.test.http.AbstractHttpTest.DEFAULT_HTTP_PORT; @@ -36,233 +37,388 @@ public class SupportedVersionsTest extends VertxTestBase { .setPort(DEFAULT_HTTPS_PORT) .setSsl(true) .setKeyCertOptions(Cert.SNI_JKS.get()); - private static final HttpServerOptions TCP_SERVER_DEFAULT_TLS_WITH_ALPN = new HttpServerOptions(TCP_SERVER_DEFAULT_TLS); + private static final HttpServerOptions TCP_SERVER_DEFAULT_TLS_WITH_ALPN = new HttpServerOptions(TCP_SERVER_DEFAULT_TLS) + .setUseAlpn(true); private static final HttpServerConfig QUIC_SERVER = new HttpServerConfig() .setVersions(HttpVersion.HTTP_3) .setQuicPort(DEFAULT_HTTPS_PORT); - private static final ServerSSLOptions DEFAULT_SERVER_TLS = new ServerSSLOptions().setKeyCertOptions(Cert.SNI_JKS.get()); - private static final ClientSSLOptions DEFAULT_CLIENT_TLS = new ClientSSLOptions().setTrustAll(true); + private static final ServerSSLOptions DEFAULT_SERVER_TLS = new ServerSSLOptions().setKeyCertOptions(Cert.SNI_JKS.get()).removeEnabledSecureTransportProtocol("TLSv1.3"); + private static final ServerSSLOptions DEFAULT_SERVER_TLS_NO_ALPN = new ServerSSLOptions().setKeyCertOptions(Cert.SNI_JKS.get()).setUseAlpn(false); + private static final ClientSSLOptions DEFAULT_CLIENT_TLS = new ClientSSLOptions().setTrustAll(true).removeEnabledSecureTransportProtocol("TLSv1.3"); private static final HttpClientOptions LEGACY_CLIENT_DEFAULT_TLS = new HttpClientOptions().setSsl(true).setTrustAll(true); private static final HttpClientOptions LEGACY_CLIENT_DEFAULT_TLS_WITH_ALPN = new HttpClientOptions(LEGACY_CLIENT_DEFAULT_TLS); private static final HttpClientConfig CLIENT_DEFAULT = new HttpClientConfig(); private static final HttpClientConfig CLIENT_DEFAULT_TLS = new HttpClientConfig().setSsl(true); - private HttpServer tcpServer; - private HttpServer quicServer; - private HttpClient client; - - @Override - protected void tearDown() throws Exception { - HttpClient c = client; - client = null; - if (c != null) { - c.close().await(); - } - HttpServer s = tcpServer; - tcpServer = null; - if (s != null) { - s.close().await(); - } - s = quicServer; - quicServer = null; - if (s != null) { - s.close().await(); - } - super.tearDown(); - } - @Test public void testLegacyDefaultTest() { - HttpVersion version = legacyTest(new HttpServerOptions(TCP_SERVER_DEFAULT), new HttpClientOptions()); + HttpServerOptions server = new HttpServerOptions(TCP_SERVER_DEFAULT); + HttpClientOptions client = new HttpClientOptions(); + HttpVersion version = testDefaultVersion(server, client); assertEquals(HttpVersion.HTTP_1_1, version); + List accepted = testAcceptedVersions(server, client); + assertEquals(Arrays.asList(HttpVersion.HTTP_1_0, HttpVersion.HTTP_1_1, null), accepted); } @Test public void testLegacyHttp11Test() { - HttpVersion version = legacyTest(new HttpServerOptions(TCP_SERVER_DEFAULT), new HttpClientOptions().setProtocolVersion(HttpVersion.HTTP_1_1)); + HttpServerOptions server = new HttpServerOptions(TCP_SERVER_DEFAULT); + HttpClientOptions client = new HttpClientOptions().setProtocolVersion(HttpVersion.HTTP_1_1); + HttpVersion version = testDefaultVersion(server, client); assertEquals(HttpVersion.HTTP_1_1, version); + List accepted = testAcceptedVersions(server, client); + assertEquals(Arrays.asList(HttpVersion.HTTP_1_0, HttpVersion.HTTP_1_1, null), accepted); } @Test public void testLegacyTlsTest() { - HttpVersion version = legacyTest(TCP_SERVER_DEFAULT_TLS, LEGACY_CLIENT_DEFAULT_TLS); + HttpServerOptions server = TCP_SERVER_DEFAULT_TLS; + HttpClientOptions client = LEGACY_CLIENT_DEFAULT_TLS; + HttpVersion version = testDefaultVersion(server, client); assertEquals(HttpVersion.HTTP_1_1, version); + List accepted = testAcceptedVersions(server, client); + assertEquals(Arrays.asList(HttpVersion.HTTP_1_1, HttpVersion.HTTP_1_1, null), accepted); } @Test public void testLegacyHttp11TlsTest() { - HttpVersion version = legacyTest(TCP_SERVER_DEFAULT_TLS, new HttpClientOptions(LEGACY_CLIENT_DEFAULT_TLS).setProtocolVersion(HttpVersion.HTTP_1_1)); - assertEquals(HttpVersion.HTTP_1_1, version); + HttpServerOptions server = TCP_SERVER_DEFAULT_TLS; + HttpClientOptions client = new HttpClientOptions(LEGACY_CLIENT_DEFAULT_TLS).setProtocolVersion(HttpVersion.HTTP_1_1); + HttpVersion version = testDefaultVersion(server, client); + assertEquals(HttpVersion.HTTP_1_1, version); // Depends on server : check that + List accepted = testAcceptedVersions(server, client); + assertEquals(Arrays.asList(HttpVersion.HTTP_1_1, HttpVersion.HTTP_1_1, null), accepted); } @Test public void testLegacyH2cWithUpgradeTest() { - HttpVersion version = legacyTest(new HttpServerOptions(TCP_SERVER_DEFAULT), new HttpClientOptions().setHttp2ClearTextUpgrade(false).setProtocolVersion(HttpVersion.HTTP_2)); + HttpServerOptions server = new HttpServerOptions(TCP_SERVER_DEFAULT); + HttpClientOptions client = new HttpClientOptions().setHttp2ClearTextUpgrade(false).setProtocolVersion(HttpVersion.HTTP_2); + HttpVersion version = testDefaultVersion(server, client); assertEquals(HttpVersion.HTTP_2, version); + List accepted = testAcceptedVersions(server, client); + assertEquals(List.of(HttpVersion.HTTP_1_0, HttpVersion.HTTP_1_1, HttpVersion.HTTP_2), accepted); } @Test public void testLegacyH2cPriorKnowledgeTest() { - HttpVersion version = legacyTest(new HttpServerOptions(TCP_SERVER_DEFAULT), new HttpClientOptions().setHttp2ClearTextUpgrade(true).setProtocolVersion(HttpVersion.HTTP_2)); + HttpServerOptions server = new HttpServerOptions(TCP_SERVER_DEFAULT); + HttpClientOptions client = new HttpClientOptions().setHttp2ClearTextUpgrade(true).setProtocolVersion(HttpVersion.HTTP_2); + HttpVersion version = testDefaultVersion(server, client); assertEquals(HttpVersion.HTTP_2, version); + List accepted = testAcceptedVersions(server, client); + assertEquals(List.of(HttpVersion.HTTP_1_0, HttpVersion.HTTP_1_1, HttpVersion.HTTP_2), accepted); } @Test public void testLegacyH2Test() { - HttpVersion version = legacyTest(TCP_SERVER_DEFAULT_TLS_WITH_ALPN, new HttpClientOptions(LEGACY_CLIENT_DEFAULT_TLS_WITH_ALPN).setProtocolVersion(HttpVersion.HTTP_2)); + HttpServerOptions server = TCP_SERVER_DEFAULT_TLS_WITH_ALPN; + HttpClientOptions client = new HttpClientOptions(LEGACY_CLIENT_DEFAULT_TLS_WITH_ALPN).setProtocolVersion(HttpVersion.HTTP_2); + HttpVersion version = testDefaultVersion(server, client); assertEquals(HttpVersion.HTTP_2, version); + List accepted = testAcceptedVersions(server, client); + assertEquals(Arrays.asList(HttpVersion.HTTP_1_1, HttpVersion.HTTP_1_1, HttpVersion.HTTP_2), accepted); } - private HttpVersion legacyTest(HttpServerOptions serverOptions, HttpClientOptions clientOptions) { - tcpServer = vertx - .createHttpServer(serverOptions) - .requestHandler(request -> { - request.response().end(); - }); - int port = tcpServer - .listen() - .await().actualPort(); - client = vertx.createHttpClient(clientOptions); - return client.request(new RequestOptions().setPort(port).setHost("localhost")) - .compose(request -> request - .send() - .expecting(HttpResponseExpectation.SC_OK) - .compose(HttpClientResponse::end) - .map(v -> request.version())) - .await(); - } - - @Test - public void testConfigDefaultTest() { - HttpVersion version = configTest(new HttpServerOptions(TCP_SERVER_DEFAULT), new HttpClientConfig()); - assertEquals(HttpVersion.HTTP_1_1, version); - } - - @Test - public void testConfigHttp11Test() { - HttpVersion version = configTest(new HttpServerOptions(TCP_SERVER_DEFAULT), new HttpClientConfig().setVersions(HttpVersion.HTTP_1_1)); - assertEquals(HttpVersion.HTTP_1_1, version); - } - - @Test - public void testConfigDefaultTlsTest() { - HttpVersion version = configTest(new HttpServerOptions(TCP_SERVER_DEFAULT_TLS), new HttpClientConfig(LEGACY_CLIENT_DEFAULT_TLS).setVersions(HttpVersion.HTTP_1_1)); - assertEquals(HttpVersion.HTTP_1_1, version); - } - - @Test - public void testConfigH2cWithUpgradeTest() { - try { - configTest(new HttpServerOptions(TCP_SERVER_DEFAULT), new HttpClientConfig().setVersions(HttpVersion.HTTP_2).setHttp2Config(new Http2ClientConfig().setClearTextUpgrade(true))); - fail(); - } catch (Exception ignore) { - // HTTP/1.1 is required - } - HttpVersion version = configTest(new HttpServerOptions(TCP_SERVER_DEFAULT), new HttpClientConfig().setVersions(HttpVersion.HTTP_2, HttpVersion.HTTP_1_1).setHttp2Config(new Http2ClientConfig().setClearTextUpgrade(true))); - assertEquals(HttpVersion.HTTP_2, version); + private HttpVersion testDefaultVersion(HttpServerOptions serverOptions, HttpClientOptions clientOptions) { + return testDefaultVersion(() -> vertx.createHttpServer(serverOptions), () -> vertx.createHttpClient(clientOptions)); } - @Test - public void testConfigH2cWithPriorKnowledgeTest() { - HttpVersion version = configTest(new HttpServerOptions(TCP_SERVER_DEFAULT), new HttpClientConfig().setVersions(HttpVersion.HTTP_2).setHttp2Config(new Http2ClientConfig().setClearTextUpgrade(false))); - assertEquals(HttpVersion.HTTP_2, version); + private List testAcceptedVersions(HttpServerOptions serverOptions, HttpClientOptions clientOptions) { + return testAcceptedVersions(() -> vertx.createHttpServer(serverOptions), () -> vertx.createHttpClient(clientOptions)); } @Test - public void testConfigH2() { - HttpVersion version = configTest(new HttpServerOptions(TCP_SERVER_DEFAULT_TLS_WITH_ALPN), new HttpClientConfig(LEGACY_CLIENT_DEFAULT_TLS_WITH_ALPN).setVersions(HttpVersion.HTTP_2)); - assertEquals(HttpVersion.HTTP_2, version); + public void testDefaultServerConfig() { + test( + new HttpServerConfig(), + new HttpClientConfig(), + HttpVersion.HTTP_1_1, + HttpVersion.HTTP_1_0, HttpVersion.HTTP_1_1, HttpVersion.HTTP_2); + test( + new HttpServerConfig(), + new HttpClientConfig().setVersions(HttpVersion.HTTP_1_1, HttpVersion.HTTP_2), + HttpVersion.HTTP_1_1, + HttpVersion.HTTP_1_0, HttpVersion.HTTP_1_1, HttpVersion.HTTP_2); + test( + new HttpServerConfig(), + new HttpClientConfig().setVersions(HttpVersion.HTTP_2, HttpVersion.HTTP_1_1), + HttpVersion.HTTP_2, + HttpVersion.HTTP_1_0, HttpVersion.HTTP_1_1, HttpVersion.HTTP_2); + test( + new HttpServerConfig(), + new HttpClientConfig().setVersions(HttpVersion.HTTP_2) + .setHttp2Config( + new Http2ClientConfig() + .setClearTextUpgrade(false)), + HttpVersion.HTTP_2, + null, null, HttpVersion.HTTP_2); } @Test - public void testConfigHttp1H2() { - HttpVersion version = configTest(new HttpServerOptions(TCP_SERVER_DEFAULT_TLS_WITH_ALPN).setPort(4043), new HttpClientConfig(LEGACY_CLIENT_DEFAULT_TLS_WITH_ALPN).setVersions(HttpVersion.HTTP_1_1, HttpVersion.HTTP_2)); - assertEquals(HttpVersion.HTTP_1_1, version); + public void testHttp1ServerConfig() { + test( + new HttpServerConfig().setVersions(HttpVersion.HTTP_1_1), + new HttpClientConfig(), + HttpVersion.HTTP_1_1, + HttpVersion.HTTP_1_0, HttpVersion.HTTP_1_1, HttpVersion.HTTP_1_1); + test( + new HttpServerConfig().setVersions(HttpVersion.HTTP_1_1), + new HttpClientConfig().setVersions(HttpVersion.HTTP_1_1, HttpVersion.HTTP_2), + HttpVersion.HTTP_1_1, + HttpVersion.HTTP_1_0, HttpVersion.HTTP_1_1, HttpVersion.HTTP_1_1); + test( + new HttpServerConfig().setVersions(HttpVersion.HTTP_1_1), + new HttpClientConfig().setVersions(HttpVersion.HTTP_2, HttpVersion.HTTP_1_1), + HttpVersion.HTTP_1_1, + HttpVersion.HTTP_1_0, HttpVersion.HTTP_1_1, HttpVersion.HTTP_1_1); + test( + new HttpServerConfig().setVersions(HttpVersion.HTTP_1_1), + new HttpClientConfig().setVersions(HttpVersion.HTTP_2) + .setHttp2Config( + new Http2ClientConfig() + .setClearTextUpgrade(false)), + null, + null, null, null); } @Test - public void testConfigH2Http1() { - HttpVersion version = configTest(new HttpServerOptions(TCP_SERVER_DEFAULT_TLS_WITH_ALPN).setPort(4043), new HttpClientConfig(LEGACY_CLIENT_DEFAULT_TLS_WITH_ALPN).setVersions(HttpVersion.HTTP_2, HttpVersion.HTTP_1_1)); - assertEquals(HttpVersion.HTTP_2, version); + public void testHttp2ServerConfig() { + test( + new HttpServerConfig().setVersions(HttpVersion.HTTP_2), + new HttpClientConfig(), + null, + null, null, null); + test( + new HttpServerConfig().setVersions(HttpVersion.HTTP_2), + new HttpClientConfig().setVersions(HttpVersion.HTTP_1_1, HttpVersion.HTTP_2), + null, + null, null, null); + test( + new HttpServerConfig().setVersions(HttpVersion.HTTP_2), + new HttpClientConfig().setVersions(HttpVersion.HTTP_2, HttpVersion.HTTP_1_1), + null, + null, null, null); + test( + new HttpServerConfig().setVersions(HttpVersion.HTTP_2), + new HttpClientConfig().setVersions(HttpVersion.HTTP_2) + .setHttp2Config( + new Http2ClientConfig() + .setClearTextUpgrade(false)), + HttpVersion.HTTP_2, + null, null, HttpVersion.HTTP_2); } @Test - public void testConfigH3() { - HttpVersion version = configTest(new HttpServerConfig(QUIC_SERVER).setQuicPort(4043), DEFAULT_SERVER_TLS.copy(), new HttpClientConfig(CLIENT_DEFAULT).setVersions(HttpVersion.HTTP_3)); - assertEquals(HttpVersion.HTTP_3, version); + public void testDefaultTlsServerConfig() { + test( + new HttpServerConfig().setSsl(true), + new HttpClientConfig().setSsl(true), + HttpVersion.HTTP_2, + null, HttpVersion.HTTP_1_1, HttpVersion.HTTP_2); + test( + new HttpServerConfig().setSsl(true), + new HttpClientConfig().setSsl(true).setVersions(HttpVersion.HTTP_1_1, HttpVersion.HTTP_2), + HttpVersion.HTTP_2, + null, HttpVersion.HTTP_1_1, HttpVersion.HTTP_2); + test( + new HttpServerConfig().setSsl(true), + new HttpClientConfig().setSsl(true).setVersions(HttpVersion.HTTP_2, HttpVersion.HTTP_1_1), + HttpVersion.HTTP_2, + null, HttpVersion.HTTP_1_1, HttpVersion.HTTP_2); + test( + new HttpServerConfig().setSsl(true), + new HttpClientConfig().setSsl(true).setVersions(HttpVersion.HTTP_2), + HttpVersion.HTTP_2, + null, null, HttpVersion.HTTP_2); + test( + new HttpServerConfig().setSsl(true), + new HttpClientConfig().setSsl(true).setVersions(HttpVersion.HTTP_1_1), + HttpVersion.HTTP_1_1, + null, HttpVersion.HTTP_1_1, null); } @Test - public void testConfigHttp1H3() { - HttpVersion version = configTest(new HttpServerOptions(TCP_SERVER_DEFAULT), new HttpServerConfig(QUIC_SERVER).setQuicPort(4043), DEFAULT_SERVER_TLS.copy(), new HttpClientConfig(CLIENT_DEFAULT).setVersions(HttpVersion.HTTP_1_1, HttpVersion.HTTP_3)); - assertEquals(HttpVersion.HTTP_1_1, version); + public void testHttp1TlsServerConfig() { + test( + new HttpServerConfig().setSsl(true).setVersions(HttpVersion.HTTP_1_1), + new HttpClientConfig().setSsl(true), + HttpVersion.HTTP_1_1, + HttpVersion.HTTP_1_1, HttpVersion.HTTP_1_1, HttpVersion.HTTP_1_1); + test( + new HttpServerConfig().setSsl(true).setVersions(HttpVersion.HTTP_1_1), + new HttpClientConfig().setSsl(true).setVersions(HttpVersion.HTTP_1_1, HttpVersion.HTTP_2), + HttpVersion.HTTP_1_1, + HttpVersion.HTTP_1_1, HttpVersion.HTTP_1_1, HttpVersion.HTTP_1_1); + test( + new HttpServerConfig().setSsl(true).setVersions(HttpVersion.HTTP_1_1), + new HttpClientConfig().setSsl(true).setVersions(HttpVersion.HTTP_2, HttpVersion.HTTP_1_1), + HttpVersion.HTTP_1_1, + HttpVersion.HTTP_1_1, HttpVersion.HTTP_1_1, HttpVersion.HTTP_1_1); + test( + new HttpServerConfig().setSsl(true).setVersions(HttpVersion.HTTP_1_1), + new HttpClientConfig().setSsl(true).setVersions(HttpVersion.HTTP_2), + null, + null, null, null); + test( + new HttpServerConfig().setSsl(true).setVersions(HttpVersion.HTTP_1_1), + new HttpClientConfig().setSsl(true).setVersions(HttpVersion.HTTP_1_1), + HttpVersion.HTTP_1_1, + HttpVersion.HTTP_1_1, HttpVersion.HTTP_1_1, null); } @Test - public void testConfigH2H3() { - HttpVersion version = configTest(new HttpServerOptions(TCP_SERVER_DEFAULT_TLS_WITH_ALPN), new HttpServerConfig(QUIC_SERVER).setQuicPort(4043), DEFAULT_SERVER_TLS.copy(), new HttpClientConfig(CLIENT_DEFAULT).setSsl(true).setVersions(HttpVersion.HTTP_2, HttpVersion.HTTP_3)); - assertEquals(HttpVersion.HTTP_2, version); + public void testHttp2TlsServerConfig() { + test( + new HttpServerConfig().setSsl(true).setVersions(HttpVersion.HTTP_2), + new HttpClientConfig().setSsl(true), + HttpVersion.HTTP_2, + null, null, HttpVersion.HTTP_2); + test( + new HttpServerConfig().setSsl(true).setVersions(HttpVersion.HTTP_2), + new HttpClientConfig().setSsl(true).setVersions(HttpVersion.HTTP_1_1, HttpVersion.HTTP_2), + HttpVersion.HTTP_2, + null, null, HttpVersion.HTTP_2); + test( + new HttpServerConfig().setSsl(true).setVersions(HttpVersion.HTTP_2), + new HttpClientConfig().setSsl(true).setVersions(HttpVersion.HTTP_2, HttpVersion.HTTP_1_1), + HttpVersion.HTTP_2, + null, null, HttpVersion.HTTP_2); + test( + new HttpServerConfig().setSsl(true).setVersions(HttpVersion.HTTP_2), + new HttpClientConfig().setSsl(true).setVersions(HttpVersion.HTTP_2), + HttpVersion.HTTP_2, + null, null, HttpVersion.HTTP_2); + test( + new HttpServerConfig().setSsl(true).setVersions(HttpVersion.HTTP_2), + new HttpClientConfig().setSsl(true).setVersions(HttpVersion.HTTP_1_1), + null, + null, null, null); } - @Test - public void testConfigH2CH3() { - HttpVersion version = configTest(new HttpServerOptions(TCP_SERVER_DEFAULT), new HttpServerConfig(QUIC_SERVER).setQuicPort(4043), DEFAULT_SERVER_TLS.copy(), new HttpClientConfig(CLIENT_DEFAULT).setVersions(HttpVersion.HTTP_2, HttpVersion.HTTP_1_1, HttpVersion.HTTP_3)); - assertEquals(HttpVersion.HTTP_2, version); +// +// @Test +// public void testConfigH3() { +// HttpVersion version = configTest(new HttpServerConfig(QUIC_SERVER).setQuicPort(4043), DEFAULT_SERVER_TLS.copy(), new HttpClientConfig(CLIENT_DEFAULT).setVersions(HttpVersion.HTTP_3)); +// assertEquals(HttpVersion.HTTP_3, version); +// } +// +// @Test +// public void testConfigHttp1H3() { +// HttpVersion version = configTest(new HttpServerOptions(TCP_SERVER_DEFAULT), new HttpServerConfig(QUIC_SERVER).setQuicPort(4043), DEFAULT_SERVER_TLS.copy(), new HttpClientConfig(CLIENT_DEFAULT).setVersions(HttpVersion.HTTP_1_1, HttpVersion.HTTP_3)); +// assertEquals(HttpVersion.HTTP_1_1, version); +// } +// +// @Test +// public void testConfigH2H3() { +// HttpVersion version = configTest(new HttpServerOptions(TCP_SERVER_DEFAULT_TLS_WITH_ALPN), new HttpServerConfig(QUIC_SERVER).setQuicPort(4043), DEFAULT_SERVER_TLS.copy(), new HttpClientConfig(CLIENT_DEFAULT).setSsl(true).setVersions(HttpVersion.HTTP_2, HttpVersion.HTTP_3)); +// assertEquals(HttpVersion.HTTP_2, version); +// } +// +// @Test +// public void testConfigH2CH3() { +// HttpVersion version = configTest(new HttpServerOptions(TCP_SERVER_DEFAULT), new HttpServerConfig(QUIC_SERVER).setQuicPort(4043), DEFAULT_SERVER_TLS.copy(), new HttpClientConfig(CLIENT_DEFAULT).setVersions(HttpVersion.HTTP_2, HttpVersion.HTTP_1_1, HttpVersion.HTTP_3)); +// assertEquals(HttpVersion.HTTP_2, version); +// } +// +// @Test +// public void testConfigH2H3NoAlpn() { +// HttpClientConfig config = new HttpClientConfig(CLIENT_DEFAULT_TLS).setVersions(HttpVersion.HTTP_2, HttpVersion.HTTP_3); +// configTest(new HttpServerOptions(TCP_SERVER_DEFAULT_TLS_WITH_ALPN), new HttpServerConfig(QUIC_SERVER).setQuicPort(4043), DEFAULT_SERVER_TLS.copy(), config); +// } + + private void test(HttpServerConfig server, HttpClientConfig client, HttpVersion defaultVersion, HttpVersion... acceptedVersions) { + test(server, client, true, defaultVersion, acceptedVersions); } - @Test - public void testConfigH2H3NoAlpn() { - HttpClientConfig config = new HttpClientConfig(CLIENT_DEFAULT_TLS).setVersions(HttpVersion.HTTP_2, HttpVersion.HTTP_3); - configTest(new HttpServerOptions(TCP_SERVER_DEFAULT_TLS_WITH_ALPN), new HttpServerConfig(QUIC_SERVER).setQuicPort(4043), DEFAULT_SERVER_TLS.copy(), config); + private void test(HttpServerConfig server, HttpClientConfig client, boolean useAlpn, HttpVersion defaultVersion, HttpVersion... acceptedVersions) { + HttpVersion version = testDefaultVersion(server, client, useAlpn); + assertEquals(defaultVersion, version); +// List accepted = testAcceptedVersions(server, client, useAlpn); +// assertEquals(Arrays.asList(acceptedVersions), accepted); } - private HttpVersion configTest(HttpServerOptions serverOptions, HttpClientConfig clientConfig) { - return configTest(serverOptions, null, null, clientConfig); + private HttpVersion testDefaultVersion(HttpServerConfig serverConfig, HttpClientConfig clientConfig, boolean useAlpn) { + return testDefaultVersion( + () -> vertx.createHttpServer(serverConfig, useAlpn ? DEFAULT_SERVER_TLS : DEFAULT_SERVER_TLS_NO_ALPN), + () -> vertx.createHttpClient(clientConfig, DEFAULT_CLIENT_TLS) + ); } - private HttpVersion configTest(HttpServerConfig quicConfig, ServerSSLOptions quicSslOptions, HttpClientConfig clientConfig) { - return configTest(null, quicConfig, quicSslOptions, clientConfig); + private List testAcceptedVersions(HttpServerConfig serverConfig, HttpClientConfig clientConfig, boolean useAlpn) { + return testAcceptedVersions( + () -> vertx.createHttpServer(serverConfig, useAlpn ? DEFAULT_SERVER_TLS : DEFAULT_SERVER_TLS_NO_ALPN), + () -> vertx.createHttpClient(clientConfig, DEFAULT_CLIENT_TLS) + ); } - private HttpVersion configTest(HttpServerOptions tcpServerOptions, HttpServerConfig quicServerConfig, - ServerSSLOptions quicSslOptions, HttpClientConfig clientConfig) { - if (tcpServerOptions != null) { - tcpServer = vertx - .createHttpServer(tcpServerOptions) - .requestHandler(request -> { - request.response().end(); - }); - tcpServer - .listen() - .await(); - } - if (quicServerConfig != null) { - quicServer = vertx - .createHttpServer(quicServerConfig, quicSslOptions) - .requestHandler(request -> { - request.response().end(); - }); - quicServer - .listen() - .await(); + private HttpVersion testDefaultVersion(Supplier serverSupplier, Supplier clientSupplier) { + HttpServer server = serverSupplier + .get() + .requestHandler(request -> { + request.response().end(); + }); + try { + int port = server + .listen(0) + .await().actualPort(); + HttpClient client = clientSupplier.get(); + try { + return client.request(new RequestOptions().setPort(port).setHost("localhost")) + .compose(request -> request + .send() + .expecting(HttpResponseExpectation.SC_OK) + .compose(HttpClientResponse::end) + .map(v -> request.version())) + .await(); + } catch (Exception ignore) { + return null; + } finally { + client.close().await(); + } + } finally { + server.close().await(); } + } - if (clientConfig.getDefaultPort() == 80) { - if (tcpServer != null) { - clientConfig.setDefaultPort(tcpServer.actualPort()); - } else if (quicServer != null) { - clientConfig.setDefaultPort(quicServer.actualPort()); + private List testAcceptedVersions(Supplier serverSupplier, Supplier clientSupplier) { + HttpServer server = serverSupplier + .get() + .requestHandler(request -> { + request.response().end(); + }); + try { + int port = server + .listen(0) + .await().actualPort(); + HttpClient client = clientSupplier.get(); + try { + List result = new ArrayList<>(); + for (HttpVersion test : List.of(HttpVersion.HTTP_1_0, HttpVersion.HTTP_1_1, HttpVersion.HTTP_2)) { + RequestOptions o = new RequestOptions() + .setPort(port) + .setHost("localhost") + .setProtocolVersion(test); // Should be named set version to match HttpClientConfig + try { + HttpVersion version = client.request(o) + .compose(request -> request + .send() + .expecting(HttpResponseExpectation.SC_OK) + .compose(HttpClientResponse::end) + .map(v -> request.version())) + .await(); + result.add(version); + } catch (Exception e) { + result.add((null)); + } + } + return result; + } finally { + client.close().await(); } + } finally { + server.close().await(); } - client = vertx.createHttpClient(clientConfig, DEFAULT_CLIENT_TLS); - return client.request(new RequestOptions().setHost("localhost")) - .compose(request -> request - .send() - .expecting(HttpResponseExpectation.SC_OK) - .compose(HttpClientResponse::end) - .map(v -> request.version())) - .await(); } - } diff --git a/vertx-core/src/test/java/io/vertx/tests/http/http3/Http3ClientTest.java b/vertx-core/src/test/java/io/vertx/tests/http/http3/Http3ClientTest.java index 9789589ba11..b1bb85ff5e2 100644 --- a/vertx-core/src/test/java/io/vertx/tests/http/http3/Http3ClientTest.java +++ b/vertx-core/src/test/java/io/vertx/tests/http/http3/Http3ClientTest.java @@ -402,11 +402,12 @@ public void testConnectionClose() throws Exception { @Test public void testSettings() throws Exception { - HttpServerConfig serverConfig = new HttpServerConfig(serverOptions); - serverConfig.getHttp3Config().setInitialSettings(new Http3Settings() - .setMaxFieldSectionSize(1024) - .setQPackMaxTableCapacity(1024) - .setQPackBlockedStreams(1024)); + HttpServerConfig serverConfig = new HttpServerConfig(serverOptions) + .setHttp3Config(new Http3ServerConfig() + .setInitialSettings(new Http3Settings() + .setMaxFieldSectionSize(1024) + .setQPackMaxTableCapacity(1024) + .setQPackBlockedStreams(1024))); server.close(); server = vertx.createHttpServer(serverConfig, serverSslOptions); diff --git a/vertx-core/src/test/java/io/vertx/tests/tls/Http1xTLSTest.java b/vertx-core/src/test/java/io/vertx/tests/tls/Http1xTLSTest.java index 6747cf9df4c..956129c4817 100644 --- a/vertx-core/src/test/java/io/vertx/tests/tls/Http1xTLSTest.java +++ b/vertx-core/src/test/java/io/vertx/tests/tls/Http1xTLSTest.java @@ -21,6 +21,7 @@ import io.vertx.test.tls.Cert; import io.vertx.test.tls.Trust; import org.junit.Assume; +import org.junit.Ignore; import org.junit.Test; import java.util.ArrayList; diff --git a/vertx-core/src/test/java/io/vertx/tests/tls/HttpTLSTest.java b/vertx-core/src/test/java/io/vertx/tests/tls/HttpTLSTest.java index 071e86cb048..6ba9f261320 100755 --- a/vertx-core/src/test/java/io/vertx/tests/tls/HttpTLSTest.java +++ b/vertx-core/src/test/java/io/vertx/tests/tls/HttpTLSTest.java @@ -1206,34 +1206,36 @@ TLSTest run(boolean shouldPass) { // The test with proxy that fails will not connect waitFor(2); } - ClientSSLOptions clientSSLOptions = new ClientSSLOptions(); - clientSSLOptions.setTrustAll(clientTrustAll); + HttpClientOptions options = createBaseClientOptions(); + options.setProtocolVersion(version); + options.setSsl(clientSSL); + options.setForceSni(clientForceSNI); + if (clientTrustAll) { + options.setTrustAll(true); + } if (clientUsesCrl) { - clientSSLOptions.addCrlPath("tls/root-ca/crl.pem"); + options.addCrlPath("tls/root-ca/crl.pem"); + } + if (clientOpenSSL) { + options.setSslEngineOptions(new OpenSSLEngineOptions()); + } else { + options.setSslEngineOptions(new JdkSSLEngineOptions()); + } + if (clientUsesAlpn) { + options.setUseAlpn(true); } - clientSSLOptions.setUseAlpn(clientUsesAlpn); - clientSSLOptions.setTrustOptions(clientTrust); - clientSSLOptions.setKeyCertOptions(clientCert); + options.setVerifyHost(clientVerifyHost); + options.setTrustOptions(clientTrust); + options.setKeyCertOptions(clientCert); for (String suite: clientEnabledCipherSuites) { - clientSSLOptions.addEnabledCipherSuite(suite); + options.addEnabledCipherSuite(suite); } - if (clientEnabledSecureTransportProtocol.length > 0) { - clientSSLOptions.getEnabledSecureTransportProtocols().clear(); - for (String protocol : clientEnabledSecureTransportProtocol) { - clientSSLOptions.addEnabledSecureTransportProtocol(protocol); - } + if(clientEnabledSecureTransportProtocol.length > 0) { + options.getEnabledSecureTransportProtocols().forEach(options::removeEnabledSecureTransportProtocol); } - HttpClientOptions clientOptions = createBaseClientOptions(); - clientOptions.setProtocolVersion(version); - clientOptions.setSsl(clientSSL); - clientOptions.setForceSni(clientForceSNI); - clientOptions.setVerifyHost(clientVerifyHost); - if (clientOpenSSL) { - clientOptions.setSslEngineOptions(new OpenSSLEngineOptions()); - } else { - clientOptions.setSslEngineOptions(new JdkSSLEngineOptions()); + for (String protocol : clientEnabledSecureTransportProtocol) { + options.addEnabledSecureTransportProtocol(protocol); } - clientOptions.setSslOptions(clientSSLOptions); if (proxyType != null) { ProxyOptions proxyOptions; if (proxyType == ProxyType.SOCKS5) { @@ -1244,35 +1246,37 @@ TLSTest run(boolean shouldPass) { if (useProxyAuth) { proxyOptions.setUsername("username").setPassword("username"); } - clientOptions.setProxyOptions(proxyOptions); + options.setProxyOptions(proxyOptions); + } + client = vertx.createHttpClient(options); + HttpServerOptions serverOptions = createBaseServerOptions(); + serverOptions.setTrustOptions(serverTrust); + serverOptions.setAlpnVersions(Arrays.asList(version)); + serverOptions.setKeyCertOptions(serverCert); + if (requiresClientAuth) { + serverOptions.setClientAuth(ClientAuth.REQUIRED); } - client = vertx.createHttpClient(clientOptions); - ServerSSLOptions serverSSLOptions = new ServerSSLOptions(); - serverSSLOptions.setTrustOptions(serverTrust); - serverSSLOptions.setKeyCertOptions(serverCert); - serverSSLOptions.setClientAuth(requiresClientAuth ? ClientAuth.REQUIRED : ClientAuth.NONE); - serverSSLOptions.setSni(serverSNI); if (serverUsesCrl) { - serverSSLOptions.addCrlPath("tls/root-ca/crl.pem"); + serverOptions.addCrlPath("tls/root-ca/crl.pem"); } - serverSSLOptions.setUseAlpn(serverUsesAlpn == Boolean.TRUE); - for (String suite: serverEnabledCipherSuites) { - serverSSLOptions.addEnabledCipherSuite(suite); + if (serverOpenSSL) { + serverOptions.setSslEngineOptions(new OpenSSLEngineOptions()); } - if(serverEnabledSecureTransportProtocol.length > 0) { - serverSSLOptions.getEnabledSecureTransportProtocols().clear(); - for (String protocol : serverEnabledSecureTransportProtocol) { - serverSSLOptions.addEnabledSecureTransportProtocol(protocol); - } + if (serverUsesAlpn == Boolean.TRUE) { + serverOptions.setUseAlpn(serverUsesAlpn); } - HttpServerOptions serverOptions = createBaseServerOptions(); - serverOptions.setAlpnVersions(Arrays.asList(version)); serverOptions.setSsl(serverSSL); + serverOptions.setSni(serverSNI); serverOptions.setUseProxyProtocol(serverUsesProxyProtocol); - if (serverOpenSSL) { - serverOptions.setSslEngineOptions(new OpenSSLEngineOptions()); + for (String suite: serverEnabledCipherSuites) { + serverOptions.addEnabledCipherSuite(suite); + } + if(serverEnabledSecureTransportProtocol.length > 0) { + serverOptions.getEnabledSecureTransportProtocols().forEach(serverOptions::removeEnabledSecureTransportProtocol); + } + for (String protocol : serverEnabledSecureTransportProtocol) { + serverOptions.addEnabledSecureTransportProtocol(protocol); } - serverOptions.setSslOptions(serverSSLOptions); server.close(); server = vertx.createHttpServer(serverOptions.setPort(DEFAULT_HTTPS_PORT)); server.connectionHandler(conn -> complete());