Release 2.4.2

- [BUGFIX] H3 framing: fix zero-byte write when space is available
- [BUGFIX] Don't send STREAM frame when incoming unidirectgional stream
  is closed
- [BUGFIX] Cancel all pending writes by stream reset by a GOAWAY
- [BUGFIX] Fix use-after-free in IETF full conn
- [OPTIMIZATION] Wait for session tickets for two seconds and then drop
  SSL object and crypto streams.
This commit is contained in:
Dmitri Tikhonov 2019-09-23 12:00:10 -04:00
parent 3ff30b2745
commit 65728dc5ee
11 changed files with 320 additions and 109 deletions

View File

@ -1,3 +1,13 @@
2019-09-23
- 2.4.2
- [BUGFIX] H3 framing: fix zero-byte write when space is available
- [BUGFIX] Don't send STREAM frame when incoming unidirectgional stream
is closed
- [BUGFIX] Cancel all pending writes by stream reset by a GOAWAY
- [BUGFIX] Fix use-after-free in IETF full conn
- [OPTIMIZATION] Wait for session tickets for two seconds and then drop
SSL object and crypto streams.
2019-09-18
- 2.4.0
- [FEATURE] QUIC and HTTP/3 Internet Draft 23 support

View File

@ -25,7 +25,7 @@ extern "C" {
#define LSQUIC_MAJOR_VERSION 2
#define LSQUIC_MINOR_VERSION 4
#define LSQUIC_PATCH_VERSION 0
#define LSQUIC_PATCH_VERSION 2
/**
* Engine flags:

View File

@ -47,6 +47,7 @@ static const char *const lsquic_alid2str[] =
[AL_CID_THROT] = "CID_THROT",
[AL_PATH_CHAL_0] = "PATH_CHAL_0",
[AL_PATH_CHAL_1] = "PATH_CHAL_1",
[AL_SESS_TICKET] = "SESS_TICKET",
};

View File

@ -35,6 +35,7 @@ enum alarm_id {
AL_PATH_CHAL,
AL_PATH_CHAL_0 = AL_PATH_CHAL,
AL_PATH_CHAL_1,
AL_SESS_TICKET,
MAX_LSQUIC_ALARMS
};
@ -54,6 +55,7 @@ enum alarm_id_bit {
ALBIT_PATH_CHAL = 1 << AL_PATH_CHAL,
ALBIT_PATH_CHAL_0 = 1 << AL_PATH_CHAL_0,
ALBIT_PATH_CHAL_1 = 1 << AL_PATH_CHAL_1,
ALBIT_SESS_TICKET = 1 << AL_SESS_TICKET,
};

View File

@ -240,6 +240,10 @@ struct conn_iface
const lsquic_cid_t *
(*ci_get_log_cid) (const struct lsquic_conn *);
/* Optional method. Only used by the IETF client code. */
void
(*ci_drop_crypto_streams) (struct lsquic_conn *);
};
#define LSCONN_CCE_BITS 3

View File

@ -2,6 +2,7 @@
#ifndef LSQUIC_ENC_SESS_H
#define LSQUIC_ENC_SESS_H 1
struct lsquic_alarmset;
struct lsquic_engine_public;
struct lsquic_packet_out;
struct lsquic_packet_in;
@ -262,7 +263,8 @@ struct enc_session_funcs_iquic
struct lsquic_conn *, const struct lsquic_cid *,
const struct ver_neg *, void *(crypto_streams)[4],
const struct crypto_stream_if *,
const unsigned char *, size_t);
const unsigned char *, size_t,
struct lsquic_alarmset *);
void
(*esfi_destroy) (enc_session_t *);

View File

