Home | History | Annotate | Download | only in http
      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 
     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 
    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 
    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.)
    129   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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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.
    429 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 
    482 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 
    511 std::string HttpResponseHeaders::GetStatusLine() const {
    512   // copy up to the null byte.
    513   return std::string(raw_headers_.c_str());
    514 }
    515 
    516 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 
    527 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 
    549 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 
    575 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 
    590 bool HttpResponseHeaders::HasHeader(const base::StringPiece& name) const {
    591   return FindHeader(0, name) != std::string::npos;
    592 }
    593 
    594 HttpResponseHeaders::HttpResponseHeaders() : response_code_(-1) {
    595 }
    596 
    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
    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').
    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 
    717 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 
    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 
    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 
    763 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 
    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 
    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 
    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 
    829 void HttpResponseHeaders::AddHopContentRangeHeaders(HeaderSet* result) {
    830   result->insert("content-range");
    831 }
    832 
    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 
    838 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 
    853 bool HttpResponseHeaders::GetMimeType(std::string* mime_type) const {
    854   std::string unused;
    855   GetMimeTypeAndCharset(mime_type, &unused);
    856   return !mime_type->empty();
    857 }
    858 
    859 bool HttpResponseHeaders::GetCharset(std::string* charset) const {
    860   std::string unused;
    861   GetMimeTypeAndCharset(&unused, charset);
    862   return !charset->empty();
    863 }
    864 
    865 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
    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 //
    909 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 //
    940 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 //
   1041 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 
   1065 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 
   1091 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 
   1102 bool HttpResponseHeaders::GetDateValue(Time* result) const {
   1103   return GetTimeValuedHeader("Date", result);
   1104 }
   1105 
   1106 bool HttpResponseHeaders::GetLastModifiedValue(Time* result) const {
   1107   return GetTimeValuedHeader("Last-Modified", result);
   1108 }
   1109 
   1110 bool HttpResponseHeaders::GetExpiresValue(Time* result) const {
   1111   return GetTimeValuedHeader("Expires", result);
   1112 }
   1113 
   1114 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 
   1134 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 
   1158 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
   1173 int64 HttpResponseHeaders::GetContentLength() const {
   1174   return GetInt64HeaderValue("content-length");
   1175 }
   1176 
   1177 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"
   1204 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 
   1310 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
   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 
   1361 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)
   1368 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 
   1394 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