litespeed-quic/src/liblsquic/lsquic_frame_writer.c

675 lines
19 KiB
C
Raw Normal View History

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_frame_writer.c -- write frames to HEADERS stream.
*
* The frame is first written to list of frame_buf's (frabs) and then
* out to the stream. This is done because frame's size is written out
* to the stream and we may not have enough room in the stream to fit
* the whole frame.
*/
#ifndef WIN32
2017-09-22 21:00:03 +00:00
#include <arpa/inet.h>
#endif
2017-09-22 21:00:03 +00:00
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include "lshpack.h"
2017-09-22 21:00:03 +00:00
#include "lsquic_mm.h"
#include "lsquic.h"
#include "lsquic_int_types.h"
#include "lsquic_hash.h"
#include "lsquic_conn.h"
2017-09-22 21:00:03 +00:00
#include "lsquic_frame_writer.h"
#include "lsquic_frame_common.h"
#include "lsquic_frab_list.h"
2017-09-22 21:00:03 +00:00
#include "lsquic_ev_log.h"
#include "fiu-local.h"
2017-09-22 21:00:03 +00:00
#define LSQUIC_LOGGER_MODULE LSQLM_FRAME_WRITER
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(\
lsquic_stream_conn(fw->fw_stream))
2017-09-22 21:00:03 +00:00
#include "lsquic_logger.h"
/* Size of the buffer passed to lshpack_enc_encode() -- this limits the size
* of a single compressed header field.
*/
#define MAX_COMP_HEADER_FIELD_SIZE (64 * 1024)
2017-09-22 21:00:03 +00:00
struct lsquic_frame_writer
{
struct lsquic_stream *fw_stream;
fw_writef_f fw_writef;
2017-09-22 21:00:03 +00:00
struct lsquic_mm *fw_mm;
struct lshpack_enc *fw_henc;
#if LSQUIC_CONN_STATS
struct conn_stats *fw_conn_stats;
#endif
struct frab_list fw_fral;
2017-09-22 21:00:03 +00:00
unsigned fw_max_frame_sz;
uint32_t fw_max_header_list_sz; /* 0 means unlimited */
enum {
FW_SERVER = (1 << 0),
} fw_flags;
};
/* RFC 7540, Section 4.2 */
#define MIN_MAX_FRAME_SIZE (1 << 14)
#define MAX_MAX_FRAME_SIZE ((1 << 24) - 1)
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define SETTINGS_FRAME_SZ 6
#define ABS_MIN_FRAME_SIZE MAX(SETTINGS_FRAME_SZ, \
sizeof(struct http_prio_frame))
static void *
fw_alloc (void *ctx, size_t size)
{
return lsquic_mm_get_4k(ctx);
}
2017-09-22 21:00:03 +00:00
struct lsquic_frame_writer *
lsquic_frame_writer_new (struct lsquic_mm *mm, struct lsquic_stream *stream,
unsigned max_frame_sz, struct lshpack_enc *henc, fw_writef_f writef,
#if LSQUIC_CONN_STATS
struct conn_stats *conn_stats,
#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
int is_server)
2017-09-22 21:00:03 +00:00
{
struct lsquic_frame_writer *fw;
/* When frame writer is instantiated, limit the maximum size to
* MIN_MAX_FRAME_SIZE. The reference implementation has this value
* hardcoded and QUIC does not provide a mechanism to advertise a
* different value.
*/
if (0 == max_frame_sz)
max_frame_sz = MIN_MAX_FRAME_SIZE;
else
LSQ_LOG1(LSQ_LOG_WARN, "max frame size specified to be %u bytes "
"-- this better be test code!", max_frame_sz);
if (!is_server && max_frame_sz < ABS_MIN_FRAME_SIZE)
{
LSQ_LOG1(LSQ_LOG_ERROR, "max frame size must be at least %zd bytes, "
"which is the size of priority information that client always "
"writes", ABS_MIN_FRAME_SIZE);
return NULL;
}
fw = calloc(1, sizeof(*fw));
2017-09-22 21:00:03 +00:00
if (!fw)
return NULL;
fw->fw_mm = mm;
fw->fw_henc = henc;
fw->fw_stream = stream;
fw->fw_writef = writef;
2017-09-22 21:00:03 +00:00
fw->fw_max_frame_sz = max_frame_sz;
fw->fw_max_header_list_sz = 0;
if (is_server)
fw->fw_flags = FW_SERVER;
else
fw->fw_flags = 0;
#if LSQUIC_CONN_STATS
fw->fw_conn_stats = conn_stats;
#endif
lsquic_frab_list_init(&fw->fw_fral, 0x1000, fw_alloc,
(void (*)(void *, void *)) lsquic_mm_put_4k, mm);
2017-09-22 21:00:03 +00:00
return fw;
}
void
lsquic_frame_writer_destroy (struct lsquic_frame_writer *fw)
{
lsquic_frab_list_cleanup(&fw->fw_fral);
2017-09-22 21:00:03 +00:00
free(fw);
}
int
lsquic_frame_writer_have_leftovers (const struct lsquic_frame_writer *fw)
{
return !lsquic_frab_list_empty(&fw->fw_fral);
2017-09-22 21:00:03 +00:00
}
int
lsquic_frame_writer_flush (struct lsquic_frame_writer *fw)
{
struct lsquic_reader reader = {
.lsqr_read = lsquic_frab_list_read,
.lsqr_size = lsquic_frab_list_size,
.lsqr_ctx = &fw->fw_fral,
};
ssize_t nw;
2017-09-22 21:00:03 +00:00
nw = fw->fw_writef(fw->fw_stream, &reader);
2017-09-22 21:00:03 +00:00
if (nw >= 0)
return 0;
else
return -1;
2017-09-22 21:00:03 +00:00
}
struct header_framer_ctx
{
struct lsquic_frame_writer
*hfc_fw;
struct {
struct frame_buf *frab;
unsigned short off;
} hfc_header_ptr; /* Points to byte *after* current frame header */
unsigned hfc_max_frame_sz; /* Maximum frame size. We always fill it. */
unsigned hfc_cur_sz; /* Number of bytes in the current frame. */
unsigned hfc_n_frames; /* Number of frames written. */
lsquic_stream_id_t
hfc_stream_id; /* Stream ID */
2017-09-22 21:00:03 +00:00
enum http_frame_header_flags
hfc_first_flags;
enum http_frame_type
hfc_frame_type;
};
static void
hfc_init (struct header_framer_ctx *hfc, struct lsquic_frame_writer *fw,
unsigned max_frame_sz, enum http_frame_type frame_type,
lsquic_stream_id_t stream_id, enum http_frame_header_flags first_flags)
2017-09-22 21:00:03 +00:00
{
memset(hfc, 0, sizeof(*hfc));
hfc->hfc_fw = fw;
hfc->hfc_frame_type = frame_type;
hfc->hfc_stream_id = stream_id;
hfc->hfc_first_flags = first_flags;
hfc->hfc_max_frame_sz = max_frame_sz;
hfc->hfc_cur_sz = max_frame_sz;
}
static void
hfc_save_ptr (struct header_framer_ctx *hfc)
{
hfc->hfc_header_ptr.frab = TAILQ_LAST(&hfc->hfc_fw->fw_fral.fl_frabs, frame_buf_head);
2017-09-22 21:00:03 +00:00
hfc->hfc_header_ptr.off = hfc->hfc_header_ptr.frab->frab_size;
}
static void
hfc_terminate_frame (struct header_framer_ctx *hfc,
enum http_frame_header_flags flags)
{
union {
struct http_frame_header fh;
unsigned char buf[ sizeof(struct http_frame_header) ];
} u;
uint32_t stream_id;
struct frame_buf *frab;
/* Construct the frame */
u.fh.hfh_length[0] = hfc->hfc_cur_sz >> 16;
u.fh.hfh_length[1] = hfc->hfc_cur_sz >> 8;
u.fh.hfh_length[2] = hfc->hfc_cur_sz;
u.fh.hfh_flags = flags;
if (1 == hfc->hfc_n_frames)
{
u.fh.hfh_type = hfc->hfc_frame_type;
u.fh.hfh_flags |= hfc->hfc_first_flags;
}
else
u.fh.hfh_type = HTTP_FRAME_CONTINUATION;
stream_id = htonl(hfc->hfc_stream_id);
memcpy(u.fh.hfh_stream_id, &stream_id, sizeof(stream_id));
if (hfc->hfc_header_ptr.off >= sizeof(u.fh))
{ /* Write in a single chunk */
assert(0 == memcmp("123456789", hfc->hfc_header_ptr.frab->frab_buf +
hfc->hfc_header_ptr.off - sizeof(u.buf), sizeof(u.buf)));
memcpy(hfc->hfc_header_ptr.frab->frab_buf + hfc->hfc_header_ptr.off -
sizeof(u.buf), u.buf, sizeof(u.buf));
}
else
{ /* Write across frab boundary */
memcpy(hfc->hfc_header_ptr.frab->frab_buf,
u.buf + sizeof(u.buf) - hfc->hfc_header_ptr.off,
hfc->hfc_header_ptr.off);
frab = TAILQ_PREV(hfc->hfc_header_ptr.frab, frame_buf_head, frab_next);
memcpy(frab->frab_buf + frab->frab_size - sizeof(u.buf) +
hfc->hfc_header_ptr.off, u.buf,
sizeof(u.buf) - hfc->hfc_header_ptr.off);
}
}
static int
hfc_write (struct header_framer_ctx *hfc, const void *buf, size_t sz)
{
const unsigned char *p = buf;
unsigned avail;
int s;
while (sz > 0)
{
if (hfc->hfc_max_frame_sz == hfc->hfc_cur_sz)
{
if (hfc->hfc_n_frames > 0)
hfc_terminate_frame(hfc, 0);
s = lsquic_frab_list_write(&hfc->hfc_fw->fw_fral, "123456789",
2017-09-22 21:00:03 +00:00
sizeof(struct http_frame_header));
if (s < 0)
return s;
++hfc->hfc_n_frames;
hfc_save_ptr(hfc);
hfc->hfc_cur_sz = 0;
}
avail = hfc->hfc_max_frame_sz - hfc->hfc_cur_sz;
if (sz < avail)
avail = sz;
if (avail)
{
s = lsquic_frab_list_write(&hfc->hfc_fw->fw_fral, p, avail);
2017-09-22 21:00:03 +00:00
if (s < 0)
return s;
hfc->hfc_cur_sz += avail;
sz -= avail;
p += avail;
}
}
return 0;
}
static unsigned
count_uppercase (const unsigned char *buf, size_t sz)
{
static const unsigned char uppercase[0x100] = {
['A'] = 1, ['B'] = 1, ['C'] = 1, ['D'] = 1, ['E'] = 1, ['F'] = 1,
['G'] = 1, ['H'] = 1, ['I'] = 1, ['J'] = 1, ['K'] = 1, ['L'] = 1,
['M'] = 1, ['N'] = 1, ['O'] = 1, ['P'] = 1, ['Q'] = 1, ['R'] = 1,
['S'] = 1, ['T'] = 1, ['U'] = 1, ['V'] = 1, ['W'] = 1, ['X'] = 1,
['Y'] = 1, ['Z'] = 1,
};
unsigned n_uppercase, i;
n_uppercase = 0;
for (i = 0; i < sz; ++i)
n_uppercase += uppercase[ buf[i] ];
return n_uppercase;
}
static uint32_t
calc_headers_size (const struct lsquic_http_headers *headers)
{
int i;
uint32_t size = 0;
for (i = 0; i < headers->count; ++i)
if (headers->headers[i].buf)
size += 32 + headers->headers[i].name_len +
headers->headers[i].val_len;
2017-09-22 21:00:03 +00:00
return size;
}
static int
have_oversize_strings (const struct lsquic_http_headers *headers)
{
#if LSXPACK_MAX_STRLEN > LSHPACK_MAX_STRLEN
int i, have;
for (i = 0, have = 0; i < headers->count; ++i)
{
if (headers->headers[i].buf)
{
have |= headers->headers[i].name_len > LSHPACK_MAX_STRLEN;
have |= headers->headers[i].val_len > LSHPACK_MAX_STRLEN;
}
}
return have;
#else
return 0;
#endif
}
2017-09-22 21:00:03 +00:00
static int
check_headers_size (const struct lsquic_frame_writer *fw,
const struct lsquic_http_headers *headers)
2017-09-22 21:00:03 +00:00
{
uint32_t headers_sz;
headers_sz = calc_headers_size(headers);
if (headers_sz <= fw->fw_max_header_list_sz)
return 0;
else if (fw->fw_flags & FW_SERVER)
{
LSQ_INFO("Sending headers larger (%u bytes) than max allowed (%u)",
headers_sz, fw->fw_max_header_list_sz);
return 0;
}
else
2017-09-22 21:00:03 +00:00
{
LSQ_INFO("Headers size %u is larger than max allowed (%u)",
headers_sz, fw->fw_max_header_list_sz);
errno = EMSGSIZE;
return -1;
}
}
static int
check_headers_case (const struct lsquic_frame_writer *fw,
const struct lsquic_http_headers *headers)
{
unsigned n_uppercase;
int i;
n_uppercase = 0;
for (i = 0; i < headers->count; ++i)
if (headers->headers[i].buf)
n_uppercase += count_uppercase((unsigned char *)
lsxpack_header_get_name(&headers->headers[i]),
headers->headers[i].name_len);
2017-09-22 21:00:03 +00:00
if (n_uppercase)
{
LSQ_INFO("Uppercase letters in header names");
errno = EINVAL;
return -1;
}
return 0;
}
static int
write_headers (struct lsquic_frame_writer *fw,
const struct lsquic_http_headers *headers,
struct header_framer_ctx *hfc, unsigned char *buf,
const unsigned buf_sz)
2017-09-22 21:00:03 +00:00
{
unsigned char *end;
int i, s;
for (i = 0; i < headers->count; ++i)
{
if (headers->headers[i].buf == NULL)
continue;
end = lshpack_enc_encode(fw->fw_henc, buf, buf + buf_sz,
&headers->headers[i]);
if (end > buf)
{
s = hfc_write(hfc, buf, end - buf);
if (s < 0)
return s;
#if LSQUIC_CONN_STATS
fw->fw_conn_stats->out.headers_uncomp +=
headers->headers[i].name_len
+ headers->headers[i].val_len;
fw->fw_conn_stats->out.headers_comp += end - buf;
#endif
}
else
2017-09-22 21:00:03 +00:00
{
/* Ignore errors, matching HTTP2 behavior in our server code */
2017-09-22 21:00:03 +00:00
}
}
return 0;
}
int
lsquic_frame_writer_write_headers (struct lsquic_frame_writer *fw,
lsquic_stream_id_t stream_id,
2017-09-22 21:00:03 +00:00
const struct lsquic_http_headers *headers,
int eos, unsigned weight)
{
struct header_framer_ctx hfc;
int s;
struct http_prio_frame prio_frame;
enum http_frame_header_flags flags;
unsigned char *buf;
/* Internal function: weight must be valid here */
assert(weight >= 1 && weight <= 256);
if (fw->fw_max_header_list_sz && 0 != check_headers_size(fw, headers))
2017-09-22 21:00:03 +00:00
return -1;
if (0 != check_headers_case(fw, headers))
return -1;
if (have_oversize_strings(headers))
return -1;
2017-09-22 21:00:03 +00:00
if (eos)
flags = HFHF_END_STREAM;
else
flags = 0;
if (!(fw->fw_flags & FW_SERVER))
flags |= HFHF_PRIORITY;
hfc_init(&hfc, fw, fw->fw_max_frame_sz, HTTP_FRAME_HEADERS, stream_id,
flags);
if (!(fw->fw_flags & FW_SERVER))
{
memset(&prio_frame.hpf_stream_id, 0, sizeof(prio_frame.hpf_stream_id));
prio_frame.hpf_weight = weight - 1;
s = hfc_write(&hfc, &prio_frame, sizeof(struct http_prio_frame));
if (s < 0)
return s;
}
buf = malloc(MAX_COMP_HEADER_FIELD_SIZE);
2017-09-22 21:00:03 +00:00
if (!buf)
return -1;
s = write_headers(fw, headers, &hfc, buf, MAX_COMP_HEADER_FIELD_SIZE);
free(buf);
2017-09-22 21:00:03 +00:00
if (0 == s)
{
EV_LOG_GENERATED_HTTP_HEADERS(LSQUIC_LOG_CONN_ID, stream_id,
fw->fw_flags & FW_SERVER, &prio_frame, headers);
hfc_terminate_frame(&hfc, HFHF_END_HEADERS);
return lsquic_frame_writer_flush(fw);
}
else
return s;
}
int
lsquic_frame_writer_write_promise (struct lsquic_frame_writer *fw,
lsquic_stream_id_t stream_id64, lsquic_stream_id_t promised_stream_id64,
const struct lsquic_http_headers *headers)
2017-09-22 21:00:03 +00:00
{
uint32_t stream_id = stream_id64;
uint32_t promised_stream_id = promised_stream_id64;
2017-09-22 21:00:03 +00:00
struct header_framer_ctx hfc;
struct http_push_promise_frame push_frame;
unsigned char *buf;
int s;
fiu_return_on("frame_writer/writer_promise", -1);
if (fw->fw_max_header_list_sz && 0 != check_headers_size(fw, headers))
2017-09-22 21:00:03 +00:00
return -1;
if (0 != check_headers_case(fw, headers))
return -1;
if (have_oversize_strings(headers))
return -1;
2017-09-22 21:00:03 +00:00
hfc_init(&hfc, fw, fw->fw_max_frame_sz, HTTP_FRAME_PUSH_PROMISE,
stream_id, 0);
promised_stream_id = htonl(promised_stream_id);
memcpy(push_frame.hppf_promised_id, &promised_stream_id, 4);
s = hfc_write(&hfc, &push_frame, sizeof(struct http_push_promise_frame));
if (s < 0)
return s;
buf = malloc(MAX_COMP_HEADER_FIELD_SIZE);
2017-09-22 21:00:03 +00:00
if (!buf)
return -1;
s = write_headers(fw, headers, &hfc, buf, MAX_COMP_HEADER_FIELD_SIZE);
2017-09-22 21:00:03 +00:00
if (s != 0)
{
free(buf);
2017-09-22 21:00:03 +00:00
return -1;
}
free(buf);
2017-09-22 21:00:03 +00:00
2020-06-03 04:13:30 +00:00
EV_LOG_GENERATED_HTTP_PUSH_PROMISE(LSQUIC_LOG_CONN_ID, stream_id,
htonl(promised_stream_id), headers);
hfc_terminate_frame(&hfc, HFHF_END_HEADERS);
return lsquic_frame_writer_flush(fw);
2017-09-22 21:00:03 +00:00
}
void
lsquic_frame_writer_max_header_list_size (struct lsquic_frame_writer *fw,
uint32_t max_size)
{
LSQ_DEBUG("set max_header_list_sz to %u", max_size);
fw->fw_max_header_list_sz = max_size;
}
static int
write_settings (struct lsquic_frame_writer *fw,
const struct lsquic_http2_setting *settings, unsigned n_settings)
{
struct http_frame_header fh;
unsigned payload_length;
uint32_t val;
uint16_t id;
int s;
payload_length = n_settings * 6;
memset(&fh, 0, sizeof(fh));
fh.hfh_type = HTTP_FRAME_SETTINGS;
fh.hfh_length[0] = payload_length >> 16;
fh.hfh_length[1] = payload_length >> 8;
fh.hfh_length[2] = payload_length;
s = lsquic_frab_list_write(&fw->fw_fral, &fh, sizeof(fh));
2017-09-22 21:00:03 +00:00
if (s != 0)
return s;
do
{
id = htons(settings->id);
val = htonl(settings->value);
if (0 != (s = lsquic_frab_list_write(&fw->fw_fral, &id, sizeof(id))) ||
0 != (s = lsquic_frab_list_write(&fw->fw_fral, &val, sizeof(val))))
2017-09-22 21:00:03 +00:00
return s;
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "wrote HTTP SETTINGS frame: "
"%s=%"PRIu32, lsquic_http_setting_id2str(settings->id),
settings->value);
++settings;
}
while (--n_settings);
return 0;
}
int
lsquic_frame_writer_write_settings (struct lsquic_frame_writer *fw,
const struct lsquic_http2_setting *settings, unsigned n_settings)
{
unsigned settings_per_frame;
unsigned n;
if (0 == n_settings)
{
errno = EINVAL;
return -1;
}
settings_per_frame = fw->fw_max_frame_sz / SETTINGS_FRAME_SZ;
n = 0;
do {
if (settings_per_frame > n_settings - n)
settings_per_frame = n_settings - n;
if (0 != write_settings(fw, &settings[n], settings_per_frame))
return -1;
n += settings_per_frame;
} while (n < n_settings);
return lsquic_frame_writer_flush(fw);
}
int
lsquic_frame_writer_write_priority (struct lsquic_frame_writer *fw,
lsquic_stream_id_t stream_id64, int exclusive,
lsquic_stream_id_t stream_dep_id64, unsigned weight)
2017-09-22 21:00:03 +00:00
{
uint32_t stream_id = stream_id64;
uint32_t stream_dep_id = stream_dep_id64;
2017-09-22 21:00:03 +00:00
unsigned char buf[ sizeof(struct http_frame_header) +
sizeof(struct http_prio_frame) ];
struct http_frame_header *fh = (void *) &buf[0];
struct http_prio_frame *prio_frame = (void *) &buf[sizeof(*fh)];
int s;
if (stream_dep_id & (1UL << 31))
{
LSQ_WARN("stream ID too high (%u): cannot write PRIORITY frame",
stream_dep_id);
return -1;
}
if (weight < 1 || weight > 256)
return -1;
memset(fh, 0, sizeof(*fh));
fh->hfh_type = HTTP_FRAME_PRIORITY;
fh->hfh_length[2] = sizeof(struct http_prio_frame);
stream_id = htonl(stream_id);
memcpy(fh->hfh_stream_id, &stream_id, 4);
stream_dep_id |= !!exclusive << 31;
stream_id = htonl(stream_dep_id);
memcpy(prio_frame->hpf_stream_id, &stream_id, 4);
prio_frame->hpf_weight = weight - 1;
s = lsquic_frab_list_write(&fw->fw_fral, buf, sizeof(buf));
2017-09-22 21:00:03 +00:00
if (s != 0)
return s;
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "wrote HTTP PRIORITY frame: "
"stream %"PRIu32"; weight: %u; exclusive: %d",
htonl(stream_id), weight, !!exclusive);
return lsquic_frame_writer_flush(fw);
}
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_frame_writer_mem_used (const struct lsquic_frame_writer *fw)
{
size_t size;
size = sizeof(*fw)
+ lsquic_frab_list_mem_used(&fw->fw_fral)
- sizeof(fw->fw_fral);
Latest changes - [API Change] Sendfile-like functionality is gone. The stream no longer opens files and deals with file descriptors. (Among other things, this makes the code more portable.) Three writing functions are provided: lsquic_stream_write lsquic_stream_writev lsquic_stream_writef (NEW) lsquic_stream_writef() is given an abstract reader that has function pointers for size() and read() functions which the user can implement. This is the most flexible way. lsquic_stream_write() and lsquic_stream_writev() are now both implemented as wrappers around lsquic_stream_writef(). - [OPTIMIZATION] When writing to stream, be it within or without the on_write() callback, place data directly into packet buffer, bypassing auxiliary data structures. This reduces amount of memory required, for the amount of data that can be written is limited by the congestion window. To support writes outside the on_write() callback, we keep N outgoing packet buffers per connection which can be written to by any stream. One half of these are reserved for the highest priority stream(s), the other half for all other streams. This way, low-priority streams cannot write instead of high-priority streams and, on the other hand, low-priority streams get a chance to send their packets out. The algorithm is as follows: - When user writes to stream outside of the callback: - If this is the highest priority stream, place it onto the reserved N/2 queue or fail. (The actual size of this queue is dynamic -- MAX(N/2, CWND) -- rather than N/2, allowing high-priority streams to write as much as can be sent.) - If the stream is not the highest priority, try to place the data onto the reserved N/2 queue or fail. - When tick occurs *and* more packets can be scheduled: - Transfer packets from the high N/2 queue to the scheduled queue. - If more scheduling is allowed: - Call on_write callbacks for highest-priority streams, placing resulting packets directly onto the scheduled queue. - If more scheduling is allowed: - Transfer packets from the low N/2 queue to the scheduled queue. - If more scheduling is allowed: - Call on_write callbacks for non-highest-priority streams, placing resulting packets directly onto the scheduled queue The number N is currently 20, but it could be varied based on resource usage. - If stream is created due to incoming headers, make headers readable from on_new. - Outgoing packets are no longer marked non-writeable to prevent placing more than one STREAM frame from the same stream into a single packet. This property is maintained via code flow and an explicit check. Packets for stream data are allocated using a special function. - STREAM frame elision is cheaper, as we only perform it if a reset stream has outgoing packets referencing it. - lsquic_packet_out_t is smaller, as stream_rec elements are now inside a union.
2017-10-31 13:35:58 +00:00
return size;
}