Skip to content

Commit

Permalink
Merge pull request #1374 from tempesta-tech/ik-fix-req-2-to-1-transform
Browse files Browse the repository at this point in the history
Fix request h2 -> http1.1 conversion
  • Loading branch information
vankoven authored Jan 21, 2020
2 parents 1d15fab + c114bed commit f010557
Show file tree
Hide file tree
Showing 11 changed files with 516 additions and 413 deletions.
602 changes: 372 additions & 230 deletions tempesta_fw/http.c

Large diffs are not rendered by default.

23 changes: 18 additions & 5 deletions tempesta_fw/http_frame.c
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ tfw_h2_context_init(TfwH2Ctx *ctx)
lset->hdr_tbl_sz = rset->hdr_tbl_sz = HPACK_TABLE_DEF_SIZE;
lset->push = rset->push = 1;
lset->max_streams = rset->max_streams = 0xffffffff;
lset->max_frame_sz = rset->max_frame_sz = 1 << 14;
lset->max_frame_sz = rset->max_frame_sz = FRAME_DEF_LENGTH;
lset->max_lhdr_sz = rset->max_lhdr_sz = UINT_MAX;
/*
* We ignore client's window size until #498, so currently
Expand Down Expand Up @@ -915,6 +915,8 @@ tfw_h2_apply_settings_entry(TfwH2Ctx *ctx, unsigned short id,
break;

case HTTP2_SETTINGS_ENABLE_PUSH:
if (val > 1)
return T_BAD;
dest->push = val;
break;

Expand All @@ -923,10 +925,14 @@ tfw_h2_apply_settings_entry(TfwH2Ctx *ctx, unsigned short id,
break;

case HTTP2_SETTINGS_INIT_WND_SIZE:
if (val > MAX_WND_SIZE)
return T_BAD;
dest->wnd_sz = val;
break;

case HTTP2_SETTINGS_MAX_FRAME_SIZE:
if (val < FRAME_DEF_LENGTH || val > FRAME_MAX_LENGTH)
return T_BAD;
dest->max_frame_sz = val;
break;

Expand Down Expand Up @@ -1177,6 +1183,17 @@ tfw_h2_frame_type_process(TfwH2Ctx *ctx)
T_DBG3("%s: hdr->type=%hhu, ctx->state=%d\n", __func__, hdr->type,
ctx->state);

if (unlikely(ctx->hdr.length > ctx->lsettings.max_frame_sz))
goto conn_term;

/*
* TODO: RFC 7540 Section 6.2:
* A HEADERS frame without the END_HEADERS flag set MUST be followed
* by a CONTINUATION frame for the same stream. A receiver MUST treat
* the receipt of any other type of frame or a frame on a different
* stream as a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
*/

