x/net/http2: support consuming PUSH_PROMISE streams in the client #18594

Open
glasser opened this Issue Jan 10, 2017 · 3 comments

Projects

None yet

3 participants

@glasser
Contributor
glasser commented Jan 10, 2017

Go 1.8 will contain support for servers to produce PUSH_PROMISE streams, but there is no support for clients to receive them.

Specifically, it would be nice if proxies built with net/http/httputil's ReverseProxy to be able to proxy server pushes.

@tombergan tombergan added this to the Go1.9Maybe milestone Jan 10, 2017
@bradfitz
Member

Let's ignore ReverseProxy for the purpose of this bug. Although, ReverseProxy being in std would require that new API be added to net/http.Client.

I think more immediately we'd want to add experimental, opt-in API only to golang.org/x/net/http2.Transport. (Unless opted-in, the Transport would continue to advertise SETTINGS_ENABLE_PUSH == 0)

Got any API proposals?

@glasser
Contributor
glasser commented Jan 10, 2017

I admit I'm not an http2 expert. I maintain a proxy based on ReverseProxy and "proxy http2 server pushes" is a feature request I got from a user.

My understanding is that receiving a push is effectively similar to receiving a request and a response from a server. So perhaps there's a new field on http2.Transport which is a ReceivePusher, where

type ReceivePusher interface {
  ReceivePush(*http.Request, *http.Response) error
}

If the field is non-nil, then the Transport advertises SETTINGS_ENABLE_PUSH == 1, and upon receiving a PUSH_PROMISE frame, the Transport creates a Request and a Response that match the PUSH_PROMISE and passes it to the ReceivePusher. Just like with RoundTrip, it is the responsibility of ReceivePush to read from and Close the response's Body.

I'm not sure what is the best way to indicate "I don't want this push" (ie send a RST_STREAM I think?) — maybe any non-nil error return value? Maybe a second return value other than the error? Some other interface passed in that it can invoke? Some method on the Transport?

My understanding is that these pushes can be received at any time during the connection: concurrently with RoundTrip or after it. Not really sure how that should affect the API.

@tombergan
Contributor

It seems right to translate each PUSH_PROMISE into an http.Request that has no request body. A common thing in browsers is to inspect the PUSH_PROMISE, check if it's already cached, and if so, cancel the push with RST_STREAM(CANCEL). Whatever the API looks like, it should allow canceling the pushed stream before any bytes of the response have been received.

A second point is that every PUSH_PROMISE must be associated with a client request. This is required by RFC 7540. It may be useful to communicate that associated request to the client's push handler.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment