Line data Source code
1 : #ifndef HEADER_fd_src_waltz_h2_fd_hpack_wr_h
2 : #define HEADER_fd_src_waltz_h2_fd_hpack_wr_h
3 :
4 : /* fd_hpack_wr.h provides simple APIs to generate HPACK header entries.
5 : Generates somewhat wasteful serializations, but is quite simple. */
6 :
7 : #include "fd_h2_base.h"
8 : #include "fd_h2_rbuf.h"
9 :
10 12 : #define FD_HPACK_INDEXED_SHORT( val ) ((uchar)( 0x80|(val) ))
11 :
12 : #if FD_HAS_X86
13 : #include <immintrin.h>
14 : #endif
15 :
16 : FD_PROTOTYPES_BEGIN
17 :
18 : static inline ulong
19 : fd_hpack_wr_varint(
20 : uchar code[9],
21 : uint prefix,
22 : uint addend,
23 : ulong number /* in [0,2^56) */
24 57 : ) {
25 57 : ulong sz;
26 57 : if( number<addend ) {
27 42 : code[0] = (uchar)( prefix|number );
28 42 : sz = 1UL;
29 42 : } else {
30 15 : code[0] = (uchar)( prefix|addend );
31 15 : ulong tail = number-addend;
32 15 : #if FD_HAS_X86 && defined(__BMI2__)
33 15 : ulong enc = _pdep_u64( tail, 0x7f7f7f7f7f7f7f7fUL );
34 : #else
35 : ulong enc =
36 : ( ( tail<<0 )&0x000000000000007fUL ) |
37 : ( ( tail<<1 )&0x0000000000007f00UL ) |
38 : ( ( tail<<2 )&0x00000000007f0000UL ) |
39 : ( ( tail<<3 )&0x000000007f000000UL ) |
40 : ( ( tail<<4 )&0x0000007f00000000UL ) |
41 : ( ( tail<<5 )&0x00007f0000000000UL ) |
42 : ( ( tail<<6 )&0x007f000000000000UL ) |
43 : ( ( tail<<7 )&0x7f00000000000000UL );
44 : #endif
45 15 : int msb = fd_ulong_find_msb_w_default( enc, 0 );
46 15 : int shift = 64-(msb&0x38);
47 15 : ulong mask = shift==64 ? 0UL : 0x8080808080808080UL>>shift;
48 15 : FD_STORE( ulong, code+1, enc|mask );
49 15 : sz = 2 + (ulong)( msb>>3 );
50 15 : }
51 57 : return sz;
52 57 : }
53 :
54 : static inline int
55 : fd_hpack_wr_private_indexed( fd_h2_rbuf_t * rbuf_tx,
56 12 : ulong idx ) {
57 12 : if( FD_UNLIKELY( !fd_h2_rbuf_free_sz( rbuf_tx ) ) ) return 0;
58 12 : uchar code[1] = { FD_HPACK_INDEXED_SHORT( idx ) };
59 12 : fd_h2_rbuf_push( rbuf_tx, code, 1 );
60 12 : return 1;
61 12 : }
62 :
63 : static inline int
64 : fd_hpack_wr_private_name_indexed_0(
65 : fd_h2_rbuf_t * rbuf_tx,
66 : ulong key,
67 : ulong value_len /* in [0,128) */
68 12 : ) {
69 12 : uchar prefix[10] = { (uchar)key };
70 12 : if( FD_UNLIKELY( fd_h2_rbuf_free_sz( rbuf_tx ) < sizeof(prefix)+value_len ) ) return 0;
71 12 : ulong prefix_len = 1+fd_hpack_wr_varint( prefix+1, 0x00, 0x7f, value_len );
72 12 : fd_h2_rbuf_push( rbuf_tx, prefix, prefix_len );
73 12 : return 1;
74 12 : }
75 :
76 : static inline int
77 6 : fd_hpack_wr_method_post( fd_h2_rbuf_t * rbuf_tx ) {
78 6 : return fd_hpack_wr_private_indexed( rbuf_tx, 0x03 );
79 6 : }
80 :
81 : static inline int
82 : fd_hpack_wr_scheme( fd_h2_rbuf_t * rbuf_tx,
83 6 : int is_https ) {
84 6 : if( is_https ) {
85 6 : return fd_hpack_wr_private_indexed( rbuf_tx, 0x07 );
86 6 : } else {
87 0 : return fd_hpack_wr_private_indexed( rbuf_tx, 0x06 );
88 0 : }
89 6 : }
90 :
91 : /* fd_hpack_wr_path writes a ':path: ...' header. path_len must be in
92 : [0,128). */
93 :
94 : static inline int
95 : fd_hpack_wr_path( fd_h2_rbuf_t * rbuf_tx,
96 : char const * path,
97 6 : ulong path_len ) {
98 6 : if( FD_UNLIKELY( !fd_hpack_wr_private_name_indexed_0( rbuf_tx, 0x04, path_len ) ) ) return 0;
99 6 : fd_h2_rbuf_push( rbuf_tx, path, path_len );
100 6 : return 1;
101 6 : }
102 :
103 : /* fd_hpack_wr_trailers writes the 'te: trailers' header. */
104 :
105 : static inline int
106 6 : fd_hpack_wr_trailers( fd_h2_rbuf_t * rbuf_tx ) {
107 6 : static char const code[] =
108 6 : "\x00"
109 6 : "\x02" "te"
110 6 : "\x08" "trailers";
111 6 : if( FD_UNLIKELY( fd_h2_rbuf_free_sz( rbuf_tx )<sizeof(code)-1 ) ) return 0;
112 6 : fd_h2_rbuf_push( rbuf_tx, code, sizeof(code)-1 );
113 6 : return 1;
114 6 : }
115 :
116 : /* fd_hpack_wr_user_agent writes a 'user-agent' header.
117 : user_agent_len is in [0,128). */
118 :
119 : static inline int
120 : fd_hpack_wr_user_agent( fd_h2_rbuf_t * rbuf_tx,
121 6 : ulong user_agent_len ) {
122 6 : return fd_hpack_wr_private_name_indexed_0( rbuf_tx, 0x7a, user_agent_len );
123 6 : }
124 :
125 : /* fd_hpack_wr_auth_bearer writes an 'authorization: Bearer xxx' header.
126 : Uses a never-indexed literal to prevent compression attacks. */
127 :
128 : static inline int
129 : fd_hpack_wr_auth_bearer( fd_h2_rbuf_t * rbuf_tx,
130 : char const * auth_token,
131 3 : ulong auth_token_len ) {
132 3 : uchar prefix[11] = { 0x1f, 0x08 };
133 3 : ulong value_len = 7UL+auth_token_len;
134 3 : if( FD_UNLIKELY( fd_h2_rbuf_free_sz( rbuf_tx ) < sizeof(prefix)+value_len ) ) return 0;
135 3 : ulong prefix_len = 2+fd_hpack_wr_varint( prefix+2, 0x00, 0x7f, value_len );
136 3 : fd_h2_rbuf_push( rbuf_tx, prefix, prefix_len );
137 3 : fd_h2_rbuf_push( rbuf_tx, "Bearer ", 7 );
138 3 : fd_h2_rbuf_push( rbuf_tx, auth_token, auth_token_len );
139 3 : return 1;
140 3 : }
141 :
142 : /* fd_hpack_wr_authority writes an ':authority: host[:port]' header.
143 : Port is only specified if it is non-zero. */
144 :
145 : static inline int
146 : fd_hpack_wr_authority( fd_h2_rbuf_t * rbuf_tx,
147 : char const * host,
148 : ulong host_len,
149 6 : ushort port ) {
150 6 : char suffix_cstr[ 7 ];
151 6 : ulong port_cstr_len = fd_ushort_base10_dig_cnt( port );
152 6 : char * p = fd_cstr_init( suffix_cstr );
153 6 : p = fd_cstr_append_char( p, ':' );
154 6 : p = fd_cstr_append_ushort_as_text( p, 0, 0, port, port_cstr_len );
155 6 : ulong suffix_len = (ulong)p - (ulong)suffix_cstr;
156 6 : fd_cstr_fini( p );
157 :
158 : //if( !port ) suffix_len = 0;
159 6 : ulong value_len = host_len+suffix_len;
160 :
161 6 : uchar prefix[10] = { (uchar)0x01 };
162 6 : if( FD_UNLIKELY( fd_h2_rbuf_free_sz( rbuf_tx ) < sizeof(prefix)+value_len ) ) return 0;
163 6 : ulong prefix_len = 1+fd_hpack_wr_varint( prefix+1, 0x00, 0x7f, value_len );
164 6 : fd_h2_rbuf_push( rbuf_tx, prefix, prefix_len );
165 6 : fd_h2_rbuf_push( rbuf_tx, host, host_len );
166 6 : fd_h2_rbuf_push( rbuf_tx, suffix_cstr, suffix_len );
167 6 : return 1;
168 6 : }
169 :
170 : FD_PROTOTYPES_END
171 :
172 : #endif /* HEADER_fd_src_waltz_h2_fd_hpack_wr_h */
|