xref: /aosp_15_r20/external/cronet/net/http/http_request_headers.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/http/http_request_headers.h"
6 
7 #include <string_view>
8 #include <utility>
9 
10 #include "base/logging.h"
11 #include "base/metrics/histogram_macros.h"
12 #include "base/notreached.h"
13 #include "base/strings/escape.h"
14 #include "base/strings/strcat.h"
15 #include "base/strings/string_split.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/values.h"
19 #include "net/base/url_util.h"
20 #include "net/http/http_log_util.h"
21 #include "net/http/http_util.h"
22 #include "net/log/net_log_capture_mode.h"
23 #include "net/log/net_log_values.h"
24 
25 namespace net {
26 
27 namespace {
28 
SupportsStreamType(const std::optional<base::flat_set<SourceStream::SourceType>> & accepted_stream_types,SourceStream::SourceType type)29 bool SupportsStreamType(
30     const std::optional<base::flat_set<SourceStream::SourceType>>&
31         accepted_stream_types,
32     SourceStream::SourceType type) {
33   if (!accepted_stream_types)
34     return true;
35   return accepted_stream_types->contains(type);
36 }
37 
38 }  // namespace
39 
40 const char HttpRequestHeaders::kConnectMethod[] = "CONNECT";
41 const char HttpRequestHeaders::kDeleteMethod[] = "DELETE";
42 const char HttpRequestHeaders::kGetMethod[] = "GET";
43 const char HttpRequestHeaders::kHeadMethod[] = "HEAD";
44 const char HttpRequestHeaders::kOptionsMethod[] = "OPTIONS";
45 const char HttpRequestHeaders::kPatchMethod[] = "PATCH";
46 const char HttpRequestHeaders::kPostMethod[] = "POST";
47 const char HttpRequestHeaders::kPutMethod[] = "PUT";
48 const char HttpRequestHeaders::kTraceMethod[] = "TRACE";
49 const char HttpRequestHeaders::kTrackMethod[] = "TRACK";
50 const char HttpRequestHeaders::kAccept[] = "Accept";
51 const char HttpRequestHeaders::kAcceptCharset[] = "Accept-Charset";
52 const char HttpRequestHeaders::kAcceptEncoding[] = "Accept-Encoding";
53 const char HttpRequestHeaders::kAcceptLanguage[] = "Accept-Language";
54 const char HttpRequestHeaders::kAuthorization[] = "Authorization";
55 const char HttpRequestHeaders::kCacheControl[] = "Cache-Control";
56 const char HttpRequestHeaders::kConnection[] = "Connection";
57 const char HttpRequestHeaders::kContentLength[] = "Content-Length";
58 const char HttpRequestHeaders::kContentType[] = "Content-Type";
59 const char HttpRequestHeaders::kCookie[] = "Cookie";
60 const char HttpRequestHeaders::kHost[] = "Host";
61 const char HttpRequestHeaders::kIfMatch[] = "If-Match";
62 const char HttpRequestHeaders::kIfModifiedSince[] = "If-Modified-Since";
63 const char HttpRequestHeaders::kIfNoneMatch[] = "If-None-Match";
64 const char HttpRequestHeaders::kIfRange[] = "If-Range";
65 const char HttpRequestHeaders::kIfUnmodifiedSince[] = "If-Unmodified-Since";
66 const char HttpRequestHeaders::kOrigin[] = "Origin";
67 const char HttpRequestHeaders::kPragma[] = "Pragma";
68 const char HttpRequestHeaders::kPriority[] = "Priority";
69 const char HttpRequestHeaders::kProxyAuthorization[] = "Proxy-Authorization";
70 const char HttpRequestHeaders::kProxyConnection[] = "Proxy-Connection";
71 const char HttpRequestHeaders::kRange[] = "Range";
72 const char HttpRequestHeaders::kReferer[] = "Referer";
73 const char HttpRequestHeaders::kTransferEncoding[] = "Transfer-Encoding";
74 const char HttpRequestHeaders::kUserAgent[] = "User-Agent";
75 
76 HttpRequestHeaders::HeaderKeyValuePair::HeaderKeyValuePair() = default;
77 
HeaderKeyValuePair(std::string_view key,std::string_view value)78 HttpRequestHeaders::HeaderKeyValuePair::HeaderKeyValuePair(
79     std::string_view key,
80     std::string_view value)
81     : HeaderKeyValuePair(key, std::string(value)) {}
82 
HeaderKeyValuePair(std::string_view key,std::string && value)83 HttpRequestHeaders::HeaderKeyValuePair::HeaderKeyValuePair(std::string_view key,
84                                                            std::string&& value)
85     : key(key), value(std::move(value)) {}
86 
Iterator(const HttpRequestHeaders & headers)87 HttpRequestHeaders::Iterator::Iterator(const HttpRequestHeaders& headers)
88     : curr_(headers.headers_.begin()), end_(headers.headers_.end()) {}
89 
90 HttpRequestHeaders::Iterator::~Iterator() = default;
91 
GetNext()92 bool HttpRequestHeaders::Iterator::GetNext() {
93   if (!started_) {
94     started_ = true;
95     return curr_ != end_;
96   }
97 
98   if (curr_ == end_)
99     return false;
100 
101   ++curr_;
102   return curr_ != end_;
103 }
104 
105 HttpRequestHeaders::HttpRequestHeaders() = default;
106 HttpRequestHeaders::HttpRequestHeaders(const HttpRequestHeaders& other) =
107     default;
108 HttpRequestHeaders::HttpRequestHeaders(HttpRequestHeaders&& other) = default;
109 HttpRequestHeaders::~HttpRequestHeaders() = default;
110 
111 HttpRequestHeaders& HttpRequestHeaders::operator=(
112     const HttpRequestHeaders& other) = default;
113 HttpRequestHeaders& HttpRequestHeaders::operator=(HttpRequestHeaders&& other) =
114     default;
115 
GetHeader(std::string_view key,std::string * out) const116 bool HttpRequestHeaders::GetHeader(std::string_view key,
117                                    std::string* out) const {
118   auto it = FindHeader(key);
119   if (it == headers_.end())
120     return false;
121   out->assign(it->value);
122   return true;
123 }
124 
Clear()125 void HttpRequestHeaders::Clear() {
126   headers_.clear();
127 }
128 
SetHeader(std::string_view key,std::string_view value)129 void HttpRequestHeaders::SetHeader(std::string_view key,
130                                    std::string_view value) {
131   SetHeader(key, std::string(value));
132 }
133 
SetHeader(std::string_view key,std::string && value)134 void HttpRequestHeaders::SetHeader(std::string_view key, std::string&& value) {
135   // Invalid header names or values could mean clients can attach
136   // browser-internal headers.
137   CHECK(HttpUtil::IsValidHeaderName(key)) << key;
138   CHECK(HttpUtil::IsValidHeaderValue(value)) << key << " has invalid value.";
139 
140   SetHeaderInternal(key, std::move(value));
141 }
142 
SetHeaderWithoutCheckForTesting(std::string_view key,std::string_view value)143 void HttpRequestHeaders::SetHeaderWithoutCheckForTesting(
144     std::string_view key,
145     std::string_view value) {
146   SetHeaderInternal(key, std::string(value));
147 }
148 
SetHeaderIfMissing(std::string_view key,std::string_view value)149 void HttpRequestHeaders::SetHeaderIfMissing(std::string_view key,
150                                             std::string_view value) {
151   // Invalid header names or values could mean clients can attach
152   // browser-internal headers.
153   CHECK(HttpUtil::IsValidHeaderName(key));
154   CHECK(HttpUtil::IsValidHeaderValue(value));
155   auto it = FindHeader(key);
156   if (it == headers_.end())
157     headers_.push_back(HeaderKeyValuePair(key, value));
158 }
159 
RemoveHeader(std::string_view key)160 void HttpRequestHeaders::RemoveHeader(std::string_view key) {
161   auto it = FindHeader(key);
162   if (it != headers_.end())
163     headers_.erase(it);
164 }
165 
AddHeaderFromString(std::string_view header_line)166 void HttpRequestHeaders::AddHeaderFromString(std::string_view header_line) {
167   DCHECK_EQ(std::string::npos, header_line.find("\r\n"))
168       << "\"" << header_line << "\" contains CRLF.";
169 
170   const std::string::size_type key_end_index = header_line.find(":");
171   if (key_end_index == std::string::npos) {
172     LOG(DFATAL) << "\"" << header_line << "\" is missing colon delimiter.";
173     return;
174   }
175 
176   if (key_end_index == 0) {
177     LOG(DFATAL) << "\"" << header_line << "\" is missing header key.";
178     return;
179   }
180 
181   const std::string_view header_key(header_line.data(), key_end_index);
182   if (!HttpUtil::IsValidHeaderName(header_key)) {
183     LOG(DFATAL) << "\"" << header_line << "\" has invalid header key.";
184     return;
185   }
186 
187   const std::string::size_type value_index = key_end_index + 1;
188 
189   if (value_index < header_line.size()) {
190     std::string_view header_value(header_line.data() + value_index,
191                                   header_line.size() - value_index);
192     header_value = HttpUtil::TrimLWS(header_value);
193     if (!HttpUtil::IsValidHeaderValue(header_value)) {
194       LOG(DFATAL) << "\"" << header_line << "\" has invalid header value.";
195       return;
196     }
197     SetHeader(header_key, header_value);
198   } else if (value_index == header_line.size()) {
199     SetHeader(header_key, "");
200   } else {
201     NOTREACHED();
202   }
203 }
204 
AddHeadersFromString(std::string_view headers)205 void HttpRequestHeaders::AddHeadersFromString(std::string_view headers) {
206   for (std::string_view header : base::SplitStringPieceUsingSubstr(
207            headers, "\r\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
208     AddHeaderFromString(header);
209   }
210 }
211 
MergeFrom(const HttpRequestHeaders & other)212 void HttpRequestHeaders::MergeFrom(const HttpRequestHeaders& other) {
213   for (const auto& header : other.headers_) {
214     SetHeader(header.key, header.value);
215   }
216 }
217 
ToString() const218 std::string HttpRequestHeaders::ToString() const {
219   std::string output;
220   for (const auto& header : headers_) {
221     base::StringAppendF(&output, "%s: %s\r\n", header.key.c_str(),
222                         header.value.c_str());
223   }
224   output.append("\r\n");
225   return output;
226 }
227 
NetLogParams(const std::string & request_line,NetLogCaptureMode capture_mode) const228 base::Value::Dict HttpRequestHeaders::NetLogParams(
229     const std::string& request_line,
230     NetLogCaptureMode capture_mode) const {
231   base::Value::Dict dict;
232   dict.Set("line", NetLogStringValue(request_line));
233   base::Value::List headers;
234   for (const auto& header : headers_) {
235     std::string log_value =
236         ElideHeaderValueForNetLog(capture_mode, header.key, header.value);
237     headers.Append(
238         NetLogStringValue(base::StrCat({header.key, ": ", log_value})));
239   }
240   dict.Set("headers", std::move(headers));
241   return dict;
242 }
243 
SetAcceptEncodingIfMissing(const GURL & url,const std::optional<base::flat_set<SourceStream::SourceType>> & accepted_stream_types,bool enable_brotli,bool enable_zstd)244 void HttpRequestHeaders::SetAcceptEncodingIfMissing(
245     const GURL& url,
246     const std::optional<base::flat_set<SourceStream::SourceType>>&
247         accepted_stream_types,
248     bool enable_brotli,
249     bool enable_zstd) {
250   if (HasHeader(kAcceptEncoding))
251     return;
252 
253   // If a range is specifically requested, set the "Accepted Encoding" header to
254   // "identity".
255   if (HasHeader(kRange)) {
256     SetHeader(kAcceptEncoding, "identity");
257     return;
258   }
259 
260   // Supply Accept-Encoding headers first so that it is more likely that they
261   // will be in the first transmitted packet. This can sometimes make it easier
262   // to filter and analyze the streams to assure that a proxy has not damaged
263   // these headers. Some proxies deliberately corrupt Accept-Encoding headers.
264   std::vector<std::string> advertised_encoding_names;
265   if (SupportsStreamType(accepted_stream_types,
266                          SourceStream::SourceType::TYPE_GZIP)) {
267     advertised_encoding_names.push_back("gzip");
268   }
269   if (SupportsStreamType(accepted_stream_types,
270                          SourceStream::SourceType::TYPE_DEFLATE)) {
271     advertised_encoding_names.push_back("deflate");
272   }
273 
274   const bool can_use_advanced_encodings =
275       (url.SchemeIsCryptographic() || IsLocalhost(url));
276 
277   // Advertise "br" encoding only if transferred data is opaque to proxy.
278   if (enable_brotli &&
279       SupportsStreamType(accepted_stream_types,
280                          SourceStream::SourceType::TYPE_BROTLI) &&
281       can_use_advanced_encodings) {
282     advertised_encoding_names.push_back("br");
283   }
284   // Advertise "zstd" encoding only if transferred data is opaque to proxy.
285   if (enable_zstd &&
286       SupportsStreamType(accepted_stream_types,
287                          SourceStream::SourceType::TYPE_ZSTD) &&
288       can_use_advanced_encodings) {
289     advertised_encoding_names.push_back("zstd");
290   }
291   if (!advertised_encoding_names.empty()) {
292     // Tell the server what compression formats are supported.
293     SetHeader(
294         kAcceptEncoding,
295         base::JoinString(base::make_span(advertised_encoding_names), ", "));
296   }
297 }
298 
FindHeader(std::string_view key)299 HttpRequestHeaders::HeaderVector::iterator HttpRequestHeaders::FindHeader(
300     std::string_view key) {
301   for (auto it = headers_.begin(); it != headers_.end(); ++it) {
302     if (base::EqualsCaseInsensitiveASCII(key, it->key))
303       return it;
304   }
305 
306   return headers_.end();
307 }
308 
FindHeader(std::string_view key) const309 HttpRequestHeaders::HeaderVector::const_iterator HttpRequestHeaders::FindHeader(
310     std::string_view key) const {
311   for (auto it = headers_.begin(); it != headers_.end(); ++it) {
312     if (base::EqualsCaseInsensitiveASCII(key, it->key))
313       return it;
314   }
315 
316   return headers_.end();
317 }
318 
SetHeaderInternal(std::string_view key,std::string && value)319 void HttpRequestHeaders::SetHeaderInternal(std::string_view key,
320                                            std::string&& value) {
321   auto it = FindHeader(key);
322   if (it != headers_.end())
323     it->value = std::move(value);
324   else
325     headers_.emplace_back(key, std::move(value));
326 }
327 
328 }  // namespace net
329