mirror of
https://gitea.invidious.io/iv-org/litespeed-quic.git
synced 2024-08-15 00:53:43 +00:00
1.13.0: [FEATURE, API Change] HTTP header bypass
Add ability to create custom header set objects via callbacks. This avoids reading and re-parsing headers from the stream. See test/http_client.c for example implementation. (Use -B flag to turn it on).
This commit is contained in:
parent
6f126d809b
commit
3b55e6ae0a
25 changed files with 1229 additions and 737 deletions
|
@ -1,3 +1,10 @@
|
|||
2018-08-27
|
||||
|
||||
- 1.13.0
|
||||
- [FEATURE, API Change] Add ability to create custom header set
|
||||
objects via callbacks. This avoids reading and re-parsing
|
||||
headers from the stream.
|
||||
|
||||
2018-08-27
|
||||
|
||||
- 1.12.4
|
||||
|
|
|
@ -24,8 +24,8 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
#define LSQUIC_MAJOR_VERSION 1
|
||||
#define LSQUIC_MINOR_VERSION 12
|
||||
#define LSQUIC_PATCH_VERSION 4
|
||||
#define LSQUIC_MINOR_VERSION 13
|
||||
#define LSQUIC_PATCH_VERSION 0
|
||||
|
||||
/**
|
||||
* Engine flags:
|
||||
|
@ -498,6 +498,72 @@ struct lsquic_packout_mem_if
|
|||
|
||||
struct stack_st_X509;
|
||||
|
||||
/**
|
||||
* When headers are processed, various errors may occur. They are listed
|
||||
* in this enum.
|
||||
*/
|
||||
enum lsquic_header_status
|
||||
{
|
||||
LSQUIC_HDR_OK,
|
||||
/** Duplicate pseudo-header */
|
||||
LSQUIC_HDR_ERR_DUPLICATE_PSDO_HDR,
|
||||
/** Not all request pseudo-headers are present */
|
||||
LSQUIC_HDR_ERR_INCOMPL_REQ_PSDO_HDR,
|
||||
/** Unnecessary request pseudo-header present in the response */
|
||||
LSQUIC_HDR_ERR_UNNEC_REQ_PSDO_HDR,
|
||||
/** Not all response pseudo-headers are present */
|
||||
LSQUIC_HDR_ERR_INCOMPL_RESP_PSDO_HDR,
|
||||
/** Unnecessary response pseudo-header present in the response. */
|
||||
LSQUIC_HDR_ERR_UNNEC_RESP_PSDO_HDR,
|
||||
/** Unknown pseudo-header */
|
||||
LSQUIC_HDR_ERR_UNKNOWN_PSDO_HDR,
|
||||
/** Uppercase letter in header */
|
||||
LSQUIC_HDR_ERR_UPPERCASE_HEADER,
|
||||
/** Misplaced pseudo-header */
|
||||
LSQUIC_HDR_ERR_MISPLACED_PSDO_HDR,
|
||||
/** Missing pseudo-header */
|
||||
LSQUIC_HDR_ERR_MISSING_PSDO_HDR,
|
||||
/** Header or headers are too large */
|
||||
LSQUIC_HDR_ERR_HEADERS_TOO_LARGE,
|
||||
/** Cannot allocate any more memory. */
|
||||
LSQUIC_HDR_ERR_NOMEM,
|
||||
};
|
||||
|
||||
struct lsquic_hset_if
|
||||
{
|
||||
/**
|
||||
* Create a new header set. This object is (and must be) fetched from a
|
||||
* stream by calling @ref lsquic_stream_get_hset() before the stream can
|
||||
* be read.
|
||||
*/
|
||||
void * (*hsi_create_header_set)(void *hsi_ctx,
|
||||
int is_push_promise);
|
||||
/**
|
||||
* Process new header. Return 0 on success, -1 if there is a problem with
|
||||
* the header. -1 is treated as a stream error: the associated stream is
|
||||
* reset.
|
||||
*
|
||||
* `hdr_set' is the header set object returned by
|
||||
* @ref hsi_create_header_set().
|
||||
*
|
||||
* `name_idx' is set to the index in the HPACK static table whose entry's
|
||||
* name element matches `name'. If there is no such match, `name_idx' is
|
||||
* set to zero.
|
||||
*
|
||||
* If `name' is NULL, this means that no more header are going to be
|
||||
* added to the set.
|
||||
*/
|
||||
enum lsquic_header_status (*hsi_process_header)(void *hdr_set,
|
||||
unsigned name_idx,
|
||||
const char *name, unsigned name_len,
|
||||
const char *value, unsigned value_len);
|
||||
/**
|
||||
* Discard header set. This is called for unclaimed header sets and
|
||||
* header sets that had an error.
|
||||
*/
|
||||
void (*hsi_discard_header_set)(void *hdr_set);
|
||||
};
|
||||
|
||||
/* TODO: describe this important data structure */
|
||||
typedef struct lsquic_engine_api
|
||||
{
|
||||
|
@ -525,6 +591,14 @@ typedef struct lsquic_engine_api
|
|||
int (*ea_verify_cert)(void *verify_ctx,
|
||||
struct stack_st_X509 *chain);
|
||||
void *ea_verify_ctx;
|
||||
|
||||
/**
|
||||
* Optional header set interface. If not specified, the incoming headers
|
||||
* are converted to HTTP/1.x format and are read from stream and have to
|
||||
* be parsed again.
|
||||
*/
|
||||
const struct lsquic_hset_if *ea_hsi_if;
|
||||
void *ea_hsi_ctx;
|
||||
} lsquic_engine_api_t;
|
||||
|
||||
/**
|
||||
|
@ -694,6 +768,20 @@ struct lsquic_http_headers
|
|||
int lsquic_stream_send_headers(lsquic_stream_t *s,
|
||||
const lsquic_http_headers_t *h, int eos);
|
||||
|
||||
/**
|
||||
* Get header set associated with the stream. The header set is created by
|
||||
* @ref hsi_create_header_set() callback. After this call, the ownership of
|
||||
* the header set is trasnferred to the caller.
|
||||
*
|
||||
* This call must precede calls to @ref lsquic_stream_read() and
|
||||
* @ref lsquic_stream_readv().
|
||||
*
|
||||
* If the optional header set interface (@ref ea_hsi_if) is not specified,
|
||||
* this function returns NULL.
|
||||
*/
|
||||
void *
|
||||
lsquic_stream_get_hset (lsquic_stream_t *);
|
||||
|
||||
int lsquic_conn_is_push_enabled(lsquic_conn_t *c);
|
||||
|
||||
/** Possible values for how are 0, 1, and 2. See shutdown(2). */
|
||||
|
@ -743,16 +831,15 @@ lsquic_stream_refuse_push (lsquic_stream_t *s);
|
|||
*
|
||||
* @param ref_stream_id Stream ID in response to which push promise was
|
||||
* sent.
|
||||
* @param headers Uncompressed request headers.
|
||||
* @param headers_sz Size of uncompressed request headers, not counting
|
||||
* the NUL byte.
|
||||
* @param hdr_set Header set. This object was passed to or generated
|
||||
* by @ref lsquic_conn_push_stream().
|
||||
*
|
||||
* @retval 0 Success.
|
||||
* @retval -1 This is not a pushed stream.
|
||||
*/
|
||||
int
|
||||
lsquic_stream_push_info (const lsquic_stream_t *, uint32_t *ref_stream_id,
|
||||
const char **headers, size_t *headers_sz);
|
||||
void **hdr_set);
|
||||
|
||||
/** Return current priority of the stream */
|
||||
unsigned lsquic_stream_priority (const lsquic_stream_t *s);
|
||||
|
|
|
@ -53,6 +53,7 @@ SET(lsquic_STAT_SRCS
|
|||
lsquic_min_heap.c
|
||||
lshpack.c
|
||||
lsquic_parse_Q044.c
|
||||
lsquic_http1x_if.c
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -5412,10 +5412,7 @@ lshpack_enc_cleanup (struct lshpack_enc *enc)
|
|||
|
||||
|
||||
//not find return 0, otherwise return the index
|
||||
#if !LS_HPACK_EMIT_TEST_CODE
|
||||
static
|
||||
#endif
|
||||
unsigned
|
||||
unsigned
|
||||
lshpack_enc_get_stx_tab_id (const char *name, lshpack_strlen_t name_len,
|
||||
const char *val, lshpack_strlen_t val_len, int *val_matched)
|
||||
{
|
||||
|
@ -6197,6 +6194,7 @@ struct dec_table_entry
|
|||
{
|
||||
uint16_t dte_name_len;
|
||||
uint16_t dte_val_len;
|
||||
uint8_t dte_name_idx;
|
||||
char dte_buf[0]; /* Contains both name and value */
|
||||
};
|
||||
|
||||
|
@ -6430,7 +6428,7 @@ hdec_get_table_entry (struct lshpack_dec *dec, uint32_t index)
|
|||
static
|
||||
#endif
|
||||
int
|
||||
lshpack_dec_push_entry (struct lshpack_dec *dec, const char *name,
|
||||
lshpack_dec_push_entry (struct lshpack_dec *dec, uint8_t name_idx, const char *name,
|
||||
uint16_t name_len, const char *val, uint16_t val_len)
|
||||
{
|
||||
struct dec_table_entry *entry;
|
||||
|
@ -6450,6 +6448,7 @@ lshpack_dec_push_entry (struct lshpack_dec *dec, const char *name,
|
|||
dec->hpd_cur_capacity += DYNAMIC_ENTRY_OVERHEAD + name_len + val_len;
|
||||
entry->dte_name_len = name_len;
|
||||
entry->dte_val_len = val_len;
|
||||
entry->dte_name_idx = name_idx;
|
||||
memcpy(DTE_NAME(entry), name, name_len);
|
||||
memcpy(DTE_VALUE(entry), val, val_len);
|
||||
return 0;
|
||||
|
@ -6459,7 +6458,8 @@ lshpack_dec_push_entry (struct lshpack_dec *dec, const char *name,
|
|||
int
|
||||
lshpack_dec_decode (struct lshpack_dec *dec,
|
||||
const unsigned char **src, const unsigned char *src_end,
|
||||
char *dst, char *const dst_end, uint16_t *name_len, uint16_t *val_len)
|
||||
char *dst, char *const dst_end, uint16_t *name_len, uint16_t *val_len,
|
||||
uint32_t *name_idx)
|
||||
{
|
||||
struct dec_table_entry *entry;
|
||||
uint32_t index, new_capacity;
|
||||
|
@ -6532,6 +6532,7 @@ lshpack_dec_decode (struct lshpack_dec *dec,
|
|||
|
||||
indexed_type = 1;
|
||||
}
|
||||
*name_idx = index;
|
||||
|
||||
char *const name = dst;
|
||||
if (index > 0)
|
||||
|
@ -6562,6 +6563,8 @@ lshpack_dec_decode (struct lshpack_dec *dec,
|
|||
|
||||
*name_len = entry->dte_name_len;
|
||||
memcpy(name, DTE_NAME(entry), *name_len);
|
||||
if (entry->dte_name_idx)
|
||||
*name_idx = entry->dte_name_idx;
|
||||
if (indexed_type == 3)
|
||||
{
|
||||
if (entry->dte_name_len + entry->dte_val_len > dst_end - dst)
|
||||
|
@ -6592,7 +6595,9 @@ lshpack_dec_decode (struct lshpack_dec *dec,
|
|||
|
||||
if (indexed_type == 0)
|
||||
{
|
||||
if (0 != lshpack_dec_push_entry(dec, name, *name_len,
|
||||
if (index > HPACK_STATIC_TABLE_SIZE)
|
||||
index = 0;
|
||||
if (0 != lshpack_dec_push_entry(dec, index, name, *name_len,
|
||||
name + *name_len, *val_len))
|
||||
return -1; //error
|
||||
}
|
||||
|
|
|
@ -101,7 +101,7 @@ int
|
|||
lshpack_dec_decode (struct lshpack_dec *dec,
|
||||
const unsigned char **src, const unsigned char *src_end,
|
||||
char *dst, char *const dst_end, lshpack_strlen_t *name_len,
|
||||
lshpack_strlen_t *val_len);
|
||||
lshpack_strlen_t *val_len, uint32_t *name_idx);
|
||||
|
||||
void
|
||||
lshpack_dec_set_max_capacity (struct lshpack_dec *, unsigned);
|
||||
|
@ -156,6 +156,10 @@ struct lshpack_dec
|
|||
struct lshpack_arr hpd_dyn_table;
|
||||
};
|
||||
|
||||
unsigned
|
||||
lshpack_enc_get_stx_tab_id (const char *name, lshpack_strlen_t name_len,
|
||||
const char *val, lshpack_strlen_t val_len, int *val_matched);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
#include "lsquic_hash.h"
|
||||
#include "lsquic_attq.h"
|
||||
#include "lsquic_min_heap.h"
|
||||
#include "lsquic_http1x_if.h"
|
||||
|
||||
#define LSQUIC_LOGGER_MODULE LSQLM_ENGINE
|
||||
#include "lsquic_logger.h"
|
||||
|
@ -325,6 +326,16 @@ lsquic_engine_new (unsigned flags,
|
|||
engine->stream_if_ctx = api->ea_stream_if_ctx;
|
||||
engine->packets_out = api->ea_packets_out;
|
||||
engine->packets_out_ctx = api->ea_packets_out_ctx;
|
||||
if (api->ea_hsi_if)
|
||||
{
|
||||
engine->pub.enp_hsi_if = api->ea_hsi_if;
|
||||
engine->pub.enp_hsi_ctx = api->ea_hsi_ctx;
|
||||
}
|
||||
else
|
||||
{
|
||||
engine->pub.enp_hsi_if = lsquic_http1x_if;
|
||||
engine->pub.enp_hsi_ctx = NULL;
|
||||
}
|
||||
if (api->ea_pmi)
|
||||
{
|
||||
engine->pub.enp_pmi = api->ea_pmi;
|
||||
|
|
|
@ -14,6 +14,8 @@ struct stack_st_X509;
|
|||
struct lsquic_engine_public {
|
||||
struct lsquic_mm enp_mm;
|
||||
struct lsquic_engine_settings enp_settings;
|
||||
const struct lsquic_hset_if *enp_hsi_if;
|
||||
void *enp_hsi_ctx;
|
||||
int (*enp_verify_cert)(void *verify_ctx,
|
||||
struct stack_st_X509 *chain);
|
||||
void *enp_verify_ctx;
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#include "lsquic_packet_out.h"
|
||||
#include "lsquic_parse.h"
|
||||
#include "lsquic_frame_common.h"
|
||||
#include "lsquic_frame_reader.h"
|
||||
#include "lsquic_headers.h"
|
||||
#include "lsquic_str.h"
|
||||
#include "lsquic_handshake.h"
|
||||
#include "lsquic_ev_log.h"
|
||||
|
@ -208,6 +208,7 @@ void
|
|||
lsquic_ev_log_http_headers_in (lsquic_cid_t cid, int is_server,
|
||||
const struct uncompressed_headers *uh)
|
||||
{
|
||||
const struct http1x_headers *h1h;
|
||||
const char *cr, *p;
|
||||
|
||||
if (uh->uh_flags & UH_PP)
|
||||
|
@ -220,13 +221,17 @@ lsquic_ev_log_http_headers_in (lsquic_cid_t cid, int is_server,
|
|||
uh->uh_stream_id, uh->uh_oth_stream_id, uh->uh_weight,
|
||||
(int) uh->uh_exclusive, !!(uh->uh_flags & UH_FIN));
|
||||
|
||||
for (p = uh->uh_headers; p < uh->uh_headers + uh->uh_size; p = cr + 2)
|
||||
if (uh->uh_flags & UH_H1H)
|
||||
{
|
||||
cr = strchr(p, '\r');
|
||||
if (cr && cr > p)
|
||||
LCID(" %.*s", (int) (cr - p), p);
|
||||
else
|
||||
break;
|
||||
h1h = uh->uh_hset;
|
||||
for (p = h1h->h1h_buf; p < h1h->h1h_buf + h1h->h1h_size; p = cr + 2)
|
||||
{
|
||||
cr = strchr(p, '\r');
|
||||
if (cr && cr > p)
|
||||
LCID(" %.*s", (int) (cr - p), p);
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#include "lsquic_mm.h"
|
||||
#include "lsquic_frame_common.h"
|
||||
#include "lsquic_frame_reader.h"
|
||||
#include "lsquic_http1x_if.h"
|
||||
#include "lsquic_headers.h"
|
||||
#include "lsquic_ev_log.h"
|
||||
|
||||
#define LSQUIC_LOGGER_MODULE LSQLM_FRAME_READER
|
||||
|
@ -26,27 +28,6 @@
|
|||
#include "lsquic_logger.h"
|
||||
|
||||
|
||||
enum pseudo_header
|
||||
{
|
||||
PSEH_METHOD,
|
||||
PSEH_SCHEME,
|
||||
PSEH_AUTHORITY,
|
||||
PSEH_PATH,
|
||||
PSEH_STATUS,
|
||||
N_PSEH
|
||||
};
|
||||
|
||||
#define BIT(x) (1 << (x))
|
||||
|
||||
#define ALL_REQUEST_PSEH (BIT(PSEH_METHOD)|BIT(PSEH_SCHEME)|BIT(PSEH_AUTHORITY)|BIT(PSEH_PATH))
|
||||
#define REQUIRED_REQUEST_PSEH (BIT(PSEH_METHOD)|BIT(PSEH_SCHEME)|BIT(PSEH_PATH))
|
||||
|
||||
#define ALL_SERVER_PSEH BIT(PSEH_STATUS)
|
||||
#define REQUIRED_SERVER_PSEH ALL_SERVER_PSEH
|
||||
|
||||
#define PSEH_LEN(h) (sizeof(#h) - 5)
|
||||
|
||||
|
||||
/* headers_state is used by HEADERS, PUSH_PROMISE, and CONTINUATION frames */
|
||||
struct headers_state
|
||||
{
|
||||
|
@ -128,6 +109,9 @@ struct lsquic_frame_reader
|
|||
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;
|
||||
/* The the header block is shared between HEADERS, PUSH_PROMISE, and
|
||||
* CONTINUATION frames. It gets added to as block fragments come in.
|
||||
*/
|
||||
|
@ -190,7 +174,8 @@ lsquic_frame_reader_new (enum frame_reader_flags flags,
|
|||
struct lsquic_stream *stream, fr_stream_read_f read,
|
||||
struct lshpack_dec *hdec,
|
||||
const struct frame_reader_callbacks *cb,
|
||||
void *frame_reader_cb_ctx)
|
||||
void *frame_reader_cb_ctx,
|
||||
const struct lsquic_hset_if *hsi_if, void *hsi_ctx)
|
||||
{
|
||||
struct lsquic_frame_reader *fr = malloc(sizeof(*fr));
|
||||
if (!fr)
|
||||
|
@ -204,6 +189,18 @@ lsquic_frame_reader_new (enum frame_reader_flags flags,
|
|||
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) {
|
||||
.cid = LSQUIC_LOG_CONN_ID,
|
||||
.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;
|
||||
reset_state(fr);
|
||||
return fr;
|
||||
}
|
||||
|
@ -500,423 +497,6 @@ skip_headers_padding (struct lsquic_frame_reader *fr)
|
|||
}
|
||||
|
||||
|
||||
struct header_writer_ctx
|
||||
{
|
||||
struct uncompressed_headers *uh;
|
||||
struct lsquic_mm *mm;
|
||||
char *buf;
|
||||
char *cookie_val;
|
||||
unsigned cookie_sz, cookie_nalloc;
|
||||
unsigned max_headers_sz,
|
||||
headers_sz,
|
||||
w_off;
|
||||
enum {
|
||||
HWC_EXPECT_COLON = (1 << 0),
|
||||
HWC_SEEN_HOST = (1 << 1),
|
||||
} hwc_flags;
|
||||
enum pseudo_header pseh_mask;
|
||||
char *pseh_bufs[N_PSEH];
|
||||
lshpack_strlen_t name_len,
|
||||
val_len;
|
||||
};
|
||||
|
||||
|
||||
#define HWC_PSEH_LEN(hwc, ph) ((int) strlen((hwc)->pseh_bufs[ph]))
|
||||
|
||||
#define HWC_PSEH_VAL(hwc, ph) ((hwc)->pseh_bufs[ph])
|
||||
|
||||
static int
|
||||
hwc_uh_write (struct header_writer_ctx *hwc, const void *buf, size_t sz)
|
||||
{
|
||||
struct uncompressed_headers *uh;
|
||||
|
||||
if (hwc->w_off + sz > hwc->headers_sz)
|
||||
{
|
||||
if (hwc->headers_sz * 2 >= hwc->w_off + sz)
|
||||
hwc->headers_sz *= 2;
|
||||
else
|
||||
hwc->headers_sz = hwc->w_off + sz;
|
||||
uh = realloc(hwc->uh, sizeof(*hwc->uh) + hwc->headers_sz);
|
||||
if (!uh)
|
||||
return -1;
|
||||
hwc->uh = uh;
|
||||
}
|
||||
memcpy(hwc->uh->uh_headers + hwc->w_off, buf, sz);
|
||||
hwc->w_off += sz;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static enum frame_reader_error
|
||||
init_hwc (struct header_writer_ctx *hwc, struct lsquic_mm *mm,
|
||||
unsigned max_headers_sz, unsigned headers_block_sz)
|
||||
{
|
||||
memset(hwc, 0, sizeof(*hwc));
|
||||
hwc->hwc_flags = HWC_EXPECT_COLON;
|
||||
hwc->max_headers_sz = max_headers_sz;
|
||||
hwc->headers_sz = headers_block_sz * 4; /* A guess */
|
||||
hwc->uh = malloc(sizeof(*hwc->uh) + hwc->headers_sz);
|
||||
if (!hwc->uh)
|
||||
return FR_ERR_NOMEM;
|
||||
hwc->mm = mm;
|
||||
hwc->buf = lsquic_mm_get_16k(mm);
|
||||
if (!hwc->buf)
|
||||
return FR_ERR_NOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
deinit_hwc (struct header_writer_ctx *hwc)
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < sizeof(hwc->pseh_bufs) / sizeof(hwc->pseh_bufs[0]); ++i)
|
||||
if (hwc->pseh_bufs[i])
|
||||
free(hwc->pseh_bufs[i]);
|
||||
if (hwc->cookie_val)
|
||||
free(hwc->cookie_val);
|
||||
free(hwc->uh);
|
||||
if (hwc->buf)
|
||||
lsquic_mm_put_16k(hwc->mm, hwc->buf);
|
||||
}
|
||||
|
||||
|
||||
static enum frame_reader_error
|
||||
save_pseudo_header (const struct lsquic_frame_reader *fr,
|
||||
struct header_writer_ctx *hwc, enum pseudo_header ph)
|
||||
{
|
||||
if (0 == (hwc->pseh_mask & BIT(ph)))
|
||||
{
|
||||
assert(!hwc->pseh_bufs[ph]);
|
||||
hwc->pseh_bufs[ph] = malloc(hwc->val_len + 1);
|
||||
if (!hwc->pseh_bufs[ph])
|
||||
return FR_ERR_NOMEM;
|
||||
hwc->pseh_mask |= BIT(ph);
|
||||
memcpy(hwc->pseh_bufs[ph], hwc->buf + hwc->name_len, hwc->val_len);
|
||||
hwc->pseh_bufs[ph][hwc->val_len] = '\0';
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
LSQ_INFO("header %u is already present", ph);
|
||||
return FR_ERR_DUPLICATE_PSEH;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static enum frame_reader_error
|
||||
add_pseudo_header_to_uh (const struct lsquic_frame_reader *fr,
|
||||
struct header_writer_ctx *hwc)
|
||||
{
|
||||
if (!(hwc->hwc_flags & HWC_EXPECT_COLON))
|
||||
{
|
||||
LSQ_INFO("unexpected colon");
|
||||
return FR_ERR_MISPLACED_PSEH;
|
||||
}
|
||||
|
||||
switch (hwc->name_len)
|
||||
{
|
||||
case 5:
|
||||
if (0 == memcmp(hwc->buf, ":path", 5))
|
||||
return save_pseudo_header(fr, hwc, PSEH_PATH);
|
||||
break;
|
||||
case 7:
|
||||
switch (hwc->buf[2])
|
||||
{
|
||||
case 'c':
|
||||
if (0 == memcmp(hwc->buf, ":scheme", 7))
|
||||
return save_pseudo_header(fr, hwc, PSEH_SCHEME);
|
||||
break;
|
||||
case 'e':
|
||||
if (0 == memcmp(hwc->buf, ":method", 7))
|
||||
return save_pseudo_header(fr, hwc, PSEH_METHOD);
|
||||
break;
|
||||
case 't':
|
||||
if (0 == memcmp(hwc->buf, ":status", 7))
|
||||
return save_pseudo_header(fr, hwc, PSEH_STATUS);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 10:
|
||||
if (0 == memcmp(hwc->buf, ":authority", 10))
|
||||
return save_pseudo_header(fr, hwc, PSEH_AUTHORITY);
|
||||
break;
|
||||
}
|
||||
|
||||
LSQ_INFO("unknown pseudo-header `%.*s'", hwc->name_len, hwc->buf);
|
||||
return FR_ERR_UNKNOWN_PSEH;
|
||||
}
|
||||
|
||||
|
||||
#define HTTP_CODE_LEN 3
|
||||
|
||||
static const char *
|
||||
code_str_to_reason (const char code_str[HTTP_CODE_LEN])
|
||||
{
|
||||
/* RFC 7231, Section 6: */
|
||||
static const char *const http_reason_phrases[] =
|
||||
{
|
||||
#define HTTP_REASON_CODE(code, reason) [code - 100] = reason
|
||||
HTTP_REASON_CODE(100, "Continue"),
|
||||
HTTP_REASON_CODE(101, "Switching Protocols"),
|
||||
HTTP_REASON_CODE(200, "OK"),
|
||||
HTTP_REASON_CODE(201, "Created"),
|
||||
HTTP_REASON_CODE(202, "Accepted"),
|
||||
HTTP_REASON_CODE(203, "Non-Authoritative Information"),
|
||||
HTTP_REASON_CODE(204, "No Content"),
|
||||
HTTP_REASON_CODE(205, "Reset Content"),
|
||||
HTTP_REASON_CODE(206, "Partial Content"),
|
||||
HTTP_REASON_CODE(300, "Multiple Choices"),
|
||||
HTTP_REASON_CODE(301, "Moved Permanently"),
|
||||
HTTP_REASON_CODE(302, "Found"),
|
||||
HTTP_REASON_CODE(303, "See Other"),
|
||||
HTTP_REASON_CODE(304, "Not Modified"),
|
||||
HTTP_REASON_CODE(305, "Use Proxy"),
|
||||
HTTP_REASON_CODE(307, "Temporary Redirect"),
|
||||
HTTP_REASON_CODE(400, "Bad Request"),
|
||||
HTTP_REASON_CODE(401, "Unauthorized"),
|
||||
HTTP_REASON_CODE(402, "Payment Required"),
|
||||
HTTP_REASON_CODE(403, "Forbidden"),
|
||||
HTTP_REASON_CODE(404, "Not Found"),
|
||||
HTTP_REASON_CODE(405, "Method Not Allowed"),
|
||||
HTTP_REASON_CODE(406, "Not Acceptable"),
|
||||
HTTP_REASON_CODE(407, "Proxy Authentication Required"),
|
||||
HTTP_REASON_CODE(408, "Request Timeout"),
|
||||
HTTP_REASON_CODE(409, "Conflict"),
|
||||
HTTP_REASON_CODE(410, "Gone"),
|
||||
HTTP_REASON_CODE(411, "Length Required"),
|
||||
HTTP_REASON_CODE(412, "Precondition Failed"),
|
||||
HTTP_REASON_CODE(413, "Payload Too Large"),
|
||||
HTTP_REASON_CODE(414, "URI Too Long"),
|
||||
HTTP_REASON_CODE(415, "Unsupported Media Type"),
|
||||
HTTP_REASON_CODE(416, "Range Not Satisfiable"),
|
||||
HTTP_REASON_CODE(417, "Expectation Failed"),
|
||||
HTTP_REASON_CODE(426, "Upgrade Required"),
|
||||
HTTP_REASON_CODE(500, "Internal Server Error"),
|
||||
HTTP_REASON_CODE(501, "Not Implemented"),
|
||||
HTTP_REASON_CODE(502, "Bad Gateway"),
|
||||
HTTP_REASON_CODE(503, "Service Unavailable"),
|
||||
HTTP_REASON_CODE(504, "Gateway Timeout"),
|
||||
HTTP_REASON_CODE(505, "HTTP Version Not Supported"),
|
||||
#undef HTTP_REASON_CODE
|
||||
};
|
||||
|
||||
long code;
|
||||
char code_buf[HTTP_CODE_LEN + 1];
|
||||
|
||||
memcpy(code_buf, code_str, HTTP_CODE_LEN);
|
||||
code_buf[HTTP_CODE_LEN] = '\0';
|
||||
code = strtol(code_buf, NULL, 10) - 100;
|
||||
if (code > 0 && code < (long) (sizeof(http_reason_phrases) /
|
||||
sizeof(http_reason_phrases[0])))
|
||||
return http_reason_phrases[code];
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static enum frame_reader_error
|
||||
convert_response_pseudo_headers (const struct lsquic_frame_reader *fr,
|
||||
struct header_writer_ctx *hwc)
|
||||
{
|
||||
if ((hwc->pseh_mask & REQUIRED_SERVER_PSEH) != REQUIRED_SERVER_PSEH)
|
||||
{
|
||||
LSQ_INFO("not all response pseudo-headers are specified");
|
||||
return FR_ERR_INCOMPL_RESP_PSEH;
|
||||
}
|
||||
if (hwc->pseh_mask & ALL_REQUEST_PSEH)
|
||||
{
|
||||
LSQ_INFO("response pseudo-headers contain request-only headers");
|
||||
return FR_ERR_UNNEC_REQ_PSEH;
|
||||
}
|
||||
|
||||
const char *code_str, *reason;
|
||||
int code_len;
|
||||
|
||||
code_str = HWC_PSEH_VAL(hwc, PSEH_STATUS);
|
||||
code_len = HWC_PSEH_LEN(hwc, PSEH_STATUS);
|
||||
|
||||
#define HWC_UH_WRITE(h, buf, sz) do { \
|
||||
if (0 != hwc_uh_write(h, buf, sz)) \
|
||||
return FR_ERR_NOMEM; \
|
||||
} while (0)
|
||||
|
||||
HWC_UH_WRITE(hwc, "HTTP/1.1 ", 9);
|
||||
HWC_UH_WRITE(hwc, code_str, code_len);
|
||||
if (HTTP_CODE_LEN == code_len && (reason = code_str_to_reason(code_str)))
|
||||
{
|
||||
HWC_UH_WRITE(hwc, " ", 1);
|
||||
HWC_UH_WRITE(hwc, reason, strlen(reason));
|
||||
HWC_UH_WRITE(hwc, "\r\n", 2);
|
||||
}
|
||||
else
|
||||
HWC_UH_WRITE(hwc, " \r\n", 3);
|
||||
if (hwc->max_headers_sz && hwc->w_off > hwc->max_headers_sz)
|
||||
{
|
||||
LSQ_INFO("headers too large");
|
||||
return FR_ERR_HEADERS_TOO_LARGE;
|
||||
}
|
||||
return 0;
|
||||
|
||||
#undef HWC_UH_WRITE
|
||||
}
|
||||
|
||||
|
||||
static enum frame_reader_error
|
||||
convert_request_pseudo_headers (const struct lsquic_frame_reader *fr,
|
||||
struct header_writer_ctx *hwc)
|
||||
{
|
||||
if ((hwc->pseh_mask & REQUIRED_REQUEST_PSEH) != REQUIRED_REQUEST_PSEH)
|
||||
{
|
||||
LSQ_INFO("not all request pseudo-headers are specified");
|
||||
return FR_ERR_INCOMPL_REQ_PSEH;
|
||||
}
|
||||
if (hwc->pseh_mask & ALL_SERVER_PSEH)
|
||||
{
|
||||
LSQ_INFO("request pseudo-headers contain response-only headers");
|
||||
return FR_ERR_UNNEC_RESP_PSEH;
|
||||
}
|
||||
|
||||
#define HWC_UH_WRITE(h, buf, sz) do { \
|
||||
if (0 != hwc_uh_write(h, buf, sz)) \
|
||||
return FR_ERR_NOMEM; \
|
||||
} while (0)
|
||||
|
||||
HWC_UH_WRITE(hwc, HWC_PSEH_VAL(hwc, PSEH_METHOD), HWC_PSEH_LEN(hwc, PSEH_METHOD));
|
||||
HWC_UH_WRITE(hwc, " ", 1);
|
||||
HWC_UH_WRITE(hwc, HWC_PSEH_VAL(hwc, PSEH_PATH), HWC_PSEH_LEN(hwc, PSEH_PATH));
|
||||
HWC_UH_WRITE(hwc, " HTTP/1.1\r\n", 11);
|
||||
|
||||
if (hwc->max_headers_sz && hwc->w_off > hwc->max_headers_sz)
|
||||
{
|
||||
LSQ_INFO("headers too large");
|
||||
return FR_ERR_HEADERS_TOO_LARGE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
#undef HWC_UH_WRITE
|
||||
}
|
||||
|
||||
|
||||
static enum frame_reader_error
|
||||
convert_pseudo_headers (const struct lsquic_frame_reader *fr,
|
||||
struct header_writer_ctx *hwc)
|
||||
{
|
||||
/* We are *reading* the message. Thus, a server expects a request, and a
|
||||
* client expects a response. Unless we receive a push promise from the
|
||||
* server, in which case this should also be a request.
|
||||
*/
|
||||
if ((fr->fr_flags & FRF_SERVER) ||
|
||||
READER_PUSH_PROMISE == fr->fr_state.reader_type)
|
||||
return convert_request_pseudo_headers(fr, hwc);
|
||||
else
|
||||
return convert_response_pseudo_headers(fr, hwc);
|
||||
}
|
||||
|
||||
|
||||
static enum frame_reader_error
|
||||
save_cookie (struct header_writer_ctx *hwc)
|
||||
{
|
||||
char *cookie_val;
|
||||
|
||||
if (0 == hwc->cookie_sz)
|
||||
{
|
||||
hwc->cookie_nalloc = hwc->cookie_sz = hwc->val_len;
|
||||
cookie_val = malloc(hwc->cookie_nalloc);
|
||||
if (!cookie_val)
|
||||
return FR_ERR_NOMEM;
|
||||
hwc->cookie_val = cookie_val;
|
||||
memcpy(hwc->cookie_val, hwc->buf + hwc->name_len, hwc->val_len);
|
||||
}
|
||||
else
|
||||
{
|
||||
hwc->cookie_sz += hwc->val_len + 2 /* "; " */;
|
||||
if (hwc->cookie_sz > hwc->cookie_nalloc)
|
||||
{
|
||||
hwc->cookie_nalloc = hwc->cookie_nalloc * 2 + hwc->val_len + 2;
|
||||
cookie_val = realloc(hwc->cookie_val, hwc->cookie_nalloc);
|
||||
if (!cookie_val)
|
||||
return FR_ERR_NOMEM;
|
||||
hwc->cookie_val = cookie_val;
|
||||
}
|
||||
memcpy(hwc->cookie_val + hwc->cookie_sz - hwc->val_len - 2, "; ", 2);
|
||||
memcpy(hwc->cookie_val + hwc->cookie_sz - hwc->val_len,
|
||||
hwc->buf + hwc->name_len, hwc->val_len);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static enum frame_reader_error
|
||||
add_real_header_to_uh (const struct lsquic_frame_reader *fr,
|
||||
struct header_writer_ctx *hwc)
|
||||
{
|
||||
enum frame_reader_error err;
|
||||
unsigned i;
|
||||
int n_upper;
|
||||
|
||||
if (hwc->hwc_flags & HWC_EXPECT_COLON)
|
||||
{
|
||||
if (0 != (err = convert_pseudo_headers(fr, hwc)))
|
||||
return err;
|
||||
hwc->hwc_flags &= ~HWC_EXPECT_COLON;
|
||||
}
|
||||
|
||||
if (4 == hwc->name_len && 0 == memcmp(hwc->buf, "host", 4))
|
||||
hwc->hwc_flags |= HWC_SEEN_HOST;
|
||||
|
||||
n_upper = 0;
|
||||
for (i = 0; i < hwc->name_len; ++i)
|
||||
n_upper += isupper(hwc->buf[i]);
|
||||
if (n_upper > 0)
|
||||
{
|
||||
LSQ_INFO("Header name `%.*s' contains uppercase letters",
|
||||
hwc->name_len, hwc->buf);
|
||||
return FR_ERR_UPPERCASE_HEADER;
|
||||
}
|
||||
|
||||
if (6 == hwc->name_len && memcmp(hwc->buf, "cookie", 6) == 0)
|
||||
{
|
||||
return save_cookie(hwc);
|
||||
}
|
||||
|
||||
#define HWC_UH_WRITE(h, buf, sz) do { \
|
||||
if (0 != hwc_uh_write(h, buf, sz)) \
|
||||
return FR_ERR_NOMEM; \
|
||||
} while (0)
|
||||
|
||||
HWC_UH_WRITE(hwc, hwc->buf, hwc->name_len);
|
||||
HWC_UH_WRITE(hwc, ": ", 2);
|
||||
HWC_UH_WRITE(hwc, hwc->buf + hwc->name_len, hwc->val_len);
|
||||
HWC_UH_WRITE(hwc, "\r\n", 2);
|
||||
|
||||
if (hwc->max_headers_sz && hwc->w_off > hwc->max_headers_sz)
|
||||
{
|
||||
LSQ_INFO("headers too large");
|
||||
return FR_ERR_HEADERS_TOO_LARGE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
#undef HWC_UH_WRITE
|
||||
}
|
||||
|
||||
|
||||
static enum frame_reader_error
|
||||
add_header_to_uh (const struct lsquic_frame_reader *fr,
|
||||
struct header_writer_ctx *hwc)
|
||||
{
|
||||
LSQ_DEBUG("Got header '%.*s': '%.*s'", hwc->name_len, hwc->buf,
|
||||
hwc->val_len, hwc->buf + hwc->name_len);
|
||||
if (':' == hwc->buf[0])
|
||||
return add_pseudo_header_to_uh(fr, hwc);
|
||||
else
|
||||
return add_real_header_to_uh(fr, hwc);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
decode_and_pass_payload (struct lsquic_frame_reader *fr)
|
||||
{
|
||||
|
@ -924,11 +504,26 @@ decode_and_pass_payload (struct lsquic_frame_reader *fr)
|
|||
const unsigned char *comp, *end;
|
||||
enum frame_reader_error err;
|
||||
int s;
|
||||
struct header_writer_ctx hwc;
|
||||
uint32_t name_idx;
|
||||
lshpack_strlen_t name_len, val_len;
|
||||
char *buf;
|
||||
struct uncompressed_headers *uh = NULL;
|
||||
void *hset = NULL;
|
||||
|
||||
err = init_hwc(&hwc, fr->fr_mm, fr->fr_max_headers_sz, fr->fr_header_block_sz);
|
||||
if (0 != err)
|
||||
buf = lsquic_mm_get_16k(fr->fr_mm);
|
||||
if (!buf)
|
||||
{
|
||||
err = FR_ERR_NOMEM;
|
||||
goto stream_error;
|
||||
}
|
||||
|
||||
hset = fr->fr_hsi_if->hsi_create_header_set(fr->fr_hsi_ctx,
|
||||
READER_PUSH_PROMISE == fr->fr_state.reader_type);
|
||||
if (!hset)
|
||||
{
|
||||
err = FR_ERR_NOMEM;
|
||||
goto stream_error;
|
||||
}
|
||||
|
||||
comp = fr->fr_header_block;
|
||||
end = comp + fr->fr_header_block_sz;
|
||||
|
@ -936,11 +531,12 @@ decode_and_pass_payload (struct lsquic_frame_reader *fr)
|
|||
while (comp < end)
|
||||
{
|
||||
s = lshpack_dec_decode(fr->fr_hdec, &comp, end,
|
||||
hwc.buf, hwc.buf + 16 * 1024,
|
||||
&hwc.name_len, &hwc.val_len);
|
||||
buf, buf + 16 * 1024, &name_len, &val_len, &name_idx);
|
||||
if (s == 0)
|
||||
{
|
||||
err = add_header_to_uh(fr, &hwc);
|
||||
err = (enum frame_reader_error)
|
||||
fr->fr_hsi_if->hsi_process_header(hset, name_idx, buf,
|
||||
name_len, buf + name_len, val_len);
|
||||
if (err == 0)
|
||||
continue;
|
||||
}
|
||||
|
@ -949,93 +545,63 @@ decode_and_pass_payload (struct lsquic_frame_reader *fr)
|
|||
goto stream_error;
|
||||
}
|
||||
assert(comp == end);
|
||||
lsquic_mm_put_16k(fr->fr_mm, buf);
|
||||
buf = NULL;
|
||||
|
||||
if (hwc.hwc_flags & HWC_EXPECT_COLON)
|
||||
err = (enum frame_reader_error)
|
||||
fr->fr_hsi_if->hsi_process_header(hset, 0, 0, 0, 0, 0);
|
||||
if (err)
|
||||
goto stream_error;
|
||||
|
||||
uh = calloc(1, sizeof(*uh));
|
||||
if (!uh)
|
||||
{
|
||||
err = convert_pseudo_headers(fr, &hwc);
|
||||
if (0 != err)
|
||||
goto stream_error;
|
||||
hwc.hwc_flags &= ~HWC_EXPECT_COLON;
|
||||
}
|
||||
|
||||
|
||||
#define HWC_UH_WRITE(h, buf, sz) do { \
|
||||
err = hwc_uh_write(h, buf, sz); \
|
||||
if (0 != err) \
|
||||
goto stream_error; \
|
||||
} while (0)
|
||||
|
||||
if ((hwc.pseh_mask & BIT(PSEH_AUTHORITY)) &&
|
||||
0 == (hwc.hwc_flags & HWC_SEEN_HOST))
|
||||
{
|
||||
LSQ_DEBUG("Setting 'Host: %.*s'", HWC_PSEH_LEN(&hwc, PSEH_AUTHORITY),
|
||||
HWC_PSEH_VAL(&hwc, PSEH_AUTHORITY));
|
||||
HWC_UH_WRITE(&hwc, "Host: ", 6);
|
||||
HWC_UH_WRITE(&hwc, HWC_PSEH_VAL(&hwc, PSEH_AUTHORITY), HWC_PSEH_LEN(&hwc, PSEH_AUTHORITY));
|
||||
HWC_UH_WRITE(&hwc, "\r\n", 2);
|
||||
}
|
||||
|
||||
if (hwc.cookie_val)
|
||||
{
|
||||
LSQ_DEBUG("Setting 'Cookie: %.*s'", hwc.cookie_sz, hwc.cookie_val);
|
||||
HWC_UH_WRITE(&hwc, "Cookie: ", 8);
|
||||
HWC_UH_WRITE(&hwc, hwc.cookie_val, hwc.cookie_sz);
|
||||
HWC_UH_WRITE(&hwc, "\r\n", 2);
|
||||
}
|
||||
|
||||
HWC_UH_WRITE(&hwc, "\r\n", 2 + 1 /* NUL byte */);
|
||||
hwc.w_off -= 1; /* Do not count NUL byte */
|
||||
|
||||
if (hwc.max_headers_sz && hwc.w_off > hwc.max_headers_sz)
|
||||
{
|
||||
LSQ_INFO("headers too large");
|
||||
err = FR_ERR_HEADERS_TOO_LARGE;
|
||||
err = FR_ERR_NOMEM;
|
||||
goto stream_error;
|
||||
}
|
||||
|
||||
memcpy(&hwc.uh->uh_stream_id, fr->fr_state.header.hfh_stream_id,
|
||||
sizeof(hwc.uh->uh_stream_id));
|
||||
hwc.uh->uh_stream_id = ntohl(hwc.uh->uh_stream_id);
|
||||
hwc.uh->uh_size = hwc.w_off;
|
||||
hwc.uh->uh_oth_stream_id = hs->oth_stream_id;
|
||||
hwc.uh->uh_off = 0;
|
||||
memcpy(&uh->uh_stream_id, fr->fr_state.header.hfh_stream_id,
|
||||
sizeof(uh->uh_stream_id));
|
||||
uh->uh_stream_id = ntohl(uh->uh_stream_id);
|
||||
uh->uh_oth_stream_id = hs->oth_stream_id;
|
||||
if (HTTP_FRAME_HEADERS == fr->fr_state.by_type.headers_state.frame_type)
|
||||
{
|
||||
hwc.uh->uh_weight = hs->weight;
|
||||
hwc.uh->uh_exclusive = hs->exclusive;
|
||||
hwc.uh->uh_flags = 0;
|
||||
uh->uh_weight = hs->weight;
|
||||
uh->uh_exclusive = hs->exclusive;
|
||||
uh->uh_flags = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(HTTP_FRAME_PUSH_PROMISE ==
|
||||
fr->fr_state.by_type.headers_state.frame_type);
|
||||
hwc.uh->uh_weight = 0; /* Zero unused value */
|
||||
hwc.uh->uh_exclusive = 0; /* Zero unused value */
|
||||
hwc.uh->uh_flags = UH_PP;
|
||||
uh->uh_weight = 0; /* Zero unused value */
|
||||
uh->uh_exclusive = 0; /* Zero unused value */
|
||||
uh->uh_flags = UH_PP;
|
||||
}
|
||||
if (fr->fr_state.header.hfh_flags & HFHF_END_STREAM)
|
||||
hwc.uh->uh_flags |= UH_FIN;
|
||||
uh->uh_flags |= UH_FIN;
|
||||
if (fr->fr_hsi_if == lsquic_http1x_if)
|
||||
uh->uh_flags |= UH_H1H;
|
||||
uh->uh_hset = hset;
|
||||
|
||||
EV_LOG_HTTP_HEADERS_IN(LSQUIC_LOG_CONN_ID, fr->fr_flags & FRF_SERVER,
|
||||
hwc.uh);
|
||||
EV_LOG_HTTP_HEADERS_IN(LSQUIC_LOG_CONN_ID, fr->fr_flags & FRF_SERVER, uh);
|
||||
if (HTTP_FRAME_HEADERS == fr->fr_state.by_type.headers_state.frame_type)
|
||||
fr->fr_callbacks->frc_on_headers(fr->fr_cb_ctx, hwc.uh);
|
||||
fr->fr_callbacks->frc_on_headers(fr->fr_cb_ctx, uh);
|
||||
else
|
||||
fr->fr_callbacks->frc_on_push_promise(fr->fr_cb_ctx, hwc.uh);
|
||||
|
||||
hwc.uh = NULL;
|
||||
|
||||
deinit_hwc(&hwc);
|
||||
fr->fr_callbacks->frc_on_push_promise(fr->fr_cb_ctx, uh);
|
||||
|
||||
return 0;
|
||||
|
||||
stream_error:
|
||||
LSQ_INFO("%s: stream error %u", __func__, err);
|
||||
deinit_hwc(&hwc);
|
||||
if (hset)
|
||||
fr->fr_hsi_if->hsi_discard_header_set(hset);
|
||||
if (uh)
|
||||
free(uh);
|
||||
if (buf)
|
||||
lsquic_mm_put_16k(fr->fr_mm, buf);
|
||||
fr->fr_callbacks->frc_on_error(fr->fr_cb_ctx, fr_get_stream_id(fr), err);
|
||||
return 0;
|
||||
|
||||
#undef HWC_UH_WRITE
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@ struct lshpack_dec;
|
|||
struct lsquic_mm;
|
||||
struct lsquic_stream;
|
||||
struct lsquic_frame_reader;
|
||||
struct lsquic_hset_if;
|
||||
struct uncompressed_headers;
|
||||
|
||||
|
||||
enum frame_reader_flags
|
||||
|
@ -31,19 +33,18 @@ enum frame_reader_flags
|
|||
*/
|
||||
enum frame_reader_error
|
||||
{
|
||||
FR_ERR_DUPLICATE_PSEH = 1, /* Duplicate pseudo-header */
|
||||
FR_ERR_INCOMPL_REQ_PSEH, /* Not all request pseudo-headers are present */
|
||||
FR_ERR_UNNEC_REQ_PSEH, /* Unnecessary request pseudo-header present in
|
||||
* the response.
|
||||
*/
|
||||
FR_ERR_INCOMPL_RESP_PSEH, /* Not all response pseudo-headers are present */
|
||||
FR_ERR_UNNEC_RESP_PSEH, /* Unnecessary response pseudo-header present in
|
||||
* the response.
|
||||
*/
|
||||
FR_ERR_UNKNOWN_PSEH, /* Unknown pseudo-header */
|
||||
FR_ERR_UPPERCASE_HEADER, /* Uppercase letter in header */
|
||||
FR_ERR_MISPLACED_PSEH,
|
||||
FR_ERR_MISSING_PSEH,
|
||||
FR_ERR_DUPLICATE_PSEH = LSQUIC_HDR_ERR_DUPLICATE_PSDO_HDR,
|
||||
FR_ERR_INCOMPL_REQ_PSEH = LSQUIC_HDR_ERR_INCOMPL_REQ_PSDO_HDR,
|
||||
FR_ERR_UNNEC_REQ_PSEH = LSQUIC_HDR_ERR_UNNEC_REQ_PSDO_HDR,
|
||||
FR_ERR_INCOMPL_RESP_PSEH = LSQUIC_HDR_ERR_INCOMPL_RESP_PSDO_HDR,
|
||||
FR_ERR_UNNEC_RESP_PSEH = LSQUIC_HDR_ERR_UNNEC_RESP_PSDO_HDR,
|
||||
FR_ERR_UNKNOWN_PSEH = LSQUIC_HDR_ERR_UNKNOWN_PSDO_HDR,
|
||||
FR_ERR_UPPERCASE_HEADER = LSQUIC_HDR_ERR_UPPERCASE_HEADER,
|
||||
FR_ERR_MISPLACED_PSEH = LSQUIC_HDR_ERR_MISPLACED_PSDO_HDR,
|
||||
FR_ERR_MISSING_PSEH = LSQUIC_HDR_ERR_MISSING_PSDO_HDR,
|
||||
FR_ERR_HEADERS_TOO_LARGE = LSQUIC_HDR_ERR_HEADERS_TOO_LARGE,
|
||||
FR_ERR_NOMEM = LSQUIC_HDR_ERR_NOMEM,
|
||||
|
||||
FR_ERR_DECOMPRESS,
|
||||
FR_ERR_INVALID_FRAME_SIZE, /* E.g. a SETTINGS frame length is not a multiple
|
||||
* of 6 (RFC 7540, Section 6.5.1).
|
||||
|
@ -53,45 +54,11 @@ enum frame_reader_error
|
|||
FR_ERR_SELF_DEP_STREAM, /* A stream in priority frame cannot depend on
|
||||
* itself (RFC 7540, Section 5.3.1).
|
||||
*/
|
||||
FR_ERR_HEADERS_TOO_LARGE,
|
||||
FR_ERR_UNEXPECTED_PUSH,
|
||||
FR_ERR_NOMEM, /* Cannot allocate any more memory. */
|
||||
FR_ERR_EXPECTED_CONTIN, /* Expected continuation frame. */
|
||||
};
|
||||
|
||||
|
||||
/* This struct is used to return decoded HEADERS and PUSH_PROMISE frames.
|
||||
* Some of the fields are only used for HEADERS frames. They are marked
|
||||
* with "H" comment below.
|
||||
*/
|
||||
struct uncompressed_headers
|
||||
{
|
||||
uint32_t uh_stream_id;
|
||||
uint32_t uh_oth_stream_id; /* For HEADERS frame, the ID of the
|
||||
* stream that this stream depends
|
||||
* on. (Zero means unset.) For
|
||||
* PUSH_PROMISE, the promised stream
|
||||
* ID.
|
||||
*/
|
||||
unsigned uh_size; /* Number of characters in uh_headers, not
|
||||
* counting the NUL byte.
|
||||
*/
|
||||
unsigned /* H */ uh_off;
|
||||
unsigned short /* H */ uh_weight; /* 1 - 256; 0 means not set */
|
||||
signed char /* H */ uh_exclusive; /* 0 or 1 when set; -1 means not set */
|
||||
enum {
|
||||
/* H */ UH_FIN = (1 << 0),
|
||||
UH_PP = (1 << 1), /* Push promise */
|
||||
} uh_flags:8;
|
||||
char uh_headers[ /* NUL-terminated C string */
|
||||
#if FRAME_READER_TESTING
|
||||
FRAME_READER_TESTING
|
||||
#else
|
||||
0
|
||||
#endif
|
||||
];
|
||||
};
|
||||
|
||||
struct frame_reader_callbacks
|
||||
{
|
||||
void (*frc_on_headers) (void *frame_cb_ctx, struct uncompressed_headers *);
|
||||
|
@ -111,8 +78,8 @@ struct lsquic_frame_reader *
|
|||
lsquic_frame_reader_new (enum frame_reader_flags, unsigned max_headers_sz,
|
||||
struct lsquic_mm *, struct lsquic_stream *,
|
||||
fr_stream_read_f, struct lshpack_dec *,
|
||||
const struct frame_reader_callbacks *,
|
||||
void *fr_cb_ctx);
|
||||
const struct frame_reader_callbacks *, void *fr_cb_ctx,
|
||||
const struct lsquic_hset_if *, void *hsi_ctx);
|
||||
|
||||
int
|
||||
lsquic_frame_reader_read (struct lsquic_frame_reader *);
|
||||
|
|
|
@ -50,8 +50,6 @@ struct frame_buf
|
|||
#define frab_left_to_write(f) ((unsigned short) sizeof((f)->frab_buf) - (f)->frab_size)
|
||||
#define frab_write_to(f) ((f)->frab_buf + (f)->frab_size)
|
||||
|
||||
#define MAX_HEADERS_SIZE (64 * 1024)
|
||||
|
||||
/* Make sure that frab_buf is at least five bytes long, otherwise a frame
|
||||
* won't fit into two adjacent frabs.
|
||||
*/
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define MAX_HEADERS_SIZE (64 * 1024)
|
||||
|
||||
struct iovec;
|
||||
struct lshpack_enc;
|
||||
struct lsquic_mm;
|
||||
|
|
|
@ -42,12 +42,15 @@
|
|||
#include "lsquic_headers_stream.h"
|
||||
#include "lsquic_frame_common.h"
|
||||
#include "lsquic_frame_reader.h"
|
||||
#include "lsquic_frame_writer.h"
|
||||
#include "lsquic_http1x_if.h"
|
||||
#include "lsquic_mm.h"
|
||||
#include "lsquic_engine_public.h"
|
||||
#include "lsquic_spi.h"
|
||||
#include "lsquic_ev_log.h"
|
||||
#include "lsquic_version.h"
|
||||
#include "lsquic_hash.h"
|
||||
#include "lsquic_headers.h"
|
||||
|
||||
#include "lsquic_conn.h"
|
||||
#include "lsquic_conn_public.h"
|
||||
|
@ -595,7 +598,7 @@ new_conn_common (lsquic_cid_t cid, struct lsquic_engine_public *enpub,
|
|||
if (conn->fc_flags & FC_HTTP)
|
||||
{
|
||||
conn->fc_pub.hs = lsquic_headers_stream_new(
|
||||
!!(conn->fc_flags & FC_SERVER), conn->fc_pub.mm, conn->fc_settings,
|
||||
!!(conn->fc_flags & FC_SERVER), conn->fc_enpub,
|
||||
headers_callbacks_ptr, conn);
|
||||
if (!conn->fc_pub.hs)
|
||||
goto cleanup_on_error;
|
||||
|
|
43
src/liblsquic/lsquic_headers.h
Normal file
43
src/liblsquic/lsquic_headers.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
/* Copyright (c) 2017 - 2018 LiteSpeed Technologies Inc. See LICENSE. */
|
||||
#ifndef LSQUIC_HEADERS_H
|
||||
#define LSQUIC_HEADERS_H 1
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* When ea_hsi_if is not specified, the headers are converted to a C string
|
||||
* that contains HTTP/1.x-like header structure.
|
||||
*/
|
||||
struct http1x_headers
|
||||
{
|
||||
unsigned h1h_size; /* Number of characters in h1h_buf, not
|
||||
* counting the NUL byte.
|
||||
*/
|
||||
unsigned h1h_off; /* Reading offset */
|
||||
char *h1h_buf;
|
||||
};
|
||||
|
||||
|
||||
/* This struct is used to return decoded HEADERS and PUSH_PROMISE frames.
|
||||
* Some of the fields are only used for HEADERS frames. They are marked
|
||||
* with "H" comment below.
|
||||
*/
|
||||
struct uncompressed_headers
|
||||
{
|
||||
uint32_t uh_stream_id;
|
||||
uint32_t uh_oth_stream_id; /* For HEADERS frame, the ID of the
|
||||
* stream that this stream depends
|
||||
* on. (Zero means unset.) For
|
||||
* PUSH_PROMISE, the promised stream
|
||||
* ID.
|
||||
*/
|
||||
unsigned short /* H */ uh_weight; /* 1 - 256; 0 means not set */
|
||||
signed char /* H */ uh_exclusive; /* 0 or 1 when set; -1 means not set */
|
||||
enum {
|
||||
/* H */ UH_FIN = (1 << 0),
|
||||
UH_PP = (1 << 1), /* Push promise */
|
||||
UH_H1H = (1 << 2), /* uh_hset points to http1x_headers */
|
||||
} uh_flags:8;
|
||||
void *uh_hset;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -13,12 +13,15 @@
|
|||
#include <vc_compat.h>
|
||||
#endif
|
||||
|
||||
#include "lsquic.h"
|
||||
#include "lsquic_types.h"
|
||||
#include "lsquic_int_types.h"
|
||||
#include "lsquic_frame_common.h"
|
||||
#include "lsquic_frame_reader.h"
|
||||
#include "lsquic_frame_writer.h"
|
||||
#include "lsquic_mm.h"
|
||||
#include "lsquic_engine_public.h"
|
||||
#include "lshpack.h"
|
||||
#include "lsquic.h"
|
||||
|
||||
#include "lsquic_headers_stream.h"
|
||||
|
||||
|
@ -38,16 +41,14 @@ struct headers_stream
|
|||
struct lsquic_frame_writer *hs_fw;
|
||||
const struct headers_stream_callbacks
|
||||
*hs_callbacks;
|
||||
const struct lsquic_engine_settings
|
||||
*hs_settings;
|
||||
void *hs_cb_ctx;
|
||||
struct lsquic_mm *hs_mm;
|
||||
struct lshpack_enc hs_henc;
|
||||
struct lshpack_dec hs_hdec;
|
||||
enum {
|
||||
HS_IS_SERVER = (1 << 0),
|
||||
HS_HENC_INITED = (1 << 1),
|
||||
} hs_flags;
|
||||
struct lsquic_engine_public *hs_enpub;
|
||||
};
|
||||
|
||||
|
||||
|
@ -85,18 +86,18 @@ headers_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream)
|
|||
hs->hs_stream = stream;
|
||||
LSQ_DEBUG("stream created");
|
||||
hs->hs_fr = lsquic_frame_reader_new((hs->hs_flags & HS_IS_SERVER) ? FRF_SERVER : 0,
|
||||
MAX_HEADERS_SIZE, hs->hs_mm,
|
||||
MAX_HEADERS_SIZE, &hs->hs_enpub->enp_mm,
|
||||
stream, lsquic_stream_read, &hs->hs_hdec,
|
||||
frame_callbacks_ptr, hs);
|
||||
frame_callbacks_ptr, hs,
|
||||
hs->hs_enpub->enp_hsi_if, hs->hs_enpub->enp_hsi_ctx);
|
||||
if (!hs->hs_fr)
|
||||
{
|
||||
LSQ_WARN("could not create frame reader: %s", strerror(errno));
|
||||
hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
|
||||
return NULL;
|
||||
}
|
||||
hs->hs_fw = lsquic_frame_writer_new(hs->hs_mm, stream, 0, &hs->hs_henc,
|
||||
lsquic_stream_write,
|
||||
(hs->hs_flags & HS_IS_SERVER));
|
||||
hs->hs_fw = lsquic_frame_writer_new(&hs->hs_enpub->enp_mm, stream, 0,
|
||||
&hs->hs_henc, lsquic_stream_write, (hs->hs_flags & HS_IS_SERVER));
|
||||
if (!hs->hs_fw)
|
||||
{
|
||||
LSQ_WARN("could not create frame writer: %s", strerror(errno));
|
||||
|
@ -193,8 +194,7 @@ lsquic_headers_stream_send_priority (struct headers_stream *hs,
|
|||
|
||||
|
||||
struct headers_stream *
|
||||
lsquic_headers_stream_new (int is_server, struct lsquic_mm *mm,
|
||||
const struct lsquic_engine_settings *settings,
|
||||
lsquic_headers_stream_new (int is_server, struct lsquic_engine_public *enpub,
|
||||
const struct headers_stream_callbacks *callbacks,
|
||||
void *cb_ctx)
|
||||
{
|
||||
|
@ -207,8 +207,7 @@ lsquic_headers_stream_new (int is_server, struct lsquic_mm *mm,
|
|||
hs->hs_flags = HS_IS_SERVER;
|
||||
else
|
||||
hs->hs_flags = 0;
|
||||
hs->hs_settings = settings;
|
||||
hs->hs_mm = mm;
|
||||
hs->hs_enpub = enpub;
|
||||
return hs;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ struct lsquic_http_headers;
|
|||
struct lsquic_frame_reader;
|
||||
struct lsquic_frame_writer;
|
||||
struct uncompressed_headers;
|
||||
struct lsquic_engine_settings;
|
||||
struct lsquic_engine_public;
|
||||
struct lsquic_http2_setting;
|
||||
|
||||
|
||||
|
@ -38,8 +38,7 @@ struct headers_stream_callbacks
|
|||
|
||||
|
||||
struct headers_stream *
|
||||
lsquic_headers_stream_new (int is_server, struct lsquic_mm *,
|
||||
const struct lsquic_engine_settings *,
|
||||
lsquic_headers_stream_new (int is_server, struct lsquic_engine_public *,
|
||||
const struct headers_stream_callbacks *,
|
||||
void *hs_cb_ctx);
|
||||
|
||||
|
|
525
src/liblsquic/lsquic_http1x_if.c
Normal file
525
src/liblsquic/lsquic_http1x_if.c
Normal file
|
@ -0,0 +1,525 @@
|
|||
/* Copyright (c) 2017 - 2018 LiteSpeed Technologies Inc. See LICENSE. */
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lsquic.h"
|
||||
#include "lsquic_headers.h"
|
||||
#include "lsquic_http1x_if.h"
|
||||
#include "lshpack.h"
|
||||
|
||||
#define LSQUIC_LOGGER_MODULE LSQLM_HTTP1X
|
||||
#define LSQUIC_LOG_CONN_ID hwc->hwc_cid
|
||||
#include "lsquic_logger.h"
|
||||
|
||||
enum pseudo_header
|
||||
{
|
||||
PSEH_METHOD,
|
||||
PSEH_SCHEME,
|
||||
PSEH_AUTHORITY,
|
||||
PSEH_PATH,
|
||||
PSEH_STATUS,
|
||||
N_PSEH
|
||||
};
|
||||
|
||||
#define BIT(x) (1 << (x))
|
||||
|
||||
#define ALL_REQUEST_PSEH (BIT(PSEH_METHOD)|BIT(PSEH_SCHEME)|BIT(PSEH_AUTHORITY)|BIT(PSEH_PATH))
|
||||
#define REQUIRED_REQUEST_PSEH (BIT(PSEH_METHOD)|BIT(PSEH_SCHEME)|BIT(PSEH_PATH))
|
||||
|
||||
#define ALL_SERVER_PSEH BIT(PSEH_STATUS)
|
||||
#define REQUIRED_SERVER_PSEH ALL_SERVER_PSEH
|
||||
|
||||
#define PSEH_LEN(h) (sizeof(#h) - 5)
|
||||
|
||||
struct header_writer_ctx
|
||||
{
|
||||
lsquic_cid_t hwc_cid;
|
||||
char *buf;
|
||||
char *cookie_val;
|
||||
unsigned cookie_sz, cookie_nalloc;
|
||||
unsigned max_headers_sz,
|
||||
headers_sz,
|
||||
w_off;
|
||||
enum {
|
||||
HWC_SERVER = 1 << 0,
|
||||
HWC_EXPECT_COLON = 1 << 1,
|
||||
HWC_SEEN_HOST = 1 << 2,
|
||||
HWC_PUSH_PROMISE = 1 << 3,
|
||||
} hwc_flags;
|
||||
enum pseudo_header pseh_mask;
|
||||
char *pseh_bufs[N_PSEH];
|
||||
struct http1x_headers hwc_h1h;
|
||||
};
|
||||
|
||||
|
||||
#define HWC_PSEH_LEN(hwc, ph) ((int) strlen((hwc)->pseh_bufs[ph]))
|
||||
|
||||
#define HWC_PSEH_VAL(hwc, ph) ((hwc)->pseh_bufs[ph])
|
||||
|
||||
static void *
|
||||
h1h_create_header_set (void *ctx, int is_push_promise)
|
||||
{
|
||||
const struct http1x_ctor_ctx *hcc = ctx;
|
||||
struct header_writer_ctx *hwc;
|
||||
|
||||
hwc = calloc(1, sizeof(*hwc));
|
||||
if (!hwc)
|
||||
return NULL;
|
||||
|
||||
hwc->hwc_flags = HWC_EXPECT_COLON;
|
||||
if (hcc->is_server)
|
||||
hwc->hwc_flags |= HWC_SERVER;
|
||||
if (is_push_promise)
|
||||
hwc->hwc_flags |= HWC_PUSH_PROMISE;
|
||||
hwc->max_headers_sz = hcc->max_headers_sz;
|
||||
hwc->hwc_cid = hcc->cid;
|
||||
return &hwc->hwc_h1h;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
hwc_uh_write (struct header_writer_ctx *hwc, const void *buf, size_t sz)
|
||||
{
|
||||
char *h1h_buf;
|
||||
|
||||
if (hwc->w_off + sz > hwc->headers_sz)
|
||||
{
|
||||
if (hwc->headers_sz * 2 >= hwc->w_off + sz)
|
||||
hwc->headers_sz *= 2;
|
||||
else
|
||||
hwc->headers_sz = hwc->w_off + sz;
|
||||
h1h_buf = realloc(hwc->hwc_h1h.h1h_buf, hwc->headers_sz);
|
||||
if (!buf)
|
||||
return -1;
|
||||
hwc->hwc_h1h.h1h_buf = h1h_buf;
|
||||
}
|
||||
memcpy(&hwc->hwc_h1h.h1h_buf[hwc->w_off], buf, sz);
|
||||
hwc->w_off += sz;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static enum lsquic_header_status
|
||||
save_pseudo_header (struct header_writer_ctx *hwc, enum pseudo_header ph,
|
||||
const char *val, unsigned val_len)
|
||||
{
|
||||
if (0 == (hwc->pseh_mask & BIT(ph)))
|
||||
{
|
||||
assert(!hwc->pseh_bufs[ph]);
|
||||
hwc->pseh_bufs[ph] = malloc(val_len + 1);
|
||||
if (!hwc->pseh_bufs[ph])
|
||||
return LSQUIC_HDR_ERR_NOMEM;
|
||||
hwc->pseh_mask |= BIT(ph);
|
||||
memcpy(hwc->pseh_bufs[ph], val, val_len);
|
||||
hwc->pseh_bufs[ph][val_len] = '\0';
|
||||
return LSQUIC_HDR_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
LSQ_INFO("header %u is already present", ph);
|
||||
return LSQUIC_HDR_ERR_DUPLICATE_PSDO_HDR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static enum lsquic_header_status
|
||||
add_pseudo_header (struct header_writer_ctx *hwc, const char *name,
|
||||
unsigned name_len, const char *val, unsigned val_len)
|
||||
{
|
||||
if (!(hwc->hwc_flags & HWC_EXPECT_COLON))
|
||||
{
|
||||
LSQ_INFO("unexpected colon");
|
||||
return LSQUIC_HDR_ERR_MISPLACED_PSDO_HDR;
|
||||
}
|
||||
|
||||
switch (name_len)
|
||||
{
|
||||
case 5:
|
||||
if (0 == memcmp(name, ":path", 5))
|
||||
return save_pseudo_header(hwc, PSEH_PATH, val, val_len);
|
||||
break;
|
||||
case 7:
|
||||
switch (name[2])
|
||||
{
|
||||
case 'c':
|
||||
if (0 == memcmp(name, ":scheme", 7))
|
||||
return save_pseudo_header(hwc, PSEH_SCHEME, val, val_len);
|
||||
break;
|
||||
case 'e':
|
||||
if (0 == memcmp(name, ":method", 7))
|
||||
return save_pseudo_header(hwc, PSEH_METHOD, val, val_len);
|
||||
break;
|
||||
case 't':
|
||||
if (0 == memcmp(name, ":status", 7))
|
||||
return save_pseudo_header(hwc, PSEH_STATUS, val, val_len);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 10:
|
||||
if (0 == memcmp(name, ":authority", 10))
|
||||
return save_pseudo_header(hwc, PSEH_AUTHORITY, val, val_len);
|
||||
break;
|
||||
}
|
||||
|
||||
LSQ_INFO("unknown pseudo-header `%.*s'", name_len, name);
|
||||
return LSQUIC_HDR_ERR_UNKNOWN_PSDO_HDR;
|
||||
}
|
||||
|
||||
|
||||
#define HTTP_CODE_LEN 3
|
||||
|
||||
static const char *
|
||||
code_str_to_reason (const char code_str[HTTP_CODE_LEN])
|
||||
{
|
||||
/* RFC 7231, Section 6: */
|
||||
static const char *const http_reason_phrases[] =
|
||||
{
|
||||
#define HTTP_REASON_CODE(code, reason) [code - 100] = reason
|
||||
HTTP_REASON_CODE(100, "Continue"),
|
||||
HTTP_REASON_CODE(101, "Switching Protocols"),
|
||||
HTTP_REASON_CODE(200, "OK"),
|
||||
HTTP_REASON_CODE(201, "Created"),
|
||||
HTTP_REASON_CODE(202, "Accepted"),
|
||||
HTTP_REASON_CODE(203, "Non-Authoritative Information"),
|
||||
HTTP_REASON_CODE(204, "No Content"),
|
||||
HTTP_REASON_CODE(205, "Reset Content"),
|
||||
HTTP_REASON_CODE(206, "Partial Content"),
|
||||
HTTP_REASON_CODE(300, "Multiple Choices"),
|
||||
HTTP_REASON_CODE(301, "Moved Permanently"),
|
||||
HTTP_REASON_CODE(302, "Found"),
|
||||
HTTP_REASON_CODE(303, "See Other"),
|
||||
HTTP_REASON_CODE(304, "Not Modified"),
|
||||
HTTP_REASON_CODE(305, "Use Proxy"),
|
||||
HTTP_REASON_CODE(307, "Temporary Redirect"),
|
||||
HTTP_REASON_CODE(400, "Bad Request"),
|
||||
HTTP_REASON_CODE(401, "Unauthorized"),
|
||||
HTTP_REASON_CODE(402, "Payment Required"),
|
||||
HTTP_REASON_CODE(403, "Forbidden"),
|
||||
HTTP_REASON_CODE(404, "Not Found"),
|
||||
HTTP_REASON_CODE(405, "Method Not Allowed"),
|
||||
HTTP_REASON_CODE(406, "Not Acceptable"),
|
||||
HTTP_REASON_CODE(407, "Proxy Authentication Required"),
|
||||
HTTP_REASON_CODE(408, "Request Timeout"),
|
||||
HTTP_REASON_CODE(409, "Conflict"),
|
||||
HTTP_REASON_CODE(410, "Gone"),
|
||||
HTTP_REASON_CODE(411, "Length Required"),
|
||||
HTTP_REASON_CODE(412, "Precondition Failed"),
|
||||
HTTP_REASON_CODE(413, "Payload Too Large"),
|
||||
HTTP_REASON_CODE(414, "URI Too Long"),
|
||||
HTTP_REASON_CODE(415, "Unsupported Media Type"),
|
||||
HTTP_REASON_CODE(416, "Range Not Satisfiable"),
|
||||
HTTP_REASON_CODE(417, "Expectation Failed"),
|
||||
HTTP_REASON_CODE(426, "Upgrade Required"),
|
||||
HTTP_REASON_CODE(500, "Internal Server Error"),
|
||||
HTTP_REASON_CODE(501, "Not Implemented"),
|
||||
HTTP_REASON_CODE(502, "Bad Gateway"),
|
||||
HTTP_REASON_CODE(503, "Service Unavailable"),
|
||||
HTTP_REASON_CODE(504, "Gateway Timeout"),
|
||||
HTTP_REASON_CODE(505, "HTTP Version Not Supported"),
|
||||
#undef HTTP_REASON_CODE
|
||||
};
|
||||
|
||||
long code;
|
||||
char code_buf[HTTP_CODE_LEN + 1];
|
||||
|
||||
memcpy(code_buf, code_str, HTTP_CODE_LEN);
|
||||
code_buf[HTTP_CODE_LEN] = '\0';
|
||||
code = strtol(code_buf, NULL, 10) - 100;
|
||||
if (code > 0 && code < (long) (sizeof(http_reason_phrases) /
|
||||
sizeof(http_reason_phrases[0])))
|
||||
return http_reason_phrases[code];
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static enum lsquic_header_status
|
||||
convert_response_pseudo_headers (struct header_writer_ctx *hwc)
|
||||
{
|
||||
if ((hwc->pseh_mask & REQUIRED_SERVER_PSEH) != REQUIRED_SERVER_PSEH)
|
||||
{
|
||||
LSQ_INFO("not all response pseudo-headers are specified");
|
||||
return LSQUIC_HDR_ERR_INCOMPL_RESP_PSDO_HDR;
|
||||
}
|
||||
if (hwc->pseh_mask & ALL_REQUEST_PSEH)
|
||||
{
|
||||
LSQ_INFO("response pseudo-headers contain request-only headers");
|
||||
return LSQUIC_HDR_ERR_UNNEC_REQ_PSDO_HDR;
|
||||
}
|
||||
|
||||
const char *code_str, *reason;
|
||||
int code_len;
|
||||
|
||||
code_str = HWC_PSEH_VAL(hwc, PSEH_STATUS);
|
||||
code_len = HWC_PSEH_LEN(hwc, PSEH_STATUS);
|
||||
|
||||
#define HWC_UH_WRITE(h, buf, sz) do { \
|
||||
if (0 != hwc_uh_write(h, buf, sz)) \
|
||||
return LSQUIC_HDR_ERR_NOMEM; \
|
||||
} while (0)
|
||||
|
||||
HWC_UH_WRITE(hwc, "HTTP/1.1 ", 9);
|
||||
HWC_UH_WRITE(hwc, code_str, code_len);
|
||||
if (HTTP_CODE_LEN == code_len && (reason = code_str_to_reason(code_str)))
|
||||
{
|
||||
HWC_UH_WRITE(hwc, " ", 1);
|
||||
HWC_UH_WRITE(hwc, reason, strlen(reason));
|
||||
HWC_UH_WRITE(hwc, "\r\n", 2);
|
||||
}
|
||||
else
|
||||
HWC_UH_WRITE(hwc, " \r\n", 3);
|
||||
if (hwc->max_headers_sz && hwc->w_off > hwc->max_headers_sz)
|
||||
{
|
||||
LSQ_INFO("headers too large");
|
||||
return LSQUIC_HDR_ERR_HEADERS_TOO_LARGE;
|
||||
}
|
||||
return LSQUIC_HDR_OK;
|
||||
|
||||
#undef HWC_UH_WRITE
|
||||
}
|
||||
|
||||
|
||||
static enum lsquic_header_status
|
||||
convert_request_pseudo_headers (struct header_writer_ctx *hwc)
|
||||
{
|
||||
if ((hwc->pseh_mask & REQUIRED_REQUEST_PSEH) != REQUIRED_REQUEST_PSEH)
|
||||
{
|
||||
LSQ_INFO("not all request pseudo-headers are specified");
|
||||
return LSQUIC_HDR_ERR_INCOMPL_REQ_PSDO_HDR;
|
||||
}
|
||||
if (hwc->pseh_mask & ALL_SERVER_PSEH)
|
||||
{
|
||||
LSQ_INFO("request pseudo-headers contain response-only headers");
|
||||
return LSQUIC_HDR_ERR_UNNEC_RESP_PSDO_HDR;
|
||||
}
|
||||
|
||||
#define HWC_UH_WRITE(h, buf, sz) do { \
|
||||
if (0 != hwc_uh_write(h, buf, sz)) \
|
||||
return LSQUIC_HDR_ERR_NOMEM; \
|
||||
} while (0)
|
||||
|
||||
HWC_UH_WRITE(hwc, HWC_PSEH_VAL(hwc, PSEH_METHOD), HWC_PSEH_LEN(hwc, PSEH_METHOD));
|
||||
HWC_UH_WRITE(hwc, " ", 1);
|
||||
HWC_UH_WRITE(hwc, HWC_PSEH_VAL(hwc, PSEH_PATH), HWC_PSEH_LEN(hwc, PSEH_PATH));
|
||||
HWC_UH_WRITE(hwc, " HTTP/1.1\r\n", 11);
|
||||
|
||||
if (hwc->max_headers_sz && hwc->w_off > hwc->max_headers_sz)
|
||||
{
|
||||
LSQ_INFO("headers too large");
|
||||
return LSQUIC_HDR_ERR_HEADERS_TOO_LARGE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
#undef HWC_UH_WRITE
|
||||
}
|
||||
|
||||
|
||||
static enum lsquic_header_status
|
||||
convert_pseudo_headers (struct header_writer_ctx *hwc)
|
||||
{
|
||||
/* We are *reading* the message. Thus, a server expects a request, and a
|
||||
* client expects a response. Unless we receive a push promise from the
|
||||
* server, in which case this should also be a request.
|
||||
*/
|
||||
if (hwc->hwc_flags & (HWC_SERVER|HWC_PUSH_PROMISE))
|
||||
return convert_request_pseudo_headers(hwc);
|
||||
else
|
||||
return convert_response_pseudo_headers(hwc);
|
||||
}
|
||||
|
||||
|
||||
static enum lsquic_header_status
|
||||
save_cookie (struct header_writer_ctx *hwc, const char *val, unsigned val_len)
|
||||
{
|
||||
char *cookie_val;
|
||||
|
||||
if (0 == hwc->cookie_sz)
|
||||
{
|
||||
hwc->cookie_nalloc = hwc->cookie_sz = val_len;
|
||||
cookie_val = malloc(hwc->cookie_nalloc);
|
||||
if (!cookie_val)
|
||||
return LSQUIC_HDR_ERR_NOMEM;
|
||||
hwc->cookie_val = cookie_val;
|
||||
memcpy(hwc->cookie_val, val, val_len);
|
||||
}
|
||||
else
|
||||
{
|
||||
hwc->cookie_sz += val_len + 2 /* "; " */;
|
||||
if (hwc->cookie_sz > hwc->cookie_nalloc)
|
||||
{
|
||||
hwc->cookie_nalloc = hwc->cookie_nalloc * 2 + val_len + 2;
|
||||
cookie_val = realloc(hwc->cookie_val, hwc->cookie_nalloc);
|
||||
if (!cookie_val)
|
||||
return LSQUIC_HDR_ERR_NOMEM;
|
||||
hwc->cookie_val = cookie_val;
|
||||
}
|
||||
memcpy(hwc->cookie_val + hwc->cookie_sz - val_len - 2, "; ", 2);
|
||||
memcpy(hwc->cookie_val + hwc->cookie_sz - val_len, val, val_len);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static enum lsquic_header_status
|
||||
add_real_header (struct header_writer_ctx *hwc, const char *name,
|
||||
unsigned name_len, const char *val, unsigned val_len)
|
||||
{
|
||||
enum lsquic_header_status err;
|
||||
unsigned i;
|
||||
int n_upper;
|
||||
|
||||
if (hwc->hwc_flags & HWC_EXPECT_COLON)
|
||||
{
|
||||
if (0 != (err = convert_pseudo_headers(hwc)))
|
||||
return err;
|
||||
hwc->hwc_flags &= ~HWC_EXPECT_COLON;
|
||||
}
|
||||
|
||||
if (4 == name_len && 0 == memcmp(name, "host", 4))
|
||||
hwc->hwc_flags |= HWC_SEEN_HOST;
|
||||
|
||||
n_upper = 0;
|
||||
for (i = 0; i < name_len; ++i)
|
||||
n_upper += isupper(name[i]);
|
||||
if (n_upper > 0)
|
||||
{
|
||||
LSQ_INFO("Header name `%.*s' contains uppercase letters",
|
||||
name_len, name);
|
||||
return LSQUIC_HDR_ERR_UPPERCASE_HEADER;
|
||||
}
|
||||
|
||||
if (6 == name_len && memcmp(name, "cookie", 6) == 0)
|
||||
{
|
||||
return save_cookie(hwc, val, val_len);
|
||||
}
|
||||
|
||||
#define HWC_UH_WRITE(h, buf, sz) do { \
|
||||
if (0 != hwc_uh_write(h, buf, sz)) \
|
||||
return LSQUIC_HDR_ERR_NOMEM; \
|
||||
} while (0)
|
||||
|
||||
HWC_UH_WRITE(hwc, name, name_len);
|
||||
HWC_UH_WRITE(hwc, ": ", 2);
|
||||
HWC_UH_WRITE(hwc, val, val_len);
|
||||
HWC_UH_WRITE(hwc, "\r\n", 2);
|
||||
|
||||
if (hwc->max_headers_sz && hwc->w_off > hwc->max_headers_sz)
|
||||
{
|
||||
LSQ_INFO("headers too large");
|
||||
return LSQUIC_HDR_ERR_HEADERS_TOO_LARGE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
#undef HWC_UH_WRITE
|
||||
}
|
||||
|
||||
|
||||
static enum lsquic_header_status
|
||||
add_header_to_uh (struct header_writer_ctx *hwc, const char *name,
|
||||
unsigned name_len, const char *val, unsigned val_len)
|
||||
{
|
||||
LSQ_DEBUG("Got header '%.*s': '%.*s'", name_len, name, val_len, val);
|
||||
if (':' == name[0])
|
||||
return add_pseudo_header(hwc, name, name_len, val, val_len);
|
||||
else
|
||||
return add_real_header(hwc, name, name_len, val, val_len);
|
||||
}
|
||||
|
||||
|
||||
static enum lsquic_header_status
|
||||
h1h_finish_hset (struct header_writer_ctx *hwc)
|
||||
{
|
||||
enum lsquic_header_status st;
|
||||
|
||||
if (hwc->hwc_flags & HWC_EXPECT_COLON)
|
||||
{
|
||||
st = convert_pseudo_headers(hwc);
|
||||
if (0 != st)
|
||||
return st;
|
||||
hwc->hwc_flags &= ~HWC_EXPECT_COLON;
|
||||
}
|
||||
|
||||
#define HWC_UH_WRITE(h, buf, sz) do { \
|
||||
st = hwc_uh_write(h, buf, sz); \
|
||||
if (0 != st) \
|
||||
return st; \
|
||||
} while (0)
|
||||
|
||||
if ((hwc->pseh_mask & BIT(PSEH_AUTHORITY)) &&
|
||||
0 == (hwc->hwc_flags & HWC_SEEN_HOST))
|
||||
{
|
||||
LSQ_DEBUG("Setting 'Host: %.*s'", HWC_PSEH_LEN(hwc, PSEH_AUTHORITY),
|
||||
HWC_PSEH_VAL(hwc, PSEH_AUTHORITY));
|
||||
HWC_UH_WRITE(hwc, "Host: ", 6);
|
||||
HWC_UH_WRITE(hwc, HWC_PSEH_VAL(hwc, PSEH_AUTHORITY),
|
||||
HWC_PSEH_LEN(hwc, PSEH_AUTHORITY));
|
||||
HWC_UH_WRITE(hwc, "\r\n", 2);
|
||||
}
|
||||
|
||||
if (hwc->cookie_val)
|
||||
{
|
||||
LSQ_DEBUG("Setting 'Cookie: %.*s'", hwc->cookie_sz, hwc->cookie_val);
|
||||
HWC_UH_WRITE(hwc, "Cookie: ", 8);
|
||||
HWC_UH_WRITE(hwc, hwc->cookie_val, hwc->cookie_sz);
|
||||
HWC_UH_WRITE(hwc, "\r\n", 2);
|
||||
}
|
||||
|
||||
HWC_UH_WRITE(hwc, "\r\n", 2 + 1 /* NUL byte */);
|
||||
hwc->w_off -= 1; /* Do not count NUL byte */
|
||||
hwc->hwc_h1h.h1h_size = hwc->w_off;
|
||||
|
||||
if (hwc->max_headers_sz && hwc->w_off > hwc->max_headers_sz)
|
||||
{
|
||||
LSQ_INFO("headers too large");
|
||||
return LSQUIC_HDR_ERR_HEADERS_TOO_LARGE;
|
||||
}
|
||||
|
||||
return LSQUIC_HDR_OK;
|
||||
}
|
||||
|
||||
#define HWC_PTR(data_in) (struct header_writer_ctx *) \
|
||||
((unsigned char *) (hset) - offsetof(struct header_writer_ctx, hwc_h1h))
|
||||
|
||||
static enum lsquic_header_status
|
||||
h1h_process_header (void *hset, unsigned name_idx,
|
||||
const char *name, unsigned name_len,
|
||||
const char *value, unsigned value_len)
|
||||
{
|
||||
struct header_writer_ctx *const hwc = HWC_PTR(hset);
|
||||
if (name)
|
||||
return add_header_to_uh(hwc, name, name_len, value, value_len);
|
||||
else
|
||||
return h1h_finish_hset(hwc);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
h1h_discard_header_set (void *hset)
|
||||
{
|
||||
struct header_writer_ctx *const hwc = HWC_PTR(hset);
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < sizeof(hwc->pseh_bufs) / sizeof(hwc->pseh_bufs[0]); ++i)
|
||||
if (hwc->pseh_bufs[i])
|
||||
free(hwc->pseh_bufs[i]);
|
||||
if (hwc->cookie_val)
|
||||
free(hwc->cookie_val);
|
||||
free(hwc->hwc_h1h.h1h_buf);
|
||||
free(hwc);
|
||||
}
|
||||
|
||||
|
||||
static const struct lsquic_hset_if http1x_if =
|
||||
{
|
||||
.hsi_create_header_set = h1h_create_header_set,
|
||||
.hsi_process_header = h1h_process_header,
|
||||
.hsi_discard_header_set = h1h_discard_header_set,
|
||||
};
|
||||
|
||||
const struct lsquic_hset_if *const lsquic_http1x_if = &http1x_if;
|
16
src/liblsquic/lsquic_http1x_if.h
Normal file
16
src/liblsquic/lsquic_http1x_if.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
/* Copyright (c) 2017 - 2018 LiteSpeed Technologies Inc. See LICENSE. */
|
||||
#ifndef LSQUIC_HTTP1X_IF_H
|
||||
#define LSQUIC_HTTP1X_IF_H 1
|
||||
|
||||
struct lsquic_hset_if;
|
||||
|
||||
struct http1x_ctor_ctx
|
||||
{
|
||||
lsquic_cid_t cid; /* Used for logging */
|
||||
unsigned max_headers_sz;
|
||||
int is_server;
|
||||
};
|
||||
|
||||
extern const struct lsquic_hset_if *const lsquic_http1x_if;
|
||||
|
||||
#endif
|
|
@ -72,6 +72,7 @@ enum lsq_log_level lsq_log_levels[N_LSQUIC_LOGGER_MODULES] = {
|
|||
[LSQLM_DI] = LSQ_LOG_WARN,
|
||||
[LSQLM_PACER] = LSQ_LOG_WARN,
|
||||
[LSQLM_MIN_HEAP] = LSQ_LOG_WARN,
|
||||
[LSQLM_HTTP1X] = LSQ_LOG_WARN,
|
||||
};
|
||||
|
||||
const char *const lsqlm_to_str[N_LSQUIC_LOGGER_MODULES] = {
|
||||
|
@ -100,6 +101,7 @@ const char *const lsqlm_to_str[N_LSQUIC_LOGGER_MODULES] = {
|
|||
[LSQLM_DI] = "di",
|
||||
[LSQLM_PACER] = "pacer",
|
||||
[LSQLM_MIN_HEAP] = "min-heap",
|
||||
[LSQLM_HTTP1X] = "http1x",
|
||||
};
|
||||
|
||||
const char *const lsq_loglevel2str[N_LSQUIC_LOG_LEVELS] = {
|
||||
|
|
|
@ -71,6 +71,7 @@ enum lsquic_logger_module {
|
|||
LSQLM_DI,
|
||||
LSQLM_PACER,
|
||||
LSQLM_MIN_HEAP,
|
||||
LSQLM_HTTP1X,
|
||||
N_LSQUIC_LOGGER_MODULES
|
||||
};
|
||||
|
||||
|
|
|
@ -40,7 +40,6 @@
|
|||
#include "lsquic_util.h"
|
||||
#include "lsquic_mm.h"
|
||||
#include "lsquic_headers_stream.h"
|
||||
#include "lsquic_frame_reader.h"
|
||||
#include "lsquic_conn.h"
|
||||
#include "lsquic_data_in_if.h"
|
||||
#include "lsquic_parse.h"
|
||||
|
@ -50,6 +49,7 @@
|
|||
#include "lsquic_pacer.h"
|
||||
#include "lsquic_cubic.h"
|
||||
#include "lsquic_send_ctl.h"
|
||||
#include "lsquic_headers.h"
|
||||
#include "lsquic_ev_log.h"
|
||||
|
||||
#define LSQUIC_LOGGER_MODULE LSQLM_STREAM
|
||||
|
@ -315,6 +315,20 @@ drop_buffered_data (struct lsquic_stream *stream)
|
|||
}
|
||||
|
||||
|
||||
static void
|
||||
destroy_uh (struct lsquic_stream *stream)
|
||||
{
|
||||
if (stream->uh)
|
||||
{
|
||||
if (stream->uh->uh_hset)
|
||||
stream->conn_pub->enpub->enp_hsi_if
|
||||
->hsi_discard_header_set(stream->uh->uh_hset);
|
||||
free(stream->uh);
|
||||
stream->uh = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
lsquic_stream_destroy (lsquic_stream_t *stream)
|
||||
{
|
||||
|
@ -336,8 +350,14 @@ lsquic_stream_destroy (lsquic_stream_t *stream)
|
|||
drop_buffered_data(stream);
|
||||
lsquic_sfcw_consume_rem(&stream->fc);
|
||||
drop_frames_in(stream);
|
||||
free(stream->push_req);
|
||||
free(stream->uh);
|
||||
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);
|
||||
}
|
||||
destroy_uh(stream);
|
||||
free(stream->sm_buf);
|
||||
LSQ_DEBUG("destroyed stream %u @%p", stream->id, stream);
|
||||
SM_HISTORY_DUMP_REMAINING(stream);
|
||||
|
@ -682,17 +702,16 @@ lsquic_stream_rst_frame_sent (lsquic_stream_t *stream)
|
|||
static size_t
|
||||
read_uh (lsquic_stream_t *stream, unsigned char *dst, size_t len)
|
||||
{
|
||||
struct uncompressed_headers *uh = stream->uh;
|
||||
size_t n_avail = uh->uh_size - uh->uh_off;
|
||||
struct http1x_headers *h1h = stream->uh->uh_hset;
|
||||
size_t n_avail = h1h->h1h_size - h1h->h1h_off;
|
||||
if (n_avail < len)
|
||||
len = n_avail;
|
||||
memcpy(dst, uh->uh_headers + uh->uh_off, len);
|
||||
uh->uh_off += len;
|
||||
if (uh->uh_off == uh->uh_size)
|
||||
memcpy(dst, h1h->h1h_buf + h1h->h1h_off, len);
|
||||
h1h->h1h_off += len;
|
||||
if (h1h->h1h_off == h1h->h1h_size)
|
||||
{
|
||||
LSQ_DEBUG("read all uncompressed headers for stream %u", stream->id);
|
||||
free(uh);
|
||||
stream->uh = NULL;
|
||||
destroy_uh(stream);
|
||||
if (stream->stream_flags & STREAM_HEAD_IN_FIN)
|
||||
{
|
||||
stream->stream_flags |= STREAM_FIN_REACHED;
|
||||
|
@ -749,18 +768,31 @@ lsquic_stream_readv (lsquic_stream_t *stream, const struct iovec *iov,
|
|||
iovidx = -1;
|
||||
NEXT_IOV();
|
||||
|
||||
if (stream->uh && AVAIL())
|
||||
if (stream->uh)
|
||||
{
|
||||
read_unc_headers = 1;
|
||||
do
|
||||
if (stream->uh->uh_flags & UH_H1H)
|
||||
{
|
||||
nread = read_uh(stream, p, AVAIL());
|
||||
p += nread;
|
||||
total_nread += nread;
|
||||
if (p == end)
|
||||
NEXT_IOV();
|
||||
if (AVAIL())
|
||||
{
|
||||
read_unc_headers = 1;
|
||||
do
|
||||
{
|
||||
nread = read_uh(stream, p, AVAIL());
|
||||
p += nread;
|
||||
total_nread += nread;
|
||||
if (p == end)
|
||||
NEXT_IOV();
|
||||
}
|
||||
while (stream->uh && AVAIL());
|
||||
}
|
||||
else
|
||||
read_unc_headers = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
LSQ_INFO("header set not claimed: cannot read from stream");
|
||||
return -1;
|
||||
}
|
||||
while (stream->uh && AVAIL());
|
||||
}
|
||||
else
|
||||
read_unc_headers = 0;
|
||||
|
@ -2006,14 +2038,13 @@ lsquic_stream_is_pushed (const lsquic_stream_t *stream)
|
|||
|
||||
int
|
||||
lsquic_stream_push_info (const lsquic_stream_t *stream,
|
||||
uint32_t *ref_stream_id, const char **headers, size_t *headers_sz)
|
||||
uint32_t *ref_stream_id, void **hset)
|
||||
{
|
||||
if (lsquic_stream_is_pushed(stream))
|
||||
{
|
||||
assert(stream->push_req);
|
||||
*ref_stream_id = stream->push_req->uh_stream_id;
|
||||
*headers = stream->push_req->uh_headers;
|
||||
*headers_sz = stream->push_req->uh_size;
|
||||
*hset = stream->push_req->uh_hset;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
|
@ -2145,3 +2176,34 @@ lsquic_stream_cid (const struct lsquic_stream *stream)
|
|||
}
|
||||
|
||||
|
||||
void *
|
||||
lsquic_stream_get_hset (struct lsquic_stream *stream)
|
||||
{
|
||||
void *hset;
|
||||
|
||||
if ((stream->stream_flags & (STREAM_USE_HEADERS|STREAM_HAVE_UH))
|
||||
!= (STREAM_USE_HEADERS|STREAM_HAVE_UH))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
if (stream->uh->uh_flags & UH_H1H)
|
||||
{
|
||||
LSQ_INFO("%s: uncompressed headers have internal format", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hset = stream->uh->uh_hset;
|
||||
stream->uh->uh_hset = NULL;
|
||||
destroy_uh(stream);
|
||||
LSQ_DEBUG("return header set");
|
||||
return hset;
|
||||
}
|
||||
|
|
|
@ -51,6 +51,12 @@ static int randomly_reprioritize_streams;
|
|||
*/
|
||||
static int promise_fd = -1;
|
||||
|
||||
/* Set to true value to use header bypass. This means that the use code
|
||||
* creates header set via callbacks and then fetches it by calling
|
||||
* lsquic_stream_get_hset() when the first "on_read" event is called.
|
||||
*/
|
||||
static int g_header_bypass;
|
||||
|
||||
struct lsquic_conn_ctx;
|
||||
|
||||
struct path_elem {
|
||||
|
@ -83,6 +89,7 @@ struct http_client_ctx {
|
|||
HCC_DISCARD_RESPONSE = (1 << 0),
|
||||
HCC_SEEN_FIN = (1 << 1),
|
||||
HCC_ABORT_ON_INCOMPLETE = (1 << 2),
|
||||
HCC_PROCESSED_HEADERS = (1 << 3),
|
||||
} hcc_flags;
|
||||
struct prog *prog;
|
||||
};
|
||||
|
@ -97,6 +104,23 @@ struct lsquic_conn_ctx {
|
|||
};
|
||||
|
||||
|
||||
struct hset_elem
|
||||
{
|
||||
STAILQ_ENTRY(hset_elem) next;
|
||||
unsigned name_idx;
|
||||
char *name;
|
||||
char *value;
|
||||
};
|
||||
|
||||
|
||||
STAILQ_HEAD(hset, hset_elem);
|
||||
|
||||
static void
|
||||
hset_dump (const struct hset *, FILE *);
|
||||
static void
|
||||
hset_destroy (void *hset);
|
||||
|
||||
|
||||
static void
|
||||
create_connections (struct http_client_ctx *client_ctx)
|
||||
{
|
||||
|
@ -362,6 +386,7 @@ static void
|
|||
http_client_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
|
||||
{
|
||||
struct http_client_ctx *const client_ctx = st_h->client_ctx;
|
||||
struct hset *hset;
|
||||
ssize_t nread;
|
||||
unsigned old_prio, new_prio;
|
||||
unsigned char buf[0x200];
|
||||
|
@ -370,6 +395,21 @@ http_client_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
|
|||
srand(GetTickCount());
|
||||
#endif
|
||||
|
||||
if (g_header_bypass
|
||||
&& !(client_ctx->hcc_flags & HCC_PROCESSED_HEADERS))
|
||||
{
|
||||
hset = lsquic_stream_get_hset(stream);
|
||||
if (!hset)
|
||||
{
|
||||
LSQ_ERROR("could not get header set from stream");
|
||||
exit(2);
|
||||
}
|
||||
if (!(client_ctx->hcc_flags & HCC_DISCARD_RESPONSE))
|
||||
hset_dump(hset, stdout);
|
||||
hset_destroy(hset);
|
||||
client_ctx->hcc_flags |= HCC_PROCESSED_HEADERS;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
nread = lsquic_stream_read(stream, buf, sizeof(buf));
|
||||
|
@ -588,6 +628,99 @@ verify_server_cert (void *ctx, STACK_OF(X509) *chain)
|
|||
#endif
|
||||
|
||||
|
||||
static void *
|
||||
hset_create (void *hsi_ctx, int is_push_promise)
|
||||
{
|
||||
struct hset *hset;
|
||||
|
||||
hset = malloc(sizeof(*hset));
|
||||
if (hset)
|
||||
{
|
||||
STAILQ_INIT(hset);
|
||||
return hset;
|
||||
}
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static enum lsquic_header_status
|
||||
hset_add_header (void *hset_p, unsigned name_idx,
|
||||
const char *name, unsigned name_len,
|
||||
const char *value, unsigned value_len)
|
||||
{
|
||||
struct hset *hset = hset_p;
|
||||
struct hset_elem *el;
|
||||
|
||||
if (!name) /* This signals end of headers. We do no post-processing. */
|
||||
return LSQUIC_HDR_OK;
|
||||
|
||||
el = malloc(sizeof(*el));
|
||||
if (!el)
|
||||
return LSQUIC_HDR_ERR_NOMEM;
|
||||
|
||||
el->name = strndup(name, name_len);
|
||||
el->value = strndup(value, value_len);
|
||||
if (!(el->name && el->value))
|
||||
{
|
||||
free(el->name);
|
||||
free(el->value);
|
||||
free(el);
|
||||
return LSQUIC_HDR_ERR_NOMEM;
|
||||
}
|
||||
|
||||
el->name_idx = name_idx;
|
||||
STAILQ_INSERT_TAIL(hset, el, next);
|
||||
return LSQUIC_HDR_OK;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
hset_destroy (void *hset_p)
|
||||
{
|
||||
struct hset *hset = hset_p;
|
||||
struct hset_elem *el, *next;
|
||||
|
||||
for (el = STAILQ_FIRST(hset); el; el = next)
|
||||
{
|
||||
next = STAILQ_NEXT(el, next);
|
||||
free(el->name);
|
||||
free(el->value);
|
||||
free(el);
|
||||
}
|
||||
free(hset);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
hset_dump (const struct hset *hset, FILE *out)
|
||||
{
|
||||
const struct hset_elem *el;
|
||||
|
||||
STAILQ_FOREACH(el, hset, next)
|
||||
if (el->name_idx)
|
||||
fprintf(out, "%s (static table idx %u): %s\n", el->name,
|
||||
el->name_idx, el->value);
|
||||
else
|
||||
fprintf(out, "%s: %s\n", el->name, el->value);
|
||||
|
||||
fprintf(out, "\n");
|
||||
fflush(out);
|
||||
}
|
||||
|
||||
|
||||
/* These are basic and for illustration purposes only. You will want to
|
||||
* do your own verification by doing something similar to what is done
|
||||
* in src/liblsquic/lsquic_http1x_if.c
|
||||
*/
|
||||
static const struct lsquic_hset_if header_bypass_api =
|
||||
{
|
||||
.hsi_create_header_set = hset_create,
|
||||
.hsi_process_header = hset_add_header,
|
||||
.hsi_discard_header_set = hset_destroy,
|
||||
};
|
||||
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
|
@ -615,7 +748,7 @@ main (int argc, char **argv)
|
|||
|
||||
prog_init(&prog, LSENG_HTTP, &sports, &http_client_if, &client_ctx);
|
||||
|
||||
while (-1 != (opt = getopt(argc, argv, PROG_OPTS "46r:R:IKu:EP:M:n:H:p:h"
|
||||
while (-1 != (opt = getopt(argc, argv, PROG_OPTS "46Br:R:IKu:EP:M:n:H:p:h"
|
||||
#ifndef WIN32
|
||||
"C:"
|
||||
#endif
|
||||
|
@ -626,6 +759,11 @@ main (int argc, char **argv)
|
|||
case '6':
|
||||
prog.prog_ipver = opt - '0';
|
||||
break;
|
||||
case 'B':
|
||||
g_header_bypass = 1;
|
||||
prog.prog_api.ea_hsi_if = &header_bypass_api;
|
||||
prog.prog_api.ea_hsi_ctx = NULL;
|
||||
break;
|
||||
case 'I':
|
||||
client_ctx.hcc_flags |= HCC_ABORT_ON_INCOMPLETE;
|
||||
break;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/* Copyright (c) 2017 - 2018 LiteSpeed Technologies Inc. See LICENSE. */
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lsquic.h"
|
||||
|
||||
|
@ -15,13 +16,10 @@ main (void)
|
|||
|
||||
lsquic_engine_init_settings(&settings, flags);
|
||||
|
||||
struct lsquic_engine_api api = {
|
||||
&settings,
|
||||
NULL, NULL, /* stream if and ctx */
|
||||
(void *) (uintptr_t) 1, NULL, /* packets out and ctx */
|
||||
NULL, NULL, /* packout mem interface and ctx */
|
||||
NULL, NULL, /* verify server cert */
|
||||
};
|
||||
struct lsquic_engine_api api;
|
||||
memset(&api, 0, sizeof(api));
|
||||
api.ea_settings = &settings;
|
||||
api.ea_packets_out = (void *) (uintptr_t) 1;
|
||||
|
||||
engine = lsquic_engine_new(flags, &api);
|
||||
assert(engine);
|
||||
|
|
|
@ -15,10 +15,18 @@
|
|||
#include "lsquic_frame_common.h"
|
||||
#include "lshpack.h"
|
||||
#include "lsquic_mm.h"
|
||||
#include "lsquic_int_types.h"
|
||||
#include "lsquic_conn_flow.h"
|
||||
#include "lsquic_sfcw.h"
|
||||
#include "lsquic_rtt.h"
|
||||
#include "lsquic_conn.h"
|
||||
#include "lsquic_stream.h"
|
||||
#include "lsquic_conn_public.h"
|
||||
#include "lsquic_logger.h"
|
||||
|
||||
#define FRAME_READER_TESTING 0x100
|
||||
#include "lsquic_frame_reader.h"
|
||||
#include "lsquic_headers.h"
|
||||
#include "lsquic_http1x_if.h"
|
||||
|
||||
|
||||
struct callback_value /* What callback returns */
|
||||
|
@ -32,7 +40,16 @@ struct callback_value /* What callback returns */
|
|||
} type;
|
||||
unsigned stream_off; /* Checked only if not zero */
|
||||
union {
|
||||
struct uncompressed_headers uh;
|
||||
struct headers {
|
||||
uint32_t stream_id;
|
||||
uint32_t oth_stream_id;
|
||||
unsigned short weight;
|
||||
signed char exclusive;
|
||||
unsigned char flags;
|
||||
unsigned size;
|
||||
unsigned off;
|
||||
char buf[0x100];
|
||||
} headers;
|
||||
struct {
|
||||
uint16_t id;
|
||||
uint32_t value;
|
||||
|
@ -53,31 +70,30 @@ struct callback_value /* What callback returns */
|
|||
|
||||
|
||||
void
|
||||
compare_headers (const struct uncompressed_headers *got_uh,
|
||||
const struct uncompressed_headers *exp_uh)
|
||||
compare_headers (const struct headers *got_h, const struct headers *exp_h)
|
||||
{
|
||||
assert(got_uh->uh_stream_id == exp_uh->uh_stream_id);
|
||||
assert(got_uh->uh_oth_stream_id == exp_uh->uh_oth_stream_id);
|
||||
assert(got_uh->uh_weight == exp_uh->uh_weight);
|
||||
assert(got_uh->uh_exclusive == exp_uh->uh_exclusive);
|
||||
assert(got_uh->uh_size == exp_uh->uh_size);
|
||||
assert(strlen(got_uh->uh_headers) == got_uh->uh_size);
|
||||
assert(got_uh->uh_off == exp_uh->uh_off);
|
||||
assert(got_uh->uh_flags == exp_uh->uh_flags);
|
||||
assert(0 == memcmp(got_uh->uh_headers, exp_uh->uh_headers, got_uh->uh_size));
|
||||
assert(got_h->stream_id == exp_h->stream_id);
|
||||
assert(got_h->oth_stream_id == exp_h->oth_stream_id);
|
||||
assert(got_h->weight == exp_h->weight);
|
||||
assert(got_h->exclusive == exp_h->exclusive);
|
||||
assert(got_h->size == exp_h->size);
|
||||
assert(strlen(got_h->buf) == got_h->size);
|
||||
assert(got_h->off == exp_h->off);
|
||||
assert(got_h->flags == exp_h->flags);
|
||||
assert(0 == memcmp(got_h->buf, exp_h->buf, got_h->size));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
compare_push_promises (const struct uncompressed_headers *got_uh,
|
||||
const struct uncompressed_headers *exp_uh)
|
||||
compare_push_promises (const struct headers *got_h, const struct headers *exp_h)
|
||||
{
|
||||
assert(got_uh->uh_stream_id == exp_uh->uh_stream_id);
|
||||
assert(got_uh->uh_oth_stream_id == exp_uh->uh_oth_stream_id);
|
||||
assert(got_uh->uh_size == exp_uh->uh_size);
|
||||
assert(strlen(got_uh->uh_headers) == got_uh->uh_size);
|
||||
assert(got_uh->uh_flags == exp_uh->uh_flags);
|
||||
assert(0 == memcmp(got_uh->uh_headers, exp_uh->uh_headers, got_uh->uh_size));
|
||||
assert(got_h->stream_id == exp_h->stream_id);
|
||||
assert(got_h->oth_stream_id == exp_h->oth_stream_id);
|
||||
assert(got_h->size == exp_h->size);
|
||||
assert(strlen(got_h->buf) == got_h->size);
|
||||
assert(got_h->off == exp_h->off);
|
||||
assert(got_h->flags == exp_h->flags);
|
||||
assert(0 == memcmp(got_h->buf, exp_h->buf, got_h->size));
|
||||
}
|
||||
|
||||
|
||||
|
@ -111,10 +127,10 @@ compare_cb_vals (const struct callback_value *got,
|
|||
switch (got->type)
|
||||
{
|
||||
case CV_HEADERS:
|
||||
compare_headers(&got->u.uh, &exp->u.uh);
|
||||
compare_headers(&got->u.headers, &exp->u.headers);
|
||||
break;
|
||||
case CV_PUSH_PROMISE:
|
||||
compare_push_promises(&got->u.uh, &exp->u.uh);
|
||||
compare_push_promises(&got->u.headers, &exp->u.headers);
|
||||
break;
|
||||
case CV_ERROR:
|
||||
compare_errors(&got->u.error, &exp->u.error);
|
||||
|
@ -152,10 +168,19 @@ reset_cb_ctx (struct cb_ctx *cb_ctx)
|
|||
}
|
||||
|
||||
|
||||
static size_t
|
||||
uh_size (const struct uncompressed_headers *uh)
|
||||
static void
|
||||
copy_uh_to_headers (const struct uncompressed_headers *uh, struct headers *h)
|
||||
{
|
||||
return sizeof(*uh) - FRAME_READER_TESTING + uh->uh_size;
|
||||
const struct http1x_headers *h1h = uh->uh_hset;
|
||||
h->flags = uh->uh_flags;
|
||||
h->weight = uh->uh_weight;
|
||||
h->stream_id = uh->uh_stream_id;
|
||||
h->exclusive = uh->uh_exclusive;
|
||||
h->oth_stream_id = uh->uh_oth_stream_id;
|
||||
h->size = h1h->h1h_size;
|
||||
h->off = h1h->h1h_off;
|
||||
memcpy(h->buf, h1h->h1h_buf, h->size);
|
||||
h->buf[h->size] = '\0';
|
||||
}
|
||||
|
||||
|
||||
|
@ -168,8 +193,9 @@ on_incoming_headers (void *ctx, struct uncompressed_headers *uh)
|
|||
assert(i < sizeof(cb_ctx->cb_vals) / sizeof(cb_ctx->cb_vals[0]));
|
||||
cb_ctx->cb_vals[i].type = CV_HEADERS;
|
||||
cb_ctx->cb_vals[i].stream_off = input.in_off;
|
||||
assert(uh_size(uh) <= sizeof(*uh));
|
||||
memcpy(&cb_ctx->cb_vals[i].u.uh, uh, uh_size(uh) + 1 /* NUL byte */);
|
||||
copy_uh_to_headers(uh, &cb_ctx->cb_vals[i].u.headers);
|
||||
assert(uh->uh_flags & UH_H1H);
|
||||
lsquic_http1x_if->hsi_discard_header_set(uh->uh_hset);
|
||||
free(uh);
|
||||
}
|
||||
|
||||
|
@ -183,8 +209,9 @@ on_push_promise (void *ctx, struct uncompressed_headers *uh)
|
|||
assert(i < sizeof(cb_ctx->cb_vals) / sizeof(cb_ctx->cb_vals[0]));
|
||||
cb_ctx->cb_vals[i].type = CV_PUSH_PROMISE;
|
||||
cb_ctx->cb_vals[i].stream_off = input.in_off;
|
||||
assert(uh_size(uh) <= sizeof(*uh));
|
||||
memcpy(&cb_ctx->cb_vals[i].u.uh, uh, uh_size(uh) + 1 /* NUL byte */);
|
||||
copy_uh_to_headers(uh, &cb_ctx->cb_vals[i].u.headers);
|
||||
assert(uh->uh_flags & UH_H1H);
|
||||
lsquic_http1x_if->hsi_discard_header_set(uh->uh_hset);
|
||||
free(uh);
|
||||
}
|
||||
|
||||
|
@ -273,7 +300,7 @@ struct frame_reader_test {
|
|||
};
|
||||
|
||||
|
||||
#define UH_HEADERS(str) .uh_headers = (str), .uh_size = sizeof(str) - 1
|
||||
#define HEADERS(str) .buf = (str), .size = sizeof(str) - 1
|
||||
|
||||
static const struct frame_reader_test tests[] = {
|
||||
{ .frt_lineno = __LINE__,
|
||||
|
@ -292,13 +319,14 @@ static const struct frame_reader_test tests[] = {
|
|||
.frt_cb_vals = {
|
||||
{
|
||||
.type = CV_HEADERS,
|
||||
.u.uh = {
|
||||
.uh_stream_id = 12345,
|
||||
.uh_oth_stream_id = 0,
|
||||
.uh_weight = 0,
|
||||
.uh_exclusive = -1,
|
||||
.uh_off = 0,
|
||||
UH_HEADERS("HTTP/1.1 302 Found\r\n\r\n"),
|
||||
.u.headers = {
|
||||
.stream_id = 12345,
|
||||
.oth_stream_id = 0,
|
||||
.weight = 0,
|
||||
.exclusive = -1,
|
||||
.off = 0,
|
||||
.flags = UH_H1H,
|
||||
HEADERS("HTTP/1.1 302 Found\r\n\r\n"),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -323,13 +351,14 @@ static const struct frame_reader_test tests[] = {
|
|||
.frt_cb_vals = {
|
||||
{
|
||||
.type = CV_HEADERS,
|
||||
.u.uh = {
|
||||
.uh_stream_id = 12345,
|
||||
.uh_oth_stream_id = 0,
|
||||
.uh_weight = 0,
|
||||
.uh_exclusive = -1,
|
||||
.uh_off = 0,
|
||||
UH_HEADERS("HTTP/1.1 302 Found\r\n\r\n"),
|
||||
.u.headers = {
|
||||
.stream_id = 12345,
|
||||
.oth_stream_id = 0,
|
||||
.weight = 0,
|
||||
.exclusive = -1,
|
||||
.off = 0,
|
||||
.flags = UH_H1H,
|
||||
HEADERS("HTTP/1.1 302 Found\r\n\r\n"),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -366,14 +395,14 @@ static const struct frame_reader_test tests[] = {
|
|||
.frt_cb_vals = {
|
||||
{
|
||||
.type = CV_HEADERS,
|
||||
.u.uh = {
|
||||
.uh_stream_id = 12345,
|
||||
.uh_oth_stream_id = 0x1234,
|
||||
.uh_weight = 0xFF + 1,
|
||||
.uh_exclusive = 1,
|
||||
.uh_off = 0,
|
||||
.uh_flags = UH_FIN,
|
||||
UH_HEADERS("HTTP/1.1 302 Found\r\n\r\n"),
|
||||
.u.headers = {
|
||||
.stream_id = 12345,
|
||||
.oth_stream_id = 0x1234,
|
||||
.weight = 0xFF + 1,
|
||||
.exclusive = 1,
|
||||
.off = 0,
|
||||
.flags = UH_FIN | UH_H1H,
|
||||
HEADERS("HTTP/1.1 302 Found\r\n\r\n"),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -407,13 +436,14 @@ static const struct frame_reader_test tests[] = {
|
|||
.frt_cb_vals = {
|
||||
{
|
||||
.type = CV_HEADERS,
|
||||
.u.uh = {
|
||||
.uh_stream_id = 12345,
|
||||
.uh_oth_stream_id = 0x1234,
|
||||
.uh_weight = 1,
|
||||
.uh_exclusive = 0,
|
||||
.uh_off = 0,
|
||||
UH_HEADERS("HTTP/1.1 302 Found\r\n\r\n"),
|
||||
.u.headers = {
|
||||
.stream_id = 12345,
|
||||
.oth_stream_id = 0x1234,
|
||||
.weight = 1,
|
||||
.exclusive = 0,
|
||||
.off = 0,
|
||||
.flags = UH_H1H,
|
||||
HEADERS("HTTP/1.1 302 Found\r\n\r\n"),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -439,13 +469,14 @@ static const struct frame_reader_test tests[] = {
|
|||
.frt_cb_vals = {
|
||||
{
|
||||
.type = CV_HEADERS,
|
||||
.u.uh = {
|
||||
.uh_stream_id = 12345,
|
||||
.uh_oth_stream_id = 0x1234,
|
||||
.uh_weight = 1,
|
||||
.uh_exclusive = 0,
|
||||
.uh_off = 0,
|
||||
UH_HEADERS("HTTP/1.1 302 Found\r\n"
|
||||
.u.headers = {
|
||||
.stream_id = 12345,
|
||||
.oth_stream_id = 0x1234,
|
||||
.weight = 1,
|
||||
.exclusive = 0,
|
||||
.off = 0,
|
||||
.flags = UH_H1H,
|
||||
HEADERS("HTTP/1.1 302 Found\r\n"
|
||||
"Cookie: a=b\r\n\r\n"),
|
||||
},
|
||||
},
|
||||
|
@ -474,13 +505,14 @@ static const struct frame_reader_test tests[] = {
|
|||
.frt_cb_vals = {
|
||||
{
|
||||
.type = CV_HEADERS,
|
||||
.u.uh = {
|
||||
.uh_stream_id = 12345,
|
||||
.uh_oth_stream_id = 0x1234,
|
||||
.uh_weight = 1,
|
||||
.uh_exclusive = 0,
|
||||
.uh_off = 0,
|
||||
UH_HEADERS("HTTP/1.1 302 Found\r\n"
|
||||
.u.headers = {
|
||||
.stream_id = 12345,
|
||||
.oth_stream_id = 0x1234,
|
||||
.weight = 1,
|
||||
.exclusive = 0,
|
||||
.off = 0,
|
||||
.flags = UH_H1H,
|
||||
HEADERS("HTTP/1.1 302 Found\r\n"
|
||||
"Cookie: a=b; c=d; e=f\r\n\r\n"),
|
||||
},
|
||||
},
|
||||
|
@ -517,13 +549,14 @@ static const struct frame_reader_test tests[] = {
|
|||
.frt_cb_vals = {
|
||||
{
|
||||
.type = CV_HEADERS,
|
||||
.u.uh = {
|
||||
.uh_stream_id = 12345,
|
||||
.uh_oth_stream_id = 0x1234,
|
||||
.uh_weight = 1,
|
||||
.uh_exclusive = 0,
|
||||
.uh_off = 0,
|
||||
UH_HEADERS("GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n"),
|
||||
.u.headers = {
|
||||
.stream_id = 12345,
|
||||
.oth_stream_id = 0x1234,
|
||||
.weight = 1,
|
||||
.exclusive = 0,
|
||||
.off = 0,
|
||||
.flags = UH_H1H,
|
||||
HEADERS("GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n"),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -559,13 +592,14 @@ static const struct frame_reader_test tests[] = {
|
|||
.frt_cb_vals = {
|
||||
{
|
||||
.type = CV_HEADERS,
|
||||
.u.uh = {
|
||||
.uh_stream_id = 12345,
|
||||
.uh_oth_stream_id = 0x1234,
|
||||
.uh_weight = 1,
|
||||
.uh_exclusive = 0,
|
||||
.uh_off = 0,
|
||||
UH_HEADERS("GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n"),
|
||||
.u.headers = {
|
||||
.stream_id = 12345,
|
||||
.oth_stream_id = 0x1234,
|
||||
.weight = 1,
|
||||
.exclusive = 0,
|
||||
.off = 0,
|
||||
.flags = UH_H1H,
|
||||
HEADERS("GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n"),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -707,13 +741,14 @@ static const struct frame_reader_test tests[] = {
|
|||
},
|
||||
{
|
||||
.type = CV_HEADERS,
|
||||
.u.uh = {
|
||||
.uh_stream_id = 12345,
|
||||
.uh_oth_stream_id = 0,
|
||||
.uh_weight = 0,
|
||||
.uh_exclusive = -1,
|
||||
.uh_off = 0,
|
||||
UH_HEADERS("GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n"),
|
||||
.u.headers = {
|
||||
.stream_id = 12345,
|
||||
.oth_stream_id = 0,
|
||||
.weight = 0,
|
||||
.exclusive = -1,
|
||||
.off = 0,
|
||||
.flags = UH_H1H,
|
||||
HEADERS("GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n"),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -737,11 +772,11 @@ static const struct frame_reader_test tests[] = {
|
|||
.frt_cb_vals = {
|
||||
{
|
||||
.type = CV_PUSH_PROMISE,
|
||||
.u.uh = {
|
||||
.uh_stream_id = 12345,
|
||||
.uh_oth_stream_id = 0x123456,
|
||||
.uh_flags = UH_PP,
|
||||
UH_HEADERS("GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n"),
|
||||
.u.headers = {
|
||||
.stream_id = 12345,
|
||||
.oth_stream_id = 0x123456,
|
||||
.flags = UH_PP | UH_H1H,
|
||||
HEADERS("GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n"),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -769,13 +804,14 @@ static const struct frame_reader_test tests[] = {
|
|||
.frt_cb_vals = {
|
||||
{
|
||||
.type = CV_HEADERS,
|
||||
.u.uh = {
|
||||
.uh_stream_id = 12345,
|
||||
.uh_oth_stream_id = 0,
|
||||
.uh_weight = 0,
|
||||
.uh_exclusive = -1,
|
||||
.uh_off = 0,
|
||||
UH_HEADERS("HTTP/1.1 302 Found\r\n\r\n"),
|
||||
.u.headers = {
|
||||
.stream_id = 12345,
|
||||
.oth_stream_id = 0,
|
||||
.weight = 0,
|
||||
.exclusive = -1,
|
||||
.off = 0,
|
||||
.flags = UH_H1H,
|
||||
HEADERS("HTTP/1.1 302 Found\r\n\r\n"),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1064,8 +1100,17 @@ test_one_frt (const struct frame_reader_test *frt)
|
|||
unsigned short exp_off;
|
||||
struct lshpack_dec hdec;
|
||||
struct lsquic_mm mm;
|
||||
struct lsquic_conn lconn;
|
||||
struct lsquic_conn_public conn_pub;
|
||||
struct lsquic_stream stream;
|
||||
int s;
|
||||
|
||||
memset(&stream, 0, sizeof(stream));
|
||||
memset(&lconn, 0, sizeof(lconn));
|
||||
memset(&conn_pub, 0, sizeof(conn_pub));
|
||||
stream.conn_pub = &conn_pub;
|
||||
conn_pub.lconn = &lconn;
|
||||
|
||||
lsquic_mm_init(&mm);
|
||||
lshpack_dec_init(&hdec);
|
||||
memset(&input, 0, sizeof(input));
|
||||
|
@ -1079,7 +1124,8 @@ test_one_frt (const struct frame_reader_test *frt)
|
|||
++input.in_max_sz;
|
||||
|
||||
fr = lsquic_frame_reader_new(frt->frt_fr_flags, frt->frt_max_headers_sz,
|
||||
&mm, NULL, read_from_stream, &hdec, &frame_callbacks, &g_cb_ctx);
|
||||
&mm, &stream, read_from_stream, &hdec, &frame_callbacks, &g_cb_ctx,
|
||||
lsquic_http1x_if, NULL);
|
||||
do
|
||||
{
|
||||
s = lsquic_frame_reader_read(fr);
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
#include "lsquic_frame_common.h"
|
||||
#include "lsquic_frame_writer.h"
|
||||
#include "lsquic_frame_reader.h"
|
||||
#include "lsquic_headers.h"
|
||||
#include "lsquic_http1x_if.h"
|
||||
|
||||
|
||||
struct lsquic_stream
|
||||
|
@ -140,11 +142,14 @@ static struct lsquic_http_header header_arr[N_HEADERS];
|
|||
static void
|
||||
compare_headers (struct uncompressed_headers *uh)
|
||||
{
|
||||
struct http1x_headers *h1h;
|
||||
char line[0x100], *s;
|
||||
FILE *in;
|
||||
unsigned i;
|
||||
|
||||
in = fmemopen(uh->uh_headers, uh->uh_size, "r");
|
||||
assert(uh->uh_flags & UH_H1H);
|
||||
h1h = uh->uh_hset;
|
||||
in = fmemopen(h1h->h1h_buf, h1h->h1h_size, "r");
|
||||
for (i = 0; i < N_HEADERS; ++i)
|
||||
{
|
||||
s = fgets(line, sizeof(line), in);
|
||||
|
@ -211,7 +216,7 @@ test_rw (unsigned max_frame_sz)
|
|||
stream->sm_off = 0;
|
||||
|
||||
fr = lsquic_frame_reader_new(0, 0, &mm, stream, read_from_stream, &hdec,
|
||||
&frame_callbacks, &uh);
|
||||
&frame_callbacks, &uh, lsquic_http1x_if, NULL);
|
||||
do
|
||||
{
|
||||
s = lsquic_frame_reader_read(fr);
|
||||
|
|
Loading…
Reference in a new issue