mirror of
https://gitea.invidious.io/iv-org/litespeed-quic.git
synced 2024-08-15 00:53:43 +00:00
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:
parent
7edaabaafe
commit
bfc7bfd842
46 changed files with 1140 additions and 547 deletions
40
CHANGELOG
40
CHANGELOG
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
20
EXAMPLES.txt
20
EXAMPLES.txt
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 *);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) \
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include "lsquic_int_types.h"
|
||||
#include "lsquic_types.h"
|
||||
#include "lsquic_packet_common.h"
|
||||
#include "lsquic_packet_in.h"
|
||||
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
|
24
test/prog.c
24
test/prog.c
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
Loading…
Reference in a new issue