diff --git a/CHANGELOG b/CHANGELOG index e314d6a..4c2a375 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -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 diff --git a/include/lsquic.h b/include/lsquic.h index adfc33c..45321d5 100644 --- a/include/lsquic.h +++ b/include/lsquic.h @@ -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); diff --git a/src/liblsquic/CMakeLists.txt b/src/liblsquic/CMakeLists.txt index b5452b4..a5ad40f 100644 --- a/src/liblsquic/CMakeLists.txt +++ b/src/liblsquic/CMakeLists.txt @@ -53,6 +53,7 @@ SET(lsquic_STAT_SRCS lsquic_min_heap.c lshpack.c lsquic_parse_Q044.c + lsquic_http1x_if.c ) diff --git a/src/liblsquic/lshpack.c b/src/liblsquic/lshpack.c index d61e0e6..0b0c3be 100644 --- a/src/liblsquic/lshpack.c +++ b/src/liblsquic/lshpack.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 } diff --git a/src/liblsquic/lshpack.h b/src/liblsquic/lshpack.h index 1fc0550..132b9fd 100644 --- a/src/liblsquic/lshpack.h +++ b/src/liblsquic/lshpack.h @@ -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 diff --git a/src/liblsquic/lsquic_engine.c b/src/liblsquic/lsquic_engine.c index 7cc5a36..d2b737a 100644 --- a/src/liblsquic/lsquic_engine.c +++ b/src/liblsquic/lsquic_engine.c @@ -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; diff --git a/src/liblsquic/lsquic_engine_public.h b/src/liblsquic/lsquic_engine_public.h index 4f7e9b5..e9badd1 100644 --- a/src/liblsquic/lsquic_engine_public.h +++ b/src/liblsquic/lsquic_engine_public.h @@ -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; diff --git a/src/liblsquic/lsquic_ev_log.c b/src/liblsquic/lsquic_ev_log.c index e06d587..b9b070d 100644 --- a/src/liblsquic/lsquic_ev_log.c +++ b/src/liblsquic/lsquic_ev_log.c @@ -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; + } } } diff --git a/src/liblsquic/lsquic_frame_reader.c b/src/liblsquic/lsquic_frame_reader.c index b24d17b..bc24e6d 100644 --- a/src/liblsquic/lsquic_frame_reader.c +++ b/src/liblsquic/lsquic_frame_reader.c @@ -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 } diff --git a/src/liblsquic/lsquic_frame_reader.h b/src/liblsquic/lsquic_frame_reader.h index f788c50..e781960 100644 --- a/src/liblsquic/lsquic_frame_reader.h +++ b/src/liblsquic/lsquic_frame_reader.h @@ -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 *); diff --git a/src/liblsquic/lsquic_frame_writer.c b/src/liblsquic/lsquic_frame_writer.c index b819b0b..c6a5ff2 100644 --- a/src/liblsquic/lsquic_frame_writer.c +++ b/src/liblsquic/lsquic_frame_writer.c @@ -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. */ diff --git a/src/liblsquic/lsquic_frame_writer.h b/src/liblsquic/lsquic_frame_writer.h index 974555f..f328d2b 100644 --- a/src/liblsquic/lsquic_frame_writer.h +++ b/src/liblsquic/lsquic_frame_writer.h @@ -9,6 +9,8 @@ #include #include +#define MAX_HEADERS_SIZE (64 * 1024) + struct iovec; struct lshpack_enc; struct lsquic_mm; diff --git a/src/liblsquic/lsquic_full_conn.c b/src/liblsquic/lsquic_full_conn.c index f81a326..157ea13 100644 --- a/src/liblsquic/lsquic_full_conn.c +++ b/src/liblsquic/lsquic_full_conn.c @@ -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; diff --git a/src/liblsquic/lsquic_headers.h b/src/liblsquic/lsquic_headers.h new file mode 100644 index 0000000..411bbf3 --- /dev/null +++ b/src/liblsquic/lsquic_headers.h @@ -0,0 +1,43 @@ +/* Copyright (c) 2017 - 2018 LiteSpeed Technologies Inc. See LICENSE. */ +#ifndef LSQUIC_HEADERS_H +#define LSQUIC_HEADERS_H 1 + +#include + +/* 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 diff --git a/src/liblsquic/lsquic_headers_stream.c b/src/liblsquic/lsquic_headers_stream.c index 56833d0..bf12d9a 100644 --- a/src/liblsquic/lsquic_headers_stream.c +++ b/src/liblsquic/lsquic_headers_stream.c @@ -13,12 +13,15 @@ #include #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; } diff --git a/src/liblsquic/lsquic_headers_stream.h b/src/liblsquic/lsquic_headers_stream.h index 993e6e6..b72875a 100644 --- a/src/liblsquic/lsquic_headers_stream.h +++ b/src/liblsquic/lsquic_headers_stream.h @@ -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); diff --git a/src/liblsquic/lsquic_http1x_if.c b/src/liblsquic/lsquic_http1x_if.c new file mode 100644 index 0000000..ae8824e --- /dev/null +++ b/src/liblsquic/lsquic_http1x_if.c @@ -0,0 +1,525 @@ +/* Copyright (c) 2017 - 2018 LiteSpeed Technologies Inc. See LICENSE. */ +#include +#include +#include +#include +#include + +#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; diff --git a/src/liblsquic/lsquic_http1x_if.h b/src/liblsquic/lsquic_http1x_if.h new file mode 100644 index 0000000..d53437b --- /dev/null +++ b/src/liblsquic/lsquic_http1x_if.h @@ -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 diff --git a/src/liblsquic/lsquic_logger.c b/src/liblsquic/lsquic_logger.c index d7c9436..f170be5 100644 --- a/src/liblsquic/lsquic_logger.c +++ b/src/liblsquic/lsquic_logger.c @@ -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] = { diff --git a/src/liblsquic/lsquic_logger.h b/src/liblsquic/lsquic_logger.h index d850f93..d07fa43 100644 --- a/src/liblsquic/lsquic_logger.h +++ b/src/liblsquic/lsquic_logger.h @@ -71,6 +71,7 @@ enum lsquic_logger_module { LSQLM_DI, LSQLM_PACER, LSQLM_MIN_HEAP, + LSQLM_HTTP1X, N_LSQUIC_LOGGER_MODULES }; diff --git a/src/liblsquic/lsquic_stream.c b/src/liblsquic/lsquic_stream.c index b69cbba..bf74228 100644 --- a/src/liblsquic/lsquic_stream.c +++ b/src/liblsquic/lsquic_stream.c @@ -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; +} diff --git a/test/http_client.c b/test/http_client.c index e7fce99..738c90f 100644 --- a/test/http_client.c +++ b/test/http_client.c @@ -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; diff --git a/test/unittests/test_engine_ctor.c b/test/unittests/test_engine_ctor.c index 9402733..9b7b5b1 100644 --- a/test/unittests/test_engine_ctor.c +++ b/test/unittests/test_engine_ctor.c @@ -1,6 +1,7 @@ /* Copyright (c) 2017 - 2018 LiteSpeed Technologies Inc. See LICENSE. */ #include #include +#include #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); diff --git a/test/unittests/test_frame_reader.c b/test/unittests/test_frame_reader.c index 23b491e..29f546c 100644 --- a/test/unittests/test_frame_reader.c +++ b/test/unittests/test_frame_reader.c @@ -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); diff --git a/test/unittests/test_frame_rw.c b/test/unittests/test_frame_rw.c index 85fb2f1..5e481e8 100644 --- a/test/unittests/test_frame_rw.c +++ b/test/unittests/test_frame_rw.c @@ -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);