How to allow both http1 and http2 requests in hyper? - rust

I'm building a proxy service in rust with hyper and I want to support both HTTP1 and HTTP2 requests. If I send a request where request.version() == "HTTP/2.0".to_owned() I get the warning
WARN hyper::client::client "Connection is HTTP/1, but request requires HTTP/2"
followed by the error
request has unsupported HTTP version
If I build my hyper::Client with client_builder.http2_only(true) I can send http2 requests, but that method clearly states that I won't be able to send http1 requests, which I still want to support. The documentation and the code in hyper::client::Builder seems to imply that it supports HTTP1 and HTTP2 by default (if the http2 feature is enabled) by it's client_config.ver field is either Ver::Auto or Ver::Http2.
I dug deeper into the code that gave the above warning and I see that it's checking if the pooled connection is HTTP1 and erroring if the request is also HTTP2. I'm seeing no way to make that connection HTTP2 short of forcing everything to be HTTP2 with only_http2.
Is it really impossible for a hyper::client::Client to support sending http2 and http1 requests? Must I maintain two separate clients and branch on the requests version?

Related

Can a server using http1.1 protocol be able to serve a request coming from browser using http2 protocol

For example I have a node server which uses the built-in module "http" (which is http1.1 version I assume). So will it be able to serve any XHR request from browser with http2 protocol?
Yes. Browsers negotiate HTTP/2 as part of the HTTPS setup (since browsers only support HTTP/2 over HTTPS). If HTTP/2 is not supported by both sides it will just use HTTP/1.1
Thanks one of the great things about HTTP/2 and why it’s made such headway - the ability to (almost!) seamlessly roll it out to the web without breaking existing sites/servers/browsers.

NodeJS - Determine if a target website uses HTTP/2

I'm using NodeJS and trying to make a simple server request. I'd like to determine if the server is HTTP/2 or not. If it is, I'd want to use the 'http2' module to make the request. Otherwise, I'll fall back to the 'http' / 'https' modules to make the request.
With NodeJS, how can I determine if a target remote server uses HTTP/2?
TIA
The general idea is that if you want to use http2 (if available), then you make your request from node.js using the http2 module. As part of that connection and part of the SSL/TLS negotiation, the two sides will exchange whether http2 can be used or not (via an extension called ALPN). If both sides agree on http2, then it will be used. If not, it will fall back to regular https.
Some relevant references:
How to check if website has http/2 protocol support
ALPN negotiation using node.js http2 library
If you're curious, this website: https://tools.keycdn.com/http2-test will tell you if any site supports HTTP/2 and the ALPN extension.

Is SSL secure connection available without browser call?

I have a question about SSL. As I know, when we use browser to request from https server, it will make an SSL handshake first then all data will be encryption in the connection. But if I make a request without browser (like request module in nodejs, postman...), will it be an SSL handshake and data encryption on the connection?
Anyone know please explain to me, thank you.
First, stop saying SSL. Its successor is TLS, and it will have 20 years next January.
TLS is a protocol sitting on top of TCP typically (other variants can also use UDP), and provides on top of TCP features some new features about endpoints authentication and transport confidentiality and integrity.
In a way, you can understand it as being sandwiched between TCP and the higher level application protocol, like HTTP.
Saying otherwise you can use many others protocols on top of TLS: you have all email related ones (SMTP, IMAP, POP, etc.), you can have FTP on top of it (while probably not a good idea nowadays), XMPP for realtime communications, etc.
In short, any protocol using TCP could use TLS with some adaptation.
So HTTP is one case among others. HTTP is between an HTTP client and an HTTP server, or webserver for short.
A browser is an HTTP client. One among many ones. When you use curl or wget you are also an HTTP client. So if any HTTP client access an http:// link it will first do the TLS handshake, after the TCP connection and before starting to do anything really related to the HTTP protocol.
You have specialized libraries dealing with TLS so that not all program need to recode everything about this again, since it is also complicated.

HTTP2 - How to have WebSocket-like functionality (Keep-Alive, EventSource, etc.)

