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
|
@ -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
|
Loading…
Add table
Add a link
Reference in a new issue