diff --git a/src/h2.c b/src/h2.c index a50c15c7f..2d9410d82 100644 --- a/src/h2.c +++ b/src/h2.c @@ -445,6 +445,9 @@ int h2_make_htx_request(struct http_hdr *list, struct htx *htx, unsigned int *ms goto fail; } + if (*msgf & H2_MSGF_BODY_TUNNEL) + *msgf &= ~(H2_MSGF_BODY|H2_MSGF_BODY_CL); + if (!(*msgf & H2_MSGF_BODY) || ((*msgf & H2_MSGF_BODY_CL) && *body_len == 0)) sl_flags |= HTX_SL_F_BODYLESS; @@ -682,6 +685,11 @@ int h2_make_htx_response(struct http_hdr *list, struct htx *htx, unsigned int *m goto fail; } + if ((*msgf & H2_MSGF_BODY_TUNNEL) && sl->info.res.status >= 200 && sl->info.res.status < 300) + *msgf &= ~(H2_MSGF_BODY|H2_MSGF_BODY_CL); + else + *msgf &= ~H2_MSGF_BODY_TUNNEL; + if (!(*msgf & H2_MSGF_BODY) || ((*msgf & H2_MSGF_BODY_CL) && *body_len == 0)) sl_flags |= HTX_SL_F_BODYLESS; diff --git a/src/mux_h2.c b/src/mux_h2.c index ed42859f0..f367cd172 100644 --- a/src/mux_h2.c +++ b/src/mux_h2.c @@ -179,7 +179,9 @@ enum h2_ss { /* stream flags indicating how data is supposed to be sent */ #define H2_SF_DATA_CLEN 0x00000100 // data sent using content-length -/* unused flags: 0x00000200, 0x00000400 */ +/* unused flags: 0x00000200 */ +#define H2_SF_BODY_TUNNEL 0x00000400 // Attempt to establish a Tunnelled stream (the result depends on the status code) + #define H2_SF_NOTIFIED 0x00000800 // a paused stream was notified to try to send again #define H2_SF_HEADERS_SENT 0x00001000 // a HEADERS frame was sent for this stream @@ -191,6 +193,8 @@ enum h2_ss { #define H2_SF_WANT_SHUTW 0x00010000 // a stream couldn't shutw() (mux full/busy) #define H2_SF_KILL_CONN 0x00020000 // kill the whole connection with this stream +#define H2_SF_TUNNEL_ABRT 0x00100000 // A tunnel attempt was aborted + /* H2 stream descriptor, describing the stream as it appears in the H2C, and as * it is being processed in the internal HTTP representation (HTX). @@ -4641,6 +4645,7 @@ next_frame: /* OK now we have our header list in */ msgf = (h2c->dff & H2_F_HEADERS_END_STREAM) ? 0 : H2_MSGF_BODY; + msgf |= (*flags & H2_SF_BODY_TUNNEL) ? H2_MSGF_BODY_TUNNEL: 0; if (*flags & H2_SF_HEADERS_RCVD) goto trailers; @@ -4665,6 +4670,15 @@ next_frame: } } + if (msgf & H2_MSGF_BODY_TUNNEL) + *flags |= H2_SF_BODY_TUNNEL; + else { + /* Abort the tunnel attempt, if any */ + if (*flags & H2_SF_BODY_TUNNEL) + *flags |= H2_SF_TUNNEL_ABRT; + *flags &= ~H2_SF_BODY_TUNNEL; + } + done: /* indicate that a HEADERS frame was received for this stream, except * for 1xx responses. For 1xx responses, another HEADERS frame is @@ -4897,6 +4911,11 @@ static size_t h2s_frt_make_resp_headers(struct h2s *h2s, struct htx *htx) TRACE_ERROR("will not encode an invalid status code", H2_EV_TX_FRAME|H2_EV_TX_HDR|H2_EV_H2S_ERR, h2c->conn, h2s); goto fail; } + else if ((h2s->flags & H2_SF_BODY_TUNNEL) && h2s->status >= 300) { + /* Abort the tunnel attempt */ + h2s->flags &= ~H2_SF_BODY_TUNNEL; + h2s->flags |= H2_SF_TUNNEL_ABRT; + } /* and the rest of the headers, that we dump starting at header 0 */ hdr = 0; @@ -5362,13 +5381,17 @@ static size_t h2s_bck_make_req_headers(struct h2s *h2s, struct htx *htx) * - request already closed, or : * - no transfer-encoding, and : * - no content-length or content-length:0 - * Fixme: this doesn't take into account CONNECT requests. + * except for CONNECT requests. */ - if (blk_end && htx_get_blk_type(blk_end) == HTX_BLK_EOM) - es_now = 1; - - if (sl->flags & HTX_SL_F_BODYLESS) - es_now = 1; + if (likely(sl->info.req.meth != HTTP_METH_CONNECT)) { + if (blk_end && htx_get_blk_type(blk_end) == HTX_BLK_EOM) + es_now = 1; + if (sl->flags & HTX_SL_F_BODYLESS) + es_now = 1; + } + else { + h2s->flags |= H2_SF_BODY_TUNNEL; + } if (!h2s->cs || h2s->cs->flags & CS_FL_SHW) es_now = 1; @@ -6021,8 +6044,12 @@ static size_t h2_rcv_buf(struct conn_stream *cs, struct buffer *buf, size_t coun cs->flags |= (CS_FL_RCV_MORE | CS_FL_WANT_ROOM); else { cs->flags &= ~(CS_FL_RCV_MORE | CS_FL_WANT_ROOM); - if (h2s->flags & H2_SF_ES_RCVD) + if (h2s->flags & H2_SF_ES_RCVD) { cs->flags |= CS_FL_EOI; + /* Add EOS flag for tunnel */ + if (h2s->flags & H2_SF_BODY_TUNNEL) + cs->flags |= CS_FL_EOS; + } if (h2c_read0_pending(h2c) || h2s->st == H2_SS_CLOSED) cs->flags |= CS_FL_EOS; if (cs->flags & CS_FL_ERR_PENDING)