LCOV - code coverage report
Current view: top level - waltz/h2 - fd_h2_hdr_match.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 35 37 94.6 %
Date: 2025-07-01 05:00:49 Functions: 3 28 10.7 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_waltz_fd_h2_hdr_match_h
       2             : #define HEADER_fd_src_waltz_fd_h2_hdr_match_h
       3             : 
       4             : /* fd_h2_hdr_match.h provides utils for building lookup tables for HTTP
       5             :    header names.
       6             : 
       7             :    Example usage:
       8             : 
       9             :      // Define a custom header ID
      10             :      #define MYAPP_HDR_FOO 1
      11             : 
      12             :      // Initialize a matcher
      13             :      ulong seed;
      14             :      static fd_h2_hdr_matcher_t matcher[1];
      15             :      fd_h2_hdr_matcher_init( matcher, seed );
      16             :      fd_h2_hdr_matcher_add_literal( matcher, MYAPP_HDR_FOO, "x-myapp-foo" );
      17             : 
      18             :      // Usage
      19             :      FD_TEST( fd_h2_hdr_match( matcher, ":authority",  10 )==FD_H2_HDR_AUTHORITY );
      20             :      FD_TEST( fd_h2_hdr_match( matcher, "x-myapp-foo", 11 )==MYAPP_HDR_FOO       );
      21             : 
      22             :    See test_h2_hdr_match.c for more examples. */
      23             : 
      24             : #include "../../ballet/siphash13/fd_siphash13.h"
      25             : #include "fd_hpack.h"
      26             : 
      27             : /* Declare an open-addressed hash table mapping header name strings to
      28             :    arbitrary IDs (usually constants provided by the user). */
      29             : 
      30             : struct __attribute__((packed)) fd_h2_hdr_match_key {
      31             :   char const * hdr;
      32             :   ushort       hdr_len;
      33             : };
      34             : 
      35             : typedef struct fd_h2_hdr_match_key fd_h2_hdr_match_key_t;
      36             : 
      37             : static inline int
      38             : fd_h2_hdr_match_key_eq( fd_h2_hdr_match_key_t k1,
      39         225 :                         fd_h2_hdr_match_key_t k2 ) {
      40         225 :   return k1.hdr_len == k2.hdr_len && fd_memeq( k1.hdr, k2.hdr, k1.hdr_len );
      41         225 : }
      42             : 
      43             : struct __attribute__((aligned(16))) fd_h2_hdr_match_entry {
      44             :   fd_h2_hdr_match_key_t key;
      45             :   short id;
      46             :   uint  hash;
      47             : };
      48             : 
      49             : typedef struct fd_h2_hdr_match_entry fd_h2_hdr_match_entry_t;
      50             : 
      51             : extern fd_h2_hdr_match_entry_t const fd_h2_hdr_match_entry_null;
      52             : 
      53        5742 : #define FD_H2_HDR_MATCH_LG_SLOT_CNT   9  /* 512 slots */
      54           3 : #define FD_H2_HDR_MATCH_MAX         300  /* target 0.7 load factor */
      55             : 
      56             : /* FIXME hack to work around lack of hash seed support in fd_map.c */
      57             : extern FD_TL ulong fd_h2_hdr_match_seed;
      58             : 
      59             : #define MAP_NAME              fd_h2_hdr_map
      60        1479 : #define MAP_T                 fd_h2_hdr_match_entry_t
      61        2664 : #define MAP_KEY_T             fd_h2_hdr_match_key_t
      62        5736 : #define MAP_KEY               key
      63        1473 : #define MAP_HASH_T            uint
      64        1740 : #define MAP_HASH              hash
      65        1473 : #define MAP_KEY_HASH(k)       (uint)fd_siphash13_hash( k.hdr, k.hdr_len, fd_h2_hdr_match_seed, 0UL )
      66             : #define MAP_MEMOIZE           1
      67        5742 : #define MAP_LG_SLOT_CNT       FD_H2_HDR_MATCH_LG_SLOT_CNT
      68        3072 : #define MAP_KEY_NULL          (fd_h2_hdr_match_key_t){0}
      69        2664 : #define MAP_KEY_INVAL(k)      ((k).hdr==NULL)
      70         225 : #define MAP_KEY_EQUAL(k1,k2)  fd_h2_hdr_match_key_eq( (k1),(k2) )
      71             : #define MAP_KEY_EQUAL_IS_SLOW 1
      72             : #include "../../util/tmpl/fd_map.c"
      73             : 
      74             : /* A h2_hdr_matcher is used to build a hash table for common header
      75             :    names.  It is primarly designed for compile-time static lists of
      76             :    headers that a user might be interested in.  The user should not
      77             :    insert arbitrary entries into the map. */
      78             : 
      79             : struct fd_h2_hdr_matcher {
      80             :   fd_h2_hdr_match_entry_t entry[ 1<<FD_H2_HDR_MATCH_LG_SLOT_CNT ];
      81             : 
      82             :   ulong seed;
      83             :   ulong entry_cnt; /* excluding HPACK entries */
      84             : };
      85             : 
      86             : typedef struct fd_h2_hdr_matcher fd_h2_hdr_matcher_t;
      87             : 
      88             : FD_PROTOTYPES_BEGIN
      89             : 
      90             : /* fd_h2_hpack_matcher maps HPACK static table indices to common header
      91             :    IDs.  For context, HTTP/2 (via HPACK) can refer to a common header
      92             :    name by a table index instead of a literal string to save space.
      93             :    Indices in range [1,61] are predefined. */
      94             : 
      95             : extern schar const __attribute__((aligned(16)))
      96             : fd_h2_hpack_matcher[ 62 ];
      97             : 
      98             : /* fd_h2_hdr_matcher_init initializes a new matcher object.  mem points
      99             :    to a memory region matching alignof(fd_h2_hdr_matcher_t) and
     100             :    sizeof(fd_h2_hdr_matcher_t).  seed is an arbitrary 64-bit value that
     101             :    permutes the hash function.  Typically, seed is chosen using
     102             :    fd_rng_secure on application startup.
     103             : 
     104             :    The map is initialized with common HTTP headers which have negative
     105             :    IDs. See FD_H2_HDR_* at the end of this header file. */
     106             : 
     107             : fd_h2_hdr_matcher_t *
     108             : fd_h2_hdr_matcher_init( void * mem,
     109             :                         ulong  seed );
     110             : 
     111             : /* fd_h2_hdr_matcher_fini destroys a matcher object, and returns the
     112             :    underlying buffer back to the caller. */
     113             : 
     114             : void *
     115             : fd_h2_hdr_matcher_fini( fd_h2_hdr_matcher_t * matcher );
     116             : 
     117             : /* fd_h2_hdr_matcher_insert adds a custom header name to the matcher.
     118             :    Calling fd_h2_hdr_match with the same name will return id.
     119             : 
     120             :    name points to an array of name_len chars (not null terminated).
     121             :    name is lowercase.  name_len is in [1,2^16).  If name was already
     122             :    added (or is part of the static list), is a no-op.
     123             : 
     124             :    id is in [1,2^15).  Aborts the application with an error log if an
     125             :    out-of-bounds value is given.
     126             : 
     127             :    Up to FD_H2_HDR_MATCH_MAX names can be added to a matcher.  Aborts
     128             :    the application with an error log if this limit is exceeded. */
     129             : 
     130             : void
     131             : fd_h2_hdr_matcher_insert( fd_h2_hdr_matcher_t * matcher,
     132             :                           int                   id,
     133             :                           char const *          name, /* static lifetime */
     134             :                           ulong                 name_len );
     135             : 
     136             : /* fd_h2_hdr_matcher_insert_literal is a safe wrapper for the above. */
     137             : 
     138             : #define fd_h2_hdr_matcher_insert_literal(matcher,id,literal) \
     139           6 :   fd_h2_hdr_matcher_insert( (matcher), (id), literal, sizeof(literal)-1 )
     140             : 
     141             : /* fd_h2_hdr_match queries the given header name in the matcher map.
     142             :    hpack_hint is the `hint` field from `fd_h2_hdr_t`, or 0 if not
     143             :    available.  name is lowercase.  name_len is in [1,2^16).
     144             : 
     145             :    Returns ...
     146             :    - Zero (FD_H2_HDR_UNKNOWN) if the given name is unknown
     147             :    - Negative (FD_H2_HDR_*) if the entry matched a HTTP/2 builtin name
     148             :    - Positive if the entry matched a value previously added with
     149             :      fd_h2_hdr_matcher_add */
     150             : 
     151             : FD_FN_PURE static inline int
     152             : fd_h2_hdr_match( fd_h2_hdr_matcher_t const * matcher,
     153             :                  char const *                name,
     154             :                  ulong                       name_len,
     155         402 :                  uint                        hpack_hint ) {
     156         402 :   if( hpack_hint & FD_H2_HDR_HINT_NAME_INDEXED ) {
     157         183 :     ulong index = hpack_hint & FD_H2_HDR_HINT_GET_INDEX( hpack_hint );
     158         183 :     if( FD_LIKELY( index && index<=61 ) ) {
     159         183 :       return (int)fd_h2_hpack_matcher[ index ];
     160         183 :     }
     161         183 :   }
     162         219 :   if( FD_UNLIKELY( !name_len ) ) return 0;
     163             : 
     164         216 :   fd_h2_hdr_match_seed = matcher->seed;
     165         216 :   fd_h2_hdr_match_key_t key = { .hdr=name, .hdr_len=(ushort)name_len };
     166         216 :   fd_h2_hdr_match_entry_t const * entry =
     167         216 :     fd_h2_hdr_map_query_const( matcher->entry, key, &fd_h2_hdr_match_entry_null );
     168         216 :   return (int)entry->id;
     169         219 : }
     170             : 
     171             : FD_PROTOTYPES_END
     172             : 
     173             : /* Define common header IDs (non-standard) */
     174             : 
     175             : // Group 1: HPACK table
     176             : #define FD_H2_HDR_UNKNOWN                         0  // *** sentinel ***
     177             : #define FD_H2_HDR_AUTHORITY                      -1  // :authority
     178             : #define FD_H2_HDR_METHOD                         -2  // :method
     179             : #define FD_H2_HDR_PATH                           -3  // :path
     180             : #define FD_H2_HDR_SCHEME                         -4  // :scheme
     181           0 : #define FD_H2_HDR_STATUS                         -5  // :status
     182             : #define FD_H2_HDR_ACCEPT_CHARSET                 -6  // accept-charset
     183             : #define FD_H2_HDR_ACCEPT_ENCODING                -7  // accept-encoding
     184             : #define FD_H2_HDR_ACCEPT_LANGUAGE                -8  // accept-language
     185             : #define FD_H2_HDR_ACCEPT_RANGES                  -9  // accept-ranges
     186             : #define FD_H2_HDR_ACCEPT                        -10  // accept
     187             : #define FD_H2_HDR_ACCESS_CONTROL_ALLOW_ORIGIN   -11  // access-control-allow-origin
     188             : #define FD_H2_HDR_AGE                           -12  // age
     189             : #define FD_H2_HDR_ALLOW                         -13  // allow
     190             : #define FD_H2_HDR_AUTHORIZATION                 -14  // authorization
     191             : #define FD_H2_HDR_CACHE_CONTROL                 -15  // cache-control
     192             : #define FD_H2_HDR_CONTENT_DISPOSITION           -16  // content-disposition
     193             : #define FD_H2_HDR_CONTENT_ENCODING              -17  // content-encoding
     194             : #define FD_H2_HDR_CONTENT_LANGUAGE              -18  // content-language
     195             : #define FD_H2_HDR_CONTENT_LENGTH                -19  // content-length
     196             : #define FD_H2_HDR_CONTENT_LOCATION              -20  // content-location
     197             : #define FD_H2_HDR_CONTENT_RANGE                 -21  // content-range
     198           0 : #define FD_H2_HDR_CONTENT_TYPE                  -22  // content-type
     199             : #define FD_H2_HDR_COOKIE                        -23  // cookie
     200             : #define FD_H2_HDR_DATE                          -24  // date
     201             : #define FD_H2_HDR_ETAG                          -25  // etag
     202             : #define FD_H2_HDR_EXPECT                        -26  // expect
     203             : #define FD_H2_HDR_EXPIRES                       -27  // expires
     204             : #define FD_H2_HDR_FROM                          -28  // from
     205             : #define FD_H2_HDR_HOST                          -29  // host
     206             : #define FD_H2_HDR_IF_MATCH                      -30  // if-match
     207             : #define FD_H2_HDR_IF_MODIFIED_SINCE             -31  // if-modified-since
     208             : #define FD_H2_HDR_IF_NONE_MATCH                 -32  // if-none-match
     209             : #define FD_H2_HDR_IF_RANGE                      -33  // if-range
     210             : #define FD_H2_HDR_IF_UNMODIFIED_SINCE           -34  // if-unmodified-since
     211             : #define FD_H2_HDR_LAST_MODIFIED                 -35  // last-modified
     212             : #define FD_H2_HDR_LINK                          -36  // link
     213             : #define FD_H2_HDR_LOCATION                      -37  // location
     214             : #define FD_H2_HDR_MAX_FORWARDS                  -38  // max-forwards
     215             : #define FD_H2_HDR_PROXY_AUTHENTICATE            -39  // proxy-authenticate
     216             : #define FD_H2_HDR_PROXY_AUTHORIZATION           -40  // proxy-authorization
     217             : #define FD_H2_HDR_RANGE                         -41  // range
     218             : #define FD_H2_HDR_REFERER                       -42  // referer
     219             : #define FD_H2_HDR_REFRESH                       -43  // refresh
     220             : #define FD_H2_HDR_RETRY_AFTER                   -44  // retry-after
     221             : #define FD_H2_HDR_SERVER                        -45  // server
     222             : #define FD_H2_HDR_SET_COOKIE                    -46  // set-cookie
     223             : #define FD_H2_HDR_STRICT_TRANSPORT_SECURITY     -47  // strict-transport-security
     224             : #define FD_H2_HDR_TRANSFER_ENCODING             -48  // transfer-encoding
     225             : #define FD_H2_HDR_USER_AGENT                    -49  // user-agent
     226             : #define FD_H2_HDR_VARY                          -50  // vary
     227             : #define FD_H2_HDR_VIA                           -51  // via
     228             : #define FD_H2_HDR_WWW_AUTHENTICATE              -52  // www-authenticate
     229             : 
     230             : // Group 2: Other common
     231           6 : #define FD_H2_SEC_WEBSOCKET_KEY                 -53  // sec-websocket-key
     232           6 : #define FD_H2_SEC_WEBSOCKET_EXTENSIONS          -54  // sec-websocket-extensions
     233           6 : #define FD_H2_SEC_WEBSOCKET_ACCEPT              -55  // sec-websocket-accept
     234           6 : #define FD_H2_SEC_WEBSOCKET_PROTOCOL            -56  // sec-websocket-protocol
     235           6 : #define FD_H2_SEC_WEBSOCKET_VERSION             -57  // sec-websocket-version
     236             : 
     237             : #endif /* HEADER_fd_src_waltz_fd_h2_hdr_match_h */
     238             : 

Generated by: LCOV version 1.14