LCOV - code coverage report
Current view: top level - lib - lwan-tables.c (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 56 69 81.2 %
Date: 2023-04-18 16:19:03 Functions: 8 10 80.0 %

          Line data    Source code
       1             : /*
       2             :  * lwan - web server
       3             :  * Copyright (c) 2012, 2013 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 <assert.h>
      22             : #include <string.h>
      23             : #include <stdlib.h>
      24             : 
      25             : #if defined(LWAN_HAVE_BROTLI)
      26             : #include <brotli/decode.h>
      27             : #elif defined(LWAN_HAVE_ZSTD)
      28             : #include <zstd.h>
      29             : #else
      30             : #include <zlib.h>
      31             : #endif
      32             : 
      33             : #include "lwan-private.h"
      34             : 
      35             : #include "mime-types.h"
      36             : 
      37             : static unsigned char uncompressed_mime_entries[MIME_UNCOMPRESSED_LEN];
      38             : static char *mime_types[MIME_ENTRIES];
      39             : static bool mime_entries_initialized = false;
      40             : 
      41          92 : void lwan_tables_init(void)
      42             : {
      43          92 :     if (mime_entries_initialized)
      44           0 :         return;
      45             : 
      46          92 :     lwan_status_debug("Uncompressing MIME type table: %u->%u bytes, %d entries",
      47             :                       MIME_COMPRESSED_LEN, MIME_UNCOMPRESSED_LEN, MIME_ENTRIES);
      48             : 
      49             : #if defined(LWAN_HAVE_BROTLI)
      50          92 :     size_t uncompressed_length = MIME_UNCOMPRESSED_LEN;
      51             :     BrotliDecoderResult ret;
      52             : 
      53          92 :     ret = BrotliDecoderDecompress(MIME_COMPRESSED_LEN, mime_entries_compressed,
      54             :                                   &uncompressed_length,
      55             :                                   uncompressed_mime_entries);
      56          92 :     if (ret != BROTLI_DECODER_RESULT_SUCCESS)
      57           0 :         lwan_status_critical("Error while uncompressing table with Brotli");
      58             : #elif defined(LWAN_HAVE_ZSTD)
      59             :     size_t uncompressed_length =
      60             :         ZSTD_decompress(uncompressed_mime_entries, MIME_UNCOMPRESSED_LEN,
      61             :                         mime_entries_compressed, MIME_COMPRESSED_LEN);
      62             :     if (ZSTD_isError(uncompressed_length))
      63             :         lwan_status_critical("Error while uncompressing table with Zstd");
      64             : #else
      65             :     uLongf uncompressed_length = MIME_UNCOMPRESSED_LEN;
      66             :     int ret =
      67             :         uncompress((Bytef *)uncompressed_mime_entries, &uncompressed_length,
      68             :                    (const Bytef *)mime_entries_compressed, MIME_COMPRESSED_LEN);
      69             :     if (ret != Z_OK) {
      70             :         lwan_status_critical("Error while uncompressing table: zlib error %d",
      71             :                              ret);
      72             :     }
      73             : #endif
      74             : 
      75          92 :     if (uncompressed_length != MIME_UNCOMPRESSED_LEN) {
      76           0 :         lwan_status_critical("Expected uncompressed length %d, got %ld",
      77             :                              MIME_UNCOMPRESSED_LEN, uncompressed_length);
      78             :     }
      79             : 
      80          92 :     unsigned char *ptr = uncompressed_mime_entries + 8 * MIME_ENTRIES;
      81       88964 :     for (size_t i = 0; i < MIME_ENTRIES; i++) {
      82       88872 :         mime_types[i] = (char *)ptr;
      83       88872 :         ptr += strlen((const char *)ptr) + 1;
      84             :     }
      85             : 
      86          92 :     mime_entries_initialized = true;
      87             : 
      88          92 :     assert(streq(lwan_determine_mime_type_for_file_name(".mkv"),
      89             :                  "video/x-matroska"));
      90          92 :     assert(streq(lwan_determine_mime_type_for_file_name(".xml"),
      91             :                  "application/xml"));
      92          92 :     assert(streq(lwan_determine_mime_type_for_file_name(".nosuchext"),
      93             :                  "application/octet-stream"));
      94          92 :     assert(streq(lwan_determine_mime_type_for_file_name("nodotinfilename"),
      95             :                  "application/octet-stream"));
      96          92 :     assert(streq(lwan_determine_mime_type_for_file_name(""),
      97             :                  "application/octet-stream"));
      98          92 :     assert(streq(lwan_determine_mime_type_for_file_name(".gif"),
      99             :                  "image/gif"));
     100          92 :     assert(streq(lwan_determine_mime_type_for_file_name(".JS"),
     101             :                  "text/javascript"));
     102          92 :     assert(streq(lwan_determine_mime_type_for_file_name(".BZ2"),
     103             :                  "application/x-bzip2"));
     104             : }
     105             : 
     106             : void
     107           0 : lwan_tables_shutdown(void)
     108             : {
     109           0 : }
     110             : 
     111             : static int
     112        3404 : compare_mime_entry(const void *a, const void *b)
     113             : {
     114        3404 :     const char *exta = (const char *)a;
     115        3404 :     const char *extb = (const char *)b;
     116             : 
     117        3404 :     return strncmp(exta, extb, 8);
     118             : }
     119             : 
     120             : const char *
     121         771 : lwan_determine_mime_type_for_file_name(const char *file_name)
     122             : {
     123         771 :     char *last_dot = strrchr(file_name, '.');
     124         771 :     if (UNLIKELY(!last_dot))
     125         194 :         goto fallback;
     126             : 
     127         577 :     STRING_SWITCH_L(last_dot) {
     128           0 :     case STR4_INT_L('.','c','s','s'): return "text/css";
     129         101 :     case STR4_INT_L('.','g','i','f'): return "image/gif";
     130          14 :     case STR4_INT_L('.','h','t','m'): return "text/html";
     131           0 :     case STR4_INT_L('.','j','p','g'): return "image/jpeg";
     132          92 :     case STR4_INT_L('.','j','s',' '): return "text/javascript";
     133           0 :     case STR4_INT_L('.','p','n','g'): return "image/png";
     134           2 :     case STR4_INT_L('.','t','x','t'): return "text/plain";
     135             :     }
     136             : 
     137         368 :     if (LIKELY(*last_dot)) {
     138             :         uint64_t key;
     139             :         const unsigned char *extension;
     140             : 
     141             : #pragma GCC diagnostic push
     142             : #pragma GCC diagnostic ignored "-Wstringop-truncation"
     143             :         /* Data is stored with NULs on strings up to 7 chars, and no NULs
     144             :          * for 8-char strings, because that's implicit.  So truncation is
     145             :          * intentional here: comparison in compare_mime_entry() uses
     146             :          * strncmp(..., 8), so even if NUL isn't present, it'll stop at the
     147             :          * right place.  */
     148         368 :         strncpy((char *)&key, last_dot + 1, 8);
     149             : #pragma GCC diagnostic pop
     150         368 :         key &= ~0x2020202020202020ull;
     151             : 
     152         368 :         extension = bsearch(&key, uncompressed_mime_entries, MIME_ENTRIES, 8,
     153             :                             compare_mime_entry);
     154         368 :         if (LIKELY(extension))
     155         276 :             return mime_types[(extension - uncompressed_mime_entries) / 8];
     156             :     }
     157             : 
     158           0 : fallback:
     159         286 :     return "application/octet-stream";
     160             : }
     161             : 
     162             : #define GENERATE_ENTRY(id, code, short, long)                                  \
     163             :     [HTTP_ ## id] = {.status = #code " " short, .description = long},
     164             : static const struct {
     165             :     const char *status;
     166             :     const char *description;
     167             : } status_table[] = {
     168             :     FOR_EACH_HTTP_STATUS(GENERATE_ENTRY)
     169             : };
     170             : #undef GENERATE_ENTRY
     171             : 
     172             : const char *
     173        1231 : lwan_http_status_as_string_with_code(enum lwan_http_status status)
     174             : {
     175        1231 :     if (LIKELY(status < N_ELEMENTS(status_table))) {
     176        1231 :         const char *ret = status_table[status].status;
     177             : 
     178        1231 :         if (LIKELY(ret))
     179        1231 :             return ret;
     180             :     }
     181             : 
     182           0 :     return "999 Invalid";
     183             : }
     184             : 
     185             : ALWAYS_INLINE const char *
     186          41 : lwan_http_status_as_string(enum lwan_http_status status)
     187             : {
     188          41 :     return lwan_http_status_as_string_with_code(status) + 4;
     189             : }
     190             : 
     191             : const char *
     192          41 : lwan_http_status_as_descriptive_string(enum lwan_http_status status)
     193             : {
     194          41 :     if (LIKELY(status < N_ELEMENTS(status_table))) {
     195          41 :         const char *ret = status_table[status].description;
     196             : 
     197          41 :         if (LIKELY(ret))
     198          41 :             return ret;
     199             :     }
     200             : 
     201           0 :     return "Invalid";
     202             : }
     203             : 
     204             : enum {
     205             :     CHAR_PROP_SPACE = 1<<0,
     206             :     CHAR_PROP_HEX = 1<<1,
     207             :     CHAR_PROP_DIG = 1<<2,
     208             : };
     209             : 
     210             : static const uint8_t char_prop_tbl[256] = {
     211             :     [' '] = CHAR_PROP_SPACE,
     212             :     ['\t'] = CHAR_PROP_SPACE,
     213             :     ['\n'] = CHAR_PROP_SPACE,
     214             :     ['\r'] = CHAR_PROP_SPACE,
     215             :     ['0'] = CHAR_PROP_HEX | CHAR_PROP_DIG,
     216             :     ['1'] = CHAR_PROP_HEX | CHAR_PROP_DIG,
     217             :     ['2'] = CHAR_PROP_HEX | CHAR_PROP_DIG,
     218             :     ['3'] = CHAR_PROP_HEX | CHAR_PROP_DIG,
     219             :     ['4'] = CHAR_PROP_HEX | CHAR_PROP_DIG,
     220             :     ['5'] = CHAR_PROP_HEX | CHAR_PROP_DIG,
     221             :     ['6'] = CHAR_PROP_HEX | CHAR_PROP_DIG,
     222             :     ['7'] = CHAR_PROP_HEX | CHAR_PROP_DIG,
     223             :     ['8'] = CHAR_PROP_HEX | CHAR_PROP_DIG,
     224             :     ['9'] = CHAR_PROP_HEX | CHAR_PROP_DIG,
     225             :     ['a'] = CHAR_PROP_HEX,
     226             :     ['b'] = CHAR_PROP_HEX,
     227             :     ['c'] = CHAR_PROP_HEX,
     228             :     ['d'] = CHAR_PROP_HEX,
     229             :     ['e'] = CHAR_PROP_HEX,
     230             :     ['f'] = CHAR_PROP_HEX,
     231             :     ['A'] = CHAR_PROP_HEX,
     232             :     ['B'] = CHAR_PROP_HEX,
     233             :     ['C'] = CHAR_PROP_HEX,
     234             :     ['D'] = CHAR_PROP_HEX,
     235             :     ['E'] = CHAR_PROP_HEX,
     236             :     ['F'] = CHAR_PROP_HEX,
     237             : };
     238             : 
     239        4668 : ALWAYS_INLINE uint8_t lwan_char_isspace(char ch)
     240             : {
     241        4668 :     return char_prop_tbl[(unsigned char)ch] & CHAR_PROP_SPACE;
     242             : }
     243             : 
     244           0 : ALWAYS_INLINE uint8_t lwan_char_isxdigit(char ch)
     245             : {
     246           0 :     return char_prop_tbl[(unsigned char)ch] & CHAR_PROP_HEX;
     247             : }
     248             : 
     249           5 : ALWAYS_INLINE uint8_t lwan_char_isdigit(char ch)
     250             : {
     251           5 :     return char_prop_tbl[(unsigned char)ch] & CHAR_PROP_DIG;
     252             : }

Generated by: LCOV version 1.15-2-gb9d6727