1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Use of this source code is governed by a BSD-style license that can be
3c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// found in the LICENSE file.
4c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
5c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// The rules for parsing content-types were borrowed from Firefox:
6c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// http://lxr.mozilla.org/mozilla/source/netwerk/base/src/nsURLHelper.cpp#834
7c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
8c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/http/http_util.h"
9c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
10c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include <algorithm>
11c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
123345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/basictypes.h"
13c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/logging.h"
143345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/string_number_conversions.h"
15c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/string_piece.h"
16c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/string_util.h"
17c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
18c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottusing std::string;
19c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
20c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottnamespace net {
21c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
22c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//-----------------------------------------------------------------------------
23c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
24c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Return the index of the closing quote of the string, if any.
25c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottstatic size_t FindStringEnd(const string& line, size_t start, char delim) {
26c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  DCHECK(start < line.length() && line[start] == delim &&
27c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott         (delim == '"' || delim == '\''));
28c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
29c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  const char set[] = { delim, '\\', '\0' };
30c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  for (;;) {
31c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // start points to either the start quote or the last
32c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // escaped char (the char following a '\\')
33c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
34c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    size_t end = line.find_first_of(set, start + 1);
35c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (end == string::npos)
36c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return line.length();
37c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
38c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (line[end] == '\\') {
39c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      // Hit a backslash-escaped char.  Need to skip over it.
40c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      start = end + 1;
41c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      if (start == line.length())
42c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        return start;
43c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
44c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      // Go back to looking for the next escape or the string end
45c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      continue;
46c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
47c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
48c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return end;
49c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
50c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
51c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  NOTREACHED();
52c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return line.length();
53c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
54c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
55c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//-----------------------------------------------------------------------------
56c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
57c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
58c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottsize_t HttpUtil::FindDelimiter(const string& line, size_t search_start,
59c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                               char delimiter) {
60c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  do {
61c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // search_start points to the spot from which we should start looking
62c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // for the delimiter.
63c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    const char delim_str[] = { delimiter, '"', '\'', '\0' };
64c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    size_t cur_delim_pos = line.find_first_of(delim_str, search_start);
65c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (cur_delim_pos == string::npos)
66c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return line.length();
67c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
68c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    char ch = line[cur_delim_pos];
69c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (ch == delimiter) {
70c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      // Found delimiter
71c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return cur_delim_pos;
72c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
73c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
74c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // We hit the start of a quoted string.  Look for its end.
75c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    search_start = FindStringEnd(line, cur_delim_pos, ch);
76c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (search_start == line.length())
77c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return search_start;
78c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
79c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    ++search_start;
80c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
81c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // search_start now points to the first char after the end of the
82c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // string, so just go back to the top of the loop and look for
83c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // |delimiter| again.
84c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  } while (true);
85c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
86c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  NOTREACHED();
87c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return line.length();
88c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
89c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
90c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
91c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid HttpUtil::ParseContentType(const string& content_type_str,
92c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                string* mime_type, string* charset,
93c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                bool *had_charset) {
94c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Trim leading and trailing whitespace from type.  We include '(' in
95c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // the trailing trim set to catch media-type comments, which are not at all
96c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // standard, but may occur in rare cases.
97c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  size_t type_val = content_type_str.find_first_not_of(HTTP_LWS);
98c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  type_val = std::min(type_val, content_type_str.length());
99c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  size_t type_end = content_type_str.find_first_of(HTTP_LWS ";(", type_val);
100c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (string::npos == type_end)
101c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    type_end = content_type_str.length();
102c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
103c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  size_t charset_val = 0;
104c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  size_t charset_end = 0;
105c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
106c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Iterate over parameters
107c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  bool type_has_charset = false;
108c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  size_t param_start = content_type_str.find_first_of(';', type_end);
109c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (param_start != string::npos) {
110c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // We have parameters.  Iterate over them.
111c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    size_t cur_param_start = param_start + 1;
112c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    do {
113c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      size_t cur_param_end =
114c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott          FindDelimiter(content_type_str, cur_param_start, ';');
115c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      size_t param_name_start = content_type_str.find_first_not_of(
117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          HTTP_LWS, cur_param_start);
118c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      param_name_start = std::min(param_name_start, cur_param_end);
119c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
120c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      static const char charset_str[] = "charset=";
121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      size_t charset_end_offset = std::min(
122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          param_name_start + sizeof(charset_str) - 1, cur_param_end);
123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (LowerCaseEqualsASCII(
124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch              content_type_str.begin() + param_name_start,
125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch              content_type_str.begin() + charset_end_offset, charset_str)) {
126c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        charset_val = param_name_start + sizeof(charset_str) - 1;
127c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        charset_end = cur_param_end;
128c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        type_has_charset = true;
129c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      }
130c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
131c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      cur_param_start = cur_param_end + 1;
132c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    } while (cur_param_start < content_type_str.length());
133c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
134c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
135c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (type_has_charset) {
136c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // Trim leading and trailing whitespace from charset_val.  We include
137c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // '(' in the trailing trim set to catch media-type comments, which are
138c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // not at all standard, but may occur in rare cases.
139c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    charset_val = content_type_str.find_first_not_of(HTTP_LWS, charset_val);
140c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    charset_val = std::min(charset_val, charset_end);
141c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    char first_char = content_type_str[charset_val];
142c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (first_char == '"' || first_char == '\'') {
143c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      charset_end = FindStringEnd(content_type_str, charset_val, first_char);
144c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      ++charset_val;
145c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      DCHECK(charset_end >= charset_val);
146c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    } else {
147c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      charset_end = std::min(content_type_str.find_first_of(HTTP_LWS ";(",
148c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                                            charset_val),
149c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                             charset_end);
150c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
151c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
152c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
153c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // if the server sent "*/*", it is meaningless, so do not store it.
154c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // also, if type_val is the same as mime_type, then just update the
155c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // charset.  however, if charset is empty and mime_type hasn't
156c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // changed, then don't wipe-out an existing charset.  We
157c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // also want to reject a mime-type if it does not include a slash.
158c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // some servers give junk after the charset parameter, which may
159c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // include a comma, so this check makes us a bit more tolerant.
160c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (content_type_str.length() != 0 &&
161c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      content_type_str != "*/*" &&
162c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      content_type_str.find_first_of('/') != string::npos) {
163c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // Common case here is that mime_type is empty
164c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    bool eq = !mime_type->empty() &&
165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch              LowerCaseEqualsASCII(content_type_str.begin() + type_val,
166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                   content_type_str.begin() + type_end,
167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                   mime_type->data());
168c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (!eq) {
169c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      mime_type->assign(content_type_str.begin() + type_val,
170c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                        content_type_str.begin() + type_end);
171c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      StringToLowerASCII(mime_type);
172c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
173c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if ((!eq && *had_charset) || type_has_charset) {
174c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      *had_charset = true;
175c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      charset->assign(content_type_str.begin() + charset_val,
176c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                      content_type_str.begin() + charset_end);
177c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      StringToLowerASCII(charset);
178c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
179c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
180c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
181c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
182c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
183c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Parse the Range header according to RFC 2616 14.35.1
184c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// ranges-specifier = byte-ranges-specifier
185c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// byte-ranges-specifier = bytes-unit "=" byte-range-set
186c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// byte-range-set  = 1#( byte-range-spec | suffix-byte-range-spec )
187c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// byte-range-spec = first-byte-pos "-" [last-byte-pos]
188c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// first-byte-pos  = 1*DIGIT
189c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// last-byte-pos   = 1*DIGIT
190c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool HttpUtil::ParseRanges(const std::string& headers,
191c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                           std::vector<HttpByteRange>* ranges) {
192c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string ranges_specifier;
193c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\r\n");
194c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
195c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  while (it.GetNext()) {
196c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // Look for "Range" header.
197c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (!LowerCaseEqualsASCII(it.name(), "range"))
198c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      continue;
199c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    ranges_specifier = it.values();
200c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // We just care about the first "Range" header, so break here.
201c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    break;
202c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
203c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
204c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (ranges_specifier.empty())
205c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
206c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
207c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return ParseRangeHeader(ranges_specifier, ranges);
208c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
209c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
210c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static
211c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool HttpUtil::ParseRangeHeader(const std::string& ranges_specifier,
212c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                std::vector<HttpByteRange>* ranges) {
213c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  size_t equal_char_offset = ranges_specifier.find('=');
214c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (equal_char_offset == std::string::npos)
215c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
216c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
217c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Try to extract bytes-unit part.
218c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string::const_iterator bytes_unit_begin = ranges_specifier.begin();
219c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string::const_iterator bytes_unit_end = bytes_unit_begin +
220c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                               equal_char_offset;
221c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string::const_iterator byte_range_set_begin = bytes_unit_end + 1;
222c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string::const_iterator byte_range_set_end = ranges_specifier.end();
223c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
224c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  TrimLWS(&bytes_unit_begin, &bytes_unit_end);
225c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // "bytes" unit identifier is not found.
226c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!LowerCaseEqualsASCII(bytes_unit_begin, bytes_unit_end, "bytes"))
227c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
228c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
229c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  ValuesIterator byte_range_set_iterator(byte_range_set_begin,
230c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                         byte_range_set_end, ',');
231c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  while (byte_range_set_iterator.GetNext()) {
232c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    size_t minus_char_offset = byte_range_set_iterator.value().find('-');
233c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // If '-' character is not found, reports failure.
234c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (minus_char_offset == std::string::npos)
235c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return false;
236c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
237c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    std::string::const_iterator first_byte_pos_begin =
238c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        byte_range_set_iterator.value_begin();
239c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    std::string::const_iterator first_byte_pos_end =
240c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        first_byte_pos_begin +  minus_char_offset;
241c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    TrimLWS(&first_byte_pos_begin, &first_byte_pos_end);
242c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    std::string first_byte_pos(first_byte_pos_begin, first_byte_pos_end);
243c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
244c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    HttpByteRange range;
245c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // Try to obtain first-byte-pos.
246c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (!first_byte_pos.empty()) {
247c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      int64 first_byte_position = -1;
2483345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      if (!base::StringToInt64(first_byte_pos, &first_byte_position))
249c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        return false;
250c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      range.set_first_byte_position(first_byte_position);
251c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
252c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
253c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    std::string::const_iterator last_byte_pos_begin =
254c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        byte_range_set_iterator.value_begin() + minus_char_offset + 1;
255c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    std::string::const_iterator last_byte_pos_end =
256c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        byte_range_set_iterator.value_end();
257c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    TrimLWS(&last_byte_pos_begin, &last_byte_pos_end);
258c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    std::string last_byte_pos(last_byte_pos_begin, last_byte_pos_end);
259c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
260c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // We have last-byte-pos or suffix-byte-range-spec in this case.
261c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (!last_byte_pos.empty()) {
262c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      int64 last_byte_position;
2633345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      if (!base::StringToInt64(last_byte_pos, &last_byte_position))
264c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        return false;
265c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      if (range.HasFirstBytePosition())
266c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        range.set_last_byte_position(last_byte_position);
267c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      else
268c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        range.set_suffix_length(last_byte_position);
269c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    } else if (!range.HasFirstBytePosition()) {
270c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return false;
271c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
272c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
273c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // Do a final check on the HttpByteRange object.
274c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (!range.IsValid())
275c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return false;
276c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    ranges->push_back(range);
277c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
278dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  return !ranges->empty();
279c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
280c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
281c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
282c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool HttpUtil::HasHeader(const std::string& headers, const char* name) {
283c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  size_t name_len = strlen(name);
284c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  string::const_iterator it =
285c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      std::search(headers.begin(),
286c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                  headers.end(),
287c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                  name,
288c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                  name + name_len,
289513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                  base::CaseInsensitiveCompareASCII<char>());
290c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (it == headers.end())
291c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
292c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
293c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // ensure match is prefixed by newline
294c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (it != headers.begin() && it[-1] != '\n')
295c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
296c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
297c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // ensure match is suffixed by colon
298c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (it + name_len >= headers.end() || it[name_len] != ':')
299c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
300c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
301c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return true;
302c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
303c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
304c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
305c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottstd::string HttpUtil::StripHeaders(const std::string& headers,
306c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                   const char* const headers_to_remove[],
307c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                   size_t headers_to_remove_len) {
308c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string stripped_headers;
309c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  net::HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\r\n");
310c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
311c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  while (it.GetNext()) {
312c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    bool should_remove = false;
313c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    for (size_t i = 0; i < headers_to_remove_len; ++i) {
314c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      if (LowerCaseEqualsASCII(it.name_begin(), it.name_end(),
315c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                               headers_to_remove[i])) {
316c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        should_remove = true;
317c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        break;
318c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      }
319c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
320c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (!should_remove) {
321c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      // Assume that name and values are on the same line.
322c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      stripped_headers.append(it.name_begin(), it.values_end());
323c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      stripped_headers.append("\r\n");
324c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
325c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
326c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return stripped_headers;
327c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
328c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
329c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
330c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool HttpUtil::IsNonCoalescingHeader(string::const_iterator name_begin,
331c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                     string::const_iterator name_end) {
332c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // NOTE: "set-cookie2" headers do not support expires attributes, so we don't
333c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // have to list them here.
334c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  const char* kNonCoalescingHeaders[] = {
335c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    "date",
336c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    "expires",
337c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    "last-modified",
338c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    "location",  // See bug 1050541 for details
339c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    "retry-after",
340c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    "set-cookie",
341c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // The format of auth-challenges mixes both space separated tokens and
342c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // comma separated properties, so coalescing on comma won't work.
343c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    "www-authenticate",
344c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    "proxy-authenticate"
345c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  };
346c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  for (size_t i = 0; i < arraysize(kNonCoalescingHeaders); ++i) {
347c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (LowerCaseEqualsASCII(name_begin, name_end, kNonCoalescingHeaders[i]))
348c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return true;
349c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
350c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return false;
351c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
352c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
353c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool HttpUtil::IsLWS(char c) {
354c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return strchr(HTTP_LWS, c) != NULL;
355c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
356c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
357c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid HttpUtil::TrimLWS(string::const_iterator* begin,
358c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                       string::const_iterator* end) {
359c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // leading whitespace
360c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  while (*begin < *end && IsLWS((*begin)[0]))
361c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    ++(*begin);
362c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
363c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // trailing whitespace
364c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  while (*begin < *end && IsLWS((*end)[-1]))
365c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    --(*end);
366c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
367c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
368c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
369c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool HttpUtil::IsQuote(char c) {
370c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Single quote mark isn't actually part of quoted-text production,
371c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // but apparently some servers rely on this.
372c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return c == '"' || c == '\'';
373c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
374c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
375c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
376c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottstd::string HttpUtil::Unquote(std::string::const_iterator begin,
377c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                              std::string::const_iterator end) {
378c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Empty string
379c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (begin == end)
380c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return std::string();
381c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
382c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Nothing to unquote.
383c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!IsQuote(*begin))
384c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return std::string(begin, end);
385c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
386c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // No terminal quote mark.
387c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (end - begin < 2 || *begin != *(end - 1))
388c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return std::string(begin, end);
389c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
390c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Strip quotemarks
391c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  ++begin;
392c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  --end;
393c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
394c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Unescape quoted-pair (defined in RFC 2616 section 2.2)
395c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string unescaped;
396c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  bool prev_escape = false;
397c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  for (; begin != end; ++begin) {
398c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    char c = *begin;
399c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (c == '\\' && !prev_escape) {
400c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      prev_escape = true;
401c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      continue;
402c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
403c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    prev_escape = false;
404c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    unescaped.push_back(c);
405c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
406c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return unescaped;
407c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
408c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
409c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
410c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottstd::string HttpUtil::Unquote(const std::string& str) {
411c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return Unquote(str.begin(), str.end());
412c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
413c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
414c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
415c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottstd::string HttpUtil::Quote(const std::string& str) {
416c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string escaped;
417c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  escaped.reserve(2 + str.size());
418c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
419c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string::const_iterator begin = str.begin();
420c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string::const_iterator end = str.end();
421c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
422c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Esape any backslashes or quotemarks within the string, and
423c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // then surround with quotes.
424c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  escaped.push_back('"');
425c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  for (; begin != end; ++begin) {
426c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    char c = *begin;
427c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (c == '"' || c == '\\')
428c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      escaped.push_back('\\');
429c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    escaped.push_back(c);
430c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
431c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  escaped.push_back('"');
432c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return escaped;
433c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
434c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
435c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Find the "http" substring in a status line. This allows for
436c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// some slop at the start. If the "http" string could not be found
437c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// then returns -1.
438c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
439c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottint HttpUtil::LocateStartOfStatusLine(const char* buf, int buf_len) {
440c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  const int slop = 4;
441c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  const int http_len = 4;
442872788f42523a0c05bc2fb43623d42344a247f2aKristian Monsen#ifdef ANDROID
443872788f42523a0c05bc2fb43623d42344a247f2aKristian Monsen  const int icy_len = 3;
444872788f42523a0c05bc2fb43623d42344a247f2aKristian Monsen#endif
445c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
446c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (buf_len >= http_len) {
447c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    int i_max = std::min(buf_len - http_len, slop);
448c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    for (int i = 0; i <= i_max; ++i) {
449c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      if (LowerCaseEqualsASCII(buf + i, buf + i + http_len, "http"))
450c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        return i;
451872788f42523a0c05bc2fb43623d42344a247f2aKristian Monsen#ifdef ANDROID
452872788f42523a0c05bc2fb43623d42344a247f2aKristian Monsen      if (LowerCaseEqualsASCII(buf + i, buf + i + icy_len, "icy"))
453872788f42523a0c05bc2fb43623d42344a247f2aKristian Monsen        return i;
454872788f42523a0c05bc2fb43623d42344a247f2aKristian Monsen#endif
455c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
456c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
457c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return -1;  // Not found
458c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
459c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
460c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottint HttpUtil::LocateEndOfHeaders(const char* buf, int buf_len, int i) {
461c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  bool was_lf = false;
462c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  char last_c = '\0';
463c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  for (; i < buf_len; ++i) {
464c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    char c = buf[i];
465c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (c == '\n') {
466c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      if (was_lf)
467c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        return i + 1;
468c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      was_lf = true;
469c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    } else if (c != '\r' || last_c != '\n') {
470c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      was_lf = false;
471c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
472c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    last_c = c;
473c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
474c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return -1;
475c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
476c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
477c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// In order for a line to be continuable, it must specify a
478c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// non-blank header-name. Line continuations are specifically for
479c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// header values -- do not allow headers names to span lines.
480c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottstatic bool IsLineSegmentContinuable(const char* begin, const char* end) {
481c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (begin == end)
482c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
483c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
484c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  const char* colon = std::find(begin, end, ':');
485c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (colon == end)
486c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
487c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
488c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  const char* name_begin = begin;
489c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  const char* name_end = colon;
490c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
491c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Name can't be empty.
492c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (name_begin == name_end)
493c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
494c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
495c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Can't start with LWS (this would imply the segment is a continuation)
496c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (HttpUtil::IsLWS(*name_begin))
497c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
498c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
499c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return true;
500c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
501c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
502c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Helper used by AssembleRawHeaders, to find the end of the status line.
503c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottstatic const char* FindStatusLineEnd(const char* begin, const char* end) {
504c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  size_t i = base::StringPiece(begin, end - begin).find_first_of("\r\n");
505c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (i == base::StringPiece::npos)
506c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return end;
507c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return begin + i;
508c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
509c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
510c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Helper used by AssembleRawHeaders, to skip past leading LWS.
511c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottstatic const char* FindFirstNonLWS(const char* begin, const char* end) {
512c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  for (const char* cur = begin; cur != end; ++cur) {
513c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (!HttpUtil::IsLWS(*cur))
514c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return cur;
515c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
516c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return end;  // Not found.
517c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
518c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
519c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottstd::string HttpUtil::AssembleRawHeaders(const char* input_begin,
520c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                         int input_len) {
521c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string raw_headers;
522c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  raw_headers.reserve(input_len);
523c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
524c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  const char* input_end = input_begin + input_len;
525c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
526c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Skip any leading slop, since the consumers of this output
527c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // (HttpResponseHeaders) don't deal with it.
528c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  int status_begin_offset = LocateStartOfStatusLine(input_begin, input_len);
529c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (status_begin_offset != -1)
530c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    input_begin += status_begin_offset;
531c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
532c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Copy the status line.
533c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  const char* status_line_end = FindStatusLineEnd(input_begin, input_end);
534c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  raw_headers.append(input_begin, status_line_end);
535c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
536c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // After the status line, every subsequent line is a header line segment.
537c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Should a segment start with LWS, it is a continuation of the previous
538c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // line's field-value.
539c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
540c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // TODO(ericroman): is this too permissive? (delimits on [\r\n]+)
541c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  CStringTokenizer lines(status_line_end, input_end, "\r\n");
542c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
543c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // This variable is true when the previous line was continuable.
544c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  bool prev_line_continuable = false;
545c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
546c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  while (lines.GetNext()) {
547c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    const char* line_begin = lines.token_begin();
548c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    const char* line_end = lines.token_end();
549c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
550c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (prev_line_continuable && IsLWS(*line_begin)) {
551c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      // Join continuation; reduce the leading LWS to a single SP.
552c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      raw_headers.push_back(' ');
553c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      raw_headers.append(FindFirstNonLWS(line_begin, line_end), line_end);
554c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    } else {
555c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      // Terminate the previous line.
556c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      raw_headers.push_back('\0');
557c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
558c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      // Copy the raw data to output.
559c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      raw_headers.append(line_begin, line_end);
560c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
561c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      // Check if the current line can be continued.
562c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      prev_line_continuable = IsLineSegmentContinuable(line_begin, line_end);
563c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
564c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
565c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
566c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  raw_headers.append("\0\0", 2);
567c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return raw_headers;
568c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
569c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
570c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// TODO(jungshik): 1. If the list is 'fr-CA,fr-FR,en,de', we have to add
571c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// 'fr' after 'fr-CA' with the same q-value as 'fr-CA' because
572c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// web servers, in general, do not fall back to 'fr' and may end up picking
573c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// 'en' which has a lower preference than 'fr-CA' and 'fr-FR'.
574c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// 2. This function assumes that the input is a comma separated list
575c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// without any whitespace. As long as it comes from the preference and
576c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// a user does not manually edit the preference file, it's the case. Still,
577c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// we may have to make it more robust.
578c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottstd::string HttpUtil::GenerateAcceptLanguageHeader(
579c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    const std::string& raw_language_list) {
580c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // We use integers for qvalue and qvalue decrement that are 10 times
581c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // larger than actual values to avoid a problem with comparing
582c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // two floating point numbers.
583c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  const unsigned int kQvalueDecrement10 = 2;
584c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  unsigned int qvalue10 = 10;
585c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  StringTokenizer t(raw_language_list, ",");
586c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string lang_list_with_q;
587c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  while (t.GetNext()) {
588c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    std::string language = t.token();
589c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (qvalue10 == 10) {
590c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      // q=1.0 is implicit.
591c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      lang_list_with_q = language;
592c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    } else {
593c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      DCHECK_LT(qvalue10, 10U);
5944a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      base::StringAppendF(&lang_list_with_q, ",%s;q=0.%d", language.c_str(),
5954a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                          qvalue10);
596c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
597c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // It does not make sense to have 'q=0'.
598c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (qvalue10 > kQvalueDecrement10)
599c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      qvalue10 -= kQvalueDecrement10;
600c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
601c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return lang_list_with_q;
602c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
603c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
604c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottstd::string HttpUtil::GenerateAcceptCharsetHeader(const std::string& charset) {
605c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string charset_with_q = charset;
606c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (LowerCaseEqualsASCII(charset, "utf-8")) {
607c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    charset_with_q += ",*;q=0.5";
608c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  } else {
609c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    charset_with_q += ",utf-8;q=0.7,*;q=0.3";
610c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
611c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return charset_with_q;
612c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
613c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
614c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid HttpUtil::AppendHeaderIfMissing(const char* header_name,
615c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                     const std::string& header_value,
616c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                     std::string* headers) {
617c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (header_value.empty())
618c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return;
619c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (net::HttpUtil::HasHeader(*headers, header_name))
620c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return;
621c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  *headers += std::string(header_name) + ": " + header_value + "\r\n";
622c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
623c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
624c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// BNF from section 4.2 of RFC 2616:
625c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//
626c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//   message-header = field-name ":" [ field-value ]
627c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//   field-name     = token
628c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//   field-value    = *( field-content | LWS )
629c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//   field-content  = <the OCTETs making up the field-value
630c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//                     and consisting of either *TEXT or combinations
631c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//                     of token, separators, and quoted-string>
632c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//
633c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
634c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick ScottHttpUtil::HeadersIterator::HeadersIterator(string::const_iterator headers_begin,
635c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                           string::const_iterator headers_end,
636c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                           const std::string& line_delimiter)
637c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    : lines_(headers_begin, headers_end, line_delimiter) {
638c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
639c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
6403345a6884c488ff3a535c2c9acdd33d74b37e311Iain MerrickHttpUtil::HeadersIterator::~HeadersIterator() {
6413345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
6423345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
643c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool HttpUtil::HeadersIterator::GetNext() {
644c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  while (lines_.GetNext()) {
645c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    name_begin_ = lines_.token_begin();
646c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    values_end_ = lines_.token_end();
647c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
648c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    string::const_iterator colon = find(name_begin_, values_end_, ':');
649c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (colon == values_end_)
650c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      continue;  // skip malformed header
651c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
652c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    name_end_ = colon;
653c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
654c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // If the name starts with LWS, it is an invalid line.
655c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // Leading LWS implies a line continuation, and these should have
656c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // already been joined by AssembleRawHeaders().
657c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (name_begin_ == name_end_ || IsLWS(*name_begin_))
658c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      continue;
659c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
660c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    TrimLWS(&name_begin_, &name_end_);
661c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (name_begin_ == name_end_)
662c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      continue;  // skip malformed header
663c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
664c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    values_begin_ = colon + 1;
665c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    TrimLWS(&values_begin_, &values_end_);
666c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
667c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // if we got a header name, then we are done.
668c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return true;
669c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
670c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return false;
671c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
672c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
673c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool HttpUtil::HeadersIterator::AdvanceTo(const char* name) {
674c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  DCHECK(name != NULL);
675c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  DCHECK_EQ(0, StringToLowerASCII<std::string>(name).compare(name))
676c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      << "the header name must be in all lower case";
677c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
678c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  while (GetNext()) {
679c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (LowerCaseEqualsASCII(name_begin_, name_end_, name)) {
680c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return true;
681c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
682c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
683c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
684c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return false;
685c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
686c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
687c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick ScottHttpUtil::ValuesIterator::ValuesIterator(
688c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    string::const_iterator values_begin,
689c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    string::const_iterator values_end,
690c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    char delimiter)
691c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    : values_(values_begin, values_end, string(1, delimiter)) {
692c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  values_.set_quote_chars("\'\"");
693c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
694c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
6953345a6884c488ff3a535c2c9acdd33d74b37e311Iain MerrickHttpUtil::ValuesIterator::~ValuesIterator() {
6963345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
6973345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
698c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool HttpUtil::ValuesIterator::GetNext() {
699c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  while (values_.GetNext()) {
700c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    value_begin_ = values_.token_begin();
701c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    value_end_ = values_.token_end();
702c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    TrimLWS(&value_begin_, &value_end_);
703c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
704c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // bypass empty values.
705c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (value_begin_ != value_end_)
706c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return true;
707c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
708c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return false;
709c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
710c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
711731df977c0511bca2206b5f333555b1205ff1f43Iain MerrickHttpUtil::NameValuePairsIterator::NameValuePairsIterator(
712731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    string::const_iterator begin,
713731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    string::const_iterator end,
714731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    char delimiter)
715731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    : props_(begin, end, delimiter),
716731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      valid_(true),
717731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      begin_(begin),
718731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      end_(end),
719731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      name_begin_(end),
720731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      name_end_(end),
721731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      value_begin_(end),
722731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      value_end_(end),
723731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      value_is_quoted_(false) {
724731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
725731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
7263f50c38dc070f4bb515c1b64450dae14f316474eKristian MonsenHttpUtil::NameValuePairsIterator::~NameValuePairsIterator() {}
7273f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen
728731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick// We expect properties to be formatted as one of:
729731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick//   name="value"
730731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick//   name='value'
731731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick//   name='\'value\''
732731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick//   name=value
733731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick//   name = value
734731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick//   name=
735731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick// Due to buggy implementations found in some embedded devices, we also
736731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick// accept values with missing close quotemark (http://crbug.com/39836):
737731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick//   name="value
738731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickbool HttpUtil::NameValuePairsIterator::GetNext() {
739731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  if (!props_.GetNext())
740731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    return false;
741731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
742731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // Set the value as everything. Next we will split out the name.
743731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  value_begin_ = props_.value_begin();
744731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  value_end_ = props_.value_end();
745731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  name_begin_ = name_end_ = value_end_;
746731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
747731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // Scan for the equals sign.
748731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  std::string::const_iterator equals = std::find(value_begin_, value_end_, '=');
749731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  if (equals == value_end_ || equals == value_begin_)
750513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    return valid_ = false;  // Malformed, no equals sign
751731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
752731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // Verify that the equals sign we found wasn't inside of quote marks.
753731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  for (std::string::const_iterator it = value_begin_; it != equals; ++it) {
754731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    if (HttpUtil::IsQuote(*it))
755513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch      return valid_ = false;  // Malformed, quote appears before equals sign
756731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  }
757731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
758731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  name_begin_ = value_begin_;
759731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  name_end_ = equals;
760731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  value_begin_ = equals + 1;
761731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
762731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  TrimLWS(&name_begin_, &name_end_);
763731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  TrimLWS(&value_begin_, &value_end_);
764731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  value_is_quoted_ = false;
765513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  unquoted_value_.clear();
766513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
767513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  if (value_begin_ == value_end_)
768513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    return valid_ = false;  // Malformed, value is empty
769513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
770513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  if (HttpUtil::IsQuote(*value_begin_)) {
771731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    // Trim surrounding quotemarks off the value
772513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    if (*value_begin_ != *(value_end_ - 1) || value_begin_ + 1 == value_end_) {
773731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      // NOTE: This is not as graceful as it sounds:
774731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      // * quoted-pairs will no longer be unquoted
775731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      //   (["\"hello] should give ["hello]).
776731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      // * Does not detect when the final quote is escaped
777731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      //   (["value\"] should give [value"])
778731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      ++value_begin_;  // Gracefully recover from mismatching quotes.
779513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    } else {
780731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      value_is_quoted_ = true;
781513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch      // Do not store iterators into this. See declaration of unquoted_value_.
782513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch      unquoted_value_ = HttpUtil::Unquote(value_begin_, value_end_);
783513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    }
784731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  }
785731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
786731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  return true;
787731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
788731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
789c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}  // namespace net
790