Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix request h2 -> http1.1 conversion #1374

Merged
merged 21 commits into from
Jan 21, 2020

Conversation

vankoven
Copy link
Contributor

Main changes:

  1. Keep h_tbl always valid since we need to read the table during
    response caching. Keep the unsent skbs within request to avoid huge
    updates of h_tbl
  2. Optimize header alteration for h2, use compatibility mode for http1.1
  3. Use tfw_msg_write functions to take care of all possible caveats at
    message creation stage. (E.g. headers may be longer than one page)

Known issues:

  1. Body and trailer conversion is not tested due to parsing issues:
    body is not appended to the request and WARN_ON is triggered.

@vankoven vankoven force-pushed the ik-fix-req-2-to-1-transform branch 2 times, most recently from 470ae9f to ffc49a6 Compare December 24, 2019 14:41
Copy link
Contributor

@aleksostapenko aleksostapenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, with minor cleanups.

tempesta_fw/http.c Outdated Show resolved Hide resolved
tempesta_fw/http.c Outdated Show resolved Hide resolved
if (host) {
h1_hdrs_sz -= ht->tbl[TFW_HTTP_HDR_HOST].len;
h1_hdrs_sz -= SLEN(S_H2_AUTH);
h1_hdrs_sz += SLEN(S_F_HOST);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like excessive SLEN(S_DLM) counted here for Host header; it seems, that this size has already counted above, in the following code:

h1_hdrs_sz = pit->hdrs_len
		+ (pit->hdrs_cnt - pseudo_num) * (SLEN(S_DLM) + SLEN(S_CRLF))
		- req->mark.len;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, please check once more time.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean - from the previous code:

if (host) {
                        h1_hdrs_sz -= ht->tbl[TFW_HTTP_HDR_HOST].len;
                        h1_hdrs_sz -= SLEN(S_H2_AUTH);
                        h1_hdrs_sz += SLEN(S_F_HOST);
                }
                else {
                        h1_hdrs_sz -= SLEN(S_H2_AUTH);
                        h1_hdrs_sz += SLEN(S_F_HOST) + SLEN(S_CRLF);
                }

just to make

if (host) {
                        h1_hdrs_sz -= ht->tbl[TFW_HTTP_HDR_HOST].len;
                        h1_hdrs_sz -= SLEN(S_H2_AUTH);
                        h1_hdrs_sz += SLEN(S_F_HOST) - SLEN(S_DLM);
                }
                else {
                        h1_hdrs_sz -= SLEN(S_H2_AUTH);
                        h1_hdrs_sz += SLEN(S_F_HOST) + SLEN(S_CRLF);
                }

to evict SLEN(S_DLM) length from SLEN(S_F_HOST), since SLEN(S_DLM) for Host header (as well as SLEN(S_CRLF)) is already contained in h1_hdrs_sz in case of Host header existence in source HTTP/2-request.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me clear up the calculations:

  • :authority psedo-header is counted in h1_hdrs_sz,
  • if host header is present it is counted in h1_hdrs_sz and SLEN(S_DLM) + SLEN(S_CRLF) was added.

So here we need to make some alterations (assuming that the :authority pseudo header exists):

If the host header exists, we replace its value with the :authority ones. So we need to:

  • subtract original length of host header and length of S_DLM and S_CRLF added to it;
  • we need subtract difference between :authority and host length:
h1_hdrs_sz -= SLEN(S_H2_AUTH);
/* S_F_HOST already contains S_DLM, but we already calculated `S_DLM` for authority header. */
h1_hdrs_sz += SLEN(S_F_HOST) - SLEN(S_DLM);

If host header doesn't exist, the we only need to rename :authority into host.

That's why che calculations are done in current way.

Copy link
Contributor

@aleksostapenko aleksostapenko Jan 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:authority psedo-header is counted in h1_hdrs_sz

Yes, but in previous version of code the :authority pseudo-header had not been counted in pit->hdrs_cnt (i.e. subtracted from it), so the SLEN(S_DLM) + SLEN(S_CRLF) was not added for it and - that's why my previous comment appeared. But with last correction in 'fix header calculations' commit - it seems the problem is gone.

By the way: the pseudo_num local variable is constant now and used only once, thus maybe it should be better to move its definition to the beginning of the function, or just evict it (and replace with 3 in the place of usage with adding appropriate comment there).

tempesta_fw/http.c Show resolved Hide resolved
tempesta_fw/http.c Show resolved Hide resolved
tempesta_fw/http.c Outdated Show resolved Hide resolved
Main changes:
1. Keep h_tbl always valid since we need to read the table during
   response caching.
2. Optimize header alteration for h2, use compatibility mode for http1.1
3. Use tfw_msg_write functions to take care of all possible caveats at
   message creation stage. (E.g. headers may be longer than one page)

Known issues:
1. Body and trailer conversion is not tested due to parsing issues:
   body is not appended to the request and WARN_ON is triggered.
@vankoven vankoven changed the base branch from ik-1368-additions to ao-309-parser-transformation December 31, 2019 04:50
When tfw_http_msg_grow_hdr_tbl() is called it relocates
the hm->ht, but all code around the call still uses the
previous pointer. Thus changes are written to previous table, not
current.
- Don't switch fsm state on processing DATA frame header, update the
fsm state only after the full DATA frame is processed. Otherwise the
fsm is triggered twice and fsm closes the connection when a new portion
of DATA frame is expected.
- The HTTP2_STREAM_REM_HALF_CLOSED flag is set when the h2 frames are
processed, this flag may be evalueted by http parser only when all the
data from current h2 frame is parsed
@vankoven vankoven mentioned this pull request Jan 10, 2020
4 tasks
Body it skb list by itself, so a new function is required.
h2 recipient will reject frame if it's too big for it. Honor remote
peer settings when send messages, or fail fast otherwise, since the
connection will be closed anyway.
Don't switch fsm state on processing frame header, update the
fsm state only after the full frame is processed. Otherwise the
fsm is triggered twice and fsm closes the connection when a new
fragment of frame is expected.
Copy link
Contributor

@aleksostapenko aleksostapenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, with minor comments.

tempesta_fw/http_parser.c Show resolved Hide resolved
tempesta_fw/http.c Show resolved Hide resolved
tempesta_fw/ss_skb.h Outdated Show resolved Hide resolved
tempesta_fw/http.c Outdated Show resolved Hide resolved
tempesta_fw/http.c Outdated Show resolved Hide resolved
tempesta_fw/http.c Show resolved Hide resolved
* 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.
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The verification of HEADERS/CONTINUATION frame sequence is done in tfw_h2_stream_fsm(). The different streams intersection can be verified here (in tfw_h2_frame_type_process()) - e.g. via implementing the active_stream number in TfwH2Ctx structure, and setting/zeroing it for appropriate stream states in tfw_h2_stream_fsm().

Copy link
Contributor Author

@vankoven vankoven Jan 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The verification of HEADERS/CONTINUATION frame sequence is done in tfw_h2_stream_fsm()

Yes, no problem there.

The different streams intersection

Yes, that is what I meant. If peer has sent a HEADER frame without HEADERS_END flag, then he can't sent messages on other streams until the headers are closed with CONTINUATION frames followed by HEADERS_END flag.

e.g. via implementing the active_stream number in TfwH2Ctx structure, and setting/zeroing it for appropriate stream states in tfw_h2_stream_fsm().

Seems like it's enough to set some ON_CONTINUATION flag for the whole connection and check that the current stream ID is the same as the lstream_id. Many variants are possible here. I didn't implemented it, but the tfw_h2_frame_type_process() looks like a good candidate to place a TODO note, since all required information is available on this state.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem with lstream_id - is that it will not allow to handle this case for requests (i.e. streams) with HEADERS/CONTINUATION frames after DATA frames.

tempesta_fw/http.c Outdated Show resolved Hide resolved
@vankoven vankoven merged commit f010557 into ao-309-parser-transformation Jan 21, 2020
@vankoven vankoven deleted the ik-fix-req-2-to-1-transform branch January 21, 2020 13:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants