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
|
||||
- 2.21.0
|
||||
- [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
|
||||
|
||||
```
|
||||
git checkout b117a3a0b7bd11fe6ebd503ec6b45d6b910b41a1
|
||||
git checkout 251b5169fd44345f455438312ec4e18ae07fd58c
|
||||
```
|
||||
|
||||
3. Compile the library
|
||||
|
|
|
@ -161,6 +161,24 @@ strndup(const char *s, size_t n)
|
|||
}
|
||||
#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 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);
|
||||
lsquic_stream_wantwrite(stream, 1);
|
||||
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));
|
||||
}
|
||||
if (s_priority_specs)
|
||||
maybe_perform_priority_actions(stream, st_h);
|
||||
if (s_abandon_early)
|
||||
{
|
||||
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;
|
||||
struct header_buf hbuf;
|
||||
unsigned h_idx = 0;
|
||||
if (!hostname)
|
||||
hostname = st_h->client_ctx->prog->prog_hostname;
|
||||
hbuf.off = 0;
|
||||
struct lsxpack_header headers_arr[7];
|
||||
struct lsxpack_header headers_arr[9];
|
||||
#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[1], &hbuf, V(":scheme"), V("https"));
|
||||
header_set_ptr(&headers_arr[2], &hbuf, V(":path"), V(st_h->path));
|
||||
header_set_ptr(&headers_arr[3], &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));
|
||||
/* The following headers only gets sent if there is request payload: */
|
||||
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));
|
||||
header_set_ptr(&headers_arr[h_idx++], &hbuf, V(":method"), V(st_h->client_ctx->method));
|
||||
header_set_ptr(&headers_arr[h_idx++], &hbuf, V(":scheme"), V("https"));
|
||||
header_set_ptr(&headers_arr[h_idx++], &hbuf, V(":path"), V(st_h->path));
|
||||
header_set_ptr(&headers_arr[h_idx++], &hbuf, V(":authority"), V(hostname));
|
||||
header_set_ptr(&headers_arr[h_idx++], &hbuf, V("user-agent"), V(st_h->client_ctx->prog->prog_settings.es_ua));
|
||||
if (randomly_reprioritize_streams)
|
||||
{
|
||||
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 = {
|
||||
.count = sizeof(headers_arr) / sizeof(headers_arr[0]),
|
||||
.count = h_idx,
|
||||
.headers = headers_arr,
|
||||
};
|
||||
if (!st_h->client_ctx->payload)
|
||||
headers.count -= 2;
|
||||
if (0 != lsquic_stream_send_headers(st_h->stream, &headers,
|
||||
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
|
||||
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)
|
||||
fwrite(buf, 1, nread, stdout);
|
||||
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);
|
||||
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",
|
||||
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)
|
||||
{
|
||||
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"
|
||||
" -3 MAX Close stream after reading at most MAX bytes. The actual\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);
|
||||
}
|
||||
|
||||
|
@ -1432,6 +1532,7 @@ main (int argc, char **argv)
|
|||
struct sport_head sports;
|
||||
struct prog prog;
|
||||
const char *token = NULL;
|
||||
struct priority_spec *priority_specs = NULL;
|
||||
|
||||
TAILQ_INIT(&sports);
|
||||
memset(&client_ctx, 0, sizeof(client_ctx));
|
||||
|
@ -1450,6 +1551,7 @@ main (int argc, char **argv)
|
|||
while (-1 != (opt = getopt(argc, argv, PROG_OPTS
|
||||
"46Br:R:IKu:EP:M:n:w:H:p:0:q:e:hatT:b:d:"
|
||||
"3:" /* 3 is 133+ for "e" ("e" for "early") */
|
||||
"9:" /* 9 sort of looks like P... */
|
||||
#ifndef WIN32
|
||||
"C:"
|
||||
#endif
|
||||
|
@ -1486,6 +1588,7 @@ main (int argc, char **argv)
|
|||
case 'E': /* E: randomly reprioritize str<E>ams. Now, that's
|
||||
* pretty random. :)
|
||||
*/
|
||||
srand((uintptr_t) argv);
|
||||
randomly_reprioritize_streams = 1;
|
||||
break;
|
||||
case 'n':
|
||||
|
@ -1569,6 +1672,45 @@ main (int argc, char **argv)
|
|||
case '3':
|
||||
s_abandon_early = strtol(optarg, NULL, 10);
|
||||
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:
|
||||
if (0 != prog_set_opt(&prog, opt, optarg))
|
||||
exit(1);
|
||||
|
@ -1660,5 +1802,6 @@ main (int argc, char **argv)
|
|||
if (client_ctx.qif_fh)
|
||||
(void) fclose(client_ctx.qif_fh);
|
||||
|
||||
free(priority_specs);
|
||||
exit(0 == s ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
}
|
||||
|
|
|
@ -41,8 +41,8 @@
|
|||
|
||||
#include <event2/event.h>
|
||||
|
||||
#include "test_common.h"
|
||||
#include "lsquic.h"
|
||||
#include "test_common.h"
|
||||
#include "prog.h"
|
||||
#include "lsxpack_header.h"
|
||||
|
||||
|
@ -1916,6 +1916,11 @@ set_engine_option (struct lsquic_engine_settings *settings,
|
|||
settings->es_scid_iss_rate = atoi(val);
|
||||
return 0;
|
||||
}
|
||||
if (0 == strncmp(name, "ext_http_prio", 13))
|
||||
{
|
||||
settings->es_ext_http_prio = atoi(val);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case 14:
|
||||
if (0 == strncmp(name, "max_streams_in", 14))
|
||||
|
@ -2043,8 +2048,8 @@ pba_init (struct packout_buf_allocator *pba, unsigned max)
|
|||
|
||||
|
||||
void *
|
||||
pba_allocate (void *packout_buf_allocator, void *peer_ctx, void *conn_ctx, unsigned short size,
|
||||
char is_ipv6)
|
||||
pba_allocate (void *packout_buf_allocator, void *peer_ctx,
|
||||
lsquic_conn_ctx_t *conn_ctx, unsigned short size, char is_ipv6)
|
||||
{
|
||||
struct packout_buf_allocator *const pba = packout_buf_allocator;
|
||||
struct packout_buf *pb;
|
||||
|
|
|
@ -115,7 +115,7 @@ void
|
|||
pba_init (struct packout_buf_allocator *, unsigned max);
|
||||
|
||||
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
|
||||
pba_release (void *packout_buf_allocator, void *, void *obj, char);
|
||||
|
|
|
@ -842,6 +842,13 @@ settings structure:
|
|||
|
||||
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
|
||||
convenience function:
|
||||
|
||||
|
@ -1066,6 +1073,10 @@ out of date. Please check your :file:`lsquic.h` for actual values.*
|
|||
|
||||
Assume optimistic NAT by default.
|
||||
|
||||
.. macro:: LSQUIC_DF_EXT_HTTP_PRIO
|
||||
|
||||
Turn on Extensible HTTP Priorities by default.
|
||||
|
||||
Receiving Packets
|
||||
-----------------
|
||||
|
||||
|
@ -2184,6 +2195,72 @@ The following log modules are defined:
|
|||
- *tokgen*: Token generation and validation.
|
||||
- *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:
|
||||
|
||||
Datagrams
|
||||
|
|
|
@ -24,9 +24,9 @@ copyright = u'2020, LiteSpeed Technologies'
|
|||
author = u'LiteSpeed Technologies'
|
||||
|
||||
# The short X.Y version
|
||||
version = u'2.21'
|
||||
version = u'2.22'
|
||||
# The full version, including alpha/beta/rc tags
|
||||
release = u'2.21.0'
|
||||
release = u'2.22.0'
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
|
|
@ -37,6 +37,7 @@ LSQUIC supports nearly all QUIC and HTTP/3 features, including
|
|||
- TLS Key updates
|
||||
- Extensions:
|
||||
|
||||
- :ref:`extensible-http-priorities`
|
||||
- :ref:`apiref-datagrams`
|
||||
- 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)
|
||||
|
|
|
@ -24,7 +24,7 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
#define LSQUIC_MAJOR_VERSION 2
|
||||
#define LSQUIC_MINOR_VERSION 21
|
||||
#define LSQUIC_MINOR_VERSION 22
|
||||
#define LSQUIC_PATCH_VERSION 0
|
||||
|
||||
/**
|
||||
|
@ -371,6 +371,9 @@ typedef struct ssl_ctx_st * (*lsquic_lookup_cert_f)(
|
|||
/** Assume optimistic NAT by default. */
|
||||
#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. */
|
||||
#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.
|
||||
*/
|
||||
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 */
|
||||
|
@ -1637,6 +1648,42 @@ unsigned lsquic_stream_priority (const lsquic_stream_t *s);
|
|||
*/
|
||||
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_*
|
||||
* functions.
|
||||
|
|
|
@ -35,7 +35,9 @@ SET(lsquic_STAT_SRCS
|
|||
lsquic_hcso_writer.c
|
||||
lsquic_headers_stream.c
|
||||
lsquic_hkdf.c
|
||||
lsquic_hpi.c
|
||||
lsquic_hspack_valid.c
|
||||
lsquic_http.c
|
||||
lsquic_http1x_if.c
|
||||
lsquic_logger.c
|
||||
lsquic_malo.c
|
||||
|
@ -75,6 +77,7 @@ SET(lsquic_STAT_SRCS
|
|||
lsquic_stream.c
|
||||
lsquic_tokgen.c
|
||||
lsquic_trans_params.c
|
||||
lsquic_trechist.c
|
||||
lsquic_util.c
|
||||
lsquic_varint.c
|
||||
lsquic_version.c
|
||||
|
@ -103,6 +106,7 @@ ADD_CUSTOM_COMMAND(
|
|||
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} ls-sfparser.c)
|
||||
|
||||
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\\\"")
|
||||
|
|
|
@ -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_SOURCES = ls-qpack/lsqpack.c \
|
||||
liblsquic_a_SOURCES = \
|
||||
ls-qpack/lsqpack.c \
|
||||
lsquic_adaptive_cc.c \
|
||||
lsquic_alarmset.c \
|
||||
lsquic_arr.c \
|
||||
|
@ -40,7 +41,9 @@ liblsquic_a_SOURCES = ls-qpack/lsqpack.c \
|
|||
lsquic_hcso_writer.c \
|
||||
lsquic_headers_stream.c \
|
||||
lsquic_hkdf.c \
|
||||
lsquic_hpi.c \
|
||||
lsquic_hspack_valid.c \
|
||||
lsquic_http.c \
|
||||
lsquic_http1x_if.c \
|
||||
lsquic_logger.c \
|
||||
lsquic_malo.c \
|
||||
|
@ -55,7 +58,6 @@ liblsquic_a_SOURCES = ls-qpack/lsqpack.c \
|
|||
lsquic_packet_in.c \
|
||||
lsquic_packet_out.c \
|
||||
lsquic_packet_resize.c \
|
||||
lsquic_packints.c \
|
||||
lsquic_parse_Q046.c \
|
||||
lsquic_parse_Q050.c \
|
||||
lsquic_parse_common.c \
|
||||
|
@ -68,6 +70,7 @@ liblsquic_a_SOURCES = ls-qpack/lsqpack.c \
|
|||
lsquic_qdec_hdl.c \
|
||||
lsquic_qenc_hdl.c \
|
||||
lsquic_qlog.c \
|
||||
lsquic_rechist.c \
|
||||
lsquic_rtt.c \
|
||||
lsquic_send_ctl.c \
|
||||
lsquic_senhist.c \
|
||||
|
@ -80,10 +83,13 @@ liblsquic_a_SOURCES = ls-qpack/lsqpack.c \
|
|||
lsquic_stream.c \
|
||||
lsquic_tokgen.c \
|
||||
lsquic_trans_params.c \
|
||||
lsquic_trechist.c \
|
||||
lsquic_util.c \
|
||||
lsquic_varint.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
|
||||
$(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_get_ctx (const struct lsquic_conn *lconn)
|
||||
{
|
||||
return lconn->conn_ctx;
|
||||
return lconn->cn_conn_ctx;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
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
|
||||
{
|
||||
void *cn_enc_session;
|
||||
lsquic_conn_ctx_t *conn_ctx;
|
||||
const struct enc_session_funcs_common
|
||||
*cn_esf_c;
|
||||
union {
|
||||
|
@ -334,6 +333,7 @@ struct lsquic_conn
|
|||
lsquic_time_t cn_last_sent;
|
||||
lsquic_time_t cn_last_ticked;
|
||||
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_version cn_version:8;
|
||||
unsigned char cn_cces_mask; /* Those that are set */
|
||||
|
|
|
@ -43,6 +43,7 @@ struct lsquic_conn_public {
|
|||
struct {
|
||||
struct qpack_enc_hdl *qeh;
|
||||
struct qpack_dec_hdl *qdh;
|
||||
struct hcso_writer *hcso;
|
||||
struct lsquic_hash *promises;
|
||||
} ietf;
|
||||
} u;
|
||||
|
|
|
@ -438,10 +438,10 @@ void serialize_cert_entries(uint8_t* out, int *out_len, cert_entry_t *entries,
|
|||
|
||||
|
||||
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);
|
||||
char *in_end = in + lsquic_str_len(compressed_crt_buf);
|
||||
const unsigned char *in = ccert->buf;
|
||||
const unsigned char *in_end = in + ccert->len;
|
||||
size_t idx = 0;
|
||||
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 */
|
||||
int
|
||||
struct compressed_cert *
|
||||
lsquic_compress_certs (lsquic_str_t **certs, size_t certs_count,
|
||||
lsquic_str_t *client_common_set_hashes,
|
||||
lsquic_str_t *client_cached_cert_hashes,
|
||||
lsquic_str_t *result)
|
||||
lsquic_str_t *client_cached_cert_hashes)
|
||||
{
|
||||
int rv;
|
||||
size_t i;
|
||||
size_t uncompressed_size = 0, compressed_size = 0 ;
|
||||
z_stream z;
|
||||
|
@ -595,10 +592,12 @@ lsquic_compress_certs (lsquic_str_t **certs, size_t certs_count,
|
|||
uint8_t* out;
|
||||
uint32_t tmp_size_32;
|
||||
cert_entry_t *entries;
|
||||
struct compressed_cert *ccert;
|
||||
|
||||
ccert = NULL;
|
||||
entries = malloc(sizeof(cert_entry_t) * certs_count);
|
||||
if (!entries)
|
||||
return -1;
|
||||
return NULL;
|
||||
|
||||
dict = lsquic_str_new(NULL, 0);
|
||||
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);
|
||||
result_length = entries_size + (uncompressed_size > 0 ? 4 : 0) +
|
||||
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);
|
||||
out += entries_size;
|
||||
|
||||
if (uncompressed_size == 0)
|
||||
{
|
||||
lsquic_str_setlen(result, entries_size);
|
||||
rv = 0;
|
||||
ccert->len = entries_size;
|
||||
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))
|
||||
goto err;
|
||||
|
||||
rv = 0;
|
||||
result_length -= z.avail_out;
|
||||
lsquic_str_setlen(result, result_length);
|
||||
ccert->len = result_length - z.avail_out;
|
||||
|
||||
cleanup:
|
||||
free(entries);
|
||||
|
@ -681,10 +680,12 @@ lsquic_compress_certs (lsquic_str_t **certs, size_t certs_count,
|
|||
lsquic_str_delete(dict);
|
||||
if (uncompressed_size)
|
||||
deflateEnd(&z);
|
||||
return rv;
|
||||
return ccert;
|
||||
|
||||
err:
|
||||
rv = -1;
|
||||
if (ccert)
|
||||
free(ccert);
|
||||
ccert = NULL;
|
||||
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_compress_certs(struct lsquic_str **certs, size_t certs_count,
|
||||
struct lsquic_str *client_common_set_hashes,
|
||||
struct lsquic_str *client_cached_cert_hashes,
|
||||
struct lsquic_str *result);
|
||||
struct compressed_cert
|
||||
{
|
||||
size_t len;
|
||||
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,
|
||||
struct lsquic_str *cached_certs, size_t cached_certs_count,
|
||||
struct lsquic_str **out_certs,
|
||||
|
|
|
@ -240,6 +240,9 @@ struct enc_sess_iquic
|
|||
ESI_RSCID = 1 << 14,
|
||||
ESI_ISCID = 1 << 15,
|
||||
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;
|
||||
enum enc_level esi_last_w;
|
||||
unsigned esi_trasec_sz;
|
||||
|
@ -475,7 +478,17 @@ strip_hp (struct enc_sess_iquic *enc_sess,
|
|||
shift += 8;
|
||||
}
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
LSQ_DEBUG("first packet in %s, packno: %"PRIu64, lsquic_pns2str[pns],
|
||||
packno);
|
||||
return packno;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
ipv6 = NP_IS_IPv6(packet_out->po_path);
|
||||
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)
|
||||
{
|
||||
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,
|
||||
packet_in->pi_packno);
|
||||
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_flags |= ESI_MAX_PACKNO_INIT << pns;
|
||||
if (is_valid_packno(pair->ykp_thresh)
|
||||
&& packet_in->pi_packno > pair->ykp_thresh)
|
||||
pair->ykp_thresh = packet_in->pi_packno;
|
||||
|
|
|
@ -76,6 +76,7 @@
|
|||
#include "lsquic_version.h"
|
||||
#include "lsquic_pr_queue.h"
|
||||
#include "lsquic_mini_conn.h"
|
||||
#include "lsquic_trechist.h"
|
||||
#include "lsquic_mini_conn_ietf.h"
|
||||
#include "lsquic_stock_shi.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_rtt_thresh = LSQUIC_DF_CC_RTT_THRESH;
|
||||
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(
|
||||
engine->pub.enp_pmi_ctx, packet_out->po_path->np_peer_ctx, conn->conn_ctx,
|
||||
packet_out->po_data_sz, ipv6);
|
||||
engine->pub.enp_pmi_ctx, packet_out->po_path->np_peer_ctx,
|
||||
conn->cn_conn_ctx, packet_out->po_data_sz, ipv6);
|
||||
if (!packet_out->po_enc_data)
|
||||
{
|
||||
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].local_sa = NP_LOCAL_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;
|
||||
}
|
||||
*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;
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
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);
|
||||
/* Now that user code knows about this connection, process incoming
|
||||
* 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),
|
||||
TAILQ_LAST(&conn->fc_pub.sending_streams, lsquic_streams_tailq),
|
||||
(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;
|
||||
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),
|
||||
TAILQ_LAST(&conn->fc_pub.read_streams, lsquic_streams_tailq),
|
||||
(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;
|
||||
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),
|
||||
TAILQ_LAST(&conn->fc_pub.read_streams, lsquic_streams_tailq),
|
||||
(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);
|
||||
for (stream = lsquic_spi_first(&spi); stream;
|
||||
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),
|
||||
TAILQ_LAST(&conn->fc_pub.write_streams, lsquic_streams_tailq),
|
||||
(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);
|
||||
|
||||
if (high_prio)
|
||||
|
|
|
@ -64,14 +64,18 @@
|
|||
#include "lsquic_http1x_if.h"
|
||||
#include "lsquic_qenc_hdl.h"
|
||||
#include "lsquic_qdec_hdl.h"
|
||||
#include "lsquic_trechist.h"
|
||||
#include "lsquic_mini_conn_ietf.h"
|
||||
#include "lsquic_tokgen.h"
|
||||
#include "lsquic_full_conn.h"
|
||||
#include "lsquic_spi.h"
|
||||
#include "lsquic_min_heap.h"
|
||||
#include "lsquic_hpi.h"
|
||||
#include "lsquic_ietf.h"
|
||||
#include "lsquic_push_promise.h"
|
||||
#include "lsquic_headers.h"
|
||||
#include "lsquic_crand.h"
|
||||
#include "ls-sfparser.h"
|
||||
|
||||
#define LSQUIC_LOGGER_MODULE LSQLM_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 lsquic_conn ifc_conn;
|
||||
|
@ -371,6 +422,7 @@ struct ietf_full_conn
|
|||
const struct lsquic_stream_if
|
||||
*ifc_stream_if;
|
||||
void *ifc_stream_ctx;
|
||||
const struct prio_iter_if *ifc_pii;
|
||||
char *ifc_errmsg;
|
||||
struct lsquic_engine_public
|
||||
*ifc_enpub;
|
||||
|
@ -454,6 +506,7 @@ struct ietf_full_conn
|
|||
} ifc_u;
|
||||
lsquic_time_t ifc_idle_to;
|
||||
lsquic_time_t ifc_ping_period;
|
||||
struct lsquic_hash *ifc_bpus;
|
||||
uint64_t ifc_last_max_data_off_sent;
|
||||
unsigned short ifc_min_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)
|
||||
flags |= SCF_DISP_RW_ONCE;
|
||||
if (conn->ifc_flags & IFC_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 = lsquic_stream_new(stream_id, &conn->ifc_pub,
|
||||
|
@ -1190,6 +1247,7 @@ ietf_full_conn_init (struct ietf_full_conn *conn,
|
|||
return -1;
|
||||
conn->ifc_pub.u.ietf.qeh = &conn->ifc_qeh;
|
||||
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.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;
|
||||
if (conn->ifc_settings->es_noprogress_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;
|
||||
}
|
||||
|
||||
|
@ -1368,9 +1430,9 @@ lsquic_ietf_full_conn_server_new (struct lsquic_engine_public *enpub,
|
|||
int have_outgoing_ack;
|
||||
lsquic_packno_t next_packno;
|
||||
lsquic_time_t now;
|
||||
packno_set_t set;
|
||||
enum packnum_space pns;
|
||||
unsigned i;
|
||||
struct ietf_mini_rechist mini_rechist;
|
||||
|
||||
conn = calloc(1, sizeof(*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;
|
||||
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;
|
||||
set && i < MAX_PACKETS; set &= ~(1ULL << i), ++i)
|
||||
if (set & (1ULL << i))
|
||||
(void) lsquic_rechist_received(&conn->ifc_rechist[pns], i, 0);
|
||||
if (i)
|
||||
lsquic_imico_rechist_init(&mini_rechist, imc, pns);
|
||||
if (0 != lsquic_rechist_copy_ranges(&conn->ifc_rechist[pns],
|
||||
&mini_rechist, lsquic_imico_rechist_first,
|
||||
lsquic_imico_rechist_next))
|
||||
goto err2;
|
||||
conn->ifc_rechist[pns].rh_largest_acked_received
|
||||
= 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;
|
||||
|
||||
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);
|
||||
|
||||
if (0 != handshake_ok(&conn->ifc_conn))
|
||||
|
@ -2708,19 +2770,21 @@ static void
|
|||
process_streams_ready_to_send (struct ietf_full_conn *conn)
|
||||
{
|
||||
struct lsquic_stream *stream;
|
||||
struct stream_prio_iter spi;
|
||||
union prio_iter pi;
|
||||
|
||||
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),
|
||||
(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;
|
||||
stream = lsquic_spi_next(&spi))
|
||||
for (stream = conn->ifc_pii->pii_first(&pi); stream;
|
||||
stream = conn->ifc_pii->pii_next(&pi))
|
||||
if (!process_stream_ready_to_send(conn, stream))
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -2976,6 +3040,13 @@ ietf_full_conn_ci_destroy (struct lsquic_conn *lconn)
|
|||
}
|
||||
for (i = 0; i < N_SITS; ++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);
|
||||
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "full connection destroyed");
|
||||
free(conn->ifc_errmsg);
|
||||
|
@ -3950,7 +4021,7 @@ process_streams_read_events (struct ietf_full_conn *conn)
|
|||
struct lsquic_stream *stream;
|
||||
int iters;
|
||||
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", };
|
||||
|
||||
if (TAILQ_EMPTY(&conn->ifc_pub.read_streams))
|
||||
|
@ -3960,19 +4031,20 @@ process_streams_read_events (struct ietf_full_conn *conn)
|
|||
iters = 0;
|
||||
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),
|
||||
(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;
|
||||
for (stream = lsquic_spi_first(&spi); stream;
|
||||
stream = lsquic_spi_next(&spi))
|
||||
for (stream = conn->ifc_pii->pii_first(&pi); stream;
|
||||
stream = conn->ifc_pii->pii_next(&pi))
|
||||
{
|
||||
q_flags = stream->sm_qflags & SMQF_SERVICE_FLAGS;
|
||||
lsquic_stream_dispatch_read_events(stream);
|
||||
needs_service |= q_flags ^ (stream->sm_qflags & SMQF_SERVICE_FLAGS);
|
||||
}
|
||||
conn->ifc_pii->pii_cleanup(&pi);
|
||||
|
||||
if (needs_service)
|
||||
service_streams(conn);
|
||||
|
@ -4045,23 +4117,25 @@ static void
|
|||
process_streams_write_events (struct ietf_full_conn *conn, int high_prio)
|
||||
{
|
||||
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),
|
||||
(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);
|
||||
|
||||
if (high_prio)
|
||||
lsquic_spi_drop_non_high(&spi);
|
||||
conn->ifc_pii->pii_drop_non_high(&pi);
|
||||
else
|
||||
lsquic_spi_drop_high(&spi);
|
||||
conn->ifc_pii->pii_drop_high(&pi);
|
||||
|
||||
for (stream = lsquic_spi_first(&spi); stream && write_is_possible(conn);
|
||||
stream = lsquic_spi_next(&spi))
|
||||
for (stream = conn->ifc_pii->pii_first(&pi);
|
||||
stream && write_is_possible(conn);
|
||||
stream = conn->ifc_pii->pii_next(&pi))
|
||||
if (stream->sm_qflags & SMQF_WRITE_Q_FLAGS)
|
||||
lsquic_stream_dispatch_write_events(stream);
|
||||
conn->ifc_pii->pii_cleanup(&pi);
|
||||
|
||||
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 */
|
||||
static struct lsquic_stream *
|
||||
new_stream (struct ietf_full_conn *conn, lsquic_stream_id_t stream_id,
|
||||
enum stream_ctor_flags flags)
|
||||
{
|
||||
const struct lsquic_stream_if *iface;
|
||||
struct buffered_priority_update *bpu;
|
||||
struct lsquic_hash_elem *el;
|
||||
void *stream_ctx;
|
||||
struct lsquic_stream *stream;
|
||||
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)
|
||||
flags |= SCF_DISP_RW_ONCE;
|
||||
if (conn->ifc_flags & IFC_HTTP)
|
||||
{
|
||||
flags |= SCF_HTTP;
|
||||
if (conn->ifc_pii == &ext_prio_iter_if)
|
||||
flags |= SCF_HTTP_PRIO;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
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,
|
||||
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
|
||||
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_goaway = on_goaway_server_27,
|
||||
.on_unexpected_frame = on_unexpected_frame,
|
||||
.on_priority_update = on_priority_update_server,
|
||||
};
|
||||
|
||||
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_goaway = on_goaway_client_28 /* sic */,
|
||||
.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_goaway = on_goaway_server /* sic */,
|
||||
.on_unexpected_frame = on_unexpected_frame,
|
||||
.on_priority_update = on_priority_update_server,
|
||||
};
|
||||
|
||||
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_goaway = on_goaway_client_28,
|
||||
.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_goaway = on_goaway_server,
|
||||
.on_unexpected_frame = on_unexpected_frame,
|
||||
.on_priority_update = on_priority_update_server,
|
||||
};
|
||||
|
||||
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_goaway = on_goaway_client,
|
||||
.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 *const lconn = &dummy_lsquic_conn;
|
||||
|
||||
static int s_ccrt_idx;
|
||||
|
||||
static const int s_log_seal_and_open;
|
||||
static char s_str[0x1000];
|
||||
|
||||
|
@ -167,7 +169,7 @@ typedef struct hs_ctx_st
|
|||
struct lsquic_str prof;
|
||||
|
||||
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 */
|
||||
} 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 *,
|
||||
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
|
||||
static
|
||||
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 put_compressed_cert (struct compressed_cert *);
|
||||
|
||||
|
||||
#if LSQUIC_KEEP_ENC_SESS_HISTORY
|
||||
static void
|
||||
|
@ -354,10 +355,26 @@ eshist_append (struct lsquic_enc_session *enc_session,
|
|||
# define ESHIST_APPEND(sess, event) do { } while (0)
|
||||
#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
|
||||
lsquic_handshake_init(int flags)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
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
|
||||
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->prof);
|
||||
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->scfg_pubs);
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
case QTAG_PUBS:
|
||||
|
@ -1884,7 +1885,7 @@ get_valid_scfg (const struct lsquic_enc_session *enc_session,
|
|||
|
||||
|
||||
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;
|
||||
lsquic_str_t **crts;
|
||||
|
@ -1893,6 +1894,7 @@ generate_crt (struct lsquic_enc_session *enc_session, int common_case)
|
|||
STACK_OF(X509) *pXchain;
|
||||
SSL_CTX *const ctx = enc_session->ssl_ctx;
|
||||
hs_ctx_t *const hs_ctx = &enc_session->hs_ctx;
|
||||
struct compressed_cert *ccert;
|
||||
|
||||
SSL_CTX_get0_chain_certs(ctx, &pXchain);
|
||||
n = sk_X509_num(pXchain);
|
||||
|
@ -1918,17 +1920,22 @@ generate_crt (struct lsquic_enc_session *enc_session, int common_case)
|
|||
OPENSSL_free(out);
|
||||
}
|
||||
|
||||
if (0 != lsquic_compress_certs(crts, crt_num, &hs_ctx->ccs, &hs_ctx->ccrt,
|
||||
&hs_ctx->crt))
|
||||
ccert = lsquic_compress_certs(crts, crt_num, &hs_ctx->ccs, &hs_ctx->ccrt);
|
||||
if (!ccert)
|
||||
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,
|
||||
make_compress_cert_hash_item(&hs_ctx->sni, &hs_ctx->crt)))
|
||||
free(ccert);
|
||||
ccert = NULL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
++ccert->refcnt;
|
||||
hs_ctx->ccert = ccert;
|
||||
|
||||
/* We got here, set rv to 0: success */
|
||||
rv = 0;
|
||||
|
||||
|
@ -1946,6 +1953,11 @@ static int
|
|||
gen_rej1_data (struct lsquic_enc_session *enc_session, uint8_t *data,
|
||||
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;
|
||||
EVP_PKEY * rsa_priv_key;
|
||||
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;
|
||||
int scfg_len = enc_session->server_config->lsc_scfg->info.scfg_len;
|
||||
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;
|
||||
struct message_writer mw;
|
||||
|
||||
|
@ -1965,33 +1973,46 @@ gen_rej1_data (struct lsquic_enc_session *enc_session, uint8_t *data,
|
|||
if (!rsa_priv_key)
|
||||
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
|
||||
|
||||
/**
|
||||
* 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)
|
||||
if (hs_ctx->ccert)
|
||||
{
|
||||
lsquic_str_d(&hs_ctx->crt);
|
||||
lsquic_str_copy(&hs_ctx->crt, compress_certs_item->crts_compress_buf);
|
||||
put_compressed_cert(hs_ctx->ccert);
|
||||
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
|
||||
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 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),
|
||||
scfg_data, scfg_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);
|
||||
|
||||
|
@ -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, 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->crt));
|
||||
if (hs_ctx->ccert)
|
||||
MSG_LEN_ADD(msg_len, hs_ctx->ccert->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);
|
||||
|
||||
|
@ -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_STTL, &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);
|
||||
|
||||
assert(data + max_len >= MW_P(&mw));
|
||||
len = MW_P(&mw) - data;
|
||||
LSQ_DEBUG("gen_rej1_data called, return len %d.", len);
|
||||
#ifdef WIN32
|
||||
end:
|
||||
_freea(prof_buf);
|
||||
#endif
|
||||
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,
|
||||
int cached_certs_count)
|
||||
{
|
||||
const unsigned char *const in =
|
||||
(const unsigned char *) lsquic_str_buf(&enc_session->hs_ctx.crt);
|
||||
const unsigned char *const in_end =
|
||||
in + lsquic_str_len(&enc_session->hs_ctx.crt);
|
||||
const unsigned char *dummy = (unsigned char *) "";
|
||||
const unsigned char *const in = enc_session->hs_ctx.ccert
|
||||
? enc_session->hs_ctx.ccert->buf : dummy;
|
||||
const unsigned char *const in_end = enc_session->hs_ctx.ccert
|
||||
? in + enc_session->hs_ctx.ccert->len : 0;
|
||||
EVP_PKEY *pub_key;
|
||||
int ret;
|
||||
size_t i;
|
||||
|
@ -2749,9 +2778,9 @@ lsquic_enc_session_handle_chlo_reply (enc_session_t *enc_session_p,
|
|||
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)
|
||||
{
|
||||
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.prof);
|
||||
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)
|
||||
{
|
||||
|
@ -3738,7 +3769,8 @@ gquic_encrypt_packet (enc_session_t *enc_session_p,
|
|||
return ENCPA_BADCRYPT; /* To cause connection to close */
|
||||
ipv6 = NP_IS_IPv6(packet_out->po_path);
|
||||
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)
|
||||
{
|
||||
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);
|
||||
ipv6 = NP_IS_IPv6(packet_out->po_path);
|
||||
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)
|
||||
{
|
||||
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;
|
||||
int s;
|
||||
|
||||
continue_reading:
|
||||
while (p < end)
|
||||
{
|
||||
switch (reader->hr_state)
|
||||
|
@ -83,6 +84,10 @@ lsquic_hcsi_reader_feed (struct hcsi_reader *reader, const void *buf,
|
|||
case HQFT_MAX_PUSH_ID:
|
||||
reader->hr_state = HR_READ_VARINT;
|
||||
break;
|
||||
case HQFT_PRIORITY_UPDATE_PUSH:
|
||||
case HQFT_PRIORITY_UPDATE_STREAM:
|
||||
reader->hr_state = HR_READ_VARINT;
|
||||
break;
|
||||
case HQFT_DATA:
|
||||
case HQFT_HEADERS:
|
||||
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;
|
||||
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)
|
||||
{
|
||||
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;
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
switch (reader->hr_frame_type)
|
||||
{
|
||||
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_u.vint_state.val);
|
||||
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:
|
||||
assert(0);
|
||||
}
|
||||
|
@ -179,6 +220,20 @@ lsquic_hcsi_reader_feed (struct hcsi_reader *reader, const void *buf,
|
|||
else
|
||||
reader->hr_state = HR_READ_SETTING_BEGIN;
|
||||
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:
|
||||
assert(0);
|
||||
/* fall-through */
|
||||
|
|
|
@ -18,6 +18,8 @@ struct hcsi_callbacks
|
|||
void (*on_setting)(void *ctx, uint64_t setting_id, uint64_t value);
|
||||
void (*on_goaway)(void *ctx, uint64_t stream_id);
|
||||
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_READ_SETTING_BEGIN,
|
||||
HR_READ_SETTING_CONTINUE,
|
||||
HR_READ_PRIORITY_UPDATE,
|
||||
HR_READ_VARINT,
|
||||
HR_READ_VARINT_CONTINUE,
|
||||
HR_ERROR,
|
||||
|
@ -40,10 +43,19 @@ struct hcsi_reader
|
|||
{
|
||||
struct varint_read_state vint_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;
|
||||
const struct hcsi_callbacks *hr_cb;
|
||||
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_CANCEL_PUSH: return "CANCEL_PUSH";
|
||||
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>";
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
static size_t
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#define LSQUIC_HCSO_WRITER_H 1
|
||||
|
||||
struct lsquic_engine_settings;
|
||||
struct lsquic_ext_http_prio;
|
||||
struct lsquic_stream;
|
||||
|
||||
struct hcso_writer
|
||||
|
@ -34,6 +35,11 @@ lsquic_hcso_write_max_push_id (struct hcso_writer *, uint64_t max_push_id);
|
|||
int
|
||||
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;
|
||||
|
||||
#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
|
||||
#define LSQUIC_HQ_H 1
|
||||
|
||||
struct lsquic_ext_http_prio;
|
||||
|
||||
/* [draft-ietf-quic-http-27] Section 11.2.1 */
|
||||
enum hq_frame_type
|
||||
{
|
||||
|
@ -16,6 +18,14 @@ enum hq_frame_type
|
|||
HQFT_PUSH_PROMISE = 5,
|
||||
HQFT_GOAWAY = 7,
|
||||
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.
|
||||
* Nevertheless, just to be on the safe side, give it a value as
|
||||
* 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
|
||||
|
|
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_ENG_HIST] = LSQ_LOG_WARN,
|
||||
[LSQLM_SPI] = LSQ_LOG_WARN,
|
||||
[LSQLM_HPI] = LSQ_LOG_WARN,
|
||||
[LSQLM_DI] = LSQ_LOG_WARN,
|
||||
[LSQLM_PRQ] = 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_ENG_HIST] = "eng-hist",
|
||||
[LSQLM_SPI] = "spi",
|
||||
[LSQLM_HPI] = "hpi",
|
||||
[LSQLM_DI] = "di",
|
||||
[LSQLM_PRQ] = "prq",
|
||||
[LSQLM_PACER] = "pacer",
|
||||
|
|
|
@ -70,6 +70,7 @@ enum lsquic_logger_module {
|
|||
LSQLM_TOKGEN,
|
||||
LSQLM_ENG_HIST,
|
||||
LSQLM_SPI,
|
||||
LSQLM_HPI,
|
||||
LSQLM_DI,
|
||||
LSQLM_PRQ,
|
||||
LSQLM_PACER,
|
||||
|
|
|
@ -44,14 +44,14 @@ heapify_min_heap (struct min_heap *heap, unsigned i)
|
|||
|
||||
|
||||
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;
|
||||
unsigned i;
|
||||
|
||||
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_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)
|
||||
{
|
||||
struct lsquic_conn *conn;
|
||||
void *item;
|
||||
|
||||
if (heap->mh_nelem == 0)
|
||||
return NULL;
|
||||
|
||||
conn = heap->mh_elems[0].mhe_conn;
|
||||
item = heap->mh_elems[0].mhe_item;
|
||||
--heap->mh_nelem;
|
||||
if (heap->mh_nelem > 0)
|
||||
{
|
||||
|
@ -83,5 +83,5 @@ lsquic_mh_pop (struct min_heap *heap)
|
|||
heapify_min_heap(heap, 0);
|
||||
}
|
||||
|
||||
return conn;
|
||||
return item;
|
||||
}
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
/* 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
|
||||
#define LSQUIC_MIN_HEAP_H 1
|
||||
|
||||
struct lsquic_conn;
|
||||
|
||||
struct min_heap_elem
|
||||
{
|
||||
struct lsquic_conn *mhe_conn;
|
||||
void *mhe_item;
|
||||
uint64_t mhe_val;
|
||||
};
|
||||
|
||||
|
@ -24,12 +23,12 @@ struct min_heap
|
|||
|
||||
|
||||
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 *);
|
||||
|
||||
#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)
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "lsquic_rtt.h"
|
||||
#include "lsquic_util.h"
|
||||
#include "lsquic_enc_sess.h"
|
||||
#include "lsquic_trechist.h"
|
||||
#include "lsquic_mini_conn_ietf.h"
|
||||
#include "lsquic_ev_log.h"
|
||||
#include "lsquic_trans_params.h"
|
||||
|
@ -34,6 +35,7 @@
|
|||
#include "lsquic_packet_ietf.h"
|
||||
#include "lsquic_attq.h"
|
||||
#include "lsquic_alarmset.h"
|
||||
#include "lsquic_crand.h"
|
||||
|
||||
#define LSQUIC_LOGGER_MODULE LSQLM_MINI_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;
|
||||
enum enc_level i;
|
||||
const struct enc_session_funcs_iquic *esfi;
|
||||
unsigned char rand_nybble;
|
||||
|
||||
if (!is_first_packet_ok(packet_in, udp_payload_size))
|
||||
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];
|
||||
}
|
||||
|
||||
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);
|
||||
enc_sess = esfi->esfi_create_server(enpub, &conn->imc_conn,
|
||||
&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)
|
||||
lconn->cn_esf.i->esfi_destroy(lconn->cn_enc_session);
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
static void
|
||||
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))
|
||||
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);
|
||||
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
|
||||
* is helpful when it is printed along with other diagnostics in dtor.
|
||||
*/
|
||||
if (0 == conn->imc_recvd_packnos[pns] ||
|
||||
packet_in->pi_packno > highest_bit_set(conn->imc_recvd_packnos[pns]))
|
||||
if (imico_packno_is_largest(conn, pns, packet_in->pi_packno))
|
||||
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))
|
||||
{
|
||||
|
@ -1483,24 +1601,21 @@ imico_have_packets_to_send (struct ietf_mini_conn *conn, lsquic_time_t now)
|
|||
}
|
||||
|
||||
|
||||
struct ietf_mini_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,
|
||||
void
|
||||
lsquic_imico_rechist_init (struct ietf_mini_rechist *rechist,
|
||||
const struct ietf_mini_conn *conn, enum packnum_space pns)
|
||||
{
|
||||
rechist->conn = conn;
|
||||
rechist->pns = pns;
|
||||
rechist->cur_set = 0;
|
||||
rechist->cur_idx = 0;
|
||||
if (conn->imc_flags & IMC_TRECHIST)
|
||||
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 *
|
||||
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;
|
||||
packno_set_t packnos;
|
||||
int i;
|
||||
|
||||
packnos = rechist->cur_set;
|
||||
packnos = rechist->u.bitmask.cur_set;
|
||||
if (0 == packnos)
|
||||
return NULL;
|
||||
|
||||
/* There may be a faster way to do this, but for now, we just want
|
||||
* correctness.
|
||||
*/
|
||||
for (i = rechist->cur_idx; i >= 0; --i)
|
||||
for (i = rechist->u.bitmask.cur_idx; i >= 0; --i)
|
||||
if (packnos & (1ULL << i))
|
||||
{
|
||||
rechist->range.low = i;
|
||||
rechist->range.high = i;
|
||||
rechist->u.bitmask.range.low = i;
|
||||
rechist->u.bitmask.range.high = i;
|
||||
break;
|
||||
}
|
||||
assert(i >= 0); /* We must have hit at least one bit */
|
||||
--i;
|
||||
for ( ; i >= 0 && (packnos & (1ULL << i)); --i)
|
||||
rechist->range.low = i;
|
||||
rechist->u.bitmask.range.low = i;
|
||||
if (i >= 0)
|
||||
{
|
||||
rechist->cur_set = packnos & ((1ULL << i) - 1);
|
||||
rechist->cur_idx = i;
|
||||
rechist->u.bitmask.cur_set = packnos & ((1ULL << i) - 1);
|
||||
rechist->u.bitmask.cur_idx = i;
|
||||
}
|
||||
else
|
||||
rechist->cur_set = 0;
|
||||
rechist->u.bitmask.cur_set = 0;
|
||||
LSQ_DEBUG("%s: return [%"PRIu64", %"PRIu64"]", __func__,
|
||||
rechist->range.low, rechist->range.high);
|
||||
return &rechist->range;
|
||||
rechist->u.bitmask.range.low, rechist->u.bitmask.range.high);
|
||||
return &rechist->u.bitmask.range;
|
||||
}
|
||||
|
||||
|
||||
static const struct lsquic_packno_range *
|
||||
imico_rechist_first (void *rechist_ctx)
|
||||
const struct lsquic_packno_range *
|
||||
lsquic_imico_rechist_next (void *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);
|
||||
return imico_rechist_next(rechist_ctx);
|
||||
|
||||
if (rechist->conn->imc_flags & IMC_TRECHIST)
|
||||
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;
|
||||
|
||||
/* 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(
|
||||
packet_out->po_data + packet_out->po_data_sz,
|
||||
lsquic_packet_out_avail(packet_out), imico_rechist_first,
|
||||
imico_rechist_next, imico_rechist_largest_recv, &rechist,
|
||||
lsquic_packet_out_avail(packet_out), lsquic_imico_rechist_first,
|
||||
lsquic_imico_rechist_next, imico_rechist_largest_recv, &rechist,
|
||||
now, ¬_used_has_missing, &packet_out->po_ack2ed, ecn_counts);
|
||||
if (len < 0)
|
||||
{
|
||||
|
@ -1625,7 +1759,7 @@ imico_generate_acks (struct ietf_mini_conn *conn, lsquic_time_t now)
|
|||
{
|
||||
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)
|
||||
&& !(pns == PNS_INIT && (conn->imc_flags & IMC_IGNORE_INIT)))
|
||||
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 &
|
||||
(IMC_QUEUED_ACK_INIT|IMC_QUEUED_ACK_HSK|IMC_QUEUED_ACK_APP))
|
||||
if (conn->imc_flags & (IMC_QUEUED_ACK_INIT|IMC_QUEUED_ACK_HSK))
|
||||
{
|
||||
if (0 != imico_generate_acks(conn, now))
|
||||
{
|
||||
|
|
|
@ -27,6 +27,13 @@ struct mini_crypto_stream
|
|||
typedef uint64_t packno_set_t;
|
||||
#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 lsquic_conn imc_conn;
|
||||
|
@ -37,7 +44,7 @@ struct ietf_mini_conn
|
|||
IMC_ENC_SESS_INITED = 1 << 0,
|
||||
IMC_QUEUED_ACK_INIT = 1 << 1,
|
||||
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_HSK_OK = 1 << 5,
|
||||
IMC_HSK_FAILED = 1 << 6,
|
||||
|
@ -58,6 +65,7 @@ struct ietf_mini_conn
|
|||
IMC_PARSE_FAILED = 1 << 20,
|
||||
IMC_PATH_CHANGED = 1 << 21,
|
||||
IMC_HSK_DONE_SENT = 1 << 22,
|
||||
IMC_TRECHIST = 1 << 23,
|
||||
} imc_flags;
|
||||
struct mini_crypto_stream imc_streams[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(, stream_frame) imc_crypto_frames;
|
||||
packno_set_t imc_sent_packnos;
|
||||
packno_set_t imc_recvd_packnos[N_PNS];
|
||||
packno_set_t imc_acked_packnos[N_PNS];
|
||||
lsquic_time_t imc_largest_recvd[N_PNS];
|
||||
union {
|
||||
packno_set_t bitmasks[IMICO_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;
|
||||
unsigned imc_error_code;
|
||||
unsigned imc_bytes_in;
|
||||
|
@ -90,8 +104,8 @@ struct ietf_mini_conn
|
|||
*/
|
||||
uint8_t imc_ecn_packnos;
|
||||
uint8_t imc_ack_exp;
|
||||
uint8_t imc_ecn_counts_in[N_PNS][4];
|
||||
uint8_t imc_ecn_counts_out[N_PNS][4];
|
||||
uint8_t imc_ecn_counts_in[IMICO_N_PNS][4];
|
||||
uint8_t imc_ecn_counts_out[IMICO_N_PNS][4];
|
||||
uint8_t imc_incoming_ecn;
|
||||
uint8_t imc_tls_alert;
|
||||
#define IMICO_MAX_DELAYED_PACKETS_UNVALIDATED 1u
|
||||
|
@ -121,4 +135,29 @@ lsquic_mini_conn_ietf_new (struct lsquic_engine_public *,
|
|||
|
||||
int
|
||||
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
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "lsquic_packet_common.h"
|
||||
#include "lsquic_mini_conn.h"
|
||||
#include "lsquic_enc_sess.h"
|
||||
#include "lsquic_trechist.h"
|
||||
#include "lsquic_mini_conn_ietf.h"
|
||||
#include "lsquic_packet_gquic.h"
|
||||
#include "lsquic_packet_in.h"
|
||||
|
|
|
@ -27,6 +27,11 @@
|
|||
#include "lsquic_engine_public.h"
|
||||
#include "lsquic_headers.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_LOG_CONN_ID lsquic_conn_log_cid(qdh->qdh_conn)
|
||||
|
@ -39,6 +44,8 @@ struct header_ctx
|
|||
{
|
||||
void *hset;
|
||||
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 *
|
||||
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)
|
||||
(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);
|
||||
}
|
||||
|
@ -525,6 +550,15 @@ qdh_header_read_results (struct qpack_dec_hdl *qdh,
|
|||
{
|
||||
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;
|
||||
uh = &stream->sm_hblock_ctx->uh;
|
||||
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.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;
|
||||
|
||||
dec_buf_sz = sizeof(dec_buf);
|
||||
|
|
|
@ -434,3 +434,34 @@ lsquic_rechist_peek (struct lsquic_rechist *rechist)
|
|||
else
|
||||
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)
|
||||
|
||||
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
|
||||
|
|
|
@ -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
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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_APP, retx_alarm_rings, ctl);
|
||||
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)
|
||||
{
|
||||
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;
|
||||
|
||||
pns = acki->pns;
|
||||
ctl->sc_flags |= SC_ACK_RECV_INIT << pns;
|
||||
packet_out = TAILQ_FIRST(&ctl->sc_unacked_packets[pns]);
|
||||
#if __GNUC__
|
||||
__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(bytes == ctl->sc_bytes_scheduled);
|
||||
}
|
||||
|
||||
|
||||
#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;
|
||||
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);
|
||||
if (!packet_out)
|
||||
return NULL;
|
||||
|
@ -2395,6 +2412,7 @@ send_ctl_log_packet_q (const lsquic_send_ctl_t *ctl, const char *prefix,
|
|||
free(buf);
|
||||
}
|
||||
|
||||
|
||||
#define LOG_PACKET_Q(prefix, queue) do { \
|
||||
if (LSQ_LOG_ENABLED(LSQ_LOG_DEBUG)) \
|
||||
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
|
||||
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 (lsquic_send_ctl_schedule_stream_packets_immediately(ctl))
|
||||
if ((ctl->sc_flags & (SC_ACK_RECV_INIT << pns))
|
||||
&& lsquic_send_ctl_schedule_stream_packets_immediately(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);
|
||||
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_POISON = 1 << 17, /* poisoned packet exists */
|
||||
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 {
|
||||
|
@ -290,7 +293,7 @@ lsquic_send_ctl_drop_scheduled (lsquic_send_ctl_t *);
|
|||
: 0 )
|
||||
|
||||
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
|
||||
lsquic_send_ctl_schedule_buffered (lsquic_send_ctl_t *, enum buf_packet_type);
|
||||
|
|
|
@ -21,6 +21,9 @@
|
|||
#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_spi.h"
|
||||
|
||||
#define LSQUIC_LOGGER_MODULE LSQLM_SPI
|
||||
|
@ -51,17 +54,18 @@ add_stream_to_spi (struct stream_prio_iter *iter, lsquic_stream_t *stream)
|
|||
|
||||
|
||||
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,
|
||||
const struct lsquic_conn *conn,
|
||||
struct lsquic_conn_public *conn_pub,
|
||||
const char *name,
|
||||
int (*filter)(void *filter_ctx, struct lsquic_stream *),
|
||||
void *filter_ctx)
|
||||
{
|
||||
struct stream_prio_iter *const iter = iter_p;
|
||||
struct lsquic_stream *stream;
|
||||
unsigned count;
|
||||
|
||||
iter->spi_conn = conn;
|
||||
iter->spi_conn = conn_pub->lconn;
|
||||
iter->spi_name = name ? name : "UNSET";
|
||||
iter->spi_set[0] = 0;
|
||||
iter->spi_set[1] = 0;
|
||||
|
@ -194,8 +198,9 @@ find_and_set_next_priority (struct stream_prio_iter *iter)
|
|||
|
||||
|
||||
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;
|
||||
unsigned set, bit;
|
||||
|
||||
|
@ -222,8 +227,9 @@ lsquic_spi_first (struct stream_prio_iter *iter)
|
|||
|
||||
|
||||
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;
|
||||
|
||||
stream = iter->spi_next_stream;
|
||||
|
@ -303,8 +309,9 @@ spi_has_more_than_one_queue (const struct stream_prio_iter *iter)
|
|||
|
||||
|
||||
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]) ];
|
||||
unsigned bit, set, n;
|
||||
|
||||
|
@ -336,14 +343,22 @@ spi_drop_high_or_non_high (struct stream_prio_iter *iter, int drop_high)
|
|||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
lsquic_spi_cleanup (void *iter_p)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -2,20 +2,13 @@
|
|||
/*
|
||||
* lsquic_spi.h - SPI: Stream Priority Iterator
|
||||
*
|
||||
* SPI purposefully does not support switching stream priorities while
|
||||
* iterator is active, because this puts iteration termination outside
|
||||
* of our control. One can imagine (admittedly theoretical) scenario
|
||||
* in which the user keeps on switching stream priorities around and
|
||||
* causing an infinite loop.
|
||||
* Changing a stream's priority when the stream is in the iterator
|
||||
* does not change the stream's position in the iterator.
|
||||
*/
|
||||
|
||||
#ifndef LSQUIC_SPI
|
||||
#define LSQUIC_SPI 1
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum stream_q_flags;
|
||||
|
||||
|
||||
struct stream_prio_iter
|
||||
{
|
||||
|
@ -30,26 +23,26 @@ struct stream_prio_iter
|
|||
|
||||
|
||||
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,
|
||||
const struct lsquic_conn *,
|
||||
struct lsquic_conn_public *,
|
||||
const char *name,
|
||||
int (*filter)(void *filter_ctx, struct lsquic_stream *),
|
||||
void *filter_ctx);
|
||||
|
||||
struct lsquic_stream *
|
||||
lsquic_spi_first (struct stream_prio_iter *);
|
||||
lsquic_spi_first (void *);
|
||||
|
||||
struct lsquic_stream *
|
||||
lsquic_spi_next (struct stream_prio_iter *);
|
||||
lsquic_spi_next (void *);
|
||||
|
||||
void
|
||||
lsquic_spi_exhaust_on (struct stream_prio_iter *);
|
||||
lsquic_spi_drop_non_high (void *);
|
||||
|
||||
void
|
||||
lsquic_spi_drop_non_high (struct stream_prio_iter *);
|
||||
lsquic_spi_drop_high (void *);
|
||||
|
||||
void
|
||||
lsquic_spi_drop_high (struct stream_prio_iter *);
|
||||
lsquic_spi_cleanup (void *);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -74,6 +74,7 @@
|
|||
#include "lsquic_byteswap.h"
|
||||
#include "lsquic_ietf.h"
|
||||
#include "lsquic_push_promise.h"
|
||||
#include "lsquic_hcso_writer.h"
|
||||
|
||||
#define LSQUIC_LOGGER_MODULE LSQLM_STREAM
|
||||
#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
|
||||
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_DEFAULT_PRIO);
|
||||
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;
|
||||
uint64_t toread;
|
||||
int fin;
|
||||
|
||||
while ((data_frame = stream->data_in->di_if->di_get_frame(
|
||||
stream->data_in, stream->read_offset)))
|
||||
{
|
||||
fin = data_frame->df_fin;
|
||||
toread = data_frame->df_size - data_frame->df_read_off;
|
||||
stream->read_offset += toread;
|
||||
data_frame->df_read_off = data_frame->df_size;
|
||||
stream->data_in->di_if->di_frame_done(stream->data_in, data_frame);
|
||||
if (fin)
|
||||
break;
|
||||
}
|
||||
|
||||
(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 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;
|
||||
if (!(stream->conn_pub->lconn->cn_flags & LSCONN_TCID0))
|
||||
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;
|
||||
size_t nw, frame_sz, avail, rem;
|
||||
unsigned bits;
|
||||
int new;
|
||||
|
||||
while (p < end)
|
||||
{
|
||||
shf = find_cur_hq_frame(stream);
|
||||
if (shf)
|
||||
{
|
||||
new = 0;
|
||||
LSQ_DEBUG("found current HQ frame of type 0x%X at offset %"PRIu64,
|
||||
shf->shf_frame_type, shf->shf_off);
|
||||
}
|
||||
else
|
||||
{
|
||||
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,
|
||||
stream->sm_payload, HQFT_DATA, 0, rem);
|
||||
if (shf)
|
||||
{
|
||||
new = 1;
|
||||
goto insert;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* 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);
|
||||
if (frame_sz > (uintptr_t) (end - p))
|
||||
{
|
||||
if (new)
|
||||
stream_hq_frame_put(stream, shf);
|
||||
break;
|
||||
}
|
||||
|
@ -4337,6 +4354,9 @@ lsquic_stream_uh_in (lsquic_stream_t *stream, struct uncompressed_headers *uh)
|
|||
unsigned
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -4349,9 +4369,20 @@ lsquic_stream_set_priority_internal (lsquic_stream_t *stream, unsigned priority)
|
|||
*/
|
||||
if (lsquic_stream_is_critical(stream))
|
||||
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)
|
||||
return -1;
|
||||
stream->sm_priority = 256 - priority;
|
||||
}
|
||||
|
||||
lsquic_send_ctl_invalidate_bpt_cache(stream->conn_pub->send_ctl);
|
||||
LSQ_DEBUG("set priority to %u", priority);
|
||||
SM_HISTORY_APPEND(stream, SHE_SET_PRIO);
|
||||
|
@ -4379,9 +4410,16 @@ maybe_send_priority_gquic (struct lsquic_stream *stream, unsigned priority)
|
|||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -4392,7 +4430,12 @@ lsquic_stream_set_priority (lsquic_stream_t *stream, unsigned priority)
|
|||
if (0 == lsquic_stream_set_priority_internal(stream, priority))
|
||||
{
|
||||
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
|
||||
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 */
|
||||
/* [draft-ietf-quic-http-30], Section 7.2.8 */
|
||||
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:
|
||||
/* Ignore unknown frames */
|
||||
return 0;
|
||||
|
@ -5239,3 +5294,47 @@ lsquic_stream_verify_len (struct lsquic_stream *stream,
|
|||
else
|
||||
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;
|
||||
#define shf_frame_ptr shf_u.frame_ptr
|
||||
#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 {
|
||||
SHF_TWO_BYTES = 1 << 0, /* Use two byte to encode frame length */
|
||||
SHF_FIXED_SIZE = 1 << 1, /* Payload size guaranteed */
|
||||
|
@ -185,7 +185,10 @@ enum stream_b_flags
|
|||
SMBF_CONN_LIMITED = 1 << 7,
|
||||
SMBF_HEADERS = 1 << 8, /* Headers stream */
|
||||
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_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_enc_level;
|
||||
enum {
|
||||
|
@ -381,6 +387,7 @@ enum stream_ctor_flags
|
|||
SCF_HTTP = SMBF_USE_HEADERS,
|
||||
SCF_CRYPTO = SMBF_CRYPTO,
|
||||
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
|
||||
h3_framing
|
||||
hkdf
|
||||
hpi
|
||||
lsquic_hash
|
||||
packet_out
|
||||
packet_resize
|
||||
|
@ -129,3 +130,6 @@ ADD_TEST(minmax test_minmax)
|
|||
|
||||
ADD_EXECUTABLE(test_rechist test_rechist.c ../src/liblsquic/lsquic_rechist.c)
|
||||
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
|
||||
test_frame_header_split (unsigned n_packets, unsigned extra_sz,
|
||||
int add_one_more)
|
||||
|
@ -1162,6 +1173,7 @@ test_frame_header_split (unsigned n_packets, unsigned extra_sz,
|
|||
unsigned char *buf_in, *buf_out;
|
||||
const unsigned wsize = 70;
|
||||
const size_t buf_in_sz = wsize, buf_out_sz = 0x500000;
|
||||
unsigned n_frames;
|
||||
|
||||
struct lsxpack_header header = { XHDR(":method", "GET") };
|
||||
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);
|
||||
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);
|
||||
n_frames = count_hq_frames(stream);
|
||||
assert(n_frames == !!stream->sm_n_buffered);
|
||||
|
||||
if (add_one_more)
|
||||
{
|
||||
|
|
|
@ -18,6 +18,11 @@ struct test
|
|||
{
|
||||
int lineno;
|
||||
|
||||
enum {
|
||||
TEST_NO_FLAGS = 0,
|
||||
TEST_NUL_OUT_LEST_FULL = 1 << 0,
|
||||
} flags;
|
||||
|
||||
unsigned char input[0x100];
|
||||
size_t input_sz;
|
||||
|
||||
|
@ -30,6 +35,7 @@ static const struct test tests[] =
|
|||
{
|
||||
{
|
||||
__LINE__,
|
||||
TEST_NO_FLAGS,
|
||||
{
|
||||
0x03,
|
||||
0x04,
|
||||
|
@ -42,6 +48,7 @@ static const struct test tests[] =
|
|||
|
||||
{
|
||||
__LINE__,
|
||||
TEST_NO_FLAGS,
|
||||
{
|
||||
HQFT_MAX_PUSH_ID,
|
||||
0x02,
|
||||
|
@ -54,6 +61,7 @@ static const struct test tests[] =
|
|||
|
||||
{
|
||||
__LINE__,
|
||||
TEST_NO_FLAGS,
|
||||
{
|
||||
HQFT_SETTINGS,
|
||||
0x00,
|
||||
|
@ -65,6 +73,7 @@ static const struct test tests[] =
|
|||
|
||||
{ /* Frame contents do not match frame length */
|
||||
__LINE__,
|
||||
TEST_NO_FLAGS,
|
||||
{
|
||||
HQFT_MAX_PUSH_ID,
|
||||
0x03,
|
||||
|
@ -77,6 +86,7 @@ static const struct test tests[] =
|
|||
|
||||
{
|
||||
__LINE__,
|
||||
TEST_NO_FLAGS,
|
||||
{
|
||||
HQFT_SETTINGS,
|
||||
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);
|
||||
}
|
||||
|
||||
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 =
|
||||
{
|
||||
.on_cancel_push = on_cancel_push,
|
||||
|
@ -140,6 +220,7 @@ static const struct hcsi_callbacks callbacks =
|
|||
.on_setting = on_setting,
|
||||
.on_goaway = on_goaway,
|
||||
.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);
|
||||
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);
|
||||
lsquic_hcsi_reader_init(&reader, &lconn, &callbacks, out_f);
|
||||
|
@ -188,6 +269,10 @@ run_test (const struct test *test)
|
|||
assert(s == test->retval);
|
||||
|
||||
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));
|
||||
free(output);
|
||||
}
|
||||
|
@ -204,7 +289,7 @@ main (void)
|
|||
|
||||
memset(&coalesced_test, 0, sizeof(coalesced_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,
|
||||
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_stream.h"
|
||||
#include "lsquic_types.h"
|
||||
#include "lsquic_rtt.h"
|
||||
#include "lsquic_conn_public.h"
|
||||
#include "lsquic_spi.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_public conn_pub = { .lconn = &lconn, };
|
||||
|
||||
|
||||
static lsquic_stream_t *
|
||||
new_stream (unsigned priority)
|
||||
|
@ -73,7 +77,7 @@ test_same_priority (unsigned priority)
|
|||
lsquic_spi_init(&spi, TAILQ_FIRST(&streams),
|
||||
TAILQ_LAST(&streams, lsquic_streams_tailq),
|
||||
(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);
|
||||
assert(stream == stream_arr[0]);
|
||||
|
@ -89,7 +93,7 @@ test_same_priority (unsigned priority)
|
|||
/* Test reinitialization: */
|
||||
lsquic_spi_init(&spi, stream_arr[0], stream_arr[1],
|
||||
(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);
|
||||
assert(stream == stream_arr[0]);
|
||||
stream = lsquic_spi_next(&spi);
|
||||
|
@ -121,7 +125,7 @@ test_different_priorities (int *priority)
|
|||
lsquic_spi_init(&spi, TAILQ_FIRST(&streams),
|
||||
TAILQ_LAST(&streams, lsquic_streams_tailq),
|
||||
(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;
|
||||
stream = lsquic_spi_next(&spi), ++count)
|
||||
{
|
||||
|
@ -214,7 +218,7 @@ test_drop (const struct drop_test *test)
|
|||
lsquic_spi_init(&spi, TAILQ_FIRST(&streams),
|
||||
TAILQ_LAST(&streams, lsquic_streams_tailq),
|
||||
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_write_stream),
|
||||
&lconn, __func__, NULL, NULL);
|
||||
&conn_pub, __func__, NULL, NULL);
|
||||
|
||||
if (drop_high)
|
||||
lsquic_spi_drop_high(&spi);
|
||||
|
@ -275,7 +279,7 @@ test_different_priorities_filter_odd (int *priority)
|
|||
lsquic_spi_init(&spi, TAILQ_FIRST(&streams),
|
||||
TAILQ_LAST(&streams, lsquic_streams_tailq),
|
||||
(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;
|
||||
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…
Reference in a new issue