/
githubmirror
/
nghttp2
Обзор
Документация
Войти
/
githubmirror
/
nghttp2
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/http2.h
454 строки
17 KB
Tatsuhiro Tsujikawa
src: Adopt std::expected for http2::make_websocket_accept_token
10 май 2026, 12:44
10 май 2026, 12:44
7e5a934
Код
Авторство
О чём код?
/* * 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 <compare> #include <expected> #include <nghttp2/nghttp2.h> #include "urlparse.h" #include "util.h" #include "memchunk.h" #include "template.h" #include "allocator.h" #include "base64.h" #include "errors.h" namespace nghttp2 { struct Header { constexpr Header(std::string name, std::string value, bool no_index = false, int32_t token = -1) noexcept : name{std::move(name)}, value{std::move(value)}, token{token}, no_index{no_index} {} constexpr Header() noexcept = default; constexpr Header(const Header &) = default; constexpr Header(Header &&) noexcept = default; constexpr Header &operator=(const Header &) = default; constexpr Header &operator=(Header &&) noexcept = default; constexpr bool operator==(const Header &other) const noexcept { return name == other.name && value == other.value; } constexpr std::strong_ordering operator<=>(const Header &other) const noexcept { if (auto cmp = name <=> other.name; cmp != 0) { return cmp; } return value <=> other.value; } std::string name; std::string value; int32_t token{-1}; bool no_index{}; }; struct HeaderRef { constexpr HeaderRef(std::string_view name, std::string_view value, bool no_index = false, int32_t token = -1) noexcept : name{name}, value{value}, token{token}, no_index{no_index} {} constexpr HeaderRef() noexcept = default; constexpr HeaderRef(const HeaderRef &) noexcept = default; constexpr HeaderRef(HeaderRef &&) noexcept = default; constexpr HeaderRef &operator=(const HeaderRef &) noexcept = default; constexpr HeaderRef &operator=(HeaderRef &&) noexcept = default; constexpr bool operator==(const HeaderRef &other) const noexcept { return name == other.name && value == other.value; } constexpr std::strong_ordering operator<=>(const HeaderRef &other) const noexcept { if (auto cmp = name <=> other.name; cmp != 0) { return cmp; } return value <=> other.value; } std::string_view name; std::string_view value; int32_t token{-1}; 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. std::string_view get_reason_phrase(unsigned int status_code); // Returns string version of |status_code|. (e.g., "404") std::string_view stringify_status(BlockAllocator &balloc, unsigned int status_code); void capitalize(DefaultMemchunks *buf, std::string_view s); Headers::value_type to_header(std::string_view name, std::string_view value, 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. void add_header(Headers &nva, std::string_view name, std::string_view value, 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, std::string_view name); // Returns true if the value of |nv| is not empty. bool non_empty_value(const HeaderRefs::value_type *nv); // Create nghttp2_nv from |name|, |value| and |flags|. inline nghttp2_nv make_field_flags(std::string_view name, std::string_view value, uint8_t flags = NGHTTP2_NV_FLAG_NONE) { auto ns = as_uint8_span(std::span{name}); auto vs = as_uint8_span(std::span{value}); return {const_cast<uint8_t *>(ns.data()), const_cast<uint8_t *>(vs.data()), ns.size(), vs.size(), flags}; } // Creates nghttp2_nv from |name|, |value| and |flags|. nghttp2 // library does not copy them. inline nghttp2_nv make_field(std::string_view name, std::string_view value, uint8_t flags = NGHTTP2_NV_FLAG_NONE) { return make_field_flags(name, value, static_cast<uint8_t>(NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE | flags)); } // Creates nghttp2_nv from |name|, |value| and |flags|. nghttp2 // library copies |value| unless |flags| includes // NGHTTP2_NV_FLAG_NO_COPY_VALUE. inline nghttp2_nv make_field_v(std::string_view name, std::string_view value, uint8_t flags = NGHTTP2_NV_FLAG_NONE) { return make_field_flags( name, value, static_cast<uint8_t>(NGHTTP2_NV_FLAG_NO_COPY_NAME | flags)); } // Creates nghttp2_nv from |name|, |value| and |flags|. nghttp2 // library copies |name| and |value| unless |flags| includes // NGHTTP2_NV_FLAG_NO_COPY_NAME or NGHTTP2_NV_FLAG_NO_COPY_VALUE. inline nghttp2_nv make_field_nv(std::string_view name, std::string_view value, uint8_t flags = NGHTTP2_NV_FLAG_NONE) { return make_field_flags(name, value, flags); } // Returns NGHTTP2_NV_FLAG_NO_INDEX if |no_index| is true, otherwise // NGHTTP2_NV_FLAG_NONE. inline uint8_t no_index(bool no_index) { return no_index ? NGHTTP2_NV_FLAG_NO_INDEX : NGHTTP2_NV_FLAG_NONE; } 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 |nva| of length |nvlen| 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 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. std::string_view rewrite_location_uri(BlockAllocator &balloc, std::string_view uri, const urlparse_url &u, std::string_view match_host, std::string_view request_authority, std::string_view upstream_scheme); // Returns parsed HTTP status code. std::expected<int, Error> parse_http_status_code(std::string_view 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|. Only headers we are // interested in are tokenized. If header name cannot be tokenized, // returns -1. int lookup_token(std::string_view 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); struct LinkHeader { // The region of URI. This might not be NULL-terminated. std::string_view 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(std::string_view 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(std::string_view base, std::string_view base_query, std::string_view rel_path, std::string_view rel_query); std::string_view path_join(BlockAllocator &balloc, std::string_view base_path, std::string_view base_query, std::string_view rel_path, std::string_view rel_query); // true if response has body, taking into account the request method // and status code. bool expect_response_body(const std::string &method, uint32_t status_code); bool expect_response_body(int method_token, uint32_t status_code); // true if response has body, taking into account status code only. bool expect_response_body(uint32_t status_code); // Looks up method token for method name |name|. Only methods defined // in llhttp.h (llhttp_method) are tokenized. If method name cannot // be tokenized, returns -1. int lookup_method_token(std::string_view 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 std::string_view is guaranteed to // be NULL-terminated. std::string_view to_method_string(int method_token); std::string_view normalize_path(BlockAllocator &balloc, std::string_view path, std::string_view 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. std::string_view normalize_path_colon(BlockAllocator &balloc, std::string_view path, std::string_view query); std::string normalize_path(std::string_view path, std::string_view query); std::string_view rewrite_clean_path(BlockAllocator &balloc, std::string_view src); // Returns path component of |uri|. The returned path does not // include query component. This function returns empty string if it // fails. std::string_view get_pure_path_component(std::string_view uri); struct PushComponent { std::string_view scheme; std::string_view authority; std::string_view path; }; // Deduces scheme, authority and path from given |uri|. If |uri| is // relative path, path resolution takes place using path given in // |base|. std::expected<PushComponent, Error> construct_push_component(BlockAllocator &balloc, std::string_view base, std::string_view uri); // Returns true if te header field value |s| contains "trailers". bool contains_trailers(std::string_view s); // Creates Sec-WebSocket-Accept value for |key|. The length of |key| // must be base64::encode_length(16), which is 24. The result is // written to |dest|. The function returns std::string_view that // references |dest|. The length of return value is equal to // |dest|.size(). std::expected<std::string_view, Error> make_websocket_accept_token(std::span<uint8_t, base64::encode_length(20)> dest, std::string_view 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(std::string_view s); // Encodes |extpri| in the wire format. std::string encode_extpri(const nghttp2_extpri &extpri); } // namespace http2 } // namespace nghttp2 #endif // !defined(HTTP2_H)