Wondering how to setup a persistent connection between client and server in Node.js.
The persistent connection should be able to send requests back and forth from both sides. It seems like it needs to have a mechanism for delineating each request, such as \r\n if requests are JSON, but not sure what best-practices are here if it's already been done before. Wondering how web-sockets handle this.
Mozilla says Keep-Alive shouldn't be used in production, so wondering if that still holds with HTTP2.
With EventSource you can receive server events, but wondering if there is a way to add client events to send to the server.
In the end, I would like to have a simple setup for two-way communication like WebSockets, but am not sure of best practices in HTTP2 and if it should just be done with WebSockets. I would prefer to try to do this without websockets.
https://daniel.haxx.se/blog/2016/06/15/no-websockets-over-http2/
With all due respect, I believe you're under the mistaken assumption that the HTTP/2 connection is page specific.
In actuality, HTTP/2 connection are often browser wide connections. It's not possible for an HTTP/2 push to know which page/tab will use the data (possibly more than one).
Also, see the discussion here that includes a myriad number of reasons as to why HTTP/2 couldn't (or shouldn't) be used as a Websocket alternative.
On the other hand, if HTTP/2 is "promised" and Websockets are unavailable, than polling might be an option.
Polling with HTTP/2 will be significantly more resource friendly than polling with HTTP/1.1, even though it would still be far more expensive than Websocket push (or poll) due to the extra header and authentication data (it will also decrease security, but that's probably something nobody really cares about all that much).
P.S.
Websockets over HTTP/2
This is a non-issue unless HTTP/1.1 is retired (in fact, IMHO, this is intentional and good).
It's true that HTTP/2 connections can't be "upgraded" (changed) to Websocket connections, nor can they tunnel Websocket data, but that means absolutely nothing.
New Websocket connections use the existing HTTP/1.1 upgrade handshake and that's it.
Any HTTP/2 server that supports Websockets will support the handshake.
Keep-Alive
Mozilla warns against the non-standard Keep-Alive header. However, the Connection: keep-alive standard for HTTP/1.1 (which is also the default for HTTP/1.1 clients) is definitely recommended.
SSE (Server Sent Events)
SSE pre-date Websockets and they didn't get much traction in the community, while Websockets were adopted with a vengeance.
wondering if there is a way to add client events to send to the server
Simple - send an HTTP request. The reply can be asynchronously received with SSE.
I like Websockets better. As far as server-side goes, I find them easier to manage and code.

Nodejs http2 behind (nginx or otherwise Webserver) with Push in Nodejs

I am trying to update our online shop to use HTTP/2 with Server Push capabilities but I can't find a solution for a webserver like Nginx(for proxying and some other stuff) with upstream HTTP/2. We are using Node.js with the node HTTP module at the moment, but would like to switch to the node spdy module. The spdy module supports HTTP/2 with Server Push. I have tried H2O as an alternative to Nginx, but it doesn't support HTTP/2 upstream either.
I am kind of lost at the moment and need help.
Nginx has only just added support for HTTP/2 Push so unless you are rubbing the latest mainline version you will not be able to do this. Also because it is so new there are still some issues with it. Nginx do not support http2 over backend connections (and have stated they won’t support this). So you cannot directly push from a downstream system all the way up like you suggest.
There is some question as to whether that is the best way to push anyway. A downstream system may push to the upstream proxy server even if the client does not support push for example - which is a wasted push.
So the better way is to push from the proxy and have the downstream system tell the upstream system (via link headers) to do that push. This has several advantages in reducing complexity, allowing the downstream system to push assets it may not control (e.g. static assets like stylesheets, JavaScript, images... etc.), a central store of already pushed assets (cache digests) and also not requiring HTTP/2 to be supported all the way through (link headers can be sent over HTTP/1.1 as easily as HTTP/2).
The main downside to pushing from the upstream proxy via link headers is that you have to wait for the requested resource to be ready as the link headers are read from the response. If the request resource takes some time to generate then it may be more beneficial to start pushing other resources while it’s being processed. This is solved by a new 103 Early Hints HTTP Status code where you can reply back earlier before sending the main 200-status code later. This early message can have link headers which can be read by the upstream proxy and be used to push the resource. I am not sure if Nginx implementation will support this.
Incidentally Apache has supported Push for a while now and has a much more mature implementation. It supports it via direct Apache config or via link headers (including via 103 responses which by default are configured not to be sent on in case on compatibility issues). It even supports proxying to backends via HTTP/2 though does not support direct push over back end connections for reasons described above. Some other less well known servers (e.g. H2O) also support HTTP/2 better than Nginx.
Finally if using a CDN then they may support HTTP/2 Push (often via link headers) without you having to upgrade any of your backend infrastructure. In fact Cloudflare is an Nginx based CDN which has had HTTP/2 Push for a while and I fact it’s two Cloudflare engineers which have back ported their implementation to the base Nginx code.
After NGINX 1.13.9 (just pushed to mainline today) you're able to have HTTP/2 server push out of the box by compiling it with the ngx_http_v2_module.
If you're interested in the recent addition, this is the commit that added most of the functionality: hg.nginx.org: HTTP/2: server push.
Its use is relatively straightforward: add the http2_push_preload directive to the server that is proxying Node and then from node make use of the Link header (as described in the W3 spec - https://www.w3.org/TR/preload/#server-push-http-2) and then NGINX will do the job of sending the h2 frame that indicates a server push.
For instance, assume that you have a / endpoint that serves a regular index.html but also pushes image.svg to the client.
In NGINX you could configure an upstream server and then in the server configuration enable http2_push_preload on the server configuration:
# Add an upstream server to proxy requests to.
upstream sample-http1 {
server localhost:8080;
}
server {
# Listen on port 8443 with http2 support on.
listen 8443 http2;
# Enable TLS such that we can have proper HTTP2
# support using browsers.
ssl on;
ssl_certificate certs/cert_example.com.pem;
ssl_certificate_key certs/key_example.com.pem;
# Enable support for using `Link` headers to indicate
# origin server push.
http2_push_preload on;
# Act as a reverse proxy for requests going to /proxy/*.
#
# Because we don't want to rewrite our endpoints in the
# Node app, rewrite the path such that `/proxy/lol` ends up
# as `/lol`.
location / {
proxy_pass http://sample-http1;
}
}
Then in the NodeJS app, you'd serve / as you'd normally do but add an extra Link header to the response:
response.setHeader('Link', '</image.svg>; rel=preload; as=image');
ps.: yeah, you'd keep those angle brackets; I do not mean that you should replace them.
By the way, the example I just gave (with some debugging tips) is written up in complete here: https://ops.tips/blog/nginx-http2-server-push/ .
You can compile/recompile nginx from source and include the --with-http_v2_module configuration parameter to enable HTTP2 push capabilities.

Resources