switch (hdr->type) {
case HTTP2_DATA:
if (!hdr->stream_id) {
Expand Down Expand Up @@ -1207,8 +1224,6 @@ tfw_h2_frame_type_process(TfwH2Ctx *ctx)
if (tfw_h2_flow_control(ctx))
return T_DROP;

STREAM_RECV_PROCESS(ctx, hdr);

ctx->data_off = FRAME_HEADER_SIZE;
ctx->plen = ctx->hdr.length;

Expand Down Expand Up @@ -1417,8 +1432,6 @@ tfw_h2_frame_type_process(TfwH2Ctx *ctx)
goto conn_term;
}

STREAM_RECV_PROCESS(ctx, hdr);

ctx->data_off = FRAME_HEADER_SIZE;
ctx->plen = ctx->hdr.length;

Expand Down
2 changes: 2 additions & 0 deletions tempesta_fw/http_frame.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@
#include "http_stream.h"
#include "hpack.h"

/* RFC 7540 Section 4.1 frame header constants. */
#define FRAME_HEADER_SIZE 9
#define FRAME_STREAM_ID_MASK ((1U << 31) - 1)
#define FRAME_RESERVED_BIT_MASK (~FRAME_STREAM_ID_MASK)
#define FRAME_MAX_LENGTH ((1U << 24) - 1)
#define FRAME_DEF_LENGTH (16384)

/**
* HTTP/2 frame types (RFC 7540 section 6).
Expand Down
19 changes: 12 additions & 7 deletions tempesta_fw/http_msg.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@ tfw_http_msg_make_hdr(TfwPool *pool, const char *name, const char *val)
const TfwStr tmp_hdr = {
.chunks = (TfwStr []){
{ .data = (void *)name, .len = n_len },
{ .data = S_DLM, .len = SLEN(S_DLM) },
{ .data = (void *)val, .len = v_len },
{ .data = (void *)val, .len = v_len,
.flags = TFW_STR_HDR_VALUE},
},
.len = n_len + SLEN(S_DLM) + v_len,
.len = n_len + v_len,
.eolen = 2,
.nchunks = (val ? 3 : 2)
.nchunks = (val ? 2 : 1)
};

return tfw_strdup(pool, &tmp_hdr);
Expand Down Expand Up @@ -621,7 +621,8 @@ int
__tfw_http_msg_add_str_data(TfwHttpMsg *hm, TfwStr *str, void *data,
size_t len, struct sk_buff *skb)
{
BUG_ON(str->flags & (TFW_STR_DUPLICATE | TFW_STR_COMPLETE));
if (WARN_ON_ONCE(str->flags & (TFW_STR_DUPLICATE | TFW_STR_COMPLETE)))
return -EINVAL;

T_DBG3("store field chunk len=%lu data=%p(%c) field=<%#x,%lu,%p>\n",
len, data, isprint(*(char *)data) ? *(char *)data : '.',
Expand Down Expand Up @@ -862,9 +863,11 @@ tfw_http_msg_hdr_xfrm_str(TfwHttpMsg *hm, const TfwStr *hdr, unsigned int hid,
if (hid == ht->off && !s_val)
/* Not found, nothing to delete. */
return 0;
if (hid == ht->size)
if (hid == ht->size) {
if (tfw_http_msg_grow_hdr_tbl(hm))
return -ENOMEM;
ht = hm->h_tbl;
}
if (hid == ht->off)
++ht->off;
else
Expand Down Expand Up @@ -1097,9 +1100,11 @@ tfw_http_msg_hdr_add(TfwHttpMsg *hm, const TfwStr *hdr)

ht = hm->h_tbl;
hid = ht->off;
if (hid == ht->size)
if (hid == ht->size) {
if (tfw_http_msg_grow_hdr_tbl(hm))
return -ENOMEM;
ht = hm->h_tbl;
}
++ht->off;

return __hdr_add(hm, hdr, hid);
Expand Down
85 changes: 56 additions & 29 deletions tempesta_fw/http_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -7956,14 +7956,22 @@ tfw_h2_parse_body(char *data, unsigned long len, TfwHttpReq *req,
return T_DROP;

parser->to_read = -1;
req->body.flags |= TFW_STR_COMPLETE;
ret = T_OK;

out:
*parsed += m_len;
return ret;
}

/**
* Parse h2 request.
*
* Parsing of HTTP/2 frames' payload never gives T_OK result since request
* can be assembled from different number of frames; only stream's state can
* indicate the moment when request is completed. After all parts of request are
* fully received and parsed, call the @tfw_h2_parse_req_finish() to check the
* parser state.
*/
int
tfw_h2_parse_req(void *req_data, unsigned char *data, size_t len,
unsigned int *parsed)
Expand All @@ -7980,42 +7988,61 @@ tfw_h2_parse_req(void *req_data, unsigned char *data, size_t len,

*parsed = 0;

if (likely(type != HTTP2_DATA)) {
if (likely(type != HTTP2_DATA))
r = tfw_hpack_decode(&ctx->hpack, data, len, req, parsed);
req->pit.hb_len += *parsed;
} else {
else
r = tfw_h2_parse_body(data, len, req, parsed);
}

if (r != T_OK && r != T_POSTPONE)
return r;
/*
* Parsing of HTTP/2 frames' payload never gives T_OK result since
* request can be assembled from different number of frames; only
* stream's state can indicate the moment when request is completed.
* Note, due to persistent linkage Stream<->Request in HTTP/2 mode
* special flag is necessary for tracking the request completeness; if
* the request is not fully assembled and is not passed to forwarding
* procedures yet, it must be deleted during corresponding stream
* removal.
*/
if (tfw_h2_stream_req_complete(req->stream)) {
TfwHttpHdrTbl *ht = req->h_tbl;
return (r == T_OK) ? T_POSTPONE : r;
}

if (req->content_length
&& req->content_length != req->body.len)
return T_DROP;
/**
* Finish parsing h2 request. The request may consist of multiple skbs and
* multiple h2 frames. Last frame is marked with End Stream flag, it's the
* only way to indicate message end.
*/
int
tfw_h2_parse_req_finish(TfwHttpReq *req)
{
TfwHttpHdrTbl *ht = req->h_tbl;

__set_bit(TFW_HTTP_B_FULLY_PARSED, req->flags);
if (WARN_ON_ONCE(!tfw_h2_stream_req_complete(req->stream)))
return T_DROP;

__h2_msg_hdr_val(&ht->tbl[TFW_HTTP_HDR_H2_AUTHORITY],
&req->host);
__h2_msg_hdr_val(&ht->tbl[TFW_HTTP_HDR_H2_PATH],
&req->uri_path);
return T_OK;
/*
* TFW_HTTP_B_H2_HDRS_FULL flag is set on first TFW_HTTP_HDR_REGULAR
* header, if no present, need to check mandatory pseudo headers.
*/
if (unlikely(!test_bit(TFW_HTTP_B_H2_HDRS_FULL, req->flags)
&& (TFW_STR_EMPTY(&ht->tbl[TFW_HTTP_HDR_H2_METHOD])
|| TFW_STR_EMPTY(&ht->tbl[TFW_HTTP_HDR_H2_SCHEME])
|| TFW_STR_EMPTY(&ht->tbl[TFW_HTTP_HDR_H2_PATH]))))
{
return T_DROP;
}
if (req->content_length
&& req->content_length != req->body.len)
{
return T_DROP;
}
/*
* RFC 7540 8.1.2.6: A request or response that includes a payload
* body can include a content-length header field.
*
* Since the h2 provides explicit message framing, the content-length
* header is not required at all. But our code may rely on
* req->content_length field value, fill it.
*/
req->content_length = req->body.len;
req->body.flags |= TFW_STR_COMPLETE;
__set_bit(TFW_HTTP_B_FULLY_PARSED, req->flags);

__h2_msg_hdr_val(&ht->tbl[TFW_HTTP_HDR_H2_AUTHORITY],
&req->host);
__h2_msg_hdr_val(&ht->tbl[TFW_HTTP_HDR_H2_PATH],
&req->uri_path);

return T_POSTPONE;
return T_OK;
}

/*
Expand Down
1 change: 1 addition & 0 deletions tempesta_fw/http_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ int tfw_h2_parse_req_hdr(unsigned char *data, unsigned long len, TfwHttpReq *req
bool fin, bool value_stage);
int tfw_h2_parse_req(void *req_data, unsigned char *data, size_t len,
unsigned int *parsed);
int tfw_h2_parse_req_finish(TfwHttpReq *req);
int tfw_http_parse_resp(void *resp_data, unsigned char *data, size_t len,
unsigned int *parsed);
int tfw_http_parse_terminate(TfwHttpMsg *hm);
Expand Down
20 changes: 15 additions & 5 deletions tempesta_fw/http_stream.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,18 +105,28 @@ tfw_h2_stream_fsm(TfwStream *stream, unsigned char type, unsigned char flags,
* (remote)' state.
*/
if (type == HTTP2_HEADERS) {
if (flags & (HTTP2_F_END_STREAM | HTTP2_F_END_HEADERS)) {
switch (flags
& (HTTP2_F_END_HEADERS | HTTP2_F_END_STREAM))
{
case HTTP2_F_END_HEADERS | HTTP2_F_END_STREAM:
stream->state = HTTP2_STREAM_REM_HALF_CLOSED;
}
break;
case HTTP2_F_END_HEADERS:
/*
* Headers is ended, next frame in the stream
* should be DATA frame.
*/
break;
/*
* If END_HEADERS flag is not received, move stream into
* the states of waiting CONTINUATION frame.
*/
else if (flags & HTTP2_F_END_STREAM) {
case HTTP2_F_END_STREAM:
stream->state = HTTP2_STREAM_CONT_CLOSED;
}
else if (!(flags & HTTP2_F_END_HEADERS)) {
break;
default:
stream->state = HTTP2_STREAM_CONT;
break;
}
}
else if (type == HTTP2_DATA && (flags & HTTP2_F_END_STREAM)) {
Expand Down
2 changes: 0 additions & 2 deletions tempesta_fw/msg.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ typedef struct {
*
* @pool - allocation pool for target buffer of decoded headers;
* @parsed_hdr - pointer to the message header which is currently processed;
* @hb_len - length of the message HTTP/2 headers block;
* @hdrs_len - accumulated length of message's decoded and parsed headers;
* @hdrs_cnt - count of all headers from message headers block;
* @__off - offset for iterator reinitializing before next processing
Expand All @@ -84,7 +83,6 @@ typedef struct {
typedef struct {
TfwPool *pool;
TfwStr *parsed_hdr;
unsigned long hb_len;
unsigned long hdrs_len;
unsigned int hdrs_cnt;
char __off[0];
Expand Down
Loading

0 comments on commit f010557

Please sign in to comment.