mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-02-28 08:30:46 +00:00
MEDIUM: h2: parse Extended CONNECT request to htx
Support for the rfc 8441 Bootstraping WebSockets with HTTP/2 Convert an Extended CONNECT HTTP/2 request into a htx representation. The htx message uses the GET method with an Upgrade header field to be fully compatible with the equivalent HTTP/1.1 Upgrade mechanism. The Extended CONNECT is of the following form : :method = CONNECT :protocol = websocket :scheme = https :path = /chat :authority = server.example.com The new pseudo-header :protocol has been defined and is used to identify an Extended CONNECT method. Contrary to standard CONNECT, Extended CONNECT must have :scheme, :path and :authority defined.
This commit is contained in:
parent
efe2276a9e
commit
c9a0afcc32
@ -50,6 +50,7 @@ enum {
|
||||
H2_PHDR_IDX_SCHM = 4, /* :scheme = 6..7 */
|
||||
H2_PHDR_IDX_STAT = 5, /* :status = 8..14 */
|
||||
H2_PHDR_IDX_HOST = 6, /* Host, never returned, just a place-holder */
|
||||
H2_PHDR_IDX_PROT = 7, /* :protocol from rfc 8441 Extended Connect */
|
||||
H2_PHDR_NUM_ENTRIES /* must be last */
|
||||
};
|
||||
|
||||
@ -64,6 +65,7 @@ enum {
|
||||
H2_PHDR_FND_SCHM = 1 << H2_PHDR_IDX_SCHM,
|
||||
H2_PHDR_FND_STAT = 1 << H2_PHDR_IDX_STAT,
|
||||
H2_PHDR_FND_HOST = 1 << H2_PHDR_IDX_HOST,
|
||||
H2_PHDR_FND_PROT = 1 << H2_PHDR_IDX_PROT,
|
||||
};
|
||||
|
||||
/* frame types, from the standard */
|
||||
@ -305,6 +307,7 @@ static inline int h2_str_to_phdr(const struct ist str)
|
||||
else if (isteq(str, ist(":scheme"))) return H2_PHDR_IDX_SCHM;
|
||||
else if (isteq(str, ist(":status"))) return H2_PHDR_IDX_STAT;
|
||||
else if (isteq(str, ist(":authority"))) return H2_PHDR_IDX_AUTH;
|
||||
else if (isteq(str, ist(":protocol"))) return H2_PHDR_IDX_PROT;
|
||||
|
||||
/* all other names starting with ':' */
|
||||
return -1;
|
||||
|
80
src/h2.c
80
src/h2.c
@ -173,28 +173,64 @@ int h2_parse_cont_len_header(unsigned int *msgf, struct ist *value, unsigned lon
|
||||
*/
|
||||
static struct htx_sl *h2_prepare_htx_reqline(uint32_t fields, struct ist *phdr, struct htx *htx, unsigned int *msgf)
|
||||
{
|
||||
struct ist uri;
|
||||
struct ist uri, meth_sl;
|
||||
unsigned int flags = HTX_SL_F_NONE;
|
||||
struct htx_sl *sl;
|
||||
size_t i;
|
||||
|
||||
if ((fields & H2_PHDR_FND_METH) && isteq(phdr[H2_PHDR_IDX_METH], ist("CONNECT"))) {
|
||||
/* RFC 7540 #8.2.6 regarding CONNECT: ":scheme" and ":path"
|
||||
* MUST be omitted ; ":authority" contains the host and port
|
||||
* to connect to.
|
||||
*/
|
||||
if (fields & H2_PHDR_FND_SCHM) {
|
||||
/* scheme not allowed */
|
||||
goto fail;
|
||||
if (fields & H2_PHDR_FND_PROT) {
|
||||
/* rfc 8441 Extended Connect Protocol
|
||||
* #4 :scheme and :path must be present, as well as
|
||||
* :authority like all h2 requests
|
||||
*/
|
||||
if (!(fields & H2_PHDR_FND_SCHM)) {
|
||||
/* missing scheme */
|
||||
goto fail;
|
||||
}
|
||||
else if (!(fields & H2_PHDR_FND_PATH)) {
|
||||
/* missing path */
|
||||
goto fail;
|
||||
}
|
||||
else if (!(fields & H2_PHDR_FND_AUTH)) {
|
||||
/* missing authority */
|
||||
goto fail;
|
||||
}
|
||||
|
||||
flags |= HTX_SL_F_HAS_SCHM;
|
||||
if (isteqi(phdr[H2_PHDR_IDX_SCHM], ist("http")))
|
||||
flags |= HTX_SL_F_SCHM_HTTP;
|
||||
else if (isteqi(phdr[H2_PHDR_IDX_SCHM], ist("https")))
|
||||
flags |= HTX_SL_F_SCHM_HTTPS;
|
||||
|
||||
meth_sl = ist("GET");
|
||||
|
||||
*msgf |= H2_MSGF_EXT_CONNECT;
|
||||
/* no ES on the HEADERS frame but no body either for
|
||||
* Extended CONNECT */
|
||||
*msgf &= ~H2_MSGF_BODY;
|
||||
}
|
||||
else if (fields & H2_PHDR_FND_PATH) {
|
||||
/* path not allowed */
|
||||
goto fail;
|
||||
}
|
||||
else if (!(fields & H2_PHDR_FND_AUTH)) {
|
||||
/* missing authority */
|
||||
goto fail;
|
||||
else {
|
||||
/* RFC 7540 #8.2.6 regarding CONNECT: ":scheme" and ":path"
|
||||
* MUST be omitted ; ":authority" contains the host and port
|
||||
* to connect to.
|
||||
*/
|
||||
if (fields & H2_PHDR_FND_SCHM) {
|
||||
/* scheme not allowed */
|
||||
goto fail;
|
||||
}
|
||||
else if (fields & H2_PHDR_FND_PATH) {
|
||||
/* path not allowed */
|
||||
goto fail;
|
||||
}
|
||||
else if (!(fields & H2_PHDR_FND_AUTH)) {
|
||||
/* missing authority */
|
||||
goto fail;
|
||||
}
|
||||
|
||||
meth_sl = phdr[H2_PHDR_IDX_METH];
|
||||
}
|
||||
|
||||
*msgf |= H2_MSGF_BODY_TUNNEL;
|
||||
}
|
||||
else if ((fields & (H2_PHDR_FND_METH|H2_PHDR_FND_SCHM|H2_PHDR_FND_PATH)) !=
|
||||
@ -230,6 +266,8 @@ static struct htx_sl *h2_prepare_htx_reqline(uint32_t fields, struct ist *phdr,
|
||||
flags |= HTX_SL_F_SCHM_HTTP;
|
||||
else if (isteqi(phdr[H2_PHDR_IDX_SCHM], ist("https")))
|
||||
flags |= HTX_SL_F_SCHM_HTTPS;
|
||||
|
||||
meth_sl = phdr[H2_PHDR_IDX_METH];
|
||||
}
|
||||
|
||||
if (!(flags & HTX_SL_F_HAS_SCHM)) {
|
||||
@ -289,11 +327,11 @@ static struct htx_sl *h2_prepare_htx_reqline(uint32_t fields, struct ist *phdr,
|
||||
flags |= HTX_SL_F_VER_11; // V2 in fact
|
||||
flags |= HTX_SL_F_XFER_LEN; // xfer len always known with H2
|
||||
|
||||
sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, phdr[H2_PHDR_IDX_METH], uri, ist("HTTP/2.0"));
|
||||
sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, meth_sl, uri, ist("HTTP/2.0"));
|
||||
if (!sl)
|
||||
goto fail;
|
||||
|
||||
sl->info.req.meth = find_http_meth(phdr[H2_PHDR_IDX_METH].ptr, phdr[H2_PHDR_IDX_METH].len);
|
||||
sl->info.req.meth = find_http_meth(meth_sl.ptr, meth_sl.len);
|
||||
if (sl->info.req.meth == HTTP_METH_HEAD)
|
||||
*msgf |= H2_MSGF_BODYLESS_RSP;
|
||||
return sl;
|
||||
@ -457,6 +495,14 @@ int h2_make_htx_request(struct http_hdr *list, struct htx *htx, unsigned int *ms
|
||||
htx->flags |= HTX_FL_EOM;
|
||||
}
|
||||
|
||||
if (*msgf & H2_MSGF_EXT_CONNECT) {
|
||||
if (!htx_add_header(htx, ist("upgrade"), phdr_val[H2_PHDR_IDX_PROT]))
|
||||
goto fail;
|
||||
if (!htx_add_header(htx, ist("connection"), ist("upgrade")))
|
||||
goto fail;
|
||||
sl_flags |= HTX_SL_F_CONN_UPG;
|
||||
}
|
||||
|
||||
/* update the start line with last detected header info */
|
||||
sl->flags |= sl_flags;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user