[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 2018-05-04
- [FEATURE] Add support for Q042.
- Remove comment: MSPC is obsolete (no code changes) - Remove comment: MSPC is obsolete (no code changes)
- Prog: use lsquic_str2ver() when processing -o version flag - Prog: use lsquic_str2ver() when processing -o version flag
- Remove unused CTIM and SRBF transport parameters - Remove unused CTIM and SRBF transport parameters

View file

@ -76,19 +76,26 @@ enum lsquic_version
*/ */
LSQVER_041, LSQVER_041,
/**
* Q042. Receiving overlapping stream data is allowed.
*/
LSQVER_042,
N_LSQVER 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 * @see lsquic_version
*/ */
#define LSQUIC_SUPPORTED_VERSIONS ((1 << LSQVER_035) | (1 << LSQVER_037) | \ #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_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 * @struct lsquic_stream_if

View file

@ -18,6 +18,7 @@ enum ins_frame
INS_FRAME_OK, INS_FRAME_OK,
INS_FRAME_ERR, INS_FRAME_ERR,
INS_FRAME_DUP, INS_FRAME_DUP,
INS_FRAME_OVERLAP,
}; };
@ -29,8 +30,14 @@ struct data_in_iface
int int
(*di_empty) (struct data_in *); (*di_empty) (struct data_in *);
/* The caller releases control of stream frame. Do not reference it /* When INS_FRAME_OK, INS_FRAME_ERR, or INS_FRAME_DUP is returned, the
* after the call. * 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 enum ins_frame
(*di_insert_frame) (struct data_in *, struct stream_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 * struct data_in *
data_in_nocopy_new (struct lsquic_conn_public *, uint32_t stream_id); 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 * struct data_in *
data_in_hash_new (struct lsquic_conn_public *, uint32_t stream_id, data_in_hash_new (struct lsquic_conn_public *, uint32_t stream_id,
uint64_t byteage); uint64_t byteage);

View file

@ -355,7 +355,12 @@ data_in_hash_insert_data_frame (struct data_in *data_in,
unsigned size, nw; unsigned size, nw;
if (data_frame->df_offset + data_frame->df_size < read_offset) 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) && if ((hdi->hdi_flags & HDI_FIN) &&
( (
@ -427,6 +432,7 @@ hash_di_insert_frame (struct data_in *data_in,
enum ins_frame ins; enum ins_frame ins;
ins = data_in_hash_insert_data_frame(data_in, data_frame, read_offset); 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_packet_in_put(hdi->hdi_conn_pub->mm, new_frame->packet_in);
lsquic_malo_put(new_frame); lsquic_malo_put(new_frame);
return ins; return ins;

View file

@ -155,54 +155,110 @@ nocopy_di_destroy (struct data_in *data_in)
free(ncdi); 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) #if LSQUIC_EXTRA_CHECKS
#else
static int static int
ordered (const struct nocopy_data_in *ncdi) frame_list_is_sane (const struct nocopy_data_in *ncdi)
{ {
const stream_frame_t *frame; const stream_frame_t *frame;
uint64_t off = 0; uint64_t prev_off = 0, prev_end = 0;
int ordered = 1; int ordered = 1, overlaps = 0;
TAILQ_FOREACH(frame, &ncdi->ncdi_frames_in, next_frame) TAILQ_FOREACH(frame, &ncdi->ncdi_frames_in, next_frame)
{ {
ordered &= off <= frame->data_frame.df_offset; ordered &= prev_off <= DF_OFF(frame);
off = frame->data_frame.df_offset; 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 #endif
/* To reduce the number of conditionals, logical operators have been replaced /* When inserting a new frame into the frame list, there are four cases to
* with arithmetic operators. Return value is an integer in range [0, 3]. * consider:
* Bit 0 is set due to FIN in previous frame. If bit 1 is set, it means that *
* it's a dup. * 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, insert_frame (struct nocopy_data_in *ncdi, struct stream_frame *new_frame,
uint64_t read_offset, unsigned *p_n_frames) uint64_t read_offset, unsigned *p_n_frames)
{ {
int ins;
unsigned count;
stream_frame_t *prev_frame, *next_frame; 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 /* Find position in the list, going backwards. We go backwards because
* that is the most likely scenario. * that is the most likely scenario.
*/ */
next_frame = TAILQ_LAST(&ncdi->ncdi_frames_in, stream_frames_tailq); 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; count = 1;
prev_frame = TAILQ_PREV(next_frame, stream_frames_tailq, next_frame); prev_frame = TAILQ_PREV(next_frame, stream_frames_tailq, next_frame);
for ( ; prev_frame && for ( ; prev_frame && DF_OFF(new_frame) < DF_OFF(next_frame);
new_frame->data_frame.df_offset < next_frame->data_frame.df_offset;
next_frame = prev_frame, next_frame = prev_frame,
prev_frame = TAILQ_PREV(prev_frame, stream_frames_tailq, next_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; break;
++count; ++count;
} }
@ -213,69 +269,74 @@ insert_frame (struct nocopy_data_in *ncdi, struct stream_frame *new_frame,
prev_frame = NULL; prev_frame = NULL;
} }
if (!prev_frame && next_frame && new_frame->data_frame.df_offset >= if (!prev_frame && next_frame && DF_OFF(new_frame) >= DF_OFF(next_frame))
next_frame->data_frame.df_offset)
{ {
prev_frame = next_frame; prev_frame = next_frame;
next_frame = TAILQ_NEXT(next_frame, next_frame); next_frame = TAILQ_NEXT(next_frame, next_frame);
} }
/* Perform checks */ const int select = !!prev_frame << 1 | !!next_frame;
if (prev_frame) switch (select)
ins = {
(((prev_frame->data_frame.df_offset == new_frame->data_frame.df_offset) & default: /* No neighbors */
(prev_frame->data_frame.df_size == new_frame->data_frame.df_size) & if (read_offset == DF_END(new_frame) && DF_SIZE(new_frame))
(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 */ if (DF_FIN(new_frame))
| (prev_frame->data_frame.df_offset + prev_frame->data_frame.df_size return INS_FRAME_OVERLAP; /* Case (C) */
> new_frame->data_frame.df_offset) /* Overlap */ else
; return INS_FRAME_DUP; /* Case (D) */
else }
ins = 0; goto list_was_empty;
case 3: /* Both left and right neighbors */
if (next_frame) case 2: /* Only left neighbor (prev_frame) */
ins |= if (DF_OFF(prev_frame) == DF_OFF(new_frame)
(((next_frame->data_frame.df_offset == new_frame->data_frame.df_offset) & && DF_SIZE(prev_frame) == DF_SIZE(new_frame))
(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 */ if (!DF_FIN(prev_frame) && DF_FIN(new_frame))
| (new_frame->data_frame.df_offset < read_offset) << 1 /* Duplicate */ return INS_FRAME_OVERLAP; /* Case (G) */
| new_frame->data_frame.df_fin /* FIN in the middle or dup */ else
| (new_frame->data_frame.df_offset + new_frame->data_frame.df_size return INS_FRAME_DUP; /* Cases (E) and (F) */
> next_frame->data_frame.df_offset) /* Overlap */ }
; if (DF_END(prev_frame) > DF_OFF(new_frame))
else return INS_FRAME_OVERLAP; /* Case (H) */
ins |= if (select == 2)
(new_frame->data_frame.df_offset < read_offset) << 1 /* Duplicate */ {
; if (DF_FIN(prev_frame))
return INS_FRAME_ERR;
if (ins) goto have_prev;
return ins; }
/* 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) if (prev_frame)
{ {
have_prev:
TAILQ_INSERT_AFTER(&ncdi->ncdi_frames_in, prev_frame, new_frame, next_frame); TAILQ_INSERT_AFTER(&ncdi->ncdi_frames_in, prev_frame, new_frame, next_frame);
ncdi->ncdi_n_holes += prev_frame->data_frame.df_offset + ncdi->ncdi_n_holes += DF_END(prev_frame) != DF_OFF(new_frame);
prev_frame->data_frame.df_size != new_frame->data_frame.df_offset;
if (next_frame) if (next_frame)
{ {
ncdi->ncdi_n_holes += new_frame->data_frame.df_offset + ncdi->ncdi_n_holes += DF_END(new_frame) != DF_OFF(next_frame);
new_frame->data_frame.df_size != next_frame->data_frame.df_offset;
--ncdi->ncdi_n_holes; --ncdi->ncdi_n_holes;
} }
} }
else else
{ {
ncdi->ncdi_n_holes += next_frame && new_frame->data_frame.df_offset + ncdi->ncdi_n_holes += next_frame
new_frame->data_frame.df_size != next_frame->data_frame.df_offset; && DF_END(new_frame) != DF_OFF(next_frame);
list_was_empty:
TAILQ_INSERT_HEAD(&ncdi->ncdi_frames_in, new_frame, next_frame); TAILQ_INSERT_HEAD(&ncdi->ncdi_frames_in, new_frame, next_frame);
} }
CHECK_ORDER(ncdi); CHECK_ORDER(ncdi);
++ncdi->ncdi_n_frames; ++ncdi->ncdi_n_frames;
ncdi->ncdi_byteage += new_frame->data_frame.df_size; ncdi->ncdi_byteage += DF_SIZE(new_frame);
*p_n_frames = count; *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); struct nocopy_data_in *const ncdi = NCDI_PTR(data_in);
unsigned count; unsigned count;
int ins; enum ins_frame ins;
assert(0 == (new_frame->data_frame.df_fin & ~1)); assert(0 == (new_frame->data_frame.df_fin & ~1));
ins = insert_frame(ncdi, new_frame, read_offset, &count); ins = insert_frame(ncdi, new_frame, read_offset, &count);
switch (ins) switch (ins)
{ {
case 0: case INS_FRAME_OK:
if (check_efficiency(ncdi, count)) if (check_efficiency(ncdi, count))
set_eff_alert(ncdi); set_eff_alert(ncdi);
return INS_FRAME_OK; break;
case 2: case INS_FRAME_DUP:
case 3: case INS_FRAME_ERR:
lsquic_packet_in_put(ncdi->ncdi_conn_pub->mm, new_frame->packet_in); lsquic_packet_in_put(ncdi->ncdi_conn_pub->mm, new_frame->packet_in);
lsquic_malo_put(new_frame); lsquic_malo_put(new_frame);
return INS_FRAME_DUP; break;
default: default:
assert(1 == ins); break;
lsquic_packet_in_put(ncdi->ncdi_conn_pub->mm, new_frame->packet_in);
lsquic_malo_put(new_frame);
return INS_FRAME_ERR;
} }
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, new_stream_ext (struct full_conn *conn, uint32_t stream_id, int if_idx,
enum stream_ctor_flags stream_ctor_flags) 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,
conn->fc_stream_ifs[if_idx].stream_if_ctx, conn->fc_settings->es_sfcw, conn->fc_stream_ifs[if_idx].stream_if_ctx, conn->fc_settings->es_sfcw,
conn->fc_cfg.max_stream_send, stream_ctor_flags); 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 << (ver)) & ((1 << LSQVER_035) | \
(1 << LSQVER_037) | (1 << LSQVER_038))) \ (1 << LSQVER_037) | (1 << LSQVER_038))) \
? &lsquic_parse_funcs_gquic_le : \ ? &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_Q039 \
: &lsquic_parse_funcs_gquic_Q041) : &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); lsquic_stream_call_on_new(stream);
if (ctor_flags & SCF_DISP_RW_ONCE) if (ctor_flags & SCF_DISP_RW_ONCE)
stream->stream_flags |= STREAM_RW_ONCE; stream->stream_flags |= STREAM_RW_ONCE;
if (ctor_flags & SCF_ALLOW_OVERLAP)
stream->stream_flags |= STREAM_ALLOW_OVERLAP;
return stream; 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; 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); ins_frame = stream->data_in->di_if->di_insert_frame(stream->data_in, frame, stream->read_offset);
if (INS_FRAME_OK == ins_frame) if (INS_FRAME_OK == ins_frame)
{ {
@ -533,6 +536,23 @@ lsquic_stream_frame_in (lsquic_stream_t *stream, stream_frame_t *frame)
{ {
return 0; 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 else
{ {
assert(INS_FRAME_ERR == ins_frame); 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_ONNEW_DONE = (1 <<26), /* on_new_stream has been called */
STREAM_AUTOSWITCH = (1 <<27), STREAM_AUTOSWITCH = (1 <<27),
STREAM_RW_ONCE = (1 <<28), /* When set, read/write events are dispatched once per call */ STREAM_RW_ONCE = (1 <<28), /* When set, read/write events are dispatched once per call */
STREAM_ALLOW_OVERLAP= (1 <<29),
} stream_flags; } stream_flags;
/* There are more than one reason that a stream may be put onto /* There are more than one reason that a stream may be put onto
@ -144,6 +145,7 @@ enum stream_ctor_flags
* performance. * performance.
*/ */
SCF_DISP_RW_ONCE = (1 << 3), 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_038] = { 'Q', '0', '3', '8', },
[LSQVER_039] = { 'Q', '0', '3', '9', }, [LSQVER_039] = { 'Q', '0', '3', '9', },
[LSQVER_041] = { 'Q', '0', '4', '1', }, [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_038] = "Q038",
[LSQVER_039] = "Q039", [LSQVER_039] = "Q039",
[LSQVER_041] = "Q041", [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 static void
test_writing_to_stream_schedule_stream_packets_immediately (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; stream_ctor_flags |= SCF_USE_DI_HASH;
break; break;
case 'l': case 'l':
lsquic_log_to_fstream(stderr, 0);
lsquic_logger_lopt(optarg); lsquic_logger_lopt(optarg);
break; break;
default: default:
@ -2231,6 +2447,7 @@ main (int argc, char **argv)
test_forced_flush_when_conn_blocked(); test_forced_flush_when_conn_blocked();
test_blocked_flags(); test_blocked_flags();
test_reading_from_stream2(); test_reading_from_stream2();
test_overlaps();
{ {
int idx[6]; int idx[6];