2 * nghttp2 - HTTP/2 C Library
4 * Copyright (c) 2012 Tatsuhiro Tsujikawa
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:
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
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.
33 std::string get_status_string(unsigned int status_code) {
34 switch (status_code) {
36 return "100 Continue";
38 return "101 Switching Protocols";
44 return "202 Accepted";
46 return "203 Non-Authoritative Information";
48 return "204 No Content";
50 return "205 Reset Content";
52 return "206 Partial Content";
54 return "300 Multiple Choices";
56 return "301 Moved Permanently";
60 return "303 See Other";
62 return "304 Not Modified";
64 return "305 Use Proxy";
65 // case 306: return "306 (Unused)";
67 return "307 Temporary Redirect";
69 return "308 Permanent Redirect";
71 return "400 Bad Request";
73 return "401 Unauthorized";
75 return "402 Payment Required";
77 return "403 Forbidden";
79 return "404 Not Found";
81 return "405 Method Not Allowed";
83 return "406 Not Acceptable";
85 return "407 Proxy Authentication Required";
87 return "408 Request Timeout";
89 return "409 Conflict";
93 return "411 Length Required";
95 return "412 Precondition Failed";
97 return "413 Payload Too Large";
99 return "414 URI Too Long";
101 return "415 Unsupported Media Type";
103 return "416 Requested Range Not Satisfiable";
105 return "417 Expectation Failed";
107 return "421 Misdirected Request";
109 return "426 Upgrade Required";
111 return "428 Precondition Required";
113 return "429 Too Many Requests";
115 return "431 Request Header Fields Too Large";
117 return "500 Internal Server Error";
119 return "501 Not Implemented";
121 return "502 Bad Gateway";
123 return "503 Service Unavailable";
125 return "504 Gateway Timeout";
127 return "505 HTTP Version Not Supported";
129 return "511 Network Authentication Required";
131 return util::utos(status_code);
135 void capitalize(std::string &s, size_t offset) {
136 s[offset] = util::upcase(s[offset]);
137 for (size_t i = offset + 1, eoi = s.size(); i < eoi; ++i) {
138 if (s[i - 1] == '-') {
139 s[i] = util::upcase(s[i]);
141 s[i] = util::lowcase(s[i]);
146 bool lws(const char *value) {
147 for (; *value; ++value) {
159 void copy_url_component(std::string &dest, const http_parser_url *u, int field,
161 if (u->field_set & (1 << field)) {
162 dest.assign(url + u->field_data[field].off, u->field_data[field].len);
166 Headers::value_type to_header(const uint8_t *name, size_t namelen,
167 const uint8_t *value, size_t valuelen,
168 bool no_index, int16_t token) {
169 return Header(std::string(reinterpret_cast<const char *>(name), namelen),
170 std::string(reinterpret_cast<const char *>(value), valuelen),
174 void add_header(Headers &nva, const uint8_t *name, size_t namelen,
175 const uint8_t *value, size_t valuelen, bool no_index,
179 for (i = 0; i < valuelen && (value[i] == ' ' || value[i] == '\t'); ++i)
181 for (j = valuelen - 1; j > i && (value[j] == ' ' || value[j] == '\t'); --j)
184 valuelen -= i + (valuelen - j - 1);
186 nva.push_back(to_header(name, namelen, value, valuelen, no_index, token));
189 const Headers::value_type *get_header(const Headers &nva, const char *name) {
190 const Headers::value_type *res = nullptr;
191 for (auto &nv : nva) {
192 if (nv.name == name) {
199 std::string value_to_str(const Headers::value_type *nv) {
206 bool non_empty_value(const Headers::value_type *nv) {
207 return nv && !nv->value.empty();
210 nghttp2_nv make_nv(const std::string &name, const std::string &value,
214 flags = no_index ? NGHTTP2_NV_FLAG_NO_INDEX : NGHTTP2_NV_FLAG_NONE;
216 return {(uint8_t *)name.c_str(), (uint8_t *)value.c_str(), name.size(),
217 value.size(), flags};
220 void copy_headers_to_nva(std::vector<nghttp2_nv> &nva, const Headers &headers) {
221 for (auto &kv : headers) {
222 if (kv.name.empty() || kv.name[0] == ':') {
229 case HD_HTTP2_SETTINGS:
231 case HD_PROXY_CONNECTION:
234 case HD_TRANSFER_ENCODING:
237 case HD_X_FORWARDED_FOR:
238 case HD_X_FORWARDED_PROTO:
241 nva.push_back(make_nv(kv.name, kv.value, kv.no_index));
245 void build_http1_headers_from_headers(std::string &hdrs,
246 const Headers &headers) {
247 for (auto &kv : headers) {
248 if (kv.name.empty() || kv.name[0] == ':') {
255 case HD_HTTP2_SETTINGS:
257 case HD_PROXY_CONNECTION:
261 case HD_X_FORWARDED_FOR:
262 case HD_X_FORWARDED_PROTO:
266 capitalize(hdrs, hdrs.size() - kv.name.size());
273 int32_t determine_window_update_transmission(nghttp2_session *session,
275 int32_t recv_length, window_size;
276 if (stream_id == 0) {
277 recv_length = nghttp2_session_get_effective_recv_data_length(session);
278 window_size = nghttp2_session_get_effective_local_window_size(session);
280 recv_length = nghttp2_session_get_stream_effective_recv_data_length(
282 window_size = nghttp2_session_get_stream_effective_local_window_size(
285 if (recv_length != -1 && window_size != -1) {
286 if (recv_length >= window_size / 2) {
293 void dump_nv(FILE *out, const char **nv) {
294 for (size_t i = 0; nv[i]; i += 2) {
295 fprintf(out, "%s: %s\n", nv[i], nv[i + 1]);
301 void dump_nv(FILE *out, const nghttp2_nv *nva, size_t nvlen) {
302 auto end = nva + nvlen;
303 for (; nva != end; ++nva) {
304 fprintf(out, "%s: %s\n", nva->name, nva->value);
310 void dump_nv(FILE *out, const Headers &nva) {
311 for (auto &nv : nva) {
312 fprintf(out, "%s: %s\n", nv.name.c_str(), nv.value.c_str());
318 std::string rewrite_location_uri(const std::string &uri,
319 const http_parser_url &u,
320 const std::string &match_host,
321 const std::string &request_authority,
322 const std::string &upstream_scheme) {
323 // We just rewrite scheme and authority.
324 if ((u.field_set & (1 << UF_HOST)) == 0) {
327 auto field = &u.field_data[UF_HOST];
328 if (!util::startsWith(std::begin(match_host), std::end(match_host),
329 &uri[field->off], &uri[field->off] + field->len) ||
330 (match_host.size() != field->len && match_host[field->len] != ':')) {
334 if (!request_authority.empty()) {
335 res += upstream_scheme;
337 res += request_authority;
339 if (u.field_set & (1 << UF_PATH)) {
340 field = &u.field_data[UF_PATH];
341 res.append(&uri[field->off], field->len);
343 if (u.field_set & (1 << UF_QUERY)) {
344 field = &u.field_data[UF_QUERY];
346 res.append(&uri[field->off], field->len);
348 if (u.field_set & (1 << UF_FRAGMENT)) {
349 field = &u.field_data[UF_FRAGMENT];
351 res.append(&uri[field->off], field->len);
356 int check_nv(const uint8_t *name, size_t namelen, const uint8_t *value,
358 if (!nghttp2_check_header_name(name, namelen)) {
361 if (!nghttp2_check_header_value(value, valuelen)) {
367 int parse_http_status_code(const std::string &src) {
368 if (src.size() != 3) {
388 int lookup_token(const std::string &name) {
389 return lookup_token(reinterpret_cast<const uint8_t *>(name.c_str()),
393 // This function was generated by genheaderfunc.py. Inspired by h2o
394 // header lookup. https://github.com/h2o/h2o
395 int lookup_token(const uint8_t *name, size_t namelen) {
400 if (util::streq_l("t", name, 1)) {
409 if (util::streq_l("vi", name, 2)) {
418 if (util::streq_l("lin", name, 3)) {
423 if (util::streq_l("hos", name, 3)) {
432 if (util::streq_l(":pat", name, 4)) {
437 if (util::streq_l(":hos", name, 4)) {
446 if (util::streq_l("cooki", name, 5)) {
451 if (util::streq_l("serve", name, 5)) {
456 if (util::streq_l("expec", name, 5)) {
465 if (util::streq_l("alt-sv", name, 6)) {
470 if (util::streq_l(":metho", name, 6)) {
475 if (util::streq_l(":schem", name, 6)) {
478 if (util::streq_l("upgrad", name, 6)) {
483 if (util::streq_l("traile", name, 6)) {
488 if (util::streq_l(":statu", name, 6)) {
497 if (util::streq_l("locatio", name, 7)) {
506 if (util::streq_l("keep-aliv", name, 9)) {
507 return HD_KEEP_ALIVE;
511 if (util::streq_l("connectio", name, 9)) {
512 return HD_CONNECTION;
516 if (util::streq_l("user-agen", name, 9)) {
517 return HD_USER_AGENT;
521 if (util::streq_l(":authorit", name, 9)) {
522 return HD__AUTHORITY;
530 if (util::streq_l("cache-contro", name, 12)) {
531 return HD_CACHE_CONTROL;
539 if (util::streq_l("content-lengt", name, 13)) {
540 return HD_CONTENT_LENGTH;
544 if (util::streq_l("http2-setting", name, 13)) {
545 return HD_HTTP2_SETTINGS;
553 if (util::streq_l("accept-languag", name, 14)) {
554 return HD_ACCEPT_LANGUAGE;
558 if (util::streq_l("accept-encodin", name, 14)) {
559 return HD_ACCEPT_ENCODING;
563 if (util::streq_l("x-forwarded-fo", name, 14)) {
564 return HD_X_FORWARDED_FOR;
572 if (util::streq_l("proxy-connectio", name, 15)) {
573 return HD_PROXY_CONNECTION;
581 if (util::streq_l("if-modified-sinc", name, 16)) {
582 return HD_IF_MODIFIED_SINCE;
586 if (util::streq_l("transfer-encodin", name, 16)) {
587 return HD_TRANSFER_ENCODING;
591 if (util::streq_l("x-forwarded-prot", name, 16)) {
592 return HD_X_FORWARDED_PROTO;
601 void init_hdidx(HeaderIndex &hdidx) {
602 std::fill(std::begin(hdidx), std::end(hdidx), -1);
605 void index_header(HeaderIndex &hdidx, int16_t token, size_t idx) {
609 assert(token < HD_MAXIDX);
613 bool check_http2_request_pseudo_header(const HeaderIndex &hdidx,
620 return hdidx[token] == -1;
626 bool check_http2_response_pseudo_header(const HeaderIndex &hdidx,
630 return hdidx[token] == -1;
636 bool http2_header_allowed(int16_t token) {
640 case HD_PROXY_CONNECTION:
641 case HD_TRANSFER_ENCODING:
649 bool http2_mandatory_request_headers_presence(const HeaderIndex &hdidx) {
650 if (hdidx[HD__METHOD] == -1 || hdidx[HD__PATH] == -1 ||
651 hdidx[HD__SCHEME] == -1 ||
652 (hdidx[HD__AUTHORITY] == -1 && hdidx[HD_HOST] == -1)) {
658 const Headers::value_type *get_header(const HeaderIndex &hdidx, int16_t token,
659 const Headers &nva) {
660 auto i = hdidx[token];
668 template <typename InputIt> InputIt skip_lws(InputIt first, InputIt last) {
669 for (; first != last; ++first) {
683 template <typename InputIt>
684 InputIt skip_to_next_field(InputIt first, InputIt last) {
685 for (; first != last; ++first) {
700 // Skip to the right dquote ('"'), handling backslash escapes.
701 // Returns |last| if input is not terminated with '"'.
702 template <typename InputIt>
703 InputIt skip_to_right_dquote(InputIt first, InputIt last) {
704 for (; first != last;) {
722 // Returns true if link-param does not match pattern |pat| of length
723 // |patlen| or it has empty value (""). |pat| should be parmname
725 bool check_link_param_empty(const char *first, const char *last,
726 const char *pat, size_t patlen) {
727 if (first + patlen <= last) {
728 if (std::equal(pat, pat + patlen, first, util::CaseCmp())) {
729 // we only accept URI if pat is followd by "" (e.g.,
730 // loadpolicy="") here.
731 if (first + patlen + 2 <= last) {
732 if (*(first + patlen) != '"' || *(first + patlen + 1) != '"') {
736 // here we got invalid production (anchor=") or anchor=?
746 std::pair<LinkHeader, const char *>
747 parse_next_link_header_once(const char *first, const char *last) {
748 first = skip_to_next_field(first, last);
749 if (first == last || *first != '<') {
750 return {{{nullptr, nullptr}}, last};
752 auto url_first = ++first;
753 first = std::find(first, last, '>');
755 return {{{nullptr, nullptr}}, first};
757 auto url_last = first++;
759 return {{{nullptr, nullptr}}, first};
761 // we expect ';' or ',' here
764 return {{{nullptr, nullptr}}, ++first};
769 return {{{nullptr, nullptr}}, last};
775 first = skip_lws(first, last);
777 return {{{nullptr, nullptr}}, first};
779 // we expect link-param
781 // rel can take several relations using quoted form.
782 static constexpr char PLP[] = "rel=\"";
783 static constexpr size_t PLPLEN = sizeof(PLP) - 1;
785 static constexpr char PLT[] = "preload";
786 static constexpr size_t PLTLEN = sizeof(PLT) - 1;
787 if (first + PLPLEN < last && *(first + PLPLEN - 1) == '"' &&
788 std::equal(PLP, PLP + PLPLEN, first, util::CaseCmp())) {
789 // we have to search preload in whitespace separated list:
790 // rel="preload something http://example.org/foo"
793 for (; first != last;) {
794 if (*first != ' ' && *first != '"') {
799 if (start == first) {
800 return {{{nullptr, nullptr}}, last};
803 if (!ok && start + PLTLEN == first &&
804 std::equal(PLT, PLT + PLTLEN, start, util::CaseCmp())) {
811 first = skip_lws(first, last);
815 return {{{nullptr, nullptr}}, first};
817 assert(*first == '"');
819 if (first == last || *first == ',') {
824 // parse next link-param
827 return {{{nullptr, nullptr}}, last};
829 // we are only interested in rel=preload parameter. Others are
831 static constexpr char PL[] = "rel=preload";
832 static constexpr size_t PLLEN = sizeof(PL) - 1;
833 if (first + PLLEN == last) {
834 if (std::equal(PL, PL + PLLEN, first, util::CaseCmp())) {
836 // this is the end of sequence
837 return {{{url_first, url_last}}, last};
839 } else if (first + PLLEN + 1 <= last) {
840 switch (*(first + PLLEN)) {
842 if (!std::equal(PL, PL + PLLEN, first, util::CaseCmp())) {
846 // skip including ','
848 return {{{url_first, url_last}}, first};
850 if (!std::equal(PL, PL + PLLEN, first, util::CaseCmp())) {
854 // skip including ';'
856 // continue parse next link-param
860 // we have to reject URI if we have nonempty anchor parameter.
861 static constexpr char ANCHOR[] = "anchor=";
862 static constexpr size_t ANCHORLEN = sizeof(ANCHOR) - 1;
863 if (!ign && !check_link_param_empty(first, last, ANCHOR, ANCHORLEN)) {
867 // reject URI if we have non-empty loadpolicy. This could be
868 // tightened up to just pick up "next" or "insert".
869 static constexpr char LOADPOLICY[] = "loadpolicy=";
870 static constexpr size_t LOADPOLICYLEN = sizeof(LOADPOLICY) - 1;
872 !check_link_param_empty(first, last, LOADPOLICY, LOADPOLICYLEN)) {
876 auto param_first = first;
877 for (; first != last;) {
878 if (util::in_attr_char(*first)) {
882 // '*' is only allowed at the end of parameter name and must be
884 if (last - first >= 2 && first != param_first) {
885 if (*first == '*' && *(first + 1) == '=') {
890 if (*first == '=' || *first == ';' || *first == ',') {
893 return {{{nullptr, nullptr}}, last};
895 if (param_first == first) {
897 return {{{nullptr, nullptr}}, last};
899 // link-param without value is acceptable (see link-extension) if
900 // it is not followed by '='
901 if (first == last || *first == ',') {
906 // parse next link-param
909 // now parsing link-param value
910 assert(*first == '=');
913 // empty value is not acceptable
914 return {{{nullptr, nullptr}}, first};
918 first = skip_to_right_dquote(first + 1, last);
920 return {{{nullptr, nullptr}}, first};
923 if (first == last || *first == ',') {
928 // parse next link-param
931 return {{{nullptr, nullptr}}, last};
933 // not quoted-string, skip to next ',' or ';'
934 if (*first == ',' || *first == ';') {
936 return {{{nullptr, nullptr}}, last};
938 for (; first != last; ++first) {
939 if (*first == ',' || *first == ';') {
943 if (first == last || *first == ',') {
946 assert(*first == ';');
948 // parse next link-param
952 assert(first == last || *first == ',');
958 return {{{url_first, url_last}}, first};
960 return {{{nullptr, nullptr}}, first};
964 std::vector<LinkHeader> parse_link_header(const char *src, size_t len) {
966 auto last = src + len;
967 std::vector<LinkHeader> res;
968 for (; first != last;) {
969 auto rv = parse_next_link_header_once(first, last);
971 if (rv.first.uri.first != nullptr && rv.first.uri.second != nullptr) {
972 res.push_back(rv.first);
979 void eat_file(std::string &path) {
984 auto p = path.size() - 1;
985 if (path[p] == '/') {
988 p = path.rfind('/', p);
989 if (p == std::string::npos) {
990 // this should not happend in normal case, where we expect path
995 path.erase(std::begin(path) + p + 1, std::end(path));
1000 void eat_dir(std::string &path) {
1005 auto p = path.size() - 1;
1006 if (path[p] != '/') {
1007 p = path.rfind('/', p);
1008 if (p == std::string::npos) {
1009 // this should not happend in normal case, where we expect path
1015 if (path[p] == '/') {
1021 p = path.rfind('/', p);
1022 if (p == std::string::npos) {
1023 // this should not happend in normal case, where we expect path
1028 path.erase(std::begin(path) + p + 1, std::end(path));
1032 std::string path_join(const char *base_path, size_t base_pathlen,
1033 const char *base_query, size_t base_querylen,
1034 const char *rel_path, size_t rel_pathlen,
1035 const char *rel_query, size_t rel_querylen) {
1037 if (rel_pathlen == 0) {
1038 if (base_pathlen == 0) {
1041 res.assign(base_path, base_pathlen);
1043 if (rel_querylen == 0) {
1044 if (base_querylen) {
1046 res.append(base_query, base_querylen);
1051 res.append(rel_query, rel_querylen);
1055 auto first = rel_path;
1056 auto last = rel_path + rel_pathlen;
1058 if (rel_path[0] == '/') {
1061 } else if (base_pathlen == 0) {
1064 res.assign(base_path, base_pathlen);
1067 for (; first != last;) {
1068 if (*first == '.') {
1069 if (first + 1 == last) {
1072 if (*(first + 1) == '/') {
1076 if (*(first + 1) == '.') {
1077 if (first + 2 == last) {
1081 if (*(first + 2) == '/') {
1088 if (res.back() != '/') {
1091 auto slash = std::find(first, last, '/');
1092 if (slash == last) {
1093 res.append(first, last);
1096 res.append(first, slash + 1);
1098 for (; first != last && *first == '/'; ++first)
1103 res.append(rel_query, rel_querylen);
1108 bool expect_response_body(int status_code) {
1109 return status_code / 100 != 1 && status_code != 304 && status_code != 204;
1112 bool expect_response_body(const std::string &method, int status_code) {
1113 return method != "HEAD" && expect_response_body(status_code);
1116 } // namespace http2
1118 } // namespace nghttp2