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