updated to upstream ran all tests again.

This commit is contained in:
Amol Deshpande 2018-03-12 19:56:06 -07:00
commit 5d77f141aa
25 changed files with 1048 additions and 895 deletions

View File

@ -1,6 +1,50 @@
2018-02-28
- Fix unit test regression: enable them correctly in cmake.
- Simplify connection has interface
2018-03-09
- [OPTIMIZATION] Merge series of ACKs if possible
Parsed single-range ACK frames (that is the majority of frames) are
saved in the connection and their processing is deferred until the
connection is ticked. If several ACKs come in a series between
adjacent ticks, we check whether the latest ACK is a strict superset
of the saved ACK. If it is, the older ACK is not processed.
If ACK frames can be merged, they are merged and only one of them is
either processed or saved.
- [OPTIMIZATION] Speed up ACK verification by simplifying send history.
Never generate a gap in the sent packet number sequence. This reduces
the send history to a single number instead of potentially a series of
packet ranges and thereby speeds up ACK verification.
By default, detecting a gap in the send history is not fatal: only a
single warning is generated per connection. The connection can continue
to operate even if the ACK verification code is not able to detect some
inconsistencies.
- [OPTIMIZATION] Rearrange the lsquic_send_ctl struct
The first part of struct lsquic_send_ctl now consists of members that
are used in lsquic_send_ctl_got_ack() (in the absense of packet loss,
which is the normal case). To speed up reads and writes, we no longer
try to save space by using 8- and 16-bit integers. Use regular integer
width for everything.
- [OPTIMIZATION] Cache size of sent packet.
- [OPTIMIZATION] Keep track of the largest ACKed in packet_out
Instead of parsing our own ACK frames when packet has been acked,
use the value saved in the packet_out structure when the ACK frame
was generated.
- [OPTIMIZATION] Take RTT sampling conditional out of ACK loop
- [OPTIMIZATION] ACK processing: only call clock_gettime() if needed
- [OPTIMIZATION] Several code-level optimizations to ACK processing.
- Fix: http_client: fix -I flag; switch assert() to abort()
2018-02-26
- [API Change] lsquic_engine_connect() returns pointer to the connection

View File

@ -57,6 +57,10 @@ IF(LSQUIC_PROFILE EQUAL 1)
SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -g -pg")
ENDIF()
IF(LSQUIC_COVERAGE EQUAL 1)
SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -fprofile-arcs -ftest-coverage")
ENDIF()
IF(MY_CMAKE_FLAGS MATCHES "fsanitize=address")
MESSAGE(STATUS "AddressSanitizer is ON")
ELSE()
@ -80,16 +84,8 @@ ENDIF()
SET (BORINGSSL_INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/../boringssl/include)
SET (VCPKG_INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/../vcpkg/installed/x64-windows-static/include )
IF(CMAKE_BUILD_TYPE MATCHES "Debug")
SET (BORINGSSL_LIB ${CMAKE_CURRENT_SOURCE_DIR}/../boringssl/build/ssl ${CMAKE_CURRENT_SOURCE_DIR}/../boringssl/build/x64/debug)
SET (VCPKG_LIB ${CMAKE_CURRENT_SOURCE_DIR}/../vcpkg/installed/x64-windows-static/lib)
SET (ZLIB_NAME "zlibd")
ELSE()
SET (BORINGSSL_LIB ${CMAKE_CURRENT_SOURCE_DIR}/../boringssl/build/ssl ${CMAKE_CURRENT_SOURCE_DIR}/../boringssl/build/x64)
SET (VCPKG_LIB ${CMAKE_CURRENT_SOURCE_DIR}/../vcpkg/installed/x64-windows-static/debug/lib)
SET (ZLIB_NAME zlibd)
ENDIF()
SET (VCPKG_BASE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../vcpkg/installed/x64-windows-static)
set (BORINGSSL_BASE_LIB_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../boringssl/build/x64)
ENDIF() #MSVC
@ -106,7 +102,7 @@ ENDIF()
include_directories(${BORINGSSL_INCLUDE} ${VCPKG_INCLUDE} )
link_directories( ${BORINGSSL_LIB} ${VCPKG_LIB} )
link_directories( ${BORINGSSL_LIB} )
SET(CMAKE_INCLUDE_CURRENT_DIR ON)
include_directories( include )
@ -124,6 +120,8 @@ add_executable(http_client
test/test_common.c
)
target_link_libraries(http_client lsquic event pthread libssl.a libcrypto.a ${FIULIB} z m)
#MSVC
ELSE()
add_executable(http_client
test/http_client.c
@ -133,7 +131,19 @@ add_executable(http_client
wincompat/getopt1.c
)
target_link_libraries(http_client lsquic event ssl crypto ${FIULIB} ${ZLIB_NAME} ws2_32)
target_link_libraries(http_client
debug $(SolutionDir)src/liblsquic/debug/lsquic.lib
debug ${VCPKG_BASE_DIR}/debug/lib/event.lib
debug ${VCPKG_BASE_DIR}/debug/lib/zlibd.lib
debug ${BORINGSSL_BASE_LIB_DIR}/debug/ssl.lib
debug ${BORINGSSL_BASE_LIB_DIR}/debug/crypto.lib
ws2_32
optimized $(SolutionDir)src/liblsquic/release/lsquic.lib
optimized ${VCPKG_BASE_DIR}/lib/event.lib
optimized ${VCPKG_BASE_DIR}/lib/zlib.lib
optimized ${BORINGSSL_BASE_LIB_DIR}/ssl.lib
optimized ${BORINGSSL_BASE_LIB_DIR}/crypto.lib
${FIULIB} )
ENDIF()

View File

@ -1123,6 +1123,7 @@ encrypt_packet (lsquic_engine_t *engine, const lsquic_conn_t *conn,
{
ssize_t enc_sz;
size_t bufsz;
unsigned sent_sz;
unsigned char *buf;
bufsz = lsquic_po_header_length(packet_out->po_flags) +
@ -1135,7 +1136,10 @@ encrypt_packet (lsquic_engine_t *engine, const lsquic_conn_t *conn,
return ENCPA_NOMEM;
}
{
enc_sz = really_encrypt_packet(conn, packet_out, buf, bufsz);
sent_sz = enc_sz;
}
if (enc_sz < 0)
{
@ -1145,7 +1149,8 @@ encrypt_packet (lsquic_engine_t *engine, const lsquic_conn_t *conn,
packet_out->po_enc_data = buf;
packet_out->po_enc_data_sz = enc_sz;
packet_out->po_flags |= PO_ENCRYPTED;
packet_out->po_sent_sz = sent_sz;
packet_out->po_flags |= PO_ENCRYPTED|PO_SENT_SZ;
return ENCPA_OK;
}

View File

@ -94,6 +94,7 @@ enum full_conn_flags {
FC_FIRST_TICK = (1 <<19),
FC_TICK_CLOSE = (1 <<20), /* We returned TICK_CLOSE */
FC_HSK_FAILED = (1 <<21),
FC_HAVE_SAVED_ACK = (1 <<22),
};
#define FC_IMMEDIATE_CLOSE_FLAGS \
@ -190,6 +191,10 @@ struct full_conn
n_dup_packets,
n_err_packets;
unsigned long stream_data_sz;
unsigned long n_ticks;
unsigned n_acks_in,
n_acks_proc,
n_acks_merged[2];
} fc_stats;
#endif
#if KEEP_CLOSED_STREAM_HISTORY
@ -205,6 +210,8 @@ struct full_conn
#endif
STAILQ_HEAD(, stream_id_to_reset)
fc_stream_ids_to_reset;
struct short_ack_info fc_saved_ack_info;
lsquic_time_t fc_saved_ack_received;
};
@ -593,6 +600,7 @@ full_conn_ci_destroy (lsquic_conn_t *lconn)
conn->fc_conn.cn_esf->esf_destroy(conn->fc_conn.cn_enc_session);
lsquic_malo_destroy(conn->fc_pub.packet_out_malo);
#if FULL_CONN_STATS
LSQ_NOTICE("# ticks: %lu", conn->fc_stats.n_ticks);
LSQ_NOTICE("received %u packets, of which %u were not decryptable, %u were "
"dups and %u were errors; sent %u packets, avg stream data per outgoing"
" packet is %lu bytes",
@ -600,6 +608,9 @@ full_conn_ci_destroy (lsquic_conn_t *lconn)
conn->fc_stats.n_dup_packets, conn->fc_stats.n_err_packets,
conn->fc_stats.n_packets_out,
conn->fc_stats.stream_data_sz / conn->fc_stats.n_packets_out);
LSQ_NOTICE("ACKs: in: %u; processed: %u; merged to: new %u, old %u",
conn->fc_stats.n_acks_in, conn->fc_stats.n_acks_proc,
conn->fc_stats.n_acks_merged[0], conn->fc_stats.n_acks_merged[1]);
#endif
while ((sitr = STAILQ_FIRST(&conn->fc_stream_ids_to_reset)))
{
@ -1095,34 +1106,214 @@ log_invalid_ack_frame (struct full_conn *conn, const unsigned char *p,
}
static int
process_ack (struct full_conn *conn, struct ack_info *acki,
lsquic_time_t received)
{
#if FULL_CONN_STATS
++conn->fc_stats.n_acks_proc;
#endif
LSQ_DEBUG("Processing ACK");
if (0 == lsquic_send_ctl_got_ack(&conn->fc_send_ctl, acki, received))
{
if (lsquic_send_ctl_largest_ack2ed(&conn->fc_send_ctl))
lsquic_rechist_stop_wait(&conn->fc_rechist,
lsquic_send_ctl_largest_ack2ed(&conn->fc_send_ctl) + 1);
return 0;
}
else
{
ABORT_ERROR("Received invalid ACK");
return -1;
}
}
static int
process_saved_ack (struct full_conn *conn, int restore_parsed_ack)
{
struct ack_info *const acki = conn->fc_pub.mm->acki;
struct lsquic_packno_range range = { 0 };
unsigned n_ranges = 0, n_timestamps = 0;
lsquic_time_t lack_delta = 0;
int retval;
if (restore_parsed_ack)
{
n_ranges = acki->n_ranges;
n_timestamps = acki->n_timestamps;
lack_delta = acki->lack_delta;
range = acki->ranges[0];
}
acki->n_ranges = 1;
acki->n_timestamps = conn->fc_saved_ack_info.sai_n_timestamps;
acki->lack_delta = conn->fc_saved_ack_info.sai_lack_delta;
acki->ranges[0] = conn->fc_saved_ack_info.sai_range;
retval = process_ack(conn, acki, conn->fc_saved_ack_received);
if (restore_parsed_ack)
{
acki->n_ranges = n_ranges;
acki->n_timestamps = n_timestamps;
acki->lack_delta = lack_delta;
acki->ranges[0] = range;
}
return retval;
}
static int
new_ack_is_superset (const struct short_ack_info *old, const struct ack_info *new)
{
const struct lsquic_packno_range *new_range;
new_range = &new->ranges[ new->n_ranges - 1 ];
return new_range->low <= old->sai_range.low
&& new_range->high >= old->sai_range.high;
}
static int
merge_saved_to_new (const struct short_ack_info *old, struct ack_info *new)
{
struct lsquic_packno_range *smallest_range;
assert(new->n_ranges > 1);
smallest_range = &new->ranges[ new->n_ranges - 1 ];
if (old->sai_range.high <= smallest_range->high
&& old->sai_range.high >= smallest_range->low
&& old->sai_range.low < smallest_range->low)
{
smallest_range->low = old->sai_range.low;
return 1;
}
else
return 0;
}
static int
merge_new_to_saved (struct short_ack_info *old, const struct ack_info *new)
{
const struct lsquic_packno_range *new_range;
assert(new->n_ranges == 1);
new_range = &new->ranges[0];
/* Only merge if new is higher, for simplicity. This is also the
* expected case.
*/
if (new_range->high > old->sai_range.high
&& new_range->low > old->sai_range.low)
{
old->sai_range.high = new_range->high;
return 1;
}
else
return 0;
}
static unsigned
process_ack_frame (struct full_conn *conn, lsquic_packet_in_t *packet_in,
const unsigned char *p, size_t len)
{
const int parsed_len = conn->fc_conn.cn_pf->pf_parse_ack_frame(p, len,
conn->fc_pub.mm->acki);
struct ack_info *const new_acki = conn->fc_pub.mm->acki;
int parsed_len;
#if FULL_CONN_STATS
++conn->fc_stats.n_acks_in;
#endif
parsed_len = conn->fc_conn.cn_pf->pf_parse_ack_frame(p, len, new_acki);
if (parsed_len < 0)
return 0;
if (packet_in->pi_packno > conn->fc_max_ack_packno)
goto err;
if (packet_in->pi_packno <= conn->fc_max_ack_packno)
{
EV_LOG_ACK_FRAME_IN(LSQUIC_LOG_CONN_ID, conn->fc_pub.mm->acki);
if (0 == lsquic_send_ctl_got_ack(&conn->fc_send_ctl,
conn->fc_pub.mm->acki, packet_in->pi_received))
{
conn->fc_max_ack_packno = packet_in->pi_packno;
if (lsquic_send_ctl_largest_ack2ed(&conn->fc_send_ctl))
lsquic_rechist_stop_wait(&conn->fc_rechist,
lsquic_send_ctl_largest_ack2ed(&conn->fc_send_ctl) + 1);
}
else
{
log_invalid_ack_frame(conn, p, parsed_len, conn->fc_pub.mm->acki);
ABORT_ERROR("Received invalid ACK");
LSQ_DEBUG("Ignore old ack (max %"PRIu64")", conn->fc_max_ack_packno);
return parsed_len;
}
EV_LOG_ACK_FRAME_IN(LSQUIC_LOG_CONN_ID, new_acki);
conn->fc_max_ack_packno = packet_in->pi_packno;
if (conn->fc_flags & FC_HAVE_SAVED_ACK)
{
LSQ_DEBUG("old ack [%"PRIu64"-%"PRIu64"]",
conn->fc_saved_ack_info.sai_range.high,
conn->fc_saved_ack_info.sai_range.low);
const int is_superset = new_ack_is_superset(&conn->fc_saved_ack_info,
new_acki);
const int is_1range = new_acki->n_ranges == 1;
switch (
(is_superset << 1)
| (is_1range << 0))
/* | |
| |
V V */ {
case (0 << 1) | (0 << 0):
if (merge_saved_to_new(&conn->fc_saved_ack_info, new_acki))
{
#if FULL_CONN_STATS
++conn->fc_stats.n_acks_merged[0]
#endif
;
}
else
process_saved_ack(conn, 1);
conn->fc_flags &= ~FC_HAVE_SAVED_ACK;
if (0 != process_ack(conn, new_acki, packet_in->pi_received))
goto err;
break;
case (0 << 1) | (1 << 0):
if (merge_new_to_saved(&conn->fc_saved_ack_info, new_acki))
{
#if FULL_CONN_STATS
++conn->fc_stats.n_acks_merged[1]
#endif
;
}
else
{
process_saved_ack(conn, 1);
conn->fc_saved_ack_info.sai_n_timestamps = new_acki->n_timestamps;
conn->fc_saved_ack_info.sai_range = new_acki->ranges[0];
}
conn->fc_saved_ack_info.sai_lack_delta = new_acki->lack_delta;
conn->fc_saved_ack_received = packet_in->pi_received;
break;
case (1 << 1) | (0 << 0):
conn->fc_flags &= ~FC_HAVE_SAVED_ACK;
if (0 != process_ack(conn, new_acki, packet_in->pi_received))
goto err;
break;
case (1 << 1) | (1 << 0):
conn->fc_saved_ack_info.sai_n_timestamps = new_acki->n_timestamps;
conn->fc_saved_ack_info.sai_lack_delta = new_acki->lack_delta;
conn->fc_saved_ack_info.sai_range = new_acki->ranges[0];
conn->fc_saved_ack_received = packet_in->pi_received;
break;
}
}
else
LSQ_DEBUG("Ignore old ack (max %"PRIu64")", conn->fc_max_ack_packno);
else if (new_acki->n_ranges == 1)
{
conn->fc_saved_ack_info.sai_n_timestamps = new_acki->n_timestamps;
conn->fc_saved_ack_info.sai_lack_delta = new_acki->lack_delta;
conn->fc_saved_ack_info.sai_range = new_acki->ranges[0];
conn->fc_saved_ack_received = packet_in->pi_received;
conn->fc_flags |= FC_HAVE_SAVED_ACK;
}
else if (0 != process_ack(conn, new_acki, packet_in->pi_received))
goto err;
return parsed_len;
err:
log_invalid_ack_frame(conn, p, parsed_len, new_acki);
return 0;
}
@ -2151,7 +2342,7 @@ generate_ack_frame (struct full_conn *conn)
(gaf_rechist_first_f) lsquic_rechist_first,
(gaf_rechist_next_f) lsquic_rechist_next,
(gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
&conn->fc_rechist, now, &has_missing);
&conn->fc_rechist, now, &has_missing, &packet_out->po_ack2ed);
if (w < 0) {
ABORT_ERROR("generating ACK frame failed: %d", errno);
return;
@ -2306,6 +2497,10 @@ full_conn_ci_tick (lsquic_conn_t *lconn, lsquic_time_t now)
} \
} while (0)
#if FULL_CONN_STATS
++conn->fc_stats.n_ticks;
#endif
if (LSQ_LOG_ENABLED(LSQ_LOG_DEBUG)
&& conn->fc_mem_logged_last + 1000000 <= now)
{
@ -2315,6 +2510,13 @@ full_conn_ci_tick (lsquic_conn_t *lconn, lsquic_time_t now)
assert(!(conn->fc_conn.cn_flags & LSCONN_RW_PENDING));
if (conn->fc_flags & FC_HAVE_SAVED_ACK)
{
(void) /* If there is an error, we'll fail shortly */
process_saved_ack(conn, 0);
conn->fc_flags &= ~FC_HAVE_SAVED_ACK;
}
lsquic_send_ctl_tick(&conn->fc_send_ctl, now);
lsquic_send_ctl_set_buffer_stream_packets(&conn->fc_send_ctl, 1);
CLOSE_IF_NECESSARY();

View File

@ -83,10 +83,12 @@ typedef struct lsquic_packet_out
* further writes are allowed.
*/
PO_SCHED = (1 <<14), /* On scheduled queue */
PO_SENT_SZ = (1 <<15),
} po_flags:16;
enum quic_ft_bit po_frame_types:16; /* Bitmask of QUIC_FRAME_* */
unsigned short po_data_sz; /* Number of usable bytes in data */
unsigned short po_enc_data_sz; /* Number of usable bytes in data */
unsigned short po_sent_sz; /* If PO_SENT_SZ is set, real size of sent buffer. */
unsigned short po_regen_sz; /* Number of bytes at the beginning
* of data containing bytes that are
* not to be retransmitted, e.g. ACK
@ -94,6 +96,9 @@ typedef struct lsquic_packet_out
*/
unsigned short po_n_alloc; /* Total number of bytes allocated in po_data */
unsigned char *po_data;
lsquic_packno_t po_ack2ed; /* If packet has ACK frame, value of
* largest acked in it.
*/
/* A lot of packets contain data belonging to only one stream. Thus,
* `one' is used first. If this is not enough, any number of
@ -142,6 +147,23 @@ typedef struct lsquic_packet_out
((p)->po_data_sz + lsquic_po_header_length((p)->po_flags) \
+ QUIC_PACKET_HASH_SZ)
#if __GNUC__
#if LSQUIC_EXTRA_CHECKS
#define lsquic_packet_out_sent_sz(p) ( \
__builtin_expect(((p)->po_flags & PO_SENT_SZ), 1) ? \
(assert((p)->po_sent_sz == lsquic_packet_out_total_sz(p)), \
(p)->po_sent_sz) : lsquic_packet_out_total_sz(p))
# else
#define lsquic_packet_out_sent_sz(p) ( \
__builtin_expect(((p)->po_flags & PO_SENT_SZ), 1) ? \
(p)->po_sent_sz : lsquic_packet_out_total_sz(p))
#endif
#else
# define lsquic_packet_out_sent_sz(p) ( \
(p)->po_flags & PO_SENT_SZ ? \
(p)->po_sent_sz : lsquic_packet_out_total_sz(p))
#endif
#define lsquic_packet_out_verneg(p) \
(((p)->po_flags & (PO_NOENCRYPT|PO_VERNEG)) == (PO_NOENCRYPT|PO_VERNEG))

View File

@ -31,6 +31,13 @@ typedef struct ack_info
#endif
} ack_info_t;
struct short_ack_info
{
unsigned sai_n_timestamps;
lsquic_time_t sai_lack_delta;
struct lsquic_packno_range sai_range;
};
#define largest_acked(acki) (+(acki)->ranges[0].high)
#define smallest_acked(acki) (+(acki)->ranges[(acki)->n_ranges - 1].low)
@ -86,13 +93,11 @@ struct parse_funcs
int
(*pf_parse_ack_frame) (const unsigned char *buf, size_t buf_len,
ack_info_t *ack_info);
lsquic_packno_t
(*pf_parse_ack_high) (const unsigned char *buf, size_t buf_len);
int
(*pf_gen_ack_frame) (unsigned char *outbuf, size_t outbuf_sz,
gaf_rechist_first_f, gaf_rechist_next_f,
gaf_rechist_largest_recv_f, void *rechist, lsquic_time_t now,
int *has_missing);
int *has_missing, lsquic_packno_t *largest_received);
int
(*pf_gen_stop_waiting_frame) (unsigned char *buf, size_t buf_len,
lsquic_packno_t cur_packno, enum lsquic_packno_bits,

View File

@ -267,28 +267,6 @@ gquic_ietf_gen_stream_frame (unsigned char *buf, size_t buf_len, uint32_t stream
}
/* This is a special function: it is used to extract the largest observed
* packet number from ACK frame that we ourselves generated. This allows
* us to skip some checks.
*/
lsquic_packno_t
gquic_ietf_parse_ack_high (const unsigned char *buf, size_t buf_len)
{
unsigned char type;
unsigned largest_obs_len;
unsigned n_blocks_len;
lsquic_packno_t packno;
type = buf[0];
largest_obs_len = twobit_to_1248((type >> 2) & 3);
n_blocks_len = !!(type & 0x10);
assert(parse_frame_type_gquic_Q041(type) == QUIC_FRAME_ACK);
assert(buf_len >= 1 + n_blocks_len + 1 + largest_obs_len);
READ_UINT(packno, 64, buf + 1 + n_blocks_len + 1, largest_obs_len);
return packno;
}
int
gquic_ietf_parse_ack_frame (const unsigned char *buf, size_t buf_len,
ack_info_t *ack)
@ -383,7 +361,8 @@ int
gquic_ietf_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
gaf_rechist_first_f rechist_first, gaf_rechist_next_f rechist_next,
gaf_rechist_largest_recv_f rechist_largest_recv,
void *rechist, lsquic_time_t now, int *has_missing)
void *rechist, lsquic_time_t now, int *has_missing,
lsquic_packno_t *largest_received)
{
lsquic_packno_t tmp_packno;
const struct lsquic_packno_range *const first = rechist_first(rechist);
@ -539,6 +518,7 @@ gquic_ietf_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
p += ack_block_len;
}
*largest_received = maxno;
return p - (unsigned char *) outbuf;
#undef CHECKOUT
@ -555,7 +535,6 @@ const struct parse_funcs lsquic_parse_funcs_gquic_Q041 =
.pf_parse_stream_frame_header_sz = gquic_ietf_parse_stream_frame_header_sz,
.pf_parse_stream_frame = gquic_ietf_parse_stream_frame,
.pf_parse_ack_frame = gquic_ietf_parse_ack_frame,
.pf_parse_ack_high = gquic_ietf_parse_ack_high,
.pf_gen_ack_frame = gquic_ietf_gen_ack_frame,
.pf_gen_stop_waiting_frame = gquic_be_gen_stop_waiting_frame,
.pf_parse_stop_waiting_frame = gquic_be_parse_stop_waiting_frame,

