summaryrefslogtreecommitdiffstats
path: root/src/http2.h
blob: 7cfe46193f7b5244d50308dc9cca2580f6a3ac29 (plain)
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
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
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
/*
 * nghttp2 - HTTP/2 C Library
 *
 * Copyright (c) 2013 Tatsuhiro Tsujikawa
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#ifndef HTTP2_H
#define HTTP2_H

#include "nghttp2_config.h"

#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include <array>

#include <nghttp2/nghttp2.h>

#include "url-parser/url_parser.h"

#include "util.h"
#include "memchunk.h"
#include "template.h"
#include "allocator.h"
#include "base64.h"

namespace nghttp2 {

struct Header {
  Header(std::string name, std::string value, bool no_index = false,
         int32_t token = -1)
      : name(std::move(name)),
        value(std::move(value)),
        token(token),
        no_index(no_index) {}

  Header() : token(-1), no_index(false) {}

  bool operator==(const Header &other) const {
    return name == other.name && value == other.value;
  }

  bool operator<(const Header &rhs) const {
    return name < rhs.name || (name == rhs.name && value < rhs.value);
  }

  std::string name;
  std::string value;
  int32_t token;
  bool no_index;
};

struct HeaderRef {
  HeaderRef(const StringRef &name, const StringRef &value,
            bool no_index = false, int32_t token = -1)
      : name(name), value(value), token(token), no_index(no_index) {}

  HeaderRef() : token(-1), no_index(false) {}

  bool operator==(const HeaderRef &other) const {
    return name == other.name && value == other.value;
  }

  bool operator<(const HeaderRef &rhs) const {
    return name < rhs.name || (name == rhs.name && value < rhs.value);
  }

  StringRef name;
  StringRef value;
  int32_t token;
  bool no_index;
};

using Headers = std::vector<Header>;
using HeaderRefs = std::vector<HeaderRef>;

namespace http2 {

// Returns reason-phrase for given |status code|.  If there is no
// known reason-phrase for the given code, returns empty string.
StringRef get_reason_phrase(unsigned int status_code);

// Returns string version of |status_code|. (e.g., "404")
StringRef stringify_status(BlockAllocator &balloc, unsigned int status_code);

void capitalize(DefaultMemchunks *buf, const StringRef &s);

// Returns true if |value| is LWS
bool lws(const char *value);

// Copies the |field| component value from |u| and |url| to the
// |dest|. If |u| does not have |field|, then this function does
// nothing.
void copy_url_component(std::string &dest, const http_parser_url *u, int field,
                        const char *url);

Headers::value_type to_header(const uint8_t *name, size_t namelen,
                              const uint8_t *value, size_t valuelen,
                              bool no_index, int32_t token);

// Add name/value pairs to |nva|.  If |no_index| is true, this
// name/value pair won't be indexed when it is forwarded to the next
// hop.  This function strips white spaces around |value|.
void add_header(Headers &nva, const uint8_t *name, size_t namelen,
                const uint8_t *value, size_t valuelen, bool no_index,
                int32_t token);

// Returns pointer to the entry in |nva| which has name |name|.  If
// more than one entries which have the name |name|, last occurrence
// in |nva| is returned.  If no such entry exist, returns nullptr.
const Headers::value_type *get_header(const Headers &nva, const char *name);

// Returns true if the value of |nv| is not empty.
bool non_empty_value(const HeaderRefs::value_type *nv);

// Creates nghttp2_nv using |name| and |value| and returns it. The
// returned value only references the data pointer to name.c_str() and
// value.c_str().  If |no_index| is true, nghttp2_nv flags member has
// NGHTTP2_NV_FLAG_NO_INDEX flag set.
nghttp2_nv make_nv(const std::string &name, const std::string &value,
                   bool no_index = false);

nghttp2_nv make_nv(const StringRef &name, const StringRef &value,
                   bool no_index = false);

nghttp2_nv make_nv_nocopy(const std::string &name, const std::string &value,
                          bool no_index = false);

nghttp2_nv make_nv_nocopy(const StringRef &name, const StringRef &value,
                          bool no_index = false);

// Create nghttp2_nv from string literal |name| and |value|.
template <size_t N, size_t M>
constexpr nghttp2_nv make_nv_ll(const char (&name)[N], const char (&value)[M]) {
  return {(uint8_t *)name, (uint8_t *)value, N - 1, M - 1,
          NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE};
}

// Create nghttp2_nv from string literal |name| and c-string |value|.
template <size_t N>
nghttp2_nv make_nv_lc(const char (&name)[N], const char *value) {
  return {(uint8_t *)name, (uint8_t *)value, N - 1, strlen(value),
          NGHTTP2_NV_FLAG_NO_COPY_NAME};
}

template <size_t N>
nghttp2_nv make_nv_lc_nocopy(const char (&name)[N], const char *value) {
  return {(uint8_t *)name, (uint8_t *)value, N - 1, strlen(value),
          NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE};
}

// Create nghttp2_nv from string literal |name| and std::string
// |value|.
template <size_t N>
nghttp2_nv make_nv_ls(const char (&name)[N], const std::string &value) {
  return {(uint8_t *)name, (uint8_t *)value.c_str(), N - 1, value.size(),
          NGHTTP2_NV_FLAG_NO_COPY_NAME};
}

template <size_t N>
nghttp2_nv make_nv_ls_nocopy(const char (&name)[N], const std::string &value) {
  return {(uint8_t *)name, (uint8_t *)value.c_str(), N - 1, value.size(),
          NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE};
}

template <size_t N>
nghttp2_nv make_nv_ls_nocopy(const char (&name)[N], const StringRef &value) {
  return {(uint8_t *)name, (uint8_t *)value.c_str(), N - 1, value.size(),
          NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE};
}

enum HeaderBuildOp {
  HDOP_NONE,
  // Forwarded header fields must be stripped.  If this flag is not
  // set, all Forwarded header fields other than last one are added.
  HDOP_STRIP_FORWARDED = 1,
  // X-Forwarded-For header fields must be stripped.  If this flag is
  // not set, all X-Forwarded-For header fields other than last one
  // are added.
  HDOP_STRIP_X_FORWARDED_FOR = 1 << 1,
  // X-Forwarded-Proto header fields must be stripped.  If this flag
  // is not set, all X-Forwarded-Proto header fields other than last
  // one are added.
  HDOP_STRIP_X_FORWARDED_PROTO = 1 << 2,
  // Via header fields must be stripped.  If this flag is not set, all
  // Via header fields other than last one are added.
  HDOP_STRIP_VIA = 1 << 3,
  // Early-Data header fields must be stripped.  If this flag is not
  // set, all Early-Data header fields are added.
  HDOP_STRIP_EARLY_DATA = 1 << 4,
  // Strip above all header fields.
  HDOP_STRIP_ALL = HDOP_STRIP_FORWARDED | HDOP_STRIP_X_FORWARDED_FOR |
                   HDOP_STRIP_X_FORWARDED_PROTO | HDOP_STRIP_VIA |
                   HDOP_STRIP_EARLY_DATA,
  // Sec-WebSocket-Accept header field must be stripped.  If this flag
  // is not set, all Sec-WebSocket-Accept header fields are added.
  HDOP_STRIP_SEC_WEBSOCKET_ACCEPT = 1 << 5,
  // Sec-WebSocket-Key header field must be stripped.  If this flag is
  // not set, all Sec-WebSocket-Key header fields are added.
  HDOP_STRIP_SEC_WEBSOCKET_KEY = 1 << 6,
  // Transfer-Encoding header field must be stripped.  If this flag is
  // not set, all Transfer-Encoding header fields are added.
  HDOP_STRIP_TRANSFER_ENCODING = 1 << 7,
};

// Appends headers in |headers| to |nv|.  |headers| must be indexed
// before this call (its element's token field is assigned).  Certain
// headers, including disallowed headers in HTTP/2 spec and headers
// which require special handling (i.e. via), are not copied.  |flags|
// is one or more of HeaderBuildOp flags.  They tell function that
// certain header fields should not be added.
void copy_headers_to_nva(std::vector<nghttp2_nv> &nva,
                         const HeaderRefs &headers, uint32_t flags);

// Just like copy_headers_to_nva(), but this adds
// NGHTTP2_NV_FLAG_NO_COPY_NAME and NGHTTP2_NV_FLAG_NO_COPY_VALUE.
void copy_headers_to_nva_nocopy(std::vector<nghttp2_nv> &nva,
                                const HeaderRefs &headers, uint32_t flags);

// Appends HTTP/1.1 style header lines to |buf| from headers in
// |headers|.  |headers| must be indexed before this call (its
// element's token field is assigned).  Certain headers, which
// requires special handling (i.e. via and cookie), are not appended.
// |flags| is one or more of HeaderBuildOp flags.  They tell function
// that certain header fields should not be added.
void build_http1_headers_from_headers(DefaultMemchunks *buf,
                                      const HeaderRefs &headers,
                                      uint32_t flags);

// Return positive window_size_increment if WINDOW_UPDATE should be
// sent for the stream |stream_id|. If |stream_id| == 0, this function
// determines the necessity of the WINDOW_UPDATE for a connection.
//
// If the function determines WINDOW_UPDATE is not necessary at the
// moment, it returns -1.
int32_t determine_window_update_transmission(nghttp2_session *session,
                                             int32_t stream_id);

// Dumps name/value pairs in |nv| to |out|. The |nv| must be
// terminated by nullptr.
void dump_nv(FILE *out, const char **nv);

// Dumps name/value pairs in |nva| to |out|.
void dump_nv(FILE *out, const nghttp2_nv *nva, size_t nvlen);

// Dumps name/value pairs in |nva| to |out|.
void dump_nv(FILE *out, const Headers &nva);

void dump_nv(FILE *out, const HeaderRefs &nva);

// Ereases header in |hd|.
void erase_header(HeaderRef *hd);

// Rewrites redirection URI which usually appears in location header
// field. The |uri| is the URI in the location header field. The |u|
// stores the result of parsed |uri|. The |request_authority| is the
// host or :authority header field value in the request. The
// |upstream_scheme| is either "https" or "http" in the upstream
// interface.  Rewrite is done only if location header field value
// contains |match_host| as host excluding port.  The |match_host| and
// |request_authority| could be different.  If |request_authority| is
// empty, strip authority.
//
// This function returns the new rewritten URI on success. If the
// location URI is not subject to the rewrite, this function returns
// empty string.
StringRef rewrite_location_uri(BlockAllocator &balloc, const StringRef &uri,
                               const http_parser_url &u,
                               const StringRef &match_host,
                               const StringRef &request_authority,
                               const StringRef &upstream_scheme);

// Returns parsed HTTP status code.  Returns -1 on failure.
int parse_http_status_code(const StringRef &src);

// Header fields to be indexed, except HD_MAXIDX which is convenient
// member to get maximum value.
//
// generated by genheaderfunc.py
enum {
  HD__AUTHORITY,
  HD__HOST,
  HD__METHOD,
  HD__PATH,
  HD__PROTOCOL,
  HD__SCHEME,
  HD__STATUS,
  HD_ACCEPT_ENCODING,
  HD_ACCEPT_LANGUAGE,
  HD_ALT_SVC,
  HD_CACHE_CONTROL,
  HD_CONNECTION,
  HD_CONTENT_LENGTH,
  HD_CONTENT_TYPE,
  HD_COOKIE,
  HD_DATE,
  HD_EARLY_DATA,
  HD_EXPECT,
  HD_FORWARDED,
  HD_HOST,
  HD_HTTP2_SETTINGS,
  HD_IF_MODIFIED_SINCE,
  HD_KEEP_ALIVE,
  HD_LINK,
  HD_LOCATION,
  HD_PRIORITY,
  HD_PROXY_CONNECTION,
  HD_SEC_WEBSOCKET_ACCEPT,
  HD_SEC_WEBSOCKET_KEY,
  HD_SERVER,
  HD_TE,
  HD_TRAILER,
  HD_TRANSFER_ENCODING,
  HD_UPGRADE,
  HD_USER_AGENT,
  HD_VIA,
  HD_X_FORWARDED_FOR,
  HD_X_FORWARDED_PROTO,
  HD_MAXIDX,
};

using HeaderIndex = std::array<int16_t, HD_MAXIDX>;

// Looks up header token for header name |name| of length |namelen|.
// Only headers we are interested in are tokenized.  If header name
// cannot be tokenized, returns -1.
int lookup_token(const uint8_t *name, size_t namelen);
int lookup_token(const StringRef &name);

// Initializes |hdidx|, header index.  The |hdidx| must point to the
// array containing at least HD_MAXIDX elements.
void init_hdidx(HeaderIndex &hdidx);
// Indexes header |token| using index |idx|.
void index_header(HeaderIndex &hdidx, int32_t token, size_t idx);

// Returns header denoted by |token| using index |hdidx|.
const Headers::value_type *get_header(const HeaderIndex &hdidx, int32_t token,
                                      const Headers &nva);

Headers::value_type *get_header(const HeaderIndex &hdidx, int32_t token,
                                Headers &nva);

struct LinkHeader {
  // The region of URI.  This might not be NULL-terminated.
  StringRef uri;
};

// Returns next URI-reference in Link header field value |src|.  If no
// URI-reference found after searching all input, returned uri field
// is empty.  This imply that empty URI-reference is ignored during
// parsing.
std::vector<LinkHeader> parse_link_header(const StringRef &src);

// Constructs path by combining base path |base_path| with another
// path |rel_path|.  The base path and another path can have optional
// query component.  This function assumes |base_path| is normalized.
// In other words, it does not contain ".." or "."  path components
// and starts with "/" if it is not empty.
std::string path_join(const StringRef &base, const StringRef &base_query,
                      const StringRef &rel_path, const StringRef &rel_query);

StringRef path_join(BlockAllocator &balloc, const StringRef &base_path,
                    const StringRef &base_query, const StringRef &rel_path,
                    const StringRef &rel_query);

// true if response has body, taking into account the request method
// and status code.
bool expect_response_body(const std::string &method, int status_code);
bool expect_response_body(int method_token, int status_code);

// true if response has body, taking into account status code only.
bool expect_response_body(int status_code);

// Looks up method token for method name |name| of length |namelen|.
// Only methods defined in llhttp.h (llhttp_method) are tokenized.  If
// method name cannot be tokenized, returns -1.
int lookup_method_token(const uint8_t *name, size_t namelen);
int lookup_method_token(const StringRef &name);

// Returns string representation of |method_token|.  This is wrapper
// around llhttp_method_name from llhttp.  If |method_token| is
// unknown, program aborts.  The returned StringRef is guaranteed to
// be NULL-terminated.
StringRef to_method_string(int method_token);

StringRef normalize_path(BlockAllocator &balloc, const StringRef &path,
                         const StringRef &query);

// normalize_path_colon is like normalize_path, but it additionally
// does percent-decoding %3A in order to workaround the issue that ':'
// cannot be included in backend pattern.
StringRef normalize_path_colon(BlockAllocator &balloc, const StringRef &path,
                               const StringRef &query);

std::string normalize_path(const StringRef &path, const StringRef &query);

StringRef rewrite_clean_path(BlockAllocator &balloc, const StringRef &src);

// Returns path component of |uri|.  The returned path does not
// include query component.  This function returns empty string if it
// fails.
StringRef get_pure_path_component(const StringRef &uri);

// Deduces scheme, authority and path from given |uri|, and stores
// them in |scheme|, |authority|, and |path| respectively.  If |uri|
// is relative path, path resolution takes place using path given in
// |base| of length |baselen|.  This function returns 0 if it
// succeeds, or -1.
int construct_push_component(BlockAllocator &balloc, StringRef &scheme,
                             StringRef &authority, StringRef &path,
                             const StringRef &base, const StringRef &uri);

// Copies |src| and return its lower-cased version.
StringRef copy_lower(BlockAllocator &balloc, const StringRef &src);

// Returns true if te header field value |s| contains "trailers".
bool contains_trailers(const StringRef &s);

// Creates Sec-WebSocket-Accept value for |key|.  The capacity of
// buffer pointed by |dest| must have at least 24 bytes (base64
// encoded length of 16 bytes data).  It returns empty string in case
// of error.
StringRef make_websocket_accept_token(uint8_t *dest, const StringRef &key);

// Returns true if HTTP version represents pre-HTTP/1.1 (e.g.,
// HTTP/0.9 or HTTP/1.0).
bool legacy_http1(int major, int minor);

// Returns true if transfer-encoding field value |s| conforms RFC
// strictly.  This function does not allow empty value, BWS, and empty
// list elements.
bool check_transfer_encoding(const StringRef &s);

} // namespace http2

} // namespace nghttp2

#endif // HTTP2_H