2022-05-06 16:49:46 +00:00
|
|
|
/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc. See LICENSE. */
|
2017-09-22 21:00:03 +00:00
|
|
|
/*
|
|
|
|
* lsquic_full_conn.c -- A "full" connection object has full functionality
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <inttypes.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2018-03-12 22:25:01 +00:00
|
|
|
#ifndef WIN32
|
|
|
|
#include <netinet/in.h>
|
2017-09-22 21:00:03 +00:00
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/time.h>
|
2018-03-12 22:25:01 +00:00
|
|
|
#endif
|
|
|
|
#include <sys/queue.h>
|
2017-09-22 21:00:03 +00:00
|
|
|
|
2019-02-25 14:12:20 +00:00
|
|
|
#include <openssl/ssl.h>
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
#include "lsquic_types.h"
|
2019-09-11 15:27:58 +00:00
|
|
|
#include "lsquic_sizes.h"
|
2017-09-22 21:00:03 +00:00
|
|
|
#include "lsquic.h"
|
|
|
|
#include "lsquic_packet_common.h"
|
2019-09-11 15:27:58 +00:00
|
|
|
#include "lsquic_alarmset.h"
|
|
|
|
#include "lsquic_packet_gquic.h"
|
2017-09-22 21:00:03 +00:00
|
|
|
#include "lsquic_parse.h"
|
|
|
|
#include "lsquic_packet_in.h"
|
|
|
|
#include "lsquic_packet_out.h"
|
|
|
|
#include "lsquic_rechist.h"
|
|
|
|
#include "lsquic_util.h"
|
|
|
|
#include "lsquic_conn_flow.h"
|
|
|
|
#include "lsquic_sfcw.h"
|
2019-09-11 15:27:58 +00:00
|
|
|
#include "lsquic_varint.h"
|
|
|
|
#include "lsquic_hq.h"
|
|
|
|
#include "lsquic_hash.h"
|
2017-09-22 21:00:03 +00:00
|
|
|
#include "lsquic_stream.h"
|
|
|
|
#include "lsquic_senhist.h"
|
|
|
|
#include "lsquic_rtt.h"
|
|
|
|
#include "lsquic_cubic.h"
|
|
|
|
#include "lsquic_pacer.h"
|
2019-09-11 15:27:58 +00:00
|
|
|
#include "lsquic_bw_sampler.h"
|
|
|
|
#include "lsquic_minmax.h"
|
|
|
|
#include "lsquic_bbr.h"
|
2020-09-15 20:42:13 +00:00
|
|
|
#include "lsquic_adaptive_cc.h"
|
2017-09-22 21:00:03 +00:00
|
|
|
#include "lsquic_set.h"
|
|
|
|
#include "lsquic_malo.h"
|
|
|
|
#include "lsquic_chsk_stream.h"
|
2019-09-11 15:27:58 +00:00
|
|
|
#include "lsquic_shsk_stream.h"
|
|
|
|
#include "lshpack.h"
|
2017-10-09 11:52:09 +00:00
|
|
|
#include "lsquic_str.h"
|
|
|
|
#include "lsquic_qtags.h"
|
2019-09-11 15:27:58 +00:00
|
|
|
#include "lsquic_enc_sess.h"
|
2017-09-22 21:00:03 +00:00
|
|
|
#include "lsquic_headers_stream.h"
|
|
|
|
#include "lsquic_frame_common.h"
|
|
|
|
#include "lsquic_frame_reader.h"
|
2018-08-28 13:59:47 +00:00
|
|
|
#include "lsquic_frame_writer.h"
|
|
|
|
#include "lsquic_http1x_if.h"
|
2017-09-22 21:00:03 +00:00
|
|
|
#include "lsquic_mm.h"
|
|
|
|
#include "lsquic_engine_public.h"
|
|
|
|
#include "lsquic_spi.h"
|
|
|
|
#include "lsquic_ev_log.h"
|
|
|
|
#include "lsquic_version.h"
|
2018-08-28 13:59:47 +00:00
|
|
|
#include "lsquic_headers.h"
|
2019-02-25 14:12:20 +00:00
|
|
|
#include "lsquic_handshake.h"
|
2019-10-15 21:02:21 +00:00
|
|
|
#include "lsquic_attq.h"
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
#include "lsquic_conn.h"
|
2020-09-08 15:43:03 +00:00
|
|
|
#include "lsquic_send_ctl.h"
|
2017-09-22 21:00:03 +00:00
|
|
|
#include "lsquic_conn_public.h"
|
|
|
|
#include "lsquic_ver_neg.h"
|
2019-09-11 15:27:58 +00:00
|
|
|
#include "lsquic_mini_conn.h"
|
2017-09-22 21:00:03 +00:00
|
|
|
#include "lsquic_full_conn.h"
|
|
|
|
|
|
|
|
#define LSQUIC_LOGGER_MODULE LSQLM_CONN
|
2019-09-11 15:27:58 +00:00
|
|
|
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(&conn->fc_conn)
|
2017-09-22 21:00:03 +00:00
|
|
|
#include "lsquic_logger.h"
|
|
|
|
|
2019-12-23 21:14:20 +00:00
|
|
|
enum stream_if { STREAM_IF_STD, STREAM_IF_HSK, STREAM_IF_HDR, N_STREAM_IFS };
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
#define MAX_RETR_PACKETS_SINCE_LAST_ACK 2
|
|
|
|
#define ACK_TIMEOUT 25000
|
|
|
|
|
2020-11-24 13:51:36 +00:00
|
|
|
/* Maximum number of ACK ranges that can fit into gQUIC ACK frame */
|
|
|
|
#define MAX_ACK_RANGES 256
|
|
|
|
|
2021-01-08 16:38:46 +00:00
|
|
|
/* HANDSHAKE and HEADERS streams are always open in gQUIC connection */
|
|
|
|
#define N_SPECIAL_STREAMS 2
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
/* IMPORTANT: Keep values of FC_SERVER and FC_HTTP same as LSENG_SERVER
|
|
|
|
* and LSENG_HTTP.
|
|
|
|
*/
|
|
|
|
enum full_conn_flags {
|
|
|
|
FC_SERVER = LSENG_SERVER, /* Server mode */
|
|
|
|
FC_HTTP = LSENG_HTTP, /* HTTP mode */
|
|
|
|
FC_TIMED_OUT = (1 << 2),
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
#define FC_BIT_ERROR 3
|
|
|
|
FC_ERROR = (1 << FC_BIT_ERROR),
|
2017-09-22 21:00:03 +00:00
|
|
|
FC_ABORTED = (1 << 4),
|
|
|
|
FC_CLOSING = (1 << 5), /* Closing */
|
|
|
|
FC_SEND_PING = (1 << 6), /* PING frame scheduled */
|
|
|
|
FC_NSTP = (1 << 7), /* NSTP mode */
|
|
|
|
FC_SEND_GOAWAY = (1 << 8),
|
|
|
|
FC_SEND_WUF = (1 << 9),
|
|
|
|
FC_SEND_STOP_WAITING
|
|
|
|
= (1 <<10),
|
|
|
|
FC_ACK_QUEUED = (1 <<11),
|
|
|
|
FC_ACK_HAD_MISS = (1 <<12), /* Last ACK frame had missing packets. */
|
|
|
|
FC_CREATED_OK = (1 <<13),
|
|
|
|
FC_RECV_CLOSE = (1 <<14), /* Received CONNECTION_CLOSE frame */
|
|
|
|
FC_GOING_AWAY = (1 <<15), /* Do not accept or create new streams */
|
|
|
|
FC_GOAWAY_SENT = (1 <<16), /* Only send GOAWAY once */
|
|
|
|
FC_SUPPORT_PUSH = (1 <<17),
|
|
|
|
FC_GOT_PRST = (1 <<18), /* Received public reset packet */
|
|
|
|
FC_FIRST_TICK = (1 <<19),
|
|
|
|
FC_TICK_CLOSE = (1 <<20), /* We returned TICK_CLOSE */
|
2018-02-26 21:01:16 +00:00
|
|
|
FC_HSK_FAILED = (1 <<21),
|
2018-03-09 19:17:39 +00:00
|
|
|
FC_HAVE_SAVED_ACK = (1 <<22),
|
2018-05-18 14:24:20 +00:00
|
|
|
FC_ABORT_COMPLAINED
|
|
|
|
= (1 <<23),
|
2019-09-11 15:27:58 +00:00
|
|
|
FC_GOT_SREJ = (1 <<24), /* Don't schedule ACK alarm */
|
2020-06-09 15:55:56 +00:00
|
|
|
FC_NOPROG_TIMEOUT = (1 <<25),
|
2017-09-22 21:00:03 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
#define FC_IMMEDIATE_CLOSE_FLAGS \
|
2018-02-26 21:01:16 +00:00
|
|
|
(FC_TIMED_OUT|FC_ERROR|FC_ABORTED|FC_HSK_FAILED)
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
#if LSQUIC_KEEP_STREAM_HISTORY
|
|
|
|
#define KEEP_CLOSED_STREAM_HISTORY 0
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if KEEP_CLOSED_STREAM_HISTORY
|
|
|
|
struct stream_history
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_stream_id_t shist_stream_id;
|
2017-09-22 21:00:03 +00:00
|
|
|
enum stream_flags shist_stream_flags;
|
|
|
|
unsigned char shist_hist_buf[1 << SM_HIST_BITS];
|
|
|
|
};
|
|
|
|
#define SHIST_BITS 5
|
|
|
|
#define SHIST_MASK ((1 << SHIST_BITS) - 1)
|
|
|
|
#endif
|
|
|
|
|
2018-02-26 21:01:16 +00:00
|
|
|
#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;
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_stream_id_t sitr_stream_id;
|
2018-02-26 21:01:16 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
struct full_conn
|
|
|
|
{
|
|
|
|
struct lsquic_conn fc_conn;
|
2019-09-11 15:27:58 +00:00
|
|
|
struct conn_cid_elem fc_cces[2];
|
2017-09-22 21:00:03 +00:00
|
|
|
struct lsquic_rechist fc_rechist;
|
|
|
|
struct {
|
|
|
|
const struct lsquic_stream_if *stream_if;
|
|
|
|
void *stream_if_ctx;
|
|
|
|
} fc_stream_ifs[N_STREAM_IFS];
|
|
|
|
struct lsquic_send_ctl fc_send_ctl;
|
|
|
|
struct lsquic_conn_public fc_pub;
|
|
|
|
lsquic_alarmset_t fc_alset;
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_set64_t fc_closed_stream_ids[2];
|
2017-09-22 21:00:03 +00:00
|
|
|
const struct lsquic_engine_settings
|
|
|
|
*fc_settings;
|
|
|
|
struct lsquic_engine_public *fc_enpub;
|
|
|
|
lsquic_packno_t fc_max_ack_packno;
|
|
|
|
lsquic_packno_t fc_max_swf_packno;
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
lsquic_time_t fc_mem_logged_last;
|
2017-09-22 21:00:03 +00:00
|
|
|
struct {
|
|
|
|
unsigned max_streams_in;
|
|
|
|
unsigned max_streams_out;
|
|
|
|
unsigned max_conn_send;
|
|
|
|
unsigned max_stream_send;
|
|
|
|
} fc_cfg;
|
|
|
|
enum full_conn_flags fc_flags;
|
|
|
|
/* Number ackable packets received since last ACK was sent: */
|
|
|
|
unsigned fc_n_slack_akbl;
|
|
|
|
unsigned fc_n_delayed_streams;
|
|
|
|
unsigned fc_n_cons_unretx;
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_stream_id_t fc_last_stream_id;
|
|
|
|
lsquic_stream_id_t fc_max_peer_stream_id;
|
|
|
|
lsquic_stream_id_t fc_goaway_stream_id;
|
2017-09-22 21:00:03 +00:00
|
|
|
struct ver_neg fc_ver_neg;
|
|
|
|
union {
|
|
|
|
struct client_hsk_ctx client;
|
2019-09-11 15:27:58 +00:00
|
|
|
struct server_hsk_ctx server;
|
2017-09-22 21:00:03 +00:00
|
|
|
} fc_hsk_ctx;
|
2019-01-16 20:13:59 +00:00
|
|
|
#if LSQUIC_CONN_STATS
|
|
|
|
struct conn_stats fc_stats;
|
2020-10-21 13:05:26 +00:00
|
|
|
struct conn_stats *fc_last_stats;
|
2017-09-22 21:00:03 +00:00
|
|
|
#endif
|
|
|
|
#if KEEP_CLOSED_STREAM_HISTORY
|
|
|
|
/* Rolling log of histories of closed streams. Older entries are
|
|
|
|
* overwritten.
|
|
|
|
*/
|
|
|
|
struct stream_history fc_stream_histories[1 << SHIST_BITS];
|
|
|
|
unsigned fc_stream_hist_idx;
|
|
|
|
#endif
|
2018-02-26 21:01:16 +00:00
|
|
|
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;
|
2018-03-09 19:17:39 +00:00
|
|
|
lsquic_time_t fc_saved_ack_received;
|
2019-09-11 15:27:58 +00:00
|
|
|
struct network_path fc_path;
|
2019-10-31 16:21:14 +00:00
|
|
|
unsigned fc_orig_versions; /* Client only */
|
2019-12-23 21:14:20 +00:00
|
|
|
enum enc_level fc_crypto_enc_level;
|
2019-12-30 16:29:05 +00:00
|
|
|
struct ack_info fc_ack;
|
2017-09-22 21:00:03 +00:00
|
|
|
};
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static const struct ver_neg server_ver_neg;
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
|
2018-02-26 21:01:16 +00:00
|
|
|
#define MAX_ERRMSG 256
|
|
|
|
|
2018-03-30 14:57:17 +00:00
|
|
|
#define SET_ERRMSG(conn, ...) do { \
|
2018-02-26 21:01:16 +00:00
|
|
|
if (!(conn)->fc_errmsg) \
|
|
|
|
(conn)->fc_errmsg = malloc(MAX_ERRMSG); \
|
|
|
|
if ((conn)->fc_errmsg) \
|
2018-03-30 14:57:17 +00:00
|
|
|
snprintf((conn)->fc_errmsg, MAX_ERRMSG, __VA_ARGS__); \
|
2018-02-26 21:01:16 +00:00
|
|
|
} while (0)
|
|
|
|
|
2018-05-18 14:24:20 +00:00
|
|
|
#define ABORT_WITH_FLAG(conn, log_level, flag, ...) do { \
|
2018-03-30 14:57:17 +00:00
|
|
|
SET_ERRMSG(conn, __VA_ARGS__); \
|
2018-05-18 14:24:20 +00:00
|
|
|
if (!((conn)->fc_flags & FC_ABORT_COMPLAINED)) \
|
|
|
|
LSQ_LOG(log_level, "Abort connection: " __VA_ARGS__); \
|
|
|
|
(conn)->fc_flags |= flag|FC_ABORT_COMPLAINED; \
|
2017-09-22 21:00:03 +00:00
|
|
|
} while (0)
|
|
|
|
|
2018-05-18 14:24:20 +00:00
|
|
|
#define ABORT_ERROR(...) \
|
|
|
|
ABORT_WITH_FLAG(conn, LSQ_LOG_ERROR, FC_ERROR, __VA_ARGS__)
|
|
|
|
#define ABORT_WARN(...) \
|
|
|
|
ABORT_WITH_FLAG(conn, LSQ_LOG_WARN, FC_ERROR, __VA_ARGS__)
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
static void
|
2019-09-11 15:27:58 +00:00
|
|
|
idle_alarm_expired (enum alarm_id, void *ctx, lsquic_time_t expiry, lsquic_time_t now);
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
static void
|
2019-09-11 15:27:58 +00:00
|
|
|
ping_alarm_expired (enum alarm_id, void *ctx, lsquic_time_t expiry, lsquic_time_t now);
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
static void
|
2019-09-11 15:27:58 +00:00
|
|
|
handshake_alarm_expired (enum alarm_id, void *ctx, lsquic_time_t expiry, lsquic_time_t now);
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
static void
|
2019-09-11 15:27:58 +00:00
|
|
|
ack_alarm_expired (enum alarm_id, void *ctx, lsquic_time_t expiry, lsquic_time_t now);
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
static lsquic_stream_t *
|
2019-09-11 15:27:58 +00:00
|
|
|
new_stream (struct full_conn *conn, lsquic_stream_id_t stream_id,
|
|
|
|
enum stream_ctor_flags);
|
2017-09-22 21:00:03 +00:00
|
|
|
|
2019-12-23 21:14:20 +00:00
|
|
|
static struct lsquic_stream *
|
|
|
|
new_stream_ext (struct full_conn *, lsquic_stream_id_t, enum stream_if,
|
|
|
|
enum stream_ctor_flags);
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
static void
|
|
|
|
reset_ack_state (struct full_conn *conn);
|
|
|
|
|
2018-02-26 21:01:16 +00:00
|
|
|
static int
|
|
|
|
write_is_possible (struct full_conn *);
|
|
|
|
|
2018-03-30 14:57:17 +00:00
|
|
|
static const struct headers_stream_callbacks *headers_callbacks_ptr;
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
#if KEEP_CLOSED_STREAM_HISTORY
|
|
|
|
|
|
|
|
static void
|
|
|
|
save_stream_history (struct full_conn *conn, const lsquic_stream_t *stream)
|
|
|
|
{
|
|
|
|
sm_hist_idx_t idx;
|
|
|
|
struct stream_history *const shist =
|
|
|
|
&conn->fc_stream_histories[ conn->fc_stream_hist_idx++ & SHIST_MASK ];
|
|
|
|
|
|
|
|
shist->shist_stream_id = stream->id;
|
|
|
|
shist->shist_stream_flags = stream->stream_flags;
|
|
|
|
|
|
|
|
idx = stream->sm_hist_idx & SM_HIST_IDX_MASK;
|
|
|
|
if ('\0' == stream->sm_hist_buf[ idx ])
|
|
|
|
memcpy(shist->shist_hist_buf, stream->sm_hist_buf, idx + 1);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
memcpy(shist->shist_hist_buf,
|
|
|
|
stream->sm_hist_buf + idx, sizeof(stream->sm_hist_buf) - idx);
|
|
|
|
memcpy(shist->shist_hist_buf + sizeof(shist->shist_hist_buf) - idx,
|
|
|
|
stream->sm_hist_buf, idx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static const struct stream_history *
|
2019-09-11 15:27:58 +00:00
|
|
|
find_stream_history (const struct full_conn *conn, lsquic_stream_id_t stream_id)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
const struct stream_history *shist;
|
|
|
|
const struct stream_history *const shist_end =
|
|
|
|
conn->fc_stream_histories + (1 << SHIST_BITS);
|
|
|
|
for (shist = conn->fc_stream_histories; shist < shist_end; ++shist)
|
|
|
|
if (shist->shist_stream_id == stream_id)
|
|
|
|
return shist;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# define SAVE_STREAM_HISTORY(conn, stream) save_stream_history(conn, stream)
|
|
|
|
#else
|
|
|
|
# define SAVE_STREAM_HISTORY(conn, stream)
|
|
|
|
#endif
|
|
|
|
|
2018-02-26 21:01:16 +00:00
|
|
|
#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;
|
|
|
|
}
|
|
|
|
|
2021-02-03 16:05:50 +00:00
|
|
|
|
2018-02-26 21:01:16 +00:00
|
|
|
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;
|
|
|
|
}
|
2021-02-03 16:05:50 +00:00
|
|
|
|
|
|
|
|
2018-02-26 21:01:16 +00:00
|
|
|
#else
|
|
|
|
#define recent_packet_hist_new(conn, out, time)
|
|
|
|
#define recent_packet_hist_frames(conn, out, frames)
|
|
|
|
#endif
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
static unsigned
|
|
|
|
highest_bit_set (unsigned sz)
|
|
|
|
{
|
|
|
|
#if __GNUC__
|
|
|
|
unsigned clz = __builtin_clz(sz);
|
|
|
|
return 31 - clz;
|
|
|
|
#else
|
|
|
|
unsigned n, y;
|
|
|
|
n = 32;
|
|
|
|
y = sz >> 16; if (y) { n -= 16; sz = y; }
|
|
|
|
y = sz >> 8; if (y) { n -= 8; sz = y; }
|
|
|
|
y = sz >> 4; if (y) { n -= 4; sz = y; }
|
|
|
|
y = sz >> 2; if (y) { n -= 2; sz = y; }
|
|
|
|
y = sz >> 1; if (y) return 31 - n + 2;
|
|
|
|
return 31 - n + sz;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
static size_t
|
|
|
|
calc_mem_used (const struct full_conn *conn)
|
|
|
|
{
|
|
|
|
const lsquic_stream_t *stream;
|
|
|
|
const struct lsquic_hash_elem *el;
|
|
|
|
size_t size;
|
|
|
|
|
|
|
|
size = sizeof(*conn);
|
|
|
|
size -= sizeof(conn->fc_send_ctl);
|
|
|
|
size += lsquic_send_ctl_mem_used(&conn->fc_send_ctl);
|
|
|
|
size += lsquic_hash_mem_used(conn->fc_pub.all_streams);
|
|
|
|
size += lsquic_malo_mem_used(conn->fc_pub.packet_out_malo);
|
2019-09-11 15:27:58 +00:00
|
|
|
if (conn->fc_pub.u.gquic.hs)
|
|
|
|
size += lsquic_headers_stream_mem_used(conn->fc_pub.u.gquic.hs);
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
|
|
|
|
for (el = lsquic_hash_first(conn->fc_pub.all_streams); el;
|
|
|
|
el = lsquic_hash_next(conn->fc_pub.all_streams))
|
|
|
|
{
|
|
|
|
stream = lsquic_hashelem_getdata(el);
|
|
|
|
size += lsquic_stream_mem_used(stream);
|
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
size += conn->fc_conn.cn_esf.g->esf_mem_used(conn->fc_conn.cn_enc_session);
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
static void
|
2019-02-11 17:56:57 +00:00
|
|
|
set_versions (struct full_conn *conn, unsigned versions,
|
|
|
|
enum lsquic_version *ver)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
conn->fc_ver_neg.vn_supp = versions;
|
2019-02-11 17:56:57 +00:00
|
|
|
conn->fc_ver_neg.vn_ver = (ver) ? *ver : highest_bit_set(versions);
|
2017-09-22 21:00:03 +00:00
|
|
|
conn->fc_ver_neg.vn_buf = lsquic_ver2tag(conn->fc_ver_neg.vn_ver);
|
|
|
|
conn->fc_conn.cn_version = conn->fc_ver_neg.vn_ver;
|
2018-08-15 19:06:31 +00:00
|
|
|
conn->fc_conn.cn_pf = select_pf_by_ver(conn->fc_ver_neg.vn_ver);
|
2017-09-22 21:00:03 +00:00
|
|
|
LSQ_DEBUG("negotiating version %s",
|
|
|
|
lsquic_ver2str[conn->fc_ver_neg.vn_ver]);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2019-02-11 17:56:57 +00:00
|
|
|
init_ver_neg (struct full_conn *conn, unsigned versions,
|
|
|
|
enum lsquic_version *ver)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-02-11 17:56:57 +00:00
|
|
|
set_versions(conn, versions, ver);
|
2017-09-22 21:00:03 +00:00
|
|
|
conn->fc_ver_neg.vn_tag = &conn->fc_ver_neg.vn_buf;
|
|
|
|
conn->fc_ver_neg.vn_state = VN_START;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* If peer supplies odd values, we abort the connection immediately rather
|
|
|
|
* that wait for it to finish "naturally" due to inability to send things.
|
|
|
|
*/
|
2019-09-11 15:27:58 +00:00
|
|
|
#ifdef NDEBUG
|
|
|
|
static
|
|
|
|
#endif
|
|
|
|
void
|
|
|
|
lsquic_full_conn_on_peer_config (struct full_conn *conn, unsigned peer_cfcw,
|
2017-09-22 21:00:03 +00:00
|
|
|
unsigned peer_sfcw, unsigned max_streams_out)
|
|
|
|
{
|
|
|
|
lsquic_stream_t *stream;
|
|
|
|
struct lsquic_hash_elem *el;
|
|
|
|
|
|
|
|
LSQ_INFO("Applying peer config: cfcw: %u; sfcw: %u; # streams: %u",
|
|
|
|
peer_cfcw, peer_sfcw, max_streams_out);
|
|
|
|
|
|
|
|
if (peer_cfcw < conn->fc_pub.conn_cap.cc_sent)
|
|
|
|
{
|
|
|
|
ABORT_ERROR("peer specified CFCW=%u bytes, which is smaller than "
|
|
|
|
"the amount of data already sent on this connection (%"PRIu64
|
|
|
|
" bytes)", peer_cfcw, conn->fc_pub.conn_cap.cc_sent);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
conn->fc_cfg.max_streams_out = max_streams_out;
|
|
|
|
conn->fc_pub.conn_cap.cc_max = peer_cfcw;
|
|
|
|
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
for (el = lsquic_hash_first(conn->fc_pub.all_streams); el;
|
|
|
|
el = lsquic_hash_next(conn->fc_pub.all_streams))
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
stream = lsquic_hashelem_getdata(el);
|
|
|
|
if (0 != lsquic_stream_set_max_send_off(stream, peer_sfcw))
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
ABORT_ERROR("cannot set peer-supplied SFCW=%u on stream %"PRIu64,
|
2017-09-22 21:00:03 +00:00
|
|
|
peer_sfcw, stream->id);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
conn->fc_cfg.max_stream_send = peer_sfcw;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
send_smhl (const struct full_conn *conn)
|
|
|
|
{
|
|
|
|
uint32_t smhl;
|
|
|
|
return conn->fc_conn.cn_enc_session
|
2019-09-11 15:27:58 +00:00
|
|
|
&& 0 == conn->fc_conn.cn_esf.g->esf_get_peer_setting(
|
2017-10-09 11:52:09 +00:00
|
|
|
conn->fc_conn.cn_enc_session, QTAG_SMHL, &smhl)
|
2017-09-22 21:00:03 +00:00
|
|
|
&& 1 == smhl;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Once handshake has been completed, send settings to peer if appropriate.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
maybe_send_settings (struct full_conn *conn)
|
|
|
|
{
|
|
|
|
struct lsquic_http2_setting settings[2];
|
|
|
|
unsigned n_settings = 0;
|
|
|
|
|
|
|
|
if (conn->fc_settings->es_max_header_list_size && send_smhl(conn))
|
|
|
|
{
|
|
|
|
settings[n_settings].id = SETTINGS_MAX_HEADER_LIST_SIZE;
|
|
|
|
settings[n_settings].value = conn->fc_settings->es_max_header_list_size;
|
|
|
|
LSQ_DEBUG("sending settings SETTINGS_MAX_HEADER_LIST_SIZE=%u",
|
|
|
|
settings[n_settings].value);
|
|
|
|
++n_settings;
|
|
|
|
}
|
|
|
|
if (!(conn->fc_flags & FC_SERVER) && !conn->fc_settings->es_support_push)
|
|
|
|
{
|
|
|
|
settings[n_settings].id = SETTINGS_ENABLE_PUSH;
|
|
|
|
settings[n_settings].value = 0;
|
|
|
|
LSQ_DEBUG("sending settings SETTINGS_ENABLE_PUSH=%u",
|
|
|
|
settings[n_settings].value);
|
|
|
|
++n_settings;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (n_settings)
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
if (0 != lsquic_headers_stream_send_settings(conn->fc_pub.u.gquic.hs,
|
2017-09-22 21:00:03 +00:00
|
|
|
settings, n_settings))
|
|
|
|
ABORT_ERROR("could not send settings");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
LSQ_DEBUG("not sending any settings");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
apply_peer_settings (struct full_conn *conn)
|
|
|
|
{
|
|
|
|
uint32_t cfcw, sfcw, mids;
|
|
|
|
unsigned n;
|
|
|
|
const struct {
|
|
|
|
uint32_t tag;
|
|
|
|
uint32_t *val;
|
|
|
|
const char *tag_str;
|
|
|
|
} tags[] = {
|
|
|
|
{ QTAG_CFCW, &cfcw, "CFCW", },
|
|
|
|
{ QTAG_SFCW, &sfcw, "SFCW", },
|
|
|
|
{ QTAG_MIDS, &mids, "MIDS", },
|
|
|
|
};
|
|
|
|
|
|
|
|
#ifndef NDEBUG
|
|
|
|
if (getenv("LSQUIC_TEST_ENGINE_DTOR"))
|
|
|
|
return 0;
|
|
|
|
#endif
|
|
|
|
|
2019-12-30 16:29:05 +00:00
|
|
|
for (n = 0; n < sizeof(tags) / sizeof(tags[0]); ++n)
|
|
|
|
if (0 != conn->fc_conn.cn_esf.g->esf_get_peer_setting(
|
|
|
|
conn->fc_conn.cn_enc_session, tags[n].tag, tags[n].val))
|
2019-09-11 15:27:58 +00:00
|
|
|
{
|
2019-12-30 16:29:05 +00:00
|
|
|
LSQ_INFO("peer did not supply value for %s", tags[n].tag_str);
|
|
|
|
return -1;
|
2019-09-11 15:27:58 +00:00
|
|
|
}
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
LSQ_DEBUG("peer settings: CFCW: %u; SFCW: %u; MIDS: %u",
|
|
|
|
cfcw, sfcw, mids);
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_full_conn_on_peer_config(conn, cfcw, sfcw, mids);
|
2017-09-22 21:00:03 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-02-03 16:05:50 +00:00
|
|
|
|
2018-03-30 14:57:17 +00:00
|
|
|
static const struct conn_iface *full_conn_iface_ptr;
|
2017-09-22 21:00:03 +00:00
|
|
|
|
2019-12-23 21:14:20 +00:00
|
|
|
|
|
|
|
/* gQUIC up to version Q046 has handshake stream 1 and headers stream 3.
|
|
|
|
* Q050 and later have "crypto streams" -- meaning CRYPTO frames, not
|
|
|
|
* STREAM frames and no stream IDs -- and headers stream 1.
|
|
|
|
*/
|
|
|
|
static lsquic_stream_id_t
|
|
|
|
headers_stream_id_by_ver (enum lsquic_version version)
|
|
|
|
{
|
|
|
|
if (version < LSQVER_050)
|
|
|
|
return 3;
|
|
|
|
else
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static lsquic_stream_id_t
|
|
|
|
headers_stream_id_by_conn (const struct full_conn *conn)
|
|
|
|
{
|
|
|
|
return headers_stream_id_by_ver(conn->fc_conn.cn_version);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static lsquic_stream_id_t
|
|
|
|
hsk_stream_id (const struct full_conn *conn)
|
|
|
|
{
|
|
|
|
if (conn->fc_conn.cn_version < LSQVER_050)
|
|
|
|
return 1;
|
|
|
|
else
|
|
|
|
/* Use this otherwise invalid stream ID as ID for the gQUIC crypto
|
|
|
|
* stream.
|
|
|
|
*/
|
|
|
|
return (uint64_t) -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
has_handshake_stream (const struct full_conn *conn)
|
|
|
|
{
|
|
|
|
return conn->fc_conn.cn_version < LSQVER_050;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
is_handshake_stream_id (const struct full_conn *conn,
|
|
|
|
lsquic_stream_id_t stream_id)
|
|
|
|
{
|
|
|
|
return conn->fc_conn.cn_version < LSQVER_050 && stream_id == 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-03-30 14:57:17 +00:00
|
|
|
static struct full_conn *
|
|
|
|
new_conn_common (lsquic_cid_t cid, struct lsquic_engine_public *enpub,
|
2019-12-23 21:14:20 +00:00
|
|
|
unsigned flags, enum lsquic_version version)
|
2018-03-30 14:57:17 +00:00
|
|
|
{
|
|
|
|
struct full_conn *conn;
|
|
|
|
lsquic_stream_t *headers_stream;
|
|
|
|
int saved_errno;
|
2017-09-22 21:00:03 +00:00
|
|
|
|
2018-03-30 14:57:17 +00:00
|
|
|
assert(0 == (flags & ~(FC_SERVER|FC_HTTP)));
|
2017-09-22 21:00:03 +00:00
|
|
|
|
2018-03-30 14:57:17 +00:00
|
|
|
conn = calloc(1, sizeof(*conn));
|
|
|
|
if (!conn)
|
|
|
|
return NULL;
|
|
|
|
headers_stream = NULL;
|
2019-09-11 15:27:58 +00:00
|
|
|
conn->fc_conn.cn_if = full_conn_iface_ptr;
|
|
|
|
conn->fc_conn.cn_cces = conn->fc_cces;
|
|
|
|
conn->fc_conn.cn_cces_mask = 1;
|
2018-03-30 14:57:17 +00:00
|
|
|
conn->fc_conn.cn_cid = cid;
|
|
|
|
conn->fc_flags = flags;
|
|
|
|
conn->fc_enpub = enpub;
|
|
|
|
conn->fc_pub.enpub = enpub;
|
|
|
|
conn->fc_pub.mm = &enpub->enp_mm;
|
|
|
|
conn->fc_pub.lconn = &conn->fc_conn;
|
|
|
|
conn->fc_pub.send_ctl = &conn->fc_send_ctl;
|
2019-01-16 20:13:59 +00:00
|
|
|
#if LSQUIC_CONN_STATS
|
|
|
|
conn->fc_pub.conn_stats = &conn->fc_stats;
|
|
|
|
#endif
|
2018-03-30 14:57:17 +00:00
|
|
|
conn->fc_pub.packet_out_malo =
|
|
|
|
lsquic_malo_create(sizeof(struct lsquic_packet_out));
|
2019-09-11 15:27:58 +00:00
|
|
|
conn->fc_pub.path = &conn->fc_path;
|
2020-11-18 14:05:15 +00:00
|
|
|
conn->fc_pub.max_peer_ack_usec = ACK_TIMEOUT;
|
2019-09-11 15:27:58 +00:00
|
|
|
conn->fc_stream_ifs[STREAM_IF_STD].stream_if = enpub->enp_stream_if;
|
|
|
|
conn->fc_stream_ifs[STREAM_IF_STD].stream_if_ctx = enpub->enp_stream_if_ctx;
|
2018-03-30 14:57:17 +00:00
|
|
|
conn->fc_settings = &enpub->enp_settings;
|
|
|
|
/* Calculate maximum number of incoming streams using the same mechanism
|
|
|
|
* and parameters as found in Chrome:
|
|
|
|
*/
|
|
|
|
conn->fc_cfg.max_streams_in =
|
|
|
|
(unsigned) ((float) enpub->enp_settings.es_max_streams_in * 1.1f);
|
|
|
|
if (conn->fc_cfg.max_streams_in <
|
|
|
|
enpub->enp_settings.es_max_streams_in + 10)
|
|
|
|
conn->fc_cfg.max_streams_in =
|
|
|
|
enpub->enp_settings.es_max_streams_in + 10;
|
|
|
|
/* `max_streams_out' gets reset when handshake is complete and we
|
|
|
|
* learn of peer settings. 100 seems like a sane default value
|
|
|
|
* because it is what other implementations use. In server mode,
|
|
|
|
* we do not open any streams until the handshake is complete; in
|
|
|
|
* client mode, we are limited to 98 outgoing requests alongside
|
|
|
|
* handshake and headers streams.
|
|
|
|
*/
|
|
|
|
conn->fc_cfg.max_streams_out = 100;
|
|
|
|
TAILQ_INIT(&conn->fc_pub.sending_streams);
|
|
|
|
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);
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_alarmset_init(&conn->fc_alset, &conn->fc_conn);
|
2018-03-30 14:57:17 +00:00
|
|
|
lsquic_alarmset_init_alarm(&conn->fc_alset, AL_IDLE, idle_alarm_expired, conn);
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_alarmset_init_alarm(&conn->fc_alset, AL_ACK_APP, ack_alarm_expired, conn);
|
2018-03-30 14:57:17 +00:00
|
|
|
lsquic_alarmset_init_alarm(&conn->fc_alset, AL_PING, ping_alarm_expired, conn);
|
|
|
|
lsquic_alarmset_init_alarm(&conn->fc_alset, AL_HANDSHAKE, handshake_alarm_expired, conn);
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_set64_init(&conn->fc_closed_stream_ids[0]);
|
|
|
|
lsquic_set64_init(&conn->fc_closed_stream_ids[1]);
|
2018-03-30 14:57:17 +00:00
|
|
|
lsquic_cfcw_init(&conn->fc_pub.cfcw, &conn->fc_pub, conn->fc_settings->es_cfcw);
|
|
|
|
lsquic_send_ctl_init(&conn->fc_send_ctl, &conn->fc_alset, conn->fc_enpub,
|
2019-09-11 15:27:58 +00:00
|
|
|
flags & FC_SERVER ? &server_ver_neg : &conn->fc_ver_neg,
|
|
|
|
&conn->fc_pub, 0);
|
2018-03-30 14:57:17 +00:00
|
|
|
|
|
|
|
conn->fc_pub.all_streams = lsquic_hash_create();
|
|
|
|
if (!conn->fc_pub.all_streams)
|
|
|
|
goto cleanup_on_error;
|
2020-11-24 13:51:36 +00:00
|
|
|
lsquic_rechist_init(&conn->fc_rechist, 0, MAX_ACK_RANGES);
|
2018-03-30 14:57:17 +00:00
|
|
|
if (conn->fc_flags & FC_HTTP)
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
conn->fc_pub.u.gquic.hs = lsquic_headers_stream_new(
|
2018-08-28 13:59:47 +00:00
|
|
|
!!(conn->fc_flags & FC_SERVER), conn->fc_enpub,
|
2019-01-16 20:13:59 +00:00
|
|
|
headers_callbacks_ptr,
|
|
|
|
#if LSQUIC_CONN_STATS
|
|
|
|
&conn->fc_stats,
|
|
|
|
#endif
|
|
|
|
conn);
|
2019-09-11 15:27:58 +00:00
|
|
|
if (!conn->fc_pub.u.gquic.hs)
|
2018-03-30 14:57:17 +00:00
|
|
|
goto cleanup_on_error;
|
|
|
|
conn->fc_stream_ifs[STREAM_IF_HDR].stream_if = lsquic_headers_stream_if;
|
2019-09-11 15:27:58 +00:00
|
|
|
conn->fc_stream_ifs[STREAM_IF_HDR].stream_if_ctx = conn->fc_pub.u.gquic.hs;
|
2019-12-23 21:14:20 +00:00
|
|
|
headers_stream = new_stream_ext(conn, headers_stream_id_by_ver(version),
|
|
|
|
STREAM_IF_HDR,
|
|
|
|
SCF_CALL_ON_NEW|SCF_DI_AUTOSWITCH|SCF_CRITICAL|SCF_HEADERS);
|
2018-03-30 14:57:17 +00:00
|
|
|
if (!headers_stream)
|
|
|
|
goto cleanup_on_error;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
conn->fc_stream_ifs[STREAM_IF_HDR].stream_if = enpub->enp_stream_if;
|
|
|
|
conn->fc_stream_ifs[STREAM_IF_HDR].stream_if_ctx = enpub->enp_stream_if_ctx;
|
2018-03-30 14:57:17 +00:00
|
|
|
}
|
|
|
|
if (conn->fc_settings->es_support_push)
|
|
|
|
conn->fc_flags |= FC_SUPPORT_PUSH;
|
2019-09-11 15:27:58 +00:00
|
|
|
conn->fc_conn.cn_n_cces = sizeof(conn->fc_cces) / sizeof(conn->fc_cces[0]);
|
2020-06-09 15:55:56 +00:00
|
|
|
if (conn->fc_settings->es_noprogress_timeout)
|
|
|
|
conn->fc_flags |= FC_NOPROG_TIMEOUT;
|
2018-03-30 14:57:17 +00:00
|
|
|
return conn;
|
|
|
|
|
|
|
|
cleanup_on_error:
|
|
|
|
saved_errno = errno;
|
|
|
|
|
|
|
|
if (conn->fc_pub.all_streams)
|
|
|
|
lsquic_hash_destroy(conn->fc_pub.all_streams);
|
|
|
|
lsquic_rechist_cleanup(&conn->fc_rechist);
|
|
|
|
if (conn->fc_flags & FC_HTTP)
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
if (conn->fc_pub.u.gquic.hs)
|
|
|
|
lsquic_headers_stream_destroy(conn->fc_pub.u.gquic.hs);
|
2018-03-30 14:57:17 +00:00
|
|
|
if (headers_stream)
|
|
|
|
lsquic_stream_destroy(headers_stream);
|
|
|
|
}
|
|
|
|
memset(conn, 0, sizeof(*conn));
|
|
|
|
free(conn);
|
|
|
|
|
|
|
|
errno = saved_errno;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct lsquic_conn *
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_gquic_full_conn_client_new (struct lsquic_engine_public *enpub,
|
2019-10-31 16:21:14 +00:00
|
|
|
unsigned versions, unsigned flags,
|
2019-02-04 13:59:11 +00:00
|
|
|
const char *hostname, unsigned short max_packet_size,
|
2019-09-11 15:27:58 +00:00
|
|
|
int is_ipv4,
|
2020-07-06 21:35:21 +00:00
|
|
|
const unsigned char *sess_resume, size_t sess_resume_len)
|
2018-03-30 14:57:17 +00:00
|
|
|
{
|
|
|
|
struct full_conn *conn;
|
2020-07-06 21:35:21 +00:00
|
|
|
enum lsquic_version version, sess_resume_version;
|
2018-03-30 14:57:17 +00:00
|
|
|
lsquic_cid_t cid;
|
2019-09-11 15:27:58 +00:00
|
|
|
const struct enc_session_funcs_gquic *esf_g;
|
2018-03-30 14:57:17 +00:00
|
|
|
|
2019-10-31 16:21:14 +00:00
|
|
|
versions &= (~LSQUIC_IETF_VERSIONS & LSQUIC_SUPPORTED_VERSIONS);
|
|
|
|
assert(versions);
|
|
|
|
version = highest_bit_set(versions);
|
2020-07-06 21:35:21 +00:00
|
|
|
if (sess_resume)
|
2019-02-11 17:56:57 +00:00
|
|
|
{
|
2020-07-06 21:35:21 +00:00
|
|
|
sess_resume_version = lsquic_sess_resume_version(sess_resume, sess_resume_len);
|
|
|
|
if (sess_resume_version < N_LSQVER && ((1 << sess_resume_version) & versions))
|
|
|
|
version = sess_resume_version;
|
2019-02-11 17:56:57 +00:00
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
esf_g = select_esf_gquic_by_ver(version);
|
|
|
|
lsquic_generate_cid_gquic(&cid);
|
|
|
|
if (!max_packet_size)
|
|
|
|
{
|
2020-07-29 15:33:52 +00:00
|
|
|
if (enpub->enp_settings.es_base_plpmtu)
|
2020-07-30 19:42:51 +00:00
|
|
|
max_packet_size = enpub->enp_settings.es_base_plpmtu;
|
2020-07-29 15:33:52 +00:00
|
|
|
else if (is_ipv4)
|
2019-09-11 15:27:58 +00:00
|
|
|
max_packet_size = GQUIC_MAX_IPv4_PACKET_SZ;
|
|
|
|
else
|
|
|
|
max_packet_size = GQUIC_MAX_IPv6_PACKET_SZ;
|
|
|
|
}
|
2019-12-23 21:14:20 +00:00
|
|
|
conn = new_conn_common(cid, enpub, flags, version);
|
2018-03-30 14:57:17 +00:00
|
|
|
if (!conn)
|
|
|
|
return NULL;
|
2019-12-23 21:14:20 +00:00
|
|
|
init_ver_neg(conn, versions, &version);
|
2019-09-11 15:27:58 +00:00
|
|
|
conn->fc_path.np_pack_size = max_packet_size;
|
|
|
|
conn->fc_conn.cn_esf_c = select_esf_common_by_ver(version);
|
|
|
|
conn->fc_conn.cn_esf.g = esf_g;
|
2018-03-30 14:57:17 +00:00
|
|
|
conn->fc_conn.cn_enc_session =
|
2019-12-11 14:38:58 +00:00
|
|
|
conn->fc_conn.cn_esf.g->esf_create_client(&conn->fc_conn, hostname,
|
2020-07-06 21:35:21 +00:00
|
|
|
cid, conn->fc_enpub, sess_resume, sess_resume_len);
|
2018-03-30 14:57:17 +00:00
|
|
|
if (!conn->fc_conn.cn_enc_session)
|
|
|
|
{
|
|
|
|
LSQ_WARN("could not create enc session: %s", strerror(errno));
|
|
|
|
conn->fc_conn.cn_if->ci_destroy(&conn->fc_conn);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (conn->fc_flags & FC_HTTP)
|
2019-12-23 21:14:20 +00:00
|
|
|
conn->fc_last_stream_id = headers_stream_id_by_conn(conn); /* Client goes (3?), 5, 7, 9.... */
|
|
|
|
else if (has_handshake_stream(conn))
|
|
|
|
conn->fc_last_stream_id = 1;
|
2018-03-30 14:57:17 +00:00
|
|
|
else
|
2019-12-23 21:14:20 +00:00
|
|
|
conn->fc_last_stream_id = (uint64_t) -1; /* +2 will get us to 1 */
|
2018-03-30 14:57:17 +00:00
|
|
|
conn->fc_hsk_ctx.client.lconn = &conn->fc_conn;
|
|
|
|
conn->fc_hsk_ctx.client.mm = &enpub->enp_mm;
|
|
|
|
conn->fc_hsk_ctx.client.ver_neg = &conn->fc_ver_neg;
|
|
|
|
conn->fc_stream_ifs[STREAM_IF_HSK]
|
|
|
|
.stream_if = &lsquic_client_hsk_stream_if;
|
|
|
|
conn->fc_stream_ifs[STREAM_IF_HSK].stream_if_ctx = &conn->fc_hsk_ctx.client;
|
2019-10-31 16:21:14 +00:00
|
|
|
conn->fc_orig_versions = versions;
|
2018-03-30 14:57:17 +00:00
|
|
|
if (conn->fc_settings->es_handshake_to)
|
|
|
|
lsquic_alarmset_set(&conn->fc_alset, AL_HANDSHAKE,
|
|
|
|
lsquic_time_now() + conn->fc_settings->es_handshake_to);
|
2019-12-23 21:14:20 +00:00
|
|
|
if (!new_stream_ext(conn, hsk_stream_id(conn), STREAM_IF_HSK,
|
|
|
|
SCF_CALL_ON_NEW|SCF_DI_AUTOSWITCH|SCF_CRITICAL|SCF_CRYPTO
|
|
|
|
|(conn->fc_conn.cn_version >= LSQVER_050 ? SCF_CRYPTO_FRAMES : 0)))
|
2018-03-30 14:57:17 +00:00
|
|
|
{
|
|
|
|
LSQ_WARN("could not create handshake stream: %s", strerror(errno));
|
|
|
|
conn->fc_conn.cn_if->ci_destroy(&conn->fc_conn);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
conn->fc_flags |= FC_CREATED_OK;
|
|
|
|
LSQ_INFO("Created new client connection");
|
2019-09-11 15:27:58 +00:00
|
|
|
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "created full connection");
|
2018-03-30 14:57:17 +00:00
|
|
|
return &conn->fc_conn;
|
|
|
|
}
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static void
|
|
|
|
full_conn_ci_client_call_on_new (struct lsquic_conn *lconn)
|
2018-02-26 21:01:16 +00:00
|
|
|
{
|
|
|
|
struct full_conn *const conn = (struct full_conn *) lconn;
|
|
|
|
assert(conn->fc_flags & FC_CREATED_OK);
|
2020-10-07 13:41:26 +00:00
|
|
|
lconn->cn_conn_ctx = conn->fc_stream_ifs[STREAM_IF_STD].stream_if
|
2018-02-26 21:01:16 +00:00
|
|
|
->on_new_conn(conn->fc_stream_ifs[STREAM_IF_STD].stream_if_ctx, lconn);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
/* This function is special in that it peeks into fc_send_ctl. Other functions
|
|
|
|
* should not do that.
|
|
|
|
*/
|
|
|
|
struct lsquic_conn *
|
|
|
|
lsquic_gquic_full_conn_server_new (struct lsquic_engine_public *enpub,
|
|
|
|
unsigned flags, lsquic_conn_t *lconn_mini)
|
|
|
|
{
|
|
|
|
struct full_conn *conn;
|
|
|
|
struct mini_conn *mc;
|
|
|
|
lsquic_conn_t *lconn_full;
|
|
|
|
lsquic_packet_in_t *packet_in;
|
|
|
|
lsquic_packet_out_t *packet_out;
|
|
|
|
lsquic_stream_t *hsk_stream;
|
|
|
|
lsquic_packno_t next_packno;
|
|
|
|
mconn_packno_set_t received;
|
|
|
|
unsigned n;
|
|
|
|
uint32_t tcid0_val;
|
|
|
|
int have_errors = 0, tcid0;
|
|
|
|
int have_outgoing_ack = 0;
|
|
|
|
|
|
|
|
mc = (struct mini_conn *) lconn_mini;
|
2019-12-23 21:14:20 +00:00
|
|
|
conn = new_conn_common(lconn_mini->cn_cid, enpub, flags,
|
|
|
|
lconn_mini->cn_version);
|
2019-09-11 15:27:58 +00:00
|
|
|
if (!conn)
|
|
|
|
return NULL;
|
|
|
|
lconn_full = &conn->fc_conn;
|
|
|
|
conn->fc_last_stream_id = 0; /* Server goes 2, 4, 6.... */
|
|
|
|
if (conn->fc_flags & FC_HTTP)
|
2019-12-23 21:14:20 +00:00
|
|
|
conn->fc_max_peer_stream_id = headers_stream_id_by_conn(conn);
|
|
|
|
else if (has_handshake_stream(conn))
|
|
|
|
conn->fc_max_peer_stream_id = 1;
|
2019-09-11 15:27:58 +00:00
|
|
|
else
|
2019-12-23 21:14:20 +00:00
|
|
|
conn->fc_max_peer_stream_id = (uint64_t) -1;
|
2019-09-11 15:27:58 +00:00
|
|
|
conn->fc_stream_ifs[STREAM_IF_HSK]
|
|
|
|
.stream_if = &lsquic_server_hsk_stream_if;
|
|
|
|
conn->fc_stream_ifs[STREAM_IF_HSK].stream_if_ctx = &conn->fc_hsk_ctx.server;
|
|
|
|
conn->fc_ver_neg.vn_ver = lconn_mini->cn_version;
|
|
|
|
conn->fc_conn.cn_version = lconn_mini->cn_version;
|
|
|
|
conn->fc_conn.cn_pf = lconn_mini->cn_pf;
|
|
|
|
conn->fc_conn.cn_esf_c = lconn_mini->cn_esf_c;
|
|
|
|
conn->fc_conn.cn_esf.g = lconn_mini->cn_esf.g;
|
|
|
|
conn->fc_conn.cn_flags |= LSCONN_VER_SET | LSCONN_SERVER;
|
|
|
|
conn->fc_pub.rtt_stats = mc->mc_rtt_stats;
|
|
|
|
|
|
|
|
conn->fc_hsk_ctx.server.lconn = lconn_full;
|
|
|
|
conn->fc_hsk_ctx.server.enpub = enpub;
|
|
|
|
|
2019-12-23 21:14:20 +00:00
|
|
|
/* TODO Optimize: we don't need an actual crypto stream and handler
|
|
|
|
* on the server side, as we don't do anything with it. We can
|
|
|
|
* throw out appropriate frames earlier.
|
|
|
|
*/
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
/* Adjust offsets in the HANDSHAKE stream: */
|
2019-12-23 21:14:20 +00:00
|
|
|
hsk_stream = new_stream_ext(conn, hsk_stream_id(conn), STREAM_IF_HSK,
|
|
|
|
SCF_CALL_ON_NEW|SCF_DI_AUTOSWITCH|SCF_CRITICAL|SCF_CRYPTO
|
|
|
|
|(conn->fc_conn.cn_version >= LSQVER_050 ? SCF_CRYPTO_FRAMES : 0));
|
2019-09-11 15:27:58 +00:00
|
|
|
if (!hsk_stream)
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("could not create handshake stream: %s", strerror(errno));
|
|
|
|
conn->fc_conn.cn_if->ci_destroy(&conn->fc_conn);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
hsk_stream->tosend_off = mc->mc_write_off;
|
|
|
|
hsk_stream->read_offset = mc->mc_read_off;
|
|
|
|
if (0 != lsquic_stream_update_sfcw(hsk_stream, mc->mc_write_off))
|
|
|
|
{
|
|
|
|
LSQ_WARN("Invalid write offset %u", mc->mc_write_off);
|
|
|
|
++have_errors;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(lconn_full->cn_enc_session == NULL);
|
|
|
|
lconn_full->cn_enc_session = lconn_mini->cn_enc_session;
|
|
|
|
lconn_mini->cn_enc_session = NULL;
|
2019-12-11 14:38:58 +00:00
|
|
|
lconn_full->cn_esf_c->esf_set_conn(lconn_full->cn_enc_session,
|
|
|
|
&conn->fc_conn);
|
2019-09-11 15:27:58 +00:00
|
|
|
|
|
|
|
lsquic_send_ctl_verneg_done(&conn->fc_send_ctl);
|
|
|
|
conn->fc_send_ctl.sc_cur_packno = mc->mc_cur_packno;
|
2019-12-30 16:29:05 +00:00
|
|
|
lsquic_send_ctl_begin_optack_detection(&conn->fc_send_ctl);
|
2019-09-11 15:27:58 +00:00
|
|
|
|
|
|
|
/* Remove those that still exist from the set: they will be marked as
|
|
|
|
* received during regular processing in ci_packet_in() later on.
|
|
|
|
*/
|
|
|
|
received = mc->mc_received_packnos;
|
|
|
|
TAILQ_FOREACH(packet_in, &mc->mc_packets_in, pi_next)
|
|
|
|
received &= ~MCONN_PACKET_MASK(packet_in->pi_packno);
|
|
|
|
|
|
|
|
for (n = 0; received; ++n)
|
|
|
|
{
|
|
|
|
if (received & (1U << n))
|
|
|
|
/* Setting `now' to zero is OK here, as we should have had at
|
|
|
|
* least one other packet above.
|
|
|
|
*/
|
|
|
|
lsquic_rechist_received(&conn->fc_rechist, n + 1, 0);
|
|
|
|
received &= ~(1U << n);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Mini connection sends out packets 1, 2, 3... and so on. It deletes
|
|
|
|
* packets that have been successfully sent and acked or those that have
|
|
|
|
* been lost. We take ownership of all packets in mc_packets_out; those
|
|
|
|
* that are not on the list are recorded in fc_send_ctl.sc_senhist.
|
|
|
|
*/
|
|
|
|
next_packno = 0;
|
|
|
|
while ((packet_out = TAILQ_FIRST(&mc->mc_packets_out)))
|
|
|
|
{
|
|
|
|
TAILQ_REMOVE(&mc->mc_packets_out, packet_out, po_next);
|
|
|
|
|
|
|
|
/* Holes in the sequence signify ACKed or lost packets */
|
|
|
|
++next_packno;
|
|
|
|
for ( ; next_packno < packet_out->po_packno; ++next_packno)
|
|
|
|
lsquic_senhist_add(&conn->fc_send_ctl.sc_senhist, next_packno);
|
|
|
|
|
|
|
|
packet_out->po_path = &conn->fc_path;
|
|
|
|
if (mc->mc_sent_packnos & MCONN_PACKET_MASK(packet_out->po_packno))
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("got sent packet_out %"PRIu64" from mini",
|
|
|
|
packet_out->po_packno);
|
|
|
|
if (0 != lsquic_send_ctl_sent_packet(&conn->fc_send_ctl,
|
|
|
|
packet_out)
|
|
|
|
&& !have_errors /* Warn once */)
|
|
|
|
{
|
|
|
|
++have_errors;
|
|
|
|
LSQ_WARN("could not add packet %"PRIu64" to sent set: %s",
|
|
|
|
packet_out->po_packno, strerror(errno));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("got unsent packet_out %"PRIu64" from mini (will send)",
|
|
|
|
packet_out->po_packno);
|
|
|
|
lsquic_send_ctl_scheduled_one(&conn->fc_send_ctl, packet_out);
|
|
|
|
have_outgoing_ack |= packet_out->po_frame_types &
|
|
|
|
(1 << QUIC_FRAME_ACK);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(lconn_mini->cn_flags & LSCONN_HANDSHAKE_DONE);
|
|
|
|
lconn_full->cn_flags |= LSCONN_HANDSHAKE_DONE;
|
|
|
|
|
|
|
|
lconn_full->cn_flags |= lconn_mini->cn_flags &
|
|
|
|
LSCONN_PEER_GOING_AWAY /* We are OK with fc_goaway_stream_id = 0 */;
|
|
|
|
conn->fc_path = mc->mc_path;
|
|
|
|
|
|
|
|
if (0 == apply_peer_settings(conn))
|
|
|
|
{
|
|
|
|
if (conn->fc_flags & FC_HTTP)
|
|
|
|
maybe_send_settings(conn);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
++have_errors;
|
|
|
|
|
|
|
|
if (0 == have_errors)
|
|
|
|
{
|
|
|
|
tcid0 = conn->fc_settings->es_support_tcid0
|
|
|
|
&& 0 == conn->fc_conn.cn_esf.g->esf_get_peer_setting(
|
|
|
|
conn->fc_conn.cn_enc_session, QTAG_TCID, &tcid0_val)
|
|
|
|
&& 0 == tcid0_val;
|
|
|
|
lsquic_send_ctl_set_tcid0(&conn->fc_send_ctl, tcid0);
|
|
|
|
if (tcid0)
|
|
|
|
conn->fc_conn.cn_flags |= LSCONN_TCID0;
|
|
|
|
conn->fc_flags |= FC_CREATED_OK|FC_FIRST_TICK;
|
|
|
|
if (conn->fc_conn.cn_version >= LSQVER_046
|
|
|
|
|| conn->fc_conn.cn_esf.g->esf_get_peer_option(
|
|
|
|
conn->fc_conn.cn_enc_session, QTAG_NSTP))
|
|
|
|
{
|
|
|
|
conn->fc_flags |= FC_NSTP;
|
|
|
|
lsquic_send_ctl_turn_nstp_on(&conn->fc_send_ctl);
|
|
|
|
}
|
|
|
|
LSQ_DEBUG("Calling on_new_conn callback");
|
2020-10-07 13:41:26 +00:00
|
|
|
lconn_full->cn_conn_ctx = enpub->enp_stream_if->on_new_conn(
|
2019-09-11 15:27:58 +00:00
|
|
|
enpub->enp_stream_if_ctx, &conn->fc_conn);
|
|
|
|
/* Now that user code knows about this connection, process incoming
|
|
|
|
* packets, if any.
|
|
|
|
*/
|
|
|
|
while ((packet_in = TAILQ_FIRST(&mc->mc_packets_in)))
|
|
|
|
{
|
|
|
|
TAILQ_REMOVE(&mc->mc_packets_in, packet_in, pi_next);
|
|
|
|
packet_in->pi_flags |= PI_FROM_MINI;
|
|
|
|
conn->fc_conn.cn_if->ci_packet_in(&conn->fc_conn, packet_in);
|
|
|
|
lsquic_packet_in_put(conn->fc_pub.mm, packet_in);
|
|
|
|
}
|
|
|
|
/* At this point we may have errors, but we promote it anyway: this is
|
|
|
|
* so that CONNECTION_CLOSE frame can be generated and sent out.
|
|
|
|
*/
|
|
|
|
if (have_outgoing_ack)
|
|
|
|
reset_ack_state(conn);
|
|
|
|
lsquic_alarmset_set(&conn->fc_alset, AL_IDLE,
|
|
|
|
lsquic_time_now() + conn->fc_settings->es_idle_conn_to);
|
|
|
|
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "created full connection");
|
|
|
|
LSQ_INFO("Created new server connection");
|
|
|
|
return &conn->fc_conn;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("hit errors creating connection, return NULL");
|
|
|
|
conn->fc_conn.cn_if->ci_destroy(&conn->fc_conn);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
static int
|
|
|
|
is_our_stream (const struct full_conn *conn, const lsquic_stream_t *stream)
|
|
|
|
{
|
|
|
|
int is_server = !!(conn->fc_flags & FC_SERVER);
|
|
|
|
return (1 & stream->id) ^ is_server;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static unsigned
|
|
|
|
count_streams (const struct full_conn *conn, int peer)
|
|
|
|
{
|
|
|
|
const lsquic_stream_t *stream;
|
|
|
|
unsigned count;
|
|
|
|
int ours;
|
|
|
|
int is_server;
|
|
|
|
struct lsquic_hash_elem *el;
|
|
|
|
|
|
|
|
peer = !!peer;
|
|
|
|
is_server = !!(conn->fc_flags & FC_SERVER);
|
|
|
|
count = 0;
|
|
|
|
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
for (el = lsquic_hash_first(conn->fc_pub.all_streams); el;
|
|
|
|
el = lsquic_hash_next(conn->fc_pub.all_streams))
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
stream = lsquic_hashelem_getdata(el);
|
|
|
|
ours = (1 & stream->id) ^ is_server;
|
|
|
|
if (ours ^ peer)
|
2018-05-18 14:24:20 +00:00
|
|
|
count += !(lsquic_stream_is_closed(stream)
|
|
|
|
/* When counting peer-initiated streams, do not
|
|
|
|
* include those that have been reset:
|
|
|
|
*/
|
|
|
|
|| (peer && lsquic_stream_is_reset(stream)));
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-05-18 14:24:20 +00:00
|
|
|
enum stream_count { SCNT_ALL, SCNT_PEER, SCNT_CLOSED, SCNT_RESET,
|
|
|
|
SCNT_RES_UNCLO /* reset and not closed */, N_SCNTS };
|
|
|
|
|
|
|
|
static void
|
|
|
|
collect_stream_counts (const struct full_conn *conn, int peer,
|
|
|
|
unsigned counts[N_SCNTS])
|
|
|
|
{
|
|
|
|
const lsquic_stream_t *stream;
|
|
|
|
int ours;
|
|
|
|
int is_server;
|
|
|
|
struct lsquic_hash_elem *el;
|
|
|
|
|
|
|
|
peer = !!peer;
|
|
|
|
is_server = !!(conn->fc_flags & FC_SERVER);
|
|
|
|
memset(counts, 0, N_SCNTS * sizeof(counts[0]));
|
|
|
|
|
|
|
|
for (el = lsquic_hash_first(conn->fc_pub.all_streams); el;
|
|
|
|
el = lsquic_hash_next(conn->fc_pub.all_streams))
|
|
|
|
{
|
|
|
|
++counts[SCNT_ALL];
|
|
|
|
stream = lsquic_hashelem_getdata(el);
|
|
|
|
ours = (1 & stream->id) ^ is_server;
|
|
|
|
if (ours ^ peer)
|
|
|
|
{
|
|
|
|
++counts[SCNT_PEER];
|
|
|
|
counts[SCNT_CLOSED] += lsquic_stream_is_closed(stream);
|
2019-09-11 15:27:58 +00:00
|
|
|
counts[SCNT_RESET] += !!lsquic_stream_is_reset(stream);
|
2018-05-18 14:24:20 +00:00
|
|
|
counts[SCNT_RES_UNCLO] += lsquic_stream_is_reset(stream)
|
|
|
|
&& !lsquic_stream_is_closed(stream);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
static void
|
|
|
|
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;
|
2018-02-26 21:01:16 +00:00
|
|
|
struct stream_id_to_reset *sitr;
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
LSQ_DEBUG("destroy connection");
|
|
|
|
conn->fc_flags |= FC_CLOSING;
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_set64_cleanup(&conn->fc_closed_stream_ids[0]);
|
|
|
|
lsquic_set64_cleanup(&conn->fc_closed_stream_ids[1]);
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
while ((el = lsquic_hash_first(conn->fc_pub.all_streams)))
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
stream = lsquic_hashelem_getdata(el);
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
lsquic_hash_erase(conn->fc_pub.all_streams, el);
|
2017-09-22 21:00:03 +00:00
|
|
|
lsquic_stream_destroy(stream);
|
|
|
|
}
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
lsquic_hash_destroy(conn->fc_pub.all_streams);
|
2017-09-22 21:00:03 +00:00
|
|
|
if (conn->fc_flags & FC_CREATED_OK)
|
|
|
|
conn->fc_stream_ifs[STREAM_IF_STD].stream_if
|
|
|
|
->on_conn_closed(&conn->fc_conn);
|
2019-09-11 15:27:58 +00:00
|
|
|
if (conn->fc_pub.u.gquic.hs)
|
|
|
|
lsquic_headers_stream_destroy(conn->fc_pub.u.gquic.hs);
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
lsquic_send_ctl_cleanup(&conn->fc_send_ctl);
|
|
|
|
lsquic_rechist_cleanup(&conn->fc_rechist);
|
|
|
|
if (conn->fc_conn.cn_enc_session)
|
2019-09-11 15:27:58 +00:00
|
|
|
conn->fc_conn.cn_esf.g->esf_destroy(conn->fc_conn.cn_enc_session);
|
2017-09-22 21:00:03 +00:00
|
|
|
lsquic_malo_destroy(conn->fc_pub.packet_out_malo);
|
2019-01-16 20:13:59 +00:00
|
|
|
#if LSQUIC_CONN_STATS
|
2018-03-09 19:17:39 +00:00
|
|
|
LSQ_NOTICE("# ticks: %lu", conn->fc_stats.n_ticks);
|
2019-01-16 20:13:59 +00:00
|
|
|
LSQ_NOTICE("received %lu packets, of which %lu were not decryptable, %lu were "
|
|
|
|
"dups and %lu were errors; sent %lu packets, avg stream data per outgoing"
|
2017-09-22 21:00:03 +00:00
|
|
|
" packet is %lu bytes",
|
2019-01-16 20:13:59 +00:00
|
|
|
conn->fc_stats.in.packets, conn->fc_stats.in.undec_packets,
|
|
|
|
conn->fc_stats.in.dup_packets, conn->fc_stats.in.err_packets,
|
|
|
|
conn->fc_stats.out.packets,
|
2020-10-28 12:10:17 +00:00
|
|
|
conn->fc_stats.out.stream_data_sz /
|
|
|
|
(conn->fc_stats.out.packets ? conn->fc_stats.out.packets : 1));
|
2019-12-30 16:29:05 +00:00
|
|
|
LSQ_NOTICE("ACKs: in: %lu; processed: %lu; merged: %lu",
|
2019-01-16 20:13:59 +00:00
|
|
|
conn->fc_stats.in.n_acks, conn->fc_stats.in.n_acks_proc,
|
2019-12-30 16:29:05 +00:00
|
|
|
conn->fc_stats.in.n_acks_merged);
|
2020-10-21 13:05:26 +00:00
|
|
|
free(conn->fc_last_stats);
|
2017-09-22 21:00:03 +00:00
|
|
|
#endif
|
2018-02-26 21:01:16 +00:00
|
|
|
while ((sitr = STAILQ_FIRST(&conn->fc_stream_ids_to_reset)))
|
|
|
|
{
|
|
|
|
STAILQ_REMOVE_HEAD(&conn->fc_stream_ids_to_reset, sitr_next);
|
|
|
|
free(sitr);
|
|
|
|
}
|
2017-09-22 21:00:03 +00:00
|
|
|
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "full connection destroyed");
|
2018-02-26 21:01:16 +00:00
|
|
|
free(conn->fc_errmsg);
|
2017-09-22 21:00:03 +00:00
|
|
|
free(conn);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2019-09-11 15:27:58 +00:00
|
|
|
conn_mark_stream_closed (struct full_conn *conn, lsquic_stream_id_t stream_id)
|
2017-09-22 21:00:03 +00:00
|
|
|
{ /* Because stream IDs are distributed unevenly -- there is a set of odd
|
|
|
|
* stream IDs and a set of even stream IDs -- it is more efficient to
|
|
|
|
* maintain two sets of closed stream IDs.
|
|
|
|
*/
|
|
|
|
int idx = stream_id & 1;
|
|
|
|
stream_id >>= 1;
|
2019-09-11 15:27:58 +00:00
|
|
|
if (0 != lsquic_set64_add(&conn->fc_closed_stream_ids[idx], stream_id))
|
2017-09-22 21:00:03 +00:00
|
|
|
ABORT_ERROR("could not add element to set: %s", strerror(errno));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2019-09-11 15:27:58 +00:00
|
|
|
conn_is_stream_closed (struct full_conn *conn, lsquic_stream_id_t stream_id)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
int idx = stream_id & 1;
|
|
|
|
stream_id >>= 1;
|
2019-09-11 15:27:58 +00:00
|
|
|
return lsquic_set64_has(&conn->fc_closed_stream_ids[idx], stream_id);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
set_ack_timer (struct full_conn *conn, lsquic_time_t now)
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_alarmset_set(&conn->fc_alset, AL_ACK_APP, now + ACK_TIMEOUT);
|
2017-09-22 21:00:03 +00:00
|
|
|
LSQ_DEBUG("ACK alarm set to %"PRIu64, now + ACK_TIMEOUT);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2019-09-11 15:27:58 +00:00
|
|
|
ack_alarm_expired (enum alarm_id al_id, void *ctx, lsquic_time_t expiry,
|
|
|
|
lsquic_time_t now)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
struct full_conn *conn = ctx;
|
|
|
|
LSQ_DEBUG("ACK timer expired (%"PRIu64" < %"PRIu64"): ACK queued",
|
|
|
|
expiry, now);
|
|
|
|
conn->fc_flags |= FC_ACK_QUEUED;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
try_queueing_ack (struct full_conn *conn, int was_missing, lsquic_time_t now)
|
|
|
|
{
|
|
|
|
if (conn->fc_n_slack_akbl >= MAX_RETR_PACKETS_SINCE_LAST_ACK ||
|
|
|
|
((conn->fc_flags & FC_ACK_HAD_MISS) && was_missing) ||
|
|
|
|
lsquic_send_ctl_n_stop_waiting(&conn->fc_send_ctl) > 1)
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_alarmset_unset(&conn->fc_alset, AL_ACK_APP);
|
2017-09-22 21:00:03 +00:00
|
|
|
lsquic_send_ctl_sanity_check(&conn->fc_send_ctl);
|
|
|
|
conn->fc_flags |= FC_ACK_QUEUED;
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("ACK queued: ackable: %u; had_miss: %d; "
|
2017-09-22 21:00:03 +00:00
|
|
|
"was_missing: %d; n_stop_waiting: %u",
|
2019-09-11 15:27:58 +00:00
|
|
|
conn->fc_n_slack_akbl,
|
2017-09-22 21:00:03 +00:00
|
|
|
!!(conn->fc_flags & FC_ACK_HAD_MISS), was_missing,
|
|
|
|
lsquic_send_ctl_n_stop_waiting(&conn->fc_send_ctl));
|
|
|
|
}
|
|
|
|
else if (conn->fc_n_slack_akbl > 0)
|
|
|
|
set_ack_timer(conn, now);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
reset_ack_state (struct full_conn *conn)
|
|
|
|
{
|
|
|
|
conn->fc_n_slack_akbl = 0;
|
|
|
|
lsquic_send_ctl_n_stop_waiting_reset(&conn->fc_send_ctl);
|
|
|
|
conn->fc_flags &= ~FC_ACK_QUEUED;
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_alarmset_unset(&conn->fc_alset, AL_ACK_APP);
|
2017-09-22 21:00:03 +00:00
|
|
|
lsquic_send_ctl_sanity_check(&conn->fc_send_ctl);
|
|
|
|
LSQ_DEBUG("ACK state reset");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-01-21 14:07:02 +00:00
|
|
|
#if 1
|
|
|
|
# define verify_ack_frame(a, b, c)
|
|
|
|
#else
|
|
|
|
static void
|
|
|
|
verify_ack_frame (struct full_conn *conn, const unsigned char *buf, int bufsz)
|
|
|
|
{
|
|
|
|
unsigned i;
|
|
|
|
int parsed_len;
|
|
|
|
struct ack_info *ack_info;
|
|
|
|
const struct lsquic_packno_range *range;
|
|
|
|
char ack_buf[512];
|
|
|
|
unsigned buf_off = 0;
|
|
|
|
int nw;
|
|
|
|
|
|
|
|
ack_info = conn->fc_pub.mm->acki;
|
|
|
|
parsed_len = parse_ack_frame(buf, bufsz, ack_info);
|
|
|
|
assert(parsed_len == bufsz);
|
|
|
|
|
|
|
|
for (range = lsquic_rechist_first(&conn->fc_rechist), i = 0; range;
|
|
|
|
range = lsquic_rechist_next(&conn->fc_rechist), ++i)
|
|
|
|
{
|
|
|
|
assert(i < ack_info->n_ranges);
|
|
|
|
assert(range->high == ack_info->ranges[i].high);
|
|
|
|
assert(range->low == ack_info->ranges[i].low);
|
|
|
|
if (LSQ_LOG_ENABLED(LSQ_LOG_DEBUG))
|
|
|
|
{
|
|
|
|
nw = snprintf(ack_buf + buf_off, sizeof(ack_buf) - buf_off,
|
|
|
|
"[%"PRIu64"-%"PRIu64"]", range->high, range->low);
|
|
|
|
assert(nw >= 0);
|
|
|
|
buf_off += nw;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert(i == ack_info->n_ranges);
|
|
|
|
LSQ_DEBUG("Sent ACK frame %s", ack_buf);
|
|
|
|
}
|
2021-02-03 16:05:50 +00:00
|
|
|
|
|
|
|
|
2019-01-21 14:07:02 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
full_conn_ci_write_ack (struct lsquic_conn *lconn,
|
|
|
|
struct lsquic_packet_out *packet_out)
|
|
|
|
{
|
|
|
|
struct full_conn *conn = (struct full_conn *) lconn;
|
|
|
|
lsquic_time_t now;
|
|
|
|
int has_missing, w;
|
|
|
|
|
|
|
|
now = lsquic_time_now();
|
|
|
|
w = conn->fc_conn.cn_pf->pf_gen_ack_frame(
|
|
|
|
packet_out->po_data + packet_out->po_data_sz,
|
|
|
|
lsquic_packet_out_avail(packet_out),
|
|
|
|
(gaf_rechist_first_f) lsquic_rechist_first,
|
|
|
|
(gaf_rechist_next_f) lsquic_rechist_next,
|
|
|
|
(gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
|
2019-09-11 15:27:58 +00:00
|
|
|
&conn->fc_rechist, now, &has_missing, &packet_out->po_ack2ed,
|
|
|
|
NULL);
|
2019-01-21 14:07:02 +00:00
|
|
|
if (w < 0) {
|
|
|
|
ABORT_ERROR("generating ACK frame failed: %d", errno);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#if LSQUIC_CONN_STATS
|
|
|
|
++conn->fc_stats.out.acks;
|
|
|
|
#endif
|
|
|
|
EV_LOG_GENERATED_ACK_FRAME(LSQUIC_LOG_CONN_ID, conn->fc_conn.cn_pf,
|
|
|
|
packet_out->po_data + packet_out->po_data_sz, w);
|
|
|
|
verify_ack_frame(conn, packet_out->po_data + packet_out->po_data_sz, w);
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_send_ctl_scheduled_ack(&conn->fc_send_ctl, PNS_APP,
|
|
|
|
packet_out->po_ack2ed);
|
2019-01-21 14:07:02 +00:00
|
|
|
packet_out->po_frame_types |= 1 << QUIC_FRAME_ACK;
|
2020-07-29 15:33:52 +00:00
|
|
|
if (0 != lsquic_packet_out_add_frame(packet_out, conn->fc_pub.mm, 0,
|
|
|
|
QUIC_FRAME_ACK, packet_out->po_data_sz, w))
|
|
|
|
{
|
|
|
|
ABORT_ERROR("adding frame to packet failed: %d", errno);
|
|
|
|
return;
|
|
|
|
}
|
2019-01-21 14:07:02 +00:00
|
|
|
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;
|
|
|
|
else
|
|
|
|
conn->fc_flags &= ~FC_ACK_HAD_MISS;
|
|
|
|
LSQ_DEBUG("Put %d bytes of ACK frame into packet on outgoing queue", w);
|
2019-09-11 15:27:58 +00:00
|
|
|
if (conn->fc_n_cons_unretx >= 20 &&
|
2019-01-21 14:07:02 +00:00
|
|
|
!lsquic_send_ctl_have_outgoing_retx_frames(&conn->fc_send_ctl))
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("schedule WINDOW_UPDATE frame after %u non-retx "
|
|
|
|
"packets sent", conn->fc_n_cons_unretx);
|
|
|
|
conn->fc_flags |= FC_SEND_WUF;
|
|
|
|
}
|
|
|
|
reset_ack_state(conn);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
static lsquic_stream_t *
|
2019-12-23 21:14:20 +00:00
|
|
|
new_stream_ext (struct full_conn *conn, lsquic_stream_id_t stream_id,
|
|
|
|
enum stream_if if_idx, enum stream_ctor_flags stream_ctor_flags)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2018-05-04 18:00:34 +00:00
|
|
|
struct lsquic_stream *stream;
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
stream = lsquic_stream_new(stream_id, &conn->fc_pub,
|
2017-09-22 21:00:03 +00:00
|
|
|
conn->fc_stream_ifs[if_idx].stream_if,
|
|
|
|
conn->fc_stream_ifs[if_idx].stream_if_ctx, conn->fc_settings->es_sfcw,
|
2019-12-23 21:14:20 +00:00
|
|
|
stream_ctor_flags & SCF_CRYPTO
|
2019-09-11 15:27:58 +00:00
|
|
|
? 16 * 1024 : conn->fc_cfg.max_stream_send,
|
|
|
|
stream_ctor_flags);
|
2017-09-22 21:00:03 +00:00
|
|
|
if (stream)
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_hash_insert(conn->fc_pub.all_streams, &stream->id,
|
|
|
|
sizeof(stream->id), stream, &stream->sm_hash_el);
|
2017-09-22 21:00:03 +00:00
|
|
|
return stream;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static lsquic_stream_t *
|
2019-09-11 15:27:58 +00:00
|
|
|
new_stream (struct full_conn *conn, lsquic_stream_id_t stream_id,
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
enum stream_ctor_flags flags)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-12-23 21:14:20 +00:00
|
|
|
flags |= SCF_DI_AUTOSWITCH;
|
|
|
|
if (conn->fc_pub.u.gquic.hs)
|
|
|
|
flags |= SCF_HTTP;
|
|
|
|
if (conn->fc_enpub->enp_settings.es_rw_once)
|
|
|
|
flags |= SCF_DISP_RW_ONCE;
|
2020-12-04 16:29:14 +00:00
|
|
|
if (conn->fc_enpub->enp_settings.es_delay_onclose)
|
|
|
|
flags |= SCF_DELAY_ONCLOSE;
|
2019-12-23 21:14:20 +00:00
|
|
|
|
|
|
|
return new_stream_ext(conn, stream_id, STREAM_IF_STD, flags);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static lsquic_stream_id_t
|
2017-09-22 21:00:03 +00:00
|
|
|
generate_stream_id (struct full_conn *conn)
|
|
|
|
{
|
|
|
|
conn->fc_last_stream_id += 2;
|
|
|
|
return conn->fc_last_stream_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static unsigned
|
|
|
|
full_conn_ci_n_pending_streams (const struct lsquic_conn *lconn)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
const struct full_conn *conn = (const struct full_conn *) lconn;
|
2017-09-22 21:00:03 +00:00
|
|
|
return conn->fc_n_delayed_streams;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static unsigned
|
|
|
|
full_conn_ci_cancel_pending_streams (struct lsquic_conn *lconn, unsigned n)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
struct full_conn *conn = (struct full_conn *) lconn;
|
|
|
|
if (n > conn->fc_n_delayed_streams)
|
|
|
|
conn->fc_n_delayed_streams = 0;
|
|
|
|
else
|
|
|
|
conn->fc_n_delayed_streams -= n;
|
|
|
|
return conn->fc_n_delayed_streams;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
either_side_going_away (const struct full_conn *conn)
|
|
|
|
{
|
|
|
|
return (conn->fc_flags & FC_GOING_AWAY)
|
|
|
|
|| (conn->fc_conn.cn_flags & LSCONN_PEER_GOING_AWAY);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static unsigned
|
|
|
|
full_conn_ci_n_avail_streams (const lsquic_conn_t *lconn)
|
2018-10-03 14:32:26 +00:00
|
|
|
{
|
|
|
|
struct full_conn *conn = (struct full_conn *) lconn;
|
|
|
|
unsigned stream_count = count_streams(conn, 0);
|
|
|
|
if (conn->fc_cfg.max_streams_out < stream_count)
|
|
|
|
return 0;
|
|
|
|
return conn->fc_cfg.max_streams_out - stream_count;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-10-15 21:02:21 +00:00
|
|
|
static int
|
2020-07-06 21:35:21 +00:00
|
|
|
handshake_done_or_doing_sess_resume (const struct full_conn *conn)
|
2019-10-15 21:02:21 +00:00
|
|
|
{
|
|
|
|
return (conn->fc_conn.cn_flags & LSCONN_HANDSHAKE_DONE)
|
2020-07-06 21:35:21 +00:00
|
|
|
|| conn->fc_conn.cn_esf_c->esf_is_sess_resume_enabled(
|
2019-10-15 21:02:21 +00:00
|
|
|
conn->fc_conn.cn_enc_session);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static void
|
|
|
|
full_conn_ci_make_stream (struct lsquic_conn *lconn)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
struct full_conn *conn = (struct full_conn *) lconn;
|
2020-07-06 21:35:21 +00:00
|
|
|
if (handshake_done_or_doing_sess_resume(conn)
|
2019-10-15 21:02:21 +00:00
|
|
|
&& full_conn_ci_n_avail_streams(lconn) > 0)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
if (!new_stream(conn, generate_stream_id(conn), SCF_CALL_ON_NEW))
|
2017-09-22 21:00:03 +00:00
|
|
|
ABORT_ERROR("could not create new stream: %s", strerror(errno));
|
|
|
|
}
|
|
|
|
else if (either_side_going_away(conn))
|
|
|
|
(void) conn->fc_stream_ifs[STREAM_IF_STD].stream_if->on_new_stream(
|
|
|
|
conn->fc_stream_ifs[STREAM_IF_STD].stream_if_ctx, NULL);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
++conn->fc_n_delayed_streams;
|
|
|
|
LSQ_DEBUG("delayed stream creation. Backlog size: %u",
|
|
|
|
conn->fc_n_delayed_streams);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static lsquic_stream_t *
|
2019-09-11 15:27:58 +00:00
|
|
|
find_stream_by_id (struct full_conn *conn, lsquic_stream_id_t stream_id)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
struct lsquic_hash_elem *el;
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
el = lsquic_hash_find(conn->fc_pub.all_streams, &stream_id, sizeof(stream_id));
|
2017-09-22 21:00:03 +00:00
|
|
|
if (el)
|
|
|
|
return lsquic_hashelem_getdata(el);
|
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-04-15 13:30:40 +00:00
|
|
|
static struct lsquic_stream *
|
|
|
|
full_conn_ci_get_stream_by_id (struct lsquic_conn *lconn,
|
|
|
|
lsquic_stream_id_t stream_id)
|
|
|
|
{
|
|
|
|
struct full_conn *conn = (struct full_conn *) lconn;
|
2020-05-12 20:50:36 +00:00
|
|
|
struct lsquic_stream *stream;
|
|
|
|
|
|
|
|
stream = find_stream_by_id(conn, stream_id);
|
|
|
|
if (stream && !lsquic_stream_is_closed(stream))
|
|
|
|
return stream;
|
|
|
|
else
|
|
|
|
return NULL;
|
2020-04-15 13:30:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static struct lsquic_engine *
|
|
|
|
full_conn_ci_get_engine (struct lsquic_conn *lconn)
|
2018-02-26 21:01:16 +00:00
|
|
|
{
|
|
|
|
struct full_conn *conn = (struct full_conn *) lconn;
|
|
|
|
return conn->fc_enpub->enp_engine;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static struct network_path *
|
|
|
|
full_conn_ci_get_path (struct lsquic_conn *lconn, const struct sockaddr *sa)
|
|
|
|
{
|
|
|
|
struct full_conn *conn = (struct full_conn *) lconn;
|
|
|
|
|
|
|
|
return &conn->fc_path;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static unsigned char
|
|
|
|
full_conn_ci_record_addrs (struct lsquic_conn *lconn, void *peer_ctx,
|
|
|
|
const struct sockaddr *local_sa, const struct sockaddr *peer_sa)
|
|
|
|
{
|
|
|
|
struct full_conn *conn = (struct full_conn *) lconn;
|
|
|
|
|
|
|
|
if (NP_IS_IPv6(&conn->fc_path) != (AF_INET6 == peer_sa->sa_family))
|
|
|
|
lsquic_send_ctl_return_enc_data(&conn->fc_send_ctl);
|
|
|
|
|
|
|
|
size_t len = peer_sa->sa_family == AF_INET ? sizeof(struct sockaddr_in)
|
|
|
|
: sizeof(struct sockaddr_in6);
|
|
|
|
|
|
|
|
memcpy(conn->fc_path.np_peer_addr, peer_sa, len);
|
|
|
|
|
|
|
|
len = local_sa->sa_family == AF_INET ? sizeof(struct sockaddr_in)
|
|
|
|
: sizeof(struct sockaddr_in6);
|
|
|
|
memcpy(conn->fc_path.np_local_addr, local_sa, len);
|
|
|
|
conn->fc_path.np_peer_ctx = peer_ctx;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-03-30 14:57:17 +00:00
|
|
|
static ptrdiff_t
|
2017-09-22 21:00:03 +00:00
|
|
|
count_zero_bytes (const unsigned char *p, size_t len)
|
|
|
|
{
|
|
|
|
const unsigned char *const end = p + len;
|
|
|
|
while (p < end && 0 == *p)
|
|
|
|
++p;
|
|
|
|
return len - (end - p);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static unsigned
|
|
|
|
process_padding_frame (struct full_conn *conn, lsquic_packet_in_t *packet_in,
|
|
|
|
const unsigned char *p, size_t len)
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
len = (size_t) count_zero_bytes(p, len);
|
|
|
|
EV_LOG_PADDING_FRAME_IN(LSQUIC_LOG_CONN_ID, len);
|
|
|
|
return len;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-09-08 15:43:03 +00:00
|
|
|
static void
|
|
|
|
log_conn_flow_control (struct full_conn *conn)
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("connection flow cap: wrote: %"PRIu64
|
|
|
|
"; max: %"PRIu64, conn->fc_pub.conn_cap.cc_sent,
|
|
|
|
conn->fc_pub.conn_cap.cc_max);
|
|
|
|
LSQ_DEBUG("connection flow control window: read: %"PRIu64
|
|
|
|
"; max: %"PRIu64, conn->fc_pub.cfcw.cf_max_recv_off,
|
|
|
|
conn->fc_pub.cfcw.cf_recv_off);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
static unsigned
|
|
|
|
process_ping_frame (struct full_conn *conn, lsquic_packet_in_t *packet_in,
|
|
|
|
const unsigned char *p, size_t len)
|
|
|
|
{ /* This frame causes ACK frame to be queued, but nothing to do here;
|
|
|
|
* return the length of this frame.
|
|
|
|
*/
|
|
|
|
EV_LOG_PING_FRAME_IN(LSQUIC_LOG_CONN_ID);
|
|
|
|
LSQ_DEBUG("received PING");
|
2020-09-08 15:43:03 +00:00
|
|
|
if (conn->fc_flags & FC_SERVER)
|
|
|
|
log_conn_flow_control(conn);
|
2017-09-22 21:00:03 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2019-09-11 15:27:58 +00:00
|
|
|
is_peer_initiated (const struct full_conn *conn, lsquic_stream_id_t stream_id)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
unsigned is_server = !!(conn->fc_flags & FC_SERVER);
|
|
|
|
int peer_initiated = (stream_id & 1) == is_server;
|
|
|
|
return peer_initiated;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-02-26 21:01:16 +00:00
|
|
|
static void
|
2019-09-11 15:27:58 +00:00
|
|
|
maybe_schedule_reset_for_stream (struct full_conn *conn, lsquic_stream_id_t stream_id)
|
2018-02-26 21:01:16 +00:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
static unsigned
|
|
|
|
process_stream_frame (struct full_conn *conn, lsquic_packet_in_t *packet_in,
|
|
|
|
const unsigned char *p, size_t len)
|
|
|
|
{
|
|
|
|
stream_frame_t *stream_frame;
|
|
|
|
lsquic_stream_t *stream;
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
enum enc_level enc_level;
|
2017-09-22 21:00:03 +00:00
|
|
|
int parsed_len;
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
#ifndef LSQUIC_REDO_FAILED_INSERTION
|
2019-12-23 21:14:20 +00:00
|
|
|
#define LSQUIC_REDO_FAILED_INSERTION 0
|
2019-09-11 15:27:58 +00:00
|
|
|
#endif
|
|
|
|
#if LSQUIC_REDO_FAILED_INSERTION
|
|
|
|
enum lsq_log_level saved_levels[3];
|
|
|
|
#if defined(__GNUC__) && !defined(__clang__)
|
|
|
|
/* gcc complains about this -- incorrectly -- in optimized mode */
|
|
|
|
saved_levels[0] = 0;
|
|
|
|
saved_levels[1] = 0;
|
|
|
|
saved_levels[2] = 0;
|
|
|
|
#endif
|
|
|
|
int again = 0;
|
|
|
|
redo:
|
|
|
|
#endif
|
2017-09-22 21:00:03 +00:00
|
|
|
stream_frame = lsquic_malo_get(conn->fc_pub.mm->malo.stream_frame);
|
|
|
|
if (!stream_frame)
|
|
|
|
{
|
|
|
|
LSQ_WARN("could not allocate stream frame: %s", strerror(errno));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
parsed_len = conn->fc_conn.cn_pf->pf_parse_stream_frame(p, len,
|
|
|
|
stream_frame);
|
|
|
|
if (parsed_len < 0) {
|
|
|
|
lsquic_malo_put(stream_frame);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EV_LOG_STREAM_FRAME_IN(LSQUIC_LOG_CONN_ID, stream_frame);
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("Got stream frame for stream #%"PRIu64, stream_frame->stream_id);
|
2019-01-16 20:13:59 +00:00
|
|
|
#if LSQUIC_CONN_STATS
|
|
|
|
++conn->fc_stats.in.stream_frames;
|
|
|
|
conn->fc_stats.in.stream_data_sz += stream_frame->data_frame.df_size;
|
|
|
|
#endif
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
|
|
|
|
enc_level = lsquic_packet_in_enc_level(packet_in);
|
2019-12-23 21:14:20 +00:00
|
|
|
if (!is_handshake_stream_id(conn, stream_frame->stream_id)
|
|
|
|
&& enc_level == ENC_LEV_CLEAR)
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
{
|
|
|
|
lsquic_malo_put(stream_frame);
|
2019-09-11 15:27:58 +00:00
|
|
|
ABORT_ERROR("received unencrypted data for stream %"PRIu64,
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
stream_frame->stream_id);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
if (conn->fc_flags & FC_CLOSING)
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("Connection closing: ignore frame");
|
|
|
|
lsquic_malo_put(stream_frame);
|
|
|
|
return parsed_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
stream = find_stream_by_id(conn, stream_frame->stream_id);
|
2018-09-12 20:26:19 +00:00
|
|
|
if (stream)
|
|
|
|
{
|
|
|
|
if (lsquic_stream_is_reset(stream))
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("stream %"PRIu64" is reset, ignore frame", stream->id);
|
2018-09-12 20:26:19 +00:00
|
|
|
lsquic_malo_put(stream_frame);
|
|
|
|
return parsed_len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
if (conn_is_stream_closed(conn, stream_frame->stream_id))
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("drop frame for closed stream %"PRIu64,
|
|
|
|
stream_frame->stream_id);
|
2017-09-22 21:00:03 +00:00
|
|
|
lsquic_malo_put(stream_frame);
|
|
|
|
return parsed_len;
|
|
|
|
}
|
|
|
|
if (is_peer_initiated(conn, stream_frame->stream_id))
|
|
|
|
{
|
|
|
|
unsigned in_count = count_streams(conn, 1);
|
|
|
|
LSQ_DEBUG("number of peer-initiated streams: %u", in_count);
|
|
|
|
if (in_count >= conn->fc_cfg.max_streams_in)
|
|
|
|
{
|
2018-05-18 14:24:20 +00:00
|
|
|
if (!(conn->fc_flags & FC_ABORT_COMPLAINED))
|
|
|
|
{
|
2018-05-18 15:34:36 +00:00
|
|
|
unsigned counts[N_SCNTS];
|
2018-05-18 14:24:20 +00:00
|
|
|
collect_stream_counts(conn, 1, counts);
|
|
|
|
ABORT_WARN("incoming stream would exceed limit: %u. "
|
|
|
|
"all: %u; peer: %u; closed: %u; reset: %u; reset "
|
|
|
|
"and not closed: %u", conn->fc_cfg.max_streams_in,
|
|
|
|
counts[SCNT_ALL], counts[SCNT_PEER],
|
|
|
|
counts[SCNT_CLOSED], counts[SCNT_RESET],
|
|
|
|
counts[SCNT_RES_UNCLO]);
|
|
|
|
}
|
2017-09-22 21:00:03 +00:00
|
|
|
lsquic_malo_put(stream_frame);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if ((conn->fc_flags & FC_GOING_AWAY) &&
|
|
|
|
stream_frame->stream_id > conn->fc_max_peer_stream_id)
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("going away: reset new incoming stream %"PRIu64,
|
2017-09-22 21:00:03 +00:00
|
|
|
stream_frame->stream_id);
|
2018-02-26 21:01:16 +00:00
|
|
|
maybe_schedule_reset_for_stream(conn, stream_frame->stream_id);
|
2017-09-22 21:00:03 +00:00
|
|
|
lsquic_malo_put(stream_frame);
|
|
|
|
return parsed_len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ABORT_ERROR("frame for never-initiated stream");
|
|
|
|
lsquic_malo_put(stream_frame);
|
|
|
|
return 0;
|
|
|
|
}
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
stream = new_stream(conn, stream_frame->stream_id, SCF_CALL_ON_NEW);
|
2017-09-22 21:00:03 +00:00
|
|
|
if (!stream)
|
|
|
|
{
|
|
|
|
ABORT_ERROR("cannot create new stream: %s", strerror(errno));
|
|
|
|
lsquic_malo_put(stream_frame);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (stream_frame->stream_id > conn->fc_max_peer_stream_id)
|
|
|
|
conn->fc_max_peer_stream_id = stream_frame->stream_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
stream_frame->packet_in = lsquic_packet_in_get(packet_in);
|
|
|
|
if (0 != lsquic_stream_frame_in(stream, stream_frame))
|
|
|
|
{
|
|
|
|
ABORT_ERROR("cannot insert stream frame");
|
2019-09-11 15:27:58 +00:00
|
|
|
#if LSQUIC_REDO_FAILED_INSERTION
|
|
|
|
if (again++)
|
|
|
|
{
|
|
|
|
lsq_log_levels[LSQLM_STREAM] = saved_levels[0];
|
|
|
|
lsq_log_levels[LSQLM_DI] = saved_levels[1];
|
|
|
|
lsq_log_levels[LSQLM_CONN] = saved_levels[2];
|
|
|
|
}
|
|
|
|
else if (!(LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_STREAM)
|
|
|
|
&& LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_DI)
|
|
|
|
&& LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_CONN)))
|
|
|
|
{
|
|
|
|
saved_levels[0] = lsq_log_levels[LSQLM_STREAM];
|
|
|
|
saved_levels[1] = lsq_log_levels[LSQLM_DI];
|
|
|
|
saved_levels[2] = lsq_log_levels[LSQLM_CONN];
|
|
|
|
lsq_log_levels[LSQLM_STREAM] = LSQ_LOG_DEBUG;
|
|
|
|
lsq_log_levels[LSQLM_DI] = LSQ_LOG_DEBUG;
|
|
|
|
lsq_log_levels[LSQLM_CONN] = LSQ_LOG_DEBUG;
|
|
|
|
lsquic_stream_dump_state(stream);
|
|
|
|
LSQ_DEBUG("inserting frame again, this time with debug logging");
|
|
|
|
goto redo;
|
|
|
|
}
|
|
|
|
#endif
|
2017-09-22 21:00:03 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-12-23 21:14:20 +00:00
|
|
|
if (lsquic_stream_is_crypto(stream)
|
2019-09-11 15:27:58 +00:00
|
|
|
&& (stream->sm_qflags & SMQF_WANT_READ)
|
2018-02-26 21:01:16 +00:00
|
|
|
&& !(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.
|
|
|
|
*/
|
2018-04-09 13:39:38 +00:00
|
|
|
lsquic_stream_dispatch_read_events(stream);
|
2018-02-26 21:01:16 +00:00
|
|
|
}
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
return parsed_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-12-23 21:14:20 +00:00
|
|
|
static unsigned
|
|
|
|
process_crypto_frame (struct full_conn *conn, lsquic_packet_in_t *packet_in,
|
|
|
|
const unsigned char *p, size_t len)
|
|
|
|
{
|
|
|
|
struct lsquic_stream *stream;
|
|
|
|
stream_frame_t *stream_frame;
|
|
|
|
enum enc_level enc_level;
|
|
|
|
int parsed_len;
|
|
|
|
|
|
|
|
stream_frame = lsquic_malo_get(conn->fc_pub.mm->malo.stream_frame);
|
|
|
|
if (!stream_frame)
|
|
|
|
{
|
|
|
|
LSQ_WARN("could not allocate stream frame: %s", strerror(errno));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
parsed_len = conn->fc_conn.cn_pf->pf_parse_crypto_frame(p, len,
|
|
|
|
stream_frame);
|
|
|
|
if (parsed_len < 0)
|
|
|
|
{
|
|
|
|
lsquic_malo_put(stream_frame);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
enc_level = lsquic_packet_in_enc_level(packet_in);
|
|
|
|
EV_LOG_CRYPTO_FRAME_IN(LSQUIC_LOG_CONN_ID, stream_frame, enc_level);
|
|
|
|
LSQ_DEBUG("Got CRYPTO frame on enc level %s", lsquic_enclev2str[enc_level]);
|
|
|
|
|
|
|
|
if (enc_level < conn->fc_crypto_enc_level)
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("Old enc level: ignore frame");
|
|
|
|
lsquic_malo_put(stream_frame);
|
|
|
|
return parsed_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (conn->fc_flags & FC_CLOSING)
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("Connection closing: ignore frame");
|
|
|
|
lsquic_malo_put(stream_frame);
|
|
|
|
return parsed_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
stream = find_stream_by_id(conn, hsk_stream_id(conn));
|
|
|
|
if (!stream)
|
|
|
|
{
|
|
|
|
LSQ_WARN("cannot find handshake stream for CRYPTO frame");
|
|
|
|
lsquic_malo_put(stream_frame);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (enc_level > conn->fc_crypto_enc_level)
|
|
|
|
{
|
|
|
|
stream->read_offset = 0;
|
|
|
|
stream->tosend_off = 0;
|
|
|
|
conn->fc_crypto_enc_level = enc_level;
|
|
|
|
LSQ_DEBUG("reset handshake stream offsets, new enc level %u",
|
|
|
|
(unsigned) enc_level);
|
|
|
|
}
|
|
|
|
|
|
|
|
stream_frame->packet_in = lsquic_packet_in_get(packet_in);
|
|
|
|
if (0 != lsquic_stream_frame_in(stream, stream_frame))
|
|
|
|
{
|
|
|
|
ABORT_ERROR("cannot insert stream frame");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((stream->sm_qflags & SMQF_WANT_READ)
|
|
|
|
&& !(conn->fc_flags & FC_SERVER)
|
|
|
|
&& !(conn->fc_conn.cn_flags & LSCONN_HANDSHAKE_DONE))
|
|
|
|
{
|
|
|
|
/* XXX what happens for server? */
|
|
|
|
lsquic_stream_dispatch_read_events(stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
return parsed_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
static unsigned
|
|
|
|
process_invalid_frame (struct full_conn *conn, lsquic_packet_in_t *packet_in,
|
|
|
|
const unsigned char *p, size_t len)
|
|
|
|
{
|
|
|
|
ABORT_ERROR("invalid frame");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Reset locally-initiated streams whose IDs is larger than the stream ID
|
|
|
|
* specified in received GOAWAY frame.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
reset_local_streams_over_goaway (struct full_conn *conn)
|
|
|
|
{
|
|
|
|
const unsigned is_server = !!(conn->fc_flags & FC_SERVER);
|
|
|
|
lsquic_stream_t *stream;
|
|
|
|
struct lsquic_hash_elem *el;
|
|
|
|
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
for (el = lsquic_hash_first(conn->fc_pub.all_streams); el;
|
|
|
|
el = lsquic_hash_next(conn->fc_pub.all_streams))
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
stream = lsquic_hashelem_getdata(el);
|
2021-01-08 16:38:46 +00:00
|
|
|
if ((int64_t) stream->id > (int64_t) conn->fc_goaway_stream_id &&
|
2017-09-22 21:00:03 +00:00
|
|
|
((stream->id & 1) ^ is_server /* Locally initiated? */))
|
|
|
|
{
|
|
|
|
lsquic_stream_received_goaway(stream);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static unsigned
|
|
|
|
process_goaway_frame (struct full_conn *conn, lsquic_packet_in_t *packet_in,
|
|
|
|
const unsigned char *p, size_t len)
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_stream_id_t stream_id;
|
|
|
|
uint32_t error_code;
|
2017-09-22 21:00:03 +00:00
|
|
|
uint16_t reason_length;
|
|
|
|
const char *reason;
|
|
|
|
const int parsed_len = conn->fc_conn.cn_pf->pf_parse_goaway_frame(p, len,
|
|
|
|
&error_code, &stream_id, &reason_length, &reason);
|
|
|
|
if (parsed_len < 0)
|
|
|
|
return 0;
|
|
|
|
EV_LOG_GOAWAY_FRAME_IN(LSQUIC_LOG_CONN_ID, error_code, stream_id,
|
|
|
|
reason_length, reason);
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("received GOAWAY frame, last good stream ID: %"PRIu64
|
|
|
|
", error code: 0x%X, reason: `%.*s'", stream_id, error_code,
|
|
|
|
reason_length, reason);
|
2017-09-22 21:00:03 +00:00
|
|
|
if (0 == (conn->fc_conn.cn_flags & LSCONN_PEER_GOING_AWAY))
|
|
|
|
{
|
|
|
|
conn->fc_conn.cn_flags |= LSCONN_PEER_GOING_AWAY;
|
|
|
|
conn->fc_goaway_stream_id = stream_id;
|
|
|
|
if (conn->fc_stream_ifs[STREAM_IF_STD].stream_if->on_goaway_received)
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("calling on_goaway_received");
|
|
|
|
conn->fc_stream_ifs[STREAM_IF_STD].stream_if->on_goaway_received(
|
|
|
|
&conn->fc_conn);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
LSQ_DEBUG("on_goaway_received not registered");
|
|
|
|
reset_local_streams_over_goaway(conn);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
LSQ_DEBUG("ignore duplicate GOAWAY frame");
|
|
|
|
return parsed_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
log_invalid_ack_frame (struct full_conn *conn, const unsigned char *p,
|
|
|
|
int parsed_len, const struct ack_info *acki)
|
|
|
|
{
|
|
|
|
char *buf;
|
|
|
|
|
|
|
|
buf = malloc(0x1000);
|
2019-12-30 16:29:05 +00:00
|
|
|
if (!buf)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
LSQ_WARN("malloc failed");
|
2019-12-30 16:29:05 +00:00
|
|
|
return;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
2019-12-30 16:29:05 +00:00
|
|
|
|
|
|
|
lsquic_senhist_tostr(&conn->fc_send_ctl.sc_senhist, buf, 0x1000);
|
|
|
|
LSQ_WARN("send history: %s", buf);
|
|
|
|
lsquic_hexdump(p, parsed_len, buf, 0x1000);
|
|
|
|
LSQ_WARN("raw ACK frame:\n%s", buf);
|
|
|
|
lsquic_acki2str(acki, buf, 0x1000);
|
|
|
|
LSQ_WARN("parsed ACK frame: %s", buf);
|
|
|
|
free(buf);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-03-09 19:17:39 +00:00
|
|
|
static int
|
|
|
|
process_ack (struct full_conn *conn, struct ack_info *acki,
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_time_t received, lsquic_time_t now)
|
2018-03-09 19:17:39 +00:00
|
|
|
{
|
2019-01-16 20:13:59 +00:00
|
|
|
#if LSQUIC_CONN_STATS
|
|
|
|
++conn->fc_stats.in.n_acks_proc;
|
2018-03-09 19:17:39 +00:00
|
|
|
#endif
|
|
|
|
LSQ_DEBUG("Processing ACK");
|
2019-09-11 15:27:58 +00:00
|
|
|
if (0 == lsquic_send_ctl_got_ack(&conn->fc_send_ctl, acki, received, now))
|
2018-03-09 19:17:39 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
if (lsquic_send_ctl_largest_ack2ed(&conn->fc_send_ctl, PNS_APP))
|
2018-03-09 19:17:39 +00:00
|
|
|
lsquic_rechist_stop_wait(&conn->fc_rechist,
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_send_ctl_largest_ack2ed(&conn->fc_send_ctl, PNS_APP)
|
|
|
|
+ 1);
|
2018-03-09 19:17:39 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ABORT_ERROR("Received invalid ACK");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
static unsigned
|
|
|
|
process_ack_frame (struct full_conn *conn, lsquic_packet_in_t *packet_in,
|
|
|
|
const unsigned char *p, size_t len)
|
|
|
|
{
|
2019-12-30 16:29:05 +00:00
|
|
|
struct ack_info *new_acki;
|
2018-03-09 19:17:39 +00:00
|
|
|
int parsed_len;
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_time_t warn_time;
|
2018-03-09 19:17:39 +00:00
|
|
|
|
2019-01-16 20:13:59 +00:00
|
|
|
#if LSQUIC_CONN_STATS
|
|
|
|
++conn->fc_stats.in.n_acks;
|
2018-03-09 19:17:39 +00:00
|
|
|
#endif
|
|
|
|
|
2019-12-30 16:29:05 +00:00
|
|
|
if (conn->fc_flags & FC_HAVE_SAVED_ACK)
|
|
|
|
new_acki = conn->fc_pub.mm->acki;
|
|
|
|
else
|
|
|
|
new_acki = &conn->fc_ack;
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
parsed_len = conn->fc_conn.cn_pf->pf_parse_ack_frame(p, len, new_acki, 0);
|
2017-09-22 21:00:03 +00:00
|
|
|
if (parsed_len < 0)
|
2018-03-09 19:17:39 +00:00
|
|
|
goto err;
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
if (empty_ack_frame(new_acki))
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("Ignore empty ACK frame");
|
|
|
|
return parsed_len;
|
|
|
|
}
|
2018-03-09 19:17:39 +00:00
|
|
|
if (packet_in->pi_packno <= conn->fc_max_ack_packno)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2018-03-09 19:17:39 +00:00
|
|
|
LSQ_DEBUG("Ignore old ack (max %"PRIu64")", conn->fc_max_ack_packno);
|
|
|
|
return parsed_len;
|
|
|
|
}
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
new_acki->pns = PNS_APP;
|
2018-03-09 19:17:39 +00:00
|
|
|
EV_LOG_ACK_FRAME_IN(LSQUIC_LOG_CONN_ID, new_acki);
|
|
|
|
conn->fc_max_ack_packno = packet_in->pi_packno;
|
|
|
|
|
2019-12-30 16:29:05 +00:00
|
|
|
if (new_acki == &conn->fc_ack)
|
2018-03-09 19:17:39 +00:00
|
|
|
{
|
2019-12-30 16:29:05 +00:00
|
|
|
LSQ_DEBUG("Saved ACK");
|
|
|
|
conn->fc_flags |= FC_HAVE_SAVED_ACK;
|
|
|
|
conn->fc_saved_ack_received = packet_in->pi_received;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (0 == lsquic_merge_acks(&conn->fc_ack, new_acki))
|
|
|
|
{
|
2019-01-16 20:13:59 +00:00
|
|
|
#if LSQUIC_CONN_STATS
|
2019-12-30 16:29:05 +00:00
|
|
|
++conn->fc_stats.in.n_acks_merged;
|
2018-03-09 19:17:39 +00:00
|
|
|
#endif
|
2019-12-30 16:29:05 +00:00
|
|
|
LSQ_DEBUG("merged into saved ACK, getting %s",
|
|
|
|
(lsquic_acki2str(&conn->fc_ack, conn->fc_pub.mm->ack_str,
|
|
|
|
MAX_ACKI_STR_SZ), conn->fc_pub.mm->ack_str));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("could not merge new ACK into saved ACK");
|
|
|
|
if (0 != process_ack(conn, &conn->fc_ack, packet_in->pi_received,
|
2019-09-11 15:27:58 +00:00
|
|
|
packet_in->pi_received))
|
2018-03-09 19:17:39 +00:00
|
|
|
goto err;
|
2019-12-30 16:29:05 +00:00
|
|
|
conn->fc_ack = *new_acki;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
2019-12-30 16:29:05 +00:00
|
|
|
conn->fc_saved_ack_received = packet_in->pi_received;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
2018-03-09 19:17:39 +00:00
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
return parsed_len;
|
2018-03-09 19:17:39 +00:00
|
|
|
|
|
|
|
err:
|
2019-09-11 15:27:58 +00:00
|
|
|
warn_time = lsquic_time_now();
|
|
|
|
if (0 == conn->fc_enpub->enp_last_warning[WT_ACKPARSE_FULL]
|
|
|
|
|| conn->fc_enpub->enp_last_warning[WT_ACKPARSE_FULL]
|
|
|
|
+ WARNING_INTERVAL < warn_time)
|
|
|
|
{
|
|
|
|
conn->fc_enpub->enp_last_warning[WT_ACKPARSE_FULL] = warn_time;
|
|
|
|
log_invalid_ack_frame(conn, p, parsed_len, new_acki);
|
|
|
|
}
|
2018-03-09 19:17:39 +00:00
|
|
|
return 0;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static unsigned
|
|
|
|
process_stop_waiting_frame (struct full_conn *conn, lsquic_packet_in_t *packet_in,
|
|
|
|
const unsigned char *p, size_t len)
|
|
|
|
{
|
|
|
|
lsquic_packno_t least, cutoff;
|
2019-04-01 15:41:55 +00:00
|
|
|
enum packno_bits bits;
|
2017-09-22 21:00:03 +00:00
|
|
|
int parsed_len;
|
|
|
|
|
|
|
|
bits = lsquic_packet_in_packno_bits(packet_in);
|
|
|
|
|
|
|
|
if (conn->fc_flags & FC_NSTP)
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("NSTP on: ignore STOP_WAITING frame");
|
|
|
|
parsed_len = conn->fc_conn.cn_pf->pf_skip_stop_waiting_frame(len, bits);
|
|
|
|
if (parsed_len > 0)
|
|
|
|
return (unsigned) parsed_len;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
parsed_len = conn->fc_conn.cn_pf->pf_parse_stop_waiting_frame(p, len,
|
|
|
|
packet_in->pi_packno, bits, &least);
|
|
|
|
if (parsed_len < 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (packet_in->pi_packno <= conn->fc_max_swf_packno)
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("ignore old STOP_WAITING frame");
|
|
|
|
return parsed_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
LSQ_DEBUG("Got STOP_WAITING frame, least unacked: %"PRIu64, least);
|
|
|
|
EV_LOG_STOP_WAITING_FRAME_IN(LSQUIC_LOG_CONN_ID, least);
|
|
|
|
|
|
|
|
if (least > packet_in->pi_packno)
|
|
|
|
{
|
|
|
|
ABORT_ERROR("received invalid STOP_WAITING: %"PRIu64" is larger "
|
|
|
|
"than the packet number%"PRIu64, least, packet_in->pi_packno);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
cutoff = lsquic_rechist_cutoff(&conn->fc_rechist);
|
|
|
|
if (cutoff && least < cutoff)
|
|
|
|
{
|
|
|
|
ABORT_ERROR("received invalid STOP_WAITING: %"PRIu64" is smaller "
|
|
|
|
"than the cutoff %"PRIu64, least, cutoff);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
conn->fc_max_swf_packno = packet_in->pi_packno;
|
|
|
|
lsquic_rechist_stop_wait(&conn->fc_rechist, least);
|
|
|
|
return parsed_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static unsigned
|
|
|
|
process_blocked_frame (struct full_conn *conn, lsquic_packet_in_t *packet_in,
|
|
|
|
const unsigned char *p, size_t len)
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_stream_id_t stream_id;
|
|
|
|
struct lsquic_stream *stream;
|
2017-09-22 21:00:03 +00:00
|
|
|
const int parsed_len = conn->fc_conn.cn_pf->pf_parse_blocked_frame(p, len,
|
|
|
|
&stream_id);
|
|
|
|
if (parsed_len < 0)
|
|
|
|
return 0;
|
|
|
|
EV_LOG_BLOCKED_FRAME_IN(LSQUIC_LOG_CONN_ID, stream_id);
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("Peer reports stream %"PRIu64" as blocked", stream_id);
|
|
|
|
if (stream_id)
|
|
|
|
{
|
|
|
|
stream = find_stream_by_id(conn, stream_id);
|
|
|
|
if (stream)
|
|
|
|
lsquic_stream_peer_blocked_gquic(stream);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
conn->fc_flags |= FC_SEND_WUF;
|
2017-09-22 21:00:03 +00:00
|
|
|
return parsed_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static unsigned
|
|
|
|
process_connection_close_frame (struct full_conn *conn, lsquic_packet_in_t *packet_in,
|
|
|
|
const unsigned char *p, size_t len)
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
uint64_t error_code;
|
2017-09-22 21:00:03 +00:00
|
|
|
uint16_t reason_len;
|
|
|
|
uint8_t reason_off;
|
|
|
|
int parsed_len;
|
|
|
|
|
|
|
|
parsed_len = conn->fc_conn.cn_pf->pf_parse_connect_close_frame(p, len,
|
2019-09-11 15:27:58 +00:00
|
|
|
NULL, &error_code, &reason_len, &reason_off);
|
2017-09-22 21:00:03 +00:00
|
|
|
if (parsed_len < 0)
|
|
|
|
return 0;
|
|
|
|
EV_LOG_CONNECTION_CLOSE_FRAME_IN(LSQUIC_LOG_CONN_ID, error_code,
|
|
|
|
(int) reason_len, (const char *) p + reason_off);
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_INFO("Received CONNECTION_CLOSE frame (code: %"PRIu64"; reason: %.*s)",
|
2017-09-22 21:00:03 +00:00
|
|
|
error_code, (int) reason_len, (const char *) p + reason_off);
|
2020-12-18 17:46:20 +00:00
|
|
|
if (conn->fc_stream_ifs[STREAM_IF_STD].stream_if->on_conncloseframe_received)
|
|
|
|
conn->fc_stream_ifs[STREAM_IF_STD].stream_if->on_conncloseframe_received(
|
|
|
|
&conn->fc_conn, -1, error_code, (const char *) p + reason_off, reason_len);
|
2021-01-08 16:38:46 +00:00
|
|
|
conn->fc_flags |= FC_RECV_CLOSE|FC_CLOSING;
|
2017-09-22 21:00:03 +00:00
|
|
|
return parsed_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static unsigned
|
|
|
|
process_rst_stream_frame (struct full_conn *conn, lsquic_packet_in_t *packet_in,
|
|
|
|
const unsigned char *p, size_t len)
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_stream_id_t stream_id;
|
|
|
|
uint64_t offset, error_code;
|
2017-09-22 21:00:03 +00:00
|
|
|
lsquic_stream_t *stream;
|
|
|
|
const int parsed_len = conn->fc_conn.cn_pf->pf_parse_rst_frame(p, len,
|
|
|
|
&stream_id, &offset, &error_code);
|
|
|
|
if (parsed_len < 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
EV_LOG_RST_STREAM_FRAME_IN(LSQUIC_LOG_CONN_ID, stream_id, offset,
|
|
|
|
error_code);
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("Got RST_STREAM; stream: %"PRIu64"; offset: 0x%"PRIX64, stream_id,
|
2017-09-22 21:00:03 +00:00
|
|
|
offset);
|
|
|
|
if (0 == stream_id)
|
|
|
|
{ /* Follow reference implementation and ignore this apparently
|
|
|
|
* invalid frame.
|
|
|
|
*/
|
|
|
|
return parsed_len;
|
|
|
|
}
|
|
|
|
|
2019-12-23 21:14:20 +00:00
|
|
|
stream = find_stream_by_id(conn, stream_id);
|
|
|
|
if (stream && lsquic_stream_is_critical(stream))
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
ABORT_ERROR("received reset on static stream %"PRIu64, stream_id);
|
2017-09-22 21:00:03 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (!stream)
|
|
|
|
{
|
|
|
|
if (conn_is_stream_closed(conn, stream_id))
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("got reset frame for closed stream %"PRIu64, stream_id);
|
2017-09-22 21:00:03 +00:00
|
|
|
return parsed_len;
|
|
|
|
}
|
|
|
|
if (!is_peer_initiated(conn, stream_id))
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
ABORT_ERROR("received reset for never-initiated stream %"PRIu64,
|
2017-09-22 21:00:03 +00:00
|
|
|
stream_id);
|
|
|
|
return 0;
|
|
|
|
}
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
stream = new_stream(conn, stream_id, SCF_CALL_ON_NEW);
|
2017-09-22 21:00:03 +00:00
|
|
|
if (!stream)
|
|
|
|
{
|
|
|
|
ABORT_ERROR("cannot create new stream: %s", strerror(errno));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (stream_id > conn->fc_max_peer_stream_id)
|
|
|
|
conn->fc_max_peer_stream_id = stream_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (0 != lsquic_stream_rst_in(stream, offset, error_code))
|
|
|
|
{
|
|
|
|
ABORT_ERROR("received invalid RST_STREAM");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return parsed_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static unsigned
|
|
|
|
process_window_update_frame (struct full_conn *conn, lsquic_packet_in_t *packet_in,
|
|
|
|
const unsigned char *p, size_t len)
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_stream_id_t stream_id;
|
2017-09-22 21:00:03 +00:00
|
|
|
uint64_t offset;
|
|
|
|
const int parsed_len =
|
|
|
|
conn->fc_conn.cn_pf->pf_parse_window_update_frame(p, len,
|
|
|
|
&stream_id, &offset);
|
|
|
|
if (parsed_len < 0)
|
|
|
|
return 0;
|
|
|
|
EV_LOG_WINDOW_UPDATE_FRAME_IN(LSQUIC_LOG_CONN_ID, stream_id, offset);
|
|
|
|
if (stream_id)
|
|
|
|
{
|
|
|
|
lsquic_stream_t *stream = find_stream_by_id(conn, stream_id);
|
|
|
|
if (stream)
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("Got window update frame, stream: %"PRIu64
|
|
|
|
"; offset: 0x%"PRIX64, stream_id, offset);
|
2017-09-22 21:00:03 +00:00
|
|
|
lsquic_stream_window_update(stream, offset);
|
|
|
|
}
|
|
|
|
else /* Perhaps a result of lost packets? */
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("Got window update frame for non-existing stream %"PRIu64
|
|
|
|
" (offset: 0x%"PRIX64")", stream_id, offset);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
else if (offset > conn->fc_pub.conn_cap.cc_max)
|
|
|
|
{
|
|
|
|
conn->fc_pub.conn_cap.cc_max = offset;
|
|
|
|
assert(conn->fc_pub.conn_cap.cc_max >= conn->fc_pub.conn_cap.cc_sent);
|
|
|
|
LSQ_DEBUG("Connection WUF, new offset 0x%"PRIX64, offset);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
LSQ_DEBUG("Throw ouw duplicate connection WUF");
|
|
|
|
return parsed_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
typedef unsigned (*process_frame_f)(
|
|
|
|
struct full_conn *, lsquic_packet_in_t *, const unsigned char *p, size_t);
|
|
|
|
|
|
|
|
static process_frame_f const process_frames[N_QUIC_FRAMES] =
|
|
|
|
{
|
|
|
|
[QUIC_FRAME_ACK] = process_ack_frame,
|
|
|
|
[QUIC_FRAME_BLOCKED] = process_blocked_frame,
|
|
|
|
[QUIC_FRAME_CONNECTION_CLOSE] = process_connection_close_frame,
|
2019-12-23 21:14:20 +00:00
|
|
|
[QUIC_FRAME_CRYPTO] = process_crypto_frame,
|
2017-09-22 21:00:03 +00:00
|
|
|
[QUIC_FRAME_GOAWAY] = process_goaway_frame,
|
|
|
|
[QUIC_FRAME_INVALID] = process_invalid_frame,
|
|
|
|
[QUIC_FRAME_PADDING] = process_padding_frame,
|
|
|
|
[QUIC_FRAME_PING] = process_ping_frame,
|
|
|
|
[QUIC_FRAME_RST_STREAM] = process_rst_stream_frame,
|
|
|
|
[QUIC_FRAME_STOP_WAITING] = process_stop_waiting_frame,
|
|
|
|
[QUIC_FRAME_STREAM] = process_stream_frame,
|
|
|
|
[QUIC_FRAME_WINDOW_UPDATE] = process_window_update_frame,
|
|
|
|
};
|
|
|
|
|
|
|
|
static unsigned
|
|
|
|
process_packet_frame (struct full_conn *conn, lsquic_packet_in_t *packet_in,
|
|
|
|
const unsigned char *p, size_t len)
|
|
|
|
{
|
2020-02-21 19:26:25 +00:00
|
|
|
enum quic_frame_type type;
|
|
|
|
|
|
|
|
type = conn->fc_conn.cn_pf->pf_parse_frame_type(p, len);
|
2017-09-22 21:00:03 +00:00
|
|
|
packet_in->pi_frame_types |= 1 << type;
|
2018-02-26 21:01:16 +00:00
|
|
|
recent_packet_hist_frames(conn, 0, 1 << type);
|
2017-09-22 21:00:03 +00:00
|
|
|
return process_frames[type](conn, packet_in, p, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
process_ver_neg_packet (struct full_conn *conn, lsquic_packet_in_t *packet_in)
|
|
|
|
{
|
|
|
|
int s;
|
|
|
|
struct ver_iter vi;
|
|
|
|
lsquic_ver_tag_t ver_tag;
|
|
|
|
enum lsquic_version version;
|
|
|
|
unsigned versions = 0;
|
|
|
|
|
|
|
|
LSQ_DEBUG("Processing version-negotiation packet");
|
|
|
|
|
|
|
|
if (conn->fc_ver_neg.vn_state != VN_START)
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("ignore a likely duplicate version negotiation packet");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-03-12 12:41:53 +00:00
|
|
|
for (s = lsquic_packet_in_ver_first(packet_in, &vi, &ver_tag); s;
|
|
|
|
s = lsquic_packet_in_ver_next(&vi, &ver_tag))
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
version = lsquic_tag2ver(ver_tag);
|
|
|
|
if (version < N_LSQVER)
|
|
|
|
{
|
|
|
|
versions |= 1 << version;
|
|
|
|
LSQ_DEBUG("server supports version %s", lsquic_ver2str[version]);
|
2019-09-11 15:27:58 +00:00
|
|
|
EV_LOG_VER_NEG(LSQUIC_LOG_CONN_ID,
|
2019-04-12 14:50:45 +00:00
|
|
|
"supports", lsquic_ver2str[version]);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (versions & (1 << conn->fc_ver_neg.vn_ver))
|
|
|
|
{
|
|
|
|
ABORT_ERROR("server replied with version we support: %s",
|
|
|
|
lsquic_ver2str[conn->fc_ver_neg.vn_ver]);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
versions &= conn->fc_ver_neg.vn_supp;
|
|
|
|
if (0 == versions)
|
|
|
|
{
|
2021-04-16 18:54:30 +00:00
|
|
|
conn->fc_flags |= FC_HSK_FAILED;
|
2017-09-22 21:00:03 +00:00
|
|
|
ABORT_ERROR("client does not support any of the server-specified "
|
|
|
|
"versions");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-02-11 17:56:57 +00:00
|
|
|
set_versions(conn, versions, NULL);
|
2017-09-22 21:00:03 +00:00
|
|
|
conn->fc_ver_neg.vn_state = VN_IN_PROGRESS;
|
|
|
|
lsquic_send_ctl_expire_all(&conn->fc_send_ctl);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
reconstruct_packet_number (struct full_conn *conn, lsquic_packet_in_t *packet_in)
|
|
|
|
{
|
|
|
|
lsquic_packno_t cur_packno, max_packno;
|
2019-04-01 15:41:55 +00:00
|
|
|
enum packno_bits bits;
|
|
|
|
unsigned packet_len;
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
cur_packno = packet_in->pi_packno;
|
|
|
|
max_packno = lsquic_rechist_largest_packno(&conn->fc_rechist);
|
|
|
|
bits = lsquic_packet_in_packno_bits(packet_in);
|
2019-04-01 15:41:55 +00:00
|
|
|
packet_len = conn->fc_conn.cn_pf->pf_packno_bits2len(bits);
|
2020-03-12 12:41:53 +00:00
|
|
|
packet_in->pi_packno = lsquic_restore_packno(cur_packno, packet_len,
|
|
|
|
max_packno);
|
2017-09-22 21:00:03 +00:00
|
|
|
LSQ_DEBUG("reconstructed (bits: %u, packno: %"PRIu64", max: %"PRIu64") "
|
|
|
|
"to %"PRIu64"", bits, cur_packno, max_packno, packet_in->pi_packno);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static enum dec_packin
|
2017-09-22 21:00:03 +00:00
|
|
|
conn_decrypt_packet (struct full_conn *conn, lsquic_packet_in_t *packet_in)
|
|
|
|
{
|
2019-12-30 16:29:05 +00:00
|
|
|
return conn->fc_conn.cn_esf_c->esf_decrypt_packet(
|
2019-09-11 15:27:58 +00:00
|
|
|
conn->fc_conn.cn_enc_session, conn->fc_enpub,
|
|
|
|
&conn->fc_conn, packet_in);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
parse_regular_packet (struct full_conn *conn, lsquic_packet_in_t *packet_in)
|
|
|
|
{
|
|
|
|
const unsigned char *p, *pend;
|
|
|
|
unsigned len;
|
|
|
|
|
|
|
|
p = packet_in->pi_data + packet_in->pi_header_sz;
|
|
|
|
pend = packet_in->pi_data + packet_in->pi_data_sz;
|
|
|
|
|
|
|
|
while (p < pend)
|
|
|
|
{
|
|
|
|
len = process_packet_frame(conn, packet_in, p, pend - p);
|
|
|
|
if (len > 0)
|
|
|
|
p += len;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ABORT_ERROR("Error parsing frame");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-15 19:06:31 +00:00
|
|
|
static int
|
|
|
|
conn_is_stateless_reset (const struct full_conn *conn,
|
|
|
|
const struct lsquic_packet_in *packet_in)
|
|
|
|
{
|
|
|
|
return packet_in->pi_data_sz > SRST_LENGTH
|
2019-09-11 15:27:58 +00:00
|
|
|
&& 0 == conn->fc_conn.cn_esf_c->esf_verify_reset_token(
|
2018-08-15 19:06:31 +00:00
|
|
|
conn->fc_conn.cn_enc_session,
|
|
|
|
packet_in->pi_data + packet_in->pi_data_sz - SRST_LENGTH,
|
|
|
|
SRST_LENGTH);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
static int
|
|
|
|
process_regular_packet (struct full_conn *conn, lsquic_packet_in_t *packet_in)
|
|
|
|
{
|
|
|
|
enum received_st st;
|
2018-02-26 21:01:16 +00:00
|
|
|
enum quic_ft_bit frame_types;
|
2017-09-22 21:00:03 +00:00
|
|
|
int was_missing;
|
|
|
|
|
2019-12-23 21:14:20 +00:00
|
|
|
if (conn->fc_conn.cn_version < LSQVER_050)
|
|
|
|
{
|
|
|
|
reconstruct_packet_number(conn, packet_in);
|
|
|
|
EV_LOG_PACKET_IN(LSQUIC_LOG_CONN_ID, packet_in);
|
|
|
|
}
|
2017-09-22 21:00:03 +00:00
|
|
|
|
2019-01-16 20:13:59 +00:00
|
|
|
#if LSQUIC_CONN_STATS
|
|
|
|
++conn->fc_stats.in.packets;
|
2017-09-22 21:00:03 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/* The packet is decrypted before receive history is updated. This is
|
|
|
|
* done to make sure that a bad packet won't occupy a slot in receive
|
|
|
|
* history and subsequent good packet won't be marked as a duplicate.
|
|
|
|
*/
|
|
|
|
if (0 == (packet_in->pi_flags & PI_DECRYPTED) &&
|
2019-09-11 15:27:58 +00:00
|
|
|
DECPI_OK != conn_decrypt_packet(conn, packet_in))
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2018-08-15 19:06:31 +00:00
|
|
|
if (conn_is_stateless_reset(conn, packet_in))
|
|
|
|
{
|
|
|
|
LSQ_INFO("received public reset packet: aborting connection");
|
|
|
|
conn->fc_flags |= FC_GOT_PRST;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LSQ_INFO("could not decrypt packet");
|
2019-01-16 20:13:59 +00:00
|
|
|
#if LSQUIC_CONN_STATS
|
|
|
|
++conn->fc_stats.in.undec_packets;
|
2017-09-22 21:00:03 +00:00
|
|
|
#endif
|
2018-08-15 19:06:31 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
2019-12-23 21:14:20 +00:00
|
|
|
if (conn->fc_conn.cn_version >= LSQVER_050)
|
|
|
|
EV_LOG_PACKET_IN(LSQUIC_LOG_CONN_ID, packet_in);
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
st = lsquic_rechist_received(&conn->fc_rechist, packet_in->pi_packno,
|
|
|
|
packet_in->pi_received);
|
|
|
|
switch (st) {
|
|
|
|
case REC_ST_OK:
|
|
|
|
parse_regular_packet(conn, packet_in);
|
2019-09-11 15:27:58 +00:00
|
|
|
if (0 == (conn->fc_flags & (FC_ACK_QUEUED|FC_GOT_SREJ)))
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2018-02-26 21:01:16 +00:00
|
|
|
frame_types = packet_in->pi_frame_types;
|
2019-09-11 15:27:58 +00:00
|
|
|
if ((conn->fc_flags & FC_GOING_AWAY)
|
2021-01-08 16:38:46 +00:00
|
|
|
&& lsquic_hash_count(conn->fc_pub.all_streams) <= N_SPECIAL_STREAMS)
|
2019-09-11 15:27:58 +00:00
|
|
|
{
|
|
|
|
/* Ignore PING frames if we are going away and there are no
|
|
|
|
* active streams. (HANDSHAKE and HEADERS streams are the
|
|
|
|
* two streams that are always in the all_streams hash).
|
|
|
|
*/
|
|
|
|
frame_types &= ~(1 << QUIC_FRAME_PING);
|
|
|
|
}
|
2017-09-22 21:00:03 +00:00
|
|
|
was_missing = packet_in->pi_packno !=
|
|
|
|
lsquic_rechist_largest_packno(&conn->fc_rechist);
|
2019-09-11 15:27:58 +00:00
|
|
|
conn->fc_n_slack_akbl += !!(frame_types & GQUIC_FRAME_ACKABLE_MASK);
|
2017-09-22 21:00:03 +00:00
|
|
|
try_queueing_ack(conn, was_missing, packet_in->pi_received);
|
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
else if (conn->fc_flags & FC_GOT_SREJ)
|
|
|
|
conn->fc_flags &= ~FC_GOT_SREJ;
|
2017-09-22 21:00:03 +00:00
|
|
|
return 0;
|
|
|
|
case REC_ST_DUP:
|
2019-01-16 20:13:59 +00:00
|
|
|
#if LSQUIC_CONN_STATS
|
|
|
|
++conn->fc_stats.in.dup_packets;
|
2017-09-22 21:00:03 +00:00
|
|
|
#endif
|
|
|
|
LSQ_INFO("packet %"PRIu64" is a duplicate", packet_in->pi_packno);
|
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
assert(0);
|
|
|
|
/* Fall through */
|
|
|
|
case REC_ST_ERR:
|
2019-01-16 20:13:59 +00:00
|
|
|
#if LSQUIC_CONN_STATS
|
|
|
|
++conn->fc_stats.in.err_packets;
|
2017-09-22 21:00:03 +00:00
|
|
|
#endif
|
|
|
|
LSQ_INFO("error processing packet %"PRIu64, packet_in->pi_packno);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
/* TODO: Possible optimization: in server mode, we do not perform version
|
|
|
|
* negotiation. We can use different functions in client mode (this
|
|
|
|
* function) and server mode (a different, faster function that ignores
|
|
|
|
* version flags).
|
|
|
|
*/
|
2017-09-22 21:00:03 +00:00
|
|
|
static int
|
|
|
|
process_incoming_packet (struct full_conn *conn, lsquic_packet_in_t *packet_in)
|
|
|
|
{
|
2018-08-15 19:06:31 +00:00
|
|
|
int is_prst, is_verneg;
|
|
|
|
|
2018-02-26 21:01:16 +00:00
|
|
|
recent_packet_hist_new(conn, 0, packet_in->pi_received);
|
2017-09-22 21:00:03 +00:00
|
|
|
LSQ_DEBUG("Processing packet %"PRIu64, packet_in->pi_packno);
|
2018-08-15 19:06:31 +00:00
|
|
|
|
|
|
|
is_prst = lsquic_packet_in_is_gquic_prst(packet_in);
|
|
|
|
is_verneg = lsquic_packet_in_is_verneg(packet_in);
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
/* See flowchart in Section 4.1 of [draft-ietf-quic-transport-00]. We test
|
|
|
|
* for the common case first.
|
|
|
|
*/
|
2018-08-15 19:06:31 +00:00
|
|
|
if (0 == is_prst && 0 == is_verneg)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
if (conn->fc_ver_neg.vn_tag)
|
|
|
|
{
|
|
|
|
assert(conn->fc_ver_neg.vn_state != VN_END);
|
|
|
|
conn->fc_ver_neg.vn_state = VN_END;
|
|
|
|
conn->fc_ver_neg.vn_tag = NULL;
|
|
|
|
conn->fc_conn.cn_version = conn->fc_ver_neg.vn_ver;
|
|
|
|
conn->fc_conn.cn_flags |= LSCONN_VER_SET;
|
2019-09-11 15:27:58 +00:00
|
|
|
assert(!(conn->fc_flags & FC_NSTP)); /* This bit off at start */
|
|
|
|
if (conn->fc_conn.cn_version >= LSQVER_046
|
|
|
|
|| conn->fc_settings->es_support_nstp)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
conn->fc_flags |= FC_NSTP;
|
|
|
|
lsquic_send_ctl_turn_nstp_on(&conn->fc_send_ctl);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
LSQ_DEBUG("end of version negotiation: agreed upon %s",
|
|
|
|
lsquic_ver2str[conn->fc_ver_neg.vn_ver]);
|
2019-02-18 13:40:51 +00:00
|
|
|
lsquic_send_ctl_verneg_done(&conn->fc_send_ctl);
|
2019-09-11 15:27:58 +00:00
|
|
|
EV_LOG_VER_NEG(LSQUIC_LOG_CONN_ID,
|
2019-04-12 14:50:45 +00:00
|
|
|
"agreed", lsquic_ver2str[conn->fc_ver_neg.vn_ver]);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
return process_regular_packet(conn, packet_in);
|
|
|
|
}
|
2018-08-15 19:06:31 +00:00
|
|
|
else if (is_prst)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
LSQ_INFO("received public reset packet: aborting connection");
|
|
|
|
conn->fc_flags |= FC_GOT_PRST;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (conn->fc_flags & FC_SERVER)
|
|
|
|
return process_regular_packet(conn, packet_in);
|
|
|
|
else if (conn->fc_ver_neg.vn_tag)
|
|
|
|
{
|
|
|
|
process_ver_neg_packet(conn, packet_in);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("unexpected version negotiation packet: ignore it");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2019-09-11 15:27:58 +00:00
|
|
|
idle_alarm_expired (enum alarm_id al_id, void *ctx, lsquic_time_t expiry,
|
|
|
|
lsquic_time_t now)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
struct full_conn *conn = ctx;
|
2020-06-09 15:55:56 +00:00
|
|
|
|
|
|
|
if ((conn->fc_flags & FC_NOPROG_TIMEOUT)
|
|
|
|
&& conn->fc_pub.last_prog + conn->fc_enpub->enp_noprog_timeout < now)
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("connection timed out due to lack of progress");
|
|
|
|
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "connection timed out due to "
|
|
|
|
"lack of progress");
|
|
|
|
/* Different flag so that CONNECTION_CLOSE frame is sent */
|
|
|
|
conn->fc_flags |= FC_ABORTED;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("connection timed out");
|
|
|
|
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "connection timed out");
|
|
|
|
conn->fc_flags |= FC_TIMED_OUT;
|
|
|
|
}
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2019-09-11 15:27:58 +00:00
|
|
|
handshake_alarm_expired (enum alarm_id al_id, void *ctx,
|
|
|
|
lsquic_time_t expiry, lsquic_time_t now)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
struct full_conn *conn = ctx;
|
|
|
|
LSQ_DEBUG("connection timed out: handshake timed out");
|
|
|
|
conn->fc_flags |= FC_TIMED_OUT;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2019-09-11 15:27:58 +00:00
|
|
|
ping_alarm_expired (enum alarm_id al_id, void *ctx, lsquic_time_t expiry,
|
|
|
|
lsquic_time_t now)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
struct full_conn *conn = ctx;
|
|
|
|
LSQ_DEBUG("Ping alarm rang: schedule PING frame to be generated");
|
|
|
|
conn->fc_flags |= FC_SEND_PING;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static lsquic_packet_out_t *
|
|
|
|
get_writeable_packet (struct full_conn *conn, unsigned need_at_least)
|
|
|
|
{
|
|
|
|
lsquic_packet_out_t *packet_out;
|
|
|
|
int is_err;
|
|
|
|
|
|
|
|
packet_out = lsquic_send_ctl_get_writeable_packet(&conn->fc_send_ctl,
|
2019-09-11 15:27:58 +00:00
|
|
|
PNS_APP, need_at_least, &conn->fc_path, 0, &is_err);
|
2017-09-22 21:00:03 +00:00
|
|
|
if (!packet_out && is_err)
|
|
|
|
ABORT_ERROR("cannot allocate packet: %s", strerror(errno));
|
|
|
|
return packet_out;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
generate_wuf_stream (struct full_conn *conn, lsquic_stream_t *stream)
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_packet_out_t *packet_out = get_writeable_packet(conn, GQUIC_WUF_SZ);
|
2017-09-22 21:00:03 +00:00
|
|
|
if (!packet_out)
|
|
|
|
return 0;
|
|
|
|
const uint64_t recv_off = lsquic_stream_fc_recv_off(stream);
|
|
|
|
int sz = conn->fc_conn.cn_pf->pf_gen_window_update_frame(
|
|
|
|
packet_out->po_data + packet_out->po_data_sz,
|
|
|
|
lsquic_packet_out_avail(packet_out), stream->id, recv_off);
|
|
|
|
if (sz < 0) {
|
|
|
|
ABORT_ERROR("gen_window_update_frame failed");
|
|
|
|
return 0;
|
|
|
|
}
|
2018-02-26 21:01:16 +00:00
|
|
|
lsquic_send_ctl_incr_pack_sz(&conn->fc_send_ctl, packet_out, sz);
|
2017-09-22 21:00:03 +00:00
|
|
|
packet_out->po_frame_types |= 1 << QUIC_FRAME_WINDOW_UPDATE;
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("wrote WUF: stream %"PRIu64"; offset 0x%"PRIX64, stream->id,
|
|
|
|
recv_off);
|
|
|
|
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID,
|
|
|
|
"wrote WUF: stream %"PRIu64"; offset 0x%"PRIX64, stream->id, recv_off);
|
2017-09-22 21:00:03 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
generate_wuf_conn (struct full_conn *conn)
|
|
|
|
{
|
|
|
|
assert(conn->fc_flags & FC_SEND_WUF);
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_packet_out_t *packet_out = get_writeable_packet(conn, GQUIC_WUF_SZ);
|
2017-09-22 21:00:03 +00:00
|
|
|
if (!packet_out)
|
|
|
|
return;
|
|
|
|
const uint64_t recv_off = lsquic_cfcw_get_fc_recv_off(&conn->fc_pub.cfcw);
|
|
|
|
int sz = conn->fc_conn.cn_pf->pf_gen_window_update_frame(
|
|
|
|
packet_out->po_data + packet_out->po_data_sz,
|
|
|
|
lsquic_packet_out_avail(packet_out), 0, recv_off);
|
|
|
|
if (sz < 0) {
|
|
|
|
ABORT_ERROR("gen_window_update_frame failed");
|
|
|
|
return;
|
|
|
|
}
|
2018-02-26 21:01:16 +00:00
|
|
|
lsquic_send_ctl_incr_pack_sz(&conn->fc_send_ctl, packet_out, sz);
|
2017-09-22 21:00:03 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static void
|
|
|
|
maybe_close_conn (struct full_conn *conn)
|
|
|
|
{
|
|
|
|
#ifndef NDEBUG
|
|
|
|
struct lsquic_stream *stream;
|
|
|
|
struct lsquic_hash_elem *el;
|
|
|
|
#endif
|
2021-01-27 15:36:25 +00:00
|
|
|
const unsigned n_special_streams = N_SPECIAL_STREAMS
|
|
|
|
- !(conn->fc_flags & FC_HTTP);
|
2019-09-11 15:27:58 +00:00
|
|
|
|
|
|
|
if ((conn->fc_flags & (FC_CLOSING|FC_GOAWAY_SENT|FC_SERVER))
|
|
|
|
== (FC_GOAWAY_SENT|FC_SERVER)
|
2021-01-27 15:36:25 +00:00
|
|
|
&& lsquic_hash_count(conn->fc_pub.all_streams) == n_special_streams)
|
2019-09-11 15:27:58 +00:00
|
|
|
{
|
|
|
|
#ifndef NDEBUG
|
|
|
|
for (el = lsquic_hash_first(conn->fc_pub.all_streams); el;
|
|
|
|
el = lsquic_hash_next(conn->fc_pub.all_streams))
|
|
|
|
{
|
|
|
|
stream = lsquic_hashelem_getdata(el);
|
2019-12-23 21:14:20 +00:00
|
|
|
assert(stream->sm_bflags & (SMBF_CRYPTO|SMBF_HEADERS));
|
2019-09-11 15:27:58 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
conn->fc_flags |= FC_RECV_CLOSE; /* Fake -- trigger "ok to close" */
|
|
|
|
conn->fc_flags |= FC_CLOSING;
|
|
|
|
LSQ_DEBUG("closing connection: GOAWAY sent and no responses remain");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
static void
|
|
|
|
generate_goaway_frame (struct full_conn *conn)
|
|
|
|
{
|
|
|
|
int reason_len = 0;
|
|
|
|
lsquic_packet_out_t *packet_out =
|
2019-09-11 15:27:58 +00:00
|
|
|
get_writeable_packet(conn, GQUIC_GOAWAY_FRAME_SZ + reason_len);
|
2017-09-22 21:00:03 +00:00
|
|
|
if (!packet_out)
|
|
|
|
return;
|
|
|
|
int sz = conn->fc_conn.cn_pf->pf_gen_goaway_frame(
|
|
|
|
packet_out->po_data + packet_out->po_data_sz,
|
|
|
|
lsquic_packet_out_avail(packet_out), 0, conn->fc_max_peer_stream_id,
|
|
|
|
NULL, reason_len);
|
|
|
|
if (sz < 0) {
|
|
|
|
ABORT_ERROR("gen_goaway_frame failed");
|
|
|
|
return;
|
|
|
|
}
|
2018-02-26 21:01:16 +00:00
|
|
|
lsquic_send_ctl_incr_pack_sz(&conn->fc_send_ctl, packet_out, sz);
|
2017-09-22 21:00:03 +00:00
|
|
|
packet_out->po_frame_types |= 1 << QUIC_FRAME_GOAWAY;
|
|
|
|
conn->fc_flags &= ~FC_SEND_GOAWAY;
|
|
|
|
conn->fc_flags |= FC_GOAWAY_SENT;
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("wrote GOAWAY frame: stream id: %"PRIu64,
|
|
|
|
conn->fc_max_peer_stream_id);
|
|
|
|
maybe_close_conn(conn);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
generate_connection_close_packet (struct full_conn *conn)
|
|
|
|
{
|
|
|
|
lsquic_packet_out_t *packet_out;
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
packet_out = lsquic_send_ctl_new_packet_out(&conn->fc_send_ctl, 0, PNS_APP,
|
|
|
|
&conn->fc_path);
|
2017-09-22 21:00:03 +00:00
|
|
|
if (!packet_out)
|
|
|
|
{
|
|
|
|
ABORT_ERROR("cannot allocate packet: %s", strerror(errno));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
lsquic_send_ctl_scheduled_one(&conn->fc_send_ctl, packet_out);
|
|
|
|
int sz = conn->fc_conn.cn_pf->pf_gen_connect_close_frame(packet_out->po_data + packet_out->po_data_sz,
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_packet_out_avail(packet_out), 0, 16 /* PEER_GOING_AWAY */,
|
2017-09-22 21:00:03 +00:00
|
|
|
NULL, 0);
|
|
|
|
if (sz < 0) {
|
|
|
|
ABORT_ERROR("generate_connection_close_packet failed");
|
|
|
|
return;
|
|
|
|
}
|
2018-02-26 21:01:16 +00:00
|
|
|
lsquic_send_ctl_incr_pack_sz(&conn->fc_send_ctl, packet_out, sz);
|
2017-09-22 21:00:03 +00:00
|
|
|
packet_out->po_frame_types |= 1 << QUIC_FRAME_CONNECTION_CLOSE;
|
|
|
|
LSQ_DEBUG("generated CONNECTION_CLOSE frame in its own packet");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2019-09-11 15:27:58 +00:00
|
|
|
generate_blocked_frame (struct full_conn *conn, lsquic_stream_id_t stream_id)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
lsquic_packet_out_t *packet_out =
|
2019-09-11 15:27:58 +00:00
|
|
|
get_writeable_packet(conn, GQUIC_BLOCKED_FRAME_SZ);
|
2017-09-22 21:00:03 +00:00
|
|
|
if (!packet_out)
|
|
|
|
return 0;
|
|
|
|
int sz = conn->fc_conn.cn_pf->pf_gen_blocked_frame(
|
|
|
|
packet_out->po_data + packet_out->po_data_sz,
|
|
|
|
lsquic_packet_out_avail(packet_out), stream_id);
|
|
|
|
if (sz < 0) {
|
|
|
|
ABORT_ERROR("gen_blocked_frame failed");
|
|
|
|
return 0;
|
|
|
|
}
|
2018-02-26 21:01:16 +00:00
|
|
|
lsquic_send_ctl_incr_pack_sz(&conn->fc_send_ctl, packet_out, sz);
|
2017-09-22 21:00:03 +00:00
|
|
|
packet_out->po_frame_types |= 1 << QUIC_FRAME_BLOCKED;
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("wrote blocked frame: stream %"PRIu64, stream_id);
|
2017-09-22 21:00:03 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
generate_stream_blocked_frame (struct full_conn *conn, lsquic_stream_t *stream)
|
|
|
|
{
|
|
|
|
if (generate_blocked_frame(conn, stream->id))
|
|
|
|
{
|
|
|
|
lsquic_stream_blocked_frame_sent(stream);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
generate_rst_stream_frame (struct full_conn *conn, lsquic_stream_t *stream)
|
|
|
|
{
|
|
|
|
lsquic_packet_out_t *packet_out;
|
|
|
|
int sz, s;
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
packet_out = get_writeable_packet(conn, GQUIC_RST_STREAM_SZ);
|
2017-09-22 21:00:03 +00:00
|
|
|
if (!packet_out)
|
|
|
|
return 0;
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
/* TODO Possible optimization: instead of using stream->tosend_off as the
|
|
|
|
* offset, keep track of the offset that was actually sent: include it
|
2020-07-29 15:33:52 +00:00
|
|
|
* into frame_rec and update a new per-stream "maximum offset actually
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
* sent" field. Then, if a stream is reset, the connection cap can be
|
|
|
|
* increased.
|
|
|
|
*/
|
2017-09-22 21:00:03 +00:00
|
|
|
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,
|
|
|
|
stream->tosend_off, stream->error_code);
|
|
|
|
if (sz < 0) {
|
|
|
|
ABORT_ERROR("gen_rst_frame failed");
|
|
|
|
return 0;
|
|
|
|
}
|
2018-02-26 21:01:16 +00:00
|
|
|
lsquic_send_ctl_incr_pack_sz(&conn->fc_send_ctl, packet_out, sz);
|
2017-09-22 21:00:03 +00:00
|
|
|
packet_out->po_frame_types |= 1 << QUIC_FRAME_RST_STREAM;
|
|
|
|
s = lsquic_packet_out_add_stream(packet_out, conn->fc_pub.mm, stream,
|
2019-01-21 14:07:02 +00:00
|
|
|
QUIC_FRAME_RST_STREAM, packet_out->po_data_sz, sz);
|
2017-09-22 21:00:03 +00:00
|
|
|
if (s != 0)
|
|
|
|
{
|
|
|
|
ABORT_ERROR("adding stream to packet failed: %s", strerror(errno));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
lsquic_stream_rst_frame_sent(stream);
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("wrote RST: stream %"PRIu64"; offset 0x%"PRIX64"; error code "
|
|
|
|
"%"PRIu64, stream->id, stream->tosend_off, stream->error_code);
|
2017-09-22 21:00:03 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
generate_ping_frame (struct full_conn *conn)
|
|
|
|
{
|
|
|
|
lsquic_packet_out_t *packet_out = get_writeable_packet(conn, 1);
|
|
|
|
if (!packet_out)
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("cannot get writeable packet for PING frame");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
int sz = conn->fc_conn.cn_pf->pf_gen_ping_frame(
|
|
|
|
packet_out->po_data + packet_out->po_data_sz,
|
|
|
|
lsquic_packet_out_avail(packet_out));
|
|
|
|
if (sz < 0) {
|
2021-09-17 18:08:39 +00:00
|
|
|
ABORT_ERROR("gen_ping_frame failed");
|
2017-09-22 21:00:03 +00:00
|
|
|
return;
|
|
|
|
}
|
2018-02-26 21:01:16 +00:00
|
|
|
lsquic_send_ctl_incr_pack_sz(&conn->fc_send_ctl, packet_out, sz);
|
2017-09-22 21:00:03 +00:00
|
|
|
packet_out->po_frame_types |= 1 << QUIC_FRAME_PING;
|
|
|
|
LSQ_DEBUG("wrote PING frame");
|
2020-09-08 15:43:03 +00:00
|
|
|
if (!(conn->fc_flags & FC_SERVER))
|
|
|
|
log_conn_flow_control(conn);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
generate_stop_waiting_frame (struct full_conn *conn)
|
|
|
|
{
|
|
|
|
assert(conn->fc_flags & FC_SEND_STOP_WAITING);
|
|
|
|
|
|
|
|
int sz;
|
|
|
|
unsigned packnum_len;
|
|
|
|
lsquic_packno_t least_unacked;
|
|
|
|
lsquic_packet_out_t *packet_out;
|
|
|
|
|
|
|
|
/* Get packet that has room for the minimum size STOP_WAITING frame: */
|
2019-04-01 15:41:55 +00:00
|
|
|
packnum_len = conn->fc_conn.cn_pf->pf_packno_bits2len(GQUIC_PACKNO_LEN_1);
|
|
|
|
packet_out = get_writeable_packet(conn, 1 + packnum_len);
|
2017-09-22 21:00:03 +00:00
|
|
|
if (!packet_out)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Now calculate number of bytes we really need. If there is not enough
|
|
|
|
* room in the current packet, get a new one.
|
|
|
|
*/
|
2019-04-01 15:41:55 +00:00
|
|
|
packnum_len = conn->fc_conn.cn_pf->pf_packno_bits2len(
|
|
|
|
lsquic_packet_out_packno_bits(packet_out));
|
2017-09-22 21:00:03 +00:00
|
|
|
if ((unsigned) lsquic_packet_out_avail(packet_out) < 1 + packnum_len)
|
|
|
|
{
|
|
|
|
packet_out = get_writeable_packet(conn, 1 + packnum_len);
|
|
|
|
if (!packet_out)
|
|
|
|
return;
|
|
|
|
/* Here, a new packet has been allocated, The number of bytes needed
|
|
|
|
* to represent packet number in the STOP_WAITING frame may have
|
|
|
|
* increased. However, this does not matter, because the newly
|
|
|
|
* allocated packet must have room for a STOP_WAITING frame of any
|
|
|
|
* size.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
least_unacked = lsquic_send_ctl_smallest_unacked(&conn->fc_send_ctl);
|
|
|
|
sz = conn->fc_conn.cn_pf->pf_gen_stop_waiting_frame(
|
|
|
|
packet_out->po_data + packet_out->po_data_sz,
|
|
|
|
lsquic_packet_out_avail(packet_out), packet_out->po_packno,
|
|
|
|
lsquic_packet_out_packno_bits(packet_out), least_unacked);
|
|
|
|
if (sz < 0) {
|
|
|
|
ABORT_ERROR("gen_stop_waiting_frame failed");
|
|
|
|
return;
|
|
|
|
}
|
2020-07-29 15:33:52 +00:00
|
|
|
if (0 != lsquic_packet_out_add_frame(packet_out, conn->fc_pub.mm, 0,
|
|
|
|
QUIC_FRAME_STOP_WAITING, packet_out->po_data_sz, sz))
|
|
|
|
{
|
|
|
|
ABORT_ERROR("adding frame to packet failed: %d", errno);
|
|
|
|
return;
|
|
|
|
}
|
2018-02-26 21:01:16 +00:00
|
|
|
lsquic_send_ctl_incr_pack_sz(&conn->fc_send_ctl, packet_out, sz);
|
2017-09-22 21:00:03 +00:00
|
|
|
packet_out->po_regen_sz += sz;
|
|
|
|
packet_out->po_frame_types |= 1 << QUIC_FRAME_STOP_WAITING;
|
|
|
|
conn->fc_flags &= ~FC_SEND_STOP_WAITING;
|
|
|
|
LSQ_DEBUG("wrote STOP_WAITING frame: least unacked: %"PRIu64,
|
|
|
|
least_unacked);
|
|
|
|
EV_LOG_GENERATED_STOP_WAITING_FRAME(LSQUIC_LOG_CONN_ID, least_unacked);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
process_stream_ready_to_send (struct full_conn *conn, lsquic_stream_t *stream)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
int r = 1;
|
2019-09-11 15:27:58 +00:00
|
|
|
if (stream->sm_qflags & SMQF_SEND_WUF)
|
2017-09-22 21:00:03 +00:00
|
|
|
r &= generate_wuf_stream(conn, stream);
|
2019-09-11 15:27:58 +00:00
|
|
|
if (stream->sm_qflags & SMQF_SEND_BLOCKED)
|
2017-09-22 21:00:03 +00:00
|
|
|
r &= generate_stream_blocked_frame(conn, stream);
|
2019-09-11 15:27:58 +00:00
|
|
|
if (stream->sm_qflags & SMQF_SEND_RST)
|
2017-09-22 21:00:03 +00:00
|
|
|
r &= generate_rst_stream_frame(conn, stream);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
static void
|
2017-09-22 21:00:03 +00:00
|
|
|
process_streams_ready_to_send (struct full_conn *conn)
|
|
|
|
{
|
|
|
|
lsquic_stream_t *stream;
|
|
|
|
struct stream_prio_iter spi;
|
|
|
|
|
|
|
|
assert(!TAILQ_EMPTY(&conn->fc_pub.sending_streams));
|
|
|
|
|
|
|
|
lsquic_spi_init(&spi, TAILQ_FIRST(&conn->fc_pub.sending_streams),
|
|
|
|
TAILQ_LAST(&conn->fc_pub.sending_streams, lsquic_streams_tailq),
|
|
|
|
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_send_stream),
|
2020-10-07 13:41:26 +00:00
|
|
|
&conn->fc_pub, "send", NULL, NULL);
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
for (stream = lsquic_spi_first(&spi); stream;
|
|
|
|
stream = lsquic_spi_next(&spi))
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
if (!process_stream_ready_to_send(conn, stream))
|
2017-09-22 21:00:03 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-02-26 21:01:16 +00:00
|
|
|
/* Return true if packetized, false otherwise */
|
|
|
|
static int
|
2019-09-11 15:27:58 +00:00
|
|
|
packetize_standalone_stream_reset (struct full_conn *conn, lsquic_stream_id_t stream_id)
|
2018-02-26 21:01:16 +00:00
|
|
|
{
|
|
|
|
lsquic_packet_out_t *packet_out;
|
|
|
|
int sz;
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
packet_out = get_writeable_packet(conn, GQUIC_RST_STREAM_SZ);
|
2018-02-26 21:01:16 +00:00
|
|
|
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;
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("generated standalone RST_STREAM frame for stream %"PRIu64,
|
2018-02-26 21:01:16 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-09 14:04:52 +00:00
|
|
|
static void
|
|
|
|
create_delayed_streams (struct full_conn *conn)
|
|
|
|
{
|
|
|
|
unsigned stream_count, avail, i;
|
2019-01-30 20:28:35 +00:00
|
|
|
struct lsquic_stream **new_streams;
|
2018-08-09 14:04:52 +00:00
|
|
|
|
|
|
|
stream_count = count_streams(conn, 0);
|
|
|
|
|
|
|
|
if (stream_count >= conn->fc_cfg.max_streams_out)
|
|
|
|
return;
|
|
|
|
|
|
|
|
avail = conn->fc_cfg.max_streams_out - stream_count;
|
|
|
|
if (conn->fc_n_delayed_streams < avail)
|
|
|
|
avail = conn->fc_n_delayed_streams;
|
2019-02-01 20:16:24 +00:00
|
|
|
if (avail == 0)
|
|
|
|
return;
|
2018-08-09 14:04:52 +00:00
|
|
|
|
2019-01-30 20:28:35 +00:00
|
|
|
new_streams = malloc(sizeof(new_streams[0]) * avail);
|
2019-02-01 20:12:02 +00:00
|
|
|
if (!new_streams)
|
|
|
|
{
|
|
|
|
ABORT_WARN("%s: malloc failed", __func__);
|
|
|
|
return;
|
|
|
|
}
|
2018-08-09 14:04:52 +00:00
|
|
|
|
|
|
|
LSQ_DEBUG("creating delayed streams");
|
|
|
|
for (i = 0; i < avail; ++i)
|
|
|
|
{
|
|
|
|
/* Delay calling on_new in order not to let the user screw up
|
|
|
|
* the counts by making more streams.
|
|
|
|
*/
|
|
|
|
new_streams[i] = new_stream(conn, generate_stream_id(conn), 0);
|
|
|
|
if (!new_streams[i])
|
|
|
|
{
|
|
|
|
ABORT_ERROR("%s: cannot create new stream: %s", __func__,
|
|
|
|
strerror(errno));
|
2019-01-30 20:28:35 +00:00
|
|
|
goto cleanup;
|
2018-08-09 14:04:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
LSQ_DEBUG("created %u delayed stream%.*s", avail, avail != 1, "s");
|
|
|
|
|
|
|
|
assert(count_streams(conn, 0) <= conn->fc_cfg.max_streams_out);
|
|
|
|
conn->fc_n_delayed_streams -= avail;
|
|
|
|
|
|
|
|
for (i = 0; i < avail; ++i)
|
|
|
|
lsquic_stream_call_on_new(new_streams[i]);
|
2019-01-30 20:28:35 +00:00
|
|
|
cleanup:
|
|
|
|
free(new_streams);
|
2018-08-09 14:04:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
static void
|
|
|
|
service_streams (struct full_conn *conn)
|
|
|
|
{
|
|
|
|
struct lsquic_hash_elem *el;
|
|
|
|
lsquic_stream_t *stream, *next;
|
2018-08-09 14:04:52 +00:00
|
|
|
int closed_some = 0;
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
for (stream = TAILQ_FIRST(&conn->fc_pub.service_streams); stream; stream = next)
|
|
|
|
{
|
|
|
|
next = TAILQ_NEXT(stream, next_service_stream);
|
2019-09-11 15:27:58 +00:00
|
|
|
if (stream->sm_qflags & SMQF_ABORT_CONN)
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
/* No need to unset this flag or remove this stream: the connection
|
|
|
|
* is about to be aborted.
|
|
|
|
*/
|
2019-09-11 15:27:58 +00:00
|
|
|
ABORT_ERROR("aborted due to error in stream %"PRIu64, stream->id);
|
|
|
|
if (stream->sm_qflags & SMQF_CALL_ONCLOSE)
|
2018-08-09 14:04:52 +00:00
|
|
|
{
|
2017-09-22 21:00:03 +00:00
|
|
|
lsquic_stream_call_on_close(stream);
|
2018-08-09 14:04:52 +00:00
|
|
|
closed_some |= is_our_stream(conn, stream);
|
|
|
|
conn_mark_stream_closed(conn, stream->id);
|
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
if (stream->sm_qflags & SMQF_FREE_STREAM)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
TAILQ_REMOVE(&conn->fc_pub.service_streams, stream, next_service_stream);
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
el = lsquic_hash_find(conn->fc_pub.all_streams, &stream->id, sizeof(stream->id));
|
2017-09-22 21:00:03 +00:00
|
|
|
if (el)
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
lsquic_hash_erase(conn->fc_pub.all_streams, el);
|
2017-09-22 21:00:03 +00:00
|
|
|
SAVE_STREAM_HISTORY(conn, stream);
|
|
|
|
lsquic_stream_destroy(stream);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (either_side_going_away(conn))
|
2019-09-11 15:27:58 +00:00
|
|
|
{
|
2017-09-22 21:00:03 +00:00
|
|
|
while (conn->fc_n_delayed_streams)
|
|
|
|
{
|
|
|
|
--conn->fc_n_delayed_streams;
|
|
|
|
LSQ_DEBUG("goaway mode: delayed stream results in null ctor");
|
|
|
|
(void) conn->fc_stream_ifs[STREAM_IF_STD].stream_if->on_new_stream(
|
|
|
|
conn->fc_stream_ifs[STREAM_IF_STD].stream_if_ctx, NULL);
|
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
maybe_close_conn(conn);
|
|
|
|
}
|
2017-09-22 21:00:03 +00:00
|
|
|
else
|
2018-08-09 14:04:52 +00:00
|
|
|
if (closed_some && conn->fc_n_delayed_streams)
|
|
|
|
create_delayed_streams(conn);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
struct filter_stream_ctx
|
|
|
|
{
|
|
|
|
struct full_conn *conn;
|
|
|
|
uint32_t last_stream_id,
|
|
|
|
max_peer_stream_id;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
filter_out_old_streams (void *ctx, lsquic_stream_t *stream)
|
|
|
|
{
|
|
|
|
struct filter_stream_ctx *const fctx = ctx;
|
|
|
|
return ((!((stream->id ^ fctx->last_stream_id) & 1) &&
|
|
|
|
stream->id > fctx->last_stream_id)
|
|
|
|
||
|
|
|
|
(!((stream->id ^ fctx->max_peer_stream_id) & 1) &&
|
|
|
|
stream->id > fctx->max_peer_stream_id));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-04-09 13:39:38 +00:00
|
|
|
static void
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
process_streams_read_events (struct full_conn *conn)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
lsquic_stream_t *stream;
|
2019-09-11 15:27:58 +00:00
|
|
|
struct filter_stream_ctx fctx;
|
|
|
|
enum stream_q_flags q_flags;
|
2019-01-16 20:13:59 +00:00
|
|
|
int needs_service;
|
2017-09-22 21:00:03 +00:00
|
|
|
struct stream_prio_iter spi;
|
|
|
|
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
if (TAILQ_EMPTY(&conn->fc_pub.read_streams))
|
2018-04-09 13:39:38 +00:00
|
|
|
return;
|
2017-09-22 21:00:03 +00:00
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
fctx.last_stream_id = conn->fc_last_stream_id;
|
|
|
|
fctx.max_peer_stream_id = conn->fc_max_peer_stream_id;
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
lsquic_spi_init(&spi, TAILQ_FIRST(&conn->fc_pub.read_streams),
|
|
|
|
TAILQ_LAST(&conn->fc_pub.read_streams, lsquic_streams_tailq),
|
|
|
|
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_read_stream),
|
2020-10-07 13:41:26 +00:00
|
|
|
&conn->fc_pub, "read", NULL, NULL);
|
2017-09-22 21:00:03 +00:00
|
|
|
|
2019-01-16 20:13:59 +00:00
|
|
|
needs_service = 0;
|
2017-09-22 21:00:03 +00:00
|
|
|
for (stream = lsquic_spi_first(&spi); stream;
|
|
|
|
stream = lsquic_spi_next(&spi))
|
2019-01-16 20:13:59 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
q_flags = stream->sm_qflags & SMQF_SERVICE_FLAGS;
|
2018-04-09 13:39:38 +00:00
|
|
|
lsquic_stream_dispatch_read_events(stream);
|
2019-09-11 15:27:58 +00:00
|
|
|
needs_service |= q_flags ^ (stream->sm_qflags & SMQF_SERVICE_FLAGS);
|
2019-01-16 20:13:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (needs_service)
|
|
|
|
service_streams(conn);
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
/* If new streams were created as result of the read dispatching above,
|
|
|
|
* process these new streams. This logic is only applicable to in the
|
|
|
|
* server mode, as a client that creates a stream from an on_read() event
|
|
|
|
* is not likely to want to *read* from it immediately.
|
|
|
|
*/
|
|
|
|
if ((conn->fc_flags & FC_SERVER) &&
|
|
|
|
(fctx.last_stream_id < conn->fc_last_stream_id ||
|
|
|
|
fctx.max_peer_stream_id < conn->fc_max_peer_stream_id))
|
|
|
|
{
|
|
|
|
fctx.conn = conn;
|
|
|
|
lsquic_spi_init(&spi, TAILQ_FIRST(&conn->fc_pub.read_streams),
|
|
|
|
TAILQ_LAST(&conn->fc_pub.read_streams, lsquic_streams_tailq),
|
|
|
|
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_read_stream),
|
2020-10-07 13:41:26 +00:00
|
|
|
&conn->fc_pub, "read-new",
|
2019-09-11 15:27:58 +00:00
|
|
|
filter_out_old_streams, &fctx);
|
|
|
|
for (stream = lsquic_spi_first(&spi); stream;
|
|
|
|
stream = lsquic_spi_next(&spi))
|
|
|
|
lsquic_stream_dispatch_read_events(stream);
|
|
|
|
}
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
maybe_conn_flush_headers_stream (struct full_conn *conn)
|
|
|
|
{
|
|
|
|
lsquic_stream_t *stream;
|
|
|
|
|
|
|
|
if (conn->fc_flags & FC_HTTP)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
stream = lsquic_headers_stream_get_stream(conn->fc_pub.u.gquic.hs);
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
if (lsquic_stream_has_data_to_flush(stream))
|
|
|
|
(void) lsquic_stream_flush(stream);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
}
|
2017-09-22 21:00:03 +00:00
|
|
|
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
|
|
|
|
static void
|
|
|
|
process_streams_write_events (struct full_conn *conn, int high_prio)
|
|
|
|
{
|
|
|
|
lsquic_stream_t *stream;
|
|
|
|
struct stream_prio_iter spi;
|
|
|
|
|
|
|
|
lsquic_spi_init(&spi, TAILQ_FIRST(&conn->fc_pub.write_streams),
|
|
|
|
TAILQ_LAST(&conn->fc_pub.write_streams, lsquic_streams_tailq),
|
|
|
|
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_write_stream),
|
2020-10-07 13:41:26 +00:00
|
|
|
&conn->fc_pub,
|
2019-01-16 20:13:59 +00:00
|
|
|
high_prio ? "write-high" : "write-low", NULL, NULL);
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
|
|
|
|
if (high_prio)
|
|
|
|
lsquic_spi_drop_non_high(&spi);
|
|
|
|
else
|
|
|
|
lsquic_spi_drop_high(&spi);
|
|
|
|
|
2018-02-26 21:01:16 +00:00
|
|
|
for (stream = lsquic_spi_first(&spi); stream && write_is_possible(conn);
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
stream = lsquic_spi_next(&spi))
|
2019-09-11 15:27:58 +00:00
|
|
|
if (stream->sm_qflags & SMQF_WRITE_Q_FLAGS)
|
2019-03-05 21:56:02 +00:00
|
|
|
lsquic_stream_dispatch_write_events(stream);
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
|
|
|
|
maybe_conn_flush_headers_stream(conn);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-04-09 13:39:38 +00:00
|
|
|
static void
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
process_hsk_stream_read_events (struct full_conn *conn)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
lsquic_stream_t *stream;
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
TAILQ_FOREACH(stream, &conn->fc_pub.read_streams, next_read_stream)
|
2019-12-23 21:14:20 +00:00
|
|
|
if (lsquic_stream_is_crypto(stream))
|
2018-04-09 13:39:38 +00:00
|
|
|
{
|
|
|
|
lsquic_stream_dispatch_read_events(stream);
|
|
|
|
break;
|
|
|
|
}
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
static void
|
|
|
|
process_hsk_stream_write_events (struct full_conn *conn)
|
|
|
|
{
|
|
|
|
lsquic_stream_t *stream;
|
|
|
|
TAILQ_FOREACH(stream, &conn->fc_pub.write_streams, next_write_stream)
|
2019-12-23 21:14:20 +00:00
|
|
|
if (lsquic_stream_is_crypto(stream))
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
{
|
|
|
|
lsquic_stream_dispatch_write_events(stream);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
static void
|
|
|
|
generate_ack_frame (struct full_conn *conn)
|
|
|
|
{
|
|
|
|
lsquic_packet_out_t *packet_out;
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
packet_out = lsquic_send_ctl_new_packet_out(&conn->fc_send_ctl, 0, PNS_APP,
|
|
|
|
&conn->fc_path);
|
2019-01-21 14:07:02 +00:00
|
|
|
if (packet_out)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-01-21 14:07:02 +00:00
|
|
|
lsquic_send_ctl_scheduled_one(&conn->fc_send_ctl, packet_out);
|
|
|
|
full_conn_ci_write_ack(&conn->fc_conn, packet_out);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
else
|
2019-01-21 14:07:02 +00:00
|
|
|
ABORT_ERROR("cannot allocate packet: %s", strerror(errno));
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
conn_ok_to_close (const struct full_conn *conn)
|
|
|
|
{
|
|
|
|
assert(conn->fc_flags & FC_CLOSING);
|
|
|
|
return !(conn->fc_flags & FC_SERVER)
|
|
|
|
|| (conn->fc_flags & FC_RECV_CLOSE)
|
|
|
|
|| (
|
|
|
|
!lsquic_send_ctl_have_outgoing_stream_frames(&conn->fc_send_ctl)
|
2021-01-08 16:38:46 +00:00
|
|
|
&& lsquic_hash_count(conn->fc_pub.all_streams) <= N_SPECIAL_STREAMS
|
2017-09-22 21:00:03 +00:00
|
|
|
&& lsquic_send_ctl_have_unacked_stream_frames(&conn->fc_send_ctl) == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static enum tick_st
|
|
|
|
immediate_close (struct full_conn *conn)
|
|
|
|
{
|
|
|
|
lsquic_packet_out_t *packet_out;
|
|
|
|
const char *error_reason;
|
|
|
|
unsigned error_code;
|
|
|
|
int sz;
|
|
|
|
|
|
|
|
if (conn->fc_flags & (FC_TICK_CLOSE|FC_GOT_PRST))
|
|
|
|
return TICK_CLOSE;
|
|
|
|
|
|
|
|
conn->fc_flags |= FC_TICK_CLOSE;
|
|
|
|
|
|
|
|
/* No reason to send anything that's been scheduled if connection is
|
|
|
|
* being closed immedately. This also ensures that packet numbers
|
|
|
|
* sequence is always increasing.
|
|
|
|
*/
|
|
|
|
lsquic_send_ctl_drop_scheduled(&conn->fc_send_ctl);
|
|
|
|
|
|
|
|
if ((conn->fc_flags & FC_TIMED_OUT) && conn->fc_settings->es_silent_close)
|
|
|
|
return TICK_CLOSE;
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
packet_out = lsquic_send_ctl_new_packet_out(&conn->fc_send_ctl, 0, PNS_APP,
|
|
|
|
&conn->fc_path);
|
2017-09-22 21:00:03 +00:00
|
|
|
if (!packet_out)
|
|
|
|
{
|
|
|
|
LSQ_WARN("cannot allocate packet: %s", strerror(errno));
|
|
|
|
return TICK_CLOSE;
|
|
|
|
}
|
|
|
|
|
2018-08-17 15:44:54 +00:00
|
|
|
assert(conn->fc_flags & (FC_ERROR|FC_ABORTED|FC_TIMED_OUT|FC_HSK_FAILED));
|
2017-09-22 21:00:03 +00:00
|
|
|
if (conn->fc_flags & FC_ERROR)
|
|
|
|
{
|
|
|
|
error_code = 0x01; /* QUIC_INTERNAL_ERROR */
|
|
|
|
error_reason = "connection error";
|
|
|
|
}
|
|
|
|
else if (conn->fc_flags & FC_ABORTED)
|
|
|
|
{
|
|
|
|
error_code = 0x10; /* QUIC_PEER_GOING_AWAY */
|
|
|
|
error_reason = "user aborted connection";
|
|
|
|
}
|
|
|
|
else if (conn->fc_flags & FC_TIMED_OUT)
|
|
|
|
{
|
|
|
|
error_code = 0x19; /* QUIC_NETWORK_IDLE_TIMEOUT */
|
|
|
|
error_reason = "connection timed out";
|
|
|
|
}
|
2018-08-17 15:44:54 +00:00
|
|
|
else if (conn->fc_flags & FC_HSK_FAILED)
|
|
|
|
{
|
|
|
|
error_code = 0x2A; /* QUIC_PROOF_INVALID */
|
|
|
|
error_reason = "handshake failed";
|
|
|
|
}
|
2017-09-22 21:00:03 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
error_code = 0x10; /* QUIC_PEER_GOING_AWAY */
|
|
|
|
error_reason = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
lsquic_send_ctl_scheduled_one(&conn->fc_send_ctl, packet_out);
|
|
|
|
sz = conn->fc_conn.cn_pf->pf_gen_connect_close_frame(
|
|
|
|
packet_out->po_data + packet_out->po_data_sz,
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_packet_out_avail(packet_out), 0, error_code,
|
2017-09-22 21:00:03 +00:00
|
|
|
error_reason, error_reason ? strlen(error_reason) : 0);
|
|
|
|
if (sz < 0) {
|
|
|
|
LSQ_WARN("%s failed", __func__);
|
|
|
|
return TICK_CLOSE;
|
|
|
|
}
|
2018-02-26 21:01:16 +00:00
|
|
|
lsquic_send_ctl_incr_pack_sz(&conn->fc_send_ctl, packet_out, sz);
|
2017-09-22 21:00:03 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
static int
|
|
|
|
write_is_possible (struct full_conn *conn)
|
|
|
|
{
|
|
|
|
const lsquic_packet_out_t *packet_out;
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
packet_out = lsquic_send_ctl_last_scheduled(&conn->fc_send_ctl, PNS_APP,
|
|
|
|
&conn->fc_path, 0);
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
return (packet_out && lsquic_packet_out_avail(packet_out) > 10)
|
|
|
|
|| lsquic_send_ctl_can_send(&conn->fc_send_ctl);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-04-25 14:58:55 +00:00
|
|
|
static int
|
|
|
|
should_generate_ack (const struct full_conn *conn)
|
|
|
|
{
|
|
|
|
return (conn->fc_flags & FC_ACK_QUEUED)
|
|
|
|
|| lsquic_send_ctl_lost_ack(&conn->fc_send_ctl);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-01-21 14:07:02 +00:00
|
|
|
static int
|
|
|
|
full_conn_ci_can_write_ack (struct lsquic_conn *lconn)
|
|
|
|
{
|
|
|
|
struct full_conn *conn = (struct full_conn *) lconn;
|
|
|
|
return should_generate_ack(conn);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-09-08 15:43:03 +00:00
|
|
|
struct full_ack_state
|
|
|
|
{
|
|
|
|
enum full_conn_flags conn_flags;
|
|
|
|
enum alarm_id_bit armed_set;
|
|
|
|
unsigned n_slack_akbl;
|
|
|
|
unsigned n_stop_waiting;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
typedef char ack_state_size[sizeof(struct full_ack_state)
|
|
|
|
<= sizeof(struct ack_state) ? 1 : - 1];
|
|
|
|
|
|
|
|
static void
|
|
|
|
full_conn_ci_ack_snapshot (struct lsquic_conn *lconn, struct ack_state *opaque)
|
|
|
|
{
|
|
|
|
struct full_conn *conn = (struct full_conn *) lconn;
|
|
|
|
struct full_ack_state *const ack_state = (struct full_ack_state *) opaque;
|
|
|
|
|
|
|
|
ack_state->conn_flags = conn->fc_flags;
|
|
|
|
ack_state->armed_set = conn->fc_alset.as_armed_set;
|
|
|
|
ack_state->n_slack_akbl = conn->fc_n_slack_akbl;
|
|
|
|
ack_state->n_stop_waiting
|
|
|
|
= lsquic_send_ctl_n_stop_waiting(&conn->fc_send_ctl);
|
|
|
|
LSQ_DEBUG("take ACK snapshot");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
full_conn_ci_ack_rollback (struct lsquic_conn *lconn, struct ack_state *opaque)
|
|
|
|
{
|
|
|
|
struct full_ack_state *const ack_state = (struct full_ack_state *) opaque;
|
|
|
|
struct full_conn *conn = (struct full_conn *) lconn;
|
|
|
|
|
|
|
|
conn->fc_flags &= ~(FC_ACK_HAD_MISS|FC_ACK_QUEUED);
|
|
|
|
conn->fc_flags |= (FC_ACK_HAD_MISS|FC_ACK_QUEUED)
|
|
|
|
& ack_state->conn_flags;
|
|
|
|
|
|
|
|
conn->fc_alset.as_armed_set &= ~ALBIT_ACK_APP;
|
|
|
|
conn->fc_alset.as_armed_set |= ALBIT_ACK_APP & ack_state->armed_set;
|
|
|
|
|
|
|
|
conn->fc_n_slack_akbl = ack_state->n_slack_akbl;
|
|
|
|
conn->fc_send_ctl.sc_n_stop_waiting = ack_state->n_stop_waiting;
|
|
|
|
|
|
|
|
LSQ_DEBUG("roll back ACK state");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-06-09 15:55:56 +00:00
|
|
|
/* This should be called before lsquic_alarmset_ring_expired() */
|
|
|
|
static void
|
|
|
|
maybe_set_noprogress_alarm (struct full_conn *conn, lsquic_time_t now)
|
|
|
|
{
|
|
|
|
lsquic_time_t exp;
|
|
|
|
|
|
|
|
if (conn->fc_flags & FC_NOPROG_TIMEOUT)
|
|
|
|
{
|
|
|
|
if (conn->fc_pub.last_tick)
|
|
|
|
{
|
|
|
|
exp = conn->fc_pub.last_prog + conn->fc_enpub->enp_noprog_timeout;
|
|
|
|
if (!lsquic_alarmset_is_set(&conn->fc_alset, AL_IDLE)
|
|
|
|
|| exp < conn->fc_alset.as_expiry[AL_IDLE])
|
|
|
|
lsquic_alarmset_set(&conn->fc_alset, AL_IDLE, exp);
|
|
|
|
conn->fc_pub.last_tick = now;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
conn->fc_pub.last_tick = now;
|
|
|
|
conn->fc_pub.last_prog = now;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
static enum tick_st
|
|
|
|
full_conn_ci_tick (lsquic_conn_t *lconn, lsquic_time_t now)
|
|
|
|
{
|
|
|
|
struct full_conn *conn = (struct full_conn *) lconn;
|
|
|
|
int have_delayed_packets;
|
|
|
|
unsigned n;
|
2018-04-09 13:39:38 +00:00
|
|
|
int s;
|
|
|
|
enum tick_st tick = 0;
|
2017-09-22 21:00:03 +00:00
|
|
|
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
#define CLOSE_IF_NECESSARY() do { \
|
|
|
|
if (conn->fc_flags & FC_IMMEDIATE_CLOSE_FLAGS) \
|
|
|
|
{ \
|
2018-04-09 13:39:38 +00:00
|
|
|
tick |= immediate_close(conn); \
|
2018-04-23 20:12:38 +00:00
|
|
|
goto close_end; \
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
} \
|
2017-09-22 21:00:03 +00:00
|
|
|
} while (0)
|
|
|
|
|
|
|
|
#define RETURN_IF_OUT_OF_PACKETS() do { \
|
|
|
|
if (!lsquic_send_ctl_can_send(&conn->fc_send_ctl)) \
|
|
|
|
{ \
|
|
|
|
if (0 == lsquic_send_ctl_n_scheduled(&conn->fc_send_ctl)) \
|
|
|
|
{ \
|
|
|
|
LSQ_DEBUG("used up packet allowance, quiet now (line %d)", \
|
|
|
|
__LINE__); \
|
2018-04-09 13:39:38 +00:00
|
|
|
tick |= TICK_QUIET; \
|
2017-09-22 21:00:03 +00:00
|
|
|
} \
|
|
|
|
else \
|
|
|
|
{ \
|
|
|
|
LSQ_DEBUG("used up packet allowance, sending now (line %d)",\
|
|
|
|
__LINE__); \
|
2018-04-09 13:39:38 +00:00
|
|
|
tick |= TICK_SEND; \
|
2017-09-22 21:00:03 +00:00
|
|
|
} \
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
goto end; \
|
2017-09-22 21:00:03 +00:00
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
2019-01-16 20:13:59 +00:00
|
|
|
#if LSQUIC_CONN_STATS
|
2018-03-09 19:17:39 +00:00
|
|
|
++conn->fc_stats.n_ticks;
|
|
|
|
#endif
|
|
|
|
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
if (LSQ_LOG_ENABLED(LSQ_LOG_DEBUG)
|
|
|
|
&& conn->fc_mem_logged_last + 1000000 <= now)
|
|
|
|
{
|
|
|
|
conn->fc_mem_logged_last = now;
|
|
|
|
LSQ_DEBUG("memory used: %zd bytes", calc_mem_used(conn));
|
|
|
|
}
|
|
|
|
|
2018-03-09 19:17:39 +00:00
|
|
|
if (conn->fc_flags & FC_HAVE_SAVED_ACK)
|
|
|
|
{
|
|
|
|
(void) /* If there is an error, we'll fail shortly */
|
2019-12-30 16:29:05 +00:00
|
|
|
process_ack(conn, &conn->fc_ack, conn->fc_saved_ack_received, now);
|
2018-03-09 19:17:39 +00:00
|
|
|
conn->fc_flags &= ~FC_HAVE_SAVED_ACK;
|
|
|
|
}
|
|
|
|
|
2020-06-09 15:55:56 +00:00
|
|
|
maybe_set_noprogress_alarm(conn, now);
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_send_ctl_tick_in(&conn->fc_send_ctl, now);
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
lsquic_send_ctl_set_buffer_stream_packets(&conn->fc_send_ctl, 1);
|
2017-09-22 21:00:03 +00:00
|
|
|
CLOSE_IF_NECESSARY();
|
|
|
|
|
|
|
|
lsquic_alarmset_ring_expired(&conn->fc_alset, now);
|
|
|
|
CLOSE_IF_NECESSARY();
|
|
|
|
|
|
|
|
/* To make things simple, only stream 1 is active until the handshake
|
|
|
|
* has been completed. This will be adjusted in the future: the client
|
|
|
|
* does not want to wait if it has the server information.
|
|
|
|
*/
|
|
|
|
if (conn->fc_conn.cn_flags & LSCONN_HANDSHAKE_DONE)
|
2018-04-09 13:39:38 +00:00
|
|
|
process_streams_read_events(conn);
|
2017-09-22 21:00:03 +00:00
|
|
|
else
|
2018-04-09 13:39:38 +00:00
|
|
|
process_hsk_stream_read_events(conn);
|
2017-09-22 21:00:03 +00:00
|
|
|
CLOSE_IF_NECESSARY();
|
|
|
|
|
2018-02-26 21:01:16 +00:00
|
|
|
if (lsquic_send_ctl_pacer_blocked(&conn->fc_send_ctl))
|
|
|
|
goto skip_write;
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
if (conn->fc_flags & FC_FIRST_TICK)
|
|
|
|
{
|
|
|
|
conn->fc_flags &= ~FC_FIRST_TICK;
|
|
|
|
have_delayed_packets = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
/* If there are any scheduled packets at this point, it means that
|
|
|
|
* they were not sent during previous tick; in other words, they
|
|
|
|
* are delayed. When there are delayed packets, the only packet
|
|
|
|
* we sometimes add is a packet with an ACK frame, and we add it
|
|
|
|
* to the *front* of the queue.
|
|
|
|
*/
|
2018-02-26 21:01:16 +00:00
|
|
|
have_delayed_packets = lsquic_send_ctl_maybe_squeeze_sched(
|
2017-09-22 21:00:03 +00:00
|
|
|
&conn->fc_send_ctl);
|
|
|
|
|
2018-04-25 14:58:55 +00:00
|
|
|
if (should_generate_ack(conn))
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
if (have_delayed_packets)
|
|
|
|
lsquic_send_ctl_reset_packnos(&conn->fc_send_ctl);
|
|
|
|
|
|
|
|
generate_ack_frame(conn);
|
|
|
|
CLOSE_IF_NECESSARY();
|
|
|
|
|
|
|
|
/* Try to send STOP_WAITING frame at the same time we send an ACK
|
|
|
|
* This follows reference implementation.
|
|
|
|
*/
|
|
|
|
if (!(conn->fc_flags & FC_NSTP))
|
|
|
|
conn->fc_flags |= FC_SEND_STOP_WAITING;
|
|
|
|
|
|
|
|
if (have_delayed_packets)
|
|
|
|
{
|
|
|
|
if (conn->fc_flags & FC_SEND_STOP_WAITING)
|
|
|
|
{
|
|
|
|
/* TODO: ensure that STOP_WAITING frame is in the same packet
|
|
|
|
* as the ACK frame in delayed packet mode.
|
|
|
|
*/
|
|
|
|
generate_stop_waiting_frame(conn);
|
|
|
|
CLOSE_IF_NECESSARY();
|
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_send_ctl_ack_to_front(&conn->fc_send_ctl, 1);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (have_delayed_packets)
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
{
|
2017-09-22 21:00:03 +00:00
|
|
|
/* The reason for not adding STOP_WAITING and other frames below
|
|
|
|
* to the packet carrying ACK frame generated when there are delayed
|
|
|
|
* packets is so that if the ACK packet itself is delayed, it can be
|
|
|
|
* dropped and replaced by new ACK packet. This way, we are never
|
|
|
|
* more than 1 packet over CWND.
|
|
|
|
*/
|
2018-04-09 13:39:38 +00:00
|
|
|
tick |= TICK_SEND;
|
2021-06-02 04:39:15 +00:00
|
|
|
if (conn->fc_flags & FC_CLOSING)
|
|
|
|
goto end_write;
|
|
|
|
else
|
|
|
|
goto end;
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
}
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
/* 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
|
|
|
|
* tried next time around.
|
|
|
|
*/
|
|
|
|
if (conn->fc_flags & FC_SEND_STOP_WAITING)
|
|
|
|
{
|
|
|
|
generate_stop_waiting_frame(conn);
|
|
|
|
CLOSE_IF_NECESSARY();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lsquic_cfcw_fc_offsets_changed(&conn->fc_pub.cfcw) ||
|
|
|
|
(conn->fc_flags & FC_SEND_WUF))
|
|
|
|
{
|
|
|
|
conn->fc_flags |= FC_SEND_WUF;
|
|
|
|
generate_wuf_conn(conn);
|
|
|
|
CLOSE_IF_NECESSARY();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (conn->fc_flags & FC_SEND_GOAWAY)
|
|
|
|
{
|
|
|
|
generate_goaway_frame(conn);
|
|
|
|
CLOSE_IF_NECESSARY();
|
|
|
|
}
|
|
|
|
|
|
|
|
n = lsquic_send_ctl_reschedule_packets(&conn->fc_send_ctl);
|
|
|
|
if (n > 0)
|
|
|
|
CLOSE_IF_NECESSARY();
|
|
|
|
|
|
|
|
if (conn->fc_conn.cn_flags & LSCONN_SEND_BLOCKED)
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
RETURN_IF_OUT_OF_PACKETS();
|
2017-09-22 21:00:03 +00:00
|
|
|
if (generate_blocked_frame(conn, 0))
|
|
|
|
conn->fc_conn.cn_flags &= ~LSCONN_SEND_BLOCKED;
|
|
|
|
}
|
|
|
|
|
2018-02-26 21:01:16 +00:00
|
|
|
if (!STAILQ_EMPTY(&conn->fc_stream_ids_to_reset))
|
|
|
|
{
|
|
|
|
packetize_standalone_stream_resets(conn);
|
|
|
|
CLOSE_IF_NECESSARY();
|
|
|
|
}
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
if (!TAILQ_EMPTY(&conn->fc_pub.sending_streams))
|
|
|
|
{
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
process_streams_ready_to_send(conn);
|
|
|
|
CLOSE_IF_NECESSARY();
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
|
|
|
|
lsquic_send_ctl_set_buffer_stream_packets(&conn->fc_send_ctl, 0);
|
2020-07-06 21:35:21 +00:00
|
|
|
if (!handshake_done_or_doing_sess_resume(conn))
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
{
|
|
|
|
process_hsk_stream_write_events(conn);
|
2020-11-11 12:50:03 +00:00
|
|
|
lsquic_send_ctl_maybe_app_limited(&conn->fc_send_ctl, &conn->fc_path);
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
goto end_write;
|
|
|
|
}
|
|
|
|
|
|
|
|
maybe_conn_flush_headers_stream(conn);
|
|
|
|
|
|
|
|
s = lsquic_send_ctl_schedule_buffered(&conn->fc_send_ctl, BPT_HIGHEST_PRIO);
|
|
|
|
conn->fc_flags |= (s < 0) << FC_BIT_ERROR;
|
|
|
|
if (!write_is_possible(conn))
|
|
|
|
goto end_write;
|
|
|
|
|
|
|
|
if (!TAILQ_EMPTY(&conn->fc_pub.write_streams))
|
|
|
|
{
|
|
|
|
process_streams_write_events(conn, 1);
|
|
|
|
if (!write_is_possible(conn))
|
|
|
|
goto end_write;
|
|
|
|
}
|
|
|
|
|
|
|
|
s = lsquic_send_ctl_schedule_buffered(&conn->fc_send_ctl, BPT_OTHER_PRIO);
|
|
|
|
conn->fc_flags |= (s < 0) << FC_BIT_ERROR;
|
|
|
|
if (!write_is_possible(conn))
|
|
|
|
goto end_write;
|
|
|
|
|
|
|
|
if (!TAILQ_EMPTY(&conn->fc_pub.write_streams))
|
|
|
|
process_streams_write_events(conn, 0);
|
|
|
|
|
2019-09-12 18:39:50 +00:00
|
|
|
lsquic_send_ctl_maybe_app_limited(&conn->fc_send_ctl, &conn->fc_path);
|
|
|
|
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
end_write:
|
2017-09-22 21:00:03 +00:00
|
|
|
|
2018-02-26 21:01:16 +00:00
|
|
|
skip_write:
|
2017-09-22 21:00:03 +00:00
|
|
|
if ((conn->fc_flags & FC_CLOSING) && conn_ok_to_close(conn))
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("connection is OK to close");
|
|
|
|
/* This is normal termination sequence.
|
|
|
|
*
|
|
|
|
* Generate CONNECTION_CLOSE frame if we are responding to one, have
|
|
|
|
* packets scheduled to send, or silent close flag is not set.
|
|
|
|
*/
|
|
|
|
conn->fc_flags |= FC_TICK_CLOSE;
|
2021-06-12 02:18:01 +00:00
|
|
|
if (conn->fc_flags & FC_RECV_CLOSE)
|
|
|
|
tick |= TICK_CLOSE;
|
2017-09-22 21:00:03 +00:00
|
|
|
if ((conn->fc_flags & FC_RECV_CLOSE) ||
|
|
|
|
0 != lsquic_send_ctl_n_scheduled(&conn->fc_send_ctl) ||
|
|
|
|
!conn->fc_settings->es_silent_close)
|
|
|
|
{
|
2021-05-31 15:24:52 +00:00
|
|
|
RETURN_IF_OUT_OF_PACKETS();
|
2017-09-22 21:00:03 +00:00
|
|
|
generate_connection_close_packet(conn);
|
2021-06-12 02:18:01 +00:00
|
|
|
tick |= TICK_SEND|TICK_CLOSE;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
2021-06-12 02:18:01 +00:00
|
|
|
else
|
|
|
|
tick |= TICK_CLOSE;
|
2021-05-31 15:24:52 +00:00
|
|
|
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
goto end;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (0 == lsquic_send_ctl_n_scheduled(&conn->fc_send_ctl))
|
|
|
|
{
|
|
|
|
if (conn->fc_flags & FC_SEND_PING)
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
RETURN_IF_OUT_OF_PACKETS();
|
2017-09-22 21:00:03 +00:00
|
|
|
conn->fc_flags &= ~FC_SEND_PING;
|
|
|
|
generate_ping_frame(conn);
|
|
|
|
CLOSE_IF_NECESSARY();
|
|
|
|
assert(lsquic_send_ctl_n_scheduled(&conn->fc_send_ctl) != 0);
|
|
|
|
}
|
|
|
|
else
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
{
|
2018-04-09 13:39:38 +00:00
|
|
|
tick |= TICK_QUIET;
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
goto end;
|
|
|
|
}
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
else if (conn->fc_settings->es_ping_period)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
lsquic_alarmset_unset(&conn->fc_alset, AL_PING);
|
|
|
|
lsquic_send_ctl_sanity_check(&conn->fc_send_ctl);
|
|
|
|
conn->fc_flags &= ~FC_SEND_PING; /* It may have rung */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* From the spec:
|
|
|
|
* " The PING frame should be used to keep a connection alive when
|
|
|
|
* " a stream is open.
|
|
|
|
*/
|
2019-09-11 15:27:58 +00:00
|
|
|
if (conn->fc_settings->es_ping_period
|
|
|
|
&& lsquic_hash_count(conn->fc_pub.all_streams) > 0)
|
|
|
|
lsquic_alarmset_set(&conn->fc_alset, AL_PING,
|
|
|
|
now + conn->fc_settings->es_ping_period * 1000 * 1000);
|
2017-09-22 21:00:03 +00:00
|
|
|
|
2018-04-09 13:39:38 +00:00
|
|
|
tick |= TICK_SEND;
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
|
|
|
|
end:
|
2018-04-23 20:12:38 +00:00
|
|
|
service_streams(conn);
|
|
|
|
CLOSE_IF_NECESSARY();
|
|
|
|
|
|
|
|
close_end:
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
lsquic_send_ctl_set_buffer_stream_packets(&conn->fc_send_ctl, 1);
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_send_ctl_tick_out(&conn->fc_send_ctl);
|
2018-04-09 13:39:38 +00:00
|
|
|
return tick;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-06-09 15:55:56 +00:00
|
|
|
static void
|
|
|
|
set_earliest_idle_alarm (struct full_conn *conn, lsquic_time_t idle_conn_to)
|
|
|
|
{
|
|
|
|
lsquic_time_t exp;
|
|
|
|
|
|
|
|
if (conn->fc_pub.last_prog
|
|
|
|
&& (assert(conn->fc_flags & FC_NOPROG_TIMEOUT),
|
|
|
|
exp = conn->fc_pub.last_prog + conn->fc_enpub->enp_noprog_timeout,
|
|
|
|
exp < idle_conn_to))
|
|
|
|
idle_conn_to = exp;
|
|
|
|
lsquic_alarmset_set(&conn->fc_alset, AL_IDLE, idle_conn_to);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
static void
|
|
|
|
full_conn_ci_packet_in (lsquic_conn_t *lconn, lsquic_packet_in_t *packet_in)
|
|
|
|
{
|
|
|
|
struct full_conn *conn = (struct full_conn *) lconn;
|
|
|
|
|
2019-01-16 20:13:59 +00:00
|
|
|
#if LSQUIC_CONN_STATS
|
|
|
|
conn->fc_stats.in.bytes += packet_in->pi_data_sz;
|
|
|
|
#endif
|
2020-06-09 15:55:56 +00:00
|
|
|
set_earliest_idle_alarm(conn,
|
2017-09-22 21:00:03 +00:00
|
|
|
packet_in->pi_received + conn->fc_settings->es_idle_conn_to);
|
|
|
|
if (0 == (conn->fc_flags & FC_ERROR))
|
|
|
|
if (0 != process_incoming_packet(conn, packet_in))
|
|
|
|
conn->fc_flags |= FC_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static lsquic_packet_out_t *
|
2020-09-15 20:42:13 +00:00
|
|
|
full_conn_ci_next_packet_to_send (struct lsquic_conn *lconn,
|
|
|
|
const struct to_coal *unused)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
struct full_conn *conn = (struct full_conn *) lconn;
|
2020-09-15 20:42:13 +00:00
|
|
|
return lsquic_send_ctl_next_packet_to_send(&conn->fc_send_ctl, NULL);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
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;
|
|
|
|
|
2018-02-26 21:01:16 +00:00
|
|
|
recent_packet_hist_new(conn, 1, packet_out->po_sent);
|
|
|
|
recent_packet_hist_frames(conn, 1, packet_out->po_frame_types);
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
if (packet_out->po_frame_types & GQUIC_FRAME_RETRANSMITTABLE_MASK)
|
2017-09-22 21:00:03 +00:00
|
|
|
conn->fc_n_cons_unretx = 0;
|
|
|
|
else
|
|
|
|
++conn->fc_n_cons_unretx;
|
2019-09-11 15:27:58 +00:00
|
|
|
s = lsquic_send_ctl_sent_packet(&conn->fc_send_ctl, packet_out);
|
2017-09-22 21:00:03 +00:00
|
|
|
if (s != 0)
|
|
|
|
ABORT_ERROR("sent packet failed: %s", strerror(errno));
|
2019-01-16 20:13:59 +00:00
|
|
|
#if LSQUIC_CONN_STATS
|
|
|
|
++conn->fc_stats.out.packets;
|
|
|
|
conn->fc_stats.out.bytes += lsquic_packet_out_sent_sz(lconn, packet_out);
|
2017-09-22 21:00:03 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
full_conn_ci_packet_not_sent (lsquic_conn_t *lconn, lsquic_packet_out_t *packet_out)
|
|
|
|
{
|
|
|
|
struct full_conn *conn = (struct full_conn *) lconn;
|
|
|
|
lsquic_send_ctl_delayed_one(&conn->fc_send_ctl, packet_out);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2019-02-04 13:59:11 +00:00
|
|
|
full_conn_ci_hsk_done (lsquic_conn_t *lconn, enum lsquic_hsk_status status)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
struct full_conn *conn = (struct full_conn *) lconn;
|
|
|
|
lsquic_alarmset_unset(&conn->fc_alset, AL_HANDSHAKE);
|
2019-02-04 13:59:11 +00:00
|
|
|
switch (status)
|
|
|
|
{
|
2020-07-06 21:35:21 +00:00
|
|
|
case LSQ_HSK_RESUMED_FAIL:
|
2019-02-04 13:59:11 +00:00
|
|
|
case LSQ_HSK_FAIL:
|
|
|
|
conn->fc_flags |= FC_HSK_FAILED;
|
|
|
|
break;
|
|
|
|
case LSQ_HSK_OK:
|
2020-07-06 21:35:21 +00:00
|
|
|
case LSQ_HSK_RESUMED_OK:
|
2019-02-04 13:59:11 +00:00
|
|
|
if (0 == apply_peer_settings(conn))
|
|
|
|
{
|
|
|
|
if (conn->fc_flags & FC_HTTP)
|
|
|
|
maybe_send_settings(conn);
|
|
|
|
lconn->cn_flags |= LSCONN_HANDSHAKE_DONE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
conn->fc_flags |= FC_ERROR;
|
|
|
|
break;
|
|
|
|
}
|
2018-05-21 19:02:33 +00:00
|
|
|
if (conn->fc_stream_ifs[STREAM_IF_STD].stream_if->on_hsk_done)
|
2019-02-04 13:59:11 +00:00
|
|
|
conn->fc_stream_ifs[STREAM_IF_STD].stream_if->on_hsk_done(lconn,
|
|
|
|
status);
|
2020-07-06 21:35:21 +00:00
|
|
|
if (status == LSQ_HSK_OK || status == LSQ_HSK_RESUMED_OK)
|
2019-09-11 15:27:58 +00:00
|
|
|
{
|
2020-07-06 21:35:21 +00:00
|
|
|
if (conn->fc_stream_ifs[STREAM_IF_STD].stream_if->on_sess_resume_info)
|
|
|
|
conn->fc_conn.cn_esf.g->esf_maybe_dispatch_sess_resume(
|
2019-12-11 14:38:58 +00:00
|
|
|
conn->fc_conn.cn_enc_session,
|
2020-07-06 21:35:21 +00:00
|
|
|
conn->fc_stream_ifs[STREAM_IF_STD].stream_if->on_sess_resume_info);
|
2019-10-15 21:02:21 +00:00
|
|
|
if (conn->fc_n_delayed_streams)
|
|
|
|
create_delayed_streams(conn);
|
2019-12-30 16:29:05 +00:00
|
|
|
if (!(conn->fc_flags & FC_SERVER))
|
|
|
|
lsquic_send_ctl_begin_optack_detection(&conn->fc_send_ctl);
|
2019-09-11 15:27:58 +00:00
|
|
|
}
|
2018-02-26 21:01:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static void
|
|
|
|
full_conn_ci_abort (struct lsquic_conn *lconn)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
struct full_conn *conn = (struct full_conn *) lconn;
|
|
|
|
LSQ_INFO("User aborted connection");
|
|
|
|
conn->fc_flags |= FC_ABORTED;
|
2021-01-06 14:00:05 +00:00
|
|
|
lsquic_engine_add_conn_to_tickable(conn->fc_enpub, lconn);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static void
|
|
|
|
full_conn_ci_internal_error (struct lsquic_conn *lconn,
|
|
|
|
const char *format, ...)
|
|
|
|
{
|
|
|
|
struct full_conn *const conn = (struct full_conn *) lconn;
|
|
|
|
LSQ_INFO("Internal error reported");
|
|
|
|
conn->fc_flags |= FC_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* This function should not be called, as this is specific to IETF QUIC */
|
|
|
|
static void
|
|
|
|
full_conn_ci_abort_error (struct lsquic_conn *lconn, int is_app,
|
|
|
|
unsigned error_code, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
struct full_conn *const conn = (struct full_conn *) lconn;
|
|
|
|
assert(0);
|
|
|
|
LSQ_WARN("(GQUIC) abort error is called unexpectedly");
|
|
|
|
conn->fc_flags |= FC_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
full_conn_ci_close (struct lsquic_conn *lconn)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
struct full_conn *conn = (struct full_conn *) lconn;
|
|
|
|
lsquic_stream_t *stream;
|
|
|
|
struct lsquic_hash_elem *el;
|
|
|
|
|
|
|
|
if (!(conn->fc_flags & FC_CLOSING))
|
|
|
|
{
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
for (el = lsquic_hash_first(conn->fc_pub.all_streams); el;
|
|
|
|
el = lsquic_hash_next(conn->fc_pub.all_streams))
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
stream = lsquic_hashelem_getdata(el);
|
2021-01-08 16:38:46 +00:00
|
|
|
if (!lsquic_stream_is_critical(stream))
|
2021-03-03 14:41:42 +00:00
|
|
|
lsquic_stream_maybe_reset(stream, 0, 1);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
conn->fc_flags |= FC_CLOSING;
|
|
|
|
if (!(conn->fc_flags & FC_GOAWAY_SENT))
|
|
|
|
conn->fc_flags |= FC_SEND_GOAWAY;
|
2020-07-06 21:35:21 +00:00
|
|
|
lsquic_engine_add_conn_to_tickable(conn->fc_enpub, lconn);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static void
|
|
|
|
full_conn_ci_going_away (struct lsquic_conn *lconn)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
struct full_conn *conn = (struct full_conn *) lconn;
|
|
|
|
if (!(conn->fc_flags & (FC_CLOSING|FC_GOING_AWAY)))
|
|
|
|
{
|
|
|
|
LSQ_INFO("connection marked as going away");
|
|
|
|
assert(!(conn->fc_flags & FC_SEND_GOAWAY));
|
|
|
|
conn->fc_flags |= FC_GOING_AWAY;
|
|
|
|
if (!(conn->fc_flags & FC_GOAWAY_SENT))
|
2019-09-11 15:27:58 +00:00
|
|
|
{
|
2017-09-22 21:00:03 +00:00
|
|
|
conn->fc_flags |= FC_SEND_GOAWAY;
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_engine_add_conn_to_tickable(conn->fc_enpub, lconn);
|
|
|
|
}
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
/* Find stream when stream ID is read from something other than a STREAM
|
|
|
|
* frame. If the stream cannot be found or created, the connection is
|
|
|
|
* aborted.
|
2017-09-22 21:00:03 +00:00
|
|
|
*/
|
|
|
|
#if __GNUC__
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
__attribute__((nonnull(4)))
|
2017-09-22 21:00:03 +00:00
|
|
|
#endif
|
|
|
|
static lsquic_stream_t *
|
2019-09-11 15:27:58 +00:00
|
|
|
find_stream_on_non_stream_frame (struct full_conn *conn,
|
|
|
|
lsquic_stream_id_t stream_id, enum stream_ctor_flags stream_ctor_flags,
|
|
|
|
const char *what)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
lsquic_stream_t *stream;
|
|
|
|
unsigned in_count;
|
|
|
|
|
|
|
|
stream = find_stream_by_id(conn, stream_id);
|
|
|
|
if (stream)
|
|
|
|
return stream;
|
|
|
|
|
|
|
|
if (conn_is_stream_closed(conn, stream_id))
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("drop incoming %s for closed stream %"PRIu64, what, stream_id);
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* XXX It seems that if we receive a priority frame for a stream, the
|
|
|
|
* stream should exist or have existed at some point. Thus, if
|
|
|
|
* it does not exist, we should return an error here.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!is_peer_initiated(conn, stream_id))
|
|
|
|
{
|
|
|
|
ABORT_ERROR("frame for never-initiated stream (push promise?)");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
in_count = count_streams(conn, 1);
|
|
|
|
LSQ_DEBUG("number of peer-initiated streams: %u", in_count);
|
|
|
|
if (in_count >= conn->fc_cfg.max_streams_in)
|
|
|
|
{
|
2018-05-18 14:24:20 +00:00
|
|
|
if (!(conn->fc_flags & FC_ABORT_COMPLAINED))
|
|
|
|
{
|
2018-05-18 15:34:36 +00:00
|
|
|
unsigned counts[N_SCNTS];
|
2018-05-18 14:24:20 +00:00
|
|
|
collect_stream_counts(conn, 1, counts);
|
2019-09-11 15:27:58 +00:00
|
|
|
ABORT_WARN("incoming %s for stream %"PRIu64" would exceed "
|
2018-05-18 14:24:20 +00:00
|
|
|
"limit: %u. all: %u; peer: %u; closed: %u; reset: %u; reset "
|
|
|
|
"and not closed: %u",
|
|
|
|
what, stream_id, conn->fc_cfg.max_streams_in, counts[SCNT_ALL],
|
|
|
|
counts[SCNT_PEER], counts[SCNT_CLOSED], counts[SCNT_RESET],
|
|
|
|
counts[SCNT_RES_UNCLO]);
|
|
|
|
}
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if ((conn->fc_flags & FC_GOING_AWAY) &&
|
|
|
|
stream_id > conn->fc_max_peer_stream_id)
|
|
|
|
{
|
2018-02-26 21:01:16 +00:00
|
|
|
maybe_schedule_reset_for_stream(conn, stream_id);
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("going away: reset new incoming stream %"PRIu64, stream_id);
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
stream = new_stream(conn, stream_id, stream_ctor_flags);
|
2017-09-22 21:00:03 +00:00
|
|
|
if (!stream)
|
|
|
|
{
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
ABORT_ERROR("cannot create new stream: %s", strerror(errno));
|
|
|
|
return NULL;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
if (stream_id > conn->fc_max_peer_stream_id)
|
|
|
|
conn->fc_max_peer_stream_id = stream_id;
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
return stream;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
headers_stream_on_conn_error (void *ctx)
|
|
|
|
{
|
|
|
|
struct full_conn *conn = ctx;
|
|
|
|
ABORT_ERROR("connection error reported by HEADERS stream");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2019-09-11 15:27:58 +00:00
|
|
|
headers_stream_on_stream_error (void *ctx, lsquic_stream_id_t stream_id)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
struct full_conn *conn = ctx;
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
lsquic_stream_t *stream;
|
|
|
|
|
|
|
|
stream = find_stream_on_non_stream_frame(conn, stream_id, SCF_CALL_ON_NEW,
|
|
|
|
"error");
|
2017-09-22 21:00:03 +00:00
|
|
|
if (stream)
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("resetting stream %"PRIu64" due to error", stream_id);
|
2017-09-22 21:00:03 +00:00
|
|
|
/* We use code 1, which is QUIC_INTERNAL_ERROR (see
|
|
|
|
* [draft-hamilton-quic-transport-protocol-01], Section 10), for all
|
|
|
|
* errors. There does not seem to be a good reason to figure out
|
|
|
|
* and send more specific error codes.
|
|
|
|
*/
|
2021-03-03 14:41:42 +00:00
|
|
|
lsquic_stream_maybe_reset(stream, 1, 0);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
headers_stream_on_enable_push (void *ctx, int enable_push)
|
|
|
|
{
|
|
|
|
struct full_conn *conn = ctx;
|
|
|
|
if (0 == enable_push)
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("server push %d -> 0", !!(conn->fc_flags & FC_SUPPORT_PUSH));
|
|
|
|
conn->fc_flags &= ~FC_SUPPORT_PUSH;
|
|
|
|
}
|
|
|
|
else if (conn->fc_settings->es_support_push)
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("server push %d -> 1", !!(conn->fc_flags & FC_SUPPORT_PUSH));
|
|
|
|
conn->fc_flags |= FC_SUPPORT_PUSH;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
LSQ_INFO("not enabling server push that's disabled in engine settings");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
headers_stream_on_incoming_headers (void *ctx, struct uncompressed_headers *uh)
|
|
|
|
{
|
|
|
|
struct full_conn *conn = ctx;
|
|
|
|
lsquic_stream_t *stream;
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("incoming headers for stream %"PRIu64, uh->uh_stream_id);
|
2017-09-22 21:00:03 +00:00
|
|
|
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
stream = find_stream_on_non_stream_frame(conn, uh->uh_stream_id, 0,
|
|
|
|
"headers");
|
2017-09-22 21:00:03 +00:00
|
|
|
if (!stream)
|
2018-09-27 18:00:46 +00:00
|
|
|
goto free_uh;
|
|
|
|
|
|
|
|
if (lsquic_stream_is_reset(stream))
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2018-09-27 18:00:46 +00:00
|
|
|
LSQ_DEBUG("stream is reset: ignore headers");
|
|
|
|
goto free_uh;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (0 != lsquic_stream_uh_in(stream, uh))
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
ABORT_ERROR("stream %"PRIu64" refused incoming headers",
|
|
|
|
uh->uh_stream_id);
|
2018-09-27 18:00:46 +00:00
|
|
|
goto free_uh;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
|
|
|
|
if (!(stream->stream_flags & STREAM_ONNEW_DONE))
|
|
|
|
lsquic_stream_call_on_new(stream);
|
2018-09-27 18:00:46 +00:00
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
free_uh:
|
|
|
|
if (uh->uh_hset)
|
|
|
|
conn->fc_enpub->enp_hsi_if->hsi_discard_header_set(uh->uh_hset);
|
|
|
|
free(uh);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
headers_stream_on_push_promise (void *ctx, struct uncompressed_headers *uh)
|
|
|
|
{
|
|
|
|
struct full_conn *conn = ctx;
|
|
|
|
lsquic_stream_t *stream;
|
|
|
|
|
|
|
|
assert(!(conn->fc_flags & FC_SERVER));
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("push promise for stream %"PRIu64" in response to %"PRIu64,
|
2017-09-22 21:00:03 +00:00
|
|
|
uh->uh_oth_stream_id, uh->uh_stream_id);
|
|
|
|
|
|
|
|
if (0 == (uh->uh_stream_id & 1) ||
|
|
|
|
0 != (uh->uh_oth_stream_id & 1))
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
ABORT_ERROR("invalid push promise stream IDs: %"PRIu64", %"PRIu64,
|
2017-09-22 21:00:03 +00:00
|
|
|
uh->uh_oth_stream_id, uh->uh_stream_id);
|
2018-09-27 18:00:46 +00:00
|
|
|
goto free_uh;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!(conn_is_stream_closed(conn, uh->uh_stream_id) ||
|
|
|
|
find_stream_by_id(conn, uh->uh_stream_id)))
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
ABORT_ERROR("invalid push promise original stream ID %"PRIu64" never "
|
2017-09-22 21:00:03 +00:00
|
|
|
"initiated", uh->uh_stream_id);
|
2018-09-27 18:00:46 +00:00
|
|
|
goto free_uh;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (conn_is_stream_closed(conn, uh->uh_oth_stream_id) ||
|
|
|
|
find_stream_by_id(conn, uh->uh_oth_stream_id))
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
ABORT_ERROR("invalid promised stream ID %"PRIu64" already used",
|
2017-09-22 21:00:03 +00:00
|
|
|
uh->uh_oth_stream_id);
|
2018-09-27 18:00:46 +00:00
|
|
|
goto free_uh;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
stream = new_stream_ext(conn, uh->uh_oth_stream_id, STREAM_IF_STD,
|
2020-12-04 16:29:14 +00:00
|
|
|
(conn->fc_enpub->enp_settings.es_delay_onclose?SCF_DELAY_ONCLOSE:0)|
|
2017-09-22 21:00:03 +00:00
|
|
|
SCF_DI_AUTOSWITCH|(conn->fc_enpub->enp_settings.es_rw_once ?
|
|
|
|
SCF_DISP_RW_ONCE : 0));
|
|
|
|
if (!stream)
|
|
|
|
{
|
|
|
|
ABORT_ERROR("cannot create stream: %s", strerror(errno));
|
2018-09-27 18:00:46 +00:00
|
|
|
goto free_uh;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
lsquic_stream_push_req(stream, uh);
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
lsquic_stream_call_on_new(stream);
|
2017-09-22 21:00:03 +00:00
|
|
|
return;
|
2018-09-27 18:00:46 +00:00
|
|
|
|
|
|
|
free_uh:
|
|
|
|
if (uh->uh_hset)
|
|
|
|
conn->fc_enpub->enp_hsi_if->hsi_discard_header_set(uh->uh_hset);
|
|
|
|
free(uh);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2019-09-11 15:27:58 +00:00
|
|
|
headers_stream_on_priority (void *ctx, lsquic_stream_id_t stream_id,
|
|
|
|
int exclusive, lsquic_stream_id_t dep_stream_id, unsigned weight)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
struct full_conn *conn = ctx;
|
|
|
|
lsquic_stream_t *stream;
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("got priority frame for stream %"PRIu64": (ex: %d; dep stream: "
|
|
|
|
"%"PRIu64"; weight: %u)", stream_id, exclusive, dep_stream_id, weight);
|
Latest changes
- [API Change] Sendfile-like functionality is gone. The stream no
longer opens files and deals with file descriptors. (Among other
things, this makes the code more portable.) Three writing functions
are provided:
lsquic_stream_write
lsquic_stream_writev
lsquic_stream_writef (NEW)
lsquic_stream_writef() is given an abstract reader that has function
pointers for size() and read() functions which the user can implement.
This is the most flexible way. lsquic_stream_write() and
lsquic_stream_writev() are now both implemented as wrappers around
lsquic_stream_writef().
- [OPTIMIZATION] When writing to stream, be it within or without the
on_write() callback, place data directly into packet buffer,
bypassing auxiliary data structures. This reduces amount of memory
required, for the amount of data that can be written is limited
by the congestion window.
To support writes outside the on_write() callback, we keep N
outgoing packet buffers per connection which can be written to
by any stream. One half of these are reserved for the highest
priority stream(s), the other half for all other streams. This way,
low-priority streams cannot write instead of high-priority streams
and, on the other hand, low-priority streams get a chance to send
their packets out.
The algorithm is as follows:
- When user writes to stream outside of the callback:
- If this is the highest priority stream, place it onto the
reserved N/2 queue or fail.
(The actual size of this queue is dynamic -- MAX(N/2, CWND) --
rather than N/2, allowing high-priority streams to write as
much as can be sent.)
- If the stream is not the highest priority, try to place the
data onto the reserved N/2 queue or fail.
- When tick occurs *and* more packets can be scheduled:
- Transfer packets from the high N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for highest-priority streams,
placing resulting packets directly onto the scheduled queue.
- If more scheduling is allowed:
- Transfer packets from the low N/2 queue to the scheduled
queue.
- If more scheduling is allowed:
- Call on_write callbacks for non-highest-priority streams,
placing resulting packets directly onto the scheduled queue
The number N is currently 20, but it could be varied based on
resource usage.
- If stream is created due to incoming headers, make headers readable
from on_new.
- Outgoing packets are no longer marked non-writeable to prevent placing
more than one STREAM frame from the same stream into a single packet.
This property is maintained via code flow and an explicit check.
Packets for stream data are allocated using a special function.
- STREAM frame elision is cheaper, as we only perform it if a reset
stream has outgoing packets referencing it.
- lsquic_packet_out_t is smaller, as stream_rec elements are now
inside a union.
2017-10-31 13:35:58 +00:00
|
|
|
stream = find_stream_on_non_stream_frame(conn, stream_id, SCF_CALL_ON_NEW,
|
|
|
|
"priority");
|
2017-09-22 21:00:03 +00:00
|
|
|
if (stream)
|
|
|
|
lsquic_stream_set_priority_internal(stream, weight);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
#define STRLEN(s) (sizeof(s) - 1)
|
|
|
|
|
|
|
|
static struct uncompressed_headers *
|
|
|
|
synthesize_push_request (struct full_conn *conn, void *hset,
|
|
|
|
lsquic_stream_id_t pushed_stream_id, const lsquic_stream_t *dep_stream)
|
|
|
|
{
|
|
|
|
struct uncompressed_headers *uh;
|
|
|
|
|
2020-03-30 17:34:43 +00:00
|
|
|
assert(hset);
|
2019-09-11 15:27:58 +00:00
|
|
|
|
|
|
|
uh = malloc(sizeof(*uh));
|
|
|
|
if (!uh)
|
2020-03-30 17:34:43 +00:00
|
|
|
return NULL;
|
2019-09-11 15:27:58 +00:00
|
|
|
|
|
|
|
uh->uh_stream_id = pushed_stream_id;
|
|
|
|
uh->uh_oth_stream_id = 0; /* We don't do dependencies */
|
|
|
|
uh->uh_weight = lsquic_stream_priority(dep_stream) / 2 + 1;
|
|
|
|
uh->uh_exclusive = 0;
|
|
|
|
uh->uh_flags = UH_FIN;
|
|
|
|
if (lsquic_http1x_if == conn->fc_enpub->enp_hsi_if)
|
|
|
|
uh->uh_flags |= UH_H1H;
|
|
|
|
uh->uh_hset = hset;
|
2021-04-16 18:54:30 +00:00
|
|
|
uh->uh_next = NULL;
|
2019-09-11 15:27:58 +00:00
|
|
|
|
|
|
|
return uh;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
full_conn_ci_is_push_enabled (struct lsquic_conn *lconn)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
struct full_conn *const conn = (struct full_conn *) lconn;
|
|
|
|
return conn->fc_flags & FC_SUPPORT_PUSH;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static int
|
|
|
|
full_conn_ci_push_stream (struct lsquic_conn *lconn, void *hset,
|
2020-03-30 17:34:43 +00:00
|
|
|
struct lsquic_stream *dep_stream, const struct lsquic_http_headers *headers)
|
2019-09-11 15:27:58 +00:00
|
|
|
{
|
|
|
|
struct full_conn *const conn = (struct full_conn *) lconn;
|
|
|
|
lsquic_stream_t *pushed_stream;
|
|
|
|
struct uncompressed_headers *uh; /* We synthesize the request */
|
|
|
|
lsquic_stream_id_t stream_id;
|
|
|
|
int hit_limit;
|
|
|
|
|
|
|
|
if ((conn->fc_flags & (FC_SERVER|FC_HTTP)) != (FC_SERVER|FC_HTTP))
|
|
|
|
{
|
|
|
|
LSQ_ERROR("must be server in HTTP mode to push streams");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lsquic_stream_is_pushed(dep_stream))
|
|
|
|
{
|
|
|
|
LSQ_WARN("cannot push stream dependent on another pushed stream "
|
|
|
|
"(%"PRIu64")", dep_stream->id);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(conn->fc_flags & FC_SUPPORT_PUSH))
|
|
|
|
{
|
|
|
|
LSQ_INFO("server push support is disabled");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2020-03-30 17:34:43 +00:00
|
|
|
if (!hset)
|
|
|
|
{
|
|
|
|
LSQ_ERROR("header set must be specified when pushing");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
hit_limit = 0;
|
|
|
|
if (either_side_going_away(conn) ||
|
|
|
|
(hit_limit = 1, count_streams(conn, 0) >= conn->fc_cfg.max_streams_out))
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("cannot create pushed stream: %s", hit_limit ?
|
|
|
|
"hit connection limit" : "connection is going away");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
stream_id = generate_stream_id(conn);
|
2020-03-30 17:34:43 +00:00
|
|
|
uh = synthesize_push_request(conn, hset, stream_id, dep_stream);
|
2019-09-11 15:27:58 +00:00
|
|
|
if (!uh)
|
|
|
|
{
|
|
|
|
ABORT_ERROR("memory allocation failure");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-06-18 13:45:44 +00:00
|
|
|
pushed_stream = new_stream(conn, stream_id, 0);
|
2019-09-11 15:27:58 +00:00
|
|
|
if (!pushed_stream)
|
|
|
|
{
|
|
|
|
LSQ_WARN("cannot create stream: %s", strerror(errno));
|
|
|
|
free(uh);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (0 != lsquic_stream_uh_in(pushed_stream, uh))
|
|
|
|
{
|
|
|
|
LSQ_WARN("stream barfed when fed synthetic request");
|
|
|
|
free(uh);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (0 != lsquic_headers_stream_push_promise(conn->fc_pub.u.gquic.hs, dep_stream->id,
|
2020-03-30 17:34:43 +00:00
|
|
|
pushed_stream->id, headers))
|
2019-09-11 15:27:58 +00:00
|
|
|
{
|
2020-10-13 12:20:25 +00:00
|
|
|
/* If forget we ever had the hset pointer: */
|
|
|
|
lsquic_stream_drop_hset_ref(pushed_stream);
|
|
|
|
/* Now roll back stream creation and return stream ID: */
|
|
|
|
if (pushed_stream->sm_hash_el.qhe_flags & QHE_HASHED)
|
|
|
|
lsquic_hash_erase(conn->fc_pub.all_streams,
|
|
|
|
&pushed_stream->sm_hash_el);
|
|
|
|
lsquic_stream_destroy(pushed_stream);
|
|
|
|
conn->fc_last_stream_id -= 2;
|
|
|
|
LSQ_INFO("could not send push promise");
|
2019-09-11 15:27:58 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-06-18 13:45:44 +00:00
|
|
|
lsquic_stream_call_on_new(pushed_stream);
|
2019-09-11 15:27:58 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
full_conn_ci_tls_alert (struct lsquic_conn *lconn, uint8_t alert)
|
|
|
|
{
|
|
|
|
assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static enum LSQUIC_CONN_STATUS
|
|
|
|
full_conn_ci_status (struct lsquic_conn *lconn, char *errbuf, size_t bufsz)
|
2018-02-26 21:01:16 +00:00
|
|
|
{
|
|
|
|
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)))
|
|
|
|
{
|
2018-09-27 18:00:46 +00:00
|
|
|
if (lconn->cn_flags & LSCONN_PEER_GOING_AWAY)
|
|
|
|
return LSCONN_ST_PEER_GOING_AWAY;
|
|
|
|
else if (lconn->cn_flags & LSCONN_HANDSHAKE_DONE)
|
2018-02-26 21:01:16 +00:00
|
|
|
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)
|
2021-04-16 18:54:30 +00:00
|
|
|
{
|
|
|
|
if (conn->fc_flags & FC_HSK_FAILED)
|
|
|
|
return LSCONN_ST_VERNEG_FAILURE;
|
|
|
|
else
|
|
|
|
return LSCONN_ST_ERROR;
|
|
|
|
}
|
2018-02-26 21:01:16 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-04-09 13:39:38 +00:00
|
|
|
static int
|
|
|
|
full_conn_ci_is_tickable (lsquic_conn_t *lconn)
|
|
|
|
{
|
|
|
|
struct full_conn *conn = (struct full_conn *) lconn;
|
2019-09-11 15:27:58 +00:00
|
|
|
struct lsquic_stream *stream;
|
2018-04-09 13:39:38 +00:00
|
|
|
|
|
|
|
if (!TAILQ_EMPTY(&conn->fc_pub.service_streams))
|
2019-10-11 12:24:24 +00:00
|
|
|
{
|
|
|
|
LSQ_DEBUG("tickable: there are streams to be serviced");
|
2018-04-09 13:39:38 +00:00
|
|
|
return 1;
|
2019-10-11 12:24:24 +00:00
|
|
|
}
|
2018-04-09 13:39:38 +00:00
|
|
|
|
2018-05-21 19:02:33 +00:00
|
|
|
if ((conn->fc_enpub->enp_flags & ENPUB_CAN_SEND)
|
2018-04-25 14:58:55 +00:00
|
|
|
&& (should_generate_ack(conn) ||
|
|
|
|
!lsquic_send_ctl_sched_is_blocked(&conn->fc_send_ctl)))
|
2018-04-09 13:39:38 +00:00
|
|
|
{
|
2019-10-11 12:24:24 +00:00
|
|
|
const enum full_conn_flags send_flags = FC_SEND_GOAWAY
|
2021-01-08 16:38:46 +00:00
|
|
|
|FC_SEND_STOP_WAITING|FC_SEND_PING|FC_SEND_WUF;
|
2019-10-11 12:24:24 +00:00
|
|
|
if (conn->fc_flags & send_flags)
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("tickable: flags: 0x%X", conn->fc_flags & send_flags);
|
2019-09-11 15:27:58 +00:00
|
|
|
goto check_can_send;
|
2019-10-11 12:24:24 +00:00
|
|
|
}
|
2020-06-18 13:45:44 +00:00
|
|
|
if (lsquic_send_ctl_has_sendable(&conn->fc_send_ctl))
|
2020-04-07 15:25:43 +00:00
|
|
|
{
|
2020-06-18 13:45:44 +00:00
|
|
|
LSQ_DEBUG("tickable: has sendable packets");
|
|
|
|
return 1; /* Don't check can_send: already on scheduled queue */
|
2020-04-07 15:25:43 +00:00
|
|
|
}
|
2019-10-15 21:02:21 +00:00
|
|
|
if ((conn->fc_conn.cn_flags & LSCONN_HANDSHAKE_DONE)
|
|
|
|
&& lsquic_send_ctl_has_buffered(&conn->fc_send_ctl))
|
2019-10-11 12:24:24 +00:00
|
|
|
{
|
|
|
|
LSQ_DEBUG("tickable: has buffered packets");
|
2019-09-11 15:27:58 +00:00
|
|
|
goto check_can_send;
|
2019-10-11 12:24:24 +00:00
|
|
|
}
|
2018-04-23 20:12:38 +00:00
|
|
|
if (!TAILQ_EMPTY(&conn->fc_pub.sending_streams))
|
2019-10-11 12:24:24 +00:00
|
|
|
{
|
|
|
|
LSQ_DEBUG("tickable: there are sending streams");
|
2019-09-11 15:27:58 +00:00
|
|
|
goto check_can_send;
|
2019-10-11 12:24:24 +00:00
|
|
|
}
|
2020-07-06 21:35:21 +00:00
|
|
|
if (handshake_done_or_doing_sess_resume(conn))
|
2019-01-28 20:41:28 +00:00
|
|
|
{
|
|
|
|
TAILQ_FOREACH(stream, &conn->fc_pub.write_streams,
|
|
|
|
next_write_stream)
|
|
|
|
if (lsquic_stream_write_avail(stream))
|
2019-10-11 12:24:24 +00:00
|
|
|
{
|
|
|
|
LSQ_DEBUG("tickable: stream %"PRIu64" can be written to",
|
|
|
|
stream->id);
|
2019-09-11 15:27:58 +00:00
|
|
|
goto check_can_send;
|
2019-10-11 12:24:24 +00:00
|
|
|
}
|
2019-01-28 20:41:28 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TAILQ_FOREACH(stream, &conn->fc_pub.write_streams,
|
|
|
|
next_write_stream)
|
2019-12-23 21:14:20 +00:00
|
|
|
if (lsquic_stream_is_crypto(stream)
|
2019-01-28 20:41:28 +00:00
|
|
|
&& lsquic_stream_write_avail(stream))
|
2019-10-11 12:24:24 +00:00
|
|
|
{
|
|
|
|
LSQ_DEBUG("tickable: stream %"PRIu64" can be written to",
|
|
|
|
stream->id);
|
2019-09-11 15:27:58 +00:00
|
|
|
goto check_can_send;
|
2019-10-11 12:24:24 +00:00
|
|
|
}
|
2019-01-28 20:41:28 +00:00
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
goto check_readable_streams;
|
|
|
|
check_can_send:
|
|
|
|
if (lsquic_send_ctl_can_send(&conn->fc_send_ctl))
|
|
|
|
return 1;
|
2018-04-09 13:39:38 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
check_readable_streams:
|
2018-04-23 20:12:38 +00:00
|
|
|
TAILQ_FOREACH(stream, &conn->fc_pub.read_streams, next_read_stream)
|
|
|
|
if (lsquic_stream_readable(stream))
|
2019-10-11 12:24:24 +00:00
|
|
|
{
|
|
|
|
LSQ_DEBUG("tickable: stream %"PRIu64" can be read from",
|
|
|
|
stream->id);
|
2018-04-23 20:12:38 +00:00
|
|
|
return 1;
|
2019-10-11 12:24:24 +00:00
|
|
|
}
|
2018-04-09 13:39:38 +00:00
|
|
|
|
2021-01-06 14:00:05 +00:00
|
|
|
if (conn->fc_flags & FC_IMMEDIATE_CLOSE_FLAGS)
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("tickable: immediate close flags: 0x%X",
|
|
|
|
(unsigned) (conn->fc_flags & FC_IMMEDIATE_CLOSE_FLAGS));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2019-10-11 12:24:24 +00:00
|
|
|
LSQ_DEBUG("not tickable");
|
2018-04-09 13:39:38 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static lsquic_time_t
|
2019-10-15 21:02:21 +00:00
|
|
|
full_conn_ci_next_tick_time (lsquic_conn_t *lconn, unsigned *why)
|
2018-04-09 13:39:38 +00:00
|
|
|
{
|
|
|
|
struct full_conn *conn = (struct full_conn *) lconn;
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_time_t alarm_time, pacer_time, now;
|
2019-10-15 21:02:21 +00:00
|
|
|
enum alarm_id al_id;
|
2018-04-09 13:39:38 +00:00
|
|
|
|
2019-10-15 21:02:21 +00:00
|
|
|
alarm_time = lsquic_alarmset_mintime(&conn->fc_alset, &al_id);
|
2018-04-09 13:39:38 +00:00
|
|
|
pacer_time = lsquic_send_ctl_next_pacer_time(&conn->fc_send_ctl);
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
if (pacer_time && LSQ_LOG_ENABLED(LSQ_LOG_DEBUG))
|
|
|
|
{
|
|
|
|
now = lsquic_time_now();
|
|
|
|
if (pacer_time < now)
|
|
|
|
LSQ_DEBUG("%s: pacer is %"PRIu64" usec in the past", __func__,
|
|
|
|
now - pacer_time);
|
|
|
|
}
|
|
|
|
|
2018-04-09 13:39:38 +00:00
|
|
|
if (alarm_time && pacer_time)
|
|
|
|
{
|
|
|
|
if (alarm_time < pacer_time)
|
2019-10-15 21:02:21 +00:00
|
|
|
{
|
|
|
|
*why = N_AEWS + al_id;
|
2018-04-09 13:39:38 +00:00
|
|
|
return alarm_time;
|
2019-10-15 21:02:21 +00:00
|
|
|
}
|
2018-04-09 13:39:38 +00:00
|
|
|
else
|
2019-10-15 21:02:21 +00:00
|
|
|
{
|
|
|
|
*why = AEW_PACER;
|
2018-04-09 13:39:38 +00:00
|
|
|
return pacer_time;
|
2019-10-15 21:02:21 +00:00
|
|
|
}
|
2018-04-09 13:39:38 +00:00
|
|
|
}
|
|
|
|
else if (alarm_time)
|
2019-10-15 21:02:21 +00:00
|
|
|
{
|
|
|
|
*why = N_AEWS + al_id;
|
2018-04-09 13:39:38 +00:00
|
|
|
return alarm_time;
|
2019-10-15 21:02:21 +00:00
|
|
|
}
|
|
|
|
else if (pacer_time)
|
|
|
|
{
|
|
|
|
*why = AEW_PACER;
|
2018-04-09 13:39:38 +00:00
|
|
|
return pacer_time;
|
2019-10-15 21:02:21 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
return 0;
|
2018-04-09 13:39:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
int
|
|
|
|
lsquic_gquic_full_conn_srej (struct lsquic_conn *lconn)
|
|
|
|
{
|
|
|
|
struct full_conn *const conn = (struct full_conn *) lconn;
|
|
|
|
const unsigned cce_idx = lconn->cn_cur_cce_idx;
|
|
|
|
struct conn_cid_elem *const cce = &lconn->cn_cces[ cce_idx ];
|
|
|
|
struct lsquic_stream *stream;
|
|
|
|
enum lsquic_version version;
|
|
|
|
|
2020-07-06 21:35:21 +00:00
|
|
|
if (lconn->cn_esf_c->esf_is_sess_resume_enabled(conn->fc_conn.cn_enc_session))
|
2019-09-11 15:27:58 +00:00
|
|
|
{
|
|
|
|
/* We need to do this because we do not clean up any data that may
|
|
|
|
* have been already sent. This is left an optimization for the
|
|
|
|
* future.
|
|
|
|
*/
|
|
|
|
LSQ_DEBUG("received SREJ when 0RTT was on: fail handshake and let "
|
|
|
|
"caller retry");
|
2020-07-06 21:35:21 +00:00
|
|
|
full_conn_ci_hsk_done(lconn, LSQ_HSK_RESUMED_FAIL);
|
2019-09-11 15:27:58 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
LSQ_DEBUG("reinitialize CID and other state due to SREJ");
|
|
|
|
|
|
|
|
/* Generate new CID and update connections hash */
|
|
|
|
if (cce->cce_hash_el.qhe_flags & QHE_HASHED)
|
|
|
|
{
|
|
|
|
lsquic_engine_retire_cid(conn->fc_enpub, lconn, cce_idx,
|
2020-11-18 14:05:15 +00:00
|
|
|
0 /* OK to omit the `now' value */, 0);
|
2019-09-11 15:27:58 +00:00
|
|
|
lconn->cn_cces_mask |= 1 << cce_idx;
|
|
|
|
lsquic_generate_cid_gquic(&cce->cce_cid);
|
|
|
|
if (0 != lsquic_engine_add_cid(conn->fc_enpub, lconn, cce_idx))
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("not hashed by CID, no need to reinsert");
|
|
|
|
lsquic_generate_cid_gquic(&cce->cce_cid);
|
|
|
|
}
|
|
|
|
lconn->cn_esf.g->esf_reset_cid(lconn->cn_enc_session, &cce->cce_cid);
|
|
|
|
|
|
|
|
/* Reset version negotiation */
|
2019-10-31 16:21:14 +00:00
|
|
|
version = highest_bit_set(conn->fc_orig_versions);
|
|
|
|
init_ver_neg(conn, conn->fc_orig_versions, &version);
|
2019-09-11 15:27:58 +00:00
|
|
|
|
|
|
|
/* Reset receive history */
|
|
|
|
lsquic_rechist_cleanup(&conn->fc_rechist);
|
2020-11-24 13:51:36 +00:00
|
|
|
lsquic_rechist_init(&conn->fc_rechist, 0, MAX_ACK_RANGES);
|
2019-09-11 15:27:58 +00:00
|
|
|
|
|
|
|
/* Reset send controller state */
|
|
|
|
lsquic_send_ctl_cleanup(&conn->fc_send_ctl);
|
|
|
|
lsquic_send_ctl_init(&conn->fc_send_ctl, &conn->fc_alset, conn->fc_enpub,
|
|
|
|
&conn->fc_ver_neg, &conn->fc_pub, 0);
|
|
|
|
|
|
|
|
/* Reset handshake stream state */
|
2019-12-23 21:14:20 +00:00
|
|
|
stream = find_stream_by_id(conn, hsk_stream_id(conn));
|
2019-09-11 15:27:58 +00:00
|
|
|
if (!stream)
|
|
|
|
return -1;
|
|
|
|
stream->n_unacked = 0;
|
|
|
|
stream->tosend_off = 0;
|
|
|
|
stream->read_offset = 0;
|
|
|
|
stream->fc.sf_read_off = 0;
|
|
|
|
stream->fc.sf_max_recv_off = 0;
|
|
|
|
|
|
|
|
lsquic_alarmset_unset(&conn->fc_alset, AL_RETX_APP);
|
|
|
|
lsquic_alarmset_unset(&conn->fc_alset, AL_ACK_APP);
|
|
|
|
conn->fc_flags &= ~(FC_ACK_QUEUED|FC_ACK_HAD_MISS|FC_NSTP);
|
|
|
|
conn->fc_flags |= FC_GOT_SREJ;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-01-16 20:13:59 +00:00
|
|
|
#if LSQUIC_CONN_STATS
|
|
|
|
static const struct conn_stats *
|
|
|
|
full_conn_ci_get_stats (struct lsquic_conn *lconn)
|
|
|
|
{
|
|
|
|
struct full_conn *conn = (struct full_conn *) lconn;
|
|
|
|
return &conn->fc_stats;
|
|
|
|
}
|
2020-10-21 13:05:26 +00:00
|
|
|
|
2021-02-03 16:05:50 +00:00
|
|
|
|
2020-10-21 13:05:26 +00:00
|
|
|
#include "lsquic_cong_ctl.h"
|
|
|
|
|
|
|
|
static void
|
|
|
|
full_conn_ci_log_stats (struct lsquic_conn *lconn)
|
|
|
|
{
|
|
|
|
struct full_conn *conn = (struct full_conn *) lconn;
|
|
|
|
struct batch_size_stats *const bs = &conn->fc_enpub->enp_batch_size_stats;
|
|
|
|
struct conn_stats diff_stats;
|
|
|
|
uint64_t cwnd;
|
|
|
|
char cidstr[MAX_CID_LEN * 2 + 1];
|
|
|
|
|
|
|
|
if (!conn->fc_last_stats)
|
|
|
|
{
|
|
|
|
conn->fc_last_stats = calloc(1, sizeof(*conn->fc_last_stats));
|
|
|
|
if (!conn->fc_last_stats)
|
|
|
|
return;
|
|
|
|
LSQ_DEBUG("allocated last stats");
|
|
|
|
}
|
|
|
|
|
|
|
|
cwnd = conn->fc_send_ctl.sc_ci->cci_get_cwnd(
|
|
|
|
conn->fc_send_ctl.sc_cong_ctl);
|
|
|
|
lsquic_conn_stats_diff(&conn->fc_stats, conn->fc_last_stats, &diff_stats);
|
|
|
|
lsquic_logger_log1(LSQ_LOG_NOTICE, LSQLM_CONN_STATS,
|
|
|
|
"%s: ticks: %lu; cwnd: %"PRIu64"; conn flow: max: %"PRIu64
|
|
|
|
", avail: %"PRIu64"; packets: sent: %lu, lost: %lu, retx: %lu, rcvd: %lu"
|
|
|
|
"; batch: count: %u; min: %u; max: %u; avg: %.2f",
|
|
|
|
(lsquic_cid2str(LSQUIC_LOG_CONN_ID, cidstr), cidstr),
|
|
|
|
diff_stats.n_ticks, cwnd,
|
|
|
|
conn->fc_pub.conn_cap.cc_max,
|
|
|
|
lsquic_conn_cap_avail(&conn->fc_pub.conn_cap),
|
|
|
|
diff_stats.out.packets, diff_stats.out.lost_packets,
|
|
|
|
diff_stats.out.retx_packets, diff_stats.in.packets,
|
|
|
|
bs->count, bs->min, bs->max, bs->avg);
|
|
|
|
|
|
|
|
*conn->fc_last_stats = conn->fc_stats;
|
|
|
|
memset(bs, 0, sizeof(*bs));
|
|
|
|
}
|
2021-02-03 16:05:50 +00:00
|
|
|
|
|
|
|
|
2019-01-16 20:13:59 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
static const struct headers_stream_callbacks headers_callbacks =
|
|
|
|
{
|
|
|
|
.hsc_on_headers = headers_stream_on_incoming_headers,
|
|
|
|
.hsc_on_push_promise = headers_stream_on_push_promise,
|
|
|
|
.hsc_on_priority = headers_stream_on_priority,
|
|
|
|
.hsc_on_stream_error = headers_stream_on_stream_error,
|
|
|
|
.hsc_on_conn_error = headers_stream_on_conn_error,
|
|
|
|
.hsc_on_enable_push = headers_stream_on_enable_push,
|
|
|
|
};
|
|
|
|
|
2018-03-30 14:57:17 +00:00
|
|
|
static const struct headers_stream_callbacks *headers_callbacks_ptr = &headers_callbacks;
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
static const struct conn_iface full_conn_iface = {
|
2019-09-11 15:27:58 +00:00
|
|
|
.ci_abort = full_conn_ci_abort,
|
|
|
|
.ci_abort_error = full_conn_ci_abort_error,
|
2020-09-08 15:43:03 +00:00
|
|
|
.ci_ack_rollback = full_conn_ci_ack_rollback,
|
|
|
|
.ci_ack_snapshot = full_conn_ci_ack_snapshot,
|
2019-01-21 14:07:02 +00:00
|
|
|
.ci_can_write_ack = full_conn_ci_can_write_ack,
|
2019-09-11 15:27:58 +00:00
|
|
|
.ci_cancel_pending_streams
|
|
|
|
= full_conn_ci_cancel_pending_streams,
|
|
|
|
.ci_client_call_on_new = full_conn_ci_client_call_on_new,
|
|
|
|
.ci_close = full_conn_ci_close,
|
2017-09-22 21:00:03 +00:00
|
|
|
.ci_destroy = full_conn_ci_destroy,
|
2020-04-15 13:30:40 +00:00
|
|
|
.ci_get_stream_by_id = full_conn_ci_get_stream_by_id,
|
2019-09-11 15:27:58 +00:00
|
|
|
.ci_get_engine = full_conn_ci_get_engine,
|
|
|
|
.ci_get_path = full_conn_ci_get_path,
|
2019-01-16 20:13:59 +00:00
|
|
|
#if LSQUIC_CONN_STATS
|
|
|
|
.ci_get_stats = full_conn_ci_get_stats,
|
2020-10-21 13:05:26 +00:00
|
|
|
.ci_log_stats = full_conn_ci_log_stats,
|
2019-01-16 20:13:59 +00:00
|
|
|
#endif
|
2019-09-11 15:27:58 +00:00
|
|
|
.ci_going_away = full_conn_ci_going_away,
|
2019-02-04 13:59:11 +00:00
|
|
|
.ci_hsk_done = full_conn_ci_hsk_done,
|
2019-09-11 15:27:58 +00:00
|
|
|
.ci_internal_error = full_conn_ci_internal_error,
|
|
|
|
.ci_is_push_enabled = full_conn_ci_is_push_enabled,
|
2018-04-09 13:39:38 +00:00
|
|
|
.ci_is_tickable = full_conn_ci_is_tickable,
|
2019-09-11 15:27:58 +00:00
|
|
|
.ci_make_stream = full_conn_ci_make_stream,
|
|
|
|
.ci_n_avail_streams = full_conn_ci_n_avail_streams,
|
|
|
|
.ci_n_pending_streams = full_conn_ci_n_pending_streams,
|
2017-09-22 21:00:03 +00:00
|
|
|
.ci_next_packet_to_send = full_conn_ci_next_packet_to_send,
|
2018-04-09 13:39:38 +00:00
|
|
|
.ci_next_tick_time = full_conn_ci_next_tick_time,
|
2017-09-22 21:00:03 +00:00
|
|
|
.ci_packet_in = full_conn_ci_packet_in,
|
|
|
|
.ci_packet_not_sent = full_conn_ci_packet_not_sent,
|
|
|
|
.ci_packet_sent = full_conn_ci_packet_sent,
|
2019-09-11 15:27:58 +00:00
|
|
|
.ci_record_addrs = full_conn_ci_record_addrs,
|
|
|
|
/* gQUIC connection does not need this functionality because it only
|
|
|
|
* uses one CID and it's liveness is updated automatically by the
|
|
|
|
* caller when packets come in.
|
|
|
|
*/
|
|
|
|
.ci_report_live = NULL,
|
|
|
|
.ci_status = full_conn_ci_status,
|
2017-09-22 21:00:03 +00:00
|
|
|
.ci_tick = full_conn_ci_tick,
|
2019-01-21 14:07:02 +00:00
|
|
|
.ci_write_ack = full_conn_ci_write_ack,
|
2019-09-11 15:27:58 +00:00
|
|
|
.ci_push_stream = full_conn_ci_push_stream,
|
|
|
|
.ci_tls_alert = full_conn_ci_tls_alert,
|
2017-09-22 21:00:03 +00:00
|
|
|
};
|
2018-03-12 22:25:01 +00:00
|
|
|
|
2018-03-30 14:57:17 +00:00
|
|
|
static const struct conn_iface *full_conn_iface_ptr = &full_conn_iface;
|