mirror of
https://gitea.invidious.io/iv-org/litespeed-quic.git
synced 2024-08-15 00:53:43 +00:00
Release 2.4.4
- [API] Add lsquic_alpn2ver() to aid parsing Alt-Svc header. - [BUGFIX] NULL dereference when H3 frame header would be split. - [BUGFIX] Do not close fixed-size H3 frame prematurely. - [BUGFIX] Allow PING frames in IETF mini conn. - [BUGFIX] Mini conns: don't send any packets after receiving CONNECTION_CLOSE. - [BUGFIX] Client migration: reserve slot for DCID from transport params. - [BUGFIX] Allow max_early_data_size=0 -- early_data might not be there. - [BUGFIX] Use an invalid stream number to reset BPT cache (zero is now a valid stream number). - [SPEC] Use FINAL_SIZE_ERROR when FIN mismatch is detected. - [OPTIMIZATION] Closed connection only gets one chance to send packets. - [OPTIMIZATION] Flush headers stream before packetizing stream data. - [OPTIMIZATION] process QPACK encoder STREAM frames immediately. - Update ls-qpack to v0.10.1.
This commit is contained in:
parent
1245d2e023
commit
662de5e197
15 changed files with 388 additions and 57 deletions
18
CHANGELOG
18
CHANGELOG
|
@ -1,3 +1,21 @@
|
|||
2019-10-08
|
||||
- 2.4.4
|
||||
- [API] Add lsquic_alpn2ver() to aid parsing Alt-Svc header.
|
||||
- [BUGFIX] NULL dereference when H3 frame header would be split.
|
||||
- [BUGFIX] Do not close fixed-size H3 frame prematurely.
|
||||
- [BUGFIX] Allow PING frames in IETF mini conn.
|
||||
- [BUGFIX] Mini conns: don't send any packets after receiving
|
||||
CONNECTION_CLOSE.
|
||||
- [BUGFIX] Client migration: reserve slot for DCID from transport params.
|
||||
- [BUGFIX] Allow max_early_data_size=0 -- early_data might not be there.
|
||||
- [BUGFIX] Use an invalid stream number to reset BPT cache (zero is now a
|
||||
valid stream number).
|
||||
- [SPEC] Use FINAL_SIZE_ERROR when FIN mismatch is detected.
|
||||
- [OPTIMIZATION] Closed connection only gets one chance to send packets.
|
||||
- [OPTIMIZATION] Flush headers stream before packetizing stream data.
|
||||
- [OPTIMIZATION] process QPACK encoder STREAM frames immediately.
|
||||
- Update ls-qpack to v0.10.1.
|
||||
|
||||
2019-09-30
|
||||
- 2.4.3
|
||||
- Add GQUIC versions to the list of h3 ALPNs for Alt-Svc header.
|
||||
|
|
|
@ -20,6 +20,8 @@ ENDIF()
|
|||
|
||||
MESSAGE(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
|
||||
|
||||
OPTION(LSQUIC_FIU "Use Fault Injection in Userspace (FIU)" OFF)
|
||||
|
||||
|
||||
IF (NOT MSVC)
|
||||
|
||||
|
@ -35,6 +37,11 @@ IF(CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9.3)
|
|||
SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -Wno-missing-field-initializers")
|
||||
ENDIF()
|
||||
|
||||
IF(LSQUIC_FIU)
|
||||
SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -DFIU_ENABLE=1")
|
||||
SET(LIBS ${LIBS} fiu)
|
||||
ENDIF()
|
||||
|
||||
IF(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -O0 -g3")
|
||||
SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -Werror")
|
||||
|
@ -42,11 +49,8 @@ IF(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
|||
NOT "$ENV{TRAVIS}" MATCHES "^true$")
|
||||
SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -fsanitize=address")
|
||||
ENDIF()
|
||||
# Uncomment to enable fault injection testing via libfiu:
|
||||
#SET (MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -DFIU_ENABLE=1")
|
||||
# Uncomment to enable cleartext protocol mode (no crypto):
|
||||
#SET (MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -DLSQUIC_ENABLE_HANDSHAKE_DISABLE=1")
|
||||
#SET(LIBS ${LIBS} fiu)
|
||||
ELSE()
|
||||
SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -O3 -g0")
|
||||
# Comment out the following line to compile out debug messages:
|
||||
|
@ -187,7 +191,7 @@ ENDIF()
|
|||
IF (CMAKE_SYSTEM_NAME STREQUAL Windows)
|
||||
FIND_LIBRARY(EVENT_LIB event)
|
||||
ELSE()
|
||||
FIND_LIBRARY(EVENT_LIB libevent.a)
|
||||
FIND_LIBRARY(EVENT_LIB libevent.a libevent.so)
|
||||
ENDIF()
|
||||
IF(EVENT_LIB)
|
||||
MESSAGE(STATUS "Found event: ${EVENT_LIB}")
|
||||
|
|
|
@ -25,7 +25,7 @@ extern "C" {
|
|||
|
||||
#define LSQUIC_MAJOR_VERSION 2
|
||||
#define LSQUIC_MINOR_VERSION 4
|
||||
#define LSQUIC_PATCH_VERSION 3
|
||||
#define LSQUIC_PATCH_VERSION 4
|
||||
|
||||
/**
|
||||
* Engine flags:
|
||||
|
@ -1505,6 +1505,10 @@ lsquic_conn_crypto_cipher (const lsquic_conn_t *c);
|
|||
enum lsquic_version
|
||||
lsquic_str2ver (const char *str, size_t len);
|
||||
|
||||
/** Translate ALPN (e.g. "h3", "h3-23", "h3-Q046") to LSQUIC enum */
|
||||
enum lsquic_version
|
||||
lsquic_alpn2ver (const char *alpn, size_t len);
|
||||
|
||||
/**
|
||||
* This function closes all mini connections and marks all full connection
|
||||
* as going away. In server mode, this also causes the engine to stop
|
||||
|
|
|
@ -37,6 +37,7 @@ print OUT <<C_CODE;
|
|||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lsquic.h"
|
||||
|
||||
|
@ -127,4 +128,37 @@ lsquic_get_h3_alpns (unsigned versions)
|
|||
C_CODE
|
||||
|
||||
|
||||
print OUT <<'C_CODE';
|
||||
|
||||
enum lsquic_version
|
||||
lsquic_alpn2ver (const char *alpn, size_t len)
|
||||
{
|
||||
static const struct el {
|
||||
size_t len;
|
||||
char alpn[10];
|
||||
enum lsquic_version version;
|
||||
} map[] = {
|
||||
C_CODE
|
||||
|
||||
for ($i = 0; $i < @versions; ++$i) {
|
||||
print OUT " {sizeof(\"h3-Q0$versions[$i]\")-1,\"h3-Q0$versions[$i]\", $enums[$i]},\n";
|
||||
}
|
||||
|
||||
for ($i = 0; $i < @draft_versions; ++$i) {
|
||||
print OUT " {sizeof(\"h3-$draft_versions[$i]\")-1,\"h3-$draft_versions[$i]\", LSQVER_ID$draft_versions[$i]},\n";
|
||||
}
|
||||
|
||||
print OUT <<'C_CODE';
|
||||
};
|
||||
const struct el *el;
|
||||
|
||||
if (alpn)
|
||||
for (el = map; el < map + sizeof(map) / sizeof(map[0]); ++el)
|
||||
if (el->len == len && 0 == strncmp(el->alpn, alpn, len))
|
||||
return el->version;
|
||||
|
||||
return -1;
|
||||
}
|
||||
C_CODE
|
||||
|
||||
close OUT;
|
||||
|
|
|
@ -1154,8 +1154,13 @@ iquic_new_session_cb (SSL *ssl, SSL_SESSION *session)
|
|||
assert(enc_sess->esi_enpub->enp_stream_if->on_zero_rtt_info);
|
||||
|
||||
max_early_data_size = SSL_SESSION_get_max_early_data_size(session);
|
||||
if (0xFFFFFFFFu != max_early_data_size)
|
||||
if (max_early_data_size && 0xFFFFFFFFu != max_early_data_size)
|
||||
{
|
||||
/* XXX We do not catch the case when early_data extension is present
|
||||
* and max_early_data_size is set to zero, which is an invalid value.
|
||||
* This is because there is no way to check this using existing
|
||||
* BoringSSL APIs.
|
||||
*/
|
||||
/* See [draft-ietf-quic-tls-23], Section 4.5 */
|
||||
LSQ_INFO("max_early_data_size=0x%X, protocol violation",
|
||||
max_early_data_size);
|
||||
|
|
|
@ -1922,7 +1922,10 @@ coi_reheap (struct conns_out_iter *iter, lsquic_engine_t *engine)
|
|||
{
|
||||
TAILQ_REMOVE(&iter->coi_active_list, conn, cn_next_out);
|
||||
conn->cn_flags &= ~LSCONN_COI_ACTIVE;
|
||||
lsquic_mh_insert(iter->coi_heap, conn, conn->cn_last_sent);
|
||||
if ((conn->cn_flags & CONN_REF_FLAGS) != LSCONN_HAS_OUTGOING)
|
||||
lsquic_mh_insert(iter->coi_heap, conn, conn->cn_last_sent);
|
||||
else /* Closed connection gets one shot at sending packets */
|
||||
(void) engine_decref_conn(engine, conn, LSCONN_HAS_OUTGOING);
|
||||
}
|
||||
while ((conn = TAILQ_FIRST(&iter->coi_inactive_list)))
|
||||
{
|
||||
|
|
|
@ -4507,6 +4507,12 @@ process_stream_frame (struct ietf_full_conn *conn,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Don't wait for the regular on_read dispatch in order to save an
|
||||
* unnecessary blocked/unblocked sequence.
|
||||
*/
|
||||
if ((conn->ifc_flags & IFC_HTTP) && conn->ifc_qdh.qdh_enc_sm_in == stream)
|
||||
lsquic_stream_dispatch_read_events(conn->ifc_qdh.qdh_enc_sm_in);
|
||||
|
||||
return parsed_len;
|
||||
}
|
||||
|
||||
|
@ -4809,6 +4815,30 @@ retire_dcids_prior_to (struct ietf_full_conn *conn, unsigned retire_prior_to)
|
|||
}
|
||||
|
||||
|
||||
/* We need to be able to allocate a DCE slot to begin migration or to retire
|
||||
* the DCID in transport parameters.
|
||||
*/
|
||||
static int
|
||||
must_reserve_one_dce_slot (struct ietf_full_conn *conn)
|
||||
{
|
||||
struct lsquic_conn *const lconn = &conn->ifc_conn;
|
||||
const struct transport_params *params;
|
||||
|
||||
if (conn->ifc_flags & IFC_SERVER)
|
||||
return 0;
|
||||
|
||||
if (lsquic_send_ctl_1rtt_acked(&conn->ifc_send_ctl))
|
||||
return 0;
|
||||
|
||||
params = lconn->cn_esf.i->esfi_get_peer_transport_params(
|
||||
lconn->cn_enc_session);
|
||||
if (params) /* Just in case */
|
||||
return !!(params->tp_flags & (TRAPA_PREFADDR_IPv4|TRAPA_PREFADDR_IPv6));
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static unsigned
|
||||
process_new_connection_id_frame (struct ietf_full_conn *conn,
|
||||
struct lsquic_packet_in *packet_in, const unsigned char *p, size_t len)
|
||||
|
@ -4883,6 +4913,16 @@ process_new_connection_id_frame (struct ietf_full_conn *conn,
|
|||
|
||||
if (dce)
|
||||
{
|
||||
if (must_reserve_one_dce_slot(conn))
|
||||
{
|
||||
for (el = dce + 1; el < DCES_END(conn) && *el; ++el)
|
||||
;
|
||||
if (el == DCES_END(conn))
|
||||
{
|
||||
action_str = "Ignored (last slot reserved for migration)";
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
*dce = lsquic_malo_get(conn->ifc_pub.mm->malo.dcid_elem);
|
||||
if (*dce)
|
||||
{
|
||||
|
@ -4901,6 +4941,7 @@ process_new_connection_id_frame (struct ietf_full_conn *conn,
|
|||
else
|
||||
action_str = "Ignored (no slots available)";
|
||||
|
||||
end:
|
||||
LSQ_DEBUGC("Got new connection ID from peer: seq=%"PRIu64"; "
|
||||
"cid: %"CID_FMT". %s.", seqno, CID_BITS(&cid), action_str);
|
||||
return parsed_len;
|
||||
|
|
|
@ -342,6 +342,24 @@ process_blocked_frame (struct mini_conn *mc, lsquic_packet_in_t *packet_in,
|
|||
}
|
||||
|
||||
|
||||
static mconn_packno_set_t
|
||||
drop_packets_out (struct mini_conn *mc)
|
||||
{
|
||||
struct lsquic_packet_out *packet_out;
|
||||
mconn_packno_set_t in_flight = 0;
|
||||
|
||||
while ((packet_out = TAILQ_FIRST(&mc->mc_packets_out)))
|
||||
{
|
||||
TAILQ_REMOVE(&mc->mc_packets_out, packet_out, po_next);
|
||||
if (packet_out->po_flags & PO_SENT)
|
||||
in_flight |= MCONN_PACKET_MASK(packet_out->po_packno);
|
||||
mini_destroy_packet(mc, packet_out);
|
||||
}
|
||||
|
||||
return in_flight;
|
||||
}
|
||||
|
||||
|
||||
static unsigned
|
||||
process_connection_close_frame (struct mini_conn *mc,
|
||||
lsquic_packet_in_t *packet_in, const unsigned char *p, size_t len)
|
||||
|
@ -350,6 +368,8 @@ process_connection_close_frame (struct mini_conn *mc,
|
|||
uint16_t reason_len;
|
||||
uint8_t reason_off;
|
||||
int parsed_len;
|
||||
|
||||
(void) drop_packets_out(mc);
|
||||
parsed_len = mc->mc_conn.cn_pf->pf_parse_connect_close_frame(p, len,
|
||||
NULL, &error_code, &reason_len, &reason_off);
|
||||
if (parsed_len < 0)
|
||||
|
@ -1700,8 +1720,7 @@ mini_conn_ci_destroy (struct lsquic_conn *lconn)
|
|||
assert(!(lconn->cn_flags & LSCONN_HASHED));
|
||||
struct mini_conn *mc = (struct mini_conn *) lconn;
|
||||
lsquic_packet_in_t *packet_in;
|
||||
lsquic_packet_out_t *packet_out;
|
||||
mconn_packno_set_t still_deferred = 0, in_flight = 0;
|
||||
mconn_packno_set_t still_deferred = 0, in_flight;
|
||||
enum lsq_log_level log_level;
|
||||
#if LSQUIC_RECORD_INORD_HIST
|
||||
char inord_str[0x100];
|
||||
|
@ -1717,13 +1736,10 @@ mini_conn_ci_destroy (struct lsquic_conn *lconn)
|
|||
still_deferred |= MCONN_PACKET_MASK(packet_in->pi_packno);
|
||||
lsquic_packet_in_put(&mc->mc_enpub->enp_mm, packet_in);
|
||||
}
|
||||
while ((packet_out = TAILQ_FIRST(&mc->mc_packets_out)))
|
||||
{
|
||||
TAILQ_REMOVE(&mc->mc_packets_out, packet_out, po_next);
|
||||
if (packet_out->po_flags & PO_SENT)
|
||||
in_flight |= MCONN_PACKET_MASK(packet_out->po_packno);
|
||||
mini_destroy_packet(mc, packet_out);
|
||||
}
|
||||
if (TAILQ_EMPTY(&mc->mc_packets_out))
|
||||
in_flight = ~0ull; /* Indicates that packets were dropped before */
|
||||
else
|
||||
in_flight = drop_packets_out(mc);
|
||||
if (mc->mc_conn.cn_enc_session)
|
||||
mc->mc_conn.cn_esf.g->esf_destroy(mc->mc_conn.cn_enc_session);
|
||||
log_level = warning_is_warranted(mc) ? LSQ_LOG_WARN : LSQ_LOG_DEBUG;
|
||||
|
|
|
@ -798,6 +798,42 @@ imico_process_ack_frame (IMICO_PROC_FRAME_ARGS)
|
|||
}
|
||||
|
||||
|
||||
static unsigned
|
||||
imico_process_ping_frame (IMICO_PROC_FRAME_ARGS)
|
||||
{
|
||||
LSQ_DEBUG("got a PING frame, do nothing");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static unsigned
|
||||
imico_process_connection_close_frame (IMICO_PROC_FRAME_ARGS)
|
||||
{
|
||||
struct lsquic_packet_out *packet_out;
|
||||
uint64_t error_code;
|
||||
uint16_t reason_len;
|
||||
uint8_t reason_off;
|
||||
int parsed_len, app_error;
|
||||
|
||||
while ((packet_out = TAILQ_FIRST(&conn->imc_packets_out)))
|
||||
{
|
||||
TAILQ_REMOVE(&conn->imc_packets_out, packet_out, po_next);
|
||||
imico_destroy_packet(conn, packet_out);
|
||||
}
|
||||
conn->imc_flags |= IMC_CLOSE_RECVD;
|
||||
parsed_len = conn->imc_conn.cn_pf->pf_parse_connect_close_frame(p, len,
|
||||
&app_error, &error_code, &reason_len, &reason_off);
|
||||
if (parsed_len < 0)
|
||||
return 0;
|
||||
EV_LOG_CONNECTION_CLOSE_FRAME_IN(LSQUIC_LOG_CONN_ID, error_code,
|
||||
(int) reason_len, (const char *) p + reason_off);
|
||||
LSQ_INFO("Received CONNECTION_CLOSE frame (%s-level code: %"PRIu64"; "
|
||||
"reason: %.*s)", app_error ? "application" : "transport",
|
||||
error_code, (int) reason_len, (const char *) p + reason_off);
|
||||
return 0; /* This shuts down the connection */
|
||||
}
|
||||
|
||||
|
||||
static unsigned
|
||||
imico_process_invalid_frame (IMICO_PROC_FRAME_ARGS)
|
||||
{
|
||||
|
@ -814,15 +850,15 @@ static unsigned (*const imico_process_frames[N_QUIC_FRAMES])
|
|||
[QUIC_FRAME_STREAM] = imico_process_stream_frame,
|
||||
[QUIC_FRAME_CRYPTO] = imico_process_crypto_frame,
|
||||
[QUIC_FRAME_ACK] = imico_process_ack_frame,
|
||||
[QUIC_FRAME_PING] = imico_process_ping_frame,
|
||||
[QUIC_FRAME_CONNECTION_CLOSE] = imico_process_connection_close_frame,
|
||||
/* XXX: Some of them are invalid, while others are unexpected. We treat
|
||||
* them the same: handshake cannot proceed.
|
||||
*/
|
||||
[QUIC_FRAME_RST_STREAM] = imico_process_invalid_frame,
|
||||
[QUIC_FRAME_CONNECTION_CLOSE] = imico_process_invalid_frame,
|
||||
[QUIC_FRAME_MAX_DATA] = imico_process_invalid_frame,
|
||||
[QUIC_FRAME_MAX_STREAM_DATA] = imico_process_invalid_frame,
|
||||
[QUIC_FRAME_MAX_STREAMS] = imico_process_invalid_frame,
|
||||
[QUIC_FRAME_PING] = imico_process_invalid_frame,
|
||||
[QUIC_FRAME_BLOCKED] = imico_process_invalid_frame,
|
||||
[QUIC_FRAME_STREAM_BLOCKED] = imico_process_invalid_frame,
|
||||
[QUIC_FRAME_STREAMS_BLOCKED] = imico_process_invalid_frame,
|
||||
|
@ -1422,7 +1458,8 @@ ietf_mini_conn_ci_tick (struct lsquic_conn *lconn, lsquic_time_t now)
|
|||
|
||||
if (conn->imc_flags & IMC_ERROR)
|
||||
{
|
||||
imico_generate_conn_close(conn);
|
||||
if (!(conn->imc_flags & IMC_CLOSE_RECVD))
|
||||
imico_generate_conn_close(conn);
|
||||
tick |= TICK_CLOSE;
|
||||
}
|
||||
else if (conn->imc_flags & IMC_HSK_OK)
|
||||
|
|
|
@ -54,6 +54,7 @@ struct ietf_mini_conn
|
|||
IMC_BAD_TRANS_PARAMS = 1 << 16,
|
||||
IMC_ADDR_VALIDATED = 1 << 17,
|
||||
IMC_HSK_PACKET_SENT = 1 << 18,
|
||||
IMC_CLOSE_RECVD = 1 << 19,
|
||||
} imc_flags;
|
||||
struct mini_crypto_stream imc_streams[N_ENC_LEVS];
|
||||
void *imc_stream_ps[N_ENC_LEVS];
|
||||
|
|
|
@ -345,6 +345,7 @@ lsquic_send_ctl_init (lsquic_send_ctl_t *ctl, struct lsquic_alarmset *alset,
|
|||
sizeof(ctl->sc_buffered_packets[0]); ++i)
|
||||
TAILQ_INIT(&ctl->sc_buffered_packets[i].bpq_packets);
|
||||
ctl->sc_max_packno_bits = PACKNO_BITS_2; /* Safe value before verneg */
|
||||
ctl->sc_cached_bpt.stream_id = UINT64_MAX;
|
||||
}
|
||||
|
||||
|
||||
|
@ -2347,16 +2348,6 @@ lsquic_send_ctl_get_packet_for_stream (lsquic_send_ctl_t *ctl,
|
|||
}
|
||||
|
||||
|
||||
|
||||
int
|
||||
lsquic_send_ctl_buffered_and_same_prio_as_headers (struct lsquic_send_ctl *ctl,
|
||||
const struct lsquic_stream *stream)
|
||||
{
|
||||
return !lsquic_send_ctl_schedule_stream_packets_immediately(ctl)
|
||||
&& BPT_HIGHEST_PRIO == send_ctl_lookup_bpt(ctl, stream);
|
||||
}
|
||||
|
||||
|
||||
#ifdef NDEBUG
|
||||
static
|
||||
#elif __GNUC__
|
||||
|
|
|
@ -282,7 +282,7 @@ lsquic_send_ctl_schedule_buffered (lsquic_send_ctl_t *, enum buf_packet_type);
|
|||
|| TAILQ_FIRST(&(ctl)->sc_buffered_packets[BPT_OTHER_PRIO].bpq_packets ))
|
||||
|
||||
#define lsquic_send_ctl_invalidate_bpt_cache(ctl) do { \
|
||||
(ctl)->sc_cached_bpt.stream_id = 0; \
|
||||
(ctl)->sc_cached_bpt.stream_id = UINT64_MAX; \
|
||||
} while (0)
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
@ -328,11 +328,6 @@ lsquic_send_ctl_pacer_blocked (struct lsquic_send_ctl *);
|
|||
int
|
||||
lsquic_send_ctl_sched_is_blocked (struct lsquic_send_ctl *);
|
||||
|
||||
int
|
||||
|
||||
lsquic_send_ctl_buffered_and_same_prio_as_headers (struct lsquic_send_ctl *,
|
||||
const struct lsquic_stream *);
|
||||
|
||||
void
|
||||
lsquic_send_ctl_verneg_done (struct lsquic_send_ctl *);
|
||||
|
||||
|
|
|
@ -870,6 +870,7 @@ lsquic_stream_frame_in (lsquic_stream_t *stream, stream_frame_t *frame)
|
|||
uint64_t max_off;
|
||||
int got_next_offset, rv, free_frame;
|
||||
enum ins_frame ins_frame;
|
||||
struct lsquic_conn *lconn;
|
||||
|
||||
assert(frame->packet_in);
|
||||
|
||||
|
@ -885,6 +886,18 @@ lsquic_stream_frame_in (lsquic_stream_t *stream, stream_frame_t *frame)
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (frame->data_frame.df_fin && (stream->sm_bflags & SMBF_IETF)
|
||||
&& (stream->stream_flags & STREAM_FIN_RECVD)
|
||||
&& stream->sm_fin_off != DF_END(frame))
|
||||
{
|
||||
lconn = stream->conn_pub->lconn;
|
||||
lconn->cn_if->ci_abort_error(lconn, 0, TEC_FINAL_SIZE_ERROR,
|
||||
"new final size %"PRIu64" from STREAM frame (id: %"PRIu64") does "
|
||||
"not match previous final size %"PRIu64, DF_END(frame),
|
||||
stream->id, stream->sm_fin_off);
|
||||
return -1;
|
||||
}
|
||||
|
||||
got_next_offset = frame->data_frame.df_offset == stream->read_offset;
|
||||
insert_frame:
|
||||
ins_frame = stream->data_in->di_if->di_insert_frame(stream->data_in, frame, stream->read_offset);
|
||||
|
@ -981,6 +994,19 @@ int
|
|||
lsquic_stream_rst_in (lsquic_stream_t *stream, uint64_t offset,
|
||||
uint64_t error_code)
|
||||
{
|
||||
struct lsquic_conn *lconn;
|
||||
|
||||
if ((stream->sm_bflags & SMBF_IETF)
|
||||
&& (stream->stream_flags & STREAM_FIN_RECVD)
|
||||
&& stream->sm_fin_off != offset)
|
||||
{
|
||||
lconn = stream->conn_pub->lconn;
|
||||
lconn->cn_if->ci_abort_error(lconn, 0, TEC_FINAL_SIZE_ERROR,
|
||||
"final size %"PRIu64" from RESET_STREAM frame (id: %"PRIu64") "
|
||||
"does not match previous final size %"PRIu64, offset,
|
||||
stream->id, stream->sm_fin_off);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (stream->stream_flags & STREAM_RST_RECVD)
|
||||
{
|
||||
|
@ -2502,12 +2528,18 @@ stream_activate_hq_frame (struct lsquic_stream *stream, uint64_t off,
|
|||
shf->shf_flags |= flags;
|
||||
shf->shf_frame_type = frame_type;
|
||||
if (shf->shf_flags & SHF_FIXED_SIZE)
|
||||
{
|
||||
shf->shf_frame_size = size;
|
||||
LSQ_DEBUG("activated fixed-size HQ frame of type 0x%X at offset "
|
||||
"%"PRIu64", size %zu", shf->shf_frame_type, shf->shf_off, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
shf->shf_frame_ptr = NULL;
|
||||
if (size >= (1 << 6))
|
||||
shf->shf_flags |= SHF_TWO_BYTES;
|
||||
LSQ_DEBUG("activated variable-size HQ frame of type 0x%X at offset "
|
||||
"%"PRIu64, shf->shf_frame_type, shf->shf_off);
|
||||
}
|
||||
|
||||
return shf;
|
||||
|
@ -2528,7 +2560,10 @@ frame_hq_gen_read (void *ctx, void *begin_buf, size_t len, int *fin)
|
|||
while (p < end)
|
||||
{
|
||||
shf = find_cur_hq_frame(stream);
|
||||
if (!shf)
|
||||
if (shf)
|
||||
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);
|
||||
if (rem)
|
||||
|
@ -2537,7 +2572,11 @@ frame_hq_gen_read (void *ctx, void *begin_buf, size_t len, int *fin)
|
|||
rem = (1 << 14) - 1;
|
||||
shf = stream_activate_hq_frame(stream,
|
||||
stream->sm_payload, HQFT_DATA, 0, rem);
|
||||
/* XXX malloc can fail */
|
||||
if (!shf)
|
||||
{
|
||||
/* TODO: abort connection? Handle failure somehow */
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
break;
|
||||
|
@ -2548,7 +2587,10 @@ 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);
|
||||
break;
|
||||
}
|
||||
LSQ_DEBUG("insert %zu-byte HQ frame of type 0x%X at payload "
|
||||
"offset %"PRIu64" (actual offset %"PRIu64")", frame_sz,
|
||||
shf->shf_frame_type, stream->sm_payload, stream->tosend_off);
|
||||
|
@ -2705,32 +2747,43 @@ stream_write_to_packet_std (struct frame_gen_ctx *fg_ctx, const size_t size)
|
|||
struct lsquic_send_ctl *const send_ctl = stream->conn_pub->send_ctl;
|
||||
unsigned stream_header_sz, need_at_least;
|
||||
struct lsquic_packet_out *packet_out;
|
||||
struct lsquic_stream *headers_stream;
|
||||
int len;
|
||||
|
||||
if (!(stream->sm_bflags & SMBF_IETF)
|
||||
&& (stream->stream_flags &
|
||||
(STREAM_HEADERS_SENT|STREAM_HDRS_FLUSHED))
|
||||
== STREAM_HEADERS_SENT
|
||||
&& lsquic_send_ctl_buffered_and_same_prio_as_headers(send_ctl, stream))
|
||||
if ((stream->stream_flags & (STREAM_HEADERS_SENT|STREAM_HDRS_FLUSHED))
|
||||
== STREAM_HEADERS_SENT)
|
||||
{
|
||||
/* TODO: make this logic work for IETF streams as well XXX */
|
||||
struct lsquic_stream *const headers_stream
|
||||
= lsquic_headers_stream_get_stream(stream->conn_pub->u.gquic.hs);
|
||||
if (lsquic_stream_has_data_to_flush(headers_stream))
|
||||
/* Optimization idea: the QPACK encoder stream needs only be flushed
|
||||
* if the headers in this stream are dependent on the buffered encoder
|
||||
* stream bytes. Knowing this would require changes to ls-qpack. For
|
||||
* this reason, we don't perform this check and just flush it.
|
||||
*/
|
||||
if (stream->sm_bflags & SMBF_IETF)
|
||||
headers_stream = stream->conn_pub->u.ietf.qeh->qeh_enc_sm_out;
|
||||
else
|
||||
headers_stream =
|
||||
lsquic_headers_stream_get_stream(stream->conn_pub->u.gquic.hs);
|
||||
if (headers_stream && lsquic_stream_has_data_to_flush(headers_stream))
|
||||
{
|
||||
LSQ_DEBUG("flushing headers stream before potential write to a "
|
||||
"buffered packet");
|
||||
LSQ_DEBUG("flushing headers stream before packetizing stream data");
|
||||
(void) lsquic_stream_flush(headers_stream);
|
||||
}
|
||||
else
|
||||
/* Some other stream must have flushed it: this means our headers
|
||||
* are flushed.
|
||||
*/
|
||||
stream->stream_flags |= STREAM_HDRS_FLUSHED;
|
||||
/* If there is nothing to flush, some other stream must have flushed it:
|
||||
* this means our headers are flushed. Either way, only do this once.
|
||||
*/
|
||||
stream->stream_flags |= STREAM_HDRS_FLUSHED;
|
||||
}
|
||||
|
||||
stream_header_sz = stream->sm_frame_header_sz(stream, size);
|
||||
need_at_least = stream_header_sz + (size > 0);
|
||||
need_at_least = stream_header_sz;
|
||||
if ((stream->sm_bflags & (SMBF_IETF|SMBF_USE_HEADERS))
|
||||
== (SMBF_IETF|SMBF_USE_HEADERS))
|
||||
{
|
||||
if (size > 0)
|
||||
need_at_least += 3; /* Enough room for HTTP/3 frame */
|
||||
}
|
||||
else
|
||||
need_at_least += size > 0;
|
||||
get_packet:
|
||||
packet_out = lsquic_send_ctl_get_packet_for_stream(send_ctl,
|
||||
need_at_least, stream->conn_pub->path, stream);
|
||||
|
@ -2826,13 +2879,14 @@ maybe_close_varsize_hq_frame (struct lsquic_stream *stream)
|
|||
|
||||
if (shf->shf_flags & SHF_FIXED_SIZE)
|
||||
{
|
||||
stream_hq_frame_put(stream, shf);
|
||||
if (shf->shf_off + shf->shf_frame_size <= stream->sm_payload)
|
||||
stream_hq_frame_put(stream, shf);
|
||||
return;
|
||||
}
|
||||
|
||||
bits = (shf->shf_flags & SHF_TWO_BYTES) > 0;
|
||||
size = stream->sm_payload + stream->sm_n_buffered - shf->shf_off;
|
||||
if (size && size <= VINT_MAX_B(bits))
|
||||
if (size && size <= VINT_MAX_B(bits) && shf->shf_frame_ptr)
|
||||
{
|
||||
if (0 == stream->sm_n_buffered)
|
||||
LSQ_DEBUG("close HQ frame type 0x%X of size %"PRIu64,
|
||||
|
@ -2850,6 +2904,14 @@ maybe_close_varsize_hq_frame (struct lsquic_stream *stream)
|
|||
shf->shf_flags |= SHF_FIXED_SIZE;
|
||||
}
|
||||
}
|
||||
else if (!shf->shf_frame_ptr)
|
||||
{
|
||||
LSQ_WARN("dangling HTTP/3 frame");
|
||||
stream->conn_pub->lconn->cn_if->ci_internal_error(
|
||||
stream->conn_pub->lconn, "dangling HTTP/3 frame on stream %"PRIu64,
|
||||
stream->id);
|
||||
stream_hq_frame_put(stream, shf);
|
||||
}
|
||||
else if (!size)
|
||||
{
|
||||
assert(!shf->shf_frame_ptr);
|
||||
|
|
|
@ -1677,7 +1677,13 @@ set_engine_option (struct lsquic_engine_settings *settings,
|
|||
*version_cleared = 1;
|
||||
settings->es_versions = 0;
|
||||
}
|
||||
const enum lsquic_version ver = lsquic_str2ver(val, strlen(val));
|
||||
enum lsquic_version ver = lsquic_str2ver(val, strlen(val));
|
||||
if (ver < N_LSQVER)
|
||||
{
|
||||
settings->es_versions |= 1 << ver;
|
||||
return 0;
|
||||
}
|
||||
ver = lsquic_alpn2ver(val, strlen(val));
|
||||
if (ver < N_LSQVER)
|
||||
{
|
||||
settings->es_versions |= 1 << ver;
|
||||
|
|
|
@ -644,6 +644,118 @@ main_test_hq_framing (void)
|
|||
}
|
||||
|
||||
|
||||
static void
|
||||
test_frame_header_split (unsigned n_packets)
|
||||
{
|
||||
struct test_objs tobjs;
|
||||
struct lsquic_stream *stream;
|
||||
size_t nw;
|
||||
int fin, s;
|
||||
unsigned char *buf_in, *buf_out;
|
||||
const unsigned wsize = 70;
|
||||
const size_t buf_in_sz = wsize, buf_out_sz = 0x500000;
|
||||
|
||||
struct lsquic_http_header header = {
|
||||
.name = { ":method", 7, },
|
||||
.value = { "GET", 3, },
|
||||
};
|
||||
struct lsquic_http_headers headers = { 1, &header, };
|
||||
|
||||
buf_in = malloc(buf_in_sz);
|
||||
buf_out = malloc(buf_out_sz);
|
||||
assert(buf_in && buf_out);
|
||||
|
||||
struct packetization_test_stream_ctx packet_stream_ctx =
|
||||
{
|
||||
.buf = buf_in,
|
||||
.off = 0,
|
||||
.len = buf_in_sz,
|
||||
.write_size = wsize,
|
||||
.flush_after_each_write = 0,
|
||||
};
|
||||
|
||||
init_buf(buf_in, buf_in_sz);
|
||||
|
||||
init_test_ctl_settings(&g_ctl_settings);
|
||||
g_ctl_settings.tcs_schedule_stream_packets_immediately = 1;
|
||||
|
||||
stream_ctor_flags |= SCF_IETF;
|
||||
init_test_objs(&tobjs, 0x1000, buf_out_sz, 1252);
|
||||
tobjs.stream_if_ctx = &packet_stream_ctx;
|
||||
tobjs.ctor_flags |= SCF_HTTP|SCF_IETF;
|
||||
|
||||
g_ctl_settings.tcs_can_send = n_packets;
|
||||
tobjs.stream_if = &packetization_inside_once_stream_if;
|
||||
tobjs.ctor_flags |= SCF_DISP_RW_ONCE;
|
||||
|
||||
struct lsquic_packet_out *const packet_out
|
||||
= lsquic_send_ctl_new_packet_out(&tobjs.send_ctl, 0, PNS_APP, &network_path);
|
||||
|
||||
const size_t pad_size = packet_out->po_n_alloc
|
||||
- 2 /* STREAM header */
|
||||
- 5 /* 3-byte HEADERS frame */
|
||||
- 2;
|
||||
packet_out->po_data_sz = pad_size;
|
||||
lsquic_send_ctl_scheduled_one(&tobjs.send_ctl, packet_out);
|
||||
|
||||
stream = new_stream(&tobjs, 0, buf_out_sz);
|
||||
|
||||
s = lsquic_stream_send_headers(stream, &headers, 0);
|
||||
assert(0 == s);
|
||||
|
||||
const ssize_t w = lsquic_stream_write(stream, buf_in, buf_in_sz);
|
||||
assert(w >= 0 && (size_t) w == buf_in_sz);
|
||||
lsquic_stream_flush(stream);
|
||||
|
||||
/* Verify written data: */
|
||||
nw = read_from_scheduled_packets(&tobjs.send_ctl, 0, buf_out, buf_out_sz,
|
||||
0, &fin, 1);
|
||||
{ /* Remove framing and verify contents */
|
||||
const unsigned char *src;
|
||||
unsigned char *dst;
|
||||
uint64_t sz;
|
||||
unsigned frame_type;
|
||||
int s;
|
||||
|
||||
src = buf_out;
|
||||
dst = buf_out;
|
||||
while (src < buf_out + nw)
|
||||
{
|
||||
frame_type = *src++;
|
||||
s = vint_read(src, buf_out + buf_out_sz, &sz);
|
||||
assert(s > 0);
|
||||
assert(sz > 0);
|
||||
assert(sz < (1 << 14));
|
||||
src += s;
|
||||
if (src == buf_out + s + 1)
|
||||
{
|
||||
/* Ignore headers */
|
||||
assert(frame_type == HQFT_HEADERS);
|
||||
src += sz;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(frame_type == HQFT_DATA);
|
||||
if (src + sz > buf_out + nw) /* Chopped DATA frame (last) */
|
||||
sz = buf_out + nw - src;
|
||||
memmove(dst, src, sz);
|
||||
dst += sz;
|
||||
src += sz;
|
||||
}
|
||||
}
|
||||
assert(0 == memcmp(buf_in, buf_out, (uintptr_t) dst - (uintptr_t) buf_out));
|
||||
}
|
||||
|
||||
lsquic_stream_destroy(stream);
|
||||
deinit_test_objs(&tobjs);
|
||||
free(buf_in);
|
||||
free(buf_out);
|
||||
|
||||
stream_ctor_flags &= ~SCF_IETF;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
|
@ -667,6 +779,8 @@ main (int argc, char **argv)
|
|||
init_test_ctl_settings(&g_ctl_settings);
|
||||
|
||||
main_test_hq_framing();
|
||||
test_frame_header_split(1);
|
||||
test_frame_header_split(2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue