[libsoup/carlosgc/headers-no-intern: 1/2] headers: stop interning all header names




commit 18ef5ed9166914334e0c107d251525a6d082722e
Author: Carlos Garcia Campos <cgarcia igalia com>
Date:   Wed Jun 2 15:27:33 2021 +0200

    headers: stop interning all header names
    
    To still optimize the headers handling we now follow an approach similar
    to the WebKit one. We use gperf to generate a fast conversion from
    commonly used name headers to an enum value. Headers are now stored in
    two arrays, one for common headers where the enum value is used as the
    name, and another one for uncommon headers where the name string is
    stored duplicated and g_ascii_strcasecmp() is used for comparisons. Both
    arrays are created on demand with preallocated space for 6 headers. This
    changes a bit the order in which headers are iterated, because we
    iterate the common headers first and then the uncommon ones.
    
    Fixes #111

 docs/reference/meson.build             |   2 +
 libsoup/generate-header-names.py       | 100 +++++
 libsoup/meson.build                    |   1 +
 libsoup/soup-header-names.c            | 698 +++++++++++++++++++++++++++++++++
 libsoup/soup-header-names.h            |  98 +++++
 libsoup/soup-header-names.in           |  90 +++++
 libsoup/soup-message-headers-private.h |  32 ++
 libsoup/soup-message-headers.c         | 597 ++++++++++++++++++----------
 tests/header-parsing-test.c            |  16 +-
 9 files changed, 1415 insertions(+), 219 deletions(-)
---
diff --git a/docs/reference/meson.build b/docs/reference/meson.build
index 19732d5b..70282eca 100644
--- a/docs/reference/meson.build
+++ b/docs/reference/meson.build
@@ -44,6 +44,8 @@ ignore_headers = [
   'soup-client-message-io-http2.h',
   'soup-body-input-stream-http2.h',
   'soup-tls-interaction.h',
+  'soup-header-names.h',
+  'soup-message-headers-private.h',
 ]
 
 mkdb_args = [
diff --git a/libsoup/generate-header-names.py b/libsoup/generate-header-names.py
new file mode 100755
index 00000000..da0f5b81
--- /dev/null
+++ b/libsoup/generate-header-names.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python3
+
+import sys
+import subprocess
+
+http_header_name_to_id = { }
+http_header_names = []
+with open('soup-header-names.in') as i:
+    for line in i.readlines():
+        name = line.strip();
+        if not name or name[0] == '#':
+            continue
+
+        http_header_name_to_id[name] = 'SOUP_HEADER_' + name.upper().replace('-', '_')
+        http_header_names.append (name)
+
+http_header_names.sort()
+
+gperf_file = '''%{
+/* This file has been generated with generate-header-names.py script, do not edit */
+#include "soup-header-names.h"
+#include <string.h>
+
+static const char * const soup_headr_name_strings[] = {
+'''
+
+for name in http_header_names:
+    gperf_file += '  "%s",\n' % name
+
+gperf_file += '''};
+%}
+%language=ANSI-C
+%struct-type
+struct SoupHeaderHashEntry {
+    int name;
+    SoupHeaderName header_name;
+};
+%define hash-function-name soup_header_name_hash_function
+%define lookup-function-name soup_header_name_find
+%readonly-tables
+%global-table
+%compare-strncmp
+%ignore-case
+%pic
+%%
+'''
+
+for name in http_header_names:
+    gperf_file += '%s, %s\n' % (name, http_header_name_to_id[name])
+
+gperf_file += '''%%
+SoupHeaderName soup_header_name_from_string (const char *str)
+{
+        const struct SoupHeaderHashEntry *entry;
+
+        entry = soup_header_name_find (str, strlen (str));
+        return entry ? entry->header_name : SOUP_HEADER_UNKNOWN;
+}
+
+const char *soup_header_name_to_string (SoupHeaderName name)
+{
+        if (name == SOUP_HEADER_UNKNOWN)
+                return NULL;
+
+        return soup_headr_name_strings[name];
+}
+'''
+
+command = ['gperf', '-k', '*', '-D', '-n', '-s', '2']
+p = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, 
text=True)
+output, error = p.communicate(gperf_file)
+
+if p.returncode != 0:
+    print (error)
+    sys.exit(p.returncode)
+
+with open('soup-header-names.c', 'w+') as o:
+    o.write(output.replace('const struct SoupHeaderHashEntry *', 'static const struct SoupHeaderHashEntry 
*', 1))
+
+
+output = '''/* This file has been generated with generate-header-names.py script, do not edit */
+
+#pragma once
+
+typedef enum {
+'''
+
+for name in http_header_names:
+    output += '        %s,\n' % http_header_name_to_id[name]
+
+output +='''
+        SOUP_HEADER_UNKNOWN
+} SoupHeaderName;
+
+SoupHeaderName soup_header_name_from_string (const char    *str);
+const char    *soup_header_name_to_string   (SoupHeaderName name);
+'''
+
+with open('soup-header-names.h', 'w+') as o:
+    o.write(output)
diff --git a/libsoup/meson.build b/libsoup/meson.build
index 6142ef7b..bb4417ca 100644
--- a/libsoup/meson.build
+++ b/libsoup/meson.build
@@ -65,6 +65,7 @@ soup_sources = [
   'soup-filter-input-stream.c',
   'soup-form.c',
   'soup-headers.c',
+  'soup-header-names.c',
   'soup-init.c',
   'soup-io-stream.c',
   'soup-logger.c',
diff --git a/libsoup/soup-header-names.c b/libsoup/soup-header-names.c
new file mode 100644
index 00000000..5e39a7eb
--- /dev/null
+++ b/libsoup/soup-header-names.c
@@ -0,0 +1,698 @@
+/* ANSI-C code produced by gperf version 3.1 */
+/* Command-line: gperf -k '*' -D -n -s 2  */
+
+#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
+      && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
+      && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
+      && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
+      && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
+      && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
+      && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
+      && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
+      && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
+      && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
+      && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
+      && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
+      && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
+      && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
+      && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
+      && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
+      && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
+      && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
+      && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
+      && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
+      && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
+      && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
+      && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
+/* The character set is not based on ISO-646.  */
+#error "gperf generated tables don't work with this execution character set. Please report a bug to 
<bug-gperf gnu org>."
+#endif
+
+
+/* This file has been generated with generate-header-names.py script, do not edit */
+#include "soup-header-names.h"
+#include <string.h>
+
+static const char * const soup_headr_name_strings[] = {
+  "Accept",
+  "Accept-Charset",
+  "Accept-Encoding",
+  "Accept-Language",
+  "Accept-Ranges",
+  "Access-Control-Allow-Credentials",
+  "Access-Control-Allow-Headers",
+  "Access-Control-Allow-Methods",
+  "Access-Control-Allow-Origin",
+  "Access-Control-Expose-Headers",
+  "Access-Control-Max-Age",
+  "Access-Control-Request-Headers",
+  "Access-Control-Request-Method",
+  "Age",
+  "Authentication-Info",
+  "Authorization",
+  "Cache-Control",
+  "Connection",
+  "Content-Disposition",
+  "Content-Encoding",
+  "Content-Language",
+  "Content-Length",
+  "Content-Location",
+  "Content-Range",
+  "Content-Security-Policy",
+  "Content-Security-Policy-Report-Only",
+  "Content-Type",
+  "Cookie",
+  "Cookie2",
+  "Cross-Origin-Resource-Policy",
+  "DNT",
+  "Date",
+  "Default-Style",
+  "ETag",
+  "Expect",
+  "Expires",
+  "Host",
+  "If-Match",
+  "If-Modified-Since",
+  "If-None-Match",
+  "If-Range",
+  "If-Unmodified-Since",
+  "Keep-Alive",
+  "Last-Event-ID",
+  "Last-Modified",
+  "Link",
+  "Location",
+  "Origin",
+  "Ping-From",
+  "Ping-To",
+  "Pragma",
+  "Proxy-Authenticate",
+  "Proxy-Authentication-Info",
+  "Proxy-Authorization",
+  "Purpose",
+  "Range",
+  "Referer",
+  "Referrer-Policy",
+  "Refresh",
+  "Sec-WebSocket-Accept",
+  "Sec-WebSocket-Extensions",
+  "Sec-WebSocket-Key",
+  "Sec-WebSocket-Protocol",
+  "Sec-WebSocket-Version",
+  "Server",
+  "Server-Timing",
+  "Service-Worker",
+  "Service-Worker-Allowed",
+  "Set-Cookie",
+  "Set-Cookie2",
+  "SourceMap",
+  "TE",
+  "Timing-Allow-Origin",
+  "Trailer",
+  "Transfer-Encoding",
+  "Upgrade",
+  "Upgrade-Insecure-Requests",
+  "User-Agent",
+  "Vary",
+  "Via",
+  "WWW-Authenticate",
+  "X-Content-Type-Options",
+  "X-DNS-Prefetch-Control",
+  "X-Frame-Options",
+  "X-SourceMap",
+  "X-Temp-Tablet",
+  "X-XSS-Protection",
+};
+struct SoupHeaderHashEntry {
+    int name;
+    SoupHeaderName header_name;
+};
+
+#define TOTAL_KEYWORDS 87
+#define MIN_WORD_LENGTH 2
+#define MAX_WORD_LENGTH 35
+#define MIN_HASH_VALUE 5
+#define MAX_HASH_VALUE 690
+/* maximum key range = 686, duplicates = 0 */
+
+#ifndef GPERF_DOWNCASE
+#define GPERF_DOWNCASE 1
+static unsigned char gperf_downcase[256] =
+  {
+      0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,
+     15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,
+     30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,
+     45,  46,  47,  48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,
+     60,  61,  62,  63,  64,  97,  98,  99, 100, 101, 102, 103, 104, 105, 106,
+    107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
+    122,  91,  92,  93,  94,  95,  96,  97,  98,  99, 100, 101, 102, 103, 104,
+    105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
+    120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
+    135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
+    150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
+    165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
+    180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194,
+    195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
+    210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224,
+    225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
+    240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
+    255
+  };
+#endif
+
+#ifndef GPERF_CASE_STRNCMP
+#define GPERF_CASE_STRNCMP 1
+static int
+gperf_case_strncmp (register const char *s1, register const char *s2, register size_t n)
+{
+  for (; n > 0;)
+    {
+      unsigned char c1 = gperf_downcase[(unsigned char)*s1++];
+      unsigned char c2 = gperf_downcase[(unsigned char)*s2++];
+      if (c1 != 0 && c1 == c2)
+        {
+          n--;
+          continue;
+        }
+      return (int)c1 - (int)c2;
+    }
+  return 0;
+}
+#endif
+
+#ifdef __GNUC__
+__inline
+#else
+#ifdef __cplusplus
+inline
+#endif
+#endif
+static unsigned int
+soup_header_name_hash_function (register const char *str, register size_t len)
+{
+  static const unsigned short asso_values[] =
+    {
+      691, 691, 691, 691, 691, 691, 691, 691, 691, 691,
+      691, 691, 691, 691, 691, 691, 691, 691, 691, 691,
+      691, 691, 691, 691, 691, 691, 691, 691, 691, 691,
+      691, 691, 691, 691, 691, 691, 691, 691, 691, 691,
+      691, 691, 691, 691, 691,   5, 691, 691, 691, 691,
+       40, 691, 691, 691, 691, 691, 691, 691, 691, 691,
+      691, 691, 691, 691, 691,   0,   5,   0,  45,   0,
+       95,  10,  65,   5, 691, 210,  20,  75,   5,   0,
+       60, 130,   5,  10,   5,  75,   4, 155, 165,  75,
+       35, 691, 691, 691, 691, 691, 691,   0,   5,   0,
+       45,   0,  95,  10,  65,   5, 691, 210,  20,  75,
+        5,   0,  60, 130,   5,  10,   5,  75,   4, 155,
+      165,  75,  35, 691, 691, 691, 691, 691, 691, 691,
+      691, 691, 691, 691, 691, 691, 691, 691, 691, 691,
+      691, 691, 691, 691, 691, 691, 691, 691, 691, 691,
+      691, 691, 691, 691, 691, 691, 691, 691, 691, 691,
+      691, 691, 691, 691, 691, 691, 691, 691, 691, 691,
+      691, 691, 691, 691, 691, 691, 691, 691, 691, 691,
+      691, 691, 691, 691, 691, 691, 691, 691, 691, 691,
+      691, 691, 691, 691, 691, 691, 691, 691, 691, 691,
+      691, 691, 691, 691, 691, 691, 691, 691, 691, 691,
+      691, 691, 691, 691, 691, 691, 691, 691, 691, 691,
+      691, 691, 691, 691, 691, 691, 691, 691, 691, 691,
+      691, 691, 691, 691, 691, 691, 691, 691, 691, 691,
+      691, 691, 691, 691, 691, 691, 691, 691, 691, 691,
+      691, 691, 691, 691, 691, 691
+    };
+  register unsigned int hval = 0;
+
+  switch (len)
+    {
+      default:
+        hval += asso_values[(unsigned char)str[34]];
+      /*FALLTHROUGH*/
+      case 34:
+        hval += asso_values[(unsigned char)str[33]];
+      /*FALLTHROUGH*/
+      case 33:
+        hval += asso_values[(unsigned char)str[32]];
+      /*FALLTHROUGH*/
+      case 32:
+        hval += asso_values[(unsigned char)str[31]];
+      /*FALLTHROUGH*/
+      case 31:
+        hval += asso_values[(unsigned char)str[30]];
+      /*FALLTHROUGH*/
+      case 30:
+        hval += asso_values[(unsigned char)str[29]];
+      /*FALLTHROUGH*/
+      case 29:
+        hval += asso_values[(unsigned char)str[28]];
+      /*FALLTHROUGH*/
+      case 28:
+        hval += asso_values[(unsigned char)str[27]];
+      /*FALLTHROUGH*/
+      case 27:
+        hval += asso_values[(unsigned char)str[26]];
+      /*FALLTHROUGH*/
+      case 26:
+        hval += asso_values[(unsigned char)str[25]];
+      /*FALLTHROUGH*/
+      case 25:
+        hval += asso_values[(unsigned char)str[24]];
+      /*FALLTHROUGH*/
+      case 24:
+        hval += asso_values[(unsigned char)str[23]];
+      /*FALLTHROUGH*/
+      case 23:
+        hval += asso_values[(unsigned char)str[22]];
+      /*FALLTHROUGH*/
+      case 22:
+        hval += asso_values[(unsigned char)str[21]];
+      /*FALLTHROUGH*/
+      case 21:
+        hval += asso_values[(unsigned char)str[20]];
+      /*FALLTHROUGH*/
+      case 20:
+        hval += asso_values[(unsigned char)str[19]];
+      /*FALLTHROUGH*/
+      case 19:
+        hval += asso_values[(unsigned char)str[18]];
+      /*FALLTHROUGH*/
+      case 18:
+        hval += asso_values[(unsigned char)str[17]];
+      /*FALLTHROUGH*/
+      case 17:
+        hval += asso_values[(unsigned char)str[16]];
+      /*FALLTHROUGH*/
+      case 16:
+        hval += asso_values[(unsigned char)str[15]];
+      /*FALLTHROUGH*/
+      case 15:
+        hval += asso_values[(unsigned char)str[14]];
+      /*FALLTHROUGH*/
+      case 14:
+        hval += asso_values[(unsigned char)str[13]];
+      /*FALLTHROUGH*/
+      case 13:
+        hval += asso_values[(unsigned char)str[12]];
+      /*FALLTHROUGH*/
+      case 12:
+        hval += asso_values[(unsigned char)str[11]];
+      /*FALLTHROUGH*/
+      case 11:
+        hval += asso_values[(unsigned char)str[10]];
+      /*FALLTHROUGH*/
+      case 10:
+        hval += asso_values[(unsigned char)str[9]];
+      /*FALLTHROUGH*/
+      case 9:
+        hval += asso_values[(unsigned char)str[8]];
+      /*FALLTHROUGH*/
+      case 8:
+        hval += asso_values[(unsigned char)str[7]];
+      /*FALLTHROUGH*/
+      case 7:
+        hval += asso_values[(unsigned char)str[6]];
+      /*FALLTHROUGH*/
+      case 6:
+        hval += asso_values[(unsigned char)str[5]];
+      /*FALLTHROUGH*/
+      case 5:
+        hval += asso_values[(unsigned char)str[4]];
+      /*FALLTHROUGH*/
+      case 4:
+        hval += asso_values[(unsigned char)str[3]];
+      /*FALLTHROUGH*/
+      case 3:
+        hval += asso_values[(unsigned char)str[2]];
+      /*FALLTHROUGH*/
+      case 2:
+        hval += asso_values[(unsigned char)str[1]];
+      /*FALLTHROUGH*/
+      case 1:
+        hval += asso_values[(unsigned char)str[0]];
+        break;
+    }
+  return hval;
+}
+
+struct stringpool_t
+  {
+    char stringpool_str0[sizeof("TE")];
+    char stringpool_str1[sizeof("Via")];
+    char stringpool_str2[sizeof("Age")];
+    char stringpool_str3[sizeof("ETag")];
+    char stringpool_str4[sizeof("Range")];
+    char stringpool_str5[sizeof("Server")];
+    char stringpool_str6[sizeof("Connection")];
+    char stringpool_str7[sizeof("Origin")];
+    char stringpool_str8[sizeof("Location")];
+    char stringpool_str9[sizeof("Trailer")];
+    char stringpool_str10[sizeof("Content-Range")];
+    char stringpool_str11[sizeof("Date")];
+    char stringpool_str12[sizeof("DNT")];
+    char stringpool_str13[sizeof("Content-Location")];
+    char stringpool_str14[sizeof("Accept")];
+    char stringpool_str15[sizeof("Host")];
+    char stringpool_str16[sizeof("Vary")];
+    char stringpool_str17[sizeof("Ping-To")];
+    char stringpool_str18[sizeof("Content-Encoding")];
+    char stringpool_str19[sizeof("Accept-Ranges")];
+    char stringpool_str20[sizeof("Cache-Control")];
+    char stringpool_str21[sizeof("Last-Event-ID")];
+    char stringpool_str22[sizeof("Referer")];
+    char stringpool_str23[sizeof("User-Agent")];
+    char stringpool_str24[sizeof("If-Range")];
+    char stringpool_str25[sizeof("Content-Length")];
+    char stringpool_str26[sizeof("Server-Timing")];
+    char stringpool_str27[sizeof("Accept-Encoding")];
+    char stringpool_str28[sizeof("Content-Language")];
+    char stringpool_str29[sizeof("Pragma")];
+    char stringpool_str30[sizeof("Accept-Charset")];
+    char stringpool_str31[sizeof("Content-Type")];
+    char stringpool_str32[sizeof("Content-Disposition")];
+    char stringpool_str33[sizeof("Refresh")];
+    char stringpool_str34[sizeof("Accept-Language")];
+    char stringpool_str35[sizeof("Upgrade")];
+    char stringpool_str36[sizeof("Transfer-Encoding")];
+    char stringpool_str37[sizeof("Authorization")];
+    char stringpool_str38[sizeof("Purpose")];
+    char stringpool_str39[sizeof("Cookie")];
+    char stringpool_str40[sizeof("SourceMap")];
+    char stringpool_str41[sizeof("Expect")];
+    char stringpool_str42[sizeof("Set-Cookie")];
+    char stringpool_str43[sizeof("Link")];
+    char stringpool_str44[sizeof("Expires")];
+    char stringpool_str45[sizeof("If-Match")];
+    char stringpool_str46[sizeof("Cookie2")];
+    char stringpool_str47[sizeof("Ping-From")];
+    char stringpool_str48[sizeof("If-None-Match")];
+    char stringpool_str49[sizeof("Set-Cookie2")];
+    char stringpool_str50[sizeof("Referrer-Policy")];
+    char stringpool_str51[sizeof("Authentication-Info")];
+    char stringpool_str52[sizeof("Access-Control-Allow-Origin")];
+    char stringpool_str53[sizeof("Keep-Alive")];
+    char stringpool_str54[sizeof("Last-Modified")];
+    char stringpool_str55[sizeof("Access-Control-Max-Age")];
+    char stringpool_str56[sizeof("Cross-Origin-Resource-Policy")];
+    char stringpool_str57[sizeof("Timing-Allow-Origin")];
+    char stringpool_str58[sizeof("X-Temp-Tablet")];
+    char stringpool_str59[sizeof("Default-Style")];
+    char stringpool_str60[sizeof("Access-Control-Allow-Credentials")];
+    char stringpool_str61[sizeof("Content-Security-Policy")];
+    char stringpool_str62[sizeof("Access-Control-Allow-Headers")];
+    char stringpool_str63[sizeof("X-SourceMap")];
+    char stringpool_str64[sizeof("If-Modified-Since")];
+    char stringpool_str65[sizeof("Service-Worker")];
+    char stringpool_str66[sizeof("Access-Control-Request-Headers")];
+    char stringpool_str67[sizeof("X-Content-Type-Options")];
+    char stringpool_str68[sizeof("Access-Control-Expose-Headers")];
+    char stringpool_str69[sizeof("Sec-WebSocket-Version")];
+    char stringpool_str70[sizeof("X-Frame-Options")];
+    char stringpool_str71[sizeof("X-XSS-Protection")];
+    char stringpool_str72[sizeof("Access-Control-Allow-Methods")];
+    char stringpool_str73[sizeof("Sec-WebSocket-Accept")];
+    char stringpool_str74[sizeof("Proxy-Authenticate")];
+    char stringpool_str75[sizeof("If-Unmodified-Since")];
+    char stringpool_str76[sizeof("Access-Control-Request-Method")];
+    char stringpool_str77[sizeof("Sec-WebSocket-Protocol")];
+    char stringpool_str78[sizeof("X-DNS-Prefetch-Control")];
+    char stringpool_str79[sizeof("Proxy-Authorization")];
+    char stringpool_str80[sizeof("Upgrade-Insecure-Requests")];
+    char stringpool_str81[sizeof("Content-Security-Policy-Report-Only")];
+    char stringpool_str82[sizeof("Proxy-Authentication-Info")];
+    char stringpool_str83[sizeof("Sec-WebSocket-Extensions")];
+    char stringpool_str84[sizeof("WWW-Authenticate")];
+    char stringpool_str85[sizeof("Service-Worker-Allowed")];
+    char stringpool_str86[sizeof("Sec-WebSocket-Key")];
+  };
+static const struct stringpool_t stringpool_contents =
+  {
+    "TE",
+    "Via",
+    "Age",
+    "ETag",
+    "Range",
+    "Server",
+    "Connection",
+    "Origin",
+    "Location",
+    "Trailer",
+    "Content-Range",
+    "Date",
+    "DNT",
+    "Content-Location",
+    "Accept",
+    "Host",
+    "Vary",
+    "Ping-To",
+    "Content-Encoding",
+    "Accept-Ranges",
+    "Cache-Control",
+    "Last-Event-ID",
+    "Referer",
+    "User-Agent",
+    "If-Range",
+    "Content-Length",
+    "Server-Timing",
+    "Accept-Encoding",
+    "Content-Language",
+    "Pragma",
+    "Accept-Charset",
+    "Content-Type",
+    "Content-Disposition",
+    "Refresh",
+    "Accept-Language",
+    "Upgrade",
+    "Transfer-Encoding",
+    "Authorization",
+    "Purpose",
+    "Cookie",
+    "SourceMap",
+    "Expect",
+    "Set-Cookie",
+    "Link",
+    "Expires",
+    "If-Match",
+    "Cookie2",
+    "Ping-From",
+    "If-None-Match",
+    "Set-Cookie2",
+    "Referrer-Policy",
+    "Authentication-Info",
+    "Access-Control-Allow-Origin",
+    "Keep-Alive",
+    "Last-Modified",
+    "Access-Control-Max-Age",
+    "Cross-Origin-Resource-Policy",
+    "Timing-Allow-Origin",
+    "X-Temp-Tablet",
+    "Default-Style",
+    "Access-Control-Allow-Credentials",
+    "Content-Security-Policy",
+    "Access-Control-Allow-Headers",
+    "X-SourceMap",
+    "If-Modified-Since",
+    "Service-Worker",
+    "Access-Control-Request-Headers",
+    "X-Content-Type-Options",
+    "Access-Control-Expose-Headers",
+    "Sec-WebSocket-Version",
+    "X-Frame-Options",
+    "X-XSS-Protection",
+    "Access-Control-Allow-Methods",
+    "Sec-WebSocket-Accept",
+    "Proxy-Authenticate",
+    "If-Unmodified-Since",
+    "Access-Control-Request-Method",
+    "Sec-WebSocket-Protocol",
+    "X-DNS-Prefetch-Control",
+    "Proxy-Authorization",
+    "Upgrade-Insecure-Requests",
+    "Content-Security-Policy-Report-Only",
+    "Proxy-Authentication-Info",
+    "Sec-WebSocket-Extensions",
+    "WWW-Authenticate",
+    "Service-Worker-Allowed",
+    "Sec-WebSocket-Key"
+  };
+#define stringpool ((const char *) &stringpool_contents)
+
+static const struct SoupHeaderHashEntry wordlist[] =
+  {
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str0, SOUP_HEADER_TE},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str1, SOUP_HEADER_VIA},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str2, SOUP_HEADER_AGE},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str3, SOUP_HEADER_ETAG},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str4, SOUP_HEADER_RANGE},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str5, SOUP_HEADER_SERVER},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str6, SOUP_HEADER_CONNECTION},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str7, SOUP_HEADER_ORIGIN},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str8, SOUP_HEADER_LOCATION},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str9, SOUP_HEADER_TRAILER},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str10, SOUP_HEADER_CONTENT_RANGE},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str11, SOUP_HEADER_DATE},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str12, SOUP_HEADER_DNT},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str13, SOUP_HEADER_CONTENT_LOCATION},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str14, SOUP_HEADER_ACCEPT},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str15, SOUP_HEADER_HOST},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str16, SOUP_HEADER_VARY},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str17, SOUP_HEADER_PING_TO},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str18, SOUP_HEADER_CONTENT_ENCODING},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str19, SOUP_HEADER_ACCEPT_RANGES},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str20, SOUP_HEADER_CACHE_CONTROL},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str21, SOUP_HEADER_LAST_EVENT_ID},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str22, SOUP_HEADER_REFERER},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str23, SOUP_HEADER_USER_AGENT},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str24, SOUP_HEADER_IF_RANGE},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str25, SOUP_HEADER_CONTENT_LENGTH},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str26, SOUP_HEADER_SERVER_TIMING},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str27, SOUP_HEADER_ACCEPT_ENCODING},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str28, SOUP_HEADER_CONTENT_LANGUAGE},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str29, SOUP_HEADER_PRAGMA},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str30, SOUP_HEADER_ACCEPT_CHARSET},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str31, SOUP_HEADER_CONTENT_TYPE},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str32, SOUP_HEADER_CONTENT_DISPOSITION},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str33, SOUP_HEADER_REFRESH},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str34, SOUP_HEADER_ACCEPT_LANGUAGE},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str35, SOUP_HEADER_UPGRADE},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str36, SOUP_HEADER_TRANSFER_ENCODING},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str37, SOUP_HEADER_AUTHORIZATION},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str38, SOUP_HEADER_PURPOSE},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str39, SOUP_HEADER_COOKIE},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str40, SOUP_HEADER_SOURCEMAP},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str41, SOUP_HEADER_EXPECT},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str42, SOUP_HEADER_SET_COOKIE},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str43, SOUP_HEADER_LINK},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str44, SOUP_HEADER_EXPIRES},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str45, SOUP_HEADER_IF_MATCH},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str46, SOUP_HEADER_COOKIE2},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str47, SOUP_HEADER_PING_FROM},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str48, SOUP_HEADER_IF_NONE_MATCH},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str49, SOUP_HEADER_SET_COOKIE2},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str50, SOUP_HEADER_REFERRER_POLICY},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str51, SOUP_HEADER_AUTHENTICATION_INFO},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str52, SOUP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str53, SOUP_HEADER_KEEP_ALIVE},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str54, SOUP_HEADER_LAST_MODIFIED},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str55, SOUP_HEADER_ACCESS_CONTROL_MAX_AGE},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str56, SOUP_HEADER_CROSS_ORIGIN_RESOURCE_POLICY},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str57, SOUP_HEADER_TIMING_ALLOW_ORIGIN},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str58, SOUP_HEADER_X_TEMP_TABLET},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str59, SOUP_HEADER_DEFAULT_STYLE},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str60, 
SOUP_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str61, SOUP_HEADER_CONTENT_SECURITY_POLICY},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str62, SOUP_HEADER_ACCESS_CONTROL_ALLOW_HEADERS},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str63, SOUP_HEADER_X_SOURCEMAP},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str64, SOUP_HEADER_IF_MODIFIED_SINCE},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str65, SOUP_HEADER_SERVICE_WORKER},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str66, SOUP_HEADER_ACCESS_CONTROL_REQUEST_HEADERS},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str67, SOUP_HEADER_X_CONTENT_TYPE_OPTIONS},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str68, SOUP_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str69, SOUP_HEADER_SEC_WEBSOCKET_VERSION},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str70, SOUP_HEADER_X_FRAME_OPTIONS},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str71, SOUP_HEADER_X_XSS_PROTECTION},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str72, SOUP_HEADER_ACCESS_CONTROL_ALLOW_METHODS},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str73, SOUP_HEADER_SEC_WEBSOCKET_ACCEPT},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str74, SOUP_HEADER_PROXY_AUTHENTICATE},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str75, SOUP_HEADER_IF_UNMODIFIED_SINCE},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str76, SOUP_HEADER_ACCESS_CONTROL_REQUEST_METHOD},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str77, SOUP_HEADER_SEC_WEBSOCKET_PROTOCOL},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str78, SOUP_HEADER_X_DNS_PREFETCH_CONTROL},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str79, SOUP_HEADER_PROXY_AUTHORIZATION},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str80, SOUP_HEADER_UPGRADE_INSECURE_REQUESTS},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str81, 
SOUP_HEADER_CONTENT_SECURITY_POLICY_REPORT_ONLY},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str82, SOUP_HEADER_PROXY_AUTHENTICATION_INFO},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str83, SOUP_HEADER_SEC_WEBSOCKET_EXTENSIONS},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str84, SOUP_HEADER_WWW_AUTHENTICATE},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str85, SOUP_HEADER_SERVICE_WORKER_ALLOWED},
+    {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str86, SOUP_HEADER_SEC_WEBSOCKET_KEY}
+  };
+
+static const signed char lookup[] =
+  {
+    -1, -1, -1, -1, -1,  0, -1, -1, -1,  1,  2, -1, -1, -1,
+    -1,  3, -1, -1, -1, -1,  4, -1, -1, -1,  5,  6, -1, -1,
+    -1, -1,  7, -1, -1, -1, -1,  8, -1, -1, -1, -1,  9, -1,
+    -1, -1, -1, 10, -1, -1, -1, -1, 11, -1, -1, -1, -1, 12,
+    -1, -1, -1, -1, 13, -1, -1, -1, -1, 14, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 15, -1, -1, -1,
+    16, -1, -1, -1, -1, -1, 17, -1, -1, -1, -1, 18, -1, -1,
+    -1, -1, 19, -1, -1, -1, -1, 20, -1, -1, -1, 21, 22, -1,
+    -1, -1, -1, 23, -1, -1, -1, -1, -1, -1, -1, -1, -1, 24,
+    -1, -1, -1, -1, 25, -1, -1, -1, 26, -1, -1, -1, -1, -1,
+    27, -1, -1, -1, -1, 28, -1, -1, -1, -1, 29, -1, -1, -1,
+    -1, 30, -1, -1, -1, -1, -1, -1, -1, -1, -1, 31, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, 32, -1, -1, -1, -1, 33, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, 34, -1, -1, -1, -1, 35,
+    -1, -1, -1, -1, 36, -1, -1, -1, -1, 37, -1, -1, -1, -1,
+    38, -1, -1, -1, -1, 39, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, 40, -1, -1, -1, -1, 41, -1, -1, -1, -1, 42, -1, -1,
+    -1, -1, 43, -1, -1, -1, -1, 44, -1, -1, -1, -1, 45, -1,
+    -1, -1, -1, 46, -1, -1, -1, -1, 47, -1, -1, -1, -1, 48,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, 49, -1, -1, -1, -1,
+    50, -1, -1, -1, -1, 51, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, 52, -1, -1, -1, -1, -1, -1, -1, -1, 53, -1, -1, -1,
+    -1, -1, 54, -1, -1, -1, -1, -1, -1, -1, -1, -1, 55, -1,
+    -1, -1, -1, 56, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, 57, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    58, -1, -1, -1, -1, 59, -1, -1, -1, -1, 60, -1, -1, -1,
+    -1, 61, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1,
+    -1, -1, -1, 63, -1, -1, -1, -1, 64, -1, -1, -1, 65, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    66, -1, -1, -1, -1, 67, -1, -1, -1, -1, 68, -1, -1, -1,
+    69, 70, -1, -1, -1, -1, -1, -1, -1, -1, -1, 71, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, 72, -1, -1, -1, -1, 73, -1, -1, -1, -1, 74,
+    -1, -1, -1, -1, 75, -1, -1, -1, -1, 76, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, 77, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, 78, -1, -1, -1, -1, -1, -1, -1, -1, -1, 79, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, 80, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, 81, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, 82, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, 83, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, 84, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, 85, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, 86
+  };
+
+static const struct SoupHeaderHashEntry *
+soup_header_name_find (register const char *str, register size_t len)
+{
+  if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
+    {
+      register unsigned int key = soup_header_name_hash_function (str, len);
+
+      if (key <= MAX_HASH_VALUE)
+        {
+          register int index = lookup[key];
+
+          if (index >= 0)
+            {
+              register const char *s = wordlist[index].name + stringpool;
+
+              if ((((unsigned char)*str ^ (unsigned char)*s) & ~32) == 0 && !gperf_case_strncmp (str, s, 
len) && s[len] == '\0')
+                return &wordlist[index];
+            }
+        }
+    }
+  return 0;
+}
+
+SoupHeaderName soup_header_name_from_string (const char *str)
+{
+        const struct SoupHeaderHashEntry *entry;
+
+        entry = soup_header_name_find (str, strlen (str));
+        return entry ? entry->header_name : SOUP_HEADER_UNKNOWN;
+}
+
+const char *soup_header_name_to_string (SoupHeaderName name)
+{
+        if (name == SOUP_HEADER_UNKNOWN)
+                return NULL;
+
+        return soup_headr_name_strings[name];
+}
diff --git a/libsoup/soup-header-names.h b/libsoup/soup-header-names.h
new file mode 100644
index 00000000..6a98f152
--- /dev/null
+++ b/libsoup/soup-header-names.h
@@ -0,0 +1,98 @@
+/* This file has been generated with generate-header-names.py script, do not edit */
+
+#pragma once
+
+typedef enum {
+        SOUP_HEADER_ACCEPT,
+        SOUP_HEADER_ACCEPT_CHARSET,
+        SOUP_HEADER_ACCEPT_ENCODING,
+        SOUP_HEADER_ACCEPT_LANGUAGE,
+        SOUP_HEADER_ACCEPT_RANGES,
+        SOUP_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS,
+        SOUP_HEADER_ACCESS_CONTROL_ALLOW_HEADERS,
+        SOUP_HEADER_ACCESS_CONTROL_ALLOW_METHODS,
+        SOUP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
+        SOUP_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS,
+        SOUP_HEADER_ACCESS_CONTROL_MAX_AGE,
+        SOUP_HEADER_ACCESS_CONTROL_REQUEST_HEADERS,
+        SOUP_HEADER_ACCESS_CONTROL_REQUEST_METHOD,
+        SOUP_HEADER_AGE,
+        SOUP_HEADER_AUTHENTICATION_INFO,
+        SOUP_HEADER_AUTHORIZATION,
+        SOUP_HEADER_CACHE_CONTROL,
+        SOUP_HEADER_CONNECTION,
+        SOUP_HEADER_CONTENT_DISPOSITION,
+        SOUP_HEADER_CONTENT_ENCODING,
+        SOUP_HEADER_CONTENT_LANGUAGE,
+        SOUP_HEADER_CONTENT_LENGTH,
+        SOUP_HEADER_CONTENT_LOCATION,
+        SOUP_HEADER_CONTENT_RANGE,
+        SOUP_HEADER_CONTENT_SECURITY_POLICY,
+        SOUP_HEADER_CONTENT_SECURITY_POLICY_REPORT_ONLY,
+        SOUP_HEADER_CONTENT_TYPE,
+        SOUP_HEADER_COOKIE,
+        SOUP_HEADER_COOKIE2,
+        SOUP_HEADER_CROSS_ORIGIN_RESOURCE_POLICY,
+        SOUP_HEADER_DNT,
+        SOUP_HEADER_DATE,
+        SOUP_HEADER_DEFAULT_STYLE,
+        SOUP_HEADER_ETAG,
+        SOUP_HEADER_EXPECT,
+        SOUP_HEADER_EXPIRES,
+        SOUP_HEADER_HOST,
+        SOUP_HEADER_IF_MATCH,
+        SOUP_HEADER_IF_MODIFIED_SINCE,
+        SOUP_HEADER_IF_NONE_MATCH,
+        SOUP_HEADER_IF_RANGE,
+        SOUP_HEADER_IF_UNMODIFIED_SINCE,
+        SOUP_HEADER_KEEP_ALIVE,
+        SOUP_HEADER_LAST_EVENT_ID,
+        SOUP_HEADER_LAST_MODIFIED,
+        SOUP_HEADER_LINK,
+        SOUP_HEADER_LOCATION,
+        SOUP_HEADER_ORIGIN,
+        SOUP_HEADER_PING_FROM,
+        SOUP_HEADER_PING_TO,
+        SOUP_HEADER_PRAGMA,
+        SOUP_HEADER_PROXY_AUTHENTICATE,
+        SOUP_HEADER_PROXY_AUTHENTICATION_INFO,
+        SOUP_HEADER_PROXY_AUTHORIZATION,
+        SOUP_HEADER_PURPOSE,
+        SOUP_HEADER_RANGE,
+        SOUP_HEADER_REFERER,
+        SOUP_HEADER_REFERRER_POLICY,
+        SOUP_HEADER_REFRESH,
+        SOUP_HEADER_SEC_WEBSOCKET_ACCEPT,
+        SOUP_HEADER_SEC_WEBSOCKET_EXTENSIONS,
+        SOUP_HEADER_SEC_WEBSOCKET_KEY,
+        SOUP_HEADER_SEC_WEBSOCKET_PROTOCOL,
+        SOUP_HEADER_SEC_WEBSOCKET_VERSION,
+        SOUP_HEADER_SERVER,
+        SOUP_HEADER_SERVER_TIMING,
+        SOUP_HEADER_SERVICE_WORKER,
+        SOUP_HEADER_SERVICE_WORKER_ALLOWED,
+        SOUP_HEADER_SET_COOKIE,
+        SOUP_HEADER_SET_COOKIE2,
+        SOUP_HEADER_SOURCEMAP,
+        SOUP_HEADER_TE,
+        SOUP_HEADER_TIMING_ALLOW_ORIGIN,
+        SOUP_HEADER_TRAILER,
+        SOUP_HEADER_TRANSFER_ENCODING,
+        SOUP_HEADER_UPGRADE,
+        SOUP_HEADER_UPGRADE_INSECURE_REQUESTS,
+        SOUP_HEADER_USER_AGENT,
+        SOUP_HEADER_VARY,
+        SOUP_HEADER_VIA,
+        SOUP_HEADER_WWW_AUTHENTICATE,
+        SOUP_HEADER_X_CONTENT_TYPE_OPTIONS,
+        SOUP_HEADER_X_DNS_PREFETCH_CONTROL,
+        SOUP_HEADER_X_FRAME_OPTIONS,
+        SOUP_HEADER_X_SOURCEMAP,
+        SOUP_HEADER_X_TEMP_TABLET,
+        SOUP_HEADER_X_XSS_PROTECTION,
+
+        SOUP_HEADER_UNKNOWN
+} SoupHeaderName;
+
+SoupHeaderName soup_header_name_from_string (const char    *str);
+const char    *soup_header_name_to_string   (SoupHeaderName name);
diff --git a/libsoup/soup-header-names.in b/libsoup/soup-header-names.in
new file mode 100644
index 00000000..4cae51bf
--- /dev/null
+++ b/libsoup/soup-header-names.in
@@ -0,0 +1,90 @@
+# This file is the input of script generate-header-names.py to generate
+# soup-header-names.c and soup-header-names.h. Run the script after any
+# modifification in this file to update the generated code.
+Accept
+Accept-Charset
+Accept-Language
+Accept-Encoding
+Accept-Ranges
+Access-Control-Allow-Credentials
+Access-Control-Allow-Headers
+Access-Control-Allow-Methods
+Access-Control-Allow-Origin
+Access-Control-Expose-Headers
+Access-Control-Max-Age
+Access-Control-Request-Headers
+Access-Control-Request-Method
+Age
+Authentication-Info
+Authorization
+Cache-Control
+Connection
+Content-Disposition
+Content-Encoding
+Content-Language
+Content-Length
+Content-Location
+Content-Security-Policy
+Content-Security-Policy-Report-Only
+Content-Type
+Content-Range
+Cookie
+Cookie2
+Cross-Origin-Resource-Policy
+Date
+DNT
+Default-Style
+ETag
+Expect
+Expires
+Host
+If-Match
+If-Modified-Since
+If-None-Match
+If-Range
+If-Unmodified-Since
+Keep-Alive
+Last-Event-ID
+Last-Modified
+Link
+Location
+Origin
+Ping-From
+Ping-To
+Purpose
+Pragma
+Proxy-Authorization
+Proxy-Authenticate
+Proxy-Authentication-Info
+Range
+Referer
+Referrer-Policy
+Refresh
+Sec-WebSocket-Accept
+Sec-WebSocket-Extensions
+Sec-WebSocket-Key
+Sec-WebSocket-Protocol
+Sec-WebSocket-Version
+Server
+Server-Timing
+Service-Worker
+Service-Worker-Allowed
+Set-Cookie
+Set-Cookie2
+SourceMap
+TE
+Timing-Allow-Origin
+Trailer
+Transfer-Encoding
+Upgrade
+Upgrade-Insecure-Requests
+User-Agent
+Vary
+Via
+WWW-Authenticate
+X-Content-Type-Options
+X-DNS-Prefetch-Control
+X-Frame-Options
+X-SourceMap
+X-XSS-Protection
+X-Temp-Tablet
diff --git a/libsoup/soup-message-headers-private.h b/libsoup/soup-message-headers-private.h
new file mode 100644
index 00000000..b606a331
--- /dev/null
+++ b/libsoup/soup-message-headers-private.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2021 Igalia S.L.
+ */
+
+#pragma once
+
+#include "soup-message-headers.h"
+#include "soup-header-names.h"
+
+G_BEGIN_DECLS
+
+void        soup_message_headers_append_common          (SoupMessageHeaders *hdrs,
+                                                         SoupHeaderName      name,
+                                                         const char         *value);
+const char *soup_message_headers_get_one_common         (SoupMessageHeaders *hdrs,
+                                                         SoupHeaderName      name);
+const char *soup_message_headers_get_list_common        (SoupMessageHeaders *hdrs,
+                                                         SoupHeaderName      name);
+void        soup_message_headers_remove_common          (SoupMessageHeaders *hdrs,
+                                                         SoupHeaderName      name);
+void        soup_message_headers_replace_common         (SoupMessageHeaders *hdrs,
+                                                         SoupHeaderName      name,
+                                                         const char         *value);
+gboolean    soup_message_headers_header_contains_common (SoupMessageHeaders *hdrs,
+                                                         SoupHeaderName      name,
+                                                         const char         *token);
+gboolean    soup_message_headers_header_equals_common   (SoupMessageHeaders *hdrs,
+                                                         SoupHeaderName      name,
+                                                         const char         *value);
+
+G_END_DECLS
diff --git a/libsoup/soup-message-headers.c b/libsoup/soup-message-headers.c
index 3abdb328..bf85590e 100644
--- a/libsoup/soup-message-headers.c
+++ b/libsoup/soup-message-headers.c
@@ -11,7 +11,7 @@
 
 #include <string.h>
 
-#include "soup-message-headers.h"
+#include "soup-message-headers-private.h"
 #include "soup.h"
 #include "soup-misc.h"
 
@@ -40,18 +40,25 @@
  * behaviors.
  **/
 
-typedef void (*SoupHeaderSetter) (SoupMessageHeaders *, const char *);
-static const char *intern_header_name (const char *name, SoupHeaderSetter *setter);
-static void clear_special_headers (SoupMessageHeaders *hdrs);
+static gboolean parse_content_foo (SoupMessageHeaders *hdrs,
+                                   const char         *header_name,
+                                   char              **foo,
+                                   GHashTable        **params);
+typedef struct {
+        SoupHeaderName name;
+        char *value;
+} SoupCommonHeader;
 
 typedef struct {
-       const char *name;
+       char *name;
        char *value;
-} SoupHeader;
+} SoupUncommonHeader;
 
 struct _SoupMessageHeaders {
-       GArray *array;
-       GHashTable *concat;
+        GArray *common_headers;
+        GHashTable *common_concat;
+       GArray *uncommon_headers;
+       GHashTable *uncommon_concat;
        SoupMessageHeadersType type;
 
        SoupEncoding encoding;
@@ -76,8 +83,6 @@ soup_message_headers_new (SoupMessageHeadersType type)
        SoupMessageHeaders *hdrs;
 
        hdrs = g_atomic_rc_box_new0 (SoupMessageHeaders);
-       /* FIXME: is "5" a good default? */
-       hdrs->array = g_array_sized_new (TRUE, FALSE, sizeof (SoupHeader), 5);
        hdrs->type = type;
        hdrs->encoding = -1;
 
@@ -104,8 +109,12 @@ static void
 soup_message_headers_destroy (SoupMessageHeaders *hdrs)
 {
         soup_message_headers_clear (hdrs);
-        g_array_free (hdrs->array, TRUE);
-        g_clear_pointer (&hdrs->concat, g_hash_table_destroy);
+        if (hdrs->common_headers)
+                g_array_free (hdrs->common_headers, TRUE);
+        g_clear_pointer (&hdrs->common_concat, g_hash_table_destroy);
+        if (hdrs->uncommon_headers)
+                g_array_free (hdrs->uncommon_headers, TRUE);
+        g_clear_pointer (&hdrs->uncommon_concat, g_hash_table_destroy);
 }
 
 /**
@@ -139,6 +148,71 @@ soup_message_headers_get_headers_type (SoupMessageHeaders *hdrs)
        return hdrs->type;
 }
 
+static void
+soup_message_headers_set (SoupMessageHeaders *hdrs,
+                          SoupHeaderName      name,
+                          const char         *value)
+{
+        switch (name) {
+        case SOUP_HEADER_CONTENT_LENGTH:
+                if (hdrs->encoding == SOUP_ENCODING_CHUNKED)
+                        return;
+
+                if (value) {
+                        char *end;
+
+                        hdrs->content_length = g_ascii_strtoull (value, &end, 10);
+                        if (*end)
+                                hdrs->encoding = SOUP_ENCODING_UNRECOGNIZED;
+                        else
+                                hdrs->encoding = SOUP_ENCODING_CONTENT_LENGTH;
+                } else
+                        hdrs->encoding = -1;
+                break;
+        case SOUP_HEADER_CONTENT_TYPE:
+                g_clear_pointer (&hdrs->content_type, g_free);
+                if (value) {
+                        char *content_type = NULL, *p;
+
+                        parse_content_foo (hdrs, "Content-Type", &content_type, NULL);
+                        g_assert (content_type != NULL);
+
+                        p = strpbrk (content_type, " /");
+                        if (!p || *p != '/' || strpbrk (p + 1, " /"))
+                                g_free (content_type);
+                        else
+                                hdrs->content_type = content_type;
+                }
+                break;
+        case SOUP_HEADER_EXPECT:
+                if (value) {
+                        if (!g_ascii_strcasecmp (value, "100-continue"))
+                                hdrs->expectations = SOUP_EXPECTATION_CONTINUE;
+                        else
+                                hdrs->expectations = SOUP_EXPECTATION_UNRECOGNIZED;
+                } else
+                        hdrs->expectations = 0;
+                break;
+        case SOUP_HEADER_TRANSFER_ENCODING:
+                if (value) {
+                        /* "identity" is a wrong value according to RFC errata 408,
+                         * and RFC 7230 does not list it as valid transfer-coding.
+                         * Nevertheless, the obsolete RFC 2616 stated "identity"
+                         * as valid, so we can't handle it as unrecognized here
+                         * for compatibility reasons.
+                         */
+                        if (g_ascii_strcasecmp (value, "chunked") == 0)
+                                hdrs->encoding = SOUP_ENCODING_CHUNKED;
+                        else if (g_ascii_strcasecmp (value, "identity") != 0)
+                                hdrs->encoding = SOUP_ENCODING_UNRECOGNIZED;
+                } else
+                        hdrs->encoding = -1;
+                break;
+        default:
+                break;
+        }
+}
+
 /**
  * soup_message_headers_clear:
  * @hdrs: a #SoupMessageHeaders
@@ -148,17 +222,33 @@ soup_message_headers_get_headers_type (SoupMessageHeaders *hdrs)
 void
 soup_message_headers_clear (SoupMessageHeaders *hdrs)
 {
-       SoupHeader *hdr_array = (SoupHeader *)hdrs->array->data;
        guint i;
 
-       for (i = 0; i < hdrs->array->len; i++)
-               g_free (hdr_array[i].value);
-       g_array_set_size (hdrs->array, 0);
+        if (hdrs->common_headers) {
+                SoupCommonHeader *hdr_array_common = (SoupCommonHeader *)hdrs->common_headers->data;
+
+                for (i = 0; i < hdrs->common_headers->len; i++) {
+                        g_free (hdr_array_common[i].value);
+                        soup_message_headers_set (hdrs, hdr_array_common[i].name, NULL);
+                }
+                g_array_set_size (hdrs->common_headers, 0);
+        }
+
+        if (hdrs->common_concat)
+                g_hash_table_remove_all (hdrs->common_concat);
 
-       if (hdrs->concat)
-               g_hash_table_remove_all (hdrs->concat);
+        if (hdrs->uncommon_headers) {
+                SoupUncommonHeader *hdr_array = (SoupUncommonHeader *)hdrs->uncommon_headers->data;
 
-       clear_special_headers (hdrs);
+                for (i = 0; i < hdrs->uncommon_headers->len; i++) {
+                        g_free (hdr_array[i].name);
+                        g_free (hdr_array[i].value);
+                }
+                g_array_set_size (hdrs->uncommon_headers, 0);
+        }
+
+       if (hdrs->uncommon_concat)
+               g_hash_table_remove_all (hdrs->uncommon_concat);
 }
 
 /**
@@ -185,6 +275,25 @@ soup_message_headers_clean_connection_headers (SoupMessageHeaders *hdrs)
        soup_header_free_list (tokens);
 }
 
+void
+soup_message_headers_append_common (SoupMessageHeaders *hdrs,
+                                    SoupHeaderName      name,
+                                    const char         *value)
+{
+        SoupCommonHeader header;
+
+        if (!hdrs->common_headers)
+                hdrs->common_headers = g_array_sized_new (FALSE, FALSE, sizeof (SoupCommonHeader), 6);
+
+        header.name = name;
+        header.value = g_strdup (value);
+        g_array_append_val (hdrs->common_headers, header);
+        if (hdrs->common_concat)
+                g_hash_table_remove (hdrs->common_concat, GUINT_TO_POINTER (header.name));
+
+        soup_message_headers_set (hdrs, name, value);
+}
+
 /**
  * soup_message_headers_append:
  * @hdrs: a #SoupMessageHeaders
@@ -203,8 +312,8 @@ void
 soup_message_headers_append (SoupMessageHeaders *hdrs,
                             const char *name, const char *value)
 {
-       SoupHeader header;
-       SoupHeaderSetter setter;
+       SoupUncommonHeader header;
+        SoupHeaderName header_name;
 
        g_return_if_fail (name != NULL);
        g_return_if_fail (value != NULL);
@@ -228,13 +337,29 @@ soup_message_headers_append (SoupMessageHeaders *hdrs,
        }
 #endif
 
-       header.name = intern_header_name (name, &setter);
+        header_name = soup_header_name_from_string (name);
+        if (header_name != SOUP_HEADER_UNKNOWN) {
+                soup_message_headers_append_common (hdrs, header_name, value);
+                return;
+        }
+
+        if (!hdrs->uncommon_headers)
+                hdrs->uncommon_headers = g_array_sized_new (FALSE, FALSE, sizeof (SoupUncommonHeader), 6);
+
+       header.name = g_strdup (name);
        header.value = g_strdup (value);
-       g_array_append_val (hdrs->array, header);
-       if (hdrs->concat)
-               g_hash_table_remove (hdrs->concat, header.name);
-       if (setter)
-               setter (hdrs, header.value);
+       g_array_append_val (hdrs->uncommon_headers, header);
+       if (hdrs->uncommon_concat)
+               g_hash_table_remove (hdrs->uncommon_concat, header.name);
+}
+
+void
+soup_message_headers_replace_common (SoupMessageHeaders *hdrs,
+                                     SoupHeaderName      name,
+                                     const char         *value)
+{
+        soup_message_headers_remove_common (hdrs, name);
+        soup_message_headers_append_common (hdrs, name, value);
 }
 
 /**
@@ -258,12 +383,32 @@ soup_message_headers_replace (SoupMessageHeaders *hdrs,
 }
 
 static int
-find_header (SoupHeader *hdr_array, const char *interned_name, int nth)
+find_common_header (GArray        *array,
+                    SoupHeaderName name,
+                    int            nth)
 {
+        SoupCommonHeader *hdr_array = (SoupCommonHeader *)array->data;
+        int i;
+
+        for (i = 0; i < array->len; i++) {
+                if (hdr_array[i].name == name) {
+                        if (nth-- == 0)
+                                return i;
+                }
+        }
+        return -1;
+}
+
+static int
+find_uncommon_header (GArray     *array,
+                      const char *name,
+                      int         nth)
+{
+        SoupUncommonHeader *hdr_array = (SoupUncommonHeader *)array->data;
        int i;
 
-       for (i = 0; hdr_array[i].name; i++) {
-               if (hdr_array[i].name == interned_name) {
+       for (i = 0; i < array->len; i++) {
+                if (g_ascii_strcasecmp (hdr_array[i].name, name) == 0) {
                        if (nth-- == 0)
                                return i;
                }
@@ -272,12 +417,32 @@ find_header (SoupHeader *hdr_array, const char *interned_name, int nth)
 }
 
 static int
-find_last_header (SoupHeader *hdr_array, guint length, const char *interned_name, int nth)
+find_last_common_header (GArray        *array,
+                         SoupHeaderName name,
+                         int            nth)
 {
+        SoupCommonHeader *hdr_array = (SoupCommonHeader *)array->data;
+        int i;
+
+        for (i = array->len - 1; i >= 0; i--) {
+                if (hdr_array[i].name == name) {
+                        if (nth-- == 0)
+                                return i;
+                }
+        }
+        return -1;
+}
+
+static int
+find_last_uncommon_header (GArray     *array,
+                           const char *name,
+                           int         nth)
+{
+        SoupUncommonHeader *hdr_array = (SoupUncommonHeader *)array->data;
        int i;
 
-       for (i = length; i >= 0; i--) {
-               if (hdr_array[i].name == interned_name) {
+       for (i = array->len - 1; i >= 0; i--) {
+                if (g_ascii_strcasecmp (hdr_array[i].name, name) == 0) {
                        if (nth-- == 0)
                                return i;
                }
@@ -285,6 +450,29 @@ find_last_header (SoupHeader *hdr_array, guint length, const char *interned_name
        return -1;
 }
 
+void
+soup_message_headers_remove_common (SoupMessageHeaders *hdrs,
+                                    SoupHeaderName      name)
+{
+        int index;
+
+        if (hdrs->common_headers) {
+                while ((index = find_common_header (hdrs->common_headers, name, 0)) != -1) {
+#ifndef __clang_analyzer__ /* False positive for double-free */
+                        SoupCommonHeader *hdr_array = (SoupCommonHeader *)hdrs->common_headers->data;
+
+                        g_free (hdr_array[index].value);
+#endif
+                        g_array_remove_index (hdrs->common_headers, index);
+                }
+        }
+
+        if (hdrs->common_concat)
+                g_hash_table_remove (hdrs->common_concat, GUINT_TO_POINTER (name));
+
+        soup_message_headers_set (hdrs, name, NULL);
+}
+
 /**
  * soup_message_headers_remove:
  * @hdrs: a #SoupMessageHeaders
@@ -296,23 +484,47 @@ find_last_header (SoupHeader *hdr_array, guint length, const char *interned_name
 void
 soup_message_headers_remove (SoupMessageHeaders *hdrs, const char *name)
 {
-       SoupHeader *hdr_array = (SoupHeader *)(hdrs->array->data);
-       SoupHeaderSetter setter;
        int index;
+        SoupHeaderName header_name;
 
        g_return_if_fail (name != NULL);
 
-       name = intern_header_name (name, &setter);
-       while ((index = find_header (hdr_array, name, 0)) != -1) {
+        header_name = soup_header_name_from_string (name);
+        if (header_name != SOUP_HEADER_UNKNOWN) {
+                soup_message_headers_remove_common (hdrs, header_name);
+                return;
+        }
+
+        if (hdrs->uncommon_headers) {
+                while ((index = find_uncommon_header (hdrs->uncommon_headers, name, 0)) != -1) {
 #ifndef __clang_analyzer__ /* False positive for double-free */
-               g_free (hdr_array[index].value);
+                        SoupUncommonHeader *hdr_array = (SoupUncommonHeader *)hdrs->uncommon_headers->data;
+
+                        g_free (hdr_array[index].name);
+                        g_free (hdr_array[index].value);
 #endif
-               g_array_remove_index (hdrs->array, index);
-       }
-       if (hdrs->concat)
-               g_hash_table_remove (hdrs->concat, name);
-       if (setter)
-               setter (hdrs, NULL);
+                        g_array_remove_index (hdrs->uncommon_headers, index);
+                }
+        }
+
+       if (hdrs->uncommon_concat)
+               g_hash_table_remove (hdrs->uncommon_concat, name);
+}
+
+const char *
+soup_message_headers_get_one_common (SoupMessageHeaders *hdrs,
+                                     SoupHeaderName      name)
+{
+        SoupCommonHeader *hdr_array;
+        int index;
+
+        if (!hdrs->common_headers)
+                return NULL;
+
+        hdr_array = (SoupCommonHeader *)hdrs->common_headers->data;
+        index = find_last_common_header (hdrs->common_headers, name, 0);
+
+        return index == -1 ? NULL : hdr_array[index].value;
 }
 
 /**
@@ -336,19 +548,36 @@ soup_message_headers_remove (SoupMessageHeaders *hdrs, const char *name)
 const char *
 soup_message_headers_get_one (SoupMessageHeaders *hdrs, const char *name)
 {
-       SoupHeader *hdr_array = (SoupHeader *)(hdrs->array->data);
-       guint hdr_length = hdrs->array->len;
+        SoupUncommonHeader *hdr_array;
        int index;
+        SoupHeaderName header_name;
 
        g_return_val_if_fail (name != NULL, NULL);
 
-       name = intern_header_name (name, NULL);
+        header_name = soup_header_name_from_string (name);
+        if (header_name != SOUP_HEADER_UNKNOWN)
+                return soup_message_headers_get_one_common (hdrs, header_name);
+
+        if (!hdrs->uncommon_headers)
+                return NULL;
 
-       index = find_last_header (hdr_array, hdr_length, name, 0);
+        hdr_array = (SoupUncommonHeader *)hdrs->uncommon_headers->data;
+       index = find_last_uncommon_header (hdrs->uncommon_headers, name, 0);
 
        return (index == -1) ? NULL : hdr_array[index].value;
 }
 
+gboolean
+soup_message_headers_header_contains_common (SoupMessageHeaders *hdrs,
+                                             SoupHeaderName      name,
+                                             const char         *token)
+{
+        const char *value;
+
+        value = soup_message_headers_get_list_common (hdrs, name);
+        return value ? soup_header_contains (value, token) : FALSE;
+}
+
 /**
  * soup_message_headers_header_contains:
  * @hdrs: a #SoupMessageHeaders
@@ -376,6 +605,17 @@ soup_message_headers_header_contains (SoupMessageHeaders *hdrs, const char *name
        return soup_header_contains (value, token);
 }
 
+gboolean
+soup_message_headers_header_equals_common (SoupMessageHeaders *hdrs,
+                                           SoupHeaderName      name,
+                                           const char         *value)
+{
+        const char *internal_value;
+
+        internal_value = soup_message_headers_get_list_common (hdrs, name);
+        return internal_value ? g_ascii_strcasecmp (internal_value, value) == 0 : FALSE;
+}
+
 /**
  * soup_message_headers_header_equals:
  * @hdrs: a #SoupMessageHeaders
@@ -400,6 +640,46 @@ soup_message_headers_header_equals (SoupMessageHeaders *hdrs, const char *name,
         return !g_ascii_strcasecmp (internal_value, value);
 }
 
+const char *
+soup_message_headers_get_list_common (SoupMessageHeaders *hdrs,
+                                      SoupHeaderName      name)
+{
+        SoupCommonHeader *hdr_array;
+        GString *concat;
+        char *value;
+        int index, i;
+
+        if (!hdrs->common_headers)
+                return NULL;
+
+        if (hdrs->common_concat) {
+                value = g_hash_table_lookup (hdrs->common_concat, GUINT_TO_POINTER (name));
+                if (value)
+                        return value;
+        }
+
+        hdr_array = (SoupCommonHeader *)hdrs->common_headers->data;
+        index = find_common_header (hdrs->common_headers, name, 0);
+        if (index == -1)
+                return NULL;
+
+        if (find_common_header (hdrs->common_headers, name, 1) == -1)
+                return hdr_array[index].value;
+
+        concat = g_string_new (NULL);
+        for (i = 0; (index = find_common_header (hdrs->common_headers, name, i)) != -1; i++) {
+                if (i != 0)
+                        g_string_append (concat, ", ");
+                g_string_append (concat, hdr_array[index].value);
+        }
+        value = g_string_free (concat, FALSE);
+
+        if (!hdrs->common_concat)
+                hdrs->common_concat = g_hash_table_new_full (NULL, NULL, NULL, g_free);
+        g_hash_table_insert (hdrs->common_concat, GUINT_TO_POINTER (name), value);
+        return value;
+}
+
 /**
  * soup_message_headers_get_list:
  * @hdrs: a #SoupMessageHeaders
@@ -424,37 +704,48 @@ soup_message_headers_header_equals (SoupMessageHeaders *hdrs, const char *name,
 const char *
 soup_message_headers_get_list (SoupMessageHeaders *hdrs, const char *name)
 {
-       SoupHeader *hdr_array = (SoupHeader *)(hdrs->array->data);
+        SoupUncommonHeader *hdr_array;
        GString *concat;
        char *value;
        int index, i;
+        SoupHeaderName header_name;
 
        g_return_val_if_fail (name != NULL, NULL);
 
-       name = intern_header_name (name, NULL);
-       if (hdrs->concat) {
-               value = g_hash_table_lookup (hdrs->concat, name);
+        header_name = soup_header_name_from_string (name);
+        if (header_name != SOUP_HEADER_UNKNOWN)
+                return soup_message_headers_get_list_common (hdrs, header_name);
+
+        if (!hdrs->uncommon_headers)
+                return NULL;
+
+       if (hdrs->uncommon_concat) {
+               value = g_hash_table_lookup (hdrs->uncommon_concat, name);
                if (value)
                        return value;
        }
 
-       index = find_header (hdr_array, name, 0);
+       index = find_uncommon_header (hdrs->uncommon_headers, name, 0);
        if (index == -1)
                return NULL;
-       else if (find_header (hdr_array, name, 1) == -1)
+
+        hdr_array = (SoupUncommonHeader *)hdrs->uncommon_headers->data;
+        if (find_uncommon_header (hdrs->uncommon_headers, name, 1) == -1)
                return hdr_array[index].value;
 
        concat = g_string_new (NULL);
-       for (i = 0; (index = find_header (hdr_array, name, i)) != -1; i++) {
+       for (i = 0; (index = find_uncommon_header (hdrs->uncommon_headers, name, i)) != -1; i++) {
                if (i != 0)
                        g_string_append (concat, ", ");
                g_string_append (concat, hdr_array[index].value);
        }
        value = g_string_free (concat, FALSE);
 
-       if (!hdrs->concat)
-               hdrs->concat = g_hash_table_new_full (NULL, NULL, NULL, g_free);
-       g_hash_table_insert (hdrs->concat, (gpointer)name, value);
+       if (!hdrs->uncommon_concat)
+               hdrs->uncommon_concat = g_hash_table_new_full (soup_str_case_hash,
+                                                               soup_str_case_equal,
+                                                               g_free, g_free);
+       g_hash_table_insert (hdrs->uncommon_concat, g_strdup (name), value);
        return value;
 }
 
@@ -473,7 +764,8 @@ soup_message_headers_get_list (SoupMessageHeaders *hdrs, const char *name)
 
 typedef struct {
        SoupMessageHeaders *hdrs;
-       int index;
+        int index_common;
+       int index_uncommon;
 } SoupMessageHeadersIterReal;
 
 /**
@@ -491,7 +783,8 @@ soup_message_headers_iter_init (SoupMessageHeadersIter *iter,
        SoupMessageHeadersIterReal *real = (SoupMessageHeadersIterReal *)iter;
 
        real->hdrs = hdrs;
-       real->index = 0;
+        real->index_common = 0;
+       real->index_uncommon = 0;
 }
 
 /**
@@ -515,15 +808,28 @@ soup_message_headers_iter_next (SoupMessageHeadersIter *iter,
                                const char **name, const char **value)
 {
        SoupMessageHeadersIterReal *real = (SoupMessageHeadersIterReal *)iter;
-       SoupHeader *hdr_array = (SoupHeader *)real->hdrs->array->data;
 
-       if (real->index >= real->hdrs->array->len)
-               return FALSE;
+        if (real->hdrs->common_headers &&
+            real->index_common < real->hdrs->common_headers->len) {
+                SoupCommonHeader *hdr_array = (SoupCommonHeader *)real->hdrs->common_headers->data;
 
-       *name = hdr_array[real->index].name;
-       *value = hdr_array[real->index].value;
-       real->index++;
-       return TRUE;
+                *name = soup_header_name_to_string (hdr_array[real->index_common].name);
+                *value = hdr_array[real->index_common].value;
+                real->index_common++;
+                return TRUE;
+        }
+
+        if (real->hdrs->uncommon_headers &&
+            real->index_uncommon < real->hdrs->uncommon_headers->len) {
+                SoupUncommonHeader *hdr_array = (SoupUncommonHeader *)real->hdrs->uncommon_headers->data;
+
+                *name = hdr_array[real->index_uncommon].name;
+                *value = hdr_array[real->index_uncommon].value;
+                real->index_uncommon++;
+                return TRUE;
+        }
+
+        return FALSE;
 }
 
 /**
@@ -555,128 +861,29 @@ soup_message_headers_iter_next (SoupMessageHeadersIter *iter,
  * You may not modify the headers from @func.
  **/
 void
