• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
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 
14 #include "base/logging.h"
15 #include "base/metrics/histogram.h"
16 #include "base/pickle.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/string_piece.h"
19 #include "base/strings/string_util.h"
20 #include "base/strings/stringprintf.h"
21 #include "base/time/time.h"
22 #include "base/values.h"
23 #include "net/base/escape.h"
24 #include "net/http/http_util.h"
25 
26 using base::StringPiece;
27 using base::Time;
28 using base::TimeDelta;
29 
30 namespace net {
31 
32 //-----------------------------------------------------------------------------
33 
34 namespace {
35 
36 // These headers are RFC 2616 hop-by-hop headers;
37 // not to be stored by caches.
38 const char* const kHopByHopResponseHeaders[] = {
39   "connection",
40   "proxy-connection",
41   "keep-alive",
42   "trailer",
43   "transfer-encoding",
44   "upgrade"
45 };
46 
47 // These headers are challenge response headers;
48 // not to be stored by caches.
49 const char* const kChallengeResponseHeaders[] = {
50   "www-authenticate",
51   "proxy-authenticate"
52 };
53 
54 // These headers are cookie setting headers;
55 // not to be stored by caches or disclosed otherwise.
56 const char* const kCookieResponseHeaders[] = {
57   "set-cookie",
58   "set-cookie2"
59 };
60 
61 // By default, do not cache Strict-Transport-Security or Public-Key-Pins.
62 // This avoids erroneously re-processing them on page loads from cache ---
63 // they are defined to be valid only on live and error-free HTTPS
64 // connections.
65 const char* const kSecurityStateHeaders[] = {
66   "strict-transport-security",
67   "public-key-pins"
68 };
69 
70 // These response headers are not copied from a 304/206 response to the cached
71 // response headers.  This list is based on Mozilla's nsHttpResponseHead.cpp.
72 const char* const kNonUpdatedHeaders[] = {
73   "connection",
74   "proxy-connection",
75   "keep-alive",
76   "www-authenticate",
77   "proxy-authenticate",
78   "trailer",
79   "transfer-encoding",
80   "upgrade",
81   "etag",
82   "x-frame-options",
83   "x-xss-protection",
84 };
85 
86 // Some header prefixes mean "Don't copy this header from a 304 response.".
87 // Rather than listing all the relevant headers, we can consolidate them into
88 // this list:
89 const char* const kNonUpdatedHeaderPrefixes[] = {
90   "content-",
91   "x-content-",
92   "x-webkit-"
93 };
94 
ShouldUpdateHeader(const std::string::const_iterator & name_begin,const std::string::const_iterator & name_end)95 bool ShouldUpdateHeader(const std::string::const_iterator& name_begin,
96                         const std::string::const_iterator& name_end) {
97   for (size_t i = 0; i < arraysize(kNonUpdatedHeaders); ++i) {
98     if (LowerCaseEqualsASCII(name_begin, name_end, kNonUpdatedHeaders[i]))
99       return false;
100   }
101   for (size_t i = 0; i < arraysize(kNonUpdatedHeaderPrefixes); ++i) {
102     if (StartsWithASCII(std::string(name_begin, name_end),
103                         kNonUpdatedHeaderPrefixes[i], false))
104       return false;
105   }
106   return true;
107 }
108 
CheckDoesNotHaveEmbededNulls(const std::string & str)109 void CheckDoesNotHaveEmbededNulls(const std::string& str) {
110   // Care needs to be taken when adding values to the raw headers string to
111   // make sure it does not contain embeded NULLs. Any embeded '\0' may be
112   // understood as line terminators and change how header lines get tokenized.
113   CHECK(str.find('\0') == std::string::npos);
114 }
115 
ShouldShowHttpHeaderValue(const std::string & header_name)116 bool ShouldShowHttpHeaderValue(const std::string& header_name) {
117 #if defined(SPDY_PROXY_AUTH_ORIGIN)
118   if (header_name == "Proxy-Authenticate")
119     return false;
120 #endif
121   return true;
122 }
123 
124 }  // namespace
125 
126 struct HttpResponseHeaders::ParsedHeader {
127   // A header "continuation" contains only a subsequent value for the
128   // preceding header.  (Header values are comma separated.)
is_continuationnet::HttpResponseHeaders::ParsedHeader129   bool is_continuation() const { return name_begin == name_end; }
130 
131   std::string::const_iterator name_begin;
132   std::string::const_iterator name_end;
133   std::string::const_iterator value_begin;
134   std::string::const_iterator value_end;
135 };
136 
137 //-----------------------------------------------------------------------------
138 
HttpResponseHeaders(const std::string & raw_input)139 HttpResponseHeaders::HttpResponseHeaders(const std::string& raw_input)
140     : response_code_(-1) {
141   Parse(raw_input);
142 
143   // The most important thing to do with this histogram is find out
144   // the existence of unusual HTTP status codes.  As it happens
145   // right now, there aren't double-constructions of response headers
146   // using this constructor, so our counts should also be accurate,
147   // without instantiating the histogram in two places.  It is also
148   // important that this histogram not collect data in the other
149   // constructor, which rebuilds an histogram from a pickle, since
150   // that would actually create a double call between the original
151   // HttpResponseHeader that was serialized, and initialization of the
152   // new object from that pickle.
153   UMA_HISTOGRAM_CUSTOM_ENUMERATION("Net.HttpResponseCode",
154                                    HttpUtil::MapStatusCodeForHistogram(
155                                        response_code_),
156                                    // Note the third argument is only
157                                    // evaluated once, see macro
158                                    // definition for details.
159                                    HttpUtil::GetStatusCodesForHistogram());
160 }
161 
HttpResponseHeaders(const Pickle & pickle,PickleIterator * iter)162 HttpResponseHeaders::HttpResponseHeaders(const Pickle& pickle,
163                                          PickleIterator* iter)
164     : response_code_(-1) {
165   std::string raw_input;
166   if (pickle.ReadString(iter, &raw_input))
167     Parse(raw_input);
168 }
169 
Persist(Pickle * pickle,PersistOptions options)170 void HttpResponseHeaders::Persist(Pickle* pickle, PersistOptions options) {
171   if (options == PERSIST_RAW) {
172     pickle->WriteString(raw_headers_);
173     return;  // Done.
174   }
175 
176   HeaderSet filter_headers;
177 
178   // Construct set of headers to filter out based on options.
179   if ((options & PERSIST_SANS_NON_CACHEABLE) == PERSIST_SANS_NON_CACHEABLE)
180     AddNonCacheableHeaders(&filter_headers);
181 
182   if ((options & PERSIST_SANS_COOKIES) == PERSIST_SANS_COOKIES)
183     AddCookieHeaders(&filter_headers);
184 
185   if ((options & PERSIST_SANS_CHALLENGES) == PERSIST_SANS_CHALLENGES)
186     AddChallengeHeaders(&filter_headers);
187 
188   if ((options & PERSIST_SANS_HOP_BY_HOP) == PERSIST_SANS_HOP_BY_HOP)
189     AddHopByHopHeaders(&filter_headers);
190 
191   if ((options & PERSIST_SANS_RANGES) == PERSIST_SANS_RANGES)
192     AddHopContentRangeHeaders(&filter_headers);
193 
194   if ((options & PERSIST_SANS_SECURITY_STATE) == PERSIST_SANS_SECURITY_STATE)
195     AddSecurityStateHeaders(&filter_headers);
196 
197   std::string blob;
198   blob.reserve(raw_headers_.size());
199 
200   // This copies the status line w/ terminator null.
201   // Note raw_headers_ has embedded nulls instead of \n,
202   // so this just copies the first header line.
203   blob.assign(raw_headers_.c_str(), strlen(raw_headers_.c_str()) + 1);
204 
205   for (size_t i = 0; i < parsed_.size(); ++i) {
206     DCHECK(!parsed_[i].is_continuation());
207 
208     // Locate the start of the next header.
209     size_t k = i;
210     while (++k < parsed_.size() && parsed_[k].is_continuation()) {}
211     --k;
212 
213     std::string header_name(parsed_[i].name_begin, parsed_[i].name_end);
214     StringToLowerASCII(&header_name);
215 
216     if (filter_headers.find(header_name) == filter_headers.end()) {
217       // Make sure there is a null after the value.
218       blob.append(parsed_[i].name_begin, parsed_[k].value_end);
219       blob.push_back('\0');
220     }
221 
222     i = k;
223   }
224   blob.push_back('\0');
225 
226   pickle->WriteString(blob);
227 }
228 
Update(const HttpResponseHeaders & new_headers)229 void HttpResponseHeaders::Update(const HttpResponseHeaders& new_headers) {
230   DCHECK(new_headers.response_code() == 304 ||
231          new_headers.response_code() == 206);
232 
233   // Copy up to the null byte.  This just copies the status line.
234   std::string new_raw_headers(raw_headers_.c_str());
235   new_raw_headers.push_back('\0');
236 
237   HeaderSet updated_headers;
238 
239   // NOTE: we write the new headers then the old headers for convenience.  The
240   // order should not matter.
241 
242   // Figure out which headers we want to take from new_headers:
243   for (size_t i = 0; i < new_headers.parsed_.size(); ++i) {
244     const HeaderList& new_parsed = new_headers.parsed_;
245 
246     DCHECK(!new_parsed[i].is_continuation());
247 
248     // Locate the start of the next header.
249     size_t k = i;
250     while (++k < new_parsed.size() && new_parsed[k].is_continuation()) {}
251     --k;
252 
253     const std::string::const_iterator& name_begin = new_parsed[i].name_begin;
254     const std::string::const_iterator& name_end = new_parsed[i].name_end;
255     if (ShouldUpdateHeader(name_begin, name_end)) {
256       std::string name(name_begin, name_end);
257       StringToLowerASCII(&name);
258       updated_headers.insert(name);
259 
260       // Preserve this header line in the merged result, making sure there is
261       // a null after the value.
262       new_raw_headers.append(name_begin, new_parsed[k].value_end);
263       new_raw_headers.push_back('\0');
264     }
265 
266     i = k;
267   }
268 
269   // Now, build the new raw headers.
270   MergeWithHeaders(new_raw_headers, updated_headers);
271 }
272 
MergeWithHeaders(const std::string & raw_headers,const HeaderSet & headers_to_remove)273 void HttpResponseHeaders::MergeWithHeaders(const std::string& raw_headers,
274                                            const HeaderSet& headers_to_remove) {
275   std::string new_raw_headers(raw_headers);
276   for (size_t i = 0; i < parsed_.size(); ++i) {
277     DCHECK(!parsed_[i].is_continuation());
278 
279     // Locate the start of the next header.
280     size_t k = i;
281     while (++k < parsed_.size() && parsed_[k].is_continuation()) {}
282     --k;
283 
284     std::string name(parsed_[i].name_begin, parsed_[i].name_end);
285     StringToLowerASCII(&name);
286     if (headers_to_remove.find(name) == headers_to_remove.end()) {
287       // It's ok to preserve this header in the final result.
288       new_raw_headers.append(parsed_[i].name_begin, parsed_[k].value_end);
289       new_raw_headers.push_back('\0');
290     }
291 
292     i = k;
293   }
294   new_raw_headers.push_back('\0');
295 
296   // Make this object hold the new data.
297   raw_headers_.clear();
298   parsed_.clear();
299   Parse(new_raw_headers);
300 }
301 
RemoveHeader(const std::string & name)302 void HttpResponseHeaders::RemoveHeader(const std::string& name) {
303   // Copy up to the null byte.  This just copies the status line.
304   std::string new_raw_headers(raw_headers_.c_str());
305   new_raw_headers.push_back('\0');
306 
307   std::string lowercase_name(name);
308   StringToLowerASCII(&lowercase_name);
309   HeaderSet to_remove;
310   to_remove.insert(lowercase_name);
311   MergeWithHeaders(new_raw_headers, to_remove);
312 }
313 
RemoveHeaderLine(const std::string & name,const std::string & value)314 void HttpResponseHeaders::RemoveHeaderLine(const std::string& name,
315                                            const std::string& value) {
316   std::string name_lowercase(name);
317   StringToLowerASCII(&name_lowercase);
318 
319   std::string new_raw_headers(GetStatusLine());
320   new_raw_headers.push_back('\0');
321 
322   new_raw_headers.reserve(raw_headers_.size());
323 
324   void* iter = NULL;
325   std::string old_header_name;
326   std::string old_header_value;
327   while (EnumerateHeaderLines(&iter, &old_header_name, &old_header_value)) {
328     std::string old_header_name_lowercase(name);
329     StringToLowerASCII(&old_header_name_lowercase);
330 
331     if (name_lowercase == old_header_name_lowercase &&
332         value == old_header_value)
333       continue;
334 
335     new_raw_headers.append(old_header_name);
336     new_raw_headers.push_back(':');
337     new_raw_headers.push_back(' ');
338     new_raw_headers.append(old_header_value);
339     new_raw_headers.push_back('\0');
340   }
341   new_raw_headers.push_back('\0');
342 
343   // Make this object hold the new data.
344   raw_headers_.clear();
345   parsed_.clear();
346   Parse(new_raw_headers);
347 }
348 
AddHeader(const std::string & header)349 void HttpResponseHeaders::AddHeader(const std::string& header) {
350   CheckDoesNotHaveEmbededNulls(header);
351   DCHECK_EQ('\0', raw_headers_[raw_headers_.size() - 2]);
352   DCHECK_EQ('\0', raw_headers_[raw_headers_.size() - 1]);
353   // Don't copy the last null.
354   std::string new_raw_headers(raw_headers_, 0, raw_headers_.size() - 1);
355   new_raw_headers.append(header);
356   new_raw_headers.push_back('\0');
357   new_raw_headers.push_back('\0');
358 
359   // Make this object hold the new data.
360   raw_headers_.clear();
361   parsed_.clear();
362   Parse(new_raw_headers);
363 }
364 
ReplaceStatusLine(const std::string & new_status)365 void HttpResponseHeaders::ReplaceStatusLine(const std::string& new_status) {
366   CheckDoesNotHaveEmbededNulls(new_status);
367   // Copy up to the null byte.  This just copies the status line.
368   std::string new_raw_headers(new_status);
369   new_raw_headers.push_back('\0');
370 
371   HeaderSet empty_to_remove;
372   MergeWithHeaders(new_raw_headers, empty_to_remove);
373 }
374 
Parse(const std::string & raw_input)375 void HttpResponseHeaders::Parse(const std::string& raw_input) {
376   raw_headers_.reserve(raw_input.size());
377 
378   // ParseStatusLine adds a normalized status line to raw_headers_
379   std::string::const_iterator line_begin = raw_input.begin();
380   std::string::const_iterator line_end =
381       std::find(line_begin, raw_input.end(), '\0');
382   // has_headers = true, if there is any data following the status line.
383   // Used by ParseStatusLine() to decide if a HTTP/0.9 is really a HTTP/1.0.
384   bool has_headers = (line_end != raw_input.end() &&
385                       (line_end + 1) != raw_input.end() &&
386                       *(line_end + 1) != '\0');
387   ParseStatusLine(line_begin, line_end, has_headers);
388   raw_headers_.push_back('\0');  // Terminate status line with a null.
389 
390   if (line_end == raw_input.end()) {
391     raw_headers_.push_back('\0');  // Ensure the headers end with a double null.
392 
393     DCHECK_EQ('\0', raw_headers_[raw_headers_.size() - 2]);
394     DCHECK_EQ('\0', raw_headers_[raw_headers_.size() - 1]);
395     return;
396   }
397 
398   // Including a terminating null byte.
399   size_t status_line_len = raw_headers_.size();
400 
401   // Now, we add the rest of the raw headers to raw_headers_, and begin parsing
402   // it (to populate our parsed_ vector).
403   raw_headers_.append(line_end + 1, raw_input.end());
404 
405   // Ensure the headers end with a double null.
406   while (raw_headers_.size() < 2 ||
407          raw_headers_[raw_headers_.size() - 2] != '\0' ||
408          raw_headers_[raw_headers_.size() - 1] != '\0') {
409     raw_headers_.push_back('\0');
410   }
411 
412   // Adjust to point at the null byte following the status line
413   line_end = raw_headers_.begin() + status_line_len - 1;
414 
415   HttpUtil::HeadersIterator headers(line_end + 1, raw_headers_.end(),
416                                     std::string(1, '\0'));
417   while (headers.GetNext()) {
418     AddHeader(headers.name_begin(),
419               headers.name_end(),
420               headers.values_begin(),
421               headers.values_end());
422   }
423 
424   DCHECK_EQ('\0', raw_headers_[raw_headers_.size() - 2]);
425   DCHECK_EQ('\0', raw_headers_[raw_headers_.size() - 1]);
426 }
427 
428 // Append all of our headers to the final output string.
GetNormalizedHeaders(std::string * output) const429 void HttpResponseHeaders::GetNormalizedHeaders(std::string* output) const {
430   // copy up to the null byte.  this just copies the status line.
431   output->assign(raw_headers_.c_str());
432 
433   // headers may appear multiple times (not necessarily in succession) in the
434   // header data, so we build a map from header name to generated header lines.
435   // to preserve the order of the original headers, the actual values are kept
436   // in a separate list.  finally, the list of headers is flattened to form
437   // the normalized block of headers.
438   //
439   // NOTE: We take special care to preserve the whitespace around any commas
440   // that may occur in the original response headers.  Because our consumer may
441   // be a web app, we cannot be certain of the semantics of commas despite the
442   // fact that RFC 2616 says that they should be regarded as value separators.
443   //
444   typedef base::hash_map<std::string, size_t> HeadersMap;
445   HeadersMap headers_map;
446   HeadersMap::iterator iter = headers_map.end();
447 
448   std::vector<std::string> headers;
449 
450   for (size_t i = 0; i < parsed_.size(); ++i) {
451     DCHECK(!parsed_[i].is_continuation());
452 
453     std::string name(parsed_[i].name_begin, parsed_[i].name_end);
454     std::string lower_name = StringToLowerASCII(name);
455 
456     iter = headers_map.find(lower_name);
457     if (iter == headers_map.end()) {
458       iter = headers_map.insert(
459           HeadersMap::value_type(lower_name, headers.size())).first;
460       headers.push_back(name + ": ");
461     } else {
462       headers[iter->second].append(", ");
463     }
464 
465     std::string::const_iterator value_begin = parsed_[i].value_begin;
466     std::string::const_iterator value_end = parsed_[i].value_end;
467     while (++i < parsed_.size() && parsed_[i].is_continuation())
468       value_end = parsed_[i].value_end;
469     --i;
470 
471     headers[iter->second].append(value_begin, value_end);
472   }
473 
474   for (size_t i = 0; i < headers.size(); ++i) {
475     output->push_back('\n');
476     output->append(headers[i]);
477   }
478 
479   output->push_back('\n');
480 }
481 
GetNormalizedHeader(const std::string & name,std::string * value) const482 bool HttpResponseHeaders::GetNormalizedHeader(const std::string& name,
483                                               std::string* value) const {
484   // If you hit this assertion, please use EnumerateHeader instead!
485   DCHECK(!HttpUtil::IsNonCoalescingHeader(name));
486 
487   value->clear();
488 
489   bool found = false;
490   size_t i = 0;
491   while (i < parsed_.size()) {
492     i = FindHeader(i, name);
493     if (i == std::string::npos)
494       break;
495 
496     found = true;
497 
498     if (!value->empty())
499       value->append(", ");
500 
501     std::string::const_iterator value_begin = parsed_[i].value_begin;
502     std::string::const_iterator value_end = parsed_[i].value_end;
503     while (++i < parsed_.size() && parsed_[i].is_continuation())
504       value_end = parsed_[i].value_end;
505     value->append(value_begin, value_end);
506   }
507 
508   return found;
509 }
510 
GetStatusLine() const511 std::string HttpResponseHeaders::GetStatusLine() const {
512   // copy up to the null byte.
513   return std::string(raw_headers_.c_str());
514 }
515 
GetStatusText() const516 std::string HttpResponseHeaders::GetStatusText() const {
517   // GetStatusLine() is already normalized, so it has the format:
518   // <http_version> SP <response_code> SP <status_text>
519   std::string status_text = GetStatusLine();
520   std::string::const_iterator begin = status_text.begin();
521   std::string::const_iterator end = status_text.end();
522   for (int i = 0; i < 2; ++i)
523     begin = std::find(begin, end, ' ') + 1;
524   return std::string(begin, end);
525 }
526 
EnumerateHeaderLines(void ** iter,std::string * name,std::string * value) const527 bool HttpResponseHeaders::EnumerateHeaderLines(void** iter,
528                                                std::string* name,
529                                                std::string* value) const {
530   size_t i = reinterpret_cast<size_t>(*iter);
531   if (i == parsed_.size())
532     return false;
533 
534   DCHECK(!parsed_[i].is_continuation());
535 
536   name->assign(parsed_[i].name_begin, parsed_[i].name_end);
537 
538   std::string::const_iterator value_begin = parsed_[i].value_begin;
539   std::string::const_iterator value_end = parsed_[i].value_end;
540   while (++i < parsed_.size() && parsed_[i].is_continuation())
541     value_end = parsed_[i].value_end;
542 
543   value->assign(value_begin, value_end);
544 
545   *iter = reinterpret_cast<void*>(i);
546   return true;
547 }
548 
EnumerateHeader(void ** iter,const base::StringPiece & name,std::string * value) const549 bool HttpResponseHeaders::EnumerateHeader(void** iter,
550                                           const base::StringPiece& name,
551                                           std::string* value) const {
552   size_t i;
553   if (!iter || !*iter) {
554     i = FindHeader(0, name);
555   } else {
556     i = reinterpret_cast<size_t>(*iter);
557     if (i >= parsed_.size()) {
558       i = std::string::npos;
559     } else if (!parsed_[i].is_continuation()) {
560       i = FindHeader(i, name);
561     }
562   }
563 
564   if (i == std::string::npos) {
565     value->clear();
566     return false;
567   }
568 
569   if (iter)
570     *iter = reinterpret_cast<void*>(i + 1);
571   value->assign(parsed_[i].value_begin, parsed_[i].value_end);
572   return true;
573 }
574 
HasHeaderValue(const base::StringPiece & name,const base::StringPiece & value) const575 bool HttpResponseHeaders::HasHeaderValue(const base::StringPiece& name,
576                                          const base::StringPiece& value) const {
577   // The value has to be an exact match.  This is important since
578   // 'cache-control: no-cache' != 'cache-control: no-cache="foo"'
579   void* iter = NULL;
580   std::string temp;
581   while (EnumerateHeader(&iter, name, &temp)) {
582     if (value.size() == temp.size() &&
583         std::equal(temp.begin(), temp.end(), value.begin(),
584                    base::CaseInsensitiveCompare<char>()))
585       return true;
586   }
587   return false;
588 }
589 
HasHeader(const base::StringPiece & name) const590 bool HttpResponseHeaders::HasHeader(const base::StringPiece& name) const {
591   return FindHeader(0, name) != std::string::npos;
592 }
593 
HttpResponseHeaders()594 HttpResponseHeaders::HttpResponseHeaders() : response_code_(-1) {
595 }
596 
~HttpResponseHeaders()597 HttpResponseHeaders::~HttpResponseHeaders() {
598 }
599 
600 // Note: this implementation implicitly assumes that line_end points at a valid
601 // sentinel character (such as '\0').
602 // static
ParseVersion(std::string::const_iterator line_begin,std::string::const_iterator line_end)603 HttpVersion HttpResponseHeaders::ParseVersion(
604     std::string::const_iterator line_begin,
605     std::string::const_iterator line_end) {
606   std::string::const_iterator p = line_begin;
607 
608   // RFC2616 sec 3.1: HTTP-Version   = "HTTP" "/" 1*DIGIT "." 1*DIGIT
609   // TODO: (1*DIGIT apparently means one or more digits, but we only handle 1).
610   // TODO: handle leading zeros, which is allowed by the rfc1616 sec 3.1.
611 
612   if ((line_end - p < 4) || !LowerCaseEqualsASCII(p, p + 4, "http")) {
613     DVLOG(1) << "missing status line";
614     return HttpVersion();
615   }
616 
617   p += 4;
618 
619   if (p >= line_end || *p != '/') {
620     DVLOG(1) << "missing version";
621     return HttpVersion();
622   }
623 
624   std::string::const_iterator dot = std::find(p, line_end, '.');
625   if (dot == line_end) {
626     DVLOG(1) << "malformed version";
627     return HttpVersion();
628   }
629 
630   ++p;  // from / to first digit.
631   ++dot;  // from . to second digit.
632 
633   if (!(*p >= '0' && *p <= '9' && *dot >= '0' && *dot <= '9')) {
634     DVLOG(1) << "malformed version number";
635     return HttpVersion();
636   }
637 
638   uint16 major = *p - '0';
639   uint16 minor = *dot - '0';
640 
641   return HttpVersion(major, minor);
642 }
643 
644 // Note: this implementation implicitly assumes that line_end points at a valid
645 // sentinel character (such as '\0').
ParseStatusLine(std::string::const_iterator line_begin,std::string::const_iterator line_end,bool has_headers)646 void HttpResponseHeaders::ParseStatusLine(
647     std::string::const_iterator line_begin,
648     std::string::const_iterator line_end,
649     bool has_headers) {
650   // Extract the version number
651   parsed_http_version_ = ParseVersion(line_begin, line_end);
652 
653   // Clamp the version number to one of: {0.9, 1.0, 1.1}
654   if (parsed_http_version_ == HttpVersion(0, 9) && !has_headers) {
655     http_version_ = HttpVersion(0, 9);
656     raw_headers_ = "HTTP/0.9";
657   } else if (parsed_http_version_ >= HttpVersion(1, 1)) {
658     http_version_ = HttpVersion(1, 1);
659     raw_headers_ = "HTTP/1.1";
660   } else {
661     // Treat everything else like HTTP 1.0
662     http_version_ = HttpVersion(1, 0);
663     raw_headers_ = "HTTP/1.0";
664   }
665   if (parsed_http_version_ != http_version_) {
666     DVLOG(1) << "assuming HTTP/" << http_version_.major_value() << "."
667              << http_version_.minor_value();
668   }
669 
670   // TODO(eroman): this doesn't make sense if ParseVersion failed.
671   std::string::const_iterator p = std::find(line_begin, line_end, ' ');
672 
673   if (p == line_end) {
674     DVLOG(1) << "missing response status; assuming 200 OK";
675     raw_headers_.append(" 200 OK");
676     response_code_ = 200;
677     return;
678   }
679 
680   // Skip whitespace.
681   while (*p == ' ')
682     ++p;
683 
684   std::string::const_iterator code = p;
685   while (*p >= '0' && *p <= '9')
686     ++p;
687 
688   if (p == code) {
689     DVLOG(1) << "missing response status number; assuming 200";
690     raw_headers_.append(" 200 OK");
691     response_code_ = 200;
692     return;
693   }
694   raw_headers_.push_back(' ');
695   raw_headers_.append(code, p);
696   raw_headers_.push_back(' ');
697   base::StringToInt(StringPiece(code, p), &response_code_);
698 
699   // Skip whitespace.
700   while (*p == ' ')
701     ++p;
702 
703   // Trim trailing whitespace.
704   while (line_end > p && line_end[-1] == ' ')
705     --line_end;
706 
707   if (p == line_end) {
708     DVLOG(1) << "missing response status text; assuming OK";
709     // Not super critical what we put here. Just use "OK"
710     // even if it isn't descriptive of response_code_.
711     raw_headers_.append("OK");
712   } else {
713     raw_headers_.append(p, line_end);
714   }
715 }
716 
FindHeader(size_t from,const base::StringPiece & search) const717 size_t HttpResponseHeaders::FindHeader(size_t from,
718                                        const base::StringPiece& search) const {
719   for (size_t i = from; i < parsed_.size(); ++i) {
720     if (parsed_[i].is_continuation())
721       continue;
722     const std::string::const_iterator& name_begin = parsed_[i].name_begin;
723     const std::string::const_iterator& name_end = parsed_[i].name_end;
724     if (static_cast<size_t>(name_end - name_begin) == search.size() &&
725         std::equal(name_begin, name_end, search.begin(),
726                    base::CaseInsensitiveCompare<char>()))
727       return i;
728   }
729 
730   return std::string::npos;
731 }
732 
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)733 void HttpResponseHeaders::AddHeader(std::string::const_iterator name_begin,
734                                     std::string::const_iterator name_end,
735                                     std::string::const_iterator values_begin,
736                                     std::string::const_iterator values_end) {
737   // If the header can be coalesced, then we should split it up.
738   if (values_begin == values_end ||
739       HttpUtil::IsNonCoalescingHeader(name_begin, name_end)) {
740     AddToParsed(name_begin, name_end, values_begin, values_end);
741   } else {
742     HttpUtil::ValuesIterator it(values_begin, values_end, ',');
743     while (it.GetNext()) {
744       AddToParsed(name_begin, name_end, it.value_begin(), it.value_end());
745       // clobber these so that subsequent values are treated as continuations
746       name_begin = name_end = raw_headers_.end();
747     }
748   }
749 }
750 
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)751 void HttpResponseHeaders::AddToParsed(std::string::const_iterator name_begin,
752                                       std::string::const_iterator name_end,
753                                       std::string::const_iterator value_begin,
754                                       std::string::const_iterator value_end) {
755   ParsedHeader header;
756   header.name_begin = name_begin;
757   header.name_end = name_end;
758   header.value_begin = value_begin;
759   header.value_end = value_end;
760   parsed_.push_back(header);
761 }
762 
AddNonCacheableHeaders(HeaderSet * result) const763 void HttpResponseHeaders::AddNonCacheableHeaders(HeaderSet* result) const {
764   // Add server specified transients.  Any 'cache-control: no-cache="foo,bar"'
765   // headers present in the response specify additional headers that we should
766   // not store in the cache.
767   const char kCacheControl[] = "cache-control";
768   const char kPrefix[] = "no-cache=\"";
769   const size_t kPrefixLen = sizeof(kPrefix) - 1;
770 
771   std::string value;
772   void* iter = NULL;
773   while (EnumerateHeader(&iter, kCacheControl, &value)) {
774     // If the value is smaller than the prefix and a terminal quote, skip
775     // it.
776     if (value.size() <= kPrefixLen ||
777         value.compare(0, kPrefixLen, kPrefix) != 0) {
778       continue;
779     }
780     // if it doesn't end with a quote, then treat as malformed
781     if (value[value.size()-1] != '\"')
782       continue;
783 
784     // process the value as a comma-separated list of items. Each
785     // item can be wrapped by linear white space.
786     std::string::const_iterator item = value.begin() + kPrefixLen;
787     std::string::const_iterator end = value.end() - 1;
788     while (item != end) {
789       // Find the comma to compute the length of the current item,
790       // and the position of the next one.
791       std::string::const_iterator item_next = std::find(item, end, ',');
792       std::string::const_iterator item_end = end;
793       if (item_next != end) {
794         // Skip over comma for next position.
795         item_end = item_next;
796         item_next++;
797       }
798       // trim off leading and trailing whitespace in this item.
799       HttpUtil::TrimLWS(&item, &item_end);
800 
801       // assuming the header is not empty, lowercase and insert into set
802       if (item_end > item) {
803         std::string name(&*item, item_end - item);
804         StringToLowerASCII(&name);
805         result->insert(name);
806       }
807 
808       // Continue to next item.
809       item = item_next;
810     }
811   }
812 }
813 
AddHopByHopHeaders(HeaderSet * result)814 void HttpResponseHeaders::AddHopByHopHeaders(HeaderSet* result) {
815   for (size_t i = 0; i < arraysize(kHopByHopResponseHeaders); ++i)
816     result->insert(std::string(kHopByHopResponseHeaders[i]));
817 }
818 
AddCookieHeaders(HeaderSet * result)819 void HttpResponseHeaders::AddCookieHeaders(HeaderSet* result) {
820   for (size_t i = 0; i < arraysize(kCookieResponseHeaders); ++i)
821     result->insert(std::string(kCookieResponseHeaders[i]));
822 }
823 
AddChallengeHeaders(HeaderSet * result)824 void HttpResponseHeaders::AddChallengeHeaders(HeaderSet* result) {
825   for (size_t i = 0; i < arraysize(kChallengeResponseHeaders); ++i)
826     result->insert(std::string(kChallengeResponseHeaders[i]));
827 }
828 
AddHopContentRangeHeaders(HeaderSet * result)829 void HttpResponseHeaders::AddHopContentRangeHeaders(HeaderSet* result) {
830   result->insert("content-range");
831 }
832 
AddSecurityStateHeaders(HeaderSet * result)833 void HttpResponseHeaders::AddSecurityStateHeaders(HeaderSet* result) {
834   for (size_t i = 0; i < arraysize(kSecurityStateHeaders); ++i)
835     result->insert(std::string(kSecurityStateHeaders[i]));
836 }
837 
GetMimeTypeAndCharset(std::string * mime_type,std::string * charset) const838 void HttpResponseHeaders::GetMimeTypeAndCharset(std::string* mime_type,
839                                                 std::string* charset) const {
840   mime_type->clear();
841   charset->clear();
842 
843   std::string name = "content-type";
844   std::string value;
845 
846   bool had_charset = false;
847 
848   void* iter = NULL;
849   while (EnumerateHeader(&iter, name, &value))
850     HttpUtil::ParseContentType(value, mime_type, charset, &had_charset, NULL);
851 }
852 
GetMimeType(std::string * mime_type) const853 bool HttpResponseHeaders::GetMimeType(std::string* mime_type) const {
854   std::string unused;
855   GetMimeTypeAndCharset(mime_type, &unused);
856   return !mime_type->empty();
857 }
858 
GetCharset(std::string * charset) const859 bool HttpResponseHeaders::GetCharset(std::string* charset) const {
860   std::string unused;
861   GetMimeTypeAndCharset(&unused, charset);
862   return !charset->empty();
863 }
864 
IsRedirect(std::string * location) const865 bool HttpResponseHeaders::IsRedirect(std::string* location) const {
866   if (!IsRedirectResponseCode(response_code_))
867     return false;
868 
869   // If we lack a Location header, then we can't treat this as a redirect.
870   // We assume that the first non-empty location value is the target URL that
871   // we want to follow.  TODO(darin): Is this consistent with other browsers?
872   size_t i = std::string::npos;
873   do {
874     i = FindHeader(++i, "location");
875     if (i == std::string::npos)
876       return false;
877     // If the location value is empty, then it doesn't count.
878   } while (parsed_[i].value_begin == parsed_[i].value_end);
879 
880   if (location) {
881     // Escape any non-ASCII characters to preserve them.  The server should
882     // only be returning ASCII here, but for compat we need to do this.
883     *location = EscapeNonASCII(
884         std::string(parsed_[i].value_begin, parsed_[i].value_end));
885   }
886 
887   return true;
888 }
889 
890 // static
IsRedirectResponseCode(int response_code)891 bool HttpResponseHeaders::IsRedirectResponseCode(int response_code) {
892   // Users probably want to see 300 (multiple choice) pages, so we don't count
893   // them as redirects that need to be followed.
894   return (response_code == 301 ||
895           response_code == 302 ||
896           response_code == 303 ||
897           response_code == 307);
898 }
899 
900 // From RFC 2616 section 13.2.4:
901 //
902 // The calculation to determine if a response has expired is quite simple:
903 //
904 //   response_is_fresh = (freshness_lifetime > current_age)
905 //
906 // Of course, there are other factors that can force a response to always be
907 // validated or re-fetched.
908 //
RequiresValidation(const Time & request_time,const Time & response_time,const Time & current_time) const909 bool HttpResponseHeaders::RequiresValidation(const Time& request_time,
910                                              const Time& response_time,
911                                              const Time& current_time) const {
912   TimeDelta lifetime =
913       GetFreshnessLifetime(response_time);
914   if (lifetime == TimeDelta())
915     return true;
916 
917   return lifetime <= GetCurrentAge(request_time, response_time, current_time);
918 }
919 
920 // From RFC 2616 section 13.2.4:
921 //
922 // The max-age directive takes priority over Expires, so if max-age is present
923 // in a response, the calculation is simply:
924 //
925 //   freshness_lifetime = max_age_value
926 //
927 // Otherwise, if Expires is present in the response, the calculation is:
928 //
929 //   freshness_lifetime = expires_value - date_value
930 //
931 // Note that neither of these calculations is vulnerable to clock skew, since
932 // all of the information comes from the origin server.
933 //
934 // Also, if the response does have a Last-Modified time, the heuristic
935 // expiration value SHOULD be no more than some fraction of the interval since
936 // that time. A typical setting of this fraction might be 10%:
937 //
938 //   freshness_lifetime = (date_value - last_modified_value) * 0.10
939 //
GetFreshnessLifetime(const Time & response_time) const940 TimeDelta HttpResponseHeaders::GetFreshnessLifetime(
941     const Time& response_time) const {
942   // Check for headers that force a response to never be fresh.  For backwards
943   // compat, we treat "Pragma: no-cache" as a synonym for "Cache-Control:
944   // no-cache" even though RFC 2616 does not specify it.
945   if (HasHeaderValue("cache-control", "no-cache") ||
946       HasHeaderValue("cache-control", "no-store") ||
947       HasHeaderValue("pragma", "no-cache") ||
948       HasHeaderValue("vary", "*"))  // see RFC 2616 section 13.6
949     return TimeDelta();  // not fresh
950 
951   // NOTE: "Cache-Control: max-age" overrides Expires, so we only check the
952   // Expires header after checking for max-age in GetFreshnessLifetime.  This
953   // is important since "Expires: <date in the past>" means not fresh, but
954   // it should not trump a max-age value.
955 
956   TimeDelta max_age_value;
957   if (GetMaxAgeValue(&max_age_value))
958     return max_age_value;
959 
960   // If there is no Date header, then assume that the server response was
961   // generated at the time when we received the response.
962   Time date_value;
963   if (!GetDateValue(&date_value))
964     date_value = response_time;
965 
966   Time expires_value;
967   if (GetExpiresValue(&expires_value)) {
968     // The expires value can be a date in the past!
969     if (expires_value > date_value)
970       return expires_value - date_value;
971 
972     return TimeDelta();  // not fresh
973   }
974 
975   // From RFC 2616 section 13.4:
976   //
977   //   A response received with a status code of 200, 203, 206, 300, 301 or 410
978   //   MAY be stored by a cache and used in reply to a subsequent request,
979   //   subject to the expiration mechanism, unless a cache-control directive
980   //   prohibits caching.
981   //   ...
982   //   A response received with any other status code (e.g. status codes 302
983   //   and 307) MUST NOT be returned in a reply to a subsequent request unless
984   //   there are cache-control directives or another header(s) that explicitly
985   //   allow it.
986   //
987   // From RFC 2616 section 14.9.4:
988   //
989   //   When the must-revalidate directive is present in a response received by
990   //   a cache, that cache MUST NOT use the entry after it becomes stale to
991   //   respond to a subsequent request without first revalidating it with the
992   //   origin server. (I.e., the cache MUST do an end-to-end revalidation every
993   //   time, if, based solely on the origin server's Expires or max-age value,
994   //   the cached response is stale.)
995   //
996   if ((response_code_ == 200 || response_code_ == 203 ||
997        response_code_ == 206) &&
998       !HasHeaderValue("cache-control", "must-revalidate")) {
999     // TODO(darin): Implement a smarter heuristic.
1000     Time last_modified_value;
1001     if (GetLastModifiedValue(&last_modified_value)) {
1002       // The last-modified value can be a date in the past!
1003       if (last_modified_value <= date_value)
1004         return (date_value - last_modified_value) / 10;
1005     }
1006   }
1007 
1008   // These responses are implicitly fresh (unless otherwise overruled):
1009   if (response_code_ == 300 || response_code_ == 301 || response_code_ == 410)
1010     return TimeDelta::FromMicroseconds(kint64max);
1011 
1012   return TimeDelta();  // not fresh
1013 }
1014 
1015 // From RFC 2616 section 13.2.3:
1016 //
1017 // Summary of age calculation algorithm, when a cache receives a response:
1018 //
1019 //   /*
1020 //    * age_value
1021 //    *      is the value of Age: header received by the cache with
1022 //    *              this response.
1023 //    * date_value
1024 //    *      is the value of the origin server's Date: header
1025 //    * request_time
1026 //    *      is the (local) time when the cache made the request
1027 //    *              that resulted in this cached response
1028 //    * response_time
1029 //    *      is the (local) time when the cache received the
1030 //    *              response
1031 //    * now
1032 //    *      is the current (local) time
1033 //    */
1034 //   apparent_age = max(0, response_time - date_value);
1035 //   corrected_received_age = max(apparent_age, age_value);
1036 //   response_delay = response_time - request_time;
1037 //   corrected_initial_age = corrected_received_age + response_delay;
1038 //   resident_time = now - response_time;
1039 //   current_age   = corrected_initial_age + resident_time;
1040 //
GetCurrentAge(const Time & request_time,const Time & response_time,const Time & current_time) const1041 TimeDelta HttpResponseHeaders::GetCurrentAge(const Time& request_time,
1042                                              const Time& response_time,
1043                                              const Time& current_time) const {
1044   // If there is no Date header, then assume that the server response was
1045   // generated at the time when we received the response.
1046   Time date_value;
1047   if (!GetDateValue(&date_value))
1048     date_value = response_time;
1049 
1050   // If there is no Age header, then assume age is zero.  GetAgeValue does not
1051   // modify its out param if the value does not exist.
1052   TimeDelta age_value;
1053   GetAgeValue(&age_value);
1054 
1055   TimeDelta apparent_age = std::max(TimeDelta(), response_time - date_value);
1056   TimeDelta corrected_received_age = std::max(apparent_age, age_value);
1057   TimeDelta response_delay = response_time - request_time;
1058   TimeDelta corrected_initial_age = corrected_received_age + response_delay;
1059   TimeDelta resident_time = current_time - response_time;
1060   TimeDelta current_age = corrected_initial_age + resident_time;
1061 
1062   return current_age;
1063 }
1064 
GetMaxAgeValue(TimeDelta * result) const1065 bool HttpResponseHeaders::GetMaxAgeValue(TimeDelta* result) const {
1066   std::string name = "cache-control";
1067   std::string value;
1068 
1069   const char kMaxAgePrefix[] = "max-age=";
1070   const size_t kMaxAgePrefixLen = arraysize(kMaxAgePrefix) - 1;
1071 
1072   void* iter = NULL;
1073   while (EnumerateHeader(&iter, name, &value)) {
1074     if (value.size() > kMaxAgePrefixLen) {
1075       if (LowerCaseEqualsASCII(value.begin(),
1076                                value.begin() + kMaxAgePrefixLen,
1077                                kMaxAgePrefix)) {
1078         int64 seconds;
1079         base::StringToInt64(StringPiece(value.begin() + kMaxAgePrefixLen,
1080                                         value.end()),
1081                             &seconds);
1082         *result = TimeDelta::FromSeconds(seconds);
1083         return true;
1084       }
1085     }
1086   }
1087 
1088   return false;
1089 }
1090 
GetAgeValue(TimeDelta * result) const1091 bool HttpResponseHeaders::GetAgeValue(TimeDelta* result) const {
1092   std::string value;
1093   if (!EnumerateHeader(NULL, "Age", &value))
1094     return false;
1095 
1096   int64 seconds;
1097   base::StringToInt64(value, &seconds);
1098   *result = TimeDelta::FromSeconds(seconds);
1099   return true;
1100 }
1101 
GetDateValue(Time * result) const1102 bool HttpResponseHeaders::GetDateValue(Time* result) const {
1103   return GetTimeValuedHeader("Date", result);
1104 }
1105 
GetLastModifiedValue(Time * result) const1106 bool HttpResponseHeaders::GetLastModifiedValue(Time* result) const {
1107   return GetTimeValuedHeader("Last-Modified", result);
1108 }
1109 
GetExpiresValue(Time * result) const1110 bool HttpResponseHeaders::GetExpiresValue(Time* result) const {
1111   return GetTimeValuedHeader("Expires", result);
1112 }
1113 
GetTimeValuedHeader(const std::string & name,Time * result) const1114 bool HttpResponseHeaders::GetTimeValuedHeader(const std::string& name,
1115                                               Time* result) const {
1116   std::string value;
1117   if (!EnumerateHeader(NULL, name, &value))
1118     return false;
1119 
1120   // When parsing HTTP dates it's beneficial to default to GMT because:
1121   // 1. RFC2616 3.3.1 says times should always be specified in GMT
1122   // 2. Only counter-example incorrectly appended "UTC" (crbug.com/153759)
1123   // 3. When adjusting cookie expiration times for clock skew
1124   //    (crbug.com/135131) this better matches our cookie expiration
1125   //    time parser which ignores timezone specifiers and assumes GMT.
1126   // 4. This is exactly what Firefox does.
1127   // TODO(pauljensen): The ideal solution would be to return false if the
1128   // timezone could not be understood so as to avoid makeing other calculations
1129   // based on an incorrect time.  This would require modifying the time
1130   // library or duplicating the code. (http://crbug.com/158327)
1131   return Time::FromUTCString(value.c_str(), result);
1132 }
1133 
IsKeepAlive() const1134 bool HttpResponseHeaders::IsKeepAlive() const {
1135   if (http_version_ < HttpVersion(1, 0))
1136     return false;
1137 
1138   // NOTE: It is perhaps risky to assume that a Proxy-Connection header is
1139   // meaningful when we don't know that this response was from a proxy, but
1140   // Mozilla also does this, so we'll do the same.
1141   std::string connection_val;
1142   if (!EnumerateHeader(NULL, "connection", &connection_val))
1143     EnumerateHeader(NULL, "proxy-connection", &connection_val);
1144 
1145   bool keep_alive;
1146 
1147   if (http_version_ == HttpVersion(1, 0)) {
1148     // HTTP/1.0 responses default to NOT keep-alive
1149     keep_alive = LowerCaseEqualsASCII(connection_val, "keep-alive");
1150   } else {
1151     // HTTP/1.1 responses default to keep-alive
1152     keep_alive = !LowerCaseEqualsASCII(connection_val, "close");
1153   }
1154 
1155   return keep_alive;
1156 }
1157 
HasStrongValidators() const1158 bool HttpResponseHeaders::HasStrongValidators() const {
1159   std::string etag_header;
1160   EnumerateHeader(NULL, "etag", &etag_header);
1161   std::string last_modified_header;
1162   EnumerateHeader(NULL, "Last-Modified", &last_modified_header);
1163   std::string date_header;
1164   EnumerateHeader(NULL, "Date", &date_header);
1165   return HttpUtil::HasStrongValidators(GetHttpVersion(),
1166                                        etag_header,
1167                                        last_modified_header,
1168                                        date_header);
1169 }
1170 
1171 // From RFC 2616:
1172 // Content-Length = "Content-Length" ":" 1*DIGIT
GetContentLength() const1173 int64 HttpResponseHeaders::GetContentLength() const {
1174   return GetInt64HeaderValue("content-length");
1175 }
1176 
GetInt64HeaderValue(const std::string & header) const1177 int64 HttpResponseHeaders::GetInt64HeaderValue(
1178     const std::string& header) const {
1179   void* iter = NULL;
1180   std::string content_length_val;
1181   if (!EnumerateHeader(&iter, header, &content_length_val))
1182     return -1;
1183 
1184   if (content_length_val.empty())
1185     return -1;
1186 
1187   if (content_length_val[0] == '+')
1188     return -1;
1189 
1190   int64 result;
1191   bool ok = base::StringToInt64(content_length_val, &result);
1192   if (!ok || result < 0)
1193     return -1;
1194 
1195   return result;
1196 }
1197 
1198 // From RFC 2616 14.16:
1199 // content-range-spec =
1200 //     bytes-unit SP byte-range-resp-spec "/" ( instance-length | "*" )
1201 // byte-range-resp-spec = (first-byte-pos "-" last-byte-pos) | "*"
1202 // instance-length = 1*DIGIT
1203 // bytes-unit = "bytes"
GetContentRange(int64 * first_byte_position,int64 * last_byte_position,int64 * instance_length) const1204 bool HttpResponseHeaders::GetContentRange(int64* first_byte_position,
1205                                           int64* last_byte_position,
1206                                           int64* instance_length) const {
1207   void* iter = NULL;
1208   std::string content_range_spec;
1209   *first_byte_position = *last_byte_position = *instance_length = -1;
1210   if (!EnumerateHeader(&iter, "content-range", &content_range_spec))
1211     return false;
1212 
1213   // If the header value is empty, we have an invalid header.
1214   if (content_range_spec.empty())
1215     return false;
1216 
1217   size_t space_position = content_range_spec.find(' ');
1218   if (space_position == std::string::npos)
1219     return false;
1220 
1221   // Invalid header if it doesn't contain "bytes-unit".
1222   std::string::const_iterator content_range_spec_begin =
1223       content_range_spec.begin();
1224   std::string::const_iterator content_range_spec_end =
1225       content_range_spec.begin() + space_position;
1226   HttpUtil::TrimLWS(&content_range_spec_begin, &content_range_spec_end);
1227   if (!LowerCaseEqualsASCII(content_range_spec_begin,
1228                             content_range_spec_end,
1229                             "bytes")) {
1230     return false;
1231   }
1232 
1233   size_t slash_position = content_range_spec.find('/', space_position + 1);
1234   if (slash_position == std::string::npos)
1235     return false;
1236 
1237   // Obtain the part behind the space and before slash.
1238   std::string::const_iterator byte_range_resp_spec_begin =
1239       content_range_spec.begin() + space_position + 1;
1240   std::string::const_iterator byte_range_resp_spec_end =
1241       content_range_spec.begin() + slash_position;
1242   HttpUtil::TrimLWS(&byte_range_resp_spec_begin, &byte_range_resp_spec_end);
1243 
1244   // Parse the byte-range-resp-spec part.
1245   std::string byte_range_resp_spec(byte_range_resp_spec_begin,
1246                                    byte_range_resp_spec_end);
1247   // If byte-range-resp-spec != "*".
1248   if (!LowerCaseEqualsASCII(byte_range_resp_spec, "*")) {
1249     size_t minus_position = byte_range_resp_spec.find('-');
1250     if (minus_position != std::string::npos) {
1251       // Obtain first-byte-pos.
1252       std::string::const_iterator first_byte_pos_begin =
1253           byte_range_resp_spec.begin();
1254       std::string::const_iterator first_byte_pos_end =
1255           byte_range_resp_spec.begin() + minus_position;
1256       HttpUtil::TrimLWS(&first_byte_pos_begin, &first_byte_pos_end);
1257 
1258       bool ok = base::StringToInt64(StringPiece(first_byte_pos_begin,
1259                                                 first_byte_pos_end),
1260                                     first_byte_position);
1261 
1262       // Obtain last-byte-pos.
1263       std::string::const_iterator last_byte_pos_begin =
1264           byte_range_resp_spec.begin() + minus_position + 1;
1265       std::string::const_iterator last_byte_pos_end =
1266           byte_range_resp_spec.end();
1267       HttpUtil::TrimLWS(&last_byte_pos_begin, &last_byte_pos_end);
1268 
1269       ok &= base::StringToInt64(StringPiece(last_byte_pos_begin,
1270                                             last_byte_pos_end),
1271                                 last_byte_position);
1272       if (!ok) {
1273         *first_byte_position = *last_byte_position = -1;
1274         return false;
1275       }
1276       if (*first_byte_position < 0 || *last_byte_position < 0 ||
1277           *first_byte_position > *last_byte_position)
1278         return false;
1279     } else {
1280       return false;
1281     }
1282   }
1283 
1284   // Parse the instance-length part.
1285   // If instance-length == "*".
1286   std::string::const_iterator instance_length_begin =
1287       content_range_spec.begin() + slash_position + 1;
1288   std::string::const_iterator instance_length_end =
1289       content_range_spec.end();
1290   HttpUtil::TrimLWS(&instance_length_begin, &instance_length_end);
1291 
1292   if (LowerCaseEqualsASCII(instance_length_begin, instance_length_end, "*")) {
1293     return false;
1294   } else if (!base::StringToInt64(StringPiece(instance_length_begin,
1295                                               instance_length_end),
1296                                   instance_length)) {
1297     *instance_length = -1;
1298     return false;
1299   }
1300 
1301   // We have all the values; let's verify that they make sense for a 206
1302   // response.
1303   if (*first_byte_position < 0 || *last_byte_position < 0 ||
1304       *instance_length < 0 || *instance_length - 1 < *last_byte_position)
1305     return false;
1306 
1307   return true;
1308 }
1309 
NetLogCallback(NetLog::LogLevel) const1310 base::Value* HttpResponseHeaders::NetLogCallback(
1311     NetLog::LogLevel /* log_level */) const {
1312   base::DictionaryValue* dict = new base::DictionaryValue();
1313   base::ListValue* headers = new base::ListValue();
1314   headers->Append(new base::StringValue(GetStatusLine()));
1315   void* iterator = NULL;
1316   std::string name;
1317   std::string value;
1318   while (EnumerateHeaderLines(&iterator, &name, &value)) {
1319     headers->Append(
1320       new base::StringValue(
1321           base::StringPrintf("%s: %s",
1322                              name.c_str(),
1323                              (ShouldShowHttpHeaderValue(name) ?
1324                                  value.c_str() : "[elided]"))));
1325   }
1326   dict->Set("headers", headers);
1327   return dict;
1328 }
1329 
1330 // static
FromNetLogParam(const base::Value * event_param,scoped_refptr<HttpResponseHeaders> * http_response_headers)1331 bool HttpResponseHeaders::FromNetLogParam(
1332     const base::Value* event_param,
1333     scoped_refptr<HttpResponseHeaders>* http_response_headers) {
1334   *http_response_headers = NULL;
1335 
1336   const base::DictionaryValue* dict = NULL;
1337   const base::ListValue* header_list = NULL;
1338 
1339   if (!event_param ||
1340       !event_param->GetAsDictionary(&dict) ||
1341       !dict->GetList("headers", &header_list)) {
1342     return false;
1343   }
1344 
1345   std::string raw_headers;
1346   for (base::ListValue::const_iterator it = header_list->begin();
1347        it != header_list->end();
1348        ++it) {
1349     std::string header_line;
1350     if (!(*it)->GetAsString(&header_line))
1351       return false;
1352 
1353     raw_headers.append(header_line);
1354     raw_headers.push_back('\0');
1355   }
1356   raw_headers.push_back('\0');
1357   *http_response_headers = new HttpResponseHeaders(raw_headers);
1358   return true;
1359 }
1360 
IsChunkEncoded() const1361 bool HttpResponseHeaders::IsChunkEncoded() const {
1362   // Ignore spurious chunked responses from HTTP/1.0 servers and proxies.
1363   return GetHttpVersion() >= HttpVersion(1, 1) &&
1364       HasHeaderValue("Transfer-Encoding", "chunked");
1365 }
1366 
1367 #if defined(SPDY_PROXY_AUTH_ORIGIN)
GetChromeProxyBypassDuration(const std::string & action_prefix,base::TimeDelta * duration) const1368 bool HttpResponseHeaders::GetChromeProxyBypassDuration(
1369     const std::string& action_prefix,
1370     base::TimeDelta* duration) const {
1371   void* iter = NULL;
1372   std::string value;
1373   std::string name = "chrome-proxy";
1374 
1375   while (EnumerateHeader(&iter, name, &value)) {
1376     if (value.size() > action_prefix.size()) {
1377       if (LowerCaseEqualsASCII(value.begin(),
1378                                value.begin() + action_prefix.size(),
1379                                action_prefix.c_str())) {
1380         int64 seconds;
1381         if (!base::StringToInt64(
1382                 StringPiece(value.begin() + action_prefix.size(), value.end()),
1383                 &seconds) || seconds < 0) {
1384           continue;  // In case there is a well formed instruction.
1385         }
1386         *duration = TimeDelta::FromSeconds(seconds);
1387         return true;
1388       }
1389     }
1390   }
1391   return false;
1392 }
1393 
GetChromeProxyInfo(ChromeProxyInfo * proxy_info) const1394 bool HttpResponseHeaders::GetChromeProxyInfo(
1395     ChromeProxyInfo* proxy_info) const {
1396   DCHECK(proxy_info);
1397   proxy_info->bypass_all = false;
1398   proxy_info->bypass_duration = base::TimeDelta();
1399 
1400   // Support header of the form Chrome-Proxy: bypass|block=<duration>, where
1401   // <duration> is the number of seconds to wait before retrying
1402   // the proxy. If the duration is 0, then the default proxy retry delay
1403   // (specified in |ProxyList::UpdateRetryInfoOnFallback|) will be used.
1404   // 'bypass' instructs Chrome to bypass the currently connected Chrome proxy,
1405   // whereas 'block' instructs Chrome to bypass all available Chrome proxies.
1406 
1407   // 'block' takes precedence over 'bypass', so look for it first.
1408   // TODO(bengr): Reduce checks for 'block' and 'bypass' to a single loop.
1409   if (GetChromeProxyBypassDuration("block=", &proxy_info->bypass_duration)) {
1410     proxy_info->bypass_all = true;
1411     return true;
1412   }
1413 
1414   // Next, look for 'bypass'.
1415   if (GetChromeProxyBypassDuration("bypass=", &proxy_info->bypass_duration))
1416     return true;
1417 
1418   return false;
1419 }
1420 #endif  // defined(SPDY_PROXY_AUTH_ORIGIN)
1421 
1422 }  // namespace net
1423