diff --git a/fw/cache.c b/fw/cache.c index 212821ff2a..897b584a4c 100644 --- a/fw/cache.c +++ b/fw/cache.c @@ -840,8 +840,8 @@ tfw_cache_send_304(TfwHttpReq *req, TfwCacheEntry *ce) if (unlikely(r)) goto err_setup; } else { - stream_id = tfw_h2_stream_id_close(req, HTTP2_HEADERS, - HTTP2_F_END_STREAM); + stream_id = tfw_h2_stream_id_send(req, HTTP2_HEADERS, + HTTP2_F_END_STREAM); if (unlikely(!stream_id)) goto err_setup; @@ -889,6 +889,7 @@ tfw_cache_send_304(TfwHttpReq *req, TfwCacheEntry *ce) if (tfw_h2_frame_local_resp(resp, stream_id, h_len, NULL)) goto err_setup; + tfw_h2_stream_id_unlink(req, false, true); tfw_h2_resp_fwd(resp); return; @@ -2520,6 +2521,10 @@ tfw_cache_build_resp_body(TDB *db, TdbVRec *trec, TfwMsgIter *it, char *p, h2, !body_sz); if (r) return r; + if (stream_id) { + skb_set_tfw_flags(it->skb, SS_F_HTTT2_FRAME_DATA); + skb_set_tfw_cb(it->skb, stream_id); + } } if (!body_sz || !(trec = tdb_next_rec_chunk(db, trec))) break; @@ -2807,8 +2812,7 @@ cache_req_process_node(TfwHttpReq *req, tfw_http_cache_cb_t action) * the backend), thus the stream will be finished. */ if (resp && TFW_MSG_H2(req)) { - id = tfw_h2_stream_id_close(req, HTTP2_HEADERS, - HTTP2_F_END_STREAM); + id = tfw_h2_stream_id_unlink(req, false, true); if (unlikely(!id)) { tfw_http_msg_free((TfwHttpMsg *)resp); tfw_http_conn_msg_free((TfwHttpMsg *)req); diff --git a/fw/hpack.c b/fw/hpack.c index 52fbbcf6ba..8a68b847dc 100644 --- a/fw/hpack.c +++ b/fw/hpack.c @@ -3772,6 +3772,7 @@ __tfw_hpack_encode(TfwHttpResp *__restrict resp, TfwStr *__restrict hdr, TfwHPackETbl *tbl = &ctx->hpack.enc_tbl; int r = HPACK_IDX_ST_NOT_FOUND; bool name_indexed = true; + struct sk_buff *prev = resp->mit.iter.skb; if (WARN_ON_ONCE(!hdr || TFW_STR_EMPTY(hdr))) return -EINVAL; @@ -3808,7 +3809,7 @@ __tfw_hpack_encode(TfwHttpResp *__restrict resp, TfwStr *__restrict hdr, return r; resp->mit.acc_len += idx.sz * !use_pool; - return r; + goto set_skb_priv; } if (st_index || HPACK_IDX_RES(r) == HPACK_IDX_ST_NM_FOUND) { @@ -3838,9 +3839,28 @@ __tfw_hpack_encode(TfwHttpResp *__restrict resp, TfwStr *__restrict hdr, encode: if (use_pool) - return tfw_hpack_hdr_add(resp, hdr, &idx, name_indexed, trans); + r = tfw_hpack_hdr_add(resp, hdr, &idx, name_indexed, trans); else - return tfw_hpack_hdr_expand(resp, hdr, &idx, name_indexed); + r = tfw_hpack_hdr_expand(resp, hdr, &idx, name_indexed); +set_skb_priv: + BUG_ON(!resp->req); + if (likely(!r) && resp->req->stream) { + struct sk_buff *skb = prev; + + /* + * Very long headers can be located in several skbs, + * mark them all. + */ + while(skb && skb != resp->mit.iter.skb) { + skb_set_tfw_flags(skb, SS_F_HTTT2_FRAME_HEADERS); + skb_set_tfw_cb(skb, resp->req->stream->id); + skb = skb->next; + } + + skb_set_tfw_flags(resp->mit.iter.skb, SS_F_HTTT2_FRAME_HEADERS); + skb_set_tfw_cb(resp->mit.iter.skb, resp->req->stream->id); + } + return r; } int diff --git a/fw/http.c b/fw/http.c index b1e2b98a9c..e514bf167f 100644 --- a/fw/http.c +++ b/fw/http.c @@ -572,19 +572,19 @@ tfw_h2_prep_resp(TfwHttpResp *resp, unsigned short status, TfwStr *msg, }; TfwStr *body = NULL; + BUG_ON(!resp->req); if (!stream_id) { - stream_id = tfw_h2_stream_id_close(req, HTTP2_HEADERS, - HTTP2_F_END_STREAM); - if (unlikely(!stream_id)) { - return -ENOENT; - } + stream_id = tfw_h2_stream_id_send(resp->req, HTTP2_HEADERS, + HTTP2_F_END_STREAM); + if (unlikely(!stream_id)) + return -EPIPE; } /* Set HTTP/2 ':status' pseudo-header. */ mit->start_off = FRAME_HEADER_SIZE; r = tfw_h2_resp_status_write(resp, status, false, false); if (unlikely(r)) - return r; + goto out; /* * Form and write HTTP/2 response headers excluding "\r\n", ':' @@ -610,7 +610,7 @@ tfw_h2_prep_resp(TfwHttpResp *resp, unsigned short status, TfwStr *msg, r = tfw_hpack_encode(resp, __TFW_STR_CH(&hdr, 0), false, false); if (unlikely(r)) - return r; + goto out; write_int(val->len, 0x7F, 0, &vlen); s_vlen.data = vlen.buf; @@ -619,11 +619,11 @@ tfw_h2_prep_resp(TfwHttpResp *resp, unsigned short status, TfwStr *msg, r = tfw_http_msg_expand_data(iter, skb_head, &s_vlen, NULL); if (unlikely(r)) - return r; + goto out; r = tfw_http_msg_expand_data(iter, skb_head, val, NULL); if (unlikely(r)) - return r; + goto out; hdrs_len += s_vlen.len + val->len; } else { @@ -634,7 +634,7 @@ tfw_h2_prep_resp(TfwHttpResp *resp, unsigned short status, TfwStr *msg, hdr.hpack_idx = name->hpack_idx; if ((r = tfw_hpack_encode(resp, &hdr, false, true))) - return r; + goto out; } } @@ -647,7 +647,12 @@ tfw_h2_prep_resp(TfwHttpResp *resp, unsigned short status, TfwStr *msg, hdrs_len += mit->acc_len; body = TFW_STR_BODY_CH(msg); - return tfw_h2_frame_local_resp(resp, stream_id, hdrs_len, body); + + r = tfw_h2_frame_local_resp(resp, stream_id, hdrs_len, body); + +out: + tfw_h2_stream_id_unlink(req, false, true); + return r; } /* @@ -955,7 +960,7 @@ static inline void tfw_http_conn_req_clean(TfwHttpReq *req) { if (TFW_MSG_H2(req)) { - tfw_h2_stream_id_close(req, _HTTP2_UNDEFINED, 0); + tfw_h2_stream_id_unlink(req, false, true); } else { spin_lock_bh(&((TfwCliConn *)req->conn)->seq_qlock); if (likely(!list_empty(&req->msg.seq_list))) @@ -2721,7 +2726,7 @@ tfw_http_conn_release(TfwConn *conn) list_for_each_entry_safe(req, tmp, &zap_queue, fwd_list) { list_del_init(&req->fwd_list); if (TFW_MSG_H2(req)) { - tfw_h2_stream_id_close(req, _HTTP2_UNDEFINED, 0); + tfw_h2_stream_id_unlink(req, false, true); } else if (unlikely(!list_empty_careful(&req->msg.seq_list))) { spin_lock_bh(&((TfwCliConn *)req->conn)->seq_qlock); @@ -4796,6 +4801,10 @@ tfw_h2_append_predefined_body(TfwHttpResp *resp, unsigned int stream_id, copy + FRAME_HEADER_SIZE); ss_skb_adjust_data_len(it->skb, copy + FRAME_HEADER_SIZE); + BUG_ON(!stream_id); + skb_set_tfw_flags(it->skb, SS_F_HTTT2_FRAME_DATA); + skb_set_tfw_cb(it->skb, stream_id); + if (it->frag + 1 == MAX_SKB_FRAGS && (r = tfw_msg_iter_append_skb(it))) { @@ -4935,7 +4944,7 @@ tfw_h2_make_frames(TfwHttpResp *resp, unsigned int stream_id, /** * Frame forwarded response. */ -int +static int tfw_h2_frame_fwd_resp(TfwHttpResp *resp, unsigned int stream_id, unsigned long h_len) { @@ -4947,7 +4956,7 @@ tfw_h2_frame_fwd_resp(TfwHttpResp *resp, unsigned int stream_id, */ int tfw_h2_frame_local_resp(TfwHttpResp *resp, unsigned int stream_id, - unsigned long h_len, const TfwStr *body) + unsigned long h_len, const TfwStr *body) { int r; @@ -5062,9 +5071,9 @@ tfw_h2_error_resp(TfwHttpReq *req, int status, bool reply, bool attack, * can just send error response, leave the connection alive and * drop request's corresponding stream; in this case stream either * is already in locally closed state (switched in - * @tfw_h2_stream_id_close() during failed proxy/internal response + * @tfw_h2_stream_id_send() during failed proxy/internal response * creation) or will be switched into locally closed state in - * @tfw_h2_send_err_resp() (or in @tfw_h2_stream_id_close() if no error + * @tfw_h2_send_err_resp() (or in @tfw_h2_stream_id_send() if no error * response is needed) below; remotely (i.e. on client side) stream * will be closed - due to END_STREAM flag set in the last frame of * error response; in case of attack we must close entire connection, @@ -5085,7 +5094,7 @@ tfw_h2_error_resp(TfwHttpReq *req, int status, bool reply, bool attack, * remote peer (via RST_STREAM frame), that the stream has entered * into closed state (RFC 7540 section 6.4). */ - stream_id = tfw_h2_stream_id_close(req, HTTP2_RST_STREAM, 0); + stream_id = tfw_h2_stream_id_unlink(req, true, true); if (stream_id && !attack) tfw_h2_send_rst_stream(ctx, stream_id, HTTP2_ECODE_CANCEL); @@ -5287,12 +5296,8 @@ tfw_h2_resp_adjust_fwd(TfwHttpResp *resp) req->vhost, TFW_VHOST_HDRMOD_RESP); - /* - * Get ID of corresponding stream to prepare/send HTTP/2 response, and - * unlink request from the stream. - */ - stream_id = tfw_h2_stream_id_close(req, HTTP2_HEADERS, - HTTP2_F_END_STREAM); + stream_id = tfw_h2_stream_id_send(req, HTTP2_HEADERS, + HTTP2_F_END_STREAM); if (unlikely(!stream_id)) goto out; @@ -5395,6 +5400,7 @@ tfw_h2_resp_adjust_fwd(TfwHttpResp *resp) req, resp); SS_SKB_QUEUE_DUMP(&resp->msg.skb_head); + tfw_h2_stream_id_unlink(req, false, true); tfw_h2_resp_fwd(resp); __tfw_h2_resp_cleanup(&cleanup); diff --git a/fw/http.h b/fw/http.h index 5bad693987..709703326b 100644 --- a/fw/http.h +++ b/fw/http.h @@ -742,8 +742,6 @@ unsigned long tfw_http_hdr_split(TfwStr *hdr, TfwStr *name_out, TfwStr *val_out, bool inplace); unsigned long tfw_h2_hdr_size(unsigned long n_len, unsigned long v_len, unsigned short st_index); -int tfw_h2_frame_fwd_resp(TfwHttpResp *resp, unsigned int stream_id, - unsigned long h_len); int tfw_h2_frame_local_resp(TfwHttpResp *resp, unsigned int stream_id, unsigned long h_len, const TfwStr *body); int tfw_http_resp_copy_encodings(TfwHttpResp *resp, TfwStr* dst, diff --git a/fw/http_frame.c b/fw/http_frame.c index 3cca192b9f..22a57d3550 100644 --- a/fw/http_frame.c +++ b/fw/http_frame.c @@ -741,15 +741,13 @@ tfw_h2_stream_id(TfwHttpReq *req) } /* - * Get stream ID for upper layer to prepare and send frame with response to - * client, and process stream FSM for the frame (of type specified in @type - * and with flags set in @flags). This procedure also unlinks request from - * corresponding stream (if linked) and moves the stream to the queue of - * closed streams (if it is not contained there yet). + * Get stream ID for upper layer to prepare and send frame with response + * to client. This procedure also unlinks request from corresponding + * stream (if linked) and moves the stream to the queue of closed streams + * (if it is not contained there yet and if it is necessary). */ unsigned int -tfw_h2_stream_id_close(TfwHttpReq *req, unsigned char type, - unsigned char flags) +tfw_h2_stream_id_unlink(TfwHttpReq *req, bool send_rst, bool move_to_closed) { TfwStream *stream; unsigned int id = 0; @@ -763,23 +761,53 @@ tfw_h2_stream_id_close(TfwHttpReq *req, unsigned char type, return 0; } - if (type < _HTTP2_UNDEFINED && - !STREAM_SEND_PROCESS(stream, type, flags)) - { - id = stream->id; - } + if (send_rst) + STREAM_SEND_PROCESS(stream, HTTP2_RST_STREAM, 0); + id = stream->id; req->stream = NULL; stream->msg = NULL; - T_DBG3("%s: ctx [%p], strm [%p] added to closed streams list\n", - __func__, ctx, stream); + if (move_to_closed) { + T_DBG3("%s: ctx [%p], strm [%p] added to closed streams list\n", + __func__, ctx, stream); + __tfw_h2_stream_add_closed(&ctx->hclosed_streams, stream); + } - __tfw_h2_stream_add_closed(&ctx->hclosed_streams, stream); + spin_unlock(&ctx->lock); + + return id; +} + +unsigned int +tfw_h2_stream_id_send(TfwHttpReq *req, unsigned char type, + unsigned char flags) +{ + TfwStream *stream; + unsigned int id = 0; + TfwH2Ctx *ctx = tfw_h2_context(req->conn); + + spin_lock(&ctx->lock); + + stream = req->stream; + if (!stream) { + spin_unlock(&ctx->lock); + return 0; + } + + if (!STREAM_SEND_PROCESS(stream, type, flags)) + id = stream->id; + + if (!id) { + req->stream = NULL; + stream->msg = NULL; + __tfw_h2_stream_add_closed(&ctx->hclosed_streams, stream); + } spin_unlock(&ctx->lock); return id; + } /* diff --git a/fw/http_frame.h b/fw/http_frame.h index 57d6735931..126a7d3b2c 100644 --- a/fw/http_frame.h +++ b/fw/http_frame.h @@ -230,8 +230,10 @@ void tfw_h2_context_clear(TfwH2Ctx *ctx); int tfw_h2_frame_process(TfwConn *c, struct sk_buff *skb); void tfw_h2_conn_streams_cleanup(TfwH2Ctx *ctx); unsigned int tfw_h2_stream_id(TfwHttpReq *req); -unsigned int tfw_h2_stream_id_close(TfwHttpReq *req, unsigned char type, - unsigned char flags); +unsigned int tfw_h2_stream_id_send(TfwHttpReq *req, unsigned char type, + unsigned char flags); +unsigned int tfw_h2_stream_id_unlink(TfwHttpReq *req, bool send_rst, + bool move_to_closed); void tfw_h2_conn_terminate_close(TfwH2Ctx *ctx, TfwH2Err err_code, bool close); int tfw_h2_send_rst_stream(TfwH2Ctx *ctx, unsigned int id, TfwH2Err err_code);