lsquic_http1x_if.c revision 229fce07
1/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc.  See LICENSE. */
2#include <assert.h>
3#include <ctype.h>
4#include <stddef.h>
5#include <stdlib.h>
6#include <string.h>
7
8#include "lsquic.h"
9#include "lsquic_headers.h"
10#include "lsquic_http1x_if.h"
11#include "lshpack.h"
12
13#define LSQUIC_LOGGER_MODULE LSQLM_HTTP1X
14#define LSQUIC_LOG_CONN_ID hwc->hwc_cid
15#include "lsquic_logger.h"
16
17enum pseudo_header
18{
19    PSEH_METHOD,
20    PSEH_SCHEME,
21    PSEH_AUTHORITY,
22    PSEH_PATH,
23    PSEH_STATUS,
24    N_PSEH
25};
26
27#define BIT(x) (1 << (x))
28
29#define ALL_REQUEST_PSEH (BIT(PSEH_METHOD)|BIT(PSEH_SCHEME)|BIT(PSEH_AUTHORITY)|BIT(PSEH_PATH))
30#define REQUIRED_REQUEST_PSEH (BIT(PSEH_METHOD)|BIT(PSEH_SCHEME)|BIT(PSEH_PATH))
31
32#define ALL_SERVER_PSEH BIT(PSEH_STATUS)
33#define REQUIRED_SERVER_PSEH ALL_SERVER_PSEH
34
35#define PSEH_LEN(h) (sizeof(#h) - 5)
36
37struct header_writer_ctx
38{
39    lsquic_cid_t                 hwc_cid;
40    char                        *buf;
41    char                        *cookie_val;
42    unsigned                     cookie_sz, cookie_nalloc;
43    unsigned                     max_headers_sz,
44                                 headers_sz,
45                                 w_off;
46    enum {
47        HWC_SERVER       = 1 << 0,
48        HWC_EXPECT_COLON = 1 << 1,
49        HWC_SEEN_HOST    = 1 << 2,
50        HWC_PUSH_PROMISE = 1 << 3,
51    }                            hwc_flags;
52    enum pseudo_header           pseh_mask;
53    char                        *pseh_bufs[N_PSEH];
54    struct http1x_headers        hwc_h1h;
55};
56
57
58#define HWC_PSEH_LEN(hwc, ph) ((int) strlen((hwc)->pseh_bufs[ph]))
59
60#define HWC_PSEH_VAL(hwc, ph) ((hwc)->pseh_bufs[ph])
61
62static void *
63h1h_create_header_set (void *ctx, int is_push_promise)
64{
65    const struct http1x_ctor_ctx *hcc = ctx;
66    struct header_writer_ctx *hwc;
67
68    hwc = calloc(1, sizeof(*hwc));
69    if (!hwc)
70        return NULL;
71
72    hwc->hwc_flags = HWC_EXPECT_COLON;
73    if (hcc->is_server)
74        hwc->hwc_flags |= HWC_SERVER;
75    if (is_push_promise)
76        hwc->hwc_flags |= HWC_PUSH_PROMISE;
77    hwc->max_headers_sz = hcc->max_headers_sz;
78    hwc->hwc_cid = hcc->cid;
79    return &hwc->hwc_h1h;
80}
81
82
83static int
84hwc_uh_write (struct header_writer_ctx *hwc, const void *buf, size_t sz)
85{
86    char *h1h_buf;
87
88    if (hwc->w_off + sz > hwc->headers_sz)
89    {
90        if (hwc->headers_sz * 2 >= hwc->w_off + sz)
91            hwc->headers_sz *= 2;
92        else
93            hwc->headers_sz = hwc->w_off + sz;
94        h1h_buf = realloc(hwc->hwc_h1h.h1h_buf, hwc->headers_sz);
95        if (!buf)
96            return -1;
97        hwc->hwc_h1h.h1h_buf = h1h_buf;
98    }
99    memcpy(&hwc->hwc_h1h.h1h_buf[hwc->w_off], buf, sz);
100    hwc->w_off += sz;
101    return 0;
102}
103
104
105static enum lsquic_header_status
106save_pseudo_header (struct header_writer_ctx *hwc, enum pseudo_header ph,
107                    const char *val, unsigned val_len)
108{
109    if (0 == (hwc->pseh_mask & BIT(ph)))
110    {
111        assert(!hwc->pseh_bufs[ph]);
112        hwc->pseh_bufs[ph] = malloc(val_len + 1);
113        if (!hwc->pseh_bufs[ph])
114            return LSQUIC_HDR_ERR_NOMEM;
115        hwc->pseh_mask |= BIT(ph);
116        memcpy(hwc->pseh_bufs[ph], val, val_len);
117        hwc->pseh_bufs[ph][val_len] = '\0';
118        return LSQUIC_HDR_OK;
119    }
120    else
121    {
122        LSQ_INFO("header %u is already present", ph);
123        return LSQUIC_HDR_ERR_DUPLICATE_PSDO_HDR;
124    }
125}
126
127
128static enum lsquic_header_status
129add_pseudo_header (struct header_writer_ctx *hwc, const char *name,
130                         unsigned name_len, const char *val, unsigned val_len)
131{
132    if (!(hwc->hwc_flags & HWC_EXPECT_COLON))
133    {
134        LSQ_INFO("unexpected colon");
135        return LSQUIC_HDR_ERR_MISPLACED_PSDO_HDR;
136    }
137
138    switch (name_len)
139    {
140    case 5:
141        if (0 == memcmp(name,     ":path", 5))
142            return save_pseudo_header(hwc, PSEH_PATH, val, val_len);
143        break;
144    case 7:
145        switch (name[2])
146        {
147        case 'c':
148            if (0 == memcmp(name, ":scheme", 7))
149                return save_pseudo_header(hwc, PSEH_SCHEME, val, val_len);
150            break;
151        case 'e':
152            if (0 == memcmp(name, ":method", 7))
153                return save_pseudo_header(hwc, PSEH_METHOD, val, val_len);
154            break;
155        case 't':
156            if (0 == memcmp(name, ":status", 7))
157                return save_pseudo_header(hwc, PSEH_STATUS, val, val_len);
158            break;
159        }
160        break;
161    case 10:
162        if (0 == memcmp(name,     ":authority", 10))
163            return save_pseudo_header(hwc, PSEH_AUTHORITY, val, val_len);
164        break;
165    }
166
167    LSQ_INFO("unknown pseudo-header `%.*s'", name_len, name);
168    return LSQUIC_HDR_ERR_UNKNOWN_PSDO_HDR;
169}
170
171
172#define HTTP_CODE_LEN 3
173
174static const char *
175code_str_to_reason (const char code_str[HTTP_CODE_LEN])
176{
177    /* RFC 7231, Section 6: */
178    static const char *const http_reason_phrases[] =
179    {
180    #define HTTP_REASON_CODE(code, reason) [code - 100] = reason
181        HTTP_REASON_CODE(100, "Continue"),
182        HTTP_REASON_CODE(101, "Switching Protocols"),
183        HTTP_REASON_CODE(200, "OK"),
184        HTTP_REASON_CODE(201, "Created"),
185        HTTP_REASON_CODE(202, "Accepted"),
186        HTTP_REASON_CODE(203, "Non-Authoritative Information"),
187        HTTP_REASON_CODE(204, "No Content"),
188        HTTP_REASON_CODE(205, "Reset Content"),
189        HTTP_REASON_CODE(206, "Partial Content"),
190        HTTP_REASON_CODE(300, "Multiple Choices"),
191        HTTP_REASON_CODE(301, "Moved Permanently"),
192        HTTP_REASON_CODE(302, "Found"),
193        HTTP_REASON_CODE(303, "See Other"),
194        HTTP_REASON_CODE(304, "Not Modified"),
195        HTTP_REASON_CODE(305, "Use Proxy"),
196        HTTP_REASON_CODE(307, "Temporary Redirect"),
197        HTTP_REASON_CODE(400, "Bad Request"),
198        HTTP_REASON_CODE(401, "Unauthorized"),
199        HTTP_REASON_CODE(402, "Payment Required"),
200        HTTP_REASON_CODE(403, "Forbidden"),
201        HTTP_REASON_CODE(404, "Not Found"),
202        HTTP_REASON_CODE(405, "Method Not Allowed"),
203        HTTP_REASON_CODE(406, "Not Acceptable"),
204        HTTP_REASON_CODE(407, "Proxy Authentication Required"),
205        HTTP_REASON_CODE(408, "Request Timeout"),
206        HTTP_REASON_CODE(409, "Conflict"),
207        HTTP_REASON_CODE(410, "Gone"),
208        HTTP_REASON_CODE(411, "Length Required"),
209        HTTP_REASON_CODE(412, "Precondition Failed"),
210        HTTP_REASON_CODE(413, "Payload Too Large"),
211        HTTP_REASON_CODE(414, "URI Too Long"),
212        HTTP_REASON_CODE(415, "Unsupported Media Type"),
213        HTTP_REASON_CODE(416, "Range Not Satisfiable"),
214        HTTP_REASON_CODE(417, "Expectation Failed"),
215        HTTP_REASON_CODE(426, "Upgrade Required"),
216        HTTP_REASON_CODE(500, "Internal Server Error"),
217        HTTP_REASON_CODE(501, "Not Implemented"),
218        HTTP_REASON_CODE(502, "Bad Gateway"),
219        HTTP_REASON_CODE(503, "Service Unavailable"),
220        HTTP_REASON_CODE(504, "Gateway Timeout"),
221        HTTP_REASON_CODE(505, "HTTP Version Not Supported"),
222    #undef HTTP_REASON_CODE
223    };
224
225    long code;
226    char code_buf[HTTP_CODE_LEN + 1];
227
228    memcpy(code_buf, code_str, HTTP_CODE_LEN);
229    code_buf[HTTP_CODE_LEN] = '\0';
230    code = strtol(code_buf, NULL, 10) - 100;
231    if (code > 0 && code < (long) (sizeof(http_reason_phrases) /
232                                        sizeof(http_reason_phrases[0])))
233        return http_reason_phrases[code];
234    else
235        return NULL;
236}
237
238
239static enum lsquic_header_status
240convert_response_pseudo_headers (struct header_writer_ctx *hwc)
241{
242    if ((hwc->pseh_mask & REQUIRED_SERVER_PSEH) != REQUIRED_SERVER_PSEH)
243    {
244        LSQ_INFO("not all response pseudo-headers are specified");
245        return LSQUIC_HDR_ERR_INCOMPL_RESP_PSDO_HDR;
246    }
247    if (hwc->pseh_mask & ALL_REQUEST_PSEH)
248    {
249        LSQ_INFO("response pseudo-headers contain request-only headers");
250        return LSQUIC_HDR_ERR_UNNEC_REQ_PSDO_HDR;
251    }
252
253    const char *code_str, *reason;
254    int code_len;
255
256    code_str = HWC_PSEH_VAL(hwc, PSEH_STATUS);
257    code_len = HWC_PSEH_LEN(hwc, PSEH_STATUS);
258
259#define HWC_UH_WRITE(h, buf, sz) do {                                   \
260    if (0 != hwc_uh_write(h, buf, sz))                                  \
261        return LSQUIC_HDR_ERR_NOMEM;                                    \
262} while (0)
263
264    HWC_UH_WRITE(hwc, "HTTP/1.1 ", 9);
265    HWC_UH_WRITE(hwc, code_str, code_len);
266    if (HTTP_CODE_LEN == code_len && (reason = code_str_to_reason(code_str)))
267    {
268        HWC_UH_WRITE(hwc, " ", 1);
269        HWC_UH_WRITE(hwc, reason, strlen(reason));
270        HWC_UH_WRITE(hwc, "\r\n", 2);
271    }
272    else
273        HWC_UH_WRITE(hwc, " \r\n", 3);
274    if (hwc->max_headers_sz && hwc->w_off > hwc->max_headers_sz)
275    {
276        LSQ_INFO("headers too large");
277        return LSQUIC_HDR_ERR_HEADERS_TOO_LARGE;
278    }
279    return LSQUIC_HDR_OK;
280
281#undef HWC_UH_WRITE
282}
283
284
285static enum lsquic_header_status
286convert_request_pseudo_headers (struct header_writer_ctx *hwc)
287{
288    if ((hwc->pseh_mask & REQUIRED_REQUEST_PSEH) != REQUIRED_REQUEST_PSEH)
289    {
290        LSQ_INFO("not all request pseudo-headers are specified");
291        return LSQUIC_HDR_ERR_INCOMPL_REQ_PSDO_HDR;
292    }
293    if (hwc->pseh_mask & ALL_SERVER_PSEH)
294    {
295        LSQ_INFO("request pseudo-headers contain response-only headers");
296        return LSQUIC_HDR_ERR_UNNEC_RESP_PSDO_HDR;
297    }
298
299#define HWC_UH_WRITE(h, buf, sz) do {                                   \
300    if (0 != hwc_uh_write(h, buf, sz))                                  \
301        return LSQUIC_HDR_ERR_NOMEM;                                    \
302} while (0)
303
304    HWC_UH_WRITE(hwc, HWC_PSEH_VAL(hwc, PSEH_METHOD), HWC_PSEH_LEN(hwc, PSEH_METHOD));
305    HWC_UH_WRITE(hwc, " ", 1);
306    HWC_UH_WRITE(hwc, HWC_PSEH_VAL(hwc, PSEH_PATH), HWC_PSEH_LEN(hwc, PSEH_PATH));
307    HWC_UH_WRITE(hwc, " HTTP/1.1\r\n", 11);
308
309    if (hwc->max_headers_sz && hwc->w_off > hwc->max_headers_sz)
310    {
311        LSQ_INFO("headers too large");
312        return LSQUIC_HDR_ERR_HEADERS_TOO_LARGE;
313    }
314
315    return 0;
316
317#undef HWC_UH_WRITE
318}
319
320
321static enum lsquic_header_status
322convert_pseudo_headers (struct header_writer_ctx *hwc)
323{
324    /* We are *reading* the message.  Thus, a server expects a request, and a
325     * client expects a response.  Unless we receive a push promise from the
326     * server, in which case this should also be a request.
327     */
328    if (hwc->hwc_flags & (HWC_SERVER|HWC_PUSH_PROMISE))
329        return convert_request_pseudo_headers(hwc);
330    else
331        return convert_response_pseudo_headers(hwc);
332}
333
334
335static enum lsquic_header_status
336save_cookie (struct header_writer_ctx *hwc, const char *val, unsigned val_len)
337{
338    char *cookie_val;
339
340    if (0 == hwc->cookie_sz)
341    {
342        hwc->cookie_nalloc = hwc->cookie_sz = val_len;
343        cookie_val = malloc(hwc->cookie_nalloc);
344        if (!cookie_val)
345            return LSQUIC_HDR_ERR_NOMEM;
346        hwc->cookie_val = cookie_val;
347        memcpy(hwc->cookie_val, val, val_len);
348    }
349    else
350    {
351        hwc->cookie_sz += val_len + 2 /* "; " */;
352        if (hwc->cookie_sz > hwc->cookie_nalloc)
353        {
354            hwc->cookie_nalloc = hwc->cookie_nalloc * 2 + val_len + 2;
355            cookie_val = realloc(hwc->cookie_val, hwc->cookie_nalloc);
356            if (!cookie_val)
357                return LSQUIC_HDR_ERR_NOMEM;
358            hwc->cookie_val = cookie_val;
359        }
360        memcpy(hwc->cookie_val + hwc->cookie_sz - val_len - 2, "; ", 2);
361        memcpy(hwc->cookie_val + hwc->cookie_sz - val_len, val, val_len);
362    }
363
364    return 0;
365}
366
367
368static enum lsquic_header_status
369add_real_header (struct header_writer_ctx *hwc, const char *name,
370                 unsigned name_len, const char *val, unsigned val_len)
371{
372    enum lsquic_header_status err;
373    unsigned i;
374    int n_upper;
375
376    if (hwc->hwc_flags & HWC_EXPECT_COLON)
377    {
378        if (0 != (err = convert_pseudo_headers(hwc)))
379            return err;
380        hwc->hwc_flags &= ~HWC_EXPECT_COLON;
381    }
382
383    if (4 == name_len && 0 == memcmp(name, "host", 4))
384        hwc->hwc_flags |= HWC_SEEN_HOST;
385
386    n_upper = 0;
387    for (i = 0; i < name_len; ++i)
388        n_upper += isupper(name[i]);
389    if (n_upper > 0)
390    {
391        LSQ_INFO("Header name `%.*s' contains uppercase letters",
392            name_len, name);
393        return LSQUIC_HDR_ERR_UPPERCASE_HEADER;
394    }
395
396    if (6 == name_len && memcmp(name, "cookie", 6) == 0)
397    {
398        return save_cookie(hwc, val, val_len);
399    }
400
401#define HWC_UH_WRITE(h, buf, sz) do {                                   \
402    if (0 != hwc_uh_write(h, buf, sz))                                  \
403        return LSQUIC_HDR_ERR_NOMEM;                                    \
404} while (0)
405
406    HWC_UH_WRITE(hwc, name, name_len);
407    HWC_UH_WRITE(hwc, ": ", 2);
408    HWC_UH_WRITE(hwc, val, val_len);
409    HWC_UH_WRITE(hwc, "\r\n", 2);
410
411    if (hwc->max_headers_sz && hwc->w_off > hwc->max_headers_sz)
412    {
413        LSQ_INFO("headers too large");
414        return LSQUIC_HDR_ERR_HEADERS_TOO_LARGE;
415    }
416
417    return 0;
418
419#undef HWC_UH_WRITE
420}
421
422
423static enum lsquic_header_status
424add_header_to_uh (struct header_writer_ctx *hwc, const char *name,
425                  unsigned name_len, const char *val, unsigned val_len)
426{
427    LSQ_DEBUG("Got header '%.*s': '%.*s'", name_len, name, val_len, val);
428    if (':' == name[0])
429        return add_pseudo_header(hwc, name, name_len, val, val_len);
430    else
431        return add_real_header(hwc, name, name_len, val, val_len);
432}
433
434
435static enum lsquic_header_status
436h1h_finish_hset (struct header_writer_ctx *hwc)
437{
438    enum lsquic_header_status st;
439
440    if (hwc->hwc_flags & HWC_EXPECT_COLON)
441    {
442        st = convert_pseudo_headers(hwc);
443        if (0 != st)
444            return st;
445        hwc->hwc_flags &= ~HWC_EXPECT_COLON;
446    }
447
448#define HWC_UH_WRITE(h, buf, sz) do {                                   \
449    st = hwc_uh_write(h, buf, sz);                                      \
450    if (0 != st)                                                        \
451        return st;                                                      \
452} while (0)
453
454    if ((hwc->pseh_mask & BIT(PSEH_AUTHORITY)) &&
455                                0 == (hwc->hwc_flags & HWC_SEEN_HOST))
456    {
457        LSQ_DEBUG("Setting 'Host: %.*s'", HWC_PSEH_LEN(hwc, PSEH_AUTHORITY),
458                                            HWC_PSEH_VAL(hwc, PSEH_AUTHORITY));
459        HWC_UH_WRITE(hwc, "Host: ", 6);
460        HWC_UH_WRITE(hwc, HWC_PSEH_VAL(hwc, PSEH_AUTHORITY),
461                                        HWC_PSEH_LEN(hwc, PSEH_AUTHORITY));
462        HWC_UH_WRITE(hwc, "\r\n", 2);
463    }
464
465    if (hwc->cookie_val)
466    {
467        LSQ_DEBUG("Setting 'Cookie: %.*s'", hwc->cookie_sz, hwc->cookie_val);
468        HWC_UH_WRITE(hwc, "Cookie: ", 8);
469        HWC_UH_WRITE(hwc, hwc->cookie_val, hwc->cookie_sz);
470        HWC_UH_WRITE(hwc, "\r\n", 2);
471    }
472
473    HWC_UH_WRITE(hwc, "\r\n", 2 + 1 /* NUL byte */);
474    hwc->w_off -= 1;     /* Do not count NUL byte */
475    hwc->hwc_h1h.h1h_size = hwc->w_off;
476
477    if (hwc->max_headers_sz && hwc->w_off > hwc->max_headers_sz)
478    {
479        LSQ_INFO("headers too large");
480        return LSQUIC_HDR_ERR_HEADERS_TOO_LARGE;
481    }
482
483    return LSQUIC_HDR_OK;
484}
485
486#define HWC_PTR(data_in) (struct header_writer_ctx *) \
487    ((unsigned char *) (hset) - offsetof(struct header_writer_ctx, hwc_h1h))
488
489static enum lsquic_header_status
490h1h_process_header (void *hset, unsigned name_idx,
491                    const char *name, unsigned name_len,
492                    const char *value, unsigned value_len)
493{
494    struct header_writer_ctx *const hwc = HWC_PTR(hset);
495    if (name)
496        return add_header_to_uh(hwc, name, name_len, value, value_len);
497    else
498        return h1h_finish_hset(hwc);
499}
500
501
502static void
503h1h_discard_header_set (void *hset)
504{
505    struct header_writer_ctx *const hwc = HWC_PTR(hset);
506    unsigned i;
507
508    for (i = 0; i < sizeof(hwc->pseh_bufs) / sizeof(hwc->pseh_bufs[0]); ++i)
509        if (hwc->pseh_bufs[i])
510            free(hwc->pseh_bufs[i]);
511    if (hwc->cookie_val)
512        free(hwc->cookie_val);
513    free(hwc->hwc_h1h.h1h_buf);
514    free(hwc);
515}
516
517
518static const struct lsquic_hset_if http1x_if =
519{
520    .hsi_create_header_set  = h1h_create_header_set,
521    .hsi_process_header     = h1h_process_header,
522    .hsi_discard_header_set = h1h_discard_header_set,
523};
524
525const struct lsquic_hset_if *const lsquic_http1x_if = &http1x_if;
526