View File

@ -367,26 +367,6 @@ gquic_be_parse_stream_frame (const unsigned char *buf, size_t rem_packet_sz,
}
/* This is a special function: it is used to extract the largest observed
* packet number from ACK frame that we ourselves generated. This allows
* us to skip some checks.
*/
lsquic_packno_t
gquic_be_parse_ack_high (const unsigned char *buf, size_t buf_len)
{
unsigned char type;
unsigned largest_obs_len;
lsquic_packno_t packno;
type = buf[0];
largest_obs_len = twobit_to_1246((type >> 2) & 3);
assert(parse_frame_type_gquic_Q035_thru_Q039(type) == QUIC_FRAME_ACK);
assert(buf_len >= 1 + largest_obs_len);
READ_UINT(packno, 64, buf + 1, largest_obs_len);
return packno;
}
static int
parse_ack_frame_without_blocks (const unsigned char *buf, size_t buf_len,
ack_info_t *ack)
@ -813,7 +793,8 @@ int
gquic_be_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
gaf_rechist_first_f rechist_first, gaf_rechist_next_f rechist_next,
gaf_rechist_largest_recv_f rechist_largest_recv,
void *rechist, lsquic_time_t now, int *has_missing)
void *rechist, lsquic_time_t now, int *has_missing,
lsquic_packno_t *largest_received)
{
lsquic_packno_t tmp_packno;
const struct lsquic_packno_range *const first = rechist_first(rechist);
@ -966,6 +947,7 @@ gquic_be_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
*p = 0;
++p;
*largest_received = maxno;
return p - (unsigned char *) outbuf;
#undef CHECKOUT
@ -982,7 +964,6 @@ const struct parse_funcs lsquic_parse_funcs_gquic_Q039 =
.pf_parse_stream_frame_header_sz = parse_stream_frame_header_sz_gquic,
.pf_parse_stream_frame = gquic_be_parse_stream_frame,
.pf_parse_ack_frame = gquic_be_parse_ack_frame,
.pf_parse_ack_high = gquic_be_parse_ack_high,
.pf_gen_ack_frame = gquic_be_gen_ack_frame,
.pf_gen_stop_waiting_frame = gquic_be_gen_stop_waiting_frame,
.pf_parse_stop_waiting_frame = gquic_be_parse_stop_waiting_frame,

View File

@ -139,6 +139,6 @@ int
gquic_be_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
gaf_rechist_first_f rechist_first, gaf_rechist_next_f rechist_next,
gaf_rechist_largest_recv_f rechist_largest_recv,
void *rechist, lsquic_time_t now, int *has_missing);
void *rechist, lsquic_time_t now, int *has_missing, lsquic_packno_t *);
#endif

View File

@ -337,24 +337,6 @@ gquic_le_parse_stream_frame (const unsigned char *buf, size_t rem_packet_sz,
}
/* This is a special function: it is used to extract the largest observed
* packet number from ACK frame that we ourselves generated. This allows
* us to skip some checks.
*/
static lsquic_packno_t
gquic_le_parse_ack_high (const unsigned char *buf, size_t buf_len)
{
unsigned char type;
unsigned largest_obs_len;
type = buf[0];
largest_obs_len = flag_to_pkt_num_len(type >> 2);
assert(parse_frame_type_gquic_Q035_thru_Q039(type) == QUIC_FRAME_ACK);
assert(buf_len >= 1 + largest_obs_len);
return get_vary_len_int64(buf + 1, largest_obs_len);
}
/* Return parsed (used) buffer length.
* If parsing failed, negative value is returned.
*/
@ -705,7 +687,8 @@ static int
gquic_le_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
gaf_rechist_first_f rechist_first, gaf_rechist_next_f rechist_next,
gaf_rechist_largest_recv_f rechist_largest_recv,
void *rechist, lsquic_time_t now, int *has_missing)
void *rechist, lsquic_time_t now, int *has_missing,
lsquic_packno_t *largest_received)
{
const struct lsquic_packno_range *const first = rechist_first(rechist);
if (!first)
@ -841,6 +824,7 @@ gquic_le_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
*p = 0;
++p;
*largest_received = maxno;
return p - (unsigned char *) outbuf;
#undef CHECKOUT
@ -857,7 +841,6 @@ const struct parse_funcs lsquic_parse_funcs_gquic_le =
.pf_parse_stream_frame_header_sz = parse_stream_frame_header_sz_gquic,
.pf_parse_stream_frame = gquic_le_parse_stream_frame,
.pf_parse_ack_frame = gquic_le_parse_ack_frame,
.pf_parse_ack_high = gquic_le_parse_ack_high,
.pf_gen_ack_frame = gquic_le_gen_ack_frame,
.pf_gen_stop_waiting_frame = gquic_le_gen_stop_waiting_frame,
.pf_parse_stop_waiting_frame = gquic_le_parse_stop_waiting_frame,

View File

@ -410,12 +410,9 @@ send_ctl_unacked_append (struct lsquic_send_ctl *ctl,
static void
send_ctl_unacked_remove (struct lsquic_send_ctl *ctl,
struct lsquic_packet_out *packet_out)
struct lsquic_packet_out *packet_out, unsigned packet_sz)
{
unsigned packet_sz;
TAILQ_REMOVE(&ctl->sc_unacked_packets, packet_out, po_next);
packet_sz = lsquic_packet_out_total_sz(packet_out);
assert(ctl->sc_bytes_unacked_all >= packet_sz);
ctl->sc_bytes_unacked_all -= packet_sz;
ctl->sc_n_in_flight_all -= 1;
@ -479,47 +476,56 @@ lsquic_send_ctl_sent_packet (lsquic_send_ctl_t *ctl,
sizeof(frames), packet_out->po_frame_types));
if (account)
ctl->sc_bytes_out -= lsquic_packet_out_total_sz(packet_out);
if (0 == lsquic_senhist_add(&ctl->sc_senhist, packet_out->po_packno))
lsquic_senhist_add(&ctl->sc_senhist, packet_out->po_packno);
send_ctl_unacked_append(ctl, packet_out);
if (packet_out->po_frame_types & QFRAME_RETRANSMITTABLE_MASK)
{
send_ctl_unacked_append(ctl, packet_out);
if (packet_out->po_frame_types & QFRAME_RETRANSMITTABLE_MASK)
{
if (!lsquic_alarmset_is_set(ctl->sc_alset, AL_RETX))
set_retx_alarm(ctl);
if (ctl->sc_n_in_flight_retx == 1)
ctl->sc_flags |= SC_WAS_QUIET;
}
/* TODO: Do we really want to use those for RTT info? Revisit this. */
/* Hold on to packets that are not retransmittable because we need them
* to sample RTT information. They are released when ACK is received.
*/
#if LSQUIC_SEND_STATS
++ctl->sc_stats.n_total_sent;
#endif
return 0;
if (!lsquic_alarmset_is_set(ctl->sc_alset, AL_RETX))
set_retx_alarm(ctl);
if (ctl->sc_n_in_flight_retx == 1)
ctl->sc_flags |= SC_WAS_QUIET;
}
else
return -1;
/* TODO: Do we really want to use those for RTT info? Revisit this. */
/* Hold on to packets that are not retransmittable because we need them
* to sample RTT information. They are released when ACK is received.
*/
#if LSQUIC_SEND_STATS
++ctl->sc_stats.n_total_sent;
#endif
lsquic_send_ctl_sanity_check(ctl);
return 0;
}
static void
take_rtt_sample (lsquic_send_ctl_t *ctl, const lsquic_packet_out_t *packet_out,
take_rtt_sample (lsquic_send_ctl_t *ctl,
lsquic_time_t now, lsquic_time_t lack_delta)
{
assert(packet_out->po_sent);
lsquic_time_t measured_rtt = now - packet_out->po_sent;
if (packet_out->po_packno > ctl->sc_max_rtt_packno && lack_delta < measured_rtt)
const lsquic_packno_t packno = ctl->sc_largest_acked_packno;
const lsquic_time_t sent = ctl->sc_largest_acked_sent_time;
const lsquic_time_t measured_rtt = now - sent;
if (packno > ctl->sc_max_rtt_packno && lack_delta < measured_rtt)
{
ctl->sc_max_rtt_packno = packet_out->po_packno;
ctl->sc_max_rtt_packno = packno;
lsquic_rtt_stats_update(&ctl->sc_conn_pub->rtt_stats, measured_rtt, lack_delta);
LSQ_DEBUG("packno %"PRIu64"; rtt: %"PRIu64"; delta: %"PRIu64"; "
"new srtt: %"PRIu64, packet_out->po_packno, measured_rtt, lack_delta,
"new srtt: %"PRIu64, packno, measured_rtt, lack_delta,
lsquic_rtt_stats_get_srtt(&ctl->sc_conn_pub->rtt_stats));
}
}
static void
send_ctl_release_enc_data (struct lsquic_send_ctl *ctl,
struct lsquic_packet_out *packet_out)
{
ctl->sc_enpub->enp_pmi->pmi_release(ctl->sc_enpub->enp_pmi_ctx,
packet_out->po_enc_data);
packet_out->po_flags &= ~PO_ENCRYPTED;
packet_out->po_enc_data = NULL;
}
/* Returns true if packet was rescheduled, false otherwise. In the latter
* case, you should not dereference packet_out after the function returns.
*/
@ -527,14 +533,13 @@ static int
send_ctl_handle_lost_packet (lsquic_send_ctl_t *ctl,
lsquic_packet_out_t *packet_out)
{
unsigned packet_sz;
assert(ctl->sc_n_in_flight_all);
send_ctl_unacked_remove(ctl, packet_out);
if (packet_out->po_flags & PO_ENCRYPTED) {
ctl->sc_enpub->enp_pmi->pmi_release(ctl->sc_enpub->enp_pmi_ctx,
packet_out->po_enc_data);
packet_out->po_flags &= ~PO_ENCRYPTED;
packet_out->po_enc_data = NULL;
}
packet_sz = lsquic_packet_out_sent_sz(packet_out);
send_ctl_unacked_remove(ctl, packet_out, packet_sz);
if (packet_out->po_flags & PO_ENCRYPTED)
send_ctl_release_enc_data(ctl, packet_out);
if (packet_out->po_frame_types & (1 << QUIC_FRAME_ACK))
{
ctl->sc_flags |= SC_LOST_ACK;
@ -652,102 +657,110 @@ lsquic_send_ctl_got_ack (lsquic_send_ctl_t *ctl,
{
struct lsquic_packets_tailq acked_acks =
TAILQ_HEAD_INITIALIZER(acked_acks);
#if !LSQUIC_CAN_REORDER
const struct lsquic_packno_range *range =
&acki->ranges[ acki->n_ranges - 1 ];
#endif
lsquic_packet_out_t *packet_out, *next;
lsquic_time_t now = lsquic_time_now();
lsquic_packno_t high;
int rtt_updated = 0;
lsquic_time_t now = 0;
lsquic_packno_t smallest_unacked;
lsquic_packno_t ack2ed[2];
unsigned packet_sz;
int app_limited;
unsigned n;
signed char do_rtt, skip_checks;
LSQ_DEBUG("Got ACK frame, largest acked: %"PRIu64"; delta: %"PRIu64,
largest_acked(acki), acki->lack_delta);
packet_out = TAILQ_FIRST(&ctl->sc_unacked_packets);
#if __GNUC__
__builtin_prefetch(packet_out);
#endif
#if __GNUC__
# define UNLIKELY(cond) __builtin_expect(cond, 0)
#else
# define UNLIKELY(cond) cond
#endif
#if __GNUC__
if (UNLIKELY(LSQ_LOG_ENABLED(LSQ_LOG_DEBUG)))
#endif
LSQ_DEBUG("Got ACK frame, largest acked: %"PRIu64"; delta: %"PRIu64,
largest_acked(acki), acki->lack_delta);
/* Validate ACK first: */
for (n = 0; n < acki->n_ranges; ++n)
if (!lsquic_senhist_sent_range(&ctl->sc_senhist, acki->ranges[n].low,
acki->ranges[n].high))
{
LSQ_INFO("at least one packet in ACK range [%"PRIu64" - %"PRIu64"] "
"was never sent", acki->ranges[n].low, acki->ranges[n].high);
return -1;
}
if (UNLIKELY(largest_acked(acki)
> lsquic_senhist_largest(&ctl->sc_senhist)))
{
LSQ_INFO("at least one packet in ACK range [%"PRIu64" - %"PRIu64"] "
"was never sent", acki->ranges[0].low, acki->ranges[0].high);
return -1;
}
if (ctl->sc_flags & SC_WAS_QUIET)
if (UNLIKELY(ctl->sc_flags & SC_WAS_QUIET))
{
ctl->sc_flags &= ~SC_WAS_QUIET;
LSQ_DEBUG("ACK comes after a period of quiescence");
if (!now)
now = lsquic_time_now();
lsquic_cubic_was_quiet(&ctl->sc_cubic, now);
}
/* Peer is acking packets that have been acked already. Schedule ACK
* and STOP_WAITING frame to chop the range if we get two of these in
* a row.
*/
if (lsquic_send_ctl_smallest_unacked(ctl) > smallest_acked(acki))
++ctl->sc_n_stop_waiting;
else
ctl->sc_n_stop_waiting = 0;
if (UNLIKELY(!packet_out))
goto no_unacked_packets;
app_limited = send_ctl_retx_bytes_out(ctl) + 3 * ctl->sc_pack_size /* This
is the "maximum burst" parameter */
< lsquic_cubic_get_cwnd(&ctl->sc_cubic);
smallest_unacked = packet_out->po_packno;
ack2ed[1] = 0;
for (packet_out = TAILQ_FIRST(&ctl->sc_unacked_packets);
packet_out
#if !LSQUIC_CAN_REORDER
&& packet_out->po_packno <= largest_acked(acki)
#endif
;
packet_out = next)
if (packet_out->po_packno > largest_acked(acki))
goto detect_losses;
do_rtt = 0, skip_checks = 0;
app_limited = -1;
do
{
next = TAILQ_NEXT(packet_out, po_next);
#if LSQUIC_CAN_REORDER
if (!in_acked_range(acki, packet_out->po_packno))
continue;
#else
/* This is faster than binary search in the normal case when the number
* of ranges is not much larger than the number of unacked packets.
*/
while (range->high < packet_out->po_packno)
--range;
if (range->low > packet_out->po_packno)
continue;
#endif
ctl->sc_largest_acked_packno = packet_out->po_packno;
ctl->sc_largest_acked_sent_time = packet_out->po_sent;
if (packet_out->po_packno == largest_acked(acki))
{
take_rtt_sample(ctl, packet_out, ack_recv_time, acki->lack_delta);
++rtt_updated;
}
lsquic_cubic_ack(&ctl->sc_cubic, now, now - packet_out->po_sent,
app_limited, lsquic_packet_out_total_sz(packet_out));
LSQ_DEBUG("Got ACK for packet %"PRIu64", remove from unacked queue",
packet_out->po_packno);
assert(ctl->sc_n_in_flight_all);
send_ctl_unacked_remove(ctl, packet_out);
lsquic_packet_out_ack_streams(packet_out);
#if __GNUC__
__builtin_prefetch(next);
#endif
if ((ctl->sc_flags & SC_NSTP) &&
(packet_out->po_frame_types & (1 << QUIC_FRAME_ACK)))
TAILQ_INSERT_TAIL(&acked_acks, packet_out, po_next);
else
if (skip_checks)
goto after_checks;
/* This is faster than binary search in the normal case when the number
* of ranges is not much larger than the number of unacked packets.
*/
while (UNLIKELY(range->high < packet_out->po_packno))
--range;
if (range->low <= packet_out->po_packno)
{
skip_checks = range == acki->ranges;
if (app_limited < 0)
app_limited = send_ctl_retx_bytes_out(ctl) + 3 * ctl->sc_pack_size /* This
is the "maximum burst" parameter */
< lsquic_cubic_get_cwnd(&ctl->sc_cubic);
if (!now)
now = lsquic_time_now();
after_checks:
packet_sz = lsquic_packet_out_sent_sz(packet_out);
ctl->sc_largest_acked_packno = packet_out->po_packno;
ctl->sc_largest_acked_sent_time = packet_out->po_sent;
send_ctl_unacked_remove(ctl, packet_out, packet_sz);
ack2ed[!!(packet_out->po_frame_types & (1 << QUIC_FRAME_ACK))]
= packet_out->po_ack2ed;
do_rtt |= packet_out->po_packno == largest_acked(acki);
lsquic_cubic_ack(&ctl->sc_cubic, now, now - packet_out->po_sent,
app_limited, packet_sz);
lsquic_packet_out_ack_streams(packet_out);
lsquic_packet_out_destroy(packet_out, ctl->sc_enpub);
}
packet_out = next;
}
while (packet_out && packet_out->po_packno <= largest_acked(acki));
if (rtt_updated)
if (do_rtt)
{
take_rtt_sample(ctl, ack_recv_time, acki->lack_delta);
ctl->sc_n_consec_rtos = 0;
ctl->sc_n_hsk = 0;
ctl->sc_n_tlp = 0;
}
detect_losses:
send_ctl_detect_losses(ctl, ack_recv_time);
if (send_ctl_first_unacked_retx_packet(ctl))
set_retx_alarm(ctl);
@ -758,29 +771,28 @@ lsquic_send_ctl_got_ack (lsquic_send_ctl_t *ctl,
}
lsquic_send_ctl_sanity_check(ctl);
/* Processing of packets that contain acked ACK frames is deferred because
* we only need to process one of them: the last one, which we know to
* contain the largest value.
*/
packet_out = TAILQ_LAST(&acked_acks, lsquic_packets_tailq);
if (packet_out)
{
high = ctl->sc_conn_pub->lconn->cn_pf->pf_parse_ack_high(
packet_out->po_data, packet_out->po_data_sz);
if (high > ctl->sc_largest_ack2ed)
ctl->sc_largest_ack2ed = high;
do
{
next = TAILQ_PREV(packet_out, lsquic_packets_tailq, po_next);
lsquic_packet_out_destroy(packet_out, ctl->sc_enpub);
}
while ((packet_out = next));
}
if ((ctl->sc_flags & SC_NSTP) && ack2ed[1] > ctl->sc_largest_ack2ed)
ctl->sc_largest_ack2ed = ack2ed[1];
if (ctl->sc_n_in_flight_retx == 0)
ctl->sc_flags |= SC_WAS_QUIET;
update_n_stop_waiting:
if (smallest_unacked > smallest_acked(acki))
/* Peer is acking packets that have been acked already. Schedule ACK
* and STOP_WAITING frame to chop the range if we get two of these in
* a row.
*/
++ctl->sc_n_stop_waiting;
else
ctl->sc_n_stop_waiting = 0;
lsquic_send_ctl_sanity_check(ctl);
return 0;
no_unacked_packets:
smallest_unacked = lsquic_senhist_largest(&ctl->sc_senhist) + 1;
ctl->sc_flags |= SC_WAS_QUIET;
goto update_n_stop_waiting;
}
@ -793,7 +805,7 @@ lsquic_send_ctl_smallest_unacked (lsquic_send_ctl_t *ctl)
* on purpose). Thus, the first packet on the unacked packets list has
* the smallest packet number of all packets on that list.
*/
if ((packet_out = TAILQ_FIRST(&ctl->sc_unacked_packets)))
if ((packet_out = TAILQ_FIRST(&ctl->sc_unacked_packets)))
return packet_out->po_packno;
else
return lsquic_senhist_largest(&ctl->sc_senhist) + 1;
@ -966,7 +978,7 @@ void
lsquic_send_ctl_sanity_check (const lsquic_send_ctl_t *ctl)
{
const struct lsquic_packet_out *packet_out;
unsigned count, sched_bytes;
unsigned count, bytes;
assert(!send_ctl_first_unacked_retx_packet(ctl) ||
lsquic_alarmset_is_set(ctl->sc_alset, AL_RETX));
@ -976,20 +988,24 @@ lsquic_send_ctl_sanity_check (const lsquic_send_ctl_t *ctl)
assert(lsquic_time_now() < ctl->sc_alset->as_expiry[AL_RETX] + MAX_RTO_DELAY);
}
count = 0;
count = 0, bytes = 0;
TAILQ_FOREACH(packet_out, &ctl->sc_unacked_packets, po_next)
{
bytes += lsquic_packet_out_sent_sz(packet_out);
++count;
}
assert(count == ctl->sc_n_in_flight_all);
assert(bytes == ctl->sc_bytes_unacked_all);
count = 0, sched_bytes = 0;
count = 0, bytes = 0;
TAILQ_FOREACH(packet_out, &ctl->sc_scheduled_packets, po_next)
{
assert(packet_out->po_flags & PO_SCHED);
sched_bytes += lsquic_packet_out_total_sz(packet_out);
bytes += lsquic_packet_out_total_sz(packet_out);
++count;
}
assert(count == ctl->sc_n_scheduled);
assert(sched_bytes == ctl->sc_bytes_scheduled);
assert(bytes == ctl->sc_bytes_scheduled);
}
@ -1204,6 +1220,7 @@ update_for_resending (lsquic_send_ctl_t *ctl, lsquic_packet_out_t *packet_out)
oldno = packet_out->po_packno;
packno = send_ctl_next_packno(ctl);
packet_out->po_flags &= ~PO_SENT_SZ;
packet_out->po_frame_types &= ~QFRAME_REGEN_MASK;
assert(packet_out->po_frame_types);
packet_out->po_packno = packno;
@ -1284,9 +1301,11 @@ lsquic_send_ctl_set_tcid0 (lsquic_send_ctl_t *ctl, int tcid0)
void
lsquic_send_ctl_elide_stream_frames (lsquic_send_ctl_t *ctl, uint32_t stream_id)
{
struct lsquic_packet_out *packet_out = NULL, *next = NULL;
struct lsquic_packet_out *packet_out, *next = NULL;
struct lsquic_packet_out *pre_dropped;
unsigned n, adj;
pre_dropped = NULL;
for (packet_out = TAILQ_FIRST(&ctl->sc_scheduled_packets); packet_out;
packet_out = next)
{
@ -1300,6 +1319,9 @@ lsquic_send_ctl_elide_stream_frames (lsquic_send_ctl_t *ctl, uint32_t stream_id)
ctl->sc_bytes_scheduled -= adj;
if (0 == packet_out->po_frame_types)
{
if (!pre_dropped)
pre_dropped = TAILQ_PREV(packet_out, lsquic_packets_tailq,
po_next);
LSQ_DEBUG("cancel packet %"PRIu64" after eliding frames for "
"stream %"PRIu32, packet_out->po_packno, stream_id);
send_ctl_sched_remove(ctl, packet_out);
@ -1308,6 +1330,21 @@ lsquic_send_ctl_elide_stream_frames (lsquic_send_ctl_t *ctl, uint32_t stream_id)
}
}
/* Need to assign new packet numbers to all packets following the first
* dropped packet to eliminate packet number gap.
*/
if (pre_dropped)
{
ctl->sc_cur_packno = lsquic_senhist_largest(&ctl->sc_senhist);
for (packet_out = TAILQ_NEXT(pre_dropped, po_next); packet_out;
packet_out = TAILQ_NEXT(packet_out, po_next))
{
packet_out->po_flags |= PO_REPACKNO;
if (packet_out->po_flags & PO_ENCRYPTED)
send_ctl_release_enc_data(ctl, packet_out);
}
}
for (n = 0; n < sizeof(ctl->sc_buffered_packets) /
sizeof(ctl->sc_buffered_packets[0]); ++n)
{
@ -1339,7 +1376,7 @@ lsquic_send_ctl_elide_stream_frames (lsquic_send_ctl_t *ctl, uint32_t stream_id)
* packets.
*/
#ifndef NDEBUG
#if __GNUC__
#if __GNUC__
__attribute__((weak))
#endif
#endif
@ -1420,12 +1457,7 @@ lsquic_send_ctl_squeeze_sched (lsquic_send_ctl_t *ctl)
if (packet_out->po_regen_sz < packet_out->po_data_sz)
{
if (packet_out->po_flags & PO_ENCRYPTED)
{
ctl->sc_enpub->enp_pmi->pmi_release(ctl->sc_enpub->enp_pmi_ctx,
packet_out->po_enc_data);
packet_out->po_enc_data = NULL;
packet_out->po_flags &= ~PO_ENCRYPTED;
}
send_ctl_release_enc_data(ctl, packet_out);
}
else
{
@ -1490,6 +1522,7 @@ lsquic_send_ctl_drop_scheduled (lsquic_send_ctl_t *ctl)
lsquic_packet_out_destroy(packet_out, ctl->sc_enpub);
}
assert(0 == ctl->sc_n_scheduled);
ctl->sc_cur_packno = lsquic_senhist_largest(&ctl->sc_senhist);
LSQ_DEBUG("dropped %u scheduled packet%s", n, n != 0 ? "s" : "");
}

View File

@ -28,23 +28,51 @@ struct buf_packet_q
unsigned bpq_count;
};
enum send_ctl_flags {
SC_TCID0 = (1 << 0),
SC_LOST_ACK = (1 << 1),
SC_NSTP = (1 << 2),
SC_PACE = (1 << 3),
SC_SCHED_TICK = (1 << 4),
SC_BUFFER_STREAM= (1 << 5),
SC_WAS_QUIET = (1 << 6),
};
typedef struct lsquic_send_ctl {
/* The first section consists of struct members which are used in the
* time-critical lsquic_send_ctl_got_ack() in the approximate order
* of usage.
*/
lsquic_senhist_t sc_senhist;
enum send_ctl_flags sc_flags;
unsigned sc_n_stop_waiting;
struct lsquic_packets_tailq sc_unacked_packets;
lsquic_packno_t sc_largest_acked_packno;
lsquic_time_t sc_largest_acked_sent_time;
unsigned sc_bytes_out;
unsigned sc_bytes_unacked_retx;
unsigned sc_bytes_scheduled;
unsigned sc_pack_size;
struct lsquic_cubic sc_cubic;
struct lsquic_engine_public *sc_enpub;
unsigned sc_bytes_unacked_all;
unsigned sc_n_in_flight_all;
unsigned sc_n_in_flight_retx;
unsigned sc_n_consec_rtos;
unsigned sc_n_hsk;
unsigned sc_n_tlp;
struct lsquic_alarmset *sc_alset;
/* Second section: everything else. */
struct lsquic_packets_tailq sc_scheduled_packets,
sc_unacked_packets,
sc_lost_packets;
struct buf_packet_q sc_buffered_packets[BPT_OTHER_PRIO + 1];
struct lsquic_engine_public *sc_enpub;
struct lsquic_alarmset *sc_alset;
const struct ver_neg *sc_ver_neg;
struct lsquic_conn_public *sc_conn_pub;
struct lsquic_cubic sc_cubic;
struct pacer sc_pacer;
lsquic_senhist_t sc_senhist;
lsquic_packno_t sc_cur_packno;
lsquic_packno_t sc_largest_sent_at_cutback;
lsquic_packno_t sc_max_rtt_packno;
lsquic_packno_t sc_largest_acked_packno;
lsquic_time_t sc_largest_acked_sent_time;
/* sc_largest_ack2ed is the packet number sent by peer that we acked and
* we know that our ACK was received by peer. This is used to determine
* the receive history cutoff point for the purposes of generating ACK
@ -59,28 +87,8 @@ typedef struct lsquic_send_ctl {
uint32_t stream_id;
enum buf_packet_type packet_type;
} sc_cached_bpt;
unsigned sc_bytes_out;
unsigned sc_bytes_unacked_all;
unsigned sc_bytes_unacked_retx;
unsigned sc_n_consec_rtos;
unsigned sc_next_limit;
unsigned sc_n_in_flight_all;
unsigned sc_n_in_flight_retx;
unsigned sc_n_scheduled;
unsigned sc_bytes_scheduled;
unsigned sc_n_stop_waiting;
unsigned short sc_pack_size;
enum {
SC_TCID0 = (1 << 0),
SC_LOST_ACK = (1 << 1),
SC_NSTP = (1 << 2),
SC_PACE = (1 << 3),
SC_SCHED_TICK = (1 << 4),
SC_BUFFER_STREAM= (1 << 5),
SC_WAS_QUIET = (1 << 6),
} sc_flags:8;
unsigned char sc_n_hsk;
unsigned char sc_n_tlp;
#if LSQUIC_SEND_STATS
struct {
unsigned n_total_sent,

View File

@ -3,126 +3,18 @@
* lsquic_senhist.c -- Sent history implementation
*/
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lsquic_int_types.h"
#include "lsquic_senhist.h"
void
lsquic_senhist_init (lsquic_senhist_t *hist)
{
lsquic_packints_init(&hist->sh_pints);
}
void
lsquic_senhist_cleanup (lsquic_senhist_t *hist)
{
lsquic_packints_cleanup(&hist->sh_pints);
}
/* At the time of this writing, the only reason the sequence of sent
* packet numbers could contain a hole is elision of stream frames from
* scheduled, but delayed packets. If such packet becomes empty after
* elision, it is dropped from the queue.
*/
/* The fast insert is used in the normal case, when packets are sent
* out in the same order in which they are scheduled: that is, their
* packet numbers are always increasing.
*/
static int
senhist_add_fast (lsquic_senhist_t *hist, lsquic_packno_t packno)
{
struct packet_interval *pi;
pi = TAILQ_FIRST(&hist->sh_pints.pk_intervals);
if (pi)
{
/* Check that packet numbers are always increasing */
assert(packno > pi->range.high);
if (packno == pi->range.high + 1)
{
++pi->range.high;
return 0;
}
}
pi = malloc(sizeof(*pi));
if (!pi)
return -1;
pi->range.high = packno;
pi->range.low = packno;
TAILQ_INSERT_HEAD(&hist->sh_pints.pk_intervals, pi, next_pi);
return 0;
}
int
lsquic_senhist_add (lsquic_senhist_t *hist, lsquic_packno_t packno)
{
return senhist_add_fast(hist, packno);
}
int
lsquic_senhist_sent_range (lsquic_senhist_t *hist, lsquic_packno_t low,
lsquic_packno_t high)
{
const struct lsquic_packno_range *range;
for (range = lsquic_packints_first(&hist->sh_pints); range;
range = lsquic_packints_next(&hist->sh_pints))
if (range->low <= low && range->high >= high)
return 1;
return 0;
}
lsquic_packno_t
lsquic_senhist_largest (lsquic_senhist_t *hist)
{
const struct lsquic_packno_range *range;
range = lsquic_packints_first(&hist->sh_pints);
if (range)
return range->high;
else
return 0;
}
void
lsquic_senhist_tostr (lsquic_senhist_t *hist, char *buf, size_t bufsz)
{
const struct lsquic_packno_range *range;
size_t off;
int n;
for (off = 0, range = lsquic_packints_first(&hist->sh_pints);
range && off < bufsz;
off += n, range = lsquic_packints_next(&hist->sh_pints))
{
n = snprintf(buf + off, bufsz - off, "[%"PRIu64"-%"PRIu64"]",
range->high, range->low);
if (n < 0 || (size_t) n >= bufsz - off)
break;
}
if (bufsz > 0)
buf[off] = '\0';
if (hist->sh_last_sent)
snprintf(buf, bufsz, "[1-%"PRIu64"]", hist->sh_last_sent);
else
snprintf(buf, bufsz, "[]");
}
size_t
lsquic_senhist_mem_used (const struct lsquic_senhist *hist)
{
return sizeof(*hist)
- sizeof(hist->sh_pints)
+ lsquic_packints_mem_used(&hist->sh_pints);
}

View File

@ -2,49 +2,61 @@
/*
* lsquic_senhist.h -- History sent packets.
*
* We only keep track of packet numbers in order to verify ACKs.
* We need to keep track of packet numbers in order to verify ACKs. To
* speed up processing, we make sure that there is never a gap in the
* packet number sequence we generate.
*/
#ifndef LSQUIC_SENHIST_H
#define LSQUIC_SENHIST_H 1
#include "lsquic_packints.h"
#ifndef LSQUIC_SENHIST_FATAL
# define LSQUIC_SENHIST_FATAL 0
#endif
typedef struct lsquic_senhist {
/* These ranges are ordered from high to low. While searching this
* structure is O(n), I expect that in practice, a very long search
* could only happen once before the connection is terminated,
* because:
* a) either the packet number far away is real, but it was so long
* ago that it would have timed out by now (RTO); or
* b) the peer sends an invalid ACK.
*/
struct packints sh_pints;
lsquic_packno_t sh_last_sent;
#if !LSQUIC_SENHIST_FATAL
enum {
SH_WARNED = 1 << 0, /* Warn once */
} sh_flags;
#endif
} lsquic_senhist_t;
void
lsquic_senhist_init (lsquic_senhist_t *);
#define lsquic_senhist_init(hist) do { \
(hist)->sh_last_sent = 0; \
} while (0)
void
lsquic_senhist_cleanup (lsquic_senhist_t *);
#define lsquic_senhist_cleanup(hist)
int
lsquic_senhist_add (lsquic_senhist_t *, lsquic_packno_t);
/* Returns true if history contains all packets numbers in this range.
*/
int
lsquic_senhist_sent_range (lsquic_senhist_t *, lsquic_packno_t low,
lsquic_packno_t high);
#if LSQUIC_SENHIST_FATAL
#define lsquic_senhist_add(hist, packno) do { \
assert((hist)->sh_last_sent == packno - 1); \
(hist)->sh_last_sent = packno; \
} while (0)
#else
#define lsquic_senhist_add(hist, packno) do { \
if ((hist)->sh_last_sent == packno - 1) \
(hist)->sh_last_sent = packno; \
else \
{ \
if (!((hist)->sh_flags & SH_WARNED)) \
{ \
LSQ_WARN("send history gap %"PRIu64" - %"PRIu64, \
(hist)->sh_last_sent, packno); \
(hist)->sh_flags |= SH_WARNED; \
} \
(hist)->sh_last_sent = packno; \
} \
} while (0)
#endif
/* Returns 0 if no packets have been sent yet */
lsquic_packno_t
lsquic_senhist_largest (lsquic_senhist_t *hist);
#define lsquic_senhist_largest(hist) (+(hist)->sh_last_sent)
void
lsquic_senhist_tostr (lsquic_senhist_t *hist, char *buf, size_t bufsz);
size_t
lsquic_senhist_mem_used (const struct lsquic_senhist *);
#define lsquic_senhist_mem_used(hist) (sizeof(*(hist)))
#endif

View File

@ -46,365 +46,366 @@ static int promise_fd = -1;
struct lsquic_conn_ctx;
struct path_elem {
TAILQ_ENTRY(path_elem) next_pe;
const char *path;
TAILQ_ENTRY(path_elem) next_pe;
const char *path;
};
struct http_client_ctx {
TAILQ_HEAD(, lsquic_conn_ctx)
conn_ctxs;
struct service_port *sport;
const char *hostname;
const char *method;
const char *payload;
char payload_size[20];
TAILQ_HEAD(, lsquic_conn_ctx)
conn_ctxs;
struct service_port *sport;
const char *hostname;
const char *method;
const char *payload;
char payload_size[20];
/* hcc_path_elems holds a list of paths which are to be requested from
* the server. Each new request gets the next path from the list (the
* iterator is stored in hcc_cur_pe); when the end is reached, the
* iterator wraps around.
*/
TAILQ_HEAD(, path_elem) hcc_path_elems;
struct path_elem *hcc_cur_pe;
/* hcc_path_elems holds a list of paths which are to be requested from
* the server. Each new request gets the next path from the list (the
* iterator is stored in hcc_cur_pe); when the end is reached, the
* iterator wraps around.
*/
TAILQ_HEAD(, path_elem) hcc_path_elems;
struct path_elem *hcc_cur_pe;
unsigned hcc_total_n_reqs;
unsigned hcc_reqs_per_conn;
unsigned hcc_concurrency;
unsigned hcc_n_open_conns;
unsigned hcc_total_n_reqs;
unsigned hcc_reqs_per_conn;
unsigned hcc_concurrency;
unsigned hcc_n_open_conns;
enum {
HCC_DISCARD_RESPONSE = (1 << 0),
HCC_SEEN_FIN = (1 << 1),
HCC_ABORT_ON_INCOMPLETE = (1 << 2),
} hcc_flags;
struct prog *prog;
enum {
HCC_DISCARD_RESPONSE = (1 << 0),
HCC_SEEN_FIN = (1 << 1),
HCC_ABORT_ON_INCOMPLETE = (1 << 2),
} hcc_flags;
struct prog *prog;
};
struct lsquic_conn_ctx {
TAILQ_ENTRY(lsquic_conn_ctx) next_ch;
lsquic_conn_t *conn;
struct http_client_ctx *client_ctx;
unsigned ch_n_reqs; /* This number gets decremented as streams are closed and
* incremented as push promises are accepted.
*/
TAILQ_ENTRY(lsquic_conn_ctx) next_ch;
lsquic_conn_t *conn;
struct http_client_ctx *client_ctx;
unsigned ch_n_reqs; /* This number gets decremented as streams are closed and
* incremented as push promises are accepted.
*/
};
static void
create_connections (struct http_client_ctx *client_ctx)
{
while (client_ctx->hcc_n_open_conns < client_ctx->hcc_concurrency &&
client_ctx->hcc_total_n_reqs > 0)
if (0 != prog_connect(client_ctx->prog))
{
LSQ_ERROR("connection failed");
exit(EXIT_FAILURE);
}
while (client_ctx->hcc_n_open_conns < client_ctx->hcc_concurrency &&
client_ctx->hcc_total_n_reqs > 0)
if (0 != prog_connect(client_ctx->prog))
{
LSQ_ERROR("connection failed");
exit(EXIT_FAILURE);
}
}
static lsquic_conn_ctx_t *
http_client_on_new_conn (void *stream_if_ctx, lsquic_conn_t *conn)
{
struct http_client_ctx *client_ctx = stream_if_ctx;
lsquic_conn_ctx_t *conn_h = calloc(1, sizeof(*conn_h));
conn_h->conn = conn;
conn_h->client_ctx = client_ctx;
conn_h->ch_n_reqs = client_ctx->hcc_total_n_reqs <
client_ctx->hcc_reqs_per_conn ?
client_ctx->hcc_total_n_reqs : client_ctx->hcc_reqs_per_conn;
client_ctx->hcc_total_n_reqs -= conn_h->ch_n_reqs;
TAILQ_INSERT_TAIL(&client_ctx->conn_ctxs, conn_h, next_ch);
++conn_h->client_ctx->hcc_n_open_conns;
lsquic_conn_make_stream(conn);
return conn_h;
struct http_client_ctx *client_ctx = stream_if_ctx;
lsquic_conn_ctx_t *conn_h = calloc(1, sizeof(*conn_h));
conn_h->conn = conn;
conn_h->client_ctx = client_ctx;
conn_h->ch_n_reqs = client_ctx->hcc_total_n_reqs <
client_ctx->hcc_reqs_per_conn ?
client_ctx->hcc_total_n_reqs : client_ctx->hcc_reqs_per_conn;
client_ctx->hcc_total_n_reqs -= conn_h->ch_n_reqs;
TAILQ_INSERT_TAIL(&client_ctx->conn_ctxs, conn_h, next_ch);
++conn_h->client_ctx->hcc_n_open_conns;
lsquic_conn_make_stream(conn);
return conn_h;
}
static void
http_client_on_conn_closed (lsquic_conn_t *conn)
{
lsquic_conn_ctx_t *conn_h = lsquic_conn_get_ctx(conn);
enum LSQUIC_CONN_STATUS status;
char errmsg[80];
lsquic_conn_ctx_t *conn_h = lsquic_conn_get_ctx(conn);
enum LSQUIC_CONN_STATUS status;
char errmsg[80];
status = lsquic_conn_status(conn, errmsg, sizeof(errmsg));
LSQ_INFO("Connection closed. Status: %d. Message: %s", status,
errmsg[0] ? errmsg : "<not set>");
#ifndef NDEBUG
if (conn_h->client_ctx->hcc_flags & HCC_ABORT_ON_INCOMPLETE)
assert(conn_h->client_ctx->hcc_flags & HCC_SEEN_FIN);
#endif
TAILQ_REMOVE(&conn_h->client_ctx->conn_ctxs, conn_h, next_ch);
--conn_h->client_ctx->hcc_n_open_conns;
create_connections(conn_h->client_ctx);
if (0 == conn_h->client_ctx->hcc_n_open_conns)
{
LSQ_INFO("All connections are closed: stop engine");
prog_stop(conn_h->client_ctx->prog);
}
free(conn_h);
status = lsquic_conn_status(conn, errmsg, sizeof(errmsg));
LSQ_INFO("Connection closed. Status: %d. Message: %s", status,
errmsg[0] ? errmsg : "<not set>");
if (conn_h->client_ctx->hcc_flags & HCC_ABORT_ON_INCOMPLETE)
{
if (!(conn_h->client_ctx->hcc_flags & HCC_SEEN_FIN))
abort();
}
TAILQ_REMOVE(&conn_h->client_ctx->conn_ctxs, conn_h, next_ch);
--conn_h->client_ctx->hcc_n_open_conns;
create_connections(conn_h->client_ctx);
if (0 == conn_h->client_ctx->hcc_n_open_conns)
{
LSQ_INFO("All connections are closed: stop engine");
prog_stop(conn_h->client_ctx->prog);
}
free(conn_h);
}
struct lsquic_stream_ctx {
lsquic_stream_t *stream;
struct http_client_ctx *client_ctx;
const char *path;
enum {
HEADERS_SENT = (1 << 0),
} sh_flags;
unsigned count;
struct lsquic_reader reader;
lsquic_stream_t *stream;
struct http_client_ctx *client_ctx;
const char *path;
enum {
HEADERS_SENT = (1 << 0),
} sh_flags;
unsigned count;
struct lsquic_reader reader;
};
static lsquic_stream_ctx_t *
http_client_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream)
{
const int pushed = lsquic_stream_is_pushed(stream);
const int pushed = lsquic_stream_is_pushed(stream);
if (pushed)
{
LSQ_INFO("not accepting server push");
lsquic_stream_refuse_push(stream);
return NULL;
}
if (pushed)
{
LSQ_INFO("not accepting server push");
lsquic_stream_refuse_push(stream);
return NULL;
}
lsquic_stream_ctx_t *st_h = calloc(1, sizeof(*st_h));
st_h->stream = stream;
st_h->client_ctx = stream_if_ctx;
if (st_h->client_ctx->hcc_cur_pe)
{
st_h->client_ctx->hcc_cur_pe = TAILQ_NEXT(
st_h->client_ctx->hcc_cur_pe, next_pe);
if (!st_h->client_ctx->hcc_cur_pe) /* Wrap around */
st_h->client_ctx->hcc_cur_pe =
TAILQ_FIRST(&st_h->client_ctx->hcc_path_elems);
}
else
st_h->client_ctx->hcc_cur_pe = TAILQ_FIRST(
&st_h->client_ctx->hcc_path_elems);
st_h->path = st_h->client_ctx->hcc_cur_pe->path;
if (st_h->client_ctx->payload)
{
st_h->reader.lsqr_read = test_reader_read;
st_h->reader.lsqr_size = test_reader_size;
st_h->reader.lsqr_ctx = create_lsquic_reader_ctx(st_h->client_ctx->payload);
if (!st_h->reader.lsqr_ctx)
exit(1);
}
else
st_h->reader.lsqr_ctx = NULL;
LSQ_INFO("created new stream, path: %s", st_h->path);
lsquic_stream_wantwrite(stream, 1);
lsquic_stream_ctx_t *st_h = calloc(1, sizeof(*st_h));
st_h->stream = stream;
st_h->client_ctx = stream_if_ctx;
if (st_h->client_ctx->hcc_cur_pe)
{
st_h->client_ctx->hcc_cur_pe = TAILQ_NEXT(
st_h->client_ctx->hcc_cur_pe, next_pe);
if (!st_h->client_ctx->hcc_cur_pe) /* Wrap around */
st_h->client_ctx->hcc_cur_pe =
TAILQ_FIRST(&st_h->client_ctx->hcc_path_elems);
}
else
st_h->client_ctx->hcc_cur_pe = TAILQ_FIRST(
&st_h->client_ctx->hcc_path_elems);
st_h->path = st_h->client_ctx->hcc_cur_pe->path;
if (st_h->client_ctx->payload)
{
st_h->reader.lsqr_read = test_reader_read;
st_h->reader.lsqr_size = test_reader_size;
st_h->reader.lsqr_ctx = create_lsquic_reader_ctx(st_h->client_ctx->payload);
if (!st_h->reader.lsqr_ctx)
exit(1);
}
else
st_h->reader.lsqr_ctx = NULL;
LSQ_INFO("created new stream, path: %s", st_h->path);
lsquic_stream_wantwrite(stream, 1);
return st_h;
return st_h;
}
static void
send_headers (lsquic_stream_ctx_t *st_h)
{
lsquic_http_header_t headers_arr[] = {
{
.name = { .iov_base = ":method", .iov_len = 7, },
.value = { .iov_base = (void *) st_h->client_ctx->method,
.iov_len = strlen(st_h->client_ctx->method), },
},
{
.name = { .iov_base = ":scheme", .iov_len = 7, },
.value = { .iov_base = "HTTP", .iov_len = 4, }
},
{
.name = { .iov_base = ":path", .iov_len = 5, },
.value = { .iov_base = (void *) st_h->path,
.iov_len = strlen(st_h->path), },
},
{
.name = { ":authority", 10, },
.value = { .iov_base = (void *) st_h->client_ctx->hostname,
.iov_len = strlen(st_h->client_ctx->hostname), },
},
/*
{
.name = { "host", 4 },
.value = { .iov_base = (void *) st_h->client_ctx->hostname,
.iov_len = strlen(st_h->client_ctx->hostname), },
},
*/
{
.name = { .iov_base = "user-agent", .iov_len = 10, },
.value = { .iov_base = (char *) st_h->client_ctx->prog->prog_settings.es_ua,
.iov_len = strlen(st_h->client_ctx->prog->prog_settings.es_ua), },
},
/* The following headers only gets sent if there is request payload: */
{
.name = { .iov_base = "content-type", .iov_len = 12, },
.value = { .iov_base = "application/octet-stream", .iov_len = 24, },
},
{
.name = { .iov_base = "content-length", .iov_len = 14, },
.value = { .iov_base = (void *) st_h->client_ctx->payload_size,
.iov_len = strlen(st_h->client_ctx->payload_size), },
},
};
lsquic_http_headers_t headers = {
.count = sizeof(headers_arr) / sizeof(headers_arr[0]),
.headers = headers_arr,
};
if (!st_h->client_ctx->payload)
headers.count -= 2;
if (0 != lsquic_stream_send_headers(st_h->stream, &headers,
st_h->client_ctx->payload == NULL))
{
LSQ_ERROR("cannot send headers: %s", strerror(errno));
exit(1);
}
lsquic_http_header_t headers_arr[] = {
{
.name = { .iov_base = ":method", .iov_len = 7, },
.value = { .iov_base = (void *) st_h->client_ctx->method,
.iov_len = strlen(st_h->client_ctx->method), },
},
{
.name = { .iov_base = ":scheme", .iov_len = 7, },
.value = { .iov_base = "HTTP", .iov_len = 4, }
},
{
.name = { .iov_base = ":path", .iov_len = 5, },
.value = { .iov_base = (void *) st_h->path,
.iov_len = strlen(st_h->path), },
},
{
.name = { ":authority", 10, },
.value = { .iov_base = (void *) st_h->client_ctx->hostname,
.iov_len = strlen(st_h->client_ctx->hostname), },
},
/*
{
.name = { "host", 4 },
.value = { .iov_base = (void *) st_h->client_ctx->hostname,
.iov_len = strlen(st_h->client_ctx->hostname), },
},
*/
{
.name = { .iov_base = "user-agent", .iov_len = 10, },
.value = { .iov_base = (char *) st_h->client_ctx->prog->prog_settings.es_ua,
.iov_len = strlen(st_h->client_ctx->prog->prog_settings.es_ua), },
},
/* The following headers only gets sent if there is request payload: */
{
.name = { .iov_base = "content-type", .iov_len = 12, },
.value = { .iov_base = "application/octet-stream", .iov_len = 24, },
},
{
.name = { .iov_base = "content-length", .iov_len = 14, },
.value = { .iov_base = (void *) st_h->client_ctx->payload_size,
.iov_len = strlen(st_h->client_ctx->payload_size), },
},
};
lsquic_http_headers_t headers = {
.count = sizeof(headers_arr) / sizeof(headers_arr[0]),
.headers = headers_arr,
};
if (!st_h->client_ctx->payload)
headers.count -= 2;
if (0 != lsquic_stream_send_headers(st_h->stream, &headers,
st_h->client_ctx->payload == NULL))
{
LSQ_ERROR("cannot send headers: %s", strerror(errno));
exit(1);
}
}
static void
http_client_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
{
ssize_t nw;
ssize_t nw;
if (st_h->sh_flags & HEADERS_SENT)
{
if (st_h->client_ctx->payload && test_reader_size(st_h->reader.lsqr_ctx) > 0)
{
nw = lsquic_stream_writef(stream, &st_h->reader);
if (nw < 0)
{
LSQ_ERROR("write error: %s", strerror(errno));
exit(1);
}
if (test_reader_size(st_h->reader.lsqr_ctx) > 0)
{
lsquic_stream_wantwrite(stream, 1);
}
else
{
lsquic_stream_shutdown(stream, 1);
lsquic_stream_wantread(stream, 1);
}
}
else
{
lsquic_stream_shutdown(stream, 1);
lsquic_stream_wantread(stream, 1);
}
}
else
{
st_h->sh_flags |= HEADERS_SENT;
send_headers(st_h);
}
if (st_h->sh_flags & HEADERS_SENT)
{
if (st_h->client_ctx->payload && test_reader_size(st_h->reader.lsqr_ctx) > 0)
{
nw = lsquic_stream_writef(stream, &st_h->reader);
if (nw < 0)
{
LSQ_ERROR("write error: %s", strerror(errno));
exit(1);
}
if (test_reader_size(st_h->reader.lsqr_ctx) > 0)
{
lsquic_stream_wantwrite(stream, 1);
}
else
{
lsquic_stream_shutdown(stream, 1);
lsquic_stream_wantread(stream, 1);
}
}
else
{
lsquic_stream_shutdown(stream, 1);
lsquic_stream_wantread(stream, 1);
}
}
else
{
st_h->sh_flags |= HEADERS_SENT;
send_headers(st_h);
}
}
static void
http_client_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
{
struct http_client_ctx *const client_ctx = st_h->client_ctx;
ssize_t nread;
unsigned old_prio, new_prio;
unsigned char buf[0x200];
unsigned nreads = 0;
struct http_client_ctx *const client_ctx = st_h->client_ctx;
ssize_t nread;
unsigned old_prio, new_prio;
unsigned char buf[0x200];
unsigned nreads = 0;
#ifdef WIN32
srand(GetTickCount());
#endif
do
{
nread = lsquic_stream_read(stream, buf, sizeof(buf));
if (nread > 0)
{
if (!(client_ctx->hcc_flags & HCC_DISCARD_RESPONSE))
write(STDOUT_FILENO, buf, nread);
if (randomly_reprioritize_streams && (st_h->count++ & 0x3F) == 0)
{
old_prio = lsquic_stream_priority(stream);
new_prio = random() & 0xFF;
do
{
nread = lsquic_stream_read(stream, buf, sizeof(buf));
if (nread > 0)
{
if (!(client_ctx->hcc_flags & HCC_DISCARD_RESPONSE))
write(STDOUT_FILENO, buf, nread);
if (randomly_reprioritize_streams && (st_h->count++ & 0x3F) == 0)
{
old_prio = lsquic_stream_priority(stream);
new_prio = random() & 0xFF;
#ifndef NDEBUG
const int s =
const int s =
#endif
lsquic_stream_set_priority(stream, new_prio);
assert(s == 0);
LSQ_NOTICE("changed stream %u priority from %u to %u",
lsquic_stream_id(stream), old_prio, new_prio);
}
}
else if (0 == nread)
{
client_ctx->hcc_flags |= HCC_SEEN_FIN;
lsquic_stream_shutdown(stream, 0);
break;
}
else if (client_ctx->prog->prog_settings.es_rw_once && EWOULDBLOCK == errno)
{
LSQ_NOTICE("emptied the buffer in 'once' mode");
break;
}
else
{
LSQ_ERROR("could not read: %s", strerror(errno));
exit(2);
}
}
while (client_ctx->prog->prog_settings.es_rw_once
&& nreads++ < 3 /* Emulate just a few reads */);
lsquic_stream_set_priority(stream, new_prio);
assert(s == 0);
LSQ_NOTICE("changed stream %u priority from %u to %u",
lsquic_stream_id(stream), old_prio, new_prio);
}
}
else if (0 == nread)
{
client_ctx->hcc_flags |= HCC_SEEN_FIN;
lsquic_stream_shutdown(stream, 0);
break;
}
else if (client_ctx->prog->prog_settings.es_rw_once && EWOULDBLOCK == errno)
{
LSQ_NOTICE("emptied the buffer in 'once' mode");
break;
}
else
{
LSQ_ERROR("could not read: %s", strerror(errno));
exit(2);
}
}
while (client_ctx->prog->prog_settings.es_rw_once
&& nreads++ < 3 /* Emulate just a few reads */);
}
static void
http_client_on_close (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
{
const int pushed = lsquic_stream_is_pushed(stream);
if (pushed)
{
assert(NULL == st_h);
return;
}
const int pushed = lsquic_stream_is_pushed(stream);
if (pushed)
{
assert(NULL == st_h);
return;
}
LSQ_INFO("%s called", __func__);
lsquic_conn_t *conn = lsquic_stream_conn(stream);
lsquic_conn_ctx_t *conn_h;
TAILQ_FOREACH(conn_h, &st_h->client_ctx->conn_ctxs, next_ch)
if (conn_h->conn == conn)
break;
assert(conn_h);
--conn_h->ch_n_reqs;
if (0 == conn_h->ch_n_reqs)
{
LSQ_INFO("all requests completed, closing connection");
lsquic_conn_close(conn_h->conn);
}
else
lsquic_conn_make_stream(conn);
if (st_h->reader.lsqr_ctx)
destroy_lsquic_reader_ctx(st_h->reader.lsqr_ctx);
free(st_h);
LSQ_INFO("%s called", __func__);
lsquic_conn_t *conn = lsquic_stream_conn(stream);
lsquic_conn_ctx_t *conn_h;
TAILQ_FOREACH(conn_h, &st_h->client_ctx->conn_ctxs, next_ch)
if (conn_h->conn == conn)
break;
assert(conn_h);
--conn_h->ch_n_reqs;
if (0 == conn_h->ch_n_reqs)
{
LSQ_INFO("all requests completed, closing connection");
lsquic_conn_close(conn_h->conn);
}
else
lsquic_conn_make_stream(conn);
if (st_h->reader.lsqr_ctx)
destroy_lsquic_reader_ctx(st_h->reader.lsqr_ctx);
free(st_h);
}
const struct lsquic_stream_if http_client_if = {
.on_new_conn = http_client_on_new_conn,
.on_conn_closed = http_client_on_conn_closed,
.on_new_stream = http_client_on_new_stream,
.on_read = http_client_on_read,
.on_write = http_client_on_write,
.on_close = http_client_on_close,
.on_new_conn = http_client_on_new_conn,
.on_conn_closed = http_client_on_conn_closed,
.on_new_stream = http_client_on_new_stream,
.on_read = http_client_on_read,
.on_write = http_client_on_write,
.on_close = http_client_on_close,
};
static void
usage (const char *prog)
{
const char *const slash = strrchr(prog, '/');
if (slash)
prog = slash + 1;
printf(
const char *const slash = strrchr(prog, '/');
if (slash)
prog = slash + 1;
printf(
"Usage: %s [opts]\n"
"\n"
"Options:\n"
@ -420,121 +421,121 @@ usage (const char *prog)
" content-length\n"
" -K Discard server response\n"
" -I Abort on incomplete reponse from server\n"
, prog);
, prog);
}
int
main (int argc, char **argv)
{
int opt, s;
struct http_client_ctx client_ctx;
struct stat st;
struct path_elem *pe;
struct sport_head sports;
struct prog prog;
int opt, s;
struct http_client_ctx client_ctx;
struct stat st;
struct path_elem *pe;
struct sport_head sports;
struct prog prog;
TAILQ_INIT(&sports);
memset(&client_ctx, 0, sizeof(client_ctx));
client_ctx.hcc_concurrency = 1;
TAILQ_INIT(&client_ctx.hcc_path_elems);
TAILQ_INIT(&client_ctx.conn_ctxs);
client_ctx.hostname = "localhost";
client_ctx.method = "GET";
client_ctx.hcc_concurrency = 1;
client_ctx.hcc_reqs_per_conn = 1;
client_ctx.hcc_total_n_reqs = 1;
client_ctx.prog = &prog;
TAILQ_INIT(&sports);
memset(&client_ctx, 0, sizeof(client_ctx));
client_ctx.hcc_concurrency = 1;
TAILQ_INIT(&client_ctx.hcc_path_elems);
TAILQ_INIT(&client_ctx.conn_ctxs);
client_ctx.hostname = "localhost";
client_ctx.method = "GET";
client_ctx.hcc_concurrency = 1;
client_ctx.hcc_reqs_per_conn = 1;
client_ctx.hcc_total_n_reqs = 1;
client_ctx.prog = &prog;
#ifdef WIN32
WSADATA wsd;
WSAStartup(MAKEWORD(2, 2), &wsd);
#endif
prog_init(&prog, LSENG_HTTP, &sports, &http_client_if, &client_ctx);
prog_init(&prog, LSENG_HTTP, &sports, &http_client_if, &client_ctx);
while (-1 != (opt = getopt(argc, argv, PROG_OPTS "r:R:Ku:EP:M:n:H:p:h")))
{
switch (opt) {
case 'I':
client_ctx.hcc_flags |= HCC_ABORT_ON_INCOMPLETE;
break;
case 'K':
client_ctx.hcc_flags |= HCC_DISCARD_RESPONSE;
break;
case 'u': /* Accept p<U>sh promise */
promise_fd = open(optarg, O_WRONLY|O_CREAT|O_TRUNC, 0644);
if (promise_fd < 0)
{
perror("open");
exit(1);
}
prog.prog_settings.es_support_push = 1; /* Pokes into prog */
break;
case 'E': /* E: randomly reprioritize str<E>ams. Now, that's
* pretty random. :)
*/
randomly_reprioritize_streams = 1;
break;
case 'n':
client_ctx.hcc_concurrency = atoi(optarg);
break;
case 'P':
client_ctx.payload = optarg;
if (0 != stat(optarg, &st))
{
perror("stat");
exit(2);
}
sprintf(client_ctx.payload_size, "%jd", (intmax_t) st.st_size);
break;
case 'M':
client_ctx.method = optarg;
break;
case 'r':
client_ctx.hcc_total_n_reqs = atoi(optarg);
break;
case 'R':
client_ctx.hcc_reqs_per_conn = atoi(optarg);
break;
case 'H':
client_ctx.hostname = optarg;
prog.prog_hostname = optarg; /* Pokes into prog */
break;
case 'p':
pe = calloc(1, sizeof(*pe));
pe->path = optarg;
TAILQ_INSERT_TAIL(&client_ctx.hcc_path_elems, pe, next_pe);
break;
case 'h':
usage(argv[0]);
prog_print_common_options(&prog, stdout);
exit(0);
default:
if (0 != prog_set_opt(&prog, opt, optarg))
exit(1);
}
}
while (-1 != (opt = getopt(argc, argv, PROG_OPTS "r:R:IKu:EP:M:n:H:p:h")))
{
switch (opt) {
case 'I':
client_ctx.hcc_flags |= HCC_ABORT_ON_INCOMPLETE;
break;
case 'K':
client_ctx.hcc_flags |= HCC_DISCARD_RESPONSE;
break;
case 'u': /* Accept p<U>sh promise */
promise_fd = open(optarg, O_WRONLY|O_CREAT|O_TRUNC, 0644);
if (promise_fd < 0)
{
perror("open");
exit(1);
}
prog.prog_settings.es_support_push = 1; /* Pokes into prog */
break;
case 'E': /* E: randomly reprioritize str<E>ams. Now, that's
* pretty random. :)
*/
randomly_reprioritize_streams = 1;
break;
case 'n':
client_ctx.hcc_concurrency = atoi(optarg);
break;
case 'P':
client_ctx.payload = optarg;
if (0 != stat(optarg, &st))
{
perror("stat");
exit(2);
}
sprintf(client_ctx.payload_size, "%jd", (intmax_t) st.st_size);
break;
case 'M':
client_ctx.method = optarg;
break;
case 'r':
client_ctx.hcc_total_n_reqs = atoi(optarg);
break;
case 'R':
client_ctx.hcc_reqs_per_conn = atoi(optarg);
break;
case 'H':
client_ctx.hostname = optarg;
prog.prog_hostname = optarg; /* Pokes into prog */
break;
case 'p':
pe = calloc(1, sizeof(*pe));
pe->path = optarg;
TAILQ_INSERT_TAIL(&client_ctx.hcc_path_elems, pe, next_pe);
break;
case 'h':
usage(argv[0]);
prog_print_common_options(&prog, stdout);
exit(0);
default:
if (0 != prog_set_opt(&prog, opt, optarg))
exit(1);
}
}
if (TAILQ_EMPTY(&client_ctx.hcc_path_elems))
{
fprintf(stderr, "Specify at least one path using -p option\n");
exit(1);
}
if (TAILQ_EMPTY(&client_ctx.hcc_path_elems))
{
fprintf(stderr, "Specify at least one path using -p option\n");
exit(1);
}
if (0 != prog_prep(&prog))
{
LSQ_ERROR("could not prep");
exit(EXIT_FAILURE);
}
if (0 != prog_prep(&prog))
{
LSQ_ERROR("could not prep");
exit(EXIT_FAILURE);
}
create_connections(&client_ctx);
create_connections(&client_ctx);
LSQ_DEBUG("entering event loop");
LSQ_DEBUG("entering event loop");
s = prog_run(&prog);
prog_cleanup(&prog);
if (promise_fd >= 0)
(void) close(promise_fd);
s = prog_run(&prog);
prog_cleanup(&prog);
if (promise_fd >= 0)
(void) close(promise_fd);
exit(0 == s ? EXIT_SUCCESS : EXIT_FAILURE);
exit(0 == s ? EXIT_SUCCESS : EXIT_FAILURE);
}

View File

@ -8,8 +8,10 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-value")
set(MIN_LIBS_LIST "m ${FIULIB}")
set(LIBS_LIST "pthread libssl.a libcrypto.a z ${MIN_LIBS_LIST}")
ELSE()
SET (UGH_LIB_LIST debug $(SolutionDir)src/liblsquic/debug/lsquic.lib debug ${VCPKG_BASE_DIR}/debug/lib/event.lib debug ${VCPKG_BASE_DIR}/debug/lib/zlibd.lib debug ${BORINGSSL_BASE_LIB_DIR}/debug/ssl.lib debug ${BORINGSSL_BASE_LIB_DIR}/debug/crypto.lib optimized $(SolutionDir)src/liblsquic/release/lsquic.lib optimized ${VCPKG_BASE_DIR}/lib/event.lib optimized ${VCPKG_BASE_DIR}/lib/zlib.lib optimized ${BORINGSSL_BASE_LIB_DIR}/ssl.lib optimized ${BORINGSSL_BASE_LIB_DIR}/crypto.lib)
set(MIN_LIBS_LIST ${FIULIB} ws2_32)
set(LIBS_LIST ssl crypto ${ZLIB_NAME} ${MIN_LIBS_LIST})
set(LIBS_LIST ${UGH_LIB_LIST} ${MIN_LIBS_LIST})
ENDIF()
enable_testing()

View File

@ -24,6 +24,7 @@ test1 (void) /* Inverse of quic_framer_test.cc -- NewAckFrameOneAckBlock */
{
lsquic_rechist_t rechist;
lsquic_time_t now = lsquic_time_now();
lsquic_packno_t largest = 0;
lsquic_rechist_init(&rechist, 0);
@ -45,14 +46,13 @@ test1 (void) /* Inverse of quic_framer_test.cc -- NewAckFrameOneAckBlock */
(gaf_rechist_first_f) lsquic_rechist_first,
(gaf_rechist_next_f) lsquic_rechist_next,
(gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
&rechist, now + 0x7FF8000, &has_missing);
&rechist, now + 0x7FF8000, &has_missing, &largest);
assert(("ACK frame generation successful", w > 0));
assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
assert(("ACK frame contents are as expected",
0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
assert(("ACK frame has no missing packets", has_missing == 0));
lsquic_packno_t ack_high = pf->pf_parse_ack_high(outbuf, sizeof(outbuf));
assert(0x1234 == ack_high);
assert(largest == 0x1234);
lsquic_rechist_cleanup(&rechist);
}
@ -102,18 +102,18 @@ test2 (void) /* Inverse of quic_framer_test.cc -- NewAckFrameOneAckBlock, minus
unsigned char outbuf[0x100];
int has_missing = -1;
lsquic_packno_t largest = 0;
int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
(gaf_rechist_first_f) lsquic_rechist_first,
(gaf_rechist_next_f) lsquic_rechist_next,
(gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
&rechist, now, &has_missing);
&rechist, now, &has_missing, &largest);
assert(("ACK frame generation successful", w > 0));
assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
assert(("ACK frame contents are as expected",
0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
assert(("ACK frame has missing packets", has_missing > 0));
lsquic_packno_t ack_high = pf->pf_parse_ack_high(outbuf, sizeof(outbuf));
assert(0x1234 == ack_high);
assert(largest == 0x1234);
lsquic_rechist_cleanup(&rechist);
}
@ -147,18 +147,18 @@ test3 (void)
unsigned char outbuf[0x100];
int has_missing = -1;
lsquic_packno_t largest = 0;
int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
(gaf_rechist_first_f) lsquic_rechist_first,
(gaf_rechist_next_f) lsquic_rechist_next,
(gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
&rechist, now, &has_missing);
&rechist, now, &has_missing, &largest);
assert(("ACK frame generation successful", w > 0));
assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
assert(("ACK frame contents are as expected",
0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
assert(("ACK frame has missing packets", has_missing > 0));
lsquic_packno_t ack_high = pf->pf_parse_ack_high(outbuf, sizeof(outbuf));
assert(3 == ack_high);
assert(largest == 0x03);
lsquic_rechist_cleanup(&rechist);
}
@ -185,18 +185,18 @@ test4 (void)
};
unsigned char outbuf[0x100];
int has_missing = -1;
lsquic_packno_t largest = 0;
int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
(gaf_rechist_first_f) lsquic_rechist_first,
(gaf_rechist_next_f) lsquic_rechist_next,
(gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
&rechist, now, &has_missing);
&rechist, now, &has_missing, &largest);
assert(("ACK frame generation successful", w > 0));
assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
assert(("ACK frame contents are as expected",
0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
assert(("ACK frame has no missing packets", has_missing == 0));
lsquic_packno_t ack_high = pf->pf_parse_ack_high(outbuf, sizeof(outbuf));
assert(1 == ack_high);
assert(largest == 1);
}
for (i = 3; i <= 5; ++i)
@ -215,18 +215,18 @@ test4 (void)
};
unsigned char outbuf[0x100];
int has_missing = -1;
lsquic_packno_t largest = 0;
int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
(gaf_rechist_first_f) lsquic_rechist_first,
(gaf_rechist_next_f) lsquic_rechist_next,
(gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
&rechist, now, &has_missing);
&rechist, now, &has_missing, &largest);
assert(("ACK frame generation successful", w > 0));
assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
assert(("ACK frame contents are as expected",
0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
assert(("ACK frame has missing packets", has_missing > 0));
lsquic_packno_t ack_high = pf->pf_parse_ack_high(outbuf, sizeof(outbuf));
assert(5 == ack_high);
assert(largest == 5);
}
lsquic_rechist_cleanup(&rechist);
@ -267,16 +267,18 @@ test_4byte_packnos (void)
unsigned char outbuf[0x100];
int has_missing = -1;
lsquic_packno_t largest = 0;
int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
(gaf_rechist_first_f) lsquic_rechist_first,
(gaf_rechist_next_f) lsquic_rechist_next,
(gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
&rechist, now, &has_missing);
&rechist, now, &has_missing, &largest);
assert(("ACK frame generation successful", w > 0));
assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
assert(("ACK frame contents are as expected",
0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
assert(("ACK frame has missing packets", has_missing > 0));
assert(largest == 0x23456789);
lsquic_rechist_cleanup(&rechist);
}
@ -316,16 +318,18 @@ test_6byte_packnos (void)
unsigned char outbuf[0x100];
int has_missing = -1;
lsquic_packno_t largest = 0;
int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
(gaf_rechist_first_f) lsquic_rechist_first,
(gaf_rechist_next_f) lsquic_rechist_next,
(gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
&rechist, now, &has_missing);
&rechist, now, &has_missing, &largest);
assert(("ACK frame generation successful", w > 0));
assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
assert(("ACK frame contents are as expected",
0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
assert(("ACK frame has missing packets", has_missing > 0));
assert(largest == 0xABCD23456789ULL);
lsquic_rechist_cleanup(&rechist);
}

View File

@ -24,6 +24,7 @@ test1 (void) /* Inverse of quic_framer_test.cc -- NewAckFrameOneAckBlock */
{
lsquic_rechist_t rechist;
lsquic_time_t now = lsquic_time_now();
lsquic_packno_t largest = 0;
lsquic_rechist_init(&rechist, 0);
@ -46,14 +47,13 @@ test1 (void) /* Inverse of quic_framer_test.cc -- NewAckFrameOneAckBlock */
(gaf_rechist_first_f) lsquic_rechist_first,
(gaf_rechist_next_f) lsquic_rechist_next,
(gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
&rechist, now + 0x7FF8000, &has_missing);
&rechist, now + 0x7FF8000, &has_missing, &largest);
assert(("ACK frame generation successful", w > 0));
assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
assert(("ACK frame contents are as expected",
0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
assert(("ACK frame has no missing packets", has_missing == 0));
lsquic_packno_t ack_high = pf->pf_parse_ack_high(outbuf, sizeof(outbuf));
assert(0x1234 == ack_high);
assert(largest == 0x1234);
lsquic_rechist_cleanup(&rechist);
}
@ -104,18 +104,18 @@ test2 (void) /* Inverse of quic_framer_test.cc -- NewAckFrameOneAckBlock, minus
unsigned char outbuf[0x100];
int has_missing = -1;
lsquic_packno_t largest = 0;
int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
(gaf_rechist_first_f) lsquic_rechist_first,
(gaf_rechist_next_f) lsquic_rechist_next,
(gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
&rechist, now, &has_missing);
&rechist, now, &has_missing, &largest);
assert(("ACK frame generation successful", w > 0));
assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
assert(("ACK frame contents are as expected",
0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
assert(("ACK frame has missing packets", has_missing > 0));
lsquic_packno_t ack_high = pf->pf_parse_ack_high(outbuf, sizeof(outbuf));
assert(0x1234 == ack_high);
assert(largest == 0x1234);
lsquic_rechist_cleanup(&rechist);
}
@ -150,18 +150,18 @@ test3 (void)
unsigned char outbuf[0x100];
int has_missing = -1;
lsquic_packno_t largest = 0;
int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
(gaf_rechist_first_f) lsquic_rechist_first,
(gaf_rechist_next_f) lsquic_rechist_next,
(gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
&rechist, now, &has_missing);
&rechist, now, &has_missing, &largest);
assert(("ACK frame generation successful", w > 0));
assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
assert(("ACK frame contents are as expected",
0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
assert(("ACK frame has missing packets", has_missing > 0));
lsquic_packno_t ack_high = pf->pf_parse_ack_high(outbuf, sizeof(outbuf));
assert(3 == ack_high);
assert(largest == 0x03);
lsquic_rechist_cleanup(&rechist);
}
@ -189,18 +189,18 @@ test4 (void)
};
unsigned char outbuf[0x100];
int has_missing = -1;
lsquic_packno_t largest = 0;
int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
(gaf_rechist_first_f) lsquic_rechist_first,
(gaf_rechist_next_f) lsquic_rechist_next,
(gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
&rechist, now, &has_missing);
&rechist, now, &has_missing, &largest);
assert(("ACK frame generation successful", w > 0));
assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
assert(("ACK frame contents are as expected",
0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
assert(("ACK frame has no missing packets", has_missing == 0));
lsquic_packno_t ack_high = pf->pf_parse_ack_high(outbuf, sizeof(outbuf));
assert(1 == ack_high);
assert(largest == 1);
}
for (i = 3; i <= 5; ++i)
@ -220,18 +220,18 @@ test4 (void)
};
unsigned char outbuf[0x100];
int has_missing = -1;
lsquic_packno_t largest = 0;
int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
(gaf_rechist_first_f) lsquic_rechist_first,
(gaf_rechist_next_f) lsquic_rechist_next,
(gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
&rechist, now, &has_missing);
&rechist, now, &has_missing, &largest);
assert(("ACK frame generation successful", w > 0));
assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
assert(("ACK frame contents are as expected",
0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
assert(("ACK frame has missing packets", has_missing > 0));
lsquic_packno_t ack_high = pf->pf_parse_ack_high(outbuf, sizeof(outbuf));
assert(5 == ack_high);
assert(largest == 5);
}
lsquic_rechist_cleanup(&rechist);
@ -270,16 +270,18 @@ test_4byte_packnos (void)
unsigned char outbuf[0x100];
int has_missing = -1;
lsquic_packno_t largest = 0;
int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
(gaf_rechist_first_f) lsquic_rechist_first,
(gaf_rechist_next_f) lsquic_rechist_next,
(gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
&rechist, now, &has_missing);
&rechist, now, &has_missing, &largest);
assert(("ACK frame generation successful", w > 0));
assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
assert(("ACK frame contents are as expected",
0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
assert(("ACK frame has missing packets", has_missing > 0));
assert(largest == 0x23456789);
lsquic_rechist_cleanup(&rechist);
}
@ -317,16 +319,18 @@ test_8byte_packnos (void)
unsigned char outbuf[0x100];
int has_missing = -1;
lsquic_packno_t largest = 0;
int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
(gaf_rechist_first_f) lsquic_rechist_first,
(gaf_rechist_next_f) lsquic_rechist_next,
(gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
&rechist, now, &has_missing);
&rechist, now, &has_missing, &largest);
assert(("ACK frame generation successful", w > 0));
assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
assert(("ACK frame contents are as expected",
0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
assert(("ACK frame has missing packets", has_missing > 0));
assert(largest == 0xABCD23456789ULL);
lsquic_rechist_cleanup(&rechist);
}

View File

@ -24,6 +24,7 @@ test1 (void) /* Inverse of quic_framer_test.cc -- NewAckFrameOneAckBlock */
{
lsquic_rechist_t rechist;
lsquic_time_t now = lsquic_time_now();
lsquic_packno_t largest = 0;
lsquic_rechist_init(&rechist, 0);
@ -45,14 +46,13 @@ test1 (void) /* Inverse of quic_framer_test.cc -- NewAckFrameOneAckBlock */
(gaf_rechist_first_f) lsquic_rechist_first,
(gaf_rechist_next_f) lsquic_rechist_next,
(gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
&rechist, now + 0x7FF8000, &has_missing);
&rechist, now + 0x7FF8000, &has_missing, &largest);
assert(("ACK frame generation successful", w > 0));
assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
assert(("ACK frame contents are as expected",
0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
assert(("ACK frame has no missing packets", has_missing == 0));
lsquic_packno_t ack_high = pf->pf_parse_ack_high(outbuf, sizeof(outbuf));
assert(0x1234 == ack_high);
assert(largest == 0x1234);
lsquic_rechist_cleanup(&rechist);
}
@ -102,18 +102,18 @@ test2 (void) /* Inverse of quic_framer_test.cc -- NewAckFrameOneAckBlock, minus
unsigned char outbuf[0x100];
int has_missing = -1;
lsquic_packno_t largest = 0;
int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
(gaf_rechist_first_f) lsquic_rechist_first,
(gaf_rechist_next_f) lsquic_rechist_next,
(gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
&rechist, now, &has_missing);
&rechist, now, &has_missing, &largest);
assert(("ACK frame generation successful", w > 0));
assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
assert(("ACK frame contents are as expected",
0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
assert(("ACK frame has missing packets", has_missing > 0));
lsquic_packno_t ack_high = pf->pf_parse_ack_high(outbuf, sizeof(outbuf));
assert(0x1234 == ack_high);
assert(largest == 0x1234);
lsquic_rechist_cleanup(&rechist);
}
@ -147,18 +147,18 @@ test3 (void)
unsigned char outbuf[0x100];
int has_missing = -1;
lsquic_packno_t largest = 0;
int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
(gaf_rechist_first_f) lsquic_rechist_first,
(gaf_rechist_next_f) lsquic_rechist_next,
(gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
&rechist, now, &has_missing);
&rechist, now, &has_missing, &largest);
assert(("ACK frame generation successful", w > 0));
assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
assert(("ACK frame contents are as expected",
0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
assert(("ACK frame has missing packets", has_missing > 0));
lsquic_packno_t ack_high = pf->pf_parse_ack_high(outbuf, sizeof(outbuf));
assert(3 == ack_high);
assert(largest == 0x03);
lsquic_rechist_cleanup(&rechist);
}
@ -185,18 +185,18 @@ test4 (void)
};
unsigned char outbuf[0x100];
int has_missing = -1;
lsquic_packno_t largest = 0;
int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
(gaf_rechist_first_f) lsquic_rechist_first,
(gaf_rechist_next_f) lsquic_rechist_next,
(gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
&rechist, now, &has_missing);
&rechist, now, &has_missing, &largest);
assert(("ACK frame generation successful", w > 0));
assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
assert(("ACK frame contents are as expected",
0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
assert(("ACK frame has no missing packets", has_missing == 0));
lsquic_packno_t ack_high = pf->pf_parse_ack_high(outbuf, sizeof(outbuf));
assert(1 == ack_high);
assert(largest == 1);
}
for (i = 3; i <= 5; ++i)
@ -215,18 +215,18 @@ test4 (void)
};
unsigned char outbuf[0x100];
int has_missing = -1;
lsquic_packno_t largest = 0;
int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
(gaf_rechist_first_f) lsquic_rechist_first,
(gaf_rechist_next_f) lsquic_rechist_next,
(gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
&rechist, now, &has_missing);
&rechist, now, &has_missing, &largest);
assert(("ACK frame generation successful", w > 0));
assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
assert(("ACK frame contents are as expected",
0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
assert(("ACK frame has missing packets", has_missing > 0));
lsquic_packno_t ack_high = pf->pf_parse_ack_high(outbuf, sizeof(outbuf));
assert(5 == ack_high);
assert(largest == 5);
}
lsquic_rechist_cleanup(&rechist);
@ -267,16 +267,18 @@ test_4byte_packnos (void)
unsigned char outbuf[0x100];
int has_missing = -1;
lsquic_packno_t largest = 0;
int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
(gaf_rechist_first_f) lsquic_rechist_first,
(gaf_rechist_next_f) lsquic_rechist_next,
(gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
&rechist, now, &has_missing);
&rechist, now, &has_missing, &largest);
assert(("ACK frame generation successful", w > 0));
assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
assert(("ACK frame contents are as expected",
0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
assert(("ACK frame has missing packets", has_missing > 0));
assert(largest == 0x23456789);
lsquic_rechist_cleanup(&rechist);
}
@ -316,16 +318,18 @@ test_6byte_packnos (void)
unsigned char outbuf[0x100];
int has_missing = -1;
lsquic_packno_t largest = 0;
int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
(gaf_rechist_first_f) lsquic_rechist_first,
(gaf_rechist_next_f) lsquic_rechist_next,
(gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
&rechist, now, &has_missing);
&rechist, now, &has_missing, &largest);
assert(("ACK frame generation successful", w > 0));
assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
assert(("ACK frame contents are as expected",
0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
assert(("ACK frame has missing packets", has_missing > 0));
assert(largest == 0xABCD23456789ULL);
lsquic_rechist_cleanup(&rechist);
}

View File

@ -50,8 +50,6 @@ test1 (void)
assert(("Number of timestamps is 0", acki.n_timestamps == 0));
unsigned n = n_acked(&acki);
assert(("Number of acked packets is 0x1234", n == 0x1234));
lsquic_packno_t ack_high = pf->pf_parse_ack_high(ack_buf, sizeof(ack_buf));
assert(0x1234 == ack_high);
{
size_t sz;
@ -113,8 +111,6 @@ test2 (void)
assert(("Number of timestamps is 2", acki.n_timestamps == 2));
unsigned n = n_acked(&acki);
assert(("Number of acked packets is 4254", n == 4254));
lsquic_packno_t ack_high = pf->pf_parse_ack_high(ack_buf, sizeof(ack_buf));
assert(0x1234 == ack_high);
for (n = 0; n < 4; ++n)
assert(("Range checks out", ranges[n].high == acki.ranges[n].high
@ -166,8 +162,6 @@ test3 (void)
assert(("Number of timestamps is 0", acki.n_timestamps == 0));
unsigned n = n_acked(&acki);
assert(("Number of acked packets is 4", n == 4));
lsquic_packno_t ack_high = pf->pf_parse_ack_high(ack_buf, sizeof(ack_buf));
assert(6 == ack_high);
for (n = 0; n < 2; ++n)
assert(("Range checks out", ranges[n].high == acki.ranges[n].high
@ -218,8 +212,6 @@ test4 (void)
assert(("Number of timestamps is 0", acki.n_timestamps == 0));
unsigned n = n_acked(&acki);
assert(("Number of acked packets is 2", n == 2));
lsquic_packno_t ack_high = pf->pf_parse_ack_high(ack_buf, sizeof(ack_buf));
assert(3 == ack_high);
for (n = 0; n < 2; ++n)
assert(("Range checks out", ranges[n].high == acki.ranges[n].high
@ -361,11 +353,12 @@ test_max_ack (void)
memset(buf, 0xAA, sizeof(buf));
lsquic_packno_t largest = 0;
sz[0] = pf->pf_gen_ack_frame(buf, sizeof(buf),
(gaf_rechist_first_f) lsquic_rechist_first,
(gaf_rechist_next_f) lsquic_rechist_next,
(gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
&rechist, now, &has_missing);
&rechist, now, &has_missing, &largest);
assert(sz[0] > 0);
assert(sz[0] <= (int) sizeof(buf));
assert(has_missing);
@ -414,11 +407,12 @@ test_ack_truncation (void)
for (bufsz = 200; bufsz < 210; ++bufsz)
{
memset(buf, 0xAA, sizeof(buf));
lsquic_packno_t largest = 0;
sz[0] = pf->pf_gen_ack_frame(buf, bufsz,
(gaf_rechist_first_f) lsquic_rechist_first,
(gaf_rechist_next_f) lsquic_rechist_next,
(gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
&rechist, now, &has_missing);
&rechist, now, &has_missing, &largest);
assert(sz[0] > 0);
assert(sz[0] <= (int) bufsz);
assert(has_missing);

View File

@ -51,8 +51,6 @@ test1 (void)
assert(("Number of timestamps is 0", acki.n_timestamps == 0));
unsigned n = n_acked(&acki);
assert(("Number of acked packets is 0x1234", n == 0x1234));
lsquic_packno_t ack_high = pf->pf_parse_ack_high(ack_buf, sizeof(ack_buf));
assert(0x1234 == ack_high);
{
size_t sz;
@ -115,8 +113,6 @@ test2 (void)
assert(("Number of timestamps is 2", acki.n_timestamps == 2));
unsigned n = n_acked(&acki);
assert(("Number of acked packets is 4254", n == 4254));
lsquic_packno_t ack_high = pf->pf_parse_ack_high(ack_buf, sizeof(ack_buf));
assert(0x1234 == ack_high);
for (n = 0; n < 4; ++n)
assert(("Range checks out", ranges[n].high == acki.ranges[n].high
@ -169,8 +165,6 @@ test3 (void)
assert(("Number of timestamps is 0", acki.n_timestamps == 0));
unsigned n = n_acked(&acki);
assert(("Number of acked packets is 4", n == 4));
lsquic_packno_t ack_high = pf->pf_parse_ack_high(ack_buf, sizeof(ack_buf));
assert(6 == ack_high);
for (n = 0; n < 2; ++n)
assert(("Range checks out", ranges[n].high == acki.ranges[n].high
@ -222,8 +216,6 @@ test4 (void)
assert(("Number of timestamps is 0", acki.n_timestamps == 0));
unsigned n = n_acked(&acki);
assert(("Number of acked packets is 2", n == 2));
lsquic_packno_t ack_high = pf->pf_parse_ack_high(ack_buf, sizeof(ack_buf));
assert(3 == ack_high);
for (n = 0; n < 2; ++n)
assert(("Range checks out", ranges[n].high == acki.ranges[n].high
@ -361,11 +353,12 @@ test_max_ack (void)
memset(buf, 0xAA, sizeof(buf));
lsquic_packno_t largest = 0;
sz[0] = pf->pf_gen_ack_frame(buf, sizeof(buf),
(gaf_rechist_first_f) lsquic_rechist_first,
(gaf_rechist_next_f) lsquic_rechist_next,
(gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
&rechist, now, &has_missing);
&rechist, now, &has_missing, &largest);
assert(sz[0] > 0);
assert(sz[0] <= (int) sizeof(buf));
assert(has_missing);
@ -414,11 +407,12 @@ test_ack_truncation (void)
for (bufsz = 200; bufsz < 210; ++bufsz)
{
memset(buf, 0xAA, sizeof(buf));
lsquic_packno_t largest = 0;
sz[0] = pf->pf_gen_ack_frame(buf, bufsz,
(gaf_rechist_first_f) lsquic_rechist_first,
(gaf_rechist_next_f) lsquic_rechist_next,
(gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
&rechist, now, &has_missing);
&rechist, now, &has_missing, &largest);
assert(sz[0] > 0);
assert(sz[0] <= (int) bufsz);
assert(has_missing);

View File

@ -49,8 +49,6 @@ test1 (void)
assert(("Number of timestamps is 0", acki.n_timestamps == 0));
unsigned n = n_acked(&acki);
assert(("Number of acked packets is 0x1234", n == 0x1234));
lsquic_packno_t ack_high = pf->pf_parse_ack_high(ack_buf, sizeof(ack_buf));
assert(0x1234 == ack_high);
{
size_t sz;
@ -112,8 +110,6 @@ test2 (void)
assert(("Number of timestamps is 2", acki.n_timestamps == 2));
unsigned n = n_acked(&acki);
assert(("Number of acked packets is 4254", n == 4254));
lsquic_packno_t ack_high = pf->pf_parse_ack_high(ack_buf, sizeof(ack_buf));
assert(0x1234 == ack_high);
for (n = 0; n < 4; ++n)
assert(("Range checks out", ranges[n].high == acki.ranges[n].high
@ -165,8 +161,6 @@ test3 (void)
assert(("Number of timestamps is 0", acki.n_timestamps == 0));
unsigned n = n_acked(&acki);
assert(("Number of acked packets is 4", n == 4));
lsquic_packno_t ack_high = pf->pf_parse_ack_high(ack_buf, sizeof(ack_buf));
assert(6 == ack_high);
for (n = 0; n < 2; ++n)
assert(("Range checks out", ranges[n].high == acki.ranges[n].high
@ -217,8 +211,6 @@ test4 (void)
assert(("Number of timestamps is 0", acki.n_timestamps == 0));
unsigned n = n_acked(&acki);
assert(("Number of acked packets is 2", n == 2));
lsquic_packno_t ack_high = pf->pf_parse_ack_high(ack_buf, sizeof(ack_buf));
assert(3 == ack_high);
for (n = 0; n < 2; ++n)
assert(("Range checks out", ranges[n].high == acki.ranges[n].high
@ -360,11 +352,12 @@ test_max_ack (void)
memset(buf, 0xAA, sizeof(buf));
lsquic_packno_t largest = 0;
sz[0] = pf->pf_gen_ack_frame(buf, sizeof(buf),
(gaf_rechist_first_f) lsquic_rechist_first,
(gaf_rechist_next_f) lsquic_rechist_next,
(gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
&rechist, now, &has_missing);
&rechist, now, &has_missing, &largest);
assert(sz[0] > 0);
assert(sz[0] <= (int) sizeof(buf));
assert(has_missing);
@ -413,11 +406,12 @@ test_ack_truncation (void)
for (bufsz = 200; bufsz < 210; ++bufsz)
{
memset(buf, 0xAA, sizeof(buf));
lsquic_packno_t largest = 0;
sz[0] = pf->pf_gen_ack_frame(buf, bufsz,
(gaf_rechist_first_f) lsquic_rechist_first,
(gaf_rechist_next_f) lsquic_rechist_next,
(gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
&rechist, now, &has_missing);
&rechist, now, &has_missing, &largest);
assert(sz[0] > 0);
assert(sz[0] <= (int) bufsz);
assert(has_missing);

View File

@ -83,6 +83,9 @@ lsquic_stream_acked (lsquic_stream_t *stream)
static void
elide_single_stream_frame (void)
{
#ifndef NDEBUG
struct packet_out_srec_iter posi;
#endif
struct lsquic_engine_public enpub;
lsquic_stream_t streams[1];
lsquic_packet_out_t *packet_out;

View File

@ -3,52 +3,27 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "lsquic_int_types.h"
#include "lsquic_senhist.h"
#include "lsquic_logger.h"
int
main (void)
{
struct lsquic_senhist hist;
struct lsquic_senhist hist = { 0 };
lsquic_packno_t packno;
int s;
lsquic_senhist_init(&hist);
assert(0 == lsquic_senhist_largest(&hist));
for (packno = 1; packno < 100; ++packno)
lsquic_senhist_add(&hist, packno);
for (packno = 1; packno < 100; ++packno)
{
s = lsquic_senhist_sent_range(&hist, packno, packno);
assert(s);
}
/* Note break in the sequence at 100 */
for (packno = 101; packno < 200; ++packno)
lsquic_senhist_add(&hist, packno);
for (packno = 1; packno < 100; ++packno)
{
s = lsquic_senhist_sent_range(&hist, packno, packno);
assert(s);
}
s = lsquic_senhist_sent_range(&hist, 100, 100);
assert(0 == s);
for (packno = 101; packno < 200; ++packno)
{
s = lsquic_senhist_sent_range(&hist, packno, packno);
assert(s);
}
s = lsquic_senhist_sent_range(&hist, 1, 99);
assert(s);
s = lsquic_senhist_sent_range(&hist, 101, 199);
assert(s);
s = lsquic_senhist_sent_range(&hist, 1, 199);
assert(0 == s);
assert(99 == lsquic_senhist_largest(&hist));
lsquic_senhist_cleanup(&hist);

View File

@ -1,7 +1,9 @@
- only debug and release are expected in the Cmakelists.txt. If you need a different config, please follow the model in that file to add it.
- vcpkg does not have boringssl, so you'll have to build it yourself. Follow the instructions at the boringssl repository.
With the caveat that you should do it from a VC command prompt for the correct architecture and make sure to set all
the paths for perl,ninja,etc. correctly. Also watch out for C runtime library mismatches. The easiest fix for me was to
change the flags in the CMake cache file.
the paths for perl,ninja,etc. correctly. Also watch out for C runtime library mismatches with the externals you link.
- zlib and libevent do exist in vcpkg.