diff --git a/CHANGELOG b/CHANGELOG index 4c2a375..aa4c2fc 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,17 @@ +2018-09-06 + - 1.14.0 + - [API Change] Disable packet sending if full batch cannot be sent + If lsquic_packets_out_f() cannot send the whole batch, disable + packet sending until lsquic_engine_send_unsent_packets() is called. + - [BUGFIX] Handle case when STREAM frame does not fit. + - [BUGFIX] Always allow incoming STREAM frames to overlap. Peers + may send overlapping STREAM frames even if using versions older + than Q043. + - Custom header set fixes: + - set "FIN reached" flag when custom header with FIN flag is + claimed; + - do not return custom header set for a reset stream. + 2018-08-27 - 1.13.0 diff --git a/CMakeLists.txt b/CMakeLists.txt index b50eaff..4dc7484 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -113,7 +113,7 @@ include_directories(${BORINGSSL_INCLUDE} ${VCPKG_INCLUDE} ) link_directories( ${BORINGSSL_LIB} ) SET(CMAKE_INCLUDE_CURRENT_DIR ON) -include_directories( include ) +include_directories( include src/lshpack) IF(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD" OR ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") # Find libevent on FreeBSD: include_directories( /usr/local/include ) diff --git a/include/lsquic.h b/include/lsquic.h index 45321d5..ba97cb9 100644 --- a/include/lsquic.h +++ b/include/lsquic.h @@ -24,7 +24,7 @@ extern "C" { #endif #define LSQUIC_MAJOR_VERSION 1 -#define LSQUIC_MINOR_VERSION 13 +#define LSQUIC_MINOR_VERSION 14 #define LSQUIC_PATCH_VERSION 0 /** @@ -471,9 +471,10 @@ struct lsquic_out_spec /** * Returns number of packets successfully sent out or -1 on error. -1 should - * only be returned if no packets were sent out. If -1 is returned, - * no packets will be attempted to be sent out until - * @ref lsquic_engine_send_unsent_packets() is called. + * only be returned if no packets were sent out. If -1 is returned or if the + * return value is smaller than `n_packets_out', this indicates that sending + * of packets is not possible No packets will be attempted to be sent out + * until @ref lsquic_engine_send_unsent_packets() is called. */ typedef int (*lsquic_packets_out_f)( void *packets_out_ctx, @@ -625,7 +626,7 @@ lsquic_engine_connect (lsquic_engine_t *, const struct sockaddr *local_sa, /** * Pass incoming packet to the QUIC engine. This function can be called * more than once in a row. After you add one or more packets, call - * lsquic_engine_process_conns_with_incoming() to schedule output, if any. + * lsquic_engine_process_conns() to schedule output, if any. * * @retval 0 Packet was processed by a real connection. * diff --git a/src/liblsquic/CMakeLists.txt b/src/liblsquic/CMakeLists.txt index a5ad40f..3049106 100644 --- a/src/liblsquic/CMakeLists.txt +++ b/src/liblsquic/CMakeLists.txt @@ -51,7 +51,7 @@ SET(lsquic_STAT_SRCS lsquic_xxhash.c lsquic_buf.c lsquic_min_heap.c - lshpack.c + ../lshpack/lshpack.c lsquic_parse_Q044.c lsquic_http1x_if.c ) diff --git a/src/liblsquic/lsquic_engine.c b/src/liblsquic/lsquic_engine.c index d2b737a..b05f32e 100644 --- a/src/liblsquic/lsquic_engine.c +++ b/src/liblsquic/lsquic_engine.c @@ -977,13 +977,17 @@ send_batch (lsquic_engine_t *engine, struct conns_out_iter *conns_iter, batch->packets[i]->po_sent = now; n_sent = engine->packets_out(engine->packets_out_ctx, batch->outs, n_to_send); + if (n_sent < (int) n_to_send) + { + engine->pub.enp_flags &= ~ENPUB_CAN_SEND; + LSQ_DEBUG("cannot send packets"); + EV_LOG_GENERIC_EVENT("cannot send packets"); + } if (n_sent >= 0) LSQ_DEBUG("packets out returned %d (out of %u)", n_sent, n_to_send); else { - engine->pub.enp_flags &= ~ENPUB_CAN_SEND; LSQ_DEBUG("packets out returned an error: %s", strerror(errno)); - EV_LOG_GENERIC_EVENT("cannot send packets"); n_sent = 0; } if (n_sent > 0) diff --git a/src/liblsquic/lsquic_full_conn.c b/src/liblsquic/lsquic_full_conn.c index 157ea13..09c124f 100644 --- a/src/liblsquic/lsquic_full_conn.c +++ b/src/liblsquic/lsquic_full_conn.c @@ -913,9 +913,6 @@ new_stream_ext (struct full_conn *conn, uint32_t stream_id, int if_idx, { struct lsquic_stream *stream; - if (conn->fc_conn.cn_version >= LSQVER_043) - 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, diff --git a/src/liblsquic/lsquic_parse.h b/src/liblsquic/lsquic_parse.h index 54986a8..6aef9bf 100644 --- a/src/liblsquic/lsquic_parse.h +++ b/src/liblsquic/lsquic_parse.h @@ -73,7 +73,12 @@ struct parse_funcs struct packin_parse_state *); enum QUIC_FRAME_TYPE (*pf_parse_frame_type) (unsigned char); - /* Return used buffer length */ + /* Return used buffer length or a negative value if there was not enough + * room to write the stream frame. In the latter case, the negative of + * the negative return value is the number of bytes required. The + * exception is -1, which is a generic error code, as we always need + * more than 1 byte to write a STREAM frame. + */ int (*pf_gen_stream_frame) (unsigned char *buf, size_t bufsz, uint32_t stream_id, uint64_t offset, diff --git a/src/liblsquic/lsquic_parse_Q044.c b/src/liblsquic/lsquic_parse_Q044.c index f30f946..18734f9 100644 --- a/src/liblsquic/lsquic_parse_Q044.c +++ b/src/liblsquic/lsquic_parse_Q044.c @@ -117,7 +117,7 @@ gen_long_pkt_header (const struct lsquic_conn *lconn, p += 4; - assert(need = p - buf); + assert(need == (unsigned int)(p - buf)); return p - buf; } diff --git a/src/liblsquic/lsquic_parse_common.h b/src/liblsquic/lsquic_parse_common.h index e02c752..6c9e692 100644 --- a/src/liblsquic/lsquic_parse_common.h +++ b/src/liblsquic/lsquic_parse_common.h @@ -21,4 +21,13 @@ int lsquic_iquic_parse_packet_in_begin (struct lsquic_packet_in *, size_t length, int is_server, struct packin_parse_state *); +/* Instead of just -1 like CHECK_SPACE(), this macro returns the number + * of bytes needed. + */ +#define CHECK_STREAM_SPACE(need, pstart, pend) do { \ + if ((intptr_t) (need) > ((pend) - (pstart))) { \ + return -((int) (need)); \ + } \ +} while (0) + #endif diff --git a/src/liblsquic/lsquic_parse_gquic_be.c b/src/liblsquic/lsquic_parse_gquic_be.c index a19071a..e30a64c 100644 --- a/src/liblsquic/lsquic_parse_gquic_be.c +++ b/src/liblsquic/lsquic_parse_gquic_be.c @@ -222,7 +222,7 @@ gquic_be_gen_stream_frame (unsigned char *buf, size_t buf_len, uint32_t stream_i dlen = (size < n_avail) << 1; n_avail -= dlen; - CHECK_SPACE(1 + olen + slen + dlen + + CHECK_STREAM_SPACE(1 + olen + slen + dlen + + 1 /* We need to write at least 1 byte */, buf, buf + buf_len); #if __BYTE_ORDER == __LITTLE_ENDIAN @@ -255,7 +255,7 @@ gquic_be_gen_stream_frame (unsigned char *buf, size_t buf_len, uint32_t stream_i else { dlen = 2; - CHECK_SPACE(1 + slen + olen + 2, buf, buf + buf_len); + CHECK_STREAM_SPACE(1 + slen + olen + 2, buf, buf + buf_len); #if __BYTE_ORDER == __LITTLE_ENDIAN stream_id = bswap_32(stream_id); #endif diff --git a/src/liblsquic/lsquic_parse_gquic_le.c b/src/liblsquic/lsquic_parse_gquic_le.c index 3ef3681..eec6d00 100644 --- a/src/liblsquic/lsquic_parse_gquic_le.c +++ b/src/liblsquic/lsquic_parse_gquic_le.c @@ -217,7 +217,7 @@ gquic_le_gen_stream_frame (unsigned char *buf, size_t buf_len, uint32_t stream_i dlen = (size < n_avail) << 1; n_avail -= dlen; - CHECK_SPACE(1 + olen + slen + dlen + + CHECK_STREAM_SPACE(1 + olen + slen + dlen + + 1 /* We need to write at least 1 byte */, buf, buf + buf_len); memcpy(p, &stream_id, slen); @@ -238,7 +238,7 @@ gquic_le_gen_stream_frame (unsigned char *buf, size_t buf_len, uint32_t stream_i else { dlen = 2; - CHECK_SPACE(1 + slen + olen + 2, buf, buf + buf_len); + CHECK_STREAM_SPACE(1 + slen + olen + 2, buf, buf + buf_len); memcpy(p, &stream_id, slen); p += slen; memcpy(p, &offset, olen); diff --git a/src/liblsquic/lsquic_stream.c b/src/liblsquic/lsquic_stream.c index bf74228..4a965b4 100644 --- a/src/liblsquic/lsquic_stream.c +++ b/src/liblsquic/lsquic_stream.c @@ -273,8 +273,6 @@ 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; } @@ -559,17 +557,12 @@ lsquic_stream_frame_in (lsquic_stream_t *stream, stream_frame_t *frame) } 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"); + 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(); lsquic_packet_in_put(stream->conn_pub->mm, frame->packet_in); lsquic_malo_put(frame); return -1; @@ -1573,6 +1566,7 @@ stream_write_to_packet (struct frame_gen_ctx *fg_ctx, const size_t size) stream->tosend_off); need_at_least = stream_header_sz + (size > 0); hsk = LSQUIC_STREAM_HANDSHAKE == stream->id; + get_packet: packet_out = get_packet[hsk](send_ctl, need_at_least, stream); if (!packet_out) return SWTP_STOP; @@ -1588,8 +1582,18 @@ stream_write_to_packet (struct frame_gen_ctx *fg_ctx, const size_t size) frame_gen_fin(fg_ctx), size, frame_gen_read, fg_ctx); if (len < 0) { - LSQ_ERROR("could not generate stream frame"); - return SWTP_ERROR; + if (-len > (int) need_at_least) + { + LSQ_DEBUG("need more room (%d bytes) than initially calculated " + "%u bytes, will try again", -len, need_at_least); + need_at_least = -len; + goto get_packet; + } + else + { + LSQ_ERROR("could not generate stream frame"); + return SWTP_ERROR; + } } EV_LOG_GENERATED_STREAM_FRAME(LSQUIC_LOG_CONN_ID, pf, @@ -2181,6 +2185,13 @@ lsquic_stream_get_hset (struct lsquic_stream *stream) { void *hset; + if (stream->stream_flags & STREAM_RST_FLAGS) + { + LSQ_INFO("%s: stream is reset, no headers returned", __func__); + errno = ECONNRESET; + return NULL; + } + if ((stream->stream_flags & (STREAM_USE_HEADERS|STREAM_HAVE_UH)) != (STREAM_USE_HEADERS|STREAM_HAVE_UH)) { @@ -2204,6 +2215,11 @@ lsquic_stream_get_hset (struct lsquic_stream *stream) hset = stream->uh->uh_hset; stream->uh->uh_hset = NULL; destroy_uh(stream); + if (stream->stream_flags & STREAM_HEAD_IN_FIN) + { + stream->stream_flags |= STREAM_FIN_REACHED; + SM_HISTORY_APPEND(stream, SHE_REACH_FIN); + } LSQ_DEBUG("return header set"); return hset; } diff --git a/src/liblsquic/lsquic_stream.h b/src/liblsquic/lsquic_stream.h index c010380..9ca0185 100644 --- a/src/liblsquic/lsquic_stream.h +++ b/src/liblsquic/lsquic_stream.h @@ -62,7 +62,6 @@ 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 @@ -140,7 +139,6 @@ enum stream_ctor_flags * performance. */ SCF_DISP_RW_ONCE = (1 << 3), - SCF_ALLOW_OVERLAP = (1 << 4), /* Allow STREAM frames to overlap */ }; lsquic_stream_t * diff --git a/src/liblsquic/lshpack.c b/src/lshpack/lshpack.c similarity index 99% rename from src/liblsquic/lshpack.c rename to src/lshpack/lshpack.c index 0b0c3be..1f12ea7 100644 --- a/src/liblsquic/lshpack.c +++ b/src/lshpack/lshpack.c @@ -36,7 +36,7 @@ SOFTWARE. #if LS_HPACK_EMIT_TEST_CODE #include "lshpack-test.h" #endif -#include XXH_HEADER_NAME +#include "lsquic_xxhash.h" #define HPACK_STATIC_TABLE_SIZE 61 #define INITIAL_DYNAMIC_TABLE_SIZE 4096 diff --git a/src/liblsquic/lshpack.h b/src/lshpack/lshpack.h similarity index 100% rename from src/liblsquic/lshpack.h rename to src/lshpack/lshpack.h diff --git a/test/prog.c b/test/prog.c index 7415a37..3e628a7 100644 --- a/test/prog.c +++ b/test/prog.c @@ -430,6 +430,7 @@ send_unsent (evutil_socket_t fd, short what, void *arg) event_del(prog->prog_send); event_free(prog->prog_send); prog->prog_send = NULL; + LSQ_DEBUG("on_write event fires"); lsquic_engine_send_unsent_packets(prog->prog_engine); } @@ -438,6 +439,7 @@ void prog_sport_cant_send (struct prog *prog, int fd) { assert(!prog->prog_send); + LSQ_DEBUG("cannot send: register on_write event"); prog->prog_send = event_new(prog->prog_eb, fd, EV_WRITE, send_unsent, prog); event_add(prog->prog_send, NULL); } diff --git a/test/test_common.c b/test/test_common.c index f0f71f3..f68a5db 100644 --- a/test/test_common.c +++ b/test/test_common.c @@ -894,6 +894,7 @@ send_packets_one_by_one (const struct lsquic_out_spec *specs, unsigned count) assert(count > 0); sport = specs[0].peer_ctx; LSQ_NOTICE("sending \"randomly\" fails"); + prog_sport_cant_send(sport->sp_prog, sport->fd); goto random_send_failure; } } @@ -951,6 +952,9 @@ send_packets_one_by_one (const struct lsquic_out_spec *specs, unsigned count) } } + if (n < count) + prog_sport_cant_send(sport->sp_prog, sport->fd); + if (n > 0) return n; else if (s < 0) @@ -958,7 +962,6 @@ send_packets_one_by_one (const struct lsquic_out_spec *specs, unsigned count) #if LSQUIC_RANDOM_SEND_FAILURE random_send_failure: #endif - prog_sport_cant_send(sport->sp_prog, sport->fd); return -1; } else diff --git a/test/unittests/test_stream.c b/test/unittests/test_stream.c index 111b251..413ba66 100644 --- a/test/unittests/test_stream.c +++ b/test/unittests/test_stream.c @@ -38,7 +38,7 @@ #include "lsquic_ver_neg.h" #include "lsquic_packet_out.h" -static const struct parse_funcs *const pf = select_pf_by_ver(LSQVER_035); +static const struct parse_funcs *const g_pf = select_pf_by_ver(LSQVER_035); struct test_ctl_settings { @@ -266,10 +266,10 @@ struct test_objs { static void init_test_objs (struct test_objs *tobjs, unsigned initial_conn_window, - unsigned initial_stream_window) + unsigned initial_stream_window, const struct parse_funcs *pf) { memset(tobjs, 0, sizeof(*tobjs)); - tobjs->lconn.cn_pf = pf; + tobjs->lconn.cn_pf = pf ? pf : g_pf; tobjs->lconn.cn_pack_size = 1370; lsquic_mm_init(&tobjs->eng_pub.enp_mm); TAILQ_INIT(&tobjs->conn_pub.sending_streams); @@ -379,7 +379,7 @@ run_frame_ordering_test (uint64_t run_id /* This is used to make it easier to se struct test_objs tobjs; - init_test_objs(&tobjs, 0x4000, 0x4000); + init_test_objs(&tobjs, 0x4000, 0x4000, NULL); lsquic_stream_t *stream = new_stream(&tobjs, 123); struct lsquic_mm *const mm = &tobjs.eng_pub.enp_mm; @@ -1195,7 +1195,7 @@ test_termination (void) { init_test_ctl_settings(&g_ctl_settings); g_ctl_settings.tcs_schedule_stream_packets_immediately = 1; - init_test_objs(&tobjs, 0x4000, 0x4000); + init_test_objs(&tobjs, 0x4000, 0x4000, NULL); test_funcs[i](&tobjs); deinit_test_objs(&tobjs); } @@ -1217,7 +1217,7 @@ test_flushing (void) for (i = 0; i < sizeof(test_funcs) / sizeof(test_funcs[0]); ++i) { - init_test_objs(&tobjs, 0x4000, 0x4000); + init_test_objs(&tobjs, 0x4000, 0x4000, NULL); test_funcs[i](&tobjs); deinit_test_objs(&tobjs); } @@ -1293,7 +1293,7 @@ test_writev (void) for (i = 0; i < sizeof(tests) / sizeof(tests[0]); ++i) { - init_test_objs(&tobjs, UINT_MAX, UINT_MAX); + init_test_objs(&tobjs, UINT_MAX, UINT_MAX, NULL); stream = new_stream(&tobjs, 12345); n = lsquic_stream_writev(stream, tests[i].iov, tests[i].count); assert(0x4000 == n); @@ -1317,7 +1317,7 @@ test_prio_conversion (void) unsigned prio; int s; - init_test_objs(&tobjs, UINT_MAX, UINT_MAX); + init_test_objs(&tobjs, UINT_MAX, UINT_MAX, NULL); stream = new_stream(&tobjs, 123); s = lsquic_stream_set_priority(stream, -2); @@ -1349,7 +1349,7 @@ test_read_in_middle (void) struct test_objs tobjs; stream_frame_t *frame; - init_test_objs(&tobjs, 0x4000, 0x4000); + init_test_objs(&tobjs, 0x4000, 0x4000, NULL); lsquic_stream_t *stream = new_stream(&tobjs, 123); @@ -1392,7 +1392,7 @@ test_conn_unlimited (void) struct test_objs tobjs; lsquic_stream_t *header_stream, *data_stream; - init_test_objs(&tobjs, 0x4000, 0x4000); + init_test_objs(&tobjs, 0x4000, 0x4000, NULL); unsigned char *const data = calloc(1, 0x4000); @@ -1438,9 +1438,9 @@ test_reading_from_stream2 (void) stream_frame_t *frame; ssize_t nw; int s; - const char data[] = "1234567890"; + const char data[10] = "1234567890"; - init_test_objs(&tobjs, 0x4000, 0x4000); + init_test_objs(&tobjs, 0x4000, 0x4000, NULL); stream = new_stream(&tobjs, 123); frame = new_frame_in_ext(&tobjs, 0, 6, 0, &data[0]); @@ -1461,18 +1461,18 @@ test_reading_from_stream2 (void) { int dup; unsigned offset, length; - for (offset = 0; offset < 9; ++offset) + for (offset = 0; offset < 7; ++offset) { - for (length = 1; length < 10; ++length) + for (length = 1; length <= sizeof(data) - offset; ++length) { dup = (offset == 0 && length == 6) || (offset == 6 && length == 4); - frame = new_frame_in(&tobjs, offset, length, 0); + frame = new_frame_in_ext(&tobjs, offset, length, 0, data + offset); s = lsquic_stream_frame_in(stream, frame); if (dup) assert(("Dup OK", 0 == s)); else - assert(("Invalid frame: overlap", -1 == s)); + assert(("Overlap OK", 0 == s)); } } } @@ -1693,9 +1693,7 @@ test_overlaps (void) }; - init_test_objs(&tobjs, 0x4000, 0x4000); - assert(!(tobjs.ctor_flags & SCF_ALLOW_OVERLAP)); /* Self-check */ - tobjs.ctor_flags |= SCF_ALLOW_OVERLAP; + init_test_objs(&tobjs, 0x4000, 0x4000, NULL); const struct overlap_test *test; for (test = tests; test < tests + sizeof(tests) / sizeof(tests[0]); ++test) @@ -1761,7 +1759,6 @@ test_overlaps (void) lsquic_stream_destroy(stream); } - tobjs.ctor_flags &= ~SCF_ALLOW_OVERLAP; deinit_test_objs(&tobjs); } @@ -1777,7 +1774,7 @@ test_insert_edge_cases (void) const char data[] = "1234567890"; unsigned buf[0x1000]; - init_test_objs(&tobjs, 0x4000, 0x4000); + init_test_objs(&tobjs, 0x4000, 0x4000, NULL); { stream = new_stream(&tobjs, 123); @@ -1847,7 +1844,7 @@ test_writing_to_stream_schedule_stream_packets_immediately (void) init_test_ctl_settings(&g_ctl_settings); g_ctl_settings.tcs_schedule_stream_packets_immediately = 1; - init_test_objs(&tobjs, 0x4000, 0x4000); + init_test_objs(&tobjs, 0x4000, 0x4000, NULL); n_closed = 0; stream = new_stream(&tobjs, 123); assert(("Stream initialized", stream)); @@ -1915,7 +1912,7 @@ test_writing_to_stream_outside_callback (void) const struct buf_packet_q *const bpq = &tobjs.send_ctl.sc_buffered_packets[g_ctl_settings.tcs_bp_type]; - init_test_objs(&tobjs, 0x4000, 0x4000); + init_test_objs(&tobjs, 0x4000, 0x4000, NULL); n_closed = 0; stream = new_stream(&tobjs, 123); assert(("Stream initialized", stream)); @@ -1982,7 +1979,7 @@ test_window_update1 (void) init_test_ctl_settings(&g_ctl_settings); g_ctl_settings.tcs_schedule_stream_packets_immediately = 1; - init_test_objs(&tobjs, 0x4000, 0x4000); + init_test_objs(&tobjs, 0x4000, 0x4000, NULL); n_closed = 0; stream = new_stream_ext(&tobjs, 123, 3); nw = lsquic_stream_write(stream, "1234567890", 10); @@ -2040,7 +2037,7 @@ test_bad_packbits_guess_2 (void) g_ctl_settings.tcs_schedule_stream_packets_immediately = 0; g_ctl_settings.tcs_guess_packno_bits = PACKNO_LEN_1; - init_test_objs(&tobjs, 0x1000, 0x1000); + init_test_objs(&tobjs, 0x1000, 0x1000, NULL); streams[0] = new_stream(&tobjs, 5); streams[1] = new_stream(&tobjs, 7); streams[2] = new_stream(&tobjs, 9); @@ -2143,7 +2140,7 @@ test_bad_packbits_guess_3 (void) g_ctl_settings.tcs_schedule_stream_packets_immediately = 0; g_ctl_settings.tcs_guess_packno_bits = PACKNO_LEN_1; - init_test_objs(&tobjs, 0x1000, 0x1000); + init_test_objs(&tobjs, 0x1000, 0x1000, NULL); streams[0] = new_stream(&tobjs, 5); nw = lsquic_stream_write(streams[0], buf, @@ -2315,7 +2312,7 @@ test_packetization (int schedule_stream_packets_immediately, int dispatch_once, init_test_objs(&tobjs, /* Test limits a bit while we are at it: */ - sizeof(buf) - 1, sizeof(buf) - 1); + sizeof(buf) - 1, sizeof(buf) - 1, NULL); tobjs.stream_if_ctx = &packet_stream_ctx; if (schedule_stream_packets_immediately) @@ -2369,6 +2366,73 @@ test_packetization (int schedule_stream_packets_immediately, int dispatch_once, } +/* Test condition when the room necessary to write a STREAM frame to a packet + * is miscalculated and a brand-new packet has to be allocated. + */ +static void +test_cant_fit_frame (const struct parse_funcs *pf) +{ + struct test_objs tobjs; + struct lsquic_stream *streams[2]; + struct lsquic_packet_out *packet_out; + size_t pad_len, rem, nr; + int fin, s; + const char dude[] = "Dude, where is my car?!"; + unsigned char buf_out[100]; + + init_test_ctl_settings(&g_ctl_settings); + g_ctl_settings.tcs_schedule_stream_packets_immediately = 0; + + init_test_objs(&tobjs, 0x8000, 0x8000, pf); + + streams[0] = new_stream(&tobjs, 5); + streams[1] = new_stream(&tobjs, 7); + + /* Allocate a packet and pad it so just a few bytes remain to trigger + * the condition we're after. + */ + lsquic_stream_write(streams[0], dude, sizeof(dude) - 1); + lsquic_stream_flush(streams[0]); + + rem = pf->pf_calc_stream_frame_header_sz(streams[1]->id, 0) + + 1 /* We'll write one byte */ + + 1 /* This triggers the refit condition */ + ; + + packet_out = TAILQ_FIRST(&tobjs.send_ctl.sc_buffered_packets[0].bpq_packets); + assert(NULL == TAILQ_NEXT(packet_out, po_next)); + pad_len = packet_out->po_n_alloc - packet_out->po_data_sz - rem; + memset(packet_out->po_data + packet_out->po_data_sz, 0, pad_len); + packet_out->po_data_sz += pad_len; + + lsquic_stream_write(streams[1], "A", 1); + s = lsquic_stream_flush(streams[1]); + assert(0 == s); + /* Allocated another packet */ + assert(TAILQ_NEXT(packet_out, po_next)); + + g_ctl_settings.tcs_schedule_stream_packets_immediately = 1; + lsquic_send_ctl_schedule_buffered(&tobjs.send_ctl, BPT_HIGHEST_PRIO); + g_ctl_settings.tcs_schedule_stream_packets_immediately = 0; + + /* Verify written data: */ + nr = read_from_scheduled_packets(&tobjs.send_ctl, streams[0]->id, buf_out, + sizeof(buf_out), 0, &fin, 1); + assert(nr == sizeof(dude) - 1); + assert(!fin); + assert(0 == memcmp(dude, buf_out, sizeof(dude) - 1)); + nr = read_from_scheduled_packets(&tobjs.send_ctl, streams[1]->id, buf_out, + sizeof(buf_out), 0, &fin, 1); + assert(nr == 1); + assert(!fin); + assert(buf_out[0] == 'A'); + + lsquic_stream_destroy(streams[0]); + lsquic_stream_destroy(streams[1]); + deinit_test_objs(&tobjs); +} + + /* Test window update logic, not connection limited */ static void test_window_update2 (void) @@ -2381,7 +2445,7 @@ test_window_update2 (void) struct lsquic_conn_cap *const conn_cap = &tobjs.conn_pub.conn_cap; unsigned char buf[0x1000]; - init_test_objs(&tobjs, 0x4000, 0x4000); + init_test_objs(&tobjs, 0x4000, 0x4000, NULL); n_closed = 0; stream = new_stream_ext(&tobjs, LSQUIC_STREAM_HANDSHAKE, 3); nw = lsquic_stream_write(stream, "1234567890", 10); @@ -2433,7 +2497,7 @@ test_blocked_flags (void) init_test_ctl_settings(&g_ctl_settings); g_ctl_settings.tcs_schedule_stream_packets_immediately = 1; - init_test_objs(&tobjs, 3, 3); + init_test_objs(&tobjs, 3, 3, NULL); stream = new_stream_ext(&tobjs, 123, 3); nw = lsquic_stream_write(stream, "1234567890", 10); assert(("lsquic_stream_write is limited by the send window", 3 == nw)); @@ -2465,7 +2529,7 @@ test_forced_flush_when_conn_blocked (void) init_test_ctl_settings(&g_ctl_settings); g_ctl_settings.tcs_schedule_stream_packets_immediately = 1; - init_test_objs(&tobjs, 3, 0x1000); + init_test_objs(&tobjs, 3, 0x1000, NULL); stream = new_stream(&tobjs, 123); nw = lsquic_stream_write(stream, "1234567890", 10); assert(("lsquic_stream_write is limited by the send window", 3 == nw)); @@ -2502,7 +2566,7 @@ test_conn_abort (void) init_test_ctl_settings(&g_ctl_settings); g_ctl_settings.tcs_schedule_stream_packets_immediately = 1; - init_test_objs(&tobjs, 0x1000, 0x1000); + init_test_objs(&tobjs, 0x1000, 0x1000, NULL); my_pf = *tobjs.lconn.cn_pf; my_pf.pf_gen_stream_frame = my_gen_stream_frame_err; tobjs.lconn.cn_pf = &my_pf; @@ -2540,7 +2604,7 @@ test_bad_packbits_guess_1 (void) g_ctl_settings.tcs_schedule_stream_packets_immediately = 0; g_ctl_settings.tcs_guess_packno_bits = PACKNO_LEN_1; - init_test_objs(&tobjs, 0x1000, 0x1000); + init_test_objs(&tobjs, 0x1000, 0x1000, NULL); streams[0] = new_stream(&tobjs, 5); streams[1] = new_stream(&tobjs, 7); streams[2] = new_stream(&tobjs, 9); @@ -2705,6 +2769,10 @@ main (int argc, char **argv) } } + enum lsquic_version ver; + for (ver = 0; ver < N_LSQVER; ++ver) + test_cant_fit_frame(select_pf_by_ver(ver)); + return 0; } diff --git a/test/unittests/test_streamgen.c b/test/unittests/test_streamgen.c index b149f07..4702e08 100644 --- a/test/unittests/test_streamgen.c +++ b/test/unittests/test_streamgen.c @@ -569,7 +569,7 @@ run_test (int i) len = test->pf->pf_gen_stream_frame(out, min, test->stream_id, test_ctx.test->offset, stream_tosend_fin(&test_ctx), stream_tosend_size(&test_ctx), stream_tosend_read, &test_ctx); - assert(-1 == len); + assert(len < 0); } /* Test that it succeeds now: */