-soup_message_headers_foreach (SoupMessageHeaders *hdrs,
+soup_message_headers_foreach (SoupMessageHeaders           *hdrs,
                              SoupMessageHeadersForeachFunc func,
-                             gpointer            user_data)
+                             gpointer                      user_data)
 {
-       SoupHeader *hdr_array = (SoupHeader *)hdrs->array->data;
        guint i;
 
-       for (i = 0; i < hdrs->array->len; i++)
-               func (hdr_array[i].name, hdr_array[i].value, user_data);
-}
+        if (hdrs->common_headers) {
+                SoupCommonHeader *hdr_array = (SoupCommonHeader *)hdrs->common_headers->data;
 
+                for (i = 0; i < hdrs->common_headers->len; i++)
+                        func (soup_header_name_to_string (hdr_array[i].name), hdr_array[i].value, user_data);
+        }
 
-G_LOCK_DEFINE_STATIC (header_pool);
-static GHashTable *header_pool, *header_setters;
+        if (hdrs->uncommon_headers) {
+                SoupUncommonHeader *hdr_array = (SoupUncommonHeader *)hdrs->uncommon_headers->data;
 
-static void transfer_encoding_setter (SoupMessageHeaders *, const char *);
-static void content_length_setter (SoupMessageHeaders *, const char *);
-static void expectation_setter (SoupMessageHeaders *, const char *);
-static void content_type_setter (SoupMessageHeaders *, const char *);
-
-static char *
-intern_header_locked (const char *name)
-{
-       char *interned;
-
-       interned = g_hash_table_lookup (header_pool, name);
-       if (!interned) {
-               char *dup = g_strdup (name);
-               g_hash_table_insert (header_pool, dup, dup);
-               interned = dup;
-       }
-       return interned;
-}
-
-static const char *
-intern_header_name (const char *name, SoupHeaderSetter *setter)
-{
-       const char *interned;
-
-       G_LOCK (header_pool);
-
-       if (!header_pool) {
-               header_pool = g_hash_table_new (soup_str_case_hash, soup_str_case_equal);
-               header_setters = g_hash_table_new (NULL, NULL);
-               g_hash_table_insert (header_setters,
-                                    intern_header_locked ("Transfer-Encoding"),
-                                    transfer_encoding_setter);
-               g_hash_table_insert (header_setters,
-                                    intern_header_locked ("Content-Length"),
-                                    content_length_setter);
-               g_hash_table_insert (header_setters,
-                                    intern_header_locked ("Expect"),
-                                    expectation_setter);
-               g_hash_table_insert (header_setters,
-                                    intern_header_locked ("Content-Type"),
-                                    content_type_setter);
-       }
-
-       interned = intern_header_locked (name);
-       if (setter)
-               *setter = g_hash_table_lookup (header_setters, interned);
-
-       G_UNLOCK (header_pool);
-       return interned;
-}
-
-static void
-clear_special_headers (SoupMessageHeaders *hdrs)
-{
-       SoupHeaderSetter setter;
-       GHashTableIter iter;
-       gpointer key, value;
-
-       /* Make sure header_setters has been initialized */
-       intern_header_name ("", NULL);
-
-       g_hash_table_iter_init (&iter, header_setters);
-       while (g_hash_table_iter_next (&iter, &key, &value)) {
-               setter = value;
-               setter (hdrs, NULL);
-       }
+                for (i = 0; i < hdrs->uncommon_headers->len; i++)
+                        func (hdr_array[i].name, hdr_array[i].value, user_data);
+        }
 }
 
 /* Specific headers */
 
