mirror of
https://gitea.invidious.io/iv-org/litespeed-quic.git
synced 2024-08-15 00:53:43 +00:00
[FEATURE] Add support for Q042
This commit is contained in:
parent
4b332c36d1
commit
be4cfad023
11 changed files with 412 additions and 79 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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",
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
Loading…
Reference in a new issue