@ -44,6 +44,7 @@
#include "lsquic_frab_list.h"
#include "lsquic_tokgen.h"
#include "lsquic_ietf.h"
#include "lsquic_alarmset.h"
#if __GNUC__
# define UNLIKELY(cond) __builtin_expect(cond, 0)
@ -106,6 +107,10 @@ iquic_esf_get_server_cert_chain (enc_session_t *);
static void
maybe_drop_SSL (struct enc_sess_iquic *);
static void
no_sess_ticket (enum alarm_id alarm_id, void *ctx,
lsquic_time_t expiry, lsquic_time_t now);
typedef void (*gen_hp_mask_f)(struct enc_sess_iquic *,
const struct header_prot *, unsigned cliser,
@ -236,6 +241,7 @@ struct enc_sess_iquic
ESI_ALPN_CHECKED = 1 << 8,
ESI_CACHED_INFO = 1 << 9,
ESI_1RTT_ACKED = 1 << 10,
ESI_WANT_TICKET = 1 << 11,
} esi_flags;
enum evp_aead_direction_t
esi_dir[2]; /* client, server */
@ -264,6 +270,8 @@ struct enc_sess_iquic
struct frab_list esi_frals[N_ENC_LEVS];
struct transport_params
esi_peer_tp;
struct lsquic_alarmset
*esi_alset;
};
@ -651,7 +659,8 @@ iquic_esfi_create_client (const char *hostname,
struct lsquic_engine_public *enpub, struct lsquic_conn *lconn,
const lsquic_cid_t *dcid, const struct ver_neg *ver_neg,
void *crypto_streams[4], const struct crypto_stream_if *cryst_if,
const unsigned char *zero_rtt, size_t zero_rtt_sz)
const unsigned char *zero_rtt, size_t zero_rtt_sz,
struct lsquic_alarmset *alset)
{
struct enc_sess_iquic *enc_sess;
@ -720,6 +729,12 @@ iquic_esfi_create_client (const char *hostname,
enc_sess->esi_zero_rtt_sz = 0;
}
if (enc_sess->esi_enpub->enp_stream_if->on_zero_rtt_info)
enc_sess->esi_flags |= ESI_WANT_TICKET;
enc_sess->esi_alset = alset;
lsquic_alarmset_init_alarm(enc_sess->esi_alset, AL_SESS_TICKET,
no_sess_ticket, enc_sess);
return enc_sess;
}
@ -1192,6 +1207,8 @@ iquic_new_session_cb (SSL *ssl, SSL_SESSION *session)
enc_sess->esi_enpub->enp_stream_if->on_zero_rtt_info(enc_sess->esi_conn,
buf, buf_sz);
free(buf);
enc_sess->esi_flags &= ~ESI_WANT_TICKET;
lsquic_alarmset_unset(enc_sess->esi_alset, AL_SESS_TICKET);
return 0;
}
@ -2202,6 +2219,20 @@ cache_info (struct enc_sess_iquic *enc_sess)
}
static void
drop_SSL (struct enc_sess_iquic *enc_sess)
{
LSQ_DEBUG("drop SSL object");
if (enc_sess->esi_conn->cn_if->ci_drop_crypto_streams)
enc_sess->esi_conn->cn_if->ci_drop_crypto_streams(
enc_sess->esi_conn);
cache_info(enc_sess);
SSL_free(enc_sess->esi_ssl);
enc_sess->esi_ssl = NULL;
free_handshake_keys(enc_sess);
}
static void
maybe_drop_SSL (struct enc_sess_iquic *enc_sess)
{
@ -2216,15 +2247,32 @@ maybe_drop_SSL (struct enc_sess_iquic *enc_sess)
&& enc_sess->esi_ssl
&& lsquic_frab_list_empty(&enc_sess->esi_frals[ENC_LEV_FORW]))
{
LSQ_DEBUG("drop SSL object");
cache_info(enc_sess);
SSL_free(enc_sess->esi_ssl);
enc_sess->esi_ssl = NULL;
free_handshake_keys(enc_sess);
if ((enc_sess->esi_flags & (ESI_SERVER|ESI_WANT_TICKET))
!= ESI_WANT_TICKET)
drop_SSL(enc_sess);
else if (enc_sess->esi_alset
&& !lsquic_alarmset_is_set(enc_sess->esi_alset, AL_SESS_TICKET))
{
LSQ_DEBUG("no session ticket: delay dropping SSL object");
lsquic_alarmset_set(enc_sess->esi_alset, AL_SESS_TICKET,
/* Wait up to two seconds for session tickets */
lsquic_time_now() + 2000000);
}
}
}
static void
no_sess_ticket (enum alarm_id alarm_id, void *ctx,
lsquic_time_t expiry, lsquic_time_t now)
{
struct enc_sess_iquic *enc_sess = ctx;
LSQ_DEBUG("no session tickets forthcoming -- drop SSL");
drop_SSL(enc_sess);
}
typedef char enums_have_the_same_value[
(int) ssl_encryption_initial == (int) ENC_LEV_CLEAR &&
(int) ssl_encryption_early_data == (int) ENC_LEV_EARLY &&
@ -2583,23 +2631,43 @@ readf_cb (void *ctx, const unsigned char *buf, size_t len, int fin)
}
static size_t
discard_cb (void *ctx, const unsigned char *buf, size_t len, int fin)
{
return len;
}
static void
chsk_ietf_on_read (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx)
{
struct enc_sess_iquic *const enc_sess = (void *) ctx;
enum enc_level enc_level = enc_sess->esi_cryst_if->csi_enc_level(stream);
struct readf_ctx readf_ctx = { enc_sess, enc_level, 0, };
ssize_t nread;
ssize_t nread = enc_sess->esi_cryst_if->csi_readf(stream,
readf_cb, &readf_ctx);
if (!(nread < 0 || readf_ctx.err))
iquic_esfi_shake_stream((enc_session_t *)enc_sess, stream, "on_read");
if (enc_sess->esi_ssl)
{
nread = enc_sess->esi_cryst_if->csi_readf(stream, readf_cb, &readf_ctx);
if (!(nread < 0 || readf_ctx.err))
iquic_esfi_shake_stream((enc_session_t *)enc_sess, stream,
"on_read");
else
enc_sess->esi_conn->cn_if->ci_internal_error(enc_sess->esi_conn,
"shaking stream failed: nread: %zd, err: %d, SSL err: %"PRIu32,
nread, readf_ctx.err, ERR_get_error());
}
else
enc_sess->esi_conn->cn_if->ci_internal_error(enc_sess->esi_conn,
"shaking stream failed: nread: %zd, err: %d, SSL err: %"PRIu32,
nread, readf_ctx.err, ERR_get_error());
{
/* This branch is reached when we don't want TLS ticket and drop
* the SSL object before we process TLS tickets that have been
* already received and waiting in the incoming stream buffer.
*/
nread = enc_sess->esi_cryst_if->csi_readf(stream, discard_cb, NULL);
lsquic_stream_wantread(stream, 0);
LSQ_DEBUG("no SSL object: discard %zd bytes of SSL data", nread);
}
}

View File

@ -123,6 +123,7 @@ enum ifull_conn_flags
IFC_GOAWAY_CLOSE = 1 << 23,
IFC_FIRST_TICK = 1 << 24,
IFC_IGNORE_HSK = 1 << 25,
IFC_PROC_CRYPTO = 1 << 26,
};
@ -359,9 +360,9 @@ struct ietf_full_conn
unsigned ifc_last_retire_prior_to;
lsquic_time_t ifc_last_live_update;
struct conn_path ifc_paths[N_PATHS];
struct lsquic_stream *ifc_crypto_streams[N_ENC_LEVS];
union {
struct {
struct lsquic_stream *crypto_streams[N_ENC_LEVS];
struct ver_neg
ifcli_ver_neg;
uint64_t ifcli_max_push_id;
@ -1035,8 +1036,8 @@ lsquic_ietf_full_conn_client_new (struct lsquic_engine_public *enpub,
conn->ifc_conn.cn_esf.i->esfi_create_client(hostname,
conn->ifc_enpub, &conn->ifc_conn, CUR_DCID(conn),
&conn->ifc_u.cli.ifcli_ver_neg,
(void **) conn->ifc_crypto_streams, &crypto_stream_if,
zero_rtt, zero_rtt_sz);
(void **) conn->ifc_u.cli.crypto_streams, &crypto_stream_if,
zero_rtt, zero_rtt_sz, &conn->ifc_alset);
if (!conn->ifc_conn.cn_enc_session)
{
/* TODO: free other stuff */
@ -1044,17 +1045,17 @@ lsquic_ietf_full_conn_client_new (struct lsquic_engine_public *enpub,
return NULL;
}
conn->ifc_crypto_streams[ENC_LEV_CLEAR] = lsquic_stream_new_crypto(
conn->ifc_u.cli.crypto_streams[ENC_LEV_CLEAR] = lsquic_stream_new_crypto(
ENC_LEV_CLEAR, &conn->ifc_pub, &lsquic_cry_sm_if,
conn->ifc_conn.cn_enc_session,
SCF_IETF|SCF_DI_AUTOSWITCH|SCF_CALL_ON_NEW|SCF_CRITICAL);
if (!conn->ifc_crypto_streams[ENC_LEV_CLEAR])
if (!conn->ifc_u.cli.crypto_streams[ENC_LEV_CLEAR])
{
/* TODO: free other stuff */
free(conn);
return NULL;
}
if (!lsquic_stream_get_ctx(conn->ifc_crypto_streams[ENC_LEV_CLEAR]))
if (!lsquic_stream_get_ctx(conn->ifc_u.cli.crypto_streams[ENC_LEV_CLEAR]))
{
/* TODO: free other stuff */
free(conn);
@ -1065,9 +1066,10 @@ lsquic_ietf_full_conn_client_new (struct lsquic_engine_public *enpub,
if (!conn->ifc_pub.packet_out_malo)
{
free(conn);
lsquic_stream_destroy(conn->ifc_crypto_streams[ENC_LEV_CLEAR]);
lsquic_stream_destroy(conn->ifc_u.cli.crypto_streams[ENC_LEV_CLEAR]);
return NULL;
}
conn->ifc_flags |= IFC_PROC_CRYPTO;
LSQ_DEBUG("negotiating version %s",
lsquic_ver2str[conn->ifc_u.cli.ifcli_ver_neg.vn_ver]);
@ -2322,6 +2324,32 @@ ietf_full_conn_ci_retire_cid (struct lsquic_conn *lconn)
}
static void
drop_crypto_streams (struct ietf_full_conn *conn)
{
struct lsquic_stream **streamp;
unsigned count;
if (!(conn->ifc_flags & IFC_PROC_CRYPTO))
return;
conn->ifc_flags &= ~IFC_PROC_CRYPTO;
count = 0;
for (streamp = conn->ifc_u.cli.crypto_streams; streamp <
conn->ifc_u.cli.crypto_streams + sizeof(conn->ifc_u.cli.crypto_streams)
/ sizeof(conn->ifc_u.cli.crypto_streams[0]); ++streamp)
if (*streamp)
{
lsquic_stream_force_finish(*streamp);
*streamp = NULL;
++count;
}
LSQ_DEBUG("dropped %u crypto stream%.*s", count, count != 1, "s");
}
static void
ietf_full_conn_ci_destroy (struct lsquic_conn *lconn)
{
@ -2332,11 +2360,15 @@ ietf_full_conn_ci_destroy (struct lsquic_conn *lconn)
struct lsquic_hash_elem *el;
unsigned i;
for (streamp = conn->ifc_crypto_streams; streamp <
conn->ifc_crypto_streams + sizeof(conn->ifc_crypto_streams)
/ sizeof(conn->ifc_crypto_streams[0]); ++streamp)
if (*streamp)
lsquic_stream_destroy(*streamp);
if (!(conn->ifc_flags & IFC_SERVER))
{
for (streamp = conn->ifc_u.cli.crypto_streams; streamp <
conn->ifc_u.cli.crypto_streams
+ sizeof(conn->ifc_u.cli.crypto_streams)
/ sizeof(conn->ifc_u.cli.crypto_streams[0]); ++streamp)
if (*streamp)
lsquic_stream_destroy(*streamp);
}
while ((el = lsquic_hash_first(conn->ifc_pub.all_streams)))
{
stream = lsquic_hashelem_getdata(el);
@ -3366,9 +3398,10 @@ process_crypto_stream_read_events (struct ietf_full_conn *conn)
{
struct lsquic_stream **stream;
for (stream = conn->ifc_crypto_streams; stream <
conn->ifc_crypto_streams + sizeof(conn->ifc_crypto_streams)
/ sizeof(conn->ifc_crypto_streams[0]); ++stream)
assert(!(conn->ifc_flags & IFC_SERVER));
for (stream = conn->ifc_u.cli.crypto_streams; stream <
conn->ifc_u.cli.crypto_streams + sizeof(conn->ifc_u.cli.crypto_streams)
/ sizeof(conn->ifc_u.cli.crypto_streams[0]); ++stream)
if (*stream && (*stream)->sm_qflags & SMQF_WANT_READ)
lsquic_stream_dispatch_read_events(*stream);
}
@ -3379,9 +3412,10 @@ process_crypto_stream_write_events (struct ietf_full_conn *conn)
{
struct lsquic_stream **stream;
for (stream = conn->ifc_crypto_streams; stream <
conn->ifc_crypto_streams + sizeof(conn->ifc_crypto_streams)
/ sizeof(conn->ifc_crypto_streams[0]); ++stream)
assert(!(conn->ifc_flags & IFC_SERVER));
for (stream = conn->ifc_u.cli.crypto_streams; stream <
conn->ifc_u.cli.crypto_streams + sizeof(conn->ifc_u.cli.crypto_streams)
/ sizeof(conn->ifc_u.cli.crypto_streams[0]); ++stream)
if (*stream && (*stream)->sm_qflags & SMQF_WRITE_Q_FLAGS)
lsquic_stream_dispatch_write_events(*stream);
}
@ -4260,9 +4294,8 @@ process_stop_sending_frame (struct ietf_full_conn *conn,
}
/* Ignore CRYPTO frames in server mode */
static unsigned
process_crypto_frame_server (struct ietf_full_conn *conn,
discard_crypto_frame (struct ietf_full_conn *conn,
struct lsquic_packet_in *packet_in, const unsigned char *p, size_t len)
{
struct stream_frame stream_frame;
@ -4271,14 +4304,17 @@ process_crypto_frame_server (struct ietf_full_conn *conn,
parsed_len = conn->ifc_conn.cn_pf->pf_parse_crypto_frame(p, len,
&stream_frame);
if (parsed_len > 0)
{
LSQ_DEBUG("discard %d-byte CRYPTO frame", parsed_len);
return (unsigned) parsed_len;
}
else
return 0;
}
static unsigned
process_crypto_frame_client (struct ietf_full_conn *conn,
process_crypto_frame (struct ietf_full_conn *conn,
struct lsquic_packet_in *packet_in, const unsigned char *p, size_t len)
{
struct stream_frame *stream_frame;
@ -4286,6 +4322,12 @@ process_crypto_frame_client (struct ietf_full_conn *conn,
enum enc_level enc_level;
int parsed_len;
/* Ignore CRYPTO frames in server mode and in client mode after SSL object
* is gone.
*/
if (!(conn->ifc_flags & IFC_PROC_CRYPTO))
return discard_crypto_frame(conn, packet_in, p, len);
stream_frame = lsquic_malo_get(conn->ifc_pub.mm->malo.stream_frame);
if (!stream_frame)
{
@ -4317,8 +4359,9 @@ process_crypto_frame_client (struct ietf_full_conn *conn,
return parsed_len;
}
if (conn->ifc_crypto_streams[enc_level])
stream = conn->ifc_crypto_streams[enc_level];
assert(!(conn->ifc_flags & IFC_SERVER));
if (conn->ifc_u.cli.crypto_streams[enc_level])
stream = conn->ifc_u.cli.crypto_streams[enc_level];
else
{
stream = lsquic_stream_new_crypto(enc_level, &conn->ifc_pub,
@ -4330,7 +4373,7 @@ process_crypto_frame_client (struct ietf_full_conn *conn,
ABORT_WARN("cannot create crypto stream for level %u", enc_level);
return 0;
}
conn->ifc_crypto_streams[enc_level] = stream;
conn->ifc_u.cli.crypto_streams[enc_level] = stream;
(void) lsquic_stream_wantread(stream, 1);
}
@ -4357,17 +4400,6 @@ process_crypto_frame_client (struct ietf_full_conn *conn,
}
static unsigned
process_crypto_frame (struct ietf_full_conn *conn,
struct lsquic_packet_in *packet_in, const unsigned char *p, size_t len)
{
if (conn->ifc_flags & IFC_SERVER)
return process_crypto_frame_server(conn, packet_in, p, len);
else
return process_crypto_frame_client(conn, packet_in, p, len);
}
static unsigned
process_stream_frame (struct ietf_full_conn *conn,
struct lsquic_packet_in *packet_in, const unsigned char *p, size_t len)
@ -5419,11 +5451,12 @@ ignore_init (struct ietf_full_conn *conn)
lsquic_alarmset_unset(&conn->ifc_alset, AL_ACK_INIT + PNS_INIT);
lsquic_send_ctl_empty_pns(&conn->ifc_send_ctl, PNS_INIT);
lsquic_rechist_cleanup(&conn->ifc_rechist[PNS_INIT]);
if (conn->ifc_crypto_streams[ENC_LEV_CLEAR])
{
lsquic_stream_destroy(conn->ifc_crypto_streams[ENC_LEV_CLEAR]);
conn->ifc_crypto_streams[ENC_LEV_CLEAR] = NULL;
}
if (!(conn->ifc_flags & IFC_SERVER))
if (conn->ifc_u.cli.crypto_streams[ENC_LEV_CLEAR])
{
lsquic_stream_destroy(conn->ifc_u.cli.crypto_streams[ENC_LEV_CLEAR]);
conn->ifc_u.cli.crypto_streams[ENC_LEV_CLEAR] = NULL;
}
}
@ -5436,11 +5469,12 @@ ignore_hsk (struct ietf_full_conn *conn)
lsquic_alarmset_unset(&conn->ifc_alset, AL_ACK_HSK);
lsquic_send_ctl_empty_pns(&conn->ifc_send_ctl, PNS_HSK);
lsquic_rechist_cleanup(&conn->ifc_rechist[PNS_HSK]);
if (conn->ifc_crypto_streams[ENC_LEV_INIT])
{
lsquic_stream_destroy(conn->ifc_crypto_streams[ENC_LEV_INIT]);
conn->ifc_crypto_streams[ENC_LEV_INIT] = NULL;
}
if (!(conn->ifc_flags & IFC_SERVER))
if (conn->ifc_u.cli.crypto_streams[ENC_LEV_INIT])
{
lsquic_stream_destroy(conn->ifc_u.cli.crypto_streams[ENC_LEV_INIT]);
conn->ifc_u.cli.crypto_streams[ENC_LEV_INIT] = NULL;
}
}
@ -6385,6 +6419,14 @@ ietf_full_conn_ci_record_addrs (struct lsquic_conn *lconn, void *peer_ctx,
}
static void
ietf_full_conn_ci_drop_crypto_streams (struct lsquic_conn *lconn)
{
struct ietf_full_conn *conn = (struct ietf_full_conn *) lconn;
drop_crypto_streams(conn);
}
static const struct conn_iface ietf_full_conn_iface = {
.ci_abort = ietf_full_conn_ci_abort,
.ci_abort_error = ietf_full_conn_ci_abort_error,
@ -6395,6 +6437,7 @@ static const struct conn_iface ietf_full_conn_iface = {
.ci_close = ietf_full_conn_ci_close,
.ci_destroy = ietf_full_conn_ci_destroy,
.ci_drain_time = ietf_full_conn_ci_drain_time,
.ci_drop_crypto_streams = ietf_full_conn_ci_drop_crypto_streams,
.ci_get_ctx = ietf_full_conn_ci_get_ctx,
.ci_get_engine = ietf_full_conn_ci_get_engine,
.ci_get_log_cid = ietf_full_conn_ci_get_log_cid,
@ -6763,10 +6806,7 @@ apply_uni_stream_class (struct ietf_full_conn *conn,
maybe_schedule_ss_for_stream(conn, stream->id,
HEC_REQUEST_CANCELLED);
}
if (stream->sm_hash_el.qhe_flags & QHE_HASHED)
lsquic_hash_erase(conn->ifc_pub.all_streams, &stream->sm_hash_el);
assert((stream->sm_qflags & SMQF_WANT_READ) == SMQF_WANT_READ);
lsquic_stream_destroy(stream);
lsquic_stream_close(stream);
break;
default:
LSQ_DEBUG("unknown unidirectional stream %"PRIu64 " of type %"PRIu64
@ -6779,10 +6819,7 @@ apply_uni_stream_class (struct ietf_full_conn *conn,
*/
maybe_schedule_ss_for_stream(conn, stream->id,
HEC_STREAM_CREATION_ERROR);
if (stream->sm_hash_el.qhe_flags & QHE_HASHED)
lsquic_hash_erase(conn->ifc_pub.all_streams, &stream->sm_hash_el);
assert((stream->sm_qflags & SMQF_WANT_READ) == SMQF_WANT_READ);
lsquic_stream_destroy(stream);
lsquic_stream_close(stream);
break;
}
}

View File

@ -113,7 +113,10 @@ static enum swtp_status
stream_write_to_packet_crypto (struct frame_gen_ctx *fg_ctx, const size_t size);
static size_t
stream_write_avail (struct lsquic_stream *);
stream_write_avail_no_frames (struct lsquic_stream *);
static size_t
stream_write_avail_with_frames (struct lsquic_stream *);
static size_t
stream_write_avail_with_headers (struct lsquic_stream *);
@ -151,6 +154,9 @@ on_write_pp_wrapper (struct lsquic_stream *, lsquic_stream_ctx_t *);
static void
stream_hq_frame_put (struct lsquic_stream *, struct stream_hq_frame *);
static size_t
stream_hq_frame_size (const struct stream_hq_frame *);
const struct stream_filter_if hq_stream_filter_if =
{
.sfi_readable = hq_filter_readable,
@ -352,7 +358,7 @@ stream_new_common (lsquic_stream_id_t id, struct lsquic_conn_public *conn_pub,
stream->stream_if = stream_if;
stream->conn_pub = conn_pub;
stream->sm_onnew_arg = stream_if_ctx;
stream->sm_write_avail = stream_write_avail;
stream->sm_write_avail = stream_write_avail_no_frames;
STAILQ_INIT(&stream->sm_hq_frames);
@ -606,20 +612,26 @@ stream_is_finished (const lsquic_stream_t *stream)
}
/* This is an internal function */
void
lsquic_stream_force_finish (struct lsquic_stream *stream)
{
LSQ_DEBUG("stream is now finished");
SM_HISTORY_APPEND(stream, SHE_FINISHED);
if (0 == (stream->sm_qflags & SMQF_SERVICE_FLAGS))
TAILQ_INSERT_TAIL(&stream->conn_pub->service_streams, stream,
next_service_stream);
stream->sm_qflags |= SMQF_FREE_STREAM;
stream->stream_flags |= STREAM_FINISHED;
}
static void
maybe_finish_stream (lsquic_stream_t *stream)
{
if (0 == (stream->stream_flags & STREAM_FINISHED) &&
stream_is_finished(stream))
{
LSQ_DEBUG("stream is now finished");
SM_HISTORY_APPEND(stream, SHE_FINISHED);
if (0 == (stream->sm_qflags & SMQF_SERVICE_FLAGS))
TAILQ_INSERT_TAIL(&stream->conn_pub->service_streams, stream,
next_service_stream);
stream->sm_qflags |= SMQF_FREE_STREAM;
stream->stream_flags |= STREAM_FINISHED;
}
lsquic_stream_force_finish(stream);
}
@ -718,10 +730,9 @@ lsquic_stream_readable (struct lsquic_stream *stream)
static size_t
stream_write_avail (struct lsquic_stream *stream)
stream_write_avail_no_frames (struct lsquic_stream *stream)
{
uint64_t stream_avail, conn_avail;
size_t hq_frames_sz;
stream_avail = stream->max_send_off - stream->tosend_off
- stream->sm_n_buffered;
@ -733,20 +744,47 @@ stream_write_avail (struct lsquic_stream *stream)
stream_avail = conn_avail;
}
if ((stream->sm_bflags & (SMBF_IETF|SMBF_USE_HEADERS))
== (SMBF_IETF|SMBF_USE_HEADERS))
{
hq_frames_sz = active_hq_frame_sizes(stream);
if (hq_frames_sz == 0)
hq_frames_sz = 3; /* Smallest new frame */
return stream_avail;
}
if (stream_avail > hq_frames_sz)
stream_avail -= hq_frames_sz;
else
stream_avail = 0;
static size_t
stream_write_avail_with_frames (struct lsquic_stream *stream)
{
uint64_t stream_avail, conn_avail;
const struct stream_hq_frame *shf;
size_t size;
stream_avail = stream->max_send_off - stream->tosend_off
- stream->sm_n_buffered;
STAILQ_FOREACH(shf, &stream->sm_hq_frames, shf_next)
if (!(shf->shf_flags & SHF_WRITTEN))
{
size = stream_hq_frame_size(shf);
assert(size <= stream_avail);
stream_avail -= size;
}
if (stream->sm_bflags & SMBF_CONN_LIMITED)
{
conn_avail = lsquic_conn_cap_avail(&stream->conn_pub->conn_cap);
STAILQ_FOREACH(shf, &stream->sm_hq_frames, shf_next)
if (!(shf->shf_flags & SHF_CC_PAID))
{
size = stream_hq_frame_size(shf);
if (size < conn_avail)
conn_avail -= size;
else
return 0;
}
if (conn_avail < stream_avail)
stream_avail = conn_avail;
}
return stream_avail;
if (stream_avail >= 3 /* Smallest new frame */)
return stream_avail;
else
return 0;
}
@ -772,7 +810,7 @@ static size_t
stream_write_avail_with_headers (struct lsquic_stream *stream)
{
if (stream->stream_flags & STREAM_PUSHING)
return stream_write_avail(stream);
return stream_write_avail_with_frames(stream);
switch (stream->sm_send_headers_state)
{
@ -787,7 +825,7 @@ stream_write_avail_with_headers (struct lsquic_stream *stream)
/* fall-through */
default:
assert(SSHS_HBLOCK_SENDING == stream->sm_send_headers_state);
return stream_write_avail(stream);
return stream_write_avail_with_frames(stream);
}
}
@ -1432,6 +1470,24 @@ stream_shutdown_read (lsquic_stream_t *stream)
}
static int
stream_is_incoming_unidir (const struct lsquic_stream *stream)
{
enum stream_id_type sit;
if (stream->sm_bflags & SMBF_IETF)
{
sit = stream->id & SIT_MASK;
if (stream->sm_bflags & SMBF_SERVER)
return sit == SIT_UNI_CLIENT;
else
return sit == SIT_UNI_SERVER;
}
else
return 0;
}
static void
stream_shutdown_write (lsquic_stream_t *stream)
{
@ -1447,6 +1503,7 @@ stream_shutdown_write (lsquic_stream_t *stream)
*/
if (!(stream->sm_bflags & SMBF_CRYPTO)
&& !(stream->stream_flags & (STREAM_FIN_SENT|STREAM_RST_SENT))
&& !stream_is_incoming_unidir(stream)
&& !(stream->sm_qflags & SMQF_SEND_RST))
{
if (stream->sm_n_buffered == 0)
@ -1544,7 +1601,7 @@ fake_reset_unused_stream (lsquic_stream_t *stream)
TAILQ_REMOVE(&stream->conn_pub->sending_streams, stream,
next_send_stream);
stream->sm_qflags &= ~SMQF_SENDING_FLAGS;
drop_buffered_data(stream);
LSQ_DEBUG("fake-reset stream%s",
stream_stalled(stream) ? " (stalled)" : "");
maybe_finish_stream(stream);
@ -1771,7 +1828,7 @@ stream_hblock_sent (struct lsquic_stream *stream)
LSQ_DEBUG("header block has been sent: restore default behavior");
stream->sm_send_headers_state = SSHS_BEGIN;
stream->sm_write_avail = stream_write_avail;
stream->sm_write_avail = stream_write_avail_with_frames;
want_write = !!(stream->sm_qflags & SMQF_WANT_WRITE);
if (want_write != stream->sm_saved_want_write)
@ -2944,7 +3001,7 @@ update_buffered_hq_frames (struct lsquic_stream *stream, size_t len,
struct stream_hq_frame *shf;
uint64_t cur_off, end;
size_t frame_sz;
int extendable;
unsigned extendable;
cur_off = stream->sm_payload + stream->sm_n_buffered;
STAILQ_FOREACH(shf, &stream->sm_hq_frames, shf_next)
@ -2958,12 +3015,13 @@ update_buffered_hq_frames (struct lsquic_stream *stream, size_t len,
if (shf)
{
if (len > end - cur_off)
len = end - cur_off;
if (len > end + extendable - cur_off)
len = end + extendable - cur_off;
frame_sz = stream_hq_frame_size(shf);
}
else if (avail >= 3)
else
{
assert(avail >= 3);
shf = stream_activate_hq_frame(stream, cur_off, HQFT_DATA, 0, len);
/* XXX malloc can fail */
if (len > stream_hq_frame_end(shf) - cur_off)
@ -2974,8 +3032,6 @@ update_buffered_hq_frames (struct lsquic_stream *stream, size_t len,
return 0;
avail -= frame_sz;
}
else
return 0;
if (!(shf->shf_flags & SHF_CC_PAID))
{
@ -3006,6 +3062,15 @@ save_to_buffer (lsquic_stream_t *stream, struct lsquic_reader *reader,
{
size_t avail, n_written, n_allowed;
avail = lsquic_stream_write_avail(stream);
if (avail < len)
len = avail;
if (len == 0)
{
LSQ_DEBUG("zero-byte write (avail: %zu)", avail);
return 0;
}
n_allowed = stream_get_n_allowed(stream);
assert(stream->sm_n_buffered + len <= n_allowed);
@ -3017,10 +3082,6 @@ save_to_buffer (lsquic_stream_t *stream, struct lsquic_reader *reader,
stream->sm_n_allocated = n_allowed;
}
avail = lsquic_stream_write_avail(stream);
if (avail < len)
len = avail;
if ((stream->sm_bflags & (SMBF_IETF|SMBF_USE_HEADERS))
== (SMBF_IETF|SMBF_USE_HEADERS))
len = update_buffered_hq_frames(stream, len, avail);

View File

@ -584,4 +584,7 @@ lsquic_stream_duplicate_push (struct lsquic_stream *, uint64_t push_id);
int
lsquic_stream_push_promise (struct lsquic_stream *, struct push_promise *);
void
lsquic_stream_force_finish (struct lsquic_stream *);
#endif

View File

@ -393,19 +393,31 @@ packetization_write_as_much_as_you_can (lsquic_stream_t *stream,
lsquic_stream_ctx_t *ctx)
{
struct packetization_test_stream_ctx *const pack_ctx = (void *) ctx;
unsigned n_to_write;
unsigned n_to_write, n_sched;
ssize_t n_written;
size_t avail;
int s;
while (pack_ctx->off < pack_ctx->len)
{
n_to_write = calc_n_to_write(pack_ctx->write_size);
n_sched = lsquic_send_ctl_n_scheduled(stream->conn_pub->send_ctl);
if (n_to_write > pack_ctx->len - pack_ctx->off)
n_to_write = pack_ctx->len - pack_ctx->off;
n_written = lsquic_stream_write(stream, pack_ctx->buf + pack_ctx->off,
n_to_write);
if (n_written == 0)
{
if (n_to_write && SSHS_BEGIN == stream->sm_send_headers_state
&& lsquic_send_ctl_can_send(stream->conn_pub->send_ctl))
{
avail = lsquic_stream_write_avail(stream);
assert(avail == 0
|| lsquic_send_ctl_n_scheduled(
stream->conn_pub->send_ctl) > n_sched);
}
break;
}
pack_ctx->off += n_written;
if (pack_ctx->flush_after_each_write)
{
@ -425,16 +437,27 @@ packetization_perform_one_write (lsquic_stream_t *stream,
lsquic_stream_ctx_t *ctx)
{
struct packetization_test_stream_ctx *const pack_ctx = (void *) ctx;
unsigned n_to_write;
unsigned n_to_write, n_sched;
ssize_t n_written;
size_t avail;
int s;
n_to_write = calc_n_to_write(pack_ctx->write_size);
if (n_to_write > pack_ctx->len - pack_ctx->off)
n_to_write = pack_ctx->len - pack_ctx->off;
n_sched = lsquic_send_ctl_n_scheduled(stream->conn_pub->send_ctl);
n_written = lsquic_stream_write(stream, pack_ctx->buf + pack_ctx->off,
n_to_write);
assert(n_written >= 0);
if (n_written == 0 && SSHS_BEGIN == stream->sm_send_headers_state
&& n_to_write
&& lsquic_send_ctl_can_send(stream->conn_pub->send_ctl))
{
avail = lsquic_stream_write_avail(stream);
assert(avail == 0
|| lsquic_send_ctl_n_scheduled(
stream->conn_pub->send_ctl) > n_sched);
}
pack_ctx->off += n_written;
if (pack_ctx->flush_after_each_write)
{