{
unsigned int status, flags = HTX_SL_F_NONE;
struct htx_sl *sl;
- unsigned char h, t, u;
+ struct ist stat;
/* only :status is allowed as a pseudo header */
if (!(fields & H2_PHDR_FND_STAT))
if (phdr[H2_PHDR_IDX_STAT].len != 3)
goto fail;
- h = phdr[H2_PHDR_IDX_STAT].ptr[0] - '0';
- t = phdr[H2_PHDR_IDX_STAT].ptr[1] - '0';
- u = phdr[H2_PHDR_IDX_STAT].ptr[2] - '0';
- if (h > 9 || t > 9 || u > 9)
- goto fail;
- status = h * 100 + t * 10 + u;
+ /* if Extended CONNECT is used, convert status code from 200 to htx 101
+ * following rfc 8441 */
+ if (unlikely(*msgf & H2_MSGF_EXT_CONNECT) &&
+ isteq(phdr[H2_PHDR_IDX_STAT], ist("200"))) {
+ stat = ist("101");
+ status = 101;
+ }
+ else {
+ unsigned char h, t, u;
+
+ stat = phdr[H2_PHDR_IDX_STAT];
+
+ h = stat.ptr[0] - '0';
+ t = stat.ptr[1] - '0';
+ u = stat.ptr[2] - '0';
+ if (h > 9 || t > 9 || u > 9)
+ goto fail;
+ status = h * 100 + t * 10 + u;
+ }
/* 101 responses are not supported in H2, so return a error.
* On 1xx responses there is no ES on the HEADERS frame but there is no
* notify the decoder another HEADERS frame is expected.
* 204/304 resposne have no body by definition. So remove the flag
* H2_MSGF_BODY and set H2_MSGF_BODYLESS_RSP.
+ *
+ * Note however that there is a special condition for Extended CONNECT.
+ * In this case, we explicitly convert it to HTX 101 to mimic
+ * Get+Upgrade HTTP/1.1 mechanism
*/
- if (status == 101)
- goto fail;
+ if (status == 101) {
+ if (!(*msgf & H2_MSGF_EXT_CONNECT))
+ goto fail;
+ }
else if (status < 200) {
*msgf |= H2_MSGF_RSP_1XX;
*msgf &= ~H2_MSGF_BODY;
}
- else if (sl->info.res.status == 204 || sl->info.res.status == 304) {
+ else if (status == 204 || status == 304) {
*msgf &= ~H2_MSGF_BODY;
*msgf |= H2_MSGF_BODYLESS_RSP;
}
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_RES_SL, flags, ist("HTTP/2.0"), phdr[H2_PHDR_IDX_STAT], ist(""));
+ sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, ist("HTTP/2.0"), stat, ist(""));
if (!sl)
goto fail;
sl->info.res.status = status;
* - n.name ignored, n.len == 0 : end of list
* - in all cases except the end of list, v.name and v.len must designate a
* valid value.
+ *
+ * <upgrade_protocol> is only used if the htx status code is 101 indicating a
+ * response to an upgrade or h2-equivalent request.
*/
-int h2_make_htx_response(struct http_hdr *list, struct htx *htx, unsigned int *msgf, unsigned long long *body_len)
+int h2_make_htx_response(struct http_hdr *list, struct htx *htx, unsigned int *msgf, unsigned long long *body_len, char *upgrade_protocol)
{
struct ist phdr_val[H2_PHDR_NUM_ENTRIES];
uint32_t fields; /* bit mask of H2_PHDR_FND_* */
goto fail;
}
- if ((*msgf & H2_MSGF_BODY_TUNNEL) && sl->info.res.status >= 200 && sl->info.res.status < 300)
+ if (sl->info.res.status == 101 && upgrade_protocol) {
+ if (!htx_add_header(htx, ist("connection"), ist("upgrade")))
+ goto fail;
+ if (!htx_add_header(htx, ist("upgrade"), ist(upgrade_protocol)))
+ goto fail;
+ sl_flags |= HTX_SL_F_CONN_UPG;
+ }
+
+ if ((*msgf & H2_MSGF_BODY_TUNNEL) &&
+ ((sl->info.res.status >= 200 && sl->info.res.status < 300) || sl->info.res.status == 101))
*msgf &= ~(H2_MSGF_BODY|H2_MSGF_BODY_CL);
else
*msgf &= ~H2_MSGF_BODY_TUNNEL;
struct list list; /* To be used when adding in h2c->send_list or h2c->fctl_lsit */
struct tasklet *shut_tl; /* deferred shutdown tasklet, to retry to send an RST after we failed to,
* in case there's no other subscription to do it */
+
+ char upgrade_protocol[16]; /* rfc 8441: requested protocol on Extended CONNECT */
};
/* descriptor for an h2 frame header */
/* h2_io_cb is exported to see it resolved in "show fd" */
struct task *h2_io_cb(struct task *t, void *ctx, unsigned short state);
static inline struct h2s *h2c_st_by_id(struct h2c *h2c, int id);
-static int h2c_decode_headers(struct h2c *h2c, struct buffer *rxbuf, uint32_t *flags, unsigned long long *body_len);
+static int h2c_decode_headers(struct h2c *h2c, struct buffer *rxbuf, uint32_t *flags, unsigned long long *body_len, char *upgrade_protocol);
static int h2_frt_transfer_data(struct h2s *h2s);
static struct task *h2_deferred_shut(struct task *t, void *ctx, unsigned short state);
static struct h2s *h2c_bck_stream_new(struct h2c *h2c, struct conn_stream *cs, struct session *sess);
h2s->status = 0;
h2s->body_len = 0;
h2s->rxbuf = BUF_NULL;
+ memset(h2s->upgrade_protocol, 0, sizeof(h2s->upgrade_protocol));
h2s->by_id.key = h2s->id = id;
if (id > 0)
if (h2s->st != H2_SS_IDLE) {
/* The stream exists/existed, this must be a trailers frame */
if (h2s->st != H2_SS_CLOSED) {
- error = h2c_decode_headers(h2c, &h2s->rxbuf, &h2s->flags, &body_len);
+ error = h2c_decode_headers(h2c, &h2s->rxbuf, &h2s->flags, &body_len, NULL);
/* unrecoverable error ? */
if (h2c->st0 >= H2_CS_ERROR)
goto out;
/* the connection was already killed by an RST, let's consume
* the data and send another RST.
*/
- error = h2c_decode_headers(h2c, &rxbuf, &flags, &body_len);
+ error = h2c_decode_headers(h2c, &rxbuf, &flags, &body_len, NULL);
h2s = (struct h2s*)h2_error_stream;
goto send_rst;
}
else if (h2c->flags & H2_CF_DEM_TOOMANY)
goto out; // IDLE but too many cs still present
- error = h2c_decode_headers(h2c, &rxbuf, &flags, &body_len);
+ error = h2c_decode_headers(h2c, &rxbuf, &flags, &body_len, NULL);
/* unrecoverable error ? */
if (h2c->st0 >= H2_CS_ERROR)
goto fail; // incomplete frame
if (h2s->st != H2_SS_CLOSED) {
- error = h2c_decode_headers(h2c, &h2s->rxbuf, &h2s->flags, &h2s->body_len);
+ error = h2c_decode_headers(h2c, &h2s->rxbuf, &h2s->flags, &h2s->body_len, h2s->upgrade_protocol);
}
else {
/* the connection was already killed by an RST, let's consume
* the data and send another RST.
*/
- error = h2c_decode_headers(h2c, &rxbuf, &flags, &body_len);
+ error = h2c_decode_headers(h2c, &rxbuf, &flags, &body_len, NULL);
h2s = (struct h2s*)h2_error_stream;
h2c->st0 = H2_CS_FRAME_E;
goto send_rst;
* decoding, in order to detect if we're dealing with a headers or a trailers
* block (the trailers block appears after H2_SF_HEADERS_RCVD was seen).
*/
-static int h2c_decode_headers(struct h2c *h2c, struct buffer *rxbuf, uint32_t *flags, unsigned long long *body_len)
+static int h2c_decode_headers(struct h2c *h2c, struct buffer *rxbuf, uint32_t *flags, unsigned long long *body_len, char *upgrade_protocol)
{
const uint8_t *hdrs = (uint8_t *)b_head(&h2c->dbuf);
struct buffer *tmp = get_trash_chunk();
/* 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 an Extended CONNECT has been sent on this stream, set message flag
+ * to convert 200 response to 101 htx reponse */
+ msgf |= (*flags & H2_SF_EXT_CONNECT_SENT) ? H2_MSGF_EXT_CONNECT: 0;
if (*flags & H2_SF_HEADERS_RCVD)
goto trailers;
/* This is the first HEADERS frame so it's a headers block */
if (h2c->flags & H2_CF_IS_BACK)
- outlen = h2_make_htx_response(list, htx, &msgf, body_len);
+ outlen = h2_make_htx_response(list, htx, &msgf, body_len, upgrade_protocol);
else
outlen = h2_make_htx_request(list, htx, &msgf, body_len);