Open imwhocodes opened 8 months ago
This is known, and it is unclear how this scenario could be improved at all. One of the questions is what would be a good value to send as last-pos
of the Content-Range header. Also, we would need to send *
instead of complete-length
while streaming, as we just do not know the overall length.
That said, I would suspect that browsers would not show a seek widget either or, in other words, I think that the problem would not go away with a 206 response, but rather browsers require a known length to support seeking (forward).
From personal experience, I know that at least ff supports backwards seeking for streaming responses of audio (200 with chunked encoding).
I think you broke user agents because you tried to only enforce a 206, your snippet is lacking a a content-range
header.
varnishtest "Emulate 206 on unbound bytes range"
server s1 {
rxreq
txresp -hdr "Transfer-Encoding: chunked" -hdr {etag: "123"}
chunked hello
delay 0.1
chunked world
chunkedlen 0
} -start
varnish v1 -vcl+backend {
import std;
sub vcl_deliver {
if (resp.is_streaming &&
std.tolower(req.http.range) == "bytes=0-" &&
req.http.if-none-match != resp.http.etag ) {
set resp.status = 206;
set resp.http.content-range = "bytes */*";
}
}
} -start
client c1 {
txreq -hdr "range: bytes=0-" -hdr {if-none-match: "abc"}
rxresp
expect resp.status == 206
expect resp.http.content-range == "bytes */*"
txreq -hdr "range: bytes=0-" -hdr {if-none-match: "def"}
rxresp
expect resp.status == 206
expect resp.http.content-range == "bytes 0-9/10"
} -run
If you really really require a 206, maybe try the VCL snippet from this test case.
For consistency with the non-streaming cases, we could consider replying with a 206 in this scenario.
For consistency with the non-streaming cases, we could consider replying with a 206 in this scenario.
Does it help? If yes, 👍🏽
@imwhocodes did you get a chance to try the VCL workaround I suggested?
sub vcl_deliver {
if (resp.is_streaming &&
std.tolower(req.http.range) == "bytes=0-" &&
req.http.if-none-match != resp.http.etag ) {
set resp.status = 206;
set resp.http.content-range = "bytes */*";
}
}
@imwhocodes did you get a chance to try the VCL workaround I suggested?
sub vcl_deliver { if (resp.is_streaming && std.tolower(req.http.range) == "bytes=0-" && req.http.if-none-match != resp.http.etag ) { set resp.status = 206; set resp.http.content-range = "bytes */*"; } }
Yes I did right after you proposed it, really appreciated!
I was waiting to answer because it didn't solve the problem but I wanted to do more in-depth testing (that I didn't had time yet to do)
As far from what I'm understanding/tested set content-range = "bytes */*";
is not valid, particularly the incl-range
part can not be *
as per spec: https://www.rfc-editor.org/rfc/rfc9110#field.content-range
Chrome was not happy about, it actually like it more if I set something fake like set content-range = "bytes 0-1024/*";
even if I'm sending more data than "advertise one"
Thanks
@imwhocodes thank you for the additional feedback. We have discussed this ticket today during bugwash and the short summary is "we could do better, but are not sure if it's worth it".
For a streaming object, the range filter could wait for at least first-pos + x bytes and then send Content-Range: bytes .../*
See also #3872 for prior work in the same direction
Expected Behavior
Given this condition:
Range: bytes=0-
Transfer-Encoding: chunked
While streaming from backend the response to the client should be:
206 (Partial Content)
Transfer-Encoding: chunked
Current Behavior
Given previous precondition:
While streaming from backend the response is:
200 (OK)
Transfer-Encoding: chunked
But when retrieving from cache (correctly):
206 (Partial Content)
Content-Length: ...
Content-Range: bytes ...
Possible Solution
I tried:
But this seem to break the response (at least on chrome)
Steps to Reproduce (for bugs)
No response
Context
I'm using Varnish as a reverse-proxy/cache in front of an aiohttp (python) server that encode on-demand some videos using ffmpeg, I want to minimize time form request to first frame out so ffmpeg output is piped out while it being processed:
ffmpeg ... -f mp4 -movflags frag_keyframe+empty_moov+default_base_moof pipe:
This is why the response from the origin/backend server is
Transfer-Encoding: chunked
The response is for a<video> html5 tag
, 200 would be ok if not that Chrome misbehave (palyback seeking is broken) when not receiving 206 on aRange: bytes=0-
requestLog
Varnish Cache version
varnish:latest (varnish-7.4.2 revision cd1d10ab53a6f6115b2b4f3b2a1da94c1f749f80)
Operating system
No response
Source of binary packages used (if any)
https://hub.docker.com/_/varnish