Crystal: HTTP/2 support

Created on 8 Feb 2016  路  15Comments  路  Source: crystal-lang/crystal

This is a feature request, tag it as you will. Since the specs have been clearly defined and do not appear to waver, I feel HTTP/2 should be supported in addition to HTTP/1.x.

Resources:

  • HTTP2 Working Group - includes specs and running list of implementations in other languages
  • Ruby HTTP2 gem - Maintained by Ilya Grigorik for both client and server
feature stdlib

Most helpful comment

An initial implementation is available: https://github.com/ysbaddaden/http2

If someone could point me to great HTTP2 compliance test suites, that would be very much appreciated.

The client part is untested. It should be capable to use HTTP2::Connection, but it may require some tweaks. It's integration into HTTP::Client may be tricky, especially since we can have many requests in parallel (yay, fibers!) and the API needs a callback to handle server-push promises (accept/refuse then receive data). We can prevent server-push by default, too. We also need to configure local HTTP2 settings (frame size, max concurrent streams, etc).

The server part is almost complete, except for flow control (required) and applying priorities to send frames (nice to have). It's capable to talk with nghttp (with or without TLS) and browsers (ie. Firefox and Chrome, TLS only) and it supports server-push.

Now we should start planning to integrate this into HTTP::Server. For example:

  • [x] Add HTTP::Server.default_ssl_context that would return an OpenSSL::SSL::Context that is compliant to HTTP2 requirements and always provides state-of-the-art OpenSSL configuration; HTTP::Client could provide the same; It's now OpenSSL::SSL::Context::Server;
  • [ ] Introduce an abstraction for HTTP1 responses, to leverage the protocol differences between HTTP1 and HTTP2 so HTTP::Server::Response::Output doesn't have to deal with protocol details (ie. encoding HTTP headers, writing body to the socket or as a stream data frame);
  • [ ] Transparently support HTTP2 in HTTP::Server:

    • [ ] configure local HTTP2 settings (max frame size, concurrent streams, etc);

    • [x] handle the ALPN protocol for SSL connections;

    • [x] react to the Upgrade: h2c header for non-SSL connections (and discard Upgrade: h2);

    • [ ] handlers will deal with HTTP2::Stream objects instead of a Socket, which is similar to how we wrap the socket in HTTP::Server::DeflateHandler (which will wrap HTTP2::Stream now);

  • [ ] Add method(s) to HTTP::Server::Context to enable server-push, with a solution to actually send the response, or not, if the Client accepted or refused them 鈥攁nd either raise or silently discard if the connection ain't HTTP2.

All 15 comments

Thanks for adding this, we'll eventually need to support HTTP/2.

I'm not sure how it works, though. Will it integrate automagically with the current server? Because in Ilya Grigorik's implementation it doesn't look like it integrates with Rack. There's also this long thread which supports my guessing. And this Go talk which says that if you are using HTTPS and Go 1.6 you are using HTTP/2, so I guess it should be transparent.

Maybe @igrigorik could chime in and at least let us know if the current client/server implementation is easily upgradable to HTTP/2? :grin:

@asterite you can definitely abstract h2 behind the same API. That said, I do think you want to think carefully about the API to enable developers take advantage of h2: persistent connections, multiplexing, server push, etc. "Rack API", at least as it was defined for http/1.1, doesn't do a good job of this.

FWIW, check out the demo client/server examples in the ruby gem:
https://github.com/igrigorik/http-2/tree/master/example

@igrigorik Thank you for replying, I'll take a look at the examples and see if I can port something to Crystal.

@asterite recently changed HTTP::Server from a Rack-like interface (get a request, return a response) to instead pass a Context object with the Request and Response objects that middlewares will change, each context being evaluated in its own coroutine.

I have a feeling that HTTP::Server could handle the HTTP/2 protocol, creating a Context object for each incoming Streams, using an intermediate IO to write to the Stream instead of the Socket 鈥攍ike deflate middleware currently does. The Context object could have additional methods for server push (among other HTTP/2 features), and either raise or silently skip when HTTP/2 isn't supported by the client.

Hopefully this wouldn't affect the current middleware API but enhance it.

An initial implementation is available: https://github.com/ysbaddaden/http2

If someone could point me to great HTTP2 compliance test suites, that would be very much appreciated.