-static void
-transfer_encoding_setter (SoupMessageHeaders *hdrs, const char *value)
-{
-       if (value) {
-               /* "identity" is a wrong value according to RFC errata 408,
-                * and RFC 7230 does not list it as valid transfer-coding.
-                * Nevertheless, the obsolete RFC 2616 stated "identity"
-                * as valid, so we can't handle it as unrecognized here
-                * for compatibility reasons.
-                */
-               if (g_ascii_strcasecmp (value, "chunked") == 0)
-                       hdrs->encoding = SOUP_ENCODING_CHUNKED;
-               else if (g_ascii_strcasecmp (value, "identity") != 0)
-                       hdrs->encoding = SOUP_ENCODING_UNRECOGNIZED;
-       } else
-               hdrs->encoding = -1;
-}
-
-static void
-content_length_setter (SoupMessageHeaders *hdrs, const char *value)
-{
-       /* Transfer-Encoding trumps Content-Length */
-       if (hdrs->encoding == SOUP_ENCODING_CHUNKED)
-               return;
-
-       if (value) {
-               char *end;
-
-               hdrs->content_length = g_ascii_strtoull (value, &end, 10);
-               if (*end)
-                       hdrs->encoding = SOUP_ENCODING_UNRECOGNIZED;
-               else
-                       hdrs->encoding = SOUP_ENCODING_CONTENT_LENGTH;
-       } else
-               hdrs->encoding = -1;
-}
-
 /**
  * SoupEncoding:
  * @SOUP_ENCODING_UNRECOGNIZED: unknown / error
@@ -716,7 +923,7 @@ soup_message_headers_get_encoding (SoupMessageHeaders *hdrs)
         */
        header = soup_message_headers_get_one (hdrs, "Content-Length");
        if (header) {
-               content_length_setter (hdrs, header);
+                soup_message_headers_set (hdrs, SOUP_HEADER_CONTENT_LENGTH, header);
                if (hdrs->encoding != -1)
                        return hdrs->encoding;
        }
