-
Notifications
You must be signed in to change notification settings - Fork 103
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
HTTP/2 Framing layer implementation (#309). #1233
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks OK.
tempesta_fw/http_frame.c
Outdated
|
||
#define VERIFY_FRAME_SIZE(ctx) \ | ||
do { \ | ||
if ((ctx)->hdr.length <= 0) \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Scanned through the http/2 description, but found no evidence of zero-length frames are forbidden. In fact, 6.9.1 mentions them explicitly: "Frames with zero length with the END_STREAM flag set (that is, an empty DATA frame) MAY be sent if there is no available space in either flow-control window."
Some client may send such zero-length frames. I think we should allow that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added handling for zero length payload.
tempesta_fw/http_frame.c
Outdated
} | ||
|
||
T_FSM_STATE(HTTP2_RECV_FRAME_SETTINGS) { | ||
BUG_ON(ctx->to_read > FRAME_HEADER_SIZE); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I propose to move this BUG_ON()
into FRAME_FSM_READ_SRVC()
, and change FRAME_HEADER_SIZE
to sizeof(ctx->rbuf)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
tempesta_fw/http_frame.c
Outdated
* certain service data should be separated from it (placed at the | ||
* frame's beginning): frame header, optional pad length and optional | ||
* priority data (the latter is for HEADERS frames only). Besides, | ||
* DATA and HEADERS frames can containing some padding in the frame's |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* DATA and HEADERS frames can containing some padding in the frame's | |
* DATA and HEADERS frames can contain some padding in the frame's |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
tempesta_fw/http_frame.c
Outdated
* DATA and HEADERS frames can containing some padding in the frame's | ||
* tail, but we don't need to worry about that here since such padding | ||
* is processed as service data, separately from app frame, and it | ||
* will be just splitted into separate skb (above). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* will be just splitted into separate skb (above). | |
* will be just split into separate skb (above). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
tempesta_fw/http_frame.c
Outdated
} | ||
|
||
/* | ||
* Initialization of HTTP/2 framing context. Due to passing frames to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's more like re-initialization, as there is a period where context is used, but this function wasn't called yet. As far as I can see, context is initialized in tfw_tls_check_alloc_h2_context()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
tempesta_fw/http_frame.c
Outdated
|
||
#define FRAME_CLI_MAGIC "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" | ||
#define FRAME_CLI_MAGIC_LEN 24 | ||
#define FRAME_SRVC1_SIZE 4 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd propose to use separate constants for WINDOW_UPDATE and RST_STREAM. Hiding both behind SRVC1 doesn't save anything, but makes code harder to read. Same for SRVC2.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
tempesta_fw/http_frame.c
Outdated
#define FRAME_CLI_MAGIC_LEN 24 | ||
#define FRAME_SRVC1_SIZE 4 | ||
#define FRAME_PRIORITY_SIZE 5 | ||
#define FRAME_STNGS_ENTRY_SIZE 6 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't help but read STNGS as "stings". Is it worth to save letters like that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
tempesta_fw/http_frame.c
Outdated
break; | ||
|
||
default: | ||
/* Only DATA and HEADERS frames cab be padded. */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/* Only DATA and HEADERS frames cab be padded. */ | |
/* Only DATA and HEADERS frames can be padded. */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
tempesta_fw/tls.c
Outdated
protos[order].id = TTLS_ALPN_ID_HTTP1; | ||
protos[!order].name = TTLS_ALPN_HTTP2; | ||
protos[!order].len = sizeof(TTLS_ALPN_HTTP2) - 1; | ||
protos[!order].id = TTLS_ALPN_ID_HTTP2; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like HTTP/2 is unconditionally enabled even if listen
directive has https:http1
and doesn't mention http2 at all. Is this intended behavior?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, currently http1
is just a shortcut for http1,http2
and http2
is a shortcut for http2,http1
. Thus, we always enable http2 handling, and since our protocols order takes precedence, we can define what protocol will be used when client sends us in any order both http/1.1
and h2
in ALPN.
But in any case, this point (as well as the names for HTTP/1.1 and HTTP/2 protocols in configuration) remains the subject TBD.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's not a good idea to enable HTTP/2 by default before we have the stable implementation - this may lead to bad user experience. I also vote for RFC compliant name like h2
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK. Corrected.
tls/tls_srv.c
Outdated
/* TODO #309 #834 confugure on Tempesta side: "h2, http/1.1" */ | ||
if (!tls->conf->alpn_list) | ||
return 0; | ||
/* If TLS processig is enabled, ALPN must be configured. */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/* If TLS processig is enabled, ALPN must be configured. */ | |
/* If TLS processing is enabled, ALPN must be configured. */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
@@ -35,8 +35,10 @@ DBG_HTTP_PARSER ?= 0 | |||
DBG_SS ?= 0 | |||
DBG_TLS ?= 0 | |||
DBG_APM ?= 0 | |||
DBG_HTTP_FRAME ?= 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I bet there will be families of debug functions for HTTP2: for frames, HPACK, and what ever else. The whole group will have the same meaning to HTTP2 as DBG_HTTP_PARSER
for HTTP1.1. Should we rename it as DBG_HTTP2_FRAME
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For now I cannot do such separation: these sets do not intersect yet; also both HTTP/1.1 and HTTP/2 protocols belong to the same HTTP domain, so there is no contradiction here. But in the nearest future (e.g. during HTTP parser implementation changing in context of HTTP/2 processing) mentioned intersections may appear, and in this case the separation for HTTP1_*
and HTTP2_*
may be really needed and will be made for preprocessor variables (and possibly for filenames too, e.g. http_frame.c/h
, http_stream.c/h
).
tempesta_fw/connection.h
Outdated
@@ -209,9 +210,11 @@ enum { | |||
typedef struct { | |||
TfwCliConn cli_conn; | |||
TlsCtx tls; | |||
TfwHttp2Ctx *h2; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The structure name becomes misleading, now it's more than TLS connection, it also stores the HTTP2 context. And a new issue is introduced: HTTP2 context is not allocated for non-TLS HTTP2 connections (which are allowed by standard).
In the same time HTTP2 connections will have two contexts in the same time: HTTP2 and HTTP1. HTTP1 context is stored as TfwConn->TfwHttpParser
. Probably we should introduce a union in TfwConn
class to store 'protocol context' whatever http2 or http1. Seems like we never want both. Even is the HTTP1 connection is upgraded to HTTP2 we can easily re-init the field.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
HTTP2 context is not allocated for non-TLS HTTP2 connections (which are allowed by standard)
In result of discussion in #309 and in chat - it has been decided not to support non-TLS HTTP/2 in TempestaFW.
Probably we should introduce a union in TfwConn class to store 'protocol context' whatever http2 or http1.
Yes, agree with that. The similar approach is proposed in #309 (comment) (in Stream Priority/Dependencies
). These changes is part of future PRs in context of #309 ("Request HTTP/2 -> HTTP/1.1"
or more soon "Disable @seq_queue for HTTP/2"
). During implementation of Framing, Scheduler and Stream States - I temporary left that part as is - for testing/debugging purposes.
static int | ||
__tfw_http_msg_process(void *conn, TfwFsmData *data) | ||
int | ||
tfw_http_msg_process_generic(void *conn, TfwFsmData *data) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bad naming. It's not generic. It's HTTP1 processing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the nearest future PRs (in context of #309) this function is planned for usage for HTTP/2 processing too, so I have run a little forward here. In my opinion, most part of tfw_http_msg_process_generic()
is general processing, whatever HTTP/1.1 or HTTP/2 protocol (except the parsing stage only), so it is worth to try adapt it for both protocols. In any case if this idea will fail, and the separate procedures will be needed - the name will be reverted accordingly.
tempesta_fw/http.c
Outdated
@@ -3930,7 +3931,9 @@ tfw_http_msg_process(void *conn, TfwFsmData *data) | |||
if (likely(r == T_OK || r == T_POSTPONE)) { | |||
data->skb->next = data->skb->prev = NULL; | |||
data->trail = !next ? trail : 0; | |||
r = __tfw_http_msg_process(conn, data); | |||
r = TFW_CONN_TLS((TfwConn *)conn) && tfw_http2_context(conn) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like TFW_CONN_TLS((TfwConn *)conn)
is extra here. HTTP2 recommends using HTTP2 over TLS (h2
) but it still allows HTTP2 over TCP (h2c
). Personally I don't understand why the unencrypted connections are allowed by standard, but we have to follow them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It has been decided not to support non-TLS HTTP/2 in TempestaFW: standard allows HTTP/2 over plain TCP, but does not force to use it.
tempesta_fw/http_frame.c
Outdated
* Tempesta FW | ||
* | ||
* Copyright (C) 2014 NatSys Lab. ([email protected]). | ||
* Copyright (C) 2015-2019 Tempesta Technologies, Inc. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bad copy-paste. This file wasn't existed before, so only 2019
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
tempesta_fw/http_frame.c
Outdated
* and GOAWAY frames to report the reasons of the stream or | ||
* connection error. | ||
*/ | ||
#define FRAME_ECODE_PROTO 0x1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Section 7 contains 14 error codes. I understand that only two are needed at this moment, but it seems that we will definitely will need all of them, let's introduce enum like TfwFrameType
to store all the possible errors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
tempesta_fw/http_frame.h
Outdated
* Tempesta FW | ||
* | ||
* Copyright (C) 2014 NatSys Lab. ([email protected]). | ||
* Copyright (C) 2015-2019 Tempesta Technologies, Inc. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here. Bad copy-paste.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
* this program; if not, write to the Free Software Foundation, Inc., 59 | ||
* Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
*/ | ||
#ifndef __HTTP_FRAME__ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
__HTTP2_FRAME__
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The file has name http_frame.h
, so this will be irrelevant (also see #1233 (comment)).
tempesta_fw/http_frame.h
Outdated
HTTP2_RECV_HEADER = __HTTP2_RECV_FRAME_APP, | ||
HTTP2_RECV_CONT, | ||
HTTP2_RECV_DATA | ||
} TfwFrameState; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need to expose this states to other modules? Why it's not in http2_frame.c
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently we need that due to necessity of correct state initialization in tfw_tls_check_alloc_h2_context()
in tls.c
.
* @flags - frame's flags; | ||
*/ | ||
typedef struct { | ||
int length; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Never going to be below 0, thus should be unsigned.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Never going to be below 0
Currently, code in tfw_http2_recv_priority()
may temporarily make length
negative. If, for example, client sends a wrong length in PRIORITY frame.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good to merge, but please make the several cleanups/improvements before. It's also good to discuss (think once more) the parser design.
tempesta_fw/http_frame.c
Outdated
return tfw_http2_stream_terminate(ctx, | ||
hdr->stream_id, | ||
NULL, | ||
HTTP2_ECODE_REFUSED); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The line is too wide. I'd propose to use tfw_h2_
prefix instead of tfw_http2_
. There is too deep nesting, probably the case
code should be moved to separate function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
tempesta_fw/tls.c
Outdated
return 0; | ||
} | ||
|
||
h2 = kzalloc(sizeof(TfwHttp2Ctx), GFP_ATOMIC); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use SLAB for fixed-size allocations. Next, h2 is first class citizen for us while HTTP/1 is legacy. We also don't have any business with plain TLS, so it's likely that any TLS connection is essentially a h2 connection. I think TfwHttp2Ctx
should be inherited from TfwTlsConn
instead of using the pointer, so let's just allocated large piece of memory to fit TLS and h2 context.
BTW @i-rinat probably h2 context is a good place to store second checksum. In HTTP/3 all the layers must be aware about each other after all, so this can be an option (I don't like it though, but if we're out of memory on TLS handshakes, then this makes sense to use the extra memory and there is guarantee that we move to either h2 or h3 after TLS stuff, even for QUIC).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
r = __tfw_http_msg_process(conn, data); | ||
r = TFW_CONN_TLS((TfwConn *)conn) && tfw_http2_context(conn) | ||
? tfw_http2_frame_process(conn, data) | ||
: tfw_http_msg_process_generic(conn, data); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In #309 we discussed that we should split HTTP parser in at least 2 pieces: headers and body and both the parsers will be called separately from H2 layer. How do you see the split in current code? Check H2 state in tfw_http_req_process()
?
Frankly, now we have single-jump cost in our parser to enter any state and I believe we're able to make GCC, probably with some assembly help, to generate proper FSM code layout to make the large parser code faster (after all, even after the parser split, we'll have complex FSM due to many checks), so probably we'll be fine with current code if it simpler than possible split parser.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check H2 state in tfw_http_req_process()?
Yeah - this must be a frame type and, maybe, a stream state (since we have to track request's start/end, and this exactly matches with stream's states, as mentioned in penultimate paragraph in https://tools.ietf.org/html/rfc7540#section-8.1).
Сurrently I'm not very optimistic about parser division into separate parts: indeed, we have rather effective implementation with single-jump cost to enter any state; and with divided parser we will get costs of additional function calls (if not inlined) and necessity for additional branching to call separate parser parts (with associated costs). But in general, I am still thinking about this point.
tempesta_fw/tls.c
Outdated
|
||
#define CONF_HTTPS "https" | ||
#define CONF_HTTP1 "http1" | ||
#define CONF_HTTP2 "http2" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please update etc/tempesta_fw.conf
and appropriate Wiki with the HTTP/2 support. Please say (in bold) in the WIki that HTTP/2 implementation is incomplete.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
tempesta_fw/tls.c
Outdated
protos[order].id = TTLS_ALPN_ID_HTTP1; | ||
protos[!order].name = TTLS_ALPN_HTTP2; | ||
protos[!order].len = sizeof(TTLS_ALPN_HTTP2) - 1; | ||
protos[!order].id = TTLS_ALPN_ID_HTTP2; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's not a good idea to enable HTTP/2 by default before we have the stable implementation - this may lead to bad user experience. I also vote for RFC compliant name like h2
.
tempesta_fw/http_stream.c
Outdated
} | ||
} | ||
|
||
new_stream = kzalloc(sizeof(TfwStream), GFP_ATOMIC); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same allocation problem: basically we usually don't use pattern kzalloc(sizeof(....), ...)
in any more or less performance crucial code - if we have fixed size allocations, then we use some kind of SLAB allocator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah - missed that. Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
tfw_http2_init_stream(new_stream, id, weight); | ||
|
||
rb_link_node(&new_stream->node, parent, new); | ||
rb_insert_color(&new_stream->node, &sched->streams); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll need priority index plus to current index for stream ID (as you said in the PR description), so we need two indexes. However, binary heap is technically an array (continuous memory location which usually gives us good spactial locality in run time - this is why I wanted binary heap in #309), and, since you already use dynamic memory allocator, all your TfwStream
instances reside somewhere in memory and it's not wise to use additional array just for heap index. It's better to introduce additional node
entries in TfwStream
to build the second index.
If you fix the kzalloc()
above, then SLAB/SLOB/SLUB already cares about spacial locality. The question is: do we expect to see more frequently frames from the same TCP connection streams or from streams as they first appeared (to fit the internal SLAB allocation pattern)? I think the first one, but I won't bet that there is so strong spacial locality in access pattern to streams from the same heap/rb-tree. Moreover, if we use binary heap, as I proposed, on top of an array, we'll have to manager erased items, i.e. move stored items and this is basically slower than SLAB's bitmasks.
All it all, I think RB tree is a good enough decision here since there is no confidence in access pattern. I write the comment for #1196 and will leave link there to the comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK. I'll think yet further about building a dependency/priority tree.
tfw_http2_init_stream(new_stream, id, weight); | ||
|
||
rb_link_node(&new_stream->node, parent, new); | ||
rb_insert_color(&new_stream->node, &sched->streams); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll need priority index plus to current index for stream ID (as you said in the PR description), so we need two indexes. However, binary heap is technically an array (continuous memory location which usually gives us good spactial locality in run time - this is why I wanted binary heap in #309), and, since you already use dynamic memory allocator, all your TfwStream
instances reside somewhere in memory and it's not wise to use additional array just for heap index. It's better to introduce additional node
entries in TfwStream
to build the second index.
If you fix the kzalloc()
above, then SLAB/SLOB/SLUB already cares about spacial locality. The question is: do we expect to see more frequently frames from the same TCP connection streams or from streams as they first appeared (to fit the internal SLAB allocation pattern)? I think the first one, but I won't bet that there is so strong spacial locality in access pattern to streams from the same heap/rb-tree. Moreover, if we use binary heap, as I proposed, on top of an array, we'll have to manager erased items, i.e. move stored items and this is basically slower than SLAB's bitmasks.
All it all, I think RB tree is a good enough decision here since there is no confidence in access pattern. I write the comment for #1196 and will leave link there to the comment.
1. "http2 -> h2" renaming for functions and structures; 2. SLAB usage for allocations.
1. Make 'TfwH2Conn' (with HTTP/2 context) inherited from 'TfwTlsConn'. 2. Avoid default enabling of HTTP/2 protocol.
Working HTTP/2 functionality in PR
h2i
utility (https://github.com/golang/net/tree/master/http2/h2i);nghttp
utility - the part ofnghttp2
/nghttp2-client
packages (https://nghttp2.org):Command:
Note: to test HTTP/2 connection establishing via
nghttp
(described above) - it is necessary to replace the implementation ofAPP_FRAME()
macro with simplefalse
value, since upper level for processing of application frames (HEADERS, DATA and CONTINUATION) is not implemented yet.HTTP/2 Stream Scheduler
In context of #309 only base implementation of priority/dependency scheduler is planned (the rest must be implemented in #1196). So this PR contains only initial form of scheduler: at stage of #309 all streams are assumed to have equal weights and depends on the one root logical stream (with ID equal 0); thus, streams are processed in order of their frames/request/response come from TCP connection, and in this case we need just a simple streams' storage with fast search by ID. In current PR red-black tree based storage is used; this structure has been chosen - compared to hash table - due to lack of per-connection memory overhead, since for hash table some amount of memory (maybe large enough) must be allocated initially - at the connection's creation moment, and besides, this could be a ddos vector.
Considering future priority/dependency scheduler implementation in context of #1196 - an appropriate scheme for the scheduler, in my opinion, would be a tree-like structure (let's call it a dependency tree), built with the help of, "parent" and "child" pointers (embedded into Stream structure) for vertical links and "prev" and "next" pointers - for the horizontal links between neighbor streams of the same level. A logical stream with identifier 0 acts as a root of this tree. In addition to the specified links, each stream has a pointer to a special priority queue (based on the binary heap), in which only its child single-level streams with jobs to be processed (frames/requests/responses) are placed.
Thus, the red-black tree, mentioned above, in proposed scheme acts as a basic storage structure for a quick search of streams; on top of it - the dependency tree acts as a structure to track current dependencies between streams and to calculate effective priorities for each stream in the tree — based on their weights (in case of re-prioritization etc.); also dependency tree provides up/down traversals between dependency levels; and the priority queues, in turn, track the next stream to be processed for each level.