/src/brpc/src/brpc/http_header.h
Line | Count | Source (jump to first uncovered line) |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | |
18 | | |
19 | | #ifndef BRPC_HTTP_HEADER_H |
20 | | #define BRPC_HTTP_HEADER_H |
21 | | |
22 | | #include <vector> |
23 | | #include "butil/strings/string_piece.h" // StringPiece |
24 | | #include "butil/containers/case_ignored_flat_map.h" |
25 | | #include "brpc/uri.h" // URI |
26 | | #include "brpc/http_method.h" // HttpMethod |
27 | | #include "brpc/http_status_code.h" |
28 | | #include "brpc/http2.h" |
29 | | |
30 | | // To rpc developers: DON'T put impl. details here, use opaque pointers instead. |
31 | | |
32 | | |
33 | | namespace brpc { |
34 | | class InputMessageBase; |
35 | | namespace policy { |
36 | | void ProcessHttpRequest(InputMessageBase *msg); |
37 | | class H2StreamContext; |
38 | | } |
39 | | |
40 | | // Non-body part of a HTTP message. |
41 | | class HttpHeader { |
42 | | public: |
43 | | typedef butil::CaseIgnoredMultiFlatMap<std::string> HeaderMap; |
44 | | typedef HeaderMap::const_iterator HeaderIterator; |
45 | | typedef HeaderMap::key_equal HeaderKeyEqual; |
46 | | |
47 | | HttpHeader(); |
48 | | |
49 | | // Exchange internal fields with another HttpHeader. |
50 | | void Swap(HttpHeader &rhs); |
51 | | |
52 | | // Reset internal fields as if they're just default-constructed. |
53 | | void Clear(); |
54 | | |
55 | | // Get http version, 1.1 by default. |
56 | 5.46k | int major_version() const { return _version.first; } |
57 | 5.46k | int minor_version() const { return _version.second; } |
58 | | // Change the http version |
59 | | void set_version(int http_major, int http_minor) |
60 | 15.1k | { _version = std::make_pair(http_major, http_minor); } |
61 | | |
62 | | // True if version of http is earlier than 1.1 |
63 | | bool before_http_1_1() const |
64 | 5.46k | { return (major_version() * 10000 + minor_version()) <= 10000; } |
65 | | |
66 | | // True if the message is from HTTP2. |
67 | 0 | bool is_http2() const { return major_version() == 2; } |
68 | | |
69 | | // Get/set "Content-Type". |
70 | | // possible values: "text/plain", "application/json" ... |
71 | | // NOTE: Equal to `GetHeader("Content-Type")', ·SetHeader("Content-Type")‘ (case-insensitive). |
72 | 0 | const std::string& content_type() const { return _content_type; } |
73 | 0 | void set_content_type(const std::string& type) { _content_type = type; } |
74 | 0 | void set_content_type(const char* type) { _content_type = type; } |
75 | 0 | std::string& mutable_content_type() { return _content_type; } |
76 | | |
77 | | // Get value of a header which is case-insensitive according to: |
78 | | // https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 |
79 | | // Namely, GetHeader("log-id"), GetHeader("Log-Id"), GetHeader("LOG-ID") |
80 | | // point to the same value. |
81 | | // Return pointer to the value, NULL on not found. |
82 | | // NOTE: If the key is "Content-Type", `GetHeader("Content-Type")' |
83 | | // (case-insensitive) is equal to `content_type()'. |
84 | | const std::string* GetHeader(const char* key) const; |
85 | | const std::string* GetHeader(const std::string& key) const; |
86 | | |
87 | | std::vector<const std::string*> GetAllSetCookieHeader() const; |
88 | | |
89 | | // Set value of a header. |
90 | | // NOTE: If the key is "Content-Type", `SetHeader("Content-Type", ...)' |
91 | | // (case-insensitive) is equal to `set_content_type(...)'. |
92 | | void SetHeader(const std::string& key, const std::string& value); |
93 | | |
94 | | // Remove all headers of key. |
95 | | void RemoveHeader(const char* key); |
96 | 0 | void RemoveHeader(const std::string& key) { RemoveHeader(key.c_str()); } |
97 | | |
98 | | // Append value to a header. If the header already exists, separate |
99 | | // old value and new value with comma(,), two-byte delimiter of "; " |
100 | | // or into a new header field, according to: |
101 | | // |
102 | | // https://datatracker.ietf.org/doc/html/rfc2616#section-4.2 |
103 | | // Multiple message-header fields with the same field-name MAY be |
104 | | // present in a message if and only if the entire field-value for that |
105 | | // header field is defined as a comma-separated list [i.e., #(values)]. |
106 | | // |
107 | | // https://datatracker.ietf.org/doc/html/rfc9114#section-4.2.1 |
108 | | // If a decompressed field section contains multiple cookie field lines, |
109 | | // these MUST be concatenated into a single byte string using the two-byte |
110 | | // delimiter of "; " (ASCII 0x3b, 0x20) before being passed into a context |
111 | | // other than HTTP/2 or HTTP/3, such as an HTTP/1.1 connection, or a generic |
112 | | // HTTP server application. |
113 | | // |
114 | | // https://datatracker.ietf.org/doc/html/rfc6265#section-3 |
115 | | // Origin servers SHOULD NOT fold multiple Set-Cookie header |
116 | | // fields into a single header field. |
117 | | void AppendHeader(const std::string& key, const butil::StringPiece& value); |
118 | | |
119 | | // Get header iterators which are invalidated after calling AppendHeader() |
120 | 0 | HeaderIterator HeaderBegin() const { return _headers.begin(); } |
121 | 0 | HeaderIterator HeaderEnd() const { return _headers.end(); } |
122 | | // #headers |
123 | 0 | size_t HeaderCount() const { return _headers.size(); } |
124 | | |
125 | | // Get the URI object, check src/brpc/uri.h for details. |
126 | 0 | const URI& uri() const { return _uri; } |
127 | 25.7k | URI& uri() { return _uri; } |
128 | | |
129 | | // Get/set http method. |
130 | 0 | HttpMethod method() const { return _method; } |
131 | 15.1k | void set_method(const HttpMethod method) { _method = method; } |
132 | | |
133 | | // Get/set status-code and reason-phrase. Notice that the const char* |
134 | | // returned by reason_phrase() will be invalidated after next call to |
135 | | // set_status_code(). |
136 | 0 | int status_code() const { return _status_code; } |
137 | | const char* reason_phrase() const; |
138 | | void set_status_code(int status_code); |
139 | | |
140 | | // The URL path removed with matched prefix. |
141 | | // NOTE: always normalized and NOT started with /. |
142 | | // |
143 | | // Accessing HttpService.Echo |
144 | | // [URL] [unresolved_path] |
145 | | // "/HttpService/Echo" "" |
146 | | // "/HttpService/Echo/Foo" "Foo" |
147 | | // "/HttpService/Echo/Foo/Bar" "Foo/Bar" |
148 | | // "/HttpService//Echo///Foo//" "Foo" |
149 | | // |
150 | | // Accessing FileService.default_method: |
151 | | // [URL] [unresolved_path] |
152 | | // "/FileService" "" |
153 | | // "/FileService/123.txt" "123.txt" |
154 | | // "/FileService/mydir/123.txt" "mydir/123.txt" |
155 | | // "/FileService//mydir///123.txt//" "mydir/123.txt" |
156 | 0 | const std::string& unresolved_path() const { return _unresolved_path; } |
157 | | |
158 | | private: |
159 | | friend class HttpMessage; |
160 | | friend class HttpMessageSerializer; |
161 | | friend class policy::H2StreamContext; |
162 | | friend void policy::ProcessHttpRequest(InputMessageBase *msg); |
163 | | |
164 | | static const char* SET_COOKIE; |
165 | | static const char* COOKIE; |
166 | | static const char* CONTENT_TYPE; |
167 | | |
168 | | std::vector<const std::string*> GetMultiLineHeaders(const std::string& key) const; |
169 | | |
170 | | std::string& GetOrAddHeader(const std::string& key); |
171 | | |
172 | | std::string& AddHeader(const std::string& key); |
173 | | |
174 | 73.3k | bool IsSetCookie(const std::string& key) const { |
175 | 73.3k | return _header_key_equal(key, SET_COOKIE); |
176 | 73.3k | } |
177 | | |
178 | 3.99k | bool IsCookie(const std::string& key) const { |
179 | 3.99k | return _header_key_equal(key, COOKIE); |
180 | 3.99k | } |
181 | | |
182 | 29.6k | bool IsContentType(const std::string& key) const { |
183 | 29.6k | return _header_key_equal(key, CONTENT_TYPE); |
184 | 29.6k | } |
185 | | |
186 | | // Return true if the header can be folded in line, |
187 | | // otherwise, returns false, i.e., Set-Cookie header. |
188 | | // See comments of `AppendHeader'. |
189 | 31.3k | bool CanFoldedInLine(const std::string& key) { |
190 | 31.3k | return !IsSetCookie(key); |
191 | 31.3k | } |
192 | | |
193 | 3.99k | const char* HeaderValueDelimiter(const std::string& key) { |
194 | 3.99k | return IsCookie(key) ? "; " : ","; |
195 | 3.99k | } |
196 | | |
197 | | HeaderKeyEqual _header_key_equal; |
198 | | HeaderMap _headers; |
199 | | URI _uri; |
200 | | int _status_code; |
201 | | HttpMethod _method; |
202 | | std::string _content_type; |
203 | | std::string _unresolved_path; |
204 | | std::pair<int, int> _version; |
205 | | std::string* _first_set_cookie; |
206 | | }; |
207 | | |
208 | | const HttpHeader& DefaultHttpHeader(); |
209 | | |
210 | | } // namespace brpc |
211 | | |
212 | | |
213 | | #endif //BRPC_HTTP_HEADER_H |