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)
 | 
			
		||||
        lsquic_stream_set_priority(stream, 1 + (random() & 0xFF));
 | 
			
		||||
    {
 | 
			
		||||
        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)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -728,16 +805,36 @@ http_client_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
 | 
			
		|||
                fwrite(buf, 1, nread, stdout);
 | 
			
		||||
            if (randomly_reprioritize_streams && (st_h->count++ & 0x3F) == 0)
 | 
			
		||||
            {
 | 
			
		||||
                old_prio = lsquic_stream_priority(stream);
 | 
			
		||||
                new_prio = 1 + (random() & 0xFF);
 | 
			
		||||
                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 =
 | 
			
		||||
                        const int s =
 | 
			
		||||
#endif
 | 
			
		||||
                lsquic_stream_set_priority(stream, new_prio);
 | 
			
		||||
                assert(s == 0);
 | 
			
		||||
                LSQ_DEBUG("changed stream %"PRIu64" priority from %u to %u",
 | 
			
		||||
                        lsquic_stream_set_http_prio(stream, &ehp);
 | 
			
		||||
                        assert(s == 0);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    old_prio = lsquic_stream_priority(stream);
 | 
			
		||||
                    new_prio = 1 + (random() & 0xFF);
 | 
			
		||||
#ifndef NDEBUG
 | 
			
		||||
                    const int s =
 | 
			
		||||
#endif
 | 
			
		||||
                    lsquic_stream_set_priority(stream, new_prio);
 | 
			
		||||
                    assert(s == 0);
 | 
			
		||||
                    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
 | 
			
		||||
-----------------
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2162,7 +2173,7 @@ The following log modules are defined:
 | 
			
		|||
- *hcsi-reader*: Reader of the HTTP/3 control stream.
 | 
			
		||||
- *hcso-writer*: Writer of the HTTP/3 control stream.
 | 
			
		||||
- *headers*: HEADERS stream (Google QUIC).
 | 
			
		||||
- *hsk-adapter*: 
 | 
			
		||||
- *hsk-adapter*:
 | 
			
		||||
- *http1x*: Header conversion to HTTP/1.x.
 | 
			
		||||
- *logger*: Logger.
 | 
			
		||||
- *mini-conn*: Mini connection.
 | 
			
		||||
| 
						 | 
				
			
			@ -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,85 +5,91 @@ 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 \
 | 
			
		||||
    lsquic_adaptive_cc.c \
 | 
			
		||||
    lsquic_alarmset.c \
 | 
			
		||||
    lsquic_arr.c \
 | 
			
		||||
    lsquic_attq.c \
 | 
			
		||||
    lsquic_bbr.c \
 | 
			
		||||
    lsquic_bw_sampler.c \
 | 
			
		||||
    lsquic_cfcw.c \
 | 
			
		||||
    lsquic_chsk_stream.c \
 | 
			
		||||
    lsquic_conn.c \
 | 
			
		||||
    lsquic_crand.c \
 | 
			
		||||
    lsquic_crt_compress.c \
 | 
			
		||||
    lsquic_crypto.c \
 | 
			
		||||
    lsquic_cubic.c \
 | 
			
		||||
    lsquic_di_error.c \
 | 
			
		||||
    lsquic_di_hash.c \
 | 
			
		||||
    lsquic_di_nocopy.c \
 | 
			
		||||
    lsquic_enc_sess_common.c \
 | 
			
		||||
    lsquic_enc_sess_ietf.c \
 | 
			
		||||
    lsquic_eng_hist.c \
 | 
			
		||||
    lsquic_engine.c \
 | 
			
		||||
    lsquic_ev_log.c \
 | 
			
		||||
    lsquic_frab_list.c \
 | 
			
		||||
    lsquic_frame_common.c \
 | 
			
		||||
    lsquic_frame_reader.c \
 | 
			
		||||
    lsquic_frame_writer.c \
 | 
			
		||||
    lsquic_full_conn.c \
 | 
			
		||||
    lsquic_full_conn_ietf.c \
 | 
			
		||||
    lsquic_global.c \
 | 
			
		||||
    lsquic_handshake.c \
 | 
			
		||||
    lsquic_hash.c \
 | 
			
		||||
    lsquic_hcsi_reader.c \
 | 
			
		||||
    lsquic_hcso_writer.c \
 | 
			
		||||
    lsquic_headers_stream.c \
 | 
			
		||||
    lsquic_hkdf.c \
 | 
			
		||||
    lsquic_hspack_valid.c \
 | 
			
		||||
    lsquic_http1x_if.c \
 | 
			
		||||
    lsquic_logger.c \
 | 
			
		||||
    lsquic_malo.c \
 | 
			
		||||
    lsquic_min_heap.c \
 | 
			
		||||
    lsquic_mini_conn.c \
 | 
			
		||||
    lsquic_mini_conn_ietf.c \
 | 
			
		||||
    lsquic_minmax.c \
 | 
			
		||||
    lsquic_mm.c \
 | 
			
		||||
    lsquic_pacer.c \
 | 
			
		||||
    lsquic_packet_common.c \
 | 
			
		||||
    lsquic_packet_gquic.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 \
 | 
			
		||||
    lsquic_parse_gquic_be.c \
 | 
			
		||||
    lsquic_parse_gquic_common.c \
 | 
			
		||||
    lsquic_parse_ietf_v1.c \
 | 
			
		||||
    lsquic_parse_iquic_common.c \
 | 
			
		||||
    lsquic_pr_queue.c \
 | 
			
		||||
    lsquic_purga.c \
 | 
			
		||||
    lsquic_qdec_hdl.c \
 | 
			
		||||
    lsquic_qenc_hdl.c \
 | 
			
		||||
    lsquic_qlog.c \
 | 
			
		||||
    lsquic_rtt.c \
 | 
			
		||||
    lsquic_send_ctl.c \
 | 
			
		||||
    lsquic_senhist.c \
 | 
			
		||||
    lsquic_set.c \
 | 
			
		||||
    lsquic_sfcw.c \
 | 
			
		||||
    lsquic_shsk_stream.c \
 | 
			
		||||
    lsquic_spi.c \
 | 
			
		||||
    lsquic_stock_shi.c \
 | 
			
		||||
    lsquic_str.c \
 | 
			
		||||
    lsquic_stream.c \
 | 
			
		||||
    lsquic_tokgen.c \
 | 
			
		||||
    lsquic_trans_params.c \
 | 
			
		||||
    lsquic_util.c \
 | 
			
		||||
    lsquic_varint.c \
 | 
			
		||||
    lsquic_version.c \
 | 
			
		||||
    lsquic_versions_to_string.c
 | 
			
		||||
liblsquic_a_SOURCES = \
 | 
			
		||||
	ls-qpack/lsqpack.c \
 | 
			
		||||
	lsquic_adaptive_cc.c \
 | 
			
		||||
	lsquic_alarmset.c \
 | 
			
		||||
	lsquic_arr.c \
 | 
			
		||||
	lsquic_attq.c \
 | 
			
		||||
	lsquic_bbr.c \
 | 
			
		||||
	lsquic_bw_sampler.c \
 | 
			
		||||
	lsquic_cfcw.c \
 | 
			
		||||
	lsquic_chsk_stream.c \
 | 
			
		||||
	lsquic_conn.c \
 | 
			
		||||
	lsquic_crand.c \
 | 
			
		||||
	lsquic_crt_compress.c \
 | 
			
		||||
	lsquic_crypto.c \
 | 
			
		||||
	lsquic_cubic.c \
 | 
			
		||||
	lsquic_di_error.c \
 | 
			
		||||
	lsquic_di_hash.c \
 | 
			
		||||
	lsquic_di_nocopy.c \
 | 
			
		||||
	lsquic_enc_sess_common.c \
 | 
			
		||||
	lsquic_enc_sess_ietf.c \
 | 
			
		||||
	lsquic_eng_hist.c \
 | 
			
		||||
	lsquic_engine.c \
 | 
			
		||||
	lsquic_ev_log.c \
 | 
			
		||||
	lsquic_frab_list.c \
 | 
			
		||||
	lsquic_frame_common.c \
 | 
			
		||||
	lsquic_frame_reader.c \
 | 
			
		||||
	lsquic_frame_writer.c \
 | 
			
		||||
	lsquic_full_conn.c \
 | 
			
		||||
	lsquic_full_conn_ietf.c \
 | 
			
		||||
	lsquic_global.c \
 | 
			
		||||
	lsquic_handshake.c \
 | 
			
		||||
	lsquic_hash.c \
 | 
			
		||||
	lsquic_hcsi_reader.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 \
 | 
			
		||||
	lsquic_min_heap.c \
 | 
			
		||||
	lsquic_mini_conn.c \
 | 
			
		||||
	lsquic_mini_conn_ietf.c \
 | 
			
		||||
	lsquic_minmax.c \
 | 
			
		||||
	lsquic_mm.c \
 | 
			
		||||
	lsquic_pacer.c \
 | 
			
		||||
	lsquic_packet_common.c \
 | 
			
		||||
	lsquic_packet_gquic.c \
 | 
			
		||||
	lsquic_packet_in.c \
 | 
			
		||||
	lsquic_packet_out.c \
 | 
			
		||||
	lsquic_packet_resize.c \
 | 
			
		||||
	lsquic_parse_Q046.c \
 | 
			
		||||
	lsquic_parse_Q050.c \
 | 
			
		||||
	lsquic_parse_common.c \
 | 
			
		||||
	lsquic_parse_gquic_be.c \
 | 
			
		||||
	lsquic_parse_gquic_common.c \
 | 
			
		||||
	lsquic_parse_ietf_v1.c \
 | 
			
		||||
	lsquic_parse_iquic_common.c \
 | 
			
		||||
	lsquic_pr_queue.c \
 | 
			
		||||
	lsquic_purga.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 \
 | 
			
		||||
	lsquic_set.c \
 | 
			
		||||
	lsquic_sfcw.c \
 | 
			
		||||
	lsquic_shsk_stream.c \
 | 
			
		||||
	lsquic_spi.c \
 | 
			
		||||
	lsquic_stock_shi.c \
 | 
			
		||||
	lsquic_str.c \
 | 
			
		||||
	lsquic_stream.c \
 | 
			
		||||
	lsquic_tokgen.c \
 | 
			
		||||
	lsquic_trans_params.c \
 | 
			
		||||
	lsquic_trechist.c \
 | 
			
		||||
	lsquic_util.c \
 | 
			
		||||
	lsquic_varint.c \
 | 
			
		||||
	lsquic_version.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];
 | 
			
		||||
    return decode_packno(enc_sess->esi_max_packno[pns], packno, shift);
 | 
			
		||||
    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,14 +1534,14 @@ 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)
 | 
			
		||||
            conn->ifc_rechist[pns].rh_largest_acked_received
 | 
			
		||||
        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)))
 | 
			
		||||
            goto cleanup;
 | 
			
		||||
        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,13 +116,20 @@ lsquic_hcsi_reader_feed (struct hcsi_reader *reader, const void *buf,
 | 
			
		|||
            reader->hr_nread += p - orig_p;
 | 
			
		||||
            if (0 == s)
 | 
			
		||||
            {
 | 
			
		||||
                if (reader->hr_nread != reader->hr_frame_length)
 | 
			
		||||
                switch (reader->hr_frame_type)
 | 
			
		||||
                {
 | 
			
		||||
                    reader->hr_conn->cn_if->ci_abort_error(reader->hr_conn, 1,
 | 
			
		||||
                        HEC_FRAME_ERROR,
 | 
			
		||||
                        "Frame length does not match actual payload length");
 | 
			
		||||
                    reader->hr_state = HR_ERROR;
 | 
			
		||||
                    return -1;
 | 
			
		||||
                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,
 | 
			
		||||
                            HEC_FRAME_ERROR,
 | 
			
		||||
                            "Frame length does not match actual payload length");
 | 
			
		||||
                        reader->hr_state = HR_ERROR;
 | 
			
		||||
                        return -1;
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                switch (reader->hr_frame_type)
 | 
			
		||||
                {
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
    rechist->conn = conn;
 | 
			
		||||
    rechist->pns  = pns;
 | 
			
		||||
    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,7 +440,11 @@ lsquic_stream_new (lsquic_stream_id_t id,
 | 
			
		|||
        }
 | 
			
		||||
        else
 | 
			
		||||
            stream->sm_readable = stream_readable_non_http;
 | 
			
		||||
        lsquic_stream_set_priority_internal(stream,
 | 
			
		||||
        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;
 | 
			
		||||
        stream->sm_frame_header_sz = stream_stream_frame_header_sz;
 | 
			
		||||
| 
						 | 
				
			
			@ -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,7 +2852,8 @@ 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))
 | 
			
		||||
            {
 | 
			
		||||
                stream_hq_frame_put(stream, shf);
 | 
			
		||||
                if (new)
 | 
			
		||||
                    stream_hq_frame_put(stream, shf);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            LSQ_DEBUG("insert %zu-byte HQ frame of type 0x%X at payload "
 | 
			
		||||
| 
						 | 
				
			
			@ -4337,7 +4354,10 @@ lsquic_stream_uh_in (lsquic_stream_t *stream, struct uncompressed_headers *uh)
 | 
			
		|||
unsigned
 | 
			
		||||
lsquic_stream_priority (const lsquic_stream_t *stream)
 | 
			
		||||
{
 | 
			
		||||
    return 256 - stream->sm_priority;
 | 
			
		||||
    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 (priority < 1 || priority > 256)
 | 
			
		||||
        return -1;
 | 
			
		||||
    stream->sm_priority = 256 - priority;
 | 
			
		||||
 | 
			
		||||
    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,10 +4410,17 @@ 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 */
 | 
			
		||||
    return -1;
 | 
			
		||||
    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,7 +269,11 @@ run_test (const struct test *test)
 | 
			
		|||
        assert(s == test->retval);
 | 
			
		||||
 | 
			
		||||
        fclose(out_f);
 | 
			
		||||
        assert(0 == strcmp(test->output, output));
 | 
			
		||||
        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…
	
	Add table
		Add a link
		
	
		Reference in a new issue