[FEATURE] Add support for Q042

This commit is contained in:
Dmitri Tikhonov 2018-05-04 14:00:34 -04:00
parent 4b332c36d1
commit be4cfad023
11 changed files with 412 additions and 79 deletions

View file

@ -1,5 +1,6 @@
2018-05-04
- [FEATURE] Add support for Q042.
- Remove comment: MSPC is obsolete (no code changes)
- Prog: use lsquic_str2ver() when processing -o version flag
- Remove unused CTIM and SRBF transport parameters

View file

@ -76,19 +76,26 @@ enum lsquic_version
*/
LSQVER_041,
/**
* Q042. Receiving overlapping stream data is allowed.
*/
LSQVER_042,
N_LSQVER
};
/**
* We currently support versions 35, 37, 38, 39, and 41.
* We currently support versions 35, 37, 38, 39, 41, and 42.
* @see lsquic_version
*/
#define LSQUIC_SUPPORTED_VERSIONS ((1 << LSQVER_035) | (1 << LSQVER_037) | \
(1 << LSQVER_038) | (1 << LSQVER_039) | (1 << LSQVER_041))
(1 << LSQVER_038) | (1 << LSQVER_039) | (1 << LSQVER_041) | \
(1 << LSQVER_042))
#define LSQUIC_EXPERIMENTAL_VERSIONS ((1 << LSQVER_041))
#define LSQUIC_DEPRECATED_VERSIONS ((1 << LSQVER_037) | (1 << LSQVER_038))
#define LSQUIC_DEPRECATED_VERSIONS ((1 << LSQVER_037) | (1 << LSQVER_038) | \
(1 << LSQVER_042))
/**
* @struct lsquic_stream_if

View file

@ -18,6 +18,7 @@ enum ins_frame
INS_FRAME_OK,
INS_FRAME_ERR,
INS_FRAME_DUP,
INS_FRAME_OVERLAP,
};
@ -29,8 +30,14 @@ struct data_in_iface
int
(*di_empty) (struct data_in *);
/* The caller releases control of stream frame. Do not reference it
* after the call.
/* When INS_FRAME_OK, INS_FRAME_ERR, or INS_FRAME_DUP is returned, the
* caller releases control of stream frame. Do not reference it after
* the call.
*
* When INS_FRAME_OVERLAP is returned the caller has a choice to switch
* to implementation that supports overlaps and try to insert the frame
* again or to treat this as an error. Either way, the caller retains
* control of the frame.
*/
enum ins_frame
(*di_insert_frame) (struct data_in *, struct stream_frame *,
@ -67,9 +74,15 @@ struct data_in
};
/* This implementation does not support overlapping frame and may return
* INS_FRAME_OVERLAP.
*/
struct data_in *
data_in_nocopy_new (struct lsquic_conn_public *, uint32_t stream_id);
/* This implementation supports overlapping frames and will never return
* INS_FRAME_OVERLAP.
*/
struct data_in *
data_in_hash_new (struct lsquic_conn_public *, uint32_t stream_id,
uint64_t byteage);

View file

