square / okhttp

Square’s meticulous HTTP client for the JVM, Android, and GraalVM.
https://square.github.io/okhttp/
Apache License 2.0
45.71k stars 9.15k forks source link

HTTP/2 Server Push (again...) #4156

Closed yschimke closed 4 years ago

yschimke commented 6 years ago

Would we consider supporting HTTP/2 server push now?

Adoption is still minuscule, and wins are not clear. https://blog.apnic.net/2018/04/26/adoption-performance-and-human-perception-of-http-2-server-push/

Mainly curious as it comes up related to DNS over HTTPS. Could be a nice optimisation there. But that is still 50/50 anyway. So just asking the question for now.

Previous https://github.com/square/okhttp/issues/3106 https://github.com/square/okhttp/issues/856 https://github.com/square/okhttp/issues/607

swankjesse commented 6 years ago

I wanna do it with fancy joining & canceling. It's difficult cause it requires some rework of our cache persistence.

Harti commented 4 years ago

@swankjesse In no way shall this be taken as offense - but have you considered working on this in the meantime? Closing feature requests to refer them here, where the last activity was in 2018, is somewhat awkward.

I suppose many devs will eventually move to Java 11's native HttpClient (supporting Server Push natively) which would be a shame, with OkHttp having reached its state-of-the-art-library status. Indeed, while you may consider Server Push a nice-to-have feature, the nonexistence of the feature is a showstopper for servers with billions of daily client requests.

yschimke commented 4 years ago

@swankjesse is it awkward because of a non user controlled request? i.e. we'd need some hook to allow clients to cancel when it gets triggered?

Or matching against the cache.

@Harti do you have specific examples you want to support from API or Web servers? I'd love to understand the typical usage.

Harti commented 4 years ago

General use cases encompass (but are not limited to):

Alas, I cannot currently offer a working example of a server-push endpoint for development and evaluation. Should that be a precondition I shall try to assist with finding a working example.

yschimke commented 4 years ago

I'm familiar with the concept. More interested how prevalent this is for usecases that OkHttp would generally also be used for. A real world use as an example to test against.

Harti commented 4 years ago

It's probably not prevalent, but for any application wanting to display any list of backend-served data where an item consists of both textual information and (an) image(s), it would serve most useful for the reasons explained above.

(Maybe I'm missing the actual question, I'm sorry)

yschimke commented 4 years ago

Yes. First, what your usage is. Second, not how useful it can be for one specific private API. More how adoption is overall in the wild.

Harti commented 4 years ago

I do not have objective measures for an expected adoption rate and it's not something I can argue for beyond this opinion. Developers would need to adjust to this on both client and backend side, which is something that will only happen over the years. Quite unlikely that many would, as the paradigm is fairly complex and unexplored (or they have been using Websockets instead). The benefit over regular HTTP/2 (or HTTP/1.1) overhead isn't really relevant to most low-/medium-scale applications. (I wonder if adoption was already higher if the feature already existed in OkHttp and was advertised through the docs? Reminds me of the chicken/egg paradox.)

I don't mean to sound entitled to this feature at all - just wanted to friendly ping you since it's something you've seemingly been wanting to do, and it's probably the most talked about feature when researching how to leverage HTTP/2. If it doesn't bother you that developers requiring such functionality might eventually move away from OkHttp and instead use Java's HttpClient (especially once Android supports JDK 11), then I agree that the general library benefit isn't big enough to warrant the implementation effort. In that case I suggest this ticket be closed as "won't do".

I must not disclose my exact usage scenario, but for what it's worth, here is the general idea:

From an OAuth-protected API, JSON data (text/json) and unknown related data of incompatible content types (e.g. image/webp) are fetched. For one, some data may not be there yet and are generated on-demand. The most crucial point however is to keep the total data usage footprint low by avoiding unnecessary outbound requests (and avoiding unnecessary inbound transfer of image URLs and the proprietary evaluation of those which makes the code extremely messy).

Instead of 100 subsequent requests (with 100 responses), 1 request should be made (with the same 100 responses, except asynchronously received, handled either with regular Callbacks or Observables/Promises) - effectively reducing traffic incurred by HTTP Headers by up to 50% (though HPACK will mitigate some of this).

