litespeed-quic/src/liblsquic/lsquic_frame_reader.c

991 lines
32 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_reader.c -- Read HTTP frames from stream
*/
#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 <ctype.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.h"
#include "lsquic_mm.h"
#include "lsquic_frame_common.h"
#include "lsquic_frame_reader.h"
#include "lsquic_http1x_if.h"
#include "lsquic_headers.h"
2017-09-22 21:00:03 +00:00
#include "lsquic_ev_log.h"
#include "lsquic_hash.h"
#include "lsquic_conn.h"
2017-09-22 21:00:03 +00:00
#define LSQUIC_LOGGER_MODULE LSQLM_FRAME_READER
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(lsquic_stream_conn(\
fr->fr_stream))
2017-09-22 21:00:03 +00:00
#include "lsquic_logger.h"
/* headers_state is used by HEADERS, PUSH_PROMISE, and CONTINUATION frames */
struct headers_state
{
enum http_frame_type
frame_type;
unsigned nread; /* Not counting pesw, only payload and padding */
/* Values parsed out from pesw buffer: */
uint32_t oth_stream_id; /* For HEADERS: ID of stream we depend on;
* for PUSH_PROMISE: promised stream ID.
*/
unsigned short weight; /* HEADERS only */
signed char exclusive; /* HEADERS only */
unsigned char pad_length;
unsigned char pseh;
/* PESW: Pad length, Exclusive, Stream Dependency, Weight. This is at
* most six bytes for HEADERS frame (RFC 7540, page 33) and five bytes
* for PUSH_PROMISE frame (Ibid, p. 40).
*/
unsigned char pesw_size;
unsigned char pesw_nread;
unsigned char pesw[6];
};
struct settings_state
{ /* RFC 7540, Section 6.5.1 */
unsigned char nread;
unsigned char set_buf[2 + 4]; /* We'll read one setting at a time */
};
struct priority_state
{ /* RFC 7540, Section 6.3 */
unsigned char nread;
union {
unsigned char prio_buf[sizeof(struct http_prio_frame)];
struct http_prio_frame prio_frame;
} u;
};
struct skip_state
{
uint32_t n_skipped;
};
struct reader_state
{
unsigned nh_read; /* Number of bytes of header read */
struct http_frame_header header;
enum {
READER_SKIP,
READER_HEADERS,
READER_PUSH_PROMISE,
READER_CONTIN,
READER_SETTINGS,
READER_PRIORITY,
} reader_type;
unsigned payload_length;
union {
struct headers_state headers_state;
struct skip_state skip_state;
struct settings_state settings_state;
struct priority_state priority_state;
} by_type;
};
struct lsquic_frame_reader
{
struct lsquic_mm *fr_mm;
struct lshpack_dec *fr_hdec;
2017-09-22 21:00:03 +00:00
struct lsquic_stream *fr_stream;
fr_stream_read_f fr_read;
const struct frame_reader_callbacks
*fr_callbacks;
void *fr_cb_ctx;
const struct lsquic_hset_if *fr_hsi_if;
void *fr_hsi_ctx;
struct http1x_ctor_ctx fr_h1x_ctor_ctx;
2017-09-22 21:00:03 +00:00
/* The the header block is shared between HEADERS, PUSH_PROMISE, and
* CONTINUATION frames. It gets added to as block fragments come in.
*/
unsigned char *fr_header_block;
#if LSQUIC_CONN_STATS
struct conn_stats *fr_conn_stats;
#endif
2017-09-22 21:00:03 +00:00
unsigned fr_header_block_sz;
unsigned fr_max_headers_sz; /* 0 means no limit */
enum frame_reader_flags fr_flags;
/* Keep some information about previous frame to catch framing errors.
*/
uint32_t fr_prev_stream_id;
enum http_frame_header_flags fr_prev_hfh_flags:8;
enum http_frame_type fr_prev_frame_type:8;
struct reader_state fr_state;
};
#define reset_state(fr) do { \
LSQ_DEBUG("reset state"); \
(fr)->fr_state.nh_read = 0; \
} while (0)
static uint32_t
fr_get_stream_id (const struct lsquic_frame_reader *fr)
{
uint32_t stream_id;
assert(fr->fr_state.nh_read >= sizeof(fr->fr_state.header));
memcpy(&stream_id, fr->fr_state.header.hfh_stream_id, sizeof(stream_id));
stream_id = ntohl(stream_id);
return stream_id;
}
static const char *
hft_to_string (enum http_frame_type hft)
{
static const char *const map[] = {
[HTTP_FRAME_DATA] = "HTTP_FRAME_DATA",
[HTTP_FRAME_HEADERS] = "HTTP_FRAME_HEADERS",
[HTTP_FRAME_PRIORITY] = "HTTP_FRAME_PRIORITY",
[HTTP_FRAME_RST_STREAM] = "HTTP_FRAME_RST_STREAM",
[HTTP_FRAME_SETTINGS] = "HTTP_FRAME_SETTINGS",
[HTTP_FRAME_PUSH_PROMISE] = "HTTP_FRAME_PUSH_PROMISE",
[HTTP_FRAME_PING] = "HTTP_FRAME_PING",
[HTTP_FRAME_GOAWAY] = "HTTP_FRAME_GOAWAY",
[HTTP_FRAME_WINDOW_UPDATE] = "HTTP_FRAME_WINDOW_UPDATE",
[HTTP_FRAME_CONTINUATION] = "HTTP_FRAME_CONTINUATION",
};
if (hft < N_HTTP_FRAME_TYPES)
return map[hft];
else
return "<unknown>";
}
struct lsquic_frame_reader *
lsquic_frame_reader_new (enum frame_reader_flags flags,
unsigned max_headers_sz,
struct lsquic_mm *mm,
struct lsquic_stream *stream, fr_stream_read_f read,
struct lshpack_dec *hdec,
2017-09-22 21:00:03 +00:00
const struct frame_reader_callbacks *cb,
void *frame_reader_cb_ctx,
#if LSQUIC_CONN_STATS
struct conn_stats *conn_stats,
#endif
const struct lsquic_hset_if *hsi_if, void *hsi_ctx)
2017-09-22 21:00:03 +00:00
{
struct lsquic_frame_reader *fr = calloc(1, sizeof(*fr));
2017-09-22 21:00:03 +00:00
if (!fr)
return NULL;
fr->fr_mm = mm;
fr->fr_hdec = hdec;
fr->fr_flags = flags;
fr->fr_stream = stream;
fr->fr_read = read;
fr->fr_callbacks = cb;
fr->fr_cb_ctx = frame_reader_cb_ctx;
fr->fr_header_block = NULL;
fr->fr_max_headers_sz = max_headers_sz;
fr->fr_hsi_if = hsi_if;
if (hsi_if == lsquic_http1x_if)
{
fr->fr_h1x_ctor_ctx = (struct http1x_ctor_ctx) {
.conn = lsquic_stream_conn(stream),
.max_headers_sz = fr->fr_max_headers_sz,
.is_server = fr->fr_flags & FRF_SERVER,
};
fr->fr_hsi_ctx = &fr->fr_h1x_ctor_ctx;
}
else
fr->fr_hsi_ctx = hsi_ctx;
2017-09-22 21:00:03 +00:00
reset_state(fr);
#if LSQUIC_CONN_STATS
fr->fr_conn_stats = conn_stats;
#endif
2017-09-22 21:00:03 +00:00
return fr;
}
void
lsquic_frame_reader_destroy (struct lsquic_frame_reader *fr)
{
free(fr->fr_header_block);
free(fr);
}
#define RETURN_ERROR(nread) do { \
assert(nread <= 0); \
if (0 == nread) \
{ \
LSQ_INFO("%s: unexpected EOF", __func__); \
return -1; \
} \
else \
{ \
LSQ_WARN("%s: error reading from stream: %s", __func__, \
strerror(errno)); \
return -1; \
} \
} while (0)
static int
prepare_for_payload (struct lsquic_frame_reader *fr)
{
uint32_t stream_id;
unsigned char *header_block;
/* RFC 7540, Section 4.1: Ignore R bit: */
fr->fr_state.header.hfh_stream_id[0] &= ~0x80;
fr->fr_state.payload_length = hfh_get_length(&fr->fr_state.header);
stream_id = fr_get_stream_id(fr);
if (fr->fr_state.header.hfh_type != HTTP_FRAME_CONTINUATION &&
(fr->fr_flags & FRF_HAVE_PREV) &&
(fr->fr_prev_frame_type == HTTP_FRAME_HEADERS ||
fr->fr_prev_frame_type == HTTP_FRAME_PUSH_PROMISE ||
fr->fr_prev_frame_type == HTTP_FRAME_CONTINUATION ) &&
0 == (fr->fr_prev_hfh_flags & HFHF_END_HEADERS))
{
LSQ_INFO("Framing error: expected CONTINUATION frame, got %u",
fr->fr_state.header.hfh_type);
fr->fr_callbacks->frc_on_error(fr->fr_cb_ctx, stream_id,
FR_ERR_EXPECTED_CONTIN);
return -1;
}
switch (fr->fr_state.header.hfh_type)
{
case HTTP_FRAME_HEADERS:
if (fr->fr_max_headers_sz &&
fr->fr_state.payload_length > fr->fr_max_headers_sz)
goto headers_too_large;
fr->fr_state.by_type.headers_state.frame_type = HTTP_FRAME_HEADERS;
fr->fr_state.by_type.headers_state.nread = 0;
fr->fr_state.by_type.headers_state.pesw_nread = 0;
fr->fr_state.by_type.headers_state.pseh = 0;
if (fr->fr_state.header.hfh_flags & HFHF_PADDED)
fr->fr_state.by_type.headers_state.pesw_size = 1;
else
{
fr->fr_state.by_type.headers_state.pad_length = 0;
fr->fr_state.by_type.headers_state.pesw_size = 0;
}
if (fr->fr_state.header.hfh_flags & HFHF_PRIORITY)
fr->fr_state.by_type.headers_state.pesw_size += 5;
else
{
fr->fr_state.by_type.headers_state.exclusive = -1;
fr->fr_state.by_type.headers_state.oth_stream_id = 0;
fr->fr_state.by_type.headers_state.weight = 0;
}
LSQ_DEBUG("pesw size: %u; payload length: %u; flags: 0x%X",
fr->fr_state.by_type.headers_state.pesw_size,
fr->fr_state.payload_length, fr->fr_state.header.hfh_flags);
if (fr->fr_state.by_type.headers_state.pesw_size >
fr->fr_state.payload_length)
{
LSQ_INFO("Invalid headers frame: payload length too small");
errno = EBADMSG;
return -1;
}
fr->fr_state.reader_type = READER_HEADERS;
break;
case HTTP_FRAME_PUSH_PROMISE:
if (fr->fr_flags & FRF_SERVER)
{
LSQ_INFO("clients should not push promised");
fr->fr_callbacks->frc_on_error(fr->fr_cb_ctx, stream_id,
FR_ERR_UNEXPECTED_PUSH);
return -1;
}
if (fr->fr_max_headers_sz &&
fr->fr_state.payload_length > fr->fr_max_headers_sz)
goto headers_too_large;
fr->fr_state.by_type.headers_state.frame_type = HTTP_FRAME_PUSH_PROMISE;
fr->fr_state.by_type.headers_state.nread = 0;
fr->fr_state.by_type.headers_state.pesw_nread = 0;
fr->fr_state.by_type.headers_state.pseh = 0;
if (fr->fr_state.header.hfh_flags & HFHF_PADDED)
fr->fr_state.by_type.headers_state.pesw_size = 5;
else
{
fr->fr_state.by_type.headers_state.pad_length = 0;
fr->fr_state.by_type.headers_state.pesw_size = 4;
}
LSQ_DEBUG("pesw size: %u; payload length: %u; flags: 0x%X",
fr->fr_state.by_type.headers_state.pesw_size,
fr->fr_state.payload_length, fr->fr_state.header.hfh_flags);
if (fr->fr_state.by_type.headers_state.pesw_size >
fr->fr_state.payload_length)
{
LSQ_INFO("Invalid headers frame: payload length too small");
errno = EBADMSG;
return -1;
}
fr->fr_state.reader_type = READER_PUSH_PROMISE;
break;
case HTTP_FRAME_CONTINUATION:
if (0 == (fr->fr_flags & FRF_HAVE_PREV))
{
LSQ_INFO("Framing error: unexpected CONTINUATION");
return -1;
}
if (!(fr->fr_prev_frame_type == HTTP_FRAME_HEADERS ||
fr->fr_prev_frame_type == HTTP_FRAME_PUSH_PROMISE ||
fr->fr_prev_frame_type == HTTP_FRAME_CONTINUATION))
{
LSQ_INFO("Framing error: unexpected CONTINUATION");
return -1;
}
if (fr->fr_prev_hfh_flags & HFHF_END_HEADERS)
{
LSQ_INFO("Framing error: unexpected CONTINUATION");
return -1;
}
if (stream_id != fr->fr_prev_stream_id)
{
LSQ_INFO("Framing error: CONTINUATION does not have matching "
"stream ID");
return -1;
}
if (fr->fr_state.reader_type == READER_SKIP)
goto continue_skipping;
fr->fr_header_block_sz += fr->fr_state.payload_length;
if (fr->fr_max_headers_sz &&
fr->fr_header_block_sz > fr->fr_max_headers_sz)
{
free(fr->fr_header_block);
fr->fr_header_block = NULL;
goto headers_too_large;
}
header_block = realloc(fr->fr_header_block, fr->fr_header_block_sz);
if (!header_block)
{
LSQ_WARN("cannot allocate %u bytes for header block",
fr->fr_header_block_sz);
fr->fr_callbacks->frc_on_error(fr->fr_cb_ctx, stream_id,
FR_ERR_OTHER_ERROR);
2017-09-22 21:00:03 +00:00
return -1;
}
fr->fr_header_block = header_block;
fr->fr_state.by_type.headers_state.nread = 0;
fr->fr_state.reader_type = READER_CONTIN;
break;
case HTTP_FRAME_SETTINGS:
if (0 == fr->fr_state.payload_length ||
0 != fr->fr_state.payload_length % 6)
{
LSQ_INFO("Framing error: %u is not a valid SETTINGS length",
fr->fr_state.payload_length);
fr->fr_callbacks->frc_on_error(fr->fr_cb_ctx, stream_id,
FR_ERR_INVALID_FRAME_SIZE);
return -1;
}
if (stream_id)
{ /* RFC 7540, Section 6.5 */
LSQ_INFO("Error: SETTINGS frame should not have stream ID set");
fr->fr_callbacks->frc_on_error(fr->fr_cb_ctx, stream_id,
FR_ERR_NONZERO_STREAM_ID);
return -1;
}
fr->fr_state.by_type.settings_state.nread = 0;
fr->fr_state.reader_type = READER_SETTINGS;
break;
case HTTP_FRAME_PRIORITY:
if (fr->fr_state.payload_length != sizeof(struct http_prio_frame))
{
LSQ_INFO("Framing error: %u is not a valid PRIORITY length",
fr->fr_state.payload_length);
fr->fr_callbacks->frc_on_error(fr->fr_cb_ctx, stream_id,
FR_ERR_INVALID_FRAME_SIZE);
return -1;
}
if (!stream_id)
{ /* RFC 7540, Section 6.3 */
LSQ_INFO("Error: PRIORITY frame must have stream ID set");
fr->fr_callbacks->frc_on_error(fr->fr_cb_ctx, stream_id,
FR_ERR_ZERO_STREAM_ID);
return -1;
}
fr->fr_state.by_type.settings_state.nread = 0;
fr->fr_state.reader_type = READER_PRIORITY;
break;
headers_too_large:
LSQ_INFO("headers are too large (%u bytes), skipping",
fr->fr_state.payload_length);
fr->fr_callbacks->frc_on_error(fr->fr_cb_ctx, stream_id,
FR_ERR_BAD_HEADER);
/* fallthru */
2017-09-22 21:00:03 +00:00
continue_skipping:
default:
fr->fr_state.by_type.skip_state.n_skipped = 0;
fr->fr_state.reader_type = READER_SKIP;
break;
}
fr->fr_flags |= FRF_HAVE_PREV;
fr->fr_prev_frame_type = fr->fr_state.header.hfh_type;
fr->fr_prev_hfh_flags = fr->fr_state.header.hfh_flags;
fr->fr_prev_stream_id = stream_id;
return 0;
}
static int
read_http_frame_header (struct lsquic_frame_reader *fr)
{
ssize_t nr;
size_t ntoread;
unsigned char *dst;
ntoread = sizeof(fr->fr_state.header) - fr->fr_state.nh_read;
dst = (unsigned char *) &fr->fr_state.header + fr->fr_state.nh_read;
nr = fr->fr_read(fr->fr_stream, dst, ntoread);
if (nr <= 0)
RETURN_ERROR(nr);
fr->fr_state.nh_read += nr;
if (fr->fr_state.nh_read == sizeof(fr->fr_state.header))
{
LSQ_DEBUG("read in frame %s", hft_to_string(fr->fr_state.header.hfh_type));
return prepare_for_payload(fr);
}
else
return 0;
}
static int
skip_payload (struct lsquic_frame_reader *fr)
{
struct skip_state *ss = &fr->fr_state.by_type.skip_state;
size_t ntoread = fr->fr_state.payload_length - ss->n_skipped;
unsigned char buf[0x100];
if (ntoread > sizeof(buf))
ntoread = sizeof(buf);
ssize_t nr = fr->fr_read(fr->fr_stream, buf, ntoread);
if (nr <= 0)
RETURN_ERROR(nr);
ss->n_skipped += nr;
if (ss->n_skipped == fr->fr_state.payload_length)
reset_state(fr);
return 0;
}
static int
skip_headers_padding (struct lsquic_frame_reader *fr)
{
unsigned char buf[0x100];
struct headers_state *hs = &fr->fr_state.by_type.headers_state;
unsigned pay_and_pad_length = fr->fr_state.payload_length - hs->pesw_size;
unsigned ntoread = pay_and_pad_length - hs->nread;
assert(ntoread <= sizeof(buf));
if (ntoread > sizeof(buf))
ntoread = sizeof(buf);
ssize_t nr = fr->fr_read(fr->fr_stream, buf, ntoread);
if (nr <= 0)
RETURN_ERROR(nr);
hs->nread += nr;
if (hs->nread == pay_and_pad_length)
reset_state(fr);
return 0;
}
static struct lsquic_stream *
find_target_stream (const struct lsquic_frame_reader *fr)
{
lsquic_stream_id_t stream_id;
struct lsquic_conn *lconn;
stream_id = fr_get_stream_id(fr);
lconn = lsquic_stream_conn(fr->fr_stream);
if (lconn->cn_if->ci_get_stream_by_id)
return lconn->cn_if->ci_get_stream_by_id(lconn, stream_id);
return NULL;
}
static void
skip_headers (struct lsquic_frame_reader *fr)
{
const unsigned char *comp, *end;
void *buf;
int s;
struct lsxpack_header xhdr;
const size_t buf_len = 64 * 1024;
buf = malloc(buf_len);
if (!buf)
{
fr->fr_callbacks->frc_on_error(fr->fr_cb_ctx, fr_get_stream_id(fr),
FR_ERR_OTHER_ERROR);
goto end;
}
comp = fr->fr_header_block;
end = comp + fr->fr_header_block_sz;
while (comp < end)
{
lsxpack_header_prepare_decode(&xhdr, buf, 0, buf_len);
s = lshpack_dec_decode(fr->fr_hdec, &comp, end, &xhdr);
if (s != 0)
{
fr->fr_callbacks->frc_on_error(fr->fr_cb_ctx, fr_get_stream_id(fr),
FR_ERR_OTHER_ERROR);
break;
}
}
end:
if (buf)
free(buf);
}
static void
2017-09-22 21:00:03 +00:00
decode_and_pass_payload (struct lsquic_frame_reader *fr)
{
struct headers_state *hs = &fr->fr_state.by_type.headers_state;
const unsigned char *comp, *end;
enum frame_reader_error err;
int s;
uint32_t stream_id32;
struct uncompressed_headers *uh = NULL;
void *hset = NULL;
struct lsxpack_header *hdr = NULL;
size_t req_space = 0;
lsquic_stream_t *target_stream = NULL;
2017-09-22 21:00:03 +00:00
if (!(fr->fr_flags & FRF_SERVER))
{
target_stream = find_target_stream(fr);
/* If the response is for a stream that cannot be found, one of two
* things is true: a) the stream has been closed or b) this is an
* error. If (a), we discard this header block. We choose to do the
* same for (b) instead of erroring out for the sake of simplicity.
* There is no way to exploit this behavior.
*/
if (!target_stream)
{
skip_headers(fr);
return;
}
}
hset = fr->fr_hsi_if->hsi_create_header_set(fr->fr_hsi_ctx, target_stream,
READER_PUSH_PROMISE == fr->fr_state.reader_type);
if (!hset)
{
err = FR_ERR_OTHER_ERROR;
2017-09-22 21:00:03 +00:00
goto stream_error;
}
2017-09-22 21:00:03 +00:00
comp = fr->fr_header_block;
end = comp + fr->fr_header_block_sz;
while (comp < end)
{
prepare:
hdr = fr->fr_hsi_if->hsi_prepare_decode(hset, hdr, req_space);
if (!hdr)
{
err = FR_ERR_OTHER_ERROR;
goto stream_error;
}
s = lshpack_dec_decode(fr->fr_hdec, &comp, end, hdr);
if (s == 0)
2017-09-22 21:00:03 +00:00
{
s = fr->fr_hsi_if->hsi_process_header(hset, hdr);
if (s == 0)
{
#if LSQUIC_CONN_STATS
fr->fr_conn_stats->in.headers_uncomp += hdr->name_len +
hdr->val_len;
#endif
req_space = 0;
hdr = NULL;
continue;
}
else if (s > 0)
err = FR_ERR_BAD_HEADER;
else
err = FR_ERR_OTHER_ERROR;
}
else if (s == LSHPACK_ERR_MORE_BUF)
{
req_space = hdr->val_len;
goto prepare;
2017-09-22 21:00:03 +00:00
}
else
err = FR_ERR_DECOMPRESS;
goto stream_error;
2017-09-22 21:00:03 +00:00
}
assert(comp == end);
s = fr->fr_hsi_if->hsi_process_header(hset, NULL);
if (s != 0)
{
err = s < 0 ? FR_ERR_OTHER_ERROR : FR_ERR_BAD_HEADER;
goto stream_error;
}
2017-09-22 21:00:03 +00:00
uh = calloc(1, sizeof(*uh));
if (!uh)
2017-09-22 21:00:03 +00:00
{
err = FR_ERR_OTHER_ERROR;
2017-09-22 21:00:03 +00:00
goto stream_error;
}
memcpy(&stream_id32, fr->fr_state.header.hfh_stream_id,
sizeof(stream_id32));
uh->uh_stream_id = ntohl(stream_id32);
uh->uh_oth_stream_id = hs->oth_stream_id;
2017-09-22 21:00:03 +00:00
if (HTTP_FRAME_HEADERS == fr->fr_state.by_type.headers_state.frame_type)
{
uh->uh_weight = hs->weight;
uh->uh_exclusive = hs->exclusive;
uh->uh_flags = 0;
2017-09-22 21:00:03 +00:00
}
else
{
assert(HTTP_FRAME_PUSH_PROMISE ==
fr->fr_state.by_type.headers_state.frame_type);
uh->uh_weight = 0; /* Zero unused value */
uh->uh_exclusive = 0; /* Zero unused value */
uh->uh_flags = UH_PP;
2017-09-22 21:00:03 +00:00
}
if (fr->fr_state.header.hfh_flags & HFHF_END_STREAM)
uh->uh_flags |= UH_FIN;
if (fr->fr_hsi_if == lsquic_http1x_if)
uh->uh_flags |= UH_H1H;
uh->uh_hset = hset;
2017-09-22 21:00:03 +00:00
EV_LOG_HTTP_HEADERS_IN(LSQUIC_LOG_CONN_ID, fr->fr_flags & FRF_SERVER, uh);
2017-09-22 21:00:03 +00:00
if (HTTP_FRAME_HEADERS == fr->fr_state.by_type.headers_state.frame_type)
fr->fr_callbacks->frc_on_headers(fr->fr_cb_ctx, uh);
2017-09-22 21:00:03 +00:00
else
fr->fr_callbacks->frc_on_push_promise(fr->fr_cb_ctx, uh);
#if LSQUIC_CONN_STATS
fr->fr_conn_stats->in.headers_comp += fr->fr_header_block_sz;
#endif
2017-09-22 21:00:03 +00:00
return;
2017-09-22 21:00:03 +00:00
stream_error:
LSQ_INFO("%s: stream error %u", __func__, err);
if (hset)
fr->fr_hsi_if->hsi_discard_header_set(hset);
2017-09-22 21:00:03 +00:00
fr->fr_callbacks->frc_on_error(fr->fr_cb_ctx, fr_get_stream_id(fr), err);
}
static int
read_headers_block_fragment (struct lsquic_frame_reader *fr)
{
struct headers_state *hs = &fr->fr_state.by_type.headers_state;
ssize_t nr;
unsigned payload_length = fr->fr_state.payload_length - hs->pesw_size -
hs->pad_length;
if (!fr->fr_header_block)
{
fr->fr_header_block_sz = payload_length;
fr->fr_header_block = malloc(payload_length);
if (!fr->fr_header_block)
return -1;
}
nr = fr->fr_read(fr->fr_stream, fr->fr_header_block + hs->nread,
fr->fr_header_block_sz - hs->nread);
if (nr <= 0)
{
free(fr->fr_header_block);
fr->fr_header_block = NULL;
RETURN_ERROR(nr);
}
hs->nread += nr;
if (hs->nread == payload_length &&
(fr->fr_state.header.hfh_flags & HFHF_END_HEADERS))
{
decode_and_pass_payload(fr);
2017-09-22 21:00:03 +00:00
free(fr->fr_header_block);
fr->fr_header_block = NULL;
}
return 0;
2017-09-22 21:00:03 +00:00
}
static int
read_headers_block_fragment_and_padding (struct lsquic_frame_reader *fr)
{
struct headers_state *hs = &fr->fr_state.by_type.headers_state;
unsigned payload_length = fr->fr_state.payload_length - hs->pesw_size;
int rv;
if (hs->nread < payload_length - hs->pad_length)
rv = read_headers_block_fragment(fr);
else if (payload_length)
rv = skip_headers_padding(fr);
else
{ /* Edge case where PESW takes up the whole frame */
fr->fr_header_block_sz = 0;
fr->fr_header_block = NULL;
rv = 0;
}
if (0 == rv && hs->nread == payload_length)
reset_state(fr);
return rv;
}
static int
read_headers_pesw (struct lsquic_frame_reader *fr)
{
struct headers_state *hs = &fr->fr_state.by_type.headers_state;
ssize_t nr = fr->fr_read(fr->fr_stream, hs->pesw + hs->pesw_nread,
hs->pesw_size - hs->pesw_nread);
if (nr <= 0)
RETURN_ERROR(nr);
hs->pesw_nread += nr;
if (hs->pesw_nread == hs->pesw_size)
{
unsigned char *p = hs->pesw;
if (fr->fr_state.header.hfh_flags & HFHF_PADDED)
hs->pad_length = *p++;
if (fr->fr_state.header.hfh_flags & HFHF_PRIORITY)
{
hs->exclusive = p[0] >> 7;
p[0] &= ~0x80; /* Note that we are modifying pesw buffer. */
memcpy(&hs->oth_stream_id, p, sizeof(hs->oth_stream_id));
hs->oth_stream_id = ntohl(hs->oth_stream_id);
p += 4;
hs->weight = 1 + *p++;
}
assert(p - hs->pesw == hs->pesw_size);
if (hs->pesw_size + hs->pad_length > fr->fr_state.payload_length)
{
LSQ_INFO("Invalid headers frame: pesw length and padding length "
"are larger than the payload length");
errno = EBADMSG;
return -1;
}
}
return 0;
}
static int
read_headers (struct lsquic_frame_reader *fr)
{
struct headers_state *hs = &fr->fr_state.by_type.headers_state;
if (hs->pesw_nread < hs->pesw_size)
return read_headers_pesw(fr);
else
return read_headers_block_fragment_and_padding(fr);
}
static int
read_push_promise_pesw (struct lsquic_frame_reader *fr)
{
struct headers_state *hs = &fr->fr_state.by_type.headers_state;
ssize_t nr = fr->fr_read(fr->fr_stream, hs->pesw + hs->pesw_nread,
hs->pesw_size - hs->pesw_nread);
if (nr <= 0)
RETURN_ERROR(nr);
hs->pesw_nread += nr;
if (hs->pesw_nread == hs->pesw_size)
{
unsigned char *p = hs->pesw;
if (fr->fr_state.header.hfh_flags & HFHF_PADDED)
hs->pad_length = *p++;
p[0] &= ~0x80; /* Clear reserved bit. Note: modifying pesw buffer. */
memcpy(&hs->oth_stream_id, p, sizeof(hs->oth_stream_id));
hs->oth_stream_id = ntohl(hs->oth_stream_id);
p += 4;
assert(p - hs->pesw == hs->pesw_size);
if (hs->pesw_size + hs->pad_length > fr->fr_state.payload_length)
{
LSQ_INFO("Invalid PUSH_PROMISE frame: pesw length and padding length "
"are larger than the payload length");
errno = EBADMSG;
return -1;
}
}
return 0;
}
static int
read_push_promise (struct lsquic_frame_reader *fr)
{
struct headers_state *hs = &fr->fr_state.by_type.headers_state;
if (hs->pesw_nread < hs->pesw_size)
return read_push_promise_pesw(fr);
else
return read_headers_block_fragment_and_padding(fr);
}
static int
read_contin (struct lsquic_frame_reader *fr)
{
struct headers_state *hs = &fr->fr_state.by_type.headers_state;
unsigned ntoread;
ssize_t nr;
ntoread = fr->fr_state.payload_length - hs->nread;
nr = fr->fr_read(fr->fr_stream,
fr->fr_header_block + fr->fr_header_block_sz - ntoread,
ntoread);
if (nr <= 0)
RETURN_ERROR(nr);
hs->nread += nr;
if (hs->nread == fr->fr_state.payload_length)
{
if (fr->fr_state.header.hfh_flags & HFHF_END_HEADERS)
{
decode_and_pass_payload(fr);
2017-09-22 21:00:03 +00:00
free(fr->fr_header_block);
fr->fr_header_block = NULL;
}
reset_state(fr);
2017-09-22 21:00:03 +00:00
}
return 0;
2017-09-22 21:00:03 +00:00
}
static int
read_settings (struct lsquic_frame_reader *fr)
{
struct settings_state *ss = &fr->fr_state.by_type.settings_state;
unsigned ntoread;
ssize_t nr;
uint32_t setting_value;
uint16_t setting_id;
ntoread = sizeof(ss->set_buf) - ss->nread;
nr = fr->fr_read(fr->fr_stream, ss->set_buf + ss->nread, ntoread);
if (nr <= 0)
RETURN_ERROR(nr);
ss->nread += nr;
if (ss->nread == sizeof(ss->set_buf))
{
memcpy(&setting_id, ss->set_buf, 2);
memcpy(&setting_value, ss->set_buf + 2, 4);
setting_id = ntohs(setting_id);
setting_value = ntohl(setting_value);
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "read HTTP SETTING %s=%"PRIu32,
lsquic_http_setting_id2str(setting_id), setting_value);
fr->fr_callbacks->frc_on_settings(fr->fr_cb_ctx, setting_id,
setting_value);
fr->fr_state.payload_length -= sizeof(ss->set_buf);
if (0 == fr->fr_state.payload_length)
reset_state(fr);
else
ss->nread = 0;
}
return 0;
}
static int
read_priority (struct lsquic_frame_reader *fr)
{
struct priority_state *ps = &fr->fr_state.by_type.priority_state;
unsigned ntoread;
ssize_t nr;
uint32_t stream_id, dep_stream_id;
int exclusive;
ntoread = sizeof(ps->u.prio_buf) - ps->nread;
nr = fr->fr_read(fr->fr_stream, ps->u.prio_buf + ps->nread, ntoread);
if (nr <= 0)
RETURN_ERROR(nr);
ps->nread += nr;
if (ps->nread == sizeof(ps->u.prio_buf))
{
memcpy(&dep_stream_id, ps->u.prio_frame.hpf_stream_id, 4);
dep_stream_id = ntohl(dep_stream_id);
exclusive = dep_stream_id >> 31;
dep_stream_id &= ~(1UL << 31);
stream_id = fr_get_stream_id(fr);
if (stream_id == dep_stream_id)
fr->fr_callbacks->frc_on_error(fr->fr_cb_ctx, stream_id,
FR_ERR_SELF_DEP_STREAM);
else
{
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "read PRIORITY frame; "
"stream: %"PRIu32", dep stream %"PRIu32", exclusive: %d, "
"weight: %u", stream_id, dep_stream_id, exclusive,
ps->u.prio_frame.hpf_weight + 1);
fr->fr_callbacks->frc_on_priority(fr->fr_cb_ctx, stream_id,
exclusive, dep_stream_id, ps->u.prio_frame.hpf_weight + 1);
}
reset_state(fr);
}
return 0;
}
static int
read_payload (struct lsquic_frame_reader *fr)
{
switch (fr->fr_state.reader_type)
{
case READER_HEADERS:
return read_headers(fr);
case READER_PUSH_PROMISE:
return read_push_promise(fr);
case READER_CONTIN:
return read_contin(fr);
case READER_SETTINGS:
return read_settings(fr);
case READER_PRIORITY:
return read_priority(fr);
default:
assert(READER_SKIP == fr->fr_state.reader_type);
return skip_payload(fr);
}
}
int
lsquic_frame_reader_read (struct lsquic_frame_reader *fr)
{
if (fr->fr_state.nh_read < sizeof(fr->fr_state.header))
return read_http_frame_header(fr);
else
return read_payload(fr);
}
Latest changes - [API Change] Sendfile-like functionality is gone. The stream no longer opens files and deals with file descriptors. (Among other things, this makes the code more portable.) Three writing functions are provided: lsquic_stream_write lsquic_stream_writev lsquic_stream_writef (NEW) lsquic_stream_writef() is given an abstract reader that has function pointers for size() and read() functions which the user can implement. This is the most flexible way. lsquic_stream_write() and lsquic_stream_writev() are now both implemented as wrappers around lsquic_stream_writef(). - [OPTIMIZATION] When writing to stream, be it within or without the on_write() callback, place data directly into packet buffer, bypassing auxiliary data structures. This reduces amount of memory required, for the amount of data that can be written is limited by the congestion window. To support writes outside the on_write() callback, we keep N outgoing packet buffers per connection which can be written to by any stream. One half of these are reserved for the highest priority stream(s), the other half for all other streams. This way, low-priority streams cannot write instead of high-priority streams and, on the other hand, low-priority streams get a chance to send their packets out. The algorithm is as follows: - When user writes to stream outside of the callback: - If this is the highest priority stream, place it onto the reserved N/2 queue or fail. (The actual size of this queue is dynamic -- MAX(N/2, CWND) -- rather than N/2, allowing high-priority streams to write as much as can be sent.) - If the stream is not the highest priority, try to place the data onto the reserved N/2 queue or fail. - When tick occurs *and* more packets can be scheduled: - Transfer packets from the high N/2 queue to the scheduled queue. - If more scheduling is allowed: - Call on_write callbacks for highest-priority streams, placing resulting packets directly onto the scheduled queue. - If more scheduling is allowed: - Transfer packets from the low N/2 queue to the scheduled queue. - If more scheduling is allowed: - Call on_write callbacks for non-highest-priority streams, placing resulting packets directly onto the scheduled queue The number N is currently 20, but it could be varied based on resource usage. - If stream is created due to incoming headers, make headers readable from on_new. - Outgoing packets are no longer marked non-writeable to prevent placing more than one STREAM frame from the same stream into a single packet. This property is maintained via code flow and an explicit check. Packets for stream data are allocated using a special function. - STREAM frame elision is cheaper, as we only perform it if a reset stream has outgoing packets referencing 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_reader_mem_used (const struct lsquic_frame_reader *fr)
{
size_t size;
size = sizeof(*fr);
if (fr->fr_header_block)
size += fr->fr_header_block_sz;
return size;
}