The client part is untested. It should be capable to use HTTP2::Connection, but it may require some tweaks. It's integration into HTTP::Client may be tricky, especially since we can have many requests in parallel (yay, fibers!) and the API needs a callback to handle server-push promises (accept/refuse then receive data). We can prevent server-push by default, too. We also need to configure local HTTP2 settings (frame size, max concurrent streams, etc).

The server part is almost complete, except for flow control (required) and applying priorities to send frames (nice to have). It's capable to talk with nghttp (with or without TLS) and browsers (ie. Firefox and Chrome, TLS only) and it supports server-push.

Now we should start planning to integrate this into HTTP::Server. For example:

  • [x] Add HTTP::Server.default_ssl_context that would return an OpenSSL::SSL::Context that is compliant to HTTP2 requirements and always provides state-of-the-art OpenSSL configuration; HTTP::Client could provide the same; It's now OpenSSL::SSL::Context::Server;
  • [ ] Introduce an abstraction for HTTP1 responses, to leverage the protocol differences between HTTP1 and HTTP2 so HTTP::Server::Response::Output doesn't have to deal with protocol details (ie. encoding HTTP headers, writing body to the socket or as a stream data frame);
  • [ ] Transparently support HTTP2 in HTTP::Server:

    • [ ] configure local HTTP2 settings (max frame size, concurrent streams, etc);

    • [x] handle the ALPN protocol for SSL connections;

    • [x] react to the Upgrade: h2c header for non-SSL connections (and discard Upgrade: h2);

    • [ ] handlers will deal with HTTP2::Stream objects instead of a Socket, which is similar to how we wrap the socket in HTTP::Server::DeflateHandler (which will wrap HTTP2::Stream now);

  • [ ] Add method(s) to HTTP::Server::Context to enable server-push, with a solution to actually send the response, or not, if the Client accepted or refused them 鈥攁nd either raise or silently discard if the connection ain't HTTP2.

@ysbaddaden take a look at https://github.com/summerwind/h2spec

Thank you @igrigorik! I was able to reduce a bunch of corner cases in the protocol, down to 4 failures, most related to the unimplemented flow-control.

Hey @ysbaddaden i remember you implementing HTTP/2 for Crystal. What's the status on that 馃槃

I too am _very_ interested in the speed improvements that HTTP2 offers. I didn't know it had such wide support.

@ysbaddaden should the work you're doing for http2 be moved into a crystal branch? I wouldn't mind writing a bunch of specs for it.

Up.
Any progress?

@DebugReport not that we are aware of. @samueleaton, if you are interested in contributing, I'd advise to do so in @ysbaddaden's repo, and as it's finished and tested, we can then include it into the stdlib.

As far as I can tell @ysbaddaden's http2 shard is feature-complete and passes all h2spec tests. Awesome work.

It doesn't look like there are any plans for integrating it back into Crystal though. I was planning on using it in my own project, a shard is fine, but I think it'd be nice to have in the std lib. I don't think http2 is going away.

Hello @asterite, @bcardiff, @ysbaddaden, @waj and other core-team members,

Would like to bring to your attention this issue, hoping if possible to introduce the changes required to support HTTP/2 into the stdlib.

Not necessarily suggesting introduce HTTP/2 itself to the stdlib, but perhaps tweak HTTP::Server and friends to avoid the differences currently required to support it, in that way, someone can load the http2 shard and use when required.

Given that we are still in pre-1.0, perhaps is a good time to do some final breaking changes to the related API in preparation for that.

HTTP/2 support will also open the door to gRPC, which can increase interoperability of Crystal for certain teams and projects. Also worth to mention HTTP/3 is around the corner :wink:

Thank you for your hard and continuous work both Crystal core and other contributors, every new release gets better and better and you can feel how much sweat and hard work has been pour into it. Thank you! 鉂わ笍 鉂わ笍 鉂わ笍

Cheers.

I think that HTTP needs a ground-up clean API redesign (without looking at the current API, the implementation is fine). It has collected a lot of issues which need to be collated into a requirements list for a HTTP module which is ready to be marked 1.0.

Regarding HTTP (Client/Server) we wanted a way to decouple the protocol implementation from the socket tls wrapper. That will allow simple http over unix socket and other use cases where a simple http api is needed. (And would actually remove the need for the without_openssl flag).

Was this page helpful?
0 / 5 - 0 ratings

Related issues

costajob picture costajob  路  3Comments

lbguilherme picture lbguilherme  路  3Comments

will picture will  路  3Comments

Sija picture Sija  路  3Comments

pbrusco picture pbrusco  路  3Comments