Skip to content

Commit

Permalink
range: New http_GetContentRange() semantics
Browse files Browse the repository at this point in the history
This function returns the content length and puts range indices in
lo and hi arguments. A content length of -1 used to be interpreted
as something to ignore, but there is a case where the content length
may be unknown.

Since we can't represent a zero-length range, because both bounds are
inclusive, zero now denotes the lack of content-range header.

Unknown range units are treated as errors as they wouldn't pass the
busyobj check for the range header, even for pass transactions. The
only way to use "extension" range units is to turn http_range_support
off.

The calling convention for http_GetContentRange() remains otherwise
the same, and for good measure http_GetContentLength() received a
similar description.

Fixes varnishcache#4089
  • Loading branch information
dridi committed Mar 27, 2024
1 parent 8f2c364 commit 788227e
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 12 deletions.
34 changes: 29 additions & 5 deletions bin/varnishd/cache/cache_http.c
Original file line number Diff line number Diff line change
Expand Up @@ -873,7 +873,14 @@ http_GetHdrField(const struct http *hp, hdr_t hdr,
return (i);
}

/*--------------------------------------------------------------------*/
/*--------------------------------------------------------------------
* Parse content-length header
*
* Return:
* -2 invalid
* -1 unknown length
* >= 0 body length
*/

ssize_t
http_GetContentLength(const struct http *hp)
Expand All @@ -895,6 +902,20 @@ http_GetContentLength(const struct http *hp)
return (cl);
}

/*--------------------------------------------------------------------
* Parse content-range header
*
* Return:
* -2 invalid
* -1 unknown length
* 0 not applicable
* > 0 full body length
*
* lo, hi:
* -1 unknown position
* >= 0 position
*/

ssize_t
http_GetContentRange(const struct http *hp, ssize_t *lo, ssize_t *hi)
{
Expand All @@ -911,14 +932,14 @@ http_GetContentRange(const struct http *hp, ssize_t *lo, ssize_t *hi)
*lo = *hi = -1;

if (!http_GetHdr(hp, H_Content_Range, &b))
return (-1);
return (0); // No content-range, ignore

t = strchr(b, ' ');
if (t == NULL)
return (-2); // Missing space after range unit

if (!http_range_at(b, bytes, t - b))
return (-1); // Unknown range unit, ignore
return (-2); // Unknown range unit
b = t + 1;

if (*b == '*') { // Content-Range: bytes */123
Expand All @@ -937,6 +958,8 @@ http_GetContentRange(const struct http *hp, ssize_t *lo, ssize_t *hi)
if (*b != '/')
return (-2);
if (b[1] == '*') { // Content-Range: bytes 1-2/*
if (*lo == -1 && *hi == -1)
return (-2); // Content-Range: bytes */*
cl = -1;
b += 2;
} else {
Expand All @@ -950,10 +973,11 @@ http_GetContentRange(const struct http *hp, ssize_t *lo, ssize_t *hi)
return (-2);
if (*lo > *hi)
return (-2);
assert(cl >= -1);
if (cl == -1)
return (-1);
assert(cl > 0);
if (*lo >= cl || *hi >= cl)
return (-2);
AN(cl);
return (cl);
}

Expand Down
12 changes: 6 additions & 6 deletions bin/varnishd/cache/cache_range.c
Original file line number Diff line number Diff line change
Expand Up @@ -265,16 +265,16 @@ VRG_CheckBo(struct busyobj *bo)
return (-1);
}

if (crlo < 0 && crhi < 0 && crlen < 0) {
AZ(http_GetHdr(bo->beresp, H_Content_Range, NULL));
return (0);
}

if (rlo < 0 && rhi < 0) {
if (rlo < 0 && rhi < 0 && crlen != 0) {
VSLb(bo->vsl, SLT_Error, "Unexpected content-range header");
return (-1);
}

if (crlo < 0 && crhi < 0 && crlen == 0) {
AZ(http_GetHdr(bo->beresp, H_Content_Range, NULL));
return (0);
}

if (crlo < 0) { // Content-Range: bytes */123
assert(crhi < 0);
assert(crlen > 0);
Expand Down
34 changes: 33 additions & 1 deletion bin/varnishtest/tests/c00034.vtc
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ varnish v1 -vsl_catchup
server s1 {
loop 2 {
rxreq
txresp -nolen -hdr "Content-Length: 100"
txresp -hdr "Content-Length: 100"
send "0123456789"
send "0123456789"
send "0123456789"
Expand Down Expand Up @@ -210,8 +210,13 @@ varnish v1 -vsl_catchup
server s1 {
rxreq
txresp

rxreq
txresp -hdr "Accept-Ranges: foobar"

rxreq
expect req.http.Range == foobar=1-2
txresp -status 206 -hdr "Content-Range: foobar 1-2/3"
} -start

varnish v1 -vcl+backend {
Expand All @@ -228,10 +233,16 @@ client c7 {
rxresp
expect resp.http.foobar == ""
expect resp.http.accept-ranges == <undef>

txreq
rxresp
expect resp.http.foobar == foobar
expect resp.http.accept-ranges == foobar

txreq -hdr "Range: foobar=1-2"
rxresp
expect resp.status == 503
expect resp.http.content-range == <undef>
} -run

server s1 {
Expand Down Expand Up @@ -265,6 +276,18 @@ server s1 {
rxreq
expect req.http.range == "bytes=0-0"
txresp -hdr "content-range: bytes */*" -bodylen 100

rxreq
expect req.http.range == "bytes=0-0"
txresp -hdr "content-range: bytes 0-0/*" -bodylen 100

# issue 4089
rxreq
expect req.http.range == "bytes=10-19"
txresp -status 206 -hdr "content-range: bytes 10-19/*" \
-hdr "transfer-encoding: chunked"
chunkedlen 10
chunkedlen 0
} -start

varnish v1 -vcl+backend {
Expand Down Expand Up @@ -305,6 +328,15 @@ client c8 {
txreq -hdr "range: bytes=0-0" -hdr "return: pass"
rxresp
expect resp.status == 503

txreq -hdr "range: bytes=0-0" -hdr "return: pass"
rxresp
expect resp.status == 503

# issue 4089
txreq -hdr "range: bytes=10-19" -hdr "return: pass"
rxresp
expect resp.status == 206
} -run

# range filter without a range header
Expand Down

0 comments on commit 788227e

Please sign in to comment.