From 0285e0eba161fc3ce57aba045ec03f7cad3cf782 Mon Sep 17 00:00:00 2001 From: Moritz Heidkamp Date: Thu, 13 Jun 2024 12:32:37 +0200 Subject: [PATCH 1/6] Document how to enable HTTP/2 support in `http/connection-pool` --- src/aleph/http.clj | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/aleph/http.clj b/src/aleph/http.clj index 8231dc76..41f9798c 100644 --- a/src/aleph/http.clj +++ b/src/aleph/http.clj @@ -133,6 +133,14 @@ (defn connection-pool "Returns a connection pool which can be used as an argument in `request`. + To enable HTTP/2, add `:http2` to the `http-versions` option. If you supply an + `io.netty.handler.ssl.SslContext` instance for `:ssl-context`, you have to set it up with ALPN + support for HTTP/2, as well. See `aleph.netty/ssl-client-context` and + `aleph.netty/application-protocol-config` for more details. If you supply an SSL options map + without an `:application-protocol-config` key instead, the necessary ALPN configuration will be + set up automatically. (You can also set `force-h2c?` to force HTTP/2 cleartext, but this is + strongly discouraged.) + Param key | Description | --- | --- | `connections-per-host` | the maximum number of simultaneous connections to any host From 774fa17672b601e03b4a418c251f2251ff669a9c Mon Sep 17 00:00:00 2001 From: Moritz Heidkamp Date: Thu, 13 Jun 2024 12:32:49 +0200 Subject: [PATCH 2/6] Document that ALPN config must match `http-versions` in `http/connection-pool` --- src/aleph/http.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aleph/http.clj b/src/aleph/http.clj index 41f9798c..016ab6c2 100644 --- a/src/aleph/http.clj +++ b/src/aleph/http.clj @@ -158,7 +158,7 @@ Param key | Description | --- | --- - | `ssl-context` | an `io.netty.handler.ssl.SslContext` object or a map of SSL context options (see `aleph.netty/ssl-client-context` for more details), only required if a custom context is required + | `ssl-context` | an `io.netty.handler.ssl.SslContext` object or a map of SSL context options (see `aleph.netty/ssl-client-context` for more details), only required if a custom context is desired. When passing an `io.netty.handler.ssl.SslContext` object, it must have an ALPN config matching the `http-versions` option (see also `aleph.netty/application-protocol-config`). | `ssl-endpoint-id-alg` | the name of the algorithm to use for SSL endpoint identification (see https://docs.oracle.com/en/java/javase/17/docs/specs/security/standard-names.html#endpoint-identification-algorithms), defaults to \"HTTPS\". Only used for HTTPS connections. Pass `nil` to disable endpoint identification. | `local-address` | an optional `java.net.SocketAddress` describing which local interface should be used | `bootstrap-transform` | a function that takes an `io.netty.bootstrap.Bootstrap` object and modifies it. From 20b9e142ea7ae63800795fc5b53198fca300c874 Mon Sep 17 00:00:00 2001 From: Moritz Heidkamp Date: Fri, 14 Jun 2024 15:11:56 +0200 Subject: [PATCH 3/6] Document and test default ALPN failure behavior config --- src/aleph/netty.clj | 11 ++++++++++- test/aleph/http_test.clj | 21 +++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/aleph/netty.clj b/src/aleph/netty.clj index 73fc1b6e..c7304575 100644 --- a/src/aleph/netty.clj +++ b/src/aleph/netty.clj @@ -938,7 +938,16 @@ "Creates a default config for Application-Layer Protocol Negotiation (ALPN), which TLS uses to negotiate which HTTP version to use during the handshake. - Takes a vector of HTTP versions, in order of preference. E.g., `[:http2 :http1]`" + Takes a vector of HTTP versions, in order of preference. E.g., `[:http2 :http1]` + + Note that the returned config uses `SelectorFailureBehavior.NO_ADVERTISE`[1] and + `SelectedListenerFailureBehavior.ACCEPT`[2] since these are the only failure behaviors + supported by all SSL providers. See their documentation for details. One important + consequence of this is that it's not possible to completely opt out of HTTP/1.1 by way of + only specifying `[:http2]`. + + 1: https://netty.io/4.1/api/io/netty/handler/ssl/ApplicationProtocolConfig.SelectorFailureBehavior.html#NO_ADVERTISE + 2: https://netty.io/4.1/api/io/netty/handler/ssl/ApplicationProtocolConfig.SelectedListenerFailureBehavior.html#ACCEPT" ^ApplicationProtocolConfig [protocols] (ApplicationProtocolConfig. diff --git a/test/aleph/http_test.clj b/test/aleph/http_test.clj index 17afb727..309fceca 100644 --- a/test/aleph/http_test.clj +++ b/test/aleph/http_test.clj @@ -1619,6 +1619,27 @@ (is (instance? IllegalArgumentException result)) (is (= "force-h2c? may only be true when HTTP/2 is enabled." (ex-message result)))))))) +(deftest http2-only-client-connecting-to-http1-only-server + (testing "No ALPN config, desiring only HTTP/2 but the server only allows HTTP/1" + (with-http1-server echo-handler http1-ssl-server-options + (with-redefs [*use-tls-requests* true] + (let [result (try-request-with-pool + {:connection-options + {:http-versions [:http2] + :ssl-context test-ssl/client-ssl-context-opts}})] + (is (= :success result) "succeeds due to the default failure behaviors (see docstring of `application-protocol-config`)")))))) + + +(deftest http1-only-client-connecting-to-http2-only-server + (testing "No ALPN config, desiring only HTTP/1.1 but the server only allows HTTP/2" + (with-http2-server echo-handler {} + (with-redefs [*use-tls-requests* true] + (let [result (try-request-with-pool + {:connection-options + {:http-versions [:http1] + :ssl-context test-ssl/client-ssl-context-opts}})] + (is (= :success result) "succeeds due to the default failure behaviors (see docstring of `application-protocol-config`)")))))) + (deftest test-in-flight-request-cancellation (let [conn-established (promise) From 7b562ae308b6647743bd5c24bd4db9ff38bc9403 Mon Sep 17 00:00:00 2001 From: Moritz Heidkamp Date: Tue, 18 Jun 2024 16:29:52 +0200 Subject: [PATCH 4/6] Harmonize `http/connection-pool` and `http/start-server` docstrings wrt ALPN --- src/aleph/http.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/aleph/http.clj b/src/aleph/http.clj index 016ab6c2..c3c9ef51 100644 --- a/src/aleph/http.clj +++ b/src/aleph/http.clj @@ -52,7 +52,7 @@ | `socket-address` | A `java.net.SocketAddress` specifying both the port and interface to bind to. | | `bootstrap-transform` | A function that takes an `io.netty.bootstrap.ServerBootstrap` object, which represents the server, and modifies it. | | `http-versions` | An optional vector of allowable HTTP versions to negotiate via ALPN, in preference order. Defaults to `[:http1]`. | - | `ssl-context` | An `io.netty.handler.ssl.SslContext` object or a map of SSL context options (see `aleph.netty/ssl-server-context` for more details) if an SSL connection is desired | + | `ssl-context` | An `io.netty.handler.ssl.SslContext` object or a map of SSL context options (see `aleph.netty/ssl-server-context` for more details) if an SSL connection is desired. When passing an `io.netty.handler.ssl.SslContext` object, it must have an ALPN config matching the `http-versions` option (see `aleph.netty/ssl-server-context` and `aleph.netty/application-protocol-config`). | `manual-ssl?` | Set to `true` to indicate that SSL is active, but the caller is managing it (this implies `:ssl-context` is nil). For example, this can be used if you want to use configure SNI (perhaps in `:pipeline-transform` ) to select the SSL context based on the client's indicated host name. | | `executor` | A `java.util.concurrent.Executor` which is used to handle individual requests. To avoid this indirection you may specify `:none`, but in this case extreme care must be taken to avoid blocking operations on the handler's thread. | | `shutdown-executor?` | If `true`, the executor will be shut down when `.close()` is called on the server, defaults to `true` . | @@ -158,7 +158,7 @@ Param key | Description | --- | --- - | `ssl-context` | an `io.netty.handler.ssl.SslContext` object or a map of SSL context options (see `aleph.netty/ssl-client-context` for more details), only required if a custom context is desired. When passing an `io.netty.handler.ssl.SslContext` object, it must have an ALPN config matching the `http-versions` option (see also `aleph.netty/application-protocol-config`). + | `ssl-context` | an `io.netty.handler.ssl.SslContext` object or a map of SSL context options (see `aleph.netty/ssl-client-context` for more details), only required if a custom context is desired. When passing an `io.netty.handler.ssl.SslContext` object, it must have an ALPN config matching the `http-versions` option (see `aleph.netty/ssl-client-context` and `aleph.netty/application-protocol-config`). | `ssl-endpoint-id-alg` | the name of the algorithm to use for SSL endpoint identification (see https://docs.oracle.com/en/java/javase/17/docs/specs/security/standard-names.html#endpoint-identification-algorithms), defaults to \"HTTPS\". Only used for HTTPS connections. Pass `nil` to disable endpoint identification. | `local-address` | an optional `java.net.SocketAddress` describing which local interface should be used | `bootstrap-transform` | a function that takes an `io.netty.bootstrap.Bootstrap` object and modifies it. From 439214b813304de5de6d32de29d2e649e54665bd Mon Sep 17 00:00:00 2001 From: Moritz Heidkamp Date: Tue, 18 Jun 2024 16:39:06 +0200 Subject: [PATCH 5/6] Add hint on how to opt out of HTTP/1.1 to `application-protocol-config` --- src/aleph/netty.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/aleph/netty.clj b/src/aleph/netty.clj index c7304575..49336df4 100644 --- a/src/aleph/netty.clj +++ b/src/aleph/netty.clj @@ -944,7 +944,8 @@ `SelectedListenerFailureBehavior.ACCEPT`[2] since these are the only failure behaviors supported by all SSL providers. See their documentation for details. One important consequence of this is that it's not possible to completely opt out of HTTP/1.1 by way of - only specifying `[:http2]`. + only specifying `[:http2]`. If you want this behavior and you know your SSL provider supports + it, you can construct an `ApplicationProtocolConfig` with the desired settings yourself. 1: https://netty.io/4.1/api/io/netty/handler/ssl/ApplicationProtocolConfig.SelectorFailureBehavior.html#NO_ADVERTISE 2: https://netty.io/4.1/api/io/netty/handler/ssl/ApplicationProtocolConfig.SelectedListenerFailureBehavior.html#ACCEPT" From e07c3a044b13afec3afdc8ce278c6cdc65ae1f64 Mon Sep 17 00:00:00 2001 From: Moritz Heidkamp Date: Thu, 20 Jun 2024 15:14:17 +0200 Subject: [PATCH 6/6] Mention that ALPN config is optional if only HTTP/1.1 is desired --- src/aleph/http.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/aleph/http.clj b/src/aleph/http.clj index c3c9ef51..68a35da4 100644 --- a/src/aleph/http.clj +++ b/src/aleph/http.clj @@ -52,7 +52,7 @@ | `socket-address` | A `java.net.SocketAddress` specifying both the port and interface to bind to. | | `bootstrap-transform` | A function that takes an `io.netty.bootstrap.ServerBootstrap` object, which represents the server, and modifies it. | | `http-versions` | An optional vector of allowable HTTP versions to negotiate via ALPN, in preference order. Defaults to `[:http1]`. | - | `ssl-context` | An `io.netty.handler.ssl.SslContext` object or a map of SSL context options (see `aleph.netty/ssl-server-context` for more details) if an SSL connection is desired. When passing an `io.netty.handler.ssl.SslContext` object, it must have an ALPN config matching the `http-versions` option (see `aleph.netty/ssl-server-context` and `aleph.netty/application-protocol-config`). + | `ssl-context` | An `io.netty.handler.ssl.SslContext` object or a map of SSL context options (see `aleph.netty/ssl-server-context` for more details) if an SSL connection is desired. When passing an `io.netty.handler.ssl.SslContext` object, it must have an ALPN config matching the `http-versions` option (see `aleph.netty/ssl-server-context` and `aleph.netty/application-protocol-config`). If only HTTP/1.1 is desired, ALPN config is optional. | `manual-ssl?` | Set to `true` to indicate that SSL is active, but the caller is managing it (this implies `:ssl-context` is nil). For example, this can be used if you want to use configure SNI (perhaps in `:pipeline-transform` ) to select the SSL context based on the client's indicated host name. | | `executor` | A `java.util.concurrent.Executor` which is used to handle individual requests. To avoid this indirection you may specify `:none`, but in this case extreme care must be taken to avoid blocking operations on the handler's thread. | | `shutdown-executor?` | If `true`, the executor will be shut down when `.close()` is called on the server, defaults to `true` . | @@ -158,7 +158,7 @@ Param key | Description | --- | --- - | `ssl-context` | an `io.netty.handler.ssl.SslContext` object or a map of SSL context options (see `aleph.netty/ssl-client-context` for more details), only required if a custom context is desired. When passing an `io.netty.handler.ssl.SslContext` object, it must have an ALPN config matching the `http-versions` option (see `aleph.netty/ssl-client-context` and `aleph.netty/application-protocol-config`). + | `ssl-context` | an `io.netty.handler.ssl.SslContext` object or a map of SSL context options (see `aleph.netty/ssl-client-context` for more details), only required if a custom context is desired. When passing an `io.netty.handler.ssl.SslContext` object, it must have an ALPN config matching the `http-versions` option (see `aleph.netty/ssl-client-context` and `aleph.netty/application-protocol-config`). If only HTTP/1.1 is desired, ALPN config is optional. | `ssl-endpoint-id-alg` | the name of the algorithm to use for SSL endpoint identification (see https://docs.oracle.com/en/java/javase/17/docs/specs/security/standard-names.html#endpoint-identification-algorithms), defaults to \"HTTPS\". Only used for HTTPS connections. Pass `nil` to disable endpoint identification. | `local-address` | an optional `java.net.SocketAddress` describing which local interface should be used | `bootstrap-transform` | a function that takes an `io.netty.bootstrap.Bootstrap` object and modifies it.