• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 // The rules for header parsing were borrowed from Firefox:
6 // http://lxr.mozilla.org/seamonkey/source/netwerk/protocol/http/src/nsHttpResponseHead.cpp
7 // The rules for parsing content-types were also borrowed from Firefox:
8 // http://lxr.mozilla.org/mozilla/source/netwerk/base/src/nsURLHelper.cpp#834
9 
10 #include "net/http/http_response_headers.h"
11 
12 #include <algorithm>
13 #include <limits>
14 #include <memory>
15 #include <unordered_map>
16 #include <utility>
17 
18 #include "base/format_macros.h"
19 #include "base/logging.h"
20 #include "base/metrics/histogram_macros.h"
21 #include "base/pickle.h"
22 #include "base/ranges/algorithm.h"
23 #include "base/strings/escape.h"
24 #include "base/strings/strcat.h"
25 #include "base/strings/string_number_conversions.h"
26 #include "base/strings/string_piece.h"
27 #include "base/strings/string_util.h"
28 #include "base/strings/stringprintf.h"
29 #include "base/time/time.h"
30 #include "base/values.h"
31 #include "net/base/parse_number.h"
32 #include "net/base/tracing.h"
33 #include "net/http/http_byte_range.h"
34 #include "net/http/http_log_util.h"
35 #include "net/http/http_status_code.h"
36 #include "net/http/http_util.h"
37 #include "net/log/net_log_capture_mode.h"
38 #include "net/log/net_log_values.h"
39 
40 using base::Time;
41 
42 namespace net {
43 
44 //-----------------------------------------------------------------------------
45 
46 namespace {
47 
48 // These headers are RFC 2616 hop-by-hop headers;
49 // not to be stored by caches.
50 const char* const kHopByHopResponseHeaders[] = {
51   "connection",
52   "proxy-connection",
53   "keep-alive",
54   "trailer",
55   "transfer-encoding",
56   "upgrade"
57 };
58 
59 // These headers are challenge response headers;
60 // not to be stored by caches.
61 const char* const kChallengeResponseHeaders[] = {
62   "www-authenticate",
63   "proxy-authenticate"
64 };
65 
66 // These headers are cookie setting headers;
67 // not to be stored by caches or disclosed otherwise.
68 const char* const kCookieResponseHeaders[] = {
69   "set-cookie",
70   "set-cookie2",
71   "clear-site-data",
72 };
73 
74 // By default, do not cache Strict-Transport-Security.
75 // This avoids erroneously re-processing it on page loads from cache ---
76 // it is defined to be valid only on live and error-free HTTPS connections.
77 const char* const kSecurityStateHeaders[] = {
78   "strict-transport-security",
79 };
80 
81 // These response headers are not copied from a 304/206 response to the cached
82 // response headers.  This list is based on Mozilla's nsHttpResponseHead.cpp.
83 const char* const kNonUpdatedHeaders[] = {
84     "connection",
85     "proxy-connection",
86     "keep-alive",
87     "www-authenticate",
88     "proxy-authenticate",
89     "proxy-authorization",
90     "te",
91     "trailer",
92     "transfer-encoding",
93     "upgrade",
94     "content-location",
95     "content-md5",
96     "etag",
97     "content-encoding",
98     "content-range",
99     "content-type",
100     "content-length",
101     "x-frame-options",
102     "x-xss-protection",
103 };
104 
105 // Some header prefixes mean "Don't copy this header from a 304 response.".
106 // Rather than listing all the relevant headers, we can consolidate them into
107 // this list:
108 const char* const kNonUpdatedHeaderPrefixes[] = {
109   "x-content-",
110   "x-webkit-"
111 };
112 
ShouldUpdateHeader(base::StringPiece name)113 bool ShouldUpdateHeader(base::StringPiece name) {
114   for (const auto* header : kNonUpdatedHeaders) {
115     if (base::EqualsCaseInsensitiveASCII(name, header))
116       return false;
117   }
118   for (const auto* prefix : kNonUpdatedHeaderPrefixes) {
119     if (base::StartsWith(name, prefix, base::CompareCase::INSENSITIVE_ASCII))
120       return false;
121   }
122   return true;
123 }
124 
HasEmbeddedNulls(base::StringPiece str)125 bool HasEmbeddedNulls(base::StringPiece str) {
126   for (char c : str) {
127     if (c == '\0')
128       return true;
129   }
130   return false;
131 }
132 
CheckDoesNotHaveEmbeddedNulls(base::StringPiece str)133 void CheckDoesNotHaveEmbeddedNulls(base::StringPiece str) {
134   // Care needs to be taken when adding values to the raw headers string to
135   // make sure it does not contain embeded NULLs. Any embeded '\0' may be
136   // understood as line terminators and change how header lines get tokenized.
137   CHECK(!HasEmbeddedNulls(str));
138 }
139 
140 }  // namespace
141 
142 const char HttpResponseHeaders::kContentRange[] = "Content-Range";
143 const char HttpResponseHeaders::kLastModified[] = "Last-Modified";
144 const char HttpResponseHeaders::kVary[] = "Vary";
145 
146 struct HttpResponseHeaders::ParsedHeader {
147   // A header "continuation" contains only a subsequent value for the
148   // preceding header.  (Header values are comma separated.)
is_continuationnet::HttpResponseHeaders::ParsedHeader149   bool is_continuation() const { return name_begin == name_end; }
150 
151   std::string::const_iterator name_begin;
152   std::string::const_iterator name_end;
153   std::string::const_iterator value_begin;
154   std::string::const_iterator value_end;
155 
156   // Write a representation of this object into a tracing proto.
WriteIntoTracenet::HttpResponseHeaders::ParsedHeader157   void WriteIntoTrace(perfetto::TracedValue context) const {
158     auto dict = std::move(context).WriteDictionary();
159     dict.Add("name", base::MakeStringPiece(name_begin, name_end));
160     dict.Add("value", base::MakeStringPiece(value_begin, value_end));
161   }
162 };
163 
164 //-----------------------------------------------------------------------------
165 
HttpResponseHeaders(const std::string & raw_input)166 HttpResponseHeaders::HttpResponseHeaders(const std::string& raw_input)
167     : response_code_(-1) {
168   Parse(raw_input);
169 
170   // The most important thing to do with this histogram is find out
171   // the existence of unusual HTTP status codes.  As it happens
172   // right now, there aren't double-constructions of response headers
173   // using this constructor, so our counts should also be accurate,
174   // without instantiating the histogram in two places.  It is also
175   // important that this histogram not collect data in the other
176   // constructor, which rebuilds an histogram from a pickle, since
177   // that would actually create a double call between the original
178   // HttpResponseHeader that was serialized, and initialization of the
179   // new object from that pickle.
180   UMA_HISTOGRAM_CUSTOM_ENUMERATION(
181       "Net.HttpResponseCode",
182       HttpUtil::MapStatusCodeForHistogram(response_code_),
183       // Note the third argument is only
184       // evaluated once, see macro
185       // definition for details.
186       HttpUtil::GetStatusCodesForHistogram());
187 }
188 
HttpResponseHeaders(base::PickleIterator * iter)189 HttpResponseHeaders::HttpResponseHeaders(base::PickleIterator* iter)
190     : response_code_(-1) {
191   std::string raw_input;
192   if (iter->ReadString(&raw_input))
193     Parse(raw_input);
194 }
195 
TryToCreate(base::StringPiece headers)196 scoped_refptr<HttpResponseHeaders> HttpResponseHeaders::TryToCreate(
197     base::StringPiece headers) {
198   // Reject strings with nulls.
199   if (HasEmbeddedNulls(headers) ||
200       headers.size() > std::numeric_limits<int>::max()) {
201     return nullptr;
202   }
203 
204   return base::MakeRefCounted<HttpResponseHeaders>(
205       HttpUtil::AssembleRawHeaders(headers));
206 }
207 
Persist(base::Pickle * pickle,PersistOptions options)208 void HttpResponseHeaders::Persist(base::Pickle* pickle,
209                                   PersistOptions options) {
210   if (options == PERSIST_RAW) {
211     pickle->WriteString(raw_headers_);
212     return;  // Done.
213   }
214 
215   HeaderSet filter_headers;
216 
217   // Construct set of headers to filter out based on options.
218   if ((options & PERSIST_SANS_NON_CACHEABLE) == PERSIST_SANS_NON_CACHEABLE)
219     AddNonCacheableHeaders(&filter_headers);
220 
221   if ((options & PERSIST_SANS_COOKIES) == PERSIST_SANS_COOKIES)
222     AddCookieHeaders(&filter_headers);
223 
224   if ((options & PERSIST_SANS_CHALLENGES) == PERSIST_SANS_CHALLENGES)
225     AddChallengeHeaders(&filter_headers);
226 
227   if ((options & PERSIST_SANS_HOP_BY_HOP) == PERSIST_SANS_HOP_BY_HOP)
228     AddHopByHopHeaders(&filter_headers);
229 
230   if ((options & PERSIST_SANS_RANGES) == PERSIST_SANS_RANGES)
231     AddHopContentRangeHeaders(&filter_headers);
232 
233   if ((options & PERSIST_SANS_SECURITY_STATE) == PERSIST_SANS_SECURITY_STATE)
234     AddSecurityStateHeaders(&filter_headers);
235 
236   std::string blob;
237   blob.reserve(raw_headers_.size());
238 
239   // This copies the status line w/ terminator null.
240   // Note raw_headers_ has embedded nulls instead of \n,
241   // so this just copies the first header line.
242   blob.assign(raw_headers_.c_str(), strlen(raw_headers_.c_str()) + 1);
243 
244   for (size_t i = 0; i < parsed_.size(); ++i) {
245     DCHECK(!parsed_[i].is_continuation());
246 
247     // Locate the start of the next header.
248     size_t k = i;
249     while (++k < parsed_.size() && parsed_[k].is_continuation()) {}
250     --k;
251 
252     std::string header_name = base::ToLowerASCII(
253         base::MakeStringPiece(parsed_[i].name_begin, parsed_[i].name_end));
254     if (filter_headers.find(header_name) == filter_headers.end()) {
255       // Make sure there is a null after the value.
256       blob.append(parsed_[i].name_begin, parsed_[k].value_end);
257       blob.push_back('\0');
258     }
259 
260     i = k;
261   }
262   blob.push_back('\0');
263 
264   pickle->WriteString(blob);
265 }
266 
Update(const HttpResponseHeaders & new_headers)267 void HttpResponseHeaders::Update(const HttpResponseHeaders& new_headers) {
268   DCHECK(new_headers.response_code() == net::HTTP_NOT_MODIFIED ||
269          new_headers.response_code() == net::HTTP_PARTIAL_CONTENT);
270 
271   // Copy up to the null byte.  This just copies the status line.
272   std::string new_raw_headers(raw_headers_.c_str());
273   new_raw_headers.push_back('\0');
274 
275   HeaderSet updated_headers;
276 
277   // NOTE: we write the new headers then the old headers for convenience.  The
278   // order should not matter.
279 
280   // Figure out which headers we want to take from new_headers:
281   for (size_t i = 0; i < new_headers.parsed_.size(); ++i) {
282     const HeaderList& new_parsed = new_headers.parsed_;
283 
284     DCHECK(!new_parsed[i].is_continuation());
285 
286     // Locate the start of the next header.
287     size_t k = i;
288     while (++k < new_parsed.size() && new_parsed[k].is_continuation()) {}
289     --k;
290 
291     auto name =
292         base::MakeStringPiece(new_parsed[i].name_begin, new_parsed[i].name_end);
293     if (ShouldUpdateHeader(name)) {
294       std::string name_lower = base::ToLowerASCII(name);
295       updated_headers.insert(name_lower);
296 
297       // Preserve this header line in the merged result, making sure there is
298       // a null after the value.
299       new_raw_headers.append(new_parsed[i].name_begin, new_parsed[k].value_end);
300       new_raw_headers.push_back('\0');
301     }
302 
303     i = k;
304   }
305 
306   // Now, build the new raw headers.
307   MergeWithHeaders(std::move(new_raw_headers), updated_headers);
308 }
309 
MergeWithHeaders(std::string raw_headers,const HeaderSet & headers_to_remove)310 void HttpResponseHeaders::MergeWithHeaders(std::string raw_headers,
311                                            const HeaderSet& headers_to_remove) {
312   for (size_t i = 0; i < parsed_.size(); ++i) {
313     DCHECK(!parsed_[i].is_continuation());
314 
315     // Locate the start of the next header.
316     size_t k = i;
317     while (++k < parsed_.size() && parsed_[k].is_continuation()) {}
318     --k;
319 
320     std::string name = base::ToLowerASCII(
321         base::MakeStringPiece(parsed_[i].name_begin, parsed_[i].name_end));
322     if (headers_to_remove.find(name) == headers_to_remove.end()) {
323       // It's ok to preserve this header in the final result.
324       raw_headers.append(parsed_[i].name_begin, parsed_[k].value_end);
325       raw_headers.push_back('\0');
326     }
327 
328     i = k;
329   }
330   raw_headers.push_back('\0');
331 
332   // Make this object hold the new data.
333   raw_headers_.clear();
334   parsed_.clear();
335   Parse(raw_headers);
336 }
337 
RemoveHeader(base::StringPiece name)338 void HttpResponseHeaders::RemoveHeader(base::StringPiece name) {
339   // Copy up to the null byte.  This just copies the status line.
340   std::string new_raw_headers(raw_headers_.c_str());
341   new_raw_headers.push_back('\0');
342 
343   HeaderSet to_remove;
344   to_remove.insert(base::ToLowerASCII(name));
345   MergeWithHeaders(std::move(new_raw_headers), to_remove);
346 }
347 
RemoveHeaders(const std::unordered_set<std::string> & header_names)348 void HttpResponseHeaders::RemoveHeaders(
349     const std::unordered_set<std::string>& header_names) {
350   // Copy up to the null byte.  This just copies the status line.
351   std::string new_raw_headers(raw_headers_.c_str());
352   new_raw_headers.push_back('\0');
353 
354   HeaderSet to_remove;
355   for (const auto& header_name : header_names) {
356     to_remove.insert(base::ToLowerASCII(header_name));
357   }
358   MergeWithHeaders(std::move(new_raw_headers), to_remove);
359 }
360 
RemoveHeaderLine(const std::string & name,const std::string & value)361 void HttpResponseHeaders::RemoveHeaderLine(const std::string& name,
362                                            const std::string& value) {
363   std::string name_lowercase = base::ToLowerASCII(name);
364 
365   std::string new_raw_headers(GetStatusLine());
366   new_raw_headers.push_back('\0');
367 
368   new_raw_headers.reserve(raw_headers_.size());
369 
370   size_t iter = 0;
371   std::string old_header_name;
372   std::string old_header_value;
373   while (EnumerateHeaderLines(&iter, &old_header_name, &old_header_value)) {
374     std::string old_header_name_lowercase = base::ToLowerASCII(old_header_name);
375     if (name_lowercase == old_header_name_lowercase &&
376         value == old_header_value)
377       continue;
378 
379     new_raw_headers.append(old_header_name);
380     new_raw_headers.push_back(':');
381     new_raw_headers.push_back(' ');
382     new_raw_headers.append(old_header_value);
383     new_raw_headers.push_back('\0');
384   }
385   new_raw_headers.push_back('\0');
386 
387   // Make this object hold the new data.
388   raw_headers_.clear();
389   parsed_.clear();
390   Parse(new_raw_headers);
391 }
392 
AddHeader(base::StringPiece name,base::StringPiece value)393 void HttpResponseHeaders::AddHeader(base::StringPiece name,
394                                     base::StringPiece value) {
395   DCHECK(HttpUtil::IsValidHeaderName(name));
396   DCHECK(HttpUtil::IsValidHeaderValue(value));
397 
398   // Don't copy the last null.
399   std::string new_raw_headers(raw_headers_, 0, raw_headers_.size() - 1);
400   new_raw_headers.append(name.begin(), name.end());
401   new_raw_headers.append(": ");
402   new_raw_headers.append(value.begin(), value.end());
403   new_raw_headers.push_back('\0');
404   new_raw_headers.push_back('\0');
405 
406   // Make this object hold the new data.
407   raw_headers_.clear();
408   parsed_.clear();
409   Parse(new_raw_headers);
410 }
411 
SetHeader(base::StringPiece name,base::StringPiece value)412 void HttpResponseHeaders::SetHeader(base::StringPiece name,
413                                     base::StringPiece value) {
414   RemoveHeader(name);
415   AddHeader(name, value);
416 }
417 
AddCookie(const std::string & cookie_string)418 void HttpResponseHeaders::AddCookie(const std::string& cookie_string) {
419   AddHeader("Set-Cookie", cookie_string);
420 }
421 
ReplaceStatusLine(const std::string & new_status)422 void HttpResponseHeaders::ReplaceStatusLine(const std::string& new_status) {
423   CheckDoesNotHaveEmbeddedNulls(new_status);
424   // Copy up to the null byte.  This just copies the status line.
425   std::string new_raw_headers(new_status);
426   new_raw_headers.push_back('\0');
427 
428   HeaderSet empty_to_remove;
429   MergeWithHeaders(std::move(new_raw_headers), empty_to_remove);
430 }
431 
UpdateWithNewRange(const HttpByteRange & byte_range,int64_t resource_size,bool replace_status_line)432 void HttpResponseHeaders::UpdateWithNewRange(const HttpByteRange& byte_range,
433                                              int64_t resource_size,
434                                              bool replace_status_line) {
435   DCHECK(byte_range.IsValid());
436   DCHECK(byte_range.HasFirstBytePosition());
437   DCHECK(byte_range.HasLastBytePosition());
438 
439   const char kLengthHeader[] = "Content-Length";
440   const char kRangeHeader[] = "Content-Range";
441 
442   RemoveHeader(kLengthHeader);
443   RemoveHeader(kRangeHeader);
444 
445   int64_t start = byte_range.first_byte_position();
446   int64_t end = byte_range.last_byte_position();
447   int64_t range_len = end - start + 1;
448 
449   if (replace_status_line)
450     ReplaceStatusLine("HTTP/1.1 206 Partial Content");
451 
452   AddHeader(kRangeHeader,
453             base::StringPrintf("bytes %" PRId64 "-%" PRId64 "/%" PRId64, start,
454                                end, resource_size));
455   AddHeader(kLengthHeader, base::StringPrintf("%" PRId64, range_len));
456 }
457 
Parse(const std::string & raw_input)458 void HttpResponseHeaders::Parse(const std::string& raw_input) {
459   raw_headers_.reserve(raw_input.size());
460 
461   // ParseStatusLine adds a normalized status line to raw_headers_
462   std::string::const_iterator line_begin = raw_input.begin();
463   std::string::const_iterator line_end = base::ranges::find(raw_input, '\0');
464   // has_headers = true, if there is any data following the status line.
465   // Used by ParseStatusLine() to decide if a HTTP/0.9 is really a HTTP/1.0.
466   bool has_headers =
467       (line_end != raw_input.end() && (line_end + 1) != raw_input.end() &&
468        *(line_end + 1) != '\0');
469   ParseStatusLine(line_begin, line_end, has_headers);
470   raw_headers_.push_back('\0');  // Terminate status line with a null.
471 
472   if (line_end == raw_input.end()) {
473     raw_headers_.push_back('\0');  // Ensure the headers end with a double null.
474 
475     DCHECK_EQ('\0', raw_headers_[raw_headers_.size() - 2]);
476     DCHECK_EQ('\0', raw_headers_[raw_headers_.size() - 1]);
477     return;
478   }
479 
480   // Including a terminating null byte.
481   size_t status_line_len = raw_headers_.size();
482 
483   // Now, we add the rest of the raw headers to raw_headers_, and begin parsing
484   // it (to populate our parsed_ vector).
485   raw_headers_.append(line_end + 1, raw_input.end());
486 
487   // Ensure the headers end with a double null.
488   while (raw_headers_.size() < 2 ||
489          raw_headers_[raw_headers_.size() - 2] != '\0' ||
490          raw_headers_[raw_headers_.size() - 1] != '\0') {
491     raw_headers_.push_back('\0');
492   }
493 
494   // Adjust to point at the null byte following the status line
495   line_end = raw_headers_.begin() + status_line_len - 1;
496 
497   HttpUtil::HeadersIterator headers(line_end + 1, raw_headers_.end(),
498                                     std::string(1, '\0'));
499   while (headers.GetNext()) {
500     AddHeader(headers.name_begin(), headers.name_end(), headers.values_begin(),
501               headers.values_end());
502   }
503 
504   DCHECK_EQ('\0', raw_headers_[raw_headers_.size() - 2]);
505   DCHECK_EQ('\0', raw_headers_[raw_headers_.size() - 1]);
506 }
507 
GetNormalizedHeader(base::StringPiece name,std::string * value) const508 bool HttpResponseHeaders::GetNormalizedHeader(base::StringPiece name,
509                                               std::string* value) const {
510   // If you hit this assertion, please use EnumerateHeader instead!
511   DCHECK(!HttpUtil::IsNonCoalescingHeader(name));
512 
513   value->clear();
514 
515   bool found = false;
516   size_t i = 0;
517   while (i < parsed_.size()) {
518     i = FindHeader(i, name);
519     if (i == std::string::npos)
520       break;
521 
522     if (found)
523       value->append(", ");
524 
525     found = true;
526 
527     std::string::const_iterator value_begin = parsed_[i].value_begin;
528     std::string::const_iterator value_end = parsed_[i].value_end;
529     while (++i < parsed_.size() && parsed_[i].is_continuation())
530       value_end = parsed_[i].value_end;
531     value->append(value_begin, value_end);
532   }
533 
534   return found;
535 }
536 
GetStatusLine() const537 std::string HttpResponseHeaders::GetStatusLine() const {
538   // copy up to the null byte.
539   return std::string(raw_headers_.c_str());
540 }
541 
GetStatusText() const542 std::string HttpResponseHeaders::GetStatusText() const {
543   // GetStatusLine() is already normalized, so it has the format:
544   // '<http_version> SP <response_code>' or
545   // '<http_version> SP <response_code> SP <status_text>'.
546   std::string status_text = GetStatusLine();
547   // Seek to beginning of <response_code>.
548   std::string::const_iterator begin = base::ranges::find(status_text, ' ');
549   std::string::const_iterator end = status_text.end();
550   CHECK(begin != end);
551   ++begin;
552   CHECK(begin != end);
553   // See if there is another space.
554   begin = std::find(begin, end, ' ');
555   if (begin == end)
556     return std::string();
557   ++begin;
558   CHECK(begin != end);
559   return std::string(begin, end);
560 }
561 
EnumerateHeaderLines(size_t * iter,std::string * name,std::string * value) const562 bool HttpResponseHeaders::EnumerateHeaderLines(size_t* iter,
563                                                std::string* name,
564                                                std::string* value) const {
565   size_t i = *iter;
566   if (i == parsed_.size())
567     return false;
568 
569   DCHECK(!parsed_[i].is_continuation());
570 
571   name->assign(parsed_[i].name_begin, parsed_[i].name_end);
572 
573   std::string::const_iterator value_begin = parsed_[i].value_begin;
574   std::string::const_iterator value_end = parsed_[i].value_end;
575   while (++i < parsed_.size() && parsed_[i].is_continuation())
576     value_end = parsed_[i].value_end;
577 
578   value->assign(value_begin, value_end);
579 
580   *iter = i;
581   return true;
582 }
583 
EnumerateHeader(size_t * iter,base::StringPiece name,std::string * value) const584 bool HttpResponseHeaders::EnumerateHeader(size_t* iter,
585                                           base::StringPiece name,
586                                           std::string* value) const {
587   size_t i;
588   if (!iter || !*iter) {
589     i = FindHeader(0, name);
590   } else {
591     i = *iter;
592     if (i >= parsed_.size()) {
593       i = std::string::npos;
594     } else if (!parsed_[i].is_continuation()) {
595       i = FindHeader(i, name);
596     }
597   }
598 
599   if (i == std::string::npos) {
600     value->clear();
601     return false;
602   }
603 
604   if (iter)
605     *iter = i + 1;
606   value->assign(parsed_[i].value_begin, parsed_[i].value_end);
607   return true;
608 }
609 
HasHeaderValue(base::StringPiece name,base::StringPiece value) const610 bool HttpResponseHeaders::HasHeaderValue(base::StringPiece name,
611                                          base::StringPiece value) const {
612   // The value has to be an exact match.  This is important since
613   // 'cache-control: no-cache' != 'cache-control: no-cache="foo"'
614   size_t iter = 0;
615   std::string temp;
616   while (EnumerateHeader(&iter, name, &temp)) {
617     if (base::EqualsCaseInsensitiveASCII(value, temp))
618       return true;
619   }
620   return false;
621 }
622 
HasHeader(base::StringPiece name) const623 bool HttpResponseHeaders::HasHeader(base::StringPiece name) const {
624   return FindHeader(0, name) != std::string::npos;
625 }
626 
627 HttpResponseHeaders::~HttpResponseHeaders() = default;
628 
629 // Note: this implementation implicitly assumes that line_end points at a valid
630 // sentinel character (such as '\0').
631 // static
ParseVersion(std::string::const_iterator line_begin,std::string::const_iterator line_end)632 HttpVersion HttpResponseHeaders::ParseVersion(
633     std::string::const_iterator line_begin,
634     std::string::const_iterator line_end) {
635   std::string::const_iterator p = line_begin;
636 
637   // RFC9112 Section 2.3:
638   // HTTP-version  = HTTP-name "/" DIGIT "." DIGIT
639   // HTTP-name     = %s"HTTP"
640 
641   if (!base::StartsWith(base::MakeStringPiece(line_begin, line_end), "http",
642                         base::CompareCase::INSENSITIVE_ASCII)) {
643     DVLOG(1) << "missing status line";
644     return HttpVersion();
645   }
646 
647   p += 4;
648 
649   if (p >= line_end || *p != '/') {
650     DVLOG(1) << "missing version";
651     return HttpVersion();
652   }
653 
654   std::string::const_iterator dot = std::find(p, line_end, '.');
655   if (dot == line_end) {
656     DVLOG(1) << "malformed version";
657     return HttpVersion();
658   }
659 
660   ++p;  // from / to first digit.
661   ++dot;  // from . to second digit.
662 
663   if (!(base::IsAsciiDigit(*p) && base::IsAsciiDigit(*dot))) {
664     DVLOG(1) << "malformed version number";
665     return HttpVersion();
666   }
667 
668   uint16_t major = *p - '0';
669   uint16_t minor = *dot - '0';
670 
671   return HttpVersion(major, minor);
672 }
673 
674 // Note: this implementation implicitly assumes that line_end points at a valid
675 // sentinel character (such as '\0').
ParseStatusLine(std::string::const_iterator line_begin,std::string::const_iterator line_end,bool has_headers)676 void HttpResponseHeaders::ParseStatusLine(
677     std::string::const_iterator line_begin,
678     std::string::const_iterator line_end,
679     bool has_headers) {
680   // Extract the version number
681   HttpVersion parsed_http_version = ParseVersion(line_begin, line_end);
682 
683   // Clamp the version number to one of: {0.9, 1.0, 1.1, 2.0}
684   if (parsed_http_version == HttpVersion(0, 9) && !has_headers) {
685     http_version_ = HttpVersion(0, 9);
686     raw_headers_ = "HTTP/0.9";
687   } else if (parsed_http_version == HttpVersion(2, 0)) {
688     http_version_ = HttpVersion(2, 0);
689     raw_headers_ = "HTTP/2.0";
690   } else if (parsed_http_version >= HttpVersion(1, 1)) {
691     http_version_ = HttpVersion(1, 1);
692     raw_headers_ = "HTTP/1.1";
693   } else {
694     // Treat everything else like HTTP 1.0
695     http_version_ = HttpVersion(1, 0);
696     raw_headers_ = "HTTP/1.0";
697   }
698   if (parsed_http_version != http_version_) {
699     DVLOG(1) << "assuming HTTP/" << http_version_.major_value() << "."
700              << http_version_.minor_value();
701   }
702 
703   // TODO(eroman): this doesn't make sense if ParseVersion failed.
704   std::string::const_iterator p = std::find(line_begin, line_end, ' ');
705 
706   if (p == line_end) {
707     DVLOG(1) << "missing response status; assuming 200 OK";
708     raw_headers_.append(" 200 OK");
709     response_code_ = net::HTTP_OK;
710     return;
711   }
712 
713   // Skip whitespace.
714   while (p < line_end && *p == ' ')
715     ++p;
716 
717   std::string::const_iterator code = p;
718   while (p < line_end && base::IsAsciiDigit(*p))
719     ++p;
720 
721   if (p == code) {
722     DVLOG(1) << "missing response status number; assuming 200";
723     raw_headers_.append(" 200");
724     response_code_ = net::HTTP_OK;
725     return;
726   }
727   raw_headers_.push_back(' ');
728   raw_headers_.append(code, p);
729   base::StringToInt(base::MakeStringPiece(code, p), &response_code_);
730 
731   // Skip whitespace.
732   while (p < line_end && *p == ' ')
733     ++p;
734 
735   // Trim trailing whitespace.
736   while (line_end > p && line_end[-1] == ' ')
737     --line_end;
738 
739   if (p == line_end)
740     return;
741 
742   raw_headers_.push_back(' ');
743   raw_headers_.append(p, line_end);
744 }
745 
FindHeader(size_t from,base::StringPiece search) const746 size_t HttpResponseHeaders::FindHeader(size_t from,
747                                        base::StringPiece search) const {
748   for (size_t i = from; i < parsed_.size(); ++i) {
749     if (parsed_[i].is_continuation())
750       continue;
751     auto name =
752         base::MakeStringPiece(parsed_[i].name_begin, parsed_[i].name_end);
753     if (base::EqualsCaseInsensitiveASCII(search, name))
754       return i;
755   }
756 
757   return std::string::npos;
758 }
759 
GetCacheControlDirective(base::StringPiece directive,base::TimeDelta * result) const760 bool HttpResponseHeaders::GetCacheControlDirective(
761     base::StringPiece directive,
762     base::TimeDelta* result) const {
763   static constexpr base::StringPiece name("cache-control");
764   std::string value;
765 
766   size_t directive_size = directive.size();
767 
768   size_t iter = 0;
769   while (EnumerateHeader(&iter, name, &value)) {
770     if (!base::StartsWith(value, directive,
771                           base::CompareCase::INSENSITIVE_ASCII)) {
772       continue;
773     }
774     if (value.size() == directive_size || value[directive_size] != '=')
775       continue;
776     // 1*DIGIT with leading and trailing spaces, as described at
777     // https://datatracker.ietf.org/doc/html/rfc7234#section-1.2.1.
778     auto start = value.cbegin() + directive_size + 1;
779     auto end = value.cend();
780     while (start < end && *start == ' ') {
781       // leading spaces
782       ++start;
783     }
784     while (start < end - 1 && *(end - 1) == ' ') {
785       // trailing spaces
786       --end;
787     }
788     if (start == end ||
789         !std::all_of(start, end, [](char c) { return '0' <= c && c <= '9'; })) {
790       continue;
791     }
792     int64_t seconds = 0;
793     base::StringToInt64(base::MakeStringPiece(start, end), &seconds);
794     // We ignore the return value because we've already checked the input
795     // string. For the overflow case we use
796     // base::TimeDelta::FiniteMax().InSeconds().
797     seconds = std::min(seconds, base::TimeDelta::FiniteMax().InSeconds());
798     *result = base::Seconds(seconds);
799     return true;
800   }
801 
802   return false;
803 }
804 
AddHeader(std::string::const_iterator name_begin,std::string::const_iterator name_end,std::string::const_iterator values_begin,std::string::const_iterator values_end)805 void HttpResponseHeaders::AddHeader(std::string::const_iterator name_begin,
806                                     std::string::const_iterator name_end,
807                                     std::string::const_iterator values_begin,
808                                     std::string::const_iterator values_end) {
809   // If the header can be coalesced, then we should split it up.
810   if (values_begin == values_end ||
811       HttpUtil::IsNonCoalescingHeader(
812           base::MakeStringPiece(name_begin, name_end))) {
813     AddToParsed(name_begin, name_end, values_begin, values_end);
814   } else {
815     HttpUtil::ValuesIterator it(values_begin, values_end, ',',
816                                 false /* ignore_empty_values */);
817     while (it.GetNext()) {
818       AddToParsed(name_begin, name_end, it.value_begin(), it.value_end());
819       // clobber these so that subsequent values are treated as continuations
820       name_begin = name_end = raw_headers_.end();
821     }
822   }
823 }
824 
AddToParsed(std::string::const_iterator name_begin,std::string::const_iterator name_end,std::string::const_iterator value_begin,std::string::const_iterator value_end)825 void HttpResponseHeaders::AddToParsed(std::string::const_iterator name_begin,
826                                       std::string::const_iterator name_end,
827                                       std::string::const_iterator value_begin,
828                                       std::string::const_iterator value_end) {
829   ParsedHeader header;
830   header.name_begin = name_begin;
831   header.name_end = name_end;
832   header.value_begin = value_begin;
833   header.value_end = value_end;
834   parsed_.push_back(header);
835 }
836 
AddNonCacheableHeaders(HeaderSet * result) const837 void HttpResponseHeaders::AddNonCacheableHeaders(HeaderSet* result) const {
838   // Add server specified transients.  Any 'cache-control: no-cache="foo,bar"'
839   // headers present in the response specify additional headers that we should
840   // not store in the cache.
841   const char kCacheControl[] = "cache-control";
842   const char kPrefix[] = "no-cache=\"";
843   const size_t kPrefixLen = sizeof(kPrefix) - 1;
844 
845   std::string value;
846   size_t iter = 0;
847   while (EnumerateHeader(&iter, kCacheControl, &value)) {
848     // If the value is smaller than the prefix and a terminal quote, skip
849     // it.
850     if (value.size() <= kPrefixLen ||
851         value.compare(0, kPrefixLen, kPrefix) != 0) {
852       continue;
853     }
854     // if it doesn't end with a quote, then treat as malformed
855     if (value[value.size() - 1] != '\"')
856       continue;
857 
858     // process the value as a comma-separated list of items. Each
859     // item can be wrapped by linear white space.
860     std::string::const_iterator item = value.begin() + kPrefixLen;
861     std::string::const_iterator end = value.end() - 1;
862     while (item != end) {
863       // Find the comma to compute the length of the current item,
864       // and the position of the next one.
865       std::string::const_iterator item_next = std::find(item, end, ',');
866       std::string::const_iterator item_end = end;
867       if (item_next != end) {
868         // Skip over comma for next position.
869         item_end = item_next;
870         item_next++;
871       }
872       // trim off leading and trailing whitespace in this item.
873       HttpUtil::TrimLWS(&item, &item_end);
874 
875       // assuming the header is not empty, lowercase and insert into set
876       if (item_end > item) {
877         result->insert(
878             base::ToLowerASCII(base::StringPiece(&*item, item_end - item)));
879       }
880 
881       // Continue to next item.
882       item = item_next;
883     }
884   }
885 }
886 
AddHopByHopHeaders(HeaderSet * result)887 void HttpResponseHeaders::AddHopByHopHeaders(HeaderSet* result) {
888   for (const auto* header : kHopByHopResponseHeaders)
889     result->insert(std::string(header));
890 }
891 
AddCookieHeaders(HeaderSet * result)892 void HttpResponseHeaders::AddCookieHeaders(HeaderSet* result) {
893   for (const auto* header : kCookieResponseHeaders)
894     result->insert(std::string(header));
895 }
896 
AddChallengeHeaders(HeaderSet * result)897 void HttpResponseHeaders::AddChallengeHeaders(HeaderSet* result) {
898   for (const auto* header : kChallengeResponseHeaders)
899     result->insert(std::string(header));
900 }
901 
AddHopContentRangeHeaders(HeaderSet * result)902 void HttpResponseHeaders::AddHopContentRangeHeaders(HeaderSet* result) {
903   result->insert(kContentRange);
904 }
905 
AddSecurityStateHeaders(HeaderSet * result)906 void HttpResponseHeaders::AddSecurityStateHeaders(HeaderSet* result) {
907   for (const auto* header : kSecurityStateHeaders)
908     result->insert(std::string(header));
909 }
910 
GetMimeTypeAndCharset(std::string * mime_type,std::string * charset) const911 void HttpResponseHeaders::GetMimeTypeAndCharset(std::string* mime_type,
912                                                 std::string* charset) const {
913   mime_type->clear();
914   charset->clear();
915 
916   std::string name = "content-type";
917   std::string value;
918 
919   bool had_charset = false;
920 
921   size_t iter = 0;
922   while (EnumerateHeader(&iter, name, &value))
923     HttpUtil::ParseContentType(value, mime_type, charset, &had_charset,
924                                nullptr);
925 }
926 
GetMimeType(std::string * mime_type) const927 bool HttpResponseHeaders::GetMimeType(std::string* mime_type) const {
928   std::string unused;
929   GetMimeTypeAndCharset(mime_type, &unused);
930   return !mime_type->empty();
931 }
932 
GetCharset(std::string * charset) const933 bool HttpResponseHeaders::GetCharset(std::string* charset) const {
934   std::string unused;
935   GetMimeTypeAndCharset(&unused, charset);
936   return !charset->empty();
937 }
938 
IsRedirect(std::string * location) const939 bool HttpResponseHeaders::IsRedirect(std::string* location) const {
940   if (!IsRedirectResponseCode(response_code_))
941     return false;
942 
943   // If we lack a Location header, then we can't treat this as a redirect.
944   // We assume that the first non-empty location value is the target URL that
945   // we want to follow.  TODO(darin): Is this consistent with other browsers?
946   size_t i = std::string::npos;
947   do {
948     i = FindHeader(++i, "location");
949     if (i == std::string::npos)
950       return false;
951     // If the location value is empty, then it doesn't count.
952   } while (parsed_[i].value_begin == parsed_[i].value_end);
953 
954   if (location) {
955     auto location_strpiece =
956         base::MakeStringPiece(parsed_[i].value_begin, parsed_[i].value_end);
957     // Escape any non-ASCII characters to preserve them.  The server should
958     // only be returning ASCII here, but for compat we need to do this.
959     //
960     // The URL parser escapes things internally, but it expect the bytes to be
961     // valid UTF-8, so encoding errors turn into replacement characters before
962     // escaping. Escaping here preserves the bytes as-is. See
963     // https://crbug.com/942073#c14.
964     *location = base::EscapeNonASCII(location_strpiece);
965   }
966 
967   return true;
968 }
969 
970 // static
IsRedirectResponseCode(int response_code)971 bool HttpResponseHeaders::IsRedirectResponseCode(int response_code) {
972   // Users probably want to see 300 (multiple choice) pages, so we don't count
973   // them as redirects that need to be followed.
974   return (response_code == net::HTTP_MOVED_PERMANENTLY ||
975           response_code == net::HTTP_FOUND ||
976           response_code == net::HTTP_SEE_OTHER ||
977           response_code == net::HTTP_TEMPORARY_REDIRECT ||
978           response_code == net::HTTP_PERMANENT_REDIRECT);
979 }
980 
981 // From RFC 2616 section 13.2.4:
982 //
983 // The calculation to determine if a response has expired is quite simple:
984 //
985 //   response_is_fresh = (freshness_lifetime > current_age)
986 //
987 // Of course, there are other factors that can force a response to always be
988 // validated or re-fetched.
989 //
990 // From RFC 5861 section 3, a stale response may be used while revalidation is
991 // performed in the background if
992 //
993 //   freshness_lifetime + stale_while_revalidate > current_age
994 //
RequiresValidation(const Time & request_time,const Time & response_time,const Time & current_time) const995 ValidationType HttpResponseHeaders::RequiresValidation(
996     const Time& request_time,
997     const Time& response_time,
998     const Time& current_time) const {
999   FreshnessLifetimes lifetimes = GetFreshnessLifetimes(response_time);
1000   if (lifetimes.freshness.is_zero() && lifetimes.staleness.is_zero())
1001     return VALIDATION_SYNCHRONOUS;
1002 
1003   base::TimeDelta age =
1004       GetCurrentAge(request_time, response_time, current_time);
1005 
1006   if (lifetimes.freshness > age)
1007     return VALIDATION_NONE;
1008 
1009   if (lifetimes.freshness + lifetimes.staleness > age)
1010     return VALIDATION_ASYNCHRONOUS;
1011 
1012   return VALIDATION_SYNCHRONOUS;
1013 }
1014 
1015 // From RFC 2616 section 13.2.4:
1016 //
1017 // The max-age directive takes priority over Expires, so if max-age is present
1018 // in a response, the calculation is simply:
1019 //
1020 //   freshness_lifetime = max_age_value
1021 //
1022 // Otherwise, if Expires is present in the response, the calculation is:
1023 //
1024 //   freshness_lifetime = expires_value - date_value
1025 //
1026 // Note that neither of these calculations is vulnerable to clock skew, since
1027 // all of the information comes from the origin server.
1028 //
1029 // Also, if the response does have a Last-Modified time, the heuristic
1030 // expiration value SHOULD be no more than some fraction of the interval since
1031 // that time. A typical setting of this fraction might be 10%:
1032 //
1033 //   freshness_lifetime = (date_value - last_modified_value) * 0.10
1034 //
1035 // If the stale-while-revalidate directive is present, then it is used to set
1036 // the |staleness| time, unless it overridden by another directive.
1037 //
1038 HttpResponseHeaders::FreshnessLifetimes
GetFreshnessLifetimes(const Time & response_time) const1039 HttpResponseHeaders::GetFreshnessLifetimes(const Time& response_time) const {
1040   FreshnessLifetimes lifetimes;
1041   // Check for headers that force a response to never be fresh.  For backwards
1042   // compat, we treat "Pragma: no-cache" as a synonym for "Cache-Control:
1043   // no-cache" even though RFC 2616 does not specify it.
1044   if (HasHeaderValue("cache-control", "no-cache") ||
1045       HasHeaderValue("cache-control", "no-store") ||
1046       HasHeaderValue("pragma", "no-cache")) {
1047     return lifetimes;
1048   }
1049 
1050   // Cache-Control directive must_revalidate overrides stale-while-revalidate.
1051   bool must_revalidate = HasHeaderValue("cache-control", "must-revalidate");
1052 
1053   if (must_revalidate || !GetStaleWhileRevalidateValue(&lifetimes.staleness)) {
1054     DCHECK_EQ(base::TimeDelta(), lifetimes.staleness);
1055   }
1056 
1057   // NOTE: "Cache-Control: max-age" overrides Expires, so we only check the
1058   // Expires header after checking for max-age in GetFreshnessLifetimes.  This
1059   // is important since "Expires: <date in the past>" means not fresh, but
1060   // it should not trump a max-age value.
1061   if (GetMaxAgeValue(&lifetimes.freshness))
1062     return lifetimes;
1063 
1064   // If there is no Date header, then assume that the server response was
1065   // generated at the time when we received the response.
1066   Time date_value;
1067   if (!GetDateValue(&date_value))
1068     date_value = response_time;
1069 
1070   Time expires_value;
1071   if (GetExpiresValue(&expires_value)) {
1072     // The expires value can be a date in the past!
1073     if (expires_value > date_value) {
1074       lifetimes.freshness = expires_value - date_value;
1075       return lifetimes;
1076     }
1077 
1078     DCHECK_EQ(base::TimeDelta(), lifetimes.freshness);
1079     return lifetimes;
1080   }
1081 
1082   // From RFC 2616 section 13.4:
1083   //
1084   //   A response received with a status code of 200, 203, 206, 300, 301 or 410
1085   //   MAY be stored by a cache and used in reply to a subsequent request,
1086   //   subject to the expiration mechanism, unless a cache-control directive
1087   //   prohibits caching.
1088   //   ...
1089   //   A response received with any other status code (e.g. status codes 302
1090   //   and 307) MUST NOT be returned in a reply to a subsequent request unless
1091   //   there are cache-control directives or another header(s) that explicitly
1092   //   allow it.
1093   //
1094   // From RFC 2616 section 14.9.4:
1095   //
1096   //   When the must-revalidate directive is present in a response received by
1097   //   a cache, that cache MUST NOT use the entry after it becomes stale to
1098   //   respond to a subsequent request without first revalidating it with the
1099   //   origin server. (I.e., the cache MUST do an end-to-end revalidation every
1100   //   time, if, based solely on the origin server's Expires or max-age value,
1101   //   the cached response is stale.)
1102   //
1103   // https://datatracker.ietf.org/doc/draft-reschke-http-status-308/ is an
1104   // experimental RFC that adds 308 permanent redirect as well, for which "any
1105   // future references ... SHOULD use one of the returned URIs."
1106   if ((response_code_ == net::HTTP_OK ||
1107        response_code_ == net::HTTP_NON_AUTHORITATIVE_INFORMATION ||
1108        response_code_ == net::HTTP_PARTIAL_CONTENT) &&
1109       !must_revalidate) {
1110     // TODO(darin): Implement a smarter heuristic.
1111     Time last_modified_value;
1112     if (GetLastModifiedValue(&last_modified_value)) {
1113       // The last-modified value can be a date in the future!
1114       if (last_modified_value <= date_value) {
1115         lifetimes.freshness = (date_value - last_modified_value) / 10;
1116         return lifetimes;
1117       }
1118     }
1119   }
1120 
1121   // These responses are implicitly fresh (unless otherwise overruled):
1122   if (response_code_ == net::HTTP_MULTIPLE_CHOICES ||
1123       response_code_ == net::HTTP_MOVED_PERMANENTLY ||
1124       response_code_ == net::HTTP_PERMANENT_REDIRECT ||
1125       response_code_ == net::HTTP_GONE) {
1126     lifetimes.freshness = base::TimeDelta::Max();
1127     lifetimes.staleness = base::TimeDelta();  // It should never be stale.
1128     return lifetimes;
1129   }
1130 
1131   // Our heuristic freshness estimate for this resource is 0 seconds, in
1132   // accordance with common browser behaviour. However, stale-while-revalidate
1133   // may still apply.
1134   DCHECK_EQ(base::TimeDelta(), lifetimes.freshness);
1135   return lifetimes;
1136 }
1137 
1138 // From RFC 7234 section 4.2.3:
1139 //
1140 // The following data is used for the age calculation:
1141 //
1142 //    age_value
1143 //
1144 //       The term "age_value" denotes the value of the Age header field
1145 //       (Section 5.1), in a form appropriate for arithmetic operation; or
1146 //       0, if not available.
1147 //
1148 //    date_value
1149 //
1150 //       The term "date_value" denotes the value of the Date header field,
1151 //       in a form appropriate for arithmetic operations.  See Section
1152 //       7.1.1.2 of [RFC7231] for the definition of the Date header field,
1153 //       and for requirements regarding responses without it.
1154 //
1155 //    now
1156 //
1157 //       The term "now" means "the current value of the clock at the host
1158 //       performing the calculation".  A host ought to use NTP ([RFC5905])
1159 //       or some similar protocol to synchronize its clocks to Coordinated
1160 //       Universal Time.
1161 //
1162 //    request_time
1163 //
1164 //       The current value of the clock at the host at the time the request
1165 //       resulting in the stored response was made.
1166 //
1167 //    response_time
1168 //
1169 //       The current value of the clock at the host at the time the
1170 //       response was received.
1171 //
1172 //    The age is then calculated as
1173 //
1174 //     apparent_age = max(0, response_time - date_value);
1175 //     response_delay = response_time - request_time;
1176 //     corrected_age_value = age_value + response_delay;
1177 //     corrected_initial_age = max(apparent_age, corrected_age_value);
1178 //     resident_time = now - response_time;
1179 //     current_age = corrected_initial_age + resident_time;
1180 //
GetCurrentAge(const Time & request_time,const Time & response_time,const Time & current_time) const1181 base::TimeDelta HttpResponseHeaders::GetCurrentAge(
1182     const Time& request_time,
1183     const Time& response_time,
1184     const Time& current_time) const {
1185   // If there is no Date header, then assume that the server response was
1186   // generated at the time when we received the response.
1187   Time date_value;
1188   if (!GetDateValue(&date_value))
1189     date_value = response_time;
1190 
1191   // If there is no Age header, then assume age is zero.  GetAgeValue does not
1192   // modify its out param if the value does not exist.
1193   base::TimeDelta age_value;
1194   GetAgeValue(&age_value);
1195 
1196   base::TimeDelta apparent_age =
1197       std::max(base::TimeDelta(), response_time - date_value);
1198   base::TimeDelta response_delay = response_time - request_time;
1199   base::TimeDelta corrected_age_value = age_value + response_delay;
1200   base::TimeDelta corrected_initial_age =
1201       std::max(apparent_age, corrected_age_value);
1202   base::TimeDelta resident_time = current_time - response_time;
1203   base::TimeDelta current_age = corrected_initial_age + resident_time;
1204 
1205   return current_age;
1206 }
1207 
GetMaxAgeValue(base::TimeDelta * result) const1208 bool HttpResponseHeaders::GetMaxAgeValue(base::TimeDelta* result) const {
1209   return GetCacheControlDirective("max-age", result);
1210 }
1211 
GetAgeValue(base::TimeDelta * result) const1212 bool HttpResponseHeaders::GetAgeValue(base::TimeDelta* result) const {
1213   std::string value;
1214   if (!EnumerateHeader(nullptr, "Age", &value))
1215     return false;
1216 
1217   // Parse the delta-seconds as 1*DIGIT.
1218   uint32_t seconds;
1219   ParseIntError error;
1220   if (!ParseUint32(value, ParseIntFormat::NON_NEGATIVE, &seconds, &error)) {
1221     if (error == ParseIntError::FAILED_OVERFLOW) {
1222       // If the Age value cannot fit in a uint32_t, saturate it to a maximum
1223       // value. This is similar to what RFC 2616 says in section 14.6 for how
1224       // caches should transmit values that overflow.
1225       seconds = std::numeric_limits<decltype(seconds)>::max();
1226     } else {
1227       return false;
1228     }
1229   }
1230 
1231   *result = base::Seconds(seconds);
1232   return true;
1233 }
1234 
GetDateValue(Time * result) const1235 bool HttpResponseHeaders::GetDateValue(Time* result) const {
1236   return GetTimeValuedHeader("Date", result);
1237 }
1238 
GetLastModifiedValue(Time * result) const1239 bool HttpResponseHeaders::GetLastModifiedValue(Time* result) const {
1240   return GetTimeValuedHeader("Last-Modified", result);
1241 }
1242 
GetExpiresValue(Time * result) const1243 bool HttpResponseHeaders::GetExpiresValue(Time* result) const {
1244   return GetTimeValuedHeader("Expires", result);
1245 }
1246 
GetStaleWhileRevalidateValue(base::TimeDelta * result) const1247 bool HttpResponseHeaders::GetStaleWhileRevalidateValue(
1248     base::TimeDelta* result) const {
1249   return GetCacheControlDirective("stale-while-revalidate", result);
1250 }
1251 
GetTimeValuedHeader(const std::string & name,Time * result) const1252 bool HttpResponseHeaders::GetTimeValuedHeader(const std::string& name,
1253                                               Time* result) const {
1254   std::string value;
1255   if (!EnumerateHeader(nullptr, name, &value))
1256     return false;
1257 
1258   // When parsing HTTP dates it's beneficial to default to GMT because:
1259   // 1. RFC2616 3.3.1 says times should always be specified in GMT
1260   // 2. Only counter-example incorrectly appended "UTC" (crbug.com/153759)
1261   // 3. When adjusting cookie expiration times for clock skew
1262   //    (crbug.com/135131) this better matches our cookie expiration
1263   //    time parser which ignores timezone specifiers and assumes GMT.
1264   // 4. This is exactly what Firefox does.
1265   // TODO(pauljensen): The ideal solution would be to return false if the
1266   // timezone could not be understood so as to avoid makeing other calculations
1267   // based on an incorrect time.  This would require modifying the time
1268   // library or duplicating the code. (http://crbug.com/158327)
1269   return Time::FromUTCString(value.c_str(), result);
1270 }
1271 
1272 // We accept the first value of "close" or "keep-alive" in a Connection or
1273 // Proxy-Connection header, in that order. Obeying "keep-alive" in HTTP/1.1 or
1274 // "close" in 1.0 is not strictly standards-compliant, but we'd like to
1275 // avoid looking at the Proxy-Connection header whenever it is reasonable to do
1276 // so.
1277 // TODO(ricea): Measure real-world usage of the "Proxy-Connection" header,
1278 // with a view to reducing support for it in order to make our Connection header
1279 // handling more RFC 7230 compliant.
IsKeepAlive() const1280 bool HttpResponseHeaders::IsKeepAlive() const {
1281   // NOTE: It is perhaps risky to assume that a Proxy-Connection header is
1282   // meaningful when we don't know that this response was from a proxy, but
1283   // Mozilla also does this, so we'll do the same.
1284   static const char* const kConnectionHeaders[] = {"connection",
1285                                                    "proxy-connection"};
1286   struct KeepAliveToken {
1287     const char* const token;
1288     bool keep_alive;
1289   };
1290   static const KeepAliveToken kKeepAliveTokens[] = {{"keep-alive", true},
1291                                                     {"close", false}};
1292 
1293   if (http_version_ < HttpVersion(1, 0))
1294     return false;
1295 
1296   for (const char* header : kConnectionHeaders) {
1297     size_t iterator = 0;
1298     std::string token;
1299     while (EnumerateHeader(&iterator, header, &token)) {
1300       for (const KeepAliveToken& keep_alive_token : kKeepAliveTokens) {
1301         if (base::EqualsCaseInsensitiveASCII(token, keep_alive_token.token))
1302           return keep_alive_token.keep_alive;
1303       }
1304     }
1305   }
1306   return http_version_ != HttpVersion(1, 0);
1307 }
1308 
HasStrongValidators() const1309 bool HttpResponseHeaders::HasStrongValidators() const {
1310   std::string etag_header;
1311   EnumerateHeader(nullptr, "etag", &etag_header);
1312   std::string last_modified_header;
1313   EnumerateHeader(nullptr, "Last-Modified", &last_modified_header);
1314   std::string date_header;
1315   EnumerateHeader(nullptr, "Date", &date_header);
1316   return HttpUtil::HasStrongValidators(GetHttpVersion(), etag_header,
1317                                        last_modified_header, date_header);
1318 }
1319 
HasValidators() const1320 bool HttpResponseHeaders::HasValidators() const {
1321   std::string etag_header;
1322   EnumerateHeader(nullptr, "etag", &etag_header);
1323   std::string last_modified_header;
1324   EnumerateHeader(nullptr, "Last-Modified", &last_modified_header);
1325   return HttpUtil::HasValidators(GetHttpVersion(), etag_header,
1326                                  last_modified_header);
1327 }
1328 
1329 // From RFC 2616:
1330 // Content-Length = "Content-Length" ":" 1*DIGIT
GetContentLength() const1331 int64_t HttpResponseHeaders::GetContentLength() const {
1332   return GetInt64HeaderValue("content-length");
1333 }
1334 
GetInt64HeaderValue(const std::string & header) const1335 int64_t HttpResponseHeaders::GetInt64HeaderValue(
1336     const std::string& header) const {
1337   size_t iter = 0;
1338   std::string content_length_val;
1339   if (!EnumerateHeader(&iter, header, &content_length_val))
1340     return -1;
1341 
1342   if (content_length_val.empty())
1343     return -1;
1344 
1345   if (content_length_val[0] == '+')
1346     return -1;
1347 
1348   int64_t result;
1349   bool ok = base::StringToInt64(content_length_val, &result);
1350   if (!ok || result < 0)
1351     return -1;
1352 
1353   return result;
1354 }
1355 
GetContentRangeFor206(int64_t * first_byte_position,int64_t * last_byte_position,int64_t * instance_length) const1356 bool HttpResponseHeaders::GetContentRangeFor206(
1357     int64_t* first_byte_position,
1358     int64_t* last_byte_position,
1359     int64_t* instance_length) const {
1360   size_t iter = 0;
1361   std::string content_range_spec;
1362   if (!EnumerateHeader(&iter, kContentRange, &content_range_spec)) {
1363     *first_byte_position = *last_byte_position = *instance_length = -1;
1364     return false;
1365   }
1366 
1367   return HttpUtil::ParseContentRangeHeaderFor206(
1368       content_range_spec, first_byte_position, last_byte_position,
1369       instance_length);
1370 }
1371 
NetLogParams(NetLogCaptureMode capture_mode) const1372 base::Value::Dict HttpResponseHeaders::NetLogParams(
1373     NetLogCaptureMode capture_mode) const {
1374   base::Value::Dict dict;
1375   base::Value::List headers;
1376   headers.Append(NetLogStringValue(GetStatusLine()));
1377   size_t iterator = 0;
1378   std::string name;
1379   std::string value;
1380   while (EnumerateHeaderLines(&iterator, &name, &value)) {
1381     std::string log_value =
1382         ElideHeaderValueForNetLog(capture_mode, name, value);
1383     headers.Append(NetLogStringValue(base::StrCat({name, ": ", log_value})));
1384   }
1385   dict.Set("headers", std::move(headers));
1386   return dict;
1387 }
1388 
IsChunkEncoded() const1389 bool HttpResponseHeaders::IsChunkEncoded() const {
1390   // Ignore spurious chunked responses from HTTP/1.0 servers and proxies.
1391   return GetHttpVersion() >= HttpVersion(1, 1) &&
1392          HasHeaderValue("Transfer-Encoding", "chunked");
1393 }
1394 
IsCookieResponseHeader(base::StringPiece name)1395 bool HttpResponseHeaders::IsCookieResponseHeader(base::StringPiece name) {
1396   for (const char* cookie_header : kCookieResponseHeaders) {
1397     if (base::EqualsCaseInsensitiveASCII(cookie_header, name))
1398       return true;
1399   }
1400   return false;
1401 }
1402 
WriteIntoTrace(perfetto::TracedValue context) const1403 void HttpResponseHeaders::WriteIntoTrace(perfetto::TracedValue context) const {
1404   perfetto::TracedDictionary dict = std::move(context).WriteDictionary();
1405   dict.Add("response_code", response_code_);
1406   dict.Add("headers", parsed_);
1407 }
1408 
1409 }  // namespace net
1410