LCOV - code coverage report
Current view: top level - lib - lwan-request.c (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 659 982 67.1 %
Date: 2023-04-18 16:19:03 Functions: 60 77 77.9 %

          Line data    Source code
       1             : /*
       2             :  * lwan - web server
       3             :  * Copyright (c) 2012-2014 L. A. F. Pereira <l@tia.mat.br>
       4             :  *
       5             :  * This program is free software; you can redistribute it and/or
       6             :  * modify it under the terms of the GNU General Public License
       7             :  * as published by the Free Software Foundation; either version 2
       8             :  * of the License, or any later version.
       9             :  *
      10             :  * This program is distributed in the hope that it will be useful,
      11             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             :  * GNU General Public License for more details.
      14             :  *
      15             :  * You should have received a copy of the GNU General Public License
      16             :  * along with this program; if not, write to the Free Software
      17             :  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
      18             :  */
      19             : 
      20             : #define _GNU_SOURCE
      21             : #include <arpa/inet.h>
      22             : #include <assert.h>
      23             : #include <ctype.h>
      24             : #include <errno.h>
      25             : #include <fcntl.h>
      26             : #include <inttypes.h>
      27             : #include <limits.h>
      28             : #include <stddef.h>
      29             : #include <stdio.h>
      30             : #include <stdlib.h>
      31             : #include <string.h>
      32             : #include <strings.h>
      33             : #include <sys/mman.h>
      34             : #include <sys/socket.h>
      35             : #include <sys/stat.h>
      36             : #include <sys/types.h>
      37             : #include <sys/vfs.h>
      38             : #include <unistd.h>
      39             : 
      40             : #include "lwan-private.h"
      41             : 
      42             : #include "base64.h"
      43             : #include "list.h"
      44             : #include "lwan-config.h"
      45             : #include "lwan-http-authorize.h"
      46             : #include "lwan-io-wrappers.h"
      47             : #include "sha1.h"
      48             : 
      49             : #define HEADER_VALUE_SEPARATOR_LEN (sizeof(": ") - 1)
      50             : #define HEADER_TERMINATOR_LEN (sizeof("\r\n") - 1)
      51             : #define MIN_REQUEST_SIZE (sizeof("GET / HTTP/1.1\r\n\r\n") - 1)
      52             : 
      53             : enum lwan_read_finalizer {
      54             :     FINALIZER_DONE,
      55             :     FINALIZER_TRY_AGAIN,
      56             :     FINALIZER_TIMEOUT,
      57             : };
      58             : 
      59             : struct proxy_header_v2 {
      60             :     uint8_t sig[12];
      61             :     uint8_t cmd_ver;
      62             :     uint8_t fam;
      63             :     uint16_t len;
      64             :     union {
      65             :         struct {
      66             :             in_addr_t src_addr;
      67             :             in_addr_t dst_addr;
      68             :             uint16_t src_port;
      69             :             uint16_t dst_port;
      70             :         } ip4;
      71             :         struct {
      72             :             struct in6_addr src_addr;
      73             :             struct in6_addr dst_addr;
      74             :             uint16_t src_port;
      75             :             uint16_t dst_port;
      76             :         } ip6;
      77             :     } addr;
      78             : };
      79             : 
      80             : static char *ignore_leading_whitespace(char *buffer) __attribute__((pure));
      81             : 
      82             : 
      83             : static bool
      84           2 : parse_ascii_port(char *port, unsigned short *out)
      85             : {
      86             :     unsigned long parsed;
      87             :     char *end_ptr;
      88             : 
      89           2 :     errno = 0;
      90           2 :     parsed = strtoul(port, &end_ptr, 10);
      91             : 
      92           2 :     if (UNLIKELY(errno != 0))
      93           0 :         return false;
      94             : 
      95           2 :     if (UNLIKELY(*end_ptr != '\0'))
      96           0 :         return false;
      97             : 
      98           2 :     if (UNLIKELY((unsigned long)(unsigned short)parsed != parsed))
      99           0 :         return false;
     100             : 
     101           2 :     *out = htons((unsigned short)parsed);
     102           2 :     return true;
     103             : }
     104             : 
     105             : static char *
     106         702 : strsep_char(char *strp, const char *end, char delim)
     107             : {
     108             :     char *ptr;
     109             : 
     110         702 :     if (UNLIKELY(!strp))
     111           0 :         return NULL;
     112             : 
     113         702 :     if (UNLIKELY(strp > end))
     114           0 :         return NULL;
     115             : 
     116         702 :     ptr = strchr(strp, delim);
     117         702 :     if (UNLIKELY(!ptr))
     118         325 :         return NULL;
     119             : 
     120         377 :     *ptr = '\0';
     121         377 :     return ptr + 1;
     122             : }
     123             : 
     124             : static char *
     125           1 : parse_proxy_protocol_v1(struct lwan_request *request, char *buffer)
     126             : {
     127             :     static const size_t line_size = 108;
     128             :     char *end, *protocol, *src_addr, *dst_addr, *src_port, *dst_port;
     129             :     unsigned int size;
     130           1 :     struct lwan_proxy *const proxy = request->proxy;
     131             : 
     132           1 :     end = memchr(buffer, '\r', line_size);
     133           1 :     if (UNLIKELY(!end || end[1] != '\n'))
     134           0 :         return NULL;
     135           1 :     *end = '\0';
     136           1 :     size = (unsigned int) (end + 2 - buffer);
     137             : 
     138           1 :     protocol = buffer + sizeof("PROXY ") - 1;
     139           1 :     src_addr = strsep_char(protocol, end, ' ');
     140           1 :     dst_addr = strsep_char(src_addr, end, ' ');
     141           1 :     src_port = strsep_char(dst_addr, end, ' ');
     142           1 :     dst_port = strsep_char(src_port, end, ' ');
     143             : 
     144           1 :     if (UNLIKELY(!dst_port))
     145           0 :         return NULL;
     146             : 
     147           1 :     STRING_SWITCH(protocol) {
     148           1 :     case STR4_INT('T', 'C', 'P', '4'): {
     149           1 :         struct sockaddr_in *from = &proxy->from.ipv4;
     150           1 :         struct sockaddr_in *to = &proxy->to.ipv4;
     151             : 
     152           1 :         from->sin_family = to->sin_family = AF_INET;
     153             : 
     154           1 :         if (UNLIKELY(inet_pton(AF_INET, src_addr, &from->sin_addr) <= 0))
     155           0 :             return NULL;
     156           1 :         if (UNLIKELY(inet_pton(AF_INET, dst_addr, &to->sin_addr) <= 0))
     157           0 :             return NULL;
     158           1 :         if (UNLIKELY(!parse_ascii_port(src_port, &from->sin_port)))
     159           0 :             return NULL;
     160           1 :         if (UNLIKELY(!parse_ascii_port(dst_port, &to->sin_port)))
     161           0 :             return NULL;
     162             : 
     163           1 :         break;
     164             :     }
     165           0 :     case STR4_INT('T', 'C', 'P', '6'): {
     166           0 :         struct sockaddr_in6 *from = &proxy->from.ipv6;
     167           0 :         struct sockaddr_in6 *to = &proxy->to.ipv6;
     168             : 
     169           0 :         from->sin6_family = to->sin6_family = AF_INET6;
     170             : 
     171           0 :         if (UNLIKELY(inet_pton(AF_INET6, src_addr, &from->sin6_addr) <= 0))
     172           0 :             return NULL;
     173           0 :         if (UNLIKELY(inet_pton(AF_INET6, dst_addr, &to->sin6_addr) <= 0))
     174           0 :             return NULL;
     175           0 :         if (UNLIKELY(!parse_ascii_port(src_port, &from->sin6_port)))
     176           0 :             return NULL;
     177           0 :         if (UNLIKELY(!parse_ascii_port(dst_port, &to->sin6_port)))
     178           0 :             return NULL;
     179             : 
     180           0 :         break;
     181             :     }
     182           0 :     default:
     183           0 :         return NULL;
     184             :     }
     185             : 
     186           1 :     request->flags |= REQUEST_PROXIED;
     187           1 :     return buffer + size;
     188             : }
     189             : 
     190           2 : static char *parse_proxy_protocol_v2(struct lwan_request *request, char *buffer)
     191             : {
     192           2 :     struct proxy_header_v2 *hdr = (struct proxy_header_v2 *)buffer;
     193           2 :     struct lwan_request_parser_helper *helper = request->helper;
     194           2 :     const unsigned int proto_signature_length = 16;
     195             :     unsigned int size;
     196           2 :     struct lwan_proxy *const proxy = request->proxy;
     197             : 
     198             :     enum { LOCAL = 0x20, PROXY = 0x21, TCP4 = 0x11, TCP6 = 0x21 };
     199             : 
     200           2 :     size = proto_signature_length + (unsigned int)ntohs(hdr->len);
     201           2 :     if (UNLIKELY(size > (unsigned int)sizeof(*hdr)))
     202           0 :         return NULL;
     203           2 :     if (UNLIKELY(size >= helper->buffer->len))
     204           0 :         return NULL;
     205             : 
     206           2 :     if (LIKELY(hdr->cmd_ver == PROXY)) {
     207           1 :         if (hdr->fam == TCP4) {
     208           1 :             struct sockaddr_in *from = &proxy->from.ipv4;
     209           1 :             struct sockaddr_in *to = &proxy->to.ipv4;
     210             : 
     211           1 :             to->sin_family = from->sin_family = AF_INET;
     212             : 
     213           1 :             from->sin_addr.s_addr = hdr->addr.ip4.src_addr;
     214           1 :             from->sin_port = hdr->addr.ip4.src_port;
     215             : 
     216           1 :             to->sin_addr.s_addr = hdr->addr.ip4.dst_addr;
     217           1 :             to->sin_port = hdr->addr.ip4.dst_port;
     218           0 :         } else if (hdr->fam == TCP6) {
     219           0 :             struct sockaddr_in6 *from = &proxy->from.ipv6;
     220           0 :             struct sockaddr_in6 *to = &proxy->to.ipv6;
     221             : 
     222           0 :             from->sin6_family = to->sin6_family = AF_INET6;
     223             : 
     224           0 :             from->sin6_addr = hdr->addr.ip6.src_addr;
     225           0 :             from->sin6_port = hdr->addr.ip6.src_port;
     226             : 
     227           0 :             to->sin6_addr = hdr->addr.ip6.dst_addr;
     228           0 :             to->sin6_port = hdr->addr.ip6.dst_port;
     229             :         } else {
     230           0 :             return NULL;
     231             :         }
     232           1 :     } else if (hdr->cmd_ver == LOCAL) {
     233           1 :         struct sockaddr_in *from = &proxy->from.ipv4;
     234           1 :         struct sockaddr_in *to = &proxy->to.ipv4;
     235             : 
     236           1 :         from->sin_family = to->sin_family = AF_UNSPEC;
     237             :     } else {
     238           0 :         return NULL;
     239             :     }
     240             : 
     241           2 :     request->flags |= REQUEST_PROXIED;
     242           2 :     return buffer + size;
     243             : }
     244             : 
     245             : #if !defined(LWAN_HAVE_BUILTIN_EXPECT_PROBABILITY)
     246             : #define __builtin_expect_with_probability(value1, value2, probability) \
     247             :     __builtin_expect(value1, value2)
     248             : #endif
     249             : 
     250             : static ALWAYS_INLINE char *identify_http_method(struct lwan_request *request,
     251             :                                                 char *buffer)
     252             : {
     253         594 :     const uint32_t first_four = string_as_uint32(buffer);
     254             : 
     255             : #define GENERATE_IF(upper, lower, mask, constant, probability)                 \
     256             :     if (__builtin_expect_with_probability(first_four == (constant), 1,         \
     257             :                                           probability)) {                      \
     258             :         request->flags |= (mask);                                              \
     259             :         return buffer + sizeof(#upper);                                        \
     260             :     }
     261             : 
     262         594 :     FOR_EACH_REQUEST_METHOD(GENERATE_IF)
     263             : 
     264             : #undef GENERATE_IF
     265             : 
     266           1 :     return NULL;
     267             : }
     268             : 
     269        1255 : __attribute__((nonnull(1))) static ssize_t url_decode(char *str)
     270             : {
     271             :     static const unsigned char tbl1[256] = {
     272             :         [0 ... 255] = 255, ['0'] = 0 << 4,  ['1'] = 1 << 4,  ['2'] = 2 << 4,
     273             :         ['3'] = 3 << 4,    ['4'] = 4 << 4,  ['5'] = 5 << 4,  ['6'] = 6 << 4,
     274             :         ['7'] = 7 << 4,    ['8'] = 8 << 4,  ['9'] = 9 << 4,  ['a'] = 10 << 4,
     275             :         ['b'] = 11 << 4,   ['c'] = 12 << 4, ['d'] = 13 << 4, ['e'] = 14 << 4,
     276             :         ['f'] = 15 << 4,   ['A'] = 10 << 4, ['B'] = 11 << 4, ['C'] = 12 << 4,
     277             :         ['D'] = 13 << 4,   ['E'] = 14 << 4, ['F'] = 15 << 4,
     278             :     };
     279             :     static const char tbl2[256] = {
     280             :         [0 ... 255] = -1, ['0'] = 0,  ['1'] = 1,  ['2'] = 2,  ['3'] = 3,
     281             :         ['4'] = 4,        ['5'] = 5,  ['6'] = 6,  ['7'] = 7,  ['8'] = 8,
     282             :         ['9'] = 9,        ['a'] = 10, ['b'] = 11, ['c'] = 12, ['d'] = 13,
     283             :         ['e'] = 14,       ['f'] = 15, ['A'] = 10, ['B'] = 11, ['C'] = 12,
     284             :         ['D'] = 13,       ['E'] = 14, ['F'] = 15,
     285             :     };
     286        1255 :     const char *inptr = str;
     287        1255 :     char *outptr = str;
     288             : 
     289        1256 :     for (char *p = strpbrk(inptr, "+%"); p; p = strpbrk(inptr, "+%")) {
     290           1 :         const ptrdiff_t diff = p - inptr;
     291           1 :         if (diff)
     292           1 :             outptr = mempmove(outptr, inptr, (size_t)diff);
     293             : 
     294           1 :         if (*p == '+') {
     295           0 :             *outptr++ = ' ';
     296           0 :             inptr = p + 1;
     297           0 :             continue;
     298             :         }
     299             : 
     300           1 :         const char first = (char)tbl1[(unsigned char)p[1]];
     301           1 :         const char second = tbl2[(unsigned char)p[2]];
     302           1 :         const char decoded = first | second;
     303           1 :         if (UNLIKELY(decoded <= 0)) {
     304             :             /* This shouldn't happen in normal circumstances, but if %00 is
     305             :              * found in the encoded string, bail here. */
     306           0 :             if (decoded == '\0')
     307           0 :                 return -1;
     308             : 
     309             :             /* OR-ing both lookups will yield a negative number if either
     310             :              * encoded character is not a valid hex digit; check it here so
     311             :              * that other valid-but-negative bytes (e.g.  0xff) are still
     312             :              * written to outptr. */
     313           0 :             if (first == -1) {
     314             :                 /* tbl1 is shifted so a valid nibble might be negative;
     315             :                  * check for all the bits here instead.  */
     316           0 :                 return -1;
     317             :             }
     318           0 :             if (second < 0) {
     319             :                 /* tbl2 isn't shifted so we can check for the sign bit only. */
     320           0 :                 return -1;
     321             :             }
     322             :         }
     323             : 
     324           1 :         *outptr++ = decoded;
     325           1 :         inptr = p + 3;
     326             :     }
     327             : 
     328        1255 :     if (inptr > outptr) {
     329           1 :         outptr = stpcpy(outptr, inptr);
     330           1 :         return (ssize_t)(outptr - str);
     331             :     }
     332             : 
     333        1254 :     return (ssize_t)strlen(str);
     334             : }
     335             : 
     336         694 : static int key_value_compare(const void *a, const void *b)
     337             : {
     338        1388 :     return strcmp(((const struct lwan_key_value *)a)->key,
     339         694 :                   ((const struct lwan_key_value *)b)->key);
     340             : }
     341             : 
     342             : static void
     343         323 : reset_key_value_array(void *data)
     344             : {
     345         323 :     struct lwan_key_value_array *array = data;
     346             : 
     347         323 :     lwan_key_value_array_reset(array);
     348         323 : }
     349             : 
     350         420 : static void parse_key_values(struct lwan_request *request,
     351             :                              struct lwan_value *helper_value,
     352             :                              struct lwan_key_value_array *array,
     353             :                              ssize_t (*decode_value)(char *value),
     354             :                              const char separator)
     355             : {
     356             :     struct lwan_key_value *kv;
     357         420 :     char *ptr = helper_value->value;
     358         420 :     const char *end = helper_value->value + helper_value->len;
     359             :     coro_deferred reset_defer;
     360             : 
     361         420 :     if (!helper_value->len)
     362          97 :         return;
     363             : 
     364         323 :     lwan_key_value_array_init(array);
     365         323 :     reset_defer = coro_defer(request->conn->coro, reset_key_value_array, array);
     366             : 
     367             :     do {
     368             :         char *key, *value;
     369             : 
     370         351 :         while (*ptr == ' ' || *ptr == separator)
     371           2 :             ptr++;
     372         349 :         if (UNLIKELY(*ptr == '\0'))
     373           0 :             break;
     374             : 
     375         349 :         key = ptr;
     376         349 :         ptr = strsep_char(key, end, separator);
     377             : 
     378         349 :         value = strsep_char(key, end, '=');
     379         349 :         if (UNLIKELY(!value)) {
     380           2 :             value = "";
     381         347 :         } else if (UNLIKELY(decode_value(value) < 0)) {
     382             :             /* Disallow values that failed decoding, but allow empty values */
     383           0 :             goto error;
     384             :         }
     385             : 
     386         349 :         if (UNLIKELY(decode_value(key) <= 0)) {
     387             :             /* Disallow keys that failed decoding, or empty keys */
     388           0 :             goto error;
     389             :         }
     390             : 
     391         349 :         kv = lwan_key_value_array_append(array);
     392         349 :         if (UNLIKELY(!kv))
     393           0 :             goto error;
     394             : 
     395         349 :         kv->key = key;
     396         349 :         kv->value = value;
     397         349 :     } while (ptr);
     398             : 
     399         323 :     lwan_key_value_array_sort(array, key_value_compare);
     400             : 
     401         323 :     return;
     402             : 
     403           0 : error:
     404           0 :     coro_defer_fire_and_disarm(request->conn->coro, reset_defer);
     405             : }
     406             : 
     407             : static ssize_t
     408          28 : identity_decode(char *input __attribute__((unused)))
     409             : {
     410          28 :     return 1;
     411             : }
     412             : 
     413          37 : static void parse_cookies(struct lwan_request *request)
     414             : {
     415          37 :     const char *cookies = lwan_request_get_header(request, "Cookie");
     416             : 
     417          37 :     if (!cookies)
     418          23 :         return;
     419             : 
     420          14 :     struct lwan_value header = {.value = (char *)cookies,
     421          14 :                                 .len = strlen(cookies)};
     422          14 :     parse_key_values(request, &header, &request->helper->cookies,
     423             :                      identity_decode, ';');
     424             : }
     425             : 
     426         405 : static void parse_query_string(struct lwan_request *request)
     427             : {
     428         405 :     struct lwan_request_parser_helper *helper = request->helper;
     429             : 
     430         405 :     parse_key_values(request, &helper->query_string, &helper->query_params,
     431             :                      url_decode, '&');
     432         405 : }
     433             : 
     434           1 : static void parse_form_data(struct lwan_request *request)
     435             : {
     436           1 :     struct lwan_request_parser_helper *helper = request->helper;
     437             :     static const char content_type[] = "application/x-www-form-urlencoded";
     438             : 
     439           1 :     if (helper->content_type.len < sizeof(content_type) - 1)
     440           0 :         return;
     441           1 :     if (UNLIKELY(strncmp(helper->content_type.value, content_type,
     442             :                          sizeof(content_type) - 1)))
     443           0 :         return;
     444             : 
     445           1 :     parse_key_values(request, &helper->body_data, &helper->post_params,
     446             :                      url_decode, '&');
     447             : }
     448             : 
     449         609 : static void find_query_string(struct lwan_request *request, const char *space)
     450             : {
     451         609 :     struct lwan_request_parser_helper *helper = request->helper;
     452             : 
     453         609 :     char *query_string = memchr(request->url.value, '?', request->url.len);
     454         609 :     if (query_string) {
     455         310 :         *query_string = '\0';
     456         310 :         helper->query_string.value = query_string + 1;
     457         310 :         helper->query_string.len = (size_t)(space - query_string - 1);
     458         310 :         request->url.len -= helper->query_string.len + 1;
     459         310 :         request->flags |= REQUEST_HAS_QUERY_STRING;
     460             :     }
     461         609 : }
     462             : 
     463             : static char *
     464         593 : identify_http_path(struct lwan_request *request, char *buffer)
     465             : {
     466         593 :     struct lwan_request_parser_helper *helper = request->helper;
     467             :     static const size_t minimal_request_line_len = sizeof("/ HTTP/1.0") - 1;
     468             :     char *space, *end_of_line;
     469             :     ptrdiff_t end_len;
     470             : 
     471         593 :     if (UNLIKELY(*buffer != '/'))
     472           1 :         return NULL;
     473             : 
     474         592 :     end_len = buffer - helper->buffer->value;
     475         592 :     if (UNLIKELY((size_t)end_len >= helper->buffer->len))
     476           0 :         return NULL;
     477             : 
     478         592 :     end_of_line = memchr(buffer, '\r', helper->buffer->len - (size_t)end_len);
     479         592 :     if (UNLIKELY(!end_of_line))
     480           0 :         return NULL;
     481         592 :     if (UNLIKELY((size_t)(end_of_line - buffer) < minimal_request_line_len))
     482           0 :         return NULL;
     483         592 :     *end_of_line = '\0';
     484             : 
     485         592 :     space = end_of_line - sizeof("HTTP/X.X");
     486             : 
     487         592 :     request->url.value = buffer;
     488         592 :     request->url.len = (size_t)(space - buffer);
     489         592 :     find_query_string(request, space);
     490         592 :     request->original_url = request->url;
     491             : 
     492         592 :     *space++ = '\0';
     493             : 
     494         592 :     STRING_SWITCH_LARGE(space) {
     495           2 :     case STR8_INT('H','T','T','P','/','1','.','0'):
     496           2 :         request->flags |= REQUEST_IS_HTTP_1_0;
     497           2 :         break;
     498         587 :     case STR8_INT('H','T','T','P','/','1','.','1'):
     499         587 :         break;
     500           3 :     default:
     501           3 :         return NULL;
     502             :     }
     503             : 
     504         589 :     return end_of_line + 1;
     505             : }
     506             : 
     507        1504 : __attribute__((noinline)) static void set_header_value(
     508             :     struct lwan_value *header, char *end, char *p, size_t header_len)
     509             : {
     510        1504 :     p += header_len;
     511             : 
     512        1504 :     if (LIKELY(string_as_uint16(p) == STR2_INT(':', ' '))) {
     513        1504 :         *end = '\0';
     514        1504 :         char *value = p + sizeof(": ") - 1;
     515             : 
     516        1504 :         header->value = value;
     517        1504 :         header->len = (size_t)(end - value);
     518             :     }
     519        1504 : }
     520             : 
     521             : #define HEADER_LENGTH(hdr)                                                     \
     522             :     ({                                                                         \
     523             :         if (UNLIKELY(end - sizeof(hdr) + 1 < p))                               \
     524             :             continue;                                                          \
     525             :         sizeof(hdr) - 1;                                                       \
     526             :     })
     527             : 
     528             : #define SET_HEADER_VALUE(dest, hdr)                                            \
     529             :     do {                                                                       \
     530             :         const size_t header_len = HEADER_LENGTH(hdr);                          \
     531             :         set_header_value(&(helper->dest), end, p, header_len);                 \
     532             :     } while (0)
     533             : 
     534             : static ALWAYS_INLINE ssize_t find_headers(char **header_start,
     535             :                                           struct lwan_value *request_buffer,
     536             :                                           char **next_request)
     537             : {
     538         589 :     char *buffer = request_buffer->value;
     539         589 :     char *buffer_end = buffer + request_buffer->len;
     540         589 :     ssize_t n_headers = 0;
     541             :     char *next_header;
     542             : 
     543         589 :     for (char *next_chr = buffer + 1;;) {
     544        2416 :         next_header = memchr(next_chr, '\r', (size_t)(buffer_end - next_chr));
     545             : 
     546        3005 :         if (UNLIKELY(!next_header))
     547           1 :             return -1;
     548             : 
     549        3004 :         if (next_chr == next_header) {
     550         587 :             if (buffer_end - next_chr >= (ptrdiff_t)HEADER_TERMINATOR_LEN) {
     551         587 :                 STRING_SWITCH_SMALL (next_header) {
     552         587 :                 case STR2_INT('\r', '\n'):
     553         587 :                     *next_request = next_header + HEADER_TERMINATOR_LEN;
     554             :                 }
     555             :             }
     556         587 :             goto out;
     557             :         }
     558             : 
     559             :         /* Is there at least a space for a minimal (H)eader and a (V)alue? */
     560        2417 :         if (LIKELY(next_header - next_chr >= (ptrdiff_t)(sizeof("H: V") - 1))) {
     561        2416 :             header_start[n_headers++] = next_chr;
     562             : 
     563        2416 :             if (UNLIKELY(n_headers >= N_HEADER_START - 1))
     564           0 :                 return -1;
     565             :         } else {
     566             :             /* Better to abort early if there's no space. */
     567           1 :             return -1;
     568             :         }
     569             : 
     570        2416 :         next_chr = next_header + HEADER_TERMINATOR_LEN;
     571        2416 :         if (UNLIKELY(next_chr >= buffer_end))
     572           0 :             return -1;
     573             :     }
     574             : 
     575         587 : out:
     576         587 :     header_start[n_headers] = next_header;
     577         587 :     return n_headers;
     578             : }
     579             : 
     580         589 : static bool parse_headers(struct lwan_request_parser_helper *helper,
     581             :                           char *buffer)
     582             : {
     583         589 :     char **header_start = helper->header_start;
     584         589 :     ssize_t n_headers = 0;
     585             : 
     586             :     /* FIXME: is there a better way to do this? */
     587         589 :     struct lwan_value header_start_buffer = {
     588             :         .value = buffer,
     589         589 :         .len = helper->buffer->len - (size_t)(buffer - helper->buffer->value)
     590             :     };
     591         589 :     n_headers = find_headers(header_start, &header_start_buffer,
     592             :                              &helper->next_request);
     593         589 :     if (UNLIKELY(n_headers < 0))
     594           2 :         return false;
     595             : 
     596        3002 :     for (ssize_t i = 0; i < n_headers; i++) {
     597        2415 :         char *p = header_start[i];
     598        2415 :         char *end = header_start[i + 1] - HEADER_TERMINATOR_LEN;
     599             : 
     600        2415 :         STRING_SWITCH_L (p) {
     601         890 :         case STR4_INT_L('A', 'c', 'c', 'e'):
     602         890 :             p += HEADER_LENGTH("Accept");
     603             : 
     604         890 :             STRING_SWITCH_L (p) {
     605         317 :             case STR4_INT_L('-', 'E', 'n', 'c'):
     606         317 :                 SET_HEADER_VALUE(accept_encoding, "-Encoding");
     607         317 :                 break;
     608             :             }
     609         890 :             break;
     610         584 :         case STR4_INT_L('C', 'o', 'n', 'n'):
     611         584 :             SET_HEADER_VALUE(connection, "Connection");
     612         584 :             break;
     613          14 :         case STR4_INT_L('C', 'o', 'n', 't'):
     614          14 :             p += HEADER_LENGTH("Content");
     615             : 
     616          14 :             STRING_SWITCH_L (p) {
     617           7 :             case STR4_INT_L('-', 'T', 'y', 'p'):
     618           7 :                 SET_HEADER_VALUE(content_type, "-Type");
     619           7 :                 break;
     620           7 :             case STR4_INT_L('-', 'L', 'e', 'n'):
     621           7 :                 SET_HEADER_VALUE(content_length, "-Length");
     622           7 :                 break;
     623             :             }
     624          14 :             break;
     625           0 :         case STR4_INT_L('I', 'f', '-', 'M'):
     626           0 :             SET_HEADER_VALUE(if_modified_since.raw, "If-Modified-Since");
     627           0 :             break;
     628         583 :         case STR4_INT_L('H', 'o', 's', 't'):
     629         583 :             SET_HEADER_VALUE(host, "Host");
     630         583 :             break;
     631           6 :         case STR4_INT_L('R', 'a', 'n', 'g'):
     632           6 :             SET_HEADER_VALUE(range.raw, "Range");
     633           6 :             break;
     634             :         }
     635             :     }
     636             : 
     637         587 :     helper->n_header_start = (size_t)n_headers;
     638         587 :     return true;
     639             : }
     640             : #undef HEADER_LENGTH
     641             : #undef SET_HEADER_VALUE
     642             : 
     643           0 : ssize_t lwan_find_headers(char **header_start, struct lwan_value *buffer,
     644             :                           char **next_request)
     645             : {
     646           0 :     return find_headers(header_start, buffer, next_request);
     647             : }
     648             : 
     649          46 : static void parse_if_modified_since(struct lwan_request_parser_helper *helper)
     650             : {
     651             :     static const size_t header_len =
     652             :         sizeof("Wed, 17 Apr 2019 13:59:27 GMT") - 1;
     653             :     time_t parsed;
     654             : 
     655          46 :     if (UNLIKELY(helper->if_modified_since.raw.len != header_len))
     656          46 :         return;
     657             : 
     658           0 :     if (UNLIKELY(lwan_parse_rfc_time(helper->if_modified_since.raw.value,
     659             :                                      &parsed) < 0))
     660           0 :         return;
     661             : 
     662           0 :     helper->if_modified_since.parsed = parsed;
     663             : }
     664             : 
     665             : static bool
     666          10 : parse_off_without_sign(const char *ptr, char **end, off_t *off)
     667             : {
     668             :     unsigned long long val;
     669             : 
     670             :     static_assert(sizeof(val) >= sizeof(off_t),
     671             :                   "off_t fits in a long long");
     672             : 
     673          10 :     errno = 0;
     674             : 
     675          10 :     val = strtoull(ptr, end, 10);
     676          10 :     if (UNLIKELY(val == 0 && *end == ptr))
     677           0 :         return false;
     678          10 :     if (UNLIKELY(errno != 0))
     679           0 :         return false;
     680          10 :     if (UNLIKELY(val > OFF_MAX))
     681           0 :         return false;
     682             : 
     683          10 :     *off = (off_t)val;
     684          10 :     return true;
     685             : }
     686             : 
     687             : static void
     688          15 : parse_range(struct lwan_request_parser_helper *helper)
     689             : {
     690          15 :     if (UNLIKELY(helper->range.raw.len <= (sizeof("bytes=") - 1)))
     691           9 :         return;
     692             : 
     693           6 :     char *range = helper->range.raw.value;
     694           6 :     if (UNLIKELY(strncmp(range, "bytes=", sizeof("bytes=") - 1)))
     695           0 :         return;
     696             : 
     697           6 :     range += sizeof("bytes=") - 1;
     698             : 
     699             :     off_t from, to;
     700             :     char *end;
     701             : 
     702           6 :     if (*range == '-') {
     703           1 :         from = 0;
     704             : 
     705           1 :         if (!parse_off_without_sign(range + 1, &end, &to))
     706           0 :             goto invalid_range;
     707           1 :         if (*end != '\0')
     708           0 :             goto invalid_range;
     709           5 :     } else if (lwan_char_isdigit(*range)) {
     710           5 :         if (!parse_off_without_sign(range, &end, &from))
     711           0 :             goto invalid_range;
     712           5 :         if (*end != '-')
     713           0 :             goto invalid_range;
     714             : 
     715           5 :         range = end + 1;
     716           5 :         if (*range == '\0') {
     717           1 :             to = -1;
     718             :         } else {
     719           4 :             if (!parse_off_without_sign(range, &end, &to))
     720           0 :                 goto invalid_range;
     721           4 :             if (*end != '\0')
     722           0 :                 goto invalid_range;
     723             :         }
     724             :     } else {
     725           0 : invalid_range:
     726           0 :         to = from = -1;
     727             :     }
     728             : 
     729           6 :     helper->range.from = from;
     730           6 :     helper->range.to = to;
     731             : }
     732             : 
     733             : static void
     734          32 : parse_accept_encoding(struct lwan_request *request)
     735             : {
     736          32 :     struct lwan_request_parser_helper *helper = request->helper;
     737             : 
     738          32 :     if (!helper->accept_encoding.len)
     739           2 :         return;
     740             : 
     741          57 :     for (const char *p = helper->accept_encoding.value; *p; p++) {
     742          57 :         STRING_SWITCH(p) {
     743          27 :         case STR4_INT('d','e','f','l'):
     744             :         case STR4_INT(' ','d','e','f'):
     745          27 :             request->flags |= REQUEST_ACCEPT_DEFLATE;
     746          27 :             break;
     747          23 :         case STR4_INT('g','z','i','p'):
     748             :         case STR4_INT(' ','g','z','i'):
     749          23 :             request->flags |= REQUEST_ACCEPT_GZIP;
     750          23 :             break;
     751             : #if defined(LWAN_HAVE_ZSTD)
     752           0 :         case STR4_INT('z','s','t','d'):
     753             :         case STR4_INT(' ','z','s','t'):
     754           0 :             request->flags |= REQUEST_ACCEPT_ZSTD;
     755           0 :             break;
     756             : #endif
     757             : #if defined(LWAN_HAVE_BROTLI)
     758           7 :         default:
     759           8 :             while (lwan_char_isspace(*p))
     760           1 :                 p++;
     761             : 
     762           7 :             STRING_SWITCH_SMALL(p) {
     763           0 :             case STR2_INT('b', 'r'):
     764           0 :                 request->flags |= REQUEST_ACCEPT_BROTLI;
     765           0 :                 break;
     766             :             }
     767             : #endif
     768             :         }
     769             : 
     770          57 :         if (!(p = strchr(p, ',')))
     771          30 :             break;
     772             :     }
     773             : }
     774             : 
     775             : static ALWAYS_INLINE char *
     776             : ignore_leading_whitespace(char *buffer)
     777             : {
     778        4660 :     while (lwan_char_isspace(*buffer))
     779        4065 :         buffer++;
     780         595 :     return buffer;
     781             : }
     782             : 
     783             : static ALWAYS_INLINE void parse_connection_header(struct lwan_request *request)
     784             : {
     785         587 :     struct lwan_request_parser_helper *helper = request->helper;
     786         587 :     bool has_keep_alive = false;
     787         587 :     bool has_close = false;
     788             : 
     789         587 :     if (!helper->connection.len)
     790           3 :         goto out;
     791             : 
     792         584 :     for (const char *p = helper->connection.value; *p; p++) {
     793         584 :         STRING_SWITCH_L(p) {
     794         583 :         case STR4_INT_L('k','e','e','p'):
     795             :         case STR4_INT_L(' ', 'k','e','e'):
     796         583 :             has_keep_alive = true;
     797         583 :             break;
     798           0 :         case STR4_INT_L('c','l','o','s'):
     799             :         case STR4_INT_L(' ', 'c','l','o'):
     800           0 :             has_close = true;
     801           0 :             break;
     802           1 :         case STR4_INT_L('u','p','g','r'):
     803             :         case STR4_INT_L(' ', 'u','p','g'):
     804           1 :             request->conn->flags |= CONN_IS_UPGRADE;
     805           1 :             break;
     806             :         }
     807             : 
     808         584 :         if (!(p = strchr(p, ',')))
     809         584 :             break;
     810             :     }
     811             : 
     812           0 : out:
     813         587 :     if (LIKELY(!(request->flags & REQUEST_IS_HTTP_1_0)))
     814         586 :         has_keep_alive = !has_close;
     815             : 
     816         587 :     if (has_keep_alive) {
     817         586 :         request->conn->flags |= CONN_IS_KEEP_ALIVE;
     818             :     } else {
     819           1 :         request->conn->flags &=
     820             :             ~(CONN_IS_KEEP_ALIVE | CONN_SENT_CONNECTION_HEADER);
     821             :     }
     822         587 : }
     823             : 
     824             : #if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
     825             : static void save_to_corpus_for_fuzzing(struct lwan_value buffer)
     826             : {
     827             :     struct lwan_value buffer_copy;
     828             :     char corpus_name[PATH_MAX];
     829             :     const char *crlfcrlf;
     830             :     int fd;
     831             : 
     832             :     if (!(crlfcrlf = memmem(buffer.value, buffer.len, "\r\n\r\n", 4)))
     833             :         return;
     834             :     buffer.len = (size_t)(crlfcrlf - buffer.value + 4);
     835             : 
     836             : try_another_file_name:
     837             :     buffer_copy = buffer;
     838             : 
     839             :     snprintf(corpus_name, sizeof(corpus_name), "corpus-request-%d", rand());
     840             : 
     841             :     fd = open(corpus_name, O_WRONLY | O_CLOEXEC | O_CREAT | O_EXCL, 0644);
     842             :     if (fd < 0)
     843             :         goto try_another_file_name;
     844             : 
     845             :     while (buffer_copy.len) {
     846             :         ssize_t r = write(fd, buffer_copy.value, buffer_copy.len);
     847             : 
     848             :         if (r < 0) {
     849             :             if (errno == EAGAIN || errno == EINTR)
     850             :                 continue;
     851             : 
     852             :             close(fd);
     853             :             unlink(corpus_name);
     854             :             goto try_another_file_name;
     855             :         }
     856             : 
     857             :         buffer_copy.value += r;
     858             :         buffer_copy.len -= r;
     859             :     }
     860             : 
     861             :     close(fd);
     862             :     lwan_status_debug("Request saved to %s", corpus_name);
     863             : }
     864             : #endif
     865             : 
     866             : static enum lwan_http_status
     867         602 : client_read(struct lwan_request *request,
     868             :             struct lwan_value *buffer,
     869             :             const size_t want_to_read,
     870             :             enum lwan_read_finalizer (*finalizer)(const struct lwan_value *buffer,
     871             :                                                   size_t want_to_read,
     872             :                                                   const struct lwan_request *request,
     873             :                                                   int n_packets))
     874             : {
     875         602 :     struct lwan_request_parser_helper *helper = request->helper;
     876         602 :     int n_packets = 0;
     877             : 
     878         602 :     if (helper->next_request) {
     879         268 :         const size_t next_request_len = (size_t)(helper->next_request - buffer->value);
     880             :         size_t new_len;
     881             : 
     882         268 :         if (__builtin_sub_overflow(buffer->len, next_request_len, &new_len)) {
     883           0 :             helper->next_request = NULL;
     884         268 :         } else if (new_len) {
     885             :             /* FIXME: This memmove() could be eventually removed if a better
     886             :              * stucture (maybe a ringbuffer, reading with readv(), and each
     887             :              * pointer is coro_strdup() if they wrap around?) were used for
     888             :              * the request buffer.  */
     889         255 :             buffer->len = new_len;
     890         255 :             memmove(buffer->value, helper->next_request, new_len);
     891         255 :             goto try_to_finalize;
     892             :         }
     893             :     }
     894             : 
     895         365 :     for (buffer->len = 0;; n_packets++) {
     896         365 :         size_t to_read = (size_t)(want_to_read - buffer->len);
     897             : 
     898         365 :         if (UNLIKELY(to_read == 0))
     899           5 :             return HTTP_TOO_LARGE;
     900             : 
     901         360 :         ssize_t n = recv(request->fd, buffer->value + buffer->len, to_read, 0);
     902         360 :         if (UNLIKELY(n <= 0)) {
     903           0 :             if (n < 0) {
     904           0 :                 switch (errno) {
     905             :                 case EINTR:
     906             :                 case EAGAIN:
     907          18 : yield_and_read_again:
     908          18 :                     coro_yield(request->conn->coro, CONN_CORO_WANT_READ);
     909          18 :                     continue;
     910             :                 }
     911             : 
     912             :                 /* Unexpected error before reading anything */
     913           0 :                 if (UNLIKELY(!buffer->len))
     914           0 :                     return HTTP_BAD_REQUEST;
     915             :             }
     916             : 
     917             :             /* Client shut down orderly (n = 0), or unrecoverable error (n < 0);
     918             :              * shut down coro. */
     919           0 :             break;
     920             :         }
     921             : 
     922         360 :         buffer->len += (size_t)n;
     923             : 
     924         615 : try_to_finalize:
     925         615 :         switch (finalizer(buffer, want_to_read, request, n_packets)) {
     926         597 :         case FINALIZER_DONE:
     927         597 :             buffer->value[buffer->len] = '\0';
     928             : #if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
     929             :             save_to_corpus_for_fuzzing(*buffer);
     930             : #endif
     931         597 :             return HTTP_OK;
     932             : 
     933          18 :         case FINALIZER_TRY_AGAIN:
     934          18 :             goto yield_and_read_again;
     935             : 
     936           0 :         case FINALIZER_TIMEOUT:
     937           0 :             return HTTP_TIMEOUT;
     938             :         }
     939             :     }
     940             : 
     941           0 :     coro_yield(request->conn->coro, CONN_CORO_ABORT);
     942           0 :     __builtin_unreachable();
     943             :     return HTTP_INTERNAL_ERROR;
     944             : }
     945             : 
     946             : static enum lwan_read_finalizer
     947         611 : read_request_finalizer_from_helper(const struct lwan_value *buffer,
     948             :                                    struct lwan_request_parser_helper *helper,
     949             :                                    int n_packets,
     950             :                                    bool allow_proxy_reqs)
     951             : {
     952             :     static const size_t min_proxied_request_size =
     953             :         MIN_REQUEST_SIZE + sizeof(struct proxy_header_v2);
     954             : 
     955         611 :     if (LIKELY(buffer->len >= MIN_REQUEST_SIZE)) {
     956        1222 :         STRING_SWITCH (buffer->value + buffer->len - 4) {
     957         332 :         case STR4_INT('\r', '\n', '\r', '\n'):
     958         332 :             return FINALIZER_DONE;
     959             :         }
     960             :     }
     961             : 
     962         279 :     char *crlfcrlf = memmem(buffer->value, buffer->len, "\r\n\r\n", 4);
     963         279 :     if (LIKELY(crlfcrlf)) {
     964         263 :         if (LIKELY(helper->next_request)) {
     965         252 :             helper->next_request = NULL;
     966         252 :             return FINALIZER_DONE;
     967             :         }
     968             : 
     969          11 :         const size_t crlfcrlf_to_base = (size_t)(crlfcrlf - buffer->value);
     970          11 :         if (crlfcrlf_to_base >= MIN_REQUEST_SIZE - 4)
     971          10 :             return FINALIZER_DONE;
     972             : 
     973           1 :         if (buffer->len > min_proxied_request_size && allow_proxy_reqs) {
     974             :             /* FIXME: Checking for PROXYv2 protocol header here is a layering
     975             :              * violation. */
     976           2 :             STRING_SWITCH_LARGE (crlfcrlf + 4) {
     977           1 :             case STR8_INT(0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, 0x54, 0x0a):
     978           1 :                 return FINALIZER_DONE;
     979             :             }
     980             :         }
     981             :     }
     982             : 
     983             :     /* Yield a timeout error to avoid clients being intentionally slow and
     984             :      * hogging the server.  (Clients can't only connect and do nothing, they
     985             :      * need to send data, otherwise the timeout queue timer will kick in and
     986             :      * close the connection.  Limit the number of packets to avoid them sending
     987             :      * just a byte at a time.) See lwan_calculate_n_packets() to see how this is
     988             :      * calculated. */
     989          16 :     if (UNLIKELY(n_packets > helper->error_when_n_packets))
     990           0 :         return FINALIZER_TIMEOUT;
     991             : 
     992          16 :     return FINALIZER_TRY_AGAIN;
     993             : }
     994             : 
     995             : static inline enum lwan_read_finalizer
     996         611 : read_request_finalizer(const struct lwan_value *buffer,
     997             :                        size_t want_to_read __attribute__((unused)),
     998             :                        const struct lwan_request *request,
     999             :                        int n_packets)
    1000             : {
    1001        1222 :     return read_request_finalizer_from_helper(
    1002         611 :         buffer, request->helper, n_packets,
    1003         611 :         request->flags & REQUEST_ALLOW_PROXY_REQS);
    1004             : }
    1005             : 
    1006             : static ALWAYS_INLINE enum lwan_http_status
    1007             : read_request(struct lwan_request *request)
    1008             : {
    1009         600 :     return client_read(request, request->helper->buffer,
    1010             :                        DEFAULT_BUFFER_SIZE - 1 /* -1 for NUL byte */,
    1011             :                        read_request_finalizer);
    1012             : }
    1013             : 
    1014             : static enum lwan_read_finalizer
    1015           4 : body_data_finalizer(const struct lwan_value *buffer,
    1016             :                     size_t want_to_read,
    1017             :                     const struct lwan_request *request,
    1018             :                     int n_packets)
    1019             : {
    1020           4 :     const struct lwan_request_parser_helper *helper = request->helper;
    1021             : 
    1022           4 :     if (want_to_read == buffer->len)
    1023           2 :         return FINALIZER_DONE;
    1024             : 
    1025             :     /* For POST requests, the body can be larger, and due to small MTUs on
    1026             :      * most ethernet connections, responding with a timeout solely based on
    1027             :      * number of packets doesn't work.  Use keepalive timeout instead.  */
    1028           2 :     if (UNLIKELY(time(NULL) > helper->error_when_time))
    1029           0 :         return FINALIZER_TIMEOUT;
    1030             : 
    1031             :     /* In addition to time, also estimate the number of packets based on an
    1032             :      * usual MTU value and the request body size.  */
    1033           2 :     if (UNLIKELY(n_packets > helper->error_when_n_packets))
    1034           0 :         return FINALIZER_TIMEOUT;
    1035             : 
    1036           2 :     return FINALIZER_TRY_AGAIN;
    1037             : }
    1038             : 
    1039         368 : static const char *is_dir(const char *v)
    1040             : {
    1041             :     struct stat st;
    1042             : 
    1043         368 :     if (!v)
    1044         276 :         return NULL;
    1045             : 
    1046          92 :     if (*v != '/')
    1047           0 :         return NULL;
    1048             : 
    1049          92 :     if (stat(v, &st) < 0)
    1050           0 :         return NULL;
    1051             : 
    1052          92 :     if (!S_ISDIR(st.st_mode))
    1053           0 :         return NULL;
    1054             : 
    1055          92 :     if (!(st.st_mode & S_ISVTX)) {
    1056           0 :         lwan_status_warning(
    1057             :             "Using %s as temporary directory, but it doesn't have "
    1058             :             "the sticky bit set.",
    1059             :             v);
    1060             :     }
    1061             : 
    1062          92 :     return v;
    1063             : }
    1064             : 
    1065         368 : static const char *is_dir_good_for_tmp(const char *v)
    1066             : {
    1067             :     struct statfs sb;
    1068             : 
    1069         368 :     v = is_dir(v);
    1070         368 :     if (!v)
    1071         276 :         return NULL;
    1072             : 
    1073          92 :     if (!statfs(v, &sb) && sb.f_type == TMPFS_MAGIC) {
    1074           0 :         lwan_status_warning("%s is a tmpfs filesystem, "
    1075             :                             "not considering it", v);
    1076           0 :         return NULL;
    1077             :     }
    1078             : 
    1079          92 :     return v;
    1080             : }
    1081             : 
    1082             : static const char *temp_dir;
    1083             : static const size_t body_buffer_temp_file_thresh = 1<<20;
    1084             : 
    1085             : static const char *
    1086          92 : get_temp_dir(void)
    1087             : {
    1088             :     const char *tmpdir;
    1089             : 
    1090          92 :     tmpdir = is_dir_good_for_tmp(secure_getenv("TMPDIR"));
    1091          92 :     if (tmpdir)
    1092           0 :         return tmpdir;
    1093             : 
    1094          92 :     tmpdir = is_dir_good_for_tmp(secure_getenv("TMP"));
    1095          92 :     if (tmpdir)
    1096           0 :         return tmpdir;
    1097             : 
    1098          92 :     tmpdir = is_dir_good_for_tmp(secure_getenv("TEMP"));
    1099          92 :     if (tmpdir)
    1100           0 :         return tmpdir;
    1101             : 
    1102          92 :     tmpdir = is_dir_good_for_tmp("/var/tmp");
    1103          92 :     if (tmpdir)
    1104          92 :         return tmpdir;
    1105             : 
    1106           0 :     tmpdir = is_dir_good_for_tmp(P_tmpdir);
    1107           0 :     if (tmpdir)
    1108           0 :         return tmpdir;
    1109             : 
    1110           0 :     lwan_status_warning("Temporary directory could not be determined. POST "
    1111             :                         "or PUT requests over %zu bytes bytes will fail.",
    1112             :                         body_buffer_temp_file_thresh);
    1113           0 :     return NULL;
    1114             : }
    1115             : 
    1116          92 : __attribute__((constructor)) static void initialize_temp_dir(void)
    1117             : {
    1118          92 :     temp_dir = get_temp_dir();
    1119          92 : }
    1120             : 
    1121           0 : static int create_temp_file(void)
    1122             : {
    1123             :     char template[PATH_MAX];
    1124             :     mode_t prev_mask;
    1125             :     int ret;
    1126             : 
    1127           0 :     if (UNLIKELY(!temp_dir))
    1128           0 :         return -ENOENT;
    1129             : 
    1130             : #if defined(O_TMPFILE)
    1131           0 :     int fd = open(temp_dir,
    1132             :                   O_TMPFILE | O_CREAT | O_RDWR | O_EXCL | O_CLOEXEC |
    1133             :                       O_NOFOLLOW | O_NOATIME,
    1134             :                   S_IRUSR | S_IWUSR);
    1135           0 :     if (LIKELY(fd >= 0))
    1136           0 :         return fd;
    1137             : #endif
    1138             : 
    1139           0 :     ret = snprintf(template, sizeof(template), "%s/lwanXXXXXX", temp_dir);
    1140           0 :     if (UNLIKELY(ret < 0 || ret >= (int)sizeof(template)))
    1141           0 :         return -EOVERFLOW;
    1142             : 
    1143           0 :     prev_mask = umask_for_tmpfile(S_IRUSR | S_IWUSR);
    1144           0 :     ret = mkostemp(template, O_CLOEXEC);
    1145           0 :     umask_for_tmpfile(prev_mask);
    1146             : 
    1147           0 :     if (LIKELY(ret >= 0))
    1148           0 :         unlink(template);
    1149             : 
    1150           0 :     return ret;
    1151             : }
    1152             : 
    1153             : struct file_backed_buffer {
    1154             :     void *ptr;
    1155             :     size_t size;
    1156             : };
    1157             : 
    1158             : static void
    1159           0 : free_body_buffer(void *data)
    1160             : {
    1161           0 :     struct file_backed_buffer *buf = data;
    1162             : 
    1163           0 :     munmap(buf->ptr, buf->size);
    1164           0 :     free(buf);
    1165           0 : }
    1166             : 
    1167             : static void*
    1168           2 : alloc_body_buffer(struct coro *coro, size_t size, bool allow_file)
    1169             : {
    1170             :     struct file_backed_buffer *buf;
    1171           2 :     void *ptr = (void *)MAP_FAILED;
    1172             :     int fd;
    1173             : 
    1174           2 :     if (LIKELY(size < body_buffer_temp_file_thresh)) {
    1175           2 :         ptr = coro_malloc(coro, size);
    1176             : 
    1177           2 :         if (LIKELY(ptr))
    1178           2 :             return ptr;
    1179             :     }
    1180             : 
    1181           0 :     if (UNLIKELY(!allow_file))
    1182           0 :         return NULL;
    1183             : 
    1184           0 :     fd = create_temp_file();
    1185           0 :     if (UNLIKELY(fd < 0))
    1186           0 :         return NULL;
    1187             : 
    1188           0 :     if (UNLIKELY(ftruncate(fd, (off_t)size) < 0)) {
    1189           0 :         close(fd);
    1190           0 :         return NULL;
    1191             :     }
    1192             : 
    1193             :     if (MAP_HUGETLB) {
    1194           0 :         ptr = mmap(NULL, size, PROT_READ | PROT_WRITE,
    1195             :                    MAP_SHARED | MAP_HUGETLB, fd, 0);
    1196             :     }
    1197           0 :     if (UNLIKELY(ptr == MAP_FAILED))
    1198           0 :         ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    1199           0 :     close(fd);
    1200           0 :     if (UNLIKELY(ptr == MAP_FAILED))
    1201           0 :         return NULL;
    1202             : 
    1203           0 :     buf = coro_malloc_full(coro, sizeof(*buf), free_body_buffer);
    1204           0 :     if (UNLIKELY(!buf)) {
    1205           0 :         munmap(ptr, size);
    1206           0 :         return NULL;
    1207             :     }
    1208             : 
    1209           0 :     buf->ptr = ptr;
    1210           0 :     buf->size = size;
    1211           0 :     return ptr;
    1212             : }
    1213             : 
    1214             : static enum lwan_http_status
    1215           7 : get_remaining_body_data_length(struct lwan_request *request,
    1216             :                                const size_t max_size,
    1217             :                                size_t *total,
    1218             :                                size_t *have)
    1219             : {
    1220           7 :     struct lwan_request_parser_helper *helper = request->helper;
    1221             :     long long parsed_size;
    1222             : 
    1223           7 :     if (UNLIKELY(!helper->content_length.value))
    1224           0 :         return HTTP_BAD_REQUEST;
    1225             : 
    1226           7 :     parsed_size = parse_long_long(helper->content_length.value, -1);
    1227           7 :     if (UNLIKELY(parsed_size < 0))
    1228           0 :         return HTTP_BAD_REQUEST;
    1229           7 :     if (UNLIKELY((size_t)parsed_size >= max_size))
    1230           0 :         return HTTP_TOO_LARGE;
    1231           7 :     if (UNLIKELY(!parsed_size))
    1232           0 :         return HTTP_OK;
    1233             : 
    1234           7 :     *total = (size_t)parsed_size;
    1235             : 
    1236           7 :     if (!helper->next_request) {
    1237           0 :         *have = 0;
    1238           0 :         return HTTP_PARTIAL_CONTENT;
    1239             :     }
    1240             : 
    1241           7 :     char *buffer_end = helper->buffer->value + helper->buffer->len;
    1242             : 
    1243           7 :     *have = (size_t)(buffer_end - helper->next_request);
    1244             : 
    1245           7 :     if (*have < *total)
    1246           2 :         return HTTP_PARTIAL_CONTENT;
    1247             : 
    1248           5 :     helper->body_data.value = helper->next_request;
    1249           5 :     helper->body_data.len = *total;
    1250           5 :     helper->next_request += *total;
    1251           5 :     return HTTP_OK;
    1252             : }
    1253             : 
    1254           7 : static int read_body_data(struct lwan_request *request)
    1255             : {
    1256             :     /* Holy indirection, Batman! */
    1257           7 :     const struct lwan_config *config = &request->conn->thread->lwan->config;
    1258           7 :     struct lwan_request_parser_helper *helper = request->helper;
    1259             :     enum lwan_http_status status;
    1260             :     size_t total, have, max_data_size;
    1261             :     bool allow_temp_file;
    1262             :     char *new_buffer;
    1263             : 
    1264           7 :     switch (lwan_request_get_method(request)) {
    1265           7 :     case REQUEST_METHOD_POST:
    1266           7 :         allow_temp_file = config->allow_post_temp_file;
    1267           7 :         max_data_size = config->max_post_data_size;
    1268           7 :         break;
    1269           0 :     case REQUEST_METHOD_PUT:
    1270           0 :         allow_temp_file = config->allow_put_temp_file;
    1271           0 :         max_data_size = config->max_put_data_size;
    1272           0 :         break;
    1273           0 :     default:
    1274           0 :         return -HTTP_NOT_ALLOWED;
    1275             :     }
    1276             : 
    1277             :     status =
    1278           7 :         get_remaining_body_data_length(request, max_data_size, &total, &have);
    1279           7 :     if (status != HTTP_PARTIAL_CONTENT)
    1280           5 :         return -(int)status;
    1281             : 
    1282             :     new_buffer =
    1283           2 :         alloc_body_buffer(request->conn->coro, total + 1, allow_temp_file);
    1284           2 :     if (UNLIKELY(!new_buffer))
    1285           0 :         return -HTTP_INTERNAL_ERROR;
    1286             : 
    1287           2 :     if (!(request->flags & REQUEST_IS_HTTP_1_0)) {
    1288             :         /* §8.2.3 https://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html */
    1289           2 :         const char *expect = lwan_request_get_header(request, "Expect");
    1290             : 
    1291           2 :         if (expect && strncmp(expect, "100-", 4) == 0) {
    1292             :             static const char continue_header[] = "HTTP/1.1 100 Continue\r\n\r\n";
    1293             : 
    1294           0 :             lwan_send(request, continue_header, sizeof(continue_header) - 1, 0);
    1295             :         }
    1296             :     }
    1297             : 
    1298           2 :     helper->body_data.value = new_buffer;
    1299           2 :     helper->body_data.len = total;
    1300           2 :     if (have) {
    1301           2 :         new_buffer = mempcpy(new_buffer, helper->next_request, have);
    1302           2 :         total -= have;
    1303             :     }
    1304           2 :     helper->next_request = NULL;
    1305             : 
    1306           2 :     helper->error_when_time = time(NULL) + config->keep_alive_timeout;
    1307           2 :     helper->error_when_n_packets = lwan_calculate_n_packets(total);
    1308             : 
    1309           2 :     struct lwan_value buffer = {.value = new_buffer, .len = total};
    1310           2 :     return (int)client_read(request, &buffer, total, body_data_finalizer);
    1311             : }
    1312             : 
    1313             : static char *
    1314         303 : parse_proxy_protocol(struct lwan_request *request, char *buffer)
    1315             : {
    1316         303 :     STRING_SWITCH(buffer) {
    1317           1 :     case STR4_INT('P','R','O','X'):
    1318           1 :         return parse_proxy_protocol_v1(request, buffer);
    1319           2 :     case STR4_INT('\x0D','\x0A','\x0D','\x0A'):
    1320           2 :         return parse_proxy_protocol_v2(request, buffer);
    1321             :     }
    1322             : 
    1323         300 :     return buffer;
    1324             : }
    1325             : 
    1326         595 : static enum lwan_http_status parse_http_request(struct lwan_request *request)
    1327             : {
    1328         595 :     struct lwan_request_parser_helper *helper = request->helper;
    1329         595 :     char *buffer = helper->buffer->value;
    1330             : 
    1331         595 :     if (request->flags & REQUEST_ALLOW_PROXY_REQS) {
    1332             :         /* REQUEST_ALLOW_PROXY_REQS will be cleared in lwan_process_request() */
    1333             : 
    1334         303 :         buffer = parse_proxy_protocol(request, buffer);
    1335         303 :         if (UNLIKELY(!buffer))
    1336           0 :             return HTTP_BAD_REQUEST;
    1337             :     }
    1338             : 
    1339         595 :     buffer = ignore_leading_whitespace(buffer);
    1340             : 
    1341         595 :     if (UNLIKELY(buffer > helper->buffer->value + helper->buffer->len -
    1342             :                                MIN_REQUEST_SIZE))
    1343           1 :         return HTTP_BAD_REQUEST;
    1344             : 
    1345         594 :     char *path = identify_http_method(request, buffer);
    1346         594 :     if (UNLIKELY(!path))
    1347           1 :         return HTTP_NOT_ALLOWED;
    1348             : 
    1349         593 :     buffer = identify_http_path(request, path);
    1350         593 :     if (UNLIKELY(!buffer))
    1351           4 :         return HTTP_BAD_REQUEST;
    1352             : 
    1353         589 :     if (UNLIKELY(!parse_headers(helper, buffer)))
    1354           2 :         return HTTP_BAD_REQUEST;
    1355             : 
    1356         587 :     ssize_t decoded_len = url_decode(request->url.value);
    1357         587 :     if (UNLIKELY(decoded_len < 0))
    1358           0 :         return HTTP_BAD_REQUEST;
    1359         587 :     request->original_url.len = request->url.len = (size_t)decoded_len;
    1360             : 
    1361             :     parse_connection_header(request);
    1362             : 
    1363         587 :     return HTTP_OK;
    1364             : }
    1365             : 
    1366             : static enum lwan_http_status
    1367           0 : prepare_websocket_handshake(struct lwan_request *request, char **encoded)
    1368             : {
    1369             :     static const unsigned char websocket_uuid[] =
    1370             :         "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
    1371             :     unsigned char digest[20];
    1372             :     sha1_context ctx;
    1373             : 
    1374           0 :     if (UNLIKELY(request->flags & RESPONSE_SENT_HEADERS))
    1375           0 :         return HTTP_INTERNAL_ERROR;
    1376             : 
    1377           0 :     if (UNLIKELY(!(request->conn->flags & CONN_IS_UPGRADE)))
    1378           0 :         return HTTP_BAD_REQUEST;
    1379             : 
    1380           0 :     const char *upgrade = lwan_request_get_header(request, "Upgrade");
    1381           0 :     if (UNLIKELY(!upgrade || !streq(upgrade, "websocket")))
    1382           0 :         return HTTP_BAD_REQUEST;
    1383             : 
    1384             :     const char *sec_websocket_key =
    1385           0 :         lwan_request_get_header(request, "Sec-WebSocket-Key");
    1386           0 :     if (UNLIKELY(!sec_websocket_key))
    1387           0 :         return HTTP_BAD_REQUEST;
    1388             : 
    1389           0 :     const size_t sec_websocket_key_len = strlen(sec_websocket_key);
    1390           0 :     if (base64_encoded_len(16) != sec_websocket_key_len)
    1391           0 :         return HTTP_BAD_REQUEST;
    1392           0 :     if (UNLIKELY(!base64_validate((void *)sec_websocket_key, sec_websocket_key_len)))
    1393           0 :         return HTTP_BAD_REQUEST;
    1394             : 
    1395           0 :     sha1_init(&ctx);
    1396           0 :     sha1_update(&ctx, (void *)sec_websocket_key, sec_websocket_key_len);
    1397           0 :     sha1_update(&ctx, websocket_uuid, sizeof(websocket_uuid) - 1);
    1398           0 :     sha1_finalize(&ctx, digest);
    1399             : 
    1400           0 :     *encoded = (char *)base64_encode(digest, sizeof(digest), NULL);
    1401           0 :     return LIKELY(*encoded) ? HTTP_SWITCHING_PROTOCOLS : HTTP_INTERNAL_ERROR;
    1402             : }
    1403             : 
    1404             : enum lwan_http_status
    1405           0 : lwan_request_websocket_upgrade(struct lwan_request *request)
    1406             : {
    1407             :     char header_buf[DEFAULT_HEADERS_SIZE];
    1408             :     size_t header_buf_len;
    1409             :     char *encoded;
    1410             : 
    1411           0 :     enum lwan_http_status r = prepare_websocket_handshake(request, &encoded);
    1412           0 :     if (r != HTTP_SWITCHING_PROTOCOLS)
    1413           0 :         return r;
    1414             : 
    1415           0 :     request->flags |= RESPONSE_NO_CONTENT_LENGTH;
    1416           0 :     header_buf_len = lwan_prepare_response_header_full(
    1417             :         request, HTTP_SWITCHING_PROTOCOLS, header_buf, sizeof(header_buf),
    1418           0 :         (struct lwan_key_value[]){
    1419             :             /* Connection: Upgrade is implicit if conn->flags & CONN_IS_UPGRADE */
    1420             :             {.key = "Sec-WebSocket-Accept", .value = encoded},
    1421             :             {.key = "Upgrade", .value = "websocket"},
    1422             :             {},
    1423             :         });
    1424           0 :     free(encoded);
    1425           0 :     if (UNLIKELY(!header_buf_len))
    1426           0 :         return HTTP_INTERNAL_ERROR;
    1427             : 
    1428           0 :     request->conn->flags |= CONN_IS_WEBSOCKET;
    1429           0 :     lwan_send(request, header_buf, header_buf_len, 0);
    1430             : 
    1431           0 :     return HTTP_SWITCHING_PROTOCOLS;
    1432             : }
    1433             : 
    1434         601 : static inline bool request_has_body(const struct lwan_request *request)
    1435             : {
    1436             :     /* 3rd bit set in method: request method has body. See lwan.h,
    1437             :      * definition of FOR_EACH_REQUEST_METHOD() for more info. */
    1438         601 :     return lwan_request_get_method(request) & 1 << 3;
    1439             : }
    1440             : 
    1441             : static enum lwan_http_status
    1442           7 : maybe_read_body_data(const struct lwan_url_map *url_map,
    1443             :                      struct lwan_request *request)
    1444             : {
    1445           7 :     int status = 0;
    1446             : 
    1447           7 :     if (url_map->flags & HANDLER_EXPECTS_BODY_DATA) {
    1448           7 :         status = read_body_data(request);
    1449           7 :         if (status > 0)
    1450           2 :             return (enum lwan_http_status)status;
    1451             :     }
    1452             : 
    1453             :     /* Instead of trying to read the body here, which will require
    1454             :      * us to allocate and read potentially a lot of bytes, force
    1455             :      * this connection to be closed as soon as we send a "not allowed"
    1456             :      * response.  */
    1457           5 :     request->conn->flags &= ~CONN_IS_KEEP_ALIVE;
    1458             : 
    1459           5 :     if (status < 0) {
    1460           5 :         status = -status;
    1461           5 :         return (enum lwan_http_status)status;
    1462             :     }
    1463             : 
    1464           0 :     return HTTP_NOT_ALLOWED;
    1465             : }
    1466             : 
    1467         604 : static enum lwan_http_status prepare_for_response(const struct lwan_url_map *url_map,
    1468             :                                                   struct lwan_request *request)
    1469             : {
    1470         604 :     request->url.value += url_map->prefix_len;
    1471         604 :     request->url.len -= url_map->prefix_len;
    1472         634 :     while (*request->url.value == '/' && request->url.len > 0) {
    1473          30 :         request->url.value++;
    1474          30 :         request->url.len--;
    1475             :     }
    1476             : 
    1477         604 :     if (UNLIKELY(url_map->flags & HANDLER_MUST_AUTHORIZE)) {
    1478           4 :         if (!lwan_http_authorize_urlmap(request, url_map))
    1479           3 :             return HTTP_NOT_AUTHORIZED;
    1480             :     }
    1481             : 
    1482         601 :     if (UNLIKELY(request_has_body(request)))
    1483           7 :         return maybe_read_body_data(url_map, request);
    1484             : 
    1485         594 :     return HTTP_OK;
    1486             : }
    1487             : 
    1488          17 : static bool handle_rewrite(struct lwan_request *request)
    1489             : {
    1490          17 :     struct lwan_request_parser_helper *helper = request->helper;
    1491             : 
    1492          17 :     request->flags &= ~RESPONSE_URL_REWRITTEN;
    1493             : 
    1494          17 :     find_query_string(request, request->url.value + request->url.len);
    1495             : 
    1496          17 :     helper->urls_rewritten++;
    1497          17 :     if (UNLIKELY(helper->urls_rewritten > 4)) {
    1498           0 :         lwan_default_response(request, HTTP_INTERNAL_ERROR);
    1499           0 :         return false;
    1500             :     }
    1501             : 
    1502          17 :     return true;
    1503             : }
    1504             : 
    1505         499 : const char *lwan_request_get_method_str(const struct lwan_request *request)
    1506             : {
    1507             : #define GENERATE_CASE_STMT(upper, lower, mask, constant, probability)          \
    1508             :     case REQUEST_METHOD_##upper:                                               \
    1509             :         return #upper;
    1510             : 
    1511         499 :     switch (lwan_request_get_method(request)) {
    1512         497 :         FOR_EACH_REQUEST_METHOD(GENERATE_CASE_STMT)
    1513           2 :     default:
    1514           2 :         return "UNKNOWN";
    1515             :     }
    1516             : #undef GENERATE_CASE_STMT
    1517             : }
    1518             : 
    1519             : #ifndef NDEBUG
    1520         499 : static void log_request(struct lwan_request *request,
    1521             :                         enum lwan_http_status status,
    1522             :                         double time_to_read_request,
    1523             :                         double time_to_process_request)
    1524             : {
    1525             :     char ip_buffer[INET6_ADDRSTRLEN];
    1526             : 
    1527         499 :     lwan_status_debug(
    1528             :         "%s [%s] %016lx \"%s %s HTTP/%s\" %d %s (r:%.3fms p:%.3fms)",
    1529             :         lwan_request_get_remote_address(request, ip_buffer),
    1530             :         request->conn->thread->date.date, lwan_request_get_id(request),
    1531             :         lwan_request_get_method_str(request), request->original_url.value,
    1532             :         request->flags & REQUEST_IS_HTTP_1_0 ? "1.0" : "1.1", status,
    1533             :         request->response.mime_type, time_to_read_request,
    1534             :         time_to_process_request);
    1535         499 : }
    1536             : #else
    1537             : #define log_request(...)
    1538             : #endif
    1539             : 
    1540             : #ifndef NDEBUG
    1541        2299 : static struct timespec current_precise_monotonic_timespec(void)
    1542             : {
    1543             :     struct timespec now;
    1544             : 
    1545        2299 :     if (UNLIKELY(clock_gettime(CLOCK_MONOTONIC, &now) < 0)) {
    1546           0 :         lwan_status_perror("clock_gettime");
    1547           0 :         return (struct timespec){};
    1548             :     }
    1549             : 
    1550        2299 :     return now;
    1551             : }
    1552             : 
    1553        1099 : static double elapsed_time_ms(const struct timespec then)
    1554             : {
    1555        1099 :     const struct timespec now = current_precise_monotonic_timespec();
    1556        1099 :     struct timespec diff = {
    1557        1099 :         .tv_sec = now.tv_sec - then.tv_sec,
    1558        1099 :         .tv_nsec = now.tv_nsec - then.tv_nsec,
    1559             :     };
    1560             : 
    1561        1099 :     if (diff.tv_nsec < 0) {
    1562           0 :         diff.tv_sec--;
    1563           0 :         diff.tv_nsec += 1000000000l;
    1564             :     }
    1565             : 
    1566        1099 :     return (double)diff.tv_sec / 1000.0 + (double)diff.tv_nsec / 1000000.0;
    1567             : }
    1568             : #endif
    1569             : 
    1570         600 : void lwan_process_request(struct lwan *l, struct lwan_request *request)
    1571             : {
    1572             :     enum lwan_http_status status;
    1573             :     struct lwan_url_map *url_map;
    1574             : 
    1575             : #ifndef NDEBUG
    1576         600 :     struct timespec request_read_begin_time = current_precise_monotonic_timespec();
    1577             : #endif
    1578         600 :     status = read_request(request);
    1579             : 
    1580             : #ifndef NDEBUG
    1581         600 :     double time_to_read_request = elapsed_time_ms(request_read_begin_time);
    1582             : 
    1583         600 :     struct timespec request_begin_time = current_precise_monotonic_timespec();
    1584             : #endif
    1585         600 :     if (UNLIKELY(status != HTTP_OK)) {
    1586             :         /* If read_request() returns any error at this point, it's probably
    1587             :          * better to just send an error response and abort the coroutine and
    1588             :          * let the client handle the error instead: we don't have
    1589             :          * information to even log the request because it has not been
    1590             :          * parsed yet at this stage.  Even if there are other requests waiting
    1591             :          * in the pipeline, this seems like the safer thing to do.  */
    1592           5 :         request->conn->flags &= ~CONN_IS_KEEP_ALIVE;
    1593           5 :         lwan_default_response(request, status);
    1594             :         /* Let process_request_coro() gracefully close the connection. */
    1595           5 :         return;
    1596             :     }
    1597             : 
    1598         595 :     status = parse_http_request(request);
    1599         595 :     if (UNLIKELY(status != HTTP_OK))
    1600           8 :         goto log_and_return;
    1601             : 
    1602         587 : lookup_again:
    1603         604 :     url_map = lwan_trie_lookup_prefix(&l->url_map_trie, request->url.value);
    1604         604 :     if (UNLIKELY(!url_map)) {
    1605           0 :         status = HTTP_NOT_FOUND;
    1606           0 :         goto log_and_return;
    1607             :     }
    1608             : 
    1609         604 :     status = prepare_for_response(url_map, request);
    1610         604 :     if (UNLIKELY(status != HTTP_OK))
    1611           3 :         goto log_and_return;
    1612             : 
    1613         601 :     status = url_map->handler(request, &request->response, url_map->data);
    1614         509 :     if (UNLIKELY(url_map->flags & HANDLER_CAN_REWRITE_URL)) {
    1615          20 :         if (request->flags & RESPONSE_URL_REWRITTEN) {
    1616          17 :             if (LIKELY(handle_rewrite(request)))
    1617          17 :                 goto lookup_again;
    1618           0 :             return;
    1619             :         }
    1620             :     }
    1621             : 
    1622         492 : log_and_return:
    1623         503 :     lwan_response(request, status);
    1624             : 
    1625         499 :     log_request(request, status, time_to_read_request, elapsed_time_ms(request_begin_time));
    1626             : }
    1627             : 
    1628             : static inline void *
    1629         832 : value_lookup(const struct lwan_key_value_array *array, const char *key)
    1630             : {
    1631         832 :     const struct lwan_array *la = (const struct lwan_array *)array;
    1632             : 
    1633         832 :     if (LIKELY(la->elements)) {
    1634         642 :         struct lwan_key_value k = { .key = (char *)key };
    1635             :         struct lwan_key_value *entry;
    1636             : 
    1637         642 :         entry = bsearch(&k, la->base, la->elements, sizeof(k), key_value_compare);
    1638         642 :         if (LIKELY(entry))
    1639         335 :             return entry->value;
    1640             :     }
    1641             : 
    1642         497 :     return NULL;
    1643             : }
    1644             : 
    1645         818 : const char *lwan_request_get_query_param(struct lwan_request *request,
    1646             :                                          const char *key)
    1647             : {
    1648         818 :     return value_lookup(lwan_request_get_query_params(request), key);
    1649             : }
    1650             : 
    1651           0 : const char *lwan_request_get_post_param(struct lwan_request *request,
    1652             :                                         const char *key)
    1653             : {
    1654           0 :     return value_lookup(lwan_request_get_post_params(request), key);
    1655             : }
    1656             : 
    1657          14 : const char *lwan_request_get_cookie(struct lwan_request *request,
    1658             :                                     const char *key)
    1659             : {
    1660          14 :     return value_lookup(lwan_request_get_cookies(request), key);
    1661             : }
    1662             : 
    1663             : const char *
    1664          45 : lwan_request_get_header_from_helper(struct lwan_request_parser_helper *helper,
    1665             :                                     const char *header)
    1666             : {
    1667          45 :     const size_t header_len = strlen(header);
    1668          45 :     const size_t header_len_with_separator =
    1669             :         header_len + HEADER_VALUE_SEPARATOR_LEN;
    1670             : 
    1671          45 :     assert(strchr(header, ':') == NULL);
    1672             : 
    1673         277 :     for (size_t i = 0; i < helper->n_header_start; i++) {
    1674         250 :         const char *start = helper->header_start[i];
    1675         250 :         char *end = helper->header_start[i + 1] - HEADER_TERMINATOR_LEN;
    1676             : 
    1677         250 :         if (UNLIKELY((size_t)(end - start) < header_len_with_separator))
    1678           4 :             continue;
    1679             : 
    1680         492 :         STRING_SWITCH_SMALL (start + header_len) {
    1681          58 :         case STR2_INT(':', ' '):
    1682          58 :             if (!strncasecmp(start, header, header_len)) {
    1683          18 :                 *end = '\0';
    1684          18 :                 return start + header_len_with_separator;
    1685             :             }
    1686             :         }
    1687             :     }
    1688             : 
    1689          27 :     return NULL;
    1690             : }
    1691             : 
    1692          45 : inline const char *lwan_request_get_header(struct lwan_request *request,
    1693             :                                            const char *header)
    1694             : {
    1695          45 :     return lwan_request_get_header_from_helper(request->helper, header);
    1696             : }
    1697             : 
    1698           0 : const char *lwan_request_get_host(struct lwan_request *request)
    1699             : {
    1700           0 :     const struct lwan_request_parser_helper *helper = request->helper;
    1701             : 
    1702           0 :     return helper->host.len ? helper->host.value : NULL;
    1703             : }
    1704             : 
    1705             : ALWAYS_INLINE int
    1706         617 : lwan_connection_get_fd(const struct lwan *lwan, const struct lwan_connection *conn)
    1707             : {
    1708         617 :     return (int)(intptr_t)(conn - lwan->conns);
    1709             : }
    1710             : 
    1711             : const char *
    1712         509 : lwan_request_get_remote_address_and_port(struct lwan_request *request,
    1713             :                                          char buffer[static INET6_ADDRSTRLEN],
    1714             :                                          uint16_t *port)
    1715             : {
    1716         509 :     struct sockaddr_storage non_proxied_addr = {.ss_family = AF_UNSPEC};
    1717             :     struct sockaddr_storage *sock_addr;
    1718             : 
    1719         509 :     *port = 0;
    1720             : 
    1721         509 :     if (request->flags & REQUEST_PROXIED) {
    1722          21 :         sock_addr = (struct sockaddr_storage *)&request->proxy->from;
    1723             : 
    1724          21 :         if (UNLIKELY(sock_addr->ss_family == AF_UNSPEC)) {
    1725             :             static const char unspecified[] = "*unspecified*";
    1726             : 
    1727             :             static_assert(sizeof(unspecified) <= INET6_ADDRSTRLEN,
    1728             :                           "Enough space for unspecified address family");
    1729           1 :             return memcpy(buffer, unspecified, sizeof(unspecified));
    1730             :         }
    1731             :     } else {
    1732         488 :         socklen_t sock_len = sizeof(non_proxied_addr);
    1733             : 
    1734         488 :         sock_addr = &non_proxied_addr;
    1735             : 
    1736         488 :         if (UNLIKELY(getpeername(request->fd, (struct sockaddr *)sock_addr,
    1737             :                                  &sock_len) < 0)) {
    1738          11 :             return NULL;
    1739             :         }
    1740             :     }
    1741             : 
    1742         497 :     if (sock_addr->ss_family == AF_INET) {
    1743         497 :         struct sockaddr_in *sin = (struct sockaddr_in *)sock_addr;
    1744         497 :         *port = ntohs(sin->sin_port);
    1745         497 :         return inet_ntop(AF_INET, &sin->sin_addr, buffer, INET6_ADDRSTRLEN);
    1746             :     }
    1747             : 
    1748           0 :     struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sock_addr;
    1749           0 :     *port = ntohs(sin6->sin6_port);
    1750           0 :     return inet_ntop(AF_INET6, &sin6->sin6_addr, buffer, INET6_ADDRSTRLEN);
    1751             : }
    1752             : 
    1753             : const char *
    1754         509 : lwan_request_get_remote_address(struct lwan_request *request,
    1755             :                                 char buffer[static INET6_ADDRSTRLEN])
    1756             : {
    1757             :     uint16_t port;
    1758         509 :     return lwan_request_get_remote_address_and_port(request, buffer, &port);
    1759             : }
    1760             : 
    1761           1 : static void remove_sleep(void *data1, void *data2)
    1762             : {
    1763             :     static const enum lwan_connection_flags suspended_sleep =
    1764             :         CONN_SUSPENDED | CONN_HAS_REMOVE_SLEEP_DEFER;
    1765           1 :     struct timeouts *wheel = data1;
    1766           1 :     struct timeout *timeout = data2;
    1767           1 :     struct lwan_request *request =
    1768             :         container_of(timeout, struct lwan_request, timeout);
    1769             : 
    1770           1 :     if ((request->conn->flags & suspended_sleep) == suspended_sleep)
    1771           0 :         timeouts_del(wheel, timeout);
    1772             : 
    1773           1 :     request->conn->flags &= ~CONN_HAS_REMOVE_SLEEP_DEFER;
    1774           1 : }
    1775             : 
    1776           1 : void lwan_request_sleep(struct lwan_request *request, uint64_t ms)
    1777             : {
    1778           1 :     struct lwan_connection *conn = request->conn;
    1779           1 :     struct timeouts *wheel = conn->thread->wheel;
    1780             :     struct timespec now;
    1781           1 :     coro_deferred defer = -1;
    1782             : 
    1783             :     /* We need to update the timer wheel right now because
    1784             :      * a request might have requested to sleep a long time
    1785             :      * before it was being serviced -- causing the timeout
    1786             :      * to essentially be a no-op. */
    1787           1 :     if (UNLIKELY(clock_gettime(monotonic_clock_id, &now) < 0))
    1788           0 :         lwan_status_critical("Could not get monotonic time");
    1789           1 :     timeouts_update(wheel, (timeout_t)(now.tv_sec * 1000 + now.tv_nsec / 1000000));
    1790             : 
    1791           1 :     request->timeout = (struct timeout) {};
    1792           1 :     timeouts_add(wheel, &request->timeout, ms);
    1793             : 
    1794           1 :     if (!(conn->flags & CONN_HAS_REMOVE_SLEEP_DEFER)) {
    1795           1 :         defer = coro_defer2(conn->coro, remove_sleep, wheel, &request->timeout);
    1796           1 :         conn->flags |= CONN_HAS_REMOVE_SLEEP_DEFER;
    1797             :     }
    1798             : 
    1799           1 :     coro_yield(conn->coro, CONN_CORO_SUSPEND);
    1800             : 
    1801           1 :     if (defer > 0)
    1802           1 :         coro_defer_fire_and_disarm(conn->coro, defer);
    1803           1 : }
    1804             : 
    1805             : ALWAYS_INLINE int
    1806          15 : lwan_request_get_range(struct lwan_request *request, off_t *from, off_t *to)
    1807             : {
    1808          15 :     struct lwan_request_parser_helper *helper = request->helper;
    1809             : 
    1810          15 :     if (!(request->flags & REQUEST_PARSED_RANGE)) {
    1811          15 :         parse_range(helper);
    1812          15 :         request->flags |= REQUEST_PARSED_RANGE;
    1813             :     }
    1814             : 
    1815          15 :     if (LIKELY(helper->range.raw.len)) {
    1816           6 :         *from = helper->range.from;
    1817           6 :         *to = helper->range.to;
    1818           6 :         return 0;
    1819             :     }
    1820             : 
    1821           9 :     return -ENOENT;
    1822             : }
    1823             : 
    1824             : ALWAYS_INLINE int
    1825          46 : lwan_request_get_if_modified_since(struct lwan_request *request, time_t *value)
    1826             : {
    1827          46 :     struct lwan_request_parser_helper *helper = request->helper;
    1828             : 
    1829          46 :     if (!(request->flags & REQUEST_PARSED_IF_MODIFIED_SINCE)) {
    1830          46 :         parse_if_modified_since(helper);
    1831          46 :         request->flags |= REQUEST_PARSED_IF_MODIFIED_SINCE;
    1832             :     }
    1833             : 
    1834          46 :     if (LIKELY(helper->if_modified_since.raw.len)) {
    1835           0 :         *value = helper->if_modified_since.parsed;
    1836           0 :         return 0;
    1837             :     }
    1838             : 
    1839          46 :     return -ENOENT;
    1840             : }
    1841             : 
    1842             : ALWAYS_INLINE const struct lwan_value *
    1843           6 : lwan_request_get_request_body(struct lwan_request *request)
    1844             : {
    1845           6 :     return &request->helper->body_data;
    1846             : }
    1847             : 
    1848             : ALWAYS_INLINE const struct lwan_value *
    1849           6 : lwan_request_get_content_type(struct lwan_request *request)
    1850             : {
    1851           6 :     return &request->helper->content_type;
    1852             : }
    1853             : 
    1854             : ALWAYS_INLINE const struct lwan_key_value_array *
    1855          75 : lwan_request_get_cookies(struct lwan_request *request)
    1856             : {
    1857          89 :     if (!(request->flags & REQUEST_PARSED_COOKIES)) {
    1858          37 :         parse_cookies(request);
    1859          37 :         request->flags |= REQUEST_PARSED_COOKIES;
    1860             :     }
    1861             : 
    1862          89 :     return &request->helper->cookies;
    1863             : }
    1864             : 
    1865             : ALWAYS_INLINE const struct lwan_key_value_array *
    1866         161 : lwan_request_get_query_params(struct lwan_request *request)
    1867             : {
    1868         979 :     if (!(request->flags & REQUEST_PARSED_QUERY_STRING)) {
    1869         405 :         parse_query_string(request);
    1870         405 :         request->flags |= REQUEST_PARSED_QUERY_STRING;
    1871             :     }
    1872             : 
    1873         979 :     return &request->helper->query_params;
    1874             : }
    1875             : 
    1876             : ALWAYS_INLINE const struct lwan_key_value_array *
    1877           7 : lwan_request_get_post_params(struct lwan_request *request)
    1878             : {
    1879           7 :     if (!(request->flags & REQUEST_PARSED_FORM_DATA)) {
    1880           1 :         parse_form_data(request);
    1881           1 :         request->flags |= REQUEST_PARSED_FORM_DATA;
    1882             :     }
    1883             : 
    1884           7 :     return &request->helper->post_params;
    1885             : }
    1886             : 
    1887             : ALWAYS_INLINE enum lwan_request_flags
    1888          64 : lwan_request_get_accept_encoding(struct lwan_request *request)
    1889             : {
    1890          64 :     if (!(request->flags & REQUEST_PARSED_ACCEPT_ENCODING)) {
    1891          32 :         parse_accept_encoding(request);
    1892          32 :         request->flags |= REQUEST_PARSED_ACCEPT_ENCODING;
    1893             :     }
    1894             : 
    1895          64 :     return request->flags & REQUEST_ACCEPT_MASK;
    1896             : }
    1897             : 
    1898             : #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
    1899             : static int useless_coro_for_fuzzing(struct coro *c __attribute__((unused)),
    1900             :                                     void *data __attribute__((unused)))
    1901             : {
    1902             :     return 0;
    1903             : }
    1904             : 
    1905             : static bool request_seems_complete(struct lwan_request_parser_helper *helper)
    1906             : {
    1907             :     return read_request_finalizer_from_helper(helper->buffer, helper, 1,
    1908             :                                               false) == FINALIZER_DONE;
    1909             : }
    1910             : 
    1911             : __attribute__((used)) int fuzz_parse_http_request(const uint8_t *data,
    1912             :                                                   size_t length)
    1913             : {
    1914             :     static struct coro_switcher switcher;
    1915             :     static struct coro *coro;
    1916             :     static char *header_start[N_HEADER_START];
    1917             :     static char data_copy[32767] = {0};
    1918             : 
    1919             :     if (length > sizeof(data_copy))
    1920             :         length = sizeof(data_copy);
    1921             :     memcpy(data_copy, data, length);
    1922             : 
    1923             :     if (!coro) {
    1924             :         coro = coro_new(&switcher, useless_coro_for_fuzzing, NULL);
    1925             : 
    1926             :         lwan_job_thread_init();
    1927             :         lwan_http_authorize_init();
    1928             :     }
    1929             : 
    1930             :     struct lwan_request_parser_helper helper = {
    1931             :         .buffer = &(struct lwan_value){.value = data_copy, .len = length},
    1932             :         .header_start = header_start,
    1933             :         .error_when_n_packets = 2,
    1934             :     };
    1935             :     struct lwan_connection conn = {.coro = coro};
    1936             :     struct lwan_proxy proxy = {};
    1937             :     struct lwan_request request = {
    1938             :         .helper = &helper,
    1939             :         .conn = &conn,
    1940             :         .flags = REQUEST_ALLOW_PROXY_REQS,
    1941             :         .proxy = &proxy,
    1942             :     };
    1943             : 
    1944             :     /* If the finalizer isn't happy with a request, there's no point in
    1945             :      * going any further with parsing it. */
    1946             :     if (!request_seems_complete(&helper))
    1947             :         return 0;
    1948             : 
    1949             :     /* client_read() NUL-terminates the string */
    1950             :     data_copy[length - 1] = '\0';
    1951             : 
    1952             :     if (parse_http_request(&request) != HTTP_OK)
    1953             :         return 0;
    1954             : 
    1955             :     off_t trash1;
    1956             :     time_t trash2;
    1957             :     char *trash3;
    1958             :     size_t gen = coro_deferred_get_generation(coro);
    1959             : 
    1960             :     /* Only pointers were set in helper struct; actually parse them here. */
    1961             :     parse_accept_encoding(&request);
    1962             : 
    1963             :     /* Requesting these items will force them to be parsed, and also
    1964             :      * exercise the lookup function. */
    1965             :     LWAN_NO_DISCARD(lwan_request_get_header(&request, "Non-Existing-Header"));
    1966             : 
    1967             :     /* Usually existing short header */
    1968             :     LWAN_NO_DISCARD(lwan_request_get_header(&request, "Host"));
    1969             : 
    1970             :     LWAN_NO_DISCARD(lwan_request_get_cookie(&request, "Non-Existing-Cookie"));
    1971             :     /* Set by some tests */
    1972             :     LWAN_NO_DISCARD(lwan_request_get_cookie(&request, "FOO"));
    1973             : 
    1974             :     LWAN_NO_DISCARD(
    1975             :         lwan_request_get_query_param(&request, "Non-Existing-Query-Param"));
    1976             : 
    1977             :     LWAN_NO_DISCARD(
    1978             :         lwan_request_get_post_param(&request, "Non-Existing-Post-Param"));
    1979             : 
    1980             :     lwan_request_get_range(&request, &trash1, &trash1);
    1981             :     LWAN_NO_DISCARD(trash1);
    1982             : 
    1983             :     lwan_request_get_if_modified_since(&request, &trash2);
    1984             :     LWAN_NO_DISCARD(trash2);
    1985             : 
    1986             :     enum lwan_http_status handshake =
    1987             :         prepare_websocket_handshake(&request, &trash3);
    1988             :     LWAN_NO_DISCARD(trash3);
    1989             :     if (handshake == HTTP_SWITCHING_PROTOCOLS)
    1990             :         free(trash3);
    1991             : 
    1992             :     LWAN_NO_DISCARD(lwan_http_authorize(&request, "Fuzzy Realm", "/dev/null"));
    1993             : 
    1994             :     coro_deferred_run(coro, gen);
    1995             : 
    1996             :     return 0;
    1997             : }
    1998             : #endif
    1999             : 
    2000             : static inline int64_t
    2001           0 : make_async_yield_value(int fd, enum lwan_connection_coro_yield event)
    2002             : {
    2003           0 :     return (int64_t)(((uint64_t)fd << 32 | event));
    2004             : }
    2005             : 
    2006           0 : static inline void async_await_fd(struct coro *coro,
    2007             :                                   int fd,
    2008             :                                   enum lwan_connection_coro_yield events)
    2009             : {
    2010           0 :     assert(events >= CONN_CORO_ASYNC_AWAIT_READ &&
    2011             :            events <= CONN_CORO_ASYNC_AWAIT_READ_WRITE);
    2012             : 
    2013           0 :     return (void)coro_yield(coro, make_async_yield_value(fd, events));
    2014             : }
    2015             : 
    2016           0 : void lwan_request_await_read(struct lwan_request *r, int fd)
    2017             : {
    2018           0 :     return async_await_fd(r->conn->coro, fd, CONN_CORO_ASYNC_AWAIT_READ);
    2019             : }
    2020             : 
    2021           0 : void lwan_request_await_write(struct lwan_request *r, int fd)
    2022             : {
    2023           0 :     return async_await_fd(r->conn->coro, fd, CONN_CORO_ASYNC_AWAIT_WRITE);
    2024             : }
    2025             : 
    2026           0 : void lwan_request_await_read_write(struct lwan_request *r, int fd)
    2027             : {
    2028           0 :     return async_await_fd(r->conn->coro, fd, CONN_CORO_ASYNC_AWAIT_READ_WRITE);
    2029             : }
    2030             : 
    2031           0 : ssize_t lwan_request_async_read_flags(
    2032             :     struct lwan_request *request, int fd, void *buf, size_t len, int flags)
    2033             : {
    2034           0 :     while (true) {
    2035           0 :         ssize_t r = recv(fd, buf, len, MSG_DONTWAIT | MSG_NOSIGNAL | flags);
    2036             : 
    2037           0 :         if (r < 0) {
    2038           0 :             switch (errno) {
    2039           0 :             case EWOULDBLOCK:
    2040           0 :                 lwan_request_await_read(request, fd);
    2041             :                 /* Fallthrough */
    2042           0 :             case EINTR:
    2043           0 :                 continue;
    2044           0 :             case EPIPE:
    2045           0 :                 return -errno;
    2046             :             }
    2047             :         }
    2048             : 
    2049           0 :         return r;
    2050             :     }
    2051             : }
    2052             : 
    2053           0 : ssize_t lwan_request_async_read(struct lwan_request *request,
    2054             :                                 int fd,
    2055             :                                 void *buf,
    2056             :                                 size_t len)
    2057             : {
    2058           0 :     return lwan_request_async_read_flags(request, fd, buf, len, 0);
    2059             : }
    2060             : 
    2061           0 : ssize_t lwan_request_async_write(struct lwan_request *request,
    2062             :                                  int fd,
    2063             :                                  const void *buf,
    2064             :                                  size_t len)
    2065             : {
    2066           0 :     while (true) {
    2067           0 :         ssize_t r = send(fd, buf, len, MSG_DONTWAIT|MSG_NOSIGNAL);
    2068             : 
    2069           0 :         if (r < 0) {
    2070           0 :             switch (errno) {
    2071           0 :             case EWOULDBLOCK:
    2072           0 :                 lwan_request_await_write(request, fd);
    2073             :                 /* Fallthrough */
    2074           0 :             case EINTR:
    2075           0 :                 continue;
    2076           0 :             case EPIPE:
    2077           0 :                 return -errno;
    2078             :             }
    2079             :         }
    2080             : 
    2081           0 :         return r;
    2082             :     }
    2083             : }
    2084             : 
    2085           0 : ssize_t lwan_request_async_writev(struct lwan_request *request,
    2086             :                                   int fd,
    2087             :                                   struct iovec *iov,
    2088             :                                   int iov_count)
    2089             : {
    2090           0 :     ssize_t total_written = 0;
    2091           0 :     int curr_iov = 0;
    2092             : 
    2093           0 :     for (int tries = 10; tries;) {
    2094           0 :         const int remaining_len = (int)(iov_count - curr_iov);
    2095             :         ssize_t written;
    2096             : 
    2097           0 :         if (remaining_len == 1) {
    2098           0 :             const struct iovec *vec = &iov[curr_iov];
    2099           0 :             return lwan_request_async_write(request, fd, vec->iov_base,
    2100           0 :                                             vec->iov_len);
    2101             :         }
    2102             : 
    2103           0 :         written = writev(fd, iov + curr_iov, (size_t)remaining_len);
    2104           0 :         if (UNLIKELY(written < 0)) {
    2105             :             /* FIXME: Consider short writes as another try as well? */
    2106           0 :             tries--;
    2107             : 
    2108           0 :             switch (errno) {
    2109           0 :             case EAGAIN:
    2110             :             case EINTR:
    2111           0 :                 goto try_again;
    2112           0 :             default:
    2113           0 :                 goto out;
    2114             :             }
    2115             :         }
    2116             : 
    2117           0 :         total_written += written;
    2118             : 
    2119           0 :         while (curr_iov < iov_count &&
    2120           0 :                written >= (ssize_t)iov[curr_iov].iov_len) {
    2121           0 :             written -= (ssize_t)iov[curr_iov].iov_len;
    2122           0 :             curr_iov++;
    2123             :         }
    2124             : 
    2125           0 :         if (curr_iov == iov_count)
    2126           0 :             return total_written;
    2127             : 
    2128           0 :         iov[curr_iov].iov_base = (char *)iov[curr_iov].iov_base + written;
    2129           0 :         iov[curr_iov].iov_len -= (size_t)written;
    2130             : 
    2131           0 :     try_again:
    2132           0 :         lwan_request_await_write(request, fd);
    2133             :     }
    2134             : 
    2135           0 : out:
    2136           0 :     coro_yield(request->conn->coro, CONN_CORO_ABORT);
    2137           0 :     __builtin_unreachable();
    2138             : }
    2139             : 
    2140           0 : void lwan_request_foreach_header_for_cgi(struct lwan_request *request,
    2141             :                                          void (*cb)(const char *header_name,
    2142             :                                                     size_t header_len,
    2143             :                                                     const char *value,
    2144             :                                                     size_t value_len,
    2145             :                                                     void *user_data),
    2146             :                                          void *user_data)
    2147             : {
    2148           0 :     struct lwan_request_parser_helper *helper = request->helper;
    2149           0 :     char **header_start = helper->header_start;
    2150           0 :     size_t n_header_start = helper->n_header_start;
    2151             : 
    2152           0 :     for (size_t i = 0; i < n_header_start; i++) {
    2153           0 :         const char *header = header_start[i];
    2154           0 :         const char *next_header = header_start[i + 1];
    2155           0 :         const char *colon = memchr(header, ':', 127 - sizeof("HTTP_: ") - 1);
    2156             :         char header_name[128];
    2157             :         int r;
    2158             : 
    2159           0 :         if (!colon)
    2160           0 :             continue;
    2161             : 
    2162           0 :         const size_t header_len = (size_t)(colon - header);
    2163           0 :         const size_t value_len = (size_t)(next_header - colon - 4);
    2164             : 
    2165           0 :         r = snprintf(header_name, sizeof(header_name), "HTTP_%.*s",
    2166             :                      (int)header_len, header);
    2167           0 :         if (r < 0 || r >= (int)sizeof(header_name))
    2168           0 :             continue;
    2169             : 
    2170             :         /* FIXME: RFC7230/RFC3875 compliance */
    2171           0 :         for (char *p = header_name; *p; p++) {
    2172           0 :             if (isalpha(*p))
    2173           0 :                 *p &= ~0x20;
    2174           0 :             else if (!isdigit(*p))
    2175           0 :                 *p = '_';
    2176             :         }
    2177             : 
    2178           0 :         if (streq(header_name, "HTTP_PROXY")) {
    2179             :             /* Mitigation for https://httpoxy.org */
    2180           0 :             continue;
    2181             :         }
    2182             : 
    2183           0 :         cb(header_name, header_len + sizeof("HTTP_") - 1, colon + 2, value_len,
    2184             :            user_data);
    2185             :     }
    2186           0 : }

Generated by: LCOV version 1.15-2-gb9d6727