1a74702c6SGeorge Wang/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc. See LICENSE. */ 23b55e6aeSDmitri Tikhonov#include <assert.h> 33b55e6aeSDmitri Tikhonov#include <ctype.h> 43b55e6aeSDmitri Tikhonov#include <stddef.h> 53b55e6aeSDmitri Tikhonov#include <stdlib.h> 63b55e6aeSDmitri Tikhonov#include <string.h> 73b55e6aeSDmitri Tikhonov 83b55e6aeSDmitri Tikhonov#include "lsquic.h" 93b55e6aeSDmitri Tikhonov#include "lsquic_headers.h" 103b55e6aeSDmitri Tikhonov#include "lsquic_http1x_if.h" 113b55e6aeSDmitri Tikhonov#include "lshpack.h" 123b55e6aeSDmitri Tikhonov 133b55e6aeSDmitri Tikhonov#define LSQUIC_LOGGER_MODULE LSQLM_HTTP1X 145392f7a3SLiteSpeed Tech#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(hwc->hwc_conn) 153b55e6aeSDmitri Tikhonov#include "lsquic_logger.h" 163b55e6aeSDmitri Tikhonov 173b55e6aeSDmitri Tikhonovenum pseudo_header 183b55e6aeSDmitri Tikhonov{ 193b55e6aeSDmitri Tikhonov PSEH_METHOD, 203b55e6aeSDmitri Tikhonov PSEH_SCHEME, 213b55e6aeSDmitri Tikhonov PSEH_AUTHORITY, 223b55e6aeSDmitri Tikhonov PSEH_PATH, 233b55e6aeSDmitri Tikhonov PSEH_STATUS, 243b55e6aeSDmitri Tikhonov N_PSEH 253b55e6aeSDmitri Tikhonov}; 263b55e6aeSDmitri Tikhonov 273b55e6aeSDmitri Tikhonov#define BIT(x) (1 << (x)) 283b55e6aeSDmitri Tikhonov 293b55e6aeSDmitri Tikhonov#define ALL_REQUEST_PSEH (BIT(PSEH_METHOD)|BIT(PSEH_SCHEME)|BIT(PSEH_AUTHORITY)|BIT(PSEH_PATH)) 303b55e6aeSDmitri Tikhonov#define REQUIRED_REQUEST_PSEH (BIT(PSEH_METHOD)|BIT(PSEH_SCHEME)|BIT(PSEH_PATH)) 313b55e6aeSDmitri Tikhonov 323b55e6aeSDmitri Tikhonov#define ALL_SERVER_PSEH BIT(PSEH_STATUS) 333b55e6aeSDmitri Tikhonov#define REQUIRED_SERVER_PSEH ALL_SERVER_PSEH 343b55e6aeSDmitri Tikhonov 353b55e6aeSDmitri Tikhonov#define PSEH_LEN(h) (sizeof(#h) - 5) 363b55e6aeSDmitri Tikhonov 373b55e6aeSDmitri Tikhonovstruct header_writer_ctx 383b55e6aeSDmitri Tikhonov{ 395392f7a3SLiteSpeed Tech const struct lsquic_conn *hwc_conn; 403b55e6aeSDmitri Tikhonov char *buf; 413b55e6aeSDmitri Tikhonov char *cookie_val; 423b55e6aeSDmitri Tikhonov unsigned cookie_sz, cookie_nalloc; 433b55e6aeSDmitri Tikhonov unsigned max_headers_sz, 443b55e6aeSDmitri Tikhonov headers_sz, 453b55e6aeSDmitri Tikhonov w_off; 463b55e6aeSDmitri Tikhonov enum { 473b55e6aeSDmitri Tikhonov HWC_SERVER = 1 << 0, 483b55e6aeSDmitri Tikhonov HWC_EXPECT_COLON = 1 << 1, 493b55e6aeSDmitri Tikhonov HWC_SEEN_HOST = 1 << 2, 503b55e6aeSDmitri Tikhonov HWC_PUSH_PROMISE = 1 << 3, 513b55e6aeSDmitri Tikhonov } hwc_flags; 523b55e6aeSDmitri Tikhonov enum pseudo_header pseh_mask; 533b55e6aeSDmitri Tikhonov char *pseh_bufs[N_PSEH]; 543b55e6aeSDmitri Tikhonov struct http1x_headers hwc_h1h; 55a5fa05f9SDmitri Tikhonov size_t hwc_header_buf_nalloc; 56a5fa05f9SDmitri Tikhonov struct lsxpack_header hwc_xhdr; 573b55e6aeSDmitri Tikhonov}; 583b55e6aeSDmitri Tikhonov 593b55e6aeSDmitri Tikhonov 603b55e6aeSDmitri Tikhonov#define HWC_PSEH_LEN(hwc, ph) ((int) strlen((hwc)->pseh_bufs[ph])) 613b55e6aeSDmitri Tikhonov 623b55e6aeSDmitri Tikhonov#define HWC_PSEH_VAL(hwc, ph) ((hwc)->pseh_bufs[ph]) 633b55e6aeSDmitri Tikhonov 643b55e6aeSDmitri Tikhonovstatic void * 6555613f44SDmitri Tikhonovh1h_create_header_set (void *ctx, lsquic_stream_t *stream, int is_push_promise) 663b55e6aeSDmitri Tikhonov{ 673b55e6aeSDmitri Tikhonov const struct http1x_ctor_ctx *hcc = ctx; 683b55e6aeSDmitri Tikhonov struct header_writer_ctx *hwc; 693b55e6aeSDmitri Tikhonov 703b55e6aeSDmitri Tikhonov hwc = calloc(1, sizeof(*hwc)); 713b55e6aeSDmitri Tikhonov if (!hwc) 723b55e6aeSDmitri Tikhonov return NULL; 733b55e6aeSDmitri Tikhonov 743b55e6aeSDmitri Tikhonov hwc->hwc_flags = HWC_EXPECT_COLON; 753b55e6aeSDmitri Tikhonov if (hcc->is_server) 763b55e6aeSDmitri Tikhonov hwc->hwc_flags |= HWC_SERVER; 773b55e6aeSDmitri Tikhonov if (is_push_promise) 783b55e6aeSDmitri Tikhonov hwc->hwc_flags |= HWC_PUSH_PROMISE; 793b55e6aeSDmitri Tikhonov hwc->max_headers_sz = hcc->max_headers_sz; 805392f7a3SLiteSpeed Tech hwc->hwc_conn = hcc->conn; 813b55e6aeSDmitri Tikhonov return &hwc->hwc_h1h; 823b55e6aeSDmitri Tikhonov} 833b55e6aeSDmitri Tikhonov 843b55e6aeSDmitri Tikhonov 853b55e6aeSDmitri Tikhonovstatic int 863b55e6aeSDmitri Tikhonovhwc_uh_write (struct header_writer_ctx *hwc, const void *buf, size_t sz) 873b55e6aeSDmitri Tikhonov{ 883b55e6aeSDmitri Tikhonov char *h1h_buf; 893b55e6aeSDmitri Tikhonov 903b55e6aeSDmitri Tikhonov if (hwc->w_off + sz > hwc->headers_sz) 913b55e6aeSDmitri Tikhonov { 923b55e6aeSDmitri Tikhonov if (hwc->headers_sz * 2 >= hwc->w_off + sz) 933b55e6aeSDmitri Tikhonov hwc->headers_sz *= 2; 943b55e6aeSDmitri Tikhonov else 953b55e6aeSDmitri Tikhonov hwc->headers_sz = hwc->w_off + sz; 963b55e6aeSDmitri Tikhonov h1h_buf = realloc(hwc->hwc_h1h.h1h_buf, hwc->headers_sz); 972d296031SDmitri Tikhonov if (!h1h_buf) 983b55e6aeSDmitri Tikhonov return -1; 993b55e6aeSDmitri Tikhonov hwc->hwc_h1h.h1h_buf = h1h_buf; 1003b55e6aeSDmitri Tikhonov } 1013b55e6aeSDmitri Tikhonov memcpy(&hwc->hwc_h1h.h1h_buf[hwc->w_off], buf, sz); 1023b55e6aeSDmitri Tikhonov hwc->w_off += sz; 1033b55e6aeSDmitri Tikhonov return 0; 1043b55e6aeSDmitri Tikhonov} 1053b55e6aeSDmitri Tikhonov 1063b55e6aeSDmitri Tikhonov 107a5fa05f9SDmitri Tikhonovstatic int 1083b55e6aeSDmitri Tikhonovsave_pseudo_header (struct header_writer_ctx *hwc, enum pseudo_header ph, 1093b55e6aeSDmitri Tikhonov const char *val, unsigned val_len) 1103b55e6aeSDmitri Tikhonov{ 1113b55e6aeSDmitri Tikhonov if (0 == (hwc->pseh_mask & BIT(ph))) 1123b55e6aeSDmitri Tikhonov { 1133b55e6aeSDmitri Tikhonov assert(!hwc->pseh_bufs[ph]); 1143b55e6aeSDmitri Tikhonov hwc->pseh_bufs[ph] = malloc(val_len + 1); 1153b55e6aeSDmitri Tikhonov if (!hwc->pseh_bufs[ph]) 116a5fa05f9SDmitri Tikhonov return -1; 1173b55e6aeSDmitri Tikhonov hwc->pseh_mask |= BIT(ph); 1183b55e6aeSDmitri Tikhonov memcpy(hwc->pseh_bufs[ph], val, val_len); 1193b55e6aeSDmitri Tikhonov hwc->pseh_bufs[ph][val_len] = '\0'; 120a5fa05f9SDmitri Tikhonov return 0; 1213b55e6aeSDmitri Tikhonov } 1223b55e6aeSDmitri Tikhonov else 1233b55e6aeSDmitri Tikhonov { 1243b55e6aeSDmitri Tikhonov LSQ_INFO("header %u is already present", ph); 125a5fa05f9SDmitri Tikhonov return 1; 1263b55e6aeSDmitri Tikhonov } 1273b55e6aeSDmitri Tikhonov} 1283b55e6aeSDmitri Tikhonov 1293b55e6aeSDmitri Tikhonov 130a5fa05f9SDmitri Tikhonovstatic int 131a5fa05f9SDmitri Tikhonovadd_pseudo_header (struct header_writer_ctx *hwc, struct lsxpack_header *xhdr) 1323b55e6aeSDmitri Tikhonov{ 133a5fa05f9SDmitri Tikhonov const char *name, *val; 134a5fa05f9SDmitri Tikhonov unsigned name_len, val_len; 135a5fa05f9SDmitri Tikhonov 1363b55e6aeSDmitri Tikhonov if (!(hwc->hwc_flags & HWC_EXPECT_COLON)) 1373b55e6aeSDmitri Tikhonov { 1383b55e6aeSDmitri Tikhonov LSQ_INFO("unexpected colon"); 139a5fa05f9SDmitri Tikhonov return 1; 1403b55e6aeSDmitri Tikhonov } 1413b55e6aeSDmitri Tikhonov 142a5fa05f9SDmitri Tikhonov name = lsxpack_header_get_name(xhdr); 143a5fa05f9SDmitri Tikhonov val = lsxpack_header_get_value(xhdr); 144a5fa05f9SDmitri Tikhonov name_len = xhdr->name_len; 145a5fa05f9SDmitri Tikhonov val_len = xhdr->val_len; 146a5fa05f9SDmitri Tikhonov 1473b55e6aeSDmitri Tikhonov switch (name_len) 1483b55e6aeSDmitri Tikhonov { 1493b55e6aeSDmitri Tikhonov case 5: 1503b55e6aeSDmitri Tikhonov if (0 == memcmp(name, ":path", 5)) 1513b55e6aeSDmitri Tikhonov return save_pseudo_header(hwc, PSEH_PATH, val, val_len); 1523b55e6aeSDmitri Tikhonov break; 1533b55e6aeSDmitri Tikhonov case 7: 1543b55e6aeSDmitri Tikhonov switch (name[2]) 1553b55e6aeSDmitri Tikhonov { 1563b55e6aeSDmitri Tikhonov case 'c': 1573b55e6aeSDmitri Tikhonov if (0 == memcmp(name, ":scheme", 7)) 1583b55e6aeSDmitri Tikhonov return save_pseudo_header(hwc, PSEH_SCHEME, val, val_len); 1593b55e6aeSDmitri Tikhonov break; 1603b55e6aeSDmitri Tikhonov case 'e': 1613b55e6aeSDmitri Tikhonov if (0 == memcmp(name, ":method", 7)) 1623b55e6aeSDmitri Tikhonov return save_pseudo_header(hwc, PSEH_METHOD, val, val_len); 1633b55e6aeSDmitri Tikhonov break; 1643b55e6aeSDmitri Tikhonov case 't': 1653b55e6aeSDmitri Tikhonov if (0 == memcmp(name, ":status", 7)) 1663b55e6aeSDmitri Tikhonov return save_pseudo_header(hwc, PSEH_STATUS, val, val_len); 1673b55e6aeSDmitri Tikhonov break; 1683b55e6aeSDmitri Tikhonov } 1693b55e6aeSDmitri Tikhonov break; 1703b55e6aeSDmitri Tikhonov case 10: 1713b55e6aeSDmitri Tikhonov if (0 == memcmp(name, ":authority", 10)) 1723b55e6aeSDmitri Tikhonov return save_pseudo_header(hwc, PSEH_AUTHORITY, val, val_len); 1733b55e6aeSDmitri Tikhonov break; 1743b55e6aeSDmitri Tikhonov } 1753b55e6aeSDmitri Tikhonov 1763b55e6aeSDmitri Tikhonov LSQ_INFO("unknown pseudo-header `%.*s'", name_len, name); 177a5fa05f9SDmitri Tikhonov return 1; 1783b55e6aeSDmitri Tikhonov} 1793b55e6aeSDmitri Tikhonov 1803b55e6aeSDmitri Tikhonov 1813b55e6aeSDmitri Tikhonov#define HTTP_CODE_LEN 3 1823b55e6aeSDmitri Tikhonov 1833b55e6aeSDmitri Tikhonovstatic const char * 1843b55e6aeSDmitri Tikhonovcode_str_to_reason (const char code_str[HTTP_CODE_LEN]) 1853b55e6aeSDmitri Tikhonov{ 1863b55e6aeSDmitri Tikhonov /* RFC 7231, Section 6: */ 1873b55e6aeSDmitri Tikhonov static const char *const http_reason_phrases[] = 1883b55e6aeSDmitri Tikhonov { 1893b55e6aeSDmitri Tikhonov #define HTTP_REASON_CODE(code, reason) [code - 100] = reason 1903b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(100, "Continue"), 1913b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(101, "Switching Protocols"), 1923b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(200, "OK"), 1933b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(201, "Created"), 1943b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(202, "Accepted"), 1953b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(203, "Non-Authoritative Information"), 1963b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(204, "No Content"), 1973b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(205, "Reset Content"), 1983b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(206, "Partial Content"), 1993b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(300, "Multiple Choices"), 2003b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(301, "Moved Permanently"), 2013b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(302, "Found"), 2023b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(303, "See Other"), 2033b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(304, "Not Modified"), 2043b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(305, "Use Proxy"), 2053b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(307, "Temporary Redirect"), 2063b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(400, "Bad Request"), 2073b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(401, "Unauthorized"), 2083b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(402, "Payment Required"), 2093b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(403, "Forbidden"), 2103b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(404, "Not Found"), 2113b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(405, "Method Not Allowed"), 2123b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(406, "Not Acceptable"), 2133b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(407, "Proxy Authentication Required"), 2143b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(408, "Request Timeout"), 2153b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(409, "Conflict"), 2163b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(410, "Gone"), 2173b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(411, "Length Required"), 2183b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(412, "Precondition Failed"), 2193b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(413, "Payload Too Large"), 2203b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(414, "URI Too Long"), 2213b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(415, "Unsupported Media Type"), 2223b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(416, "Range Not Satisfiable"), 2233b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(417, "Expectation Failed"), 2243b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(426, "Upgrade Required"), 2253b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(500, "Internal Server Error"), 2263b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(501, "Not Implemented"), 2273b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(502, "Bad Gateway"), 2283b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(503, "Service Unavailable"), 2293b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(504, "Gateway Timeout"), 2303b55e6aeSDmitri Tikhonov HTTP_REASON_CODE(505, "HTTP Version Not Supported"), 2313b55e6aeSDmitri Tikhonov #undef HTTP_REASON_CODE 2323b55e6aeSDmitri Tikhonov }; 2333b55e6aeSDmitri Tikhonov 2343b55e6aeSDmitri Tikhonov long code; 2353b55e6aeSDmitri Tikhonov char code_buf[HTTP_CODE_LEN + 1]; 2363b55e6aeSDmitri Tikhonov 2373b55e6aeSDmitri Tikhonov memcpy(code_buf, code_str, HTTP_CODE_LEN); 2383b55e6aeSDmitri Tikhonov code_buf[HTTP_CODE_LEN] = '\0'; 2393b55e6aeSDmitri Tikhonov code = strtol(code_buf, NULL, 10) - 100; 2403b55e6aeSDmitri Tikhonov if (code > 0 && code < (long) (sizeof(http_reason_phrases) / 2413b55e6aeSDmitri Tikhonov sizeof(http_reason_phrases[0]))) 2423b55e6aeSDmitri Tikhonov return http_reason_phrases[code]; 2433b55e6aeSDmitri Tikhonov else 2443b55e6aeSDmitri Tikhonov return NULL; 2453b55e6aeSDmitri Tikhonov} 2463b55e6aeSDmitri Tikhonov 2473b55e6aeSDmitri Tikhonov 248a5fa05f9SDmitri Tikhonovstatic int 2493b55e6aeSDmitri Tikhonovconvert_response_pseudo_headers (struct header_writer_ctx *hwc) 2503b55e6aeSDmitri Tikhonov{ 2513b55e6aeSDmitri Tikhonov if ((hwc->pseh_mask & REQUIRED_SERVER_PSEH) != REQUIRED_SERVER_PSEH) 2523b55e6aeSDmitri Tikhonov { 2533b55e6aeSDmitri Tikhonov LSQ_INFO("not all response pseudo-headers are specified"); 254a5fa05f9SDmitri Tikhonov return 1; 2553b55e6aeSDmitri Tikhonov } 2563b55e6aeSDmitri Tikhonov if (hwc->pseh_mask & ALL_REQUEST_PSEH) 2573b55e6aeSDmitri Tikhonov { 2583b55e6aeSDmitri Tikhonov LSQ_INFO("response pseudo-headers contain request-only headers"); 259a5fa05f9SDmitri Tikhonov return 1; 2603b55e6aeSDmitri Tikhonov } 2613b55e6aeSDmitri Tikhonov 2623b55e6aeSDmitri Tikhonov const char *code_str, *reason; 2633b55e6aeSDmitri Tikhonov int code_len; 2643b55e6aeSDmitri Tikhonov 2653b55e6aeSDmitri Tikhonov code_str = HWC_PSEH_VAL(hwc, PSEH_STATUS); 2663b55e6aeSDmitri Tikhonov code_len = HWC_PSEH_LEN(hwc, PSEH_STATUS); 2673b55e6aeSDmitri Tikhonov 2683b55e6aeSDmitri Tikhonov#define HWC_UH_WRITE(h, buf, sz) do { \ 2693b55e6aeSDmitri Tikhonov if (0 != hwc_uh_write(h, buf, sz)) \ 270a5fa05f9SDmitri Tikhonov return -1; \ 2713b55e6aeSDmitri Tikhonov} while (0) 2723b55e6aeSDmitri Tikhonov 2733b55e6aeSDmitri Tikhonov HWC_UH_WRITE(hwc, "HTTP/1.1 ", 9); 2743b55e6aeSDmitri Tikhonov HWC_UH_WRITE(hwc, code_str, code_len); 2753b55e6aeSDmitri Tikhonov if (HTTP_CODE_LEN == code_len && (reason = code_str_to_reason(code_str))) 2763b55e6aeSDmitri Tikhonov { 2773b55e6aeSDmitri Tikhonov HWC_UH_WRITE(hwc, " ", 1); 2783b55e6aeSDmitri Tikhonov HWC_UH_WRITE(hwc, reason, strlen(reason)); 2793b55e6aeSDmitri Tikhonov HWC_UH_WRITE(hwc, "\r\n", 2); 2803b55e6aeSDmitri Tikhonov } 2813b55e6aeSDmitri Tikhonov else 2823b55e6aeSDmitri Tikhonov HWC_UH_WRITE(hwc, " \r\n", 3); 2833b55e6aeSDmitri Tikhonov if (hwc->max_headers_sz && hwc->w_off > hwc->max_headers_sz) 2843b55e6aeSDmitri Tikhonov { 2853b55e6aeSDmitri Tikhonov LSQ_INFO("headers too large"); 286a5fa05f9SDmitri Tikhonov return 1; 2873b55e6aeSDmitri Tikhonov } 288a5fa05f9SDmitri Tikhonov return 0; 2893b55e6aeSDmitri Tikhonov 2903b55e6aeSDmitri Tikhonov#undef HWC_UH_WRITE 2913b55e6aeSDmitri Tikhonov} 2923b55e6aeSDmitri Tikhonov 2933b55e6aeSDmitri Tikhonov 294a5fa05f9SDmitri Tikhonovstatic int 2953b55e6aeSDmitri Tikhonovconvert_request_pseudo_headers (struct header_writer_ctx *hwc) 2963b55e6aeSDmitri Tikhonov{ 2973b55e6aeSDmitri Tikhonov if ((hwc->pseh_mask & REQUIRED_REQUEST_PSEH) != REQUIRED_REQUEST_PSEH) 2983b55e6aeSDmitri Tikhonov { 2993b55e6aeSDmitri Tikhonov LSQ_INFO("not all request pseudo-headers are specified"); 300a5fa05f9SDmitri Tikhonov return 1; 3013b55e6aeSDmitri Tikhonov } 3023b55e6aeSDmitri Tikhonov if (hwc->pseh_mask & ALL_SERVER_PSEH) 3033b55e6aeSDmitri Tikhonov { 3043b55e6aeSDmitri Tikhonov LSQ_INFO("request pseudo-headers contain response-only headers"); 305a5fa05f9SDmitri Tikhonov return 1; 3063b55e6aeSDmitri Tikhonov } 3073b55e6aeSDmitri Tikhonov 3083b55e6aeSDmitri Tikhonov#define HWC_UH_WRITE(h, buf, sz) do { \ 3093b55e6aeSDmitri Tikhonov if (0 != hwc_uh_write(h, buf, sz)) \ 310a5fa05f9SDmitri Tikhonov return -1; \ 3113b55e6aeSDmitri Tikhonov} while (0) 3123b55e6aeSDmitri Tikhonov 3133b55e6aeSDmitri Tikhonov HWC_UH_WRITE(hwc, HWC_PSEH_VAL(hwc, PSEH_METHOD), HWC_PSEH_LEN(hwc, PSEH_METHOD)); 3143b55e6aeSDmitri Tikhonov HWC_UH_WRITE(hwc, " ", 1); 3153b55e6aeSDmitri Tikhonov HWC_UH_WRITE(hwc, HWC_PSEH_VAL(hwc, PSEH_PATH), HWC_PSEH_LEN(hwc, PSEH_PATH)); 3163b55e6aeSDmitri Tikhonov HWC_UH_WRITE(hwc, " HTTP/1.1\r\n", 11); 3173b55e6aeSDmitri Tikhonov 3183b55e6aeSDmitri Tikhonov if (hwc->max_headers_sz && hwc->w_off > hwc->max_headers_sz) 3193b55e6aeSDmitri Tikhonov { 3203b55e6aeSDmitri Tikhonov LSQ_INFO("headers too large"); 321a5fa05f9SDmitri Tikhonov return 1; 3223b55e6aeSDmitri Tikhonov } 3233b55e6aeSDmitri Tikhonov 3243b55e6aeSDmitri Tikhonov return 0; 3253b55e6aeSDmitri Tikhonov 3263b55e6aeSDmitri Tikhonov#undef HWC_UH_WRITE 3273b55e6aeSDmitri Tikhonov} 3283b55e6aeSDmitri Tikhonov 3293b55e6aeSDmitri Tikhonov 330a5fa05f9SDmitri Tikhonovstatic int 3313b55e6aeSDmitri Tikhonovconvert_pseudo_headers (struct header_writer_ctx *hwc) 3323b55e6aeSDmitri Tikhonov{ 3333b55e6aeSDmitri Tikhonov /* We are *reading* the message. Thus, a server expects a request, and a 3343b55e6aeSDmitri Tikhonov * client expects a response. Unless we receive a push promise from the 3353b55e6aeSDmitri Tikhonov * server, in which case this should also be a request. 3363b55e6aeSDmitri Tikhonov */ 3373b55e6aeSDmitri Tikhonov if (hwc->hwc_flags & (HWC_SERVER|HWC_PUSH_PROMISE)) 3383b55e6aeSDmitri Tikhonov return convert_request_pseudo_headers(hwc); 3393b55e6aeSDmitri Tikhonov else 3403b55e6aeSDmitri Tikhonov return convert_response_pseudo_headers(hwc); 3413b55e6aeSDmitri Tikhonov} 3423b55e6aeSDmitri Tikhonov 3433b55e6aeSDmitri Tikhonov 344a5fa05f9SDmitri Tikhonovstatic int 3453b55e6aeSDmitri Tikhonovsave_cookie (struct header_writer_ctx *hwc, const char *val, unsigned val_len) 3463b55e6aeSDmitri Tikhonov{ 3473b55e6aeSDmitri Tikhonov char *cookie_val; 3483b55e6aeSDmitri Tikhonov 3493b55e6aeSDmitri Tikhonov if (0 == hwc->cookie_sz) 3503b55e6aeSDmitri Tikhonov { 3513b55e6aeSDmitri Tikhonov hwc->cookie_nalloc = hwc->cookie_sz = val_len; 3523b55e6aeSDmitri Tikhonov cookie_val = malloc(hwc->cookie_nalloc); 3533b55e6aeSDmitri Tikhonov if (!cookie_val) 354a5fa05f9SDmitri Tikhonov return -1; 3553b55e6aeSDmitri Tikhonov hwc->cookie_val = cookie_val; 3563b55e6aeSDmitri Tikhonov memcpy(hwc->cookie_val, val, val_len); 3573b55e6aeSDmitri Tikhonov } 3583b55e6aeSDmitri Tikhonov else 3593b55e6aeSDmitri Tikhonov { 3603b55e6aeSDmitri Tikhonov hwc->cookie_sz += val_len + 2 /* "; " */; 3613b55e6aeSDmitri Tikhonov if (hwc->cookie_sz > hwc->cookie_nalloc) 3623b55e6aeSDmitri Tikhonov { 3633b55e6aeSDmitri Tikhonov hwc->cookie_nalloc = hwc->cookie_nalloc * 2 + val_len + 2; 3643b55e6aeSDmitri Tikhonov cookie_val = realloc(hwc->cookie_val, hwc->cookie_nalloc); 3653b55e6aeSDmitri Tikhonov if (!cookie_val) 366a5fa05f9SDmitri Tikhonov return -1; 3673b55e6aeSDmitri Tikhonov hwc->cookie_val = cookie_val; 3683b55e6aeSDmitri Tikhonov } 3693b55e6aeSDmitri Tikhonov memcpy(hwc->cookie_val + hwc->cookie_sz - val_len - 2, "; ", 2); 3703b55e6aeSDmitri Tikhonov memcpy(hwc->cookie_val + hwc->cookie_sz - val_len, val, val_len); 3713b55e6aeSDmitri Tikhonov } 3723b55e6aeSDmitri Tikhonov 3733b55e6aeSDmitri Tikhonov return 0; 3743b55e6aeSDmitri Tikhonov} 3753b55e6aeSDmitri Tikhonov 3763b55e6aeSDmitri Tikhonov 377a5fa05f9SDmitri Tikhonovstatic int 378a5fa05f9SDmitri Tikhonovadd_real_header (struct header_writer_ctx *hwc, struct lsxpack_header *xhdr) 3793b55e6aeSDmitri Tikhonov{ 380a5fa05f9SDmitri Tikhonov int err; 3813b55e6aeSDmitri Tikhonov unsigned i; 3823b55e6aeSDmitri Tikhonov int n_upper; 383a5fa05f9SDmitri Tikhonov const char *name, *val; 384a5fa05f9SDmitri Tikhonov unsigned name_len, val_len; 3853b55e6aeSDmitri Tikhonov 3863b55e6aeSDmitri Tikhonov if (hwc->hwc_flags & HWC_EXPECT_COLON) 3873b55e6aeSDmitri Tikhonov { 3883b55e6aeSDmitri Tikhonov if (0 != (err = convert_pseudo_headers(hwc))) 3893b55e6aeSDmitri Tikhonov return err; 3903b55e6aeSDmitri Tikhonov hwc->hwc_flags &= ~HWC_EXPECT_COLON; 3913b55e6aeSDmitri Tikhonov } 3923b55e6aeSDmitri Tikhonov 393a5fa05f9SDmitri Tikhonov name = lsxpack_header_get_name(xhdr); 394a5fa05f9SDmitri Tikhonov val = lsxpack_header_get_value(xhdr); 395a5fa05f9SDmitri Tikhonov name_len = xhdr->name_len; 396a5fa05f9SDmitri Tikhonov val_len = xhdr->val_len; 397a5fa05f9SDmitri Tikhonov 3983b55e6aeSDmitri Tikhonov if (4 == name_len && 0 == memcmp(name, "host", 4)) 3993b55e6aeSDmitri Tikhonov hwc->hwc_flags |= HWC_SEEN_HOST; 4003b55e6aeSDmitri Tikhonov 4013b55e6aeSDmitri Tikhonov n_upper = 0; 4023b55e6aeSDmitri Tikhonov for (i = 0; i < name_len; ++i) 4033b55e6aeSDmitri Tikhonov n_upper += isupper(name[i]); 4043b55e6aeSDmitri Tikhonov if (n_upper > 0) 4053b55e6aeSDmitri Tikhonov { 4063b55e6aeSDmitri Tikhonov LSQ_INFO("Header name `%.*s' contains uppercase letters", 4073b55e6aeSDmitri Tikhonov name_len, name); 408a5fa05f9SDmitri Tikhonov return 1; 4093b55e6aeSDmitri Tikhonov } 4103b55e6aeSDmitri Tikhonov 4113b55e6aeSDmitri Tikhonov if (6 == name_len && memcmp(name, "cookie", 6) == 0) 4123b55e6aeSDmitri Tikhonov { 4133b55e6aeSDmitri Tikhonov return save_cookie(hwc, val, val_len); 4143b55e6aeSDmitri Tikhonov } 4153b55e6aeSDmitri Tikhonov 4163b55e6aeSDmitri Tikhonov#define HWC_UH_WRITE(h, buf, sz) do { \ 4173b55e6aeSDmitri Tikhonov if (0 != hwc_uh_write(h, buf, sz)) \ 418a5fa05f9SDmitri Tikhonov return -1; \ 4193b55e6aeSDmitri Tikhonov} while (0) 4203b55e6aeSDmitri Tikhonov 4213b55e6aeSDmitri Tikhonov HWC_UH_WRITE(hwc, name, name_len); 4223b55e6aeSDmitri Tikhonov HWC_UH_WRITE(hwc, ": ", 2); 4233b55e6aeSDmitri Tikhonov HWC_UH_WRITE(hwc, val, val_len); 4243b55e6aeSDmitri Tikhonov HWC_UH_WRITE(hwc, "\r\n", 2); 4253b55e6aeSDmitri Tikhonov 4263b55e6aeSDmitri Tikhonov if (hwc->max_headers_sz && hwc->w_off > hwc->max_headers_sz) 4273b55e6aeSDmitri Tikhonov { 4283b55e6aeSDmitri Tikhonov LSQ_INFO("headers too large"); 429a5fa05f9SDmitri Tikhonov return 1; 4303b55e6aeSDmitri Tikhonov } 4313b55e6aeSDmitri Tikhonov 4323b55e6aeSDmitri Tikhonov return 0; 4333b55e6aeSDmitri Tikhonov 4343b55e6aeSDmitri Tikhonov#undef HWC_UH_WRITE 4353b55e6aeSDmitri Tikhonov} 4363b55e6aeSDmitri Tikhonov 4373b55e6aeSDmitri Tikhonov 438a5fa05f9SDmitri Tikhonovstatic int 439a5fa05f9SDmitri Tikhonovadd_header_to_uh (struct header_writer_ctx *hwc, struct lsxpack_header *xhdr) 4403b55e6aeSDmitri Tikhonov{ 441a5fa05f9SDmitri Tikhonov const char *name; 442a5fa05f9SDmitri Tikhonov 443a5fa05f9SDmitri Tikhonov name = lsxpack_header_get_name(xhdr); 444a5fa05f9SDmitri Tikhonov LSQ_DEBUG("Got header '%.*s': '%.*s'", (int) xhdr->name_len, name, 445a5fa05f9SDmitri Tikhonov (int) xhdr->val_len, lsxpack_header_get_value(xhdr)); 4463b55e6aeSDmitri Tikhonov if (':' == name[0]) 447a5fa05f9SDmitri Tikhonov return add_pseudo_header(hwc, xhdr); 4483b55e6aeSDmitri Tikhonov else 449a5fa05f9SDmitri Tikhonov return add_real_header(hwc, xhdr); 4503b55e6aeSDmitri Tikhonov} 4513b55e6aeSDmitri Tikhonov 4523b55e6aeSDmitri Tikhonov 453a5fa05f9SDmitri Tikhonovstatic int 4543b55e6aeSDmitri Tikhonovh1h_finish_hset (struct header_writer_ctx *hwc) 4553b55e6aeSDmitri Tikhonov{ 456a5fa05f9SDmitri Tikhonov int st; 4573b55e6aeSDmitri Tikhonov 4583b55e6aeSDmitri Tikhonov if (hwc->hwc_flags & HWC_EXPECT_COLON) 4593b55e6aeSDmitri Tikhonov { 4603b55e6aeSDmitri Tikhonov st = convert_pseudo_headers(hwc); 4613b55e6aeSDmitri Tikhonov if (0 != st) 4623b55e6aeSDmitri Tikhonov return st; 4633b55e6aeSDmitri Tikhonov hwc->hwc_flags &= ~HWC_EXPECT_COLON; 4643b55e6aeSDmitri Tikhonov } 4653b55e6aeSDmitri Tikhonov 4663b55e6aeSDmitri Tikhonov#define HWC_UH_WRITE(h, buf, sz) do { \ 4673b55e6aeSDmitri Tikhonov st = hwc_uh_write(h, buf, sz); \ 4683b55e6aeSDmitri Tikhonov if (0 != st) \ 4693b55e6aeSDmitri Tikhonov return st; \ 4703b55e6aeSDmitri Tikhonov} while (0) 4713b55e6aeSDmitri Tikhonov 4723b55e6aeSDmitri Tikhonov if ((hwc->pseh_mask & BIT(PSEH_AUTHORITY)) && 4733b55e6aeSDmitri Tikhonov 0 == (hwc->hwc_flags & HWC_SEEN_HOST)) 4743b55e6aeSDmitri Tikhonov { 4753b55e6aeSDmitri Tikhonov LSQ_DEBUG("Setting 'Host: %.*s'", HWC_PSEH_LEN(hwc, PSEH_AUTHORITY), 4763b55e6aeSDmitri Tikhonov HWC_PSEH_VAL(hwc, PSEH_AUTHORITY)); 4773b55e6aeSDmitri Tikhonov HWC_UH_WRITE(hwc, "Host: ", 6); 4783b55e6aeSDmitri Tikhonov HWC_UH_WRITE(hwc, HWC_PSEH_VAL(hwc, PSEH_AUTHORITY), 4793b55e6aeSDmitri Tikhonov HWC_PSEH_LEN(hwc, PSEH_AUTHORITY)); 4803b55e6aeSDmitri Tikhonov HWC_UH_WRITE(hwc, "\r\n", 2); 4813b55e6aeSDmitri Tikhonov } 4823b55e6aeSDmitri Tikhonov 4833b55e6aeSDmitri Tikhonov if (hwc->cookie_val) 4843b55e6aeSDmitri Tikhonov { 4853b55e6aeSDmitri Tikhonov LSQ_DEBUG("Setting 'Cookie: %.*s'", hwc->cookie_sz, hwc->cookie_val); 4863b55e6aeSDmitri Tikhonov HWC_UH_WRITE(hwc, "Cookie: ", 8); 4873b55e6aeSDmitri Tikhonov HWC_UH_WRITE(hwc, hwc->cookie_val, hwc->cookie_sz); 4883b55e6aeSDmitri Tikhonov HWC_UH_WRITE(hwc, "\r\n", 2); 4893b55e6aeSDmitri Tikhonov } 4903b55e6aeSDmitri Tikhonov 4913b55e6aeSDmitri Tikhonov HWC_UH_WRITE(hwc, "\r\n", 2 + 1 /* NUL byte */); 4923b55e6aeSDmitri Tikhonov hwc->w_off -= 1; /* Do not count NUL byte */ 4933b55e6aeSDmitri Tikhonov hwc->hwc_h1h.h1h_size = hwc->w_off; 4943b55e6aeSDmitri Tikhonov 4953b55e6aeSDmitri Tikhonov if (hwc->max_headers_sz && hwc->w_off > hwc->max_headers_sz) 4963b55e6aeSDmitri Tikhonov { 4973b55e6aeSDmitri Tikhonov LSQ_INFO("headers too large"); 498a5fa05f9SDmitri Tikhonov return 1; 4993b55e6aeSDmitri Tikhonov } 5003b55e6aeSDmitri Tikhonov 501a5fa05f9SDmitri Tikhonov return 0; 5023b55e6aeSDmitri Tikhonov} 5033b55e6aeSDmitri Tikhonov 5043b55e6aeSDmitri Tikhonov#define HWC_PTR(data_in) (struct header_writer_ctx *) \ 5053b55e6aeSDmitri Tikhonov ((unsigned char *) (hset) - offsetof(struct header_writer_ctx, hwc_h1h)) 5063b55e6aeSDmitri Tikhonov 507a5fa05f9SDmitri Tikhonov 508a5fa05f9SDmitri Tikhonovstatic struct lsxpack_header * 509992bbcdbSDmitri Tikhonovh1h_prepare_decode (void *hset, struct lsxpack_header *xhdr, size_t req_space) 510a5fa05f9SDmitri Tikhonov{ 511a5fa05f9SDmitri Tikhonov struct header_writer_ctx *const hwc = HWC_PTR(hset); 51255613f44SDmitri Tikhonov size_t nalloc; 51355613f44SDmitri Tikhonov char *buf; 514a5fa05f9SDmitri Tikhonov 51555613f44SDmitri Tikhonov if (req_space < 0x100) 516992bbcdbSDmitri Tikhonov req_space = 0x100; 517a5fa05f9SDmitri Tikhonov 518992bbcdbSDmitri Tikhonov if (req_space > MAX_HTTP1X_HEADERS_SIZE || req_space > LSXPACK_MAX_STRLEN) 519a5fa05f9SDmitri Tikhonov { 520a5fa05f9SDmitri Tikhonov LSQ_DEBUG("requested space for header is too large: %zd bytes", 521992bbcdbSDmitri Tikhonov req_space); 522a5fa05f9SDmitri Tikhonov return NULL; 523a5fa05f9SDmitri Tikhonov } 524a5fa05f9SDmitri Tikhonov 52555613f44SDmitri Tikhonov if (!xhdr) 526a5fa05f9SDmitri Tikhonov { 52755613f44SDmitri Tikhonov if (0 == hwc->hwc_header_buf_nalloc 52855613f44SDmitri Tikhonov || req_space > hwc->hwc_header_buf_nalloc) 529a5fa05f9SDmitri Tikhonov { 530fb3e20e0SDmitri Tikhonov buf = malloc(req_space); 53155613f44SDmitri Tikhonov if (!buf) 53255613f44SDmitri Tikhonov { 533fb3e20e0SDmitri Tikhonov LSQ_DEBUG("cannot allocate %zd bytes", req_space); 53455613f44SDmitri Tikhonov return NULL; 53555613f44SDmitri Tikhonov } 536fb3e20e0SDmitri Tikhonov hwc->hwc_header_buf_nalloc = req_space; 537a5fa05f9SDmitri Tikhonov } 53855613f44SDmitri Tikhonov else 53955613f44SDmitri Tikhonov buf = hwc->hwc_xhdr.buf; 54055613f44SDmitri Tikhonov lsxpack_header_prepare_decode(&hwc->hwc_xhdr, buf, 0, req_space); 54155613f44SDmitri Tikhonov } 54255613f44SDmitri Tikhonov else 54355613f44SDmitri Tikhonov { 54455613f44SDmitri Tikhonov if (req_space > hwc->hwc_header_buf_nalloc) 54555613f44SDmitri Tikhonov { 54655613f44SDmitri Tikhonov if (req_space < hwc->hwc_header_buf_nalloc * 2) 54755613f44SDmitri Tikhonov nalloc = hwc->hwc_header_buf_nalloc * 2; 54855613f44SDmitri Tikhonov else 54955613f44SDmitri Tikhonov nalloc = req_space; 55055613f44SDmitri Tikhonov buf = realloc(hwc->hwc_xhdr.buf, nalloc); 55155613f44SDmitri Tikhonov if (!buf) 55255613f44SDmitri Tikhonov { 55355613f44SDmitri Tikhonov LSQ_DEBUG("cannot reallocate to %zd bytes", nalloc); 55455613f44SDmitri Tikhonov return NULL; 55555613f44SDmitri Tikhonov } 55655613f44SDmitri Tikhonov hwc->hwc_xhdr.buf = buf; 55755613f44SDmitri Tikhonov hwc->hwc_header_buf_nalloc = nalloc; 55855613f44SDmitri Tikhonov } 55955613f44SDmitri Tikhonov hwc->hwc_xhdr.val_len = req_space; 560a5fa05f9SDmitri Tikhonov } 561a5fa05f9SDmitri Tikhonov 562a5fa05f9SDmitri Tikhonov return &hwc->hwc_xhdr; 563a5fa05f9SDmitri Tikhonov} 564a5fa05f9SDmitri Tikhonov 565a5fa05f9SDmitri Tikhonov 566a5fa05f9SDmitri Tikhonovstatic int 567a5fa05f9SDmitri Tikhonovh1h_process_header (void *hset, struct lsxpack_header *xhdr) 5683b55e6aeSDmitri Tikhonov{ 5693b55e6aeSDmitri Tikhonov struct header_writer_ctx *const hwc = HWC_PTR(hset); 570a5fa05f9SDmitri Tikhonov if (xhdr) 571a5fa05f9SDmitri Tikhonov return add_header_to_uh(hwc, xhdr); 5723b55e6aeSDmitri Tikhonov else 5733b55e6aeSDmitri Tikhonov return h1h_finish_hset(hwc); 5743b55e6aeSDmitri Tikhonov} 5753b55e6aeSDmitri Tikhonov 5763b55e6aeSDmitri Tikhonov 5773b55e6aeSDmitri Tikhonovstatic void 5783b55e6aeSDmitri Tikhonovh1h_discard_header_set (void *hset) 5793b55e6aeSDmitri Tikhonov{ 5803b55e6aeSDmitri Tikhonov struct header_writer_ctx *const hwc = HWC_PTR(hset); 5813b55e6aeSDmitri Tikhonov unsigned i; 5823b55e6aeSDmitri Tikhonov 5833b55e6aeSDmitri Tikhonov for (i = 0; i < sizeof(hwc->pseh_bufs) / sizeof(hwc->pseh_bufs[0]); ++i) 5843b55e6aeSDmitri Tikhonov if (hwc->pseh_bufs[i]) 5853b55e6aeSDmitri Tikhonov free(hwc->pseh_bufs[i]); 5863b55e6aeSDmitri Tikhonov if (hwc->cookie_val) 5873b55e6aeSDmitri Tikhonov free(hwc->cookie_val); 5883b55e6aeSDmitri Tikhonov free(hwc->hwc_h1h.h1h_buf); 58955613f44SDmitri Tikhonov free(hwc->hwc_xhdr.buf); 5903b55e6aeSDmitri Tikhonov free(hwc); 5913b55e6aeSDmitri Tikhonov} 5923b55e6aeSDmitri Tikhonov 5933b55e6aeSDmitri Tikhonov 5943b55e6aeSDmitri Tikhonovstatic const struct lsquic_hset_if http1x_if = 5953b55e6aeSDmitri Tikhonov{ 5963b55e6aeSDmitri Tikhonov .hsi_create_header_set = h1h_create_header_set, 597a5fa05f9SDmitri Tikhonov .hsi_prepare_decode = h1h_prepare_decode, 5983b55e6aeSDmitri Tikhonov .hsi_process_header = h1h_process_header, 5993b55e6aeSDmitri Tikhonov .hsi_discard_header_set = h1h_discard_header_set, 6003b55e6aeSDmitri Tikhonov}; 6013b55e6aeSDmitri Tikhonov 6023b55e6aeSDmitri Tikhonovconst struct lsquic_hset_if *const lsquic_http1x_if = &http1x_if; 603