• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * nghttp2 - HTTP/2 C Library
3  *
4  * Copyright (c) 2013 Tatsuhiro Tsujikawa
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25 #ifndef HTTP2_H
26 #define HTTP2_H
27 
28 #include "nghttp2_config.h"
29 
30 #include <cstdio>
31 #include <cstring>
32 #include <string>
33 #include <vector>
34 #include <array>
35 
36 #include <nghttp2/nghttp2.h>
37 
38 #include "url-parser/url_parser.h"
39 
40 #include "util.h"
41 #include "memchunk.h"
42 #include "template.h"
43 #include "allocator.h"
44 #include "base64.h"
45 
46 namespace nghttp2 {
47 
48 struct Header {
49   Header(std::string name, std::string value, bool no_index = false,
50          int32_t token = -1)
nameHeader51     : name(std::move(name)),
52       value(std::move(value)),
53       token(token),
54       no_index(no_index) {}
55 
HeaderHeader56   Header() : token(-1), no_index(false) {}
57 
58   bool operator==(const Header &other) const {
59     return name == other.name && value == other.value;
60   }
61 
62   bool operator<(const Header &rhs) const {
63     return name < rhs.name || (name == rhs.name && value < rhs.value);
64   }
65 
66   std::string name;
67   std::string value;
68   int32_t token;
69   bool no_index;
70 };
71 
72 struct HeaderRef {
73   HeaderRef(const StringRef &name, const StringRef &value,
74             bool no_index = false, int32_t token = -1)
nameHeaderRef75     : name(name), value(value), token(token), no_index(no_index) {}
76 
HeaderRefHeaderRef77   HeaderRef() : token(-1), no_index(false) {}
78 
79   bool operator==(const HeaderRef &other) const {
80     return name == other.name && value == other.value;
81   }
82 
83   bool operator<(const HeaderRef &rhs) const {
84     return name < rhs.name || (name == rhs.name && value < rhs.value);
85   }
86 
87   StringRef name;
88   StringRef value;
89   int32_t token;
90   bool no_index;
91 };
92 
93 using Headers = std::vector<Header>;
94 using HeaderRefs = std::vector<HeaderRef>;
95 
96 namespace http2 {
97 
98 // Returns reason-phrase for given |status code|.  If there is no
99 // known reason-phrase for the given code, returns empty string.
100 StringRef get_reason_phrase(unsigned int status_code);
101 
102 // Returns string version of |status_code|. (e.g., "404")
103 StringRef stringify_status(BlockAllocator &balloc, unsigned int status_code);
104 
105 void capitalize(DefaultMemchunks *buf, const StringRef &s);
106 
107 // Returns true if |value| is LWS
108 bool lws(const char *value);
109 
110 // Copies the |field| component value from |u| and |url| to the
111 // |dest|. If |u| does not have |field|, then this function does
112 // nothing.
113 void copy_url_component(std::string &dest, const http_parser_url *u, int field,
114                         const char *url);
115 
116 Headers::value_type to_header(const StringRef &name, const StringRef &value,
117                               bool no_index, int32_t token);
118 
119 // Add name/value pairs to |nva|.  If |no_index| is true, this
120 // name/value pair won't be indexed when it is forwarded to the next
121 // hop.
122 void add_header(Headers &nva, const StringRef &name, const StringRef &value,
123                 bool no_index, int32_t token);
124 
125 // Returns pointer to the entry in |nva| which has name |name|.  If
126 // more than one entries which have the name |name|, last occurrence
127 // in |nva| is returned.  If no such entry exist, returns nullptr.
128 const Headers::value_type *get_header(const Headers &nva, const char *name);
129 
130 // Returns true if the value of |nv| is not empty.
131 bool non_empty_value(const HeaderRefs::value_type *nv);
132 
133 // Create nghttp2_nv from |name|, |value| and |flags|.
134 inline nghttp2_nv make_field_flags(const StringRef &name,
135                                    const StringRef &value,
136                                    uint8_t flags = NGHTTP2_NV_FLAG_NONE) {
137   auto ns = as_uint8_span(std::span{name});
138   auto vs = as_uint8_span(std::span{value});
139 
140   return {const_cast<uint8_t *>(ns.data()), const_cast<uint8_t *>(vs.data()),
141           ns.size(), vs.size(), flags};
142 }
143 
144 // Creates nghttp2_nv from |name|, |value| and |flags|.  nghttp2
145 // library does not copy them.
146 inline nghttp2_nv make_field(const StringRef &name, const StringRef &value,
147                              uint8_t flags = NGHTTP2_NV_FLAG_NONE) {
148   return make_field_flags(name, value,
149                           static_cast<uint8_t>(NGHTTP2_NV_FLAG_NO_COPY_NAME |
150                                                NGHTTP2_NV_FLAG_NO_COPY_VALUE |
151                                                flags));
152 }
153 
154 // Creates nghttp2_nv from |name|, |value| and |flags|.  nghttp2
155 // library copies |value| unless |flags| includes
156 // NGHTTP2_NV_FLAG_NO_COPY_VALUE.
157 inline nghttp2_nv make_field_v(const StringRef &name, const StringRef &value,
158                                uint8_t flags = NGHTTP2_NV_FLAG_NONE) {
159   return make_field_flags(
160     name, value, static_cast<uint8_t>(NGHTTP2_NV_FLAG_NO_COPY_NAME | flags));
161 }
162 
163 // Creates nghttp2_nv from |name|, |value| and |flags|.  nghttp2
164 // library copies |name| and |value| unless |flags| includes
165 // NGHTTP2_NV_FLAG_NO_COPY_NAME or NGHTTP2_NV_FLAG_NO_COPY_VALUE.
166 inline nghttp2_nv make_field_nv(const StringRef &name, const StringRef &value,
167                                 uint8_t flags = NGHTTP2_NV_FLAG_NONE) {
168   return make_field_flags(name, value, flags);
169 }
170 
171 // Returns NGHTTP2_NV_FLAG_NO_INDEX if |no_index| is true, otherwise
172 // NGHTTP2_NV_FLAG_NONE.
no_index(bool no_index)173 inline uint8_t no_index(bool no_index) {
174   return no_index ? NGHTTP2_NV_FLAG_NO_INDEX : NGHTTP2_NV_FLAG_NONE;
175 }
176 
177 enum HeaderBuildOp {
178   HDOP_NONE,
179   // Forwarded header fields must be stripped.  If this flag is not
180   // set, all Forwarded header fields other than last one are added.
181   HDOP_STRIP_FORWARDED = 1,
182   // X-Forwarded-For header fields must be stripped.  If this flag is
183   // not set, all X-Forwarded-For header fields other than last one
184   // are added.
185   HDOP_STRIP_X_FORWARDED_FOR = 1 << 1,
186   // X-Forwarded-Proto header fields must be stripped.  If this flag
187   // is not set, all X-Forwarded-Proto header fields other than last
188   // one are added.
189   HDOP_STRIP_X_FORWARDED_PROTO = 1 << 2,
190   // Via header fields must be stripped.  If this flag is not set, all
191   // Via header fields other than last one are added.
192   HDOP_STRIP_VIA = 1 << 3,
193   // Early-Data header fields must be stripped.  If this flag is not
194   // set, all Early-Data header fields are added.
195   HDOP_STRIP_EARLY_DATA = 1 << 4,
196   // Strip above all header fields.
197   HDOP_STRIP_ALL = HDOP_STRIP_FORWARDED | HDOP_STRIP_X_FORWARDED_FOR |
198                    HDOP_STRIP_X_FORWARDED_PROTO | HDOP_STRIP_VIA |
199                    HDOP_STRIP_EARLY_DATA,
200   // Sec-WebSocket-Accept header field must be stripped.  If this flag
201   // is not set, all Sec-WebSocket-Accept header fields are added.
202   HDOP_STRIP_SEC_WEBSOCKET_ACCEPT = 1 << 5,
203   // Sec-WebSocket-Key header field must be stripped.  If this flag is
204   // not set, all Sec-WebSocket-Key header fields are added.
205   HDOP_STRIP_SEC_WEBSOCKET_KEY = 1 << 6,
206   // Transfer-Encoding header field must be stripped.  If this flag is
207   // not set, all Transfer-Encoding header fields are added.
208   HDOP_STRIP_TRANSFER_ENCODING = 1 << 7,
209 };
210 
211 // Appends headers in |headers| to |nv|.  |headers| must be indexed
212 // before this call (its element's token field is assigned).  Certain
213 // headers, including disallowed headers in HTTP/2 spec and headers
214 // which require special handling (i.e. via), are not copied.  |flags|
215 // is one or more of HeaderBuildOp flags.  They tell function that
216 // certain header fields should not be added.
217 void copy_headers_to_nva(std::vector<nghttp2_nv> &nva,
218                          const HeaderRefs &headers, uint32_t flags);
219 
220 // Just like copy_headers_to_nva(), but this adds
221 // NGHTTP2_NV_FLAG_NO_COPY_NAME and NGHTTP2_NV_FLAG_NO_COPY_VALUE.
222 void copy_headers_to_nva_nocopy(std::vector<nghttp2_nv> &nva,
223                                 const HeaderRefs &headers, uint32_t flags);
224 
225 // Appends HTTP/1.1 style header lines to |buf| from headers in
226 // |headers|.  |headers| must be indexed before this call (its
227 // element's token field is assigned).  Certain headers, which
228 // requires special handling (i.e. via and cookie), are not appended.
229 // |flags| is one or more of HeaderBuildOp flags.  They tell function
230 // that certain header fields should not be added.
231 void build_http1_headers_from_headers(DefaultMemchunks *buf,
232                                       const HeaderRefs &headers,
233                                       uint32_t flags);
234 
235 // Return positive window_size_increment if WINDOW_UPDATE should be
236 // sent for the stream |stream_id|. If |stream_id| == 0, this function
237 // determines the necessity of the WINDOW_UPDATE for a connection.
238 //
239 // If the function determines WINDOW_UPDATE is not necessary at the
240 // moment, it returns -1.
241 int32_t determine_window_update_transmission(nghttp2_session *session,
242                                              int32_t stream_id);
243 
244 // Dumps name/value pairs in |nv| to |out|. The |nv| must be
245 // terminated by nullptr.
246 void dump_nv(FILE *out, const char **nv);
247 
248 // Dumps name/value pairs in |nva| to |out|.
249 void dump_nv(FILE *out, const nghttp2_nv *nva, size_t nvlen);
250 
251 // Dumps name/value pairs in |nva| to |out|.
252 void dump_nv(FILE *out, const Headers &nva);
253 
254 void dump_nv(FILE *out, const HeaderRefs &nva);
255 
256 // Ereases header in |hd|.
257 void erase_header(HeaderRef *hd);
258 
259 // Rewrites redirection URI which usually appears in location header
260 // field. The |uri| is the URI in the location header field. The |u|
261 // stores the result of parsed |uri|. The |request_authority| is the
262 // host or :authority header field value in the request. The
263 // |upstream_scheme| is either "https" or "http" in the upstream
264 // interface.  Rewrite is done only if location header field value
265 // contains |match_host| as host excluding port.  The |match_host| and
266 // |request_authority| could be different.  If |request_authority| is
267 // empty, strip authority.
268 //
269 // This function returns the new rewritten URI on success. If the
270 // location URI is not subject to the rewrite, this function returns
271 // empty string.
272 StringRef rewrite_location_uri(BlockAllocator &balloc, const StringRef &uri,
273                                const http_parser_url &u,
274                                const StringRef &match_host,
275                                const StringRef &request_authority,
276                                const StringRef &upstream_scheme);
277 
278 // Returns parsed HTTP status code.  Returns -1 on failure.
279 int parse_http_status_code(const StringRef &src);
280 
281 // Header fields to be indexed, except HD_MAXIDX which is convenient
282 // member to get maximum value.
283 //
284 // generated by genheaderfunc.py
285 enum {
286   HD__AUTHORITY,
287   HD__HOST,
288   HD__METHOD,
289   HD__PATH,
290   HD__PROTOCOL,
291   HD__SCHEME,
292   HD__STATUS,
293   HD_ACCEPT_ENCODING,
294   HD_ACCEPT_LANGUAGE,
295   HD_ALT_SVC,
296   HD_CACHE_CONTROL,
297   HD_CONNECTION,
298   HD_CONTENT_LENGTH,
299   HD_CONTENT_TYPE,
300   HD_COOKIE,
301   HD_DATE,
302   HD_EARLY_DATA,
303   HD_EXPECT,
304   HD_FORWARDED,
305   HD_HOST,
306   HD_HTTP2_SETTINGS,
307   HD_IF_MODIFIED_SINCE,
308   HD_KEEP_ALIVE,
309   HD_LINK,
310   HD_LOCATION,
311   HD_PRIORITY,
312   HD_PROXY_CONNECTION,
313   HD_SEC_WEBSOCKET_ACCEPT,
314   HD_SEC_WEBSOCKET_KEY,
315   HD_SERVER,
316   HD_TE,
317   HD_TRAILER,
318   HD_TRANSFER_ENCODING,
319   HD_UPGRADE,
320   HD_USER_AGENT,
321   HD_VIA,
322   HD_X_FORWARDED_FOR,
323   HD_X_FORWARDED_PROTO,
324   HD_MAXIDX,
325 };
326 
327 using HeaderIndex = std::array<int16_t, HD_MAXIDX>;
328 
329 // Looks up header token for header name |name|.  Only headers we are
330 // interested in are tokenized.  If header name cannot be tokenized,
331 // returns -1.
332 int lookup_token(const StringRef &name);
333 
334 // Initializes |hdidx|, header index.  The |hdidx| must point to the
335 // array containing at least HD_MAXIDX elements.
336 void init_hdidx(HeaderIndex &hdidx);
337 // Indexes header |token| using index |idx|.
338 void index_header(HeaderIndex &hdidx, int32_t token, size_t idx);
339 
340 // Returns header denoted by |token| using index |hdidx|.
341 const Headers::value_type *get_header(const HeaderIndex &hdidx, int32_t token,
342                                       const Headers &nva);
343 
344 Headers::value_type *get_header(const HeaderIndex &hdidx, int32_t token,
345                                 Headers &nva);
346 
347 struct LinkHeader {
348   // The region of URI.  This might not be NULL-terminated.
349   StringRef uri;
350 };
351 
352 // Returns next URI-reference in Link header field value |src|.  If no
353 // URI-reference found after searching all input, returned uri field
354 // is empty.  This imply that empty URI-reference is ignored during
355 // parsing.
356 std::vector<LinkHeader> parse_link_header(const StringRef &src);
357 
358 // Constructs path by combining base path |base_path| with another
359 // path |rel_path|.  The base path and another path can have optional
360 // query component.  This function assumes |base_path| is normalized.
361 // In other words, it does not contain ".." or "."  path components
362 // and starts with "/" if it is not empty.
363 std::string path_join(const StringRef &base, const StringRef &base_query,
364                       const StringRef &rel_path, const StringRef &rel_query);
365 
366 StringRef path_join(BlockAllocator &balloc, const StringRef &base_path,
367                     const StringRef &base_query, const StringRef &rel_path,
368                     const StringRef &rel_query);
369 
370 // true if response has body, taking into account the request method
371 // and status code.
372 bool expect_response_body(const std::string &method, int status_code);
373 bool expect_response_body(int method_token, int status_code);
374 
375 // true if response has body, taking into account status code only.
376 bool expect_response_body(int status_code);
377 
378 // Looks up method token for method name |name|.  Only methods defined
379 // in llhttp.h (llhttp_method) are tokenized.  If method name cannot
380 // be tokenized, returns -1.
381 int lookup_method_token(const StringRef &name);
382 
383 // Returns string representation of |method_token|.  This is wrapper
384 // around llhttp_method_name from llhttp.  If |method_token| is
385 // unknown, program aborts.  The returned StringRef is guaranteed to
386 // be NULL-terminated.
387 StringRef to_method_string(int method_token);
388 
389 StringRef normalize_path(BlockAllocator &balloc, const StringRef &path,
390                          const StringRef &query);
391 
392 // normalize_path_colon is like normalize_path, but it additionally
393 // does percent-decoding %3A in order to workaround the issue that ':'
394 // cannot be included in backend pattern.
395 StringRef normalize_path_colon(BlockAllocator &balloc, const StringRef &path,
396                                const StringRef &query);
397 
398 std::string normalize_path(const StringRef &path, const StringRef &query);
399 
400 StringRef rewrite_clean_path(BlockAllocator &balloc, const StringRef &src);
401 
402 // Returns path component of |uri|.  The returned path does not
403 // include query component.  This function returns empty string if it
404 // fails.
405 StringRef get_pure_path_component(const StringRef &uri);
406 
407 // Deduces scheme, authority and path from given |uri|, and stores
408 // them in |scheme|, |authority|, and |path| respectively.  If |uri|
409 // is relative path, path resolution takes place using path given in
410 // |base| of length |baselen|.  This function returns 0 if it
411 // succeeds, or -1.
412 int construct_push_component(BlockAllocator &balloc, StringRef &scheme,
413                              StringRef &authority, StringRef &path,
414                              const StringRef &base, const StringRef &uri);
415 
416 // Copies |src| and return its lower-cased version.
417 StringRef copy_lower(BlockAllocator &balloc, const StringRef &src);
418 
419 // Returns true if te header field value |s| contains "trailers".
420 bool contains_trailers(const StringRef &s);
421 
422 // Creates Sec-WebSocket-Accept value for |key|.  The capacity of
423 // buffer pointed by |dest| must have at least 24 bytes (base64
424 // encoded length of 16 bytes data).  It returns empty string in case
425 // of error.
426 StringRef make_websocket_accept_token(uint8_t *dest, const StringRef &key);
427 
428 // Returns true if HTTP version represents pre-HTTP/1.1 (e.g.,
429 // HTTP/0.9 or HTTP/1.0).
430 bool legacy_http1(int major, int minor);
431 
432 // Returns true if transfer-encoding field value |s| conforms RFC
433 // strictly.  This function does not allow empty value, BWS, and empty
434 // list elements.
435 bool check_transfer_encoding(const StringRef &s);
436 
437 } // namespace http2
438 
439 } // namespace nghttp2
440 
441 #endif // HTTP2_H
442