mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2024-12-17 00:44:33 +00:00
MEDIUM: mux-h2: Don't emit DATA frame for bodyless responses
Some responses must not contain data. Reponses to HEAD requests and 204/304 responses. But there is no warranty that this will be really respected by the senders or even if it is possible. For instance, the method may be rewritten by an http-request rule (HEAD->GET). Thus, it is not really possible to always strip these data from the response at the receive stage. And the response may be emitted by an applet or an internal service not strictly following the spec. All that to say that we may be prepared to handle payload for bodyless responses on the sending path. In addition, unlike the HTTP/1, it is not really clear that the trailers is part of the payload or not. Thus, some clients may expect to have the trailers, if any, in the response to a HEAD request. For instance, the GRPC status is placed in a trailer and clients rely on it. But what happens for 204 responses then. Read the following thread for details : https://lists.w3.org/Archives/Public/ietf-http-wg/2020OctDec/0040.html So, thanks to previous patches, it is now possible to know on the sending path if a response must be bodyless or not. So, for such responses, no DATA frame is emitted, except eventually the last empty one carring the ES flag. However, the TRAILERS frames are still emitted. The h2s_skip_data() function is added to take care to remove HTX DATA blocks without emitting any DATA frame expect the last one, if there is no trailers.
This commit is contained in:
parent
7d247f0771
commit
991febdfe0
94
src/mux_h2.c
94
src/mux_h2.c
@ -5059,18 +5059,12 @@ static size_t h2s_frt_make_resp_headers(struct h2s *h2s, struct htx *htx)
|
||||
/* Response already closed: add END_STREAM */
|
||||
es_now = 1;
|
||||
}
|
||||
else if ((h2s->flags & H2_SF_BODY_TUNNEL) && h2s->status >= 200 && h2s->status < 300) {
|
||||
/* Don't set ES if a tunnel is successfully established (2xx responses to a connect). */
|
||||
}
|
||||
else if (h2s->status == 204 || h2s->status == 304) {
|
||||
/* no contents, claim c-len is present and set to zero */
|
||||
es_now = 1;
|
||||
}
|
||||
else if ((htx->flags & HTX_FL_EOM) && htx_is_empty(htx) && h2s->status >= 200) {
|
||||
/* EOM+empty: we may need to add END_STREAM except for 1xx
|
||||
* responses.
|
||||
* responses and tunneled response.
|
||||
*/
|
||||
es_now = 1;
|
||||
if (!(h2s->flags & H2_SF_BODY_TUNNEL) || h2s->status >= 300)
|
||||
es_now = 1;
|
||||
}
|
||||
|
||||
if (es_now)
|
||||
@ -5754,6 +5748,82 @@ static size_t h2s_make_data(struct h2s *h2s, struct buffer *buf, size_t count)
|
||||
return total;
|
||||
}
|
||||
|
||||
/* Skip the message payload (DATA blocks) and emit an empty DATA frame with the
|
||||
* ES flag set for stream <h2s>. This function is called for response known to
|
||||
* have no payload. Only DATA blocks are skipped. This means the trailers are
|
||||
* still emited. The caller must check the stream's status to detect any error
|
||||
* which might have happened subsequently to a successful send. Returns the
|
||||
* number of data bytes consumed, or zero if nothing done.
|
||||
*/
|
||||
static size_t h2s_skip_data(struct h2s *h2s, struct buffer *buf, size_t count)
|
||||
{
|
||||
struct h2c *h2c = h2s->h2c;
|
||||
struct htx *htx;
|
||||
int bsize; /* htx block size */
|
||||
int fsize; /* h2 frame size */
|
||||
struct htx_blk *blk;
|
||||
enum htx_blk_type type;
|
||||
size_t total = 0;
|
||||
|
||||
TRACE_ENTER(H2_EV_TX_FRAME|H2_EV_TX_DATA, h2c->conn, h2s);
|
||||
|
||||
if (h2c_mux_busy(h2c, h2s)) {
|
||||
TRACE_STATE("mux output busy", H2_EV_TX_FRAME|H2_EV_TX_DATA, h2c->conn, h2s);
|
||||
h2s->flags |= H2_SF_BLK_MBUSY;
|
||||
TRACE_LEAVE(H2_EV_TX_FRAME|H2_EV_TX_DATA, h2c->conn, h2s);
|
||||
goto end;
|
||||
}
|
||||
|
||||
htx = htx_from_buf(buf);
|
||||
|
||||
next_data:
|
||||
if (!count || htx_is_empty(htx))
|
||||
goto end;
|
||||
blk = htx_get_head_blk(htx);
|
||||
type = htx_get_blk_type(blk);
|
||||
bsize = htx_get_blksz(blk);
|
||||
fsize = bsize;
|
||||
if (type != HTX_BLK_DATA)
|
||||
goto end;
|
||||
|
||||
if (fsize > count)
|
||||
fsize = count;
|
||||
|
||||
if (fsize != bsize)
|
||||
goto skip_data;
|
||||
|
||||
if (!(htx->flags & HTX_FL_EOM) || !htx_is_unique_blk(htx, blk))
|
||||
goto skip_data;
|
||||
|
||||
/* Here, it is the last block and it is also the end of the message. So
|
||||
* we can emit an empty DATA frame with the ES flag set
|
||||
*/
|
||||
if (h2_send_empty_data_es(h2s) <= 0)
|
||||
goto end;
|
||||
|
||||
if (h2s->st == H2_SS_OPEN)
|
||||
h2s->st = H2_SS_HLOC;
|
||||
else
|
||||
h2s_close(h2s);
|
||||
|
||||
skip_data:
|
||||
/* consume incoming HTX block */
|
||||
total += fsize;
|
||||
if (fsize == bsize) {
|
||||
TRACE_DEVEL("more data may be available, trying to skip another frame", H2_EV_TX_FRAME|H2_EV_TX_DATA, h2c->conn, h2s);
|
||||
htx_remove_blk(htx, blk);
|
||||
goto next_data;
|
||||
}
|
||||
else {
|
||||
/* we've truncated this block */
|
||||
htx_cut_data_blk(htx, blk, fsize);
|
||||
}
|
||||
|
||||
end:
|
||||
TRACE_LEAVE(H2_EV_TX_FRAME|H2_EV_TX_DATA, h2c->conn, h2s);
|
||||
return total;
|
||||
}
|
||||
|
||||
/* Try to send a HEADERS frame matching HTX_BLK_TLR series of blocks present in
|
||||
* HTX message <htx> for the H2 stream <h2s>. Returns the number of bytes
|
||||
* processed. The caller must check the stream's status to detect any error
|
||||
@ -6173,7 +6243,11 @@ static size_t h2_snd_buf(struct conn_stream *cs, struct buffer *buf, size_t coun
|
||||
|
||||
case HTX_BLK_DATA:
|
||||
/* all these cause the emission of a DATA frame (possibly empty) */
|
||||
ret = h2s_make_data(h2s, buf, count);
|
||||
if (!(h2s->h2c->flags & H2_CF_IS_BACK) &&
|
||||
(h2s->flags & (H2_SF_BODY_TUNNEL|H2_SF_BODYLESS_RESP)) == H2_SF_BODYLESS_RESP)
|
||||
ret = h2s_skip_data(h2s, buf, count);
|
||||
else
|
||||
ret = h2s_make_data(h2s, buf, count);
|
||||
if (ret > 0) {
|
||||
htx = htx_from_buf(buf);
|
||||
total += ret;
|
||||
|
Loading…
Reference in New Issue
Block a user