swankjesse commented 4 years ago

I think we should abandon server push and instead support early hints. It's a better design that avoids a lot of the drawbacks of server push. https://tools.ietf.org/html/rfc8297

[edit: #3671]

yschimke commented 4 years ago

Push client support looks decent

https://dev.to/meyer9/http-2-server-push-explained-4gh

image

But I've only really found test servers using it. Nothing compelling to say implementing it would help more than a few specific users. So I agree, and I'll close this issue unless we decide to support.

$ okurl --frames https://http2-push.appspot.com/
>> CONNECTION 505249202a20485454502f322e300d0a0d0a534d0d0a0d0a
>> 0x00000000     6 SETTINGS
>> 0x00000000     4 WINDOW_UPDATE
<< 0x00000000    18 SETTINGS
<< 0x00000000     4 WINDOW_UPDATE
>> 0x00000000     0 SETTINGS      ACK
<< 0x00000000     0 SETTINGS      ACK
>> 0x00000003    38 HEADERS       END_STREAM|END_HEADERS
<< 0x00000003   164 PUSH_PROMISE  END_PUSH_PROMISE
<< 0x00000003    27 PUSH_PROMISE  END_PUSH_PROMISE
>> 0x00000002     4 RST_STREAM
<< 0x00000003    28 PUSH_PROMISE  END_PUSH_PROMISE
>> 0x00000004     4 RST_STREAM
<< 0x00000003    53 PUSH_PROMISE  END_PUSH_PROMISE
>> 0x00000006     4 RST_STREAM
>> 0x00000008     4 RST_STREAM
<< 0x00000003    49 PUSH_PROMISE  END_PUSH_PROMISE
>> 0x0000000a     4 RST_STREAM
<< 0x00000003    50 PUSH_PROMISE  END_PUSH_PROMISE
>> 0x0000000c     4 RST_STREAM
<< 0x00000003    54 PUSH_PROMISE  END_PUSH_PROMISE
<< 0x00000003    26 PUSH_PROMISE  END_PUSH_PROMISE
>> 0x0000000e     4 RST_STREAM
>> 0x00000010     4 RST_STREAM
<< 0x00000003    35 PUSH_PROMISE  END_PUSH_PROMISE
<< 0x00000003    60 PUSH_PROMISE  END_PUSH_PROMISE
>> 0x00000012     4 RST_STREAM
<< 0x00000003    46 PUSH_PROMISE  END_PUSH_PROMISE
>> 0x00000014     4 RST_STREAM
<< 0x00000003    59 PUSH_PROMISE  END_PUSH_PROMISE
>> 0x00000016     4 RST_STREAM
<< 0x00000003  1115 HEADERS       END_HEADERS
>> 0x00000018     4 RST_STREAM
<< 0x00000003  2262 DATA
<< 0x00000003  1339 DATA
<< 0x00000003     0 DATA          END_STREAM
yschimke commented 4 years ago

FWIW an interesting post on the implementation challenges for push https://jakearchibald.com/2017/h2-push-tougher-than-i-thought/

dsanso commented 2 years ago

I think HTTP/2 Server Push is very useful and should be implemented.

Apple's original Low-Latency HTTP Live Streaming (HLS) specification had HTTP/2 Server Push as a requirement.

HLS is the most used way of live streaming in the world, and LHLS lowers latency to a second or two from real-time.

In LHLS the client ends up requesting two files multiple times a second (because part segments are video clips of half a second or less each). The first file it requests is the m3u8 playlist file and then it reads the file and immediately requests the video clip it's been awaiting for, it cannot request the video clip beforehand because it does not know the corresponding URL to call. But with HTTP/2 Server Push the server knows what clip the client will be requesting after it reads the m3u8 playlist file so it immediately sends the clip with the m3u8 playlist file every time to avoid the client requesting the clip milliseconds afterward, thus saving up to 50% load on the server.

Source WWDC2019 Live Event: https://developer.apple.com/videos/play/wwdc2019/502/

yschimke commented 2 years ago

Sounds conpelling.

Is it currently implemented as in any public servers to test? What about clients? Will clients like exoplayer expect it and make the right requests, if so does it work with cronet or other http clients to compare against?