mirror of
https://gitea.invidious.io/iv-org/litespeed-quic.git
synced 2024-08-15 00:53:43 +00:00
Release 2.22.0
- [FEATURE] Extensible HTTP Priorities (HTTP/3 only). - [FEATURE] Add conn context to packet-out memory interface (PR #175). - [BUGFIX] gQUIC proof generation: allocate buffer big enough for signature (issue #173). - [BUGFIX] Make library thread-safe: drop use of global variables (issue #133, issue #167). - [BUGFIX] Deactivate only *recent* HQ frame, not any HQ frame. - [BUGFIX] gQUIC server: associate compressed cert with SSL_CTX, instead of keeping them in a separate hash, potentially leading to mismatches. - [BUGFIX] Stream data discard infinite loop: break on FIN. - cmake: add install target via -DCMAKE_INSTALL_PREFIX (PR #171). - Support randomized packet number to begin a connection. - Mini and full IETF connection size optimization. - http_client: specify HTTP priorities based on stream conditions.
This commit is contained in:
parent
cb1e8c1022
commit
fbc6cc0413
55 changed files with 6557 additions and 391 deletions
18
CHANGELOG
18
CHANGELOG
|
@ -1,3 +1,21 @@
|
||||||
|
2020-10-07
|
||||||
|
- 2.22.0
|
||||||
|
- [FEATURE] Extensible HTTP Priorities (HTTP/3 only).
|
||||||
|
- [FEATURE] Add conn context to packet-out memory interface (PR #175).
|
||||||
|
- [BUGFIX] gQUIC proof generation: allocate buffer big enough for
|
||||||
|
signature (issue #173).
|
||||||
|
- [BUGFIX] Make library thread-safe: drop use of global variables
|
||||||
|
(issue #133, issue #167).
|
||||||
|
- [BUGFIX] Deactivate only *recent* HQ frame, not any HQ frame.
|
||||||
|
- [BUGFIX] gQUIC server: associate compressed cert with SSL_CTX,
|
||||||
|
instead of keeping them in a separate hash, potentially leading
|
||||||
|
to mismatches.
|
||||||
|
- [BUGFIX] Stream data discard infinite loop: break on FIN.
|
||||||
|
- cmake: add install target via -DCMAKE_INSTALL_PREFIX (PR #171).
|
||||||
|
- Support randomized packet number to begin a connection.
|
||||||
|
- Mini and full IETF connection size optimization.
|
||||||
|
- http_client: specify HTTP priorities based on stream conditions.
|
||||||
|
|
||||||
2020-09-29
|
2020-09-29
|
||||||
- 2.21.0
|
- 2.21.0
|
||||||
- [FEATURE] QUIC and HTTP/3 Internet Draft 31 support.
|
- [FEATURE] QUIC and HTTP/3 Internet Draft 31 support.
|
||||||
|
|
|
@ -50,7 +50,7 @@ You may need to install pre-requisites like zlib and libevent.
|
||||||
2. Use specific BoringSSL version
|
2. Use specific BoringSSL version
|
||||||
|
|
||||||
```
|
```
|
||||||
git checkout b117a3a0b7bd11fe6ebd503ec6b45d6b910b41a1
|
git checkout 251b5169fd44345f455438312ec4e18ae07fd58c
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Compile the library
|
3. Compile the library
|
||||||
|
|
|
@ -161,6 +161,24 @@ strndup(const char *s, size_t n)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* When more than `nread' bytes are read from stream `stream_id', apply
|
||||||
|
* priority in `ehp'.
|
||||||
|
*/
|
||||||
|
struct priority_spec
|
||||||
|
{
|
||||||
|
enum {
|
||||||
|
PRIORITY_SPEC_ACTIVE = 1 << 0,
|
||||||
|
} flags;
|
||||||
|
lsquic_stream_id_t stream_id;
|
||||||
|
size_t nread;
|
||||||
|
struct lsquic_ext_http_prio ehp;
|
||||||
|
};
|
||||||
|
static struct priority_spec *s_priority_specs;
|
||||||
|
static unsigned s_n_prio_specs;
|
||||||
|
|
||||||
|
static void
|
||||||
|
maybe_perform_priority_actions (struct lsquic_stream *, lsquic_stream_ctx_t *);
|
||||||
|
|
||||||
struct lsquic_conn_ctx;
|
struct lsquic_conn_ctx;
|
||||||
|
|
||||||
struct path_elem {
|
struct path_elem {
|
||||||
|
@ -531,7 +549,20 @@ http_client_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream)
|
||||||
LSQ_INFO("created new stream, path: %s", st_h->path);
|
LSQ_INFO("created new stream, path: %s", st_h->path);
|
||||||
lsquic_stream_wantwrite(stream, 1);
|
lsquic_stream_wantwrite(stream, 1);
|
||||||
if (randomly_reprioritize_streams)
|
if (randomly_reprioritize_streams)
|
||||||
|
{
|
||||||
|
if ((1 << lsquic_conn_quic_version(lsquic_stream_conn(stream)))
|
||||||
|
& LSQUIC_IETF_VERSIONS)
|
||||||
|
lsquic_stream_set_http_prio(stream,
|
||||||
|
&(struct lsquic_ext_http_prio){
|
||||||
|
.urgency = random() & 7,
|
||||||
|
.incremental = random() & 1,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
else
|
||||||
lsquic_stream_set_priority(stream, 1 + (random() & 0xFF));
|
lsquic_stream_set_priority(stream, 1 + (random() & 0xFF));
|
||||||
|
}
|
||||||
|
if (s_priority_specs)
|
||||||
|
maybe_perform_priority_actions(stream, st_h);
|
||||||
if (s_abandon_early)
|
if (s_abandon_early)
|
||||||
{
|
{
|
||||||
st_h->sh_stop = random() % (s_abandon_early + 1);
|
st_h->sh_stop = random() % (s_abandon_early + 1);
|
||||||
|
@ -547,25 +578,37 @@ send_headers (lsquic_stream_ctx_t *st_h)
|
||||||
{
|
{
|
||||||
const char *hostname = st_h->client_ctx->hostname;
|
const char *hostname = st_h->client_ctx->hostname;
|
||||||
struct header_buf hbuf;
|
struct header_buf hbuf;
|
||||||
|
unsigned h_idx = 0;
|
||||||
if (!hostname)
|
if (!hostname)
|
||||||
hostname = st_h->client_ctx->prog->prog_hostname;
|
hostname = st_h->client_ctx->prog->prog_hostname;
|
||||||
hbuf.off = 0;
|
hbuf.off = 0;
|
||||||
struct lsxpack_header headers_arr[7];
|
struct lsxpack_header headers_arr[9];
|
||||||
#define V(v) (v), strlen(v)
|
#define V(v) (v), strlen(v)
|
||||||
header_set_ptr(&headers_arr[0], &hbuf, V(":method"), V(st_h->client_ctx->method));
|
header_set_ptr(&headers_arr[h_idx++], &hbuf, V(":method"), V(st_h->client_ctx->method));
|
||||||
header_set_ptr(&headers_arr[1], &hbuf, V(":scheme"), V("https"));
|
header_set_ptr(&headers_arr[h_idx++], &hbuf, V(":scheme"), V("https"));
|
||||||
header_set_ptr(&headers_arr[2], &hbuf, V(":path"), V(st_h->path));
|
header_set_ptr(&headers_arr[h_idx++], &hbuf, V(":path"), V(st_h->path));
|
||||||
header_set_ptr(&headers_arr[3], &hbuf, V(":authority"), V(hostname));
|
header_set_ptr(&headers_arr[h_idx++], &hbuf, V(":authority"), V(hostname));
|
||||||
header_set_ptr(&headers_arr[4], &hbuf, V("user-agent"), V(st_h->client_ctx->prog->prog_settings.es_ua));
|
header_set_ptr(&headers_arr[h_idx++], &hbuf, V("user-agent"), V(st_h->client_ctx->prog->prog_settings.es_ua));
|
||||||
/* The following headers only gets sent if there is request payload: */
|
if (randomly_reprioritize_streams)
|
||||||
header_set_ptr(&headers_arr[5], &hbuf, V("content-type"), V("application/octet-stream"));
|
{
|
||||||
header_set_ptr(&headers_arr[6], &hbuf, V("content-length"), V( st_h->client_ctx->payload_size));
|
char pfv[10];
|
||||||
|
sprintf(pfv, "u=%ld", random() & 7);
|
||||||
|
header_set_ptr(&headers_arr[h_idx++], &hbuf, V("priority"), V(pfv));
|
||||||
|
if (random() & 1)
|
||||||
|
sprintf(pfv, "i");
|
||||||
|
else
|
||||||
|
sprintf(pfv, "i=?0");
|
||||||
|
header_set_ptr(&headers_arr[h_idx++], &hbuf, V("priority"), V(pfv));
|
||||||
|
}
|
||||||
|
if (st_h->client_ctx->payload)
|
||||||
|
{
|
||||||
|
header_set_ptr(&headers_arr[h_idx++], &hbuf, V("content-type"), V("application/octet-stream"));
|
||||||
|
header_set_ptr(&headers_arr[h_idx++], &hbuf, V("content-length"), V( st_h->client_ctx->payload_size));
|
||||||
|
}
|
||||||
lsquic_http_headers_t headers = {
|
lsquic_http_headers_t headers = {
|
||||||
.count = sizeof(headers_arr) / sizeof(headers_arr[0]),
|
.count = h_idx,
|
||||||
.headers = headers_arr,
|
.headers = headers_arr,
|
||||||
};
|
};
|
||||||
if (!st_h->client_ctx->payload)
|
|
||||||
headers.count -= 2;
|
|
||||||
if (0 != lsquic_stream_send_headers(st_h->stream, &headers,
|
if (0 != lsquic_stream_send_headers(st_h->stream, &headers,
|
||||||
st_h->client_ctx->payload == NULL))
|
st_h->client_ctx->payload == NULL))
|
||||||
{
|
{
|
||||||
|
@ -659,6 +702,40 @@ discard (void *ctx, const unsigned char *buf, size_t sz, int fin)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
maybe_perform_priority_actions (struct lsquic_stream *stream,
|
||||||
|
lsquic_stream_ctx_t *st_h)
|
||||||
|
{
|
||||||
|
const lsquic_stream_id_t stream_id = lsquic_stream_id(stream);
|
||||||
|
struct priority_spec *spec;
|
||||||
|
unsigned n_active;
|
||||||
|
int s;
|
||||||
|
|
||||||
|
n_active = 0;
|
||||||
|
for (spec = s_priority_specs; spec < s_priority_specs + s_n_prio_specs;
|
||||||
|
++spec)
|
||||||
|
{
|
||||||
|
if ((spec->flags & PRIORITY_SPEC_ACTIVE)
|
||||||
|
&& spec->stream_id == stream_id
|
||||||
|
&& st_h->sh_nread >= spec->nread)
|
||||||
|
{
|
||||||
|
s = lsquic_stream_set_http_prio(stream, &spec->ehp);
|
||||||
|
if (s != 0)
|
||||||
|
{
|
||||||
|
LSQ_ERROR("could not apply priorities to stream %"PRIu64,
|
||||||
|
stream_id);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
spec->flags &= ~PRIORITY_SPEC_ACTIVE;
|
||||||
|
}
|
||||||
|
n_active += !!(spec->flags & PRIORITY_SPEC_ACTIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n_active == 0)
|
||||||
|
s_priority_specs = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
http_client_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
|
http_client_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
|
||||||
{
|
{
|
||||||
|
@ -727,6 +804,23 @@ http_client_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
|
||||||
if (!s_discard_response)
|
if (!s_discard_response)
|
||||||
fwrite(buf, 1, nread, stdout);
|
fwrite(buf, 1, nread, stdout);
|
||||||
if (randomly_reprioritize_streams && (st_h->count++ & 0x3F) == 0)
|
if (randomly_reprioritize_streams && (st_h->count++ & 0x3F) == 0)
|
||||||
|
{
|
||||||
|
if ((1 << lsquic_conn_quic_version(lsquic_stream_conn(stream)))
|
||||||
|
& LSQUIC_IETF_VERSIONS)
|
||||||
|
{
|
||||||
|
struct lsquic_ext_http_prio ehp;
|
||||||
|
if (0 == lsquic_stream_get_http_prio(stream, &ehp))
|
||||||
|
{
|
||||||
|
ehp.urgency = 7 & (ehp.urgency + 1);
|
||||||
|
ehp.incremental = !ehp.incremental;
|
||||||
|
#ifndef NDEBUG
|
||||||
|
const int s =
|
||||||
|
#endif
|
||||||
|
lsquic_stream_set_http_prio(stream, &ehp);
|
||||||
|
assert(s == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
old_prio = lsquic_stream_priority(stream);
|
old_prio = lsquic_stream_priority(stream);
|
||||||
new_prio = 1 + (random() & 0xFF);
|
new_prio = 1 + (random() & 0xFF);
|
||||||
|
@ -738,6 +832,9 @@ http_client_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
|
||||||
LSQ_DEBUG("changed stream %"PRIu64" priority from %u to %u",
|
LSQ_DEBUG("changed stream %"PRIu64" priority from %u to %u",
|
||||||
lsquic_stream_id(stream), old_prio, new_prio);
|
lsquic_stream_id(stream), old_prio, new_prio);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (s_priority_specs)
|
||||||
|
maybe_perform_priority_actions(stream, st_h);
|
||||||
if ((st_h->sh_flags & ABANDON) && st_h->sh_nread >= st_h->sh_stop)
|
if ((st_h->sh_flags & ABANDON) && st_h->sh_nread >= st_h->sh_stop)
|
||||||
{
|
{
|
||||||
LSQ_DEBUG("closing stream early having read %zd bytes",
|
LSQ_DEBUG("closing stream early having read %zd bytes",
|
||||||
|
@ -864,6 +961,9 @@ usage (const char *prog)
|
||||||
" -e TOKEN Hexadecimal string representing resume token.\n"
|
" -e TOKEN Hexadecimal string representing resume token.\n"
|
||||||
" -3 MAX Close stream after reading at most MAX bytes. The actual\n"
|
" -3 MAX Close stream after reading at most MAX bytes. The actual\n"
|
||||||
" number of bytes read is randominzed.\n"
|
" number of bytes read is randominzed.\n"
|
||||||
|
" -9 SPEC Priority specification. May be specified several times.\n"
|
||||||
|
" SPEC takes the form stream_id:nread:UI, where U is\n"
|
||||||
|
" urgency and I is incremental. Matched \\d+:\\d+:[0-7][01]\n"
|
||||||
, prog);
|
, prog);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1432,6 +1532,7 @@ main (int argc, char **argv)
|
||||||
struct sport_head sports;
|
struct sport_head sports;
|
||||||
struct prog prog;
|
struct prog prog;
|
||||||
const char *token = NULL;
|
const char *token = NULL;
|
||||||
|
struct priority_spec *priority_specs = NULL;
|
||||||
|
|
||||||
TAILQ_INIT(&sports);
|
TAILQ_INIT(&sports);
|
||||||
memset(&client_ctx, 0, sizeof(client_ctx));
|
memset(&client_ctx, 0, sizeof(client_ctx));
|
||||||
|
@ -1450,6 +1551,7 @@ main (int argc, char **argv)
|
||||||
while (-1 != (opt = getopt(argc, argv, PROG_OPTS
|
while (-1 != (opt = getopt(argc, argv, PROG_OPTS
|
||||||
"46Br:R:IKu:EP:M:n:w:H:p:0:q:e:hatT:b:d:"
|
"46Br:R:IKu:EP:M:n:w:H:p:0:q:e:hatT:b:d:"
|
||||||
"3:" /* 3 is 133+ for "e" ("e" for "early") */
|
"3:" /* 3 is 133+ for "e" ("e" for "early") */
|
||||||
|
"9:" /* 9 sort of looks like P... */
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
"C:"
|
"C:"
|
||||||
#endif
|
#endif
|
||||||
|
@ -1486,6 +1588,7 @@ main (int argc, char **argv)
|
||||||
case 'E': /* E: randomly reprioritize str<E>ams. Now, that's
|
case 'E': /* E: randomly reprioritize str<E>ams. Now, that's
|
||||||
* pretty random. :)
|
* pretty random. :)
|
||||||
*/
|
*/
|
||||||
|
srand((uintptr_t) argv);
|
||||||
randomly_reprioritize_streams = 1;
|
randomly_reprioritize_streams = 1;
|
||||||
break;
|
break;
|
||||||
case 'n':
|
case 'n':
|
||||||
|
@ -1569,6 +1672,45 @@ main (int argc, char **argv)
|
||||||
case '3':
|
case '3':
|
||||||
s_abandon_early = strtol(optarg, NULL, 10);
|
s_abandon_early = strtol(optarg, NULL, 10);
|
||||||
break;
|
break;
|
||||||
|
case '9':
|
||||||
|
{
|
||||||
|
/* Parse priority spec and tack it onto the end of the array */
|
||||||
|
lsquic_stream_id_t stream_id;
|
||||||
|
size_t nread;
|
||||||
|
struct lsquic_ext_http_prio ehp;
|
||||||
|
struct priority_spec *new_specs;
|
||||||
|
stream_id = strtoull(optarg, &optarg, 10);
|
||||||
|
if (*optarg != ':')
|
||||||
|
exit(1);
|
||||||
|
++optarg;
|
||||||
|
nread = strtoull(optarg, &optarg, 10);
|
||||||
|
if (*optarg != ':')
|
||||||
|
exit(1);
|
||||||
|
++optarg;
|
||||||
|
if (!(*optarg >= '0' && *optarg <= '7'))
|
||||||
|
exit(1);
|
||||||
|
ehp.urgency = *optarg++ - '0';
|
||||||
|
if (!(*optarg >= '0' && *optarg <= '1'))
|
||||||
|
exit(1);
|
||||||
|
ehp.incremental = *optarg++ - '0';
|
||||||
|
++s_n_prio_specs;
|
||||||
|
new_specs = realloc(priority_specs,
|
||||||
|
sizeof(priority_specs[0]) * s_n_prio_specs);
|
||||||
|
if (!new_specs)
|
||||||
|
{
|
||||||
|
perror("malloc");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
priority_specs = new_specs;
|
||||||
|
priority_specs[s_n_prio_specs - 1] = (struct priority_spec) {
|
||||||
|
.flags = PRIORITY_SPEC_ACTIVE,
|
||||||
|
.stream_id = stream_id,
|
||||||
|
.nread = nread,
|
||||||
|
.ehp = ehp,
|
||||||
|
};
|
||||||
|
s_priority_specs = priority_specs;
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
if (0 != prog_set_opt(&prog, opt, optarg))
|
if (0 != prog_set_opt(&prog, opt, optarg))
|
||||||
exit(1);
|
exit(1);
|
||||||
|
@ -1660,5 +1802,6 @@ main (int argc, char **argv)
|
||||||
if (client_ctx.qif_fh)
|
if (client_ctx.qif_fh)
|
||||||
(void) fclose(client_ctx.qif_fh);
|
(void) fclose(client_ctx.qif_fh);
|
||||||
|
|
||||||
|
free(priority_specs);
|
||||||
exit(0 == s ? EXIT_SUCCESS : EXIT_FAILURE);
|
exit(0 == s ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,8 +41,8 @@
|
||||||
|
|
||||||
#include <event2/event.h>
|
#include <event2/event.h>
|
||||||
|
|
||||||
#include "test_common.h"
|
|
||||||
#include "lsquic.h"
|
#include "lsquic.h"
|
||||||
|
#include "test_common.h"
|
||||||
#include "prog.h"
|
#include "prog.h"
|
||||||
#include "lsxpack_header.h"
|
#include "lsxpack_header.h"
|
||||||
|
|
||||||
|
@ -1916,6 +1916,11 @@ set_engine_option (struct lsquic_engine_settings *settings,
|
||||||
settings->es_scid_iss_rate = atoi(val);
|
settings->es_scid_iss_rate = atoi(val);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
if (0 == strncmp(name, "ext_http_prio", 13))
|
||||||
|
{
|
||||||
|
settings->es_ext_http_prio = atoi(val);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 14:
|
case 14:
|
||||||
if (0 == strncmp(name, "max_streams_in", 14))
|
if (0 == strncmp(name, "max_streams_in", 14))
|
||||||
|
@ -2043,8 +2048,8 @@ pba_init (struct packout_buf_allocator *pba, unsigned max)
|
||||||
|
|
||||||
|
|
||||||
void *
|
void *
|
||||||
pba_allocate (void *packout_buf_allocator, void *peer_ctx, void *conn_ctx, unsigned short size,
|
pba_allocate (void *packout_buf_allocator, void *peer_ctx,
|
||||||
char is_ipv6)
|
lsquic_conn_ctx_t *conn_ctx, unsigned short size, char is_ipv6)
|
||||||
{
|
{
|
||||||
struct packout_buf_allocator *const pba = packout_buf_allocator;
|
struct packout_buf_allocator *const pba = packout_buf_allocator;
|
||||||
struct packout_buf *pb;
|
struct packout_buf *pb;
|
||||||
|
|
|
@ -115,7 +115,7 @@ void
|
||||||
pba_init (struct packout_buf_allocator *, unsigned max);
|
pba_init (struct packout_buf_allocator *, unsigned max);
|
||||||
|
|
||||||
void *
|
void *
|
||||||
pba_allocate (void *packout_buf_allocator, void*, void *conn_ctx, unsigned short, char);
|
pba_allocate (void *packout_buf_allocator, void*, lsquic_conn_ctx_t *, unsigned short, char);
|
||||||
|
|
||||||
void
|
void
|
||||||
pba_release (void *packout_buf_allocator, void *, void *obj, char);
|
pba_release (void *packout_buf_allocator, void *, void *obj, char);
|
||||||
|
|
|
@ -842,6 +842,13 @@ settings structure:
|
||||||
|
|
||||||
Default value is :macro:`LSQUIC_DF_OPTIMISTIC_NAT`
|
Default value is :macro:`LSQUIC_DF_OPTIMISTIC_NAT`
|
||||||
|
|
||||||
|
.. member:: int es_ext_http_prio
|
||||||
|
|
||||||
|
If set to true, Extensible HTTP Priorities are enabled. This
|
||||||
|
is HTTP/3-only setting.
|
||||||
|
|
||||||
|
Default value is :macro:`LSQUIC_DF_EXT_HTTP_PRIO`
|
||||||
|
|
||||||
To initialize the settings structure to library defaults, use the following
|
To initialize the settings structure to library defaults, use the following
|
||||||
convenience function:
|
convenience function:
|
||||||
|
|
||||||
|
@ -1066,6 +1073,10 @@ out of date. Please check your :file:`lsquic.h` for actual values.*
|
||||||
|
|
||||||
Assume optimistic NAT by default.
|
Assume optimistic NAT by default.
|
||||||
|
|
||||||
|
.. macro:: LSQUIC_DF_EXT_HTTP_PRIO
|
||||||
|
|
||||||
|
Turn on Extensible HTTP Priorities by default.
|
||||||
|
|
||||||
Receiving Packets
|
Receiving Packets
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
@ -2184,6 +2195,72 @@ The following log modules are defined:
|
||||||
- *tokgen*: Token generation and validation.
|
- *tokgen*: Token generation and validation.
|
||||||
- *trapa*: Transport parameter processing.
|
- *trapa*: Transport parameter processing.
|
||||||
|
|
||||||
|
.. _extensible-http-priorities:
|
||||||
|
|
||||||
|
Extensible HTTP Priorities
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
lsquic supports the
|
||||||
|
`Extensible HTTP Priorities Extension <https://tools.ietf.org/html/draft-ietf-httpbis-priority>`_.
|
||||||
|
It is enabled by default when HTTP/3 is used. The "urgency" and "incremental"
|
||||||
|
parameters are included into a dedicated type:
|
||||||
|
|
||||||
|
.. type:: struct lsquic_ext_http_prio
|
||||||
|
|
||||||
|
.. member:: unsigned char urgency
|
||||||
|
|
||||||
|
This value's range is [0, 7], where 0 is the highest and 7 is
|
||||||
|
the lowest urgency.
|
||||||
|
|
||||||
|
.. member:: signed char incremental
|
||||||
|
|
||||||
|
This is a boolean value. The valid range is [0, 1].
|
||||||
|
|
||||||
|
Some useful macros are also available:
|
||||||
|
|
||||||
|
.. macro:: LSQUIC_MAX_HTTP_URGENCY
|
||||||
|
|
||||||
|
The maximum value of the "urgency" parameter is 7.
|
||||||
|
|
||||||
|
.. macro:: LSQUIC_DEF_HTTP_URGENCY
|
||||||
|
|
||||||
|
The default value of the "urgency" parameter is 3.
|
||||||
|
|
||||||
|
.. macro:: LSQUIC_DEF_HTTP_INCREMENTAL
|
||||||
|
|
||||||
|
The default value of the "incremental" parameter is 0.
|
||||||
|
|
||||||
|
There are two functions to
|
||||||
|
manage a stream's priority:
|
||||||
|
|
||||||
|
.. function:: int lsquic_stream_get_http_prio (lsquic_stream_t \*stream, struct lsquic_ext_http_prio \*ehp)
|
||||||
|
|
||||||
|
Get a stream's priority information.
|
||||||
|
|
||||||
|
:param stream: The stream whose priority informaion we want.
|
||||||
|
|
||||||
|
:param ehp: Structure that is to be populated with the stream's
|
||||||
|
priority information.
|
||||||
|
|
||||||
|
:return: Returns zero on success of a negative value on failure.
|
||||||
|
A failure occurs if this is not an HTTP/3 stream or if
|
||||||
|
Extensible HTTP Priorities have not been enabled.
|
||||||
|
See :member:`lsquic_engine_settings.es_ext_http_prio`.
|
||||||
|
|
||||||
|
.. function:: int lsquic_stream_set_http_prio (lsquic_stream_t \*stream, const struct lsquic_ext_http_prio \*ehp)
|
||||||
|
|
||||||
|
Set a stream's priority information.
|
||||||
|
|
||||||
|
:param stream: The stream whose priority we want to set.
|
||||||
|
|
||||||
|
:param ehp: Structure containing the stream's new priority information.
|
||||||
|
|
||||||
|
:return: Returns zero on success of a negative value on failure.
|
||||||
|
A failure occurs if some internal error occured or if this
|
||||||
|
is not an HTTP/3 stream or if Extensible HTTP Priorities
|
||||||
|
haven't been enabled.
|
||||||
|
See :member:`lsquic_engine_settings.es_ext_http_prio`.
|
||||||
|
|
||||||
.. _apiref-datagrams:
|
.. _apiref-datagrams:
|
||||||
|
|
||||||
Datagrams
|
Datagrams
|
||||||
|
|
|
@ -24,9 +24,9 @@ copyright = u'2020, LiteSpeed Technologies'
|
||||||
author = u'LiteSpeed Technologies'
|
author = u'LiteSpeed Technologies'
|
||||||
|
|
||||||
# The short X.Y version
|
# The short X.Y version
|
||||||
version = u'2.21'
|
version = u'2.22'
|
||||||
# The full version, including alpha/beta/rc tags
|
# The full version, including alpha/beta/rc tags
|
||||||
release = u'2.21.0'
|
release = u'2.22.0'
|
||||||
|
|
||||||
|
|
||||||
# -- General configuration ---------------------------------------------------
|
# -- General configuration ---------------------------------------------------
|
||||||
|
|
|
@ -37,6 +37,7 @@ LSQUIC supports nearly all QUIC and HTTP/3 features, including
|
||||||
- TLS Key updates
|
- TLS Key updates
|
||||||
- Extensions:
|
- Extensions:
|
||||||
|
|
||||||
|
- :ref:`extensible-http-priorities`
|
||||||
- :ref:`apiref-datagrams`
|
- :ref:`apiref-datagrams`
|
||||||
- Loss bits extension (allowing network observer to locate source of packet loss)
|
- Loss bits extension (allowing network observer to locate source of packet loss)
|
||||||
- Timestamps extension (allowing for one-way delay calculation, improving performance of some congestion controllers)
|
- Timestamps extension (allowing for one-way delay calculation, improving performance of some congestion controllers)
|
||||||
|
|
|
@ -24,7 +24,7 @@ extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define LSQUIC_MAJOR_VERSION 2
|
#define LSQUIC_MAJOR_VERSION 2
|
||||||
#define LSQUIC_MINOR_VERSION 21
|
#define LSQUIC_MINOR_VERSION 22
|
||||||
#define LSQUIC_PATCH_VERSION 0
|
#define LSQUIC_PATCH_VERSION 0
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -371,6 +371,9 @@ typedef struct ssl_ctx_st * (*lsquic_lookup_cert_f)(
|
||||||
/** Assume optimistic NAT by default. */
|
/** Assume optimistic NAT by default. */
|
||||||
#define LSQUIC_DF_OPTIMISTIC_NAT 1
|
#define LSQUIC_DF_OPTIMISTIC_NAT 1
|
||||||
|
|
||||||
|
/** Turn on Extensible HTTP Priorities by default. */
|
||||||
|
#define LSQUIC_DF_EXT_HTTP_PRIO 1
|
||||||
|
|
||||||
/** By default, incoming packet size is not limited. */
|
/** By default, incoming packet size is not limited. */
|
||||||
#define LSQUIC_DF_MAX_UDP_PAYLOAD_SIZE_RX 0
|
#define LSQUIC_DF_MAX_UDP_PAYLOAD_SIZE_RX 0
|
||||||
|
|
||||||
|
@ -939,6 +942,14 @@ struct lsquic_engine_settings {
|
||||||
* Default value is @ref LSQUIC_DF_OPTIMISTIC_NAT.
|
* Default value is @ref LSQUIC_DF_OPTIMISTIC_NAT.
|
||||||
*/
|
*/
|
||||||
int es_optimistic_nat;
|
int es_optimistic_nat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set to true, Extensible HTTP Priorities are enabled. This
|
||||||
|
* is HTTP/3-only setting.
|
||||||
|
*
|
||||||
|
* Default value is @ref LSQUIC_DF_EXT_HTTP_PRIO
|
||||||
|
*/
|
||||||
|
int es_ext_http_prio;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Initialize `settings' to default values */
|
/* Initialize `settings' to default values */
|
||||||
|
@ -1637,6 +1648,42 @@ unsigned lsquic_stream_priority (const lsquic_stream_t *s);
|
||||||
*/
|
*/
|
||||||
int lsquic_stream_set_priority (lsquic_stream_t *s, unsigned priority);
|
int lsquic_stream_set_priority (lsquic_stream_t *s, unsigned priority);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Definitions for Extensible HTTP Priorities:
|
||||||
|
* https://tools.ietf.org/html/draft-ietf-httpbis-priority-01
|
||||||
|
*/
|
||||||
|
/* This is maximum *value* -- but it's the lowest *priority* */
|
||||||
|
#define LSQUIC_MAX_HTTP_URGENCY 7
|
||||||
|
#define LSQUIC_DEF_HTTP_URGENCY 3
|
||||||
|
#define LSQUIC_DEF_HTTP_INCREMENTAL 0
|
||||||
|
|
||||||
|
struct lsquic_ext_http_prio
|
||||||
|
{
|
||||||
|
unsigned char urgency;
|
||||||
|
signed char incremental;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Extensible HTTP Priorities associated with the stream.
|
||||||
|
*
|
||||||
|
* Returns zero on success of a negative value on failure. A failure occurs
|
||||||
|
* if this is not an HTTP/3 stream or if Extensible HTTP Priorities haven't
|
||||||
|
* been enabled. See @ref es_ext_http_prio.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
lsquic_stream_get_http_prio (lsquic_stream_t *, struct lsquic_ext_http_prio *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Extensible HTTP Priorities of the stream.
|
||||||
|
*
|
||||||
|
* Returns zero on success of a negative value on failure. A failure occurs
|
||||||
|
* if some internal error occured or if this is not an HTTP/3 stream or if
|
||||||
|
* Extensible HTTP Priorities haven't been enabled. See @ref es_ext_http_prio.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
lsquic_stream_set_http_prio (lsquic_stream_t *,
|
||||||
|
const struct lsquic_ext_http_prio *);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a pointer to the connection object. Use it with lsquic_conn_*
|
* Get a pointer to the connection object. Use it with lsquic_conn_*
|
||||||
* functions.
|
* functions.
|
||||||
|
|
|
@ -35,7 +35,9 @@ SET(lsquic_STAT_SRCS
|
||||||
lsquic_hcso_writer.c
|
lsquic_hcso_writer.c
|
||||||
lsquic_headers_stream.c
|
lsquic_headers_stream.c
|
||||||
lsquic_hkdf.c
|
lsquic_hkdf.c
|
||||||
|
lsquic_hpi.c
|
||||||
lsquic_hspack_valid.c
|
lsquic_hspack_valid.c
|
||||||
|
lsquic_http.c
|
||||||
lsquic_http1x_if.c
|
lsquic_http1x_if.c
|
||||||
lsquic_logger.c
|
lsquic_logger.c
|
||||||
lsquic_malo.c
|
lsquic_malo.c
|
||||||
|
@ -75,6 +77,7 @@ SET(lsquic_STAT_SRCS
|
||||||
lsquic_stream.c
|
lsquic_stream.c
|
||||||
lsquic_tokgen.c
|
lsquic_tokgen.c
|
||||||
lsquic_trans_params.c
|
lsquic_trans_params.c
|
||||||
|
lsquic_trechist.c
|
||||||
lsquic_util.c
|
lsquic_util.c
|
||||||
lsquic_varint.c
|
lsquic_varint.c
|
||||||
lsquic_version.c
|
lsquic_version.c
|
||||||
|
@ -103,6 +106,7 @@ ADD_CUSTOM_COMMAND(
|
||||||
DEPENDS ./gen-verstrs.pl ${CMAKE_CURRENT_SOURCE_DIR}/../../include/lsquic.h
|
DEPENDS ./gen-verstrs.pl ${CMAKE_CURRENT_SOURCE_DIR}/../../include/lsquic.h
|
||||||
)
|
)
|
||||||
SET(lsquic_STAT_SRCS ${lsquic_STAT_SRCS} lsquic_versions_to_string.c)
|
SET(lsquic_STAT_SRCS ${lsquic_STAT_SRCS} lsquic_versions_to_string.c)
|
||||||
|
SET(lsquic_STAT_SRCS ${lsquic_STAT_SRCS} ls-sfparser.c)
|
||||||
|
|
||||||
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DXXH_HEADER_NAME=\\\"lsquic_xxhash.h\\\"")
|
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DXXH_HEADER_NAME=\\\"lsquic_xxhash.h\\\"")
|
||||||
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DLSQPACK_ENC_LOGGER_HEADER=\\\"lsquic_qpack_enc_logger.h\\\"")
|
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DLSQPACK_ENC_LOGGER_HEADER=\\\"lsquic_qpack_enc_logger.h\\\"")
|
||||||
|
|
|
@ -5,7 +5,8 @@ AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src -I$(top_srcdir)/src/ls
|
||||||
|
|
||||||
liblsquic_a_METASOURCES = AUTO
|
liblsquic_a_METASOURCES = AUTO
|
||||||
|
|
||||||
liblsquic_a_SOURCES = ls-qpack/lsqpack.c \
|
liblsquic_a_SOURCES = \
|
||||||
|
ls-qpack/lsqpack.c \
|
||||||
lsquic_adaptive_cc.c \
|
lsquic_adaptive_cc.c \
|
||||||
lsquic_alarmset.c \
|
lsquic_alarmset.c \
|
||||||
lsquic_arr.c \
|
lsquic_arr.c \
|
||||||
|
@ -40,7 +41,9 @@ liblsquic_a_SOURCES = ls-qpack/lsqpack.c \
|
||||||
lsquic_hcso_writer.c \
|
lsquic_hcso_writer.c \
|
||||||
lsquic_headers_stream.c \
|
lsquic_headers_stream.c \
|
||||||
lsquic_hkdf.c \
|
lsquic_hkdf.c \
|
||||||
|
lsquic_hpi.c \
|
||||||
lsquic_hspack_valid.c \
|
lsquic_hspack_valid.c \
|
||||||
|
lsquic_http.c \
|
||||||
lsquic_http1x_if.c \
|
lsquic_http1x_if.c \
|
||||||
lsquic_logger.c \
|
lsquic_logger.c \
|
||||||
lsquic_malo.c \
|
lsquic_malo.c \
|
||||||
|
@ -55,7 +58,6 @@ liblsquic_a_SOURCES = ls-qpack/lsqpack.c \
|
||||||
lsquic_packet_in.c \
|
lsquic_packet_in.c \
|
||||||
lsquic_packet_out.c \
|
lsquic_packet_out.c \
|
||||||
lsquic_packet_resize.c \
|
lsquic_packet_resize.c \
|
||||||
lsquic_packints.c \
|
|
||||||
lsquic_parse_Q046.c \
|
lsquic_parse_Q046.c \
|
||||||
lsquic_parse_Q050.c \
|
lsquic_parse_Q050.c \
|
||||||
lsquic_parse_common.c \
|
lsquic_parse_common.c \
|
||||||
|
@ -68,6 +70,7 @@ liblsquic_a_SOURCES = ls-qpack/lsqpack.c \
|
||||||
lsquic_qdec_hdl.c \
|
lsquic_qdec_hdl.c \
|
||||||
lsquic_qenc_hdl.c \
|
lsquic_qenc_hdl.c \
|
||||||
lsquic_qlog.c \
|
lsquic_qlog.c \
|
||||||
|
lsquic_rechist.c \
|
||||||
lsquic_rtt.c \
|
lsquic_rtt.c \
|
||||||
lsquic_send_ctl.c \
|
lsquic_send_ctl.c \
|
||||||
lsquic_senhist.c \
|
lsquic_senhist.c \
|
||||||
|
@ -80,10 +83,13 @@ liblsquic_a_SOURCES = ls-qpack/lsqpack.c \
|
||||||
lsquic_stream.c \
|
lsquic_stream.c \
|
||||||
lsquic_tokgen.c \
|
lsquic_tokgen.c \
|
||||||
lsquic_trans_params.c \
|
lsquic_trans_params.c \
|
||||||
|
lsquic_trechist.c \
|
||||||
lsquic_util.c \
|
lsquic_util.c \
|
||||||
lsquic_varint.c \
|
lsquic_varint.c \
|
||||||
lsquic_version.c \
|
lsquic_version.c \
|
||||||
lsquic_versions_to_string.c
|
lsquic_xxhash.c \
|
||||||
|
lsquic_versions_to_string.c \
|
||||||
|
ls-sfparser.c
|
||||||
|
|
||||||
lsquic_versions_to_string.c: gen-verstrs.pl
|
lsquic_versions_to_string.c: gen-verstrs.pl
|
||||||
$(top_srcdir)/src/liblsquic/gen-verstrs.pl $(top_srcdir)/include/lsquic.h lsquic_versions_to_string.c
|
$(top_srcdir)/src/liblsquic/gen-verstrs.pl $(top_srcdir)/include/lsquic.h lsquic_versions_to_string.c
|
||||||
|
|
3173
src/liblsquic/ls-sfparser.c
Normal file
3173
src/liblsquic/ls-sfparser.c
Normal file
File diff suppressed because it is too large
Load diff
129
src/liblsquic/ls-sfparser.h
Normal file
129
src/liblsquic/ls-sfparser.h
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc. See LICENSE. */
|
||||||
|
/*
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 LiteSpeed Technologies Inc
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* The LiteSpeed Structured Fields Parser parses structured fields decribed in
|
||||||
|
* https://tools.ietf.org/html/draft-ietf-httpbis-header-structure-19
|
||||||
|
*
|
||||||
|
* It provides a simple streaming interface which allows the user to process
|
||||||
|
* structured fields in any manner.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LS_SFPARSER_H
|
||||||
|
#define LS_SFPARSER_H 1
|
||||||
|
|
||||||
|
enum ls_sf_dt
|
||||||
|
{ /* LS SF DT: LiteSpeed Structured Field Data Type */
|
||||||
|
LS_SF_DT_INTEGER,
|
||||||
|
LS_SF_DT_DECIMAL,
|
||||||
|
|
||||||
|
/* Name only applies to dictionary names. They may repeat: the
|
||||||
|
* parser does not drop duplicates.
|
||||||
|
*/
|
||||||
|
LS_SF_DT_NAME,
|
||||||
|
|
||||||
|
/* Parameter name may apply to any applicable preceding Item or
|
||||||
|
* Inner List.
|
||||||
|
*/
|
||||||
|
LS_SF_DT_PARAM_NAME,
|
||||||
|
|
||||||
|
/* The returned string does not include enclosing double quotes. */
|
||||||
|
LS_SF_DT_STRING,
|
||||||
|
|
||||||
|
LS_SF_DT_TOKEN,
|
||||||
|
|
||||||
|
/* The byte sequence is not base64-decoded; it is up to the caller
|
||||||
|
* to do so. The returned string does not include the enclosing
|
||||||
|
* colons.
|
||||||
|
*/
|
||||||
|
LS_SF_DT_BYTESEQ,
|
||||||
|
|
||||||
|
/* Note that true boolean values are serialized *without* the values.
|
||||||
|
* The parser makes one up and passes a pointer to its internal buffer.
|
||||||
|
*/
|
||||||
|
LS_SF_DT_BOOLEAN,
|
||||||
|
|
||||||
|
/* The Inner List has a beginning and an end. The returned strings
|
||||||
|
* are opening and closing parentheses.
|
||||||
|
*/
|
||||||
|
LS_SF_DT_INNER_LIST_BEGIN,
|
||||||
|
LS_SF_DT_INNER_LIST_END,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
enum ls_sf_tlt
|
||||||
|
{ /* LS SF TLT: LiteSpeed Structured Field Top-Level Type */
|
||||||
|
LS_SF_TLT_DICTIONARY,
|
||||||
|
LS_SF_TLT_LIST,
|
||||||
|
LS_SF_TLT_ITEM,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* Return 0 if parsed correctly, -1 on error, -2 if ran out of memory. */
|
||||||
|
int
|
||||||
|
ls_sf_parse (
|
||||||
|
/* Expected type of top-level input. This tells the parser how to
|
||||||
|
* parse the input.
|
||||||
|
*/
|
||||||
|
enum ls_sf_tlt,
|
||||||
|
|
||||||
|
/* Input; does not have to be NUL-terminated: */
|
||||||
|
const char *input, size_t input_sz,
|
||||||
|
|
||||||
|
/* Callback function to call each time a token is parsed. A non-zero
|
||||||
|
* return value indicates that parsing should stop.
|
||||||
|
*/
|
||||||
|
int (*callback)(
|
||||||
|
/* The first argument to the callback is user-specified additional
|
||||||
|
* data.
|
||||||
|
*/
|
||||||
|
void *user_data,
|
||||||
|
/* The second argument is the data type. */
|
||||||
|
enum ls_sf_dt,
|
||||||
|
/* The third and fourth arguments are NUL-terminated string and
|
||||||
|
* its length, respectively. The string can be modified, because
|
||||||
|
* the parser makes a copy.
|
||||||
|
*/
|
||||||
|
char *str, size_t len,
|
||||||
|
/* Offset to the token in the input buffer. In the special case
|
||||||
|
* of an implicit boolean value, this value is negative: this is
|
||||||
|
* because this value is not present in the input buffer.
|
||||||
|
*/
|
||||||
|
int off),
|
||||||
|
|
||||||
|
/* Additional data to pass to the callback: */
|
||||||
|
void *user_data,
|
||||||
|
|
||||||
|
/* Allocate memory from this memory buffer. If set to NULL, regular
|
||||||
|
* system memory allocator will be used.
|
||||||
|
*/
|
||||||
|
char *mem_buf, size_t mem_buf_sz);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Convenience array with type names. */
|
||||||
|
extern const char *const ls_sf_dt2str[];
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -205,14 +205,14 @@ lsquic_conn_push_stream (struct lsquic_conn *lconn, void *hset,
|
||||||
lsquic_conn_ctx_t *
|
lsquic_conn_ctx_t *
|
||||||
lsquic_conn_get_ctx (const struct lsquic_conn *lconn)
|
lsquic_conn_get_ctx (const struct lsquic_conn *lconn)
|
||||||
{
|
{
|
||||||
return lconn->conn_ctx;
|
return lconn->cn_conn_ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
lsquic_conn_set_ctx (struct lsquic_conn *lconn, lsquic_conn_ctx_t *ctx)
|
lsquic_conn_set_ctx (struct lsquic_conn *lconn, lsquic_conn_ctx_t *ctx)
|
||||||
{
|
{
|
||||||
lconn->conn_ctx = ctx;
|
lconn->cn_conn_ctx = ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -312,7 +312,6 @@ struct conn_cid_elem
|
||||||
struct lsquic_conn
|
struct lsquic_conn
|
||||||
{
|
{
|
||||||
void *cn_enc_session;
|
void *cn_enc_session;
|
||||||
lsquic_conn_ctx_t *conn_ctx;
|
|
||||||
const struct enc_session_funcs_common
|
const struct enc_session_funcs_common
|
||||||
*cn_esf_c;
|
*cn_esf_c;
|
||||||
union {
|
union {
|
||||||
|
@ -334,6 +333,7 @@ struct lsquic_conn
|
||||||
lsquic_time_t cn_last_sent;
|
lsquic_time_t cn_last_sent;
|
||||||
lsquic_time_t cn_last_ticked;
|
lsquic_time_t cn_last_ticked;
|
||||||
struct conn_cid_elem *cn_cces; /* At least one is available */
|
struct conn_cid_elem *cn_cces; /* At least one is available */
|
||||||
|
lsquic_conn_ctx_t *cn_conn_ctx;
|
||||||
enum lsquic_conn_flags cn_flags;
|
enum lsquic_conn_flags cn_flags;
|
||||||
enum lsquic_version cn_version:8;
|
enum lsquic_version cn_version:8;
|
||||||
unsigned char cn_cces_mask; /* Those that are set */
|
unsigned char cn_cces_mask; /* Those that are set */
|
||||||
|
|
|
@ -43,6 +43,7 @@ struct lsquic_conn_public {
|
||||||
struct {
|
struct {
|
||||||
struct qpack_enc_hdl *qeh;
|
struct qpack_enc_hdl *qeh;
|
||||||
struct qpack_dec_hdl *qdh;
|
struct qpack_dec_hdl *qdh;
|
||||||
|
struct hcso_writer *hcso;
|
||||||
struct lsquic_hash *promises;
|
struct lsquic_hash *promises;
|
||||||
} ietf;
|
} ietf;
|
||||||
} u;
|
} u;
|
||||||
|
|
|
@ -438,10 +438,10 @@ void serialize_cert_entries(uint8_t* out, int *out_len, cert_entry_t *entries,
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
lsquic_get_certs_count(lsquic_str_t *compressed_crt_buf)
|
lsquic_get_certs_count (const struct compressed_cert *ccert)
|
||||||
{
|
{
|
||||||
char *in = lsquic_str_buf(compressed_crt_buf);
|
const unsigned char *in = ccert->buf;
|
||||||
char *in_end = in + lsquic_str_len(compressed_crt_buf);
|
const unsigned char *in_end = in + ccert->len;
|
||||||
size_t idx = 0;
|
size_t idx = 0;
|
||||||
uint8_t type_byte;
|
uint8_t type_byte;
|
||||||
|
|
||||||
|
@ -578,14 +578,11 @@ static int parse_entries(const unsigned char **in_out, const unsigned char *cons
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* return 0 for OK */
|
struct compressed_cert *
|
||||||
int
|
|
||||||
lsquic_compress_certs (lsquic_str_t **certs, size_t certs_count,
|
lsquic_compress_certs (lsquic_str_t **certs, size_t certs_count,
|
||||||
lsquic_str_t *client_common_set_hashes,
|
lsquic_str_t *client_common_set_hashes,
|
||||||
lsquic_str_t *client_cached_cert_hashes,
|
lsquic_str_t *client_cached_cert_hashes)
|
||||||
lsquic_str_t *result)
|
|
||||||
{
|
{
|
||||||
int rv;
|
|
||||||
size_t i;
|
size_t i;
|
||||||
size_t uncompressed_size = 0, compressed_size = 0 ;
|
size_t uncompressed_size = 0, compressed_size = 0 ;
|
||||||
z_stream z;
|
z_stream z;
|
||||||
|
@ -595,10 +592,12 @@ lsquic_compress_certs (lsquic_str_t **certs, size_t certs_count,
|
||||||
uint8_t* out;
|
uint8_t* out;
|
||||||
uint32_t tmp_size_32;
|
uint32_t tmp_size_32;
|
||||||
cert_entry_t *entries;
|
cert_entry_t *entries;
|
||||||
|
struct compressed_cert *ccert;
|
||||||
|
|
||||||
|
ccert = NULL;
|
||||||
entries = malloc(sizeof(cert_entry_t) * certs_count);
|
entries = malloc(sizeof(cert_entry_t) * certs_count);
|
||||||
if (!entries)
|
if (!entries)
|
||||||
return -1;
|
return NULL;
|
||||||
|
|
||||||
dict = lsquic_str_new(NULL, 0);
|
dict = lsquic_str_new(NULL, 0);
|
||||||
if (!dict)
|
if (!dict)
|
||||||
|
@ -631,16 +630,18 @@ lsquic_compress_certs (lsquic_str_t **certs, size_t certs_count,
|
||||||
entries_size = get_entries_size(entries, certs_count);
|
entries_size = get_entries_size(entries, certs_count);
|
||||||
result_length = entries_size + (uncompressed_size > 0 ? 4 : 0) +
|
result_length = entries_size + (uncompressed_size > 0 ? 4 : 0) +
|
||||||
compressed_size;
|
compressed_size;
|
||||||
lsquic_str_prealloc(result, result_length);
|
ccert = malloc(sizeof(*ccert) + result_length);
|
||||||
|
if (!ccert)
|
||||||
|
goto err;
|
||||||
|
ccert->refcnt = 0;
|
||||||
|
|
||||||
out = (unsigned char *)lsquic_str_buf(result);
|
out = ccert->buf;
|
||||||
serialize_cert_entries(out, &out_len, entries, certs_count);
|
serialize_cert_entries(out, &out_len, entries, certs_count);
|
||||||
out += entries_size;
|
out += entries_size;
|
||||||
|
|
||||||
if (uncompressed_size == 0)
|
if (uncompressed_size == 0)
|
||||||
{
|
{
|
||||||
lsquic_str_setlen(result, entries_size);
|
ccert->len = entries_size;
|
||||||
rv = 0;
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -671,9 +672,7 @@ lsquic_compress_certs (lsquic_str_t **certs, size_t certs_count,
|
||||||
if (Z_STREAM_END != deflate(&z, Z_FINISH))
|
if (Z_STREAM_END != deflate(&z, Z_FINISH))
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
rv = 0;
|
ccert->len = result_length - z.avail_out;
|
||||||
result_length -= z.avail_out;
|
|
||||||
lsquic_str_setlen(result, result_length);
|
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
free(entries);
|
free(entries);
|
||||||
|
@ -681,10 +680,12 @@ lsquic_compress_certs (lsquic_str_t **certs, size_t certs_count,
|
||||||
lsquic_str_delete(dict);
|
lsquic_str_delete(dict);
|
||||||
if (uncompressed_size)
|
if (uncompressed_size)
|
||||||
deflateEnd(&z);
|
deflateEnd(&z);
|
||||||
return rv;
|
return ccert;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
rv = -1;
|
if (ccert)
|
||||||
|
free(ccert);
|
||||||
|
ccert = NULL;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,12 +42,22 @@ struct lsquic_str * lsquic_get_common_certs_hash();
|
||||||
|
|
||||||
int lsquic_get_common_cert(uint64_t hash, uint32_t index, struct lsquic_str *buf);
|
int lsquic_get_common_cert(uint64_t hash, uint32_t index, struct lsquic_str *buf);
|
||||||
|
|
||||||
int lsquic_compress_certs(struct lsquic_str **certs, size_t certs_count,
|
struct compressed_cert
|
||||||
struct lsquic_str *client_common_set_hashes,
|
{
|
||||||
struct lsquic_str *client_cached_cert_hashes,
|
size_t len;
|
||||||
struct lsquic_str *result);
|
unsigned refcnt;
|
||||||
|
unsigned char buf[0]; /* len bytes */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Returns newly allocated structure or NULL on error */
|
||||||
|
struct compressed_cert *
|
||||||
|
lsquic_compress_certs(struct lsquic_str **certs, size_t certs_count,
|
||||||
|
struct lsquic_str *client_common_set_hashes,
|
||||||
|
struct lsquic_str *client_cached_cert_hashes);
|
||||||
|
|
||||||
|
int
|
||||||
|
lsquic_get_certs_count (const struct compressed_cert *);
|
||||||
|
|
||||||
int lsquic_get_certs_count(struct lsquic_str *compressed_crt_buf);
|
|
||||||
int lsquic_decompress_certs(const unsigned char *in, const unsigned char *in_end,
|
int lsquic_decompress_certs(const unsigned char *in, const unsigned char *in_end,
|
||||||
struct lsquic_str *cached_certs, size_t cached_certs_count,
|
struct lsquic_str *cached_certs, size_t cached_certs_count,
|
||||||
struct lsquic_str **out_certs,
|
struct lsquic_str **out_certs,
|
||||||
|
|
|
@ -240,6 +240,9 @@ struct enc_sess_iquic
|
||||||
ESI_RSCID = 1 << 14,
|
ESI_RSCID = 1 << 14,
|
||||||
ESI_ISCID = 1 << 15,
|
ESI_ISCID = 1 << 15,
|
||||||
ESI_RETRY = 1 << 16, /* Connection was retried */
|
ESI_RETRY = 1 << 16, /* Connection was retried */
|
||||||
|
ESI_MAX_PACKNO_INIT = 1 << 17,
|
||||||
|
ESI_MAX_PACKNO_HSK = ESI_MAX_PACKNO_INIT << PNS_HSK,
|
||||||
|
ESI_MAX_PACKNO_APP = ESI_MAX_PACKNO_INIT << PNS_APP,
|
||||||
} esi_flags;
|
} esi_flags;
|
||||||
enum enc_level esi_last_w;
|
enum enc_level esi_last_w;
|
||||||
unsigned esi_trasec_sz;
|
unsigned esi_trasec_sz;
|
||||||
|
@ -475,8 +478,18 @@ strip_hp (struct enc_sess_iquic *enc_sess,
|
||||||
shift += 8;
|
shift += 8;
|
||||||
}
|
}
|
||||||
pns = lsquic_enclev2pns[hp->hp_enc_level];
|
pns = lsquic_enclev2pns[hp->hp_enc_level];
|
||||||
|
if (enc_sess->esi_flags & (ESI_MAX_PACKNO_INIT << pns))
|
||||||
|
{
|
||||||
|
LSQ_DEBUG("pre-decode packno: %"PRIu64, packno);
|
||||||
return decode_packno(enc_sess->esi_max_packno[pns], packno, shift);
|
return decode_packno(enc_sess->esi_max_packno[pns], packno, shift);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LSQ_DEBUG("first packet in %s, packno: %"PRIu64, lsquic_pns2str[pns],
|
||||||
|
packno);
|
||||||
|
return packno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -1976,7 +1989,7 @@ iquic_esf_encrypt_packet (enc_session_t *enc_session_p,
|
||||||
dst_sz = lconn->cn_pf->pf_packout_size(lconn, packet_out);
|
dst_sz = lconn->cn_pf->pf_packout_size(lconn, packet_out);
|
||||||
ipv6 = NP_IS_IPv6(packet_out->po_path);
|
ipv6 = NP_IS_IPv6(packet_out->po_path);
|
||||||
dst = enpub->enp_pmi->pmi_allocate(enpub->enp_pmi_ctx,
|
dst = enpub->enp_pmi->pmi_allocate(enpub->enp_pmi_ctx,
|
||||||
packet_out->po_path->np_peer_ctx, lconn->conn_ctx, dst_sz, ipv6);
|
packet_out->po_path->np_peer_ctx, lconn->cn_conn_ctx, dst_sz, ipv6);
|
||||||
if (!dst)
|
if (!dst)
|
||||||
{
|
{
|
||||||
LSQ_DEBUG("could not allocate memory for outgoing packet of size %zd",
|
LSQ_DEBUG("could not allocate memory for outgoing packet of size %zd",
|
||||||
|
@ -2307,8 +2320,10 @@ iquic_esf_decrypt_packet (enc_session_t *enc_session_p,
|
||||||
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "decrypted packet %"PRIu64,
|
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "decrypted packet %"PRIu64,
|
||||||
packet_in->pi_packno);
|
packet_in->pi_packno);
|
||||||
pns = lsquic_enclev2pns[enc_level];
|
pns = lsquic_enclev2pns[enc_level];
|
||||||
if (packet_in->pi_packno > enc_sess->esi_max_packno[pns])
|
if (packet_in->pi_packno > enc_sess->esi_max_packno[pns]
|
||||||
|
|| !(enc_sess->esi_flags & (ESI_MAX_PACKNO_INIT << pns)))
|
||||||
enc_sess->esi_max_packno[pns] = packet_in->pi_packno;
|
enc_sess->esi_max_packno[pns] = packet_in->pi_packno;
|
||||||
|
enc_sess->esi_flags |= ESI_MAX_PACKNO_INIT << pns;
|
||||||
if (is_valid_packno(pair->ykp_thresh)
|
if (is_valid_packno(pair->ykp_thresh)
|
||||||
&& packet_in->pi_packno > pair->ykp_thresh)
|
&& packet_in->pi_packno > pair->ykp_thresh)
|
||||||
pair->ykp_thresh = packet_in->pi_packno;
|
pair->ykp_thresh = packet_in->pi_packno;
|
||||||
|
|
|
@ -76,6 +76,7 @@
|
||||||
#include "lsquic_version.h"
|
#include "lsquic_version.h"
|
||||||
#include "lsquic_pr_queue.h"
|
#include "lsquic_pr_queue.h"
|
||||||
#include "lsquic_mini_conn.h"
|
#include "lsquic_mini_conn.h"
|
||||||
|
#include "lsquic_trechist.h"
|
||||||
#include "lsquic_mini_conn_ietf.h"
|
#include "lsquic_mini_conn_ietf.h"
|
||||||
#include "lsquic_stock_shi.h"
|
#include "lsquic_stock_shi.h"
|
||||||
#include "lsquic_purga.h"
|
#include "lsquic_purga.h"
|
||||||
|
@ -364,6 +365,7 @@ lsquic_engine_init_settings (struct lsquic_engine_settings *settings,
|
||||||
settings->es_cc_algo = LSQUIC_DF_CC_ALGO;
|
settings->es_cc_algo = LSQUIC_DF_CC_ALGO;
|
||||||
settings->es_cc_rtt_thresh = LSQUIC_DF_CC_RTT_THRESH;
|
settings->es_cc_rtt_thresh = LSQUIC_DF_CC_RTT_THRESH;
|
||||||
settings->es_optimistic_nat = LSQUIC_DF_OPTIMISTIC_NAT;
|
settings->es_optimistic_nat = LSQUIC_DF_OPTIMISTIC_NAT;
|
||||||
|
settings->es_ext_http_prio = LSQUIC_DF_EXT_HTTP_PRIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1969,8 +1971,8 @@ copy_packet (struct lsquic_engine *engine, struct lsquic_conn *conn,
|
||||||
}
|
}
|
||||||
|
|
||||||
packet_out->po_enc_data = engine->pub.enp_pmi->pmi_allocate(
|
packet_out->po_enc_data = engine->pub.enp_pmi->pmi_allocate(
|
||||||
engine->pub.enp_pmi_ctx, packet_out->po_path->np_peer_ctx, conn->conn_ctx,
|
engine->pub.enp_pmi_ctx, packet_out->po_path->np_peer_ctx,
|
||||||
packet_out->po_data_sz, ipv6);
|
conn->cn_conn_ctx, packet_out->po_data_sz, ipv6);
|
||||||
if (!packet_out->po_enc_data)
|
if (!packet_out->po_enc_data)
|
||||||
{
|
{
|
||||||
LSQ_DEBUG("could not allocate memory for outgoing unencrypted packet "
|
LSQ_DEBUG("could not allocate memory for outgoing unencrypted packet "
|
||||||
|
@ -2477,7 +2479,7 @@ send_packets_out (struct lsquic_engine *engine,
|
||||||
batch->outs [n].peer_ctx = packet_out->po_path->np_peer_ctx;
|
batch->outs [n].peer_ctx = packet_out->po_path->np_peer_ctx;
|
||||||
batch->outs [n].local_sa = NP_LOCAL_SA(packet_out->po_path);
|
batch->outs [n].local_sa = NP_LOCAL_SA(packet_out->po_path);
|
||||||
batch->outs [n].dest_sa = NP_PEER_SA(packet_out->po_path);
|
batch->outs [n].dest_sa = NP_PEER_SA(packet_out->po_path);
|
||||||
batch->outs [n].conn_ctx = conn->conn_ctx;
|
batch->outs [n].conn_ctx = conn->cn_conn_ctx;
|
||||||
batch->conns [n] = conn;
|
batch->conns [n] = conn;
|
||||||
}
|
}
|
||||||
*packet = packet_out;
|
*packet = packet_out;
|
||||||
|
|
|
@ -797,7 +797,7 @@ full_conn_ci_client_call_on_new (struct lsquic_conn *lconn)
|
||||||
{
|
{
|
||||||
struct full_conn *const conn = (struct full_conn *) lconn;
|
struct full_conn *const conn = (struct full_conn *) lconn;
|
||||||
assert(conn->fc_flags & FC_CREATED_OK);
|
assert(conn->fc_flags & FC_CREATED_OK);
|
||||||
lconn->conn_ctx = conn->fc_stream_ifs[STREAM_IF_STD].stream_if
|
lconn->cn_conn_ctx = conn->fc_stream_ifs[STREAM_IF_STD].stream_if
|
||||||
->on_new_conn(conn->fc_stream_ifs[STREAM_IF_STD].stream_if_ctx, lconn);
|
->on_new_conn(conn->fc_stream_ifs[STREAM_IF_STD].stream_if_ctx, lconn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -971,7 +971,7 @@ lsquic_gquic_full_conn_server_new (struct lsquic_engine_public *enpub,
|
||||||
lsquic_send_ctl_turn_nstp_on(&conn->fc_send_ctl);
|
lsquic_send_ctl_turn_nstp_on(&conn->fc_send_ctl);
|
||||||
}
|
}
|
||||||
LSQ_DEBUG("Calling on_new_conn callback");
|
LSQ_DEBUG("Calling on_new_conn callback");
|
||||||
lconn_full->conn_ctx = enpub->enp_stream_if->on_new_conn(
|
lconn_full->cn_conn_ctx = enpub->enp_stream_if->on_new_conn(
|
||||||
enpub->enp_stream_if_ctx, &conn->fc_conn);
|
enpub->enp_stream_if_ctx, &conn->fc_conn);
|
||||||
/* Now that user code knows about this connection, process incoming
|
/* Now that user code knows about this connection, process incoming
|
||||||
* packets, if any.
|
* packets, if any.
|
||||||
|
@ -2866,7 +2866,7 @@ process_streams_ready_to_send (struct full_conn *conn)
|
||||||
lsquic_spi_init(&spi, TAILQ_FIRST(&conn->fc_pub.sending_streams),
|
lsquic_spi_init(&spi, TAILQ_FIRST(&conn->fc_pub.sending_streams),
|
||||||
TAILQ_LAST(&conn->fc_pub.sending_streams, lsquic_streams_tailq),
|
TAILQ_LAST(&conn->fc_pub.sending_streams, lsquic_streams_tailq),
|
||||||
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_send_stream),
|
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_send_stream),
|
||||||
&conn->fc_conn, "send", NULL, NULL);
|
&conn->fc_pub, "send", NULL, NULL);
|
||||||
|
|
||||||
for (stream = lsquic_spi_first(&spi); stream;
|
for (stream = lsquic_spi_first(&spi); stream;
|
||||||
stream = lsquic_spi_next(&spi))
|
stream = lsquic_spi_next(&spi))
|
||||||
|
@ -3054,7 +3054,7 @@ process_streams_read_events (struct full_conn *conn)
|
||||||
lsquic_spi_init(&spi, TAILQ_FIRST(&conn->fc_pub.read_streams),
|
lsquic_spi_init(&spi, TAILQ_FIRST(&conn->fc_pub.read_streams),
|
||||||
TAILQ_LAST(&conn->fc_pub.read_streams, lsquic_streams_tailq),
|
TAILQ_LAST(&conn->fc_pub.read_streams, lsquic_streams_tailq),
|
||||||
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_read_stream),
|
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_read_stream),
|
||||||
&conn->fc_conn, "read", NULL, NULL);
|
&conn->fc_pub, "read", NULL, NULL);
|
||||||
|
|
||||||
needs_service = 0;
|
needs_service = 0;
|
||||||
for (stream = lsquic_spi_first(&spi); stream;
|
for (stream = lsquic_spi_first(&spi); stream;
|
||||||
|
@ -3081,7 +3081,7 @@ process_streams_read_events (struct full_conn *conn)
|
||||||
lsquic_spi_init(&spi, TAILQ_FIRST(&conn->fc_pub.read_streams),
|
lsquic_spi_init(&spi, TAILQ_FIRST(&conn->fc_pub.read_streams),
|
||||||
TAILQ_LAST(&conn->fc_pub.read_streams, lsquic_streams_tailq),
|
TAILQ_LAST(&conn->fc_pub.read_streams, lsquic_streams_tailq),
|
||||||
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_read_stream),
|
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_read_stream),
|
||||||
&conn->fc_conn, "read-new",
|
&conn->fc_pub, "read-new",
|
||||||
filter_out_old_streams, &fctx);
|
filter_out_old_streams, &fctx);
|
||||||
for (stream = lsquic_spi_first(&spi); stream;
|
for (stream = lsquic_spi_first(&spi); stream;
|
||||||
stream = lsquic_spi_next(&spi))
|
stream = lsquic_spi_next(&spi))
|
||||||
|
@ -3113,7 +3113,7 @@ process_streams_write_events (struct full_conn *conn, int high_prio)
|
||||||
lsquic_spi_init(&spi, TAILQ_FIRST(&conn->fc_pub.write_streams),
|
lsquic_spi_init(&spi, TAILQ_FIRST(&conn->fc_pub.write_streams),
|
||||||
TAILQ_LAST(&conn->fc_pub.write_streams, lsquic_streams_tailq),
|
TAILQ_LAST(&conn->fc_pub.write_streams, lsquic_streams_tailq),
|
||||||
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_write_stream),
|
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_write_stream),
|
||||||
&conn->fc_conn,
|
&conn->fc_pub,
|
||||||
high_prio ? "write-high" : "write-low", NULL, NULL);
|
high_prio ? "write-high" : "write-low", NULL, NULL);
|
||||||
|
|
||||||
if (high_prio)
|
if (high_prio)
|
||||||
|
|
|
@ -64,14 +64,18 @@
|
||||||
#include "lsquic_http1x_if.h"
|
#include "lsquic_http1x_if.h"
|
||||||
#include "lsquic_qenc_hdl.h"
|
#include "lsquic_qenc_hdl.h"
|
||||||
#include "lsquic_qdec_hdl.h"
|
#include "lsquic_qdec_hdl.h"
|
||||||
|
#include "lsquic_trechist.h"
|
||||||
#include "lsquic_mini_conn_ietf.h"
|
#include "lsquic_mini_conn_ietf.h"
|
||||||
#include "lsquic_tokgen.h"
|
#include "lsquic_tokgen.h"
|
||||||
#include "lsquic_full_conn.h"
|
#include "lsquic_full_conn.h"
|
||||||
#include "lsquic_spi.h"
|
#include "lsquic_spi.h"
|
||||||
|
#include "lsquic_min_heap.h"
|
||||||
|
#include "lsquic_hpi.h"
|
||||||
#include "lsquic_ietf.h"
|
#include "lsquic_ietf.h"
|
||||||
#include "lsquic_push_promise.h"
|
#include "lsquic_push_promise.h"
|
||||||
#include "lsquic_headers.h"
|
#include "lsquic_headers.h"
|
||||||
#include "lsquic_crand.h"
|
#include "lsquic_crand.h"
|
||||||
|
#include "ls-sfparser.h"
|
||||||
|
|
||||||
#define LSQUIC_LOGGER_MODULE LSQLM_CONN
|
#define LSQUIC_LOGGER_MODULE LSQLM_CONN
|
||||||
#define LSQUIC_LOG_CONN_ID ietf_full_conn_ci_get_log_cid(&conn->ifc_conn)
|
#define LSQUIC_LOG_CONN_ID ietf_full_conn_ci_get_log_cid(&conn->ifc_conn)
|
||||||
|
@ -338,6 +342,53 @@ struct inc_ack_stats /* Incoming ACK stats */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
union prio_iter
|
||||||
|
{
|
||||||
|
struct stream_prio_iter spi;
|
||||||
|
struct http_prio_iter hpi;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct prio_iter_if
|
||||||
|
{
|
||||||
|
void (*pii_init) (void *, struct lsquic_stream *first,
|
||||||
|
struct lsquic_stream *last, uintptr_t next_ptr_offset,
|
||||||
|
struct lsquic_conn_public *, const char *name,
|
||||||
|
int (*filter)(void *filter_ctx, struct lsquic_stream *),
|
||||||
|
void *filter_ctx);
|
||||||
|
|
||||||
|
struct lsquic_stream * (*pii_first) (void *);
|
||||||
|
|
||||||
|
struct lsquic_stream * (*pii_next) (void *);
|
||||||
|
|
||||||
|
void (*pii_drop_non_high) (void *);
|
||||||
|
|
||||||
|
void (*pii_drop_high) (void *);
|
||||||
|
|
||||||
|
void (*pii_cleanup) (void *);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static const struct prio_iter_if orig_prio_iter_if = {
|
||||||
|
lsquic_spi_init,
|
||||||
|
lsquic_spi_first,
|
||||||
|
lsquic_spi_next,
|
||||||
|
lsquic_spi_drop_non_high,
|
||||||
|
lsquic_spi_drop_high,
|
||||||
|
lsquic_spi_cleanup,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static const struct prio_iter_if ext_prio_iter_if = {
|
||||||
|
lsquic_hpi_init,
|
||||||
|
lsquic_hpi_first,
|
||||||
|
lsquic_hpi_next,
|
||||||
|
lsquic_hpi_drop_non_high,
|
||||||
|
lsquic_hpi_drop_high,
|
||||||
|
lsquic_hpi_cleanup,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
struct ietf_full_conn
|
struct ietf_full_conn
|
||||||
{
|
{
|
||||||
struct lsquic_conn ifc_conn;
|
struct lsquic_conn ifc_conn;
|
||||||
|
@ -371,6 +422,7 @@ struct ietf_full_conn
|
||||||
const struct lsquic_stream_if
|
const struct lsquic_stream_if
|
||||||
*ifc_stream_if;
|
*ifc_stream_if;
|
||||||
void *ifc_stream_ctx;
|
void *ifc_stream_ctx;
|
||||||
|
const struct prio_iter_if *ifc_pii;
|
||||||
char *ifc_errmsg;
|
char *ifc_errmsg;
|
||||||
struct lsquic_engine_public
|
struct lsquic_engine_public
|
||||||
*ifc_enpub;
|
*ifc_enpub;
|
||||||
|
@ -454,6 +506,7 @@ struct ietf_full_conn
|
||||||
} ifc_u;
|
} ifc_u;
|
||||||
lsquic_time_t ifc_idle_to;
|
lsquic_time_t ifc_idle_to;
|
||||||
lsquic_time_t ifc_ping_period;
|
lsquic_time_t ifc_ping_period;
|
||||||
|
struct lsquic_hash *ifc_bpus;
|
||||||
uint64_t ifc_last_max_data_off_sent;
|
uint64_t ifc_last_max_data_off_sent;
|
||||||
unsigned short ifc_min_dg_sz,
|
unsigned short ifc_min_dg_sz,
|
||||||
ifc_max_dg_sz;
|
ifc_max_dg_sz;
|
||||||
|
@ -994,7 +1047,11 @@ create_bidi_stream_out (struct ietf_full_conn *conn)
|
||||||
if (conn->ifc_enpub->enp_settings.es_rw_once)
|
if (conn->ifc_enpub->enp_settings.es_rw_once)
|
||||||
flags |= SCF_DISP_RW_ONCE;
|
flags |= SCF_DISP_RW_ONCE;
|
||||||
if (conn->ifc_flags & IFC_HTTP)
|
if (conn->ifc_flags & IFC_HTTP)
|
||||||
|
{
|
||||||
flags |= SCF_HTTP;
|
flags |= SCF_HTTP;
|
||||||
|
if (conn->ifc_pii == &ext_prio_iter_if)
|
||||||
|
flags |= SCF_HTTP_PRIO;
|
||||||
|
}
|
||||||
|
|
||||||
stream_id = generate_stream_id(conn, SD_BIDI);
|
stream_id = generate_stream_id(conn, SD_BIDI);
|
||||||
stream = lsquic_stream_new(stream_id, &conn->ifc_pub,
|
stream = lsquic_stream_new(stream_id, &conn->ifc_pub,
|
||||||
|
@ -1190,6 +1247,7 @@ ietf_full_conn_init (struct ietf_full_conn *conn,
|
||||||
return -1;
|
return -1;
|
||||||
conn->ifc_pub.u.ietf.qeh = &conn->ifc_qeh;
|
conn->ifc_pub.u.ietf.qeh = &conn->ifc_qeh;
|
||||||
conn->ifc_pub.u.ietf.qdh = &conn->ifc_qdh;
|
conn->ifc_pub.u.ietf.qdh = &conn->ifc_qdh;
|
||||||
|
conn->ifc_pub.u.ietf.hcso = &conn->ifc_hcso;
|
||||||
|
|
||||||
conn->ifc_peer_hq_settings.header_table_size = HQ_DF_QPACK_MAX_TABLE_CAPACITY;
|
conn->ifc_peer_hq_settings.header_table_size = HQ_DF_QPACK_MAX_TABLE_CAPACITY;
|
||||||
conn->ifc_peer_hq_settings.qpack_blocked_streams = HQ_DF_QPACK_BLOCKED_STREAMS;
|
conn->ifc_peer_hq_settings.qpack_blocked_streams = HQ_DF_QPACK_BLOCKED_STREAMS;
|
||||||
|
@ -1209,6 +1267,10 @@ ietf_full_conn_init (struct ietf_full_conn *conn,
|
||||||
conn->ifc_max_retx_since_last_ack = MAX_RETR_PACKETS_SINCE_LAST_ACK;
|
conn->ifc_max_retx_since_last_ack = MAX_RETR_PACKETS_SINCE_LAST_ACK;
|
||||||
if (conn->ifc_settings->es_noprogress_timeout)
|
if (conn->ifc_settings->es_noprogress_timeout)
|
||||||
conn->ifc_mflags |= MF_NOPROG_TIMEOUT;
|
conn->ifc_mflags |= MF_NOPROG_TIMEOUT;
|
||||||
|
if (conn->ifc_settings->es_ext_http_prio)
|
||||||
|
conn->ifc_pii = &ext_prio_iter_if;
|
||||||
|
else
|
||||||
|
conn->ifc_pii = &orig_prio_iter_if;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1368,9 +1430,9 @@ lsquic_ietf_full_conn_server_new (struct lsquic_engine_public *enpub,
|
||||||
int have_outgoing_ack;
|
int have_outgoing_ack;
|
||||||
lsquic_packno_t next_packno;
|
lsquic_packno_t next_packno;
|
||||||
lsquic_time_t now;
|
lsquic_time_t now;
|
||||||
packno_set_t set;
|
|
||||||
enum packnum_space pns;
|
enum packnum_space pns;
|
||||||
unsigned i;
|
unsigned i;
|
||||||
|
struct ietf_mini_rechist mini_rechist;
|
||||||
|
|
||||||
conn = calloc(1, sizeof(*conn));
|
conn = calloc(1, sizeof(*conn));
|
||||||
if (!conn)
|
if (!conn)
|
||||||
|
@ -1472,13 +1534,13 @@ lsquic_ietf_full_conn_server_new (struct lsquic_engine_public *enpub,
|
||||||
conn->ifc_send_ctl.sc_cur_packno = imc->imc_next_packno - 1;
|
conn->ifc_send_ctl.sc_cur_packno = imc->imc_next_packno - 1;
|
||||||
lsquic_send_ctl_begin_optack_detection(&conn->ifc_send_ctl);
|
lsquic_send_ctl_begin_optack_detection(&conn->ifc_send_ctl);
|
||||||
|
|
||||||
for (pns = 0; pns < N_PNS; ++pns)
|
for (pns = 0; pns < IMICO_N_PNS; ++pns)
|
||||||
{
|
{
|
||||||
for (set = imc->imc_recvd_packnos[pns], i = 0;
|
lsquic_imico_rechist_init(&mini_rechist, imc, pns);
|
||||||
set && i < MAX_PACKETS; set &= ~(1ULL << i), ++i)
|
if (0 != lsquic_rechist_copy_ranges(&conn->ifc_rechist[pns],
|
||||||
if (set & (1ULL << i))
|
&mini_rechist, lsquic_imico_rechist_first,
|
||||||
(void) lsquic_rechist_received(&conn->ifc_rechist[pns], i, 0);
|
lsquic_imico_rechist_next))
|
||||||
if (i)
|
goto err2;
|
||||||
conn->ifc_rechist[pns].rh_largest_acked_received
|
conn->ifc_rechist[pns].rh_largest_acked_received
|
||||||
= imc->imc_largest_recvd[pns];
|
= imc->imc_largest_recvd[pns];
|
||||||
}
|
}
|
||||||
|
@ -1551,7 +1613,7 @@ lsquic_ietf_full_conn_server_new (struct lsquic_engine_public *enpub,
|
||||||
conn->ifc_last_live_update = now;
|
conn->ifc_last_live_update = now;
|
||||||
|
|
||||||
LSQ_DEBUG("Calling on_new_conn callback");
|
LSQ_DEBUG("Calling on_new_conn callback");
|
||||||
conn->ifc_conn.conn_ctx = conn->ifc_enpub->enp_stream_if->on_new_conn(
|
conn->ifc_conn.cn_conn_ctx = conn->ifc_enpub->enp_stream_if->on_new_conn(
|
||||||
conn->ifc_enpub->enp_stream_if_ctx, &conn->ifc_conn);
|
conn->ifc_enpub->enp_stream_if_ctx, &conn->ifc_conn);
|
||||||
|
|
||||||
if (0 != handshake_ok(&conn->ifc_conn))
|
if (0 != handshake_ok(&conn->ifc_conn))
|
||||||
|
@ -2708,19 +2770,21 @@ static void
|
||||||
process_streams_ready_to_send (struct ietf_full_conn *conn)
|
process_streams_ready_to_send (struct ietf_full_conn *conn)
|
||||||
{
|
{
|
||||||
struct lsquic_stream *stream;
|
struct lsquic_stream *stream;
|
||||||
struct stream_prio_iter spi;
|
union prio_iter pi;
|
||||||
|
|
||||||
assert(!TAILQ_EMPTY(&conn->ifc_pub.sending_streams));
|
assert(!TAILQ_EMPTY(&conn->ifc_pub.sending_streams));
|
||||||
|
|
||||||
lsquic_spi_init(&spi, TAILQ_FIRST(&conn->ifc_pub.sending_streams),
|
conn->ifc_pii->pii_init(&pi, TAILQ_FIRST(&conn->ifc_pub.sending_streams),
|
||||||
TAILQ_LAST(&conn->ifc_pub.sending_streams, lsquic_streams_tailq),
|
TAILQ_LAST(&conn->ifc_pub.sending_streams, lsquic_streams_tailq),
|
||||||
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_send_stream),
|
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_send_stream),
|
||||||
&conn->ifc_conn, "send", NULL, NULL);
|
&conn->ifc_pub, "send", NULL, NULL);
|
||||||
|
|
||||||
for (stream = lsquic_spi_first(&spi); stream;
|
for (stream = conn->ifc_pii->pii_first(&pi); stream;
|
||||||
stream = lsquic_spi_next(&spi))
|
stream = conn->ifc_pii->pii_next(&pi))
|
||||||
if (!process_stream_ready_to_send(conn, stream))
|
if (!process_stream_ready_to_send(conn, stream))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
conn->ifc_pii->pii_cleanup(&pi);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2760,7 +2824,7 @@ ietf_full_conn_ci_client_call_on_new (struct lsquic_conn *lconn)
|
||||||
{
|
{
|
||||||
struct ietf_full_conn *conn = (struct ietf_full_conn *) lconn;
|
struct ietf_full_conn *conn = (struct ietf_full_conn *) lconn;
|
||||||
assert(conn->ifc_flags & IFC_CREATED_OK);
|
assert(conn->ifc_flags & IFC_CREATED_OK);
|
||||||
lconn->conn_ctx = conn->ifc_enpub->enp_stream_if->on_new_conn(
|
lconn->cn_conn_ctx = conn->ifc_enpub->enp_stream_if->on_new_conn(
|
||||||
conn->ifc_enpub->enp_stream_if_ctx, lconn);
|
conn->ifc_enpub->enp_stream_if_ctx, lconn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2976,6 +3040,13 @@ ietf_full_conn_ci_destroy (struct lsquic_conn *lconn)
|
||||||
}
|
}
|
||||||
for (i = 0; i < N_SITS; ++i)
|
for (i = 0; i < N_SITS; ++i)
|
||||||
lsquic_set64_cleanup(&conn->ifc_closed_stream_ids[i]);
|
lsquic_set64_cleanup(&conn->ifc_closed_stream_ids[i]);
|
||||||
|
if (conn->ifc_bpus)
|
||||||
|
{
|
||||||
|
for (el = lsquic_hash_first(conn->ifc_bpus); el;
|
||||||
|
el = lsquic_hash_next(conn->ifc_bpus))
|
||||||
|
free(lsquic_hashelem_getdata(el));
|
||||||
|
lsquic_hash_destroy(conn->ifc_bpus);
|
||||||
|
}
|
||||||
lsquic_hash_destroy(conn->ifc_pub.all_streams);
|
lsquic_hash_destroy(conn->ifc_pub.all_streams);
|
||||||
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "full connection destroyed");
|
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "full connection destroyed");
|
||||||
free(conn->ifc_errmsg);
|
free(conn->ifc_errmsg);
|
||||||
|
@ -3950,7 +4021,7 @@ process_streams_read_events (struct ietf_full_conn *conn)
|
||||||
struct lsquic_stream *stream;
|
struct lsquic_stream *stream;
|
||||||
int iters;
|
int iters;
|
||||||
enum stream_q_flags q_flags, needs_service;
|
enum stream_q_flags q_flags, needs_service;
|
||||||
struct stream_prio_iter spi;
|
union prio_iter pi;
|
||||||
static const char *const labels[2] = { "read-0", "read-1", };
|
static const char *const labels[2] = { "read-0", "read-1", };
|
||||||
|
|
||||||
if (TAILQ_EMPTY(&conn->ifc_pub.read_streams))
|
if (TAILQ_EMPTY(&conn->ifc_pub.read_streams))
|
||||||
|
@ -3960,19 +4031,20 @@ process_streams_read_events (struct ietf_full_conn *conn)
|
||||||
iters = 0;
|
iters = 0;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
lsquic_spi_init(&spi, TAILQ_FIRST(&conn->ifc_pub.read_streams),
|
conn->ifc_pii->pii_init(&pi, TAILQ_FIRST(&conn->ifc_pub.read_streams),
|
||||||
TAILQ_LAST(&conn->ifc_pub.read_streams, lsquic_streams_tailq),
|
TAILQ_LAST(&conn->ifc_pub.read_streams, lsquic_streams_tailq),
|
||||||
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_read_stream),
|
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_read_stream),
|
||||||
&conn->ifc_conn, labels[iters], NULL, NULL);
|
&conn->ifc_pub, labels[iters], NULL, NULL);
|
||||||
|
|
||||||
needs_service = 0;
|
needs_service = 0;
|
||||||
for (stream = lsquic_spi_first(&spi); stream;
|
for (stream = conn->ifc_pii->pii_first(&pi); stream;
|
||||||
stream = lsquic_spi_next(&spi))
|
stream = conn->ifc_pii->pii_next(&pi))
|
||||||
{
|
{
|
||||||
q_flags = stream->sm_qflags & SMQF_SERVICE_FLAGS;
|
q_flags = stream->sm_qflags & SMQF_SERVICE_FLAGS;
|
||||||
lsquic_stream_dispatch_read_events(stream);
|
lsquic_stream_dispatch_read_events(stream);
|
||||||
needs_service |= q_flags ^ (stream->sm_qflags & SMQF_SERVICE_FLAGS);
|
needs_service |= q_flags ^ (stream->sm_qflags & SMQF_SERVICE_FLAGS);
|
||||||
}
|
}
|
||||||
|
conn->ifc_pii->pii_cleanup(&pi);
|
||||||
|
|
||||||
if (needs_service)
|
if (needs_service)
|
||||||
service_streams(conn);
|
service_streams(conn);
|
||||||
|
@ -4045,23 +4117,25 @@ static void
|
||||||
process_streams_write_events (struct ietf_full_conn *conn, int high_prio)
|
process_streams_write_events (struct ietf_full_conn *conn, int high_prio)
|
||||||
{
|
{
|
||||||
struct lsquic_stream *stream;
|
struct lsquic_stream *stream;
|
||||||
struct stream_prio_iter spi;
|
union prio_iter pi;
|
||||||
|
|
||||||
lsquic_spi_init(&spi, TAILQ_FIRST(&conn->ifc_pub.write_streams),
|
conn->ifc_pii->pii_init(&pi, TAILQ_FIRST(&conn->ifc_pub.write_streams),
|
||||||
TAILQ_LAST(&conn->ifc_pub.write_streams, lsquic_streams_tailq),
|
TAILQ_LAST(&conn->ifc_pub.write_streams, lsquic_streams_tailq),
|
||||||
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_write_stream),
|
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_write_stream),
|
||||||
&conn->ifc_conn,
|
&conn->ifc_pub,
|
||||||
high_prio ? "write-high" : "write-low", NULL, NULL);
|
high_prio ? "write-high" : "write-low", NULL, NULL);
|
||||||
|
|
||||||
if (high_prio)
|
if (high_prio)
|
||||||
lsquic_spi_drop_non_high(&spi);
|
conn->ifc_pii->pii_drop_non_high(&pi);
|
||||||
else
|
else
|
||||||
lsquic_spi_drop_high(&spi);
|
conn->ifc_pii->pii_drop_high(&pi);
|
||||||
|
|
||||||
for (stream = lsquic_spi_first(&spi); stream && write_is_possible(conn);
|
for (stream = conn->ifc_pii->pii_first(&pi);
|
||||||
stream = lsquic_spi_next(&spi))
|
stream && write_is_possible(conn);
|
||||||
|
stream = conn->ifc_pii->pii_next(&pi))
|
||||||
if (stream->sm_qflags & SMQF_WRITE_Q_FLAGS)
|
if (stream->sm_qflags & SMQF_WRITE_Q_FLAGS)
|
||||||
lsquic_stream_dispatch_write_events(stream);
|
lsquic_stream_dispatch_write_events(stream);
|
||||||
|
conn->ifc_pii->pii_cleanup(&pi);
|
||||||
|
|
||||||
maybe_conn_flush_special_streams(conn);
|
maybe_conn_flush_special_streams(conn);
|
||||||
}
|
}
|
||||||
|
@ -4844,12 +4918,22 @@ maybe_schedule_ss_for_stream (struct ietf_full_conn *conn,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct buffered_priority_update
|
||||||
|
{
|
||||||
|
struct lsquic_hash_elem hash_el;
|
||||||
|
lsquic_stream_id_t stream_id;
|
||||||
|
struct lsquic_ext_http_prio ehp;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/* This function is called to create incoming streams */
|
/* This function is called to create incoming streams */
|
||||||
static struct lsquic_stream *
|
static struct lsquic_stream *
|
||||||
new_stream (struct ietf_full_conn *conn, lsquic_stream_id_t stream_id,
|
new_stream (struct ietf_full_conn *conn, lsquic_stream_id_t stream_id,
|
||||||
enum stream_ctor_flags flags)
|
enum stream_ctor_flags flags)
|
||||||
{
|
{
|
||||||
const struct lsquic_stream_if *iface;
|
const struct lsquic_stream_if *iface;
|
||||||
|
struct buffered_priority_update *bpu;
|
||||||
|
struct lsquic_hash_elem *el;
|
||||||
void *stream_ctx;
|
void *stream_ctx;
|
||||||
struct lsquic_stream *stream;
|
struct lsquic_stream *stream;
|
||||||
unsigned initial_window;
|
unsigned initial_window;
|
||||||
|
@ -4876,7 +4960,11 @@ new_stream (struct ietf_full_conn *conn, lsquic_stream_id_t stream_id,
|
||||||
if (conn->ifc_enpub->enp_settings.es_rw_once)
|
if (conn->ifc_enpub->enp_settings.es_rw_once)
|
||||||
flags |= SCF_DISP_RW_ONCE;
|
flags |= SCF_DISP_RW_ONCE;
|
||||||
if (conn->ifc_flags & IFC_HTTP)
|
if (conn->ifc_flags & IFC_HTTP)
|
||||||
|
{
|
||||||
flags |= SCF_HTTP;
|
flags |= SCF_HTTP;
|
||||||
|
if (conn->ifc_pii == &ext_prio_iter_if)
|
||||||
|
flags |= SCF_HTTP_PRIO;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (((stream_id >> SD_SHIFT) & 1) == SD_UNI)
|
if (((stream_id >> SD_SHIFT) & 1) == SD_UNI)
|
||||||
|
@ -4891,6 +4979,20 @@ new_stream (struct ietf_full_conn *conn, lsquic_stream_id_t stream_id,
|
||||||
conn->ifc_cfg.max_stream_send, flags);
|
conn->ifc_cfg.max_stream_send, flags);
|
||||||
if (stream)
|
if (stream)
|
||||||
{
|
{
|
||||||
|
if (conn->ifc_bpus)
|
||||||
|
{
|
||||||
|
el = lsquic_hash_find(conn->ifc_bpus, &stream->id,
|
||||||
|
sizeof(stream->id));
|
||||||
|
if (el)
|
||||||
|
{
|
||||||
|
LSQ_DEBUG("apply buffered PRIORITY_UPDATE to stream %"PRIu64,
|
||||||
|
stream->id);
|
||||||
|
lsquic_hash_erase(conn->ifc_bpus, el);
|
||||||
|
bpu = lsquic_hashelem_getdata(el);
|
||||||
|
(void) lsquic_stream_set_http_prio(stream, &bpu->ehp);
|
||||||
|
free(bpu);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (lsquic_hash_insert(conn->ifc_pub.all_streams, &stream->id,
|
if (lsquic_hash_insert(conn->ifc_pub.all_streams, &stream->id,
|
||||||
sizeof(stream->id), stream, &stream->sm_hash_el))
|
sizeof(stream->id), stream, &stream->sm_hash_el))
|
||||||
{
|
{
|
||||||
|
@ -8522,6 +8624,174 @@ on_goaway_server (void *ctx, uint64_t max_push_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_priority_update_client (void *ctx, enum hq_frame_type frame_type,
|
||||||
|
uint64_t id, const char *pfv, size_t pfv_sz)
|
||||||
|
{
|
||||||
|
struct ietf_full_conn *const conn = ctx;
|
||||||
|
|
||||||
|
if (conn->ifc_pii == &ext_prio_iter_if)
|
||||||
|
ABORT_QUIETLY(1, HEC_FRAME_UNEXPECTED, "Frame type %u is not "
|
||||||
|
"expected to be sent by the server", (unsigned) frame_type);
|
||||||
|
/* else ignore */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* This should not happen often, so do not bother to optimize memory. */
|
||||||
|
static int
|
||||||
|
buffer_priority_update (struct ietf_full_conn *conn,
|
||||||
|
lsquic_stream_id_t stream_id, const struct lsquic_ext_http_prio *ehp)
|
||||||
|
{
|
||||||
|
struct buffered_priority_update *bpu;
|
||||||
|
struct lsquic_hash_elem *el;
|
||||||
|
|
||||||
|
if (!conn->ifc_bpus)
|
||||||
|
{
|
||||||
|
conn->ifc_bpus = lsquic_hash_create();
|
||||||
|
if (!conn->ifc_bpus)
|
||||||
|
{
|
||||||
|
ABORT_ERROR("cannot allocate BPUs hash");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
goto insert_new;
|
||||||
|
}
|
||||||
|
|
||||||
|
el = lsquic_hash_find(conn->ifc_bpus, &stream_id, sizeof(stream_id));
|
||||||
|
if (el)
|
||||||
|
{
|
||||||
|
bpu = lsquic_hashelem_getdata(el);
|
||||||
|
bpu->ehp = *ehp;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
insert_new:
|
||||||
|
bpu = malloc(sizeof(*bpu));
|
||||||
|
if (!bpu)
|
||||||
|
{
|
||||||
|
ABORT_ERROR("cannot allocate BPU");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bpu->hash_el.qhe_flags = 0;
|
||||||
|
bpu->stream_id = stream_id;
|
||||||
|
bpu->ehp = *ehp;
|
||||||
|
if (!lsquic_hash_insert(conn->ifc_bpus, &bpu->stream_id,
|
||||||
|
sizeof(bpu->stream_id), bpu, &bpu->hash_el))
|
||||||
|
{
|
||||||
|
free(bpu);
|
||||||
|
ABORT_ERROR("cannot insert BPU");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_priority_update_server (void *ctx, enum hq_frame_type frame_type,
|
||||||
|
uint64_t id, const char *pfv, size_t pfv_sz)
|
||||||
|
{
|
||||||
|
struct ietf_full_conn *const conn = ctx;
|
||||||
|
struct lsquic_hash_elem *el;
|
||||||
|
struct push_promise *promise;
|
||||||
|
struct lsquic_stream *stream;
|
||||||
|
enum stream_id_type sit;
|
||||||
|
struct lsquic_ext_http_prio ehp;
|
||||||
|
|
||||||
|
if (conn->ifc_pii != &ext_prio_iter_if)
|
||||||
|
{
|
||||||
|
LSQ_DEBUG("Ignore PRIORITY_UPDATE frame");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frame_type == HQFT_PRIORITY_UPDATE_STREAM)
|
||||||
|
{
|
||||||
|
sit = id & SIT_MASK;
|
||||||
|
if (sit != SIT_BIDI_CLIENT)
|
||||||
|
{
|
||||||
|
ABORT_QUIETLY(1, HEC_ID_ERROR, "PRIORITY_UPDATE for non-request "
|
||||||
|
"stream");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (id >= conn->ifc_max_allowed_stream_id[sit])
|
||||||
|
{
|
||||||
|
ABORT_QUIETLY(1, HEC_ID_ERROR, "PRIORITY_UPDATE for non-existing "
|
||||||
|
"stream %"PRIu64" exceeds allowed max of %"PRIu64,
|
||||||
|
id, conn->ifc_max_allowed_stream_id[sit]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
stream = find_stream_by_id(conn, id);
|
||||||
|
if (!stream && conn_is_stream_closed(conn, id))
|
||||||
|
{
|
||||||
|
LSQ_DEBUG("stream %"PRIu64" closed, ignore PRIORITY_UPDATE", id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (id >= conn->ifc_u.ser.ifser_next_push_id)
|
||||||
|
{
|
||||||
|
ABORT_QUIETLY(1, HEC_ID_ERROR, "received PRIORITY_UPDATE with "
|
||||||
|
"ID=%"PRIu64", which is greater than the maximum Push ID "
|
||||||
|
"ever generated by this connection", id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
el = lsquic_hash_find(conn->ifc_pub.u.ietf.promises, &id, sizeof(id));
|
||||||
|
if (!el)
|
||||||
|
{
|
||||||
|
LSQ_DEBUG("push promise %"PRIu64" not found, ignore "
|
||||||
|
"PRIORITY_UPDATE", id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
promise = lsquic_hashelem_getdata(el);
|
||||||
|
stream = promise->pp_pushed_stream;
|
||||||
|
assert(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
ehp = (struct lsquic_ext_http_prio) {
|
||||||
|
.urgency = LSQUIC_DEF_HTTP_URGENCY,
|
||||||
|
.incremental = LSQUIC_DEF_HTTP_INCREMENTAL,
|
||||||
|
};
|
||||||
|
if (pfv_sz)
|
||||||
|
{
|
||||||
|
switch (lsquic_http_parse_pfv(pfv, pfv_sz, NULL, &ehp,
|
||||||
|
(char *) conn->ifc_pub.mm->acki,
|
||||||
|
sizeof(*conn->ifc_pub.mm->acki)))
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
LSQ_DEBUG("Parsed PFV `%.*s' correctly", (int) pfv_sz, pfv);
|
||||||
|
break;
|
||||||
|
case -2: /* Out of memory, ignore */
|
||||||
|
LSQ_INFO("Ignore PFV `%.*s': out of memory", (int) pfv_sz, pfv);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
LSQ_INFO("connection error due to invalid PFV `%.*s'",
|
||||||
|
(int) pfv_sz, pfv);
|
||||||
|
/* From the draft (between versions 1 and 2):
|
||||||
|
" Failure to parse the Priority Field Value MUST be treated
|
||||||
|
" as a connection error of type FRAME_ENCODING_ERROR.
|
||||||
|
*/
|
||||||
|
ABORT_QUIETLY(1, HEC_FRAME_ERROR, "cannot parse Priority Field "
|
||||||
|
"Value in PRIORITY_UPDATE frame");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ /* Empty PFV means "use defaults" */ }
|
||||||
|
|
||||||
|
if (stream)
|
||||||
|
(void) lsquic_stream_set_http_prio(stream, &ehp);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assert(frame_type == HQFT_PRIORITY_UPDATE_STREAM);
|
||||||
|
if (0 == buffer_priority_update(conn, id, &ehp))
|
||||||
|
LSQ_INFO("buffered priority update for stream %"PRIu64"; "
|
||||||
|
"urgency: %hhu, incremental: %hhd", id, ehp.urgency,
|
||||||
|
ehp.incremental);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
on_unexpected_frame (void *ctx, uint64_t frame_type)
|
on_unexpected_frame (void *ctx, uint64_t frame_type)
|
||||||
{
|
{
|
||||||
|
@ -8539,6 +8809,7 @@ static const struct hcsi_callbacks hcsi_callbacks_server_27 =
|
||||||
.on_setting = on_setting,
|
.on_setting = on_setting,
|
||||||
.on_goaway = on_goaway_server_27,
|
.on_goaway = on_goaway_server_27,
|
||||||
.on_unexpected_frame = on_unexpected_frame,
|
.on_unexpected_frame = on_unexpected_frame,
|
||||||
|
.on_priority_update = on_priority_update_server,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct hcsi_callbacks hcsi_callbacks_client_27 =
|
static const struct hcsi_callbacks hcsi_callbacks_client_27 =
|
||||||
|
@ -8549,6 +8820,7 @@ static const struct hcsi_callbacks hcsi_callbacks_client_27 =
|
||||||
.on_setting = on_setting,
|
.on_setting = on_setting,
|
||||||
.on_goaway = on_goaway_client_28 /* sic */,
|
.on_goaway = on_goaway_client_28 /* sic */,
|
||||||
.on_unexpected_frame = on_unexpected_frame,
|
.on_unexpected_frame = on_unexpected_frame,
|
||||||
|
.on_priority_update = on_priority_update_client,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -8560,6 +8832,7 @@ static const struct hcsi_callbacks hcsi_callbacks_server_28 =
|
||||||
.on_setting = on_setting,
|
.on_setting = on_setting,
|
||||||
.on_goaway = on_goaway_server /* sic */,
|
.on_goaway = on_goaway_server /* sic */,
|
||||||
.on_unexpected_frame = on_unexpected_frame,
|
.on_unexpected_frame = on_unexpected_frame,
|
||||||
|
.on_priority_update = on_priority_update_server,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct hcsi_callbacks hcsi_callbacks_client_28 =
|
static const struct hcsi_callbacks hcsi_callbacks_client_28 =
|
||||||
|
@ -8570,6 +8843,7 @@ static const struct hcsi_callbacks hcsi_callbacks_client_28 =
|
||||||
.on_setting = on_setting,
|
.on_setting = on_setting,
|
||||||
.on_goaway = on_goaway_client_28,
|
.on_goaway = on_goaway_client_28,
|
||||||
.on_unexpected_frame = on_unexpected_frame,
|
.on_unexpected_frame = on_unexpected_frame,
|
||||||
|
.on_priority_update = on_priority_update_client,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -8581,6 +8855,7 @@ static const struct hcsi_callbacks hcsi_callbacks_server_29 =
|
||||||
.on_setting = on_setting,
|
.on_setting = on_setting,
|
||||||
.on_goaway = on_goaway_server,
|
.on_goaway = on_goaway_server,
|
||||||
.on_unexpected_frame = on_unexpected_frame,
|
.on_unexpected_frame = on_unexpected_frame,
|
||||||
|
.on_priority_update = on_priority_update_server,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct hcsi_callbacks hcsi_callbacks_client_29 =
|
static const struct hcsi_callbacks hcsi_callbacks_client_29 =
|
||||||
|
@ -8591,6 +8866,7 @@ static const struct hcsi_callbacks hcsi_callbacks_client_29 =
|
||||||
.on_setting = on_setting,
|
.on_setting = on_setting,
|
||||||
.on_goaway = on_goaway_client,
|
.on_goaway = on_goaway_client,
|
||||||
.on_unexpected_frame = on_unexpected_frame,
|
.on_unexpected_frame = on_unexpected_frame,
|
||||||
|
.on_priority_update = on_priority_update_client,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -76,6 +76,8 @@ static struct conn_cid_elem dummy_cce;
|
||||||
static const struct lsquic_conn dummy_lsquic_conn = { .cn_cces = &dummy_cce, };
|
static const struct lsquic_conn dummy_lsquic_conn = { .cn_cces = &dummy_cce, };
|
||||||
static const struct lsquic_conn *const lconn = &dummy_lsquic_conn;
|
static const struct lsquic_conn *const lconn = &dummy_lsquic_conn;
|
||||||
|
|
||||||
|
static int s_ccrt_idx;
|
||||||
|
|
||||||
static const int s_log_seal_and_open;
|
static const int s_log_seal_and_open;
|
||||||
static char s_str[0x1000];
|
static char s_str[0x1000];
|
||||||
|
|
||||||
|
@ -167,7 +169,7 @@ typedef struct hs_ctx_st
|
||||||
struct lsquic_str prof;
|
struct lsquic_str prof;
|
||||||
|
|
||||||
struct lsquic_str csct;
|
struct lsquic_str csct;
|
||||||
struct lsquic_str crt; /* compressed certs buffer */
|
struct compressed_cert *ccert;
|
||||||
struct lsquic_str scfg_pubs; /* Need to copy PUBS, as KEXS comes after it */
|
struct lsquic_str scfg_pubs; /* Need to copy PUBS, as KEXS comes after it */
|
||||||
} hs_ctx_t;
|
} hs_ctx_t;
|
||||||
|
|
||||||
|
@ -314,9 +316,6 @@ static void s_free_cert_hash_item(cert_item_t *item);
|
||||||
static cert_item_t* insert_cert(struct lsquic_engine_public *,
|
static cert_item_t* insert_cert(struct lsquic_engine_public *,
|
||||||
const unsigned char *key, size_t key_sz, const struct lsquic_str *crt);
|
const unsigned char *key, size_t key_sz, const struct lsquic_str *crt);
|
||||||
|
|
||||||
static compress_cert_hash_item_t* find_compress_certs(struct lsquic_engine_public *, struct lsquic_str *domain);
|
|
||||||
static compress_cert_hash_item_t *make_compress_cert_hash_item(struct lsquic_str *domain, struct lsquic_str *crts_compress_buf);
|
|
||||||
|
|
||||||
#ifdef NDEBUG
|
#ifdef NDEBUG
|
||||||
static
|
static
|
||||||
enum hsk_failure_reason
|
enum hsk_failure_reason
|
||||||
|
@ -338,6 +337,8 @@ static uint64_t get_tag_value_i64(unsigned char *, int);
|
||||||
|
|
||||||
static void determine_keys(struct lsquic_enc_session *enc_session);
|
static void determine_keys(struct lsquic_enc_session *enc_session);
|
||||||
|
|
||||||
|
static void put_compressed_cert (struct compressed_cert *);
|
||||||
|
|
||||||
|
|
||||||
#if LSQUIC_KEEP_ENC_SESS_HISTORY
|
#if LSQUIC_KEEP_ENC_SESS_HISTORY
|
||||||
static void
|
static void
|
||||||
|
@ -354,10 +355,26 @@ eshist_append (struct lsquic_enc_session *enc_session,
|
||||||
# define ESHIST_APPEND(sess, event) do { } while (0)
|
# define ESHIST_APPEND(sess, event) do { } while (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
free_compressed_cert (void *parent, void *ptr, CRYPTO_EX_DATA *ad,
|
||||||
|
int index, long argl, void *argp)
|
||||||
|
{
|
||||||
|
put_compressed_cert(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
lsquic_handshake_init(int flags)
|
lsquic_handshake_init(int flags)
|
||||||
{
|
{
|
||||||
lsquic_crypto_init();
|
lsquic_crypto_init();
|
||||||
|
if (flags & LSQUIC_GLOBAL_SERVER)
|
||||||
|
{
|
||||||
|
s_ccrt_idx = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,
|
||||||
|
free_compressed_cert);
|
||||||
|
if (s_ccrt_idx < 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
return lsquic_crt_init();
|
return lsquic_crt_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -526,53 +543,6 @@ insert_cert (struct lsquic_engine_public *enpub, const unsigned char *key,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* server */
|
|
||||||
static compress_cert_hash_item_t *
|
|
||||||
find_compress_certs (struct lsquic_engine_public *enpub, lsquic_str_t *domain)
|
|
||||||
{
|
|
||||||
struct lsquic_hash_elem *el;
|
|
||||||
|
|
||||||
if (!enpub->enp_compressed_server_certs)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
el = lsquic_hash_find(enpub->enp_compressed_server_certs,
|
|
||||||
lsquic_str_cstr(domain), lsquic_str_len(domain));
|
|
||||||
if (el == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return lsquic_hashelem_getdata(el);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* server */
|
|
||||||
static compress_cert_hash_item_t *
|
|
||||||
make_compress_cert_hash_item(lsquic_str_t *domain, lsquic_str_t *crts_compress_buf)
|
|
||||||
{
|
|
||||||
compress_cert_hash_item_t *item = calloc(1, sizeof(*item));
|
|
||||||
item->crts_compress_buf = lsquic_str_new(NULL, 0);
|
|
||||||
item->domain = lsquic_str_new(NULL, 0);
|
|
||||||
lsquic_str_copy(item->domain, domain);
|
|
||||||
lsquic_str_copy(item->crts_compress_buf, crts_compress_buf);
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* server */
|
|
||||||
static int
|
|
||||||
insert_compress_certs (struct lsquic_engine_public *enpub,
|
|
||||||
compress_cert_hash_item_t *item)
|
|
||||||
{
|
|
||||||
if (lsquic_hash_insert(enpub->enp_compressed_server_certs,
|
|
||||||
lsquic_str_cstr(item->domain),
|
|
||||||
lsquic_str_len(item->domain), item, &item->hash_el) == NULL)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
enum rtt_deserialize_return_type
|
enum rtt_deserialize_return_type
|
||||||
{
|
{
|
||||||
RTT_DESERIALIZE_OK = 0,
|
RTT_DESERIALIZE_OK = 0,
|
||||||
|
@ -928,6 +898,35 @@ lsquic_enc_session_reset_cid (enc_session_t *enc_session_p,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
put_compressed_cert (struct compressed_cert *ccert)
|
||||||
|
{
|
||||||
|
if (ccert)
|
||||||
|
{
|
||||||
|
assert(ccert->refcnt > 0);
|
||||||
|
--ccert->refcnt;
|
||||||
|
if (0 == ccert->refcnt)
|
||||||
|
free(ccert);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct compressed_cert *
|
||||||
|
new_compressed_cert (const unsigned char *buf, size_t len)
|
||||||
|
{
|
||||||
|
struct compressed_cert *ccert;
|
||||||
|
|
||||||
|
ccert = malloc(sizeof(*ccert) + len);
|
||||||
|
if (ccert)
|
||||||
|
{
|
||||||
|
ccert->refcnt = 1;
|
||||||
|
ccert->len = len;
|
||||||
|
memcpy(ccert->buf, buf, len);
|
||||||
|
}
|
||||||
|
return ccert;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
lsquic_enc_session_destroy (enc_session_t *enc_session_p)
|
lsquic_enc_session_destroy (enc_session_t *enc_session_p)
|
||||||
{
|
{
|
||||||
|
@ -947,7 +946,8 @@ lsquic_enc_session_destroy (enc_session_t *enc_session_p)
|
||||||
lsquic_str_d(&hs_ctx->sno);
|
lsquic_str_d(&hs_ctx->sno);
|
||||||
lsquic_str_d(&hs_ctx->prof);
|
lsquic_str_d(&hs_ctx->prof);
|
||||||
lsquic_str_d(&hs_ctx->csct);
|
lsquic_str_d(&hs_ctx->csct);
|
||||||
lsquic_str_d(&hs_ctx->crt);
|
put_compressed_cert(hs_ctx->ccert);
|
||||||
|
hs_ctx->ccert = NULL;
|
||||||
lsquic_str_d(&hs_ctx->uaid);
|
lsquic_str_d(&hs_ctx->uaid);
|
||||||
lsquic_str_d(&hs_ctx->scfg_pubs);
|
lsquic_str_d(&hs_ctx->scfg_pubs);
|
||||||
lsquic_str_d(&enc_session->chlo);
|
lsquic_str_d(&enc_session->chlo);
|
||||||
|
@ -1075,7 +1075,8 @@ static int parse_hs_data (struct lsquic_enc_session *enc_session, uint32_t tag,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case QTAG_CRT:
|
case QTAG_CRT:
|
||||||
lsquic_str_setto(&hs_ctx->crt, val, len);
|
put_compressed_cert(hs_ctx->ccert);
|
||||||
|
hs_ctx->ccert = new_compressed_cert(val, len);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case QTAG_PUBS:
|
case QTAG_PUBS:
|
||||||
|
@ -1884,7 +1885,7 @@ get_valid_scfg (const struct lsquic_enc_session *enc_session,
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
generate_crt (struct lsquic_enc_session *enc_session, int common_case)
|
generate_crt (struct lsquic_enc_session *enc_session)
|
||||||
{
|
{
|
||||||
int i, n, len, crt_num, rv = -1;
|
int i, n, len, crt_num, rv = -1;
|
||||||
lsquic_str_t **crts;
|
lsquic_str_t **crts;
|
||||||
|
@ -1893,6 +1894,7 @@ generate_crt (struct lsquic_enc_session *enc_session, int common_case)
|
||||||
STACK_OF(X509) *pXchain;
|
STACK_OF(X509) *pXchain;
|
||||||
SSL_CTX *const ctx = enc_session->ssl_ctx;
|
SSL_CTX *const ctx = enc_session->ssl_ctx;
|
||||||
hs_ctx_t *const hs_ctx = &enc_session->hs_ctx;
|
hs_ctx_t *const hs_ctx = &enc_session->hs_ctx;
|
||||||
|
struct compressed_cert *ccert;
|
||||||
|
|
||||||
SSL_CTX_get0_chain_certs(ctx, &pXchain);
|
SSL_CTX_get0_chain_certs(ctx, &pXchain);
|
||||||
n = sk_X509_num(pXchain);
|
n = sk_X509_num(pXchain);
|
||||||
|
@ -1918,17 +1920,22 @@ generate_crt (struct lsquic_enc_session *enc_session, int common_case)
|
||||||
OPENSSL_free(out);
|
OPENSSL_free(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (0 != lsquic_compress_certs(crts, crt_num, &hs_ctx->ccs, &hs_ctx->ccrt,
|
ccert = lsquic_compress_certs(crts, crt_num, &hs_ctx->ccs, &hs_ctx->ccrt);
|
||||||
&hs_ctx->crt))
|
if (!ccert)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
if (common_case)
|
if (SSL_CTX_set_ex_data(ctx, s_ccrt_idx, ccert))
|
||||||
|
++ccert->refcnt;
|
||||||
|
else
|
||||||
{
|
{
|
||||||
if (0 != insert_compress_certs(enc_session->enpub,
|
free(ccert);
|
||||||
make_compress_cert_hash_item(&hs_ctx->sni, &hs_ctx->crt)))
|
ccert = NULL;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
++ccert->refcnt;
|
||||||
|
hs_ctx->ccert = ccert;
|
||||||
|
|
||||||
/* We got here, set rv to 0: success */
|
/* We got here, set rv to 0: success */
|
||||||
rv = 0;
|
rv = 0;
|
||||||
|
|
||||||
|
@ -1946,6 +1953,11 @@ static int
|
||||||
gen_rej1_data (struct lsquic_enc_session *enc_session, uint8_t *data,
|
gen_rej1_data (struct lsquic_enc_session *enc_session, uint8_t *data,
|
||||||
size_t max_len, const struct sockaddr *ip, time_t t)
|
size_t max_len, const struct sockaddr *ip, time_t t)
|
||||||
{
|
{
|
||||||
|
#ifndef WIN32
|
||||||
|
# define ERR(e_) do { return (e_); } while (0)
|
||||||
|
#else
|
||||||
|
# define ERR(e_) do { len = (e_); goto end; } while (0)
|
||||||
|
#endif
|
||||||
int len;
|
int len;
|
||||||
EVP_PKEY * rsa_priv_key;
|
EVP_PKEY * rsa_priv_key;
|
||||||
SSL_CTX *ctx = enc_session->ssl_ctx;
|
SSL_CTX *ctx = enc_session->ssl_ctx;
|
||||||
|
@ -1954,10 +1966,6 @@ gen_rej1_data (struct lsquic_enc_session *enc_session, uint8_t *data,
|
||||||
hs_ctx_t *const hs_ctx = &enc_session->hs_ctx;
|
hs_ctx_t *const hs_ctx = &enc_session->hs_ctx;
|
||||||
int scfg_len = enc_session->server_config->lsc_scfg->info.scfg_len;
|
int scfg_len = enc_session->server_config->lsc_scfg->info.scfg_len;
|
||||||
uint8_t *scfg_data = enc_session->server_config->lsc_scfg->scfg;
|
uint8_t *scfg_data = enc_session->server_config->lsc_scfg->scfg;
|
||||||
char prof_buf[512];
|
|
||||||
size_t prof_len = 512;
|
|
||||||
compress_cert_hash_item_t* compress_certs_item;
|
|
||||||
int common_case;
|
|
||||||
size_t msg_len;
|
size_t msg_len;
|
||||||
struct message_writer mw;
|
struct message_writer mw;
|
||||||
|
|
||||||
|
@ -1965,33 +1973,46 @@ gen_rej1_data (struct lsquic_enc_session *enc_session, uint8_t *data,
|
||||||
if (!rsa_priv_key)
|
if (!rsa_priv_key)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
lsquic_str_d(&hs_ctx->crt);
|
size_t prof_len = (size_t) EVP_PKEY_size(rsa_priv_key);
|
||||||
|
#ifndef WIN32
|
||||||
|
char prof_buf[prof_len];
|
||||||
|
#else
|
||||||
|
prof_buf = _malloca(prof_len);
|
||||||
|
if (!prof_buf)
|
||||||
|
return -1;
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
if (hs_ctx->ccert)
|
||||||
* Only cache hs_ctx->ccs is the hardcoded common certs and hs_ctx->ccrt is empty case
|
|
||||||
* This is the most common case
|
|
||||||
*/
|
|
||||||
common_case = lsquic_str_len(&hs_ctx->ccrt) == 0
|
|
||||||
&& lsquic_str_bcmp(&hs_ctx->ccs, lsquic_get_common_certs_hash()) == 0;
|
|
||||||
if (common_case)
|
|
||||||
compress_certs_item = find_compress_certs(enc_session->enpub, &hs_ctx->sni);
|
|
||||||
else
|
|
||||||
compress_certs_item = NULL;
|
|
||||||
|
|
||||||
if (compress_certs_item)
|
|
||||||
{
|
{
|
||||||
lsquic_str_d(&hs_ctx->crt);
|
put_compressed_cert(hs_ctx->ccert);
|
||||||
lsquic_str_copy(&hs_ctx->crt, compress_certs_item->crts_compress_buf);
|
hs_ctx->ccert = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hs_ctx->ccert = SSL_CTX_get_ex_data(ctx, s_ccrt_idx);
|
||||||
|
if (hs_ctx->ccert)
|
||||||
|
{
|
||||||
|
++hs_ctx->ccert->refcnt;
|
||||||
|
LSQ_DEBUG("use cached compressed cert");
|
||||||
|
}
|
||||||
|
else if (0 == generate_crt(enc_session))
|
||||||
|
LSQ_DEBUG("generated compressed cert");
|
||||||
else
|
else
|
||||||
generate_crt(enc_session, common_case);
|
{
|
||||||
|
LSQ_INFO("cannot could not generate compressed cert for");
|
||||||
|
ERR(-1);
|
||||||
|
}
|
||||||
|
|
||||||
LSQ_DEBUG("gQUIC rej1 data");
|
LSQ_DEBUG("gQUIC rej1 data");
|
||||||
LSQ_DEBUG("gQUIC NOT enabled");
|
LSQ_DEBUG("gQUIC NOT enabled");
|
||||||
lsquic_gen_prof((const uint8_t *)lsquic_str_cstr(&enc_session->chlo),
|
const int s = lsquic_gen_prof((const uint8_t *)lsquic_str_cstr(&enc_session->chlo),
|
||||||
(size_t)lsquic_str_len(&enc_session->chlo),
|
(size_t)lsquic_str_len(&enc_session->chlo),
|
||||||
scfg_data, scfg_len,
|
scfg_data, scfg_len,
|
||||||
rsa_priv_key, (uint8_t *)prof_buf, &prof_len);
|
rsa_priv_key, (uint8_t *)prof_buf, &prof_len);
|
||||||
|
if (s != 0)
|
||||||
|
{
|
||||||
|
LSQ_INFO("could not generate server proof, code %d", s);
|
||||||
|
ERR(-1);
|
||||||
|
}
|
||||||
|
|
||||||
lsquic_str_setto(&hs_ctx->prof, prof_buf, prof_len);
|
lsquic_str_setto(&hs_ctx->prof, prof_buf, prof_len);
|
||||||
|
|
||||||
|
@ -2008,10 +2029,11 @@ gen_rej1_data (struct lsquic_enc_session *enc_session, uint8_t *data,
|
||||||
MSG_LEN_ADD(msg_len, SNO_LENGTH);
|
MSG_LEN_ADD(msg_len, SNO_LENGTH);
|
||||||
MSG_LEN_ADD(msg_len, sizeof(settings->es_sttl));
|
MSG_LEN_ADD(msg_len, sizeof(settings->es_sttl));
|
||||||
MSG_LEN_ADD(msg_len, lsquic_str_len(&hs_ctx->prof));
|
MSG_LEN_ADD(msg_len, lsquic_str_len(&hs_ctx->prof));
|
||||||
MSG_LEN_ADD(msg_len, lsquic_str_len(&hs_ctx->crt));
|
if (hs_ctx->ccert)
|
||||||
|
MSG_LEN_ADD(msg_len, hs_ctx->ccert->len);
|
||||||
|
|
||||||
if (MSG_LEN_VAL(msg_len) > max_len)
|
if (MSG_LEN_VAL(msg_len) > max_len)
|
||||||
return -1;
|
ERR(-1);
|
||||||
|
|
||||||
memcpy(enc_session->priv_key, enc_session->server_config->lsc_scfg->info.priv_key, 32);
|
memcpy(enc_session->priv_key, enc_session->server_config->lsc_scfg->info.priv_key, 32);
|
||||||
|
|
||||||
|
@ -2040,13 +2062,19 @@ gen_rej1_data (struct lsquic_enc_session *enc_session, uint8_t *data,
|
||||||
MW_WRITE_BUFFER(&mw, QTAG_RREJ, &hs_ctx->rrej, sizeof(hs_ctx->rrej));
|
MW_WRITE_BUFFER(&mw, QTAG_RREJ, &hs_ctx->rrej, sizeof(hs_ctx->rrej));
|
||||||
MW_WRITE_BUFFER(&mw, QTAG_STTL, &settings->es_sttl,
|
MW_WRITE_BUFFER(&mw, QTAG_STTL, &settings->es_sttl,
|
||||||
sizeof(settings->es_sttl));
|
sizeof(settings->es_sttl));
|
||||||
MW_WRITE_LS_STR(&mw, QTAG_CRT, &hs_ctx->crt);
|
if (hs_ctx->ccert)
|
||||||
|
MW_WRITE_BUFFER(&mw, QTAG_CRT, hs_ctx->ccert->buf, hs_ctx->ccert->len);
|
||||||
MW_END(&mw);
|
MW_END(&mw);
|
||||||
|
|
||||||
assert(data + max_len >= MW_P(&mw));
|
assert(data + max_len >= MW_P(&mw));
|
||||||
len = MW_P(&mw) - data;
|
len = MW_P(&mw) - data;
|
||||||
LSQ_DEBUG("gen_rej1_data called, return len %d.", len);
|
LSQ_DEBUG("gen_rej1_data called, return len %d.", len);
|
||||||
|
#ifdef WIN32
|
||||||
|
end:
|
||||||
|
_freea(prof_buf);
|
||||||
|
#endif
|
||||||
return len;
|
return len;
|
||||||
|
#undef ERR
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2316,10 +2344,11 @@ static int handle_chlo_reply_verify_prof(struct lsquic_enc_session *enc_session,
|
||||||
lsquic_str_t *cached_certs,
|
lsquic_str_t *cached_certs,
|
||||||
int cached_certs_count)
|
int cached_certs_count)
|
||||||
{
|
{
|
||||||
const unsigned char *const in =
|
const unsigned char *dummy = (unsigned char *) "";
|
||||||
(const unsigned char *) lsquic_str_buf(&enc_session->hs_ctx.crt);
|
const unsigned char *const in = enc_session->hs_ctx.ccert
|
||||||
const unsigned char *const in_end =
|
? enc_session->hs_ctx.ccert->buf : dummy;
|
||||||
in + lsquic_str_len(&enc_session->hs_ctx.crt);
|
const unsigned char *const in_end = enc_session->hs_ctx.ccert
|
||||||
|
? in + enc_session->hs_ctx.ccert->len : 0;
|
||||||
EVP_PKEY *pub_key;
|
EVP_PKEY *pub_key;
|
||||||
int ret;
|
int ret;
|
||||||
size_t i;
|
size_t i;
|
||||||
|
@ -2749,9 +2778,9 @@ lsquic_enc_session_handle_chlo_reply (enc_session_t *enc_session_p,
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lsquic_str_len(&enc_session->hs_ctx.crt) > 0)
|
if (enc_session->hs_ctx.ccert)
|
||||||
{
|
{
|
||||||
out_certs_count = lsquic_get_certs_count(&enc_session->hs_ctx.crt);
|
out_certs_count = lsquic_get_certs_count(enc_session->hs_ctx.ccert);
|
||||||
if (out_certs_count > 0)
|
if (out_certs_count > 0)
|
||||||
{
|
{
|
||||||
out_certs = malloc(out_certs_count * sizeof(lsquic_str_t *));
|
out_certs = malloc(out_certs_count * sizeof(lsquic_str_t *));
|
||||||
|
@ -3559,7 +3588,9 @@ lsquic_enc_session_mem_used (enc_session_t *enc_session_p)
|
||||||
size += lsquic_str_len(&enc_session->hs_ctx.sno);
|
size += lsquic_str_len(&enc_session->hs_ctx.sno);
|
||||||
size += lsquic_str_len(&enc_session->hs_ctx.prof);
|
size += lsquic_str_len(&enc_session->hs_ctx.prof);
|
||||||
size += lsquic_str_len(&enc_session->hs_ctx.csct);
|
size += lsquic_str_len(&enc_session->hs_ctx.csct);
|
||||||
size += lsquic_str_len(&enc_session->hs_ctx.crt);
|
if (enc_session->hs_ctx.ccert)
|
||||||
|
size += enc_session->hs_ctx.ccert->len
|
||||||
|
+ sizeof(*enc_session->hs_ctx.ccert);
|
||||||
|
|
||||||
if (enc_session->info)
|
if (enc_session->info)
|
||||||
{
|
{
|
||||||
|
@ -3738,7 +3769,8 @@ gquic_encrypt_packet (enc_session_t *enc_session_p,
|
||||||
return ENCPA_BADCRYPT; /* To cause connection to close */
|
return ENCPA_BADCRYPT; /* To cause connection to close */
|
||||||
ipv6 = NP_IS_IPv6(packet_out->po_path);
|
ipv6 = NP_IS_IPv6(packet_out->po_path);
|
||||||
buf = enpub->enp_pmi->pmi_allocate(enpub->enp_pmi_ctx,
|
buf = enpub->enp_pmi->pmi_allocate(enpub->enp_pmi_ctx,
|
||||||
packet_out->po_path->np_peer_ctx, lconn->conn_ctx, bufsz, ipv6);
|
packet_out->po_path->np_peer_ctx, lconn->cn_conn_ctx,
|
||||||
|
bufsz, ipv6);
|
||||||
if (!buf)
|
if (!buf)
|
||||||
{
|
{
|
||||||
LSQ_DEBUG("could not allocate memory for outgoing packet of size %zd",
|
LSQ_DEBUG("could not allocate memory for outgoing packet of size %zd",
|
||||||
|
@ -3936,7 +3968,8 @@ gquic2_esf_encrypt_packet (enc_session_t *enc_session_p,
|
||||||
dst_sz = lconn->cn_pf->pf_packout_size(lconn, packet_out);
|
dst_sz = lconn->cn_pf->pf_packout_size(lconn, packet_out);
|
||||||
ipv6 = NP_IS_IPv6(packet_out->po_path);
|
ipv6 = NP_IS_IPv6(packet_out->po_path);
|
||||||
dst = enpub->enp_pmi->pmi_allocate(enpub->enp_pmi_ctx,
|
dst = enpub->enp_pmi->pmi_allocate(enpub->enp_pmi_ctx,
|
||||||
packet_out->po_path->np_peer_ctx, lconn->conn_ctx, dst_sz, ipv6);
|
packet_out->po_path->np_peer_ctx, lconn->cn_conn_ctx,
|
||||||
|
dst_sz, ipv6);
|
||||||
if (!dst)
|
if (!dst)
|
||||||
{
|
{
|
||||||
LSQ_DEBUG("could not allocate memory for outgoing packet of size %zd",
|
LSQ_DEBUG("could not allocate memory for outgoing packet of size %zd",
|
||||||
|
|
|
@ -46,6 +46,7 @@ lsquic_hcsi_reader_feed (struct hcsi_reader *reader, const void *buf,
|
||||||
uint64_t len;
|
uint64_t len;
|
||||||
int s;
|
int s;
|
||||||
|
|
||||||
|
continue_reading:
|
||||||
while (p < end)
|
while (p < end)
|
||||||
{
|
{
|
||||||
switch (reader->hr_state)
|
switch (reader->hr_state)
|
||||||
|
@ -83,6 +84,10 @@ lsquic_hcsi_reader_feed (struct hcsi_reader *reader, const void *buf,
|
||||||
case HQFT_MAX_PUSH_ID:
|
case HQFT_MAX_PUSH_ID:
|
||||||
reader->hr_state = HR_READ_VARINT;
|
reader->hr_state = HR_READ_VARINT;
|
||||||
break;
|
break;
|
||||||
|
case HQFT_PRIORITY_UPDATE_PUSH:
|
||||||
|
case HQFT_PRIORITY_UPDATE_STREAM:
|
||||||
|
reader->hr_state = HR_READ_VARINT;
|
||||||
|
break;
|
||||||
case HQFT_DATA:
|
case HQFT_DATA:
|
||||||
case HQFT_HEADERS:
|
case HQFT_HEADERS:
|
||||||
case HQFT_PUSH_PROMISE:
|
case HQFT_PUSH_PROMISE:
|
||||||
|
@ -111,6 +116,11 @@ lsquic_hcsi_reader_feed (struct hcsi_reader *reader, const void *buf,
|
||||||
reader->hr_nread += p - orig_p;
|
reader->hr_nread += p - orig_p;
|
||||||
if (0 == s)
|
if (0 == s)
|
||||||
{
|
{
|
||||||
|
switch (reader->hr_frame_type)
|
||||||
|
{
|
||||||
|
case HQFT_GOAWAY:
|
||||||
|
case HQFT_CANCEL_PUSH:
|
||||||
|
case HQFT_MAX_PUSH_ID:
|
||||||
if (reader->hr_nread != reader->hr_frame_length)
|
if (reader->hr_nread != reader->hr_frame_length)
|
||||||
{
|
{
|
||||||
reader->hr_conn->cn_if->ci_abort_error(reader->hr_conn, 1,
|
reader->hr_conn->cn_if->ci_abort_error(reader->hr_conn, 1,
|
||||||
|
@ -119,6 +129,8 @@ lsquic_hcsi_reader_feed (struct hcsi_reader *reader, const void *buf,
|
||||||
reader->hr_state = HR_ERROR;
|
reader->hr_state = HR_ERROR;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
switch (reader->hr_frame_type)
|
switch (reader->hr_frame_type)
|
||||||
{
|
{
|
||||||
case HQFT_GOAWAY:
|
case HQFT_GOAWAY:
|
||||||
|
@ -133,6 +145,35 @@ lsquic_hcsi_reader_feed (struct hcsi_reader *reader, const void *buf,
|
||||||
reader->hr_cb->on_max_push_id(reader->hr_ctx,
|
reader->hr_cb->on_max_push_id(reader->hr_ctx,
|
||||||
reader->hr_u.vint_state.val);
|
reader->hr_u.vint_state.val);
|
||||||
break;
|
break;
|
||||||
|
case HQFT_PRIORITY_UPDATE_PUSH:
|
||||||
|
case HQFT_PRIORITY_UPDATE_STREAM:
|
||||||
|
len = reader->hr_frame_length - reader->hr_nread;
|
||||||
|
if (len <= (uintptr_t) (end - p))
|
||||||
|
{
|
||||||
|
reader->hr_cb->on_priority_update(reader->hr_ctx,
|
||||||
|
reader->hr_frame_type, reader->hr_u.vint_state.val,
|
||||||
|
(char *) p, len);
|
||||||
|
p += len;
|
||||||
|
}
|
||||||
|
else if (len <= sizeof(reader->hr_u.prio_state.buf))
|
||||||
|
{
|
||||||
|
reader->hr_frame_length = len;
|
||||||
|
reader->hr_nread = 0;
|
||||||
|
reader->hr_state = HR_READ_PRIORITY_UPDATE;
|
||||||
|
goto continue_reading;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
p += len;
|
||||||
|
/* 16 bytes is more than enough for a PRIORITY_UPDATE
|
||||||
|
* frame, anything larger than that is unreasonable.
|
||||||
|
*/
|
||||||
|
if (reader->hr_frame_length
|
||||||
|
> sizeof(reader->hr_u.prio_state.buf))
|
||||||
|
LSQ_INFO("skip PRIORITY_UPDATE frame that's too "
|
||||||
|
"long (%"PRIu64" bytes)", len);
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
|
@ -179,6 +220,20 @@ lsquic_hcsi_reader_feed (struct hcsi_reader *reader, const void *buf,
|
||||||
else
|
else
|
||||||
reader->hr_state = HR_READ_SETTING_BEGIN;
|
reader->hr_state = HR_READ_SETTING_BEGIN;
|
||||||
break;
|
break;
|
||||||
|
case HR_READ_PRIORITY_UPDATE:
|
||||||
|
len = MIN((uintptr_t) (end - p),
|
||||||
|
reader->hr_frame_length - reader->hr_nread);
|
||||||
|
memcpy(reader->hr_u.prio_state.buf + reader->hr_nread, p, len);
|
||||||
|
reader->hr_nread += len;
|
||||||
|
p += len;
|
||||||
|
if (reader->hr_frame_length == reader->hr_nread)
|
||||||
|
{
|
||||||
|
reader->hr_cb->on_priority_update(reader->hr_ctx,
|
||||||
|
reader->hr_frame_type, reader->hr_u.vint_state.val,
|
||||||
|
reader->hr_u.prio_state.buf, reader->hr_frame_length);
|
||||||
|
reader->hr_state = HR_READ_FRAME_BEGIN;
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
assert(0);
|
assert(0);
|
||||||
/* fall-through */
|
/* fall-through */
|
||||||
|
|
|
@ -18,6 +18,8 @@ struct hcsi_callbacks
|
||||||
void (*on_setting)(void *ctx, uint64_t setting_id, uint64_t value);
|
void (*on_setting)(void *ctx, uint64_t setting_id, uint64_t value);
|
||||||
void (*on_goaway)(void *ctx, uint64_t stream_id);
|
void (*on_goaway)(void *ctx, uint64_t stream_id);
|
||||||
void (*on_unexpected_frame)(void *ctx, uint64_t frame_type);
|
void (*on_unexpected_frame)(void *ctx, uint64_t frame_type);
|
||||||
|
void (*on_priority_update)(void *ctx, enum hq_frame_type, uint64_t id,
|
||||||
|
const char *, size_t);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -29,6 +31,7 @@ struct hcsi_reader
|
||||||
HR_SKIPPING,
|
HR_SKIPPING,
|
||||||
HR_READ_SETTING_BEGIN,
|
HR_READ_SETTING_BEGIN,
|
||||||
HR_READ_SETTING_CONTINUE,
|
HR_READ_SETTING_CONTINUE,
|
||||||
|
HR_READ_PRIORITY_UPDATE,
|
||||||
HR_READ_VARINT,
|
HR_READ_VARINT,
|
||||||
HR_READ_VARINT_CONTINUE,
|
HR_READ_VARINT_CONTINUE,
|
||||||
HR_ERROR,
|
HR_ERROR,
|
||||||
|
@ -40,10 +43,19 @@ struct hcsi_reader
|
||||||
{
|
{
|
||||||
struct varint_read_state vint_state;
|
struct varint_read_state vint_state;
|
||||||
struct varint_read2_state vint2_state;
|
struct varint_read2_state vint2_state;
|
||||||
|
struct {
|
||||||
|
/* We just need the offset to rest of prio_state to read Priority
|
||||||
|
* Field Value.
|
||||||
|
*/
|
||||||
|
struct varint_read_state UNUSED;
|
||||||
|
char buf[
|
||||||
|
sizeof(struct varint_read2_state)
|
||||||
|
- sizeof(struct varint_read_state)];
|
||||||
|
} prio_state;
|
||||||
} hr_u;
|
} hr_u;
|
||||||
const struct hcsi_callbacks *hr_cb;
|
const struct hcsi_callbacks *hr_cb;
|
||||||
void *hr_ctx;
|
void *hr_ctx;
|
||||||
unsigned hr_nread; /* Used for PRIORITY and SETTINGS frames */
|
unsigned hr_nread; /* Used for PRIORITY_UPDATE and SETTINGS frames */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -213,6 +213,8 @@ hqft2str (enum hq_frame_type type)
|
||||||
case HQFT_MAX_PUSH_ID: return "MAX_PUSH_ID";
|
case HQFT_MAX_PUSH_ID: return "MAX_PUSH_ID";
|
||||||
case HQFT_CANCEL_PUSH: return "CANCEL_PUSH";
|
case HQFT_CANCEL_PUSH: return "CANCEL_PUSH";
|
||||||
case HQFT_GOAWAY: return "GOAWAY";
|
case HQFT_GOAWAY: return "GOAWAY";
|
||||||
|
case HQFT_PRIORITY_UPDATE_PUSH:return "PRIORITY_UPDATE (push)";
|
||||||
|
case HQFT_PRIORITY_UPDATE_STREAM:return "PRIORITY_UPDATE (stream)";
|
||||||
default: return "<unknown>";
|
default: return "<unknown>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -275,6 +277,60 @@ lsquic_hcso_write_cancel_push (struct hcso_writer *writer, uint64_t push_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
lsquic_hcso_write_priority_update (struct hcso_writer *writer,
|
||||||
|
enum hq_frame_type type, uint64_t stream_or_push_id,
|
||||||
|
const struct lsquic_ext_http_prio *ehp)
|
||||||
|
{
|
||||||
|
unsigned char *p, *len;
|
||||||
|
unsigned bits;
|
||||||
|
int was_empty;
|
||||||
|
unsigned char buf[8 /* Frame type */ + /* Frame size */ 1 + 8 /* Value */
|
||||||
|
+ 5 /* PFV: "u=.,i" or "u=." */];
|
||||||
|
|
||||||
|
p = buf;
|
||||||
|
bits = vint_val2bits(type);
|
||||||
|
vint_write(p, type, bits, 1 << bits);
|
||||||
|
p += 1 << bits;
|
||||||
|
|
||||||
|
bits = vint_val2bits(stream_or_push_id);
|
||||||
|
len = p;
|
||||||
|
++p;
|
||||||
|
|
||||||
|
vint_write(p, stream_or_push_id, bits, 1 << bits);
|
||||||
|
p += 1 << bits;
|
||||||
|
if (!(ehp->urgency == LSQUIC_DEF_HTTP_URGENCY
|
||||||
|
&& ehp->incremental == LSQUIC_DEF_HTTP_INCREMENTAL))
|
||||||
|
{
|
||||||
|
*p++ = 'u';
|
||||||
|
*p++ = '=';
|
||||||
|
*p++ = '0' + ehp->urgency;
|
||||||
|
if (ehp->incremental)
|
||||||
|
{
|
||||||
|
*p++ = ',';
|
||||||
|
*p++ = 'i';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*len = p - len - 1;
|
||||||
|
|
||||||
|
was_empty = lsquic_frab_list_empty(&writer->how_fral);
|
||||||
|
|
||||||
|
if (0 != lsquic_frab_list_write(&writer->how_fral, buf, p - buf))
|
||||||
|
{
|
||||||
|
LSQ_INFO("cannot write %s frame to frab list", hqft2str(type));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (was_empty)
|
||||||
|
lsquic_stream_wantwrite(writer->how_stream, 1);
|
||||||
|
|
||||||
|
LSQ_DEBUG("generated %u-byte %s frame", (unsigned) (p - buf),
|
||||||
|
hqft2str(type));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||||
static size_t
|
static size_t
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#define LSQUIC_HCSO_WRITER_H 1
|
#define LSQUIC_HCSO_WRITER_H 1
|
||||||
|
|
||||||
struct lsquic_engine_settings;
|
struct lsquic_engine_settings;
|
||||||
|
struct lsquic_ext_http_prio;
|
||||||
struct lsquic_stream;
|
struct lsquic_stream;
|
||||||
|
|
||||||
struct hcso_writer
|
struct hcso_writer
|
||||||
|
@ -34,6 +35,11 @@ lsquic_hcso_write_max_push_id (struct hcso_writer *, uint64_t max_push_id);
|
||||||
int
|
int
|
||||||
lsquic_hcso_write_cancel_push (struct hcso_writer *, uint64_t push_id);
|
lsquic_hcso_write_cancel_push (struct hcso_writer *, uint64_t push_id);
|
||||||
|
|
||||||
|
int
|
||||||
|
lsquic_hcso_write_priority_update (struct hcso_writer *,
|
||||||
|
enum hq_frame_type, uint64_t stream_or_push_id,
|
||||||
|
const struct lsquic_ext_http_prio *);
|
||||||
|
|
||||||
extern const struct lsquic_stream_if *const lsquic_hcso_writer_if;
|
extern const struct lsquic_stream_if *const lsquic_hcso_writer_if;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
394
src/liblsquic/lsquic_hpi.c
Normal file
394
src/liblsquic/lsquic_hpi.c
Normal file
|
@ -0,0 +1,394 @@
|
||||||
|
/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc. See LICENSE. */
|
||||||
|
/*
|
||||||
|
* lsquic_hpi.c - implementation of (Extensible) HTTP Priority Iterator.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/queue.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#ifdef WIN32
|
||||||
|
#include <vc_compat.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "lsquic.h"
|
||||||
|
#include "lsquic_types.h"
|
||||||
|
#include "lsquic_int_types.h"
|
||||||
|
#include "lsquic_sfcw.h"
|
||||||
|
#include "lsquic_varint.h"
|
||||||
|
#include "lsquic_hq.h"
|
||||||
|
#include "lsquic_hash.h"
|
||||||
|
#include "lsquic_stream.h"
|
||||||
|
#include "lsquic_conn_flow.h"
|
||||||
|
#include "lsquic_rtt.h"
|
||||||
|
#include "lsquic_conn_public.h"
|
||||||
|
#include "lsquic_min_heap.h"
|
||||||
|
#include "lsquic_mm.h"
|
||||||
|
#include "lsquic_hpi.h"
|
||||||
|
|
||||||
|
#define LSQUIC_LOGGER_MODULE LSQLM_HPI
|
||||||
|
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(iter->hpi_conn_pub->lconn)
|
||||||
|
#include "lsquic_logger.h"
|
||||||
|
|
||||||
|
#define HPI_DEBUG(fmt, ...) LSQ_DEBUG("%s: " fmt, iter->hpi_name, __VA_ARGS__)
|
||||||
|
|
||||||
|
#define NEXT_STREAM(stream, off) \
|
||||||
|
(* (struct lsquic_stream **) ((unsigned char *) (stream) + (off)))
|
||||||
|
|
||||||
|
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||||
|
|
||||||
|
static void
|
||||||
|
add_stream_to_hpi (struct http_prio_iter *iter,
|
||||||
|
struct lsquic_stream *new_stream)
|
||||||
|
{
|
||||||
|
unsigned prio, incr;
|
||||||
|
|
||||||
|
if (lsquic_stream_is_critical(new_stream))
|
||||||
|
{
|
||||||
|
prio = 0;
|
||||||
|
incr = 1; /* Place in incremental bucket: these do not need to be
|
||||||
|
* ordered by stream ID.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
prio = 1 + MIN(new_stream->sm_priority, LSQUIC_MAX_HTTP_URGENCY);
|
||||||
|
incr = !!(new_stream->sm_bflags & SMBF_INCREMENTAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(iter->hpi_set[incr] & (1u << prio)))
|
||||||
|
{
|
||||||
|
iter->hpi_set[incr] |= 1u << prio;
|
||||||
|
if (0 == incr)
|
||||||
|
iter->hpi_counts[prio] = 0;
|
||||||
|
TAILQ_INIT(&iter->hpi_streams[incr][prio]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 == incr)
|
||||||
|
++iter->hpi_counts[prio];
|
||||||
|
TAILQ_INSERT_TAIL(&iter->hpi_streams[incr][prio],
|
||||||
|
new_stream, next_prio_stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
lsquic_hpi_init (void *iter_p, struct lsquic_stream *first,
|
||||||
|
struct lsquic_stream *last, uintptr_t next_ptr_offset,
|
||||||
|
struct lsquic_conn_public *conn_pub, const char *name,
|
||||||
|
int (*filter)(void *filter_ctx, struct lsquic_stream *),
|
||||||
|
void *filter_ctx)
|
||||||
|
{
|
||||||
|
struct http_prio_iter *const iter = iter_p;
|
||||||
|
struct lsquic_stream *stream;
|
||||||
|
unsigned count;
|
||||||
|
|
||||||
|
iter->hpi_conn_pub = conn_pub;
|
||||||
|
iter->hpi_name = name ? name : "UNSET";
|
||||||
|
iter->hpi_flags = 0;
|
||||||
|
iter->hpi_heaped = 0;
|
||||||
|
iter->hpi_set[0] = 0;
|
||||||
|
iter->hpi_set[1] = 0;
|
||||||
|
memset(&iter->hpi_min_heap, 0, sizeof(iter->hpi_min_heap));
|
||||||
|
|
||||||
|
stream = first;
|
||||||
|
count = 0;
|
||||||
|
|
||||||
|
if (filter)
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
if (filter(filter_ctx, stream))
|
||||||
|
{
|
||||||
|
add_stream_to_hpi(iter, stream);
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
if (stream == last)
|
||||||
|
break;
|
||||||
|
stream = NEXT_STREAM(stream, next_ptr_offset);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
add_stream_to_hpi(iter, stream);
|
||||||
|
++count;
|
||||||
|
if (stream == last)
|
||||||
|
break;
|
||||||
|
stream = NEXT_STREAM(stream, next_ptr_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count > 2)
|
||||||
|
HPI_DEBUG("initialized; # elems: %u; sets: [ %08X, %08X ]",
|
||||||
|
count, iter->hpi_set[0], iter->hpi_set[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Number of trailing zeroes */
|
||||||
|
static const unsigned char ntz[] = {
|
||||||
|
9, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0,
|
||||||
|
1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0,
|
||||||
|
2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0,
|
||||||
|
1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0,
|
||||||
|
3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0,
|
||||||
|
1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0,
|
||||||
|
2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0,
|
||||||
|
1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
||||||
|
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0,
|
||||||
|
1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0,
|
||||||
|
2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0,
|
||||||
|
1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0,
|
||||||
|
3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0,
|
||||||
|
1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0,
|
||||||
|
2, 0, 1, 0, 8, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0,
|
||||||
|
1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
||||||
|
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0,
|
||||||
|
1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0,
|
||||||
|
2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0,
|
||||||
|
1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0,
|
||||||
|
3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0,
|
||||||
|
1, 0, 2, 0, 1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0,
|
||||||
|
2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0,
|
||||||
|
1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
||||||
|
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0,
|
||||||
|
1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0,
|
||||||
|
2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0,
|
||||||
|
1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0,
|
||||||
|
3, 0, 1, 0, 2, 0, 1, 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* Sets prio_ and incr_ */
|
||||||
|
#define calc_next_prio_and_incr(iter_, prio_, incr_) do { \
|
||||||
|
prio_ = ntz[iter_->hpi_set[0]]; \
|
||||||
|
if (prio_ <= ntz[iter_->hpi_set[1]]) \
|
||||||
|
incr_ = 0; \
|
||||||
|
else \
|
||||||
|
{ \
|
||||||
|
prio_ = ntz[iter_->hpi_set[1]]; \
|
||||||
|
incr_ = 1; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
|
||||||
|
struct lsquic_stream *
|
||||||
|
lsquic_hpi_first (void *iter_p)
|
||||||
|
{
|
||||||
|
struct http_prio_iter *const iter = iter_p;
|
||||||
|
|
||||||
|
assert(!(iter->hpi_set[0] & ~((1 << N_HPI_PRIORITIES) - 1)));
|
||||||
|
assert(!(iter->hpi_set[1] & ~((1 << N_HPI_PRIORITIES) - 1)));
|
||||||
|
|
||||||
|
return lsquic_hpi_next(iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct lsquic_stream *
|
||||||
|
next_incr (struct http_prio_iter *iter, unsigned prio)
|
||||||
|
{
|
||||||
|
struct lsquic_stream *stream;
|
||||||
|
|
||||||
|
stream = TAILQ_FIRST(&iter->hpi_streams[1][prio]);
|
||||||
|
TAILQ_REMOVE(&iter->hpi_streams[1][prio], stream, next_prio_stream);
|
||||||
|
if (TAILQ_EMPTY(&iter->hpi_streams[1][prio]))
|
||||||
|
iter->hpi_set[1] &= ~(1u << prio);
|
||||||
|
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
free_heap_elems (struct http_prio_iter *iter)
|
||||||
|
{
|
||||||
|
if (0 == (iter->hpi_flags & (HPI_MH_4K|HPI_MH_MALLOC)))
|
||||||
|
/* Expected condition: nothing to do */ ;
|
||||||
|
else if (iter->hpi_flags & HPI_MH_4K)
|
||||||
|
{
|
||||||
|
lsquic_mm_put_4k(iter->hpi_conn_pub->mm, iter->hpi_min_heap.mh_elems);
|
||||||
|
iter->hpi_flags &= ~HPI_MH_4K;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assert(iter->hpi_flags & HPI_MH_MALLOC);
|
||||||
|
iter->hpi_flags &= ~HPI_MH_MALLOC;
|
||||||
|
free(iter->hpi_min_heap.mh_elems);
|
||||||
|
}
|
||||||
|
iter->hpi_min_heap.mh_elems = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
static int lsquic_hpi_heap_test = (
|
||||||
|
LSQUIC_HPI_HEAP_TEST_STACK_OK | LSQUIC_HPI_HEAP_TEST_4K_OK);
|
||||||
|
void
|
||||||
|
lsquic_hpi_set_heap_test (int val)
|
||||||
|
{
|
||||||
|
lsquic_hpi_heap_test = val;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
heap_nonincr_bucket (struct http_prio_iter *iter, unsigned prio)
|
||||||
|
{
|
||||||
|
struct lsquic_stream *stream;
|
||||||
|
size_t need;
|
||||||
|
|
||||||
|
if (iter->hpi_counts[prio] <= sizeof(iter->hpi_min_heap_els)
|
||||||
|
/ sizeof(iter->hpi_min_heap_els[0])
|
||||||
|
#ifndef NDEBUG
|
||||||
|
&& (lsquic_hpi_heap_test & LSQUIC_HPI_HEAP_TEST_STACK_OK)
|
||||||
|
#endif
|
||||||
|
)
|
||||||
|
iter->hpi_min_heap.mh_elems = iter->hpi_min_heap_els;
|
||||||
|
else if (need = iter->hpi_counts[prio] * sizeof(struct min_heap_elem),
|
||||||
|
need <= 0x1000
|
||||||
|
#ifndef NDEBUG
|
||||||
|
&& (lsquic_hpi_heap_test & LSQUIC_HPI_HEAP_TEST_4K_OK)
|
||||||
|
#endif
|
||||||
|
)
|
||||||
|
{
|
||||||
|
iter->hpi_min_heap.mh_elems = lsquic_mm_get_4k(iter->hpi_conn_pub->mm);
|
||||||
|
if (!iter->hpi_min_heap.mh_elems)
|
||||||
|
return -1;
|
||||||
|
iter->hpi_flags |= HPI_MH_4K;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
iter->hpi_min_heap.mh_elems = malloc(need);
|
||||||
|
if (!iter->hpi_min_heap.mh_elems)
|
||||||
|
return -1;
|
||||||
|
iter->hpi_flags |= HPI_MH_MALLOC;
|
||||||
|
}
|
||||||
|
|
||||||
|
iter->hpi_min_heap.mh_nalloc = iter->hpi_counts[prio];
|
||||||
|
TAILQ_FOREACH(stream, &iter->hpi_streams[0][prio], next_prio_stream)
|
||||||
|
lsquic_mh_insert(&iter->hpi_min_heap, stream, stream->id);
|
||||||
|
iter->hpi_heaped |= 1u << prio;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct lsquic_stream *
|
||||||
|
next_nonincr (struct http_prio_iter *iter, unsigned prio)
|
||||||
|
{
|
||||||
|
struct lsquic_stream *stream;
|
||||||
|
|
||||||
|
if (iter->hpi_heaped & (1u << prio))
|
||||||
|
{
|
||||||
|
pop_stream:
|
||||||
|
stream = lsquic_mh_pop(&iter->hpi_min_heap);
|
||||||
|
if (lsquic_mh_count(&iter->hpi_min_heap) == 0)
|
||||||
|
{
|
||||||
|
free_heap_elems(iter);
|
||||||
|
iter->hpi_set[0] &= ~(1u << prio);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (iter->hpi_counts[prio] > 1)
|
||||||
|
{
|
||||||
|
if (0 == heap_nonincr_bucket(iter, prio))
|
||||||
|
goto pop_stream;
|
||||||
|
/* Handle memory allocation failure by abandoning attempts to order
|
||||||
|
* the streams:
|
||||||
|
*/
|
||||||
|
iter->hpi_counts[prio] = 1;
|
||||||
|
goto first_stream;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
first_stream:
|
||||||
|
stream = TAILQ_FIRST(&iter->hpi_streams[0][prio]);
|
||||||
|
TAILQ_REMOVE(&iter->hpi_streams[0][prio], stream, next_prio_stream);
|
||||||
|
if (TAILQ_EMPTY(&iter->hpi_streams[0][prio]))
|
||||||
|
iter->hpi_set[0] &= ~(1u << prio);
|
||||||
|
}
|
||||||
|
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct lsquic_stream *
|
||||||
|
lsquic_hpi_next (void *iter_p)
|
||||||
|
{
|
||||||
|
struct http_prio_iter *const iter = iter_p;
|
||||||
|
struct lsquic_stream *stream;
|
||||||
|
unsigned prio, incr;
|
||||||
|
|
||||||
|
calc_next_prio_and_incr(iter, prio, incr);
|
||||||
|
|
||||||
|
if (prio >= N_HPI_PRIORITIES)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (incr)
|
||||||
|
stream = next_incr(iter, prio);
|
||||||
|
else
|
||||||
|
stream = next_nonincr(iter, prio);
|
||||||
|
|
||||||
|
if (LSQ_LOG_ENABLED(LSQ_LOG_DEBUG))
|
||||||
|
HPI_DEBUG("%s: return stream %"PRIu64", incr: %u, priority %u",
|
||||||
|
__func__, stream->id, incr, prio);
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if __GNUC__
|
||||||
|
# define popcount __builtin_popcount
|
||||||
|
#else
|
||||||
|
static int
|
||||||
|
popcount (unsigned v)
|
||||||
|
{
|
||||||
|
int count;
|
||||||
|
unsigned i;
|
||||||
|
for (i = 0, count = 0; i < sizeof(v) * 8; ++i)
|
||||||
|
if (v & (1 << i))
|
||||||
|
++count;
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
hpi_drop_high_or_non_high (void *iter_p, int drop_high)
|
||||||
|
{
|
||||||
|
struct http_prio_iter *const iter = iter_p;
|
||||||
|
unsigned prio, incr;
|
||||||
|
|
||||||
|
/* Nothing to drop if there is only one bucket */
|
||||||
|
if (popcount(iter->hpi_set[0]) + popcount(iter->hpi_set[1]) < 2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
calc_next_prio_and_incr(iter, prio, incr);
|
||||||
|
|
||||||
|
if (drop_high)
|
||||||
|
iter->hpi_set[incr] &= ~(1u << prio);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
iter->hpi_set[incr] = 1u << prio;
|
||||||
|
iter->hpi_set[!incr] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
lsquic_hpi_drop_high (void *iter_p)
|
||||||
|
{
|
||||||
|
hpi_drop_high_or_non_high(iter_p, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
lsquic_hpi_drop_non_high (void *iter_p)
|
||||||
|
{
|
||||||
|
hpi_drop_high_or_non_high(iter_p, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
lsquic_hpi_cleanup (void *iter_p)
|
||||||
|
{
|
||||||
|
struct http_prio_iter *const iter = iter_p;
|
||||||
|
|
||||||
|
if (iter->hpi_min_heap.mh_elems)
|
||||||
|
free_heap_elems(iter);
|
||||||
|
}
|
75
src/liblsquic/lsquic_hpi.h
Normal file
75
src/liblsquic/lsquic_hpi.h
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc. See LICENSE. */
|
||||||
|
/*
|
||||||
|
* lsquic_hpi.h - HPI: (Extensible) HTTP Priority Iterator
|
||||||
|
*
|
||||||
|
* https://tools.ietf.org/html/draft-ietf-httpbis-priority-01
|
||||||
|
*
|
||||||
|
* Changing a stream's priority when the stream is in the iterator
|
||||||
|
* does not change the stream's position in the iterator.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LSQUIC_HPI
|
||||||
|
#define LSQUIC_HPI 1
|
||||||
|
|
||||||
|
struct lsquic_conn_public;
|
||||||
|
|
||||||
|
/* We add 1 to the urgency when we place them on hpi_streams. Critical
|
||||||
|
* streams get the highest-priority slot zero.
|
||||||
|
*/
|
||||||
|
#define N_HPI_PRIORITIES (2 + LSQUIC_MAX_HTTP_URGENCY)
|
||||||
|
|
||||||
|
|
||||||
|
struct http_prio_iter
|
||||||
|
{
|
||||||
|
const char *hpi_name; /* Used for logging */
|
||||||
|
struct lsquic_conn_public *hpi_conn_pub;
|
||||||
|
enum {
|
||||||
|
HPI_MH_4K = 1 << 0,
|
||||||
|
HPI_MH_MALLOC = 1 << 1,
|
||||||
|
} hpi_flags;
|
||||||
|
unsigned hpi_set[2]; /* Bitmask */
|
||||||
|
unsigned hpi_counts[N_HPI_PRIORITIES]; /* For non-incr only */
|
||||||
|
unsigned hpi_heaped; /* Bitmask */
|
||||||
|
struct lsquic_streams_tailq hpi_streams[2][N_HPI_PRIORITIES];
|
||||||
|
struct min_heap hpi_min_heap;
|
||||||
|
/* We do this because http_prio_iter is used in a union with
|
||||||
|
* stream_prio_iter, which is over 4KB on the stack. Since we
|
||||||
|
* are already allocating this memory on the stack, we might as well
|
||||||
|
* use it.
|
||||||
|
*/
|
||||||
|
struct min_heap_elem hpi_min_heap_els[236];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
lsquic_hpi_init (void *, struct lsquic_stream *first,
|
||||||
|
struct lsquic_stream *last, uintptr_t next_ptr_offset,
|
||||||
|
struct lsquic_conn_public *, const char *name,
|
||||||
|
int (*filter)(void *filter_ctx, struct lsquic_stream *),
|
||||||
|
void *filter_ctx);
|
||||||
|
|
||||||
|
struct lsquic_stream *
|
||||||
|
lsquic_hpi_first (void *);
|
||||||
|
|
||||||
|
struct lsquic_stream *
|
||||||
|
lsquic_hpi_next (void *);
|
||||||
|
|
||||||
|
void
|
||||||
|
lsquic_hpi_drop_non_high (void *);
|
||||||
|
|
||||||
|
void
|
||||||
|
lsquic_hpi_drop_high (void *);
|
||||||
|
|
||||||
|
void
|
||||||
|
lsquic_hpi_cleanup (void *);
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
#define LSQUIC_HPI_HEAP_TEST_STACK_OK (1 << 0)
|
||||||
|
#define LSQUIC_HPI_HEAP_TEST_4K_OK (1 << 1)
|
||||||
|
#if LSQUIC_TEST
|
||||||
|
void
|
||||||
|
lsquic_hpi_set_heap_test (int val);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -6,6 +6,8 @@
|
||||||
#ifndef LSQUIC_HQ_H
|
#ifndef LSQUIC_HQ_H
|
||||||
#define LSQUIC_HQ_H 1
|
#define LSQUIC_HQ_H 1
|
||||||
|
|
||||||
|
struct lsquic_ext_http_prio;
|
||||||
|
|
||||||
/* [draft-ietf-quic-http-27] Section 11.2.1 */
|
/* [draft-ietf-quic-http-27] Section 11.2.1 */
|
||||||
enum hq_frame_type
|
enum hq_frame_type
|
||||||
{
|
{
|
||||||
|
@ -16,6 +18,14 @@ enum hq_frame_type
|
||||||
HQFT_PUSH_PROMISE = 5,
|
HQFT_PUSH_PROMISE = 5,
|
||||||
HQFT_GOAWAY = 7,
|
HQFT_GOAWAY = 7,
|
||||||
HQFT_MAX_PUSH_ID = 0xD,
|
HQFT_MAX_PUSH_ID = 0xD,
|
||||||
|
/* These made me expand shf_frame_type to 4 bytes from 1. If at some
|
||||||
|
* point we have to support a frame that is wider than 4 byte, it will
|
||||||
|
* be time to bite the bullet and use our own enum for these types
|
||||||
|
* (just like we do for transport parameters). A simpler alternative
|
||||||
|
* would be to drop the enum and use #define's, but it would stink...
|
||||||
|
*/
|
||||||
|
HQFT_PRIORITY_UPDATE_STREAM= 0xF0700,
|
||||||
|
HQFT_PRIORITY_UPDATE_PUSH = 0xF0701,
|
||||||
/* This frame is made up and its type is never written to stream.
|
/* This frame is made up and its type is never written to stream.
|
||||||
* Nevertheless, just to be on the safe side, give it a value as
|
* Nevertheless, just to be on the safe side, give it a value as
|
||||||
* described in [draft-ietf-quic-http-20] Section 4.2.10.
|
* described in [draft-ietf-quic-http-20] Section 4.2.10.
|
||||||
|
@ -76,4 +86,17 @@ enum http_error_code
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
enum ppc_flags
|
||||||
|
{
|
||||||
|
PPC_URG_NAME = 1 << 0,
|
||||||
|
PPC_INC_NAME = 1 << 1,
|
||||||
|
PPC_URG_SET = 1 << 2, /* 'urgency' is set */
|
||||||
|
PPC_INC_SET = 1 << 3, /* 'incremental' is set */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
lsquic_http_parse_pfv (const char *, size_t, enum ppc_flags * /* optional */,
|
||||||
|
struct lsquic_ext_http_prio *, char *str, size_t);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
77
src/liblsquic/lsquic_http.c
Normal file
77
src/liblsquic/lsquic_http.c
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc. See LICENSE. */
|
||||||
|
/* Various HTTP-related functions. */
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#ifdef WIN32
|
||||||
|
#include <vc_compat.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "ls-sfparser.h"
|
||||||
|
#include "lsquic.h"
|
||||||
|
#include "lsquic_hq.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct parse_pfv_ctx
|
||||||
|
{
|
||||||
|
enum ppc_flags ppc_flags;
|
||||||
|
struct lsquic_ext_http_prio *ppc_ehp;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
parse_pfv (void *user_data, enum ls_sf_dt type, char *str, size_t len, int off)
|
||||||
|
{
|
||||||
|
struct parse_pfv_ctx *const pfv_ctx = user_data;
|
||||||
|
unsigned urgency;
|
||||||
|
|
||||||
|
if (type == LS_SF_DT_NAME)
|
||||||
|
{
|
||||||
|
if (1 == len)
|
||||||
|
switch (str[0])
|
||||||
|
{
|
||||||
|
case 'u': pfv_ctx->ppc_flags |= PPC_URG_NAME; return 0;
|
||||||
|
case 'i': pfv_ctx->ppc_flags |= PPC_INC_NAME; return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (pfv_ctx->ppc_flags & PPC_URG_NAME)
|
||||||
|
{
|
||||||
|
if (type == LS_SF_DT_INTEGER)
|
||||||
|
{
|
||||||
|
urgency = atoi(str);
|
||||||
|
if (urgency <= LSQUIC_MAX_HTTP_URGENCY)
|
||||||
|
{
|
||||||
|
pfv_ctx->ppc_ehp->urgency = urgency;
|
||||||
|
pfv_ctx->ppc_flags |= PPC_URG_SET;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (pfv_ctx->ppc_flags & PPC_INC_NAME)
|
||||||
|
{
|
||||||
|
if (type == LS_SF_DT_BOOLEAN)
|
||||||
|
{
|
||||||
|
pfv_ctx->ppc_ehp->incremental = str[0] - '0';
|
||||||
|
pfv_ctx->ppc_flags |= PPC_INC_SET;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pfv_ctx->ppc_flags &= ~(PPC_INC_NAME|PPC_URG_NAME);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
lsquic_http_parse_pfv (const char *pfv, size_t pfv_sz,
|
||||||
|
enum ppc_flags *flags, struct lsquic_ext_http_prio *ehp,
|
||||||
|
char *scratch_buf, size_t scratch_sz)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct parse_pfv_ctx pfv_ctx = { .ppc_flags = flags ? *flags : 0,
|
||||||
|
.ppc_ehp = ehp, };
|
||||||
|
|
||||||
|
ret = ls_sf_parse(LS_SF_TLT_DICTIONARY, pfv, pfv_sz, parse_pfv, &pfv_ctx,
|
||||||
|
scratch_buf, scratch_sz);
|
||||||
|
if (flags)
|
||||||
|
*flags = pfv_ctx.ppc_flags;
|
||||||
|
return ret;
|
||||||
|
}
|
|
@ -79,6 +79,7 @@ enum lsq_log_level lsq_log_levels[N_LSQUIC_LOGGER_MODULES] = {
|
||||||
[LSQLM_TOKGEN] = LSQ_LOG_WARN,
|
[LSQLM_TOKGEN] = LSQ_LOG_WARN,
|
||||||
[LSQLM_ENG_HIST] = LSQ_LOG_WARN,
|
[LSQLM_ENG_HIST] = LSQ_LOG_WARN,
|
||||||
[LSQLM_SPI] = LSQ_LOG_WARN,
|
[LSQLM_SPI] = LSQ_LOG_WARN,
|
||||||
|
[LSQLM_HPI] = LSQ_LOG_WARN,
|
||||||
[LSQLM_DI] = LSQ_LOG_WARN,
|
[LSQLM_DI] = LSQ_LOG_WARN,
|
||||||
[LSQLM_PRQ] = LSQ_LOG_WARN,
|
[LSQLM_PRQ] = LSQ_LOG_WARN,
|
||||||
[LSQLM_PACER] = LSQ_LOG_WARN,
|
[LSQLM_PACER] = LSQ_LOG_WARN,
|
||||||
|
@ -122,6 +123,7 @@ const char *const lsqlm_to_str[N_LSQUIC_LOGGER_MODULES] = {
|
||||||
[LSQLM_TOKGEN] = "tokgen",
|
[LSQLM_TOKGEN] = "tokgen",
|
||||||
[LSQLM_ENG_HIST] = "eng-hist",
|
[LSQLM_ENG_HIST] = "eng-hist",
|
||||||
[LSQLM_SPI] = "spi",
|
[LSQLM_SPI] = "spi",
|
||||||
|
[LSQLM_HPI] = "hpi",
|
||||||
[LSQLM_DI] = "di",
|
[LSQLM_DI] = "di",
|
||||||
[LSQLM_PRQ] = "prq",
|
[LSQLM_PRQ] = "prq",
|
||||||
[LSQLM_PACER] = "pacer",
|
[LSQLM_PACER] = "pacer",
|
||||||
|
|
|
@ -70,6 +70,7 @@ enum lsquic_logger_module {
|
||||||
LSQLM_TOKGEN,
|
LSQLM_TOKGEN,
|
||||||
LSQLM_ENG_HIST,
|
LSQLM_ENG_HIST,
|
||||||
LSQLM_SPI,
|
LSQLM_SPI,
|
||||||
|
LSQLM_HPI,
|
||||||
LSQLM_DI,
|
LSQLM_DI,
|
||||||
LSQLM_PRQ,
|
LSQLM_PRQ,
|
||||||
LSQLM_PACER,
|
LSQLM_PACER,
|
||||||
|
|
|
@ -44,14 +44,14 @@ heapify_min_heap (struct min_heap *heap, unsigned i)
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
lsquic_mh_insert (struct min_heap *heap, struct lsquic_conn *conn, uint64_t val)
|
lsquic_mh_insert (struct min_heap *heap, void *item, uint64_t val)
|
||||||
{
|
{
|
||||||
struct min_heap_elem el;
|
struct min_heap_elem el;
|
||||||
unsigned i;
|
unsigned i;
|
||||||
|
|
||||||
assert(heap->mh_nelem < heap->mh_nalloc);
|
assert(heap->mh_nelem < heap->mh_nalloc);
|
||||||
|
|
||||||
heap->mh_elems[ heap->mh_nelem ].mhe_conn = conn;
|
heap->mh_elems[ heap->mh_nelem ].mhe_item = item;
|
||||||
heap->mh_elems[ heap->mh_nelem ].mhe_val = val;
|
heap->mh_elems[ heap->mh_nelem ].mhe_val = val;
|
||||||
++heap->mh_nelem;
|
++heap->mh_nelem;
|
||||||
|
|
||||||
|
@ -67,15 +67,15 @@ lsquic_mh_insert (struct min_heap *heap, struct lsquic_conn *conn, uint64_t val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct lsquic_conn *
|
void *
|
||||||
lsquic_mh_pop (struct min_heap *heap)
|
lsquic_mh_pop (struct min_heap *heap)
|
||||||
{
|
{
|
||||||
struct lsquic_conn *conn;
|
void *item;
|
||||||
|
|
||||||
if (heap->mh_nelem == 0)
|
if (heap->mh_nelem == 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
conn = heap->mh_elems[0].mhe_conn;
|
item = heap->mh_elems[0].mhe_item;
|
||||||
--heap->mh_nelem;
|
--heap->mh_nelem;
|
||||||
if (heap->mh_nelem > 0)
|
if (heap->mh_nelem > 0)
|
||||||
{
|
{
|
||||||
|
@ -83,5 +83,5 @@ lsquic_mh_pop (struct min_heap *heap)
|
||||||
heapify_min_heap(heap, 0);
|
heapify_min_heap(heap, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return conn;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc. See LICENSE. */
|
/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc. See LICENSE. */
|
||||||
/*
|
/*
|
||||||
* lsquic_min_heap.h -- Min-heap for connections
|
* lsquic_min_heap.h -- Min-heap for pointers
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LSQUIC_MIN_HEAP_H
|
#ifndef LSQUIC_MIN_HEAP_H
|
||||||
#define LSQUIC_MIN_HEAP_H 1
|
#define LSQUIC_MIN_HEAP_H 1
|
||||||
|
|
||||||
struct lsquic_conn;
|
|
||||||
|
|
||||||
struct min_heap_elem
|
struct min_heap_elem
|
||||||
{
|
{
|
||||||
struct lsquic_conn *mhe_conn;
|
void *mhe_item;
|
||||||
uint64_t mhe_val;
|
uint64_t mhe_val;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -24,12 +23,12 @@ struct min_heap
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
lsquic_mh_insert (struct min_heap *, struct lsquic_conn *conn, uint64_t val);
|
lsquic_mh_insert (struct min_heap *, void *item, uint64_t val);
|
||||||
|
|
||||||
struct lsquic_conn *
|
void *
|
||||||
lsquic_mh_pop (struct min_heap *);
|
lsquic_mh_pop (struct min_heap *);
|
||||||
|
|
||||||
#define lsquic_mh_peek(heap) ((heap)->mh_elems[0].mhe_conn)
|
#define lsquic_mh_peek(heap) ((heap)->mh_elems[0].mhe_item)
|
||||||
|
|
||||||
#define lsquic_mh_count(heap) (+(heap)->mh_nelem)
|
#define lsquic_mh_count(heap) (+(heap)->mh_nelem)
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include "lsquic_rtt.h"
|
#include "lsquic_rtt.h"
|
||||||
#include "lsquic_util.h"
|
#include "lsquic_util.h"
|
||||||
#include "lsquic_enc_sess.h"
|
#include "lsquic_enc_sess.h"
|
||||||
|
#include "lsquic_trechist.h"
|
||||||
#include "lsquic_mini_conn_ietf.h"
|
#include "lsquic_mini_conn_ietf.h"
|
||||||
#include "lsquic_ev_log.h"
|
#include "lsquic_ev_log.h"
|
||||||
#include "lsquic_trans_params.h"
|
#include "lsquic_trans_params.h"
|
||||||
|
@ -34,6 +35,7 @@
|
||||||
#include "lsquic_packet_ietf.h"
|
#include "lsquic_packet_ietf.h"
|
||||||
#include "lsquic_attq.h"
|
#include "lsquic_attq.h"
|
||||||
#include "lsquic_alarmset.h"
|
#include "lsquic_alarmset.h"
|
||||||
|
#include "lsquic_crand.h"
|
||||||
|
|
||||||
#define LSQUIC_LOGGER_MODULE LSQLM_MINI_CONN
|
#define LSQUIC_LOGGER_MODULE LSQLM_MINI_CONN
|
||||||
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(&conn->imc_conn)
|
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(&conn->imc_conn)
|
||||||
|
@ -469,6 +471,7 @@ lsquic_mini_conn_ietf_new (struct lsquic_engine_public *enpub,
|
||||||
enc_session_t *enc_sess;
|
enc_session_t *enc_sess;
|
||||||
enum enc_level i;
|
enum enc_level i;
|
||||||
const struct enc_session_funcs_iquic *esfi;
|
const struct enc_session_funcs_iquic *esfi;
|
||||||
|
unsigned char rand_nybble;
|
||||||
|
|
||||||
if (!is_first_packet_ok(packet_in, udp_payload_size))
|
if (!is_first_packet_ok(packet_in, udp_payload_size))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -513,6 +516,23 @@ lsquic_mini_conn_ietf_new (struct lsquic_engine_public *enpub,
|
||||||
conn->imc_stream_ps[i] = &conn->imc_streams[i];
|
conn->imc_stream_ps[i] = &conn->imc_streams[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rand_nybble = lsquic_crand_get_nybble(enpub->enp_crand);
|
||||||
|
if (rand_nybble == 0)
|
||||||
|
{
|
||||||
|
/* Use trechist for about one out of every sixteen connections so
|
||||||
|
* that the code does not grow stale.
|
||||||
|
*/
|
||||||
|
LSQ_DEBUG("using trechist");
|
||||||
|
conn->imc_flags |= IMC_TRECHIST;
|
||||||
|
conn->imc_recvd_packnos.trechist.hist_elems
|
||||||
|
= malloc(TRECHIST_SIZE * IMICO_N_PNS);
|
||||||
|
if (!conn->imc_recvd_packnos.trechist.hist_elems)
|
||||||
|
{
|
||||||
|
LSQ_WARN("cannot allocate trechist elems");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
esfi = select_esf_iquic_by_ver(version);
|
esfi = select_esf_iquic_by_ver(version);
|
||||||
enc_sess = esfi->esfi_create_server(enpub, &conn->imc_conn,
|
enc_sess = esfi->esfi_create_server(enpub, &conn->imc_conn,
|
||||||
&packet_in->pi_dcid, conn->imc_stream_ps, &crypto_stream_if,
|
&packet_in->pi_dcid, conn->imc_stream_ps, &crypto_stream_if,
|
||||||
|
@ -581,6 +601,8 @@ ietf_mini_conn_ci_destroy (struct lsquic_conn *lconn)
|
||||||
if (lconn->cn_enc_session)
|
if (lconn->cn_enc_session)
|
||||||
lconn->cn_esf.i->esfi_destroy(lconn->cn_enc_session);
|
lconn->cn_esf.i->esfi_destroy(lconn->cn_enc_session);
|
||||||
LSQ_DEBUG("ietf_mini_conn_ci_destroyed");
|
LSQ_DEBUG("ietf_mini_conn_ci_destroyed");
|
||||||
|
if (conn->imc_flags & IMC_TRECHIST)
|
||||||
|
free(conn->imc_recvd_packnos.trechist.hist_elems);
|
||||||
lsquic_malo_put(conn);
|
lsquic_malo_put(conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1253,6 +1275,96 @@ imico_maybe_validate_by_dcid (struct ietf_mini_conn *conn,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
imico_received_packet_is_dup (struct ietf_mini_conn *conn,
|
||||||
|
enum packnum_space pns, lsquic_packno_t packno)
|
||||||
|
{
|
||||||
|
if (conn->imc_flags & IMC_TRECHIST)
|
||||||
|
return lsquic_trechist_contains(
|
||||||
|
conn->imc_recvd_packnos.trechist.hist_masks[pns],
|
||||||
|
conn->imc_recvd_packnos.trechist.hist_elems
|
||||||
|
+ TRECHIST_MAX_RANGES * pns, packno);
|
||||||
|
else
|
||||||
|
return !!(conn->imc_recvd_packnos.bitmasks[pns] & (1ULL << packno));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
imico_packno_is_largest (struct ietf_mini_conn *conn,
|
||||||
|
enum packnum_space pns, lsquic_packno_t packno)
|
||||||
|
{
|
||||||
|
if (conn->imc_flags & IMC_TRECHIST)
|
||||||
|
return 0 == conn->imc_recvd_packnos.trechist.hist_masks[pns]
|
||||||
|
|| packno > lsquic_trechist_max(
|
||||||
|
conn->imc_recvd_packnos.trechist.hist_masks[pns],
|
||||||
|
conn->imc_recvd_packnos.trechist.hist_elems
|
||||||
|
+ TRECHIST_MAX_RANGES * pns);
|
||||||
|
else
|
||||||
|
return 0 == conn->imc_recvd_packnos.bitmasks[pns]
|
||||||
|
|| packno > highest_bit_set(conn->imc_recvd_packnos.bitmasks[pns]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
imico_record_recvd_packno (struct ietf_mini_conn *conn,
|
||||||
|
enum packnum_space pns, lsquic_packno_t packno)
|
||||||
|
{
|
||||||
|
if (conn->imc_flags & IMC_TRECHIST)
|
||||||
|
{
|
||||||
|
if (0 != lsquic_trechist_insert(
|
||||||
|
&conn->imc_recvd_packnos.trechist.hist_masks[pns],
|
||||||
|
conn->imc_recvd_packnos.trechist.hist_elems
|
||||||
|
+ TRECHIST_MAX_RANGES * pns, packno))
|
||||||
|
{
|
||||||
|
LSQ_INFO("too many ranges for trechist to hold or range too wide");
|
||||||
|
conn->imc_flags |= IMC_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
conn->imc_recvd_packnos.bitmasks[pns] |= 1ULL << packno;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
imico_switch_to_trechist (struct ietf_mini_conn *conn)
|
||||||
|
{
|
||||||
|
uint32_t masks[IMICO_N_PNS];
|
||||||
|
enum packnum_space pns;
|
||||||
|
struct trechist_elem *elems;
|
||||||
|
struct ietf_mini_rechist iter;
|
||||||
|
|
||||||
|
elems = malloc(TRECHIST_SIZE * N_PNS);
|
||||||
|
if (!elems)
|
||||||
|
{
|
||||||
|
LSQ_WARN("cannot allocate trechist elems");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (pns = 0; pns < IMICO_N_PNS; ++pns)
|
||||||
|
if (conn->imc_recvd_packnos.bitmasks[pns])
|
||||||
|
{
|
||||||
|
lsquic_imico_rechist_init(&iter, conn, pns);
|
||||||
|
if (0 != lsquic_trechist_copy_ranges(&masks[pns],
|
||||||
|
elems + TRECHIST_MAX_RANGES * pns, &iter,
|
||||||
|
lsquic_imico_rechist_first,
|
||||||
|
lsquic_imico_rechist_next))
|
||||||
|
{
|
||||||
|
LSQ_WARN("cannot copy ranges from bitmask to trechist");
|
||||||
|
free(elems);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
masks[pns] = 0;
|
||||||
|
|
||||||
|
memcpy(conn->imc_recvd_packnos.trechist.hist_masks, masks, sizeof(masks));
|
||||||
|
conn->imc_recvd_packnos.trechist.hist_elems = elems;
|
||||||
|
conn->imc_flags |= IMC_TRECHIST;
|
||||||
|
LSQ_DEBUG("switched to trechist");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Only a single packet is supported */
|
/* Only a single packet is supported */
|
||||||
static void
|
static void
|
||||||
ietf_mini_conn_ci_packet_in (struct lsquic_conn *lconn,
|
ietf_mini_conn_ci_packet_in (struct lsquic_conn *lconn,
|
||||||
|
@ -1318,7 +1430,14 @@ ietf_mini_conn_ci_packet_in (struct lsquic_conn *lconn,
|
||||||
if (pns == PNS_HSK && !(conn->imc_flags & IMC_IGNORE_INIT))
|
if (pns == PNS_HSK && !(conn->imc_flags & IMC_IGNORE_INIT))
|
||||||
ignore_init(conn);
|
ignore_init(conn);
|
||||||
|
|
||||||
if (conn->imc_recvd_packnos[pns] & (1ULL << packet_in->pi_packno))
|
if (packet_in->pi_packno > MAX_PACKETS
|
||||||
|
&& !(conn->imc_flags & IMC_TRECHIST))
|
||||||
|
{
|
||||||
|
if (0 != imico_switch_to_trechist(conn))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (imico_received_packet_is_dup(conn, pns, packet_in->pi_packno))
|
||||||
{
|
{
|
||||||
LSQ_DEBUG("duplicate packet %"PRIu64, packet_in->pi_packno);
|
LSQ_DEBUG("duplicate packet %"PRIu64, packet_in->pi_packno);
|
||||||
return;
|
return;
|
||||||
|
@ -1328,10 +1447,9 @@ ietf_mini_conn_ci_packet_in (struct lsquic_conn *lconn,
|
||||||
* error, the connection is terminated and recording this packet number
|
* error, the connection is terminated and recording this packet number
|
||||||
* is helpful when it is printed along with other diagnostics in dtor.
|
* is helpful when it is printed along with other diagnostics in dtor.
|
||||||
*/
|
*/
|
||||||
if (0 == conn->imc_recvd_packnos[pns] ||
|
if (imico_packno_is_largest(conn, pns, packet_in->pi_packno))
|
||||||
packet_in->pi_packno > highest_bit_set(conn->imc_recvd_packnos[pns]))
|
|
||||||
conn->imc_largest_recvd[pns] = packet_in->pi_received;
|
conn->imc_largest_recvd[pns] = packet_in->pi_received;
|
||||||
conn->imc_recvd_packnos[pns] |= 1ULL << packet_in->pi_packno;
|
imico_record_recvd_packno(conn, pns, packet_in->pi_packno);
|
||||||
|
|
||||||
if (0 != imico_parse_regular_packet(conn, packet_in))
|
if (0 != imico_parse_regular_packet(conn, packet_in))
|
||||||
{
|
{
|
||||||
|
@ -1483,24 +1601,21 @@ imico_have_packets_to_send (struct ietf_mini_conn *conn, lsquic_time_t now)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct ietf_mini_rechist
|
void
|
||||||
{
|
lsquic_imico_rechist_init (struct ietf_mini_rechist *rechist,
|
||||||
const struct ietf_mini_conn *conn;
|
|
||||||
packno_set_t cur_set;
|
|
||||||
struct lsquic_packno_range range; /* We return a pointer to this */
|
|
||||||
int cur_idx;
|
|
||||||
enum packnum_space pns;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
imico_rechist_init (struct ietf_mini_rechist *rechist,
|
|
||||||
const struct ietf_mini_conn *conn, enum packnum_space pns)
|
const struct ietf_mini_conn *conn, enum packnum_space pns)
|
||||||
{
|
{
|
||||||
rechist->conn = conn;
|
rechist->conn = conn;
|
||||||
rechist->pns = pns;
|
rechist->pns = pns;
|
||||||
rechist->cur_set = 0;
|
if (conn->imc_flags & IMC_TRECHIST)
|
||||||
rechist->cur_idx = 0;
|
lsquic_trechist_iter(&rechist->u.trechist_iter,
|
||||||
|
conn->imc_recvd_packnos.trechist.hist_masks[pns],
|
||||||
|
conn->imc_recvd_packnos.trechist.hist_elems + TRECHIST_MAX_RANGES * pns);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rechist->u.bitmask.cur_set = 0;
|
||||||
|
rechist->u.bitmask.cur_idx = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1513,51 +1628,70 @@ imico_rechist_largest_recv (void *rechist_ctx)
|
||||||
|
|
||||||
|
|
||||||
static const struct lsquic_packno_range *
|
static const struct lsquic_packno_range *
|
||||||
imico_rechist_next (void *rechist_ctx)
|
imico_bitmask_rechist_next (struct ietf_mini_rechist *rechist)
|
||||||
{
|
{
|
||||||
struct ietf_mini_rechist *rechist = rechist_ctx;
|
|
||||||
const struct ietf_mini_conn *conn = rechist->conn;
|
const struct ietf_mini_conn *conn = rechist->conn;
|
||||||
packno_set_t packnos;
|
packno_set_t packnos;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
packnos = rechist->cur_set;
|
packnos = rechist->u.bitmask.cur_set;
|
||||||
if (0 == packnos)
|
if (0 == packnos)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* There may be a faster way to do this, but for now, we just want
|
/* There may be a faster way to do this, but for now, we just want
|
||||||
* correctness.
|
* correctness.
|
||||||
*/
|
*/
|
||||||
for (i = rechist->cur_idx; i >= 0; --i)
|
for (i = rechist->u.bitmask.cur_idx; i >= 0; --i)
|
||||||
if (packnos & (1ULL << i))
|
if (packnos & (1ULL << i))
|
||||||
{
|
{
|
||||||
rechist->range.low = i;
|
rechist->u.bitmask.range.low = i;
|
||||||
rechist->range.high = i;
|
rechist->u.bitmask.range.high = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
assert(i >= 0); /* We must have hit at least one bit */
|
assert(i >= 0); /* We must have hit at least one bit */
|
||||||
--i;
|
--i;
|
||||||
for ( ; i >= 0 && (packnos & (1ULL << i)); --i)
|
for ( ; i >= 0 && (packnos & (1ULL << i)); --i)
|
||||||
rechist->range.low = i;
|
rechist->u.bitmask.range.low = i;
|
||||||
if (i >= 0)
|
if (i >= 0)
|
||||||
{
|
{
|
||||||
rechist->cur_set = packnos & ((1ULL << i) - 1);
|
rechist->u.bitmask.cur_set = packnos & ((1ULL << i) - 1);
|
||||||
rechist->cur_idx = i;
|
rechist->u.bitmask.cur_idx = i;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
rechist->cur_set = 0;
|
rechist->u.bitmask.cur_set = 0;
|
||||||
LSQ_DEBUG("%s: return [%"PRIu64", %"PRIu64"]", __func__,
|
LSQ_DEBUG("%s: return [%"PRIu64", %"PRIu64"]", __func__,
|
||||||
rechist->range.low, rechist->range.high);
|
rechist->u.bitmask.range.low, rechist->u.bitmask.range.high);
|
||||||
return &rechist->range;
|
return &rechist->u.bitmask.range;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static const struct lsquic_packno_range *
|
const struct lsquic_packno_range *
|
||||||
imico_rechist_first (void *rechist_ctx)
|
lsquic_imico_rechist_next (void *rechist_ctx)
|
||||||
{
|
{
|
||||||
struct ietf_mini_rechist *rechist = rechist_ctx;
|
struct ietf_mini_rechist *rechist = rechist_ctx;
|
||||||
rechist->cur_set = rechist->conn->imc_recvd_packnos[ rechist->pns ];
|
|
||||||
rechist->cur_idx = highest_bit_set(rechist->cur_set);
|
if (rechist->conn->imc_flags & IMC_TRECHIST)
|
||||||
return imico_rechist_next(rechist_ctx);
|
return lsquic_trechist_next(&rechist->u.trechist_iter);
|
||||||
|
else
|
||||||
|
return imico_bitmask_rechist_next(rechist);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const struct lsquic_packno_range *
|
||||||
|
lsquic_imico_rechist_first (void *rechist_ctx)
|
||||||
|
{
|
||||||
|
struct ietf_mini_rechist *rechist = rechist_ctx;
|
||||||
|
|
||||||
|
if (rechist->conn->imc_flags & IMC_TRECHIST)
|
||||||
|
return lsquic_trechist_first(&rechist->u.trechist_iter);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rechist->u.bitmask.cur_set
|
||||||
|
= rechist->conn->imc_recvd_packnos.bitmasks[ rechist->pns ];
|
||||||
|
rechist->u.bitmask.cur_idx
|
||||||
|
= highest_bit_set(rechist->u.bitmask.cur_set);
|
||||||
|
return lsquic_imico_rechist_next(rechist_ctx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1598,11 +1732,11 @@ imico_generate_ack (struct ietf_mini_conn *conn, enum packnum_space pns,
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
/* Generate ACK frame */
|
/* Generate ACK frame */
|
||||||
imico_rechist_init(&rechist, conn, pns);
|
lsquic_imico_rechist_init(&rechist, conn, pns);
|
||||||
len = conn->imc_conn.cn_pf->pf_gen_ack_frame(
|
len = conn->imc_conn.cn_pf->pf_gen_ack_frame(
|
||||||
packet_out->po_data + packet_out->po_data_sz,
|
packet_out->po_data + packet_out->po_data_sz,
|
||||||
lsquic_packet_out_avail(packet_out), imico_rechist_first,
|
lsquic_packet_out_avail(packet_out), lsquic_imico_rechist_first,
|
||||||
imico_rechist_next, imico_rechist_largest_recv, &rechist,
|
lsquic_imico_rechist_next, imico_rechist_largest_recv, &rechist,
|
||||||
now, ¬_used_has_missing, &packet_out->po_ack2ed, ecn_counts);
|
now, ¬_used_has_missing, &packet_out->po_ack2ed, ecn_counts);
|
||||||
if (len < 0)
|
if (len < 0)
|
||||||
{
|
{
|
||||||
|
@ -1625,7 +1759,7 @@ imico_generate_acks (struct ietf_mini_conn *conn, lsquic_time_t now)
|
||||||
{
|
{
|
||||||
enum packnum_space pns;
|
enum packnum_space pns;
|
||||||
|
|
||||||
for (pns = PNS_INIT; pns < N_PNS; ++pns)
|
for (pns = PNS_INIT; pns < IMICO_N_PNS; ++pns)
|
||||||
if (conn->imc_flags & (IMC_QUEUED_ACK_INIT << pns)
|
if (conn->imc_flags & (IMC_QUEUED_ACK_INIT << pns)
|
||||||
&& !(pns == PNS_INIT && (conn->imc_flags & IMC_IGNORE_INIT)))
|
&& !(pns == PNS_INIT && (conn->imc_flags & IMC_IGNORE_INIT)))
|
||||||
if (0 != imico_generate_ack(conn, pns, now))
|
if (0 != imico_generate_ack(conn, pns, now))
|
||||||
|
@ -1814,8 +1948,7 @@ ietf_mini_conn_ci_tick (struct lsquic_conn *lconn, lsquic_time_t now)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (conn->imc_flags &
|
if (conn->imc_flags & (IMC_QUEUED_ACK_INIT|IMC_QUEUED_ACK_HSK))
|
||||||
(IMC_QUEUED_ACK_INIT|IMC_QUEUED_ACK_HSK|IMC_QUEUED_ACK_APP))
|
|
||||||
{
|
{
|
||||||
if (0 != imico_generate_acks(conn, now))
|
if (0 != imico_generate_acks(conn, now))
|
||||||
{
|
{
|
||||||
|
|
|
@ -27,6 +27,13 @@ struct mini_crypto_stream
|
||||||
typedef uint64_t packno_set_t;
|
typedef uint64_t packno_set_t;
|
||||||
#define MAX_PACKETS ((sizeof(packno_set_t) * 8) - 1)
|
#define MAX_PACKETS ((sizeof(packno_set_t) * 8) - 1)
|
||||||
|
|
||||||
|
/* We do not handle packets in the App packet number space in the mini
|
||||||
|
* connection. They are all buffered to be handled later when the
|
||||||
|
* connection is promoted. This means we do not have to have data
|
||||||
|
* structures to track the App PNS.
|
||||||
|
*/
|
||||||
|
#define IMICO_N_PNS (N_PNS - 1)
|
||||||
|
|
||||||
struct ietf_mini_conn
|
struct ietf_mini_conn
|
||||||
{
|
{
|
||||||
struct lsquic_conn imc_conn;
|
struct lsquic_conn imc_conn;
|
||||||
|
@ -37,7 +44,7 @@ struct ietf_mini_conn
|
||||||
IMC_ENC_SESS_INITED = 1 << 0,
|
IMC_ENC_SESS_INITED = 1 << 0,
|
||||||
IMC_QUEUED_ACK_INIT = 1 << 1,
|
IMC_QUEUED_ACK_INIT = 1 << 1,
|
||||||
IMC_QUEUED_ACK_HSK = IMC_QUEUED_ACK_INIT << PNS_HSK,
|
IMC_QUEUED_ACK_HSK = IMC_QUEUED_ACK_INIT << PNS_HSK,
|
||||||
IMC_QUEUED_ACK_APP = IMC_QUEUED_ACK_INIT << PNS_APP,
|
IMC_UNUSED3 = 1 << 3,
|
||||||
IMC_ERROR = 1 << 4,
|
IMC_ERROR = 1 << 4,
|
||||||
IMC_HSK_OK = 1 << 5,
|
IMC_HSK_OK = 1 << 5,
|
||||||
IMC_HSK_FAILED = 1 << 6,
|
IMC_HSK_FAILED = 1 << 6,
|
||||||
|
@ -58,6 +65,7 @@ struct ietf_mini_conn
|
||||||
IMC_PARSE_FAILED = 1 << 20,
|
IMC_PARSE_FAILED = 1 << 20,
|
||||||
IMC_PATH_CHANGED = 1 << 21,
|
IMC_PATH_CHANGED = 1 << 21,
|
||||||
IMC_HSK_DONE_SENT = 1 << 22,
|
IMC_HSK_DONE_SENT = 1 << 22,
|
||||||
|
IMC_TRECHIST = 1 << 23,
|
||||||
} imc_flags;
|
} imc_flags;
|
||||||
struct mini_crypto_stream imc_streams[N_ENC_LEVS];
|
struct mini_crypto_stream imc_streams[N_ENC_LEVS];
|
||||||
void *imc_stream_ps[N_ENC_LEVS];
|
void *imc_stream_ps[N_ENC_LEVS];
|
||||||
|
@ -69,9 +77,15 @@ struct ietf_mini_conn
|
||||||
TAILQ_HEAD(, lsquic_packet_out) imc_packets_out;
|
TAILQ_HEAD(, lsquic_packet_out) imc_packets_out;
|
||||||
TAILQ_HEAD(, stream_frame) imc_crypto_frames;
|
TAILQ_HEAD(, stream_frame) imc_crypto_frames;
|
||||||
packno_set_t imc_sent_packnos;
|
packno_set_t imc_sent_packnos;
|
||||||
packno_set_t imc_recvd_packnos[N_PNS];
|
union {
|
||||||
packno_set_t imc_acked_packnos[N_PNS];
|
packno_set_t bitmasks[IMICO_N_PNS];
|
||||||
lsquic_time_t imc_largest_recvd[N_PNS];
|
struct {
|
||||||
|
struct trechist_elem *hist_elems;
|
||||||
|
trechist_mask_t hist_masks[IMICO_N_PNS];
|
||||||
|
} trechist;
|
||||||
|
} imc_recvd_packnos;
|
||||||
|
packno_set_t imc_acked_packnos[IMICO_N_PNS];
|
||||||
|
lsquic_time_t imc_largest_recvd[IMICO_N_PNS];
|
||||||
struct lsquic_rtt_stats imc_rtt_stats;
|
struct lsquic_rtt_stats imc_rtt_stats;
|
||||||
unsigned imc_error_code;
|
unsigned imc_error_code;
|
||||||
unsigned imc_bytes_in;
|
unsigned imc_bytes_in;
|
||||||
|
@ -90,8 +104,8 @@ struct ietf_mini_conn
|
||||||
*/
|
*/
|
||||||
uint8_t imc_ecn_packnos;
|
uint8_t imc_ecn_packnos;
|
||||||
uint8_t imc_ack_exp;
|
uint8_t imc_ack_exp;
|
||||||
uint8_t imc_ecn_counts_in[N_PNS][4];
|
uint8_t imc_ecn_counts_in[IMICO_N_PNS][4];
|
||||||
uint8_t imc_ecn_counts_out[N_PNS][4];
|
uint8_t imc_ecn_counts_out[IMICO_N_PNS][4];
|
||||||
uint8_t imc_incoming_ecn;
|
uint8_t imc_incoming_ecn;
|
||||||
uint8_t imc_tls_alert;
|
uint8_t imc_tls_alert;
|
||||||
#define IMICO_MAX_DELAYED_PACKETS_UNVALIDATED 1u
|
#define IMICO_MAX_DELAYED_PACKETS_UNVALIDATED 1u
|
||||||
|
@ -121,4 +135,29 @@ lsquic_mini_conn_ietf_new (struct lsquic_engine_public *,
|
||||||
|
|
||||||
int
|
int
|
||||||
lsquic_mini_conn_ietf_ecn_ok (const struct ietf_mini_conn *);
|
lsquic_mini_conn_ietf_ecn_ok (const struct ietf_mini_conn *);
|
||||||
|
|
||||||
|
struct ietf_mini_rechist
|
||||||
|
{
|
||||||
|
const struct ietf_mini_conn *conn;
|
||||||
|
enum packnum_space pns;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
packno_set_t cur_set;
|
||||||
|
struct lsquic_packno_range range; /* We return a pointer to this */
|
||||||
|
int cur_idx;
|
||||||
|
} bitmask;
|
||||||
|
struct trechist_iter trechist_iter;
|
||||||
|
} u;
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
lsquic_imico_rechist_init (struct ietf_mini_rechist *rechist,
|
||||||
|
const struct ietf_mini_conn *conn, enum packnum_space pns);
|
||||||
|
|
||||||
|
const struct lsquic_packno_range *
|
||||||
|
lsquic_imico_rechist_first (void *rechist_ctx);
|
||||||
|
|
||||||
|
const struct lsquic_packno_range *
|
||||||
|
lsquic_imico_rechist_next (void *rechist_ctx);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "lsquic_packet_common.h"
|
#include "lsquic_packet_common.h"
|
||||||
#include "lsquic_mini_conn.h"
|
#include "lsquic_mini_conn.h"
|
||||||
#include "lsquic_enc_sess.h"
|
#include "lsquic_enc_sess.h"
|
||||||
|
#include "lsquic_trechist.h"
|
||||||
#include "lsquic_mini_conn_ietf.h"
|
#include "lsquic_mini_conn_ietf.h"
|
||||||
#include "lsquic_packet_gquic.h"
|
#include "lsquic_packet_gquic.h"
|
||||||
#include "lsquic_packet_in.h"
|
#include "lsquic_packet_in.h"
|
||||||
|
|
|
@ -27,6 +27,11 @@
|
||||||
#include "lsquic_engine_public.h"
|
#include "lsquic_engine_public.h"
|
||||||
#include "lsquic_headers.h"
|
#include "lsquic_headers.h"
|
||||||
#include "lsquic_conn.h"
|
#include "lsquic_conn.h"
|
||||||
|
#include "lsquic_conn_flow.h"
|
||||||
|
#include "lsquic_rtt.h"
|
||||||
|
#include "lsquic_conn_public.h"
|
||||||
|
#include "lsquic_hq.h"
|
||||||
|
#include "lsquic_parse.h"
|
||||||
|
|
||||||
#define LSQUIC_LOGGER_MODULE LSQLM_QDEC_HDL
|
#define LSQUIC_LOGGER_MODULE LSQLM_QDEC_HDL
|
||||||
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(qdh->qdh_conn)
|
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(qdh->qdh_conn)
|
||||||
|
@ -39,6 +44,8 @@ struct header_ctx
|
||||||
{
|
{
|
||||||
void *hset;
|
void *hset;
|
||||||
struct qpack_dec_hdl *qdh;
|
struct qpack_dec_hdl *qdh;
|
||||||
|
enum ppc_flags ppc_flags;
|
||||||
|
struct lsquic_ext_http_prio ehp;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -471,6 +478,14 @@ is_content_length (const struct lsxpack_header *xhdr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
is_priority (const struct lsxpack_header *xhdr)
|
||||||
|
{
|
||||||
|
return xhdr->name_len == 8
|
||||||
|
&& 0 == memcmp(lsxpack_header_get_name(xhdr), "priority", 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static struct lsxpack_header *
|
static struct lsxpack_header *
|
||||||
qdh_prepare_decode (void *stream_p, struct lsxpack_header *xhdr, size_t space)
|
qdh_prepare_decode (void *stream_p, struct lsxpack_header *xhdr, size_t space)
|
||||||
{
|
{
|
||||||
|
@ -499,6 +514,16 @@ qdh_process_header (void *stream_p, struct lsxpack_header *xhdr)
|
||||||
if (cl.has > 0)
|
if (cl.has > 0)
|
||||||
(void) lsquic_stream_verify_len(stream, cl.value);
|
(void) lsquic_stream_verify_len(stream, cl.value);
|
||||||
}
|
}
|
||||||
|
else if ((stream->sm_bflags & (SMBF_HTTP_PRIO|SMBF_HPRIO_SET))
|
||||||
|
== SMBF_HTTP_PRIO
|
||||||
|
&& is_priority(xhdr))
|
||||||
|
{
|
||||||
|
u->ctx.ppc_flags &= ~(PPC_INC_NAME|PPC_URG_NAME);
|
||||||
|
(void) lsquic_http_parse_pfv(lsxpack_header_get_value(xhdr),
|
||||||
|
xhdr->val_len, &u->ctx.ppc_flags, &u->ctx.ehp,
|
||||||
|
(char *) stream->conn_pub->mm->acki,
|
||||||
|
sizeof(*stream->conn_pub->mm->acki));
|
||||||
|
}
|
||||||
|
|
||||||
return qdh->qdh_enpub->enp_hsi_if->hsi_process_header(u->ctx.hset, xhdr);
|
return qdh->qdh_enpub->enp_hsi_if->hsi_process_header(u->ctx.hset, xhdr);
|
||||||
}
|
}
|
||||||
|
@ -525,6 +550,15 @@ qdh_header_read_results (struct qpack_dec_hdl *qdh,
|
||||||
{
|
{
|
||||||
if (!lsquic_stream_header_is_trailer(stream))
|
if (!lsquic_stream_header_is_trailer(stream))
|
||||||
{
|
{
|
||||||
|
if (stream->sm_hblock_ctx->ctx.ppc_flags
|
||||||
|
& (PPC_INC_SET|PPC_URG_SET))
|
||||||
|
{
|
||||||
|
assert(stream->sm_bflags & SMBF_HTTP_PRIO);
|
||||||
|
LSQ_DEBUG("Apply Priority from headers to stream %"PRIu64,
|
||||||
|
stream->id);
|
||||||
|
(void) lsquic_stream_set_http_prio(stream,
|
||||||
|
&stream->sm_hblock_ctx->ctx.ehp);
|
||||||
|
}
|
||||||
hset = stream->sm_hblock_ctx->ctx.hset;
|
hset = stream->sm_hblock_ctx->ctx.hset;
|
||||||
uh = &stream->sm_hblock_ctx->uh;
|
uh = &stream->sm_hblock_ctx->uh;
|
||||||
stream->sm_hblock_ctx = NULL;
|
stream->sm_hblock_ctx = NULL;
|
||||||
|
@ -621,6 +655,11 @@ lsquic_qdh_header_in_begin (struct qpack_dec_hdl *qdh,
|
||||||
|
|
||||||
u->ctx.hset = hset;
|
u->ctx.hset = hset;
|
||||||
u->ctx.qdh = qdh;
|
u->ctx.qdh = qdh;
|
||||||
|
u->ctx.ppc_flags = 0;
|
||||||
|
u->ctx.ehp = (struct lsquic_ext_http_prio) {
|
||||||
|
.urgency = LSQUIC_DEF_HTTP_URGENCY,
|
||||||
|
.incremental = LSQUIC_DEF_HTTP_INCREMENTAL,
|
||||||
|
};
|
||||||
stream->sm_hblock_ctx = u;
|
stream->sm_hblock_ctx = u;
|
||||||
|
|
||||||
dec_buf_sz = sizeof(dec_buf);
|
dec_buf_sz = sizeof(dec_buf);
|
||||||
|
|
|
@ -434,3 +434,34 @@ lsquic_rechist_peek (struct lsquic_rechist *rechist)
|
||||||
else
|
else
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
lsquic_rechist_copy_ranges (struct lsquic_rechist *rechist, void *src_rechist,
|
||||||
|
const struct lsquic_packno_range * (*first) (void *),
|
||||||
|
const struct lsquic_packno_range * (*next) (void *))
|
||||||
|
{
|
||||||
|
const struct lsquic_packno_range *range;
|
||||||
|
struct rechist_elem *el;
|
||||||
|
unsigned *next_idx;
|
||||||
|
int idx;
|
||||||
|
|
||||||
|
/* This function only works if rechist contains no elements */
|
||||||
|
assert(rechist->rh_n_used == 0);
|
||||||
|
|
||||||
|
next_idx = &rechist->rh_head;
|
||||||
|
for (range = first(src_rechist); range; range = next(src_rechist))
|
||||||
|
{
|
||||||
|
idx = rechist_alloc_elem(rechist);
|
||||||
|
if (idx < 0)
|
||||||
|
return -1;
|
||||||
|
el = &rechist->rh_elems[idx];
|
||||||
|
el->re_low = range->low;
|
||||||
|
el->re_count = range->high - range->low + 1;
|
||||||
|
el->re_next = UINT_MAX;
|
||||||
|
*next_idx = idx;
|
||||||
|
next_idx = &el->re_next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -84,4 +84,9 @@ lsquic_rechist_peek (struct lsquic_rechist *);
|
||||||
|
|
||||||
#define lsquic_rechist_is_empty(rechist_) ((rechist_)->rh_n_used == 0)
|
#define lsquic_rechist_is_empty(rechist_) ((rechist_)->rh_n_used == 0)
|
||||||
|
|
||||||
|
int
|
||||||
|
lsquic_rechist_copy_ranges (struct lsquic_rechist *, void *rechist_ctx,
|
||||||
|
const struct lsquic_packno_range * (*first) (void *),
|
||||||
|
const struct lsquic_packno_range * (*next) (void *));
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -305,16 +305,23 @@ first_packno (const struct lsquic_send_ctl *ctl)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* [draft-ietf-quic-transport-12], Section 4.4.1:
|
|
||||||
*
|
|
||||||
* " The first Initial packet that is sent by a client contains a packet
|
|
||||||
* " number of 0. All subsequent packets contain a packet number that is
|
|
||||||
* " incremented by at least one, see (Section 4.8).
|
|
||||||
*/
|
|
||||||
static void
|
static void
|
||||||
send_ctl_pick_initial_packno (struct lsquic_send_ctl *ctl)
|
send_ctl_pick_initial_packno (struct lsquic_send_ctl *ctl)
|
||||||
{
|
{
|
||||||
|
#ifndef NDEBUG
|
||||||
|
lsquic_packno_t packno;
|
||||||
|
const char *s;
|
||||||
|
|
||||||
|
if (!(ctl->sc_conn_pub->lconn->cn_flags & LSCONN_SERVER)
|
||||||
|
&& (s = getenv("LSQUIC_STARTING_PACKNO"), s != NULL))
|
||||||
|
{
|
||||||
|
packno = (lsquic_packno_t) strtoull(s, NULL, 10);
|
||||||
|
LSQ_DEBUG("starting sending packet numbers starting with %"PRIu64
|
||||||
|
" based on environment variable", packno);
|
||||||
|
ctl->sc_cur_packno = packno - 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
ctl->sc_cur_packno = first_packno(ctl) - 1;
|
ctl->sc_cur_packno = first_packno(ctl) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -352,6 +359,13 @@ lsquic_send_ctl_init (lsquic_send_ctl_t *ctl, struct lsquic_alarmset *alset,
|
||||||
lsquic_alarmset_init_alarm(alset, AL_RETX_HSK, retx_alarm_rings, ctl);
|
lsquic_alarmset_init_alarm(alset, AL_RETX_HSK, retx_alarm_rings, ctl);
|
||||||
lsquic_alarmset_init_alarm(alset, AL_RETX_APP, retx_alarm_rings, ctl);
|
lsquic_alarmset_init_alarm(alset, AL_RETX_APP, retx_alarm_rings, ctl);
|
||||||
lsquic_senhist_init(&ctl->sc_senhist, ctl->sc_flags & SC_IETF);
|
lsquic_senhist_init(&ctl->sc_senhist, ctl->sc_flags & SC_IETF);
|
||||||
|
#ifndef NDEBUG
|
||||||
|
/* TODO: the logic to select the "previously sent" packno should not be
|
||||||
|
* duplicated here and in lsquic_senhist_init()...
|
||||||
|
*/
|
||||||
|
if (!(ctl->sc_conn_pub->lconn->cn_flags & LSCONN_SERVER))
|
||||||
|
ctl->sc_senhist.sh_last_sent = ctl->sc_cur_packno;
|
||||||
|
#endif
|
||||||
switch (enpub->enp_settings.es_cc_algo)
|
switch (enpub->enp_settings.es_cc_algo)
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -1093,6 +1107,7 @@ lsquic_send_ctl_got_ack (lsquic_send_ctl_t *ctl,
|
||||||
unsigned ecn_total_acked, ecn_ce_cnt, one_rtt_cnt;
|
unsigned ecn_total_acked, ecn_ce_cnt, one_rtt_cnt;
|
||||||
|
|
||||||
pns = acki->pns;
|
pns = acki->pns;
|
||||||
|
ctl->sc_flags |= SC_ACK_RECV_INIT << pns;
|
||||||
packet_out = TAILQ_FIRST(&ctl->sc_unacked_packets[pns]);
|
packet_out = TAILQ_FIRST(&ctl->sc_unacked_packets[pns]);
|
||||||
#if __GNUC__
|
#if __GNUC__
|
||||||
__builtin_prefetch(packet_out);
|
__builtin_prefetch(packet_out);
|
||||||
|
@ -1704,6 +1719,8 @@ lsquic_send_ctl_do_sanity_check (const struct lsquic_send_ctl *ctl)
|
||||||
assert(count == ctl->sc_n_scheduled);
|
assert(count == ctl->sc_n_scheduled);
|
||||||
assert(bytes == ctl->sc_bytes_scheduled);
|
assert(bytes == ctl->sc_bytes_scheduled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
@ -2073,7 +2090,7 @@ lsquic_send_ctl_new_packet_out (lsquic_send_ctl_t *ctl, unsigned need_at_least,
|
||||||
lsquic_packet_out_t *packet_out;
|
lsquic_packet_out_t *packet_out;
|
||||||
enum packno_bits bits;
|
enum packno_bits bits;
|
||||||
|
|
||||||
bits = lsquic_send_ctl_packno_bits(ctl);
|
bits = lsquic_send_ctl_packno_bits(ctl, pns);
|
||||||
packet_out = send_ctl_allocate_packet(ctl, bits, need_at_least, pns, path);
|
packet_out = send_ctl_allocate_packet(ctl, bits, need_at_least, pns, path);
|
||||||
if (!packet_out)
|
if (!packet_out)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -2395,6 +2412,7 @@ send_ctl_log_packet_q (const lsquic_send_ctl_t *ctl, const char *prefix,
|
||||||
free(buf);
|
free(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#define LOG_PACKET_Q(prefix, queue) do { \
|
#define LOG_PACKET_Q(prefix, queue) do { \
|
||||||
if (LSQ_LOG_ENABLED(LSQ_LOG_DEBUG)) \
|
if (LSQ_LOG_ENABLED(LSQ_LOG_DEBUG)) \
|
||||||
send_ctl_log_packet_q(ctl, queue, prefix); \
|
send_ctl_log_packet_q(ctl, queue, prefix); \
|
||||||
|
@ -2772,13 +2790,22 @@ lsquic_send_ctl_calc_packno_bits (lsquic_send_ctl_t *ctl)
|
||||||
|
|
||||||
|
|
||||||
enum packno_bits
|
enum packno_bits
|
||||||
lsquic_send_ctl_packno_bits (lsquic_send_ctl_t *ctl)
|
lsquic_send_ctl_packno_bits (struct lsquic_send_ctl *ctl,
|
||||||
|
enum packnum_space pns)
|
||||||
{
|
{
|
||||||
|
if ((ctl->sc_flags & (SC_ACK_RECV_INIT << pns))
|
||||||
if (lsquic_send_ctl_schedule_stream_packets_immediately(ctl))
|
&& lsquic_send_ctl_schedule_stream_packets_immediately(ctl))
|
||||||
return lsquic_send_ctl_calc_packno_bits(ctl);
|
return lsquic_send_ctl_calc_packno_bits(ctl);
|
||||||
else
|
else if (ctl->sc_flags & (SC_ACK_RECV_INIT << pns))
|
||||||
return lsquic_send_ctl_guess_packno_bits(ctl);
|
return lsquic_send_ctl_guess_packno_bits(ctl);
|
||||||
|
else
|
||||||
|
/* From [draft-ietf-quic-transport-31] Section 17.1:
|
||||||
|
*
|
||||||
|
" Prior to receiving an acknowledgement for a packet number space, the
|
||||||
|
" full packet number MUST be included; it is not to be truncated as
|
||||||
|
" described below.
|
||||||
|
*/
|
||||||
|
return vint_val2bits(ctl->sc_cur_packno + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,9 @@ enum send_ctl_flags {
|
||||||
SC_CIDLEN = 1 << 16, /* sc_cidlen is set */
|
SC_CIDLEN = 1 << 16, /* sc_cidlen is set */
|
||||||
SC_POISON = 1 << 17, /* poisoned packet exists */
|
SC_POISON = 1 << 17, /* poisoned packet exists */
|
||||||
SC_CLEANUP_BBR = 1 << 18,
|
SC_CLEANUP_BBR = 1 << 18,
|
||||||
|
SC_ACK_RECV_INIT= 1 << 19,
|
||||||
|
SC_ACK_RECV_HSK = SC_ACK_RECV_INIT << PNS_HSK,
|
||||||
|
SC_ACK_RECV_APP = SC_ACK_RECV_INIT << PNS_APP,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct lsquic_send_ctl {
|
typedef struct lsquic_send_ctl {
|
||||||
|
@ -290,7 +293,7 @@ lsquic_send_ctl_drop_scheduled (lsquic_send_ctl_t *);
|
||||||
: 0 )
|
: 0 )
|
||||||
|
|
||||||
enum packno_bits
|
enum packno_bits
|
||||||
lsquic_send_ctl_packno_bits (lsquic_send_ctl_t *);
|
lsquic_send_ctl_packno_bits (struct lsquic_send_ctl *, enum packnum_space);
|
||||||
|
|
||||||
int
|
int
|
||||||
lsquic_send_ctl_schedule_buffered (lsquic_send_ctl_t *, enum buf_packet_type);
|
lsquic_send_ctl_schedule_buffered (lsquic_send_ctl_t *, enum buf_packet_type);
|
||||||
|
|
|
@ -21,6 +21,9 @@
|
||||||
#include "lsquic_hq.h"
|
#include "lsquic_hq.h"
|
||||||
#include "lsquic_hash.h"
|
#include "lsquic_hash.h"
|
||||||
#include "lsquic_stream.h"
|
#include "lsquic_stream.h"
|
||||||
|
#include "lsquic_conn_flow.h"
|
||||||
|
#include "lsquic_rtt.h"
|
||||||
|
#include "lsquic_conn_public.h"
|
||||||
#include "lsquic_spi.h"
|
#include "lsquic_spi.h"
|
||||||
|
|
||||||
#define LSQUIC_LOGGER_MODULE LSQLM_SPI
|
#define LSQUIC_LOGGER_MODULE LSQLM_SPI
|
||||||
|
@ -51,17 +54,18 @@ add_stream_to_spi (struct stream_prio_iter *iter, lsquic_stream_t *stream)
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
lsquic_spi_init (struct stream_prio_iter *iter, struct lsquic_stream *first,
|
lsquic_spi_init (void *iter_p, struct lsquic_stream *first,
|
||||||
struct lsquic_stream *last, uintptr_t next_ptr_offset,
|
struct lsquic_stream *last, uintptr_t next_ptr_offset,
|
||||||
const struct lsquic_conn *conn,
|
struct lsquic_conn_public *conn_pub,
|
||||||
const char *name,
|
const char *name,
|
||||||
int (*filter)(void *filter_ctx, struct lsquic_stream *),
|
int (*filter)(void *filter_ctx, struct lsquic_stream *),
|
||||||
void *filter_ctx)
|
void *filter_ctx)
|
||||||
{
|
{
|
||||||
|
struct stream_prio_iter *const iter = iter_p;
|
||||||
struct lsquic_stream *stream;
|
struct lsquic_stream *stream;
|
||||||
unsigned count;
|
unsigned count;
|
||||||
|
|
||||||
iter->spi_conn = conn;
|
iter->spi_conn = conn_pub->lconn;
|
||||||
iter->spi_name = name ? name : "UNSET";
|
iter->spi_name = name ? name : "UNSET";
|
||||||
iter->spi_set[0] = 0;
|
iter->spi_set[0] = 0;
|
||||||
iter->spi_set[1] = 0;
|
iter->spi_set[1] = 0;
|
||||||
|
@ -194,8 +198,9 @@ find_and_set_next_priority (struct stream_prio_iter *iter)
|
||||||
|
|
||||||
|
|
||||||
lsquic_stream_t *
|
lsquic_stream_t *
|
||||||
lsquic_spi_first (struct stream_prio_iter *iter)
|
lsquic_spi_first (void *iter_p)
|
||||||
{
|
{
|
||||||
|
struct stream_prio_iter *const iter = iter_p;
|
||||||
lsquic_stream_t *stream;
|
lsquic_stream_t *stream;
|
||||||
unsigned set, bit;
|
unsigned set, bit;
|
||||||
|
|
||||||
|
@ -222,8 +227,9 @@ lsquic_spi_first (struct stream_prio_iter *iter)
|
||||||
|
|
||||||
|
|
||||||
lsquic_stream_t *
|
lsquic_stream_t *
|
||||||
lsquic_spi_next (struct stream_prio_iter *iter)
|
lsquic_spi_next (void *iter_p)
|
||||||
{
|
{
|
||||||
|
struct stream_prio_iter *const iter = iter_p;
|
||||||
lsquic_stream_t *stream;
|
lsquic_stream_t *stream;
|
||||||
|
|
||||||
stream = iter->spi_next_stream;
|
stream = iter->spi_next_stream;
|
||||||
|
@ -303,8 +309,9 @@ spi_has_more_than_one_queue (const struct stream_prio_iter *iter)
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
spi_drop_high_or_non_high (struct stream_prio_iter *iter, int drop_high)
|
spi_drop_high_or_non_high (void *iter_p, int drop_high)
|
||||||
{
|
{
|
||||||
|
struct stream_prio_iter *const iter = iter_p;
|
||||||
uint64_t new_set[ sizeof(iter->spi_set) / sizeof(iter->spi_set[0]) ];
|
uint64_t new_set[ sizeof(iter->spi_set) / sizeof(iter->spi_set[0]) ];
|
||||||
unsigned bit, set, n;
|
unsigned bit, set, n;
|
||||||
|
|
||||||
|
@ -336,14 +343,22 @@ spi_drop_high_or_non_high (struct stream_prio_iter *iter, int drop_high)
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
lsquic_spi_drop_high (struct stream_prio_iter *iter)
|
lsquic_spi_drop_high (void *iter_p)
|
||||||
{
|
{
|
||||||
|
struct stream_prio_iter *const iter = iter_p;
|
||||||
spi_drop_high_or_non_high(iter, 1);
|
spi_drop_high_or_non_high(iter, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
lsquic_spi_drop_non_high (struct stream_prio_iter *iter)
|
lsquic_spi_drop_non_high (void *iter_p)
|
||||||
{
|
{
|
||||||
|
struct stream_prio_iter *const iter = iter_p;
|
||||||
spi_drop_high_or_non_high(iter, 0);
|
spi_drop_high_or_non_high(iter, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
lsquic_spi_cleanup (void *iter_p)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
|
@ -2,20 +2,13 @@
|
||||||
/*
|
/*
|
||||||
* lsquic_spi.h - SPI: Stream Priority Iterator
|
* lsquic_spi.h - SPI: Stream Priority Iterator
|
||||||
*
|
*
|
||||||
* SPI purposefully does not support switching stream priorities while
|
* Changing a stream's priority when the stream is in the iterator
|
||||||
* iterator is active, because this puts iteration termination outside
|
* does not change the stream's position in the iterator.
|
||||||
* of our control. One can imagine (admittedly theoretical) scenario
|
|
||||||
* in which the user keeps on switching stream priorities around and
|
|
||||||
* causing an infinite loop.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LSQUIC_SPI
|
#ifndef LSQUIC_SPI
|
||||||
#define LSQUIC_SPI 1
|
#define LSQUIC_SPI 1
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
enum stream_q_flags;
|
|
||||||
|
|
||||||
|
|
||||||
struct stream_prio_iter
|
struct stream_prio_iter
|
||||||
{
|
{
|
||||||
|
@ -30,26 +23,26 @@ struct stream_prio_iter
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
lsquic_spi_init (struct stream_prio_iter *, struct lsquic_stream *first,
|
lsquic_spi_init (void *, struct lsquic_stream *first,
|
||||||
struct lsquic_stream *last, uintptr_t next_ptr_offset,
|
struct lsquic_stream *last, uintptr_t next_ptr_offset,
|
||||||
const struct lsquic_conn *,
|
struct lsquic_conn_public *,
|
||||||
const char *name,
|
const char *name,
|
||||||
int (*filter)(void *filter_ctx, struct lsquic_stream *),
|
int (*filter)(void *filter_ctx, struct lsquic_stream *),
|
||||||
void *filter_ctx);
|
void *filter_ctx);
|
||||||
|
|
||||||
struct lsquic_stream *
|
struct lsquic_stream *
|
||||||
lsquic_spi_first (struct stream_prio_iter *);
|
lsquic_spi_first (void *);
|
||||||
|
|
||||||
struct lsquic_stream *
|
struct lsquic_stream *
|
||||||
lsquic_spi_next (struct stream_prio_iter *);
|
lsquic_spi_next (void *);
|
||||||
|
|
||||||
void
|
void
|
||||||
lsquic_spi_exhaust_on (struct stream_prio_iter *);
|
lsquic_spi_drop_non_high (void *);
|
||||||
|
|
||||||
void
|
void
|
||||||
lsquic_spi_drop_non_high (struct stream_prio_iter *);
|
lsquic_spi_drop_high (void *);
|
||||||
|
|
||||||
void
|
void
|
||||||
lsquic_spi_drop_high (struct stream_prio_iter *);
|
lsquic_spi_cleanup (void *);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -74,6 +74,7 @@
|
||||||
#include "lsquic_byteswap.h"
|
#include "lsquic_byteswap.h"
|
||||||
#include "lsquic_ietf.h"
|
#include "lsquic_ietf.h"
|
||||||
#include "lsquic_push_promise.h"
|
#include "lsquic_push_promise.h"
|
||||||
|
#include "lsquic_hcso_writer.h"
|
||||||
|
|
||||||
#define LSQUIC_LOGGER_MODULE LSQLM_STREAM
|
#define LSQUIC_LOGGER_MODULE LSQLM_STREAM
|
||||||
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(stream->conn_pub->lconn)
|
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(stream->conn_pub->lconn)
|
||||||
|
@ -439,6 +440,10 @@ lsquic_stream_new (lsquic_stream_id_t id,
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
stream->sm_readable = stream_readable_non_http;
|
stream->sm_readable = stream_readable_non_http;
|
||||||
|
if ((ctor_flags & (SCF_HTTP|SCF_HTTP_PRIO))
|
||||||
|
== (SCF_HTTP|SCF_HTTP_PRIO))
|
||||||
|
lsquic_stream_set_priority_internal(stream, LSQUIC_DEF_HTTP_URGENCY);
|
||||||
|
else
|
||||||
lsquic_stream_set_priority_internal(stream,
|
lsquic_stream_set_priority_internal(stream,
|
||||||
LSQUIC_STREAM_DEFAULT_PRIO);
|
LSQUIC_STREAM_DEFAULT_PRIO);
|
||||||
stream->sm_write_to_packet = stream_write_to_packet_std;
|
stream->sm_write_to_packet = stream_write_to_packet_std;
|
||||||
|
@ -807,14 +812,18 @@ stream_readable_discard (struct lsquic_stream *stream)
|
||||||
{
|
{
|
||||||
struct data_frame *data_frame;
|
struct data_frame *data_frame;
|
||||||
uint64_t toread;
|
uint64_t toread;
|
||||||
|
int fin;
|
||||||
|
|
||||||
while ((data_frame = stream->data_in->di_if->di_get_frame(
|
while ((data_frame = stream->data_in->di_if->di_get_frame(
|
||||||
stream->data_in, stream->read_offset)))
|
stream->data_in, stream->read_offset)))
|
||||||
{
|
{
|
||||||
|
fin = data_frame->df_fin;
|
||||||
toread = data_frame->df_size - data_frame->df_read_off;
|
toread = data_frame->df_size - data_frame->df_read_off;
|
||||||
stream->read_offset += toread;
|
stream->read_offset += toread;
|
||||||
data_frame->df_read_off = data_frame->df_size;
|
data_frame->df_read_off = data_frame->df_size;
|
||||||
stream->data_in->di_if->di_frame_done(stream->data_in, data_frame);
|
stream->data_in->di_if->di_frame_done(stream->data_in, data_frame);
|
||||||
|
if (fin)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
(void) maybe_switch_data_in(stream);
|
(void) maybe_switch_data_in(stream);
|
||||||
|
@ -2417,7 +2426,7 @@ lsquic_stream_flush_threshold (const struct lsquic_stream *stream,
|
||||||
size_t packet_header_sz, stream_header_sz, tag_len;
|
size_t packet_header_sz, stream_header_sz, tag_len;
|
||||||
size_t threshold;
|
size_t threshold;
|
||||||
|
|
||||||
bits = lsquic_send_ctl_packno_bits(stream->conn_pub->send_ctl);
|
bits = lsquic_send_ctl_packno_bits(stream->conn_pub->send_ctl, PNS_APP);
|
||||||
flags = bits << POBIT_SHIFT;
|
flags = bits << POBIT_SHIFT;
|
||||||
if (!(stream->conn_pub->lconn->cn_flags & LSCONN_TCID0))
|
if (!(stream->conn_pub->lconn->cn_flags & LSCONN_TCID0))
|
||||||
flags |= PO_CONN_ID;
|
flags |= PO_CONN_ID;
|
||||||
|
@ -2802,13 +2811,17 @@ frame_hq_gen_read (void *ctx, void *begin_buf, size_t len, int *fin)
|
||||||
struct stream_hq_frame *shf;
|
struct stream_hq_frame *shf;
|
||||||
size_t nw, frame_sz, avail, rem;
|
size_t nw, frame_sz, avail, rem;
|
||||||
unsigned bits;
|
unsigned bits;
|
||||||
|
int new;
|
||||||
|
|
||||||
while (p < end)
|
while (p < end)
|
||||||
{
|
{
|
||||||
shf = find_cur_hq_frame(stream);
|
shf = find_cur_hq_frame(stream);
|
||||||
if (shf)
|
if (shf)
|
||||||
|
{
|
||||||
|
new = 0;
|
||||||
LSQ_DEBUG("found current HQ frame of type 0x%X at offset %"PRIu64,
|
LSQ_DEBUG("found current HQ frame of type 0x%X at offset %"PRIu64,
|
||||||
shf->shf_frame_type, shf->shf_off);
|
shf->shf_frame_type, shf->shf_off);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
rem = frame_std_gen_size(ctx);
|
rem = frame_std_gen_size(ctx);
|
||||||
|
@ -2819,7 +2832,10 @@ frame_hq_gen_read (void *ctx, void *begin_buf, size_t len, int *fin)
|
||||||
shf = stream_activate_hq_frame(stream,
|
shf = stream_activate_hq_frame(stream,
|
||||||
stream->sm_payload, HQFT_DATA, 0, rem);
|
stream->sm_payload, HQFT_DATA, 0, rem);
|
||||||
if (shf)
|
if (shf)
|
||||||
|
{
|
||||||
|
new = 1;
|
||||||
goto insert;
|
goto insert;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* TODO: abort connection? Handle failure somehow */
|
/* TODO: abort connection? Handle failure somehow */
|
||||||
|
@ -2836,6 +2852,7 @@ frame_hq_gen_read (void *ctx, void *begin_buf, size_t len, int *fin)
|
||||||
frame_sz = stream_hq_frame_size(shf);
|
frame_sz = stream_hq_frame_size(shf);
|
||||||
if (frame_sz > (uintptr_t) (end - p))
|
if (frame_sz > (uintptr_t) (end - p))
|
||||||
{
|
{
|
||||||
|
if (new)
|
||||||
stream_hq_frame_put(stream, shf);
|
stream_hq_frame_put(stream, shf);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -4337,6 +4354,9 @@ lsquic_stream_uh_in (lsquic_stream_t *stream, struct uncompressed_headers *uh)
|
||||||
unsigned
|
unsigned
|
||||||
lsquic_stream_priority (const lsquic_stream_t *stream)
|
lsquic_stream_priority (const lsquic_stream_t *stream)
|
||||||
{
|
{
|
||||||
|
if (stream->sm_bflags & SMBF_HTTP_PRIO)
|
||||||
|
return stream->sm_priority;
|
||||||
|
else
|
||||||
return 256 - stream->sm_priority;
|
return 256 - stream->sm_priority;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4349,9 +4369,20 @@ lsquic_stream_set_priority_internal (lsquic_stream_t *stream, unsigned priority)
|
||||||
*/
|
*/
|
||||||
if (lsquic_stream_is_critical(stream))
|
if (lsquic_stream_is_critical(stream))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
if (stream->sm_bflags & SMBF_HTTP_PRIO)
|
||||||
|
{
|
||||||
|
if (priority > LSQUIC_MAX_HTTP_URGENCY)
|
||||||
|
return -1;
|
||||||
|
stream->sm_priority = priority;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
if (priority < 1 || priority > 256)
|
if (priority < 1 || priority > 256)
|
||||||
return -1;
|
return -1;
|
||||||
stream->sm_priority = 256 - priority;
|
stream->sm_priority = 256 - priority;
|
||||||
|
}
|
||||||
|
|
||||||
lsquic_send_ctl_invalidate_bpt_cache(stream->conn_pub->send_ctl);
|
lsquic_send_ctl_invalidate_bpt_cache(stream->conn_pub->send_ctl);
|
||||||
LSQ_DEBUG("set priority to %u", priority);
|
LSQ_DEBUG("set priority to %u", priority);
|
||||||
SM_HISTORY_APPEND(stream, SHE_SET_PRIO);
|
SM_HISTORY_APPEND(stream, SHE_SET_PRIO);
|
||||||
|
@ -4379,9 +4410,16 @@ maybe_send_priority_gquic (struct lsquic_stream *stream, unsigned priority)
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
send_priority_ietf (struct lsquic_stream *stream, unsigned priority)
|
send_priority_ietf (struct lsquic_stream *stream)
|
||||||
{
|
{
|
||||||
LSQ_WARN("%s: TODO", __func__); /* TODO */
|
struct lsquic_ext_http_prio ehp;
|
||||||
|
|
||||||
|
if (0 == lsquic_stream_get_http_prio(stream, &ehp)
|
||||||
|
&& 0 == lsquic_hcso_write_priority_update(
|
||||||
|
stream->conn_pub->u.ietf.hcso,
|
||||||
|
HQFT_PRIORITY_UPDATE_STREAM, stream->id, &ehp))
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4392,7 +4430,12 @@ lsquic_stream_set_priority (lsquic_stream_t *stream, unsigned priority)
|
||||||
if (0 == lsquic_stream_set_priority_internal(stream, priority))
|
if (0 == lsquic_stream_set_priority_internal(stream, priority))
|
||||||
{
|
{
|
||||||
if (stream->sm_bflags & SMBF_IETF)
|
if (stream->sm_bflags & SMBF_IETF)
|
||||||
return send_priority_ietf(stream, priority);
|
{
|
||||||
|
if (stream->sm_bflags & SMBF_HTTP_PRIO)
|
||||||
|
return send_priority_ietf(stream);
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
return maybe_send_priority_gquic(stream, priority);
|
return maybe_send_priority_gquic(stream, priority);
|
||||||
}
|
}
|
||||||
|
@ -4559,6 +4602,18 @@ update_type_hist_and_check (const struct lsquic_stream *stream,
|
||||||
case 9: /* HTTP/2 CONTINUATION */
|
case 9: /* HTTP/2 CONTINUATION */
|
||||||
/* [draft-ietf-quic-http-30], Section 7.2.8 */
|
/* [draft-ietf-quic-http-30], Section 7.2.8 */
|
||||||
return -1;
|
return -1;
|
||||||
|
case HQFT_PRIORITY_UPDATE_STREAM:
|
||||||
|
case HQFT_PRIORITY_UPDATE_PUSH:
|
||||||
|
if (stream->sm_bflags & SMBF_HTTP_PRIO)
|
||||||
|
/* If we know about Extensible HTTP Priorities, we should check
|
||||||
|
* that they do not arrive on any but the control stream:
|
||||||
|
*/
|
||||||
|
return -1;
|
||||||
|
else
|
||||||
|
/* On the other hand, if we do not support Priorities, treat it
|
||||||
|
* as an unknown frame:
|
||||||
|
*/
|
||||||
|
return 0;
|
||||||
default:
|
default:
|
||||||
/* Ignore unknown frames */
|
/* Ignore unknown frames */
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -5239,3 +5294,47 @@ lsquic_stream_verify_len (struct lsquic_stream *stream,
|
||||||
else
|
else
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
lsquic_stream_get_http_prio (struct lsquic_stream *stream,
|
||||||
|
struct lsquic_ext_http_prio *ehp)
|
||||||
|
{
|
||||||
|
if (stream->sm_bflags & SMBF_HTTP_PRIO)
|
||||||
|
{
|
||||||
|
ehp->urgency = MIN(stream->sm_priority, LSQUIC_MAX_HTTP_URGENCY);
|
||||||
|
ehp->incremental = !!(stream->sm_bflags & SMBF_INCREMENTAL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
lsquic_stream_set_http_prio (struct lsquic_stream *stream,
|
||||||
|
const struct lsquic_ext_http_prio *ehp)
|
||||||
|
{
|
||||||
|
if (stream->sm_bflags & SMBF_HTTP_PRIO)
|
||||||
|
{
|
||||||
|
if (ehp->urgency > LSQUIC_MAX_HTTP_URGENCY)
|
||||||
|
{
|
||||||
|
LSQ_INFO("%s: invalid urgency: %hhu", __func__, ehp->urgency);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
stream->sm_priority = ehp->urgency;
|
||||||
|
if (ehp->incremental)
|
||||||
|
stream->sm_bflags |= SMBF_INCREMENTAL;
|
||||||
|
else
|
||||||
|
stream->sm_bflags &= ~SMBF_INCREMENTAL;
|
||||||
|
stream->sm_bflags |= SMBF_HPRIO_SET;
|
||||||
|
LSQ_DEBUG("set urgency to %hhu, incremental to %hhd", ehp->urgency,
|
||||||
|
ehp->incremental);
|
||||||
|
if (!(stream->sm_bflags & SMBF_SERVER))
|
||||||
|
return send_priority_ietf(stream);
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
|
@ -81,7 +81,7 @@ struct stream_hq_frame
|
||||||
} shf_u;
|
} shf_u;
|
||||||
#define shf_frame_ptr shf_u.frame_ptr
|
#define shf_frame_ptr shf_u.frame_ptr
|
||||||
#define shf_frame_size shf_u.frame_size
|
#define shf_frame_size shf_u.frame_size
|
||||||
enum hq_frame_type shf_frame_type:8;
|
enum hq_frame_type shf_frame_type;
|
||||||
enum shf_flags {
|
enum shf_flags {
|
||||||
SHF_TWO_BYTES = 1 << 0, /* Use two byte to encode frame length */
|
SHF_TWO_BYTES = 1 << 0, /* Use two byte to encode frame length */
|
||||||
SHF_FIXED_SIZE = 1 << 1, /* Payload size guaranteed */
|
SHF_FIXED_SIZE = 1 << 1, /* Payload size guaranteed */
|
||||||
|
@ -185,7 +185,10 @@ enum stream_b_flags
|
||||||
SMBF_CONN_LIMITED = 1 << 7,
|
SMBF_CONN_LIMITED = 1 << 7,
|
||||||
SMBF_HEADERS = 1 << 8, /* Headers stream */
|
SMBF_HEADERS = 1 << 8, /* Headers stream */
|
||||||
SMBF_VERIFY_CL = 1 << 9, /* Verify content-length (stored in sm_cont_len) */
|
SMBF_VERIFY_CL = 1 << 9, /* Verify content-length (stored in sm_cont_len) */
|
||||||
#define N_SMBF_FLAGS 10
|
SMBF_HTTP_PRIO = 1 <<10, /* Extensible HTTP Priorities are used */
|
||||||
|
SMBF_INCREMENTAL = 1 <<11, /* Value of the "incremental" HTTP Priority parameter */
|
||||||
|
SMBF_HPRIO_SET = 1 <<12, /* Extensible HTTP Priorities have been set once */
|
||||||
|
#define N_SMBF_FLAGS 13
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -343,6 +346,9 @@ struct lsquic_stream
|
||||||
unsigned short sm_n_buffered; /* Amount of data in sm_buf */
|
unsigned short sm_n_buffered; /* Amount of data in sm_buf */
|
||||||
unsigned short sm_n_allocated; /* Size of sm_buf */
|
unsigned short sm_n_allocated; /* Size of sm_buf */
|
||||||
|
|
||||||
|
/* If SMBF_HTTP_PRIO is set, the priority is used to represent the
|
||||||
|
* Extensible Priority urgency, which is in the range [0, 7].
|
||||||
|
*/
|
||||||
unsigned char sm_priority; /* 0: high; 255: low */
|
unsigned char sm_priority; /* 0: high; 255: low */
|
||||||
unsigned char sm_enc_level;
|
unsigned char sm_enc_level;
|
||||||
enum {
|
enum {
|
||||||
|
@ -381,6 +387,7 @@ enum stream_ctor_flags
|
||||||
SCF_HTTP = SMBF_USE_HEADERS,
|
SCF_HTTP = SMBF_USE_HEADERS,
|
||||||
SCF_CRYPTO = SMBF_CRYPTO,
|
SCF_CRYPTO = SMBF_CRYPTO,
|
||||||
SCF_HEADERS = SMBF_HEADERS,
|
SCF_HEADERS = SMBF_HEADERS,
|
||||||
|
SCF_HTTP_PRIO = SMBF_HTTP_PRIO,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
236
src/liblsquic/lsquic_trechist.c
Normal file
236
src/liblsquic/lsquic_trechist.c
Normal file
|
@ -0,0 +1,236 @@
|
||||||
|
/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc. See LICENSE. */
|
||||||
|
#include <assert.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "lsquic_int_types.h"
|
||||||
|
#include "lsquic_trechist.h"
|
||||||
|
|
||||||
|
|
||||||
|
static unsigned
|
||||||
|
find_free_slot (uint32_t slots)
|
||||||
|
{
|
||||||
|
#if __GNUC__
|
||||||
|
return __builtin_ctz(~slots);
|
||||||
|
#else
|
||||||
|
unsigned n;
|
||||||
|
|
||||||
|
slots =~ slots;
|
||||||
|
n = 0;
|
||||||
|
|
||||||
|
if (0 == (slots & ((1ULL << 16) - 1))) { n += 16; slots >>= 16; }
|
||||||
|
if (0 == (slots & ((1ULL << 8) - 1))) { n += 8; slots >>= 8; }
|
||||||
|
if (0 == (slots & ((1ULL << 4) - 1))) { n += 4; slots >>= 4; }
|
||||||
|
if (0 == (slots & ((1ULL << 2) - 1))) { n += 2; slots >>= 2; }
|
||||||
|
if (0 == (slots & ((1ULL << 1) - 1))) { n += 1; slots >>= 1; }
|
||||||
|
return n;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Returns 0 on success, 1 if dup, -1 if out of elements */
|
||||||
|
int
|
||||||
|
lsquic_trechist_insert (trechist_mask_t *mask, struct trechist_elem *elems,
|
||||||
|
uint32_t packno)
|
||||||
|
{
|
||||||
|
struct trechist_elem *el, *prev;
|
||||||
|
unsigned idx;
|
||||||
|
|
||||||
|
if (*mask == 0)
|
||||||
|
{
|
||||||
|
elems[0].te_low = packno;
|
||||||
|
elems[0].te_count = 1;
|
||||||
|
elems[0].te_next = 0;
|
||||||
|
*mask |= 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
el = elems;
|
||||||
|
prev = NULL;
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
if (packno > TE_HIGH(el) + 1)
|
||||||
|
goto insert_before;
|
||||||
|
if (packno == el->te_low - 1)
|
||||||
|
{
|
||||||
|
if (el->te_count == UCHAR_MAX)
|
||||||
|
return -1;
|
||||||
|
--el->te_low;
|
||||||
|
++el->te_count;
|
||||||
|
if (el->te_next && el->te_low == TE_HIGH(&elems[el->te_next]) + 1)
|
||||||
|
{
|
||||||
|
*mask &= ~(1u << el->te_next);
|
||||||
|
el->te_count += elems[el->te_next].te_count;
|
||||||
|
el->te_low = elems[el->te_next].te_low;
|
||||||
|
el->te_next = elems[el->te_next].te_next;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (packno == TE_HIGH(el) + 1)
|
||||||
|
{
|
||||||
|
if (el->te_count == UCHAR_MAX)
|
||||||
|
return -1;
|
||||||
|
++el->te_count;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (packno >= el->te_low && packno <= TE_HIGH(el))
|
||||||
|
return 1; /* Dup */
|
||||||
|
if (!el->te_next)
|
||||||
|
break; /* insert tail */
|
||||||
|
prev = el;
|
||||||
|
el = &elems[el->te_next];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*mask == ((1u << TRECHIST_MAX_RANGES) - 1))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
idx = find_free_slot(*mask);
|
||||||
|
elems[idx].te_low = packno;
|
||||||
|
elems[idx].te_count = 1;
|
||||||
|
elems[idx].te_next = 0;
|
||||||
|
*mask |= 1u << idx;;
|
||||||
|
el->te_next = idx;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
insert_before:
|
||||||
|
|
||||||
|
if (*mask == ((1u << TRECHIST_MAX_RANGES) - 1))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
idx = find_free_slot(*mask);
|
||||||
|
*mask |= 1u << idx;;
|
||||||
|
if (el == elems)
|
||||||
|
{
|
||||||
|
elems[idx] = *el;
|
||||||
|
elems[0].te_low = packno;
|
||||||
|
elems[0].te_count = 1;
|
||||||
|
elems[0].te_next = idx;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assert(prev);
|
||||||
|
elems[idx].te_low = packno;
|
||||||
|
elems[idx].te_count = 1;
|
||||||
|
elems[idx].te_next = prev->te_next;
|
||||||
|
prev->te_next = idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
lsquic_trechist_iter (struct trechist_iter *iter, trechist_mask_t mask,
|
||||||
|
const struct trechist_elem *elems)
|
||||||
|
{
|
||||||
|
iter->mask = mask;
|
||||||
|
iter->elems = elems;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const struct lsquic_packno_range *
|
||||||
|
lsquic_trechist_first (void *iter_p)
|
||||||
|
{
|
||||||
|
struct trechist_iter *const iter = iter_p;
|
||||||
|
|
||||||
|
if (iter->mask == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
iter->next = iter->elems[0].te_next;
|
||||||
|
iter->range.low = iter->elems[0].te_low;
|
||||||
|
iter->range.high = TE_HIGH(&iter->elems[0]);
|
||||||
|
return &iter->range;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const struct lsquic_packno_range *
|
||||||
|
lsquic_trechist_next (void *iter_p)
|
||||||
|
{
|
||||||
|
struct trechist_iter *const iter = iter_p;
|
||||||
|
|
||||||
|
if (iter->next == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
iter->range.low = iter->elems[iter->next].te_low;
|
||||||
|
iter->range.high = TE_HIGH(&iter->elems[iter->next]);
|
||||||
|
iter->next = iter->elems[iter->next].te_next;
|
||||||
|
return &iter->range;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
lsquic_trechist_copy_ranges (trechist_mask_t *mask,
|
||||||
|
struct trechist_elem *elems, void *src_rechist,
|
||||||
|
const struct lsquic_packno_range * (*first) (void *),
|
||||||
|
const struct lsquic_packno_range * (*next) (void *))
|
||||||
|
{
|
||||||
|
const struct lsquic_packno_range *range;
|
||||||
|
struct trechist_elem *el;
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
for (el = NULL, i = 0, range = first(src_rechist);
|
||||||
|
i < TRECHIST_MAX_RANGES && range;
|
||||||
|
range = next(src_rechist), ++i)
|
||||||
|
{
|
||||||
|
/* This should never happen: */
|
||||||
|
assert(range->high - range->low + 1 <= UINT_MAX);
|
||||||
|
|
||||||
|
el = &elems[i];
|
||||||
|
el->te_low = range->low;
|
||||||
|
el->te_count = range->high - range->low + 1;
|
||||||
|
el->te_next = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!range && el)
|
||||||
|
{
|
||||||
|
el->te_next = 0;
|
||||||
|
*mask = (1u << i) - 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if (!el)
|
||||||
|
{
|
||||||
|
*mask = 0;
|
||||||
|
return 0; /* Must have been an empty */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
lsquic_trechist_contains (trechist_mask_t mask,
|
||||||
|
const struct trechist_elem *elems, uint32_t packno)
|
||||||
|
{
|
||||||
|
const struct trechist_elem *el;
|
||||||
|
if (mask == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
el = &elems[0];
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
if (packno > TE_HIGH(el))
|
||||||
|
return 0;
|
||||||
|
if (packno >= el->te_low)
|
||||||
|
return 1;
|
||||||
|
if (el->te_next)
|
||||||
|
el = &elems[el->te_next];
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
lsquic_trechist_max (trechist_mask_t mask, const struct trechist_elem *elems)
|
||||||
|
{
|
||||||
|
if (mask)
|
||||||
|
{
|
||||||
|
assert(mask & 1);
|
||||||
|
return TE_HIGH(&elems[0]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
74
src/liblsquic/lsquic_trechist.h
Normal file
74
src/liblsquic/lsquic_trechist.h
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc. See LICENSE. */
|
||||||
|
/*
|
||||||
|
* Tiny receive history. It is used in IETF mini connection, where we want
|
||||||
|
* to use as little memory as possible. This data structure is an array of
|
||||||
|
* packet ranges. Each packet range is six bytes. This is possible because
|
||||||
|
* initial packets must not be wider than four bytes.
|
||||||
|
*
|
||||||
|
* Another limitation of this history is that it never shrinks. (Although
|
||||||
|
* technically is is possible to implement.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LSQUIC_TRECHIST
|
||||||
|
#define LSQUIC_TRECHIST 1
|
||||||
|
|
||||||
|
struct lsquic_packno_range;
|
||||||
|
|
||||||
|
/* This value could be as large as 32, which is how many bits wide
|
||||||
|
* trechist_mask_t is. The other limit on the number of ranges is
|
||||||
|
* UCHAR_MAX, which is how many different values can fit into te_next.
|
||||||
|
*/
|
||||||
|
#define TRECHIST_MAX_RANGES 16
|
||||||
|
|
||||||
|
struct trechist_elem
|
||||||
|
{
|
||||||
|
uint32_t te_low;
|
||||||
|
unsigned char te_count;
|
||||||
|
unsigned char te_next; /* 0 means no next element */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define TE_HIGH(te_) ((te_)->te_low + (te_)->te_count - 1)
|
||||||
|
|
||||||
|
#define TRECHIST_SIZE (TRECHIST_MAX_RANGES * sizeof(struct trechist_elem))
|
||||||
|
|
||||||
|
/* There are two parts to this: the array of trechist_elem's and the bitmask
|
||||||
|
* that tracks which elements are used. The smallest range must always be at
|
||||||
|
* offset zero.
|
||||||
|
*/
|
||||||
|
typedef uint32_t trechist_mask_t;
|
||||||
|
|
||||||
|
int
|
||||||
|
lsquic_trechist_insert (trechist_mask_t *, struct trechist_elem *, uint32_t);
|
||||||
|
|
||||||
|
struct trechist_iter {
|
||||||
|
struct lsquic_packno_range range;
|
||||||
|
const struct trechist_elem *elems;
|
||||||
|
trechist_mask_t mask;
|
||||||
|
unsigned char next;
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
lsquic_trechist_iter (struct trechist_iter *iter, trechist_mask_t mask,
|
||||||
|
const struct trechist_elem *);
|
||||||
|
|
||||||
|
/* Don't modify history while iterating */
|
||||||
|
const struct lsquic_packno_range *
|
||||||
|
lsquic_trechist_first (void *iter);
|
||||||
|
|
||||||
|
const struct lsquic_packno_range *
|
||||||
|
lsquic_trechist_next (void *iter);
|
||||||
|
|
||||||
|
int
|
||||||
|
lsquic_trechist_copy_ranges (trechist_mask_t *mask /* This gets overwritten */,
|
||||||
|
struct trechist_elem *elems, void *src_rechist,
|
||||||
|
const struct lsquic_packno_range * (*first) (void *),
|
||||||
|
const struct lsquic_packno_range * (*next) (void *));
|
||||||
|
|
||||||
|
int
|
||||||
|
lsquic_trechist_contains (trechist_mask_t mask,
|
||||||
|
const struct trechist_elem *elems, uint32_t packno);
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
lsquic_trechist_max (trechist_mask_t mask, const struct trechist_elem *elems);
|
||||||
|
|
||||||
|
#endif
|
|
@ -46,6 +46,7 @@ SET(TESTS
|
||||||
goaway_gquic_be
|
goaway_gquic_be
|
||||||
h3_framing
|
h3_framing
|
||||||
hkdf
|
hkdf
|
||||||
|
hpi
|
||||||
lsquic_hash
|
lsquic_hash
|
||||||
packet_out
|
packet_out
|
||||||
packet_resize
|
packet_resize
|
||||||
|
@ -129,3 +130,6 @@ ADD_TEST(minmax test_minmax)
|
||||||
|
|
||||||
ADD_EXECUTABLE(test_rechist test_rechist.c ../src/liblsquic/lsquic_rechist.c)
|
ADD_EXECUTABLE(test_rechist test_rechist.c ../src/liblsquic/lsquic_rechist.c)
|
||||||
ADD_TEST(rechist test_rechist)
|
ADD_TEST(rechist test_rechist)
|
||||||
|
|
||||||
|
ADD_EXECUTABLE(test_trechist test_trechist.c ../src/liblsquic/lsquic_trechist.c)
|
||||||
|
ADD_TEST(trechist test_trechist)
|
||||||
|
|
|
@ -1151,6 +1151,17 @@ fuzz_guided_pwritev_testing (const char *input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static unsigned
|
||||||
|
count_hq_frames (const struct lsquic_stream *stream)
|
||||||
|
{
|
||||||
|
const struct stream_hq_frame *shf;
|
||||||
|
unsigned n_frames = 0;
|
||||||
|
STAILQ_FOREACH(shf, &stream->sm_hq_frames, shf_next)
|
||||||
|
++n_frames;
|
||||||
|
return n_frames;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
test_frame_header_split (unsigned n_packets, unsigned extra_sz,
|
test_frame_header_split (unsigned n_packets, unsigned extra_sz,
|
||||||
int add_one_more)
|
int add_one_more)
|
||||||
|
@ -1162,6 +1173,7 @@ test_frame_header_split (unsigned n_packets, unsigned extra_sz,
|
||||||
unsigned char *buf_in, *buf_out;
|
unsigned char *buf_in, *buf_out;
|
||||||
const unsigned wsize = 70;
|
const unsigned wsize = 70;
|
||||||
const size_t buf_in_sz = wsize, buf_out_sz = 0x500000;
|
const size_t buf_in_sz = wsize, buf_out_sz = 0x500000;
|
||||||
|
unsigned n_frames;
|
||||||
|
|
||||||
struct lsxpack_header header = { XHDR(":method", "GET") };
|
struct lsxpack_header header = { XHDR(":method", "GET") };
|
||||||
struct lsquic_http_headers headers = { 1, &header, };
|
struct lsquic_http_headers headers = { 1, &header, };
|
||||||
|
@ -1210,7 +1222,12 @@ test_frame_header_split (unsigned n_packets, unsigned extra_sz,
|
||||||
|
|
||||||
const ssize_t w = lsquic_stream_write(stream, buf_in, buf_in_sz);
|
const ssize_t w = lsquic_stream_write(stream, buf_in, buf_in_sz);
|
||||||
assert(w >= 0 && (size_t) w == buf_in_sz);
|
assert(w >= 0 && (size_t) w == buf_in_sz);
|
||||||
|
n_frames = count_hq_frames(stream);
|
||||||
|
assert(n_frames == 1 + (w > 0));
|
||||||
|
|
||||||
lsquic_stream_flush(stream);
|
lsquic_stream_flush(stream);
|
||||||
|
n_frames = count_hq_frames(stream);
|
||||||
|
assert(n_frames == !!stream->sm_n_buffered);
|
||||||
|
|
||||||
if (add_one_more)
|
if (add_one_more)
|
||||||
{
|
{
|
||||||
|
|
|
@ -18,6 +18,11 @@ struct test
|
||||||
{
|
{
|
||||||
int lineno;
|
int lineno;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
TEST_NO_FLAGS = 0,
|
||||||
|
TEST_NUL_OUT_LEST_FULL = 1 << 0,
|
||||||
|
} flags;
|
||||||
|
|
||||||
unsigned char input[0x100];
|
unsigned char input[0x100];
|
||||||
size_t input_sz;
|
size_t input_sz;
|
||||||
|
|
||||||
|
@ -30,6 +35,7 @@ static const struct test tests[] =
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
__LINE__,
|
__LINE__,
|
||||||
|
TEST_NO_FLAGS,
|
||||||
{
|
{
|
||||||
0x03,
|
0x03,
|
||||||
0x04,
|
0x04,
|
||||||
|
@ -42,6 +48,7 @@ static const struct test tests[] =
|
||||||
|
|
||||||
{
|
{
|
||||||
__LINE__,
|
__LINE__,
|
||||||
|
TEST_NO_FLAGS,
|
||||||
{
|
{
|
||||||
HQFT_MAX_PUSH_ID,
|
HQFT_MAX_PUSH_ID,
|
||||||
0x02,
|
0x02,
|
||||||
|
@ -54,6 +61,7 @@ static const struct test tests[] =
|
||||||
|
|
||||||
{
|
{
|
||||||
__LINE__,
|
__LINE__,
|
||||||
|
TEST_NO_FLAGS,
|
||||||
{
|
{
|
||||||
HQFT_SETTINGS,
|
HQFT_SETTINGS,
|
||||||
0x00,
|
0x00,
|
||||||
|
@ -65,6 +73,7 @@ static const struct test tests[] =
|
||||||
|
|
||||||
{ /* Frame contents do not match frame length */
|
{ /* Frame contents do not match frame length */
|
||||||
__LINE__,
|
__LINE__,
|
||||||
|
TEST_NO_FLAGS,
|
||||||
{
|
{
|
||||||
HQFT_MAX_PUSH_ID,
|
HQFT_MAX_PUSH_ID,
|
||||||
0x03,
|
0x03,
|
||||||
|
@ -77,6 +86,7 @@ static const struct test tests[] =
|
||||||
|
|
||||||
{
|
{
|
||||||
__LINE__,
|
__LINE__,
|
||||||
|
TEST_NO_FLAGS,
|
||||||
{
|
{
|
||||||
HQFT_SETTINGS,
|
HQFT_SETTINGS,
|
||||||
13,
|
13,
|
||||||
|
@ -93,6 +103,58 @@ static const struct test tests[] =
|
||||||
,
|
,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
__LINE__,
|
||||||
|
TEST_NO_FLAGS,
|
||||||
|
{
|
||||||
|
0x80, 0x0F, 0x07, 0x00, /* HQFT_PRIORITY_UPDATE_STREAM */
|
||||||
|
7,
|
||||||
|
0x52, 0x34,
|
||||||
|
0x41, 0x42, 0x43, 0x44, 0x45, /* ABCDE */
|
||||||
|
HQFT_MAX_PUSH_ID,
|
||||||
|
0x02,
|
||||||
|
0x41, 0x23,
|
||||||
|
},
|
||||||
|
16,
|
||||||
|
0,
|
||||||
|
"on_priority_update: stream=0x1234, [ABCDE]\n"
|
||||||
|
"on_max_push_id: 291\n"
|
||||||
|
,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
__LINE__,
|
||||||
|
TEST_NO_FLAGS,
|
||||||
|
{
|
||||||
|
0x80, 0x0F, 0x07, 0x01, /* HQFT_PRIORITY_UPDATE_PUSH */
|
||||||
|
6,
|
||||||
|
0x08,
|
||||||
|
0x50, 0x51, 0x52, 0x53, 0x54, /* PQRST */
|
||||||
|
},
|
||||||
|
11,
|
||||||
|
0,
|
||||||
|
"on_priority_update: push=0x8, [PQRST]\n"
|
||||||
|
,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
__LINE__,
|
||||||
|
TEST_NUL_OUT_LEST_FULL,
|
||||||
|
{
|
||||||
|
0x80, 0x0F, 0x07, 0x01, /* HQFT_PRIORITY_UPDATE_PUSH */
|
||||||
|
21,
|
||||||
|
0x08,
|
||||||
|
0x50, 0x51, 0x52, 0x53, 0x54, /* PQRST */
|
||||||
|
0x50, 0x51, 0x52, 0x53, 0x54, /* PQRST */
|
||||||
|
0x50, 0x51, 0x52, 0x53, 0x54, /* PQRST */
|
||||||
|
0x50, 0x51, 0x52, 0x53, 0x54, /* PQRST */
|
||||||
|
},
|
||||||
|
26,
|
||||||
|
0,
|
||||||
|
"on_priority_update: push=0x8, [PQRSTPQRSTPQRSTPQRST]\n"
|
||||||
|
,
|
||||||
|
},
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -132,6 +194,24 @@ on_unexpected_frame (void *ctx, uint64_t frame_type)
|
||||||
fprintf(ctx, "%s: %"PRIu64"\n", __func__, frame_type);
|
fprintf(ctx, "%s: %"PRIu64"\n", __func__, frame_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_priority_update (void *ctx, enum hq_frame_type frame_type,
|
||||||
|
/* PFV: Priority Field Value */
|
||||||
|
uint64_t id, const char *pfv, size_t pfv_sz)
|
||||||
|
{
|
||||||
|
const char *type;
|
||||||
|
|
||||||
|
switch (frame_type)
|
||||||
|
{
|
||||||
|
case HQFT_PRIORITY_UPDATE_STREAM: type = "stream"; break;
|
||||||
|
case HQFT_PRIORITY_UPDATE_PUSH: type = "push"; break;
|
||||||
|
default: assert(0); return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(ctx, "%s: %s=0x%"PRIX64", [%.*s]\n", __func__, type, id,
|
||||||
|
(int) pfv_sz, pfv);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct hcsi_callbacks callbacks =
|
static const struct hcsi_callbacks callbacks =
|
||||||
{
|
{
|
||||||
.on_cancel_push = on_cancel_push,
|
.on_cancel_push = on_cancel_push,
|
||||||
|
@ -140,6 +220,7 @@ static const struct hcsi_callbacks callbacks =
|
||||||
.on_setting = on_setting,
|
.on_setting = on_setting,
|
||||||
.on_goaway = on_goaway,
|
.on_goaway = on_goaway,
|
||||||
.on_unexpected_frame = on_unexpected_frame,
|
.on_unexpected_frame = on_unexpected_frame,
|
||||||
|
.on_priority_update = on_priority_update,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -167,7 +248,7 @@ run_test (const struct test *test)
|
||||||
struct lsquic_conn lconn = LSCONN_INITIALIZER_CIDLEN(lconn, 0);
|
struct lsquic_conn lconn = LSCONN_INITIALIZER_CIDLEN(lconn, 0);
|
||||||
lconn.cn_if = &conn_iface;
|
lconn.cn_if = &conn_iface;
|
||||||
|
|
||||||
for (read_sz = 1; read_sz < test->input_sz; ++read_sz)
|
for (read_sz = 1; read_sz <= test->input_sz; ++read_sz)
|
||||||
{
|
{
|
||||||
out_f = open_memstream(&output, &out_sz);
|
out_f = open_memstream(&output, &out_sz);
|
||||||
lsquic_hcsi_reader_init(&reader, &lconn, &callbacks, out_f);
|
lsquic_hcsi_reader_init(&reader, &lconn, &callbacks, out_f);
|
||||||
|
@ -188,6 +269,10 @@ run_test (const struct test *test)
|
||||||
assert(s == test->retval);
|
assert(s == test->retval);
|
||||||
|
|
||||||
fclose(out_f);
|
fclose(out_f);
|
||||||
|
if (test->retval == 0 && read_sz < test->input_sz
|
||||||
|
&& (test->flags & TEST_NUL_OUT_LEST_FULL))
|
||||||
|
assert(0 == strcmp(output, ""));
|
||||||
|
else
|
||||||
assert(0 == strcmp(test->output, output));
|
assert(0 == strcmp(test->output, output));
|
||||||
free(output);
|
free(output);
|
||||||
}
|
}
|
||||||
|
@ -204,7 +289,7 @@ main (void)
|
||||||
|
|
||||||
memset(&coalesced_test, 0, sizeof(coalesced_test));
|
memset(&coalesced_test, 0, sizeof(coalesced_test));
|
||||||
for (test = tests; test < tests + sizeof(tests) / sizeof(tests[0]); ++test)
|
for (test = tests; test < tests + sizeof(tests) / sizeof(tests[0]); ++test)
|
||||||
if (test->retval == 0)
|
if (test->retval == 0 && !(test->flags & TEST_NUL_OUT_LEST_FULL))
|
||||||
{
|
{
|
||||||
memcpy(coalesced_test.input + coalesced_test.input_sz,
|
memcpy(coalesced_test.input + coalesced_test.input_sz,
|
||||||
test->input, test->input_sz);
|
test->input, test->input_sz);
|
||||||
|
|
351
tests/test_hpi.c
Normal file
351
tests/test_hpi.c
Normal file
|
@ -0,0 +1,351 @@
|
||||||
|
/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc. See LICENSE. */
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/queue.h>
|
||||||
|
#ifndef WIN32
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "lsquic.h"
|
||||||
|
|
||||||
|
#include "lsquic_int_types.h"
|
||||||
|
#include "lsquic_packet_common.h"
|
||||||
|
#include "lsquic_packet_in.h"
|
||||||
|
#include "lsquic_conn_flow.h"
|
||||||
|
#include "lsquic_sfcw.h"
|
||||||
|
#include "lsquic_varint.h"
|
||||||
|
#include "lsquic_hq.h"
|
||||||
|
#include "lsquic_hash.h"
|
||||||
|
#include "lsquic_conn.h"
|
||||||
|
#include "lsquic_stream.h"
|
||||||
|
#include "lsquic_types.h"
|
||||||
|
#include "lsquic_rtt.h"
|
||||||
|
#include "lsquic_conn_flow.h"
|
||||||
|
#include "lsquic_conn_public.h"
|
||||||
|
#include "lsquic_mm.h"
|
||||||
|
#include "lsquic_min_heap.h"
|
||||||
|
#include "lsquic_hpi.h"
|
||||||
|
#include "lsquic_logger.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DSL:
|
||||||
|
*
|
||||||
|
* S\d+:\d+:\d+ Create stream and insert it into list. The three numbers
|
||||||
|
* are stream ID, priority, and incremental boolean flag.
|
||||||
|
* Priority can be negative, which indicates a critical
|
||||||
|
* stream. Otherwise, the priorirty should be in the range
|
||||||
|
* [0, MAX_HTTP_PRIORITY]. The incremental flag is ignored
|
||||||
|
* for critical streams.
|
||||||
|
*
|
||||||
|
* F\d+ Use filter identified by the number. For "F" to take
|
||||||
|
* effect, it should be called before "I".
|
||||||
|
*
|
||||||
|
* I Initialize the iterator.
|
||||||
|
*
|
||||||
|
* D[hH] Call drop high (h) or drop non-high (H).
|
||||||
|
*
|
||||||
|
* N\d+ Call "next" and verify that the stream ID is this number.
|
||||||
|
* If "next" returns NULL, the test fails.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const struct test_spec {
|
||||||
|
int lineno;
|
||||||
|
const char *prog;
|
||||||
|
} test_specs[] = {
|
||||||
|
|
||||||
|
{ __LINE__,
|
||||||
|
"S0:3:0;"
|
||||||
|
"I;"
|
||||||
|
"N0;"
|
||||||
|
},
|
||||||
|
|
||||||
|
{ __LINE__,
|
||||||
|
/* Insert non-incremental streams with same priority, check that
|
||||||
|
* they come back out in the order of stream IDs.
|
||||||
|
*/
|
||||||
|
"S1:3:0;" "S0:3:0;" "S2:3:0;"
|
||||||
|
"I;"
|
||||||
|
"N0;" "N1;" "N2;"
|
||||||
|
},
|
||||||
|
|
||||||
|
{ __LINE__,
|
||||||
|
/* Insert incremental streams with same priority, check that they
|
||||||
|
* come back out in the same order.
|
||||||
|
*/
|
||||||
|
"S1:3:1;" "S0:3:1;" "S2:3:1;"
|
||||||
|
"I;"
|
||||||
|
"N1;" "N0;" "N2;"
|
||||||
|
},
|
||||||
|
|
||||||
|
{ __LINE__,
|
||||||
|
/* Insert incremental streams with same priority, filter out out odd
|
||||||
|
* IDs, check that they come back out in the same order and without
|
||||||
|
* the odd stream ID"
|
||||||
|
*/
|
||||||
|
"S1:3:1;" "S0:3:1;" "S2:3:1;"
|
||||||
|
"F;"
|
||||||
|
"I;"
|
||||||
|
"N0;" "N2;"
|
||||||
|
},
|
||||||
|
|
||||||
|
{ __LINE__,
|
||||||
|
/* Insert incremental and non-incremental streams with same priority.
|
||||||
|
* Check that non-incrementals are returned first.
|
||||||
|
*/
|
||||||
|
"S1:3:1;" "S0:3:1;" "S2:3:1;"
|
||||||
|
"S6:3:0;" "S10:3:0;" "S3:3:0;"
|
||||||
|
"I;"
|
||||||
|
"N3;N6;N10;"
|
||||||
|
"N1;N0;N2;"
|
||||||
|
},
|
||||||
|
|
||||||
|
{ __LINE__,
|
||||||
|
/* Drop high with same priority: nothing should be dropped */
|
||||||
|
"S1:3:1;" "S0:3:1;" "S2:3:1;"
|
||||||
|
"I;"
|
||||||
|
"Dh;"
|
||||||
|
"N1;" "N0;" "N2;"
|
||||||
|
},
|
||||||
|
|
||||||
|
{ __LINE__,
|
||||||
|
/* Drop non-high with same priority: nothing should be dropped */
|
||||||
|
"S1:3:1;" "S0:3:1;" "S2:3:1;"
|
||||||
|
"I;"
|
||||||
|
"DH;"
|
||||||
|
"N1;" "N0;" "N2;"
|
||||||
|
},
|
||||||
|
|
||||||
|
{ __LINE__,
|
||||||
|
/* Drop high with same priority: drop non-incrementals */
|
||||||
|
"S1:3:1;" "S0:3:1;" "S2:3:1;"
|
||||||
|
"S6:3:0;" "S10:3:0;" "S3:3:0;"
|
||||||
|
"I;"
|
||||||
|
"Dh;"
|
||||||
|
"N1;" "N0;" "N2;"
|
||||||
|
},
|
||||||
|
|
||||||
|
{ __LINE__,
|
||||||
|
/* Drop non-high with same priority: drop incrementals */
|
||||||
|
"S1:3:1;" "S0:3:1;" "S2:3:1;"
|
||||||
|
"S6:3:0;" "S10:3:0;" "S3:3:0;"
|
||||||
|
"I;"
|
||||||
|
"DH;"
|
||||||
|
"N3;N6;N10;"
|
||||||
|
},
|
||||||
|
|
||||||
|
{ __LINE__,
|
||||||
|
/* Insert streams with different priorities */
|
||||||
|
"S1:1:1;" "S2:2:1;" "S3:3:1;"
|
||||||
|
"S6:6:0;" "S5:5:0;" "S4:4:0;"
|
||||||
|
"I;"
|
||||||
|
"N1;N2;N3;N4;N5;N6;"
|
||||||
|
},
|
||||||
|
|
||||||
|
{ __LINE__,
|
||||||
|
/* Insert regular and critical streams */
|
||||||
|
"S1:1:1;" "S2:2:1;" "S3333:-1:1;"
|
||||||
|
"S6:6:0;" "S2222:-1:0;" "S4:4:0;"
|
||||||
|
"I;"
|
||||||
|
"N3333;N2222;N1;N2;N4;N6;"
|
||||||
|
},
|
||||||
|
|
||||||
|
{ __LINE__,
|
||||||
|
/* Insert regular and critical streams; drop high */
|
||||||
|
"S1:1:1;" "S2:2:1;" "S3333:-1:1;"
|
||||||
|
"S6:6:0;" "S2222:-1:0;" "S4:4:0;"
|
||||||
|
"I;"
|
||||||
|
"Dh;"
|
||||||
|
"N1;N2;N4;N6;"
|
||||||
|
},
|
||||||
|
|
||||||
|
{ __LINE__,
|
||||||
|
/* Insert regular and critical streams; drop non-high */
|
||||||
|
"S1:1:1;" "S2:2:1;" "S3333:-1:1;"
|
||||||
|
"S6:6:0;" "S2222:-1:0;" "S4:4:0;"
|
||||||
|
"I;"
|
||||||
|
"DH;"
|
||||||
|
"N3333;N2222;"
|
||||||
|
},
|
||||||
|
|
||||||
|
{ __LINE__,
|
||||||
|
/* Insert streams with different priorities, non-incremental,
|
||||||
|
* several per bucket.
|
||||||
|
*/
|
||||||
|
"S1:1:0;" "S4:2:0;" "S3:2:0;"
|
||||||
|
"S6:1:0;" "S5:1:0;" "S2:2:0;"
|
||||||
|
"I;"
|
||||||
|
"N1;N5;N6;N2;N3;N4;"
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Sharing the same HPI tests safety of reusing the same iterator object
|
||||||
|
* (no need to deinitialize it).
|
||||||
|
*/
|
||||||
|
static struct http_prio_iter hpi;
|
||||||
|
|
||||||
|
static struct lsquic_conn lconn = LSCONN_INITIALIZER_CIDLEN(lconn, 0);
|
||||||
|
|
||||||
|
static struct lsquic_conn_public conn_pub = { .lconn = &lconn, };
|
||||||
|
|
||||||
|
|
||||||
|
static struct lsquic_stream *
|
||||||
|
new_stream (lsquic_stream_id_t stream_id, int priority, int incr)
|
||||||
|
{
|
||||||
|
struct lsquic_stream *stream = calloc(1, sizeof(*stream));
|
||||||
|
stream->id = stream_id;
|
||||||
|
if (priority >= 0)
|
||||||
|
{
|
||||||
|
stream->sm_priority = priority;
|
||||||
|
if (incr)
|
||||||
|
stream->sm_bflags |= SMBF_INCREMENTAL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
stream->sm_bflags |= SMBF_CRITICAL;
|
||||||
|
/* Critical streams are never incremental */
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define MAGIC 0x12312312U
|
||||||
|
|
||||||
|
struct my_filter_ctx
|
||||||
|
{
|
||||||
|
unsigned magic;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
filter_out_odd_stream_ids (void *ctx, struct lsquic_stream *stream)
|
||||||
|
{
|
||||||
|
struct my_filter_ctx *fctx = ctx;
|
||||||
|
assert(fctx->magic == MAGIC);
|
||||||
|
return 0 == (stream->id & 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int (*const filters[])(void *, struct lsquic_stream *) = {
|
||||||
|
filter_out_odd_stream_ids,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Just pick one (as long as it's not next_prio_stream) */
|
||||||
|
#define next_field next_write_stream
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
run_test (const struct test_spec *spec)
|
||||||
|
{
|
||||||
|
struct lsquic_streams_tailq streams;
|
||||||
|
struct lsquic_stream *stream;
|
||||||
|
lsquic_stream_id_t stream_id;
|
||||||
|
long incr, priority, tmp;
|
||||||
|
const char *pc;
|
||||||
|
char cmd;
|
||||||
|
char name[20];
|
||||||
|
struct my_filter_ctx filter_ctx = { MAGIC };
|
||||||
|
int (*filter_cb)(void *, struct lsquic_stream *) = NULL;
|
||||||
|
int first_called = 0;
|
||||||
|
struct lsquic_mm mm;
|
||||||
|
|
||||||
|
lsquic_mm_init(&mm);
|
||||||
|
conn_pub.mm = &mm;
|
||||||
|
TAILQ_INIT(&streams);
|
||||||
|
snprintf(name, sizeof(name), "line-%d", spec->lineno);
|
||||||
|
|
||||||
|
for (pc = spec->prog; *pc; ++pc)
|
||||||
|
{
|
||||||
|
cmd = *pc++;
|
||||||
|
switch (cmd)
|
||||||
|
{
|
||||||
|
case 'S':
|
||||||
|
stream_id = strtol(pc, (char **) &pc, 10);
|
||||||
|
assert(':' == *pc);
|
||||||
|
priority = strtol(pc + 1, (char **) &pc, 10);
|
||||||
|
assert(':' == *pc);
|
||||||
|
incr = strtol(pc + 1, (char **) &pc, 10);
|
||||||
|
stream = new_stream(stream_id, priority, incr);
|
||||||
|
TAILQ_INSERT_TAIL(&streams, stream, next_field);
|
||||||
|
break;
|
||||||
|
case 'I':
|
||||||
|
lsquic_hpi_init(&hpi, TAILQ_FIRST(&streams),
|
||||||
|
TAILQ_LAST(&streams, lsquic_streams_tailq),
|
||||||
|
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_field),
|
||||||
|
&conn_pub, name, filter_cb, &filter_ctx);
|
||||||
|
break;
|
||||||
|
case 'N':
|
||||||
|
stream_id = strtol(pc, (char **) &pc, 10);
|
||||||
|
if (first_called)
|
||||||
|
stream = lsquic_hpi_next(&hpi);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stream = lsquic_hpi_first(&hpi);
|
||||||
|
first_called = 1;
|
||||||
|
}
|
||||||
|
assert(stream);
|
||||||
|
assert(stream->id == stream_id);
|
||||||
|
break;
|
||||||
|
case 'F':
|
||||||
|
tmp = strtol(pc, (char **) &pc, 10);
|
||||||
|
assert(tmp >= 0
|
||||||
|
&& (size_t) tmp < sizeof(filters) / sizeof(filters[0]));
|
||||||
|
filter_cb = filters[tmp];
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
switch (*pc++)
|
||||||
|
{
|
||||||
|
case 'h':
|
||||||
|
lsquic_hpi_drop_high(&hpi);
|
||||||
|
break;
|
||||||
|
case 'H':
|
||||||
|
lsquic_hpi_drop_non_high(&hpi);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
assert(*pc == ';');
|
||||||
|
}
|
||||||
|
lsquic_hpi_cleanup(&hpi);
|
||||||
|
|
||||||
|
while (stream = TAILQ_FIRST(&streams), stream != NULL)
|
||||||
|
{
|
||||||
|
TAILQ_REMOVE(&streams, stream, next_field);
|
||||||
|
free(stream);
|
||||||
|
}
|
||||||
|
lsquic_mm_cleanup(&mm);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc, char **argv)
|
||||||
|
{
|
||||||
|
unsigned n;
|
||||||
|
|
||||||
|
lsquic_log_to_fstream(stderr, LLTS_NONE);
|
||||||
|
lsq_log_levels[LSQLM_HPI] = LSQ_LOG_DEBUG;
|
||||||
|
|
||||||
|
for (n = 0; n < sizeof(test_specs) / sizeof(test_specs[0]); ++n)
|
||||||
|
run_test(&test_specs[n]);
|
||||||
|
|
||||||
|
lsquic_hpi_set_heap_test(LSQUIC_HPI_HEAP_TEST_STACK_OK);
|
||||||
|
for (n = 0; n < sizeof(test_specs) / sizeof(test_specs[0]); ++n)
|
||||||
|
run_test(&test_specs[n]);
|
||||||
|
|
||||||
|
lsquic_hpi_set_heap_test(LSQUIC_HPI_HEAP_TEST_4K_OK);
|
||||||
|
for (n = 0; n < sizeof(test_specs) / sizeof(test_specs[0]); ++n)
|
||||||
|
run_test(&test_specs[n]);
|
||||||
|
|
||||||
|
lsquic_hpi_set_heap_test(0);
|
||||||
|
for (n = 0; n < sizeof(test_specs) / sizeof(test_specs[0]); ++n)
|
||||||
|
run_test(&test_specs[n]);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -22,6 +22,8 @@
|
||||||
#include "lsquic_conn.h"
|
#include "lsquic_conn.h"
|
||||||
#include "lsquic_stream.h"
|
#include "lsquic_stream.h"
|
||||||
#include "lsquic_types.h"
|
#include "lsquic_types.h"
|
||||||
|
#include "lsquic_rtt.h"
|
||||||
|
#include "lsquic_conn_public.h"
|
||||||
#include "lsquic_spi.h"
|
#include "lsquic_spi.h"
|
||||||
#include "lsquic_logger.h"
|
#include "lsquic_logger.h"
|
||||||
|
|
||||||
|
@ -33,6 +35,8 @@ static struct stream_prio_iter spi;
|
||||||
|
|
||||||
static struct lsquic_conn lconn = LSCONN_INITIALIZER_CIDLEN(lconn, 0);
|
static struct lsquic_conn lconn = LSCONN_INITIALIZER_CIDLEN(lconn, 0);
|
||||||
|
|
||||||
|
static struct lsquic_conn_public conn_pub = { .lconn = &lconn, };
|
||||||
|
|
||||||
|
|
||||||
static lsquic_stream_t *
|
static lsquic_stream_t *
|
||||||
new_stream (unsigned priority)
|
new_stream (unsigned priority)
|
||||||
|
@ -73,7 +77,7 @@ test_same_priority (unsigned priority)
|
||||||
lsquic_spi_init(&spi, TAILQ_FIRST(&streams),
|
lsquic_spi_init(&spi, TAILQ_FIRST(&streams),
|
||||||
TAILQ_LAST(&streams, lsquic_streams_tailq),
|
TAILQ_LAST(&streams, lsquic_streams_tailq),
|
||||||
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_write_stream),
|
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_write_stream),
|
||||||
&lconn, __func__, NULL, NULL);
|
&conn_pub, __func__, NULL, NULL);
|
||||||
|
|
||||||
stream = lsquic_spi_first(&spi);
|
stream = lsquic_spi_first(&spi);
|
||||||
assert(stream == stream_arr[0]);
|
assert(stream == stream_arr[0]);
|
||||||
|
@ -89,7 +93,7 @@ test_same_priority (unsigned priority)
|
||||||
/* Test reinitialization: */
|
/* Test reinitialization: */
|
||||||
lsquic_spi_init(&spi, stream_arr[0], stream_arr[1],
|
lsquic_spi_init(&spi, stream_arr[0], stream_arr[1],
|
||||||
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_write_stream),
|
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_write_stream),
|
||||||
&lconn, __func__, NULL, NULL);
|
&conn_pub, __func__, NULL, NULL);
|
||||||
stream = lsquic_spi_first(&spi);
|
stream = lsquic_spi_first(&spi);
|
||||||
assert(stream == stream_arr[0]);
|
assert(stream == stream_arr[0]);
|
||||||
stream = lsquic_spi_next(&spi);
|
stream = lsquic_spi_next(&spi);
|
||||||
|
@ -121,7 +125,7 @@ test_different_priorities (int *priority)
|
||||||
lsquic_spi_init(&spi, TAILQ_FIRST(&streams),
|
lsquic_spi_init(&spi, TAILQ_FIRST(&streams),
|
||||||
TAILQ_LAST(&streams, lsquic_streams_tailq),
|
TAILQ_LAST(&streams, lsquic_streams_tailq),
|
||||||
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_send_stream),
|
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_send_stream),
|
||||||
&lconn, __func__, NULL, NULL);
|
&conn_pub, __func__, NULL, NULL);
|
||||||
for (prev_prio = -1, count = 0, stream = lsquic_spi_first(&spi); stream;
|
for (prev_prio = -1, count = 0, stream = lsquic_spi_first(&spi); stream;
|
||||||
stream = lsquic_spi_next(&spi), ++count)
|
stream = lsquic_spi_next(&spi), ++count)
|
||||||
{
|
{
|
||||||
|
@ -214,7 +218,7 @@ test_drop (const struct drop_test *test)
|
||||||
lsquic_spi_init(&spi, TAILQ_FIRST(&streams),
|
lsquic_spi_init(&spi, TAILQ_FIRST(&streams),
|
||||||
TAILQ_LAST(&streams, lsquic_streams_tailq),
|
TAILQ_LAST(&streams, lsquic_streams_tailq),
|
||||||
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_write_stream),
|
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_write_stream),
|
||||||
&lconn, __func__, NULL, NULL);
|
&conn_pub, __func__, NULL, NULL);
|
||||||
|
|
||||||
if (drop_high)
|
if (drop_high)
|
||||||
lsquic_spi_drop_high(&spi);
|
lsquic_spi_drop_high(&spi);
|
||||||
|
@ -275,7 +279,7 @@ test_different_priorities_filter_odd (int *priority)
|
||||||
lsquic_spi_init(&spi, TAILQ_FIRST(&streams),
|
lsquic_spi_init(&spi, TAILQ_FIRST(&streams),
|
||||||
TAILQ_LAST(&streams, lsquic_streams_tailq),
|
TAILQ_LAST(&streams, lsquic_streams_tailq),
|
||||||
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_send_stream),
|
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_send_stream),
|
||||||
&lconn, __func__, filter_out_odd_priorities, &my_filter_ctx);
|
&conn_pub, __func__, filter_out_odd_priorities, &my_filter_ctx);
|
||||||
|
|
||||||
for (prev_prio = -1, count = 0, stream = lsquic_spi_first(&spi); stream;
|
for (prev_prio = -1, count = 0, stream = lsquic_spi_first(&spi); stream;
|
||||||
stream = lsquic_spi_next(&spi), ++count)
|
stream = lsquic_spi_next(&spi), ++count)
|
||||||
|
|
362
tests/test_trechist.c
Normal file
362
tests/test_trechist.c
Normal file
|
@ -0,0 +1,362 @@
|
||||||
|
/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc. See LICENSE. */
|
||||||
|
/* Tests based on rechist tests */
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
#include "vc_compat.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "lsquic_int_types.h"
|
||||||
|
#include "lsquic_trechist.h"
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_clone (trechist_mask_t src_mask, struct trechist_elem *src_elems)
|
||||||
|
{
|
||||||
|
trechist_mask_t hist_mask;
|
||||||
|
struct trechist_elem *hist_elems;
|
||||||
|
const struct lsquic_packno_range *ranges[2];
|
||||||
|
struct trechist_iter iters[2];
|
||||||
|
int s;
|
||||||
|
|
||||||
|
hist_elems = malloc(sizeof(hist_elems[0]) * TRECHIST_MAX_RANGES);
|
||||||
|
|
||||||
|
lsquic_trechist_iter(&iters[0], src_mask, src_elems);
|
||||||
|
s = lsquic_trechist_copy_ranges(&hist_mask, hist_elems, &iters[0],
|
||||||
|
lsquic_trechist_first, lsquic_trechist_next);
|
||||||
|
assert(s == 0);
|
||||||
|
|
||||||
|
lsquic_trechist_iter(&iters[0], src_mask, src_elems);
|
||||||
|
lsquic_trechist_iter(&iters[1], hist_mask, hist_elems);
|
||||||
|
|
||||||
|
for (ranges[0] = lsquic_trechist_first(&iters[0]),
|
||||||
|
ranges[1] = lsquic_trechist_first(&iters[1]);
|
||||||
|
|
||||||
|
ranges[0] && ranges[1];
|
||||||
|
|
||||||
|
ranges[0] = lsquic_trechist_next(&iters[0]),
|
||||||
|
ranges[1] = lsquic_trechist_next(&iters[1]))
|
||||||
|
{
|
||||||
|
assert(ranges[0]->low == ranges[1]->low);
|
||||||
|
assert(ranges[0]->high == ranges[1]->high);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(!ranges[0] && !ranges[1]);
|
||||||
|
|
||||||
|
free(hist_elems);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
test4 (void)
|
||||||
|
{
|
||||||
|
trechist_mask_t hist_mask;
|
||||||
|
struct trechist_elem *hist_elems;
|
||||||
|
const struct lsquic_packno_range *range;
|
||||||
|
struct trechist_iter iter;
|
||||||
|
lsquic_packno_t packno;
|
||||||
|
|
||||||
|
hist_elems = malloc(sizeof(hist_elems[0]) * TRECHIST_MAX_RANGES);
|
||||||
|
hist_mask = 0;
|
||||||
|
test_clone(hist_mask, hist_elems);
|
||||||
|
|
||||||
|
for (packno = 11917; packno <= 11941; ++packno)
|
||||||
|
lsquic_trechist_insert(&hist_mask, hist_elems, packno);
|
||||||
|
for (packno = 11946; packno <= 11994; ++packno)
|
||||||
|
lsquic_trechist_insert(&hist_mask, hist_elems, packno);
|
||||||
|
|
||||||
|
test_clone(hist_mask, hist_elems);
|
||||||
|
lsquic_trechist_iter(&iter, hist_mask, hist_elems);
|
||||||
|
range = lsquic_trechist_first(&iter);
|
||||||
|
assert(range);
|
||||||
|
assert(range->high == 11994);
|
||||||
|
assert(range->low == 11946);
|
||||||
|
range = lsquic_trechist_next(&iter);
|
||||||
|
assert(range);
|
||||||
|
assert(range->high == 11941);
|
||||||
|
assert(range->low == 11917);
|
||||||
|
range = lsquic_trechist_next(&iter);
|
||||||
|
assert(!range);
|
||||||
|
|
||||||
|
lsquic_trechist_insert(&hist_mask, hist_elems, 11995);
|
||||||
|
lsquic_trechist_insert(&hist_mask, hist_elems, 11996);
|
||||||
|
test_clone(hist_mask, hist_elems);
|
||||||
|
|
||||||
|
lsquic_trechist_iter(&iter, hist_mask, hist_elems);
|
||||||
|
range = lsquic_trechist_first(&iter);
|
||||||
|
assert(range);
|
||||||
|
assert(range->high == 11996);
|
||||||
|
assert(range->low == 11946);
|
||||||
|
range = lsquic_trechist_next(&iter);
|
||||||
|
assert(range);
|
||||||
|
assert(range->high == 11941);
|
||||||
|
assert(range->low == 11917);
|
||||||
|
range = lsquic_trechist_next(&iter);
|
||||||
|
assert(!range);
|
||||||
|
test_clone(hist_mask, hist_elems);
|
||||||
|
|
||||||
|
lsquic_trechist_insert(&hist_mask, hist_elems, 11912);
|
||||||
|
|
||||||
|
lsquic_trechist_iter(&iter, hist_mask, hist_elems);
|
||||||
|
range = lsquic_trechist_first(&iter);
|
||||||
|
assert(range);
|
||||||
|
assert(range->high == 11996);
|
||||||
|
assert(range->low == 11946);
|
||||||
|
range = lsquic_trechist_next(&iter);
|
||||||
|
assert(range);
|
||||||
|
assert(range->high == 11941);
|
||||||
|
assert(range->low == 11917);
|
||||||
|
range = lsquic_trechist_next(&iter);
|
||||||
|
assert(range);
|
||||||
|
assert(range->high == 11912);
|
||||||
|
assert(range->low == 11912);
|
||||||
|
range = lsquic_trechist_next(&iter);
|
||||||
|
assert(!range);
|
||||||
|
|
||||||
|
for (packno = 12169; packno <= 12193; ++packno)
|
||||||
|
lsquic_trechist_insert(&hist_mask, hist_elems, packno);
|
||||||
|
|
||||||
|
test_clone(hist_mask, hist_elems);
|
||||||
|
|
||||||
|
lsquic_trechist_iter(&iter, hist_mask, hist_elems);
|
||||||
|
range = lsquic_trechist_first(&iter);
|
||||||
|
assert(range);
|
||||||
|
assert(range->high == 12193);
|
||||||
|
assert(range->low == 12169);
|
||||||
|
range = lsquic_trechist_next(&iter);
|
||||||
|
assert(range);
|
||||||
|
assert(range->high == 11996);
|
||||||
|
assert(range->low == 11946);
|
||||||
|
range = lsquic_trechist_next(&iter);
|
||||||
|
assert(range);
|
||||||
|
assert(range->high == 11941);
|
||||||
|
assert(range->low == 11917);
|
||||||
|
range = lsquic_trechist_next(&iter);
|
||||||
|
assert(range);
|
||||||
|
assert(range->high == 11912);
|
||||||
|
assert(range->low == 11912);
|
||||||
|
range = lsquic_trechist_next(&iter);
|
||||||
|
assert(!range);
|
||||||
|
|
||||||
|
test_clone(hist_mask, hist_elems);
|
||||||
|
|
||||||
|
free(hist_elems);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
rechist2str (trechist_mask_t hist_mask, const struct trechist_elem *hist_elems,
|
||||||
|
char *buf, size_t bufsz)
|
||||||
|
{
|
||||||
|
const struct lsquic_packno_range *range;
|
||||||
|
struct trechist_iter iter;
|
||||||
|
size_t off;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
lsquic_trechist_iter(&iter, hist_mask, hist_elems);
|
||||||
|
for (off = 0, range = lsquic_trechist_first(&iter);
|
||||||
|
range && off < bufsz;
|
||||||
|
off += n, range = lsquic_trechist_next(&iter))
|
||||||
|
{
|
||||||
|
n = snprintf(buf + off, bufsz - off, "[%"PRIu64"-%"PRIu64"]",
|
||||||
|
range->high, range->low);
|
||||||
|
if (n < 0 || (size_t) n >= bufsz - off)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
test5 (void)
|
||||||
|
{
|
||||||
|
trechist_mask_t hist_mask;
|
||||||
|
struct trechist_elem *hist_elems;
|
||||||
|
char buf[100];
|
||||||
|
|
||||||
|
hist_elems = malloc(sizeof(hist_elems[0]) * TRECHIST_MAX_RANGES);
|
||||||
|
hist_mask = 0;
|
||||||
|
|
||||||
|
lsquic_trechist_insert(&hist_mask, hist_elems, 1);
|
||||||
|
/* Packet 2 omitted because it could not be decrypted */
|
||||||
|
lsquic_trechist_insert(&hist_mask, hist_elems, 3);
|
||||||
|
lsquic_trechist_insert(&hist_mask, hist_elems, 12);
|
||||||
|
|
||||||
|
rechist2str(hist_mask, hist_elems, buf, sizeof(buf));
|
||||||
|
assert(0 == strcmp(buf, "[12-12][3-3][1-1]"));
|
||||||
|
|
||||||
|
lsquic_trechist_insert(&hist_mask, hist_elems, 4);
|
||||||
|
rechist2str(hist_mask, hist_elems, buf, sizeof(buf));
|
||||||
|
assert(0 == strcmp(buf, "[12-12][4-3][1-1]"));
|
||||||
|
|
||||||
|
lsquic_trechist_insert(&hist_mask, hist_elems, 10);
|
||||||
|
rechist2str(hist_mask, hist_elems, buf, sizeof(buf));
|
||||||
|
assert(0 == strcmp(buf, "[12-12][10-10][4-3][1-1]"));
|
||||||
|
|
||||||
|
lsquic_trechist_insert(&hist_mask, hist_elems, 6);
|
||||||
|
|
||||||
|
rechist2str(hist_mask, hist_elems, buf, sizeof(buf));
|
||||||
|
assert(0 == strcmp(buf, "[12-12][10-10][6-6][4-3][1-1]"));
|
||||||
|
|
||||||
|
lsquic_trechist_insert(&hist_mask, hist_elems, 7);
|
||||||
|
lsquic_trechist_insert(&hist_mask, hist_elems, 8);
|
||||||
|
|
||||||
|
rechist2str(hist_mask, hist_elems, buf, sizeof(buf));
|
||||||
|
assert(0 == strcmp(buf, "[12-12][10-10][8-6][4-3][1-1]"));
|
||||||
|
test_clone(hist_mask, hist_elems);
|
||||||
|
assert(!(lsquic_trechist_contains(hist_mask, hist_elems, 0)));
|
||||||
|
assert(!(lsquic_trechist_contains(hist_mask, hist_elems, 9)));
|
||||||
|
assert(!(lsquic_trechist_contains(hist_mask, hist_elems, 20)));
|
||||||
|
assert(lsquic_trechist_contains(hist_mask, hist_elems, 4));
|
||||||
|
assert(lsquic_trechist_contains(hist_mask, hist_elems, 1));
|
||||||
|
assert(lsquic_trechist_contains(hist_mask, hist_elems, 7));
|
||||||
|
assert(lsquic_trechist_contains(hist_mask, hist_elems, 8));
|
||||||
|
assert(lsquic_trechist_contains(hist_mask, hist_elems, 6));
|
||||||
|
|
||||||
|
lsquic_trechist_insert(&hist_mask, hist_elems, 9);
|
||||||
|
|
||||||
|
rechist2str(hist_mask, hist_elems, buf, sizeof(buf));
|
||||||
|
assert(0 == strcmp(buf, "[12-12][10-6][4-3][1-1]"));
|
||||||
|
test_clone(hist_mask, hist_elems);
|
||||||
|
|
||||||
|
lsquic_trechist_insert(&hist_mask, hist_elems, 5);
|
||||||
|
lsquic_trechist_insert(&hist_mask, hist_elems, 11);
|
||||||
|
|
||||||
|
rechist2str(hist_mask, hist_elems, buf, sizeof(buf));
|
||||||
|
assert(0 == strcmp(buf, "[12-3][1-1]"));
|
||||||
|
|
||||||
|
free(hist_elems);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
basic_test (void)
|
||||||
|
{
|
||||||
|
trechist_mask_t hist_mask;
|
||||||
|
struct trechist_elem *hist_elems;
|
||||||
|
const struct lsquic_packno_range *range;
|
||||||
|
struct trechist_iter iter;
|
||||||
|
unsigned i;
|
||||||
|
int s;
|
||||||
|
|
||||||
|
hist_elems = malloc(sizeof(hist_elems[0]) * TRECHIST_MAX_RANGES);
|
||||||
|
hist_mask = 0;
|
||||||
|
|
||||||
|
lsquic_trechist_iter(&iter, hist_mask, hist_elems);
|
||||||
|
range = lsquic_trechist_first(&iter);
|
||||||
|
assert(!range);
|
||||||
|
|
||||||
|
s = lsquic_trechist_insert(&hist_mask, hist_elems, 1);
|
||||||
|
assert(("inserting packet number one is successful", 0 == s));
|
||||||
|
|
||||||
|
s = lsquic_trechist_insert(&hist_mask, hist_elems, 1);
|
||||||
|
assert(("inserting packet number one again results in duplicate error",
|
||||||
|
s == 1));
|
||||||
|
|
||||||
|
lsquic_trechist_iter(&iter, hist_mask, hist_elems);
|
||||||
|
range = lsquic_trechist_first(&iter);
|
||||||
|
assert(("first range returned correctly", range));
|
||||||
|
assert(("first range low value checks out", range->low == 1));
|
||||||
|
assert(("first range high value checks out", range->high == 1));
|
||||||
|
range = lsquic_trechist_next(&iter);
|
||||||
|
assert(!range);
|
||||||
|
assert(("second range does not exist", !range));
|
||||||
|
|
||||||
|
for (i = 3; i <= 5; ++i)
|
||||||
|
{
|
||||||
|
s = lsquic_trechist_insert(&hist_mask, hist_elems, i);
|
||||||
|
assert(("inserting packet", s == 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
lsquic_trechist_iter(&iter, hist_mask, hist_elems);
|
||||||
|
range = lsquic_trechist_first(&iter);
|
||||||
|
assert(("first range returned correctly", range));
|
||||||
|
assert(("first range low value checks out", range->low == 3));
|
||||||
|
assert(("first range high value checks out", range->high == 5));
|
||||||
|
assert(!(lsquic_trechist_contains(hist_mask, hist_elems, 7)));
|
||||||
|
assert(!(lsquic_trechist_contains(hist_mask, hist_elems, 2)));
|
||||||
|
assert(lsquic_trechist_contains(hist_mask, hist_elems, 4));
|
||||||
|
range = lsquic_trechist_next(&iter);
|
||||||
|
assert(("second range returned correctly", range));
|
||||||
|
assert(("second range low value checks out", range->low == 1));
|
||||||
|
assert(("second range high value checks out", range->high == 1));
|
||||||
|
range = lsquic_trechist_next(&iter);
|
||||||
|
assert(("third range does not exist", !range));
|
||||||
|
|
||||||
|
assert(5 == lsquic_trechist_max(hist_mask, hist_elems));
|
||||||
|
|
||||||
|
s = lsquic_trechist_insert(&hist_mask, hist_elems, 10);
|
||||||
|
assert(("inserting packet", s == 0));
|
||||||
|
|
||||||
|
assert(10 == lsquic_trechist_max(hist_mask, hist_elems));
|
||||||
|
|
||||||
|
lsquic_trechist_iter(&iter, hist_mask, hist_elems);
|
||||||
|
range = lsquic_trechist_first(&iter);
|
||||||
|
assert(("first range returned correctly", range));
|
||||||
|
assert(("first range low value checks out", range->low == 10));
|
||||||
|
assert(("first range high value checks out", range->high == 10));
|
||||||
|
test_clone(hist_mask, hist_elems);
|
||||||
|
|
||||||
|
s = lsquic_trechist_insert(&hist_mask, hist_elems, 8);
|
||||||
|
assert(("inserting packet", s == 0));
|
||||||
|
s = lsquic_trechist_insert(&hist_mask, hist_elems, 9);
|
||||||
|
assert(("inserting packet", s == 0));
|
||||||
|
|
||||||
|
/* Check merge */
|
||||||
|
lsquic_trechist_iter(&iter, hist_mask, hist_elems);
|
||||||
|
range = lsquic_trechist_first(&iter);
|
||||||
|
assert(("first range returned correctly", range));
|
||||||
|
assert(("first range low value checks out", range->low == 8));
|
||||||
|
assert(("first range high value checks out", range->high == 10));
|
||||||
|
|
||||||
|
free(hist_elems);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_limits (void)
|
||||||
|
{
|
||||||
|
trechist_mask_t hist_mask;
|
||||||
|
struct trechist_elem *hist_elems;
|
||||||
|
unsigned i;
|
||||||
|
int s;
|
||||||
|
|
||||||
|
hist_elems = malloc(sizeof(hist_elems[0]) * TRECHIST_MAX_RANGES);
|
||||||
|
hist_mask = 0;
|
||||||
|
|
||||||
|
for (i = 1; i <= UCHAR_MAX; ++i)
|
||||||
|
{
|
||||||
|
s = lsquic_trechist_insert(&hist_mask, hist_elems, i);
|
||||||
|
assert(s == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
s = lsquic_trechist_insert(&hist_mask, hist_elems, i);
|
||||||
|
assert(s == -1); /* Overflow */
|
||||||
|
|
||||||
|
for (i = 0; i < TRECHIST_MAX_RANGES - 1; ++i)
|
||||||
|
{
|
||||||
|
s = lsquic_trechist_insert(&hist_mask, hist_elems, 1000 + 2 * i);
|
||||||
|
assert(s == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
s = lsquic_trechist_insert(&hist_mask, hist_elems, 1000 + 2 * i);
|
||||||
|
assert(s == -1); /* Out of ranges */
|
||||||
|
|
||||||
|
free(hist_elems);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main (void)
|
||||||
|
{
|
||||||
|
basic_test();
|
||||||
|
test4();
|
||||||
|
test5();
|
||||||
|
test_limits();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue