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_stream.c -- stream processing
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <inttypes.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/queue.h>
|
|
|
|
#include <stddef.h>
|
|
|
|
|
2020-06-03 04:13:30 +00:00
|
|
|
#ifdef WIN32
|
|
|
|
#include <malloc.h>
|
|
|
|
#endif
|
|
|
|
|
2020-01-20 14:41:37 +00:00
|
|
|
#include "fiu-local.h"
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
#include "lsquic.h"
|
|
|
|
|
|
|
|
#include "lsquic_int_types.h"
|
|
|
|
#include "lsquic_packet_common.h"
|
|
|
|
#include "lsquic_packet_in.h"
|
|
|
|
#include "lsquic_malo.h"
|
|
|
|
#include "lsquic_conn_flow.h"
|
|
|
|
#include "lsquic_rtt.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_conn_public.h"
|
|
|
|
#include "lsquic_util.h"
|
|
|
|
#include "lsquic_mm.h"
|
|
|
|
#include "lsquic_headers_stream.h"
|
|
|
|
#include "lsquic_conn.h"
|
|
|
|
#include "lsquic_data_in_if.h"
|
|
|
|
#include "lsquic_parse.h"
|
2020-07-29 15:33:52 +00:00
|
|
|
#include "lsquic_packet_in.h"
|
2017-09-22 21:00:03 +00:00
|
|
|
#include "lsquic_packet_out.h"
|
|
|
|
#include "lsquic_engine_public.h"
|
|
|
|
#include "lsquic_senhist.h"
|
|
|
|
#include "lsquic_pacer.h"
|
|
|
|
#include "lsquic_cubic.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_send_ctl.h"
|
2018-08-28 13:59:47 +00:00
|
|
|
#include "lsquic_headers.h"
|
2017-09-22 21:00:03 +00:00
|
|
|
#include "lsquic_ev_log.h"
|
2019-09-11 15:27:58 +00:00
|
|
|
#include "lsquic_enc_sess.h"
|
|
|
|
#include "lsqpack.h"
|
|
|
|
#include "lsquic_frab_list.h"
|
|
|
|
#include "lsquic_http1x_if.h"
|
|
|
|
#include "lsquic_qdec_hdl.h"
|
|
|
|
#include "lsquic_qenc_hdl.h"
|
|
|
|
#include "lsquic_byteswap.h"
|
|
|
|
#include "lsquic_ietf.h"
|
|
|
|
#include "lsquic_push_promise.h"
|
2020-10-07 13:41:26 +00:00
|
|
|
#include "lsquic_hcso_writer.h"
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
#define LSQUIC_LOGGER_MODULE LSQLM_STREAM
|
2019-09-11 15:27:58 +00:00
|
|
|
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(stream->conn_pub->lconn)
|
2017-09-22 21:00:03 +00:00
|
|
|
#define LSQUIC_LOG_STREAM_ID stream->id
|
|
|
|
#include "lsquic_logger.h"
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
static void
|
|
|
|
drop_frames_in (lsquic_stream_t *stream);
|
|
|
|
|
|
|
|
static void
|
|
|
|
maybe_schedule_call_on_close (lsquic_stream_t *stream);
|
|
|
|
|
|
|
|
static int
|
|
|
|
stream_wantread (lsquic_stream_t *stream, int is_want);
|
|
|
|
|
|
|
|
static int
|
|
|
|
stream_wantwrite (lsquic_stream_t *stream, int is_want);
|
|
|
|
|
2020-09-08 15:43:03 +00:00
|
|
|
enum stream_write_options
|
|
|
|
{
|
|
|
|
SWO_BUFFER = 1 << 0, /* Allow buffering in sm_buf */
|
|
|
|
};
|
|
|
|
|
|
|
|
|
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 ssize_t
|
2020-09-08 15:43:03 +00:00
|
|
|
stream_write_to_packets (lsquic_stream_t *, struct lsquic_reader *, size_t,
|
|
|
|
enum stream_write_options);
|
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 ssize_t
|
|
|
|
save_to_buffer (lsquic_stream_t *, struct lsquic_reader *, size_t len);
|
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 int
|
|
|
|
stream_flush (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
|
|
|
static int
|
|
|
|
stream_flush_nocheck (lsquic_stream_t *stream);
|
2017-09-22 21:00:03 +00:00
|
|
|
|
2018-02-26 21:01:16 +00:00
|
|
|
static void
|
2019-09-11 15:27:58 +00:00
|
|
|
maybe_remove_from_write_q (lsquic_stream_t *stream, enum stream_q_flags flag);
|
|
|
|
|
|
|
|
enum swtp_status { SWTP_OK, SWTP_STOP, SWTP_ERROR };
|
|
|
|
|
|
|
|
static enum swtp_status
|
|
|
|
stream_write_to_packet_std (struct frame_gen_ctx *fg_ctx, const size_t size);
|
|
|
|
|
|
|
|
static enum swtp_status
|
|
|
|
stream_write_to_packet_hsk (struct frame_gen_ctx *fg_ctx, const size_t size);
|
|
|
|
|
|
|
|
static enum swtp_status
|
|
|
|
stream_write_to_packet_crypto (struct frame_gen_ctx *fg_ctx, const size_t size);
|
|
|
|
|
|
|
|
static size_t
|
2019-09-23 16:00:10 +00:00
|
|
|
stream_write_avail_no_frames (struct lsquic_stream *);
|
|
|
|
|
|
|
|
static size_t
|
|
|
|
stream_write_avail_with_frames (struct lsquic_stream *);
|
2019-09-11 15:27:58 +00:00
|
|
|
|
|
|
|
static size_t
|
|
|
|
stream_write_avail_with_headers (struct lsquic_stream *);
|
|
|
|
|
|
|
|
static int
|
|
|
|
hq_filter_readable (struct lsquic_stream *stream);
|
|
|
|
|
|
|
|
static void
|
|
|
|
hq_decr_left (struct lsquic_stream *stream, size_t);
|
|
|
|
|
|
|
|
static size_t
|
|
|
|
hq_filter_df (struct lsquic_stream *stream, struct data_frame *data_frame);
|
|
|
|
|
|
|
|
static int
|
|
|
|
stream_readable_non_http (struct lsquic_stream *stream);
|
|
|
|
|
|
|
|
static int
|
|
|
|
stream_readable_http_gquic (struct lsquic_stream *stream);
|
|
|
|
|
|
|
|
static int
|
|
|
|
stream_readable_http_ietf (struct lsquic_stream *stream);
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
stream_write_buf (struct lsquic_stream *stream, const void *buf, size_t sz);
|
|
|
|
|
2021-03-03 14:41:42 +00:00
|
|
|
static void
|
|
|
|
stream_reset (struct lsquic_stream *, uint64_t error_code, int do_close);
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static size_t
|
|
|
|
active_hq_frame_sizes (const struct lsquic_stream *);
|
|
|
|
|
|
|
|
static void
|
|
|
|
on_write_pp_wrapper (struct lsquic_stream *, lsquic_stream_ctx_t *);
|
|
|
|
|
|
|
|
static void
|
|
|
|
stream_hq_frame_put (struct lsquic_stream *, struct stream_hq_frame *);
|
|
|
|
|
2019-09-23 16:00:10 +00:00
|
|
|
static size_t
|
|
|
|
stream_hq_frame_size (const struct stream_hq_frame *);
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
const struct stream_filter_if hq_stream_filter_if =
|
|
|
|
{
|
|
|
|
.sfi_readable = hq_filter_readable,
|
|
|
|
.sfi_filter_df = hq_filter_df,
|
|
|
|
.sfi_decr_left = hq_decr_left,
|
|
|
|
};
|
2018-02-26 21:01:16 +00:00
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
#if LSQUIC_KEEP_STREAM_HISTORY
|
|
|
|
/* These values are printable ASCII characters for ease of printing the
|
|
|
|
* whole history in a single line of a log message.
|
|
|
|
*
|
|
|
|
* The list of events is not exhaustive: only most interesting events
|
|
|
|
* are recorded.
|
|
|
|
*/
|
|
|
|
enum stream_history_event
|
|
|
|
{
|
|
|
|
SHE_EMPTY = '\0', /* Special entry. No init besides memset required */
|
|
|
|
SHE_PLUS = '+', /* Special entry: previous event occured more than once */
|
|
|
|
SHE_REACH_FIN = 'a',
|
2020-09-08 15:43:03 +00:00
|
|
|
SHE_EARLY_READ_STOP = 'A',
|
2017-09-22 21:00:03 +00:00
|
|
|
SHE_BLOCKED_OUT = 'b',
|
|
|
|
SHE_CREATED = 'C',
|
|
|
|
SHE_FRAME_IN = 'd',
|
|
|
|
SHE_FRAME_OUT = 'D',
|
|
|
|
SHE_RESET = 'e',
|
|
|
|
SHE_WINDOW_UPDATE = 'E',
|
|
|
|
SHE_FIN_IN = 'f',
|
|
|
|
SHE_FINISHED = 'F',
|
|
|
|
SHE_GOAWAY_IN = 'g',
|
|
|
|
SHE_USER_WRITE_HEADER = 'h',
|
|
|
|
SHE_HEADERS_IN = 'H',
|
2019-09-11 15:27:58 +00:00
|
|
|
SHE_IF_SWITCH = 'i',
|
2017-09-22 21:00:03 +00:00
|
|
|
SHE_ONCLOSE_SCHED = 'l',
|
|
|
|
SHE_ONCLOSE_CALL = 'L',
|
|
|
|
SHE_ONNEW = 'N',
|
|
|
|
SHE_SET_PRIO = 'p',
|
2020-09-08 15:43:03 +00:00
|
|
|
SHE_SHORT_WRITE = 'q',
|
2017-09-22 21:00:03 +00:00
|
|
|
SHE_USER_READ = 'r',
|
|
|
|
SHE_SHUTDOWN_READ = 'R',
|
|
|
|
SHE_RST_IN = 's',
|
2020-09-08 15:43:03 +00:00
|
|
|
SHE_STOP_SENDIG_IN = 'S',
|
2017-09-22 21:00:03 +00:00
|
|
|
SHE_RST_OUT = 't',
|
2019-09-11 15:27:58 +00:00
|
|
|
SHE_RST_ACKED = 'T',
|
2017-09-22 21:00:03 +00:00
|
|
|
SHE_FLUSH = 'u',
|
2020-09-08 15:43:03 +00:00
|
|
|
SHE_STOP_SENDIG_OUT = 'U',
|
2017-09-22 21:00:03 +00:00
|
|
|
SHE_USER_WRITE_DATA = 'w',
|
|
|
|
SHE_SHUTDOWN_WRITE = 'W',
|
|
|
|
SHE_CLOSE = 'X',
|
2019-09-11 15:27:58 +00:00
|
|
|
SHE_DELAY_SW = 'y',
|
2017-09-22 21:00:03 +00:00
|
|
|
SHE_FORCE_FINISH = 'Z',
|
2019-10-11 12:24:24 +00:00
|
|
|
SHE_WANTREAD_NO = '0', /* "YES" must be one more than "NO" */
|
|
|
|
SHE_WANTREAD_YES = '1',
|
|
|
|
SHE_WANTWRITE_NO = '2',
|
|
|
|
SHE_WANTWRITE_YES = '3',
|
2017-09-22 21:00:03 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
sm_history_append (lsquic_stream_t *stream, enum stream_history_event sh_event)
|
|
|
|
{
|
|
|
|
enum stream_history_event prev_event;
|
|
|
|
sm_hist_idx_t idx;
|
|
|
|
int plus;
|
|
|
|
|
|
|
|
idx = (stream->sm_hist_idx - 1) & SM_HIST_IDX_MASK;
|
|
|
|
plus = SHE_PLUS == stream->sm_hist_buf[idx];
|
|
|
|
idx = (idx - plus) & SM_HIST_IDX_MASK;
|
|
|
|
prev_event = stream->sm_hist_buf[idx];
|
|
|
|
|
|
|
|
if (prev_event == sh_event && plus)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (prev_event == sh_event)
|
|
|
|
sh_event = SHE_PLUS;
|
|
|
|
stream->sm_hist_buf[ stream->sm_hist_idx++ & SM_HIST_IDX_MASK ] = sh_event;
|
|
|
|
|
|
|
|
if (0 == (stream->sm_hist_idx & SM_HIST_IDX_MASK))
|
|
|
|
LSQ_DEBUG("history: [%.*s]", (int) sizeof(stream->sm_hist_buf),
|
|
|
|
stream->sm_hist_buf);
|
|
|
|
}
|
|
|
|
|
2018-05-16 14:45:31 +00:00
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
# define SM_HISTORY_APPEND(stream, event) sm_history_append(stream, event)
|
|
|
|
# define SM_HISTORY_DUMP_REMAINING(stream) do { \
|
|
|
|
if (stream->sm_hist_idx & SM_HIST_IDX_MASK) \
|
|
|
|
LSQ_DEBUG("history: [%.*s]", \
|
|
|
|
(int) ((stream)->sm_hist_idx & SM_HIST_IDX_MASK), \
|
|
|
|
(stream)->sm_hist_buf); \
|
|
|
|
} while (0)
|
|
|
|
#else
|
|
|
|
# define SM_HISTORY_APPEND(stream, event)
|
|
|
|
# define SM_HISTORY_DUMP_REMAINING(stream)
|
|
|
|
#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 int
|
|
|
|
stream_inside_callback (const lsquic_stream_t *stream)
|
|
|
|
{
|
|
|
|
return stream->conn_pub->enpub->enp_flags & ENPUB_PROC;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-06-09 15:55:56 +00:00
|
|
|
/* This is an approximation. If data is written or read outside of the
|
|
|
|
* event loop, last_prog will be somewhat out of date, but it's close
|
|
|
|
* enough for our purposes.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
maybe_update_last_progress (struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
if (stream->conn_pub && !lsquic_stream_is_critical(stream))
|
|
|
|
{
|
|
|
|
if (stream->conn_pub->last_prog != stream->conn_pub->last_tick)
|
|
|
|
LSQ_DEBUG("update last progress to %"PRIu64,
|
|
|
|
stream->conn_pub->last_tick);
|
|
|
|
stream->conn_pub->last_prog = stream->conn_pub->last_tick;
|
|
|
|
#ifndef NDEBUG
|
|
|
|
stream->sm_last_prog = stream->conn_pub->last_tick;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-04-19 17:47:37 +00:00
|
|
|
static void
|
|
|
|
maybe_conn_to_tickable (lsquic_stream_t *stream)
|
|
|
|
{
|
|
|
|
if (!stream_inside_callback(stream))
|
|
|
|
lsquic_engine_add_conn_to_tickable(stream->conn_pub->enpub,
|
|
|
|
stream->conn_pub->lconn);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
/* Here, "readable" means that the user is able to read from the stream. */
|
|
|
|
static void
|
2018-04-09 13:39:38 +00:00
|
|
|
maybe_conn_to_tickable_if_readable (lsquic_stream_t *stream)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2018-04-09 13:39:38 +00:00
|
|
|
if (!stream_inside_callback(stream) && lsquic_stream_readable(stream))
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2018-04-09 13:39:38 +00:00
|
|
|
lsquic_engine_add_conn_to_tickable(stream->conn_pub->enpub,
|
|
|
|
stream->conn_pub->lconn);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Here, "writeable" means that data can be put into packets to be
|
|
|
|
* scheduled to be sent out.
|
2018-04-19 17:47:37 +00:00
|
|
|
*
|
|
|
|
* If `check_can_send' is false, it means that we do not need to check
|
|
|
|
* whether packets can be sent. This check was already performed when
|
|
|
|
* we packetized stream data.
|
2017-09-22 21:00:03 +00:00
|
|
|
*/
|
|
|
|
static void
|
2018-04-19 17:47:37 +00:00
|
|
|
maybe_conn_to_tickable_if_writeable (lsquic_stream_t *stream,
|
|
|
|
int check_can_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
|
|
|
if (!stream_inside_callback(stream) &&
|
2018-04-19 17:47:37 +00:00
|
|
|
(!check_can_send
|
|
|
|
|| lsquic_send_ctl_can_send(stream->conn_pub->send_ctl)) &&
|
2017-09-22 21:00:03 +00:00
|
|
|
! lsquic_send_ctl_have_delayed_packets(stream->conn_pub->send_ctl))
|
|
|
|
{
|
2018-04-09 13:39:38 +00:00
|
|
|
lsquic_engine_add_conn_to_tickable(stream->conn_pub->enpub,
|
|
|
|
stream->conn_pub->lconn);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
stream_stalled (const lsquic_stream_t *stream)
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
return 0 == (stream->sm_qflags & (SMQF_WANT_WRITE|SMQF_WANT_READ)) &&
|
2017-09-22 21:00:03 +00:00
|
|
|
((STREAM_U_READ_DONE|STREAM_U_WRITE_DONE) & stream->stream_flags)
|
|
|
|
!= (STREAM_U_READ_DONE|STREAM_U_WRITE_DONE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static size_t
|
|
|
|
stream_stream_frame_header_sz (const struct lsquic_stream *stream,
|
|
|
|
unsigned data_sz)
|
|
|
|
{
|
|
|
|
return stream->conn_pub->lconn->cn_pf->pf_calc_stream_frame_header_sz(
|
|
|
|
stream->id, stream->tosend_off, data_sz);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static size_t
|
|
|
|
stream_crypto_frame_header_sz (const struct lsquic_stream *stream,
|
2019-12-23 21:14:20 +00:00
|
|
|
unsigned data_sz)
|
2019-09-11 15:27:58 +00:00
|
|
|
{
|
|
|
|
return stream->conn_pub->lconn->cn_pf
|
2019-12-23 21:14:20 +00:00
|
|
|
->pf_calc_crypto_frame_header_sz(stream->tosend_off, data_sz);
|
2019-09-11 15:27:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* GQUIC-only function */
|
|
|
|
static int
|
|
|
|
stream_is_hsk (const struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
if (stream->sm_bflags & SMBF_IETF)
|
|
|
|
return 0;
|
|
|
|
else
|
2019-12-23 21:14:20 +00:00
|
|
|
return lsquic_stream_is_crypto(stream);
|
2019-09-11 15:27:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-10-13 12:20:25 +00:00
|
|
|
/* This function's only job is to change the allocated packet's header
|
|
|
|
* type to HETY_0RTT when stream frames are written before handshake
|
|
|
|
* is complete.
|
|
|
|
*/
|
|
|
|
static struct lsquic_packet_out *
|
|
|
|
stream_get_packet_for_stream_0rtt (struct lsquic_send_ctl *ctl,
|
|
|
|
unsigned need_at_least, const struct network_path *path,
|
|
|
|
const struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
struct lsquic_packet_out *packet_out;
|
|
|
|
|
|
|
|
if (stream->conn_pub->lconn->cn_flags & LSCONN_HANDSHAKE_DONE)
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("switch to regular \"get packet for stream\" function");
|
|
|
|
/* Here we drop the "const" because this is a static function.
|
|
|
|
* Otherwise, we would not condone such sorcery.
|
|
|
|
*/
|
|
|
|
((struct lsquic_stream *) stream)->sm_get_packet_for_stream
|
|
|
|
= lsquic_send_ctl_get_packet_for_stream;
|
|
|
|
return lsquic_send_ctl_get_packet_for_stream(ctl, need_at_least,
|
|
|
|
path, stream);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
packet_out = lsquic_send_ctl_get_packet_for_stream(ctl, need_at_least,
|
|
|
|
path, stream);
|
|
|
|
if (packet_out)
|
|
|
|
packet_out->po_header_type = HETY_0RTT;
|
|
|
|
return packet_out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static struct lsquic_stream *
|
|
|
|
stream_new_common (lsquic_stream_id_t id, struct lsquic_conn_public *conn_pub,
|
|
|
|
const struct lsquic_stream_if *stream_if, void *stream_if_ctx,
|
|
|
|
enum stream_ctor_flags ctor_flags)
|
|
|
|
{
|
|
|
|
struct lsquic_stream *stream;
|
|
|
|
|
|
|
|
stream = calloc(1, sizeof(*stream));
|
|
|
|
if (!stream)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (ctor_flags & SCF_USE_DI_HASH)
|
2020-03-12 12:41:53 +00:00
|
|
|
stream->data_in = lsquic_data_in_hash_new(conn_pub, id, 0);
|
2019-09-11 15:27:58 +00:00
|
|
|
else
|
2020-03-12 12:41:53 +00:00
|
|
|
stream->data_in = lsquic_data_in_nocopy_new(conn_pub, id);
|
2019-09-11 15:27:58 +00:00
|
|
|
if (!stream->data_in)
|
|
|
|
{
|
|
|
|
free(stream);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
stream->id = id;
|
|
|
|
stream->stream_if = stream_if;
|
|
|
|
stream->conn_pub = conn_pub;
|
|
|
|
stream->sm_onnew_arg = stream_if_ctx;
|
2019-09-23 16:00:10 +00:00
|
|
|
stream->sm_write_avail = stream_write_avail_no_frames;
|
2019-09-11 15:27:58 +00:00
|
|
|
|
|
|
|
STAILQ_INIT(&stream->sm_hq_frames);
|
|
|
|
|
2019-12-23 21:14:20 +00:00
|
|
|
stream->sm_bflags |= ctor_flags & ((1 << N_SMBF_FLAGS) - 1);
|
2019-09-11 15:27:58 +00:00
|
|
|
if (conn_pub->lconn->cn_flags & LSCONN_SERVER)
|
|
|
|
stream->sm_bflags |= SMBF_SERVER;
|
2020-10-13 12:20:25 +00:00
|
|
|
stream->sm_get_packet_for_stream = lsquic_send_ctl_get_packet_for_stream;
|
2019-09-11 15:27:58 +00:00
|
|
|
|
|
|
|
return stream;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
lsquic_stream_t *
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_stream_new (lsquic_stream_id_t id,
|
|
|
|
struct lsquic_conn_public *conn_pub,
|
|
|
|
const struct lsquic_stream_if *stream_if, void *stream_if_ctx,
|
|
|
|
unsigned initial_window, uint64_t initial_send_off,
|
|
|
|
enum stream_ctor_flags ctor_flags)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
lsquic_cfcw_t *cfcw;
|
|
|
|
lsquic_stream_t *stream;
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
stream = stream_new_common(id, conn_pub, stream_if, stream_if_ctx,
|
|
|
|
ctor_flags);
|
2017-09-22 21:00:03 +00:00
|
|
|
if (!stream)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!initial_window)
|
|
|
|
initial_window = 16 * 1024;
|
2019-09-11 15:27:58 +00:00
|
|
|
|
|
|
|
if (ctor_flags & SCF_IETF)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
cfcw = &conn_pub->cfcw;
|
2019-09-11 15:27:58 +00:00
|
|
|
stream->sm_bflags |= SMBF_CONN_LIMITED;
|
|
|
|
if (ctor_flags & SCF_HTTP)
|
|
|
|
{
|
|
|
|
stream->sm_write_avail = stream_write_avail_with_headers;
|
|
|
|
stream->sm_readable = stream_readable_http_ietf;
|
|
|
|
stream->sm_sfi = &hq_stream_filter_if;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
stream->sm_readable = stream_readable_non_http;
|
2020-10-07 13:41:26 +00:00
|
|
|
if ((ctor_flags & (SCF_HTTP|SCF_HTTP_PRIO))
|
|
|
|
== (SCF_HTTP|SCF_HTTP_PRIO))
|
2020-12-04 16:29:14 +00:00
|
|
|
lsquic_stream_set_priority_internal(stream, LSQUIC_DEF_HTTP_URGENCY);
|
2020-10-07 13:41:26 +00:00
|
|
|
else
|
|
|
|
lsquic_stream_set_priority_internal(stream,
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQUIC_STREAM_DEFAULT_PRIO);
|
|
|
|
stream->sm_write_to_packet = stream_write_to_packet_std;
|
2019-12-23 21:14:20 +00:00
|
|
|
stream->sm_frame_header_sz = stream_stream_frame_header_sz;
|
2019-09-11 15:27:58 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-12-23 21:14:20 +00:00
|
|
|
if (ctor_flags & SCF_CRITICAL)
|
2019-09-11 15:27:58 +00:00
|
|
|
cfcw = NULL;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cfcw = &conn_pub->cfcw;
|
|
|
|
stream->sm_bflags |= SMBF_CONN_LIMITED;
|
|
|
|
lsquic_stream_set_priority_internal(stream,
|
|
|
|
LSQUIC_STREAM_DEFAULT_PRIO);
|
|
|
|
}
|
|
|
|
if (stream->sm_bflags & SMBF_USE_HEADERS)
|
|
|
|
stream->sm_readable = stream_readable_http_gquic;
|
|
|
|
else
|
|
|
|
stream->sm_readable = stream_readable_non_http;
|
2019-12-23 21:14:20 +00:00
|
|
|
if (ctor_flags & SCF_CRYPTO_FRAMES)
|
|
|
|
{
|
|
|
|
stream->sm_frame_header_sz = stream_crypto_frame_header_sz;
|
|
|
|
stream->sm_write_to_packet = stream_write_to_packet_crypto;
|
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
else
|
2019-12-23 21:14:20 +00:00
|
|
|
{
|
|
|
|
if (stream_is_hsk(stream))
|
|
|
|
stream->sm_write_to_packet = stream_write_to_packet_hsk;
|
|
|
|
else
|
|
|
|
stream->sm_write_to_packet = stream_write_to_packet_std;
|
|
|
|
stream->sm_frame_header_sz = stream_stream_frame_header_sz;
|
|
|
|
}
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
|
2020-10-13 12:20:25 +00:00
|
|
|
if ((stream->sm_bflags & (SMBF_SERVER|SMBF_IETF)) == SMBF_IETF
|
|
|
|
&& !(conn_pub->lconn->cn_flags & LSCONN_HANDSHAKE_DONE))
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("use wrapper \"get packet for stream\" function");
|
|
|
|
stream->sm_get_packet_for_stream = stream_get_packet_for_stream_0rtt;
|
|
|
|
}
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
lsquic_sfcw_init(&stream->fc, initial_window, cfcw, conn_pub, id);
|
|
|
|
stream->max_send_off = initial_send_off;
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("created stream");
|
|
|
|
SM_HISTORY_APPEND(stream, SHE_CREATED);
|
|
|
|
if (ctor_flags & SCF_CALL_ON_NEW)
|
|
|
|
lsquic_stream_call_on_new(stream);
|
|
|
|
return stream;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct lsquic_stream *
|
|
|
|
lsquic_stream_new_crypto (enum enc_level enc_level,
|
|
|
|
struct lsquic_conn_public *conn_pub,
|
|
|
|
const struct lsquic_stream_if *stream_if, void *stream_if_ctx,
|
|
|
|
enum stream_ctor_flags ctor_flags)
|
|
|
|
{
|
|
|
|
struct lsquic_stream *stream;
|
|
|
|
lsquic_stream_id_t stream_id;
|
|
|
|
|
|
|
|
assert(ctor_flags & SCF_CRITICAL);
|
|
|
|
|
2020-01-20 14:41:37 +00:00
|
|
|
fiu_return_on("stream/new_crypto", NULL);
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
stream_id = ~0ULL - enc_level;
|
|
|
|
stream = stream_new_common(stream_id, conn_pub, stream_if,
|
|
|
|
stream_if_ctx, ctor_flags);
|
|
|
|
if (!stream)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
stream->sm_bflags |= SMBF_CRYPTO|SMBF_IETF;
|
|
|
|
stream->sm_enc_level = enc_level;
|
2020-09-15 20:42:13 +00:00
|
|
|
/* We allow buffering of up to 16 KB of CRYPTO data (I guess we could
|
|
|
|
* make this configurable?). The window is opened (without sending
|
|
|
|
* MAX_STREAM_DATA) as CRYPTO data is consumed. If too much comes in
|
|
|
|
* at a time, we abort with TEC_CRYPTO_BUFFER_EXCEEDED.
|
|
|
|
*/
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_sfcw_init(&stream->fc, 16 * 1024, NULL, conn_pub, stream_id);
|
2020-09-15 20:42:13 +00:00
|
|
|
/* Don't limit ourselves from sending CRYPTO data. We assume that
|
|
|
|
* the underlying crypto library behaves in a sane manner.
|
|
|
|
*/
|
|
|
|
stream->max_send_off = UINT64_MAX;
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("created crypto stream");
|
2017-09-22 21:00:03 +00:00
|
|
|
SM_HISTORY_APPEND(stream, SHE_CREATED);
|
2019-09-11 15:27:58 +00:00
|
|
|
stream->sm_frame_header_sz = stream_crypto_frame_header_sz;
|
|
|
|
stream->sm_write_to_packet = stream_write_to_packet_crypto;
|
|
|
|
stream->sm_readable = stream_readable_non_http;
|
2017-09-22 21:00:03 +00:00
|
|
|
if (ctor_flags & SCF_CALL_ON_NEW)
|
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 stream;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
lsquic_stream_call_on_new (lsquic_stream_t *stream)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
assert(!(stream->stream_flags & STREAM_ONNEW_DONE));
|
|
|
|
if (!(stream->stream_flags & STREAM_ONNEW_DONE))
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("calling on_new_stream");
|
|
|
|
SM_HISTORY_APPEND(stream, SHE_ONNEW);
|
|
|
|
stream->stream_flags |= STREAM_ONNEW_DONE;
|
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->st_ctx = stream->stream_if->on_new_stream(stream->sm_onnew_arg,
|
|
|
|
stream);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
decr_conn_cap (struct lsquic_stream *stream, size_t incr)
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
if (stream->sm_bflags & SMBF_CONN_LIMITED)
|
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
|
|
|
{
|
|
|
|
assert(stream->conn_pub->conn_cap.cc_sent >= incr);
|
|
|
|
stream->conn_pub->conn_cap.cc_sent -= incr;
|
2023-01-26 16:19:06 +00:00
|
|
|
LSQ_DEBUG("decrease cc_sent by %zd to %"PRIu64, incr,
|
|
|
|
stream->conn_pub->conn_cap.cc_sent);
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static void
|
|
|
|
maybe_resize_stream_buffer (struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
assert(0 == stream->sm_n_buffered);
|
|
|
|
|
|
|
|
if (stream->sm_n_allocated < stream->conn_pub->path->np_pack_size)
|
|
|
|
{
|
|
|
|
free(stream->sm_buf);
|
|
|
|
stream->sm_buf = NULL;
|
|
|
|
stream->sm_n_allocated = 0;
|
|
|
|
}
|
|
|
|
else if (stream->sm_n_allocated > stream->conn_pub->path->np_pack_size)
|
|
|
|
stream->sm_n_allocated = stream->conn_pub->path->np_pack_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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
|
|
|
|
drop_buffered_data (struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
decr_conn_cap(stream, stream->sm_n_buffered);
|
|
|
|
stream->sm_n_buffered = 0;
|
2019-09-11 15:27:58 +00:00
|
|
|
maybe_resize_stream_buffer(stream);
|
|
|
|
if (stream->sm_qflags & SMQF_WRITE_Q_FLAGS)
|
|
|
|
maybe_remove_from_write_q(stream, SMQF_WRITE_Q_FLAGS);
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-10-13 12:20:25 +00:00
|
|
|
void
|
|
|
|
lsquic_stream_drop_hset_ref (struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
if (stream->uh)
|
|
|
|
stream->uh->uh_hset = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-28 13:59:47 +00:00
|
|
|
static void
|
2021-04-12 13:52:42 +00:00
|
|
|
destroy_uh (struct uncompressed_headers *uh, const struct lsquic_hset_if *hsi_if)
|
2018-08-28 13:59:47 +00:00
|
|
|
{
|
2021-04-12 13:52:42 +00:00
|
|
|
if (uh)
|
2018-08-28 13:59:47 +00:00
|
|
|
{
|
2021-04-12 13:52:42 +00:00
|
|
|
if (uh->uh_hset)
|
|
|
|
hsi_if->hsi_discard_header_set(uh->uh_hset);
|
|
|
|
free(uh);
|
2018-08-28 13:59:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
void
|
|
|
|
lsquic_stream_destroy (lsquic_stream_t *stream)
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
struct push_promise *promise;
|
|
|
|
struct stream_hq_frame *shf;
|
2021-04-12 13:52:42 +00:00
|
|
|
struct uncompressed_headers *uh;
|
2019-09-11 15:27:58 +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->stream_flags |= STREAM_U_WRITE_DONE|STREAM_U_READ_DONE;
|
2017-09-22 21:00:03 +00:00
|
|
|
if ((stream->stream_flags & (STREAM_ONNEW_DONE|STREAM_ONCLOSE_DONE)) ==
|
|
|
|
STREAM_ONNEW_DONE)
|
|
|
|
{
|
|
|
|
stream->stream_flags |= STREAM_ONCLOSE_DONE;
|
2020-07-29 15:33:52 +00:00
|
|
|
SM_HISTORY_APPEND(stream, SHE_ONCLOSE_CALL);
|
2017-09-22 21:00:03 +00:00
|
|
|
stream->stream_if->on_close(stream, stream->st_ctx);
|
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
if (stream->sm_qflags & SMQF_SENDING_FLAGS)
|
2017-09-22 21:00:03 +00:00
|
|
|
TAILQ_REMOVE(&stream->conn_pub->sending_streams, stream, next_send_stream);
|
2019-09-11 15:27:58 +00:00
|
|
|
if (stream->sm_qflags & SMQF_WANT_READ)
|
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_REMOVE(&stream->conn_pub->read_streams, stream, next_read_stream);
|
2019-09-11 15:27:58 +00:00
|
|
|
if (stream->sm_qflags & SMQF_WRITE_Q_FLAGS)
|
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_REMOVE(&stream->conn_pub->write_streams, stream, next_write_stream);
|
2019-09-11 15:27:58 +00:00
|
|
|
if (stream->sm_qflags & SMQF_SERVICE_FLAGS)
|
2017-09-22 21:00:03 +00:00
|
|
|
TAILQ_REMOVE(&stream->conn_pub->service_streams, stream, next_service_stream);
|
2019-09-11 15:27:58 +00:00
|
|
|
if (stream->sm_qflags & SMQF_QPACK_DEC)
|
2020-04-24 13:44:39 +00:00
|
|
|
lsquic_qdh_cancel_stream(stream->conn_pub->u.ietf.qdh, stream);
|
|
|
|
else if ((stream->sm_bflags & (SMBF_IETF|SMBF_USE_HEADERS))
|
|
|
|
== (SMBF_IETF|SMBF_USE_HEADERS)
|
|
|
|
&& !(stream->stream_flags & STREAM_FIN_REACHED))
|
|
|
|
lsquic_qdh_cancel_stream_id(stream->conn_pub->u.ietf.qdh, 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
|
|
|
drop_buffered_data(stream);
|
2017-09-22 21:00:03 +00:00
|
|
|
lsquic_sfcw_consume_rem(&stream->fc);
|
|
|
|
drop_frames_in(stream);
|
2018-08-28 13:59:47 +00:00
|
|
|
if (stream->push_req)
|
|
|
|
{
|
|
|
|
if (stream->push_req->uh_hset)
|
|
|
|
stream->conn_pub->enpub->enp_hsi_if
|
|
|
|
->hsi_discard_header_set(stream->push_req->uh_hset);
|
|
|
|
free(stream->push_req);
|
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
while ((promise = SLIST_FIRST(&stream->sm_promises)))
|
|
|
|
{
|
|
|
|
SLIST_REMOVE_HEAD(&stream->sm_promises, pp_next);
|
|
|
|
lsquic_pp_put(promise, stream->conn_pub->u.ietf.promises);
|
|
|
|
}
|
|
|
|
if (stream->sm_promise)
|
|
|
|
{
|
|
|
|
assert(stream->sm_promise->pp_pushed_stream == stream);
|
|
|
|
stream->sm_promise->pp_pushed_stream = NULL;
|
|
|
|
lsquic_pp_put(stream->sm_promise, stream->conn_pub->u.ietf.promises);
|
|
|
|
}
|
|
|
|
while ((shf = STAILQ_FIRST(&stream->sm_hq_frames)))
|
|
|
|
stream_hq_frame_put(stream, shf);
|
2021-04-12 13:52:42 +00:00
|
|
|
while(stream->uh)
|
|
|
|
{
|
|
|
|
uh = stream->uh;
|
|
|
|
stream->uh = uh->uh_next;
|
|
|
|
destroy_uh(uh, stream->conn_pub->enpub->enp_hsi_if);
|
|
|
|
}
|
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
|
|
|
free(stream->sm_buf);
|
2019-09-13 13:42:22 +00:00
|
|
|
free(stream->sm_header_block);
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("destroyed stream");
|
2017-09-22 21:00:03 +00:00
|
|
|
SM_HISTORY_DUMP_REMAINING(stream);
|
|
|
|
free(stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2020-12-04 16:29:14 +00:00
|
|
|
stream_is_finished (struct lsquic_stream *stream)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
return lsquic_stream_is_closed(stream)
|
2020-12-04 16:29:14 +00:00
|
|
|
&& (stream->sm_bflags & SMBF_DELAY_ONCLOSE ?
|
|
|
|
/* Need a stricter check when on_close() is delayed: */
|
|
|
|
!lsquic_stream_has_unacked_data(stream) :
|
2017-09-22 21:00:03 +00:00
|
|
|
/* n_unacked checks that no outgoing packets that reference this
|
|
|
|
* stream are outstanding:
|
|
|
|
*/
|
2020-12-04 16:29:14 +00:00
|
|
|
0 == stream->n_unacked)
|
2020-09-08 15:43:03 +00:00
|
|
|
&& 0 == (stream->sm_qflags & (
|
2017-09-22 21:00:03 +00:00
|
|
|
/* This checks that no packets that reference this stream will
|
|
|
|
* become outstanding:
|
|
|
|
*/
|
2020-09-08 15:43:03 +00:00
|
|
|
SMQF_SEND_RST
|
|
|
|
/* Can't finish stream until all "self" flags are unset: */
|
|
|
|
| SMQF_SELF_FLAGS))
|
2017-09-22 21:00:03 +00:00
|
|
|
&& ((stream->stream_flags & STREAM_FORCE_FINISH)
|
2019-05-06 13:40:59 +00:00
|
|
|
|| (stream->stream_flags & (STREAM_FIN_SENT |STREAM_RST_SENT)));
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-23 16:00:10 +00:00
|
|
|
/* This is an internal function */
|
|
|
|
void
|
|
|
|
lsquic_stream_force_finish (struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("stream is now finished");
|
|
|
|
SM_HISTORY_APPEND(stream, SHE_FINISHED);
|
|
|
|
if (0 == (stream->sm_qflags & SMQF_SERVICE_FLAGS))
|
|
|
|
TAILQ_INSERT_TAIL(&stream->conn_pub->service_streams, stream,
|
|
|
|
next_service_stream);
|
|
|
|
stream->sm_qflags |= SMQF_FREE_STREAM;
|
|
|
|
stream->stream_flags |= STREAM_FINISHED;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
static void
|
|
|
|
maybe_finish_stream (lsquic_stream_t *stream)
|
|
|
|
{
|
|
|
|
if (0 == (stream->stream_flags & STREAM_FINISHED) &&
|
|
|
|
stream_is_finished(stream))
|
2019-09-23 16:00:10 +00:00
|
|
|
lsquic_stream_force_finish(stream);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
maybe_schedule_call_on_close (lsquic_stream_t *stream)
|
|
|
|
{
|
|
|
|
if ((stream->stream_flags & (STREAM_U_READ_DONE|STREAM_U_WRITE_DONE|
|
2019-09-11 15:27:58 +00:00
|
|
|
STREAM_ONNEW_DONE|STREAM_ONCLOSE_DONE))
|
|
|
|
== (STREAM_U_READ_DONE|STREAM_U_WRITE_DONE|STREAM_ONNEW_DONE)
|
2020-12-04 16:29:14 +00:00
|
|
|
&& (!(stream->sm_bflags & SMBF_DELAY_ONCLOSE)
|
|
|
|
|| !lsquic_stream_has_unacked_data(stream))
|
2019-09-11 15:27:58 +00:00
|
|
|
&& !(stream->sm_qflags & SMQF_CALL_ONCLOSE))
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
if (0 == (stream->sm_qflags & SMQF_SERVICE_FLAGS))
|
2017-09-22 21:00:03 +00:00
|
|
|
TAILQ_INSERT_TAIL(&stream->conn_pub->service_streams, stream,
|
|
|
|
next_service_stream);
|
2019-09-11 15:27:58 +00:00
|
|
|
stream->sm_qflags |= SMQF_CALL_ONCLOSE;
|
|
|
|
LSQ_DEBUG("scheduled calling on_close");
|
2017-09-22 21:00:03 +00:00
|
|
|
SM_HISTORY_APPEND(stream, SHE_ONCLOSE_SCHED);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
lsquic_stream_call_on_close (lsquic_stream_t *stream)
|
|
|
|
{
|
|
|
|
assert(stream->stream_flags & STREAM_ONNEW_DONE);
|
2019-09-11 15:27:58 +00:00
|
|
|
stream->sm_qflags &= ~SMQF_CALL_ONCLOSE;
|
|
|
|
if (!(stream->sm_qflags & SMQF_SERVICE_FLAGS))
|
2017-09-22 21:00:03 +00:00
|
|
|
TAILQ_REMOVE(&stream->conn_pub->service_streams, stream,
|
|
|
|
next_service_stream);
|
|
|
|
if (0 == (stream->stream_flags & STREAM_ONCLOSE_DONE))
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("calling on_close");
|
2017-09-22 21:00:03 +00:00
|
|
|
stream->stream_flags |= STREAM_ONCLOSE_DONE;
|
|
|
|
SM_HISTORY_APPEND(stream, SHE_ONCLOSE_CALL);
|
|
|
|
stream->stream_if->on_close(stream, stream->st_ctx);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-10-31 16:21:14 +00:00
|
|
|
static int
|
|
|
|
stream_has_frame_at_read_offset (struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
if (!((stream->stream_flags & STREAM_CACHED_FRAME)
|
|
|
|
&& stream->read_offset == stream->sm_last_frame_off))
|
|
|
|
{
|
|
|
|
stream->sm_has_frame = stream->data_in->di_if->di_get_frame(
|
|
|
|
stream->data_in, stream->read_offset) != NULL;
|
|
|
|
stream->sm_last_frame_off = stream->read_offset;
|
|
|
|
stream->stream_flags |= STREAM_CACHED_FRAME;
|
|
|
|
}
|
|
|
|
return stream->sm_has_frame;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static int
|
|
|
|
stream_readable_non_http (struct lsquic_stream *stream)
|
|
|
|
{
|
2019-10-31 16:21:14 +00:00
|
|
|
return stream_has_frame_at_read_offset(stream);
|
2019-09-11 15:27:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
stream_readable_http_gquic (struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
return (stream->stream_flags & STREAM_HAVE_UH)
|
|
|
|
&& (stream->uh
|
2019-10-31 16:21:14 +00:00
|
|
|
|| stream_has_frame_at_read_offset(stream));
|
2019-09-11 15:27:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
stream_readable_http_ietf (struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
return
|
|
|
|
/* If we have read the header set and the header set has not yet
|
|
|
|
* been read, the stream is readable.
|
|
|
|
*/
|
|
|
|
((stream->stream_flags & STREAM_HAVE_UH) && stream->uh)
|
|
|
|
||
|
|
|
|
/* Alternatively, run the filter and check for payload availability. */
|
|
|
|
(stream->sm_sfi->sfi_readable(stream)
|
|
|
|
&& (/* Running the filter may result in hitting FIN: */
|
|
|
|
(stream->stream_flags & STREAM_FIN_REACHED)
|
2019-10-31 16:21:14 +00:00
|
|
|
|| stream_has_frame_at_read_offset(stream)));
|
2019-09-11 15:27:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-04-24 13:44:39 +00:00
|
|
|
static int
|
|
|
|
maybe_switch_data_in (struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
if ((stream->sm_bflags & SMBF_AUTOSWITCH) &&
|
|
|
|
(stream->data_in->di_flags & DI_SWITCH_IMPL))
|
|
|
|
{
|
|
|
|
stream->data_in = stream->data_in->di_if->di_switch_impl(
|
|
|
|
stream->data_in, stream->read_offset);
|
|
|
|
if (!stream->data_in)
|
|
|
|
{
|
|
|
|
stream->data_in = lsquic_data_in_error_new();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Drain and discard any incoming data */
|
|
|
|
static int
|
|
|
|
stream_readable_discard (struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
struct data_frame *data_frame;
|
2020-06-24 15:06:43 +00:00
|
|
|
uint64_t toread;
|
2020-10-07 13:41:26 +00:00
|
|
|
int fin;
|
2020-04-24 13:44:39 +00:00
|
|
|
|
|
|
|
while ((data_frame = stream->data_in->di_if->di_get_frame(
|
|
|
|
stream->data_in, stream->read_offset)))
|
|
|
|
{
|
2020-10-07 13:41:26 +00:00
|
|
|
fin = data_frame->df_fin;
|
2020-06-24 15:06:43 +00:00
|
|
|
toread = data_frame->df_size - data_frame->df_read_off;
|
|
|
|
stream->read_offset += toread;
|
2020-04-24 13:44:39 +00:00
|
|
|
data_frame->df_read_off = data_frame->df_size;
|
|
|
|
stream->data_in->di_if->di_frame_done(stream->data_in, data_frame);
|
2020-10-07 13:41:26 +00:00
|
|
|
if (fin)
|
|
|
|
break;
|
2020-04-24 13:44:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
(void) maybe_switch_data_in(stream);
|
|
|
|
|
|
|
|
return 0; /* Never readable */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-12-23 14:06:05 +00:00
|
|
|
static int
|
|
|
|
stream_is_read_reset (const struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
if (stream->sm_bflags & SMBF_IETF)
|
|
|
|
return stream->stream_flags & STREAM_RST_RECVD;
|
|
|
|
else
|
|
|
|
return (stream->stream_flags & (STREAM_RST_RECVD|STREAM_RST_SENT))
|
|
|
|
|| (stream->sm_qflags & SMQF_SEND_RST);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-04-09 13:39:38 +00:00
|
|
|
int
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_stream_readable (struct lsquic_stream *stream)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
/* A stream is readable if one of the following is true: */
|
|
|
|
return
|
|
|
|
/* - It is already finished: in that case, lsquic_stream_read() will
|
|
|
|
* return 0.
|
|
|
|
*/
|
|
|
|
(stream->stream_flags & STREAM_FIN_REACHED)
|
|
|
|
/* - The stream is reset, by either side. In this case,
|
|
|
|
* lsquic_stream_read() will return -1 (we want the user to be
|
|
|
|
* able to collect the error).
|
|
|
|
*/
|
2020-12-23 14:06:05 +00:00
|
|
|
|| stream_is_read_reset(stream)
|
2019-09-11 15:27:58 +00:00
|
|
|
/* Type-dependent readability check: */
|
|
|
|
|| stream->sm_readable(stream);
|
2017-09-22 21:00:03 +00:00
|
|
|
;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-12-23 14:06:05 +00:00
|
|
|
/* Return true if write end of the stream has been reset.
|
|
|
|
* Note that the logic for gQUIC is the same for write and read resets.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
lsquic_stream_is_write_reset (const struct lsquic_stream *stream)
|
|
|
|
{
|
2021-01-18 18:26:33 +00:00
|
|
|
/* The two protocols use different frames to effect write reset: */
|
|
|
|
const enum stream_flags cause_flag = stream->sm_bflags & SMBF_IETF
|
|
|
|
? STREAM_SS_RECVD : STREAM_RST_RECVD;
|
|
|
|
return (stream->stream_flags & (cause_flag|STREAM_RST_SENT))
|
|
|
|
|| (stream->sm_qflags & SMQF_SEND_RST);
|
2020-12-23 14:06:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-09-08 15:43:03 +00:00
|
|
|
static int
|
|
|
|
stream_writeable (struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
/* A stream is writeable if one of the following is true: */
|
|
|
|
return
|
|
|
|
/* - The stream is reset, by either side. In this case,
|
|
|
|
* lsquic_stream_write() will return -1 (we want the user to be
|
|
|
|
* able to collect the error).
|
|
|
|
*/
|
2020-12-23 14:06:05 +00:00
|
|
|
lsquic_stream_is_write_reset(stream)
|
2020-09-08 15:43:03 +00:00
|
|
|
/* - Data can be written to stream: */
|
|
|
|
|| lsquic_stream_write_avail(stream)
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static size_t
|
2019-09-23 16:00:10 +00:00
|
|
|
stream_write_avail_no_frames (struct lsquic_stream *stream)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
uint64_t stream_avail, conn_avail;
|
|
|
|
|
|
|
|
stream_avail = stream->max_send_off - stream->tosend_off
|
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->sm_n_buffered;
|
2019-09-11 15:27:58 +00:00
|
|
|
|
|
|
|
if (stream->sm_bflags & SMBF_CONN_LIMITED)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
conn_avail = lsquic_conn_cap_avail(&stream->conn_pub->conn_cap);
|
|
|
|
if (conn_avail < stream_avail)
|
2019-09-11 15:27:58 +00:00
|
|
|
stream_avail = conn_avail;
|
|
|
|
}
|
|
|
|
|
2019-09-23 16:00:10 +00:00
|
|
|
return stream_avail;
|
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
|
2019-09-23 16:00:10 +00:00
|
|
|
|
|
|
|
static size_t
|
|
|
|
stream_write_avail_with_frames (struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
uint64_t stream_avail, conn_avail;
|
|
|
|
const struct stream_hq_frame *shf;
|
|
|
|
size_t size;
|
|
|
|
|
|
|
|
stream_avail = stream->max_send_off - stream->tosend_off
|
|
|
|
- stream->sm_n_buffered;
|
|
|
|
STAILQ_FOREACH(shf, &stream->sm_hq_frames, shf_next)
|
|
|
|
if (!(shf->shf_flags & SHF_WRITTEN))
|
|
|
|
{
|
|
|
|
size = stream_hq_frame_size(shf);
|
|
|
|
assert(size <= stream_avail);
|
|
|
|
stream_avail -= size;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stream->sm_bflags & SMBF_CONN_LIMITED)
|
|
|
|
{
|
|
|
|
conn_avail = lsquic_conn_cap_avail(&stream->conn_pub->conn_cap);
|
|
|
|
STAILQ_FOREACH(shf, &stream->sm_hq_frames, shf_next)
|
|
|
|
if (!(shf->shf_flags & SHF_CC_PAID))
|
|
|
|
{
|
|
|
|
size = stream_hq_frame_size(shf);
|
|
|
|
if (size < conn_avail)
|
|
|
|
conn_avail -= size;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (conn_avail < stream_avail)
|
|
|
|
stream_avail = conn_avail;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
2019-09-23 16:00:10 +00:00
|
|
|
if (stream_avail >= 3 /* Smallest new frame */)
|
|
|
|
return stream_avail;
|
|
|
|
else
|
|
|
|
return 0;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static int
|
|
|
|
stream_is_pushing_promise (const struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
return (stream->stream_flags & STREAM_PUSHING)
|
|
|
|
&& SLIST_FIRST(&stream->sm_promises)
|
|
|
|
&& (SLIST_FIRST(&stream->sm_promises))->pp_write_state != PPWS_DONE
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* To prevent deadlocks, ensure that when headers are sent, the bytes
|
|
|
|
* sent on the encoder stream are written first.
|
|
|
|
*
|
|
|
|
* XXX If the encoder is set up in non-risking mode, it is perfectly
|
|
|
|
* fine to send the header block first. TODO: update the logic to
|
|
|
|
* reflect this. There should be two sending behaviors: risk and non-risk.
|
|
|
|
* For now, we assume risk for everything to be on the conservative side.
|
|
|
|
*/
|
|
|
|
static size_t
|
|
|
|
stream_write_avail_with_headers (struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
if (stream->stream_flags & STREAM_PUSHING)
|
2019-09-23 16:00:10 +00:00
|
|
|
return stream_write_avail_with_frames(stream);
|
2019-09-11 15:27:58 +00:00
|
|
|
|
|
|
|
switch (stream->sm_send_headers_state)
|
|
|
|
{
|
|
|
|
case SSHS_BEGIN:
|
|
|
|
return lsquic_qeh_write_avail(stream->conn_pub->u.ietf.qeh);
|
|
|
|
case SSHS_ENC_SENDING:
|
|
|
|
if (stream->sm_hb_compl >
|
|
|
|
lsquic_qeh_enc_off(stream->conn_pub->u.ietf.qeh))
|
|
|
|
return 0;
|
|
|
|
LSQ_DEBUG("encoder stream bytes have all been sent");
|
|
|
|
stream->sm_send_headers_state = SSHS_HBLOCK_SENDING;
|
|
|
|
/* fall-through */
|
|
|
|
default:
|
|
|
|
assert(SSHS_HBLOCK_SENDING == stream->sm_send_headers_state);
|
2019-09-23 16:00:10 +00:00
|
|
|
return stream_write_avail_with_frames(stream);
|
2019-09-11 15:27:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
size_t
|
|
|
|
lsquic_stream_write_avail (struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
return stream->sm_write_avail(stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
int
|
|
|
|
lsquic_stream_update_sfcw (lsquic_stream_t *stream, uint64_t max_off)
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
struct lsquic_conn *lconn;
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
if (max_off > lsquic_sfcw_get_max_recv_off(&stream->fc) &&
|
|
|
|
!lsquic_sfcw_set_max_recv_off(&stream->fc, max_off))
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
if (stream->sm_bflags & SMBF_IETF)
|
|
|
|
{
|
|
|
|
lconn = stream->conn_pub->lconn;
|
2020-09-15 20:42:13 +00:00
|
|
|
if (lsquic_stream_is_crypto(stream))
|
|
|
|
lconn->cn_if->ci_abort_error(lconn, 0,
|
|
|
|
TEC_CRYPTO_BUFFER_EXCEEDED,
|
|
|
|
"crypto buffer exceeded on in crypto level %"PRIu64,
|
|
|
|
crypto_level(stream));
|
|
|
|
else
|
|
|
|
lconn->cn_if->ci_abort_error(lconn, 0, TEC_FLOW_CONTROL_ERROR,
|
|
|
|
"flow control violation on stream %"PRIu64, stream->id);
|
2019-09-11 15:27:58 +00:00
|
|
|
}
|
2017-09-22 21:00:03 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (lsquic_sfcw_fc_offsets_changed(&stream->fc))
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
if (!(stream->sm_qflags & SMQF_SENDING_FLAGS))
|
2017-09-22 21:00:03 +00:00
|
|
|
TAILQ_INSERT_TAIL(&stream->conn_pub->sending_streams, stream,
|
|
|
|
next_send_stream);
|
2019-09-11 15:27:58 +00:00
|
|
|
stream->sm_qflags |= SMQF_SEND_WUF;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
lsquic_stream_frame_in (lsquic_stream_t *stream, stream_frame_t *frame)
|
|
|
|
{
|
|
|
|
uint64_t max_off;
|
2019-09-12 18:39:50 +00:00
|
|
|
int got_next_offset, rv, free_frame;
|
2017-09-22 21:00:03 +00:00
|
|
|
enum ins_frame ins_frame;
|
2019-10-08 12:36:49 +00:00
|
|
|
struct lsquic_conn *lconn;
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
assert(frame->packet_in);
|
|
|
|
|
|
|
|
SM_HISTORY_APPEND(stream, SHE_FRAME_IN);
|
2020-11-05 16:01:46 +00:00
|
|
|
LSQ_DEBUG("received stream frame, offset %"PRIu64", len %u; "
|
2019-09-11 15:27:58 +00:00
|
|
|
"fin: %d", frame->data_frame.df_offset, frame->data_frame.df_size, !!frame->data_frame.df_fin);
|
2017-09-22 21:00:03 +00:00
|
|
|
|
2022-05-13 14:52:37 +00:00
|
|
|
rv = -1;
|
2019-09-11 15:27:58 +00:00
|
|
|
if ((stream->sm_bflags & SMBF_USE_HEADERS)
|
|
|
|
&& (stream->stream_flags & STREAM_HEAD_IN_FIN))
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2022-05-13 14:52:37 +00:00
|
|
|
goto release_packet_frame;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
2019-10-08 12:36:49 +00:00
|
|
|
if (frame->data_frame.df_fin && (stream->sm_bflags & SMBF_IETF)
|
|
|
|
&& (stream->stream_flags & STREAM_FIN_RECVD)
|
|
|
|
&& stream->sm_fin_off != DF_END(frame))
|
|
|
|
{
|
|
|
|
lconn = stream->conn_pub->lconn;
|
|
|
|
lconn->cn_if->ci_abort_error(lconn, 0, TEC_FINAL_SIZE_ERROR,
|
|
|
|
"new final size %"PRIu64" from STREAM frame (id: %"PRIu64") does "
|
|
|
|
"not match previous final size %"PRIu64, DF_END(frame),
|
|
|
|
stream->id, stream->sm_fin_off);
|
2022-05-13 14:52:37 +00:00
|
|
|
goto release_packet_frame;
|
2019-10-08 12:36:49 +00:00
|
|
|
}
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
got_next_offset = frame->data_frame.df_offset == stream->read_offset;
|
2018-05-04 18:00:34 +00:00
|
|
|
insert_frame:
|
2017-09-22 21:00:03 +00:00
|
|
|
ins_frame = stream->data_in->di_if->di_insert_frame(stream->data_in, frame, stream->read_offset);
|
|
|
|
if (INS_FRAME_OK == ins_frame)
|
|
|
|
{
|
|
|
|
/* Update maximum offset in the flow controller and check for flow
|
|
|
|
* control violation:
|
|
|
|
*/
|
2019-09-12 18:39:50 +00:00
|
|
|
free_frame = !stream->data_in->di_if->di_own_on_ok;
|
2017-09-22 21:00:03 +00:00
|
|
|
max_off = frame->data_frame.df_offset + frame->data_frame.df_size;
|
|
|
|
if (0 != lsquic_stream_update_sfcw(stream, max_off))
|
2019-09-12 18:39:50 +00:00
|
|
|
goto end_ok;
|
2017-09-22 21:00:03 +00:00
|
|
|
if (frame->data_frame.df_fin)
|
|
|
|
{
|
|
|
|
SM_HISTORY_APPEND(stream, SHE_FIN_IN);
|
|
|
|
stream->stream_flags |= STREAM_FIN_RECVD;
|
2020-09-08 15:43:03 +00:00
|
|
|
stream->sm_qflags &= ~SMQF_WAIT_FIN_OFF;
|
2019-09-11 15:27:58 +00:00
|
|
|
stream->sm_fin_off = DF_END(frame);
|
2017-09-22 21:00:03 +00:00
|
|
|
maybe_finish_stream(stream);
|
|
|
|
}
|
2020-04-24 13:44:39 +00:00
|
|
|
if (0 != maybe_switch_data_in(stream))
|
|
|
|
goto end_ok;
|
2017-09-22 21:00:03 +00:00
|
|
|
if (got_next_offset)
|
|
|
|
/* Checking the offset saves di_get_frame() call */
|
2018-04-09 13:39:38 +00:00
|
|
|
maybe_conn_to_tickable_if_readable(stream);
|
2019-09-12 18:39:50 +00:00
|
|
|
rv = 0;
|
|
|
|
end_ok:
|
|
|
|
if (free_frame)
|
|
|
|
lsquic_malo_put(frame);
|
2019-10-31 16:21:14 +00:00
|
|
|
stream->stream_flags &= ~STREAM_CACHED_FRAME;
|
2019-09-12 18:39:50 +00:00
|
|
|
return rv;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
else if (INS_FRAME_DUP == ins_frame)
|
|
|
|
{
|
2022-05-13 14:52:37 +00:00
|
|
|
rv = 0;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
2018-05-04 18:00:34 +00:00
|
|
|
else if (INS_FRAME_OVERLAP == ins_frame)
|
|
|
|
{
|
2018-09-06 18:05:15 +00:00
|
|
|
LSQ_DEBUG("overlap: switching DATA IN implementation");
|
|
|
|
stream->data_in = stream->data_in->di_if->di_switch_impl(
|
|
|
|
stream->data_in, stream->read_offset);
|
|
|
|
if (stream->data_in)
|
|
|
|
goto insert_frame;
|
2020-03-12 12:41:53 +00:00
|
|
|
stream->data_in = lsquic_data_in_error_new();
|
2018-05-04 18:00:34 +00:00
|
|
|
}
|
2017-09-22 21:00:03 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
assert(INS_FRAME_ERR == ins_frame);
|
|
|
|
}
|
2022-05-13 14:52:37 +00:00
|
|
|
release_packet_frame:
|
|
|
|
lsquic_packet_in_put(stream->conn_pub->mm, frame->packet_in);
|
|
|
|
lsquic_malo_put(frame);
|
|
|
|
return rv;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
drop_frames_in (lsquic_stream_t *stream)
|
|
|
|
{
|
|
|
|
if (stream->data_in)
|
|
|
|
{
|
|
|
|
stream->data_in->di_if->di_destroy(stream->data_in);
|
|
|
|
/* To avoid checking whether `data_in` is set, just set to the error
|
|
|
|
* data-in stream. It does the right thing after incoming data is
|
|
|
|
* dropped.
|
|
|
|
*/
|
2020-03-12 12:41:53 +00:00
|
|
|
stream->data_in = lsquic_data_in_error_new();
|
2019-10-31 16:21:14 +00:00
|
|
|
stream->stream_flags &= ~STREAM_CACHED_FRAME;
|
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
|
|
|
|
maybe_elide_stream_frames (struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
if (!(stream->stream_flags & STREAM_FRAMES_ELIDED))
|
|
|
|
{
|
|
|
|
if (stream->n_unacked)
|
|
|
|
lsquic_send_ctl_elide_stream_frames(stream->conn_pub->send_ctl,
|
|
|
|
stream->id);
|
|
|
|
stream->stream_flags |= STREAM_FRAMES_ELIDED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
int
|
|
|
|
lsquic_stream_rst_in (lsquic_stream_t *stream, uint64_t offset,
|
2019-09-11 15:27:58 +00:00
|
|
|
uint64_t error_code)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-10-08 12:36:49 +00:00
|
|
|
struct lsquic_conn *lconn;
|
|
|
|
|
|
|
|
if ((stream->sm_bflags & SMBF_IETF)
|
|
|
|
&& (stream->stream_flags & STREAM_FIN_RECVD)
|
|
|
|
&& stream->sm_fin_off != offset)
|
|
|
|
{
|
|
|
|
lconn = stream->conn_pub->lconn;
|
|
|
|
lconn->cn_if->ci_abort_error(lconn, 0, TEC_FINAL_SIZE_ERROR,
|
|
|
|
"final size %"PRIu64" from RESET_STREAM frame (id: %"PRIu64") "
|
|
|
|
"does not match previous final size %"PRIu64, offset,
|
|
|
|
stream->id, stream->sm_fin_off);
|
|
|
|
return -1;
|
|
|
|
}
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
if (stream->stream_flags & STREAM_RST_RECVD)
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("ignore duplicate RST_STREAM frame");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
SM_HISTORY_APPEND(stream, SHE_RST_IN);
|
|
|
|
/* This flag must always be set, even if we are "ignoring" it: it is
|
|
|
|
* used by elision code.
|
|
|
|
*/
|
|
|
|
stream->stream_flags |= STREAM_RST_RECVD;
|
|
|
|
|
|
|
|
if (lsquic_sfcw_get_max_recv_off(&stream->fc) > offset)
|
|
|
|
{
|
2020-11-05 16:01:46 +00:00
|
|
|
LSQ_INFO("RST_STREAM invalid: its offset %"PRIu64" is "
|
2017-09-22 21:00:03 +00:00
|
|
|
"smaller than that of byte following the last byte we have seen: "
|
2020-11-05 16:01:46 +00:00
|
|
|
"%"PRIu64, offset,
|
2017-09-22 21:00:03 +00:00
|
|
|
lsquic_sfcw_get_max_recv_off(&stream->fc));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!lsquic_sfcw_set_max_recv_off(&stream->fc, offset))
|
|
|
|
{
|
2020-11-05 16:01:46 +00:00
|
|
|
LSQ_INFO("RST_STREAM invalid: its offset %"PRIu64
|
2019-09-11 15:27:58 +00:00
|
|
|
" violates flow control", offset);
|
2017-09-22 21:00:03 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-12-04 16:29:14 +00:00
|
|
|
if (stream->stream_if->on_reset
|
|
|
|
&& !(stream->stream_flags & STREAM_ONCLOSE_DONE))
|
|
|
|
{
|
|
|
|
if (stream->sm_bflags & SMBF_IETF)
|
|
|
|
{
|
|
|
|
if (!(stream->sm_dflags & SMDF_ONRESET0))
|
|
|
|
{
|
|
|
|
stream->stream_if->on_reset(stream, stream->st_ctx, 0);
|
|
|
|
stream->sm_dflags |= SMDF_ONRESET0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ((stream->sm_dflags & (SMDF_ONRESET0|SMDF_ONRESET1))
|
|
|
|
!= (SMDF_ONRESET0|SMDF_ONRESET1))
|
|
|
|
{
|
|
|
|
stream->stream_if->on_reset(stream, stream->st_ctx, 2);
|
|
|
|
stream->sm_dflags |= SMDF_ONRESET0|SMDF_ONRESET1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
/* Let user collect error: */
|
2018-04-09 13:39:38 +00:00
|
|
|
maybe_conn_to_tickable_if_readable(stream);
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
lsquic_sfcw_consume_rem(&stream->fc);
|
|
|
|
drop_frames_in(stream);
|
|
|
|
|
2020-09-08 15:43:03 +00:00
|
|
|
if (!(stream->sm_bflags & SMBF_IETF))
|
|
|
|
{
|
|
|
|
drop_buffered_data(stream);
|
|
|
|
maybe_elide_stream_frames(stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stream->sm_qflags & SMQF_WAIT_FIN_OFF)
|
|
|
|
{
|
|
|
|
stream->sm_qflags &= ~SMQF_WAIT_FIN_OFF;
|
|
|
|
LSQ_DEBUG("final offset is now known: %"PRIu64, offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(stream->stream_flags &
|
|
|
|
(STREAM_RST_SENT|STREAM_SS_SENT|STREAM_FIN_SENT))
|
|
|
|
&& !(stream->sm_bflags & SMBF_IETF)
|
2019-09-11 15:27:58 +00:00
|
|
|
&& !(stream->sm_qflags & SMQF_SEND_RST))
|
2021-03-03 14:41:42 +00:00
|
|
|
stream_reset(stream, 7 /* QUIC_RST_ACKNOWLEDGEMENT */, 0);
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
stream->stream_flags |= STREAM_RST_RECVD;
|
|
|
|
|
|
|
|
maybe_finish_stream(stream);
|
|
|
|
maybe_schedule_call_on_close(stream);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
void
|
|
|
|
lsquic_stream_stop_sending_in (struct lsquic_stream *stream,
|
|
|
|
uint64_t error_code)
|
|
|
|
{
|
|
|
|
if (stream->stream_flags & STREAM_SS_RECVD)
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("ignore duplicate STOP_SENDING frame");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-09-08 15:43:03 +00:00
|
|
|
SM_HISTORY_APPEND(stream, SHE_STOP_SENDIG_IN);
|
2019-09-11 15:27:58 +00:00
|
|
|
stream->stream_flags |= STREAM_SS_RECVD;
|
|
|
|
|
2020-12-04 16:29:14 +00:00
|
|
|
if (stream->stream_if->on_reset && !(stream->sm_dflags & SMDF_ONRESET1)
|
|
|
|
&& !(stream->stream_flags & STREAM_ONCLOSE_DONE))
|
|
|
|
{
|
|
|
|
stream->stream_if->on_reset(stream, stream->st_ctx, 1);
|
|
|
|
stream->sm_dflags |= SMDF_ONRESET1;
|
|
|
|
}
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
/* Let user collect error: */
|
2020-12-04 16:29:14 +00:00
|
|
|
maybe_conn_to_tickable_if_writeable(stream, 0);
|
2019-09-11 15:27:58 +00:00
|
|
|
|
|
|
|
lsquic_sfcw_consume_rem(&stream->fc);
|
|
|
|
drop_buffered_data(stream);
|
|
|
|
maybe_elide_stream_frames(stream);
|
|
|
|
|
|
|
|
if (!(stream->stream_flags & (STREAM_RST_SENT|STREAM_FIN_SENT))
|
|
|
|
&& !(stream->sm_qflags & SMQF_SEND_RST))
|
2021-03-03 14:41:42 +00:00
|
|
|
stream_reset(stream, 0, 0);
|
2019-09-11 15:27:58 +00:00
|
|
|
|
2024-02-07 17:32:13 +00:00
|
|
|
if (stream->sm_qflags & (SMQF_SEND_WUF | SMQF_SEND_BLOCKED \
|
|
|
|
| SMQF_SEND_STOP_SENDING))
|
|
|
|
{
|
|
|
|
stream->sm_qflags &= ~(SMQF_SEND_WUF | SMQF_SEND_BLOCKED \
|
|
|
|
| SMQF_SEND_STOP_SENDING);
|
|
|
|
if (!(stream->sm_qflags & SMQF_SENDING_FLAGS))
|
|
|
|
TAILQ_REMOVE(&stream->conn_pub->sending_streams, stream, next_send_stream);
|
|
|
|
}
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
maybe_finish_stream(stream);
|
|
|
|
maybe_schedule_call_on_close(stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
uint64_t
|
|
|
|
lsquic_stream_fc_recv_off_const (const struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
return lsquic_sfcw_get_fc_recv_off(&stream->fc);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
lsquic_stream_max_stream_data_sent (struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
assert(stream->sm_qflags & SMQF_SEND_MAX_STREAM_DATA);
|
|
|
|
stream->sm_qflags &= ~SMQF_SEND_MAX_STREAM_DATA;
|
|
|
|
if (!(stream->sm_qflags & SMQF_SENDING_FLAGS))
|
|
|
|
TAILQ_REMOVE(&stream->conn_pub->sending_streams, stream, next_send_stream);
|
|
|
|
stream->sm_last_recv_off = lsquic_sfcw_get_fc_recv_off(&stream->fc);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
uint64_t
|
|
|
|
lsquic_stream_fc_recv_off (lsquic_stream_t *stream)
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
assert(stream->sm_qflags & SMQF_SEND_WUF);
|
|
|
|
stream->sm_qflags &= ~SMQF_SEND_WUF;
|
|
|
|
if (!(stream->sm_qflags & SMQF_SENDING_FLAGS))
|
2017-09-22 21:00:03 +00:00
|
|
|
TAILQ_REMOVE(&stream->conn_pub->sending_streams, stream, next_send_stream);
|
2019-09-11 15:27:58 +00:00
|
|
|
return stream->sm_last_recv_off = lsquic_sfcw_get_fc_recv_off(&stream->fc);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
lsquic_stream_peer_blocked (struct lsquic_stream *stream, uint64_t peer_off)
|
|
|
|
{
|
|
|
|
uint64_t last_off;
|
|
|
|
|
|
|
|
if (stream->sm_last_recv_off)
|
|
|
|
last_off = stream->sm_last_recv_off;
|
|
|
|
else
|
|
|
|
/* This gets advertized in transport parameters */
|
|
|
|
last_off = lsquic_sfcw_get_max_recv_off(&stream->fc);
|
|
|
|
|
|
|
|
LSQ_DEBUG("Peer blocked at %"PRIu64", while the last MAX_STREAM_DATA "
|
|
|
|
"frame we sent advertized the limit of %"PRIu64, peer_off, last_off);
|
|
|
|
|
|
|
|
if (peer_off > last_off && !(stream->sm_qflags & SMQF_SEND_WUF))
|
|
|
|
{
|
|
|
|
if (!(stream->sm_qflags & SMQF_SENDING_FLAGS))
|
|
|
|
TAILQ_INSERT_TAIL(&stream->conn_pub->sending_streams, stream,
|
|
|
|
next_send_stream);
|
|
|
|
stream->sm_qflags |= SMQF_SEND_WUF;
|
|
|
|
LSQ_DEBUG("marked to send MAX_STREAM_DATA frame");
|
|
|
|
}
|
|
|
|
else if (stream->sm_qflags & SMQF_SEND_WUF)
|
|
|
|
LSQ_DEBUG("MAX_STREAM_DATA frame is already scheduled");
|
|
|
|
else if (stream->sm_last_recv_off)
|
|
|
|
LSQ_DEBUG("MAX_STREAM_DATA(%"PRIu64") has already been either "
|
|
|
|
"packetized or sent", stream->sm_last_recv_off);
|
|
|
|
else
|
|
|
|
LSQ_INFO("Peer should have receive transport param limit "
|
|
|
|
"of %"PRIu64"; odd.", last_off);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* GQUIC's BLOCKED frame does not have an offset */
|
|
|
|
void
|
|
|
|
lsquic_stream_peer_blocked_gquic (struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("Peer blocked: schedule another WINDOW_UPDATE frame");
|
|
|
|
if (!(stream->sm_qflags & SMQF_SEND_WUF))
|
|
|
|
{
|
|
|
|
if (!(stream->sm_qflags & SMQF_SENDING_FLAGS))
|
|
|
|
TAILQ_INSERT_TAIL(&stream->conn_pub->sending_streams, stream,
|
|
|
|
next_send_stream);
|
|
|
|
stream->sm_qflags |= SMQF_SEND_WUF;
|
|
|
|
LSQ_DEBUG("marked to send MAX_STREAM_DATA frame");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
LSQ_DEBUG("WINDOW_UPDATE frame is already scheduled");
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
lsquic_stream_blocked_frame_sent (lsquic_stream_t *stream)
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
assert(stream->sm_qflags & SMQF_SEND_BLOCKED);
|
2017-09-22 21:00:03 +00:00
|
|
|
SM_HISTORY_APPEND(stream, SHE_BLOCKED_OUT);
|
2019-09-11 15:27:58 +00:00
|
|
|
stream->sm_qflags &= ~SMQF_SEND_BLOCKED;
|
|
|
|
stream->stream_flags |= STREAM_BLOCKED_SENT;
|
|
|
|
if (!(stream->sm_qflags & SMQF_SENDING_FLAGS))
|
2017-09-22 21:00:03 +00:00
|
|
|
TAILQ_REMOVE(&stream->conn_pub->sending_streams, stream, next_send_stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
lsquic_stream_rst_frame_sent (lsquic_stream_t *stream)
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
assert(stream->sm_qflags & SMQF_SEND_RST);
|
2017-09-22 21:00:03 +00:00
|
|
|
SM_HISTORY_APPEND(stream, SHE_RST_OUT);
|
2019-09-11 15:27:58 +00:00
|
|
|
stream->sm_qflags &= ~SMQF_SEND_RST;
|
|
|
|
if (!(stream->sm_qflags & SMQF_SENDING_FLAGS))
|
2017-09-22 21:00:03 +00:00
|
|
|
TAILQ_REMOVE(&stream->conn_pub->sending_streams, stream, next_send_stream);
|
2022-11-18 02:34:57 +00:00
|
|
|
|
|
|
|
/* [RFC9000 QUIC] Section 19.4. RESET_Frames
|
|
|
|
* An endpoint uses a RESET_STREAM frame (type=0x04)
|
|
|
|
* to abruptly terminate the sending part of a stream.
|
|
|
|
*/
|
|
|
|
stream->stream_flags |= STREAM_RST_SENT|STREAM_U_WRITE_DONE;
|
2017-09-22 21:00:03 +00:00
|
|
|
maybe_finish_stream(stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static size_t
|
2019-09-11 15:27:58 +00:00
|
|
|
read_uh (struct lsquic_stream *stream,
|
|
|
|
size_t (*readf)(void *, const unsigned char *, size_t, int), void *ctx)
|
|
|
|
{
|
2021-04-12 13:52:42 +00:00
|
|
|
struct uncompressed_headers *uh = stream->uh;
|
|
|
|
struct http1x_headers *const h1h = uh->uh_hset;
|
2019-09-11 15:27:58 +00:00
|
|
|
size_t nread;
|
|
|
|
|
|
|
|
nread = readf(ctx, (unsigned char *) h1h->h1h_buf + h1h->h1h_off,
|
|
|
|
h1h->h1h_size - h1h->h1h_off,
|
|
|
|
(stream->stream_flags & STREAM_HEAD_IN_FIN) > 0);
|
|
|
|
h1h->h1h_off += nread;
|
2018-08-28 13:59:47 +00:00
|
|
|
if (h1h->h1h_off == h1h->h1h_size)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2021-04-12 13:52:42 +00:00
|
|
|
stream->uh = uh->uh_next;
|
|
|
|
LSQ_DEBUG("read all uncompressed headers from uh: %p, next uh: %p",
|
|
|
|
uh, stream->uh);
|
|
|
|
destroy_uh(uh, stream->conn_pub->enpub->enp_hsi_if);
|
2017-09-22 21:00:03 +00:00
|
|
|
if (stream->stream_flags & STREAM_HEAD_IN_FIN)
|
|
|
|
{
|
|
|
|
stream->stream_flags |= STREAM_FIN_REACHED;
|
|
|
|
SM_HISTORY_APPEND(stream, SHE_REACH_FIN);
|
|
|
|
}
|
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
return nread;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-01-06 05:47:12 +00:00
|
|
|
static void
|
|
|
|
verify_cl_on_fin (struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
struct lsquic_conn *lconn;
|
|
|
|
|
|
|
|
/* The rules in RFC7230, Section 3.3.2 are a bit too intricate. We take
|
|
|
|
* a simple approach and verify content-length only when there was any
|
|
|
|
* payload at all.
|
|
|
|
*/
|
|
|
|
if (stream->sm_data_in != 0 && stream->sm_cont_len != stream->sm_data_in)
|
|
|
|
{
|
|
|
|
lconn = stream->conn_pub->lconn;
|
2021-02-10 13:51:11 +00:00
|
|
|
lconn->cn_if->ci_abort_error(lconn, 1, HEC_MESSAGE_ERROR,
|
2020-01-06 05:47:12 +00:00
|
|
|
"number of bytes in DATA frames of stream %"PRIu64" is %llu, "
|
|
|
|
"while content-length specified of %llu", stream->id,
|
|
|
|
stream->sm_data_in, stream->sm_cont_len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static void
|
|
|
|
stream_consumed_bytes (struct lsquic_stream *stream)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_sfcw_set_read_off(&stream->fc, stream->read_offset);
|
2020-09-15 20:42:13 +00:00
|
|
|
if (lsquic_sfcw_fc_offsets_changed(&stream->fc)
|
|
|
|
/* We advance crypto streams' offsets (to control amount of
|
|
|
|
* buffering we allow), but do not send MAX_STREAM_DATA frames.
|
|
|
|
*/
|
|
|
|
&& !((stream->sm_bflags & (SMBF_IETF|SMBF_CRYPTO))
|
|
|
|
== (SMBF_IETF|SMBF_CRYPTO)))
|
2019-09-11 15:27:58 +00:00
|
|
|
{
|
|
|
|
if (!(stream->sm_qflags & SMQF_SENDING_FLAGS))
|
|
|
|
TAILQ_INSERT_TAIL(&stream->conn_pub->sending_streams, stream,
|
|
|
|
next_send_stream);
|
|
|
|
stream->sm_qflags |= SMQF_SEND_WUF;
|
|
|
|
maybe_conn_to_tickable_if_writeable(stream, 1);
|
|
|
|
}
|
|
|
|
}
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
|
2020-04-15 13:30:40 +00:00
|
|
|
static ssize_t
|
2019-09-11 15:27:58 +00:00
|
|
|
read_data_frames (struct lsquic_stream *stream, int do_filtering,
|
|
|
|
size_t (*readf)(void *, const unsigned char *, size_t, int), void *ctx)
|
|
|
|
{
|
|
|
|
struct data_frame *data_frame;
|
|
|
|
size_t nread, toread, total_nread;
|
|
|
|
int short_read, processed_frames;
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
processed_frames = 0;
|
2019-09-11 15:27:58 +00:00
|
|
|
total_nread = 0;
|
2017-09-22 21:00:03 +00:00
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
while ((data_frame = stream->data_in->di_if->di_get_frame(
|
|
|
|
stream->data_in, stream->read_offset)))
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
|
|
|
|
++processed_frames;
|
|
|
|
|
|
|
|
do
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
if (do_filtering && stream->sm_sfi)
|
|
|
|
toread = stream->sm_sfi->sfi_filter_df(stream, data_frame);
|
|
|
|
else
|
|
|
|
toread = data_frame->df_size - data_frame->df_read_off;
|
|
|
|
|
|
|
|
if (toread || data_frame->df_fin)
|
2018-08-28 13:59:47 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
nread = readf(ctx, data_frame->df_data + data_frame->df_read_off,
|
|
|
|
toread, data_frame->df_fin);
|
|
|
|
if (do_filtering && stream->sm_sfi)
|
|
|
|
stream->sm_sfi->sfi_decr_left(stream, nread);
|
|
|
|
data_frame->df_read_off += nread;
|
|
|
|
stream->read_offset += nread;
|
|
|
|
total_nread += nread;
|
|
|
|
short_read = nread < toread;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
short_read = 0;
|
|
|
|
|
|
|
|
if (data_frame->df_read_off == data_frame->df_size)
|
|
|
|
{
|
|
|
|
const int fin = data_frame->df_fin;
|
|
|
|
stream->data_in->di_if->di_frame_done(stream->data_in, data_frame);
|
|
|
|
data_frame = NULL;
|
2020-04-24 13:44:39 +00:00
|
|
|
if (0 != maybe_switch_data_in(stream))
|
|
|
|
return -1;
|
2019-09-11 15:27:58 +00:00
|
|
|
if (fin)
|
|
|
|
{
|
|
|
|
stream->stream_flags |= STREAM_FIN_REACHED;
|
2020-01-06 05:47:12 +00:00
|
|
|
if (stream->sm_bflags & SMBF_VERIFY_CL)
|
|
|
|
verify_cl_on_fin(stream);
|
2019-09-11 15:27:58 +00:00
|
|
|
goto end_while;
|
2018-08-28 13:59:47 +00:00
|
|
|
}
|
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
else if (short_read)
|
|
|
|
goto end_while;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
while (data_frame);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
end_while:
|
2017-09-22 21:00:03 +00:00
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
if (processed_frames)
|
|
|
|
stream_consumed_bytes(stream);
|
|
|
|
|
2020-04-15 13:30:40 +00:00
|
|
|
return total_nread;
|
2019-09-11 15:27:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
stream_readf (struct lsquic_stream *stream,
|
|
|
|
size_t (*readf)(void *, const unsigned char *, size_t, int), void *ctx)
|
|
|
|
{
|
2020-04-15 13:30:40 +00:00
|
|
|
size_t total_nread;
|
|
|
|
ssize_t nread;
|
2019-09-11 15:27:58 +00:00
|
|
|
|
|
|
|
total_nread = 0;
|
|
|
|
|
|
|
|
if ((stream->sm_bflags & (SMBF_USE_HEADERS|SMBF_IETF))
|
|
|
|
== (SMBF_USE_HEADERS|SMBF_IETF)
|
|
|
|
&& !(stream->stream_flags & STREAM_HAVE_UH)
|
|
|
|
&& !stream->uh)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
if (stream->sm_readable(stream))
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
if (stream->sm_hq_filter.hqfi_flags & HQFI_FLAG_ERROR)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_INFO("HQ filter hit an error: cannot read from stream");
|
|
|
|
errno = EBADMSG;
|
|
|
|
return -1;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
2022-08-02 19:43:40 +00:00
|
|
|
|
|
|
|
if (!stream->uh)
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("cannot read: headers not available");
|
|
|
|
errno = EBADMSG;
|
|
|
|
return -1;
|
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
errno = EWOULDBLOCK;
|
|
|
|
return -1;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
if (stream->uh)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
if (stream->uh->uh_flags & UH_H1H)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2020-04-15 13:30:40 +00:00
|
|
|
total_nread += read_uh(stream, readf, ctx);
|
2019-09-11 15:27:58 +00:00
|
|
|
if (stream->uh)
|
|
|
|
return total_nread;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LSQ_INFO("header set not claimed: cannot read from stream");
|
|
|
|
return -1;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
else if ((stream->sm_bflags & SMBF_USE_HEADERS)
|
|
|
|
&& !(stream->stream_flags & STREAM_HAVE_UH))
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("cannot read: headers not available");
|
|
|
|
errno = EWOULDBLOCK;
|
|
|
|
return -1;
|
|
|
|
}
|
2017-09-22 21:00:03 +00:00
|
|
|
|
2020-04-15 13:30:40 +00:00
|
|
|
nread = read_data_frames(stream, 1, readf, ctx);
|
|
|
|
if (nread < 0)
|
|
|
|
return nread;
|
|
|
|
total_nread += (size_t) nread;
|
2019-09-11 15:27:58 +00:00
|
|
|
|
2020-04-15 13:30:40 +00:00
|
|
|
LSQ_DEBUG("%s: read %zd bytes, read offset %"PRIu64", reached fin: %d",
|
|
|
|
__func__, total_nread, stream->read_offset,
|
|
|
|
!!(stream->stream_flags & STREAM_FIN_REACHED));
|
2019-09-11 15:27:58 +00:00
|
|
|
|
2020-04-15 13:30:40 +00:00
|
|
|
if (total_nread)
|
2017-09-22 21:00:03 +00:00
|
|
|
return total_nread;
|
2020-04-15 13:30:40 +00:00
|
|
|
else if (stream->stream_flags & STREAM_FIN_REACHED)
|
|
|
|
return 0;
|
2017-09-22 21:00:03 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
errno = EWOULDBLOCK;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
/* This function returns 0 when EOF is reached.
|
|
|
|
*/
|
|
|
|
ssize_t
|
|
|
|
lsquic_stream_readf (struct lsquic_stream *stream,
|
|
|
|
size_t (*readf)(void *, const unsigned char *, size_t, int), void *ctx)
|
|
|
|
{
|
2020-06-09 15:55:56 +00:00
|
|
|
ssize_t nread;
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
SM_HISTORY_APPEND(stream, SHE_USER_READ);
|
|
|
|
|
2020-12-23 14:06:05 +00:00
|
|
|
if (stream_is_read_reset(stream))
|
2019-09-11 15:27:58 +00:00
|
|
|
{
|
|
|
|
if (stream->stream_flags & STREAM_RST_RECVD)
|
|
|
|
stream->stream_flags |= STREAM_RST_READ;
|
|
|
|
errno = ECONNRESET;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (stream->stream_flags & STREAM_U_READ_DONE)
|
|
|
|
{
|
|
|
|
errno = EBADF;
|
|
|
|
return -1;
|
|
|
|
}
|
2019-11-20 16:43:10 +00:00
|
|
|
if (stream->stream_flags & STREAM_FIN_REACHED)
|
|
|
|
{
|
|
|
|
if (stream->sm_bflags & SMBF_USE_HEADERS)
|
|
|
|
{
|
|
|
|
if ((stream->stream_flags & STREAM_HAVE_UH) && !stream->uh)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
|
2020-06-09 15:55:56 +00:00
|
|
|
nread = stream_readf(stream, readf, ctx);
|
|
|
|
if (nread >= 0)
|
|
|
|
maybe_update_last_progress(stream);
|
|
|
|
|
|
|
|
return nread;
|
2019-09-11 15:27:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct readv_ctx
|
|
|
|
{
|
|
|
|
const struct iovec *iov;
|
|
|
|
const struct iovec *const end;
|
|
|
|
unsigned char *p;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static size_t
|
|
|
|
readv_f (void *ctx_p, const unsigned char *buf, size_t len, int fin)
|
|
|
|
{
|
|
|
|
struct readv_ctx *const ctx = ctx_p;
|
|
|
|
const unsigned char *const end = buf + len;
|
|
|
|
size_t ntocopy;
|
|
|
|
|
|
|
|
while (ctx->iov < ctx->end && buf < end)
|
|
|
|
{
|
|
|
|
ntocopy = (unsigned char *) ctx->iov->iov_base + ctx->iov->iov_len
|
|
|
|
- ctx->p;
|
|
|
|
if (ntocopy > (size_t) (end - buf))
|
|
|
|
ntocopy = end - buf;
|
|
|
|
memcpy(ctx->p, buf, ntocopy);
|
|
|
|
ctx->p += ntocopy;
|
|
|
|
buf += ntocopy;
|
|
|
|
if (ctx->p == (unsigned char *) ctx->iov->iov_base + ctx->iov->iov_len)
|
|
|
|
{
|
|
|
|
do
|
|
|
|
++ctx->iov;
|
|
|
|
while (ctx->iov < ctx->end && ctx->iov->iov_len == 0);
|
|
|
|
if (ctx->iov < ctx->end)
|
|
|
|
ctx->p = ctx->iov->iov_base;
|
|
|
|
else
|
2019-10-31 16:21:14 +00:00
|
|
|
break;
|
2019-09-11 15:27:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return len - (end - buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ssize_t
|
|
|
|
lsquic_stream_readv (struct lsquic_stream *stream, const struct iovec *iov,
|
|
|
|
int iovcnt)
|
|
|
|
{
|
|
|
|
struct readv_ctx ctx = { iov, iov + iovcnt, iov->iov_base, };
|
|
|
|
return lsquic_stream_readf(stream, readv_f, &ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
ssize_t
|
|
|
|
lsquic_stream_read (lsquic_stream_t *stream, void *buf, size_t len)
|
|
|
|
{
|
|
|
|
struct iovec iov = { .iov_base = buf, .iov_len = len, };
|
|
|
|
return lsquic_stream_readv(stream, &iov, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-09-08 15:43:03 +00:00
|
|
|
void
|
|
|
|
lsquic_stream_ss_frame_sent (struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
assert(stream->sm_qflags & SMQF_SEND_STOP_SENDING);
|
|
|
|
SM_HISTORY_APPEND(stream, SHE_STOP_SENDIG_OUT);
|
|
|
|
stream->sm_qflags &= ~SMQF_SEND_STOP_SENDING;
|
|
|
|
stream->stream_flags |= STREAM_SS_SENT;
|
|
|
|
if (!(stream->sm_qflags & SMQF_SENDING_FLAGS))
|
|
|
|
TAILQ_REMOVE(&stream->conn_pub->sending_streams, stream, next_send_stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
handle_early_read_shutdown_ietf (struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
if (!(stream->sm_qflags & SMQF_SENDING_FLAGS))
|
|
|
|
TAILQ_INSERT_TAIL(&stream->conn_pub->sending_streams, stream,
|
|
|
|
next_send_stream);
|
|
|
|
stream->sm_qflags |= SMQF_SEND_STOP_SENDING|SMQF_WAIT_FIN_OFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
handle_early_read_shutdown_gquic (struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
if (!(stream->stream_flags & STREAM_RST_SENT))
|
|
|
|
{
|
2021-03-03 14:41:42 +00:00
|
|
|
stream_reset(stream, 7 /* QUIC_STREAM_CANCELLED */, 0);
|
2020-09-08 15:43:03 +00:00
|
|
|
stream->sm_qflags |= SMQF_WAIT_FIN_OFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
handle_early_read_shutdown (struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
if (stream->sm_bflags & SMBF_IETF)
|
|
|
|
handle_early_read_shutdown_ietf(stream);
|
|
|
|
else
|
|
|
|
handle_early_read_shutdown_gquic(stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
static void
|
|
|
|
stream_shutdown_read (lsquic_stream_t *stream)
|
|
|
|
{
|
|
|
|
if (!(stream->stream_flags & STREAM_U_READ_DONE))
|
|
|
|
{
|
2020-09-08 15:43:03 +00:00
|
|
|
if (!(stream->stream_flags & STREAM_FIN_REACHED))
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("read shut down before reading FIN. (FIN received: %d)",
|
|
|
|
!!(stream->stream_flags & STREAM_FIN_RECVD));
|
|
|
|
SM_HISTORY_APPEND(stream, SHE_EARLY_READ_STOP);
|
|
|
|
if (!(stream->stream_flags & (STREAM_FIN_RECVD|STREAM_RST_RECVD)))
|
|
|
|
handle_early_read_shutdown(stream);
|
|
|
|
}
|
2017-09-22 21:00:03 +00:00
|
|
|
SM_HISTORY_APPEND(stream, SHE_SHUTDOWN_READ);
|
|
|
|
stream->stream_flags |= STREAM_U_READ_DONE;
|
2020-04-24 13:44:39 +00:00
|
|
|
stream->sm_readable = stream_readable_discard;
|
2017-09-22 21:00:03 +00:00
|
|
|
stream_wantread(stream, 0);
|
|
|
|
maybe_finish_stream(stream);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-23 16:00:10 +00:00
|
|
|
static int
|
|
|
|
stream_is_incoming_unidir (const struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
enum stream_id_type sit;
|
|
|
|
|
|
|
|
if (stream->sm_bflags & SMBF_IETF)
|
|
|
|
{
|
|
|
|
sit = stream->id & SIT_MASK;
|
|
|
|
if (stream->sm_bflags & SMBF_SERVER)
|
|
|
|
return sit == SIT_UNI_CLIENT;
|
|
|
|
else
|
|
|
|
return sit == SIT_UNI_SERVER;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
static void
|
|
|
|
stream_shutdown_write (lsquic_stream_t *stream)
|
|
|
|
{
|
|
|
|
if (stream->stream_flags & STREAM_U_WRITE_DONE)
|
|
|
|
return;
|
|
|
|
|
|
|
|
SM_HISTORY_APPEND(stream, SHE_SHUTDOWN_WRITE);
|
|
|
|
stream->stream_flags |= STREAM_U_WRITE_DONE;
|
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_wantwrite(stream, 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
|
|
|
/* Don't bother to check whether there is anything else to write if
|
|
|
|
* the flags indicate that nothing else should be written.
|
|
|
|
*/
|
2019-09-11 15:27:58 +00:00
|
|
|
if (!(stream->sm_bflags & SMBF_CRYPTO)
|
2021-01-08 16:38:46 +00:00
|
|
|
&& !((stream->stream_flags & (STREAM_FIN_SENT|STREAM_RST_SENT))
|
|
|
|
|| (stream->sm_qflags & SMQF_SEND_RST))
|
2019-09-23 16:00:10 +00:00
|
|
|
&& !stream_is_incoming_unidir(stream)
|
2020-09-08 15:43:03 +00:00
|
|
|
/* In gQUIC, receiving a RESET means "stop sending" */
|
2020-12-23 14:06:05 +00:00
|
|
|
&& !(!(stream->sm_bflags & SMBF_IETF)
|
2020-09-08 15:43:03 +00:00
|
|
|
&& (stream->stream_flags & STREAM_RST_RECVD)))
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-10-15 21:02:21 +00:00
|
|
|
if ((stream->sm_bflags & SMBF_USE_HEADERS)
|
|
|
|
&& !(stream->stream_flags & STREAM_HEADERS_SENT))
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("headers not sent, send a reset");
|
2021-03-03 14:41:42 +00:00
|
|
|
stream_reset(stream, 0, 1);
|
2019-10-15 21:02:21 +00:00
|
|
|
}
|
|
|
|
else if (stream->sm_n_buffered == 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
|
|
|
{
|
|
|
|
if (0 == lsquic_send_ctl_turn_on_fin(stream->conn_pub->send_ctl,
|
|
|
|
stream))
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("turned on FIN flag in the yet-unsent STREAM frame");
|
|
|
|
stream->stream_flags |= STREAM_FIN_SENT;
|
2024-03-12 21:46:16 +00:00
|
|
|
if (stream->sm_qflags & SMQF_WANT_FLUSH)
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("turned off SMQF_WANT_FLUSH flag as FIN flag is turned on.");
|
|
|
|
maybe_remove_from_write_q(stream, SMQF_WANT_FLUSH);
|
|
|
|
}
|
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
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("have to create a separate STREAM frame with FIN "
|
|
|
|
"flag in it");
|
|
|
|
(void) stream_flush_nocheck(stream);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
(void) stream_flush_nocheck(stream);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static void
|
|
|
|
maybe_stream_shutdown_write (struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
if (stream->sm_send_headers_state == SSHS_BEGIN)
|
|
|
|
stream_shutdown_write(stream);
|
|
|
|
else if (0 == (stream->stream_flags & STREAM_DELAYED_SW))
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("shutdown delayed");
|
|
|
|
SM_HISTORY_APPEND(stream, SHE_DELAY_SW);
|
|
|
|
stream->stream_flags |= STREAM_DELAYED_SW;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
int
|
|
|
|
lsquic_stream_shutdown (lsquic_stream_t *stream, int how)
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("shutdown; how: %d", how);
|
2017-09-22 21:00:03 +00:00
|
|
|
if (lsquic_stream_is_closed(stream))
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_INFO("Attempt to shut down a closed stream");
|
2017-09-22 21:00:03 +00:00
|
|
|
errno = EBADF;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
/* 0: read, 1: write: 2: read and write
|
|
|
|
*/
|
|
|
|
if (how < 0 || how > 2)
|
|
|
|
{
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (how)
|
2019-09-11 15:27:58 +00:00
|
|
|
maybe_stream_shutdown_write(stream);
|
2017-09-22 21:00:03 +00:00
|
|
|
if (how != 1)
|
|
|
|
stream_shutdown_read(stream);
|
|
|
|
|
|
|
|
maybe_finish_stream(stream);
|
|
|
|
maybe_schedule_call_on_close(stream);
|
2019-09-11 15:27:58 +00:00
|
|
|
if (how && !(stream->stream_flags & STREAM_DELAYED_SW))
|
2018-04-19 17:47:37 +00:00
|
|
|
maybe_conn_to_tickable_if_writeable(stream, 1);
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
lsquic_stream_shutdown_internal (lsquic_stream_t *stream)
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("internal shutdown");
|
2020-08-26 13:00:45 +00:00
|
|
|
stream->stream_flags |= STREAM_U_READ_DONE|STREAM_U_WRITE_DONE;
|
2020-09-08 15:43:03 +00:00
|
|
|
stream_wantwrite(stream, 0);
|
|
|
|
stream_wantread(stream, 0);
|
2019-09-11 15:27:58 +00:00
|
|
|
if (lsquic_stream_is_critical(stream))
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("add flag to force-finish special stream");
|
2017-09-22 21:00:03 +00:00
|
|
|
stream->stream_flags |= STREAM_FORCE_FINISH;
|
|
|
|
SM_HISTORY_APPEND(stream, SHE_FORCE_FINISH);
|
|
|
|
}
|
|
|
|
maybe_finish_stream(stream);
|
|
|
|
maybe_schedule_call_on_close(stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
fake_reset_unused_stream (lsquic_stream_t *stream)
|
|
|
|
{
|
|
|
|
stream->stream_flags |=
|
|
|
|
STREAM_RST_RECVD /* User will pick this up on read or write */
|
|
|
|
| STREAM_RST_SENT /* Don't send anything else on this stream */
|
|
|
|
;
|
|
|
|
|
|
|
|
/* Cancel all writes to the network scheduled for this stream: */
|
2019-09-11 15:27:58 +00:00
|
|
|
if (stream->sm_qflags & SMQF_SENDING_FLAGS)
|
2017-09-22 21:00:03 +00:00
|
|
|
TAILQ_REMOVE(&stream->conn_pub->sending_streams, stream,
|
|
|
|
next_send_stream);
|
2019-09-11 15:27:58 +00:00
|
|
|
stream->sm_qflags &= ~SMQF_SENDING_FLAGS;
|
2019-09-23 16:00:10 +00:00
|
|
|
drop_buffered_data(stream);
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("fake-reset stream%s",
|
|
|
|
stream_stalled(stream) ? " (stalled)" : "");
|
2017-09-22 21:00:03 +00:00
|
|
|
maybe_finish_stream(stream);
|
|
|
|
maybe_schedule_call_on_close(stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* This function should only be called for locally-initiated streams whose ID
|
|
|
|
* is larger than that received in GOAWAY frame. This may occur when GOAWAY
|
|
|
|
* frame sent by peer but we have not yet received it and created a stream.
|
|
|
|
* In this situation, we mark the stream as reset, so that user's on_read or
|
|
|
|
* on_write event callback picks up the error. That, in turn, should result
|
|
|
|
* in stream being closed.
|
|
|
|
*
|
|
|
|
* If we have received any data frames on this stream, this probably indicates
|
|
|
|
* a bug in peer code: it should not have sent GOAWAY frame with stream ID
|
|
|
|
* lower than this. However, we still try to handle it gracefully and peform
|
|
|
|
* a shutdown, as if the stream was not reset.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
lsquic_stream_received_goaway (lsquic_stream_t *stream)
|
|
|
|
{
|
|
|
|
SM_HISTORY_APPEND(stream, SHE_GOAWAY_IN);
|
2020-06-18 13:45:44 +00:00
|
|
|
|
|
|
|
if (stream->stream_flags & STREAM_GOAWAY_IN)
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("ignore duplicate GOAWAY");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
stream->stream_flags |= STREAM_GOAWAY_IN;
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
if (0 == stream->read_offset &&
|
|
|
|
stream->data_in->di_if->di_empty(stream->data_in))
|
|
|
|
fake_reset_unused_stream(stream); /* Normal condition */
|
|
|
|
else
|
|
|
|
{ /* This is odd, let's handle it the best we can: */
|
|
|
|
LSQ_WARN("GOAWAY received but have incoming data: shut down instead");
|
|
|
|
lsquic_stream_shutdown_internal(stream);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
uint64_t
|
|
|
|
lsquic_stream_read_offset (const lsquic_stream_t *stream)
|
|
|
|
{
|
|
|
|
return stream->read_offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
stream_wantread (lsquic_stream_t *stream, int is_want)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
const int old_val = !!(stream->sm_qflags & SMQF_WANT_READ);
|
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
|
|
|
const int new_val = !!is_want;
|
|
|
|
if (old_val != new_val)
|
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_val)
|
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 (!old_val)
|
|
|
|
TAILQ_INSERT_TAIL(&stream->conn_pub->read_streams, stream,
|
|
|
|
next_read_stream);
|
2019-09-11 15:27:58 +00:00
|
|
|
stream->sm_qflags |= SMQF_WANT_READ;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
stream->sm_qflags &= ~SMQF_WANT_READ;
|
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 (old_val)
|
|
|
|
TAILQ_REMOVE(&stream->conn_pub->read_streams, stream,
|
|
|
|
next_read_stream);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return old_val;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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
|
2019-09-11 15:27:58 +00:00
|
|
|
maybe_put_onto_write_q (lsquic_stream_t *stream, enum stream_q_flags flag)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
assert(SMQF_WRITE_Q_FLAGS & flag);
|
|
|
|
if (!(stream->sm_qflags & SMQF_WRITE_Q_FLAGS))
|
2023-01-26 16:19:06 +00:00
|
|
|
{
|
|
|
|
LSQ_DEBUG("put on write queue");
|
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_INSERT_TAIL(&stream->conn_pub->write_streams, stream,
|
|
|
|
next_write_stream);
|
2023-01-26 16:19:06 +00:00
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
stream->sm_qflags |= flag;
|
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
|
2019-09-11 15:27:58 +00:00
|
|
|
maybe_remove_from_write_q (lsquic_stream_t *stream, enum stream_q_flags flag)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
assert(SMQF_WRITE_Q_FLAGS & flag);
|
|
|
|
if (stream->sm_qflags & flag)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
stream->sm_qflags &= ~flag;
|
|
|
|
if (!(stream->sm_qflags & SMQF_WRITE_Q_FLAGS))
|
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_REMOVE(&stream->conn_pub->write_streams, stream,
|
|
|
|
next_write_stream);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2019-09-11 15:27:58 +00:00
|
|
|
stream_wantwrite (struct lsquic_stream *stream, int new_val)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
const int old_val = !!(stream->sm_qflags & SMQF_WANT_WRITE);
|
|
|
|
|
|
|
|
assert(0 == (new_val & ~1)); /* new_val is either 0 or 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
|
|
|
if (old_val != new_val)
|
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_val)
|
2019-09-11 15:27:58 +00:00
|
|
|
maybe_put_onto_write_q(stream, SMQF_WANT_WRITE);
|
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
|
|
|
else
|
2019-09-11 15:27:58 +00:00
|
|
|
maybe_remove_from_write_q(stream, SMQF_WANT_WRITE);
|
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
|
|
|
return old_val;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
lsquic_stream_wantread (lsquic_stream_t *stream, int is_want)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-10-11 12:24:24 +00:00
|
|
|
SM_HISTORY_APPEND(stream, SHE_WANTREAD_NO + !!is_want);
|
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_U_READ_DONE))
|
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 (is_want)
|
2018-04-09 13:39:38 +00:00
|
|
|
maybe_conn_to_tickable_if_readable(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
|
|
|
return stream_wantread(stream, is_want);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
errno = EBADF;
|
|
|
|
return -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
|
|
|
int
|
|
|
|
lsquic_stream_wantwrite (lsquic_stream_t *stream, int is_want)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
int old_val;
|
|
|
|
|
|
|
|
is_want = !!is_want;
|
|
|
|
|
2019-10-11 12:24:24 +00:00
|
|
|
SM_HISTORY_APPEND(stream, SHE_WANTWRITE_NO + is_want);
|
2019-09-11 15:27:58 +00:00
|
|
|
if (0 == (stream->stream_flags & STREAM_U_WRITE_DONE)
|
|
|
|
&& SSHS_BEGIN == stream->sm_send_headers_state)
|
2018-04-19 17:47:37 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
stream->sm_saved_want_write = is_want;
|
2018-04-19 17:47:37 +00:00
|
|
|
if (is_want)
|
|
|
|
maybe_conn_to_tickable_if_writeable(stream, 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
|
|
|
return stream_wantwrite(stream, is_want);
|
2018-04-19 17:47:37 +00:00
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
else if (SSHS_BEGIN != stream->sm_send_headers_state)
|
|
|
|
{
|
|
|
|
old_val = stream->sm_saved_want_write;
|
|
|
|
stream->sm_saved_want_write = is_want;
|
|
|
|
return old_val;
|
|
|
|
}
|
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
|
|
|
else
|
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
|
|
|
errno = EBADF;
|
|
|
|
return -1;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
struct progress
|
|
|
|
{
|
|
|
|
enum stream_flags s_flags;
|
|
|
|
enum stream_q_flags q_flags;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static struct progress
|
|
|
|
stream_progress (const struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
return (struct progress) {
|
|
|
|
.s_flags = stream->stream_flags
|
|
|
|
& (STREAM_U_WRITE_DONE|STREAM_U_READ_DONE),
|
|
|
|
.q_flags = stream->sm_qflags
|
|
|
|
& (SMQF_WANT_READ|SMQF_WANT_WRITE|SMQF_WANT_FLUSH|SMQF_SEND_RST),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
progress_eq (struct progress a, struct progress b)
|
|
|
|
{
|
|
|
|
return a.s_flags == b.s_flags && a.q_flags == b.q_flags;
|
|
|
|
}
|
2017-09-22 21:00:03 +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
|
|
|
stream_dispatch_read_events_loop (lsquic_stream_t *stream)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
unsigned no_progress_count, no_progress_limit;
|
2019-09-11 15:27:58 +00:00
|
|
|
struct progress progress;
|
2017-09-22 21:00:03 +00:00
|
|
|
uint64_t size;
|
|
|
|
|
|
|
|
no_progress_limit = stream->conn_pub->enpub->enp_settings.es_progress_check;
|
|
|
|
|
|
|
|
no_progress_count = 0;
|
2019-09-11 15:27:58 +00:00
|
|
|
while ((stream->sm_qflags & SMQF_WANT_READ)
|
2018-04-09 13:39:38 +00:00
|
|
|
&& lsquic_stream_readable(stream))
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
progress = stream_progress(stream);
|
2017-09-22 21:00:03 +00:00
|
|
|
size = stream->read_offset;
|
|
|
|
|
|
|
|
stream->stream_if->on_read(stream, stream->st_ctx);
|
|
|
|
|
|
|
|
if (no_progress_limit && size == stream->read_offset &&
|
2019-09-11 15:27:58 +00:00
|
|
|
progress_eq(progress, stream_progress(stream)))
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
++no_progress_count;
|
|
|
|
if (no_progress_count >= no_progress_limit)
|
|
|
|
{
|
|
|
|
LSQ_WARN("broke suspected infinite loop (%u callback%s without "
|
|
|
|
"progress) in user code reading from stream",
|
|
|
|
no_progress_count,
|
|
|
|
no_progress_count == 1 ? "" : "s");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
no_progress_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
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static void
|
|
|
|
stream_hblock_sent (struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
int want_write;
|
|
|
|
|
|
|
|
LSQ_DEBUG("header block has been sent: restore default behavior");
|
|
|
|
stream->sm_send_headers_state = SSHS_BEGIN;
|
2019-09-23 16:00:10 +00:00
|
|
|
stream->sm_write_avail = stream_write_avail_with_frames;
|
2019-09-11 15:27:58 +00:00
|
|
|
|
|
|
|
want_write = !!(stream->sm_qflags & SMQF_WANT_WRITE);
|
|
|
|
if (want_write != stream->sm_saved_want_write)
|
|
|
|
(void) lsquic_stream_wantwrite(stream, stream->sm_saved_want_write);
|
|
|
|
|
|
|
|
if (stream->stream_flags & STREAM_DELAYED_SW)
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("performing delayed shutdown write");
|
|
|
|
stream->stream_flags &= ~STREAM_DELAYED_SW;
|
|
|
|
stream_shutdown_write(stream);
|
|
|
|
maybe_schedule_call_on_close(stream);
|
|
|
|
maybe_finish_stream(stream);
|
|
|
|
maybe_conn_to_tickable_if_writeable(stream, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
on_write_header_wrapper (struct lsquic_stream *stream, lsquic_stream_ctx_t *h)
|
|
|
|
{
|
|
|
|
ssize_t nw;
|
|
|
|
|
|
|
|
nw = stream_write_buf(stream,
|
|
|
|
stream->sm_header_block + stream->sm_hblock_off,
|
|
|
|
stream->sm_hblock_sz - stream->sm_hblock_off);
|
|
|
|
if (nw > 0)
|
|
|
|
{
|
|
|
|
stream->sm_hblock_off += nw;
|
|
|
|
if (stream->sm_hblock_off == stream->sm_hblock_sz)
|
|
|
|
{
|
|
|
|
stream->stream_flags |= STREAM_HEADERS_SENT;
|
|
|
|
free(stream->sm_header_block);
|
|
|
|
stream->sm_header_block = NULL;
|
|
|
|
stream->sm_hblock_sz = 0;
|
|
|
|
stream_hblock_sent(stream);
|
|
|
|
LSQ_DEBUG("header block written out successfully");
|
|
|
|
/* TODO: if there was eos, do something else */
|
|
|
|
if (stream->sm_qflags & SMQF_WANT_WRITE)
|
|
|
|
stream->stream_if->on_write(stream, h);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("wrote %zd bytes more of header block; not done yet",
|
|
|
|
nw);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (nw < 0)
|
|
|
|
{
|
|
|
|
/* XXX What should happen if we hit an error? TODO */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
(*select_on_write (struct lsquic_stream *stream))(struct lsquic_stream *,
|
|
|
|
lsquic_stream_ctx_t *)
|
|
|
|
{
|
|
|
|
if (0 == (stream->stream_flags & STREAM_PUSHING)
|
|
|
|
&& SSHS_HBLOCK_SENDING != stream->sm_send_headers_state)
|
|
|
|
/* Common case */
|
|
|
|
return stream->stream_if->on_write;
|
|
|
|
else if (SSHS_HBLOCK_SENDING == stream->sm_send_headers_state)
|
|
|
|
return on_write_header_wrapper;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
assert(stream->stream_flags & STREAM_PUSHING);
|
|
|
|
if (stream_is_pushing_promise(stream))
|
|
|
|
return on_write_pp_wrapper;
|
|
|
|
else
|
|
|
|
return stream->stream_if->on_write;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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
|
|
|
|
stream_dispatch_write_events_loop (lsquic_stream_t *stream)
|
|
|
|
{
|
|
|
|
unsigned no_progress_count, no_progress_limit;
|
2019-09-11 15:27:58 +00:00
|
|
|
void (*on_write) (struct lsquic_stream *, lsquic_stream_ctx_t *);
|
|
|
|
struct progress progress;
|
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_progress_limit = stream->conn_pub->enpub->enp_settings.es_progress_check;
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
no_progress_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
|
|
|
stream->stream_flags |= STREAM_LAST_WRITE_OK;
|
2019-09-11 15:27:58 +00:00
|
|
|
while ((stream->sm_qflags & SMQF_WANT_WRITE)
|
2023-01-04 20:21:42 +00:00
|
|
|
&& (stream->stream_flags & STREAM_LAST_WRITE_OK)
|
|
|
|
&& !(stream->stream_flags & STREAM_ONCLOSE_DONE)
|
|
|
|
&& stream_writeable(stream))
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
progress = stream_progress(stream);
|
2017-09-22 21:00:03 +00:00
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
on_write = select_on_write(stream);
|
|
|
|
on_write(stream, stream->st_ctx);
|
2017-09-22 21:00:03 +00:00
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
if (no_progress_limit && progress_eq(progress, stream_progress(stream)))
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
++no_progress_count;
|
|
|
|
if (no_progress_count >= no_progress_limit)
|
|
|
|
{
|
|
|
|
LSQ_WARN("broke suspected infinite loop (%u callback%s without "
|
|
|
|
"progress) in user code writing to stream",
|
|
|
|
no_progress_count,
|
|
|
|
no_progress_count == 1 ? "" : "s");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
no_progress_count = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
stream_dispatch_read_events_once (lsquic_stream_t *stream)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
if ((stream->sm_qflags & SMQF_WANT_READ) && lsquic_stream_readable(stream))
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
stream->stream_if->on_read(stream, stream->st_ctx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
uint64_t
|
|
|
|
lsquic_stream_combined_send_off (const struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
size_t frames_sizes;
|
|
|
|
|
|
|
|
frames_sizes = active_hq_frame_sizes(stream);
|
|
|
|
return stream->tosend_off + stream->sm_n_buffered + frames_sizes;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
static void
|
|
|
|
maybe_mark_as_blocked (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
|
|
|
struct lsquic_conn_cap *cc;
|
2019-09-11 15:27:58 +00:00
|
|
|
uint64_t used;
|
2017-09-22 21:00:03 +00:00
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
used = lsquic_stream_combined_send_off(stream);
|
|
|
|
if (stream->max_send_off == used)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
if (stream->blocked_off < stream->max_send_off)
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
stream->blocked_off = used;
|
|
|
|
if (!(stream->sm_qflags & SMQF_SENDING_FLAGS))
|
2017-09-22 21:00:03 +00:00
|
|
|
TAILQ_INSERT_TAIL(&stream->conn_pub->sending_streams, stream,
|
|
|
|
next_send_stream);
|
2019-09-11 15:27:58 +00:00
|
|
|
stream->sm_qflags |= SMQF_SEND_BLOCKED;
|
2017-09-22 21:00:03 +00:00
|
|
|
LSQ_DEBUG("marked stream-blocked at stream offset "
|
|
|
|
"%"PRIu64, stream->blocked_off);
|
|
|
|
}
|
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
|
|
|
else
|
|
|
|
LSQ_DEBUG("stream is blocked, but BLOCKED frame for offset %"PRIu64
|
|
|
|
" has been, or is about to be, sent", stream->blocked_off);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
if ((stream->sm_bflags & SMBF_CONN_LIMITED)
|
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
|
|
|
&& (cc = &stream->conn_pub->conn_cap,
|
|
|
|
stream->sm_n_buffered == lsquic_conn_cap_avail(cc)))
|
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 (cc->cc_blocked < cc->cc_max)
|
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
|
|
|
cc->cc_blocked = cc->cc_max;
|
|
|
|
stream->conn_pub->lconn->cn_flags |= LSCONN_SEND_BLOCKED;
|
|
|
|
LSQ_DEBUG("marked connection-blocked at connection offset "
|
|
|
|
"%"PRIu64, cc->cc_max);
|
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
|
|
|
else
|
|
|
|
LSQ_DEBUG("stream has already been marked connection-blocked "
|
|
|
|
"at offset %"PRIu64, cc->cc_blocked);
|
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
|
|
|
|
|
|
|
void
|
|
|
|
lsquic_stream_dispatch_read_events (lsquic_stream_t *stream)
|
|
|
|
{
|
2021-01-27 15:36:25 +00:00
|
|
|
if (stream->sm_qflags & SMQF_WANT_READ)
|
|
|
|
{
|
|
|
|
if (stream->sm_bflags & SMBF_RW_ONCE)
|
|
|
|
stream_dispatch_read_events_once(stream);
|
|
|
|
else
|
|
|
|
stream_dispatch_read_events_loop(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
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
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
|
|
|
lsquic_stream_dispatch_write_events (lsquic_stream_t *stream)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
void (*on_write) (struct lsquic_stream *, lsquic_stream_ctx_t *);
|
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 progress;
|
2017-09-22 21:00:03 +00:00
|
|
|
uint64_t tosend_off;
|
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
|
|
|
unsigned short n_buffered;
|
2019-09-11 15:27:58 +00:00
|
|
|
enum stream_q_flags q_flags;
|
2017-09-22 21:00:03 +00:00
|
|
|
|
2023-12-26 15:23:22 +00:00
|
|
|
LSQ_DEBUG("dispatch_write_events, sm_qflags: %d. stream_flags: %d, sm_bflags: %d, "
|
|
|
|
"max_send_off: %" PRIu64 ", tosend_off: %" PRIu64 ", sm_n_buffered: %u",
|
|
|
|
stream->sm_qflags, stream->stream_flags, stream->sm_bflags,
|
|
|
|
stream->max_send_off, stream->tosend_off, stream->sm_n_buffered);
|
2023-01-26 16:19:06 +00:00
|
|
|
|
2022-04-27 19:40:59 +00:00
|
|
|
if (!(stream->sm_qflags & SMQF_WRITE_Q_FLAGS)
|
|
|
|
|| (stream->stream_flags & STREAM_FINISHED))
|
2021-01-27 15:36:25 +00:00
|
|
|
return;
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
q_flags = stream->sm_qflags & SMQF_WRITE_Q_FLAGS;
|
2017-09-22 21:00:03 +00:00
|
|
|
tosend_off = stream->tosend_off;
|
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
|
|
|
n_buffered = stream->sm_n_buffered;
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
if (stream->sm_qflags & SMQF_WANT_FLUSH)
|
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
|
|
|
(void) stream_flush(stream);
|
2017-09-22 21:00:03 +00:00
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
if (stream->sm_bflags & SMBF_RW_ONCE)
|
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
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
if ((stream->sm_qflags & SMQF_WANT_WRITE)
|
2023-01-04 20:21:42 +00:00
|
|
|
&& !(stream->stream_flags & STREAM_ONCLOSE_DONE)
|
2020-09-08 15:43:03 +00:00
|
|
|
&& stream_writeable(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
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
on_write = select_on_write(stream);
|
|
|
|
on_write(stream, stream->st_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
|
|
|
}
|
|
|
|
}
|
2017-09-22 21:00:03 +00:00
|
|
|
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
|
|
|
stream_dispatch_write_events_loop(stream);
|
2017-09-22 21:00:03 +00:00
|
|
|
|
2024-01-09 03:43:47 +00:00
|
|
|
if ((stream->sm_qflags & SMQF_SEND_BLOCKED) &&
|
|
|
|
(stream->sm_bflags & SMBF_IETF))
|
|
|
|
{
|
|
|
|
lsquic_sendctl_gen_stream_blocked_frame(stream->conn_pub->send_ctl, 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
|
|
|
/* Progress means either flags or offsets changed: */
|
2019-09-11 15:27:58 +00:00
|
|
|
progress = !((stream->sm_qflags & SMQF_WRITE_Q_FLAGS) == q_flags &&
|
2017-09-22 21:00:03 +00:00
|
|
|
stream->tosend_off == tosend_off &&
|
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->sm_n_buffered == n_buffered);
|
2017-09-22 21:00:03 +00:00
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
if (stream->sm_qflags & SMQF_WRITE_Q_FLAGS)
|
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 (progress)
|
2017-09-22 21:00:03 +00:00
|
|
|
{ /* Move the stream to the end of the list to ensure fairness. */
|
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_REMOVE(&stream->conn_pub->write_streams, stream,
|
|
|
|
next_write_stream);
|
|
|
|
TAILQ_INSERT_TAIL(&stream->conn_pub->write_streams, stream,
|
|
|
|
next_write_stream);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static size_t
|
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
|
|
|
inner_reader_empty_size (void *ctx)
|
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
|
|
|
return 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
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
static size_t
|
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
|
|
|
inner_reader_empty_read (void *ctx, void *buf, size_t count)
|
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
|
|
|
return 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
|
|
|
static int
|
|
|
|
stream_flush (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
|
|
|
struct lsquic_reader empty_reader;
|
|
|
|
ssize_t nw;
|
2017-09-22 21:00:03 +00:00
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
assert(stream->sm_qflags & SMQF_WANT_FLUSH);
|
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
|
|
|
assert(stream->sm_n_buffered > 0 ||
|
|
|
|
/* Flushing is also used to packetize standalone FIN: */
|
|
|
|
((stream->stream_flags & (STREAM_U_WRITE_DONE|STREAM_FIN_SENT))
|
|
|
|
== STREAM_U_WRITE_DONE));
|
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
|
|
|
empty_reader.lsqr_size = inner_reader_empty_size;
|
|
|
|
empty_reader.lsqr_read = inner_reader_empty_read;
|
|
|
|
empty_reader.lsqr_ctx = NULL; /* pro forma */
|
2020-09-08 15:43:03 +00:00
|
|
|
nw = stream_write_to_packets(stream, &empty_reader, 0, SWO_BUFFER);
|
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 (nw >= 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
|
|
|
assert(nw == 0); /* Empty reader: must have read zero bytes */
|
|
|
|
return 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
|
|
|
else
|
|
|
|
return -1;
|
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 int
|
|
|
|
stream_flush_nocheck (lsquic_stream_t *stream)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
size_t frames;
|
|
|
|
|
|
|
|
frames = active_hq_frame_sizes(stream);
|
|
|
|
stream->sm_flush_to = stream->tosend_off + stream->sm_n_buffered + frames;
|
|
|
|
stream->sm_flush_to_payload = stream->sm_payload + stream->sm_n_buffered;
|
|
|
|
maybe_put_onto_write_q(stream, SMQF_WANT_FLUSH);
|
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
|
|
|
LSQ_DEBUG("will flush up to offset %"PRIu64, stream->sm_flush_to);
|
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
|
|
|
return 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
|
|
|
int
|
|
|
|
lsquic_stream_flush (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
|
|
|
if (stream->stream_flags & STREAM_U_WRITE_DONE)
|
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
|
|
|
LSQ_DEBUG("cannot flush closed stream");
|
|
|
|
errno = EBADF;
|
|
|
|
return -1;
|
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 (0 == stream->sm_n_buffered)
|
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
|
|
|
LSQ_DEBUG("flushing 0 bytes: noop");
|
|
|
|
return 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
|
|
|
return stream_flush_nocheck(stream);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static size_t
|
|
|
|
stream_get_n_allowed (const struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
if (stream->sm_n_allocated)
|
|
|
|
return stream->sm_n_allocated;
|
|
|
|
else
|
|
|
|
return stream->conn_pub->path->np_pack_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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
|
|
|
/* The flush threshold is the maximum size of stream data that can be sent
|
|
|
|
* in a full packet.
|
|
|
|
*/
|
2018-05-18 19:29:07 +00:00
|
|
|
#ifdef NDEBUG
|
|
|
|
static
|
|
|
|
#endif
|
|
|
|
size_t
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_stream_flush_threshold (const struct lsquic_stream *stream,
|
|
|
|
unsigned data_sz)
|
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
|
|
|
enum packet_out_flags flags;
|
2019-04-01 15:41:55 +00:00
|
|
|
enum packno_bits bits;
|
2019-09-11 15:27:58 +00:00
|
|
|
size_t packet_header_sz, stream_header_sz, tag_len;
|
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
|
|
|
size_t threshold;
|
2017-09-22 21:00:03 +00:00
|
|
|
|
2020-10-07 13:41:26 +00:00
|
|
|
bits = lsquic_send_ctl_packno_bits(stream->conn_pub->send_ctl, PNS_APP);
|
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
|
|
|
flags = bits << POBIT_SHIFT;
|
|
|
|
if (!(stream->conn_pub->lconn->cn_flags & LSCONN_TCID0))
|
|
|
|
flags |= PO_CONN_ID;
|
2019-09-11 15:27:58 +00:00
|
|
|
if (stream_is_hsk(stream))
|
2018-08-15 19:06:31 +00:00
|
|
|
flags |= PO_LONGHEAD;
|
2017-09-22 21:00:03 +00:00
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
packet_header_sz = lsquic_po_header_length(stream->conn_pub->lconn, flags,
|
2023-03-14 17:29:13 +00:00
|
|
|
stream->conn_pub->path->np_dcid.len, HETY_SHORT);
|
2019-09-11 15:27:58 +00:00
|
|
|
stream_header_sz = stream->sm_frame_header_sz(stream, data_sz);
|
|
|
|
tag_len = stream->conn_pub->lconn->cn_esf_c->esf_tag_len;
|
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
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
threshold = stream_get_n_allowed(stream) - tag_len
|
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
|
|
|
- packet_header_sz - stream_header_sz;
|
|
|
|
return threshold;
|
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 COMMON_WRITE_CHECKS() do { \
|
2019-09-11 15:27:58 +00:00
|
|
|
if ((stream->sm_bflags & SMBF_USE_HEADERS) \
|
|
|
|
&& !(stream->stream_flags & STREAM_HEADERS_SENT)) \
|
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
|
|
|
{ \
|
2019-09-11 15:27:58 +00:00
|
|
|
if (SSHS_BEGIN != stream->sm_send_headers_state) \
|
|
|
|
{ \
|
|
|
|
LSQ_DEBUG("still sending headers: no writing allowed"); \
|
|
|
|
return 0; \
|
|
|
|
} \
|
|
|
|
else \
|
|
|
|
{ \
|
|
|
|
LSQ_INFO("Attempt to write to stream before sending HTTP " \
|
|
|
|
"headers"); \
|
|
|
|
errno = EILSEQ; \
|
|
|
|
return -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
|
|
|
} \
|
2020-12-23 14:06:05 +00:00
|
|
|
if (lsquic_stream_is_write_reset(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
|
|
|
{ \
|
|
|
|
LSQ_INFO("Attempt to write to stream after it had been reset"); \
|
|
|
|
errno = ECONNRESET; \
|
|
|
|
return -1; \
|
|
|
|
} \
|
|
|
|
if (stream->stream_flags & (STREAM_U_WRITE_DONE|STREAM_FIN_SENT)) \
|
|
|
|
{ \
|
2018-08-20 15:07:29 +00:00
|
|
|
LSQ_INFO("Attempt to write to stream after it was closed for " \
|
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
|
|
|
"writing"); \
|
|
|
|
errno = EBADF; \
|
|
|
|
return -1; \
|
|
|
|
} \
|
|
|
|
} while (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
|
|
|
struct frame_gen_ctx
|
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 *fgc_stream;
|
|
|
|
struct lsquic_reader *fgc_reader;
|
|
|
|
/* We keep our own count of how many bytes were read from reader because
|
|
|
|
* some readers are external. The external caller does not have to rely
|
|
|
|
* on our count, but it can.
|
|
|
|
*/
|
|
|
|
size_t fgc_nread_from_reader;
|
2019-09-11 15:27:58 +00:00
|
|
|
size_t (*fgc_size) (void *ctx);
|
|
|
|
int (*fgc_fin) (void *ctx);
|
|
|
|
gsf_read_f fgc_read;
|
2020-10-22 19:12:04 +00:00
|
|
|
size_t fgc_thresh;
|
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
|
|
|
|
|
|
|
|
|
|
|
static size_t
|
2019-09-11 15:27:58 +00:00
|
|
|
frame_std_gen_size (void *ctx)
|
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
|
|
|
struct frame_gen_ctx *fg_ctx = ctx;
|
|
|
|
size_t available, remaining;
|
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
|
|
|
/* Make sure we are not writing past available size: */
|
|
|
|
remaining = fg_ctx->fgc_reader->lsqr_size(fg_ctx->fgc_reader->lsqr_ctx);
|
2018-04-09 13:39:38 +00:00
|
|
|
available = lsquic_stream_write_avail(fg_ctx->fgc_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
|
|
|
if (available < remaining)
|
|
|
|
remaining = available;
|
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
|
|
|
return remaining + fg_ctx->fgc_stream->sm_n_buffered;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static size_t
|
|
|
|
stream_hq_frame_size (const struct stream_hq_frame *shf)
|
|
|
|
{
|
|
|
|
if (0 == (shf->shf_flags & (SHF_FIXED_SIZE|SHF_PHANTOM)))
|
|
|
|
return 1 + 1 + ((shf->shf_flags & SHF_TWO_BYTES) > 0);
|
|
|
|
else if ((shf->shf_flags & (SHF_FIXED_SIZE|SHF_PHANTOM)) == SHF_FIXED_SIZE)
|
|
|
|
return 1 + (1 << vint_val2bits(shf->shf_frame_size));
|
|
|
|
else
|
|
|
|
{
|
|
|
|
assert((shf->shf_flags & (SHF_FIXED_SIZE|SHF_PHANTOM))
|
|
|
|
== (SHF_FIXED_SIZE|SHF_PHANTOM));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static size_t
|
|
|
|
active_hq_frame_sizes (const struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
const struct stream_hq_frame *shf;
|
|
|
|
size_t size;
|
|
|
|
|
|
|
|
size = 0;
|
|
|
|
if ((stream->sm_bflags & (SMBF_IETF|SMBF_USE_HEADERS))
|
|
|
|
== (SMBF_IETF|SMBF_USE_HEADERS))
|
|
|
|
STAILQ_FOREACH(shf, &stream->sm_hq_frames, shf_next)
|
|
|
|
if (!(shf->shf_flags & SHF_WRITTEN))
|
|
|
|
size += stream_hq_frame_size(shf);
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static uint64_t
|
|
|
|
stream_hq_frame_end (const struct stream_hq_frame *shf)
|
|
|
|
{
|
|
|
|
if (shf->shf_flags & SHF_FIXED_SIZE)
|
|
|
|
return shf->shf_off + shf->shf_frame_size;
|
|
|
|
else if (shf->shf_flags & SHF_TWO_BYTES)
|
|
|
|
return shf->shf_off + ((1 << 14) - 1);
|
|
|
|
else
|
|
|
|
return shf->shf_off + ((1 << 6) - 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
|
|
|
static int
|
2019-09-11 15:27:58 +00:00
|
|
|
frame_in_stream (const struct lsquic_stream *stream,
|
|
|
|
const struct stream_hq_frame *shf)
|
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
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
return shf >= stream->sm_hq_frame_arr
|
|
|
|
&& shf < stream->sm_hq_frame_arr + sizeof(stream->sm_hq_frame_arr)
|
|
|
|
/ sizeof(stream->sm_hq_frame_arr[0])
|
|
|
|
;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2019-09-11 15:27:58 +00:00
|
|
|
stream_hq_frame_put (struct lsquic_stream *stream,
|
|
|
|
struct stream_hq_frame *shf)
|
|
|
|
{
|
2021-03-08 15:44:37 +00:00
|
|
|
/* In vast majority of cases, the frame to put is at the head: */
|
|
|
|
STAILQ_REMOVE(&stream->sm_hq_frames, shf, stream_hq_frame, shf_next);
|
2019-09-11 15:27:58 +00:00
|
|
|
if (frame_in_stream(stream, shf))
|
|
|
|
memset(shf, 0, sizeof(*shf));
|
|
|
|
else
|
|
|
|
lsquic_malo_put(shf);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
stream_hq_frame_close (struct lsquic_stream *stream,
|
|
|
|
struct stream_hq_frame *shf)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
unsigned bits;
|
|
|
|
|
|
|
|
LSQ_DEBUG("close HQ frame of type 0x%X at payload offset %"PRIu64
|
|
|
|
" (actual offset %"PRIu64")", shf->shf_frame_type,
|
|
|
|
stream->sm_payload, stream->tosend_off);
|
|
|
|
assert(shf->shf_flags & SHF_ACTIVE);
|
|
|
|
if (!(shf->shf_flags & SHF_FIXED_SIZE))
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
shf->shf_frame_ptr[0] = shf->shf_frame_type;
|
|
|
|
bits = (shf->shf_flags & SHF_TWO_BYTES) > 0;
|
|
|
|
vint_write(shf->shf_frame_ptr + 1, stream->sm_payload - shf->shf_off,
|
|
|
|
bits, 1 << bits);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
stream_hq_frame_put(stream, shf);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static size_t
|
2019-09-11 15:27:58 +00:00
|
|
|
frame_hq_gen_size (void *ctx)
|
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
|
|
|
struct frame_gen_ctx *fg_ctx = ctx;
|
2019-09-11 15:27:58 +00:00
|
|
|
struct lsquic_stream *const stream = fg_ctx->fgc_stream;
|
|
|
|
size_t available, remaining, frames;
|
|
|
|
const struct stream_hq_frame *shf;
|
2017-09-22 21:00:03 +00:00
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
frames = 0;
|
|
|
|
STAILQ_FOREACH(shf, &stream->sm_hq_frames, shf_next)
|
|
|
|
if (shf->shf_off >= stream->sm_payload)
|
|
|
|
frames += stream_hq_frame_size(shf);
|
|
|
|
|
|
|
|
/* Make sure we are not writing past available size: */
|
|
|
|
remaining = fg_ctx->fgc_reader->lsqr_size(fg_ctx->fgc_reader->lsqr_ctx);
|
|
|
|
available = lsquic_stream_write_avail(stream);
|
|
|
|
if (available < remaining)
|
|
|
|
remaining = available;
|
|
|
|
|
|
|
|
return remaining + stream->sm_n_buffered + frames;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
frame_std_gen_fin (void *ctx)
|
|
|
|
{
|
|
|
|
struct frame_gen_ctx *fg_ctx = ctx;
|
|
|
|
return !(fg_ctx->fgc_stream->sm_bflags & SMBF_CRYPTO)
|
|
|
|
&& (fg_ctx->fgc_stream->stream_flags & STREAM_U_WRITE_DONE)
|
|
|
|
&& 0 == fg_ctx->fgc_stream->sm_n_buffered
|
|
|
|
/* Do not use frame_std_gen_size() as it may chop the real size: */
|
|
|
|
&& 0 == fg_ctx->fgc_reader->lsqr_size(fg_ctx->fgc_reader->lsqr_ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
incr_conn_cap (struct lsquic_stream *stream, size_t incr)
|
|
|
|
{
|
|
|
|
if (stream->sm_bflags & SMBF_CONN_LIMITED)
|
|
|
|
{
|
|
|
|
stream->conn_pub->conn_cap.cc_sent += incr;
|
|
|
|
assert(stream->conn_pub->conn_cap.cc_sent
|
|
|
|
<= stream->conn_pub->conn_cap.cc_max);
|
2023-01-26 16:19:06 +00:00
|
|
|
LSQ_DEBUG("increase cc_sent by %zd to %"PRIu64, incr,
|
|
|
|
stream->conn_pub->conn_cap.cc_sent);
|
2019-09-11 15:27:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-03-12 12:41:53 +00:00
|
|
|
static void
|
2019-09-11 15:27:58 +00:00
|
|
|
incr_sm_payload (struct lsquic_stream *stream, size_t incr)
|
|
|
|
{
|
|
|
|
stream->sm_payload += incr;
|
|
|
|
stream->tosend_off += incr;
|
|
|
|
assert(stream->tosend_off <= stream->max_send_off);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-10-22 19:12:04 +00:00
|
|
|
static void
|
|
|
|
maybe_resize_threshold (struct frame_gen_ctx *fg_ctx)
|
|
|
|
{
|
|
|
|
struct lsquic_stream *stream = fg_ctx->fgc_stream;
|
|
|
|
size_t old;
|
|
|
|
|
|
|
|
if (fg_ctx->fgc_thresh)
|
|
|
|
{
|
|
|
|
old = fg_ctx->fgc_thresh;
|
|
|
|
fg_ctx->fgc_thresh
|
|
|
|
= lsquic_stream_flush_threshold(stream, fg_ctx->fgc_size(fg_ctx));
|
|
|
|
LSQ_DEBUG("changed threshold from %zd to %zd", old, fg_ctx->fgc_thresh);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static size_t
|
|
|
|
frame_std_gen_read (void *ctx, void *begin_buf, size_t len, int *fin)
|
|
|
|
{
|
|
|
|
struct frame_gen_ctx *fg_ctx = ctx;
|
|
|
|
unsigned char *p = begin_buf;
|
|
|
|
unsigned char *const end = p + len;
|
|
|
|
lsquic_stream_t *const stream = fg_ctx->fgc_stream;
|
|
|
|
size_t n_written, available, n_to_write;
|
|
|
|
|
|
|
|
if (stream->sm_n_buffered > 0)
|
|
|
|
{
|
|
|
|
if (len <= stream->sm_n_buffered)
|
|
|
|
{
|
|
|
|
memcpy(p, stream->sm_buf, len);
|
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
|
|
|
memmove(stream->sm_buf, stream->sm_buf + len,
|
|
|
|
stream->sm_n_buffered - len);
|
|
|
|
stream->sm_n_buffered -= len;
|
2019-09-11 15:27:58 +00:00
|
|
|
if (0 == stream->sm_n_buffered)
|
2020-10-22 19:12:04 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
maybe_resize_stream_buffer(stream);
|
2020-10-22 19:12:04 +00:00
|
|
|
maybe_resize_threshold(fg_ctx);
|
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
assert(stream->max_send_off >= stream->tosend_off + stream->sm_n_buffered);
|
|
|
|
incr_sm_payload(stream, len);
|
|
|
|
*fin = fg_ctx->fgc_fin(fg_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
|
|
|
return len;
|
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
|
|
|
memcpy(p, stream->sm_buf, stream->sm_n_buffered);
|
|
|
|
p += stream->sm_n_buffered;
|
|
|
|
stream->sm_n_buffered = 0;
|
2019-09-11 15:27:58 +00:00
|
|
|
maybe_resize_stream_buffer(stream);
|
2020-10-22 19:12:04 +00:00
|
|
|
maybe_resize_threshold(fg_ctx);
|
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
|
|
|
|
2018-04-09 13:39:38 +00:00
|
|
|
available = lsquic_stream_write_avail(fg_ctx->fgc_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
|
|
|
n_to_write = end - p;
|
|
|
|
if (n_to_write > available)
|
|
|
|
n_to_write = available;
|
|
|
|
n_written = fg_ctx->fgc_reader->lsqr_read(fg_ctx->fgc_reader->lsqr_ctx, p,
|
|
|
|
n_to_write);
|
|
|
|
p += n_written;
|
|
|
|
fg_ctx->fgc_nread_from_reader += n_written;
|
2019-09-11 15:27:58 +00:00
|
|
|
*fin = fg_ctx->fgc_fin(fg_ctx);
|
|
|
|
incr_sm_payload(stream, p - (const unsigned char *) begin_buf);
|
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
|
|
|
incr_conn_cap(stream, n_written);
|
|
|
|
return p - (const unsigned char *) begin_buf;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static struct stream_hq_frame *
|
|
|
|
find_hq_frame (const struct lsquic_stream *stream, uint64_t off)
|
|
|
|
{
|
|
|
|
struct stream_hq_frame *shf;
|
|
|
|
|
|
|
|
STAILQ_FOREACH(shf, &stream->sm_hq_frames, shf_next)
|
|
|
|
if (shf->shf_off <= off && stream_hq_frame_end(shf) > off)
|
|
|
|
return shf;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static struct stream_hq_frame *
|
|
|
|
find_cur_hq_frame (const struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
return find_hq_frame(stream, stream->sm_payload);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static struct stream_hq_frame *
|
|
|
|
open_hq_frame (struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
struct stream_hq_frame *shf;
|
|
|
|
|
|
|
|
for (shf = stream->sm_hq_frame_arr; shf < stream->sm_hq_frame_arr
|
|
|
|
+ sizeof(stream->sm_hq_frame_arr)
|
|
|
|
/ sizeof(stream->sm_hq_frame_arr[0]); ++shf)
|
|
|
|
if (!(shf->shf_flags & SHF_ACTIVE))
|
|
|
|
goto found;
|
|
|
|
|
|
|
|
shf = lsquic_malo_get(stream->conn_pub->mm->malo.stream_hq_frame);
|
|
|
|
if (!shf)
|
|
|
|
{
|
|
|
|
LSQ_WARN("cannot allocate HQ frame");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
memset(shf, 0, sizeof(*shf));
|
|
|
|
|
|
|
|
found:
|
|
|
|
STAILQ_INSERT_TAIL(&stream->sm_hq_frames, shf, shf_next);
|
|
|
|
shf->shf_flags = SHF_ACTIVE;
|
|
|
|
return shf;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static struct stream_hq_frame *
|
|
|
|
stream_activate_hq_frame (struct lsquic_stream *stream, uint64_t off,
|
|
|
|
enum hq_frame_type frame_type, enum shf_flags flags, size_t size)
|
|
|
|
{
|
|
|
|
struct stream_hq_frame *shf;
|
|
|
|
|
|
|
|
shf = open_hq_frame(stream);
|
|
|
|
if (!shf)
|
|
|
|
{
|
|
|
|
LSQ_WARN("could not open HQ frame");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
shf->shf_off = off;
|
|
|
|
shf->shf_flags |= flags;
|
|
|
|
shf->shf_frame_type = frame_type;
|
|
|
|
if (shf->shf_flags & SHF_FIXED_SIZE)
|
2019-10-08 12:36:49 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
shf->shf_frame_size = size;
|
2019-10-08 12:36:49 +00:00
|
|
|
LSQ_DEBUG("activated fixed-size HQ frame of type 0x%X at offset "
|
|
|
|
"%"PRIu64", size %zu", shf->shf_frame_type, shf->shf_off, size);
|
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
shf->shf_frame_ptr = NULL;
|
|
|
|
if (size >= (1 << 6))
|
|
|
|
shf->shf_flags |= SHF_TWO_BYTES;
|
2019-10-08 12:36:49 +00:00
|
|
|
LSQ_DEBUG("activated variable-size HQ frame of type 0x%X at offset "
|
|
|
|
"%"PRIu64, shf->shf_frame_type, shf->shf_off);
|
2019-09-11 15:27:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return shf;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-09-08 15:43:03 +00:00
|
|
|
struct hq_arr
|
|
|
|
{
|
|
|
|
unsigned char **p;
|
|
|
|
unsigned count;
|
|
|
|
unsigned max;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
save_hq_ptr (struct hq_arr *hq_arr, void *p)
|
|
|
|
{
|
|
|
|
if (hq_arr->count < hq_arr->max)
|
|
|
|
{
|
|
|
|
hq_arr->p[hq_arr->count++] = p;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static size_t
|
|
|
|
frame_hq_gen_read (void *ctx, void *begin_buf, size_t len, int *fin)
|
|
|
|
{
|
|
|
|
struct frame_gen_ctx *fg_ctx = ctx;
|
|
|
|
unsigned char *p = begin_buf;
|
|
|
|
unsigned char *const end = p + len;
|
|
|
|
struct lsquic_stream *const stream = fg_ctx->fgc_stream;
|
|
|
|
struct stream_hq_frame *shf;
|
|
|
|
size_t nw, frame_sz, avail, rem;
|
|
|
|
unsigned bits;
|
2020-10-07 13:41:26 +00:00
|
|
|
int new;
|
2019-09-11 15:27:58 +00:00
|
|
|
|
|
|
|
while (p < end)
|
|
|
|
{
|
|
|
|
shf = find_cur_hq_frame(stream);
|
2019-10-08 12:36:49 +00:00
|
|
|
if (shf)
|
2020-10-07 13:41:26 +00:00
|
|
|
{
|
|
|
|
new = 0;
|
2019-10-08 12:36:49 +00:00
|
|
|
LSQ_DEBUG("found current HQ frame of type 0x%X at offset %"PRIu64,
|
|
|
|
shf->shf_frame_type, shf->shf_off);
|
2020-10-07 13:41:26 +00:00
|
|
|
}
|
2019-10-08 12:36:49 +00:00
|
|
|
else
|
2019-09-11 15:27:58 +00:00
|
|
|
{
|
|
|
|
rem = frame_std_gen_size(ctx);
|
|
|
|
if (rem)
|
|
|
|
{
|
|
|
|
if (rem > ((1 << 14) - 1))
|
|
|
|
rem = (1 << 14) - 1;
|
|
|
|
shf = stream_activate_hq_frame(stream,
|
|
|
|
stream->sm_payload, HQFT_DATA, 0, rem);
|
2019-10-31 16:21:14 +00:00
|
|
|
if (shf)
|
2020-10-07 13:41:26 +00:00
|
|
|
{
|
|
|
|
new = 1;
|
2019-10-31 16:21:14 +00:00
|
|
|
goto insert;
|
2020-10-07 13:41:26 +00:00
|
|
|
}
|
2019-10-31 16:21:14 +00:00
|
|
|
else
|
2019-10-08 12:36:49 +00:00
|
|
|
{
|
2021-01-06 14:00:05 +00:00
|
|
|
stream->conn_pub->lconn->cn_if->ci_internal_error(
|
|
|
|
stream->conn_pub->lconn, "cannot activate HQ frame");
|
2019-10-08 12:36:49 +00:00
|
|
|
break;
|
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (shf->shf_off == stream->sm_payload
|
|
|
|
&& !(shf->shf_flags & SHF_WRITTEN))
|
|
|
|
{
|
2019-10-31 16:21:14 +00:00
|
|
|
insert:
|
2019-09-11 15:27:58 +00:00
|
|
|
frame_sz = stream_hq_frame_size(shf);
|
|
|
|
if (frame_sz > (uintptr_t) (end - p))
|
2019-10-08 12:36:49 +00:00
|
|
|
{
|
2020-10-07 13:41:26 +00:00
|
|
|
if (new)
|
|
|
|
stream_hq_frame_put(stream, shf);
|
2019-09-11 15:27:58 +00:00
|
|
|
break;
|
2019-10-08 12:36:49 +00:00
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("insert %zu-byte HQ frame of type 0x%X at payload "
|
|
|
|
"offset %"PRIu64" (actual offset %"PRIu64")", frame_sz,
|
|
|
|
shf->shf_frame_type, stream->sm_payload, stream->tosend_off);
|
|
|
|
if (0 == (shf->shf_flags & (SHF_FIXED_SIZE|SHF_PHANTOM)))
|
|
|
|
{
|
|
|
|
shf->shf_frame_ptr = p;
|
2020-09-08 15:43:03 +00:00
|
|
|
if (stream->sm_hq_arr && 0 != save_hq_ptr(stream->sm_hq_arr, p))
|
|
|
|
{
|
|
|
|
stream_hq_frame_put(stream, shf);
|
|
|
|
break;
|
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
memset(p, 0, frame_sz);
|
|
|
|
p += frame_sz;
|
|
|
|
}
|
|
|
|
else if ((shf->shf_flags & (SHF_FIXED_SIZE|SHF_PHANTOM))
|
|
|
|
== SHF_FIXED_SIZE)
|
|
|
|
{
|
|
|
|
*p++ = shf->shf_frame_type;
|
|
|
|
bits = vint_val2bits(shf->shf_frame_size);
|
|
|
|
vint_write(p, shf->shf_frame_size, bits, 1 << bits);
|
|
|
|
p += 1 << bits;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
assert((shf->shf_flags & (SHF_FIXED_SIZE|SHF_PHANTOM))
|
|
|
|
== (SHF_FIXED_SIZE|SHF_PHANTOM));
|
|
|
|
if (!(shf->shf_flags & SHF_CC_PAID))
|
|
|
|
{
|
|
|
|
incr_conn_cap(stream, frame_sz);
|
|
|
|
shf->shf_flags |= SHF_CC_PAID;
|
|
|
|
}
|
|
|
|
shf->shf_flags |= SHF_WRITTEN;
|
|
|
|
stream->tosend_off += frame_sz;
|
|
|
|
assert(stream->tosend_off <= stream->max_send_off);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-10-31 16:21:14 +00:00
|
|
|
avail = stream->sm_n_buffered + stream->sm_write_avail(stream);
|
2019-09-11 15:27:58 +00:00
|
|
|
len = stream_hq_frame_end(shf) - stream->sm_payload;
|
|
|
|
assert(len);
|
|
|
|
if (len > (unsigned) (end - p))
|
|
|
|
len = end - p;
|
|
|
|
if (len > avail)
|
|
|
|
len = avail;
|
|
|
|
if (!len)
|
|
|
|
break;
|
|
|
|
nw = frame_std_gen_read(ctx, p, len, fin);
|
|
|
|
p += nw;
|
|
|
|
if (nw < len)
|
|
|
|
break;
|
|
|
|
if (stream_hq_frame_end(shf) == stream->sm_payload)
|
|
|
|
stream_hq_frame_close(stream, shf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return p - (unsigned char *) begin_buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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
|
|
|
|
check_flush_threshold (lsquic_stream_t *stream)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
if ((stream->sm_qflags & SMQF_WANT_FLUSH) &&
|
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->tosend_off >= stream->sm_flush_to)
|
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
|
|
|
LSQ_DEBUG("flushed to or past required offset %"PRIu64,
|
|
|
|
stream->sm_flush_to);
|
2019-09-11 15:27:58 +00:00
|
|
|
maybe_remove_from_write_q(stream, SMQF_WANT_FLUSH);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-11-15 14:02:07 +00:00
|
|
|
#if LSQUIC_EXTRA_CHECKS
|
|
|
|
static void
|
|
|
|
verify_conn_cap (const struct lsquic_conn_public *conn_pub)
|
|
|
|
{
|
|
|
|
const struct lsquic_stream *stream;
|
|
|
|
struct lsquic_hash_elem *el;
|
|
|
|
unsigned n_buffered;
|
|
|
|
|
2020-06-09 15:55:56 +00:00
|
|
|
if (conn_pub->wtp_level > 1)
|
|
|
|
return;
|
|
|
|
|
2019-11-15 14:02:07 +00:00
|
|
|
if (!conn_pub->all_streams)
|
|
|
|
/* TODO: enable this check for unit tests as well */
|
|
|
|
return;
|
|
|
|
|
|
|
|
n_buffered = 0;
|
|
|
|
for (el = lsquic_hash_first(conn_pub->all_streams); el;
|
|
|
|
el = lsquic_hash_next(conn_pub->all_streams))
|
|
|
|
{
|
|
|
|
stream = lsquic_hashelem_getdata(el);
|
|
|
|
if (stream->sm_bflags & SMBF_CONN_LIMITED)
|
|
|
|
n_buffered += stream->sm_n_buffered;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(n_buffered + conn_pub->stream_frame_bytes
|
|
|
|
== conn_pub->conn_cap.cc_sent);
|
2020-06-09 15:55:56 +00:00
|
|
|
LSQ_DEBUG("%s: cc_sent: %"PRIu64, __func__, conn_pub->conn_cap.cc_sent);
|
2019-11-15 14:02:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static int
|
|
|
|
write_stream_frame (struct frame_gen_ctx *fg_ctx, const size_t size,
|
|
|
|
struct lsquic_packet_out *packet_out)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_stream_t *const stream = fg_ctx->fgc_stream;
|
|
|
|
const struct parse_funcs *const pf = stream->conn_pub->lconn->cn_pf;
|
|
|
|
struct lsquic_send_ctl *const send_ctl = stream->conn_pub->send_ctl;
|
|
|
|
unsigned off;
|
|
|
|
int len, s;
|
|
|
|
|
2019-11-15 14:02:07 +00:00
|
|
|
#if LSQUIC_CONN_STATS || LSQUIC_EXTRA_CHECKS
|
2019-09-11 15:27:58 +00:00
|
|
|
const uint64_t begin_off = stream->tosend_off;
|
|
|
|
#endif
|
|
|
|
off = packet_out->po_data_sz;
|
|
|
|
len = pf->pf_gen_stream_frame(
|
|
|
|
packet_out->po_data + packet_out->po_data_sz,
|
|
|
|
lsquic_packet_out_avail(packet_out), stream->id,
|
|
|
|
stream->tosend_off,
|
|
|
|
fg_ctx->fgc_fin(fg_ctx), size, fg_ctx->fgc_read, fg_ctx);
|
2021-09-30 15:23:56 +00:00
|
|
|
if (len <= 0)
|
2019-09-11 15:27:58 +00:00
|
|
|
return len;
|
|
|
|
|
|
|
|
#if LSQUIC_CONN_STATS
|
|
|
|
stream->conn_pub->conn_stats->out.stream_frames += 1;
|
|
|
|
stream->conn_pub->conn_stats->out.stream_data_sz
|
|
|
|
+= stream->tosend_off - begin_off;
|
|
|
|
#endif
|
|
|
|
EV_LOG_GENERATED_STREAM_FRAME(LSQUIC_LOG_CONN_ID, pf,
|
|
|
|
packet_out->po_data + packet_out->po_data_sz, len);
|
|
|
|
lsquic_send_ctl_incr_pack_sz(send_ctl, packet_out, len);
|
|
|
|
packet_out->po_frame_types |= 1 << QUIC_FRAME_STREAM;
|
|
|
|
if (0 == lsquic_packet_out_avail(packet_out))
|
|
|
|
packet_out->po_flags |= PO_STREAM_END;
|
|
|
|
s = lsquic_packet_out_add_stream(packet_out, stream->conn_pub->mm,
|
|
|
|
stream, QUIC_FRAME_STREAM, off, len);
|
|
|
|
if (s != 0)
|
|
|
|
{
|
|
|
|
LSQ_ERROR("adding stream to packet failed: %s", strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
2019-11-15 14:02:07 +00:00
|
|
|
#if LSQUIC_EXTRA_CHECKS
|
|
|
|
if (stream->sm_bflags & SMBF_CONN_LIMITED)
|
|
|
|
{
|
|
|
|
stream->conn_pub->stream_frame_bytes += stream->tosend_off - begin_off;
|
|
|
|
verify_conn_cap(stream->conn_pub);
|
|
|
|
}
|
|
|
|
#endif
|
2019-09-11 15:27:58 +00:00
|
|
|
|
|
|
|
check_flush_threshold(stream);
|
|
|
|
return len;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static enum swtp_status
|
|
|
|
stream_write_to_packet_hsk (struct frame_gen_ctx *fg_ctx, const size_t size)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
struct lsquic_stream *const stream = fg_ctx->fgc_stream;
|
|
|
|
struct lsquic_send_ctl *const send_ctl = stream->conn_pub->send_ctl;
|
|
|
|
struct lsquic_packet_out *packet_out;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
packet_out = lsquic_send_ctl_new_packet_out(send_ctl, 0, PNS_APP,
|
|
|
|
stream->conn_pub->path);
|
|
|
|
if (!packet_out)
|
|
|
|
return SWTP_STOP;
|
|
|
|
packet_out->po_header_type = stream->tosend_off == 0
|
|
|
|
? HETY_INITIAL : HETY_HANDSHAKE;
|
|
|
|
|
|
|
|
len = write_stream_frame(fg_ctx, size, packet_out);
|
2017-09-22 21:00:03 +00:00
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
if (len > 0)
|
|
|
|
{
|
|
|
|
packet_out->po_flags |= PO_HELLO;
|
|
|
|
lsquic_packet_out_zero_pad(packet_out);
|
|
|
|
lsquic_send_ctl_scheduled_one(send_ctl, packet_out);
|
|
|
|
return SWTP_OK;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return SWTP_ERROR;
|
|
|
|
}
|
2017-09-22 21:00:03 +00:00
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
|
|
|
|
static enum swtp_status
|
|
|
|
stream_write_to_packet_std (struct frame_gen_ctx *fg_ctx, const size_t size)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
struct lsquic_stream *const stream = fg_ctx->fgc_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
|
|
|
struct lsquic_send_ctl *const send_ctl = stream->conn_pub->send_ctl;
|
2019-09-11 15:27:58 +00:00
|
|
|
unsigned stream_header_sz, need_at_least;
|
|
|
|
struct lsquic_packet_out *packet_out;
|
2019-10-08 12:36:49 +00:00
|
|
|
struct lsquic_stream *headers_stream;
|
2019-09-11 15:27:58 +00:00
|
|
|
int len;
|
2017-09-22 21:00:03 +00:00
|
|
|
|
2019-10-08 12:36:49 +00:00
|
|
|
if ((stream->stream_flags & (STREAM_HEADERS_SENT|STREAM_HDRS_FLUSHED))
|
|
|
|
== STREAM_HEADERS_SENT)
|
2019-01-21 14:07:02 +00:00
|
|
|
{
|
2019-10-08 12:36:49 +00:00
|
|
|
if (stream->sm_bflags & SMBF_IETF)
|
2019-10-08 19:54:01 +00:00
|
|
|
{
|
|
|
|
if (stream->stream_flags & STREAM_ENCODER_DEP)
|
|
|
|
headers_stream = stream->conn_pub->u.ietf.qeh->qeh_enc_sm_out;
|
|
|
|
else
|
|
|
|
headers_stream = NULL;
|
|
|
|
}
|
2019-10-08 12:36:49 +00:00
|
|
|
else
|
|
|
|
headers_stream =
|
|
|
|
lsquic_headers_stream_get_stream(stream->conn_pub->u.gquic.hs);
|
|
|
|
if (headers_stream && lsquic_stream_has_data_to_flush(headers_stream))
|
2019-01-21 14:07:02 +00:00
|
|
|
{
|
2019-10-08 12:36:49 +00:00
|
|
|
LSQ_DEBUG("flushing headers stream before packetizing stream data");
|
2019-01-21 14:07:02 +00:00
|
|
|
(void) lsquic_stream_flush(headers_stream);
|
|
|
|
}
|
2019-10-08 12:36:49 +00:00
|
|
|
/* If there is nothing to flush, some other stream must have flushed it:
|
|
|
|
* this means our headers are flushed. Either way, only do this once.
|
|
|
|
*/
|
|
|
|
stream->stream_flags |= STREAM_HDRS_FLUSHED;
|
2019-01-21 14:07:02 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
stream_header_sz = stream->sm_frame_header_sz(stream, size);
|
2019-10-08 12:36:49 +00:00
|
|
|
need_at_least = stream_header_sz;
|
|
|
|
if ((stream->sm_bflags & (SMBF_IETF|SMBF_USE_HEADERS))
|
|
|
|
== (SMBF_IETF|SMBF_USE_HEADERS))
|
|
|
|
{
|
|
|
|
if (size > 0)
|
|
|
|
need_at_least += 3; /* Enough room for HTTP/3 frame */
|
|
|
|
}
|
|
|
|
else
|
|
|
|
need_at_least += size > 0;
|
2018-09-06 18:05:15 +00:00
|
|
|
get_packet:
|
2020-10-13 12:20:25 +00:00
|
|
|
packet_out = stream->sm_get_packet_for_stream(send_ctl,
|
2019-09-11 15:27:58 +00:00
|
|
|
need_at_least, stream->conn_pub->path, stream);
|
|
|
|
if (packet_out)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
len = write_stream_frame(fg_ctx, size, packet_out);
|
|
|
|
if (len > 0)
|
|
|
|
return SWTP_OK;
|
2021-09-30 15:23:56 +00:00
|
|
|
if (len == 0)
|
|
|
|
return SWTP_STOP;
|
2018-09-06 18:05:15 +00:00
|
|
|
if (-len > (int) need_at_least)
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("need more room (%d bytes) than initially calculated "
|
|
|
|
"%u bytes, will try again", -len, need_at_least);
|
|
|
|
need_at_least = -len;
|
|
|
|
goto get_packet;
|
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
return SWTP_ERROR;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
else
|
|
|
|
return SWTP_STOP;
|
|
|
|
}
|
2017-09-22 21:00:03 +00:00
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
|
2019-12-23 21:14:20 +00:00
|
|
|
/* Use for IETF crypto streams and gQUIC crypto stream for versions >= Q050. */
|
2019-09-11 15:27:58 +00:00
|
|
|
static enum swtp_status
|
|
|
|
stream_write_to_packet_crypto (struct frame_gen_ctx *fg_ctx, const size_t size)
|
|
|
|
{
|
|
|
|
struct lsquic_stream *const stream = fg_ctx->fgc_stream;
|
|
|
|
struct lsquic_send_ctl *const send_ctl = stream->conn_pub->send_ctl;
|
|
|
|
const struct parse_funcs *const pf = stream->conn_pub->lconn->cn_pf;
|
|
|
|
unsigned crypto_header_sz, need_at_least;
|
|
|
|
struct lsquic_packet_out *packet_out;
|
|
|
|
unsigned short off;
|
2019-12-23 21:14:20 +00:00
|
|
|
enum packnum_space pns;
|
2019-09-11 15:27:58 +00:00
|
|
|
int len, s;
|
|
|
|
|
2019-12-23 21:14:20 +00:00
|
|
|
if (stream->sm_bflags & SMBF_IETF)
|
|
|
|
pns = lsquic_enclev2pns[ crypto_level(stream) ];
|
|
|
|
else
|
2019-12-30 16:29:05 +00:00
|
|
|
pns = PNS_APP;
|
2019-12-23 21:14:20 +00:00
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
assert(size > 0);
|
|
|
|
crypto_header_sz = stream->sm_frame_header_sz(stream, size);
|
|
|
|
need_at_least = crypto_header_sz + 1;
|
|
|
|
|
|
|
|
packet_out = lsquic_send_ctl_get_packet_for_crypto(send_ctl,
|
|
|
|
need_at_least, pns, stream->conn_pub->path);
|
|
|
|
if (!packet_out)
|
|
|
|
return SWTP_STOP;
|
|
|
|
|
|
|
|
off = packet_out->po_data_sz;
|
|
|
|
len = pf->pf_gen_crypto_frame(packet_out->po_data + packet_out->po_data_sz,
|
2020-07-29 15:33:52 +00:00
|
|
|
lsquic_packet_out_avail(packet_out), 0, stream->tosend_off, 0,
|
|
|
|
size, frame_std_gen_read, fg_ctx);
|
2019-09-11 15:27:58 +00:00
|
|
|
if (len < 0)
|
|
|
|
return len;
|
|
|
|
|
|
|
|
EV_LOG_GENERATED_CRYPTO_FRAME(LSQUIC_LOG_CONN_ID, pf,
|
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
|
|
|
packet_out->po_data + packet_out->po_data_sz, len);
|
2018-02-26 21:01:16 +00:00
|
|
|
lsquic_send_ctl_incr_pack_sz(send_ctl, packet_out, len);
|
2019-09-11 15:27:58 +00:00
|
|
|
packet_out->po_frame_types |= 1 << QUIC_FRAME_CRYPTO;
|
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
|
|
|
s = lsquic_packet_out_add_stream(packet_out, stream->conn_pub->mm,
|
2019-09-11 15:27:58 +00:00
|
|
|
stream, QUIC_FRAME_CRYPTO, off, len);
|
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 (s != 0)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_WARN("adding crypto stream to packet failed: %s", strerror(errno));
|
|
|
|
return -1;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
packet_out->po_flags |= PO_HELLO;
|
2017-09-22 21:00:03 +00:00
|
|
|
|
2019-12-23 21:14:20 +00:00
|
|
|
if (!(stream->sm_bflags & SMBF_IETF))
|
|
|
|
{
|
|
|
|
const unsigned short before = packet_out->po_data_sz;
|
|
|
|
lsquic_packet_out_zero_pad(packet_out);
|
|
|
|
/* XXX: too hacky */
|
|
|
|
if (before < packet_out->po_data_sz)
|
|
|
|
send_ctl->sc_bytes_scheduled += packet_out->po_data_sz - before;
|
|
|
|
}
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
check_flush_threshold(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
|
|
|
return SWTP_OK;
|
2017-09-22 21:00:03 +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
|
|
|
abort_connection (struct lsquic_stream *stream)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
if (0 == (stream->sm_qflags & SMQF_SERVICE_FLAGS))
|
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_INSERT_TAIL(&stream->conn_pub->service_streams, stream,
|
|
|
|
next_service_stream);
|
2019-09-11 15:27:58 +00:00
|
|
|
stream->sm_qflags |= SMQF_ABORT_CONN;
|
2022-01-11 16:17:44 +00:00
|
|
|
LSQ_INFO("connection will be aborted");
|
2018-04-19 17:47:37 +00:00
|
|
|
maybe_conn_to_tickable(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
|
|
|
}
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static void
|
|
|
|
maybe_close_varsize_hq_frame (struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
struct stream_hq_frame *shf;
|
|
|
|
uint64_t size;
|
|
|
|
unsigned bits;
|
|
|
|
|
|
|
|
shf = find_cur_hq_frame(stream);
|
|
|
|
if (!shf)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (shf->shf_flags & SHF_FIXED_SIZE)
|
|
|
|
{
|
2019-10-08 12:36:49 +00:00
|
|
|
if (shf->shf_off + shf->shf_frame_size <= stream->sm_payload)
|
|
|
|
stream_hq_frame_put(stream, shf);
|
2019-09-11 15:27:58 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
bits = (shf->shf_flags & SHF_TWO_BYTES) > 0;
|
|
|
|
size = stream->sm_payload + stream->sm_n_buffered - shf->shf_off;
|
2019-11-12 19:29:32 +00:00
|
|
|
if (size <= VINT_MAX_B(bits) && shf->shf_frame_ptr)
|
2019-09-11 15:27:58 +00:00
|
|
|
{
|
|
|
|
if (0 == stream->sm_n_buffered)
|
|
|
|
LSQ_DEBUG("close HQ frame type 0x%X of size %"PRIu64,
|
|
|
|
shf->shf_frame_type, size);
|
|
|
|
else
|
|
|
|
LSQ_DEBUG("convert HQ frame type 0x%X of to fixed %"PRIu64,
|
|
|
|
shf->shf_frame_type, size);
|
|
|
|
shf->shf_frame_ptr[0] = shf->shf_frame_type;
|
|
|
|
vint_write(shf->shf_frame_ptr + 1, size, bits, 1 << bits);
|
|
|
|
if (0 == stream->sm_n_buffered)
|
|
|
|
stream_hq_frame_put(stream, shf);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
shf->shf_frame_size = size;
|
|
|
|
shf->shf_flags |= SHF_FIXED_SIZE;
|
|
|
|
}
|
|
|
|
}
|
2019-10-08 12:36:49 +00:00
|
|
|
else if (!shf->shf_frame_ptr)
|
2020-02-14 14:11:22 +00:00
|
|
|
LSQ_DEBUG("HQ frame of type 0x%X has not yet been written, not "
|
|
|
|
"closing", shf->shf_frame_type);
|
2019-09-11 15:27:58 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
assert(stream->sm_n_buffered);
|
2019-11-12 19:29:32 +00:00
|
|
|
LSQ_ERROR("cannot close frame of size %"PRIu64" on stream %"PRIu64
|
|
|
|
" -- too large", size, stream->id);
|
|
|
|
stream->conn_pub->lconn->cn_if->ci_internal_error(
|
|
|
|
stream->conn_pub->lconn, "HTTP/3 frame too large");
|
2019-09-11 15:27:58 +00:00
|
|
|
stream_hq_frame_put(stream, shf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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 ssize_t
|
|
|
|
stream_write_to_packets (lsquic_stream_t *stream, struct lsquic_reader *reader,
|
2020-09-08 15:43:03 +00:00
|
|
|
size_t thresh, enum stream_write_options swo)
|
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
|
|
|
{
|
|
|
|
size_t size;
|
|
|
|
ssize_t nw;
|
2018-04-19 17:47:37 +00:00
|
|
|
unsigned seen_ok;
|
2019-09-11 15:27:58 +00:00
|
|
|
int use_framing;
|
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
|
|
|
struct frame_gen_ctx fg_ctx = {
|
|
|
|
.fgc_stream = stream,
|
|
|
|
.fgc_reader = reader,
|
|
|
|
.fgc_nread_from_reader = 0,
|
2020-10-22 19:12:04 +00:00
|
|
|
.fgc_thresh = thresh,
|
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
|
|
|
|
2020-06-09 15:55:56 +00:00
|
|
|
#if LSQUIC_EXTRA_CHECKS
|
|
|
|
if (stream->conn_pub)
|
|
|
|
++stream->conn_pub->wtp_level;
|
|
|
|
#endif
|
2019-09-11 15:27:58 +00:00
|
|
|
use_framing = (stream->sm_bflags & (SMBF_IETF|SMBF_USE_HEADERS))
|
|
|
|
== (SMBF_IETF|SMBF_USE_HEADERS);
|
|
|
|
if (use_framing)
|
|
|
|
{
|
|
|
|
fg_ctx.fgc_size = frame_hq_gen_size;
|
|
|
|
fg_ctx.fgc_read = frame_hq_gen_read;
|
|
|
|
fg_ctx.fgc_fin = frame_std_gen_fin; /* This seems to work for either? XXX */
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fg_ctx.fgc_size = frame_std_gen_size;
|
|
|
|
fg_ctx.fgc_read = frame_std_gen_read;
|
|
|
|
fg_ctx.fgc_fin = frame_std_gen_fin;
|
|
|
|
}
|
|
|
|
|
2018-04-19 17:47:37 +00:00
|
|
|
seen_ok = 0;
|
2020-10-22 19:12:04 +00:00
|
|
|
while ((size = fg_ctx.fgc_size(&fg_ctx),
|
|
|
|
fg_ctx.fgc_thresh
|
|
|
|
? size >= fg_ctx.fgc_thresh : size > 0)
|
2019-09-11 15:27:58 +00:00
|
|
|
|| fg_ctx.fgc_fin(&fg_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
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
switch (stream->sm_write_to_packet(&fg_ctx, size))
|
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
|
|
|
{
|
|
|
|
case SWTP_OK:
|
2018-04-19 17:47:37 +00:00
|
|
|
if (!seen_ok++)
|
2020-06-09 15:55:56 +00:00
|
|
|
{
|
2018-04-19 17:47:37 +00:00
|
|
|
maybe_conn_to_tickable_if_writeable(stream, 0);
|
2020-06-09 15:55:56 +00:00
|
|
|
maybe_update_last_progress(stream);
|
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
if (fg_ctx.fgc_fin(&fg_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
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
if (use_framing && seen_ok)
|
|
|
|
maybe_close_varsize_hq_frame(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
|
|
|
stream->stream_flags |= STREAM_FIN_SENT;
|
2024-03-12 21:46:16 +00:00
|
|
|
if (stream->sm_qflags & SMQF_WANT_FLUSH)
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("turned off SMQF_WANT_FLUSH flag as FIN has been sent.");
|
|
|
|
maybe_remove_from_write_q(stream, SMQF_WANT_FLUSH);
|
|
|
|
}
|
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;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
case SWTP_STOP:
|
|
|
|
stream->stream_flags &= ~STREAM_LAST_WRITE_OK;
|
2019-09-11 15:27:58 +00:00
|
|
|
if (use_framing && seen_ok)
|
|
|
|
maybe_close_varsize_hq_frame(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
|
|
|
goto end;
|
|
|
|
default:
|
|
|
|
abort_connection(stream);
|
|
|
|
stream->stream_flags &= ~STREAM_LAST_WRITE_OK;
|
2020-06-09 15:55:56 +00:00
|
|
|
goto err;
|
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
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
if (use_framing && seen_ok)
|
|
|
|
maybe_close_varsize_hq_frame(stream);
|
|
|
|
|
2020-10-22 19:12:04 +00:00
|
|
|
if (fg_ctx.fgc_thresh && (swo & SWO_BUFFER))
|
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
|
|
|
{
|
2020-10-22 19:12:04 +00:00
|
|
|
assert(size < fg_ctx.fgc_thresh);
|
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
|
|
|
assert(size >= stream->sm_n_buffered);
|
|
|
|
size -= stream->sm_n_buffered;
|
|
|
|
if (size > 0)
|
|
|
|
{
|
|
|
|
nw = save_to_buffer(stream, reader, size);
|
|
|
|
if (nw < 0)
|
2020-06-09 15:55:56 +00:00
|
|
|
goto err;
|
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
|
|
|
fg_ctx.fgc_nread_from_reader += nw; /* Make this cleaner? */
|
|
|
|
}
|
|
|
|
}
|
2020-09-08 15:43:03 +00:00
|
|
|
#ifndef NDEBUG
|
|
|
|
else if (swo & SWO_BUFFER)
|
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
|
|
|
{
|
|
|
|
/* We count flushed data towards both stream and connection limits,
|
|
|
|
* so we should have been able to packetize all of it:
|
|
|
|
*/
|
|
|
|
assert(0 == stream->sm_n_buffered);
|
|
|
|
assert(size == 0);
|
|
|
|
}
|
2020-09-08 15:43:03 +00:00
|
|
|
#endif
|
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
|
|
|
maybe_mark_as_blocked(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
|
|
|
end:
|
2020-06-09 15:55:56 +00:00
|
|
|
#if LSQUIC_EXTRA_CHECKS
|
|
|
|
if (stream->conn_pub)
|
|
|
|
--stream->conn_pub->wtp_level;
|
|
|
|
#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
|
|
|
return fg_ctx.fgc_nread_from_reader;
|
2020-06-09 15:55:56 +00:00
|
|
|
|
|
|
|
err:
|
|
|
|
#if LSQUIC_EXTRA_CHECKS
|
|
|
|
if (stream->conn_pub)
|
|
|
|
--stream->conn_pub->wtp_level;
|
|
|
|
#endif
|
|
|
|
return -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
|
|
|
}
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
|
2021-02-03 16:05:50 +00:00
|
|
|
/* Perform an implicit flush when we hit connection or stream flow control
|
|
|
|
* limit while buffering data.
|
|
|
|
*
|
|
|
|
* This is to prevent a (theoretical) stall. Scenario 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
|
|
|
*
|
|
|
|
* Imagine a number of streams, all of which buffered some data. The buffered
|
|
|
|
* data is up to connection cap, which means no further writes are possible.
|
|
|
|
* None of them flushes, which means that data is not sent and connection
|
|
|
|
* WINDOW_UPDATE frame never arrives from peer. Stall.
|
2021-02-03 16:05:50 +00:00
|
|
|
*
|
|
|
|
* Scenario 2:
|
|
|
|
*
|
|
|
|
* Stream flow control window is smaller than the packetizing threshold. In
|
|
|
|
* this case, without a flush, the peer will never send a WINDOW_UPDATE. Stall.
|
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-19 17:47:37 +00:00
|
|
|
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
|
|
|
maybe_flush_stream (struct lsquic_stream *stream)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2021-02-03 16:05:50 +00:00
|
|
|
if (stream->sm_n_buffered > 0 && stream->sm_write_avail(stream) == 0)
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("out of flow control credits, flush %zu buffered bytes",
|
|
|
|
stream->sm_n_buffered + active_hq_frame_sizes(stream));
|
2018-04-19 17:47:37 +00:00
|
|
|
return stream_flush_nocheck(stream);
|
2021-02-03 16:05:50 +00:00
|
|
|
}
|
2018-04-19 17:47:37 +00:00
|
|
|
else
|
|
|
|
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
|
|
|
}
|
2017-09-22 21:00:03 +00:00
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static int
|
|
|
|
stream_hq_frame_extendable (const struct stream_hq_frame *shf, uint64_t cur_off,
|
|
|
|
unsigned len)
|
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
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
return (shf->shf_flags & (SHF_TWO_BYTES|SHF_FIXED_SIZE)) == 0
|
|
|
|
&& cur_off - shf->shf_off < (1 << 6)
|
|
|
|
&& cur_off - shf->shf_off + len >= (1 << 6)
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Update currently buffered HQ frame or create a new one, if possible.
|
|
|
|
* Return update length to be buffered. If a HQ frame cannot be
|
|
|
|
* buffered due to size, 0 is returned, thereby preventing both HQ frame
|
|
|
|
* creation and buffering.
|
|
|
|
*/
|
|
|
|
static size_t
|
|
|
|
update_buffered_hq_frames (struct lsquic_stream *stream, size_t len,
|
|
|
|
size_t avail)
|
|
|
|
{
|
|
|
|
struct stream_hq_frame *shf;
|
|
|
|
uint64_t cur_off, end;
|
|
|
|
size_t frame_sz;
|
2019-09-23 16:00:10 +00:00
|
|
|
unsigned extendable;
|
2020-06-03 04:13:30 +00:00
|
|
|
#if _MSC_VER
|
|
|
|
end = 0;
|
|
|
|
extendable = 0;
|
|
|
|
#endif
|
2019-09-11 15:27:58 +00:00
|
|
|
|
|
|
|
cur_off = stream->sm_payload + stream->sm_n_buffered;
|
|
|
|
STAILQ_FOREACH(shf, &stream->sm_hq_frames, shf_next)
|
|
|
|
if (shf->shf_off <= cur_off)
|
|
|
|
{
|
|
|
|
end = stream_hq_frame_end(shf);
|
|
|
|
extendable = stream_hq_frame_extendable(shf, cur_off, len);
|
|
|
|
if (cur_off < end + extendable)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (shf)
|
|
|
|
{
|
2019-09-23 16:00:10 +00:00
|
|
|
if (len > end + extendable - cur_off)
|
|
|
|
len = end + extendable - cur_off;
|
2019-09-11 15:27:58 +00:00
|
|
|
frame_sz = stream_hq_frame_size(shf);
|
|
|
|
}
|
2019-09-23 16:00:10 +00:00
|
|
|
else
|
2019-09-11 15:27:58 +00:00
|
|
|
{
|
2019-09-23 16:00:10 +00:00
|
|
|
assert(avail >= 3);
|
2019-09-11 15:27:58 +00:00
|
|
|
shf = stream_activate_hq_frame(stream, cur_off, HQFT_DATA, 0, len);
|
2020-01-06 05:47:12 +00:00
|
|
|
if (!shf)
|
|
|
|
return 0;
|
2019-09-11 15:27:58 +00:00
|
|
|
if (len > stream_hq_frame_end(shf) - cur_off)
|
|
|
|
len = stream_hq_frame_end(shf) - cur_off;
|
|
|
|
extendable = 0;
|
|
|
|
frame_sz = stream_hq_frame_size(shf);
|
|
|
|
if (avail < frame_sz)
|
|
|
|
return 0;
|
|
|
|
avail -= frame_sz;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(shf->shf_flags & SHF_CC_PAID))
|
|
|
|
{
|
|
|
|
incr_conn_cap(stream, frame_sz);
|
|
|
|
shf->shf_flags |= SHF_CC_PAID;
|
|
|
|
}
|
|
|
|
if (extendable)
|
|
|
|
{
|
|
|
|
shf->shf_flags |= SHF_TWO_BYTES;
|
|
|
|
incr_conn_cap(stream, 1);
|
|
|
|
avail -= 1;
|
|
|
|
if ((stream->sm_qflags & SMQF_WANT_FLUSH)
|
|
|
|
&& shf->shf_off <= stream->sm_payload
|
|
|
|
&& stream_hq_frame_end(shf) >= stream->sm_flush_to_payload)
|
|
|
|
stream->sm_flush_to += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len <= avail)
|
|
|
|
return len;
|
|
|
|
else
|
|
|
|
return avail;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
save_to_buffer (lsquic_stream_t *stream, struct lsquic_reader *reader,
|
|
|
|
size_t len)
|
|
|
|
{
|
|
|
|
size_t avail, n_written, n_allowed;
|
2017-09-22 21:00:03 +00:00
|
|
|
|
2019-09-23 16:00:10 +00:00
|
|
|
avail = lsquic_stream_write_avail(stream);
|
|
|
|
if (avail < len)
|
|
|
|
len = avail;
|
|
|
|
if (len == 0)
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("zero-byte write (avail: %zu)", avail);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
n_allowed = stream_get_n_allowed(stream);
|
|
|
|
assert(stream->sm_n_buffered + len <= n_allowed);
|
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->sm_buf)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
stream->sm_buf = malloc(n_allowed);
|
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->sm_buf)
|
|
|
|
return -1;
|
2019-09-11 15:27:58 +00:00
|
|
|
stream->sm_n_allocated = n_allowed;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
if ((stream->sm_bflags & (SMBF_IETF|SMBF_USE_HEADERS))
|
|
|
|
== (SMBF_IETF|SMBF_USE_HEADERS))
|
|
|
|
len = update_buffered_hq_frames(stream, len, avail);
|
|
|
|
|
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
|
|
|
n_written = reader->lsqr_read(reader->lsqr_ctx,
|
|
|
|
stream->sm_buf + stream->sm_n_buffered, len);
|
|
|
|
stream->sm_n_buffered += n_written;
|
2019-09-11 15:27:58 +00:00
|
|
|
assert(stream->max_send_off >= stream->tosend_off + stream->sm_n_buffered);
|
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
|
|
|
incr_conn_cap(stream, n_written);
|
|
|
|
LSQ_DEBUG("buffered %zd bytes; %hu bytes are now in buffer",
|
|
|
|
n_written, stream->sm_n_buffered);
|
2018-04-19 17:47:37 +00:00
|
|
|
if (0 != maybe_flush_stream(stream))
|
|
|
|
return -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
|
|
|
return n_written;
|
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 ssize_t
|
2020-09-08 15:43:03 +00:00
|
|
|
stream_write (lsquic_stream_t *stream, struct lsquic_reader *reader,
|
|
|
|
enum stream_write_options swo)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
const struct stream_hq_frame *shf;
|
|
|
|
size_t thresh, len, frames, total_len, n_allowed, nwritten;
|
|
|
|
ssize_t nw;
|
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
|
|
|
|
|
|
|
len = reader->lsqr_size(reader->lsqr_ctx);
|
2019-09-11 15:27:58 +00:00
|
|
|
if (len == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
frames = 0;
|
|
|
|
if ((stream->sm_bflags & (SMBF_IETF|SMBF_USE_HEADERS))
|
|
|
|
== (SMBF_IETF|SMBF_USE_HEADERS))
|
|
|
|
STAILQ_FOREACH(shf, &stream->sm_hq_frames, shf_next)
|
|
|
|
if (shf->shf_off >= stream->sm_payload)
|
|
|
|
frames += stream_hq_frame_size(shf);
|
|
|
|
total_len = len + frames + stream->sm_n_buffered;
|
|
|
|
thresh = lsquic_stream_flush_threshold(stream, total_len);
|
|
|
|
n_allowed = stream_get_n_allowed(stream);
|
|
|
|
if (total_len <= n_allowed && total_len < thresh)
|
|
|
|
{
|
2020-09-08 15:43:03 +00:00
|
|
|
if (!(swo & SWO_BUFFER))
|
|
|
|
return 0;
|
2019-09-11 15:27:58 +00:00
|
|
|
nwritten = 0;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
nw = save_to_buffer(stream, reader, len - nwritten);
|
|
|
|
if (nw > 0)
|
|
|
|
nwritten += (size_t) nw;
|
|
|
|
else if (nw == 0)
|
|
|
|
break;
|
|
|
|
else
|
|
|
|
return nw;
|
|
|
|
}
|
|
|
|
while (nwritten < len
|
|
|
|
&& stream->sm_n_buffered < stream->sm_n_allocated);
|
|
|
|
}
|
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
|
|
|
else
|
2023-12-20 03:48:06 +00:00
|
|
|
nwritten = stream_write_to_packets(stream, reader, thresh, swo);
|
|
|
|
if ((stream->sm_qflags & SMQF_SEND_BLOCKED) &&
|
|
|
|
(stream->sm_bflags & SMBF_IETF))
|
|
|
|
{
|
|
|
|
lsquic_sendctl_gen_stream_blocked_frame(stream->conn_pub->send_ctl, stream);
|
|
|
|
}
|
|
|
|
return nwritten;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ssize_t
|
|
|
|
lsquic_stream_write (lsquic_stream_t *stream, const void *buf, size_t len)
|
|
|
|
{
|
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
|
|
|
struct iovec iov = { .iov_base = (void *) buf, .iov_len = len, };
|
|
|
|
return lsquic_stream_writev(stream, &iov, 1);
|
|
|
|
}
|
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
|
|
|
struct inner_reader_iovec {
|
|
|
|
const struct iovec *iov;
|
2018-03-12 22:25:01 +00:00
|
|
|
const struct iovec *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
|
|
|
unsigned cur_iovec_off;
|
|
|
|
};
|
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 size_t
|
|
|
|
inner_reader_iovec_read (void *ctx, void *buf, size_t count)
|
|
|
|
{
|
|
|
|
struct inner_reader_iovec *const iro = ctx;
|
|
|
|
unsigned char *p = buf;
|
|
|
|
unsigned char *const end = p + count;
|
|
|
|
unsigned n_tocopy;
|
|
|
|
|
|
|
|
while (iro->iov < iro->end && p < end)
|
|
|
|
{
|
|
|
|
n_tocopy = iro->iov->iov_len - iro->cur_iovec_off;
|
|
|
|
if (n_tocopy > (unsigned) (end - p))
|
|
|
|
n_tocopy = end - p;
|
|
|
|
memcpy(p, (unsigned char *) iro->iov->iov_base + iro->cur_iovec_off,
|
|
|
|
n_tocopy);
|
|
|
|
p += n_tocopy;
|
|
|
|
iro->cur_iovec_off += n_tocopy;
|
|
|
|
if (iro->iov->iov_len == iro->cur_iovec_off)
|
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
|
|
|
++iro->iov;
|
|
|
|
iro->cur_iovec_off = 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
|
|
|
return p + count - end;
|
|
|
|
}
|
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 size_t
|
|
|
|
inner_reader_iovec_size (void *ctx)
|
|
|
|
{
|
|
|
|
struct inner_reader_iovec *const iro = ctx;
|
|
|
|
const struct iovec *iov;
|
|
|
|
size_t size;
|
|
|
|
|
|
|
|
size = 0;
|
|
|
|
for (iov = iro->iov; iov < iro->end; ++iov)
|
|
|
|
size += iov->iov_len;
|
|
|
|
|
|
|
|
return size - iro->cur_iovec_off;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ssize_t
|
|
|
|
lsquic_stream_writev (lsquic_stream_t *stream, const struct iovec *iov,
|
|
|
|
int iovcnt)
|
|
|
|
{
|
|
|
|
COMMON_WRITE_CHECKS();
|
|
|
|
SM_HISTORY_APPEND(stream, SHE_USER_WRITE_DATA);
|
|
|
|
|
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
|
|
|
struct inner_reader_iovec iro = {
|
|
|
|
.iov = iov,
|
|
|
|
.end = iov + iovcnt,
|
|
|
|
.cur_iovec_off = 0,
|
|
|
|
};
|
|
|
|
struct lsquic_reader reader = {
|
|
|
|
.lsqr_read = inner_reader_iovec_read,
|
|
|
|
.lsqr_size = inner_reader_iovec_size,
|
|
|
|
.lsqr_ctx = &iro,
|
|
|
|
};
|
2017-09-22 21:00:03 +00:00
|
|
|
|
2020-09-08 15:43:03 +00:00
|
|
|
return stream_write(stream, &reader, SWO_BUFFER);
|
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
|
|
|
ssize_t
|
|
|
|
lsquic_stream_writef (lsquic_stream_t *stream, struct lsquic_reader *reader)
|
|
|
|
{
|
|
|
|
COMMON_WRITE_CHECKS();
|
|
|
|
SM_HISTORY_APPEND(stream, SHE_USER_WRITE_DATA);
|
2020-09-08 15:43:03 +00:00
|
|
|
return stream_write(stream, reader, SWO_BUFFER);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Configuration for lsquic_stream_pwritev: */
|
|
|
|
#ifndef LSQUIC_PWRITEV_DEF_IOVECS
|
|
|
|
#define LSQUIC_PWRITEV_DEF_IOVECS 16
|
|
|
|
#endif
|
|
|
|
/* This is an overkill, this limit should only be reached during testing: */
|
|
|
|
#ifndef LSQUIC_PWRITEV_DEF_FRAMES
|
|
|
|
#define LSQUIC_PWRITEV_DEF_FRAMES (LSQUIC_PWRITEV_DEF_IOVECS * 2)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef NDEBUG
|
|
|
|
#define PWRITEV_IOVECS LSQUIC_PWRITEV_DEF_IOVECS
|
|
|
|
#define PWRITEV_FRAMES LSQUIC_PWRITEV_DEF_FRAMES
|
|
|
|
#else
|
2020-09-08 18:16:25 +00:00
|
|
|
#if _MSC_VER
|
|
|
|
#define MALLOC_PWRITEV 1
|
|
|
|
#else
|
|
|
|
#define MALLOC_PWRITEV 0
|
|
|
|
#endif
|
2020-09-08 15:43:03 +00:00
|
|
|
static unsigned
|
|
|
|
PWRITEV_IOVECS = LSQUIC_PWRITEV_DEF_IOVECS,
|
|
|
|
PWRITEV_FRAMES = LSQUIC_PWRITEV_DEF_FRAMES;
|
|
|
|
|
|
|
|
void
|
|
|
|
lsquic_stream_set_pwritev_params (unsigned iovecs, unsigned frames)
|
|
|
|
{
|
|
|
|
PWRITEV_IOVECS = iovecs;
|
|
|
|
PWRITEV_FRAMES = frames;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
struct pwritev_ctx
|
|
|
|
{
|
|
|
|
struct iovec *iov;
|
|
|
|
const struct hq_arr *hq_arr;
|
|
|
|
size_t total_bytes;
|
|
|
|
size_t n_to_write;
|
|
|
|
unsigned n_iovecs, max_iovecs;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static size_t
|
|
|
|
pwritev_size (void *lsqr_ctx)
|
|
|
|
{
|
|
|
|
struct pwritev_ctx *const ctx = lsqr_ctx;
|
|
|
|
|
|
|
|
if (ctx->n_iovecs < ctx->max_iovecs
|
|
|
|
&& ctx->hq_arr->count < ctx->hq_arr->max)
|
|
|
|
return ctx->n_to_write - ctx->total_bytes;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static size_t
|
|
|
|
pwritev_read (void *lsqr_ctx, void *buf, size_t count)
|
|
|
|
{
|
|
|
|
struct pwritev_ctx *const ctx = lsqr_ctx;
|
|
|
|
|
|
|
|
assert(ctx->n_iovecs < ctx->max_iovecs);
|
|
|
|
ctx->iov[ctx->n_iovecs].iov_base = buf;
|
|
|
|
ctx->iov[ctx->n_iovecs].iov_len = count;
|
|
|
|
++ctx->n_iovecs;
|
|
|
|
ctx->total_bytes += count;
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* pwritev works as follows: allocate packets via lsquic_stream_writef() call
|
|
|
|
* and record pointers and sizes into an iovec array. Then issue a single call
|
|
|
|
* to user-supplied preadv() to populate all packets in one shot.
|
|
|
|
*
|
|
|
|
* Unwinding state changes due to a short write is by far the most complicated
|
|
|
|
* part of the machinery that follows. We optimize the normal path: it should
|
|
|
|
* be cheap to be prepared for the unwinding; unwinding itself can be more
|
|
|
|
* expensive, as we do not expect it to happen often.
|
|
|
|
*/
|
|
|
|
ssize_t
|
|
|
|
lsquic_stream_pwritev (struct lsquic_stream *stream,
|
|
|
|
ssize_t (*preadv)(void *user_data, const struct iovec *iov, int iovcnt),
|
|
|
|
void *user_data, size_t n_to_write)
|
|
|
|
{
|
|
|
|
struct lsquic_send_ctl *const ctl = stream->conn_pub->send_ctl;
|
2020-09-08 18:16:25 +00:00
|
|
|
#if MALLOC_PWRITEV
|
|
|
|
struct iovec *iovecs;
|
|
|
|
unsigned char **hq_frames;
|
|
|
|
#else
|
|
|
|
struct iovec iovecs[PWRITEV_IOVECS];
|
2020-09-08 15:43:03 +00:00
|
|
|
unsigned char *hq_frames[PWRITEV_FRAMES];
|
2020-09-08 18:16:25 +00:00
|
|
|
#endif
|
|
|
|
struct iovec *last_iov;
|
2020-09-08 15:43:03 +00:00
|
|
|
struct pwritev_ctx ctx;
|
|
|
|
struct lsquic_reader reader;
|
|
|
|
struct send_ctl_state ctl_state;
|
|
|
|
struct hq_arr hq_arr;
|
|
|
|
ssize_t nw;
|
|
|
|
size_t n_allocated, sum;
|
|
|
|
#ifndef NDEBUG
|
|
|
|
const unsigned short n_buffered = stream->sm_n_buffered;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
COMMON_WRITE_CHECKS();
|
|
|
|
SM_HISTORY_APPEND(stream, SHE_USER_WRITE_DATA);
|
|
|
|
|
2020-09-08 18:16:25 +00:00
|
|
|
#if MALLOC_PWRITEV
|
|
|
|
iovecs = malloc(sizeof(iovecs[0]) * PWRITEV_IOVECS);
|
|
|
|
hq_frames = malloc(sizeof(hq_frames[0]) * PWRITEV_FRAMES);
|
|
|
|
if (!(iovecs && hq_frames))
|
|
|
|
{
|
|
|
|
free(iovecs);
|
|
|
|
free(hq_frames);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2020-09-08 15:43:03 +00:00
|
|
|
lsquic_send_ctl_snapshot(ctl, &ctl_state);
|
|
|
|
|
|
|
|
ctx.total_bytes = 0;
|
|
|
|
ctx.n_to_write = n_to_write;
|
|
|
|
ctx.n_iovecs = 0;
|
2020-09-08 18:16:25 +00:00
|
|
|
ctx.max_iovecs = PWRITEV_IOVECS;
|
2020-09-08 15:43:03 +00:00
|
|
|
ctx.iov = iovecs;
|
|
|
|
ctx.hq_arr = &hq_arr;
|
|
|
|
|
|
|
|
hq_arr.p = hq_frames;
|
|
|
|
hq_arr.count = 0;
|
2020-09-08 18:16:25 +00:00
|
|
|
hq_arr.max = PWRITEV_FRAMES;
|
2020-09-08 15:43:03 +00:00
|
|
|
stream->sm_hq_arr = &hq_arr;
|
|
|
|
|
|
|
|
reader.lsqr_ctx = &ctx;
|
|
|
|
reader.lsqr_size = pwritev_size;
|
|
|
|
reader.lsqr_read = pwritev_read;
|
|
|
|
|
|
|
|
nw = stream_write(stream, &reader, 0);
|
|
|
|
LSQ_DEBUG("pwritev: stream_write returned %zd, n_iovecs: %d", nw,
|
|
|
|
ctx.n_iovecs);
|
|
|
|
if (nw > 0)
|
|
|
|
{
|
|
|
|
/* Amount of buffered data shouldn't have increased */
|
|
|
|
assert(n_buffered >= stream->sm_n_buffered);
|
|
|
|
n_allocated = (size_t) nw;
|
|
|
|
nw = preadv(user_data, ctx.iov, ctx.n_iovecs);
|
|
|
|
LSQ_DEBUG("pwritev: preadv returned %zd", nw);
|
|
|
|
if (nw >= 0 && (size_t) nw < n_allocated)
|
|
|
|
goto unwind_short_write;
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
stream->sm_hq_arr = NULL;
|
2020-09-08 18:16:25 +00:00
|
|
|
#if MALLOC_PWRITEV
|
|
|
|
free(iovecs);
|
|
|
|
free(hq_frames);
|
|
|
|
#endif
|
2020-09-08 15:43:03 +00:00
|
|
|
return nw;
|
|
|
|
|
|
|
|
unwind_short_write:
|
|
|
|
/* What follows is not the most efficient process. The emphasis here is
|
|
|
|
* on being simple instead. We expect short writes to be rare, so being
|
|
|
|
* slower than possible is a good tradeoff for being correct.
|
|
|
|
*/
|
|
|
|
LSQ_DEBUG("short write occurred, unwind");
|
|
|
|
SM_HISTORY_APPEND(stream, SHE_SHORT_WRITE);
|
|
|
|
|
|
|
|
/* First, adjust connection cap and stream offsets, and HTTP/3 framing,
|
|
|
|
* if necessary.
|
|
|
|
*/
|
|
|
|
if ((stream->sm_bflags & (SMBF_USE_HEADERS|SMBF_IETF))
|
|
|
|
== (SMBF_USE_HEADERS|SMBF_IETF))
|
|
|
|
{
|
|
|
|
size_t shortfall, payload_sz, decr;
|
|
|
|
unsigned char *p;
|
|
|
|
unsigned bits;
|
|
|
|
|
|
|
|
assert(hq_arr.count > 0);
|
|
|
|
shortfall = n_allocated - (size_t) nw;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
const unsigned count = hq_arr.count;
|
|
|
|
(void) count;
|
|
|
|
p = hq_frames[--hq_arr.count];
|
|
|
|
assert(p[0] == HQFT_DATA);
|
|
|
|
assert(!(p[1] & 0x80)); /* Only one- and two-byte frame sizes */
|
|
|
|
if (p[1] & 0x40)
|
|
|
|
{
|
|
|
|
payload_sz = (p[1] & 0x3F) << 8;
|
|
|
|
payload_sz |= p[2];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
payload_sz = p[1];
|
|
|
|
if (payload_sz > shortfall)
|
|
|
|
{
|
|
|
|
bits = p[1] >> 6;
|
|
|
|
vint_write(p + 1, payload_sz - shortfall, bits, 1 << bits);
|
|
|
|
decr = shortfall;
|
2023-01-26 16:19:06 +00:00
|
|
|
decr_conn_cap(stream, decr);
|
2020-09-08 15:43:03 +00:00
|
|
|
stream->sm_payload -= decr;
|
|
|
|
stream->tosend_off -= decr;
|
|
|
|
shortfall = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
decr = payload_sz + 2 + (p[1] >> 6);
|
2023-01-26 16:19:06 +00:00
|
|
|
decr_conn_cap(stream, decr);
|
2020-09-08 15:43:03 +00:00
|
|
|
stream->sm_payload -= payload_sz;
|
|
|
|
stream->tosend_off -= decr;
|
|
|
|
shortfall -= payload_sz;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (hq_arr.count);
|
|
|
|
assert(shortfall == 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const size_t shortfall = n_allocated - (size_t) nw;
|
2023-01-26 16:19:06 +00:00
|
|
|
decr_conn_cap(stream, shortfall);
|
2020-09-08 15:43:03 +00:00
|
|
|
stream->sm_payload -= shortfall;
|
|
|
|
stream->tosend_off -= shortfall;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find last iovec: */
|
|
|
|
sum = 0;
|
2020-09-08 18:16:25 +00:00
|
|
|
for (last_iov = iovecs; last_iov < iovecs + PWRITEV_IOVECS; ++last_iov)
|
2020-09-08 15:43:03 +00:00
|
|
|
{
|
|
|
|
sum += last_iov->iov_len;
|
|
|
|
if ((last_iov == iovecs || (size_t) nw > sum - last_iov->iov_len)
|
|
|
|
&& (size_t) nw <= sum)
|
|
|
|
break;
|
|
|
|
}
|
2020-09-08 18:16:25 +00:00
|
|
|
assert(last_iov < iovecs + PWRITEV_IOVECS);
|
2020-09-08 15:43:03 +00:00
|
|
|
lsquic_send_ctl_rollback(ctl, &ctl_state, last_iov, sum - nw);
|
|
|
|
|
|
|
|
goto cleanup;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
/* This bypasses COMMON_WRITE_CHECKS */
|
|
|
|
static ssize_t
|
|
|
|
stream_write_buf (struct lsquic_stream *stream, const void *buf, size_t sz)
|
|
|
|
{
|
2020-02-13 14:36:04 +00:00
|
|
|
const struct iovec iov[1] = {{ (void *) buf, sz, }};
|
2019-09-11 15:27:58 +00:00
|
|
|
struct inner_reader_iovec iro = {
|
2020-02-13 14:36:04 +00:00
|
|
|
.iov = iov,
|
|
|
|
.end = iov + 1,
|
2019-09-11 15:27:58 +00:00
|
|
|
.cur_iovec_off = 0,
|
|
|
|
};
|
|
|
|
struct lsquic_reader reader = {
|
|
|
|
.lsqr_read = inner_reader_iovec_read,
|
|
|
|
.lsqr_size = inner_reader_iovec_size,
|
|
|
|
.lsqr_ctx = &iro,
|
|
|
|
};
|
2020-09-08 15:43:03 +00:00
|
|
|
return stream_write(stream, &reader, SWO_BUFFER);
|
2019-09-11 15:27:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-01-14 19:26:11 +00:00
|
|
|
/* This limits the cumulative size of the compressed header fields */
|
2019-09-11 15:27:58 +00:00
|
|
|
#define MAX_HEADERS_SIZE (64 * 1024)
|
|
|
|
|
|
|
|
static int
|
|
|
|
send_headers_ietf (struct lsquic_stream *stream,
|
|
|
|
const struct lsquic_http_headers *headers, int eos)
|
|
|
|
{
|
|
|
|
enum qwh_status qwh;
|
|
|
|
const size_t max_prefix_size =
|
|
|
|
lsquic_qeh_max_prefix_size(stream->conn_pub->u.ietf.qeh);
|
|
|
|
const size_t max_push_size = 1 /* Stream type */ + 8 /* Push ID */;
|
|
|
|
size_t prefix_sz, headers_sz, hblock_sz, push_sz;
|
|
|
|
unsigned bits;
|
|
|
|
ssize_t nw;
|
|
|
|
unsigned char *header_block;
|
2019-10-08 19:54:01 +00:00
|
|
|
enum lsqpack_enc_header_flags hflags;
|
2020-06-03 04:13:30 +00:00
|
|
|
int rv;
|
|
|
|
const size_t buf_sz = max_push_size + max_prefix_size + MAX_HEADERS_SIZE;
|
|
|
|
#ifndef WIN32
|
|
|
|
unsigned char buf[buf_sz];
|
|
|
|
#else
|
|
|
|
unsigned char *buf = _malloca(buf_sz);
|
|
|
|
if (!buf)
|
|
|
|
return -1;
|
|
|
|
#endif
|
2019-09-11 15:27:58 +00:00
|
|
|
|
2021-02-03 16:05:50 +00:00
|
|
|
if (stream->stream_flags & STREAM_PUSHING)
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("push promise still being written, cannot send header now");
|
|
|
|
errno = EBADMSG;
|
|
|
|
return -1;
|
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
stream->stream_flags |= STREAM_NOPUSH;
|
|
|
|
|
|
|
|
/* TODO: Optimize for the common case: write directly to sm_buf and fall
|
|
|
|
* back to a larger buffer if that fails.
|
|
|
|
*/
|
|
|
|
prefix_sz = max_prefix_size;
|
2020-06-03 04:13:30 +00:00
|
|
|
headers_sz = buf_sz - max_prefix_size - max_push_size;
|
2019-09-11 15:27:58 +00:00
|
|
|
qwh = lsquic_qeh_write_headers(stream->conn_pub->u.ietf.qeh, stream->id, 0,
|
|
|
|
headers, buf + max_push_size + max_prefix_size, &prefix_sz,
|
2019-10-08 19:54:01 +00:00
|
|
|
&headers_sz, &stream->sm_hb_compl, &hflags);
|
2019-09-11 15:27:58 +00:00
|
|
|
|
|
|
|
if (!(qwh == QWH_FULL || qwh == QWH_PARTIAL))
|
|
|
|
{
|
|
|
|
if (qwh == QWH_ENOBUF)
|
|
|
|
LSQ_INFO("not enough room for header block");
|
|
|
|
else
|
|
|
|
LSQ_WARN("internal error encoding and sending HTTP headers");
|
2020-06-03 04:13:30 +00:00
|
|
|
goto err;
|
2019-09-11 15:27:58 +00:00
|
|
|
}
|
|
|
|
|
2019-10-08 19:54:01 +00:00
|
|
|
if (hflags & LSQECH_REF_NEW_ENTRIES)
|
|
|
|
stream->stream_flags |= STREAM_ENCODER_DEP;
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
if (stream->sm_promise)
|
|
|
|
{
|
|
|
|
assert(lsquic_stream_is_pushed(stream));
|
|
|
|
bits = vint_val2bits(stream->sm_promise->pp_id);
|
|
|
|
push_sz = 1 + (1 << bits);
|
|
|
|
if (!stream_activate_hq_frame(stream,
|
|
|
|
stream->sm_payload + stream->sm_n_buffered, HQFT_PUSH_PREAMBLE,
|
|
|
|
SHF_FIXED_SIZE|SHF_PHANTOM, push_sz))
|
2020-06-03 04:13:30 +00:00
|
|
|
goto err;
|
2019-09-11 15:27:58 +00:00
|
|
|
buf[max_push_size + max_prefix_size - prefix_sz - push_sz] = HQUST_PUSH;
|
|
|
|
vint_write(buf + max_push_size + max_prefix_size - prefix_sz
|
|
|
|
- push_sz + 1,stream->sm_promise->pp_id, bits, 1 << bits);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
push_sz = 0;
|
|
|
|
|
|
|
|
/* Construct contiguous header block buffer including HQ framing */
|
|
|
|
header_block = buf + max_push_size + max_prefix_size - prefix_sz - push_sz;
|
|
|
|
hblock_sz = push_sz + prefix_sz + headers_sz;
|
|
|
|
if (!stream_activate_hq_frame(stream,
|
|
|
|
stream->sm_payload + stream->sm_n_buffered + push_sz,
|
|
|
|
HQFT_HEADERS, SHF_FIXED_SIZE, hblock_sz - push_sz))
|
2020-06-03 04:13:30 +00:00
|
|
|
goto err;
|
2019-09-11 15:27:58 +00:00
|
|
|
|
|
|
|
if (qwh == QWH_FULL)
|
|
|
|
{
|
|
|
|
stream->sm_send_headers_state = SSHS_HBLOCK_SENDING;
|
|
|
|
if (lsquic_stream_write_avail(stream))
|
|
|
|
{
|
|
|
|
nw = stream_write_buf(stream, header_block, hblock_sz);
|
|
|
|
if (nw < 0)
|
|
|
|
{
|
|
|
|
LSQ_WARN("cannot write to stream: %s", strerror(errno));
|
2020-06-03 04:13:30 +00:00
|
|
|
goto err;
|
2019-09-11 15:27:58 +00:00
|
|
|
}
|
|
|
|
if ((size_t) nw == hblock_sz)
|
|
|
|
{
|
|
|
|
stream->stream_flags |= STREAM_HEADERS_SENT;
|
|
|
|
stream_hblock_sent(stream);
|
|
|
|
LSQ_DEBUG("wrote all %zu bytes of header block", hblock_sz);
|
2020-06-03 04:13:30 +00:00
|
|
|
goto end;
|
2019-09-11 15:27:58 +00:00
|
|
|
}
|
|
|
|
LSQ_DEBUG("wrote only %zd bytes of header block, stash", nw);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("cannot write to stream, stash all %zu bytes of "
|
|
|
|
"header block", hblock_sz);
|
|
|
|
nw = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
stream->sm_send_headers_state = SSHS_ENC_SENDING;
|
|
|
|
nw = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
stream->sm_saved_want_write = !!(stream->sm_qflags & SMQF_WANT_WRITE);
|
|
|
|
stream_wantwrite(stream, 1);
|
|
|
|
|
|
|
|
stream->sm_header_block = malloc(hblock_sz - (size_t) nw);
|
|
|
|
if (!stream->sm_header_block)
|
|
|
|
{
|
|
|
|
LSQ_WARN("cannot allocate %zd bytes to stash %s header block",
|
|
|
|
hblock_sz - (size_t) nw, qwh == QWH_FULL ? "full" : "partial");
|
2020-06-03 04:13:30 +00:00
|
|
|
goto err;
|
2019-09-11 15:27:58 +00:00
|
|
|
}
|
|
|
|
memcpy(stream->sm_header_block, header_block + (size_t) nw,
|
|
|
|
hblock_sz - (size_t) nw);
|
|
|
|
stream->sm_hblock_sz = hblock_sz - (size_t) nw;
|
|
|
|
stream->sm_hblock_off = 0;
|
|
|
|
LSQ_DEBUG("stashed %u bytes of header block", stream->sm_hblock_sz);
|
2020-06-03 04:13:30 +00:00
|
|
|
|
|
|
|
end:
|
|
|
|
rv = 0;
|
|
|
|
clean:
|
|
|
|
#ifdef WIN32
|
|
|
|
_freea(buf);
|
|
|
|
#endif
|
|
|
|
return rv;
|
|
|
|
|
|
|
|
err:
|
|
|
|
rv = -1;
|
|
|
|
goto clean;
|
2019-09-11 15:27:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
send_headers_gquic (struct lsquic_stream *stream,
|
|
|
|
const struct lsquic_http_headers *headers, int eos)
|
|
|
|
{
|
|
|
|
int s = lsquic_headers_stream_send_headers(stream->conn_pub->u.gquic.hs,
|
|
|
|
stream->id, headers, eos, lsquic_stream_priority(stream));
|
|
|
|
if (0 == s)
|
|
|
|
{
|
|
|
|
SM_HISTORY_APPEND(stream, SHE_USER_WRITE_HEADER);
|
|
|
|
stream->stream_flags |= STREAM_HEADERS_SENT;
|
|
|
|
if (eos)
|
|
|
|
stream->stream_flags |= STREAM_FIN_SENT;
|
|
|
|
LSQ_INFO("sent headers");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
LSQ_WARN("could not send headers: %s", strerror(errno));
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
int
|
|
|
|
lsquic_stream_send_headers (lsquic_stream_t *stream,
|
|
|
|
const lsquic_http_headers_t *headers, int eos)
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
if ((stream->sm_bflags & SMBF_USE_HEADERS)
|
2021-04-12 13:52:42 +00:00
|
|
|
&& !(stream->stream_flags & (STREAM_U_WRITE_DONE)))
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
if (stream->sm_bflags & SMBF_IETF)
|
|
|
|
return send_headers_ietf(stream, headers, eos);
|
2017-09-22 21:00:03 +00:00
|
|
|
else
|
2019-09-11 15:27:58 +00:00
|
|
|
return send_headers_gquic(stream, headers, eos);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_INFO("cannot send headers in this state");
|
2017-09-22 21:00:03 +00:00
|
|
|
errno = EBADMSG;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
lsquic_stream_window_update (lsquic_stream_t *stream, uint64_t offset)
|
|
|
|
{
|
|
|
|
if (offset > stream->max_send_off)
|
|
|
|
{
|
|
|
|
SM_HISTORY_APPEND(stream, SHE_WINDOW_UPDATE);
|
2020-11-05 16:01:46 +00:00
|
|
|
LSQ_DEBUG("update max send offset from %"PRIu64" to "
|
|
|
|
"%"PRIu64, stream->max_send_off, offset);
|
2017-09-22 21:00:03 +00:00
|
|
|
stream->max_send_off = offset;
|
|
|
|
}
|
|
|
|
else
|
2020-11-05 16:01:46 +00:00
|
|
|
LSQ_DEBUG("new offset %"PRIu64" is not larger than old "
|
|
|
|
"max send offset %"PRIu64", ignoring", offset,
|
2017-09-22 21:00:03 +00:00
|
|
|
stream->max_send_off);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* This function is used to update offsets after handshake completes and we
|
|
|
|
* learn of peer's limits from the handshake values.
|
|
|
|
*/
|
|
|
|
int
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_stream_set_max_send_off (lsquic_stream_t *stream, uint64_t offset)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("setting max_send_off to %"PRIu64, offset);
|
2017-09-22 21:00:03 +00:00
|
|
|
if (offset > stream->max_send_off)
|
|
|
|
{
|
|
|
|
lsquic_stream_window_update(stream, offset);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else if (offset < stream->tosend_off)
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_INFO("new offset (%"PRIu64" bytes) is smaller than the amount of "
|
|
|
|
"data already sent on this stream (%"PRIu64" bytes)", offset,
|
2017-09-22 21:00:03 +00:00
|
|
|
stream->tosend_off);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
stream->max_send_off = offset;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2021-03-03 14:41:42 +00:00
|
|
|
lsquic_stream_maybe_reset (struct lsquic_stream *stream, uint64_t error_code,
|
|
|
|
int do_close)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2021-03-03 14:41:42 +00:00
|
|
|
if (!((stream->stream_flags
|
|
|
|
& (STREAM_RST_SENT|STREAM_FIN_SENT|STREAM_U_WRITE_DONE))
|
|
|
|
|| (stream->sm_qflags & SMQF_SEND_RST)))
|
|
|
|
{
|
|
|
|
stream_reset(stream, error_code, do_close);
|
|
|
|
}
|
|
|
|
else if (do_close)
|
|
|
|
stream_shutdown_read(stream);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-03 14:41:42 +00:00
|
|
|
static void
|
|
|
|
stream_reset (struct lsquic_stream *stream, uint64_t error_code, int do_close)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
if ((stream->stream_flags & STREAM_RST_SENT)
|
|
|
|
|| (stream->sm_qflags & SMQF_SEND_RST))
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
LSQ_INFO("reset already sent");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
SM_HISTORY_APPEND(stream, SHE_RESET);
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_INFO("reset, error code %"PRIu64, error_code);
|
2017-09-22 21:00:03 +00:00
|
|
|
stream->error_code = error_code;
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
if (!(stream->sm_qflags & SMQF_SENDING_FLAGS))
|
2017-09-22 21:00:03 +00:00
|
|
|
TAILQ_INSERT_TAIL(&stream->conn_pub->sending_streams, stream,
|
|
|
|
next_send_stream);
|
2019-09-11 15:27:58 +00:00
|
|
|
stream->sm_qflags &= ~SMQF_SENDING_FLAGS;
|
|
|
|
stream->sm_qflags |= SMQF_SEND_RST;
|
|
|
|
|
|
|
|
if (stream->sm_qflags & SMQF_QPACK_DEC)
|
|
|
|
{
|
|
|
|
lsquic_qdh_cancel_stream(stream->conn_pub->u.ietf.qdh, stream);
|
2019-12-05 13:44:25 +00:00
|
|
|
stream->sm_qflags &= ~SMQF_QPACK_DEC;
|
2019-09-11 15:27:58 +00:00
|
|
|
}
|
2017-09-22 21:00:03 +00:00
|
|
|
|
2018-02-26 21:01:16 +00:00
|
|
|
drop_buffered_data(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_elide_stream_frames(stream);
|
2017-09-22 21:00:03 +00:00
|
|
|
maybe_schedule_call_on_close(stream);
|
|
|
|
|
|
|
|
if (do_close)
|
|
|
|
lsquic_stream_close(stream);
|
|
|
|
else
|
2018-04-19 17:47:37 +00:00
|
|
|
maybe_conn_to_tickable_if_writeable(stream, 1);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_stream_id_t
|
2017-09-22 21:00:03 +00:00
|
|
|
lsquic_stream_id (const lsquic_stream_t *stream)
|
|
|
|
{
|
|
|
|
return stream->id;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
#if !defined(NDEBUG) && __GNUC__
|
|
|
|
__attribute__((weak))
|
|
|
|
#endif
|
2017-09-22 21:00:03 +00:00
|
|
|
struct lsquic_conn *
|
|
|
|
lsquic_stream_conn (const lsquic_stream_t *stream)
|
|
|
|
{
|
|
|
|
return stream->conn_pub->lconn;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
lsquic_stream_close (lsquic_stream_t *stream)
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("lsquic_stream_close() called");
|
2017-09-22 21:00:03 +00:00
|
|
|
SM_HISTORY_APPEND(stream, SHE_CLOSE);
|
|
|
|
if (lsquic_stream_is_closed(stream))
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_INFO("Attempt to close an already-closed stream");
|
2017-09-22 21:00:03 +00:00
|
|
|
errno = EBADF;
|
|
|
|
return -1;
|
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
maybe_stream_shutdown_write(stream);
|
2017-09-22 21:00:03 +00:00
|
|
|
stream_shutdown_read(stream);
|
|
|
|
maybe_schedule_call_on_close(stream);
|
|
|
|
maybe_finish_stream(stream);
|
2019-09-11 15:27:58 +00:00
|
|
|
if (!(stream->stream_flags & STREAM_DELAYED_SW))
|
|
|
|
maybe_conn_to_tickable_if_writeable(stream, 1);
|
2017-09-22 21:00:03 +00:00
|
|
|
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
|
|
|
#ifndef NDEBUG
|
|
|
|
#if __GNUC__
|
|
|
|
__attribute__((weak))
|
|
|
|
#endif
|
|
|
|
#endif
|
2017-09-22 21:00:03 +00:00
|
|
|
void
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_stream_acked (struct lsquic_stream *stream,
|
|
|
|
enum quic_frame_type frame_type)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2022-08-16 16:16:46 +00:00
|
|
|
if (stream->n_unacked > 0)
|
|
|
|
{
|
|
|
|
--stream->n_unacked;
|
|
|
|
LSQ_DEBUG("ACKed; n_unacked: %u", stream->n_unacked);
|
|
|
|
if (frame_type == QUIC_FRAME_RST_STREAM)
|
|
|
|
{
|
|
|
|
SM_HISTORY_APPEND(stream, SHE_RST_ACKED);
|
|
|
|
LSQ_DEBUG("RESET that we sent has been acked by peer");
|
|
|
|
stream->stream_flags |= STREAM_RST_ACKED;
|
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
}
|
2017-09-22 21:00:03 +00:00
|
|
|
if (0 == stream->n_unacked)
|
2020-12-04 16:29:14 +00:00
|
|
|
{
|
|
|
|
maybe_schedule_call_on_close(stream);
|
2017-09-22 21:00:03 +00:00
|
|
|
maybe_finish_stream(stream);
|
2020-12-04 16:29:14 +00:00
|
|
|
}
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
lsquic_stream_push_req (lsquic_stream_t *stream,
|
|
|
|
struct uncompressed_headers *push_req)
|
|
|
|
{
|
|
|
|
assert(!stream->push_req);
|
|
|
|
stream->push_req = push_req;
|
|
|
|
stream->stream_flags |= STREAM_U_WRITE_DONE; /* Writing not allowed */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
lsquic_stream_is_pushed (const lsquic_stream_t *stream)
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
enum stream_id_type sit;
|
|
|
|
|
2020-08-26 13:00:45 +00:00
|
|
|
switch (stream->sm_bflags & (SMBF_IETF|SMBF_USE_HEADERS))
|
2019-09-11 15:27:58 +00:00
|
|
|
{
|
2020-08-26 13:00:45 +00:00
|
|
|
case SMBF_IETF|SMBF_USE_HEADERS:
|
2019-09-11 15:27:58 +00:00
|
|
|
sit = stream->id & SIT_MASK;
|
|
|
|
return sit == SIT_UNI_SERVER;
|
2020-08-26 13:00:45 +00:00
|
|
|
case SMBF_USE_HEADERS:
|
2019-09-11 15:27:58 +00:00
|
|
|
return 1 & ~stream->id;
|
2020-08-26 13:00:45 +00:00
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
lsquic_stream_push_info (const lsquic_stream_t *stream,
|
2019-09-11 15:27:58 +00:00
|
|
|
lsquic_stream_id_t *ref_stream_id, void **hset)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
if (lsquic_stream_is_pushed(stream))
|
|
|
|
{
|
|
|
|
assert(stream->push_req);
|
|
|
|
*ref_stream_id = stream->push_req->uh_stream_id;
|
2018-08-28 13:59:47 +00:00
|
|
|
*hset = stream->push_req->uh_hset;
|
2017-09-22 21:00:03 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-11-22 05:40:05 +00:00
|
|
|
static int
|
|
|
|
stream_uh_in_gquic (struct lsquic_stream *stream,
|
|
|
|
struct uncompressed_headers *uh)
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
2021-04-12 13:52:42 +00:00
|
|
|
struct uncompressed_headers **next;
|
|
|
|
if ((stream->sm_bflags & SMBF_USE_HEADERS))
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
SM_HISTORY_APPEND(stream, SHE_HEADERS_IN);
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("received uncompressed headers");
|
2017-09-22 21:00:03 +00:00
|
|
|
stream->stream_flags |= STREAM_HAVE_UH;
|
|
|
|
if (uh->uh_flags & UH_FIN)
|
|
|
|
stream->stream_flags |= STREAM_FIN_RECVD|STREAM_HEAD_IN_FIN;
|
2021-04-12 13:52:42 +00:00
|
|
|
next = &stream->uh;
|
|
|
|
while(*next)
|
|
|
|
next = &(*next)->uh_next;
|
|
|
|
*next = uh;
|
|
|
|
assert(uh->uh_next == NULL);
|
2017-09-22 21:00:03 +00:00
|
|
|
if (uh->uh_oth_stream_id == 0)
|
|
|
|
{
|
|
|
|
if (uh->uh_weight)
|
|
|
|
lsquic_stream_set_priority_internal(stream, uh->uh_weight);
|
|
|
|
}
|
|
|
|
else
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_NOTICE("don't know how to depend on stream %"PRIu64,
|
2017-09-22 21:00:03 +00:00
|
|
|
uh->uh_oth_stream_id);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_ERROR("received unexpected uncompressed headers");
|
2017-09-22 21:00:03 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-11-22 05:40:05 +00:00
|
|
|
static int
|
|
|
|
stream_uh_in_ietf (struct lsquic_stream *stream,
|
|
|
|
struct uncompressed_headers *uh)
|
|
|
|
{
|
|
|
|
int push_promise;
|
2021-04-12 13:52:42 +00:00
|
|
|
struct uncompressed_headers **next;
|
2019-11-22 05:40:05 +00:00
|
|
|
|
|
|
|
push_promise = lsquic_stream_header_is_pp(stream);
|
2021-04-12 13:52:42 +00:00
|
|
|
if (!push_promise)
|
2019-11-22 05:40:05 +00:00
|
|
|
{
|
|
|
|
SM_HISTORY_APPEND(stream, SHE_HEADERS_IN);
|
|
|
|
LSQ_DEBUG("received uncompressed headers");
|
|
|
|
stream->stream_flags |= STREAM_HAVE_UH;
|
|
|
|
if (uh->uh_flags & UH_FIN)
|
|
|
|
{
|
|
|
|
/* IETF QUIC only sets UH_FIN for a pushed stream on the server to
|
|
|
|
* mark request as done:
|
|
|
|
*/
|
|
|
|
if (stream->sm_bflags & SMBF_IETF)
|
|
|
|
assert((stream->sm_bflags & SMBF_SERVER)
|
|
|
|
&& lsquic_stream_is_pushed(stream));
|
|
|
|
stream->stream_flags |= STREAM_FIN_RECVD|STREAM_HEAD_IN_FIN;
|
|
|
|
}
|
2021-04-12 13:52:42 +00:00
|
|
|
next = &stream->uh;
|
|
|
|
while(*next)
|
|
|
|
next = &(*next)->uh_next;
|
|
|
|
*next = uh;
|
|
|
|
assert(uh->uh_next == NULL);
|
2019-11-22 05:40:05 +00:00
|
|
|
if (uh->uh_oth_stream_id == 0)
|
|
|
|
{
|
|
|
|
if (uh->uh_weight)
|
|
|
|
lsquic_stream_set_priority_internal(stream, uh->uh_weight);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
LSQ_NOTICE("don't know how to depend on stream %"PRIu64,
|
|
|
|
uh->uh_oth_stream_id);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Trailer should never make here, as we discard it in qdh */
|
|
|
|
LSQ_DEBUG("discard %s header set",
|
|
|
|
push_promise ? "push promise" : "trailer");
|
|
|
|
if (uh->uh_hset)
|
|
|
|
stream->conn_pub->enpub->enp_hsi_if
|
|
|
|
->hsi_discard_header_set(uh->uh_hset);
|
|
|
|
free(uh);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
lsquic_stream_uh_in (lsquic_stream_t *stream, struct uncompressed_headers *uh)
|
|
|
|
{
|
|
|
|
if (stream->sm_bflags & SMBF_USE_HEADERS)
|
|
|
|
{
|
|
|
|
if (stream->sm_bflags & SMBF_IETF)
|
|
|
|
return stream_uh_in_ietf(stream, uh);
|
|
|
|
else
|
|
|
|
return stream_uh_in_gquic(stream, uh);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
unsigned
|
|
|
|
lsquic_stream_priority (const lsquic_stream_t *stream)
|
|
|
|
{
|
2020-10-07 13:41:26 +00:00
|
|
|
if (stream->sm_bflags & SMBF_HTTP_PRIO)
|
|
|
|
return stream->sm_priority;
|
|
|
|
else
|
|
|
|
return 256 - stream->sm_priority;
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
lsquic_stream_set_priority_internal (lsquic_stream_t *stream, unsigned priority)
|
|
|
|
{
|
|
|
|
/* The user should never get a reference to the special streams,
|
|
|
|
* but let's check just in case:
|
|
|
|
*/
|
2019-09-11 15:27:58 +00:00
|
|
|
if (lsquic_stream_is_critical(stream))
|
2017-09-22 21:00:03 +00:00
|
|
|
return -1;
|
2020-10-07 13:41:26 +00:00
|
|
|
|
|
|
|
if (stream->sm_bflags & SMBF_HTTP_PRIO)
|
|
|
|
{
|
|
|
|
if (priority > LSQUIC_MAX_HTTP_URGENCY)
|
|
|
|
return -1;
|
|
|
|
stream->sm_priority = priority;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (priority < 1 || priority > 256)
|
|
|
|
return -1;
|
|
|
|
stream->sm_priority = 256 - priority;
|
|
|
|
}
|
|
|
|
|
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_invalidate_bpt_cache(stream->conn_pub->send_ctl);
|
2017-09-22 21:00:03 +00:00
|
|
|
LSQ_DEBUG("set priority to %u", priority);
|
|
|
|
SM_HISTORY_APPEND(stream, SHE_SET_PRIO);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static int
|
|
|
|
maybe_send_priority_gquic (struct lsquic_stream *stream, unsigned priority)
|
|
|
|
{
|
|
|
|
if ((stream->sm_bflags & SMBF_USE_HEADERS)
|
|
|
|
&& (stream->stream_flags & STREAM_HEADERS_SENT))
|
|
|
|
{
|
|
|
|
/* We need to send headers only if we are a) using HEADERS stream
|
|
|
|
* and b) we already sent initial headers. If initial headers
|
|
|
|
* have not been sent yet, stream priority will be sent in the
|
|
|
|
* HEADERS frame.
|
|
|
|
*/
|
|
|
|
return lsquic_headers_stream_send_priority(stream->conn_pub->u.gquic.hs,
|
|
|
|
stream->id, 0, 0, priority);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2020-10-07 13:41:26 +00:00
|
|
|
send_priority_ietf (struct lsquic_stream *stream)
|
2019-09-11 15:27:58 +00:00
|
|
|
{
|
2020-10-07 13:41:26 +00:00
|
|
|
struct lsquic_ext_http_prio ehp;
|
|
|
|
|
|
|
|
if (0 == lsquic_stream_get_http_prio(stream, &ehp)
|
|
|
|
&& 0 == lsquic_hcso_write_priority_update(
|
|
|
|
stream->conn_pub->u.ietf.hcso,
|
|
|
|
HQFT_PRIORITY_UPDATE_STREAM, stream->id, &ehp))
|
|
|
|
return 0;
|
|
|
|
else
|
|
|
|
return -1;
|
2019-09-11 15:27:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
int
|
|
|
|
lsquic_stream_set_priority (lsquic_stream_t *stream, unsigned priority)
|
|
|
|
{
|
|
|
|
if (0 == lsquic_stream_set_priority_internal(stream, priority))
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
if (stream->sm_bflags & SMBF_IETF)
|
2020-10-07 13:41:26 +00:00
|
|
|
{
|
|
|
|
if (stream->sm_bflags & SMBF_HTTP_PRIO)
|
|
|
|
return send_priority_ietf(stream);
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
2017-09-22 21:00:03 +00:00
|
|
|
else
|
2019-09-11 15:27:58 +00:00
|
|
|
return maybe_send_priority_gquic(stream, priority);
|
2017-09-22 21:00:03 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
lsquic_stream_ctx_t *
|
|
|
|
lsquic_stream_get_ctx (const lsquic_stream_t *stream)
|
|
|
|
{
|
2020-01-20 14:41:37 +00:00
|
|
|
fiu_return_on("stream/get_ctx", NULL);
|
2017-09-22 21:00:03 +00:00
|
|
|
return stream->st_ctx;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-09-30 15:23:56 +00:00
|
|
|
void
|
|
|
|
lsquic_stream_set_ctx (lsquic_stream_t *stream, lsquic_stream_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
stream->st_ctx = ctx;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-22 21:00:03 +00:00
|
|
|
int
|
|
|
|
lsquic_stream_refuse_push (lsquic_stream_t *stream)
|
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
if (lsquic_stream_is_pushed(stream)
|
|
|
|
&& !(stream->sm_qflags & SMQF_SEND_RST)
|
|
|
|
&& !(stream->stream_flags & STREAM_RST_SENT))
|
2017-09-22 21:00:03 +00:00
|
|
|
{
|
|
|
|
LSQ_DEBUG("refusing pushed stream: send reset");
|
2021-03-03 14:41:42 +00:00
|
|
|
stream_reset(stream, 8 /* QUIC_REFUSED_STREAM */, 1);
|
2017-09-22 21:00:03 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return -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
|
|
|
|
|
|
|
|
|
|
|
size_t
|
|
|
|
lsquic_stream_mem_used (const struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
size_t size;
|
|
|
|
|
|
|
|
size = sizeof(stream);
|
|
|
|
if (stream->sm_buf)
|
2019-09-11 15:27:58 +00:00
|
|
|
size += stream->sm_n_allocated;
|
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->data_in)
|
|
|
|
size += stream->data_in->di_if->di_mem_used(stream->data_in);
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
const lsquic_cid_t *
|
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_cid (const struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
return LSQUIC_LOG_CONN_ID;
|
|
|
|
}
|
2018-05-16 14:45:31 +00:00
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
void
|
|
|
|
lsquic_stream_dump_state (const struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("flags: %X; read off: %"PRIu64, stream->stream_flags,
|
|
|
|
stream->read_offset);
|
|
|
|
stream->data_in->di_if->di_dump_state(stream->data_in);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-28 13:59:47 +00:00
|
|
|
void *
|
|
|
|
lsquic_stream_get_hset (struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
void *hset;
|
2021-04-12 13:52:42 +00:00
|
|
|
struct uncompressed_headers *uh;
|
2018-08-28 13:59:47 +00:00
|
|
|
|
2020-12-23 14:06:05 +00:00
|
|
|
if (stream_is_read_reset(stream))
|
2018-09-06 18:05:15 +00:00
|
|
|
{
|
|
|
|
LSQ_INFO("%s: stream is reset, no headers returned", __func__);
|
|
|
|
errno = ECONNRESET;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
if (!((stream->sm_bflags & SMBF_USE_HEADERS)
|
|
|
|
&& (stream->stream_flags & STREAM_HAVE_UH)))
|
2018-08-28 13:59:47 +00:00
|
|
|
{
|
|
|
|
LSQ_INFO("%s: unexpected call, flags: 0x%X", __func__,
|
|
|
|
stream->stream_flags);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!stream->uh)
|
|
|
|
{
|
|
|
|
LSQ_INFO("%s: headers unavailable (already fetched?)", __func__);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
hset = stream->uh->uh_hset;
|
|
|
|
stream->uh->uh_hset = NULL;
|
2021-04-12 13:52:42 +00:00
|
|
|
|
|
|
|
uh = stream->uh;
|
|
|
|
stream->uh = uh->uh_next;
|
|
|
|
free(uh);
|
|
|
|
|
2018-09-06 18:05:15 +00:00
|
|
|
if (stream->stream_flags & STREAM_HEAD_IN_FIN)
|
|
|
|
{
|
2021-04-12 13:52:42 +00:00
|
|
|
|
2018-09-06 18:05:15 +00:00
|
|
|
stream->stream_flags |= STREAM_FIN_REACHED;
|
|
|
|
SM_HISTORY_APPEND(stream, SHE_REACH_FIN);
|
|
|
|
}
|
2020-06-09 15:55:56 +00:00
|
|
|
maybe_update_last_progress(stream);
|
2018-08-28 13:59:47 +00:00
|
|
|
LSQ_DEBUG("return header set");
|
|
|
|
return hset;
|
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
lsquic_stream_set_stream_if (struct lsquic_stream *stream,
|
|
|
|
const struct lsquic_stream_if *stream_if, void *stream_if_ctx)
|
|
|
|
{
|
|
|
|
SM_HISTORY_APPEND(stream, SHE_IF_SWITCH);
|
|
|
|
stream->stream_if = stream_if;
|
|
|
|
stream->sm_onnew_arg = stream_if_ctx;
|
|
|
|
LSQ_DEBUG("switched interface");
|
|
|
|
assert(stream->stream_flags & STREAM_ONNEW_DONE);
|
|
|
|
stream->st_ctx = stream->stream_if->on_new_stream(stream->sm_onnew_arg,
|
|
|
|
stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2019-11-22 05:40:05 +00:00
|
|
|
update_type_hist_and_check (const struct lsquic_stream *stream,
|
|
|
|
struct hq_filter *filter)
|
2019-09-11 15:27:58 +00:00
|
|
|
{
|
|
|
|
switch (filter->hqfi_type)
|
|
|
|
{
|
|
|
|
case HQFT_HEADERS:
|
2021-04-12 13:52:42 +00:00
|
|
|
if (filter->hqfi_flags & HQFI_FLAG_TRAILER)
|
|
|
|
return -1;
|
|
|
|
if (filter->hqfi_flags & HQFI_FLAG_DATA)
|
|
|
|
filter->hqfi_flags |= HQFI_FLAG_TRAILER;
|
|
|
|
else
|
|
|
|
filter->hqfi_flags |= HQFI_FLAG_HEADER;
|
2019-09-11 15:27:58 +00:00
|
|
|
break;
|
|
|
|
case HQFT_DATA:
|
2021-04-12 13:52:42 +00:00
|
|
|
if ((filter->hqfi_flags & (HQFI_FLAG_HEADER
|
|
|
|
| HQFI_FLAG_TRAILER)) != HQFI_FLAG_HEADER)
|
|
|
|
return -1;
|
|
|
|
filter->hqfi_flags |= HQFI_FLAG_DATA;
|
2019-09-11 15:27:58 +00:00
|
|
|
break;
|
2019-11-22 05:40:05 +00:00
|
|
|
case HQFT_PUSH_PROMISE:
|
|
|
|
/* [draft-ietf-quic-http-24], Section 7 */
|
|
|
|
if ((stream->id & SIT_MASK) == SIT_BIDI_CLIENT
|
|
|
|
&& !(stream->sm_bflags & SMBF_SERVER))
|
|
|
|
return 0;
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
case HQFT_CANCEL_PUSH:
|
|
|
|
case HQFT_SETTINGS:
|
|
|
|
case HQFT_GOAWAY:
|
|
|
|
case HQFT_MAX_PUSH_ID:
|
|
|
|
/* [draft-ietf-quic-http-24], Section 7 */
|
|
|
|
return -1;
|
2020-09-15 20:42:13 +00:00
|
|
|
case 2: /* HTTP/2 PRIORITY */
|
|
|
|
case 6: /* HTTP/2 PING */
|
|
|
|
case 8: /* HTTP/2 WINDOW_UPDATE */
|
|
|
|
case 9: /* HTTP/2 CONTINUATION */
|
|
|
|
/* [draft-ietf-quic-http-30], Section 7.2.8 */
|
|
|
|
return -1;
|
2020-10-07 13:41:26 +00:00
|
|
|
case HQFT_PRIORITY_UPDATE_STREAM:
|
|
|
|
case HQFT_PRIORITY_UPDATE_PUSH:
|
|
|
|
if (stream->sm_bflags & SMBF_HTTP_PRIO)
|
|
|
|
/* If we know about Extensible HTTP Priorities, we should check
|
|
|
|
* that they do not arrive on any but the control stream:
|
|
|
|
*/
|
|
|
|
return -1;
|
|
|
|
else
|
|
|
|
/* On the other hand, if we do not support Priorities, treat it
|
|
|
|
* as an unknown frame:
|
|
|
|
*/
|
|
|
|
return 0;
|
2019-09-11 15:27:58 +00:00
|
|
|
default:
|
|
|
|
/* Ignore unknown frames */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-04-12 13:52:42 +00:00
|
|
|
return 0;
|
2019-09-11 15:27:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-11-22 05:40:05 +00:00
|
|
|
int
|
|
|
|
lsquic_stream_header_is_pp (const struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
return stream->sm_hq_filter.hqfi_type == HQFT_PUSH_PROMISE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
lsquic_stream_header_is_trailer (const struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
return (stream->stream_flags & STREAM_HAVE_UH)
|
|
|
|
&& stream->sm_hq_filter.hqfi_type == HQFT_HEADERS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-01-06 05:47:12 +00:00
|
|
|
static void
|
|
|
|
verify_cl_on_new_data_frame (struct lsquic_stream *stream,
|
|
|
|
struct hq_filter *filter)
|
|
|
|
{
|
|
|
|
struct lsquic_conn *lconn;
|
|
|
|
|
|
|
|
stream->sm_data_in += filter->hqfi_left;
|
|
|
|
if (stream->sm_data_in > stream->sm_cont_len)
|
|
|
|
{
|
|
|
|
lconn = stream->conn_pub->lconn;
|
2021-02-10 13:51:11 +00:00
|
|
|
lconn->cn_if->ci_abort_error(lconn, 1, HEC_MESSAGE_ERROR,
|
2020-01-06 05:47:12 +00:00
|
|
|
"number of bytes in DATA frames of stream %"PRIu64" exceeds "
|
|
|
|
"content-length limit of %llu", stream->id, stream->sm_cont_len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-11 15:27:58 +00:00
|
|
|
static size_t
|
|
|
|
hq_read (void *ctx, const unsigned char *buf, size_t sz, int fin)
|
|
|
|
{
|
|
|
|
struct lsquic_stream *const stream = ctx;
|
|
|
|
struct hq_filter *const filter = &stream->sm_hq_filter;
|
|
|
|
const unsigned char *p = buf, *prev;
|
|
|
|
const unsigned char *const end = buf + sz;
|
|
|
|
struct lsquic_conn *lconn;
|
|
|
|
enum lsqpack_read_header_status rhs;
|
|
|
|
int s;
|
|
|
|
|
|
|
|
while (p < end)
|
|
|
|
{
|
|
|
|
switch (filter->hqfi_state)
|
|
|
|
{
|
|
|
|
case HQFI_STATE_FRAME_HEADER_BEGIN:
|
2019-11-22 05:40:05 +00:00
|
|
|
filter->hqfi_vint2_state.vr2s_state = 0;
|
2019-09-11 15:27:58 +00:00
|
|
|
filter->hqfi_state = HQFI_STATE_FRAME_HEADER_CONTINUE;
|
|
|
|
/* fall-through */
|
|
|
|
case HQFI_STATE_FRAME_HEADER_CONTINUE:
|
2019-11-22 05:40:05 +00:00
|
|
|
s = lsquic_varint_read_two(&p, end, &filter->hqfi_vint2_state);
|
2019-09-11 15:27:58 +00:00
|
|
|
if (s < 0)
|
|
|
|
break;
|
|
|
|
filter->hqfi_flags |= HQFI_FLAG_BEGIN;
|
|
|
|
filter->hqfi_state = HQFI_STATE_READING_PAYLOAD;
|
|
|
|
LSQ_DEBUG("HQ frame type 0x%"PRIX64" at offset %"PRIu64", size %"PRIu64,
|
|
|
|
filter->hqfi_type, stream->read_offset + (unsigned) (p - buf),
|
|
|
|
filter->hqfi_left);
|
2019-11-22 05:40:05 +00:00
|
|
|
if (0 != update_type_hist_and_check(stream, filter))
|
2019-09-11 15:27:58 +00:00
|
|
|
{
|
|
|
|
lconn = stream->conn_pub->lconn;
|
|
|
|
filter->hqfi_flags |= HQFI_FLAG_ERROR;
|
2021-04-12 13:52:42 +00:00
|
|
|
LSQ_INFO("unexpected HTTP/3 frame sequence");
|
2019-09-18 15:22:20 +00:00
|
|
|
lconn->cn_if->ci_abort_error(lconn, 1, HEC_FRAME_UNEXPECTED,
|
2019-09-11 15:27:58 +00:00
|
|
|
"unexpected HTTP/3 frame sequence on stream %"PRIu64,
|
|
|
|
stream->id);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
if (filter->hqfi_left > 0)
|
|
|
|
{
|
|
|
|
if (filter->hqfi_type == HQFT_DATA)
|
2020-01-06 05:47:12 +00:00
|
|
|
{
|
|
|
|
if (stream->sm_bflags & SMBF_VERIFY_CL)
|
|
|
|
verify_cl_on_new_data_frame(stream, filter);
|
2019-09-11 15:27:58 +00:00
|
|
|
goto end;
|
2020-01-06 05:47:12 +00:00
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
else if (filter->hqfi_type == HQFT_PUSH_PROMISE)
|
|
|
|
{
|
|
|
|
if (stream->sm_bflags & SMBF_SERVER)
|
2019-11-22 05:40:05 +00:00
|
|
|
{
|
|
|
|
lconn = stream->conn_pub->lconn;
|
2019-09-11 15:27:58 +00:00
|
|
|
lconn->cn_if->ci_abort_error(lconn, 1,
|
2019-09-18 15:22:20 +00:00
|
|
|
HEC_FRAME_UNEXPECTED, "Received PUSH_PROMISE frame "
|
2019-09-11 15:27:58 +00:00
|
|
|
"on stream %"PRIu64" (clients are not supposed to "
|
|
|
|
"send those)", stream->id);
|
2019-11-22 05:40:05 +00:00
|
|
|
goto end;
|
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
else
|
2019-11-22 05:40:05 +00:00
|
|
|
filter->hqfi_state = HQFI_STATE_PUSH_ID_BEGIN;
|
2019-09-11 15:27:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
switch (filter->hqfi_type)
|
|
|
|
{
|
|
|
|
case HQFT_CANCEL_PUSH:
|
|
|
|
case HQFT_GOAWAY:
|
|
|
|
case HQFT_HEADERS:
|
|
|
|
case HQFT_MAX_PUSH_ID:
|
|
|
|
case HQFT_PUSH_PROMISE:
|
|
|
|
case HQFT_SETTINGS:
|
|
|
|
filter->hqfi_flags |= HQFI_FLAG_ERROR;
|
|
|
|
LSQ_INFO("HQ frame of type %"PRIu64" cannot be size 0",
|
|
|
|
filter->hqfi_type);
|
|
|
|
abort_connection(stream); /* XXX Overkill? */
|
|
|
|
goto end;
|
|
|
|
default:
|
|
|
|
filter->hqfi_flags &= ~HQFI_FLAG_BEGIN;
|
|
|
|
filter->hqfi_state = HQFI_STATE_FRAME_HEADER_BEGIN;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2019-11-22 05:40:05 +00:00
|
|
|
case HQFI_STATE_PUSH_ID_BEGIN:
|
|
|
|
filter->hqfi_vint1_state.pos = 0;
|
|
|
|
filter->hqfi_state = HQFI_STATE_PUSH_ID_CONTINUE;
|
|
|
|
/* Fall-through */
|
|
|
|
case HQFI_STATE_PUSH_ID_CONTINUE:
|
|
|
|
prev = p;
|
|
|
|
s = lsquic_varint_read_nb(&p, end, &filter->hqfi_vint1_state);
|
|
|
|
filter->hqfi_left -= p - prev;
|
|
|
|
if (s == 0)
|
|
|
|
filter->hqfi_state = HQFI_STATE_READING_PAYLOAD;
|
|
|
|
/* A bit of a white lie here */
|
|
|
|
break;
|
2019-09-11 15:27:58 +00:00
|
|
|
case HQFI_STATE_READING_PAYLOAD:
|
|
|
|
if (filter->hqfi_type == HQFT_DATA)
|
|
|
|
goto end;
|
|
|
|
sz = filter->hqfi_left;
|
|
|
|
if (sz > (uintptr_t) (end - p))
|
|
|
|
sz = (uintptr_t) (end - p);
|
|
|
|
switch (filter->hqfi_type)
|
|
|
|
{
|
|
|
|
case HQFT_HEADERS:
|
2019-11-22 05:40:05 +00:00
|
|
|
case HQFT_PUSH_PROMISE:
|
2019-09-11 15:27:58 +00:00
|
|
|
prev = p;
|
|
|
|
if (filter->hqfi_flags & HQFI_FLAG_BEGIN)
|
|
|
|
{
|
|
|
|
filter->hqfi_flags &= ~HQFI_FLAG_BEGIN;
|
|
|
|
rhs = lsquic_qdh_header_in_begin(
|
|
|
|
stream->conn_pub->u.ietf.qdh,
|
|
|
|
stream, filter->hqfi_left, &p, sz);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
rhs = lsquic_qdh_header_in_continue(
|
|
|
|
stream->conn_pub->u.ietf.qdh, stream, &p, sz);
|
|
|
|
assert(p > prev || LQRHS_ERROR == rhs);
|
|
|
|
filter->hqfi_left -= p - prev;
|
|
|
|
if (filter->hqfi_left == 0)
|
|
|
|
filter->hqfi_state = HQFI_STATE_FRAME_HEADER_BEGIN;
|
|
|
|
switch (rhs)
|
|
|
|
{
|
|
|
|
case LQRHS_DONE:
|
|
|
|
assert(filter->hqfi_left == 0);
|
|
|
|
stream->sm_qflags &= ~SMQF_QPACK_DEC;
|
|
|
|
break;
|
|
|
|
case LQRHS_NEED:
|
|
|
|
stream->sm_qflags |= SMQF_QPACK_DEC;
|
|
|
|
break;
|
|
|
|
case LQRHS_BLOCKED:
|
|
|
|
stream->sm_qflags |= SMQF_QPACK_DEC;
|
|
|
|
filter->hqfi_flags |= HQFI_FLAG_BLOCKED;
|
|
|
|
goto end;
|
|
|
|
default:
|
|
|
|
assert(LQRHS_ERROR == rhs);
|
2019-12-05 13:44:25 +00:00
|
|
|
stream->sm_qflags &= ~SMQF_QPACK_DEC;
|
2019-09-11 15:27:58 +00:00
|
|
|
filter->hqfi_flags |= HQFI_FLAG_ERROR;
|
|
|
|
LSQ_INFO("error processing header block");
|
|
|
|
abort_connection(stream); /* XXX Overkill? */
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Simply skip unknown frame type payload for now */
|
|
|
|
filter->hqfi_flags &= ~HQFI_FLAG_BEGIN;
|
|
|
|
p += sz;
|
|
|
|
filter->hqfi_left -= sz;
|
|
|
|
if (filter->hqfi_left == 0)
|
|
|
|
filter->hqfi_state = HQFI_STATE_FRAME_HEADER_BEGIN;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(0);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
end:
|
|
|
|
if (fin && p == end && filter->hqfi_state != HQFI_STATE_FRAME_HEADER_BEGIN)
|
|
|
|
{
|
|
|
|
LSQ_INFO("FIN at unexpected place in filter; state: %u",
|
|
|
|
filter->hqfi_state);
|
|
|
|
filter->hqfi_flags |= HQFI_FLAG_ERROR;
|
2020-06-09 15:55:56 +00:00
|
|
|
/* From [draft-ietf-quic-http-28] Section 7.1:
|
|
|
|
" When a stream terminates cleanly, if the last frame on the stream was
|
|
|
|
" truncated, this MUST be treated as a connection error (Section 8) of
|
|
|
|
" type H3_FRAME_ERROR. Streams which terminate abruptly may be reset
|
|
|
|
" at any point in a frame.
|
2019-09-11 15:27:58 +00:00
|
|
|
*/
|
2020-06-09 15:55:56 +00:00
|
|
|
lconn = stream->conn_pub->lconn;
|
|
|
|
lconn->cn_if->ci_abort_error(lconn, 1, HEC_FRAME_ERROR,
|
|
|
|
"last HTTP/3 frame on stream %"PRIu64" was truncated", stream->id);
|
2019-09-11 15:27:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return p - buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
hq_filter_readable_now (const struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
const struct hq_filter *const filter = &stream->sm_hq_filter;
|
|
|
|
|
|
|
|
return (filter->hqfi_type == HQFT_DATA
|
|
|
|
&& filter->hqfi_state == HQFI_STATE_READING_PAYLOAD)
|
|
|
|
|| (filter->hqfi_flags & HQFI_FLAG_ERROR)
|
|
|
|
|| stream->uh
|
|
|
|
|| (stream->stream_flags & STREAM_FIN_REACHED)
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
hq_filter_readable (struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
struct hq_filter *const filter = &stream->sm_hq_filter;
|
2020-04-15 13:30:40 +00:00
|
|
|
ssize_t nread;
|
2019-09-11 15:27:58 +00:00
|
|
|
|
|
|
|
if (filter->hqfi_flags & HQFI_FLAG_BLOCKED)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!hq_filter_readable_now(stream))
|
|
|
|
{
|
2020-04-15 13:30:40 +00:00
|
|
|
nread = read_data_frames(stream, 0, hq_read, stream);
|
|
|
|
if (nread <= 0)
|
2019-09-11 15:27:58 +00:00
|
|
|
{
|
2020-04-15 13:30:40 +00:00
|
|
|
if (nread < 0)
|
2019-09-11 15:27:58 +00:00
|
|
|
{
|
|
|
|
filter->hqfi_flags |= HQFI_FLAG_ERROR;
|
|
|
|
abort_connection(stream); /* XXX Overkill? */
|
|
|
|
return 1; /* Collect error */
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return hq_filter_readable_now(stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static size_t
|
|
|
|
hq_filter_df (struct lsquic_stream *stream, struct data_frame *data_frame)
|
|
|
|
{
|
|
|
|
struct hq_filter *const filter = &stream->sm_hq_filter;
|
|
|
|
size_t nr;
|
|
|
|
|
|
|
|
if (!(filter->hqfi_state == HQFI_STATE_READING_PAYLOAD
|
|
|
|
&& filter->hqfi_type == HQFT_DATA))
|
|
|
|
{
|
|
|
|
nr = hq_read(stream, data_frame->df_data + data_frame->df_read_off,
|
|
|
|
data_frame->df_size - data_frame->df_read_off,
|
|
|
|
data_frame->df_fin);
|
|
|
|
if (nr)
|
|
|
|
{
|
|
|
|
stream->read_offset += nr;
|
|
|
|
stream_consumed_bytes(stream);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
nr = 0;
|
|
|
|
|
|
|
|
if (0 == (filter->hqfi_flags & HQFI_FLAG_ERROR))
|
|
|
|
{
|
|
|
|
data_frame->df_read_off += nr;
|
|
|
|
if (filter->hqfi_state == HQFI_STATE_READING_PAYLOAD
|
|
|
|
&& filter->hqfi_type == HQFT_DATA)
|
|
|
|
return MIN(filter->hqfi_left,
|
|
|
|
(unsigned) data_frame->df_size - data_frame->df_read_off);
|
|
|
|
else
|
|
|
|
{
|
2021-09-30 15:23:56 +00:00
|
|
|
if (!((filter->hqfi_type == HQFT_HEADERS
|
|
|
|
|| filter->hqfi_type == HQFT_PUSH_PROMISE)
|
2021-06-02 04:39:15 +00:00
|
|
|
&& (filter->hqfi_flags & HQFI_FLAG_BLOCKED)))
|
|
|
|
assert(data_frame->df_read_off == data_frame->df_size);
|
2019-09-11 15:27:58 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
data_frame->df_read_off = data_frame->df_size;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
hq_decr_left (struct lsquic_stream *stream, size_t read)
|
|
|
|
{
|
|
|
|
struct hq_filter *const filter = &stream->sm_hq_filter;
|
|
|
|
|
|
|
|
if (read)
|
|
|
|
{
|
|
|
|
assert(filter->hqfi_state == HQFI_STATE_READING_PAYLOAD
|
|
|
|
&& filter->hqfi_type == HQFT_DATA);
|
|
|
|
assert(read <= filter->hqfi_left);
|
|
|
|
}
|
|
|
|
|
|
|
|
filter->hqfi_left -= read;
|
|
|
|
if (0 == filter->hqfi_left)
|
|
|
|
filter->hqfi_state = HQFI_STATE_FRAME_HEADER_BEGIN;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* These are IETF QUIC states */
|
|
|
|
enum stream_state_sending
|
|
|
|
lsquic_stream_sending_state (const struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
if (0 == (stream->stream_flags & STREAM_RST_SENT))
|
|
|
|
{
|
|
|
|
if (stream->stream_flags & STREAM_FIN_SENT)
|
|
|
|
{
|
|
|
|
if (stream->n_unacked)
|
|
|
|
return SSS_DATA_SENT;
|
|
|
|
else
|
|
|
|
return SSS_DATA_RECVD;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (stream->tosend_off
|
|
|
|
|| (stream->stream_flags & STREAM_BLOCKED_SENT))
|
|
|
|
return SSS_SEND;
|
|
|
|
else
|
|
|
|
return SSS_READY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (stream->stream_flags & STREAM_RST_ACKED)
|
|
|
|
return SSS_RESET_RECVD;
|
|
|
|
else
|
|
|
|
return SSS_RESET_SENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const char *const lsquic_sss2str[] =
|
|
|
|
{
|
|
|
|
[SSS_READY] = "Ready",
|
|
|
|
[SSS_SEND] = "Send",
|
|
|
|
[SSS_DATA_SENT] = "Data Sent",
|
|
|
|
[SSS_RESET_SENT] = "Reset Sent",
|
|
|
|
[SSS_DATA_RECVD] = "Data Recvd",
|
|
|
|
[SSS_RESET_RECVD] = "Reset Recvd",
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const char *const lsquic_ssr2str[] =
|
|
|
|
{
|
|
|
|
[SSR_RECV] = "Recv",
|
|
|
|
[SSR_SIZE_KNOWN] = "Size Known",
|
|
|
|
[SSR_DATA_RECVD] = "Data Recvd",
|
|
|
|
[SSR_RESET_RECVD] = "Reset Recvd",
|
|
|
|
[SSR_DATA_READ] = "Data Read",
|
|
|
|
[SSR_RESET_READ] = "Reset Read",
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* These are IETF QUIC states */
|
|
|
|
enum stream_state_receiving
|
|
|
|
lsquic_stream_receiving_state (struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
uint64_t n_bytes;
|
|
|
|
|
|
|
|
if (0 == (stream->stream_flags & STREAM_RST_RECVD))
|
|
|
|
{
|
|
|
|
if (0 == (stream->stream_flags & STREAM_FIN_RECVD))
|
|
|
|
return SSR_RECV;
|
|
|
|
if (stream->stream_flags & STREAM_FIN_REACHED)
|
|
|
|
return SSR_DATA_READ;
|
|
|
|
if (0 == (stream->stream_flags & STREAM_DATA_RECVD))
|
|
|
|
{
|
|
|
|
n_bytes = stream->data_in->di_if->di_readable_bytes(
|
|
|
|
stream->data_in, stream->read_offset);
|
|
|
|
if (stream->read_offset + n_bytes == stream->sm_fin_off)
|
|
|
|
{
|
|
|
|
stream->stream_flags |= STREAM_DATA_RECVD;
|
|
|
|
return SSR_DATA_RECVD;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return SSR_SIZE_KNOWN;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return SSR_DATA_RECVD;
|
|
|
|
}
|
|
|
|
else if (stream->stream_flags & STREAM_RST_READ)
|
|
|
|
return SSR_RESET_READ;
|
|
|
|
else
|
|
|
|
return SSR_RESET_RECVD;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
lsquic_stream_qdec_unblocked (struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
struct hq_filter *const filter = &stream->sm_hq_filter;
|
|
|
|
|
|
|
|
assert(stream->sm_qflags & SMQF_QPACK_DEC);
|
|
|
|
assert(filter->hqfi_flags & HQFI_FLAG_BLOCKED);
|
|
|
|
|
|
|
|
filter->hqfi_flags &= ~HQFI_FLAG_BLOCKED;
|
|
|
|
stream->conn_pub->cp_flags |= CP_STREAM_UNBLOCKED;
|
|
|
|
LSQ_DEBUG("QPACK decoder unblocked");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
lsquic_stream_is_rejected (const struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
return stream->stream_flags & STREAM_SS_RECVD;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
lsquic_stream_can_push (const struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
if (lsquic_stream_is_pushed(stream))
|
|
|
|
return 0;
|
|
|
|
else if (stream->sm_bflags & SMBF_IETF)
|
|
|
|
return (stream->sm_bflags & SMBF_USE_HEADERS)
|
|
|
|
&& !(stream->stream_flags & (STREAM_HEADERS_SENT|STREAM_NOPUSH))
|
|
|
|
&& stream->sm_send_headers_state == SSHS_BEGIN
|
|
|
|
;
|
|
|
|
else
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static size_t
|
|
|
|
pp_reader_read (void *lsqr_ctx, void *buf, size_t count)
|
|
|
|
{
|
|
|
|
struct push_promise *const promise = lsqr_ctx;
|
|
|
|
unsigned char *dst = buf;
|
2020-06-03 04:13:30 +00:00
|
|
|
unsigned char *const end = dst + count;
|
2019-09-11 15:27:58 +00:00
|
|
|
size_t len;
|
|
|
|
|
|
|
|
while (dst < end)
|
|
|
|
{
|
|
|
|
switch (promise->pp_write_state)
|
|
|
|
{
|
|
|
|
case PPWS_ID0:
|
|
|
|
case PPWS_ID1:
|
|
|
|
case PPWS_ID2:
|
|
|
|
case PPWS_ID3:
|
|
|
|
case PPWS_ID4:
|
|
|
|
case PPWS_ID5:
|
|
|
|
case PPWS_ID6:
|
|
|
|
case PPWS_ID7:
|
|
|
|
*dst++ = promise->pp_encoded_push_id[promise->pp_write_state];
|
|
|
|
++promise->pp_write_state;
|
|
|
|
break;
|
|
|
|
case PPWS_PFX0:
|
|
|
|
*dst++ = 0;
|
|
|
|
++promise->pp_write_state;
|
|
|
|
break;
|
|
|
|
case PPWS_PFX1:
|
|
|
|
*dst++ = 0;
|
|
|
|
++promise->pp_write_state;
|
|
|
|
break;
|
|
|
|
case PPWS_HBLOCK:
|
|
|
|
len = MIN(promise->pp_content_len - promise->pp_write_off,
|
|
|
|
(size_t) (end - dst));
|
|
|
|
memcpy(dst, promise->pp_content_buf + promise->pp_write_off,
|
|
|
|
len);
|
|
|
|
promise->pp_write_off += len;
|
|
|
|
dst += len;
|
|
|
|
if (promise->pp_content_len == promise->pp_write_off)
|
|
|
|
{
|
|
|
|
LSQ_LOG1(LSQ_LOG_DEBUG, "finish writing push promise %"PRIu64
|
|
|
|
": reset push state", promise->pp_id);
|
|
|
|
promise->pp_write_state = PPWS_DONE;
|
|
|
|
}
|
|
|
|
goto end;
|
|
|
|
default:
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
end:
|
|
|
|
return dst - (unsigned char *) buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static size_t
|
|
|
|
pp_reader_size (void *lsqr_ctx)
|
|
|
|
{
|
|
|
|
struct push_promise *const promise = lsqr_ctx;
|
|
|
|
size_t size;
|
|
|
|
|
|
|
|
size = 0;
|
|
|
|
switch (promise->pp_write_state)
|
|
|
|
{
|
|
|
|
case PPWS_ID0:
|
|
|
|
case PPWS_ID1:
|
|
|
|
case PPWS_ID2:
|
|
|
|
case PPWS_ID3:
|
|
|
|
case PPWS_ID4:
|
|
|
|
case PPWS_ID5:
|
|
|
|
case PPWS_ID6:
|
|
|
|
case PPWS_ID7:
|
|
|
|
size += 8 - promise->pp_write_state;
|
2020-01-06 05:47:12 +00:00
|
|
|
/* fall-through */
|
2019-09-11 15:27:58 +00:00
|
|
|
case PPWS_PFX0:
|
|
|
|
++size;
|
|
|
|
/* fall-through */
|
|
|
|
case PPWS_PFX1:
|
|
|
|
++size;
|
|
|
|
/* fall-through */
|
|
|
|
case PPWS_HBLOCK:
|
|
|
|
size += promise->pp_content_len - promise->pp_write_off;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
init_pp_reader (struct push_promise *promise, struct lsquic_reader *reader)
|
|
|
|
{
|
|
|
|
reader->lsqr_read = pp_reader_read;
|
|
|
|
reader->lsqr_size = pp_reader_size;
|
|
|
|
reader->lsqr_ctx = promise;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
on_write_pp_wrapper (struct lsquic_stream *stream, lsquic_stream_ctx_t *h)
|
|
|
|
{
|
|
|
|
struct lsquic_reader pp_reader;
|
|
|
|
struct push_promise *promise;
|
|
|
|
ssize_t nw;
|
|
|
|
int want_write;
|
|
|
|
|
|
|
|
assert(stream_is_pushing_promise(stream));
|
|
|
|
|
|
|
|
promise = SLIST_FIRST(&stream->sm_promises);
|
|
|
|
init_pp_reader(promise, &pp_reader);
|
2020-09-08 15:43:03 +00:00
|
|
|
nw = stream_write(stream, &pp_reader, SWO_BUFFER);
|
2019-09-11 15:27:58 +00:00
|
|
|
if (nw > 0)
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("wrote %zd bytes more of push promise (%s)",
|
|
|
|
nw, promise->pp_write_state == PPWS_DONE ? "done" : "not done");
|
|
|
|
if (promise->pp_write_state == PPWS_DONE)
|
|
|
|
{
|
2021-02-03 16:05:50 +00:00
|
|
|
stream->stream_flags &= ~STREAM_PUSHING;
|
2019-09-11 15:27:58 +00:00
|
|
|
/* Restore want_write flag */
|
|
|
|
want_write = !!(stream->sm_qflags & SMQF_WANT_WRITE);
|
|
|
|
if (want_write != stream->sm_saved_want_write)
|
|
|
|
(void) lsquic_stream_wantwrite(stream,
|
|
|
|
stream->sm_saved_want_write);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (nw < 0)
|
|
|
|
{
|
|
|
|
LSQ_WARN("could not write push promise (wrapper)");
|
|
|
|
/* XXX What should happen if we hit an error? TODO */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Success means that the push promise has been placed on sm_promises list and
|
|
|
|
* the stream now owns it. Failure means that the push promise should be
|
|
|
|
* destroyed by the caller.
|
|
|
|
*
|
|
|
|
* A push promise is written immediately. If it cannot be written to packets
|
|
|
|
* or buffered whole, the stream is marked as unable to push further promises.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
lsquic_stream_push_promise (struct lsquic_stream *stream,
|
|
|
|
struct push_promise *promise)
|
|
|
|
{
|
|
|
|
struct lsquic_reader pp_reader;
|
2021-02-03 16:05:50 +00:00
|
|
|
struct stream_hq_frame *shf;
|
2019-09-11 15:27:58 +00:00
|
|
|
unsigned bits, len;
|
|
|
|
ssize_t nw;
|
|
|
|
|
|
|
|
assert(stream->sm_bflags & SMBF_IETF);
|
2021-03-08 15:44:37 +00:00
|
|
|
|
|
|
|
if (stream->stream_flags & STREAM_NOPUSH)
|
|
|
|
return -1;
|
2019-09-11 15:27:58 +00:00
|
|
|
|
|
|
|
bits = vint_val2bits(promise->pp_id);
|
|
|
|
len = 1 << bits;
|
|
|
|
promise->pp_write_state = 8 - len;
|
|
|
|
vint_write(promise->pp_encoded_push_id + 8 - len, promise->pp_id,
|
|
|
|
bits, 1 << bits);
|
|
|
|
|
2021-02-03 16:05:50 +00:00
|
|
|
shf = stream_activate_hq_frame(stream,
|
2019-09-11 15:27:58 +00:00
|
|
|
stream->sm_payload + stream->sm_n_buffered, HQFT_PUSH_PROMISE,
|
2021-02-03 16:05:50 +00:00
|
|
|
SHF_FIXED_SIZE, pp_reader_size(promise));
|
|
|
|
if (!shf)
|
2019-09-11 15:27:58 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
stream->stream_flags |= STREAM_PUSHING;
|
|
|
|
|
|
|
|
init_pp_reader(promise, &pp_reader);
|
2021-02-03 16:05:50 +00:00
|
|
|
#ifdef FIU_ENABLE
|
|
|
|
if (fiu_fail("stream/fail_initial_pp_write"))
|
|
|
|
{
|
|
|
|
LSQ_NOTICE("%s: failed to write push promise (fiu)", __func__);
|
|
|
|
nw = -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
2020-09-08 15:43:03 +00:00
|
|
|
nw = stream_write(stream, &pp_reader, SWO_BUFFER);
|
2019-09-11 15:27:58 +00:00
|
|
|
if (nw > 0)
|
|
|
|
{
|
|
|
|
SLIST_INSERT_HEAD(&stream->sm_promises, promise, pp_next);
|
|
|
|
++promise->pp_refcnt;
|
|
|
|
if (promise->pp_write_state == PPWS_DONE)
|
2021-02-03 16:05:50 +00:00
|
|
|
{
|
2019-09-11 15:27:58 +00:00
|
|
|
LSQ_DEBUG("fully wrote promise %"PRIu64, promise->pp_id);
|
2021-02-03 16:05:50 +00:00
|
|
|
stream->stream_flags &= ~STREAM_PUSHING;
|
|
|
|
}
|
2019-09-11 15:27:58 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
LSQ_DEBUG("partially wrote promise %"PRIu64" (state: %d, off: %u)"
|
|
|
|
", disable further pushing", promise->pp_id,
|
|
|
|
promise->pp_write_state, promise->pp_write_off);
|
|
|
|
stream->stream_flags |= STREAM_NOPUSH;
|
|
|
|
stream->sm_saved_want_write =
|
|
|
|
!!(stream->sm_qflags & SMQF_WANT_WRITE);
|
2021-02-03 16:05:50 +00:00
|
|
|
lsquic_stream_flush(stream);
|
2019-09-11 15:27:58 +00:00
|
|
|
stream_wantwrite(stream, 1);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (nw < 0)
|
|
|
|
LSQ_WARN("failure writing push promise");
|
2021-02-03 16:05:50 +00:00
|
|
|
stream_hq_frame_put(stream, shf);
|
2019-09-11 15:27:58 +00:00
|
|
|
stream->stream_flags |= STREAM_NOPUSH;
|
|
|
|
stream->stream_flags &= ~STREAM_PUSHING;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2020-01-06 05:47:12 +00:00
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
lsquic_stream_verify_len (struct lsquic_stream *stream,
|
|
|
|
unsigned long long cont_len)
|
|
|
|
{
|
|
|
|
if ((stream->sm_bflags & (SMBF_IETF|SMBF_USE_HEADERS))
|
|
|
|
== (SMBF_IETF|SMBF_USE_HEADERS))
|
|
|
|
{
|
|
|
|
stream->sm_cont_len = cont_len;
|
|
|
|
stream->sm_bflags |= SMBF_VERIFY_CL;
|
|
|
|
LSQ_DEBUG("will verify that incoming DATA frames have %llu bytes",
|
|
|
|
cont_len);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
2020-10-07 13:41:26 +00:00
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
lsquic_stream_get_http_prio (struct lsquic_stream *stream,
|
|
|
|
struct lsquic_ext_http_prio *ehp)
|
|
|
|
{
|
|
|
|
if (stream->sm_bflags & SMBF_HTTP_PRIO)
|
|
|
|
{
|
|
|
|
ehp->urgency = MIN(stream->sm_priority, LSQUIC_MAX_HTTP_URGENCY);
|
|
|
|
ehp->incremental = !!(stream->sm_bflags & SMBF_INCREMENTAL);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
lsquic_stream_set_http_prio (struct lsquic_stream *stream,
|
|
|
|
const struct lsquic_ext_http_prio *ehp)
|
|
|
|
{
|
|
|
|
if (stream->sm_bflags & SMBF_HTTP_PRIO)
|
|
|
|
{
|
|
|
|
if (ehp->urgency > LSQUIC_MAX_HTTP_URGENCY)
|
|
|
|
{
|
|
|
|
LSQ_INFO("%s: invalid urgency: %hhu", __func__, ehp->urgency);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
stream->sm_priority = ehp->urgency;
|
|
|
|
if (ehp->incremental)
|
|
|
|
stream->sm_bflags |= SMBF_INCREMENTAL;
|
|
|
|
else
|
|
|
|
stream->sm_bflags &= ~SMBF_INCREMENTAL;
|
|
|
|
stream->sm_bflags |= SMBF_HPRIO_SET;
|
|
|
|
LSQ_DEBUG("set urgency to %hhu, incremental to %hhd", ehp->urgency,
|
|
|
|
ehp->incremental);
|
|
|
|
if (!(stream->sm_bflags & SMBF_SERVER))
|
|
|
|
return send_priority_ietf(stream);
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
2020-12-04 16:29:14 +00:00
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
lsquic_stream_has_unacked_data (struct lsquic_stream *stream)
|
|
|
|
{
|
|
|
|
return stream->n_unacked > 0 || stream->sm_n_buffered > 0;
|
|
|
|
}
|