Skip to content

Improve docs for http/connection-pool's HTTP/2 support and ALPN config #729

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jun 20, 2024
12 changes: 10 additions & 2 deletions src/aleph/http.clj
Original file line number Diff line number Diff line change
Expand Up @@ -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`). 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` . |
Expand Down Expand Up @@ -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
Expand All @@ -150,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 `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.
Expand Down
12 changes: 11 additions & 1 deletion src/aleph/netty.clj
Original file line number Diff line number Diff line change
Expand Up @@ -938,7 +938,17 @@
"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]`. 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"
^ApplicationProtocolConfig
[protocols]
(ApplicationProtocolConfig.
Expand Down
21 changes: 21 additions & 0 deletions test/aleph/http_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down