diff --git a/include/common/htx.h b/include/common/htx.h index 9f339b877..8d639a301 100644 --- a/include/common/htx.h +++ b/include/common/htx.h @@ -91,6 +91,7 @@ /* HTX flags */ #define HTX_FL_NONE 0x00000000 #define HTX_FL_PARSING_ERROR 0x00000001 +#define HTX_FL_UPGRADE 0x00000002 /* Pseudo header types (max 255). */ @@ -727,7 +728,7 @@ static inline struct htx *htx_from_buf(struct buffer *buf) /* Upate accordingly to the HTX message */ static inline void htx_to_buf(struct htx *htx, struct buffer *buf) { - if (!htx->used && !(htx->flags & HTX_FL_PARSING_ERROR)) { + if (!htx->used && !(htx->flags & (HTX_FL_PARSING_ERROR|HTX_FL_UPGRADE))) { htx_reset(htx); b_set_data(buf, 0); } diff --git a/src/mux_h1.c b/src/mux_h1.c index ef9dd01dc..5ea5f3c03 100644 --- a/src/mux_h1.c +++ b/src/mux_h1.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -48,6 +49,7 @@ #define H1C_F_CS_WAIT_CONN 0x00008000 /* waiting for the connection establishment */ #define H1C_F_WAIT_NEXT_REQ 0x00010000 /* waiting for the next request to start, use keep-alive timeout */ +#define H1C_F_UPG_H2C 0x00020000 /* set if an upgrade to h2 should be done */ /* * H1 Stream flags (32 bits) @@ -450,6 +452,16 @@ static void h1_release(struct h1c *h1c) conn = NULL; if (h1c) { + if (h1c->flags & H1C_F_UPG_H2C) { + h1c->flags &= ~H1C_F_UPG_H2C; + if (conn_upgrade_mux_fe(conn, NULL, &h1c->ibuf, ist("h2"), PROTO_MODE_HTX) != -1) { + /* connection successfully upgraded to H2, this + * mux was already released */ + return; + } + sess_log(conn->owner); /* Log if the upgrade failed */ + } + if (!LIST_ISEMPTY(&h1c->buf_wait.list)) { HA_SPIN_LOCK(BUF_WQ_LOCK, &buffer_wq_lock); LIST_DEL(&h1c->buf_wait.list); @@ -908,6 +920,13 @@ static size_t h1_process_headers(struct h1s *h1s, struct h1m *h1m, struct htx *h if (b_head(buf) + b_data(buf) > b_wrap(buf)) b_slow_realign(buf, trash.area, 0); + if (!(h1m->flags & H1_MF_RESP)) { + /* Try to match H2 preface before parsing the request headers. */ + ret = b_isteq(buf, 0, b_data(buf), ist(H2_CONN_PREFACE)); + if (ret > 0) + goto h2c_upgrade; + } + ret = h1_headers_to_hdr_list(b_peek(buf, *ofs), b_peek(buf, *ofs) + max, hdrs, sizeof(hdrs)/sizeof(hdrs[0]), h1m, &h1sl); if (ret <= 0) { @@ -1049,6 +1068,13 @@ static size_t h1_process_headers(struct h1s *h1s, struct h1m *h1m, struct htx *h h1_capture_bad_message(h1s->h1c, h1s, h1m, buf); ret = 0; goto end; + + h2c_upgrade: + h1s->h1c->flags |= H1C_F_UPG_H2C; + h1s->cs->flags |= CS_FL_REOS; + htx->flags |= HTX_FL_UPGRADE; + ret = 0; + goto end; } /* @@ -2067,7 +2093,7 @@ static void h1_detach(struct conn_stream *cs) } /* We don't want to close right now unless the connection is in error */ - if ((h1c->flags & (H1C_F_CS_ERROR|H1C_F_CS_SHUTDOWN)) || + if ((h1c->flags & (H1C_F_CS_ERROR|H1C_F_CS_SHUTDOWN|H1C_F_UPG_H2C)) || (h1c->conn->flags & CO_FL_ERROR) || !h1c->conn->owner) h1_release(h1c); else { @@ -2097,7 +2123,7 @@ static void h1_shutr(struct conn_stream *cs, enum cs_shr_mode mode) if ((cs->flags & CS_FL_KILL_CONN) || (h1c->conn->flags & (CO_FL_ERROR | CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH))) goto do_shutr; - if (h1s->flags & H1S_F_WANT_KAL) + if ((h1c->flags & H1C_F_UPG_H2C) || (h1s->flags & H1S_F_WANT_KAL)) return; do_shutr: @@ -2122,7 +2148,8 @@ static void h1_shutw(struct conn_stream *cs, enum cs_shw_mode mode) if ((cs->flags & CS_FL_KILL_CONN) || (h1c->conn->flags & (CO_FL_ERROR | CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH))) goto do_shutw; - if ((h1s->flags & H1S_F_WANT_KAL) && h1s->req.state == H1_MSG_DONE && h1s->res.state == H1_MSG_DONE) + if ((h1c->flags & H1C_F_UPG_H2C) || + ((h1s->flags & H1S_F_WANT_KAL) && h1s->req.state == H1_MSG_DONE && h1s->res.state == H1_MSG_DONE)) return; do_shutw: diff --git a/src/proto_htx.c b/src/proto_htx.c index 3837666b0..3c6df3079 100644 --- a/src/proto_htx.c +++ b/src/proto_htx.c @@ -145,6 +145,9 @@ int htx_wait_for_request(struct stream *s, struct channel *req, int an_bit) goto return_bad_req; } + if (htx->flags & HTX_FL_UPGRADE) + goto failed_keep_alive; + /* 1: have we encountered a read error ? */ if (req->flags & CF_READ_ERROR) { if (!(s->flags & SF_ERR_MASK))