I've been using http2 example as reference and
(def http2-ssl-ctx
"This creates a self-signed certificate SslContext that supports both HTTP/2
and HTTP/1.1. The protocol array lists which protocols are acceptable, and
the order indicates the preference; in this example, HTTP/2 is preferred over
HTTP/1 if the client supports both.
NB: Avoid self-signed certs unless you control the clients, too. Browsers
will not accept them. See aleph.netty/ssl-server-context."
(netty/self-signed-ssl-context
"localhost"
{:application-protocol-config (netty/application-protocol-config [:http2 :http1])}))
(http/start-server #'api-v1 {:port port
:http-versions [:http2 :http1]
:ssl-context http2-ssl-ctx})
Kinda works for (from browser i get odd certificate warning but that's to be expected)
But when i try to do i expected http/1 to be served and yet i get The connection was reset and on the backend
Sep 10, 2024 3:36:34 PM io.netty.handler.ssl.ApplicationProtocolNegotiationHandler handshakeFailure
WARNING: [id: 0xad2fa4cd, L:0.0.0.0/0.0.0.0:8080] TLS handshake failed:
io.netty.handler.ssl.NotSslRecordException: not an SSL/TLS record:
Is this to be expected?Looks like you are trying to do https and http on the same port, which I've never seen done before (http is typically port 80, https is 443, but you are overriding the port in your urls to be 8080 for both)
Neither http or https in the URL have anything to do with http1 vs http2, so I am not sure why changing that in the URL would change how you expect the connection to happen?
So there is skill gap here i think. I've just followed example without much deeper thought. I can't have my connection on 433 address is already in use. What i expected to have is http/2 as optional thing that clients may use if they want to but still use http/1 as primary way of connecting to the backend.
Sure, what you have there likely speaks both http1 and http2, it just expects it over tls
Https != Http2
Is there a way for me to force http/1 connection via browser/curl/postman? to make sure what you said is the case (both are served)
There for sure is a curl command line option, check the man page
It's quite possible i don't know even how to ask the question... in order to get response i ignored certificate
curl --http1.1 -v -k
curl --http2 -v -k
This allowed me to verify that both are supported on the port i specified which is nice. ✅
Reason why i was mentioning https is because http2 requires certificate to work with browsers and in production environment i can't be using self signed ssl-context.
To make matter little more complicated we are using nginx for reverse proxy and i am not sure and and what kind of config needs to be set in order for nginx to pass http/2 requests to the webserver.Which certificates should be used in prod environment? Is there a way to setup automatic certificate refresh with open ssl?
I think i am basically looking for advice on how to use http/2 in production environment
I am pretty sure that the most common way to deploy http2 is to terminate it at the load balancer and have the loader balancer just speak http1 to the backend
Note that it's also possible to use HTTP/2 over a non-TLS TCP connection (exposed in Aleph via the use-h2c? server option). But I would also recommend the approach suggested by @hiredman
Thank you everyone for the context. This proved to be super helpful!
> Is there a way for me to force http/1 connection via browser/curl/postman? to make sure what you said is the case (both are served) Curl, yes. Browser, don't think so. Postman, they finally added it in mid-August. Generally, I've found curl to be the best bet for testing advanced HTTP features.
> What i expected to have is http/2 as optional thing that clients may use if they want to but still use http/1 as primary way of connecting to the backend.
You can do this via Application-Layer Protocol Negotiation (ALPN), where, in the process of setting up the SSL/TLS connection, the two peers advertise which protocols they support, and the client peer picks one. E.g., setting :application-protocol-config (netty/application-protocol-config [:http2 :http1]) says "I support HTTP1 and HTTP2, and prefer HTTP2". The only catch is, the server can't force the client to choose http1 over http2 while still supporting both, if that's what you want.
(NB: I don't know of a way to do that in a nonsecure way. Generally, HTTP2 doesn't support public-facing cleartext connections for security purposes. Though I'm sure somewhere out there, someone's written a server that examines the first few bytes after forming the TLS connection, and determines whether it's HTTP1 or 2.)
If you're behind a load balancer/reverse proxy, then browser --H2-> proxy --H1--> server will work just fine. Most of HTTP2's advantages are primarily for browsers over the public net, and much less important over internal networks.
That being said, there are some disadvantages. H2 traffic is pretty common these days, and the formats are very different from H1 to H2/3, so the proxy will have to convert everything in both directions. On top of which, H2 is smaller and more efficient to parse, so converting adds a bit of CPU and bandwidth overhead to both the proxy and the server. From a security perspective, downgrading makes the connection vulnerable to the union of each protocols' vulnerabilities. And H1 makes it slightly easier/more tempting to use unencrypted traffic internally. Nothing major, though.
TBH, I think H2 on the internal side would be just as good a default as H1 these days, but since nginx won't support it (despite already having a gRPC proxy, which requires H2), it's not that common.