@@ -823,18 +1030,6 @@ soup_message_headers_set_content_length (SoupMessageHeaders *hdrs,
        soup_message_headers_replace (hdrs, "Content-Length", length);
 }
 
-static void
-expectation_setter (SoupMessageHeaders *hdrs, const char *value)
-{
-       if (value) {
-               if (!g_ascii_strcasecmp (value, "100-continue"))
-                       hdrs->expectations = SOUP_EXPECTATION_CONTINUE;
-               else
-                       hdrs->expectations = SOUP_EXPECTATION_UNRECOGNIZED;
-       } else
-               hdrs->expectations = 0;
-}
-
 /**
  * SoupExpectation:
  * @SOUP_EXPECTATION_CONTINUE: "100-continue"
@@ -1299,26 +1494,6 @@ set_content_foo (SoupMessageHeaders *hdrs, const char *header_name,
        g_string_free (str, TRUE);
 }
 
-static void
-content_type_setter (SoupMessageHeaders *hdrs, const char *value)
-{
-       g_free (hdrs->content_type);
-       hdrs->content_type = NULL;
-
-       if (value) {
-               char *content_type = NULL, *p;
-
-               parse_content_foo (hdrs, "Content-Type", &content_type, NULL);
-               g_return_if_fail (content_type != NULL);
-
-               p = strpbrk (content_type, " /");
-               if (!p || *p != '/' || strpbrk (p + 1, " /"))
-                       g_free (content_type);
-               else
-                       hdrs->content_type = content_type;
-       }
-}
-
 /**
  * soup_message_headers_get_content_type:
  * @hdrs: a #SoupMessageHeaders
diff --git a/tests/header-parsing-test.c b/tests/header-parsing-test.c
index b7c46ea0..0e5f252a 100644
--- a/tests/header-parsing-test.c
+++ b/tests/header-parsing-test.c
@@ -124,8 +124,8 @@ static struct RequestTest {
          "GET / HTTP/1.1\r\nFoo: bar\r\n baz\r\nConnection: close\r\nBlah: blah\r\n", -1,
          SOUP_STATUS_OK,
          "GET", "/", SOUP_HTTP_1_1,
-         { { "Foo", "bar baz" },
-           { "Connection", "close" },
+         { { "Connection", "close" },
+            { "Foo", "bar baz" },
            { "Blah", "blah" },
            { NULL }
          }
@@ -166,8 +166,8 @@ static struct RequestTest {
          "GET / HTTP/1.0\r\nFoo: bar\r\nConnection: Bar, Quux\r\nBar: baz\r\nQuux: foo\r\n", -1,
          SOUP_STATUS_OK,
          "GET", "/", SOUP_HTTP_1_0,
-         { { "Foo", "bar" },
-           { "Connection", "Bar, Quux" },
+         { { "Connection", "Bar, Quux" },
+            { "Foo", "bar" },
            { NULL }
          }
        },
@@ -321,8 +321,8 @@ static struct RequestTest {
          "GET / HTTP/1.1\r\na: b\r\nHost: example\rcom\r\np: \rq\r\ns: t\r\r\nc: d\r\n", -1,
          SOUP_STATUS_OK,
          "GET", "/", SOUP_HTTP_1_1,
-         { { "a", "b" },
-           { "Host", "example com" },  /* CR in the middle turns to space */
+         { { "Host", "example com" },  /* CR in the middle turns to space */
+            { "a", "b" },
            { "p", "q" },               /* CR at beginning is ignored */
            { "s", "t" },               /* CR at end is ignored */
            { "c", "d" },
@@ -528,8 +528,8 @@ static struct ResponseTest {
        { "Connection header on HTTP/1.0 message", NULL,
          "HTTP/1.0 200 ok\r\nFoo: bar\r\nConnection: Bar\r\nBar: quux\r\n", -1,
          SOUP_HTTP_1_0, SOUP_STATUS_OK, "ok",
-         { { "Foo", "bar" },
-           { "Connection", "Bar" },
+         { { "Connection", "Bar" },
+            { "Foo", "bar" },
            { NULL }
          }
        },


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]