Skip to content
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

http3: alternative API for stream hijacking #4405

Open
marten-seemann opened this issue Apr 2, 2024 · 4 comments · May be fixed by #4490
Open

http3: alternative API for stream hijacking #4405

marten-seemann opened this issue Apr 2, 2024 · 4 comments · May be fixed by #4490
Milestone

Comments

@marten-seemann
Copy link
Member

This is just a quick thought I had, and might not work at all. Instead of adding stream hijacker callbacks (both for unidirectional and bidirectional) streams, we could introduce an Server.ServeStream method.

The WebTransport layer would then be responsible for spawning a QUIC listener, accepting a QUIC connection, and accepting streams from that connection. It would then decide where a stream belongs (i.e. is it an HTTP/3 stream or a WebTransport stream?) and then pass HTTP/3 streams to the Server.
Analogously for the RoundTripper.

Open question: How do we send SETTINGS, and how do we handle the peer's SETTINGS and QPACK unidirectional streams?

@marten-seemann
Copy link
Member Author

Alternatively, we could have the http3 layer wrap a quic.Connection: It would grab all streams that are actual HTTP/3 streams (as identified by the signalling frame / unidirectional stream type). All other streams could then be at the upper layer via the quic.Connection interface.

Not sure what to do with datagrams, since the HTTP layer takes total control of both receiving and sending. This is less of a problem for ReceiveDatagram (would never return anything), but more so for SendDatagram. We could define a smaller interface (http3.Connection?), or just add panics to these functions.

@marten-seemann marten-seemann added this to the v0.43 milestone Apr 3, 2024
@marten-seemann
Copy link
Member Author

Yet another alternative, and what I believe will work best: We'll have the WebTransport layer wrap the quic.Connection, and grab all the streams that are WebTransport streams. We then pass this connection to the HTTP/3 layer, which handles all remaining streams. This allows us to get rid of the StreamHijacker and the UniStreamHijacker, both on the http3.Server and the http3.RoundTripper. We can also remove the StreamCreator interface, as well as the FrameType. Big API cleanup!

For the server we don't need to anything, since we already have a ServeQUICConn:

func (s *Server) ServeQUICConn(conn quic.Connection) error {

For the client, we'll introduce a new struct, the SingleOriginRoundTripper. It behaves like a normal RoundTripper, but it only handles a single QUIC connection. It exposes a OpenHTTPStream(context.Context) (HTTPStream, error) method, that, with #4409, is used to send HTTP requests.
It also exposes a function to get access to the server's SETTINGS. This mean we can also remove the RoundTripOpt.CheckSettings that we introduced in #4355.

Servers still need to be able to hijack the Extended CONNECT request, so we won't be able to remove all hijacker. However, all they need is the ability to take over the stream. They don't need to be able to access the quic.Connection from there.


Support HTTP Datagrams (#3522) will be very straightforward from here:

  • On the server side, the hijacked stream will expose functions to send and receive datagrams.
  • On the client side, the same applies to the HTTPStream returned from OpenHTTPStream.

@marten-seemann marten-seemann changed the title http3: alternative API idea for stream hijacking http3: alternative API for stream hijacking Apr 4, 2024
@marten-seemann
Copy link
Member Author

marten-seemann commented Apr 11, 2024

After playing around with this proposal for a while now, I concluded that it doesn't really work. It's just too complicated to pass the connection back and forth between the WebTransport and the HTTP/3 layer.

However, there's another thing we can do. We can simplify these callback by making them less general: WebTransport uses stream types for unidirectional streams, and signal values for bidirectional streams:

http3.Server{
     SignalValues map[uint64]func(quic.Stream)
     UniStreamType map[uint64]func(quic.ReceiveStream)
}

This is a significant simplification over the current callbacks: Fewer function parameters, and once called, the stream is WebTransport's responsibility (there's no way for the WebTransport layer to give the stream back to HTTP/3).

Open question: do we still need to pass the connection (or the connection tracing ID) to these callbacks?

@marten-seemann
Copy link
Member Author

Open question: do we still need to pass the connection (or the connection tracing ID) to these callbacks?

Probably yes. The ReceiveStream unfortunately doesn't have a context, so we can't just have the application retrieve the tracing ID from there.

@marten-seemann marten-seemann linked a pull request May 5, 2024 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
1 participant