@ -355,7 +355,12 @@ data_in_hash_insert_data_frame (struct data_in *data_in,
unsigned size, nw;
if (data_frame->df_offset + data_frame->df_size < read_offset)
return INS_FRAME_DUP;
{
if (data_frame->df_fin)
return INS_FRAME_ERR;
else
return INS_FRAME_DUP;
}
if ((hdi->hdi_flags & HDI_FIN) &&
(
@ -427,6 +432,7 @@ hash_di_insert_frame (struct data_in *data_in,
enum ins_frame ins;
ins = data_in_hash_insert_data_frame(data_in, data_frame, read_offset);
assert(ins != INS_FRAME_OVERLAP);
lsquic_packet_in_put(hdi->hdi_conn_pub->mm, new_frame->packet_in);
lsquic_malo_put(new_frame);
return ins;

View file

@ -155,54 +155,110 @@ nocopy_di_destroy (struct data_in *data_in)
free(ncdi);
}
#define DF_OFF(frame) (frame)->data_frame.df_offset
#define DF_FIN(frame) (frame)->data_frame.df_fin
#define DF_SIZE(frame) (frame)->data_frame.df_size
#define DF_END(frame) (DF_OFF(frame) + DF_SIZE(frame))
#if 1
#define CHECK_ORDER(ncdi)
#else
#if LSQUIC_EXTRA_CHECKS
static int
ordered (const struct nocopy_data_in *ncdi)
frame_list_is_sane (const struct nocopy_data_in *ncdi)
{
const stream_frame_t *frame;
uint64_t off = 0;
int ordered = 1;
uint64_t prev_off = 0, prev_end = 0;
int ordered = 1, overlaps = 0;
TAILQ_FOREACH(frame, &ncdi->ncdi_frames_in, next_frame)
{
ordered &= off <= frame->data_frame.df_offset;
off = frame->data_frame.df_offset;
ordered &= prev_off <= DF_OFF(frame);
overlaps |= prev_end > DF_OFF(frame);
prev_off = DF_OFF(frame);
prev_end = DF_END(frame);
}
return ordered;
return ordered && !overlaps;
}
#define CHECK_ORDER(ncdi) assert(ordered(ncdi))
#define CHECK_ORDER(ncdi) assert(frame_list_is_sane(ncdi))
#else
#define CHECK_ORDER(ncdi)
#endif
/* To reduce the number of conditionals, logical operators have been replaced
* with arithmetic operators. Return value is an integer in range [0, 3].
* Bit 0 is set due to FIN in previous frame. If bit 1 is set, it means that
* it's a dup.
/* When inserting a new frame into the frame list, there are four cases to
* consider:
*
* I. New frame is the only frame in the list;
* II. New frame only has a neighbor to its left (previous frame);
* III. New frame only has a neighbor to its right (next frame); and
* IV. New frame has both left and right neighbors.
*
* I. New frame is the only frame in the list.
*
* A) If the read offset is larger than the end of the new frame and
* the new frame has a FIN, it is an ERROR.
*
* B) If the read offset is larger than the end of the new frame and
* the new frame does not have a FIN, it is a DUP.
*
* C) If the read offset is equal to the end of the new frame and
* the new frame has a FIN, it is an OVERLAP.
*
* D) If the read offset is equal to the end of the new frame and
* the new frame does not have a FIN, it is a DUP.
*
* II. New frame only has a neighbor to its left.
*
* - (A) and (B) apply.
*
* E) New frame could be the same as the previous frame: DUP.
*
* F) New frame has the same offset and size as previous frame, but
* previous frame has FIN and the new frame does not: DUP.
*
* G) New frame has the same offset and size as previous frame, but
* previous frame does not have a FIN and the new one does: OVERLAP.
*
* H) New frame could start inside previous frame: OVERLAP.
*
* III. New frame only has a neighbor to its right.
*
* - (A) and (B) apply.
*
* I) Right neighbor could start inside new frame: OVERLAP.
*
* IV. New frame has both left and right neighbors.
*
* - (A), (B), (E), (F), (G), (H), and (I) apply.
*/
static int
static enum ins_frame
insert_frame (struct nocopy_data_in *ncdi, struct stream_frame *new_frame,
uint64_t read_offset, unsigned *p_n_frames)
{
int ins;
unsigned count;
stream_frame_t *prev_frame, *next_frame;
unsigned count;
if (read_offset > DF_END(new_frame))
{
if (DF_FIN(new_frame))
return INS_FRAME_ERR; /* Case (A) */
else
return INS_FRAME_DUP; /* Case (B) */
}
/* Find position in the list, going backwards. We go backwards because
* that is the most likely scenario.
*/
next_frame = TAILQ_LAST(&ncdi->ncdi_frames_in, stream_frames_tailq);
if (next_frame && new_frame->data_frame.df_offset < next_frame->data_frame.df_offset)
if (next_frame && DF_OFF(new_frame) < DF_OFF(next_frame))
{
count = 1;
prev_frame = TAILQ_PREV(next_frame, stream_frames_tailq, next_frame);
for ( ; prev_frame &&
new_frame->data_frame.df_offset < next_frame->data_frame.df_offset;
for ( ; prev_frame && DF_OFF(new_frame) < DF_OFF(next_frame);
next_frame = prev_frame,
prev_frame = TAILQ_PREV(prev_frame, stream_frames_tailq, next_frame))
{
if (new_frame->data_frame.df_offset >= prev_frame->data_frame.df_offset)
if (DF_OFF(new_frame) >= DF_OFF(prev_frame))
break;
++count;
}
@ -213,69 +269,74 @@ insert_frame (struct nocopy_data_in *ncdi, struct stream_frame *new_frame,
prev_frame = NULL;
}
if (!prev_frame && next_frame && new_frame->data_frame.df_offset >=
next_frame->data_frame.df_offset)
if (!prev_frame && next_frame && DF_OFF(new_frame) >= DF_OFF(next_frame))
{
prev_frame = next_frame;
next_frame = TAILQ_NEXT(next_frame, next_frame);
}
/* Perform checks */
if (prev_frame)
ins =
(((prev_frame->data_frame.df_offset == new_frame->data_frame.df_offset) &
(prev_frame->data_frame.df_size == new_frame->data_frame.df_size) &
(prev_frame->data_frame.df_fin == new_frame->data_frame.df_fin)) << 1) /* Duplicate */
| prev_frame->data_frame.df_fin /* FIN in the middle or dup */
| (prev_frame->data_frame.df_offset + prev_frame->data_frame.df_size
> new_frame->data_frame.df_offset) /* Overlap */
;
else
ins = 0;
if (next_frame)
ins |=
(((next_frame->data_frame.df_offset == new_frame->data_frame.df_offset) &
(next_frame->data_frame.df_size == new_frame->data_frame.df_size) &
(next_frame->data_frame.df_fin == new_frame->data_frame.df_fin)) << 1) /* Duplicate */
| (new_frame->data_frame.df_offset < read_offset) << 1 /* Duplicate */
| new_frame->data_frame.df_fin /* FIN in the middle or dup */
| (new_frame->data_frame.df_offset + new_frame->data_frame.df_size
> next_frame->data_frame.df_offset) /* Overlap */
;
else
ins |=
(new_frame->data_frame.df_offset < read_offset) << 1 /* Duplicate */
;
if (ins)
return ins;
const int select = !!prev_frame << 1 | !!next_frame;
switch (select)
{
default: /* No neighbors */
if (read_offset == DF_END(new_frame) && DF_SIZE(new_frame))
{
if (DF_FIN(new_frame))
return INS_FRAME_OVERLAP; /* Case (C) */
else
return INS_FRAME_DUP; /* Case (D) */
}
goto list_was_empty;
case 3: /* Both left and right neighbors */
case 2: /* Only left neighbor (prev_frame) */
if (DF_OFF(prev_frame) == DF_OFF(new_frame)
&& DF_SIZE(prev_frame) == DF_SIZE(new_frame))
{
if (!DF_FIN(prev_frame) && DF_FIN(new_frame))
return INS_FRAME_OVERLAP; /* Case (G) */
else
return INS_FRAME_DUP; /* Cases (E) and (F) */
}
if (DF_END(prev_frame) > DF_OFF(new_frame))
return INS_FRAME_OVERLAP; /* Case (H) */
if (select == 2)
{
if (DF_FIN(prev_frame))
return INS_FRAME_ERR;
goto have_prev;
}
/* Fall-through */
case 1: /* Only right neighbor (next_frame) */
if (DF_END(new_frame) > DF_OFF(next_frame))
return INS_FRAME_OVERLAP; /* Case (I) */
break;
}
if (prev_frame)
{
have_prev:
TAILQ_INSERT_AFTER(&ncdi->ncdi_frames_in, prev_frame, new_frame, next_frame);
ncdi->ncdi_n_holes += prev_frame->data_frame.df_offset +
prev_frame->data_frame.df_size != new_frame->data_frame.df_offset;
ncdi->ncdi_n_holes += DF_END(prev_frame) != DF_OFF(new_frame);
if (next_frame)
{
ncdi->ncdi_n_holes += new_frame->data_frame.df_offset +
new_frame->data_frame.df_size != next_frame->data_frame.df_offset;
ncdi->ncdi_n_holes += DF_END(new_frame) != DF_OFF(next_frame);
--ncdi->ncdi_n_holes;
}
}
else
{
ncdi->ncdi_n_holes += next_frame && new_frame->data_frame.df_offset +
new_frame->data_frame.df_size != next_frame->data_frame.df_offset;
ncdi->ncdi_n_holes += next_frame
&& DF_END(new_frame) != DF_OFF(next_frame);
list_was_empty:
TAILQ_INSERT_HEAD(&ncdi->ncdi_frames_in, new_frame, next_frame);
}
CHECK_ORDER(ncdi);
++ncdi->ncdi_n_frames;
ncdi->ncdi_byteage += new_frame->data_frame.df_size;
ncdi->ncdi_byteage += DF_SIZE(new_frame);
*p_n_frames = count;
return 0;
return INS_FRAME_OK;
}
@ -321,27 +382,26 @@ nocopy_di_insert_frame (struct data_in *data_in,
{
struct nocopy_data_in *const ncdi = NCDI_PTR(data_in);
unsigned count;
int ins;
enum ins_frame ins;
assert(0 == (new_frame->data_frame.df_fin & ~1));
ins = insert_frame(ncdi, new_frame, read_offset, &count);
switch (ins)
{
case 0:
case INS_FRAME_OK:
if (check_efficiency(ncdi, count))
set_eff_alert(ncdi);
return INS_FRAME_OK;
case 2:
case 3:
break;
case INS_FRAME_DUP:
case INS_FRAME_ERR:
lsquic_packet_in_put(ncdi->ncdi_conn_pub->mm, new_frame->packet_in);
lsquic_malo_put(new_frame);
return INS_FRAME_DUP;
break;
default:
assert(1 == ins);
lsquic_packet_in_put(ncdi->ncdi_conn_pub->mm, new_frame->packet_in);
lsquic_malo_put(new_frame);
return INS_FRAME_ERR;
break;
}
return ins;
}

View file

@ -866,7 +866,12 @@ static lsquic_stream_t *
new_stream_ext (struct full_conn *conn, uint32_t stream_id, int if_idx,
enum stream_ctor_flags stream_ctor_flags)
{
lsquic_stream_t *stream = lsquic_stream_new_ext(stream_id, &conn->fc_pub,
struct lsquic_stream *stream;
if (conn->fc_conn.cn_version >= LSQVER_042)
stream_ctor_flags |= SCF_ALLOW_OVERLAP;
stream = lsquic_stream_new_ext(stream_id, &conn->fc_pub,
conn->fc_stream_ifs[if_idx].stream_if,
conn->fc_stream_ifs[if_idx].stream_if_ctx, conn->fc_settings->es_sfcw,
conn->fc_cfg.max_stream_send, stream_ctor_flags);

View file

@ -167,7 +167,7 @@ extern const struct parse_funcs lsquic_parse_funcs_gquic_Q041;
((1 << (ver)) & ((1 << LSQVER_035) | \
(1 << LSQVER_037) | (1 << LSQVER_038))) \
? &lsquic_parse_funcs_gquic_le : \
((1 << (ver)) & (1 << LSQVER_039)) \
((1 << (ver)) & ((1 << LSQVER_039) | (1 << LSQVER_042))) \
? &lsquic_parse_funcs_gquic_Q039 \
: &lsquic_parse_funcs_gquic_Q041)

View file

@ -272,6 +272,8 @@ lsquic_stream_new_ext (uint32_t id, struct lsquic_conn_public *conn_pub,
lsquic_stream_call_on_new(stream);
if (ctor_flags & SCF_DISP_RW_ONCE)
stream->stream_flags |= STREAM_RW_ONCE;
if (ctor_flags & SCF_ALLOW_OVERLAP)
stream->stream_flags |= STREAM_ALLOW_OVERLAP;
return stream;
}
@ -498,6 +500,7 @@ lsquic_stream_frame_in (lsquic_stream_t *stream, stream_frame_t *frame)
}
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);
if (INS_FRAME_OK == ins_frame)
{
@ -533,6 +536,23 @@ lsquic_stream_frame_in (lsquic_stream_t *stream, stream_frame_t *frame)
{
return 0;
}
else if (INS_FRAME_OVERLAP == ins_frame)
{
if (stream->stream_flags & STREAM_ALLOW_OVERLAP)
{
LSQ_DEBUG("overlap: switching DATA IN implementation");
stream->data_in = stream->data_in->di_if->di_switch_impl(
stream->data_in, stream->read_offset);
if (stream->data_in)
goto insert_frame;
stream->data_in = data_in_error_new();
}
else
LSQ_DEBUG("overlap not supported");
lsquic_packet_in_put(stream->conn_pub->mm, frame->packet_in);
lsquic_malo_put(frame);
return -1;
}
else
{
assert(INS_FRAME_ERR == ins_frame);

View file

@ -66,6 +66,7 @@ struct lsquic_stream
STREAM_ONNEW_DONE = (1 <<26), /* on_new_stream has been called */
STREAM_AUTOSWITCH = (1 <<27),
STREAM_RW_ONCE = (1 <<28), /* When set, read/write events are dispatched once per call */
STREAM_ALLOW_OVERLAP= (1 <<29),
} stream_flags;
/* There are more than one reason that a stream may be put onto
@ -144,6 +145,7 @@ enum stream_ctor_flags
* performance.
*/
SCF_DISP_RW_ONCE = (1 << 3),
SCF_ALLOW_OVERLAP = (1 << 4), /* Allow STREAM frames to overlap */
};

View file

@ -13,6 +13,7 @@ static const unsigned char version_tags[N_LSQVER][4] =
[LSQVER_038] = { 'Q', '0', '3', '8', },
[LSQVER_039] = { 'Q', '0', '3', '9', },
[LSQVER_041] = { 'Q', '0', '4', '1', },
[LSQVER_042] = { 'Q', '0', '4', '2', },
};
@ -62,6 +63,7 @@ const char *const lsquic_ver2str[N_LSQVER] = {
[LSQVER_038] = "Q038",
[LSQVER_039] = "Q039",
[LSQVER_041] = "Q041",
[LSQVER_042] = "Q042",
};

View file

@ -1407,6 +1407,221 @@ test_reading_from_stream2 (void)
}
/* This tests stream overlap support */
static void
test_overlaps (void)
{
struct test_objs tobjs;
char buf[0x1000];
lsquic_stream_t *stream;
stream_frame_t *frame;
int s;
const char data[] = "1234567890";
struct frame_spec
{
unsigned off;
unsigned len;
signed char fin;
};
struct frame_step
{
struct frame_spec frame_spec;
int insert_res; /* Expected result */
};
struct overlap_test
{
int line; /* Test identifier */
struct frame_step steps[10]; /* Sequence of steps */
unsigned n_steps;
const unsigned char buf[20]; /* Expected result of read */
ssize_t sz; /* Expected size of first read */
ssize_t second_read; /* Expected size of second read:
* 0 means EOS (FIN).
*/
};
static const struct overlap_test tests[] =
{
{
.line = __LINE__,
.steps =
{
{
.frame_spec = { .off = 0, .len = 10, .fin = 0, },
.insert_res = 0,
},
},
.n_steps = 1,
.buf = "0123456789",
.sz = 10,
.second_read = -1,
},
{
.line = __LINE__,
.steps =
{
{
.frame_spec = { .off = 0, .len = 5, .fin = 0, },
.insert_res = 0,
},
{
.frame_spec = { .off = 0, .len = 10, .fin = 0, },
.insert_res = 0,
},
},
.n_steps = 2,
.buf = "0123456789",
.sz = 10,
.second_read = -1,
},
{
.line = __LINE__,
.steps =
{
{
.frame_spec = { .off = 1, .len = 9, .fin = 0, },
.insert_res = 0,
},
{
.frame_spec = { .off = 1, .len = 9, .fin = 1, },
.insert_res = 0,
},
{
.frame_spec = { .off = 0, .len = 2, .fin = 0, },
.insert_res = 0,
},
{
.frame_spec = { .off = 2, .len = 6, .fin = 0, },
.insert_res = 0,
},
},
.n_steps = 4,
.buf = "0123456789",
.sz = 10,
.second_read = 0,
},
{
.line = __LINE__,
.steps =
{
{
.frame_spec = { .off = 1, .len = 9, .fin = 1, },
.insert_res = 0,
},
{
.frame_spec = { .off = 0, .len = 2, .fin = 0, },
.insert_res = 0,
},
},
.n_steps = 2,
.buf = "0123456789",
.sz = 10,
.second_read = 0,
},
{
.line = __LINE__,
.steps =
{
{ .frame_spec = { .off = 1, .len = 6, .fin = 0, }, .insert_res = 0, },
{ .frame_spec = { .off = 2, .len = 1, .fin = 0, }, .insert_res = 0, },
{ .frame_spec = { .off = 8, .len = 2, .fin = 1, }, .insert_res = 0, },
{ .frame_spec = { .off = 3, .len = 2, .fin = 0, }, .insert_res = 0, },
{ .frame_spec = { .off = 4, .len = 1, .fin = 0, }, .insert_res = 0, },
{ .frame_spec = { .off = 5, .len = 2, .fin = 0, }, .insert_res = 0, },
{ .frame_spec = { .off = 6, .len = 1, .fin = 0, }, .insert_res = 0, },
{ .frame_spec = { .off = 7, .len = 3, .fin = 0, }, .insert_res = 0, },
{ .frame_spec = { .off = 9, .len = 1, .fin = 1, }, .insert_res = 0, },
{ .frame_spec = { .off = 0, .len = 2, .fin = 0, }, .insert_res = 0, },
},
.n_steps = 10,
.buf = "0123456789",
.sz = 10,
.second_read = 0,
},
};
init_test_objs(&tobjs, 0x4000, 0x4000);
assert(!(tobjs.ctor_flags & SCF_ALLOW_OVERLAP)); /* Self-check */
tobjs.ctor_flags |= SCF_ALLOW_OVERLAP;
const struct overlap_test *test;
for (test = tests; test < tests + sizeof(tests) / sizeof(tests[0]); ++test)
{
LSQ_NOTICE("executing stream overlap test, line %d", test->line);
stream = new_stream(&tobjs, test->line);
const struct frame_step *step;
for (step = test->steps; step < test->steps + test->n_steps; ++step)
{
frame = new_frame_in_ext(&tobjs, step->frame_spec.off,
step->frame_spec.len, step->frame_spec.fin,
&data[step->frame_spec.off]);
s = lsquic_stream_frame_in(stream, frame);
assert(s == step->insert_res);
}
ssize_t nread = lsquic_stream_read(stream, buf, sizeof(buf));
assert(nread == test->sz);
assert(0 == memcmp(data, buf, test->sz));
nread = lsquic_stream_read(stream, buf, sizeof(buf));
assert(nread == test->second_read);
if (nread < 0)
assert(EWOULDBLOCK == errno);
lsquic_stream_destroy(stream);
}
{
LSQ_NOTICE("Special test on line %d", __LINE__);
stream = new_stream(&tobjs, __LINE__);
frame = new_frame_in_ext(&tobjs, 0, 5, 0, &data[0]);
s = lsquic_stream_frame_in(stream, frame);
assert(0 == s);
ssize_t nread = lsquic_stream_read(stream, buf, sizeof(buf));
assert(nread == 5);
assert(0 == memcmp(data, buf, 5));
nread = lsquic_stream_read(stream, buf, sizeof(buf));
assert(nread < 0);
assert(EWOULDBLOCK == errno);
/* Test that a frame with FIN that ends before the read offset
* results in an error.
*/
frame = new_frame_in_ext(&tobjs, 0, 3, 1, &data[0]);
s = lsquic_stream_frame_in(stream, frame);
assert(s < 0);
/* This frame should be a DUP: the next read should still return -1.
*/
frame = new_frame_in_ext(&tobjs, 3, 2, 0, &data[3]);
s = lsquic_stream_frame_in(stream, frame);
assert(s == 0);
nread = lsquic_stream_read(stream, buf, sizeof(buf));
assert(nread < 0);
assert(EWOULDBLOCK == errno);
/* This frame should be an overlap: FIN should register and
* the next read should return 0.
*/
frame = new_frame_in_ext(&tobjs, 0, 5, 1, &data[0]);
s = lsquic_stream_frame_in(stream, frame);
assert(s == 0);
nread = lsquic_stream_read(stream, buf, sizeof(buf));
assert(nread == 0);
lsquic_stream_destroy(stream);
}
tobjs.ctor_flags &= ~SCF_ALLOW_OVERLAP;
deinit_test_objs(&tobjs);
}
static void
test_writing_to_stream_schedule_stream_packets_immediately (void)
{
@ -2215,6 +2430,7 @@ main (int argc, char **argv)
stream_ctor_flags |= SCF_USE_DI_HASH;
break;
case 'l':
lsquic_log_to_fstream(stderr, 0);
lsquic_logger_lopt(optarg);
break;
default:
@ -2231,6 +2447,7 @@ main (int argc, char **argv)
test_forced_flush_when_conn_blocked();
test_blocked_flags();
test_reading_from_stream2();
test_overlaps();
{
int idx[6];