diff --git a/reg-tests/http-messaging/h1_to_h1.vtc b/reg-tests/http-messaging/h1_to_h1.vtc index 0d65366986..67aba14409 100644 --- a/reg-tests/http-messaging/h1_to_h1.vtc +++ b/reg-tests/http-messaging/h1_to_h1.vtc @@ -273,3 +273,29 @@ client c3h1 -connect ${h1_feh1_sock} { # arrive here. expect_close } -run + +client c4h1 -connect ${h1_feh1_sock} { + # this request is invalid and advertises an invalid C-L ending with an + # empty value, which results in a stream error. + txreq \ + -req "GET" \ + -url "/test31.html" \ + -hdr "content-length: 0," \ + -hdr "connection: close" + rxresp + expect resp.status == 400 + expect_close +} -run + +client c5h1 -connect ${h1_feh1_sock} { + # this request is invalid and advertises an empty C-L, which results + # in a stream error. + txreq \ + -req "GET" \ + -url "/test41.html" \ + -hdr "content-length:" \ + -hdr "connection: close" + rxresp + expect resp.status == 400 + expect_close +} -run diff --git a/reg-tests/http-messaging/h2_to_h1.vtc b/reg-tests/http-messaging/h2_to_h1.vtc index 91d1056d86..637b664866 100644 --- a/reg-tests/http-messaging/h2_to_h1.vtc +++ b/reg-tests/http-messaging/h2_to_h1.vtc @@ -9,6 +9,8 @@ barrier b1 cond 2 -cyclic barrier b2 cond 2 -cyclic barrier b3 cond 2 -cyclic barrier b4 cond 2 -cyclic +barrier b5 cond 2 -cyclic +barrier b6 cond 2 -cyclic server s1 { rxreq @@ -30,6 +32,12 @@ server s1 { barrier b4 sync # the next request is never received + + barrier b5 sync + # the next request is never received + + barrier b6 sync + # the next request is never received } -repeat 2 -start haproxy h1 -conf { @@ -119,6 +127,32 @@ client c1h2 -connect ${h1_feh2_sock} { txdata -data "this is sent and ignored" rxrst } -run + + # fifth request is invalid and advertises an invalid C-L ending with an + # empty value, which results in a stream error. + stream 9 { + barrier b5 sync + txreq \ + -req "GET" \ + -scheme "https" \ + -url "/test5.html" \ + -hdr "content-length" "0," \ + -nostrend + rxrst + } -run + + # sixth request is invalid and advertises an empty C-L, which results + # in a stream error. + stream 11 { + barrier b6 sync + txreq \ + -req "GET" \ + -scheme "https" \ + -url "/test6.html" \ + -hdr "content-length" "" \ + -nostrend + rxrst + } -run } -run # HEAD requests : don't work well yet @@ -261,4 +295,30 @@ client c3h2 -connect ${h1_feh2_sock} { txdata -data "this is sent and ignored" rxrst } -run + + # fifth request is invalid and advertises invalid C-L ending with an + # empty value, which results in a stream error. + stream 9 { + barrier b5 sync + txreq \ + -req "POST" \ + -scheme "https" \ + -url "/test25.html" \ + -hdr "content-length" "0," \ + -nostrend + rxrst + } -run + + # sixth request is invalid and advertises an empty C-L, which results + # in a stream error. + stream 11 { + barrier b6 sync + txreq \ + -req "POST" \ + -scheme "https" \ + -url "/test26.html" \ + -hdr "content-length" "" \ + -nostrend + rxrst + } -run } -run diff --git a/src/h1.c b/src/h1.c index 4e224f8954..92ec96bfe1 100644 --- a/src/h1.c +++ b/src/h1.c @@ -34,13 +34,20 @@ int h1_parse_cont_len_header(struct h1m *h1m, struct ist *value) int not_first = !!(h1m->flags & H1_MF_CLEN); struct ist word; - word.ptr = value->ptr - 1; // -1 for next loop's pre-increment + word.ptr = value->ptr; e = value->ptr + value->len; - while (++word.ptr < e) { + while (1) { + if (word.ptr >= e) { + /* empty header or empty value */ + goto fail; + } + /* skip leading delimiter and blanks */ - if (unlikely(HTTP_IS_LWS(*word.ptr))) + if (unlikely(HTTP_IS_LWS(*word.ptr))) { + word.ptr++; continue; + } /* digits only now */ for (cl = 0, n = word.ptr; n < e; n++) { @@ -79,6 +86,13 @@ int h1_parse_cont_len_header(struct h1m *h1m, struct ist *value) h1m->flags |= H1_MF_CLEN; h1m->curr_len = h1m->body_len = cl; *value = word; + + /* Now either n==e and we're done, or n points to the comma, + * and we skip it and continue. + */ + if (n++ == e) + break; + word.ptr = n; } /* here we've reached the end with a single value or a series of diff --git a/src/http.c b/src/http.c index 18f111c955..85cd2f2e1d 100644 --- a/src/http.c +++ b/src/http.c @@ -707,13 +707,20 @@ int http_parse_cont_len_header(struct ist *value, unsigned long long *body_len, struct ist word; int check_prev = not_first; - word.ptr = value->ptr - 1; // -1 for next loop's pre-increment + word.ptr = value->ptr; e = value->ptr + value->len; - while (++word.ptr < e) { + while (1) { + if (word.ptr >= e) { + /* empty header or empty value */ + goto fail; + } + /* skip leading delimiter and blanks */ - if (unlikely(HTTP_IS_LWS(*word.ptr))) + if (unlikely(HTTP_IS_LWS(*word.ptr))) { + word.ptr++; continue; + } /* digits only now */ for (cl = 0, n = word.ptr; n < e; n++) { @@ -751,6 +758,13 @@ int http_parse_cont_len_header(struct ist *value, unsigned long long *body_len, /* OK, store this result as the one to be indexed */ *body_len = cl; *value = word; + + /* Now either n==e and we're done, or n points to the comma, + * and we skip it and continue. + */ + if (n++ == e) + break; + word.ptr = n; check_prev = 1; }