On 5/4/2015 8:11 AM, Daniel Stenberg wrote:
Anyone around who has been thinking about using HTTP/2 and server push in an application? Server push means that a new stream is initiated from the server without the client specifically asking for it. Typically it would be resource B that the server thinks the client wants since it asked for resource A. Like in a web scenario it could be the CSS if the client asked for the html.

A client can of course deny it (or disallow it completely in the first place), but how would you like the API for libcurl to expose this functionality?

Method 1:

I started out thinking we should have an option that allows us to setup an easy handle and add it to the multi handle just idling until the server (might) do a push. This is rather inflexible and forces the application to know before-hand how many pushed streams it'd like.

Method 2:

We would (somehow) dynamically create a new easy handle and stream for a pushed resource and allow the application to deny it or to receive it like an ordinary transfer. The creation of the easy handle would probably then have to be presented to the application with a callback or something and the application would then have to setup options etc for the transfer (like write callback etc). We could also imagine the application being allowed to cancel such a request.

Method 3:

I'm sure we can think of other ways. What do you think?


1 by itself seems impractical, if not now then in the future. The advent of push and multiplexing could lead to a lot of resources being pushed. Resources that were once inline are no more; would-be same-origin resources that were once sparse on several servers are no more. The flexibility to handle push and stream priority/dependency will be crucial.

If I was contributing code to help implement this (it's not likely, sorry) I'd model to the maxim you don't pay for what you don't use. I'd disable server push by default and I'd expose SETTINGS_MAX_CONCURRENT_STREAMS for when it is enabled. I'm not saying you must know how many streams or only beforehand as in 1.

I'm unclear on whether push stream DATA is sent immediately following the push of (promise) headers to the client or whether the client needs to give some explicit approval first. I'm guessing the former because of latency but really I don't know. The spec in section 8.2.2 Push Responses [1] says "Once a client receives a PUSH_PROMISE frame and chooses to accept the pushed response,...". Does that mean approval required? In other words do clients have to signal the server to start a push stream's DATA or does it start on its own?

If push data starts on its own (and assuming that's not preventable via flow control setting) you'd need internally at least an object for each stream. Again that may sound like 1 but it's not, at this point it's abstract to the user. Internal stream objects (buffers basically) are automatically created and receiving data. Then while all that is happening call the user's callback like you say in 2 as a push-notify and offer a helper function to transition to an easy handle:

CURLPUSHcode push_cb(curl_http2_stream *foo) {
// foo was pushed
if(find(foo->resource, accepted_urls) && check_headerorsomething(foo)) {
CURL *bar = curl_stream_to_easy(foo, INHERIT_FROM_PARENT);
// bar is created and is in the same multi as foo's parent
set_my_options(bar);
// user sets options for bar (some of which will be ignored
// curl_easy_init does nothing here. returning "starts" the transfer which has actually already started but the application will process it as just started
return CURLPUSH_ACCEPT; // again don't know about this
}
return CURLPUSH_REJECT; // don't know about this either..set some error code to pass to server for the reject?
}

I don't like it now that I see it written out. It's an easy handle but it can't use many of the options of an easy handle. How about a new handle type curl_push_handle and make the easy handle options that do work backwards compatible so users can use their existing set_options functions.

CURLPUSHcode push_cb(curl_push_stream *foo) {
if(!check(foo)) {
return CURLPUSH_REJECT; //?
}
set_my_options((CURL *)bar); //or maybe set_my_push_options(bar)
curl_push_perform(bar); // this is non-blocking
// now the application's callbacks set on bar are called as if the transfer just started
return CURLPUSH_ACCEPT; //? don't stop receiving data
}

I'm rambling now I suppose but maybe something here is beneficial.


[1]: https://http2.github.io/http2-spec/#PushResponses

-------------------------------------------------------------------
List admin: http://cool.haxx.se/list/listinfo/curl-library
Etiquette:  http://curl.haxx.se/mail/etiquette.html

Reply via email to