diff --git a/include/haproxy/filters-t.h b/include/haproxy/filters-t.h index d2f8c7386..73fe574da 100644 --- a/include/haproxy/filters-t.h +++ b/include/haproxy/filters-t.h @@ -33,6 +33,7 @@ /* Flags set on the stream, common to all filters attached to its stream */ #define STRM_FLT_FL_HAS_FILTERS 0x0001 /* The stream has at least one filter */ +#define STRM_FLT_FL_HOLD_HTTP_HDRS 0x0002 /* At least one filter on the stream want to hold the message headers */ struct http_msg; diff --git a/reg-tests/filters/random-forwarding.vtc b/reg-tests/filters/random-forwarding.vtc index fdda4e05a..2982c880f 100644 --- a/reg-tests/filters/random-forwarding.vtc +++ b/reg-tests/filters/random-forwarding.vtc @@ -24,6 +24,19 @@ server s1 { recv 36000 send_n 1000 "0123456789abcdefghijklmnopqrstuvwxyz" barrier b1 sync + + accept + rxreq + expect req.url == "/" + txresp -nolen \ + -hdr "Content-Type: text/plain" \ + -bodylen 20480 + close + + accept + rxreq + expect req.url == "/" + txresp -nolen } -start haproxy h1 -conf { @@ -69,3 +82,27 @@ client c1 -connect ${h1_fe1_sock} { recv 36000 barrier b1 sync } -run + +client c2 -connect ${h1_fe1_sock} { + txreq -url "/" \ + -hdr "Accept-Encoding: gzip" \ + -hdr "Content-Type: text/plain" + rxresp + expect resp.status == 200 + expect resp.http.content-encoding == "" + expect resp.http.transfer-encoding == "" + expect resp.http.content-length == "" + expect resp.bodylen == 20480 +} -run + +client c3 -connect ${h1_fe1_sock} { + txreq -url "/" \ + -hdr "Accept-Encoding: gzip" \ + -hdr "Content-Type: text/plain" + rxresp + expect resp.status == 200 + expect resp.http.content-encoding == "" + expect resp.http.transfer-encoding == "" + expect resp.http.content-length == "" + expect resp.bodylen == 0 +} -run diff --git a/src/filters.c b/src/filters.c index fd5c56eab..69ebf525f 100644 --- a/src/filters.c +++ b/src/filters.c @@ -632,6 +632,8 @@ flt_http_payload(struct stream *s, struct http_msg *msg, unsigned int len) unsigned int out = co_data(msg->chn); int ret, data; + strm_flt(s)->flags &= ~STRM_FLT_FL_HOLD_HTTP_HDRS; + ret = data = len - out; DBG_TRACE_ENTER(STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA|STRM_EV_FLT_ANA, s, s->txn, msg); list_for_each_entry(filter, &strm_flt(s)->filters, list) { @@ -656,11 +658,22 @@ flt_http_payload(struct stream *s, struct http_msg *msg, unsigned int len) } } - /* Only forward data if the last filter decides to forward something */ - if (ret > 0) { - ret = data; - *strm_off += ret; - } + /* If nothing was forwarded yet, we take care to hold the headers if + * following conditions are met : + * + * - *strm_off == 0 (nothing forwarded yet) + * - ret == 0 (no data forwarded at all on this turn) + * - STRM_FLT_FL_HOLD_HTTP_HDRS flag set (at least one filter want to hold the headers) + * + * Be careful, STRM_FLT_FL_HOLD_HTTP_HDRS is removed before each http_payload loop. + * Thus, it must explicitly be set when necessary. We must do that to hold the headers + * when there is no payload. + */ + if (!ret && !*strm_off && (strm_flt(s)->flags & STRM_FLT_FL_HOLD_HTTP_HDRS)) + goto end; + + ret = data; + *strm_off += ret; end: DBG_TRACE_LEAVE(STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA|STRM_EV_FLT_ANA, s); return ret;