]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: mux-h2: Add 2 flags to help to properly handle tunnel mode
authorChristopher Faulet <cfaulet@haproxy.com>
Fri, 22 Jan 2021 10:46:30 +0000 (11:46 +0100)
committerChristopher Faulet <cfaulet@haproxy.com>
Thu, 28 Jan 2021 15:37:14 +0000 (16:37 +0100)
H2_SF_BODY_TUNNEL and H2_SF_TUNNEL_ABRT flags are added to properly handle
the tunnel mode in the H2 mux. The first one is used to detect tunnel
establishment or fully established tunnel. The second one is used to abort a
tunnel attempt. It is the first commit having as a goal to fix tunnel
establishment between H1 and H2 muxes.

There is a subtlety in h2_rcv_buf(). CS_FL_EOS flag is added on the
conn-stream when ES is received on a tunneled stream. It really reflects the
conn-stream state and is mandatory for next commits.

src/h2.c
src/mux_h2.c

index a50c15c7fb797c98c18af51641cdbb13527025a7..2d9410d827bf7471f069640dd2869e3bd8249d53 100644 (file)
--- 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;
 
index ed42859f0a46e1b907a314c021d1cfa4cbcf215e..f367cd172e9ae5176192c8d86d2e72f0fbc5cc43 100644 (file)
@@ -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 <list> */
        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)