Commit 6acf1777 authored by Poul-Henning Kamp's avatar Poul-Henning Kamp

Major tour through the Range code to make us more strict and RFC7233 conforming.

parent e012bc3e
...@@ -82,72 +82,64 @@ vrg_range_bytes(struct req *req, enum vdp_action act, void **priv, ...@@ -82,72 +82,64 @@ vrg_range_bytes(struct req *req, enum vdp_action act, void **priv,
/*--------------------------------------------------------------------*/ /*--------------------------------------------------------------------*/
void static int
VRG_dorange(struct req *req, struct busyobj *bo, const char *r) vrg_dorange(struct req *req, const struct busyobj *bo, ssize_t len,
const char *r)
{ {
ssize_t len, low, high, has_low; ssize_t low, high, has_low, has_high, t;
struct vrg_priv *vrg_priv; struct vrg_priv *vrg_priv;
CHECK_OBJ_NOTNULL(req, REQ_MAGIC); (void)bo;
CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC);
assert(http_IsStatus(req->resp, 200));
/* We must snapshot the length if we're streaming from the backend */
if (bo != NULL)
len = VBO_waitlen(req->wrk, bo, -1);
else
len = ObjGetLen(req->wrk, req->objcore);
if (strncasecmp(r, "bytes=", 6)) if (strncasecmp(r, "bytes=", 6))
return; return (__LINE__);
r += 6; r += 6;
/* The low end of range */ /* The low end of range */
has_low = low = 0; has_low = low = 0;
while (vct_isdigit(*r)) { while (vct_isdigit(*r)) {
has_low = 1; has_low = 1;
t = low;
low *= 10; low *= 10;
low += *r - '0'; low += *r++ - '0';
r++; if (low < t)
return (__LINE__);
} }
if (*r != '-') if (*r++ != '-')
return; return (__LINE__);
r++;
/* The high end of range */ /* The high end of range */
if (vct_isdigit(*r)) { has_high = high = 0;
high = 0; while (vct_isdigit(*r)) {
while (vct_isdigit(*r)) { has_high = 1;
high *= 10; t = high;
high += *r - '0'; high *= 10;
r++; high += *r++ - '0';
} if (high < t)
if (high < low) return (__LINE__);
return; }
if (!has_low) {
low = len - high;
if (low < 0)
low = 0;
high = len - 1;
} else if (high >= len)
high = len - 1;
} else if (has_low)
high = len - 1;
else
return;
if (*r != '\0') if (*r != '\0')
return; return (__LINE__);
if (low >= len) { if (has_high + has_low == 0)
http_PrintfHeader(req->resp, "Content-Range: bytes */%jd", return (__LINE__);
(intmax_t)len);
http_Unset(req->resp, H_Content_Length); if (!has_low) {
http_PutResponse(req->resp, "HTTP/1.1", 416, NULL); if (high == 0)
req->wantbody = 0; return (__LINE__);
return; low = len - high;
} if (low < 0)
low = 0;
high = len - 1;
} else if (high >= len || !has_high)
high = len - 1;
if (high < low)
return (__LINE__);
if (low >= len)
return (__LINE__);
http_PrintfHeader(req->resp, "Content-Range: bytes %jd-%jd/%jd", http_PrintfHeader(req->resp, "Content-Range: bytes %jd-%jd/%jd",
(intmax_t)low, (intmax_t)high, (intmax_t)len); (intmax_t)low, (intmax_t)high, (intmax_t)len);
...@@ -161,4 +153,33 @@ VRG_dorange(struct req *req, struct busyobj *bo, const char *r) ...@@ -161,4 +153,33 @@ VRG_dorange(struct req *req, struct busyobj *bo, const char *r)
vrg_priv->range_low = low; vrg_priv->range_low = low;
vrg_priv->range_high = high + 1; vrg_priv->range_high = high + 1;
VDP_push(req, vrg_range_bytes, vrg_priv, 1); VDP_push(req, vrg_range_bytes, vrg_priv, 1);
return (0);
}
void
VRG_dorange(struct req *req, struct busyobj *bo, const char *r)
{
size_t len;
int i;
CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC);
assert(http_IsStatus(req->resp, 200));
/* We must snapshot the length if we're streaming from the backend */
if (bo != NULL)
len = VBO_waitlen(req->wrk, bo, -1);
else
len = ObjGetLen(req->wrk, req->objcore);
i = vrg_dorange(req, bo, len, r);
if (i) {
VSLb(req->vsl, SLT_Debug, "RANGE_FAIL line %d", i);
http_Unset(req->resp, H_Content_Length);
if (bo == NULL)
http_PrintfHeader(req->resp,
"Content-Range: bytes */%jd", (intmax_t)len);
http_PutResponse(req->resp, "HTTP/1.1", 416, NULL);
req->wantbody = 0;
}
} }
...@@ -5,12 +5,14 @@ server s1 { ...@@ -5,12 +5,14 @@ server s1 {
txresp -bodylen 100 txresp -bodylen 100
} -start } -start
varnish v1 -arg "-p http_range_support=off" -vcl+backend { varnish v1 -vcl+backend {
sub vcl_backend_response { sub vcl_backend_response {
set beresp.do_stream = false; set beresp.do_stream = false;
} }
} -start } -start
varnish v1 -cliok "param.set http_range_support off"
client c1 { client c1 {
txreq -hdr "Range: bytes=0-9" txreq -hdr "Range: bytes=0-9"
rxresp rxresp
...@@ -18,61 +20,61 @@ client c1 { ...@@ -18,61 +20,61 @@ client c1 {
expect resp.bodylen == 100 expect resp.bodylen == 100
} -run } -run
varnish v1 -cliok "param.set http_range_support on"
varnish v1 -expect s_resp_bodybytes == 100 varnish v1 -expect s_resp_bodybytes == 100
varnish v1 -cliok "param.set http_range_support on"
client c1 { client c1 {
# Invalid range requests
txreq -hdr "Range: bytes =0-9" txreq -hdr "Range: bytes =0-9"
rxresp rxresp
expect resp.status == 200 expect resp.status == 416
expect resp.bodylen == 100 expect resp.bodylen == 0
expect resp.http.content-range == "bytes */100"
txreq -hdr "Range: bytes=0- 9" txreq -hdr "Range: bytes=0- 9"
rxresp rxresp
expect resp.status == 200 expect resp.status == 416
expect resp.bodylen == 100 expect resp.bodylen == 0
txreq -hdr "Range: bytes =-9" txreq -hdr "Range: bytes =-9"
rxresp rxresp
expect resp.status == 200 expect resp.status == 416
expect resp.bodylen == 100 expect resp.bodylen == 0
txreq -hdr "Range: bytes =0-a" txreq -hdr "Range: bytes =0-a"
rxresp rxresp
expect resp.status == 200 expect resp.status == 416
expect resp.bodylen == 100 expect resp.bodylen == 0
txreq -hdr "Range: bytes=-" txreq -hdr "Range: bytes=-"
rxresp rxresp
expect resp.status == 200 expect resp.status == 416
expect resp.bodylen == 100 expect resp.bodylen == 0
txreq -hdr "Range: bytes=5-2" txreq -hdr "Range: bytes=5-2"
rxresp rxresp
expect resp.status == 200 expect resp.status == 416
expect resp.bodylen == 100 expect resp.bodylen == 0
} -run
varnish v1 -expect s_resp_bodybytes == 700
client c1 {
txreq -hdr "Range: bytes=-0" txreq -hdr "Range: bytes=-0"
rxresp rxresp
expect resp.status == 416 expect resp.status == 416
expect resp.bodylen == 0 expect resp.bodylen == 0
expect resp.http.content-range == "bytes */100"
txreq -hdr "Range: bytes=100-" txreq -hdr "Range: bytes=100-"
rxresp rxresp
expect resp.status == 416 expect resp.status == 416
expect resp.bodylen == 0 expect resp.bodylen == 0
expect resp.http.content-range == "bytes */100"
} -run } -run
varnish v1 -expect s_resp_bodybytes == 700 varnish v1 -expect s_resp_bodybytes == 100
delay .1
client c1 { client c1 {
# Valid range requests
txreq -hdr "Range: bytes=0-49" txreq -hdr "Range: bytes=0-49"
rxresp rxresp
expect resp.status == 206 expect resp.status == 206
...@@ -110,4 +112,4 @@ client c1 { ...@@ -110,4 +112,4 @@ client c1 {
expect resp.http.content-range == "bytes 0-99/100" expect resp.http.content-range == "bytes 0-99/100"
} -run } -run
varnish v1 -expect s_resp_bodybytes == 1001 varnish v1 -expect s_resp_bodybytes == 401
...@@ -28,11 +28,13 @@ varnish v1 -vcl+backend { ...@@ -28,11 +28,13 @@ varnish v1 -vcl+backend {
varnish v1 -cliok "param.set debug +syncvsl" varnish v1 -cliok "param.set debug +syncvsl"
client c1 { client c1 {
txreq -url "/foo" -hdr "Range: 100-200" txreq -url "/foo" -hdr "Range: bytes=1-2"
rxresp rxresp
expect resp.status == 200 expect resp.status == 206
expect resp.http.Content-Length == 2
expect resp.http.X-Varnish == "1001" expect resp.http.X-Varnish == "1001"
# NB: Deliberately bogus Range header
txreq -url "/bar" -hdr "Range: 200-300" txreq -url "/bar" -hdr "Range: 200-300"
rxresp rxresp
expect resp.status == 206 expect resp.status == 206
......
...@@ -14,5 +14,10 @@ varnish v1 -vcl+backend { ...@@ -14,5 +14,10 @@ varnish v1 -vcl+backend {
client c1 { client c1 {
txreq -hdr "Range: 0-1" txreq -hdr "Range: 0-1"
rxresp rxresp
expect resp.status == 200 expect resp.status == 416
txreq -hdr "Range: bytes=0-1"
rxresp
expect resp.status == 206
expect resp.http.content-length == 2
} -run } -run
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment