diff --git a/CHANGELOG b/CHANGELOG index 04ba03f..09e3cb6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,8 @@ +2018-05-21 + + - [API Change] Add optional callback to call when handshake is done + - [API Change, BUGFIX] After send failure, wait until transport available + 2018-05-18 - [API] Expose useful lsquic_ver2str[] in lsquic.h diff --git a/EXAMPLES.txt b/EXAMPLES.txt index 9e1f7a6..143955d 100644 --- a/EXAMPLES.txt +++ b/EXAMPLES.txt @@ -97,6 +97,13 @@ LSQUIC_CUBIC_SAMPLING_RATE Only available in debug builds. +LSQUIC_RANDOM_SEND_FAILURE + + Frequency with which sending of packets fails: one out of this many + times on average. + + Only available when compiled with -DLSQUIC_RANDOM_SEND_FAILURE=1 + Control Network-Related Stuff ----------------------------- @@ -141,3 +148,9 @@ More Compilation Options Add relatively expensive run-time sanity checks +-DLSQUIC_RANDOM_SEND_FAILURE=1 + + Simulate failure to send packets to test send resumption logic. When + this flag is specified, sending of packets will randomly fail, about + one out of every 10 attempts. Set environment variable + LSQUIC_RANDOM_SEND_FAILURE to change this frequency. diff --git a/include/lsquic.h b/include/lsquic.h index cf74361..4ff4847 100644 --- a/include/lsquic.h +++ b/include/lsquic.h @@ -143,6 +143,14 @@ struct lsquic_stream_if { void (*on_read) (lsquic_stream_t *s, lsquic_stream_ctx_t *h); void (*on_write) (lsquic_stream_t *s, lsquic_stream_ctx_t *h); void (*on_close) (lsquic_stream_t *s, lsquic_stream_ctx_t *h); + /** + * When handshake is completed, this callback is called. `ok' is set + * to true if handshake was successful; otherwise, `ok' is set to + * false. + * + * This callback is optional. + */ + void (*on_hsk_done)(lsquic_conn_t *c, int ok); }; /** @@ -428,7 +436,9 @@ struct lsquic_out_spec /** * Returns number of packets successfully sent out or -1 on error. -1 should - * only be returned if no packets were sent out. + * only be returned if no packets were sent out. If -1 is returned, + * no packets will be attempted to be sent out until + * @ref lsquic_engine_send_unsent_packets() is called. */ typedef int (*lsquic_packets_out_f)( void *packets_out_ctx, @@ -519,6 +529,10 @@ lsquic_engine_has_unsent_packets (lsquic_engine_t *engine); /** * Send out as many unsent packets as possibe: until we are out of unsent * packets or until @ref ea_packets_out() fails. + * + * If @ref ea_packets_out() does fail (that is, it returns an error), this + * function must be called to signify that sending of packets is possible + * again. */ void lsquic_engine_send_unsent_packets (lsquic_engine_t *engine); diff --git a/src/liblsquic/lsquic_engine.c b/src/liblsquic/lsquic_engine.c index f60d0a4..3ac2ff2 100644 --- a/src/liblsquic/lsquic_engine.c +++ b/src/liblsquic/lsquic_engine.c @@ -302,6 +302,7 @@ lsquic_engine_new (unsigned flags, return NULL; } engine->pub.enp_ver_tags_len = tag_buf_len; + engine->pub.enp_flags = ENPUB_CAN_SEND; engine->flags = flags; engine->stream_if = api->ea_stream_if; @@ -951,7 +952,9 @@ send_batch (lsquic_engine_t *engine, struct conns_out_iter *conns_iter, LSQ_DEBUG("packets out returned %d (out of %u)", n_sent, n_to_send); else { + engine->pub.enp_flags &= ~ENPUB_CAN_SEND; LSQ_DEBUG("packets out returned an error: %s", strerror(errno)); + EV_LOG_GENERIC_EVENT("cannot send packets"); n_sent = 0; } if (n_sent > 0) @@ -1147,6 +1150,12 @@ lsquic_engine_send_unsent_packets (lsquic_engine_t *engine) STAILQ_INIT(&closed_conns); reset_deadline(engine, lsquic_time_now()); + if (!(engine->pub.enp_flags & ENPUB_CAN_SEND)) + { + LSQ_DEBUG("can send again"); + EV_LOG_GENERIC_EVENT("can send again"); + engine->pub.enp_flags |= ENPUB_CAN_SEND; + } send_packets_out(engine, &closed_conns); @@ -1199,7 +1208,8 @@ process_connections (lsquic_engine_t *engine, conn_iter_f next_conn, STAILQ_INSERT_TAIL(&ticked_conns, conn, cn_next_ticked); } - if (lsquic_engine_has_unsent_packets(engine)) + if ((engine->pub.enp_flags & ENPUB_CAN_SEND) + && lsquic_engine_has_unsent_packets(engine)) send_packets_out(engine, &closed_conns); while ((conn = STAILQ_FIRST(&closed_conns))) { diff --git a/src/liblsquic/lsquic_engine_public.h b/src/liblsquic/lsquic_engine_public.h index 629936a..880e8eb 100644 --- a/src/liblsquic/lsquic_engine_public.h +++ b/src/liblsquic/lsquic_engine_public.h @@ -21,6 +21,7 @@ struct lsquic_engine_public { ENPUB_PROC = (1 << 0), /* Being processed by one of the user-facing * functions. */ + ENPUB_CAN_SEND = (1 << 1), } enp_flags; unsigned char enp_ver_tags_buf[ sizeof(lsquic_ver_tag_t) * N_LSQVER ]; unsigned enp_ver_tags_len; diff --git a/src/liblsquic/lsquic_full_conn.c b/src/liblsquic/lsquic_full_conn.c index ed0afb3..b277a22 100644 --- a/src/liblsquic/lsquic_full_conn.c +++ b/src/liblsquic/lsquic_full_conn.c @@ -3033,6 +3033,8 @@ full_conn_ci_handshake_ok (lsquic_conn_t *lconn) lconn->cn_flags |= LSCONN_HANDSHAKE_DONE; else conn->fc_flags |= FC_ERROR; + if (conn->fc_stream_ifs[STREAM_IF_STD].stream_if->on_hsk_done) + conn->fc_stream_ifs[STREAM_IF_STD].stream_if->on_hsk_done(lconn, 1); } @@ -3043,6 +3045,8 @@ full_conn_ci_handshake_failed (lsquic_conn_t *lconn) LSQ_DEBUG("handshake failed"); lsquic_alarmset_unset(&conn->fc_alset, AL_HANDSHAKE); conn->fc_flags |= FC_HSK_FAILED; + if (conn->fc_stream_ifs[STREAM_IF_STD].stream_if->on_hsk_done) + conn->fc_stream_ifs[STREAM_IF_STD].stream_if->on_hsk_done(lconn, 0); } @@ -3389,7 +3393,8 @@ full_conn_ci_is_tickable (lsquic_conn_t *lconn) if (!TAILQ_EMPTY(&conn->fc_pub.service_streams)) return 1; - if (lsquic_send_ctl_can_send(&conn->fc_send_ctl) + if ((conn->fc_enpub->enp_flags & ENPUB_CAN_SEND) + && lsquic_send_ctl_can_send(&conn->fc_send_ctl) && (should_generate_ack(conn) || !lsquic_send_ctl_sched_is_blocked(&conn->fc_send_ctl))) { diff --git a/test/http_client.c b/test/http_client.c index d04e450..3164ad2 100644 --- a/test/http_client.c +++ b/test/http_client.c @@ -147,6 +147,13 @@ http_client_on_conn_closed (lsquic_conn_t *conn) } +static void +http_client_on_hsk_done (lsquic_conn_t *conn, int ok) +{ + LSQ_INFO("handshake %s", ok ? "completed successfully" : "failed"); +} + + struct lsquic_stream_ctx { lsquic_stream_t *stream; struct http_client_ctx *client_ctx; @@ -398,6 +405,7 @@ const struct lsquic_stream_if http_client_if = { .on_read = http_client_on_read, .on_write = http_client_on_write, .on_close = http_client_on_close, + .on_hsk_done = http_client_on_hsk_done, }; diff --git a/test/prog.c b/test/prog.c index 593ffa2..ac815e2 100644 --- a/test/prog.c +++ b/test/prog.c @@ -419,3 +419,24 @@ prog_is_stopped (void) { return prog_stopped != 0; } + + +static void +send_unsent (evutil_socket_t fd, short what, void *arg) +{ + struct prog *const prog = arg; + assert(prog->prog_send); + event_del(prog->prog_send); + event_free(prog->prog_send); + prog->prog_send = NULL; + lsquic_engine_send_unsent_packets(prog->prog_engine); +} + + +void +prog_sport_cant_send (struct prog *prog, int fd) +{ + assert(!prog->prog_send); + prog->prog_send = event_new(prog->prog_eb, fd, EV_WRITE, send_unsent, prog); + event_add(prog->prog_send, NULL); +} diff --git a/test/prog.h b/test/prog.h index b58010b..89b2a69 100644 --- a/test/prog.h +++ b/test/prog.h @@ -23,6 +23,7 @@ struct prog int prog_version_cleared; struct event_base *prog_eb; struct event *prog_timer, + *prog_send, *prog_usr1; struct sport_head *prog_sports; struct lsquic_engine *prog_engine; @@ -83,4 +84,7 @@ prog_is_stopped (void); void prog_process_conns (struct prog *); +void +prog_sport_cant_send (struct prog *, int fd); + #endif diff --git a/test/test_common.c b/test/test_common.c index aaf1641..7870dd8 100644 --- a/test/test_common.c +++ b/test/test_common.c @@ -875,6 +875,23 @@ send_packets_one_by_one (const struct lsquic_out_spec *specs, unsigned count) if (0 == count) return 0; +#if LSQUIC_RANDOM_SEND_FAILURE + { + const char *freq_str = getenv("LSQUIC_RANDOM_SEND_FAILURE"); + int freq; + if (freq_str) + freq = atoi(freq_str); + else + freq = 10; + if (rand() % freq == 0) + { + assert(count > 0); + sport = specs[0].peer_ctx; + LSQ_NOTICE("sending \"randomly\" fails"); + goto random_send_failure; + } + } +#endif for (n = 0; n < count; ++n) { @@ -931,7 +948,13 @@ send_packets_one_by_one (const struct lsquic_out_spec *specs, unsigned count) if (n > 0) return n; else if (s < 0) + { +#if LSQUIC_RANDOM_SEND_FAILURE + random_send_failure: +#endif + prog_sport_cant_send(sport->sp_prog, sport->fd); return -1; + } else return 0; }