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

Inspired by #1356: Make handling of req.body much more robust and

RFC-compliant.

Fixes #1356
parent e6868989
...@@ -652,7 +652,6 @@ struct req { ...@@ -652,7 +652,6 @@ struct req {
struct { struct {
ssize_t bytes_done; ssize_t bytes_done;
ssize_t bytes_yet; ssize_t bytes_yet;
enum {CL, CHUNKED} mode;
} h1; /* HTTP1 specific */ } h1; /* HTTP1 specific */
/* The busy objhead we sleep on */ /* The busy objhead we sleep on */
......
...@@ -240,6 +240,13 @@ V1F_fetch_hdr(struct worker *wrk, struct busyobj *bo, struct req *req) ...@@ -240,6 +240,13 @@ V1F_fetch_hdr(struct worker *wrk, struct busyobj *bo, struct req *req)
i = HTTP1_IterateReqBody(req, vbf_iter_req_body, wrk); i = HTTP1_IterateReqBody(req, vbf_iter_req_body, wrk);
if (req->req_body_status == REQ_BODY_DONE) if (req->req_body_status == REQ_BODY_DONE)
retry = -1; retry = -1;
if (req->req_body_status == REQ_BODY_FAIL) {
VSLb(bo->vsl, SLT_FetchError,
"req.body read error: %d (%s)",
errno, strerror(errno));
req->doclose = SC_RX_BODY;
retry = -1;
}
} }
if (WRW_FlushRelease(wrk) || i != 0) { if (WRW_FlushRelease(wrk) || i != 0) {
......
...@@ -255,16 +255,11 @@ http1_req_body_status(struct req *req) ...@@ -255,16 +255,11 @@ http1_req_body_status(struct req *req)
return (REQ_BODY_FAIL); return (REQ_BODY_FAIL);
if (req->req_bodybytes == 0) if (req->req_bodybytes == 0)
return (REQ_BODY_NONE); return (REQ_BODY_NONE);
req->h1.mode = CL;
req->h1.bytes_yet = req->req_bodybytes - req->h1.bytes_done; req->h1.bytes_yet = req->req_bodybytes - req->h1.bytes_done;
return (REQ_BODY_PRESENT); return (REQ_BODY_PRESENT);
} }
if (http_GetHdr(req->http, H_Transfer_Encoding, NULL))
if (http_GetHdr(req->http, H_Transfer_Encoding, NULL)) {
req->h1.mode = CHUNKED;
VSLb(req->vsl, SLT_Debug, "Transfer-Encoding in request");
return (REQ_BODY_FAIL); return (REQ_BODY_FAIL);
}
return (REQ_BODY_NONE); return (REQ_BODY_NONE);
} }
...@@ -274,7 +269,8 @@ http1_req_body_status(struct req *req) ...@@ -274,7 +269,8 @@ http1_req_body_status(struct req *req)
static enum req_fsm_nxt static enum req_fsm_nxt
http1_dissect(struct worker *wrk, struct req *req) http1_dissect(struct worker *wrk, struct req *req)
{ {
const char *r = "HTTP/1.1 100 Continue\r\n\r\n"; const char *r_100 = "HTTP/1.1 100 Continue\r\n\r\n";
const char *r_411 = "HTTP/1.1 411 Length Required\r\n\r\n";
char *p; char *p;
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
...@@ -308,6 +304,19 @@ http1_dissect(struct worker *wrk, struct req *req) ...@@ -308,6 +304,19 @@ http1_dissect(struct worker *wrk, struct req *req)
SES_Close(req->sp, SC_RX_JUNK); SES_Close(req->sp, SC_RX_JUNK);
return (REQ_FSM_DONE); return (REQ_FSM_DONE);
} }
if (req->req_body_status == REQ_BODY_INIT)
req->req_body_status = http1_req_body_status(req);
else
assert(req->req_body_status == REQ_BODY_NONE); // ESI
if (req->req_body_status == REQ_BODY_FAIL) {
wrk->stats.client_req_411++;
(void)write(req->sp->fd, r_411, strlen(r_411));
SES_Close(req->sp, SC_RX_JUNK);
return (REQ_FSM_DONE);
}
req->acct_req.req++; req->acct_req.req++;
req->ws_req = WS_Snapshot(req->ws); req->ws_req = WS_Snapshot(req->ws);
...@@ -318,7 +327,8 @@ http1_dissect(struct worker *wrk, struct req *req) ...@@ -318,7 +327,8 @@ http1_dissect(struct worker *wrk, struct req *req)
if (strcasecmp(p, "100-continue")) { if (strcasecmp(p, "100-continue")) {
wrk->stats.client_req_417++; wrk->stats.client_req_417++;
req->err_code = 417; req->err_code = 417;
} else if (strlen(r) != write(req->sp->fd, r, strlen(r))) { } else if (strlen(r_100) !=
write(req->sp->fd, r_100, strlen(r_100))) {
SES_Close(req->sp, SC_REM_CLOSE); SES_Close(req->sp, SC_REM_CLOSE);
return (REQ_FSM_DONE); return (REQ_FSM_DONE);
} }
...@@ -328,10 +338,6 @@ http1_dissect(struct worker *wrk, struct req *req) ...@@ -328,10 +338,6 @@ http1_dissect(struct worker *wrk, struct req *req)
wrk->stats.client_req++; wrk->stats.client_req++;
http_Unset(req->http, H_Expect); http_Unset(req->http, H_Expect);
if (req->req_body_status == REQ_BODY_INIT)
req->req_body_status = http1_req_body_status(req);
else
assert(req->req_body_status == REQ_BODY_NONE);
assert(req->req_body_status != REQ_BODY_INIT); assert(req->req_body_status != REQ_BODY_INIT);
...@@ -437,26 +443,23 @@ http1_iter_req_body(struct req *req, void *buf, ssize_t len) ...@@ -437,26 +443,23 @@ http1_iter_req_body(struct req *req, void *buf, ssize_t len)
{ {
CHECK_OBJ_NOTNULL(req, REQ_MAGIC); CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
if (req->h1.mode == CL) { AN(req->req_bodybytes);
AN(req->req_bodybytes); AN(len);
AN(len); AN(buf);
AN(buf); if (len > req->req_bodybytes - req->h1.bytes_done)
if (len > req->req_bodybytes - req->h1.bytes_done) len = req->req_bodybytes - req->h1.bytes_done;
len = req->req_bodybytes - req->h1.bytes_done; if (len == 0) {
if (len == 0) { req->req_body_status = REQ_BODY_DONE;
req->req_body_status = REQ_BODY_DONE; return (0);
return (0);
}
len = HTTP1_Read(req->htc, buf, len);
if (len <= 0) {
req->req_body_status = REQ_BODY_FAIL;
return (-1);
}
req->h1.bytes_done += len;
req->h1.bytes_yet = req->req_bodybytes - req->h1.bytes_done;
return (len);
} }
INCOMPL(); len = HTTP1_Read(req->htc, buf, len);
if (len <= 0) {
req->req_body_status = REQ_BODY_FAIL;
return (-1);
}
req->h1.bytes_done += len;
req->h1.bytes_yet = req->req_bodybytes - req->h1.bytes_done;
return (len);
} }
/*---------------------------------------------------------------------- /*----------------------------------------------------------------------
...@@ -513,6 +516,7 @@ HTTP1_IterateReqBody(struct req *req, req_body_iter_f *func, void *priv) ...@@ -513,6 +516,7 @@ HTTP1_IterateReqBody(struct req *req, req_body_iter_f *func, void *priv)
do { do {
l = http1_iter_req_body(req, buf, sizeof buf); l = http1_iter_req_body(req, buf, sizeof buf);
if (l < 0) { if (l < 0) {
req->doclose = SC_RX_BODY;
return (l); return (l);
} }
if (l > 0) { if (l > 0) {
...@@ -548,6 +552,8 @@ HTTP1_DiscardReqBody(struct req *req) ...@@ -548,6 +552,8 @@ HTTP1_DiscardReqBody(struct req *req)
if (req->req_body_status == REQ_BODY_DONE) if (req->req_body_status == REQ_BODY_DONE)
return(0); return(0);
if (req->req_body_status == REQ_BODY_FAIL)
return(0);
if (req->req_body_status == REQ_BODY_TAKEN) if (req->req_body_status == REQ_BODY_TAKEN)
return(0); return(0);
return(HTTP1_IterateReqBody(req, httpq_req_body_discard, NULL)); return(HTTP1_IterateReqBody(req, httpq_req_body_discard, NULL));
...@@ -568,8 +574,11 @@ HTTP1_CacheReqBody(struct req *req, ssize_t maxsize) ...@@ -568,8 +574,11 @@ HTTP1_CacheReqBody(struct req *req, ssize_t maxsize)
CHECK_OBJ_NOTNULL(req, REQ_MAGIC); CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
assert (req->req_step == R_STP_RECV);
switch(req->req_body_status) { switch(req->req_body_status) {
case REQ_BODY_CACHED: case REQ_BODY_CACHED:
case REQ_BODY_FAIL:
return (-1);
case REQ_BODY_NONE: case REQ_BODY_NONE:
return (0); return (0);
case REQ_BODY_PRESENT: case REQ_BODY_PRESENT:
...@@ -599,8 +608,10 @@ HTTP1_CacheReqBody(struct req *req, ssize_t maxsize) ...@@ -599,8 +608,10 @@ HTTP1_CacheReqBody(struct req *req, ssize_t maxsize)
l = st->space - st->len; l = st->space - st->len;
l = http1_iter_req_body(req, st->ptr + st->len, l); l = http1_iter_req_body(req, st->ptr + st->len, l);
if (l < 0) if (l < 0) {
req->doclose = SC_RX_BODY;
return (l); return (l);
}
if (req->req_bodybytes > maxsize) { if (req->req_bodybytes > maxsize) {
req->req_body_status = REQ_BODY_FAIL; req->req_body_status = REQ_BODY_FAIL;
return (-1); return (-1);
......
...@@ -693,6 +693,11 @@ cnt_recv(struct worker *wrk, struct req *req) ...@@ -693,6 +693,11 @@ cnt_recv(struct worker *wrk, struct req *req)
http_CollectHdr(req->http, H_Cache_Control); http_CollectHdr(req->http, H_Cache_Control);
VCL_recv_method(req->vcl, wrk, req, NULL, req->http->ws); VCL_recv_method(req->vcl, wrk, req, NULL, req->http->ws);
/* Attempts to cache req.body may fail */
if (req->req_body_status == REQ_BODY_FAIL) {
return (REQ_FSM_DONE);
}
recv_handling = wrk->handling; recv_handling = wrk->handling;
if (cache_param->http_gzip_support && if (cache_param->http_gzip_support &&
......
...@@ -30,3 +30,19 @@ client c1 { ...@@ -30,3 +30,19 @@ client c1 {
expect resp.http.Foo == "Foo" expect resp.http.Foo == "Foo"
expect resp.bodylen == 2 expect resp.bodylen == 2
} -run } -run
client c1 {
txreq -req POST -nolen -hdr "Content-Length: 52"
delay .3
} -run
server s1 {
rxreq
txresp
} -start
client c1 {
txreq -url "/is_varnish_still_running"
rxresp
expect resp.status == 200
} -run
varnishtest "#1356, req.body failure"
server s1 {
rxhdrs
expect_close
} -start
varnish v1 -vcl+backend { } -start
client c1 {
txreq -req POST -nolen -hdr "Transfer-Encoding: carrier-pigeon"
rxresp
expect resp.status == 411
} -run
client c1 {
txreq -req POST -nolen -hdr "Content-Length: carrier-pigeon"
rxresp
expect resp.status == 411
} -run
client c1 {
txreq -req POST -nolen -hdr "Content-Length: 56"
} -run
# Check that varnishd still runs
server s1 {
rxreq
txresp
} -start
client c1 {
txreq
rxresp
expect resp.status == 200
} -run
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
SESS_CLOSE(REM_CLOSE, "Client Closed") SESS_CLOSE(REM_CLOSE, "Client Closed")
SESS_CLOSE(REQ_CLOSE, "Client requested close") SESS_CLOSE(REQ_CLOSE, "Client requested close")
SESS_CLOSE(REQ_HTTP10, "proto < HTTP.1.1") SESS_CLOSE(REQ_HTTP10, "proto < HTTP.1.1")
SESS_CLOSE(RX_BODY, "Failure receiving req.body")
SESS_CLOSE(RX_JUNK, "Received junk data") SESS_CLOSE(RX_JUNK, "Received junk data")
SESS_CLOSE(RX_OVERFLOW, "Received buffer overflow") SESS_CLOSE(RX_OVERFLOW, "Received buffer overflow")
SESS_CLOSE(RX_TIMEOUT, "Receive timeout") SESS_CLOSE(RX_TIMEOUT, "Receive timeout")
......
...@@ -101,6 +101,11 @@ VSC_F(client_req_400, uint64_t, 1, 'a', info, ...@@ -101,6 +101,11 @@ VSC_F(client_req_400, uint64_t, 1, 'a', info,
" malformed in some drastic way." " malformed in some drastic way."
) )
VSC_F(client_req_411, uint64_t, 1, 'a', info,
"Client requests received, subject to 411 errors",
"411 means the client did not send a Content-Lenght for the req.body."
)
VSC_F(client_req_413, uint64_t, 1, 'a', info, VSC_F(client_req_413, uint64_t, 1, 'a', info,
"Client requests received, subject to 413 errors", "Client requests received, subject to 413 errors",
"413 means that HTTP headers execeeded length or count limits." "413 means that HTTP headers execeeded length or count limits."
......
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