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 :