lsquic_http1x_if.c revision fb3e20e0
17d09751dSDmitri Tikhonov/* Copyright (c) 2017 - 2020 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