| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // The rules for parsing content-types were borrowed from Firefox: | 5 // The rules for parsing content-types were borrowed from Firefox: |
| 6 // http://lxr.mozilla.org/mozilla/source/netwerk/base/src/nsURLHelper.cpp#834 | 6 // http://lxr.mozilla.org/mozilla/source/netwerk/base/src/nsURLHelper.cpp#834 |
| 7 | 7 |
| 8 #include "net/http/http_util.h" | 8 #include "net/http/http_util.h" |
| 9 | 9 |
| 10 #include <algorithm> | 10 #include <algorithm> |
| (...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 111 | 111 |
| 112 std::string::const_iterator param_name_begin = tokenizer.token_begin(); | 112 std::string::const_iterator param_name_begin = tokenizer.token_begin(); |
| 113 std::string::const_iterator param_name_end = equals_sign; | 113 std::string::const_iterator param_name_end = equals_sign; |
| 114 TrimLWS(¶m_name_begin, ¶m_name_end); | 114 TrimLWS(¶m_name_begin, ¶m_name_end); |
| 115 | 115 |
| 116 std::string::const_iterator param_value_begin = equals_sign + 1; | 116 std::string::const_iterator param_value_begin = equals_sign + 1; |
| 117 std::string::const_iterator param_value_end = tokenizer.token_end(); | 117 std::string::const_iterator param_value_end = tokenizer.token_end(); |
| 118 DCHECK(param_value_begin <= tokenizer.token_end()); | 118 DCHECK(param_value_begin <= tokenizer.token_end()); |
| 119 TrimLWS(¶m_value_begin, ¶m_value_end); | 119 TrimLWS(¶m_value_begin, ¶m_value_end); |
| 120 | 120 |
| 121 if (LowerCaseEqualsASCII(param_name_begin, param_name_end, "charset")) { | 121 if (base::LowerCaseEqualsASCII(param_name_begin, param_name_end, |
| 122 "charset")) { |
| 122 // TODO(abarth): Refactor this function to consistently use iterators. | 123 // TODO(abarth): Refactor this function to consistently use iterators. |
| 123 charset_val = param_value_begin - begin; | 124 charset_val = param_value_begin - begin; |
| 124 charset_end = param_value_end - begin; | 125 charset_end = param_value_end - begin; |
| 125 type_has_charset = true; | 126 type_has_charset = true; |
| 126 } else if (LowerCaseEqualsASCII(param_name_begin, param_name_end, | 127 } else if (base::LowerCaseEqualsASCII(param_name_begin, param_name_end, |
| 127 "boundary")) { | 128 "boundary")) { |
| 128 if (boundary) | 129 if (boundary) |
| 129 boundary->assign(param_value_begin, param_value_end); | 130 boundary->assign(param_value_begin, param_value_end); |
| 130 } | 131 } |
| 131 } | 132 } |
| 132 } | 133 } |
| 133 | 134 |
| 134 if (type_has_charset) { | 135 if (type_has_charset) { |
| 135 // Trim leading and trailing whitespace from charset_val. We include | 136 // Trim leading and trailing whitespace from charset_val. We include |
| 136 // '(' in the trailing trim set to catch media-type comments, which are | 137 // '(' in the trailing trim set to catch media-type comments, which are |
| 137 // not at all standard, but may occur in rare cases. | 138 // not at all standard, but may occur in rare cases. |
| (...skipping 15 matching lines...) Expand all Loading... |
| 153 // also, if type_val is the same as mime_type, then just update the | 154 // also, if type_val is the same as mime_type, then just update the |
| 154 // charset. however, if charset is empty and mime_type hasn't | 155 // charset. however, if charset is empty and mime_type hasn't |
| 155 // changed, then don't wipe-out an existing charset. We | 156 // changed, then don't wipe-out an existing charset. We |
| 156 // also want to reject a mime-type if it does not include a slash. | 157 // also want to reject a mime-type if it does not include a slash. |
| 157 // some servers give junk after the charset parameter, which may | 158 // some servers give junk after the charset parameter, which may |
| 158 // include a comma, so this check makes us a bit more tolerant. | 159 // include a comma, so this check makes us a bit more tolerant. |
| 159 if (content_type_str.length() != 0 && | 160 if (content_type_str.length() != 0 && |
| 160 content_type_str != "*/*" && | 161 content_type_str != "*/*" && |
| 161 content_type_str.find_first_of('/') != std::string::npos) { | 162 content_type_str.find_first_of('/') != std::string::npos) { |
| 162 // Common case here is that mime_type is empty | 163 // Common case here is that mime_type is empty |
| 163 bool eq = !mime_type->empty() && LowerCaseEqualsASCII(begin + type_val, | 164 bool eq = !mime_type->empty() && |
| 164 begin + type_end, | 165 base::LowerCaseEqualsASCII(begin + type_val, begin + type_end, |
| 165 mime_type->data()); | 166 mime_type->data()); |
| 166 if (!eq) { | 167 if (!eq) { |
| 167 mime_type->assign(begin + type_val, begin + type_end); | 168 mime_type->assign(begin + type_val, begin + type_end); |
| 168 base::StringToLowerASCII(mime_type); | 169 base::StringToLowerASCII(mime_type); |
| 169 } | 170 } |
| 170 if ((!eq && *had_charset) || type_has_charset) { | 171 if ((!eq && *had_charset) || type_has_charset) { |
| 171 *had_charset = true; | 172 *had_charset = true; |
| 172 charset->assign(begin + charset_val, begin + charset_end); | 173 charset->assign(begin + charset_val, begin + charset_end); |
| 173 base::StringToLowerASCII(charset); | 174 base::StringToLowerASCII(charset); |
| 174 } | 175 } |
| 175 } | 176 } |
| 176 } | 177 } |
| 177 | 178 |
| 178 // static | 179 // static |
| 179 // Parse the Range header according to RFC 2616 14.35.1 | 180 // Parse the Range header according to RFC 2616 14.35.1 |
| 180 // ranges-specifier = byte-ranges-specifier | 181 // ranges-specifier = byte-ranges-specifier |
| 181 // byte-ranges-specifier = bytes-unit "=" byte-range-set | 182 // byte-ranges-specifier = bytes-unit "=" byte-range-set |
| 182 // byte-range-set = 1#( byte-range-spec | suffix-byte-range-spec ) | 183 // byte-range-set = 1#( byte-range-spec | suffix-byte-range-spec ) |
| 183 // byte-range-spec = first-byte-pos "-" [last-byte-pos] | 184 // byte-range-spec = first-byte-pos "-" [last-byte-pos] |
| 184 // first-byte-pos = 1*DIGIT | 185 // first-byte-pos = 1*DIGIT |
| 185 // last-byte-pos = 1*DIGIT | 186 // last-byte-pos = 1*DIGIT |
| 186 bool HttpUtil::ParseRanges(const std::string& headers, | 187 bool HttpUtil::ParseRanges(const std::string& headers, |
| 187 std::vector<HttpByteRange>* ranges) { | 188 std::vector<HttpByteRange>* ranges) { |
| 188 std::string ranges_specifier; | 189 std::string ranges_specifier; |
| 189 HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\r\n"); | 190 HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\r\n"); |
| 190 | 191 |
| 191 while (it.GetNext()) { | 192 while (it.GetNext()) { |
| 192 // Look for "Range" header. | 193 // Look for "Range" header. |
| 193 if (!LowerCaseEqualsASCII(it.name(), "range")) | 194 if (!base::LowerCaseEqualsASCII(it.name(), "range")) |
| 194 continue; | 195 continue; |
| 195 ranges_specifier = it.values(); | 196 ranges_specifier = it.values(); |
| 196 // We just care about the first "Range" header, so break here. | 197 // We just care about the first "Range" header, so break here. |
| 197 break; | 198 break; |
| 198 } | 199 } |
| 199 | 200 |
| 200 if (ranges_specifier.empty()) | 201 if (ranges_specifier.empty()) |
| 201 return false; | 202 return false; |
| 202 | 203 |
| 203 return ParseRangeHeader(ranges_specifier, ranges); | 204 return ParseRangeHeader(ranges_specifier, ranges); |
| 204 } | 205 } |
| 205 | 206 |
| 206 // static | 207 // static |
| 207 bool HttpUtil::ParseRangeHeader(const std::string& ranges_specifier, | 208 bool HttpUtil::ParseRangeHeader(const std::string& ranges_specifier, |
| 208 std::vector<HttpByteRange>* ranges) { | 209 std::vector<HttpByteRange>* ranges) { |
| 209 size_t equal_char_offset = ranges_specifier.find('='); | 210 size_t equal_char_offset = ranges_specifier.find('='); |
| 210 if (equal_char_offset == std::string::npos) | 211 if (equal_char_offset == std::string::npos) |
| 211 return false; | 212 return false; |
| 212 | 213 |
| 213 // Try to extract bytes-unit part. | 214 // Try to extract bytes-unit part. |
| 214 std::string::const_iterator bytes_unit_begin = ranges_specifier.begin(); | 215 std::string::const_iterator bytes_unit_begin = ranges_specifier.begin(); |
| 215 std::string::const_iterator bytes_unit_end = bytes_unit_begin + | 216 std::string::const_iterator bytes_unit_end = bytes_unit_begin + |
| 216 equal_char_offset; | 217 equal_char_offset; |
| 217 std::string::const_iterator byte_range_set_begin = bytes_unit_end + 1; | 218 std::string::const_iterator byte_range_set_begin = bytes_unit_end + 1; |
| 218 std::string::const_iterator byte_range_set_end = ranges_specifier.end(); | 219 std::string::const_iterator byte_range_set_end = ranges_specifier.end(); |
| 219 | 220 |
| 220 TrimLWS(&bytes_unit_begin, &bytes_unit_end); | 221 TrimLWS(&bytes_unit_begin, &bytes_unit_end); |
| 221 // "bytes" unit identifier is not found. | 222 // "bytes" unit identifier is not found. |
| 222 if (!LowerCaseEqualsASCII(bytes_unit_begin, bytes_unit_end, "bytes")) | 223 if (!base::LowerCaseEqualsASCII(bytes_unit_begin, bytes_unit_end, "bytes")) |
| 223 return false; | 224 return false; |
| 224 | 225 |
| 225 ValuesIterator byte_range_set_iterator(byte_range_set_begin, | 226 ValuesIterator byte_range_set_iterator(byte_range_set_begin, |
| 226 byte_range_set_end, ','); | 227 byte_range_set_end, ','); |
| 227 while (byte_range_set_iterator.GetNext()) { | 228 while (byte_range_set_iterator.GetNext()) { |
| 228 size_t minus_char_offset = byte_range_set_iterator.value().find('-'); | 229 size_t minus_char_offset = byte_range_set_iterator.value().find('-'); |
| 229 // If '-' character is not found, reports failure. | 230 // If '-' character is not found, reports failure. |
| 230 if (minus_char_offset == std::string::npos) | 231 if (minus_char_offset == std::string::npos) |
| 231 return false; | 232 return false; |
| 232 | 233 |
| (...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 378 // static | 379 // static |
| 379 std::string HttpUtil::StripHeaders(const std::string& headers, | 380 std::string HttpUtil::StripHeaders(const std::string& headers, |
| 380 const char* const headers_to_remove[], | 381 const char* const headers_to_remove[], |
| 381 size_t headers_to_remove_len) { | 382 size_t headers_to_remove_len) { |
| 382 std::string stripped_headers; | 383 std::string stripped_headers; |
| 383 HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\r\n"); | 384 HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\r\n"); |
| 384 | 385 |
| 385 while (it.GetNext()) { | 386 while (it.GetNext()) { |
| 386 bool should_remove = false; | 387 bool should_remove = false; |
| 387 for (size_t i = 0; i < headers_to_remove_len; ++i) { | 388 for (size_t i = 0; i < headers_to_remove_len; ++i) { |
| 388 if (LowerCaseEqualsASCII(it.name_begin(), it.name_end(), | 389 if (base::LowerCaseEqualsASCII(it.name_begin(), it.name_end(), |
| 389 headers_to_remove[i])) { | 390 headers_to_remove[i])) { |
| 390 should_remove = true; | 391 should_remove = true; |
| 391 break; | 392 break; |
| 392 } | 393 } |
| 393 } | 394 } |
| 394 if (!should_remove) { | 395 if (!should_remove) { |
| 395 // Assume that name and values are on the same line. | 396 // Assume that name and values are on the same line. |
| 396 stripped_headers.append(it.name_begin(), it.values_end()); | 397 stripped_headers.append(it.name_begin(), it.values_end()); |
| 397 stripped_headers.append("\r\n"); | 398 stripped_headers.append("\r\n"); |
| 398 } | 399 } |
| 399 } | 400 } |
| (...skipping 14 matching lines...) Expand all Loading... |
| 414 "set-cookie", | 415 "set-cookie", |
| 415 // The format of auth-challenges mixes both space separated tokens and | 416 // The format of auth-challenges mixes both space separated tokens and |
| 416 // comma separated properties, so coalescing on comma won't work. | 417 // comma separated properties, so coalescing on comma won't work. |
| 417 "www-authenticate", | 418 "www-authenticate", |
| 418 "proxy-authenticate", | 419 "proxy-authenticate", |
| 419 // STS specifies that UAs must not process any STS headers after the first | 420 // STS specifies that UAs must not process any STS headers after the first |
| 420 // one. | 421 // one. |
| 421 "strict-transport-security" | 422 "strict-transport-security" |
| 422 }; | 423 }; |
| 423 for (size_t i = 0; i < arraysize(kNonCoalescingHeaders); ++i) { | 424 for (size_t i = 0; i < arraysize(kNonCoalescingHeaders); ++i) { |
| 424 if (LowerCaseEqualsASCII(name_begin, name_end, kNonCoalescingHeaders[i])) | 425 if (base::LowerCaseEqualsASCII(name_begin, name_end, |
| 426 kNonCoalescingHeaders[i])) |
| 425 return true; | 427 return true; |
| 426 } | 428 } |
| 427 return false; | 429 return false; |
| 428 } | 430 } |
| 429 | 431 |
| 430 bool HttpUtil::IsLWS(char c) { | 432 bool HttpUtil::IsLWS(char c) { |
| 431 return strchr(HTTP_LWS, c) != NULL; | 433 return strchr(HTTP_LWS, c) != NULL; |
| 432 } | 434 } |
| 433 | 435 |
| 434 void HttpUtil::TrimLWS(std::string::const_iterator* begin, | 436 void HttpUtil::TrimLWS(std::string::const_iterator* begin, |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 528 // some slop at the start. If the "http" string could not be found | 530 // some slop at the start. If the "http" string could not be found |
| 529 // then returns -1. | 531 // then returns -1. |
| 530 // static | 532 // static |
| 531 int HttpUtil::LocateStartOfStatusLine(const char* buf, int buf_len) { | 533 int HttpUtil::LocateStartOfStatusLine(const char* buf, int buf_len) { |
| 532 const int slop = 4; | 534 const int slop = 4; |
| 533 const int http_len = 4; | 535 const int http_len = 4; |
| 534 | 536 |
| 535 if (buf_len >= http_len) { | 537 if (buf_len >= http_len) { |
| 536 int i_max = std::min(buf_len - http_len, slop); | 538 int i_max = std::min(buf_len - http_len, slop); |
| 537 for (int i = 0; i <= i_max; ++i) { | 539 for (int i = 0; i <= i_max; ++i) { |
| 538 if (LowerCaseEqualsASCII(buf + i, buf + i + http_len, "http")) | 540 if (base::LowerCaseEqualsASCII(buf + i, buf + i + http_len, "http")) |
| 539 return i; | 541 return i; |
| 540 } | 542 } |
| 541 } | 543 } |
| 542 return -1; // Not found | 544 return -1; // Not found |
| 543 } | 545 } |
| 544 | 546 |
| 545 int HttpUtil::LocateEndOfHeaders(const char* buf, int buf_len, int i) { | 547 int HttpUtil::LocateEndOfHeaders(const char* buf, int buf_len, int i) { |
| 546 bool was_lf = false; | 548 bool was_lf = false; |
| 547 char last_c = '\0'; | 549 char last_c = '\0'; |
| 548 for (; i < buf_len; ++i) { | 550 for (; i < buf_len; ++i) { |
| (...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 724 return false; | 726 return false; |
| 725 | 727 |
| 726 if (!etag_header.empty()) { | 728 if (!etag_header.empty()) { |
| 727 size_t slash = etag_header.find('/'); | 729 size_t slash = etag_header.find('/'); |
| 728 if (slash == std::string::npos || slash == 0) | 730 if (slash == std::string::npos || slash == 0) |
| 729 return true; | 731 return true; |
| 730 | 732 |
| 731 std::string::const_iterator i = etag_header.begin(); | 733 std::string::const_iterator i = etag_header.begin(); |
| 732 std::string::const_iterator j = etag_header.begin() + slash; | 734 std::string::const_iterator j = etag_header.begin() + slash; |
| 733 TrimLWS(&i, &j); | 735 TrimLWS(&i, &j); |
| 734 if (!LowerCaseEqualsASCII(i, j, "w")) | 736 if (!base::LowerCaseEqualsASCII(i, j, "w")) |
| 735 return true; | 737 return true; |
| 736 } | 738 } |
| 737 | 739 |
| 738 base::Time last_modified; | 740 base::Time last_modified; |
| 739 if (!base::Time::FromString(last_modified_header.c_str(), &last_modified)) | 741 if (!base::Time::FromString(last_modified_header.c_str(), &last_modified)) |
| 740 return false; | 742 return false; |
| 741 | 743 |
| 742 base::Time date; | 744 base::Time date; |
| 743 if (!base::Time::FromString(date_header.c_str(), &date)) | 745 if (!base::Time::FromString(date_header.c_str(), &date)) |
| 744 return false; | 746 return false; |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 825 } | 827 } |
| 826 return false; | 828 return false; |
| 827 } | 829 } |
| 828 | 830 |
| 829 bool HttpUtil::HeadersIterator::AdvanceTo(const char* name) { | 831 bool HttpUtil::HeadersIterator::AdvanceTo(const char* name) { |
| 830 DCHECK(name != NULL); | 832 DCHECK(name != NULL); |
| 831 DCHECK_EQ(0, base::StringToLowerASCII<std::string>(name).compare(name)) | 833 DCHECK_EQ(0, base::StringToLowerASCII<std::string>(name).compare(name)) |
| 832 << "the header name must be in all lower case"; | 834 << "the header name must be in all lower case"; |
| 833 | 835 |
| 834 while (GetNext()) { | 836 while (GetNext()) { |
| 835 if (LowerCaseEqualsASCII(name_begin_, name_end_, name)) { | 837 if (base::LowerCaseEqualsASCII(name_begin_, name_end_, name)) { |
| 836 return true; | 838 return true; |
| 837 } | 839 } |
| 838 } | 840 } |
| 839 | 841 |
| 840 return false; | 842 return false; |
| 841 } | 843 } |
| 842 | 844 |
| 843 HttpUtil::ValuesIterator::ValuesIterator( | 845 HttpUtil::ValuesIterator::ValuesIterator( |
| 844 std::string::const_iterator values_begin, | 846 std::string::const_iterator values_begin, |
| 845 std::string::const_iterator values_end, | 847 std::string::const_iterator values_end, |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 934 value_is_quoted_ = true; | 936 value_is_quoted_ = true; |
| 935 // Do not store iterators into this. See declaration of unquoted_value_. | 937 // Do not store iterators into this. See declaration of unquoted_value_. |
| 936 unquoted_value_ = HttpUtil::Unquote(value_begin_, value_end_); | 938 unquoted_value_ = HttpUtil::Unquote(value_begin_, value_end_); |
| 937 } | 939 } |
| 938 } | 940 } |
| 939 | 941 |
| 940 return true; | 942 return true; |
| 941 } | 943 } |
| 942 | 944 |
| 943 } // namespace net | 945 } // namespace net |
| OLD | NEW |