mirror of
https://gitea.invidious.io/iv-org/litespeed-quic.git
synced 2024-08-15 00:53:43 +00:00
Release 2.19.9
- [FEATURE] Add lsquic_stream_pwritev(). This function allows one to reduce the number of system calls required to read a file from disk by using lsquic_stream_pwritev() together with preadv(2). - [BUGFIX] When stream is reset, it is writeable -- let user collect the error. - [BUGFIX] Calculate correct conn flow control if reading ends early. - [BUGFIX] Remove stream from read and write queues on internal shutdown. This is a regression introduced in 2.19.7. - [BUGFIX] Swapped arguments in IETF RESET_FRAME generation. - Turn off mini conn history when compiling with Visual Studio; this allows the project to compile on Windows again. - http_client: Add -3 flag to stop reading from streams early; code cleanup. - Don't use -Werror.
This commit is contained in:
parent
49f1f4f620
commit
2f2f436324
25 changed files with 1545 additions and 85 deletions
|
@ -59,6 +59,9 @@
|
|||
#include "lsquic_hq.h"
|
||||
#include "lsquic_data_in_if.h"
|
||||
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
static const struct parse_funcs *g_pf = select_pf_by_ver(LSQVER_ID27);
|
||||
|
||||
struct test_ctl_settings
|
||||
|
@ -270,11 +273,24 @@ struct test_objs {
|
|||
struct lsquic_hset_if hsi_if;
|
||||
};
|
||||
|
||||
static int s_ack_written;
|
||||
|
||||
static void
|
||||
write_ack (struct lsquic_conn *conn, struct lsquic_packet_out *packet_out)
|
||||
{
|
||||
/* We don't need to generate full-blown ACK, as logic in
|
||||
* lsquic_send_ctl_rollback() only looks at po_frame_types.
|
||||
*/
|
||||
packet_out->po_frame_types |= QUIC_FRAME_ACK;
|
||||
s_ack_written = 1;
|
||||
}
|
||||
|
||||
static int s_can_write_ack;
|
||||
|
||||
static int
|
||||
unit_test_doesnt_write_ack (struct lsquic_conn *lconn)
|
||||
can_write_ack (struct lsquic_conn *lconn)
|
||||
{
|
||||
return 0;
|
||||
return s_can_write_ack;
|
||||
}
|
||||
|
||||
|
||||
|
@ -286,11 +302,31 @@ get_network_path (struct lsquic_conn *lconn, const struct sockaddr *sa)
|
|||
return &network_path;
|
||||
}
|
||||
|
||||
static enum {
|
||||
SNAPSHOT_STATE_NONE = 0,
|
||||
SNAPSHOT_STATE_TAKEN = 1 << 0,
|
||||
SNAPSHOT_STATE_ROLLED_BACK = 1 << 1,
|
||||
} s_snapshot_state;
|
||||
|
||||
static void
|
||||
ack_snapshot (struct lsquic_conn *lconn, struct ack_state *ack_state)
|
||||
{
|
||||
s_snapshot_state |= SNAPSHOT_STATE_TAKEN;
|
||||
}
|
||||
|
||||
static void
|
||||
ack_rollback (struct lsquic_conn *lconn, struct ack_state *ack_state)
|
||||
{
|
||||
s_snapshot_state |= SNAPSHOT_STATE_ROLLED_BACK;
|
||||
}
|
||||
|
||||
static const struct conn_iface our_conn_if =
|
||||
{
|
||||
.ci_can_write_ack = unit_test_doesnt_write_ack,
|
||||
.ci_can_write_ack = can_write_ack,
|
||||
.ci_write_ack = write_ack,
|
||||
.ci_get_path = get_network_path,
|
||||
.ci_ack_snapshot = ack_snapshot,
|
||||
.ci_ack_rollback = ack_rollback,
|
||||
};
|
||||
|
||||
|
||||
|
@ -662,7 +698,7 @@ main_test_hq_framing (void)
|
|||
* file. This allows afl-fuzz explore the code paths.
|
||||
*/
|
||||
void
|
||||
fuzz_guided_testing (const char *input)
|
||||
fuzz_guided_hq_framing_testing (const char *input)
|
||||
{
|
||||
/* Range */ /* Bytes from file */
|
||||
unsigned short packet_sz; /* [200, 0x3FFF] */ /* 2 */
|
||||
|
@ -728,6 +764,391 @@ fuzz_guided_testing (const char *input)
|
|||
}
|
||||
|
||||
|
||||
struct pwritev_stream_ctx
|
||||
{
|
||||
int limit; /* Test limit */
|
||||
size_t avail;
|
||||
const unsigned char *input;
|
||||
size_t input_sz;
|
||||
ssize_t nw; /* Number of bytes written */
|
||||
};
|
||||
|
||||
|
||||
static ssize_t
|
||||
my_preadv (void *user_data, const struct iovec *iov, int iovcnt)
|
||||
{
|
||||
struct pwritev_stream_ctx *const pw_ctx = user_data;
|
||||
const unsigned char *p;
|
||||
size_t ntoread, tocopy;
|
||||
int i;
|
||||
|
||||
ntoread = 0;
|
||||
for (i = 0; i < iovcnt; ++i)
|
||||
ntoread += iov[i].iov_len;
|
||||
|
||||
if (pw_ctx->limit < 0)
|
||||
{
|
||||
if ((size_t) -pw_ctx->limit < ntoread)
|
||||
ntoread -= (size_t) -pw_ctx->limit;
|
||||
}
|
||||
else if ((size_t) pw_ctx->limit < ntoread)
|
||||
ntoread = (size_t) pw_ctx->limit;
|
||||
|
||||
assert(ntoread <= pw_ctx->input_sz); /* Self-check */
|
||||
|
||||
p = pw_ctx->input;
|
||||
for (i = 0; i < iovcnt; ++i)
|
||||
{
|
||||
tocopy = MIN(iov[i].iov_len, ntoread - (p - pw_ctx->input));
|
||||
memcpy(iov[i].iov_base, p, tocopy);
|
||||
p += tocopy;
|
||||
if (ntoread == (size_t) (p - pw_ctx->input))
|
||||
break;
|
||||
}
|
||||
|
||||
assert(ntoread == (size_t) (p - pw_ctx->input));
|
||||
return (ssize_t) (p - pw_ctx->input);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
pwritev_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *ctx)
|
||||
{
|
||||
struct pwritev_stream_ctx *const pw_ctx = (void *) ctx;
|
||||
ssize_t nw;
|
||||
|
||||
nw = lsquic_stream_pwritev(stream, my_preadv, pw_ctx, pw_ctx->input_sz);
|
||||
pw_ctx->nw = nw;
|
||||
lsquic_stream_wantwrite(stream, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static const struct lsquic_stream_if pwritev_stream_if = {
|
||||
.on_new_stream = packetization_on_new_stream,
|
||||
.on_close = packetization_on_close,
|
||||
.on_write = pwritev_on_write,
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
test_pwritev (enum lsquic_version version, int http, int sched_immed,
|
||||
int limit, unsigned short packet_sz, size_t prologue_sz,
|
||||
unsigned n_packets)
|
||||
{
|
||||
struct test_objs tobjs;
|
||||
struct lsquic_stream *stream;
|
||||
size_t nw;
|
||||
int fin, s;
|
||||
unsigned char *buf_in, *buf_out;
|
||||
/* Some values that are large enough: */
|
||||
const size_t buf_in_sz = MAX(n_packets * packet_sz, 0x1000),
|
||||
buf_out_sz = (float) buf_in_sz * 1.1;
|
||||
const int ietf = (1 << version) & LSQUIC_IETF_VERSIONS;
|
||||
const enum stream_ctor_flags ietf_flags = ietf ? SCF_IETF : 0;
|
||||
|
||||
s_snapshot_state = 0;
|
||||
s_ack_written = 0;
|
||||
|
||||
/* We'll write headers first after which stream will switch to using
|
||||
* data-framing writer. This is simply so that we don't have to
|
||||
* expose more stream things only for testing.
|
||||
*/
|
||||
struct lsxpack_header header = { XHDR(":method", "GET") };
|
||||
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 pwritev_stream_ctx pwritev_stream_ctx =
|
||||
{
|
||||
.input = buf_in + prologue_sz,
|
||||
.input_sz = buf_in_sz - prologue_sz,
|
||||
.limit = limit,
|
||||
};
|
||||
|
||||
init_buf(buf_in, buf_in_sz);
|
||||
|
||||
init_test_ctl_settings(&g_ctl_settings);
|
||||
g_ctl_settings.tcs_schedule_stream_packets_immediately = sched_immed;
|
||||
|
||||
stream_ctor_flags |= ietf_flags;
|
||||
init_test_objs(&tobjs, buf_out_sz, buf_out_sz, packet_sz);
|
||||
tobjs.lconn.cn_version = version;
|
||||
tobjs.lconn.cn_esf_c = select_esf_common_by_ver(version);
|
||||
tobjs.stream_if_ctx = &pwritev_stream_ctx;
|
||||
tobjs.ctor_flags |= (http ? SCF_HTTP : 0)|ietf_flags;
|
||||
if (sched_immed)
|
||||
{
|
||||
g_ctl_settings.tcs_can_send = n_packets;
|
||||
tobjs.stream_if = &pwritev_stream_if;
|
||||
}
|
||||
else
|
||||
{
|
||||
lsquic_send_ctl_set_max_bpq_count(n_packets);
|
||||
g_ctl_settings.tcs_can_send = INT_MAX;
|
||||
g_ctl_settings.tcs_bp_type = BPT_OTHER_PRIO;
|
||||
/* Need this for on_new_stream() callback not to mess with
|
||||
* the context, otherwise this is not used.
|
||||
*/
|
||||
tobjs.stream_if = &pwritev_stream_if;
|
||||
}
|
||||
|
||||
stream = new_stream(&tobjs, 0, buf_out_sz);
|
||||
|
||||
if (http)
|
||||
{
|
||||
if (ietf)
|
||||
{
|
||||
s = lsquic_stream_send_headers(stream, &headers, 0);
|
||||
assert(0 == s);
|
||||
}
|
||||
else
|
||||
/* Here we fake it in order not to have to set up frame writer. */
|
||||
stream->stream_flags |= STREAM_HEADERS_SENT;
|
||||
}
|
||||
|
||||
if (prologue_sz)
|
||||
{
|
||||
ssize_t written = lsquic_stream_write(stream, buf_in, prologue_sz);
|
||||
assert(written > 0 && (size_t) written == prologue_sz);
|
||||
}
|
||||
|
||||
if (sched_immed)
|
||||
{
|
||||
lsquic_stream_dispatch_write_events(stream);
|
||||
assert(!(s_snapshot_state & SNAPSHOT_STATE_TAKEN));
|
||||
// lsquic_stream_flush(stream);
|
||||
}
|
||||
else
|
||||
{
|
||||
pwritev_on_write(stream, (void *) &pwritev_stream_ctx);
|
||||
assert(s_snapshot_state & SNAPSHOT_STATE_TAKEN);
|
||||
if (n_packets > 0
|
||||
&& s_ack_written
|
||||
&& tobjs.send_ctl.sc_buffered_packets[BPT_OTHER_PRIO].bpq_count == 0)
|
||||
assert(s_snapshot_state & SNAPSHOT_STATE_ROLLED_BACK);
|
||||
g_ctl_settings.tcs_schedule_stream_packets_immediately = 1;
|
||||
lsquic_send_ctl_schedule_buffered(&tobjs.send_ctl, BPT_OTHER_PRIO);
|
||||
g_ctl_settings.tcs_schedule_stream_packets_immediately = 0;
|
||||
lsquic_send_ctl_set_max_bpq_count(10);
|
||||
}
|
||||
|
||||
assert(pwritev_stream_ctx.nw >= 0);
|
||||
|
||||
/* Verify written data: */
|
||||
nw = read_from_scheduled_packets(&tobjs.send_ctl, 0, buf_out, buf_out_sz,
|
||||
0, &fin, 1);
|
||||
assert(nw <= buf_in_sz);
|
||||
|
||||
if (ietf && http)
|
||||
{ /* 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);
|
||||
/* In some rare circumstances it is possible to produce zero-length
|
||||
* DATA frames:
|
||||
*
|
||||
* 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(nw <= buf_in_sz);
|
||||
if (n_packets && pwritev_stream_ctx.nw)
|
||||
{
|
||||
assert((size_t) pwritev_stream_ctx.nw + prologue_sz == (uintptr_t) dst - (uintptr_t) buf_out);
|
||||
assert(0 == memcmp(buf_in, buf_out, (uintptr_t) dst - (uintptr_t) buf_out));
|
||||
}
|
||||
else
|
||||
assert((uintptr_t) dst - (uintptr_t) buf_out == 0
|
||||
|| (uintptr_t) dst - (uintptr_t) buf_out == prologue_sz);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(nw <= buf_in_sz);
|
||||
assert(nw <= buf_out_sz);
|
||||
if (n_packets && pwritev_stream_ctx.nw)
|
||||
{
|
||||
assert((size_t) pwritev_stream_ctx.nw + prologue_sz == nw);
|
||||
assert(0 == memcmp(buf_in, buf_out, (size_t) nw));
|
||||
}
|
||||
else
|
||||
assert(nw == 0 || nw == prologue_sz);
|
||||
}
|
||||
|
||||
lsquic_stream_destroy(stream);
|
||||
deinit_test_objs(&tobjs);
|
||||
free(buf_in);
|
||||
free(buf_out);
|
||||
|
||||
stream_ctor_flags &= ~ietf_flags;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
main_test_pwritev (void)
|
||||
{
|
||||
const int limits[] = { INT_MAX, -1, -2, -3, -7, -10, -50, -100, -201, -211,
|
||||
-1000, -2003, -3000, -4000, -17803, -20000, 16 * 1024, 16 * 1024 - 1,
|
||||
16 * 1024 - 2, 8000, 273, 65, 63, 10, 5, 1, 0, };
|
||||
unsigned n_packets;
|
||||
const unsigned short packet_sz[] = { 1252, 1370, 0x1000, 0xFF00, };
|
||||
const size_t prologues[] = { 0, 17, 238, };
|
||||
unsigned i, j, k;
|
||||
enum lsquic_version version;
|
||||
int http, sched_immed;
|
||||
const struct { unsigned iovecs, frames; } combos[] =
|
||||
{
|
||||
{ 32, 16, },
|
||||
{ 16, 16, },
|
||||
{ 16, 8, },
|
||||
{ 3, 7, },
|
||||
{ 7, 3, },
|
||||
{ 100, 100, },
|
||||
}, *combo = combos;
|
||||
|
||||
s_can_write_ack = 1;
|
||||
|
||||
run_test:
|
||||
for (version = 0; version < N_LSQVER; ++version)
|
||||
if ((1 << version) & LSQUIC_SUPPORTED_VERSIONS)
|
||||
for (http = 0; http < 2; ++http)
|
||||
for (sched_immed = 0; sched_immed <= 1; ++sched_immed)
|
||||
for (i = 0; i < sizeof(limits) / sizeof(limits[i]); ++i)
|
||||
for (j = 0; j < sizeof(packet_sz) / sizeof(packet_sz[0]);
|
||||
++j)
|
||||
for (k = 0; k < sizeof(prologues) / sizeof(prologues[0]); ++k)
|
||||
for (n_packets = 1; n_packets < 21; ++n_packets)
|
||||
test_pwritev(version, http, sched_immed,
|
||||
limits[i], packet_sz[j], prologues[k], n_packets);
|
||||
|
||||
if (combo < combos + sizeof(combos) / sizeof(combos[0]))
|
||||
{
|
||||
lsquic_stream_set_pwritev_params(combo->iovecs, combo->frames);
|
||||
++combo;
|
||||
goto run_test;
|
||||
}
|
||||
|
||||
s_can_write_ack = 0;
|
||||
}
|
||||
|
||||
|
||||
/* Instead of the not-very-random testing done in main_test_pwritev(),
|
||||
* the fuzz-guided testing initializes parameters based on the fuzz input
|
||||
* file. This allows afl-fuzz explore the code paths.
|
||||
*/
|
||||
void
|
||||
fuzz_guided_pwritev_testing (const char *input)
|
||||
{
|
||||
/* Range */ /* Bytes from file */
|
||||
unsigned short packet_sz; /* [1200, 0xFF00] */ /* 2 */
|
||||
int limit; /* [INT_MIN, INT_MAX] */ /* 2 */
|
||||
unsigned n_packets; /* [0, 255] */ /* 1 */
|
||||
unsigned n_iovecs; /* [0, 255] */ /* 1 */
|
||||
unsigned n_frames; /* [0, 255] */ /* 1 */
|
||||
size_t prologue_sz; /* [0, 170] */ /* 1 */
|
||||
enum lsquic_version version;/* [0,7] */ /* 1 */
|
||||
int sched_immed; /* 0 or 1 */ /* 1 (same byte) */
|
||||
int http; /* 0 or 1 */ /* 1 (same byte) */
|
||||
|
||||
/* TOTAL: 9 bytes */
|
||||
|
||||
FILE *f;
|
||||
size_t nread;
|
||||
union {
|
||||
uint16_t tmp;
|
||||
int16_t itmp;
|
||||
} u;
|
||||
unsigned char buf[10];
|
||||
|
||||
f = fopen(input, "rb");
|
||||
if (!f)
|
||||
{
|
||||
assert(0);
|
||||
return;
|
||||
}
|
||||
|
||||
nread = fread(buf, 1, sizeof(buf), f);
|
||||
if (nread != 9)
|
||||
goto cleanup;
|
||||
|
||||
memcpy(&u.tmp, &buf[0], 2);
|
||||
if (u.tmp < 1200)
|
||||
u.tmp = 1200;
|
||||
else if (u.tmp > 0xFF00)
|
||||
u.tmp = 0xFF00;
|
||||
packet_sz = u.tmp;
|
||||
|
||||
memcpy(&u.itmp, &buf[2], 2);
|
||||
if (u.itmp < SHRT_MIN / 2)
|
||||
limit = INT_MIN;
|
||||
else if (u.itmp < SHRT_MIN / 4)
|
||||
limit = 0;
|
||||
else if (u.itmp > SHRT_MAX / 2)
|
||||
limit = INT_MAX;
|
||||
else if (u.itmp > SHRT_MAX / 2)
|
||||
limit = 0;
|
||||
else
|
||||
limit = u.itmp;
|
||||
|
||||
n_packets = buf[4];
|
||||
n_iovecs = buf[5];
|
||||
n_frames = buf[6];
|
||||
|
||||
prologue_sz = buf[7];
|
||||
if (prologue_sz > 170)
|
||||
prologue_sz = 170;
|
||||
|
||||
switch (buf[8] & 7)
|
||||
{
|
||||
case 0: version = LSQVER_043; break;
|
||||
case 1: version = LSQVER_046; break;
|
||||
case 2: version = LSQVER_050; break;
|
||||
case 3: version = LSQVER_ID27; break;
|
||||
case 4: version = LSQVER_ID28; break;
|
||||
default:
|
||||
case 5: version = LSQVER_ID29; break;
|
||||
}
|
||||
|
||||
sched_immed = !!(buf[8] & 0x08);
|
||||
http = !!(buf[8] & 0x10);
|
||||
|
||||
lsquic_stream_set_pwritev_params(n_iovecs, n_frames);
|
||||
test_pwritev(version, http, sched_immed, limit, packet_sz, prologue_sz,
|
||||
n_packets);
|
||||
|
||||
cleanup:
|
||||
(void) fclose(f);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_frame_header_split (unsigned n_packets, unsigned extra_sz,
|
||||
int add_one_more)
|
||||
|
@ -1170,18 +1591,22 @@ test_reading_zero_size_data_frame_scenario3 (void)
|
|||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
const char *fuzz_input = NULL;
|
||||
const char *fuzz_hq_framing_input = NULL;
|
||||
const char *fuzz_pwritev_input = NULL;
|
||||
int opt, add_one_more;
|
||||
unsigned n_packets, extra_sz;
|
||||
|
||||
lsquic_global_init(LSQUIC_GLOBAL_SERVER);
|
||||
|
||||
while (-1 != (opt = getopt(argc, argv, "f:l:")))
|
||||
while (-1 != (opt = getopt(argc, argv, "f:p:l:")))
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case 'f':
|
||||
fuzz_input = optarg;
|
||||
fuzz_hq_framing_input = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
fuzz_pwritev_input = optarg;
|
||||
break;
|
||||
case 'l':
|
||||
lsquic_log_to_fstream(stderr, 0);
|
||||
|
@ -1194,10 +1619,14 @@ main (int argc, char **argv)
|
|||
|
||||
init_test_ctl_settings(&g_ctl_settings);
|
||||
|
||||
if (fuzz_input)
|
||||
fuzz_guided_testing(fuzz_input);
|
||||
if (fuzz_hq_framing_input)
|
||||
fuzz_guided_hq_framing_testing(fuzz_hq_framing_input);
|
||||
else if (fuzz_pwritev_input)
|
||||
fuzz_guided_pwritev_testing(fuzz_pwritev_input);
|
||||
else
|
||||
{
|
||||
main_test_pwritev();
|
||||
return 0;
|
||||
main_test_hq_framing();
|
||||
for (n_packets = 1; n_packets <= 2; ++n_packets)
|
||||
for (extra_sz = 0; extra_sz <= 2; ++extra_sz)
|
||||
|
|
|
@ -340,6 +340,9 @@ test_flushes_and_closes (void)
|
|||
assert(s == 0);
|
||||
s = lsquic_stream_close(stream);
|
||||
assert(s == 0);
|
||||
/* OK, we did not read FIN, expect these flags: */
|
||||
assert((stream->sm_qflags & (SMQF_SEND_STOP_SENDING|SMQF_WAIT_FIN_OFF)) == (SMQF_SEND_STOP_SENDING|SMQF_WAIT_FIN_OFF));
|
||||
lsquic_stream_ss_frame_sent(stream);
|
||||
assert(stream->sm_hblock_sz == test_vals.prefix_sz + test_vals.headers_sz);
|
||||
assert(0 == stream->sm_n_buffered);
|
||||
assert(stream->sm_qflags & SMQF_WANT_WRITE); /* Still set */
|
||||
|
@ -348,6 +351,9 @@ test_flushes_and_closes (void)
|
|||
assert(stream->sm_qflags & SMQF_CALL_ONCLOSE);
|
||||
lsquic_stream_acked(stream, QUIC_FRAME_STREAM);
|
||||
lsquic_stream_call_on_close(stream);
|
||||
assert(!(stream->sm_qflags & SMQF_FREE_STREAM)); /* Not yet */
|
||||
lsquic_stream_rst_in(stream, 0, 0);
|
||||
assert(!(stream->sm_qflags & (SMQF_SEND_STOP_SENDING|SMQF_WAIT_FIN_OFF)));
|
||||
assert(stream->sm_qflags & SMQF_FREE_STREAM);
|
||||
lsquic_stream_destroy(stream);
|
||||
|
||||
|
@ -386,6 +392,7 @@ test_headers_wantwrite_restoration (const int want_write)
|
|||
assert(s == 0);
|
||||
hset = lsquic_stream_get_hset(stream);
|
||||
assert(hset == (void *) 12345);
|
||||
stream->stream_flags |= STREAM_FIN_RECVD; /* Pretend we received FIN */
|
||||
s = lsquic_stream_shutdown(stream, 0);
|
||||
assert(0 == s);
|
||||
test_vals.status = QWH_PARTIAL;
|
||||
|
|
|
@ -745,7 +745,7 @@ test_rem_FIN_loc_FIN (struct test_objs *tobjs)
|
|||
|
||||
|
||||
/* Server: we read data and close the read side before reading FIN, which
|
||||
* DOES NOT result in stream being reset.
|
||||
* results in stream being reset.
|
||||
*/
|
||||
static void
|
||||
test_rem_data_loc_close_and_rst_in (struct test_objs *tobjs)
|
||||
|
@ -765,6 +765,13 @@ test_rem_data_loc_close_and_rst_in (struct test_objs *tobjs)
|
|||
|
||||
s = lsquic_stream_shutdown(stream, 0);
|
||||
assert(0 == s);
|
||||
/* Early read shutdown results in different frames on different QUIC
|
||||
* transports:
|
||||
*/
|
||||
if (stream->sm_bflags & SMBF_IETF)
|
||||
assert(stream->sm_qflags & SMQF_SEND_STOP_SENDING);
|
||||
else
|
||||
assert(stream->sm_qflags & SMQF_SEND_RST);
|
||||
assert(TAILQ_EMPTY(&tobjs->conn_pub.service_streams));
|
||||
assert(!((stream->sm_qflags & (SMQF_SERVICE_FLAGS)) == SMQF_CALL_ONCLOSE));
|
||||
|
||||
|
@ -776,6 +783,7 @@ test_rem_data_loc_close_and_rst_in (struct test_objs *tobjs)
|
|||
assert(0 == s);
|
||||
|
||||
assert(1 == lsquic_send_ctl_n_scheduled(&tobjs->send_ctl)); /* Shutdown performs a flush */
|
||||
assert(stream->n_unacked == 1);
|
||||
|
||||
assert(!TAILQ_EMPTY(&tobjs->conn_pub.service_streams));
|
||||
assert((stream->sm_qflags & (SMQF_SERVICE_FLAGS)) == SMQF_CALL_ONCLOSE);
|
||||
|
@ -783,7 +791,17 @@ test_rem_data_loc_close_and_rst_in (struct test_objs *tobjs)
|
|||
s = lsquic_stream_rst_in(stream, 100, 1);
|
||||
assert(0 == s);
|
||||
|
||||
assert(stream->sm_qflags & SMQF_FREE_STREAM);
|
||||
assert(!(stream->sm_qflags & SMQF_FREE_STREAM)); /* Not yet */
|
||||
assert(stream->sm_qflags & SMQF_CALL_ONCLOSE);
|
||||
|
||||
lsquic_stream_rst_frame_sent(stream);
|
||||
stream->n_unacked++; /* RESET frame take a reference */
|
||||
assert(!(stream->sm_qflags & SMQF_FREE_STREAM)); /* Not yet,
|
||||
because: */ assert(stream->n_unacked == 2);
|
||||
|
||||
lsquic_stream_acked(stream, QUIC_FRAME_STREAM);
|
||||
lsquic_stream_acked(stream, QUIC_FRAME_RST_STREAM);
|
||||
assert(stream->sm_qflags & SMQF_FREE_STREAM); /* OK, now */
|
||||
|
||||
lsquic_stream_destroy(stream);
|
||||
/* This simply checks that the stream got removed from the queue: */
|
||||
|
@ -795,8 +813,8 @@ test_rem_data_loc_close_and_rst_in (struct test_objs *tobjs)
|
|||
|
||||
|
||||
/* Server: we read data and close the read side before reading FIN. No
|
||||
* FIN or RST arrive from peer. This should still place the stream on
|
||||
* the "streams to be freed" list.
|
||||
* FIN or RST arrive from peer. This should schedule RST_STREAM to be
|
||||
* sent (this is gQUIC) and add "wait for known FIN" flag.
|
||||
*/
|
||||
static void
|
||||
test_rem_data_loc_close (struct test_objs *tobjs)
|
||||
|
@ -833,7 +851,16 @@ test_rem_data_loc_close (struct test_objs *tobjs)
|
|||
|
||||
assert(!(stream->sm_qflags & SMQF_FREE_STREAM));
|
||||
lsquic_stream_acked(stream, QUIC_FRAME_STREAM);
|
||||
assert(stream->sm_qflags & SMQF_FREE_STREAM);
|
||||
|
||||
lsquic_stream_rst_frame_sent(stream);
|
||||
stream->n_unacked++; /* RESET frame take a reference */
|
||||
assert(!(stream->sm_qflags & SMQF_FREE_STREAM)); /* No */
|
||||
|
||||
lsquic_stream_acked(stream, QUIC_FRAME_RST_STREAM);
|
||||
assert(!(stream->sm_qflags & SMQF_FREE_STREAM)); /* Still no */
|
||||
|
||||
/* Stream will linger until we have the offset: */
|
||||
assert(stream->sm_qflags & SMQF_WAIT_FIN_OFF);
|
||||
|
||||
lsquic_stream_destroy(stream);
|
||||
/* This simply checks that the stream got removed from the queue: */
|
||||
|
@ -918,6 +945,13 @@ test_loc_FIN_rem_RST (struct test_objs *tobjs)
|
|||
ack_packet(&tobjs->send_ctl, 1);
|
||||
ack_packet(&tobjs->send_ctl, 2);
|
||||
|
||||
#if 0
|
||||
/* OK, here we pretend that we sent a RESET and it was acked */
|
||||
assert(stream->sm_qflags & SMQF_SEND_RST);
|
||||
stream->sm_qflags |= SMQF_SEND_RST;
|
||||
stream->stream_flags
|
||||
#endif
|
||||
|
||||
assert(!TAILQ_EMPTY(&tobjs->conn_pub.service_streams));
|
||||
assert((stream->sm_qflags & (SMQF_SERVICE_FLAGS)) == (SMQF_CALL_ONCLOSE|SMQF_FREE_STREAM));
|
||||
|
||||
|
@ -1044,7 +1078,7 @@ test_loc_RST_rem_FIN (struct test_objs *tobjs)
|
|||
assert(!TAILQ_EMPTY(&tobjs->conn_pub.sending_streams));
|
||||
assert((stream->sm_qflags & SMQF_SENDING_FLAGS) == SMQF_SEND_RST);
|
||||
sss = lsquic_stream_sending_state(stream);
|
||||
assert(SSS_SEND == sss); /* Reset hasn't been packetized yet */
|
||||
assert(SSS_DATA_SENT == sss); /* FIN was packetized */
|
||||
|
||||
s = lsquic_stream_frame_in(stream, new_frame_in(tobjs, 0, 90, 1));
|
||||
assert(s == 0);
|
||||
|
@ -1061,9 +1095,12 @@ test_loc_RST_rem_FIN (struct test_objs *tobjs)
|
|||
sss = lsquic_stream_sending_state(stream);
|
||||
assert(SSS_RESET_RECVD == sss);
|
||||
|
||||
lsquic_stream_call_on_close(stream);
|
||||
|
||||
assert(TAILQ_EMPTY(&tobjs->conn_pub.sending_streams));
|
||||
|
||||
lsquic_stream_call_on_close(stream);
|
||||
assert(TAILQ_EMPTY(&tobjs->conn_pub.service_streams)); /* Not acked yet */
|
||||
lsquic_stream_acked(stream, QUIC_FRAME_STREAM);
|
||||
|
||||
assert(!TAILQ_EMPTY(&tobjs->conn_pub.service_streams));
|
||||
assert((stream->sm_qflags & SMQF_SERVICE_FLAGS) == SMQF_FREE_STREAM);
|
||||
|
||||
|
@ -1317,7 +1354,12 @@ test_data_flush_on_close (struct test_objs *tobjs)
|
|||
assert(0 == lsquic_send_ctl_n_scheduled(&tobjs->send_ctl));
|
||||
|
||||
lsquic_stream_close(stream);
|
||||
assert(1 == lsquic_send_ctl_n_scheduled(&tobjs->send_ctl));
|
||||
/* Nothing is scheduled because STREAM frames are elided */
|
||||
assert(0 == lsquic_send_ctl_n_scheduled(&tobjs->send_ctl));
|
||||
|
||||
assert(stream->sm_qflags & SMQF_SEND_RST);
|
||||
assert(!(stream->sm_qflags & SMQF_FREE_STREAM));
|
||||
assert(stream->sm_qflags & SMQF_WAIT_FIN_OFF);
|
||||
|
||||
/* We take connection cap hit after stream is flushed: */
|
||||
assert(0x4000 - 100 == lsquic_conn_cap_avail(cap)); /* Conn cap hit */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue