Latest changes

- [API Change] lsquic_engine_connect() returns pointer to the connection
  object.
- [API Change] Add lsquic_conn_get_engine() to get engine object from
  connection object.
- [API Change] Add lsquic_conn_status() to query connection status.
- [API Change] Add add lsquic_conn_set_ctx().
- [API Change] Add new timestamp format, e.g. 2017-03-21 13:43:46.671345
- [OPTIMIZATION] Process handshake STREAM frames as soon as packet
  arrives.
- [OPTIMIZATION] Do not compile expensive send controller sanity check
  by default.
- [OPTIMIZATION] Add fast path to gquic_be_gen_reg_pkt_header.
- [OPTIMIZATION] Only make squeeze function call if necessary.
- [OPTIMIZATION] Speed up Q039 ACK frame parsing.
- [OPTIMIZATION] Fit most used elements of packet_out into first 64 bytes.
- [OPTIMIZATION] Keep track of scheduled bytes instead of calculating.
- [OPTIMIZATION] Prefetch next unacked packet when processing ACK.
- [OPTIMIZATION] Leverage fact that ACK ranges and unacked list are.
  ordered.
- [OPTIMIZATION] Reduce function pointer use for STREAM frame generation
- Fix: reset incoming streams that arrive after we send GOAWAY.
- Fix: delay client on_new_conn() call until connection is fully set up.
- Fixes to buffered packets logic: splitting, STREAM frame elision.
- Fix: do not dispatch on_write callback if no packets are available.
- Fix WINDOW_UPDATE send and resend logic.
- Fix STREAM frame extension code.
- Fix: Drop unflushed data when stream is reset.
- Switch to tracking CWND using bytes rather than packets.
- Fix TCP friendly adjustment in cubic.
- Fix: do not generate invalid STOP_WAITING frames during high packet
  loss.
- Pacer fixes.
This commit is contained in:
Dmitri Tikhonov 2018-02-26 16:01:16 -05:00
parent 7edaabaafe
commit bfc7bfd842
46 changed files with 1140 additions and 547 deletions

View file

@ -1,3 +1,43 @@
2018-02-26
- [API Change] lsquic_engine_connect() returns pointer to the connection
object.
- [API Change] Add lsquic_conn_get_engine() to get engine object from
connection object.
- [API Change] Add lsquic_conn_status() to query connection status.
- [API Change] Add add lsquic_conn_set_ctx().
- [API Change] Add new timestamp format, e.g. 2017-03-21 13:43:46.671345
- [OPTIMIZATION] Process handshake STREAM frames as soon as packet
arrives.
- [OPTIMIZATION] Do not compile expensive send controller sanity check
by default.
- [OPTIMIZATION] Add fast path to gquic_be_gen_reg_pkt_header.
- [OPTIMIZATION] Only make squeeze function call if necessary.
- [OPTIMIZATION] Speed up Q039 ACK frame parsing.
- [OPTIMIZATION] Fit most used elements of packet_out into first 64 bytes.
- [OPTIMIZATION] Keep track of scheduled bytes instead of calculating.
- [OPTIMIZATION] Prefetch next unacked packet when processing ACK.
- [OPTIMIZATION] Leverage fact that ACK ranges and unacked list are.
ordered.
- [OPTIMIZATION] Reduce function pointer use for STREAM frame generation
- Fix: reset incoming streams that arrive after we send GOAWAY.
- Fix: delay client on_new_conn() call until connection is fully set up.
- Fixes to buffered packets logic: splitting, STREAM frame elision.
- Fix: do not dispatch on_write callback if no packets are available.
- Fix WINDOW_UPDATE send and resend logic.
- Fix STREAM frame extension code.
- Fix: Drop unflushed data when stream is reset.
- Switch to tracking CWND using bytes rather than packets.
- Fix TCP friendly adjustment in cubic.
- Fix: do not generate invalid STOP_WAITING frames during high packet
loss.
- Pacer fixes.
2017-12-18
- Fix: better follow cubic curve after idle period
- Fix: add missing parts to outgoing packet splitting code
- Fix: compilation using gcc 4.8.4
2017-10-31
- Add APIs.txt -- describes LSQUIC APIs

View file

@ -91,13 +91,7 @@ target_link_libraries(http_client lsquic event pthread libssl.a libcrypto.a ${FI
add_subdirectory(src)
IF(DEVEL_MODE EQUAL 1)
# Our test framework relies on assertions, only compile if assertions are
# enabled.
#
add_subdirectory(test)
enable_testing()
ENDIF()
add_subdirectory(test)
ADD_CUSTOM_TARGET(docs doxygen dox.cfg)

View file

@ -81,15 +81,6 @@ lsquic_engine_settings.
Control LSQUIC Behavior via Environment Variables
-------------------------------------------------
LSQUIC_CUBIC_SHIFT_EPOCH
This environment variable determines whether cubic epoch is shifted
when sender is application-limited.
This is a leftover from the time when application-limited behavior was
implemented and is only available in debug builds. By default, the
epoch is shifted.
LSQUIC_PACER_INTERTICK
Number of microsecods to use as constant intertick time in lieu of the
@ -97,6 +88,12 @@ LSQUIC_PACER_INTERTICK
Only available in debug builds.
LSQUIC_CUBIC_SAMPLING_RATE
Number of microseconds between times CWND is logged at info level.
Only available in debug builds.
Control Network-Related Stuff
-----------------------------
@ -136,3 +133,8 @@ More Compilation Options
-DLSQUIC_LOWEST_LOG_LEVEL=LSQ_LOG_WARN
If you want to go even faster: compile out some log levels entirely.
-DLSQUIC_EXTRA_CHECKS=1
Add relatively expensive run-time sanity checks

View file

@ -476,10 +476,10 @@ lsquic_engine_new (unsigned lsquic_engine_flags,
* If `max_packet_size' is set to zero, it is inferred based on `peer_sa':
* 1350 for IPv6 and 1370 for IPv4.
*/
int
lsquic_conn_t *
lsquic_engine_connect (lsquic_engine_t *, const struct sockaddr *peer_sa,
void *peer_ctx, const char *hostname,
unsigned short max_packet_size);
void *peer_ctx, lsquic_conn_ctx_t *conn_ctx,
const char *hostname, unsigned short max_packet_size);
/**
* Pass incoming packet to the QUIC engine. This function can be called
@ -734,6 +734,10 @@ lsquic_conn_get_stream_by_id (lsquic_conn_t *c, uint32_t stream_id);
lsquic_cid_t
lsquic_conn_id (const lsquic_conn_t *c);
/** Get pointer to the engine */
lsquic_engine_t *
lsquic_conn_get_engine (lsquic_conn_t *c);
int lsquic_conn_get_sockaddr(const lsquic_conn_t *c,
const struct sockaddr **local, const struct sockaddr **peer);
@ -777,6 +781,12 @@ enum lsquic_logger_timestamp_style {
*/
LLTS_HHMMSSUS,
/**
* Date and time using microsecond resolution,
* e.g: 2017-03-21 13:43:46.671123
*/
LLTS_YYYYMMDD_HHMMSSUS,
N_LLTS
};
@ -863,6 +873,11 @@ lsquic_str2ver (const char *str, size_t len);
lsquic_conn_ctx_t *
lsquic_conn_get_ctx (const lsquic_conn_t *c);
/**
* Set user-supplied context associated with the connection.
*/
void lsquic_conn_set_ctx (lsquic_conn_t *c, lsquic_conn_ctx_t *h);
/**
* Get peer context associated with the connection.
*/
@ -890,6 +905,25 @@ lsquic_engine_earliest_adv_tick (lsquic_engine_t *engine, int *diff);
unsigned
lsquic_engine_count_attq (lsquic_engine_t *engine, int from_now);
enum LSQUIC_CONN_STATUS
{
LSCONN_ST_HSK_IN_PROGRESS,
LSCONN_ST_CONNECTED,
LSCONN_ST_HSK_FAILURE,
LSCONN_ST_GOING_AWAY,
LSCONN_ST_TIMED_OUT,
/* If es_honor_prst is not set, the connection will never get public
* reset packets and this flag will not be set.
*/
LSCONN_ST_RESET,
LSCONN_ST_USER_ABORTED,
LSCONN_ST_ERROR,
LSCONN_ST_CLOSED,
};
enum LSQUIC_CONN_STATUS
lsquic_conn_status (lsquic_conn_t *, char *errbuf, size_t bufsz);
#ifdef __cplusplus
}
#endif

View file

@ -95,6 +95,7 @@ hsk_client_on_read (lsquic_stream_t *stream, struct lsquic_stream_ctx *sh)
lsquic_mm_put_16k(c_hsk->mm, c_hsk->buf_in);
c_hsk->buf_in = NULL;
lsquic_stream_wantread(stream, 0);
c_hsk->lconn->cn_if->ci_handshake_failed(c_hsk->lconn);
lsquic_conn_close(c_hsk->lconn);
}
break;
@ -104,8 +105,8 @@ hsk_client_on_read (lsquic_stream_t *stream, struct lsquic_stream_ctx *sh)
lsquic_stream_wantread(stream, 0);
if (c_hsk->lconn->cn_esf->esf_is_hsk_done(c_hsk->lconn->cn_enc_session))
{
LSQ_DEBUG("handshake is complete, inform connection");
c_hsk->lconn->cn_if->ci_handshake_done(c_hsk->lconn);
LSQ_DEBUG("handshake is successful, inform connection");
c_hsk->lconn->cn_if->ci_handshake_ok(c_hsk->lconn);
}
else
{
@ -118,6 +119,10 @@ hsk_client_on_read (lsquic_stream_t *stream, struct lsquic_stream_ctx *sh)
LSQ_WARN("lsquic_enc_session_handle_chlo_reply returned unknown value %d", s);
case DATA_FORMAT_ERROR:
LSQ_INFO("lsquic_enc_session_handle_chlo_reply returned an error");
c_hsk->buf_in = NULL;
lsquic_stream_wantread(stream, 0);
c_hsk->lconn->cn_if->ci_handshake_failed(c_hsk->lconn);
lsquic_conn_close(c_hsk->lconn);
break;
}
}

View file

@ -39,7 +39,7 @@ enum lsquic_conn_flags {
LSCONN_ATTQ = (1 <<19),
};
#define TICK_BIT_PROGRESS 3
#define TICK_BIT_PROGRESS 2
/* A connection may have things to send and be closed at the same time.
*/
@ -72,7 +72,10 @@ struct conn_iface
(*ci_packet_not_sent) (struct lsquic_conn *, struct lsquic_packet_out *);
void
(*ci_handshake_done) (struct lsquic_conn *);
(*ci_handshake_ok) (struct lsquic_conn *);
void
(*ci_handshake_failed) (struct lsquic_conn *);
int
(*ci_user_wants_read) (struct lsquic_conn *);

View file

@ -29,24 +29,24 @@ static void
cubic_reset (struct lsquic_cubic *cubic)
{
memset(cubic, 0, offsetof(struct lsquic_cubic, cu_cid));
cubic->cu_cwnd = 32;
cubic->cu_last_max_cwnd = 32;
cubic->cu_cwnd = 32 * TCP_MSS;
cubic->cu_last_max_cwnd = 32 * TCP_MSS;
cubic->cu_tcp_cwnd = 32 * TCP_MSS;
}
static void
cubic_update (struct lsquic_cubic *cubic, lsquic_time_t now)
cubic_update (struct lsquic_cubic *cubic, lsquic_time_t now, unsigned n_bytes)
{
lsquic_time_t delta_t, t, target;
unsigned tcp_cwnd;
double delta_t, t;
lsquic_time_t target;
if (0 == cubic->cu_epoch_start)
{
cubic->cu_epoch_start = now;
if (cubic->cu_cwnd < cubic->cu_last_max_cwnd)
{
cubic->cu_K = cbrt((cubic->cu_last_max_cwnd - cubic->cu_cwnd) *
ONE_OVER_C / 1024) * 1000000;
cubic->cu_K = cbrt(cubic->cu_last_max_cwnd / TCP_MSS / 2);
cubic->cu_origin_point = cubic->cu_last_max_cwnd;
}
else
@ -54,36 +54,38 @@ cubic_update (struct lsquic_cubic *cubic, lsquic_time_t now)
cubic->cu_K = 0;
cubic->cu_origin_point = cubic->cu_cwnd;
}
}
else if ((cubic->cu_flags & CU_SHIFT_EPOCH) && cubic->cu_app_limited)
{
LSQ_DEBUG("increment epoch_start by %"PRIu64" microseconds", now - cubic->cu_app_limited);
cubic->cu_epoch_start += now - cubic->cu_app_limited;
LSQ_DEBUG("cwnd: %lu; last_max_cwnd: %lu; K: %lf; origin_point: %lu",
cubic->cu_cwnd, cubic->cu_last_max_cwnd, cubic->cu_K, cubic->cu_origin_point);
}
delta_t = now + cubic->cu_min_delay - cubic->cu_epoch_start;
delta_t = (double) (now + cubic->cu_min_delay - cubic->cu_epoch_start) / 1000000;
if (delta_t < cubic->cu_K)
{
t = cubic->cu_K - delta_t;
t /= 62500;
target = cubic->cu_origin_point - C * t * t * t / 1024 / 4096;
target = cubic->cu_origin_point - t * t * t * 0.4 * TCP_MSS;
LSQ_DEBUG("delta_t: %lf; t: %lf; target 1: %"PRIu64, delta_t, t, target);
}
else
{
t = delta_t - cubic->cu_K;
t /= 62500;
target = cubic->cu_origin_point + C * t * t * t / 1024 / 4096;
if (cubic->cu_flags & CU_TCP_FRIENDLY)
{
tcp_cwnd = cubic->cu_last_max_cwnd * ONE_MINUS_BETA / 1024 +
(delta_t - cubic->cu_K) * C / 1024 / cubic->cu_min_delay;
if (tcp_cwnd > target)
target = tcp_cwnd;
}
target = cubic->cu_origin_point + t * t * t * 0.4 * TCP_MSS;
LSQ_DEBUG("target 2: %"PRIu64, target);
}
if (cubic->cu_flags & CU_TCP_FRIENDLY)
{
cubic->cu_tcp_cwnd += n_bytes * TCP_MSS * ONE_MINUS_BETA / 1024
/ cubic->cu_tcp_cwnd;
LSQ_DEBUG("delta_t: %lf; last_max: %lu; cu_tcp_cwnd: %lu; target: "
"%"PRIu64"; over: %d; left: %d", delta_t, cubic->cu_last_max_cwnd,
cubic->cu_tcp_cwnd, target, cubic->cu_tcp_cwnd > target,
delta_t < cubic->cu_K);
if (cubic->cu_tcp_cwnd > target)
target = cubic->cu_tcp_cwnd;
}
if (target == 0)
target = 1;
target = TCP_MSS;
cubic->cu_cwnd = target;
}
@ -94,23 +96,18 @@ lsquic_cubic_init_ext (struct lsquic_cubic *cubic, lsquic_cid_t cid,
enum cubic_flags flags)
{
cubic_reset(cubic);
cubic->cu_ssthresh = 10000; /* Emulate "unbounded" slow start */
cubic->cu_ssthresh = 10000 * TCP_MSS; /* Emulate "unbounded" slow start */
cubic->cu_cid = cid;
cubic->cu_flags = flags;
LSQ_DEBUG("%s(cubic, %"PRIu64", 0x%X)", __func__, cid, flags);
#ifndef NDEBUG
{
const char *shift;
shift = getenv("LSQUIC_CUBIC_SHIFT_EPOCH");
if (shift)
{
if (atoi(shift))
cubic->cu_flags |= CU_SHIFT_EPOCH;
else
cubic->cu_flags &= ~CU_SHIFT_EPOCH;
}
}
const char *s;
s = getenv("LSQUIC_CUBIC_SAMPLING_RATE");
if (s)
cubic->cu_sampling_rate = atoi(s);
else
#endif
cubic->cu_sampling_rate = 100000;
LSQ_DEBUG("%s(cubic, %"PRIu64", 0x%X)", __func__, cid, flags);
LSQ_INFO("initialized");
}
@ -118,20 +115,29 @@ lsquic_cubic_init_ext (struct lsquic_cubic *cubic, lsquic_cid_t cid,
#define LOG_CWND(c) do { \
if (LSQ_LOG_ENABLED(LSQ_LOG_INFO)) { \
lsquic_time_t now = lsquic_time_now(); \
now -= now % 100000; \
now -= now % (c)->cu_sampling_rate; \
if (now > (c)->cu_last_logged) { \
LSQ_INFO("CWND: %u", (c)->cu_cwnd); \
LSQ_INFO("CWND: %lu", (c)->cu_cwnd); \
(c)->cu_last_logged = now; \
} \
} \
} while (0)
void
lsquic_cubic_was_quiet (struct lsquic_cubic *cubic, lsquic_time_t now)
{
LSQ_DEBUG("%s(cubic, %"PRIu64")", __func__, now);
cubic->cu_epoch_start = 0;
}
void
lsquic_cubic_ack (struct lsquic_cubic *cubic, lsquic_time_t now,
lsquic_time_t rtt, int app_limited)
lsquic_time_t rtt, int app_limited, unsigned n_bytes)
{
LSQ_DEBUG("%s(cubic, %"PRIu64", %"PRIu64", %d)", __func__, now, rtt,
app_limited);
LSQ_DEBUG("%s(cubic, %"PRIu64", %"PRIu64", %d, %u)", __func__, now, rtt,
app_limited, n_bytes);
if (0 == cubic->cu_min_delay || rtt < cubic->cu_min_delay)
{
cubic->cu_min_delay = rtt;
@ -140,31 +146,15 @@ lsquic_cubic_ack (struct lsquic_cubic *cubic, lsquic_time_t now,
if (cubic->cu_cwnd <= cubic->cu_ssthresh)
{
++cubic->cu_cwnd;
LSQ_DEBUG("ACK: slow threshold, cwnd: %u", cubic->cu_cwnd);
cubic->cu_cwnd += TCP_MSS;
LSQ_DEBUG("ACK: slow threshold, cwnd: %lu", cubic->cu_cwnd);
}
else
else if (!app_limited)
{
if (app_limited)
{
if (cubic->cu_flags & CU_SHIFT_EPOCH)
{
if (0 == cubic->cu_app_limited)
{
cubic->cu_app_limited = now;
LSQ_DEBUG("set app_limited to %"PRIu64, now);
}
}
else
cubic->cu_epoch_start = 0;
}
else
{
cubic_update(cubic, now);
cubic->cu_app_limited = 0;
}
LSQ_DEBUG("ACK: cwnd: %u", cubic->cu_cwnd);
cubic_update(cubic, now, n_bytes);
LSQ_DEBUG("ACK: cwnd: %lu", cubic->cu_cwnd);
}
LOG_CWND(cubic);
}
@ -174,14 +164,14 @@ lsquic_cubic_loss (struct lsquic_cubic *cubic)
{
LSQ_DEBUG("%s(cubic)", __func__);
cubic->cu_epoch_start = 0;
cubic->cu_app_limited = 0;
if (FAST_CONVERGENCE && cubic->cu_cwnd < cubic->cu_last_max_cwnd)
cubic->cu_last_max_cwnd = cubic->cu_cwnd * TWO_MINUS_BETA_OVER_TWO / 1024;
else
cubic->cu_last_max_cwnd = cubic->cu_cwnd;
cubic->cu_cwnd = cubic->cu_cwnd * ONE_MINUS_BETA / 1024;
cubic->cu_tcp_cwnd = cubic->cu_cwnd;
cubic->cu_ssthresh = cubic->cu_cwnd;
LSQ_INFO("loss detected, last_max_cwnd: %u, cwnd: %u",
LSQ_INFO("loss detected, last_max_cwnd: %lu, cwnd: %lu",
cubic->cu_last_max_cwnd, cubic->cu_cwnd);
LOG_CWND(cubic);
}
@ -193,6 +183,7 @@ lsquic_cubic_timeout (struct lsquic_cubic *cubic)
LSQ_DEBUG("%s(cubic)", __func__);
cubic_reset(cubic);
cubic->cu_ssthresh = cubic->cu_cwnd;
LSQ_INFO("timeout, cwnd: %u", cubic->cu_cwnd);
cubic->cu_tcp_cwnd = cubic->cu_cwnd;
LSQ_INFO("timeout, cwnd: %lu", cubic->cu_cwnd);
LOG_CWND(cubic);
}

View file

@ -9,21 +9,23 @@
struct lsquic_cubic {
lsquic_time_t cu_min_delay;
lsquic_time_t cu_epoch_start;
lsquic_time_t cu_K;
lsquic_time_t cu_app_limited;
unsigned cu_origin_point;
unsigned cu_last_max_cwnd;
unsigned cu_cwnd;
unsigned cu_ssthresh;
double cu_K;
unsigned long cu_origin_point;
unsigned long cu_last_max_cwnd;
unsigned long cu_cwnd;
unsigned long cu_tcp_cwnd;
unsigned long cu_ssthresh;
lsquic_cid_t cu_cid; /* Used for logging */
enum cubic_flags {
CU_TCP_FRIENDLY = (1 << 0),
CU_SHIFT_EPOCH = (1 << 1),
} cu_flags;
unsigned cu_sampling_rate;
lsquic_time_t cu_last_logged;
};
#define DEFAULT_CUBIC_FLAGS (CU_TCP_FRIENDLY|CU_SHIFT_EPOCH)
#define DEFAULT_CUBIC_FLAGS (CU_TCP_FRIENDLY)
#define TCP_MSS 1460
void
lsquic_cubic_init_ext (struct lsquic_cubic *, lsquic_cid_t, enum cubic_flags);
@ -33,7 +35,7 @@ lsquic_cubic_init_ext (struct lsquic_cubic *, lsquic_cid_t, enum cubic_flags);
void
lsquic_cubic_ack (struct lsquic_cubic *cubic, lsquic_time_t now,
lsquic_time_t rtt, int app_limited);
lsquic_time_t rtt, int app_limited, unsigned n_bytes);
void
lsquic_cubic_loss (struct lsquic_cubic *cubic);
@ -41,6 +43,9 @@ lsquic_cubic_loss (struct lsquic_cubic *cubic);
void
lsquic_cubic_timeout (struct lsquic_cubic *cubic);
void
lsquic_cubic_was_quiet (struct lsquic_cubic *, lsquic_time_t now);
#define lsquic_cubic_get_cwnd(c) (+(c)->cu_cwnd)
#define lsquic_cubic_in_slow_start(cubic) \

View file

@ -22,6 +22,7 @@
#include "lsquic_int_types.h"
#include "lsquic_types.h"
#include "lsquic_conn_flow.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_in.h"
#include "lsquic_rtt.h"
#include "lsquic_sfcw.h"

View file

@ -53,6 +53,7 @@
#include "lsquic_types.h"
#include "lsquic_int_types.h"
#include "lsquic_conn_flow.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_in.h"
#include "lsquic_rtt.h"
#include "lsquic_sfcw.h"

View file

@ -49,6 +49,7 @@
#include "lsquic_eng_hist.h"
#include "lsquic_ev_log.h"
#include "lsquic_version.h"
#include "lsquic_hash.h"
#include "lsquic_attq.h"
#define LSQUIC_LOGGER_MODULE LSQLM_ENGINE
@ -134,6 +135,8 @@ struct out_heap
};
struct lsquic_engine
{
struct lsquic_engine_public pub;
@ -467,9 +470,11 @@ shrink_batch_size (struct lsquic_engine *engine)
}
/* Wrapper to make sure LSCONN_NEVER_PEND_RW gets set */
/* Wrapper to make sure important things occur before the connection is
* really destroyed.
*/
static void
destroy_conn (lsquic_conn_t *conn)
destroy_conn (struct lsquic_engine *engine, lsquic_conn_t *conn)
{
conn->cn_flags |= LSCONN_NEVER_PEND_RW;
conn->cn_if->ci_destroy(conn);
@ -491,7 +496,7 @@ new_full_conn_client (lsquic_engine_t *engine, const char *hostname,
{
LSQ_WARN("cannot add connection %"PRIu64" to hash - destroy",
conn->cn_cid);
destroy_conn(conn);
destroy_conn(engine, conn);
return NULL;
}
assert(!(conn->cn_flags &
@ -849,17 +854,17 @@ conn_iter_next_one (lsquic_engine_t *engine)
}
int
lsquic_conn_t *
lsquic_engine_connect (lsquic_engine_t *engine, const struct sockaddr *peer_sa,
void *conn_ctx, const char *hostname,
unsigned short max_packet_size)
void *peer_ctx, lsquic_conn_ctx_t *conn_ctx,
const char *hostname, unsigned short max_packet_size)
{
lsquic_conn_t *conn;
if (engine->flags & ENG_SERVER)
{
LSQ_ERROR("`%s' must only be called in client mode", __func__);
return -1;
return NULL;
}
if (0 == max_packet_size)
@ -877,14 +882,16 @@ lsquic_engine_connect (lsquic_engine_t *engine, const struct sockaddr *peer_sa,
conn = new_full_conn_client(engine, hostname, max_packet_size);
if (!conn)
return -1;
return NULL;
ENGINE_IN(engine);
lsquic_conn_record_peer_sa(conn, peer_sa);
conn->cn_peer_ctx = conn_ctx;
conn->cn_peer_ctx = peer_ctx;
lsquic_conn_set_ctx(conn, conn_ctx);
engine->iter_state.one.conn = conn;
full_conn_client_call_on_new(conn);
process_connections(engine, conn_iter_next_one);
ENGINE_OUT(engine);
return 0;
return conn;
}
@ -938,7 +945,7 @@ engine_decref_conn (lsquic_engine_t *engine, lsquic_conn_t *conn,
if (0 == (conn->cn_flags & CONN_REF_FLAGS))
{
eng_hist_inc(&engine->history, 0, sl_del_full_conns);
destroy_conn(conn);
destroy_conn(engine, conn);
return NULL;
}
else
@ -1034,13 +1041,24 @@ lsquic_engine_proc_all (lsquic_engine_t *engine)
void
lsquic_engine_process_conns_to_tick (lsquic_engine_t *engine)
{
lsquic_time_t prev_min, cutoff;
lsquic_time_t prev_min, now;
now = lsquic_time_now();
if (LSQ_LOG_ENABLED(LSQ_LOG_DEBUG))
{
const lsquic_time_t *expected_time;
int64_t diff;
expected_time = attq_next_time(engine->attq);
if (expected_time)
diff = *expected_time - now;
else
diff = -1;
LSQ_DEBUG("process connections in attq; time diff: %"PRIi64, diff);
}
LSQ_DEBUG("process connections in attq");
ENGINE_IN(engine);
cutoff = lsquic_time_now();
prev_min = attq_set_min(engine->attq, cutoff); /* Prevent infinite loop */
engine->iter_state.attq.cutoff = cutoff;
prev_min = attq_set_min(engine->attq, now); /* Prevent infinite loop */
engine->iter_state.attq.cutoff = now;
process_connections(engine, conn_iter_next_attq);
attq_set_min(engine->attq, prev_min); /* Restore previos value */
ENGINE_OUT(engine);

View file

@ -91,10 +91,11 @@ enum full_conn_flags {
FC_GOT_PRST = (1 <<18), /* Received public reset packet */
FC_FIRST_TICK = (1 <<19),
FC_TICK_CLOSE = (1 <<20), /* We returned TICK_CLOSE */
FC_HSK_FAILED = (1 <<21),
};
#define FC_IMMEDIATE_CLOSE_FLAGS \
(FC_TIMED_OUT|FC_ERROR|FC_ABORTED)
(FC_TIMED_OUT|FC_ERROR|FC_ABORTED|FC_HSK_FAILED)
#if LSQUIC_KEEP_STREAM_HISTORY
#define KEEP_CLOSED_STREAM_HISTORY 0
@ -111,6 +112,35 @@ struct stream_history
#define SHIST_MASK ((1 << SHIST_BITS) - 1)
#endif
#ifndef KEEP_PACKET_HISTORY
#ifdef NDEBUG
#define KEEP_PACKET_HISTORY 0
#else
#define KEEP_PACKET_HISTORY 16
#endif
#endif
#if KEEP_PACKET_HISTORY
struct packet_el
{
lsquic_time_t time;
enum quic_ft_bit frame_types;
};
struct recent_packets
{
struct packet_el els[KEEP_PACKET_HISTORY];
unsigned idx;
};
#endif
struct stream_id_to_reset
{
STAILQ_ENTRY(stream_id_to_reset) sitr_next;
uint32_t sitr_stream_id;
};
struct full_conn
{
struct lsquic_conn fc_conn;
@ -167,10 +197,26 @@ struct full_conn
struct stream_history fc_stream_histories[1 << SHIST_BITS];
unsigned fc_stream_hist_idx;
#endif
char *fc_errmsg;
#if KEEP_PACKET_HISTORY
struct recent_packets fc_recent_packets[2]; /* 0: in; 1: out */
#endif
STAILQ_HEAD(, stream_id_to_reset)
fc_stream_ids_to_reset;
};
#define MAX_ERRMSG 256
#define SET_ERRMSG(conn, errmsg...) do { \
if (!(conn)->fc_errmsg) \
(conn)->fc_errmsg = malloc(MAX_ERRMSG); \
if ((conn)->fc_errmsg) \
snprintf((conn)->fc_errmsg, MAX_ERRMSG, errmsg); \
} while (0)
#define ABORT_WITH_FLAG(conn, flag, errmsg...) do { \
SET_ERRMSG(conn, errmsg); \
(conn)->fc_flags |= flag; \
LSQ_ERROR("Abort connection: " errmsg); \
} while (0)
@ -197,6 +243,12 @@ new_stream (struct full_conn *conn, uint32_t stream_id, enum stream_ctor_flags);
static void
reset_ack_state (struct full_conn *conn);
static int
write_is_possible (struct full_conn *);
static int
dispatch_stream_read_events (struct full_conn *, struct lsquic_stream *);
static const struct headers_stream_callbacks headers_callbacks;
#if KEEP_CLOSED_STREAM_HISTORY
@ -242,6 +294,32 @@ find_stream_history (const struct full_conn *conn, uint32_t stream_id)
# define SAVE_STREAM_HISTORY(conn, stream)
#endif
#if KEEP_PACKET_HISTORY
static void
recent_packet_hist_new (struct full_conn *conn, unsigned out,
lsquic_time_t time)
{
unsigned idx;
idx = conn->fc_recent_packets[out].idx++ % KEEP_PACKET_HISTORY;
conn->fc_recent_packets[out].els[idx].time = time;
}
static void
recent_packet_hist_frames (struct full_conn *conn, unsigned out,
enum quic_ft_bit frame_types)
{
unsigned idx;
idx = (conn->fc_recent_packets[out].idx - 1) % KEEP_PACKET_HISTORY;
conn->fc_recent_packets[out].els[idx].frame_types |= frame_types;
}
#else
#define recent_packet_hist_new(conn, out, time)
#define recent_packet_hist_frames(conn, out, frames)
#endif
static unsigned
highest_bit_set (unsigned sz)
{
@ -486,6 +564,7 @@ new_conn_common (lsquic_cid_t cid, struct lsquic_engine_public *enpub,
TAILQ_INIT(&conn->fc_pub.read_streams);
TAILQ_INIT(&conn->fc_pub.write_streams);
TAILQ_INIT(&conn->fc_pub.service_streams);
STAILQ_INIT(&conn->fc_stream_ids_to_reset);
lsquic_conn_cap_init(&conn->fc_pub.conn_cap, LSQUIC_MIN_FCW);
lsquic_alarmset_init(&conn->fc_alset, cid);
lsquic_alarmset_init_alarm(&conn->fc_alset, AL_IDLE, idle_alarm_expired, conn);
@ -597,13 +676,22 @@ full_conn_client_new (struct lsquic_engine_public *enpub,
return NULL;
}
conn->fc_flags |= FC_CREATED_OK;
conn->fc_conn_ctx = stream_if->on_new_conn(stream_if_ctx, &conn->fc_conn);
LSQ_INFO("Created new client connection");
EV_LOG_CONN_EVENT(cid, "created full connection");
return &conn->fc_conn;
}
void
full_conn_client_call_on_new (struct lsquic_conn *lconn)
{
struct full_conn *const conn = (struct full_conn *) lconn;
assert(conn->fc_flags & FC_CREATED_OK);
conn->fc_conn_ctx = conn->fc_stream_ifs[STREAM_IF_STD].stream_if
->on_new_conn(conn->fc_stream_ifs[STREAM_IF_STD].stream_if_ctx, lconn);
}
static int
is_our_stream (const struct full_conn *conn, const lsquic_stream_t *stream)
{
@ -644,6 +732,7 @@ full_conn_ci_destroy (lsquic_conn_t *lconn)
struct full_conn *conn = (struct full_conn *) lconn;
struct lsquic_hash_elem *el;
struct lsquic_stream *stream;
struct stream_id_to_reset *sitr;
LSQ_DEBUG("destroy connection");
conn->fc_flags |= FC_CLOSING;
@ -676,7 +765,13 @@ full_conn_ci_destroy (lsquic_conn_t *lconn)
conn->fc_stats.n_packets_out,
conn->fc_stats.stream_data_sz / conn->fc_stats.n_packets_out);
#endif
while ((sitr = STAILQ_FIRST(&conn->fc_stream_ids_to_reset)))
{
STAILQ_REMOVE_HEAD(&conn->fc_stream_ids_to_reset, sitr_next);
free(sitr);
}
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "full connection destroyed");
free(conn->fc_errmsg);
free(conn);
}
@ -879,6 +974,14 @@ lsquic_conn_get_stream_by_id (lsquic_conn_t *lconn, uint32_t stream_id)
}
lsquic_engine_t *
lsquic_conn_get_engine (lsquic_conn_t *lconn)
{
struct full_conn *conn = (struct full_conn *) lconn;
return conn->fc_enpub->enp_engine;
}
static unsigned
count_zero_bytes (const unsigned char *p, size_t len)
{
@ -926,6 +1029,24 @@ is_peer_initiated (const struct full_conn *conn, uint32_t stream_id)
}
static void
maybe_schedule_reset_for_stream (struct full_conn *conn, uint32_t stream_id)
{
struct stream_id_to_reset *sitr;
if (conn_is_stream_closed(conn, stream_id))
return;
sitr = malloc(sizeof(*sitr));
if (!sitr)
return;
sitr->sitr_stream_id = stream_id;
STAILQ_INSERT_TAIL(&conn->fc_stream_ids_to_reset, sitr, sitr_next);
conn_mark_stream_closed(conn, stream_id);
}
static unsigned
process_stream_frame (struct full_conn *conn, lsquic_packet_in_t *packet_in,
const unsigned char *p, size_t len)
@ -992,8 +1113,9 @@ process_stream_frame (struct full_conn *conn, lsquic_packet_in_t *packet_in,
if ((conn->fc_flags & FC_GOING_AWAY) &&
stream_frame->stream_id > conn->fc_max_peer_stream_id)
{
LSQ_DEBUG("going away: drop frame for new stream %u",
LSQ_DEBUG("going away: reset new incoming stream %"PRIu32,
stream_frame->stream_id);
maybe_schedule_reset_for_stream(conn, stream_frame->stream_id);
lsquic_malo_put(stream_frame);
return parsed_len;
}
@ -1022,6 +1144,20 @@ process_stream_frame (struct full_conn *conn, lsquic_packet_in_t *packet_in,
return 0;
}
if (stream->id == LSQUIC_STREAM_HANDSHAKE
&& !(conn->fc_flags & FC_SERVER)
&& !(conn->fc_conn.cn_flags & LSCONN_HANDSHAKE_DONE))
{ /* To enable decryption, process handshake stream as soon as its
* data frames are received.
*
* TODO: this does not work when packets are reordered. A more
* flexible solution would defer packet decryption if handshake
* has not been completed yet. Nevertheless, this is good enough
* for now.
*/
dispatch_stream_read_events(conn, stream);
}
return parsed_len;
}
@ -1382,6 +1518,7 @@ process_packet_frame (struct full_conn *conn, lsquic_packet_in_t *packet_in,
{
enum QUIC_FRAME_TYPE type = conn->fc_conn.cn_pf->pf_parse_frame_type(p[0]);
packet_in->pi_frame_types |= 1 << type;
recent_packet_hist_frames(conn, 0, 1 << type);
return process_frames[type](conn, packet_in, p, len);
}
@ -1485,6 +1622,7 @@ static int
process_regular_packet (struct full_conn *conn, lsquic_packet_in_t *packet_in)
{
enum received_st st;
enum quic_ft_bit frame_types;
int was_missing;
reconstruct_packet_number(conn, packet_in);
@ -1515,11 +1653,11 @@ process_regular_packet (struct full_conn *conn, lsquic_packet_in_t *packet_in)
parse_regular_packet(conn, packet_in);
if (0 == (conn->fc_flags & FC_ACK_QUEUED))
{
frame_types = packet_in->pi_frame_types;
was_missing = packet_in->pi_packno !=
lsquic_rechist_largest_packno(&conn->fc_rechist);
conn->fc_n_slack_all += 1;
conn->fc_n_slack_akbl +=
!!(packet_in->pi_frame_types & QFRAME_ACKABLE_MASK);
conn->fc_n_slack_akbl += !!(frame_types & QFRAME_ACKABLE_MASK);
try_queueing_ack(conn, was_missing, packet_in->pi_received);
}
return 0;
@ -1545,6 +1683,7 @@ process_regular_packet (struct full_conn *conn, lsquic_packet_in_t *packet_in)
static int
process_incoming_packet (struct full_conn *conn, lsquic_packet_in_t *packet_in)
{
recent_packet_hist_new(conn, 0, packet_in->pi_received);
LSQ_DEBUG("Processing packet %"PRIu64, packet_in->pi_packno);
/* See flowchart in Section 4.1 of [draft-ietf-quic-transport-00]. We test
* for the common case first.
@ -1653,7 +1792,7 @@ generate_wuf_stream (struct full_conn *conn, lsquic_stream_t *stream)
ABORT_ERROR("gen_window_update_frame failed");
return 0;
}
packet_out->po_data_sz += sz;
lsquic_send_ctl_incr_pack_sz(&conn->fc_send_ctl, packet_out, sz);
packet_out->po_frame_types |= 1 << QUIC_FRAME_WINDOW_UPDATE;
LSQ_DEBUG("wrote WUF: stream %u; offset 0x%"PRIX64, stream->id, recv_off);
return 1;
@ -1675,7 +1814,7 @@ generate_wuf_conn (struct full_conn *conn)
ABORT_ERROR("gen_window_update_frame failed");
return;
}
packet_out->po_data_sz += sz;
lsquic_send_ctl_incr_pack_sz(&conn->fc_send_ctl, packet_out, sz);
packet_out->po_frame_types |= 1 << QUIC_FRAME_WINDOW_UPDATE;
conn->fc_flags &= ~FC_SEND_WUF;
LSQ_DEBUG("wrote connection WUF: offset 0x%"PRIX64, recv_off);
@ -1698,7 +1837,7 @@ generate_goaway_frame (struct full_conn *conn)
ABORT_ERROR("gen_goaway_frame failed");
return;
}
packet_out->po_data_sz += sz;
lsquic_send_ctl_incr_pack_sz(&conn->fc_send_ctl, packet_out, sz);
packet_out->po_frame_types |= 1 << QUIC_FRAME_GOAWAY;
conn->fc_flags &= ~FC_SEND_GOAWAY;
conn->fc_flags |= FC_GOAWAY_SENT;
@ -1726,7 +1865,7 @@ generate_connection_close_packet (struct full_conn *conn)
ABORT_ERROR("generate_connection_close_packet failed");
return;
}
packet_out->po_data_sz += sz;
lsquic_send_ctl_incr_pack_sz(&conn->fc_send_ctl, packet_out, sz);
packet_out->po_frame_types |= 1 << QUIC_FRAME_CONNECTION_CLOSE;
LSQ_DEBUG("generated CONNECTION_CLOSE frame in its own packet");
}
@ -1746,7 +1885,7 @@ generate_blocked_frame (struct full_conn *conn, uint32_t stream_id)
ABORT_ERROR("gen_blocked_frame failed");
return 0;
}
packet_out->po_data_sz += sz;
lsquic_send_ctl_incr_pack_sz(&conn->fc_send_ctl, packet_out, sz);
packet_out->po_frame_types |= 1 << QUIC_FRAME_BLOCKED;
LSQ_DEBUG("wrote blocked frame: stream %u", stream_id);
return 1;
@ -1772,8 +1911,6 @@ generate_rst_stream_frame (struct full_conn *conn, lsquic_stream_t *stream)
lsquic_packet_out_t *packet_out;
int sz, s;
assert(stream->n_unacked == 0);
packet_out = get_writeable_packet(conn, QUIC_RST_STREAM_SZ);
if (!packet_out)
return 0;
@ -1791,7 +1928,7 @@ generate_rst_stream_frame (struct full_conn *conn, lsquic_stream_t *stream)
ABORT_ERROR("gen_rst_frame failed");
return 0;
}
packet_out->po_data_sz += sz;
lsquic_send_ctl_incr_pack_sz(&conn->fc_send_ctl, packet_out, sz);
packet_out->po_frame_types |= 1 << QUIC_FRAME_RST_STREAM;
s = lsquic_packet_out_add_stream(packet_out, conn->fc_pub.mm, stream,
QUIC_FRAME_RST_STREAM, 0, 0);
@ -1823,7 +1960,7 @@ generate_ping_frame (struct full_conn *conn)
ABORT_ERROR("gen_blocked_frame failed");
return;
}
packet_out->po_data_sz += sz;
lsquic_send_ctl_incr_pack_sz(&conn->fc_send_ctl, packet_out, sz);
packet_out->po_frame_types |= 1 << QUIC_FRAME_PING;
LSQ_DEBUG("wrote PING frame");
}
@ -1870,7 +2007,7 @@ generate_stop_waiting_frame (struct full_conn *conn)
ABORT_ERROR("gen_stop_waiting_frame failed");
return;
}
packet_out->po_data_sz += sz;
lsquic_send_ctl_incr_pack_sz(&conn->fc_send_ctl, packet_out, sz);
packet_out->po_regen_sz += sz;
packet_out->po_frame_types |= 1 << QUIC_FRAME_STOP_WAITING;
conn->fc_flags &= ~FC_SEND_STOP_WAITING;
@ -1914,6 +2051,49 @@ process_streams_ready_to_send (struct full_conn *conn)
}
/* Return true if packetized, false otherwise */
static int
packetize_standalone_stream_reset (struct full_conn *conn, uint32_t stream_id)
{
lsquic_packet_out_t *packet_out;
int sz;
packet_out = get_writeable_packet(conn, QUIC_RST_STREAM_SZ);
if (!packet_out)
return 0;
sz = conn->fc_conn.cn_pf->pf_gen_rst_frame(
packet_out->po_data + packet_out->po_data_sz,
lsquic_packet_out_avail(packet_out), stream_id,
0, 0x10 /* QUIC_PEER_GOING_AWAY */);
if (sz < 0) {
ABORT_ERROR("gen_rst_frame failed");
return 0;
}
lsquic_send_ctl_incr_pack_sz(&conn->fc_send_ctl, packet_out, sz);
packet_out->po_frame_types |= 1 << QUIC_FRAME_RST_STREAM;
LSQ_DEBUG("generated standaloen RST_STREAM frame for stream %"PRIu32,
stream_id);
return 1;
}
static void
packetize_standalone_stream_resets (struct full_conn *conn)
{
struct stream_id_to_reset *sitr;
while ((sitr = STAILQ_FIRST(&conn->fc_stream_ids_to_reset)))
if (packetize_standalone_stream_reset(conn, sitr->sitr_stream_id))
{
STAILQ_REMOVE_HEAD(&conn->fc_stream_ids_to_reset, sitr_next);
free(sitr);
}
else
break;
}
static void
service_streams (struct full_conn *conn)
{
@ -2040,7 +2220,7 @@ process_streams_write_events (struct full_conn *conn, int high_prio)
else
lsquic_spi_drop_high(&spi);
for (stream = lsquic_spi_first(&spi); stream;
for (stream = lsquic_spi_first(&spi); stream && write_is_possible(conn);
stream = lsquic_spi_next(&spi))
lsquic_stream_dispatch_write_events(stream);
@ -2145,7 +2325,7 @@ generate_ack_frame (struct full_conn *conn)
verify_ack_frame(conn, packet_out->po_data + packet_out->po_data_sz, w);
lsquic_send_ctl_scheduled_ack(&conn->fc_send_ctl);
packet_out->po_frame_types |= 1 << QUIC_FRAME_ACK;
packet_out->po_data_sz += w;
lsquic_send_ctl_incr_pack_sz(&conn->fc_send_ctl, packet_out, w);
packet_out->po_regen_sz += w;
if (has_missing)
conn->fc_flags |= FC_ACK_HAD_MISS;
@ -2236,7 +2416,7 @@ immediate_close (struct full_conn *conn)
LSQ_WARN("%s failed", __func__);
return TICK_CLOSE;
}
packet_out->po_data_sz += sz;
lsquic_send_ctl_incr_pack_sz(&conn->fc_send_ctl, packet_out, sz);
packet_out->po_frame_types |= 1 << QUIC_FRAME_CONNECTION_CLOSE;
LSQ_DEBUG("generated CONNECTION_CLOSE frame in its own packet");
return TICK_SEND|TICK_CLOSE;
@ -2323,6 +2503,9 @@ full_conn_ci_tick (lsquic_conn_t *lconn, lsquic_time_t now)
progress_tick |= progress_made << TICK_BIT_PROGRESS;
CLOSE_IF_NECESSARY();
if (lsquic_send_ctl_pacer_blocked(&conn->fc_send_ctl))
goto skip_write;
if (conn->fc_flags & FC_FIRST_TICK)
{
conn->fc_flags &= ~FC_FIRST_TICK;
@ -2335,7 +2518,7 @@ full_conn_ci_tick (lsquic_conn_t *lconn, lsquic_time_t now)
* we sometimes add is a packet with an ACK frame, and we add it
* to the *front* of the queue.
*/
have_delayed_packets = lsquic_send_ctl_squeeze_sched(
have_delayed_packets = lsquic_send_ctl_maybe_squeeze_sched(
&conn->fc_send_ctl);
if ((conn->fc_flags & FC_ACK_QUEUED) ||
@ -2383,8 +2566,6 @@ full_conn_ci_tick (lsquic_conn_t *lconn, lsquic_time_t now)
goto end;
}
RETURN_IF_OUT_OF_PACKETS();
/* Try to fit any of the following three frames -- STOP_WAITING,
* WINDOW_UPDATE, and GOAWAY -- before checking if we have run
* out of packets. If either of them does not fit, it will be
@ -2424,6 +2605,12 @@ full_conn_ci_tick (lsquic_conn_t *lconn, lsquic_time_t now)
RETURN_IF_OUT_OF_PACKETS();
}
if (!STAILQ_EMPTY(&conn->fc_stream_ids_to_reset))
{
packetize_standalone_stream_resets(conn);
CLOSE_IF_NECESSARY();
}
if (!TAILQ_EMPTY(&conn->fc_pub.sending_streams))
{
process_streams_ready_to_send(conn);
@ -2464,6 +2651,7 @@ full_conn_ci_tick (lsquic_conn_t *lconn, lsquic_time_t now)
progress_made = (n_sched < lsquic_send_ctl_n_scheduled(&conn->fc_send_ctl));
progress_tick |= progress_made << TICK_BIT_PROGRESS;
skip_write:
service_streams(conn);
CLOSE_IF_NECESSARY();
@ -2559,6 +2747,9 @@ full_conn_ci_packet_sent (lsquic_conn_t *lconn, lsquic_packet_out_t *packet_out)
struct full_conn *conn = (struct full_conn *) lconn;
int s;
recent_packet_hist_new(conn, 1, packet_out->po_sent);
recent_packet_hist_frames(conn, 1, packet_out->po_frame_types);
if (packet_out->po_frame_types & QFRAME_RETRANSMITTABLE_MASK)
{
conn->fc_n_cons_unretx = 0;
@ -2567,7 +2758,7 @@ full_conn_ci_packet_sent (lsquic_conn_t *lconn, lsquic_packet_out_t *packet_out)
}
else
++conn->fc_n_cons_unretx;
s = lsquic_send_ctl_sent_packet(&conn->fc_send_ctl, packet_out);
s = lsquic_send_ctl_sent_packet(&conn->fc_send_ctl, packet_out, 1);
if (s != 0)
ABORT_ERROR("sent packet failed: %s", strerror(errno));
#if FULL_CONN_STATS
@ -2585,7 +2776,7 @@ full_conn_ci_packet_not_sent (lsquic_conn_t *lconn, lsquic_packet_out_t *packet_
static void
full_conn_ci_handshake_done (lsquic_conn_t *lconn)
full_conn_ci_handshake_ok (lsquic_conn_t *lconn)
{
struct full_conn *conn = (struct full_conn *) lconn;
LSQ_DEBUG("handshake reportedly done");
@ -2597,6 +2788,16 @@ full_conn_ci_handshake_done (lsquic_conn_t *lconn)
}
static void
full_conn_ci_handshake_failed (lsquic_conn_t *lconn)
{
struct full_conn *conn = (struct full_conn *) lconn;
LSQ_DEBUG("handshake failed");
lsquic_alarmset_unset(&conn->fc_alset, AL_HANDSHAKE);
conn->fc_flags |= FC_HSK_FAILED;
}
static int
full_conn_ci_user_wants_read (lsquic_conn_t *lconn)
{
@ -2615,7 +2816,7 @@ lsquic_conn_abort (lsquic_conn_t *lconn)
void
full_conn_close_internal (lsquic_conn_t *lconn, int is_user)
lsquic_conn_close (lsquic_conn_t *lconn)
{
struct full_conn *conn = (struct full_conn *) lconn;
lsquic_stream_t *stream;
@ -2623,8 +2824,6 @@ full_conn_close_internal (lsquic_conn_t *lconn, int is_user)
if (!(conn->fc_flags & FC_CLOSING))
{
if (is_user)
LSQ_INFO("User closed connection");
for (el = lsquic_hash_first(conn->fc_pub.all_streams); el;
el = lsquic_hash_next(conn->fc_pub.all_streams))
{
@ -2638,13 +2837,6 @@ full_conn_close_internal (lsquic_conn_t *lconn, int is_user)
}
void
lsquic_conn_close (lsquic_conn_t *lconn)
{
full_conn_close_internal(lconn, 1);
}
void
lsquic_conn_going_away (lsquic_conn_t *lconn)
{
@ -2707,8 +2899,8 @@ find_stream_on_non_stream_frame (struct full_conn *conn, uint32_t stream_id,
if ((conn->fc_flags & FC_GOING_AWAY) &&
stream_id > conn->fc_max_peer_stream_id)
{
LSQ_DEBUG("going away: drop headers for new stream %u",
stream_id);
maybe_schedule_reset_for_stream(conn, stream_id);
LSQ_DEBUG("going away: reset new incoming stream %u", stream_id);
return NULL;
}
@ -2882,6 +3074,63 @@ lsquic_conn_get_ctx (const lsquic_conn_t *lconn)
}
void lsquic_conn_set_ctx (lsquic_conn_t *lconn, lsquic_conn_ctx_t *ctx)
{
struct full_conn *const conn = (struct full_conn *) lconn;
conn->fc_conn_ctx = ctx;
}
enum LSQUIC_CONN_STATUS
lsquic_conn_status (lsquic_conn_t *lconn, char *errbuf, size_t bufsz)
{
struct full_conn *const conn = (struct full_conn *) lconn;
size_t n;
/* Test the common case first: */
if (!(conn->fc_flags & (FC_ERROR
|FC_TIMED_OUT
|FC_ABORTED
|FC_GOT_PRST
|FC_HSK_FAILED
|FC_CLOSING
|FC_GOING_AWAY)))
{
if (lconn->cn_flags & LSCONN_HANDSHAKE_DONE)
return LSCONN_ST_CONNECTED;
else
return LSCONN_ST_HSK_IN_PROGRESS;
}
if (errbuf && bufsz)
{
if (conn->fc_errmsg)
{
n = bufsz < MAX_ERRMSG ? bufsz : MAX_ERRMSG;
strncpy(errbuf, conn->fc_errmsg, n);
errbuf[n - 1] = '\0';
}
else
errbuf[0] = '\0';
}
if (conn->fc_flags & FC_ERROR)
return LSCONN_ST_ERROR;
if (conn->fc_flags & FC_TIMED_OUT)
return LSCONN_ST_TIMED_OUT;
if (conn->fc_flags & FC_ABORTED)
return LSCONN_ST_USER_ABORTED;
if (conn->fc_flags & FC_GOT_PRST)
return LSCONN_ST_RESET;
if (conn->fc_flags & FC_HSK_FAILED)
return LSCONN_ST_HSK_FAILURE;
if (conn->fc_flags & FC_CLOSING)
return LSCONN_ST_CLOSED;
assert(conn->fc_flags & FC_GOING_AWAY);
return LSCONN_ST_GOING_AWAY;
}
static const struct headers_stream_callbacks headers_callbacks =
{
.hsc_on_headers = headers_stream_on_incoming_headers,
@ -2896,7 +3145,8 @@ static const struct headers_stream_callbacks headers_callbacks =
static const struct conn_iface full_conn_iface = {
.ci_destroy = full_conn_ci_destroy,
.ci_handshake_done = full_conn_ci_handshake_done,
.ci_handshake_failed = full_conn_ci_handshake_failed,
.ci_handshake_ok = full_conn_ci_handshake_ok,
.ci_next_packet_to_send = full_conn_ci_next_packet_to_send,
.ci_packet_in = full_conn_ci_packet_in,
.ci_packet_not_sent = full_conn_ci_packet_not_sent,

View file

@ -14,6 +14,6 @@ full_conn_client_new (struct lsquic_engine_public *,
const char *hostname, unsigned short max_packet_size);
void
full_conn_close_internal (lsquic_conn_t *, int is_user);
full_conn_client_call_on_new (struct lsquic_conn *);
#endif

View file

@ -68,6 +68,7 @@ typedef struct hs_ctx_st
HSET_TCID = (1 << 0), /* tcid is set */
HSET_SMHL = (1 << 1), /* smhl is set */
HSET_SCID = (1 << 2),
HSET_IRTT = (1 << 3),
} set;
enum {
HOPT_NSTP = (1 << 0), /* NSTP option present in COPT */
@ -302,7 +303,7 @@ make_cert_hash_item (lsquic_str_t *domain, lsquic_str_t **certs, int count)
/* client */
void
static void
c_free_cert_hash_item (cert_hash_item_t *item)
{
int i;
@ -572,7 +573,9 @@ static int parse_hs_data (lsquic_enc_session_t *enc_session, uint32_t tag,
break;
case QTAG_IRTT:
hs_ctx->irtt = get_tag_value_i32(val, len);
if (0 != get_tag_val_u32(val, len, &hs_ctx->irtt))
return -1;
hs_ctx->set |= HSET_IRTT;
break;
case QTAG_COPT:
@ -1701,6 +1704,14 @@ lsquic_enc_session_get_peer_setting (const lsquic_enc_session_t *enc_session,
}
else
return -1;
case QTAG_IRTT:
if (enc_session->hs_ctx.set & HSET_IRTT)
{
*val = enc_session->hs_ctx.irtt;
return 0;
}
else
return -1;
}
/* XXX For the following values, there is no record which were present

View file

@ -126,7 +126,11 @@ print_timestamp (void)
struct timeval tv;
gettimeofday(&tv, NULL);
localtime_r(&tv.tv_sec, &tm);
if (g_llts == LLTS_YYYYMMDD_HHMMSSMS)
if (g_llts == LLTS_YYYYMMDD_HHMMSSUS)
lsquic_printf("%04d-%02d-%02d %02d:%02d:%02d.%06d ",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec, (int) (tv.tv_usec));
else if (g_llts == LLTS_YYYYMMDD_HHMMSSMS)
lsquic_printf("%04d-%02d-%02d %02d:%02d:%02d.%03d ",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec, (int) (tv.tv_usec / 1000));

View file

@ -41,6 +41,15 @@ pacer_init (struct pacer *pacer, lsquic_cid_t cid, unsigned max_intertick)
}
void
pacer_cleanup (struct pacer *pacer)
{
#ifndef NDEBUG
LSQ_NOTICE("scheduled calls: %u", pacer->pa_stats.n_scheduled);
#endif
}
void
pacer_packet_scheduled (struct pacer *pacer, unsigned n_in_flight,
int in_recovery, tx_time_f tx_time, void *tx_ctx)
@ -48,6 +57,10 @@ pacer_packet_scheduled (struct pacer *pacer, unsigned n_in_flight,
lsquic_time_t delay, sched_time;
int app_limited, making_up;
#ifndef NDEBUG
++pacer->pa_stats.n_scheduled;
#endif
if (n_in_flight == 0 && !in_recovery)
{
pacer->pa_burst_tokens = 10;
@ -100,12 +113,7 @@ pacer_loss_event (struct pacer *pacer)
static unsigned
clock_granularity (const struct pacer *pacer)
{
#ifndef NDEBUG
if (pacer->pa_flags & PA_CONSTANT_INTERTICK)
return pacer->pa_max_intertick;
#endif
return pacer->pa_intertick_var;
return pacer->pa_intertick_avg;
}

View file

@ -28,6 +28,11 @@ struct pacer
PA_CONSTANT_INTERTICK = (1 << 1), /* Use fake intertick time for testing */
#endif
} pa_flags:8;
#ifndef NDEBUG
struct {
unsigned n_scheduled;
} pa_stats;
#endif
};
@ -36,6 +41,9 @@ typedef lsquic_time_t (*tx_time_f)(void *ctx);
void
pacer_init (struct pacer *, lsquic_cid_t, unsigned max_intertick);
void
pacer_cleanup (struct pacer *);
void
pacer_tick (struct pacer *, lsquic_time_t);

View file

@ -45,7 +45,8 @@ const size_t lsquic_frame_types_str_sz =
const char *
lsquic_frame_types_to_str (char *buf, size_t bufsz, short frame_types)
lsquic_frame_types_to_str (char *buf, size_t bufsz,
enum quic_ft_bit frame_types)
{
char *p;
int i, w;

View file

@ -36,10 +36,24 @@ enum QUIC_FRAME_TYPE
N_QUIC_FRAMES
};
enum quic_ft_bit {
QUIC_FTBIT_INVALID = 1 << QUIC_FRAME_INVALID,
QUIC_FTBIT_STREAM = 1 << QUIC_FRAME_STREAM,
QUIC_FTBIT_ACK = 1 << QUIC_FRAME_ACK,
QUIC_FTBIT_PADDING = 1 << QUIC_FRAME_PADDING,
QUIC_FTBIT_RST_STREAM = 1 << QUIC_FRAME_RST_STREAM,
QUIC_FTBIT_CONNECTION_CLOSE = 1 << QUIC_FRAME_CONNECTION_CLOSE,
QUIC_FTBIT_GOAWAY = 1 << QUIC_FRAME_GOAWAY,
QUIC_FTBIT_WINDOW_UPDATE = 1 << QUIC_FRAME_WINDOW_UPDATE,
QUIC_FTBIT_BLOCKED = 1 << QUIC_FRAME_BLOCKED,
QUIC_FTBIT_STOP_WAITING = 1 << QUIC_FRAME_STOP_WAITING,
QUIC_FTBIT_PING = 1 << QUIC_FRAME_PING,
};
extern const size_t lsquic_frame_types_str_sz;
const char *
lsquic_frame_types_to_str (char *buf, size_t bufsz, short frame_types);
lsquic_frame_types_to_str (char *buf, size_t bufsz, enum quic_ft_bit);
#define QFRAME_REGEN_MASK ((1 << QUIC_FRAME_ACK) \
| (1 << QUIC_FRAME_STOP_WAITING))

View file

@ -5,6 +5,7 @@
#include "lsquic_int_types.h"
#include "lsquic_types.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_in.h"

View file

@ -48,7 +48,7 @@ typedef struct lsquic_packet_in
* list.
*/
unsigned short pi_refcnt;
short pi_frame_types;
enum quic_ft_bit pi_frame_types:16;
unsigned short pi_hsk_stream; /* Offset to handshake stream
* frame, only valid if
* PI_HSK_STREAM is set.

View file

@ -128,6 +128,8 @@ lsquic_packet_out_add_stream (lsquic_packet_out_t *packet_out,
int last_taken;
unsigned i;
assert(!(new_stream->stream_flags & STREAM_FINISHED));
for (srec = posi_first(&posi, packet_out); srec; srec = posi_next(&posi))
if (srec->sr_stream == new_stream)
{
@ -150,8 +152,6 @@ lsquic_packet_out_add_stream (lsquic_packet_out_t *packet_out,
else if (srec->sr_frame_types & (1 << QUIC_FRAME_STREAM) & (1 << frame_type))
assert(srec->sr_off < off); /* Check that STREAM frames are added in order */
++new_stream->n_unacked;
if (!(packet_out->po_flags & PO_SREC_ARR))
{
if (!srec_taken(&packet_out->po_srecs.one))
@ -160,6 +160,7 @@ lsquic_packet_out_add_stream (lsquic_packet_out_t *packet_out,
packet_out->po_srecs.one.sr_stream = new_stream;
packet_out->po_srecs.one.sr_off = off;
packet_out->po_srecs.one.sr_len = len;
++new_stream->n_unacked;
return 0; /* Insert in first slot */
}
srec_arr = lsquic_malo_get(mm->malo.stream_rec_arr);
@ -190,6 +191,7 @@ lsquic_packet_out_add_stream (lsquic_packet_out_t *packet_out,
srec_arr->srecs[i].sr_stream = new_stream;
srec_arr->srecs[i].sr_off = off;
srec_arr->srecs[i].sr_len = len;
++new_stream->n_unacked;
return 0; /* Insert in existing srec */
}
@ -203,6 +205,7 @@ lsquic_packet_out_add_stream (lsquic_packet_out_t *packet_out,
srec_arr->srecs[0].sr_off = off;
srec_arr->srecs[0].sr_len = len;
TAILQ_INSERT_TAIL(&packet_out->po_srecs.arr, srec_arr, next_stream_rec_arr);
++new_stream->n_unacked;
return 0; /* Insert in new srec */
}
@ -283,7 +286,7 @@ lsquic_packet_out_destroy (lsquic_packet_out_t *packet_out,
/* If `stream_id' is zero, stream frames from all reset streams are elided.
* Otherwise, elision is limited to the specified stream.
*/
void
unsigned
lsquic_packet_out_elide_reset_stream_frames (lsquic_packet_out_t *packet_out,
uint32_t stream_id)
{
@ -335,6 +338,8 @@ lsquic_packet_out_elide_reset_stream_frames (lsquic_packet_out_t *packet_out,
assert(n_stream_frames);
if (n_elided == n_stream_frames)
packet_out->po_frame_types &= ~(1 << QUIC_FRAME_STREAM);
return adj;
}
@ -532,8 +537,8 @@ split_largest_frame (struct lsquic_mm *mm, lsquic_packet_out_t *packet_out,
new_packet_out->po_data + new_packet_out->po_data_sz,
lsquic_packet_out_avail(new_packet_out), frame.stream_id,
frame.data_frame.df_offset + frame.data_frame.df_size / 2,
split_reader_fin, split_reader_size, split_reader_read,
&reader_ctx);
split_reader_fin(&reader_ctx), split_reader_size(&reader_ctx),
split_reader_read, &reader_ctx);
if (len < 0)
{
LSQ_ERROR("could not generate new frame 1");
@ -544,6 +549,11 @@ split_largest_frame (struct lsquic_mm *mm, lsquic_packet_out_t *packet_out,
new_packet_out->po_data_sz, len))
return -1;
new_packet_out->po_data_sz += len;
if (0 == lsquic_packet_out_avail(new_packet_out))
{
assert(0); /* We really should not fill here, but JIC */
new_packet_out->po_flags |= PO_STREAM_END;
}
memcpy(reader_ctx.buf, frame.data_frame.df_data,
frame.data_frame.df_size / 2);
@ -553,8 +563,8 @@ split_largest_frame (struct lsquic_mm *mm, lsquic_packet_out_t *packet_out,
len = pf->pf_gen_stream_frame(
packet_out->po_data + max_srec->sr_off, max_srec->sr_len,
frame.stream_id, frame.data_frame.df_offset,
split_reader_fin, split_reader_size, split_reader_read,
&reader_ctx);
split_reader_fin(&reader_ctx), split_reader_size(&reader_ctx),
split_reader_read, &reader_ctx);
if (len < 0)
{
LSQ_ERROR("could not generate new frame 2");
@ -611,11 +621,15 @@ lsquic_packet_out_split_in_two (struct lsquic_mm *mm,
#endif
int rv;
/* We only split buffered packets; buffered packets contain only STREAM
* frames:
*/
assert(packet_out->po_frame_types == (1 << QUIC_FRAME_STREAM));
n_srecs = 0;
for (srec = posi_first(&posi, packet_out); srec; srec = posi_next(&posi))
{
/* We only expect references to STREAM frames (buffered packets): */
assert(srec->sr_frame_types == (1 << QUIC_FRAME_STREAM));
if (n_srecs >= n_srecs_alloced)
{
@ -688,13 +702,14 @@ lsquic_packet_out_split_in_two (struct lsquic_mm *mm,
end:
if (srecs != local_arr)
free(srecs);
#ifndef NDEBUG
if (0 == rv)
{
new_packet_out->po_frame_types |= 1 << QUIC_FRAME_STREAM;
#ifndef NDEBUG
verify_srecs(packet_out);
verify_srecs(new_packet_out);
}
#endif
}
return rv;
err:

View file

@ -39,7 +39,7 @@ struct stream_rec {
struct lsquic_stream *sr_stream;
unsigned short sr_off,
sr_len;
short sr_frame_types;
enum quic_ft_bit sr_frame_types:16;
};
#define srec_taken(srec) ((srec)->sr_frame_types)
@ -65,6 +65,36 @@ typedef struct lsquic_packet_out
lsquic_time_t po_sent; /* Time sent */
lsquic_packno_t po_packno;
enum packet_out_flags {
PO_HELLO = (1 << 1), /* Packet contains SHLO or CHLO data */
PO_ENCRYPTED= (1 << 3), /* po_enc_data has encrypted data */
PO_SREC_ARR = (1 << 4),
#define POBIT_SHIFT 5
PO_BITS_0 = (1 << 5), /* PO_BITS_0 and PO_BITS_1 encode the */
PO_BITS_1 = (1 << 6), /* packet number length. See macros below. */
PO_NONCE = (1 << 7), /* Use value in `po_nonce' to generate header */
PO_VERSION = (1 << 8), /* Use value in `po_ver_tag' to generate header */
PO_CONN_ID = (1 << 9), /* Include connection ID in public header */
PO_REPACKNO = (1 <<10), /* Regenerate packet number */
PO_NOENCRYPT= (1 <<11), /* Do not encrypt data in po_data */
PO_VERNEG = (1 <<12), /* Version negotiation packet. */
PO_STREAM_END
= (1 <<13), /* STREAM frame reaches the end of the packet: no
* further writes are allowed.
*/
PO_SCHED = (1 <<14), /* On scheduled queue */
} 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_regen_sz; /* Number of bytes at the beginning
* of data containing bytes that are
* not to be retransmitted, e.g. ACK
* frames.
*/
unsigned short po_n_alloc; /* Total number of bytes allocated in po_data */
unsigned char *po_data;
/* A lot of packets contain data belonging to only one stream. Thus,
* `one' is used first. If this is not enough, any number of
* stream_rec_arr structures can be allocated to handle more stream
@ -81,31 +111,7 @@ typedef struct lsquic_packet_out
unsigned char *po_enc_data;
lsquic_ver_tag_t po_ver_tag; /* Set if PO_VERSION is set */
short po_frame_types; /* 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_regen_sz; /* Number of bytes at the beginning
* of data containing bytes that are
* not to be retransmitted, e.g. ACK
* frames.
*/
unsigned short po_n_alloc; /* Total number of bytes allocated in po_data */
enum packet_out_flags {
PO_HELLO = (1 << 1), /* Packet contains SHLO or CHLO data */
PO_ENCRYPTED= (1 << 3), /* po_enc_data has encrypted data */
PO_SREC_ARR = (1 << 4),
#define POBIT_SHIFT 5
PO_BITS_0 = (1 << 5), /* PO_BITS_0 and PO_BITS_1 encode the */
PO_BITS_1 = (1 << 6), /* packet number length. See macros below. */
PO_NONCE = (1 << 7), /* Use value in `po_nonce' to generate header */
PO_VERSION = (1 << 8), /* Use value in `po_ver_tag' to generate header */
PO_CONN_ID = (1 << 9), /* Include connection ID in public header */
PO_REPACKNO = (1 <<10), /* Regenerate packet number */
PO_NOENCRYPT= (1 <<11), /* Do not encrypt data in po_data */
PO_VERNEG = (1 <<12), /* Version negotiation packet. */
} po_flags:16;
unsigned char *po_nonce; /* Use to generate header if PO_NONCE is set */
unsigned char *po_data;
} lsquic_packet_out_t;
/* The size of lsquic_packet_out_t could be further reduced:
@ -119,7 +125,11 @@ typedef struct lsquic_packet_out
#define lsquic_packet_out_packno_bits(p) (((p)->po_flags >> POBIT_SHIFT) & 0x3)
/* XXX This will need to be made into a method for Q041 */
#define lsquic_packet_out_set_packno_bits(p, b) do { \
(p)->po_flags &= ~(0x3 << POBIT_SHIFT); \
(p)->po_flags |= ((b) & 0x3) << POBIT_SHIFT; \
} while (0)
#define lsquic_po_header_length(po_flags) ( \
1 /* Type */ \
+ (!!((po_flags) & PO_CONN_ID) << 3) /* Connection ID */ \
@ -128,6 +138,10 @@ typedef struct lsquic_packet_out
+ packno_bits2len(((po_flags) >> POBIT_SHIFT) & 0x3) /* Packet number */ \
)
#define lsquic_packet_out_total_sz(p) \
((p)->po_data_sz + lsquic_po_header_length((p)->po_flags) \
+ QUIC_PACKET_HASH_SZ)
#define lsquic_packet_out_verneg(p) \
(((p)->po_flags & (PO_NOENCRYPT|PO_VERNEG)) == (PO_NOENCRYPT|PO_VERNEG))
@ -163,7 +177,7 @@ lsquic_packet_out_add_stream (lsquic_packet_out_t *packet_out,
enum QUIC_FRAME_TYPE,
unsigned short off, unsigned short len);
void
unsigned
lsquic_packet_out_elide_reset_stream_frames (lsquic_packet_out_t *, uint32_t);
int

View file

@ -17,9 +17,7 @@ typedef struct ack_info
unsigned n_ranges; /* This is at least 1 */
/* Largest acked is ack_info.ranges[0].high */
lsquic_time_t lack_delta;
struct {
lsquic_packno_t high, low;
} ranges[256];
struct lsquic_packno_range ranges[256];
#if LSQUIC_PARSE_ACK_TIMESTAMPS
struct {
/* Currently we just read these timestamps in (assuming it is
@ -47,8 +45,6 @@ typedef lsquic_time_t
(*gaf_rechist_largest_recv_f) (void *rechist);
/* gsf_: generate stream frame */
typedef int (*gsf_fin_f) (void *stream);
typedef size_t (*gsf_size_f) (void *stream);
typedef size_t (*gsf_read_f) (void *stream, void *buf, size_t len, int *fin);
struct packin_parse_state {
@ -81,7 +77,7 @@ struct parse_funcs
int
(*pf_gen_stream_frame) (unsigned char *buf, size_t bufsz,
uint32_t stream_id, uint64_t offset,
gsf_fin_f, gsf_size_f, gsf_read_f, void *stream);
int fin, size_t size, gsf_read_f, void *stream);
unsigned
(*pf_parse_stream_frame_header_sz) (unsigned char type);
int

View file

@ -164,13 +164,12 @@ gquic_ietf_calc_stream_frame_header_sz (uint32_t stream_id, uint64_t offset)
int
gquic_ietf_gen_stream_frame (unsigned char *buf, size_t buf_len, uint32_t stream_id,
uint64_t offset, gsf_fin_f gsf_fin, gsf_size_f gsf_size,
uint64_t offset, int fin, size_t size,
gsf_read_f gsf_read, void *stream)
{
/* 11FSSOOD */
unsigned slen, olen, dlen;
unsigned char *p = buf + 1;
int fin;
/* SS: Stream ID length: 1, 2, 3, or 4 bytes */
slen = (stream_id > 0x0000FF)
@ -184,13 +183,11 @@ gquic_ietf_gen_stream_frame (unsigned char *buf, size_t buf_len, uint32_t stream
+ ((offset > 1) << 1)
;
fin = gsf_fin(stream);
if (!fin)
{
unsigned size, n_avail;
unsigned n_avail;
uint16_t nr;
size = gsf_size(stream);
n_avail = buf_len - (p + slen + olen - buf);
/* If we cannot fill remaining buffer, we need to include data

View file

@ -128,40 +128,55 @@ gquic_be_gen_reg_pkt_header (unsigned char *buf, size_t bufsz, const lsquic_cid_
packnum_len = packno_bits2len(bits);
header_len = 1 + (!!conn_id << 3) + (!!ver << 2) + ((!!nonce) << 5)
+ packnum_len;
if (header_len > bufsz)
if (!(conn_id || ver || nonce))
{
errno = ENOBUFS;
return -1;
header_len = 1 + packnum_len;
if (header_len > bufsz)
{
errno = ENOBUFS;
return -1;
}
p = buf;
*p = bits << 4;
++p;
}
else
{
header_len = 1 + (!!conn_id << 3) + (!!ver << 2) + ((!!nonce) << 5)
+ packnum_len;
if (header_len > bufsz)
{
errno = ENOBUFS;
return -1;
}
p = buf;
*p = (!!conn_id << 3)
| (bits << 4)
| ((!!nonce) << 2)
| !!ver;
++p;
if (conn_id)
{
memcpy(p, conn_id , sizeof(*conn_id));
p += sizeof(*conn_id);
}
if (ver)
{
memcpy(p, ver, 4);
p += 4;
}
if (nonce)
{
memcpy(p, nonce , 32);
p += 32;
}
}
p = buf;
*p = (!!conn_id << 3)
| (bits << 4)
| ((!!nonce) << 2)
| !!ver;
++p;
if (conn_id)
{
memcpy(p, conn_id , sizeof(*conn_id));
p += sizeof(*conn_id);
}
if (ver)
{
memcpy(p, ver, 4);
p += 4;
}
if (nonce)
{
memcpy(p, nonce , 32);
p += 32;
}
#if __BYTE_ORDER == __LITTLE_ENDIAN
packno = bswap_64(packno);
#endif
@ -176,13 +191,12 @@ gquic_be_gen_reg_pkt_header (unsigned char *buf, size_t bufsz, const lsquic_cid_
int
gquic_be_gen_stream_frame (unsigned char *buf, size_t buf_len, uint32_t stream_id,
uint64_t offset, gsf_fin_f gsf_fin, gsf_size_f gsf_size,
uint64_t offset, int fin, size_t size,
gsf_read_f gsf_read, void *stream)
{
/* 1fdoooss */
unsigned slen, olen, dlen;
unsigned char *p = buf + 1;
int fin;
/* ss: Stream ID length: 1, 2, 3, or 4 bytes */
slen = (stream_id > 0x0000FF)
@ -199,13 +213,11 @@ gquic_be_gen_stream_frame (unsigned char *buf, size_t buf_len, uint32_t stream_i
+ (offset >= (1ULL << 16))
+ ((offset > 0) << 1);
fin = gsf_fin(stream);
if (!fin)
{
unsigned size, n_avail;
unsigned n_avail;
uint16_t nr;
size = gsf_size(stream);
n_avail = buf_len - (p + slen + olen - buf);
/* If we cannot fill remaining buffer, we need to include data
@ -371,11 +383,51 @@ gquic_be_parse_ack_high (const unsigned char *buf, size_t buf_len)
}
/* Return parsed (used) buffer length.
* If parsing failed, negative value is returned.
*/
int
gquic_be_parse_ack_frame (const unsigned char *buf, size_t buf_len, ack_info_t *ack)
static int
parse_ack_frame_without_blocks (const unsigned char *buf, size_t buf_len,
ack_info_t *ack)
{
/* 01nullmm */
lsquic_packno_t tmp_packno;
const unsigned char type = buf[0];
const unsigned char *p = buf + 1;
const unsigned char *const pend = buf + buf_len;
const int ack_block_len = twobit_to_1246(type & 3); /* mm */
const int largest_obs_len = twobit_to_1246((type >> 2) & 3); /* ll */
CHECK_SPACE(largest_obs_len + 2 + ack_block_len + 1, p, pend);
READ_UINT(ack->ranges[0].high, 64, p, largest_obs_len);
p += largest_obs_len;
ack->lack_delta = gquic_be_read_float_time16(p);
p += 2;
READ_UINT(tmp_packno, 64, p, ack_block_len);
ack->ranges[0].low = ack->ranges[0].high - tmp_packno + 1;
p += ack_block_len;
ack->n_ranges = 1;
ack->n_timestamps = *p;
++p;
if (ack->n_timestamps)
{
unsigned timestamps_size = 5 + 3 * (ack->n_timestamps - 1);
CHECK_SPACE(timestamps_size, p, pend);
p += timestamps_size;
}
assert(p <= pend);
return p - (unsigned char *) buf;
}
static int
parse_ack_frame_with_blocks (const unsigned char *buf, size_t buf_len, ack_info_t *ack)
{
/* 01nullmm */
lsquic_packno_t tmp_packno;
@ -388,54 +440,42 @@ gquic_be_parse_ack_frame (const unsigned char *buf, size_t buf_len, ack_info_t *
const int ack_block_len = twobit_to_1246(type & 3); /* mm */
const int largest_obs_len = twobit_to_1246((type >> 2) & 3); /* ll */
CHECK_SPACE(largest_obs_len, p , pend);
CHECK_SPACE(largest_obs_len + 2 + 1 + ack_block_len, p, pend);
READ_UINT(ack->ranges[0].high, 64, p, largest_obs_len);
p += largest_obs_len;
CHECK_SPACE(2, p , pend);
ack->lack_delta = gquic_be_read_float_time16(p);
p += 2;
unsigned n_blocks;
if (type & 0x20)
{
CHECK_SPACE(1, p , pend);
n_blocks = *p;
++p;
}
else
n_blocks = 0;
CHECK_SPACE(1, p , pend);
n_blocks = *p;
++p;
CHECK_SPACE(ack_block_len, p , pend);
READ_UINT(tmp_packno, 64, p, ack_block_len);
ack->ranges[0].low = ack->ranges[0].high - tmp_packno + 1;
p += ack_block_len;
if (n_blocks)
CHECK_SPACE((ack_block_len + 1) * n_blocks + /* timestamp count: */ 1,
p , pend);
unsigned i, n, gap;
for (i = 0, n = 1, gap = 0; i < n_blocks; ++i)
{
CHECK_SPACE((ack_block_len + 1) * n_blocks, p , pend);
unsigned i, n, gap;
for (i = 0, n = 1, gap = 0; i < n_blocks; ++i)
uint64_t length;
gap += *p;
READ_UINT(length, 64, p + 1, ack_block_len);
p += 1 + ack_block_len;
if (length)
{
uint64_t length;
gap += *p;
READ_UINT(length, 64, p + 1, ack_block_len);
p += 1 + ack_block_len;
if (length)
{
ack->ranges[n].high = ack->ranges[n - 1].low - gap - 1;
ack->ranges[n].low = ack->ranges[n].high - length + 1;
++n;
gap = 0;
}
ack->ranges[n].high = ack->ranges[n - 1].low - gap - 1;
ack->ranges[n].low = ack->ranges[n].high - length + 1;
++n;
gap = 0;
}
ack->n_ranges = n;
}
else
ack->n_ranges = 1;
ack->n_ranges = n;
CHECK_SPACE(1, p , pend);
ack->n_timestamps = *p;
++p;
@ -469,6 +509,19 @@ gquic_be_parse_ack_frame (const unsigned char *buf, size_t buf_len, ack_info_t *
}
/* Return parsed (used) buffer length.
* If parsing failed, negative value is returned.
*/
int
gquic_be_parse_ack_frame (const unsigned char *buf, size_t buf_len, ack_info_t *ack)
{
if (!(buf[0] & 0x20))
return parse_ack_frame_without_blocks(buf, buf_len, ack);
else
return parse_ack_frame_with_blocks(buf, buf_len, ack);
}
int
gquic_be_gen_stop_waiting_frame(unsigned char *buf, size_t buf_len,
lsquic_packno_t cur_packno, enum lsquic_packno_bits bits,

View file

@ -61,7 +61,7 @@ gquic_be_gen_reg_pkt_header (unsigned char *buf, size_t bufsz, const lsquic_cid_
int
gquic_be_gen_stream_frame (unsigned char *buf, size_t buf_len, uint32_t stream_id,
uint64_t offset, gsf_fin_f gsf_fin, gsf_size_f gsf_size,
uint64_t offset, int fin, size_t size,
gsf_read_f gsf_read, void *stream);
int

View file

@ -193,13 +193,12 @@ gquic_le_gen_reg_pkt_header (unsigned char *buf, size_t bufsz, const lsquic_cid_
static int
gquic_le_gen_stream_frame (unsigned char *buf, size_t buf_len, uint32_t stream_id,
uint64_t offset, gsf_fin_f gsf_fin, gsf_size_f gsf_size,
uint64_t offset, int fin, size_t size,
gsf_read_f gsf_read, void *stream)
{
/* 1fdoooss */
unsigned slen, olen, dlen;
unsigned char *p = buf + 1;
int fin;
/* ss: Stream ID length: 1, 2, 3, or 4 bytes */
slen = (stream_id > 0x0000FF)
@ -216,13 +215,11 @@ gquic_le_gen_stream_frame (unsigned char *buf, size_t buf_len, uint32_t stream_i
+ (offset >= (1ULL << 16))
+ ((offset > 0) << 1);
fin = gsf_fin(stream);
if (!fin)
{
unsigned size, n_avail;
unsigned n_avail;
uint16_t nr;
size = gsf_size(stream);
n_avail = buf_len - (p + slen + olen - buf);
/* If we cannot fill remaining buffer, we need to include data

View file

@ -78,6 +78,9 @@ set_retx_alarm (lsquic_send_ctl_t *ctl);
static void
send_ctl_detect_losses (lsquic_send_ctl_t *ctl, lsquic_time_t time);
static unsigned
send_ctl_retx_bytes_out (const struct lsquic_send_ctl *ctl);
#ifdef NDEBUG
static
@ -275,7 +278,7 @@ calculate_tlp_delay (lsquic_send_ctl_t *ctl)
lsquic_time_t srtt, delay;
srtt = lsquic_rtt_stats_get_srtt(&ctl->sc_conn_pub->rtt_stats);
if (ctl->sc_n_in_flight > 1)
if (ctl->sc_n_in_flight_all > 1)
{
delay = 10000; /* 10 ms is the minimum tail loss probe delay */
if (delay < 2 * srtt)
@ -313,9 +316,14 @@ set_retx_alarm (lsquic_send_ctl_t *ctl)
* handshake_count++;
*/
delay = lsquic_rtt_stats_get_srtt(&ctl->sc_conn_pub->rtt_stats);
delay += delay / 2;
if (10000 > delay)
delay = 10000;
if (delay)
{
delay += delay / 2;
if (10000 > delay)
delay = 10000;
}
else
delay = 150000;
delay <<= ctl->sc_n_hsk;
++ctl->sc_n_hsk;
break;
@ -363,13 +371,13 @@ send_ctl_transfer_time (void *ctx)
lsquic_send_ctl_t *const ctl = ctx;
uint64_t bandwidth, pacing_rate;
lsquic_time_t srtt, tx_time;
unsigned cwnd;
unsigned long cwnd;
srtt = lsquic_rtt_stats_get_srtt(&ctl->sc_conn_pub->rtt_stats);
if (srtt == 0)
srtt = 50000;
cwnd = lsquic_cubic_get_cwnd(&ctl->sc_cubic);
bandwidth = (uint64_t) cwnd * (uint64_t) ctl->sc_pack_size * 1000000 / srtt;
bandwidth = cwnd * 1000000 / srtt;
if (send_ctl_in_slow_start(ctl))
pacing_rate = bandwidth * 2;
else if (send_ctl_in_recovery(ctl))
@ -378,31 +386,113 @@ send_ctl_transfer_time (void *ctx)
pacing_rate = bandwidth + bandwidth / 4;
tx_time = (uint64_t) ctl->sc_pack_size * 1000000 / pacing_rate;
LSQ_DEBUG("srtt: %"PRIu64"; ss: %d; rec: %d; cwnd: %u; bandwidth: "
LSQ_DEBUG("srtt: %"PRIu64"; ss: %d; rec: %d; cwnd: %lu; bandwidth: "
"%"PRIu64"; tx_time: %"PRIu64, srtt, send_ctl_in_slow_start(ctl),
send_ctl_in_recovery(ctl), cwnd, bandwidth, tx_time);
return tx_time;
}
static void
send_ctl_unacked_append (struct lsquic_send_ctl *ctl,
struct lsquic_packet_out *packet_out)
{
TAILQ_INSERT_TAIL(&ctl->sc_unacked_packets, packet_out, po_next);
ctl->sc_bytes_unacked_all += lsquic_packet_out_total_sz(packet_out);
ctl->sc_n_in_flight_all += 1;
if (packet_out->po_frame_types & QFRAME_RETRANSMITTABLE_MASK)
{
ctl->sc_bytes_unacked_retx += lsquic_packet_out_total_sz(packet_out);
++ctl->sc_n_in_flight_retx;
}
}
static void
send_ctl_unacked_remove (struct lsquic_send_ctl *ctl,
struct lsquic_packet_out *packet_out)
{
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;
if (packet_out->po_frame_types & QFRAME_RETRANSMITTABLE_MASK)
{
ctl->sc_bytes_unacked_retx -= packet_sz;
--ctl->sc_n_in_flight_retx;
}
}
static void
send_ctl_sched_Xpend_common (struct lsquic_send_ctl *ctl,
struct lsquic_packet_out *packet_out)
{
packet_out->po_flags |= PO_SCHED;
++ctl->sc_n_scheduled;
ctl->sc_bytes_scheduled += lsquic_packet_out_total_sz(packet_out);
lsquic_send_ctl_sanity_check(ctl);
}
static void
send_ctl_sched_append (struct lsquic_send_ctl *ctl,
struct lsquic_packet_out *packet_out)
{
TAILQ_INSERT_TAIL(&ctl->sc_scheduled_packets, packet_out, po_next);
send_ctl_sched_Xpend_common(ctl, packet_out);
}
static void
send_ctl_sched_prepend (struct lsquic_send_ctl *ctl,
struct lsquic_packet_out *packet_out)
{
TAILQ_INSERT_HEAD(&ctl->sc_scheduled_packets, packet_out, po_next);
send_ctl_sched_Xpend_common(ctl, packet_out);
}
static void
send_ctl_sched_remove (struct lsquic_send_ctl *ctl,
struct lsquic_packet_out *packet_out)
{
TAILQ_REMOVE(&ctl->sc_scheduled_packets, packet_out, po_next);
packet_out->po_flags &= ~PO_SCHED;
assert(ctl->sc_n_scheduled);
--ctl->sc_n_scheduled;
ctl->sc_bytes_scheduled -= lsquic_packet_out_total_sz(packet_out);
lsquic_send_ctl_sanity_check(ctl);
}
int
lsquic_send_ctl_sent_packet (lsquic_send_ctl_t *ctl,
struct lsquic_packet_out *packet_out)
struct lsquic_packet_out *packet_out, int account)
{
char frames[lsquic_frame_types_str_sz];
LSQ_DEBUG("packet %"PRIu64" has been sent (frame types: %s)",
packet_out->po_packno, lsquic_frame_types_to_str(frames,
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))
{
TAILQ_INSERT_TAIL(&ctl->sc_unacked_packets, packet_out, po_next);
if ((packet_out->po_frame_types & QFRAME_RETRANSMITTABLE_MASK) &&
!lsquic_alarmset_is_set(ctl->sc_alset, AL_RETX))
set_retx_alarm(ctl);
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.
*/
++ctl->sc_n_in_flight;
#if LSQUIC_SEND_STATS
++ctl->sc_stats.n_total_sent;
#endif
@ -413,28 +503,6 @@ lsquic_send_ctl_sent_packet (lsquic_send_ctl_t *ctl,
}
static int
in_acked_range (const ack_info_t *acki, lsquic_packno_t packno)
{
int i, low, high;
low = 0, high = (int) acki->n_ranges - 1;
do
{
i = low + (high - low) / 2;
if (acki->ranges[i].low <= packno && acki->ranges[i].high >= packno)
return 1;
else if (acki->ranges[i].high < packno)
high = i - 1;
else
low = i + 1;
}
while (low <= high);
return 0;
}
static void
take_rtt_sample (lsquic_send_ctl_t *ctl, const lsquic_packet_out_t *packet_out,
lsquic_time_t now, lsquic_time_t lack_delta)
@ -459,9 +527,8 @@ static int
send_ctl_handle_lost_packet (lsquic_send_ctl_t *ctl,
lsquic_packet_out_t *packet_out)
{
assert(ctl->sc_n_in_flight);
--ctl->sc_n_in_flight;
TAILQ_REMOVE(&ctl->sc_unacked_packets, packet_out, po_next);
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);
@ -512,7 +579,6 @@ send_ctl_detect_losses (lsquic_send_ctl_t *ctl, lsquic_time_t time)
largest_retx_packno = largest_retx_packet_number(ctl);
largest_lost_packno = 0;
assert(largest_retx_packno); /* Otherwise, why detect losses? */
ctl->sc_loss_to = 0;
for (packet_out = TAILQ_FIRST(&ctl->sc_unacked_packets);
@ -531,8 +597,9 @@ send_ctl_detect_losses (lsquic_send_ctl_t *ctl, lsquic_time_t time)
continue;
}
if ((packet_out->po_frame_types & QFRAME_RETRANSMITTABLE_MASK) &&
largest_retx_packno <= ctl->sc_largest_acked_packno)
if (largest_retx_packno
&& (packet_out->po_frame_types & QFRAME_RETRANSMITTABLE_MASK)
&& largest_retx_packno <= ctl->sc_largest_acked_packno)
{
LSQ_DEBUG("loss by early retransmit detected, packet %"PRIu64,
packet_out->po_packno);
@ -550,7 +617,9 @@ send_ctl_detect_losses (lsquic_send_ctl_t *ctl, lsquic_time_t time)
{
LSQ_DEBUG("loss by sent time detected: packet %"PRIu64,
packet_out->po_packno);
largest_lost_packno = packet_out->po_packno;
if (packet_out->po_frame_types & QFRAME_RETRANSMITTABLE_MASK)
largest_lost_packno = packet_out->po_packno;
else { /* don't count it as a loss */; }
(void) send_ctl_handle_lost_packet(ctl, packet_out);
continue;
}
@ -561,6 +630,8 @@ send_ctl_detect_losses (lsquic_send_ctl_t *ctl, lsquic_time_t time)
LSQ_DEBUG("detected new loss: packet %"PRIu64"; new lsac: "
"%"PRIu64, largest_lost_packno, ctl->sc_largest_sent_at_cutback);
lsquic_cubic_loss(&ctl->sc_cubic);
if (ctl->sc_flags & SC_PACE)
pacer_loss_event(&ctl->sc_pacer);
ctl->sc_largest_sent_at_cutback =
lsquic_senhist_largest(&ctl->sc_senhist);
}
@ -581,6 +652,10 @@ 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;
@ -601,6 +676,13 @@ lsquic_send_ctl_got_ack (lsquic_send_ctl_t *ctl,
return -1;
}
if (ctl->sc_flags & SC_WAS_QUIET)
{
ctl->sc_flags &= ~SC_WAS_QUIET;
LSQ_DEBUG("ACK comes after a period of quiescence");
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.
@ -610,16 +692,31 @@ lsquic_send_ctl_got_ack (lsquic_send_ctl_t *ctl,
else
ctl->sc_n_stop_waiting = 0;
app_limited = ctl->sc_n_in_flight + 3 /* This is the "maximum
burst" parameter */ < lsquic_cubic_get_cwnd(&ctl->sc_cubic);
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);
for (packet_out = TAILQ_FIRST(&ctl->sc_unacked_packets);
packet_out && packet_out->po_packno <= largest_acked(acki);
packet_out
#if !LSQUIC_CAN_REORDER
&& packet_out->po_packno <= largest_acked(acki)
#endif
;
packet_out = next)
{
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))
@ -628,18 +725,20 @@ lsquic_send_ctl_got_ack (lsquic_send_ctl_t *ctl,
++rtt_updated;
}
lsquic_cubic_ack(&ctl->sc_cubic, now, now - packet_out->po_sent,
app_limited);
app_limited, lsquic_packet_out_total_sz(packet_out));
LSQ_DEBUG("Got ACK for packet %"PRIu64", remove from unacked queue",
packet_out->po_packno);
TAILQ_REMOVE(&ctl->sc_unacked_packets, packet_out, po_next);
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
lsquic_packet_out_destroy(packet_out, ctl->sc_enpub);
assert(ctl->sc_n_in_flight);
--ctl->sc_n_in_flight;
}
if (rtt_updated)
@ -649,20 +748,12 @@ lsquic_send_ctl_got_ack (lsquic_send_ctl_t *ctl,
ctl->sc_n_tlp = 0;
}
send_ctl_detect_losses(ctl, ack_recv_time);
if (send_ctl_first_unacked_retx_packet(ctl))
{
send_ctl_detect_losses(ctl, ack_recv_time);
if (send_ctl_first_unacked_retx_packet(ctl))
set_retx_alarm(ctl);
else
{
LSQ_DEBUG("All retransmittable packets lost: clear alarm");
lsquic_alarmset_unset(ctl->sc_alset, AL_RETX);
}
}
set_retx_alarm(ctl);
else
{
LSQ_DEBUG("No unacked retransmittable packets: clear retx alarm");
LSQ_DEBUG("No retransmittable packets: clear alarm");
lsquic_alarmset_unset(ctl->sc_alset, AL_RETX);
}
lsquic_send_ctl_sanity_check(ctl);
@ -686,6 +777,9 @@ lsquic_send_ctl_got_ack (lsquic_send_ctl_t *ctl,
while ((packet_out = next));
}
if (ctl->sc_n_in_flight_retx == 0)
ctl->sc_flags |= SC_WAS_QUIET;
return 0;
}
@ -695,19 +789,6 @@ lsquic_send_ctl_smallest_unacked (lsquic_send_ctl_t *ctl)
{
const lsquic_packet_out_t *packet_out;
#ifndef NDEBUG
if ((ctl->sc_senhist.sh_flags & SH_REORDER) &&
!TAILQ_EMPTY(&ctl->sc_unacked_packets))
{
lsquic_packno_t smallest_unacked = UINT64_MAX;
TAILQ_FOREACH(packet_out, &ctl->sc_unacked_packets, po_next)
if (packet_out->po_packno < smallest_unacked)
smallest_unacked = packet_out->po_packno;
assert(smallest_unacked < UINT64_MAX);
return smallest_unacked;
}
else
#endif
/* Packets are always sent out in order (unless we are reordering them
* on purpose). Thus, the first packet on the unacked packets list has
* the smallest packet number of all packets on that list.
@ -751,23 +832,26 @@ lsquic_send_ctl_cleanup (lsquic_send_ctl_t *ctl)
lsquic_senhist_cleanup(&ctl->sc_senhist);
while ((packet_out = TAILQ_FIRST(&ctl->sc_scheduled_packets)))
{
TAILQ_REMOVE(&ctl->sc_scheduled_packets, packet_out, po_next);
send_ctl_sched_remove(ctl, packet_out);
lsquic_packet_out_destroy(packet_out, ctl->sc_enpub);
--ctl->sc_n_scheduled;
}
assert(0 == ctl->sc_n_scheduled);
assert(0 == ctl->sc_bytes_scheduled);
while ((packet_out = TAILQ_FIRST(&ctl->sc_unacked_packets)))
{
TAILQ_REMOVE(&ctl->sc_unacked_packets, packet_out, po_next);
ctl->sc_bytes_unacked_all -= lsquic_packet_out_total_sz(packet_out);
lsquic_packet_out_destroy(packet_out, ctl->sc_enpub);
--ctl->sc_n_in_flight;
--ctl->sc_n_in_flight_all;
}
assert(0 == ctl->sc_n_in_flight);
assert(0 == ctl->sc_n_in_flight_all);
assert(0 == ctl->sc_bytes_unacked_all);
while ((packet_out = TAILQ_FIRST(&ctl->sc_lost_packets)))
{
TAILQ_REMOVE(&ctl->sc_lost_packets, packet_out, po_next);
lsquic_packet_out_destroy(packet_out, ctl->sc_enpub);
}
pacer_cleanup(&ctl->sc_pacer);
#if LSQUIC_SEND_STATS
LSQ_NOTICE("stats: n_total_sent: %u; n_resent: %u; n_delayed: %u",
ctl->sc_stats.n_total_sent, ctl->sc_stats.n_resent,
@ -776,18 +860,40 @@ lsquic_send_ctl_cleanup (lsquic_send_ctl_t *ctl)
}
static unsigned
send_ctl_retx_bytes_out (const struct lsquic_send_ctl *ctl)
{
return ctl->sc_bytes_scheduled
+ ctl->sc_bytes_unacked_retx
+ ctl->sc_bytes_out;
}
int
lsquic_send_ctl_pacer_blocked (struct lsquic_send_ctl *ctl)
{
return (ctl->sc_flags & SC_PACE)
&& !pacer_can_schedule(&ctl->sc_pacer,
ctl->sc_n_scheduled + ctl->sc_n_in_flight_all);
}
#ifndef NDEBUG
__attribute__((weak))
#endif
int
lsquic_send_ctl_can_send (lsquic_send_ctl_t *ctl)
{
const unsigned n_out = ctl->sc_n_scheduled + ctl->sc_n_in_flight;
const unsigned n_out = send_ctl_retx_bytes_out(ctl);
LSQ_DEBUG("%s: n_out: %u (unacked_retx: %u, out: %u); cwnd: %lu", __func__,
n_out, ctl->sc_bytes_unacked_retx, ctl->sc_bytes_out,
lsquic_cubic_get_cwnd(&ctl->sc_cubic));
if (ctl->sc_flags & SC_PACE)
{
if (n_out >= lsquic_cubic_get_cwnd(&ctl->sc_cubic))
return 0;
if (pacer_can_schedule(&ctl->sc_pacer, n_out))
if (pacer_can_schedule(&ctl->sc_pacer,
ctl->sc_n_scheduled + ctl->sc_n_in_flight_all))
return 1;
if (ctl->sc_flags & SC_SCHED_TICK)
{
@ -853,12 +959,12 @@ lsquic_send_ctl_expire_all (lsquic_send_ctl_t *ctl)
}
#ifndef NDEBUG
#if LSQUIC_EXTRA_CHECKS
void
lsquic_send_ctl_sanity_check (const lsquic_send_ctl_t *ctl)
{
const struct lsquic_packet_out *packet_out;
unsigned count;
unsigned count, sched_bytes;
assert(!send_ctl_first_unacked_retx_packet(ctl) ||
lsquic_alarmset_is_set(ctl->sc_alset, AL_RETX));
@ -871,7 +977,17 @@ lsquic_send_ctl_sanity_check (const lsquic_send_ctl_t *ctl)
count = 0;
TAILQ_FOREACH(packet_out, &ctl->sc_unacked_packets, po_next)
++count;
assert(count == ctl->sc_n_in_flight);
assert(count == ctl->sc_n_in_flight_all);
count = 0, sched_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);
++count;
}
assert(count == ctl->sc_n_scheduled);
assert(sched_bytes == ctl->sc_bytes_scheduled);
}
@ -889,8 +1005,13 @@ lsquic_send_ctl_scheduled_one (lsquic_send_ctl_t *ctl,
assert((last->po_flags & PO_REPACKNO) ||
last->po_packno < packet_out->po_packno);
#endif
TAILQ_INSERT_TAIL(&ctl->sc_scheduled_packets, packet_out, po_next);
++ctl->sc_n_scheduled;
if (ctl->sc_flags & SC_PACE)
{
unsigned n_out = ctl->sc_n_in_flight_retx + ctl->sc_n_scheduled;
pacer_packet_scheduled(&ctl->sc_pacer, n_out,
send_ctl_in_recovery(ctl), send_ctl_transfer_time, ctl);
}
send_ctl_sched_append(ctl, packet_out);
}
@ -918,8 +1039,8 @@ lsquic_send_ctl_next_packet_to_send (lsquic_send_ctl_t *ctl)
packet_out->po_flags &= ~PO_REPACKNO;
}
TAILQ_REMOVE(&ctl->sc_scheduled_packets, packet_out, po_next);
--ctl->sc_n_scheduled;
send_ctl_sched_remove(ctl, packet_out);
ctl->sc_bytes_out += lsquic_packet_out_total_sz(packet_out);
return packet_out;
}
@ -928,8 +1049,8 @@ void
lsquic_send_ctl_delayed_one (lsquic_send_ctl_t *ctl,
lsquic_packet_out_t *packet_out)
{
TAILQ_INSERT_HEAD(&ctl->sc_scheduled_packets, packet_out, po_next);
++ctl->sc_n_scheduled;
send_ctl_sched_prepend(ctl, packet_out);
ctl->sc_bytes_out -= lsquic_packet_out_total_sz(packet_out);
LSQ_DEBUG("packet %"PRIu64" has been delayed", packet_out->po_packno);
#if LSQUIC_SEND_STATS
++ctl->sc_stats.n_delayed;
@ -1013,12 +1134,12 @@ lsquic_send_ctl_get_writeable_packet (lsquic_send_ctl_t *ctl,
unsigned need_at_least, int *is_err)
{
lsquic_packet_out_t *packet_out;
unsigned n_out;
assert(need_at_least > 0);
packet_out = lsquic_send_ctl_last_scheduled(ctl);
if (packet_out
&& !(packet_out->po_flags & PO_STREAM_END)
&& lsquic_packet_out_avail(packet_out) >= need_at_least)
{
return packet_out;
@ -1032,15 +1153,7 @@ lsquic_send_ctl_get_writeable_packet (lsquic_send_ctl_t *ctl,
packet_out = lsquic_send_ctl_new_packet_out(ctl, need_at_least);
if (packet_out)
{
if (ctl->sc_flags & SC_PACE)
{
n_out = ctl->sc_n_in_flight + ctl->sc_n_scheduled;
pacer_packet_scheduled(&ctl->sc_pacer, n_out,
send_ctl_in_recovery(ctl), send_ctl_transfer_time, ctl);
}
lsquic_send_ctl_scheduled_one(ctl, packet_out);
}
else
*is_err = 1;
return packet_out;
@ -1052,12 +1165,12 @@ send_ctl_get_packet_for_stream (lsquic_send_ctl_t *ctl,
unsigned need_at_least, const lsquic_stream_t *stream)
{
lsquic_packet_out_t *packet_out;
unsigned n_out;
assert(need_at_least > 0);
packet_out = lsquic_send_ctl_last_scheduled(ctl);
if (packet_out
&& !(packet_out->po_flags & PO_STREAM_END)
&& lsquic_packet_out_avail(packet_out) >= need_at_least
&& !lsquic_packet_out_has_frame(packet_out, stream, QUIC_FRAME_STREAM))
{
@ -1071,12 +1184,6 @@ send_ctl_get_packet_for_stream (lsquic_send_ctl_t *ctl,
if (!packet_out)
return NULL;
if (ctl->sc_flags & SC_PACE)
{
n_out = ctl->sc_n_in_flight + ctl->sc_n_scheduled;
pacer_packet_scheduled(&ctl->sc_pacer, n_out,
send_ctl_in_recovery(ctl), send_ctl_transfer_time, ctl);
}
lsquic_send_ctl_scheduled_one(ctl, packet_out);
return packet_out;
}
@ -1106,12 +1213,11 @@ update_for_resending (lsquic_send_ctl_t *ctl, lsquic_packet_out_t *packet_out)
}
assert(packet_out->po_regen_sz < packet_out->po_data_sz);
/* TODO: in Q038 and later, we can simply replace the ACK with NUL bytes
* representing PADDING frame instead of doing memmove and adjusting
* offsets.
*/
if (packet_out->po_regen_sz)
{
assert(!(packet_out->po_flags & PO_SCHED));
lsquic_packet_out_chop_regen(packet_out);
}
LSQ_DEBUG("Packet %"PRIu64" repackaged for resending as packet %"PRIu64,
oldno, packno);
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "packet %"PRIu64" repackaged for "
@ -1119,21 +1225,6 @@ update_for_resending (lsquic_send_ctl_t *ctl, lsquic_packet_out_t *packet_out)
}
/* A droppable hello packet is a packet that contains a part of hello message
* after handshake has been completed.
*/
static int
droppable_hello_packet (const lsquic_send_ctl_t *ctl,
const lsquic_packet_out_t *packet_out)
{
return 0 /* TODO: we cannot not resend HELLO packets if we are server.
* For now, do not discard any HELLO packets.
*/
&& (packet_out->po_flags & PO_HELLO)
&& (ctl->sc_conn_pub->lconn->cn_flags & LSCONN_HANDSHAKE_DONE);
}
unsigned
lsquic_send_ctl_reschedule_packets (lsquic_send_ctl_t *ctl)
{
@ -1143,8 +1234,7 @@ lsquic_send_ctl_reschedule_packets (lsquic_send_ctl_t *ctl)
while (lsquic_send_ctl_can_send(ctl) &&
(packet_out = send_ctl_next_lost(ctl)))
{
if ((packet_out->po_regen_sz < packet_out->po_data_sz)
&& !droppable_hello_packet(ctl, packet_out))
if (packet_out->po_regen_sz < packet_out->po_data_sz)
{
++n;
update_for_resending(ctl, packet_out);
@ -1193,7 +1283,7 @@ void
lsquic_send_ctl_elide_stream_frames (lsquic_send_ctl_t *ctl, uint32_t stream_id)
{
struct lsquic_packet_out *packet_out, *next;
unsigned n;
unsigned n, adj;
for (packet_out = TAILQ_FIRST(&ctl->sc_scheduled_packets); packet_out;
packet_out = next)
@ -1203,15 +1293,15 @@ lsquic_send_ctl_elide_stream_frames (lsquic_send_ctl_t *ctl, uint32_t stream_id)
if ((packet_out->po_frame_types & (1 << QUIC_FRAME_STREAM))
)
{
lsquic_packet_out_elide_reset_stream_frames(packet_out, stream_id);
adj = lsquic_packet_out_elide_reset_stream_frames(packet_out,
stream_id);
ctl->sc_bytes_scheduled -= adj;
if (0 == packet_out->po_frame_types)
{
LSQ_DEBUG("cancel packet %"PRIu64" after eliding frames for "
"stream %"PRIu32, packet_out->po_packno, stream_id);
TAILQ_REMOVE(&ctl->sc_scheduled_packets, packet_out, po_next);
send_ctl_sched_remove(ctl, packet_out);
lsquic_packet_out_destroy(packet_out, ctl->sc_enpub);
assert(ctl->sc_n_scheduled);
--ctl->sc_n_scheduled;
}
}
}
@ -1225,10 +1315,11 @@ lsquic_send_ctl_elide_stream_frames (lsquic_send_ctl_t *ctl, uint32_t stream_id)
if (!(packet_out->po_frame_types & (1 << QUIC_FRAME_STREAM)))
continue;
next = TAILQ_NEXT(packet_out, po_next);
lsquic_packet_out_elide_reset_stream_frames(packet_out, stream_id);
if (0 == packet_out->po_frame_types)
{
LSQ_DEBUG("cancel packet %"PRIu64" after eliding frames for "
"stream %"PRIu32, packet_out->po_packno, stream_id);
LSQ_DEBUG("cancel buffered packet in queue #%u after eliding "
"frames for stream %"PRIu32, n, stream_id);
TAILQ_REMOVE(&ctl->sc_buffered_packets[n].bpq_packets,
packet_out, po_next);
--ctl->sc_buffered_packets[n].bpq_count;
@ -1322,8 +1413,7 @@ lsquic_send_ctl_squeeze_sched (lsquic_send_ctl_t *ctl)
packet_out = next)
{
next = TAILQ_NEXT(packet_out, po_next);
if (packet_out->po_regen_sz < packet_out->po_data_sz
&& !droppable_hello_packet(ctl, packet_out))
if (packet_out->po_regen_sz < packet_out->po_data_sz)
{
if (packet_out->po_flags & PO_ENCRYPTED)
{
@ -1341,9 +1431,7 @@ lsquic_send_ctl_squeeze_sched (lsquic_send_ctl_t *ctl)
LOG_PACKET_Q(&ctl->sc_scheduled_packets,
"unacked packets before squeezing");
#endif
TAILQ_REMOVE(&ctl->sc_scheduled_packets, packet_out, po_next);
assert(ctl->sc_n_scheduled);
--ctl->sc_n_scheduled;
send_ctl_sched_remove(ctl, packet_out);
LSQ_DEBUG("Dropping packet %"PRIu64" from scheduled queue",
packet_out->po_packno);
lsquic_packet_out_destroy(packet_out, ctl->sc_enpub);
@ -1394,9 +1482,8 @@ lsquic_send_ctl_drop_scheduled (lsquic_send_ctl_t *ctl)
const unsigned n = ctl->sc_n_scheduled;
while ((packet_out = TAILQ_FIRST(&ctl->sc_scheduled_packets)))
{
TAILQ_REMOVE(&ctl->sc_scheduled_packets, packet_out, po_next);
send_ctl_sched_remove(ctl, packet_out);
lsquic_packet_out_destroy(packet_out, ctl->sc_enpub);
--ctl->sc_n_scheduled;
}
assert(0 == ctl->sc_n_scheduled);
LSQ_DEBUG("dropped %u scheduled packet%s", n, n != 0 ? "s" : "");
@ -1457,10 +1544,10 @@ send_ctl_max_bpq_count (const lsquic_send_ctl_t *ctl,
return MAX_BPQ_COUNT;
case BPT_HIGHEST_PRIO:
default: /* clang does not complain about absence of `default'... */
count = ctl->sc_n_scheduled + ctl->sc_n_in_flight;
if (count < lsquic_cubic_get_cwnd(&ctl->sc_cubic))
count = ctl->sc_n_scheduled + ctl->sc_n_in_flight_retx;
if (count < lsquic_cubic_get_cwnd(&ctl->sc_cubic) / ctl->sc_pack_size)
{
count -= lsquic_cubic_get_cwnd(&ctl->sc_cubic);
count -= lsquic_cubic_get_cwnd(&ctl->sc_cubic) / ctl->sc_pack_size;
if (count > MAX_BPQ_COUNT)
return count;
}
@ -1481,6 +1568,7 @@ send_ctl_get_buffered_packet (lsquic_send_ctl_t *ctl,
packet_out = TAILQ_LAST(&packet_q->bpq_packets, lsquic_packets_tailq);
if (packet_out
&& !(packet_out->po_flags & PO_STREAM_END)
&& lsquic_packet_out_avail(packet_out) >= need_at_least
&& !lsquic_packet_out_has_frame(packet_out, stream, QUIC_FRAME_STREAM))
{
@ -1532,7 +1620,7 @@ lsquic_send_ctl_calc_packno_bits (lsquic_send_ctl_t *ctl)
unsigned n_in_flight;
smallest_unacked = lsquic_send_ctl_smallest_unacked(ctl);
n_in_flight = lsquic_cubic_get_cwnd(&ctl->sc_cubic);
n_in_flight = lsquic_cubic_get_cwnd(&ctl->sc_cubic) / ctl->sc_pack_size;
return calc_packno_bits(ctl->sc_cur_packno + 1, smallest_unacked,
n_in_flight);
}
@ -1567,6 +1655,7 @@ split_buffered_packet (lsquic_send_ctl_t *ctl,
if (0 == lsquic_packet_out_split_in_two(&ctl->sc_enpub->enp_mm, packet_out,
new_packet_out, ctl->sc_conn_pub->lconn->cn_pf, excess_bytes))
{
lsquic_packet_out_set_packno_bits(packet_out, bits);
TAILQ_INSERT_AFTER(&packet_q->bpq_packets, packet_out, new_packet_out,
po_next);
++packet_q->bpq_count;

View file

@ -59,10 +59,15 @@ 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; /* Number of packets in flight */
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 {
@ -72,6 +77,7 @@ typedef struct lsquic_send_ctl {
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;
@ -90,7 +96,8 @@ lsquic_send_ctl_init (lsquic_send_ctl_t *, struct lsquic_alarmset *,
struct lsquic_conn_public *, unsigned short max_packet_size);
int
lsquic_send_ctl_sent_packet (lsquic_send_ctl_t *, struct lsquic_packet_out *);
lsquic_send_ctl_sent_packet (lsquic_send_ctl_t *, struct lsquic_packet_out *,
int);
int
lsquic_send_ctl_got_ack (lsquic_send_ctl_t *, const struct ack_info *,
@ -126,11 +133,11 @@ lsquic_send_ctl_expire_all (lsquic_send_ctl_t *ctl);
#define lsquic_send_ctl_largest_ack2ed(ctl) (+(ctl)->sc_largest_ack2ed)
#ifdef NDEBUG
# define lsquic_send_ctl_sanity_check(ctl)
#else
#if LSQUIC_EXTRA_CHECKS
void
lsquic_send_ctl_sanity_check (const lsquic_send_ctl_t *ctl);
#else
# define lsquic_send_ctl_sanity_check(ctl)
#endif
int
@ -173,6 +180,10 @@ lsquic_send_ctl_elide_stream_frames (lsquic_send_ctl_t *, uint32_t);
int
lsquic_send_ctl_squeeze_sched (lsquic_send_ctl_t *);
#define lsquic_send_ctl_maybe_squeeze_sched(ctl) ( \
(ctl)->sc_n_scheduled && lsquic_send_ctl_squeeze_sched(ctl) \
)
/* Same return value as for squeezing, but without actual squeezing. */
int
lsquic_send_ctl_have_delayed_packets (const lsquic_send_ctl_t *ctl);
@ -237,4 +248,14 @@ int
lsquic_send_ctl_turn_on_fin (struct lsquic_send_ctl *,
const struct lsquic_stream *);
int
lsquic_send_ctl_pacer_blocked (struct lsquic_send_ctl *);
#define lsquic_send_ctl_incr_pack_sz(ctl, packet, delta) do { \
(packet)->po_data_sz += delta; \
if ((packet)->po_flags & PO_SCHED) \
(ctl)->sc_bytes_scheduled += delta; \
lsquic_send_ctl_sanity_check(ctl); \
} while (0)
#endif

View file

@ -17,16 +17,6 @@ void
lsquic_senhist_init (lsquic_senhist_t *hist)
{
lsquic_packints_init(&hist->sh_pints);
#ifndef NDEBUG
{
const char *env;
env = getenv("LSQUIC_REORDER_SENT");
if (env && atoi(env))
hist->sh_flags = SH_REORDER;
else
hist->sh_flags = 0;
}
#endif
}
@ -75,32 +65,9 @@ senhist_add_fast (lsquic_senhist_t *hist, lsquic_packno_t packno)
}
#ifndef NDEBUG
static int
senhist_add_slow (lsquic_senhist_t *hist, lsquic_packno_t packno)
{
switch (lsquic_packints_add(&hist->sh_pints, packno))
{
case PACKINTS_OK:
return 0;
case PACKINTS_DUP: /* We should not generate duplicate packet numbers! */
default:
assert(0);
case PACKINTS_ERR:
return -1;
}
}
#endif
int
lsquic_senhist_add (lsquic_senhist_t *hist, lsquic_packno_t packno)
{
#ifndef NDEBUG
if (hist->sh_flags & SH_REORDER)
return senhist_add_slow(hist, packno);
else
#endif
return senhist_add_fast(hist, packno);
}
@ -157,3 +124,5 @@ lsquic_senhist_mem_used (const struct lsquic_senhist *hist)
- sizeof(hist->sh_pints)
+ lsquic_packints_mem_used(&hist->sh_pints);
}

View file

@ -20,11 +20,6 @@ typedef struct lsquic_senhist {
* b) the peer sends an invalid ACK.
*/
struct packints sh_pints;
#ifndef NDEBUG
enum {
SH_REORDER = (1 << 0),
} sh_flags;
#endif
} lsquic_senhist_t;
void

View file

@ -86,6 +86,9 @@ stream_flush (lsquic_stream_t *stream);
static int
stream_flush_nocheck (lsquic_stream_t *stream);
static void
maybe_remove_from_write_q (lsquic_stream_t *stream, enum stream_flags flag);
#if LSQUIC_KEEP_STREAM_HISTORY
/* These values are printable ASCII characters for ease of printing the
@ -251,7 +254,7 @@ lsquic_stream_new_ext (uint32_t id, struct lsquic_conn_public *conn_pub,
stream->data_in = data_in_hash_new(conn_pub, id, 0);
else
stream->data_in = data_in_nocopy_new(conn_pub, id);
LSQ_DEBUG("created stream %u", id);
LSQ_DEBUG("created stream %u @%p", id, stream);
SM_HISTORY_APPEND(stream, SHE_CREATED);
if (ctor_flags & SCF_DI_AUTOSWITCH)
stream->stream_flags |= STREAM_AUTOSWITCH;
@ -294,6 +297,8 @@ drop_buffered_data (struct lsquic_stream *stream)
{
decr_conn_cap(stream, stream->sm_n_buffered);
stream->sm_n_buffered = 0;
if (stream->stream_flags & STREAM_WRITE_Q_FLAGS)
maybe_remove_from_write_q(stream, STREAM_WRITE_Q_FLAGS);
}
@ -321,7 +326,7 @@ lsquic_stream_destroy (lsquic_stream_t *stream)
free(stream->push_req);
free(stream->uh);
free(stream->sm_buf);
LSQ_DEBUG("destroyed stream %u", stream->id);
LSQ_DEBUG("destroyed stream %u @%p", stream->id, stream);
SM_HISTORY_DUMP_REMAINING(stream);
free(stream);
}
@ -340,8 +345,7 @@ stream_is_finished (const lsquic_stream_t *stream)
*/
&& 0 == (stream->stream_flags & STREAM_SEND_RST)
&& ((stream->stream_flags & STREAM_FORCE_FINISH)
|| (((stream->stream_flags & (STREAM_FIN_SENT |STREAM_RST_SENT))
|| lsquic_stream_is_pushed(stream))
|| ((stream->stream_flags & (STREAM_FIN_SENT |STREAM_RST_SENT))
&& (stream->stream_flags & (STREAM_FIN_RECVD|STREAM_RST_RECVD))));
}
@ -603,6 +607,7 @@ lsquic_stream_rst_in (lsquic_stream_t *stream, uint64_t offset,
lsquic_sfcw_consume_rem(&stream->fc);
drop_frames_in(stream);
drop_buffered_data(stream);
maybe_elide_stream_frames(stream);
if (!(stream->stream_flags &
@ -1492,7 +1497,7 @@ static struct lsquic_packet_out * (* const get_packet[])(
static enum { SWTP_OK, SWTP_STOP, SWTP_ERROR }
stream_write_to_packet (struct frame_gen_ctx *fg_ctx)
stream_write_to_packet (struct frame_gen_ctx *fg_ctx, const size_t size)
{
lsquic_stream_t *const stream = fg_ctx->fgc_stream;
const struct parse_funcs *const pf = stream->conn_pub->lconn->cn_pf;
@ -1503,7 +1508,7 @@ stream_write_to_packet (struct frame_gen_ctx *fg_ctx)
stream_header_sz = pf->pf_calc_stream_frame_header_sz(stream->id,
stream->tosend_off);
need_at_least = stream_header_sz + (frame_gen_size(fg_ctx) > 0);
need_at_least = stream_header_sz + (size > 0);
hsk = LSQUIC_STREAM_HANDSHAKE == stream->id;
packet_out = get_packet[hsk](send_ctl, need_at_least, stream);
if (!packet_out)
@ -1514,7 +1519,7 @@ stream_write_to_packet (struct frame_gen_ctx *fg_ctx)
packet_out->po_data + packet_out->po_data_sz,
lsquic_packet_out_avail(packet_out), stream->id,
stream->tosend_off,
frame_gen_fin, frame_gen_size, frame_gen_read, fg_ctx);
frame_gen_fin(fg_ctx), size, frame_gen_read, fg_ctx);
if (len < 0)
{
LSQ_ERROR("could not generate stream frame");
@ -1523,8 +1528,10 @@ stream_write_to_packet (struct frame_gen_ctx *fg_ctx)
EV_LOG_GENERATED_STREAM_FRAME(LSQUIC_LOG_CONN_ID, pf,
packet_out->po_data + packet_out->po_data_sz, len);
packet_out->po_data_sz += len;
lsquic_send_ctl_incr_pack_sz(send_ctl, packet_out, len);
packet_out->po_frame_types |= 1 << QUIC_FRAME_STREAM;
if (0 == lsquic_packet_out_avail(packet_out))
packet_out->po_flags |= PO_STREAM_END;
s = lsquic_packet_out_add_stream(packet_out, stream->conn_pub->mm,
stream, QUIC_FRAME_STREAM, off, len);
if (s != 0)
@ -1573,7 +1580,7 @@ stream_write_to_packets (lsquic_stream_t *stream, struct lsquic_reader *reader,
while ((size = frame_gen_size(&fg_ctx), thresh ? size >= thresh : size > 0)
|| frame_gen_fin(&fg_ctx))
{
switch (stream_write_to_packet(&fg_ctx))
switch (stream_write_to_packet(&fg_ctx, size))
{
case SWTP_OK:
if (frame_gen_fin(&fg_ctx))
@ -1877,6 +1884,7 @@ lsquic_stream_reset_ext (lsquic_stream_t *stream, uint32_t error_code,
stream->stream_flags &= ~STREAM_SENDING_FLAGS;
stream->stream_flags |= STREAM_SEND_RST;
drop_buffered_data(stream);
maybe_elide_stream_frames(stream);
maybe_schedule_call_on_close(stream);

View file

@ -18,6 +18,10 @@ CHECK_SYMBOL_EXISTS(
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/test_config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/test_config.h)
add_subdirectory(unittests)
enable_testing()
IF(DEVEL_MODE EQUAL 1)
# Our test framework relies on assertions, only compile if assertions are
# enabled.
#
add_subdirectory(unittests)
enable_testing()
ENDIF()

View file

@ -61,6 +61,8 @@ struct http_client_ctx {
enum {
HCC_DISCARD_RESPONSE = (1 << 0),
HCC_SEEN_FIN = (1 << 1),
HCC_ABORT_ON_INCOMPLETE = (1 << 2),
} hcc_flags;
struct prog *prog;
};
@ -110,7 +112,16 @@ static void
http_client_on_conn_closed (lsquic_conn_t *conn)
{
lsquic_conn_ctx_t *conn_h = lsquic_conn_get_ctx(conn);
LSQ_INFO("Connection closed");
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);
@ -282,10 +293,9 @@ http_client_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
static void
http_client_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
{
const struct http_client_ctx *const client_ctx = st_h->client_ctx;
struct http_client_ctx *const client_ctx = st_h->client_ctx;
ssize_t nread;
unsigned old_prio, new_prio;
int s;
unsigned char buf[0x200];
unsigned nreads = 0;
@ -300,7 +310,10 @@ http_client_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
{
old_prio = lsquic_stream_priority(stream);
new_prio = random() & 0xFF;
s = lsquic_stream_set_priority(stream, new_prio);
#ifndef NDEBUG
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);
@ -308,6 +321,7 @@ http_client_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
}
else if (0 == nread)
{
client_ctx->hcc_flags |= HCC_SEEN_FIN;
lsquic_stream_shutdown(stream, 0);
break;
}
@ -389,6 +403,7 @@ usage (const char *prog)
" content-type: application/octet-stream and\n"
" content-length\n"
" -K Discard server response\n"
" -I Abort on incomplete reponse from server\n"
, prog);
}
@ -420,6 +435,9 @@ main (int argc, char **argv)
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;

View file

@ -110,6 +110,8 @@ prog_print_common_options (const struct prog *prog, FILE *out)
" Example: 1223/104613.946956\n"
" 4 Microsecond time.\n"
" Example: 11:04:05.196308\n"
" 5 Full date and microsecond time.\n"
" Example: 2017-03-21 13:43:46.671345\n"
" -S opt=val Socket options. Supported options:\n"
" sndbuf=12345 # Sets SO_SNDBUF\n"
" rcvbuf=12345 # Sets SO_RCVBUF\n"
@ -233,8 +235,8 @@ prog_connect (struct prog *prog)
struct service_port *sport;
sport = TAILQ_FIRST(prog->prog_sports);
if (0 != lsquic_engine_connect(prog->prog_engine,
(struct sockaddr *) &sport->sas, sport,
if (NULL == lsquic_engine_connect(prog->prog_engine,
(struct sockaddr *) &sport->sas, sport, NULL,
prog->prog_hostname ? prog->prog_hostname : sport->host,
prog->prog_max_packet_size))
return -1;
@ -382,12 +384,18 @@ prog_stop (struct prog *prog)
}
drop_onetimer(prog);
event_del(prog->prog_timer);
event_free(prog->prog_timer);
prog->prog_timer = NULL;
event_del(prog->prog_usr1);
event_free(prog->prog_usr1);
prog->prog_usr1 = NULL;
if (prog->prog_timer)
{
event_del(prog->prog_timer);
event_free(prog->prog_timer);
prog->prog_timer = NULL;
}
if (prog->prog_usr1)
{
event_del(prog->prog_usr1);
event_free(prog->prog_usr1);
prog->prog_usr1 = NULL;
}
}

View file

@ -48,7 +48,7 @@ prog_init (struct prog *, unsigned lsquic_engine_flags, struct sport_head *,
# define IP_DONTFRAG_FLAG ""
#endif
#define PROG_OPTS "i:m:c:y:L:l:o:H:s:S:z:" SENDMMSG_FLAG IP_DONTFRAG_FLAG
#define PROG_OPTS "i:m:c:y:L:l:o:H:s:S:Y:z:" SENDMMSG_FLAG IP_DONTFRAG_FLAG
/* Returns:
* 0 Applied

View file

@ -231,5 +231,9 @@ add_executable(test_buf test_buf.c)
target_link_libraries(test_buf lsquic pthread libssl.a libcrypto.a m ${FIULIB})
add_test(buf test_buf)
add_executable(test_cubic test_cubic.c)
target_link_libraries(test_cubic lsquic pthread libssl.a libcrypto.a m ${FIULIB})
add_test(cubic test_cubic)
add_executable(test_dec test_dec.c)
target_link_libraries(test_dec libssl.a libcrypto.a z m pthread ${FIULIB})

View file

@ -88,7 +88,7 @@ main (int argc, char **argv)
n = i + atoi(optarg);
for ( ; i < n; ++i)
{
lsquic_cubic_ack(&cubic, MS(unit * i), MS(rtt_ms), app_limited);
lsquic_cubic_ack(&cubic, MS(unit * i), MS(rtt_ms), app_limited, 1370);
REC(EV_ACK);
}
break;

View file

@ -98,8 +98,8 @@ elide_single_stream_frame (void)
len = pf->pf_gen_stream_frame(packet_out->po_data + packet_out->po_data_sz,
lsquic_packet_out_avail(packet_out),
streams[0].id, lsquic_stream_tosend_offset(&streams[0]),
(gsf_fin_f) lsquic_stream_tosend_fin,
(gsf_size_f) lsquic_stream_tosend_sz,
lsquic_stream_tosend_fin(&streams[0]),
lsquic_stream_tosend_sz(&streams[0]),
(gsf_read_f) lsquic_stream_tosend_read,
&streams[0]);
packet_out->po_data_sz += len;
@ -163,8 +163,8 @@ elide_three_stream_frames (int chop_regen)
len = pf->pf_gen_stream_frame(ref_out->po_data + ref_out->po_data_sz,
lsquic_packet_out_avail(ref_out),
streams[0].id, lsquic_stream_tosend_offset(&streams[0]),
(gsf_fin_f) lsquic_stream_tosend_fin,
(gsf_size_f) lsquic_stream_tosend_sz,
lsquic_stream_tosend_fin(&streams[0]),
lsquic_stream_tosend_sz(&streams[0]),
(gsf_read_f) lsquic_stream_tosend_read,
&streams[0]);
b_off = ref_out->po_data_sz;
@ -178,8 +178,8 @@ elide_three_stream_frames (int chop_regen)
len = pf->pf_gen_stream_frame(ref_out->po_data + ref_out->po_data_sz,
lsquic_packet_out_avail(ref_out),
streams[0].id, lsquic_stream_tosend_offset(&streams[0]),
(gsf_fin_f) lsquic_stream_tosend_fin,
(gsf_size_f) lsquic_stream_tosend_sz,
lsquic_stream_tosend_fin(&streams[0]),
lsquic_stream_tosend_sz(&streams[0]),
(gsf_read_f) lsquic_stream_tosend_read,
&streams[0]);
d_off = ref_out->po_data_sz;
@ -200,8 +200,8 @@ elide_three_stream_frames (int chop_regen)
len = pf->pf_gen_stream_frame(packet_out->po_data + packet_out->po_data_sz,
lsquic_packet_out_avail(packet_out),
streams[0].id, lsquic_stream_tosend_offset(&streams[0]),
(gsf_fin_f) lsquic_stream_tosend_fin,
(gsf_size_f) lsquic_stream_tosend_sz,
lsquic_stream_tosend_fin(&streams[0]),
lsquic_stream_tosend_sz(&streams[0]),
(gsf_read_f) lsquic_stream_tosend_read,
&streams[0]);
lsquic_packet_out_add_stream(packet_out, &enpub.enp_mm, &streams[0],
@ -213,8 +213,8 @@ elide_three_stream_frames (int chop_regen)
len = pf->pf_gen_stream_frame(packet_out->po_data + packet_out->po_data_sz,
lsquic_packet_out_avail(packet_out),
streams[1].id, lsquic_stream_tosend_offset(&streams[1]),
(gsf_fin_f) lsquic_stream_tosend_fin,
(gsf_size_f) lsquic_stream_tosend_sz,
lsquic_stream_tosend_fin(&streams[1]),
lsquic_stream_tosend_sz(&streams[1]),
(gsf_read_f) lsquic_stream_tosend_read,
&streams[1]);
lsquic_packet_out_add_stream(packet_out, &enpub.enp_mm, &streams[1],
@ -226,8 +226,8 @@ elide_three_stream_frames (int chop_regen)
len = pf->pf_gen_stream_frame(packet_out->po_data + packet_out->po_data_sz,
lsquic_packet_out_avail(packet_out),
streams[2].id, lsquic_stream_tosend_offset(&streams[2]),
(gsf_fin_f) lsquic_stream_tosend_fin,
(gsf_size_f) lsquic_stream_tosend_sz,
lsquic_stream_tosend_fin(&streams[2]),
lsquic_stream_tosend_sz(&streams[2]),
(gsf_read_f) lsquic_stream_tosend_read,
&streams[2]);
lsquic_packet_out_add_stream(packet_out, &enpub.enp_mm, &streams[2],
@ -245,8 +245,8 @@ elide_three_stream_frames (int chop_regen)
len = pf->pf_gen_stream_frame(packet_out->po_data + packet_out->po_data_sz,
lsquic_packet_out_avail(packet_out),
streams[3].id, lsquic_stream_tosend_offset(&streams[3]),
(gsf_fin_f) lsquic_stream_tosend_fin,
(gsf_size_f) lsquic_stream_tosend_sz,
lsquic_stream_tosend_fin(&streams[3]),
lsquic_stream_tosend_sz(&streams[3]),
(gsf_read_f) lsquic_stream_tosend_read,
&streams[3]);
lsquic_packet_out_add_stream(packet_out, &enpub.enp_mm, &streams[3],
@ -258,8 +258,8 @@ elide_three_stream_frames (int chop_regen)
len = pf->pf_gen_stream_frame(packet_out->po_data + packet_out->po_data_sz,
lsquic_packet_out_avail(packet_out),
streams[4].id, lsquic_stream_tosend_offset(&streams[4]),
(gsf_fin_f) lsquic_stream_tosend_fin,
(gsf_size_f) lsquic_stream_tosend_sz,
lsquic_stream_tosend_fin(&streams[4]),
lsquic_stream_tosend_sz(&streams[4]),
(gsf_read_f) lsquic_stream_tosend_read,
&streams[4]);
lsquic_packet_out_add_stream(packet_out, &enpub.enp_mm, &streams[4],

View file

@ -37,6 +37,7 @@ main (void)
struct stream_rec *srec;
memset(&enpub, 0, sizeof(enpub));
memset(&streams, 0, sizeof(streams));
lsquic_mm_init(&enpub.enp_mm);
packet_out = lsquic_mm_get_packet_out(&enpub.enp_mm, NULL, QUIC_MAX_PAYLOAD_SZ);

View file

@ -14,6 +14,7 @@
#include "lsquic_parse.h"
#include "lsquic_sfcw.h"
#include "lsquic_stream.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_in.h"
struct lsquic_stream_if;
@ -77,8 +78,8 @@ static int make_complex_packet(unsigned char *pkt_buf, int max_buf_len)
buf_len = pf->pf_gen_stream_frame(p, pend - p,
stream->id, lsquic_stream_tosend_offset(stream),
(gsf_fin_f) lsquic_stream_tosend_fin,
(gsf_size_f) lsquic_stream_tosend_sz,
lsquic_stream_tosend_fin(stream),
lsquic_stream_tosend_sz(stream),
(gsf_read_f) lsquic_stream_tosend_read,
stream);
p += buf_len;
@ -99,8 +100,8 @@ void test_stream_frame()
uint8_t buf[1500];
int buf_len = pf->pf_gen_stream_frame(buf, 1500,
stream->id, lsquic_stream_tosend_offset(stream),
(gsf_fin_f) lsquic_stream_tosend_fin,
(gsf_size_f) lsquic_stream_tosend_sz,
lsquic_stream_tosend_fin(stream),
lsquic_stream_tosend_sz(stream),
(gsf_read_f) lsquic_stream_tosend_read,
stream);
stream_frame_t stream_frame2;

View file

@ -10,6 +10,7 @@
#include "lsquic.h"
#include "lsquic_alarmset.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_in.h"
#include "lsquic_conn_flow.h"
#include "lsquic_sfcw.h"

View file

@ -13,6 +13,7 @@
#include "lsquic.h"
#include "lsquic_alarmset.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_in.h"
#include "lsquic_conn_flow.h"
#include "lsquic_rtt.h"
@ -494,7 +495,7 @@ test_loc_FIN_rem_FIN (struct test_objs *tobjs)
/* Pretend we sent out a packet: */
packet_out = lsquic_send_ctl_next_packet_to_send(&tobjs->send_ctl);
lsquic_send_ctl_sent_packet(&tobjs->send_ctl, packet_out);
lsquic_send_ctl_sent_packet(&tobjs->send_ctl, packet_out, 1);
s = lsquic_stream_shutdown(stream, 1);
assert(s == 0);
@ -508,7 +509,7 @@ test_loc_FIN_rem_FIN (struct test_objs *tobjs)
/* Pretend we sent out this packet as well: */
packet_out = lsquic_send_ctl_next_packet_to_send(&tobjs->send_ctl);
lsquic_send_ctl_sent_packet(&tobjs->send_ctl, packet_out);
lsquic_send_ctl_sent_packet(&tobjs->send_ctl, packet_out, 1);
assert(TAILQ_EMPTY(&tobjs->conn_pub.service_streams)); /* No need to close stream yet */
@ -592,7 +593,7 @@ test_rem_FIN_loc_FIN (struct test_objs *tobjs)
/* Pretend we sent out a packet: */
packet_out = lsquic_send_ctl_next_packet_to_send(&tobjs->send_ctl);
lsquic_send_ctl_sent_packet(&tobjs->send_ctl, packet_out);
lsquic_send_ctl_sent_packet(&tobjs->send_ctl, packet_out, 1);
assert(TAILQ_EMPTY(&tobjs->conn_pub.service_streams)); /* No need to close stream yet */
@ -612,7 +613,7 @@ test_rem_FIN_loc_FIN (struct test_objs *tobjs)
/* Pretend we sent out this packet as well: */
packet_out = lsquic_send_ctl_next_packet_to_send(&tobjs->send_ctl);
lsquic_send_ctl_sent_packet(&tobjs->send_ctl, packet_out);
lsquic_send_ctl_sent_packet(&tobjs->send_ctl, packet_out, 1);
/* Cannot free stream yet: packets have not been acked */
assert(!TAILQ_EMPTY(&tobjs->conn_pub.service_streams));
@ -718,7 +719,7 @@ test_loc_FIN_rem_RST (struct test_objs *tobjs)
/* Pretend we sent out a packet: */
packet_out = lsquic_send_ctl_next_packet_to_send(&tobjs->send_ctl);
lsquic_send_ctl_sent_packet(&tobjs->send_ctl, packet_out);
lsquic_send_ctl_sent_packet(&tobjs->send_ctl, packet_out, 1);
s = lsquic_stream_shutdown(stream, 1);
assert(s == 0);
@ -732,7 +733,7 @@ test_loc_FIN_rem_RST (struct test_objs *tobjs)
/* Pretend we sent out this packet as well: */
packet_out = lsquic_send_ctl_next_packet_to_send(&tobjs->send_ctl);
lsquic_send_ctl_sent_packet(&tobjs->send_ctl, packet_out);
lsquic_send_ctl_sent_packet(&tobjs->send_ctl, packet_out, 1);
assert(TAILQ_EMPTY(&tobjs->conn_pub.service_streams)); /* No need to close stream yet */
@ -804,7 +805,7 @@ test_loc_data_rem_RST (struct test_objs *tobjs)
/* Pretend we sent out a packet: */
packet_out = lsquic_send_ctl_next_packet_to_send(&tobjs->send_ctl);
lsquic_send_ctl_sent_packet(&tobjs->send_ctl, packet_out);
lsquic_send_ctl_sent_packet(&tobjs->send_ctl, packet_out, 1);
s = lsquic_stream_frame_in(stream, new_frame_in(tobjs, 0, 100, 0));
assert(0 == s);
@ -877,7 +878,7 @@ test_loc_RST_rem_FIN (struct test_objs *tobjs)
/* Pretend we sent out a packet: */
packet_out = lsquic_send_ctl_next_packet_to_send(&tobjs->send_ctl);
lsquic_send_ctl_sent_packet(&tobjs->send_ctl, packet_out);
lsquic_send_ctl_sent_packet(&tobjs->send_ctl, packet_out, 1);
assert(1 == stream->n_unacked);
ack_packet(&tobjs->send_ctl, 1);
@ -1569,7 +1570,7 @@ test_window_update1 (void)
/* Pretend we sent out a packet: */
packet_out = lsquic_send_ctl_next_packet_to_send(&tobjs.send_ctl);
lsquic_send_ctl_sent_packet(&tobjs.send_ctl, packet_out);
lsquic_send_ctl_sent_packet(&tobjs.send_ctl, packet_out, 1);
lsquic_stream_window_update(stream, 20);
nw = lsquic_stream_write(stream, "4567890", 7);
@ -1663,13 +1664,15 @@ test_bad_packbits_guess_2 (void)
/* Verify packets */
packet_out = lsquic_send_ctl_next_packet_to_send(&tobjs.send_ctl);
assert(lsquic_packet_out_packno_bits(packet_out) == PACKNO_LEN_1);
assert(lsquic_packet_out_packno_bits(packet_out) == PACKNO_LEN_6);
assert(1 == packet_out->po_packno);
lsquic_send_ctl_sent_packet(&tobjs.send_ctl, packet_out);
assert(packet_out->po_frame_types & (1 << QUIC_FRAME_STREAM));
lsquic_send_ctl_sent_packet(&tobjs.send_ctl, packet_out, 1);
packet_out = lsquic_send_ctl_next_packet_to_send(&tobjs.send_ctl);
assert(lsquic_packet_out_packno_bits(packet_out) == PACKNO_LEN_6);
assert(2 == packet_out->po_packno);
lsquic_send_ctl_sent_packet(&tobjs.send_ctl, packet_out);
assert(packet_out->po_frame_types & (1 << QUIC_FRAME_STREAM));
lsquic_send_ctl_sent_packet(&tobjs.send_ctl, packet_out, 1);
assert(1 == streams[0]->n_unacked);
assert(1 == streams[1]->n_unacked);
@ -1743,13 +1746,15 @@ test_bad_packbits_guess_3 (void)
/* Verify packets */
packet_out = lsquic_send_ctl_next_packet_to_send(&tobjs.send_ctl);
assert(lsquic_packet_out_packno_bits(packet_out) == PACKNO_LEN_1);
assert(lsquic_packet_out_packno_bits(packet_out) == PACKNO_LEN_4);
assert(1 == packet_out->po_packno);
lsquic_send_ctl_sent_packet(&tobjs.send_ctl, packet_out);
assert(packet_out->po_frame_types & (1 << QUIC_FRAME_STREAM));
lsquic_send_ctl_sent_packet(&tobjs.send_ctl, packet_out, 1);
packet_out = lsquic_send_ctl_next_packet_to_send(&tobjs.send_ctl);
assert(lsquic_packet_out_packno_bits(packet_out) == PACKNO_LEN_4);
assert(2 == packet_out->po_packno);
lsquic_send_ctl_sent_packet(&tobjs.send_ctl, packet_out);
assert(packet_out->po_frame_types & (1 << QUIC_FRAME_STREAM));
lsquic_send_ctl_sent_packet(&tobjs.send_ctl, packet_out, 1);
assert(2 == streams[0]->n_unacked);
ack_packet(&tobjs.send_ctl, 1);
@ -1964,7 +1969,7 @@ test_window_update2 (void)
/* Pretend we sent out a packet: */
packet_out = lsquic_send_ctl_next_packet_to_send(&tobjs.send_ctl);
lsquic_send_ctl_sent_packet(&tobjs.send_ctl, packet_out);
lsquic_send_ctl_sent_packet(&tobjs.send_ctl, packet_out, 1);
lsquic_stream_window_update(stream, 20);
nw = lsquic_stream_write(stream, "4567890", 7);
@ -2048,7 +2053,7 @@ test_forced_flush_when_conn_blocked (void)
static int
my_gen_stream_frame_err (unsigned char *buf, size_t bufsz,
uint32_t stream_id, uint64_t offset,
gsf_fin_f fin, gsf_size_f size, gsf_read_f read,
int fin, size_t size, gsf_read_f read,
void *stream)
{
return -1;
@ -2159,13 +2164,15 @@ test_bad_packbits_guess_1 (void)
/* Verify packets */
packet_out = lsquic_send_ctl_next_packet_to_send(&tobjs.send_ctl);
assert(lsquic_packet_out_packno_bits(packet_out) == PACKNO_LEN_1);
assert(lsquic_packet_out_packno_bits(packet_out) == PACKNO_LEN_6);
assert(1 == packet_out->po_packno);
lsquic_send_ctl_sent_packet(&tobjs.send_ctl, packet_out);
assert(packet_out->po_frame_types & (1 << QUIC_FRAME_STREAM));
lsquic_send_ctl_sent_packet(&tobjs.send_ctl, packet_out, 1);
packet_out = lsquic_send_ctl_next_packet_to_send(&tobjs.send_ctl);
assert(lsquic_packet_out_packno_bits(packet_out) == PACKNO_LEN_6);
assert(2 == packet_out->po_packno);
lsquic_send_ctl_sent_packet(&tobjs.send_ctl, packet_out);
assert(packet_out->po_frame_types & (1 << QUIC_FRAME_STREAM));
lsquic_send_ctl_sent_packet(&tobjs.send_ctl, packet_out, 1);
assert(1 == streams[0]->n_unacked);
assert(1 == streams[1]->n_unacked);

View file

@ -796,21 +796,24 @@ run_test (int i)
for (min = 0; min < test->min_sz; ++min)
{
reset_ctx(test);
len = test->pf->pf_gen_stream_frame(out, min, test->stream_id, test_ctx.test->offset,
stream_tosend_fin, stream_tosend_size, stream_tosend_read, &test_ctx);
len = test->pf->pf_gen_stream_frame(out, min, test->stream_id,
test_ctx.test->offset, stream_tosend_fin(&test_ctx),
stream_tosend_size(&test_ctx), stream_tosend_read, &test_ctx);
assert(-1 == len);
}
/* Test that it succeeds now: */
reset_ctx(test);
len = test->pf->pf_gen_stream_frame(out, min, test->stream_id, test_ctx.test->offset,
stream_tosend_fin, stream_tosend_size, stream_tosend_read, &test_ctx);
len = test->pf->pf_gen_stream_frame(out, min, test->stream_id,
test_ctx.test->offset, stream_tosend_fin(&test_ctx),
stream_tosend_size(&test_ctx), stream_tosend_read, &test_ctx);
assert(len == (int) min);
}
reset_ctx(test);
len = test->pf->pf_gen_stream_frame(out, test->avail, test->stream_id, test_ctx.test->offset,
stream_tosend_fin, stream_tosend_size, stream_tosend_read, &test_ctx);
len = test->pf->pf_gen_stream_frame(out, test->avail, test->stream_id,
test_ctx.test->offset, stream_tosend_fin(&test_ctx),
stream_tosend_size(&test_ctx), stream_tosend_read, &test_ctx);
if (test->len > 0) {
/* Check parser operation */

View file

@ -8,6 +8,7 @@
#include "lsquic_types.h"
#include "lsquic_alarmset.h"
#include "lsquic_parse.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_in.h"
#include "lsquic.h"