| 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 header parsing were borrowed from Firefox: | 5 // The rules for header parsing were borrowed from Firefox: |
| 6 // http://lxr.mozilla.org/seamonkey/source/netwerk/protocol/http/src/nsHttpRespo
nseHead.cpp | 6 // http://lxr.mozilla.org/seamonkey/source/netwerk/protocol/http/src/nsHttpRespo
nseHead.cpp |
| 7 // The rules for parsing content-types were also borrowed from Firefox: | 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 | 8 // http://lxr.mozilla.org/mozilla/source/netwerk/base/src/nsURLHelper.cpp#834 |
| 9 | 9 |
| 10 #include "net/http/http_response_headers.h" | 10 #include "net/http/http_response_headers.h" |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 91 // this list: | 91 // this list: |
| 92 const char* const kNonUpdatedHeaderPrefixes[] = { | 92 const char* const kNonUpdatedHeaderPrefixes[] = { |
| 93 "content-", | 93 "content-", |
| 94 "x-content-", | 94 "x-content-", |
| 95 "x-webkit-" | 95 "x-webkit-" |
| 96 }; | 96 }; |
| 97 | 97 |
| 98 bool ShouldUpdateHeader(const std::string::const_iterator& name_begin, | 98 bool ShouldUpdateHeader(const std::string::const_iterator& name_begin, |
| 99 const std::string::const_iterator& name_end) { | 99 const std::string::const_iterator& name_end) { |
| 100 for (size_t i = 0; i < arraysize(kNonUpdatedHeaders); ++i) { | 100 for (size_t i = 0; i < arraysize(kNonUpdatedHeaders); ++i) { |
| 101 if (LowerCaseEqualsASCII(name_begin, name_end, kNonUpdatedHeaders[i])) | 101 if (base::LowerCaseEqualsASCII(name_begin, name_end, kNonUpdatedHeaders[i])) |
| 102 return false; | 102 return false; |
| 103 } | 103 } |
| 104 for (size_t i = 0; i < arraysize(kNonUpdatedHeaderPrefixes); ++i) { | 104 for (size_t i = 0; i < arraysize(kNonUpdatedHeaderPrefixes); ++i) { |
| 105 if (StartsWithASCII(std::string(name_begin, name_end), | 105 if (base::StartsWithASCII(std::string(name_begin, name_end), |
| 106 kNonUpdatedHeaderPrefixes[i], false)) | 106 kNonUpdatedHeaderPrefixes[i], false)) |
| 107 return false; | 107 return false; |
| 108 } | 108 } |
| 109 return true; | 109 return true; |
| 110 } | 110 } |
| 111 | 111 |
| 112 void CheckDoesNotHaveEmbededNulls(const std::string& str) { | 112 void CheckDoesNotHaveEmbededNulls(const std::string& str) { |
| 113 // Care needs to be taken when adding values to the raw headers string to | 113 // Care needs to be taken when adding values to the raw headers string to |
| 114 // make sure it does not contain embeded NULLs. Any embeded '\0' may be | 114 // make sure it does not contain embeded NULLs. Any embeded '\0' may be |
| 115 // understood as line terminators and change how header lines get tokenized. | 115 // understood as line terminators and change how header lines get tokenized. |
| 116 CHECK(str.find('\0') == std::string::npos); | 116 CHECK(str.find('\0') == std::string::npos); |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 149 // new object from that pickle. | 149 // new object from that pickle. |
| 150 UMA_HISTOGRAM_CUSTOM_ENUMERATION("Net.HttpResponseCode", | 150 UMA_HISTOGRAM_CUSTOM_ENUMERATION("Net.HttpResponseCode", |
| 151 HttpUtil::MapStatusCodeForHistogram( | 151 HttpUtil::MapStatusCodeForHistogram( |
| 152 response_code_), | 152 response_code_), |
| 153 // Note the third argument is only | 153 // Note the third argument is only |
| 154 // evaluated once, see macro | 154 // evaluated once, see macro |
| 155 // definition for details. | 155 // definition for details. |
| 156 HttpUtil::GetStatusCodesForHistogram()); | 156 HttpUtil::GetStatusCodesForHistogram()); |
| 157 } | 157 } |
| 158 | 158 |
| 159 HttpResponseHeaders::HttpResponseHeaders(PickleIterator* iter) | 159 HttpResponseHeaders::HttpResponseHeaders(base::PickleIterator* iter) |
| 160 : response_code_(-1) { | 160 : response_code_(-1) { |
| 161 std::string raw_input; | 161 std::string raw_input; |
| 162 if (iter->ReadString(&raw_input)) | 162 if (iter->ReadString(&raw_input)) |
| 163 Parse(raw_input); | 163 Parse(raw_input); |
| 164 } | 164 } |
| 165 | 165 |
| 166 void HttpResponseHeaders::Persist(Pickle* pickle, PersistOptions options) { | 166 void HttpResponseHeaders::Persist(base::Pickle* pickle, |
| 167 PersistOptions options) { |
| 167 if (options == PERSIST_RAW) { | 168 if (options == PERSIST_RAW) { |
| 168 pickle->WriteString(raw_headers_); | 169 pickle->WriteString(raw_headers_); |
| 169 return; // Done. | 170 return; // Done. |
| 170 } | 171 } |
| 171 | 172 |
| 172 HeaderSet filter_headers; | 173 HeaderSet filter_headers; |
| 173 | 174 |
| 174 // Construct set of headers to filter out based on options. | 175 // Construct set of headers to filter out based on options. |
| 175 if ((options & PERSIST_SANS_NON_CACHEABLE) == PERSIST_SANS_NON_CACHEABLE) | 176 if ((options & PERSIST_SANS_NON_CACHEABLE) == PERSIST_SANS_NON_CACHEABLE) |
| 176 AddNonCacheableHeaders(&filter_headers); | 177 AddNonCacheableHeaders(&filter_headers); |
| (...skipping 419 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 596 | 597 |
| 597 bool HttpResponseHeaders::HasHeaderValue(const base::StringPiece& name, | 598 bool HttpResponseHeaders::HasHeaderValue(const base::StringPiece& name, |
| 598 const base::StringPiece& value) const { | 599 const base::StringPiece& value) const { |
| 599 // The value has to be an exact match. This is important since | 600 // The value has to be an exact match. This is important since |
| 600 // 'cache-control: no-cache' != 'cache-control: no-cache="foo"' | 601 // 'cache-control: no-cache' != 'cache-control: no-cache="foo"' |
| 601 void* iter = NULL; | 602 void* iter = NULL; |
| 602 std::string temp; | 603 std::string temp; |
| 603 while (EnumerateHeader(&iter, name, &temp)) { | 604 while (EnumerateHeader(&iter, name, &temp)) { |
| 604 if (value.size() == temp.size() && | 605 if (value.size() == temp.size() && |
| 605 std::equal(temp.begin(), temp.end(), value.begin(), | 606 std::equal(temp.begin(), temp.end(), value.begin(), |
| 606 base::CaseInsensitiveCompare<char>())) | 607 base::CaseInsensitiveCompareASCII<char>())) |
| 607 return true; | 608 return true; |
| 608 } | 609 } |
| 609 return false; | 610 return false; |
| 610 } | 611 } |
| 611 | 612 |
| 612 bool HttpResponseHeaders::HasHeader(const base::StringPiece& name) const { | 613 bool HttpResponseHeaders::HasHeader(const base::StringPiece& name) const { |
| 613 return FindHeader(0, name) != std::string::npos; | 614 return FindHeader(0, name) != std::string::npos; |
| 614 } | 615 } |
| 615 | 616 |
| 616 HttpResponseHeaders::HttpResponseHeaders() : response_code_(-1) { | 617 HttpResponseHeaders::HttpResponseHeaders() : response_code_(-1) { |
| 617 } | 618 } |
| 618 | 619 |
| 619 HttpResponseHeaders::~HttpResponseHeaders() { | 620 HttpResponseHeaders::~HttpResponseHeaders() { |
| 620 } | 621 } |
| 621 | 622 |
| 622 // Note: this implementation implicitly assumes that line_end points at a valid | 623 // Note: this implementation implicitly assumes that line_end points at a valid |
| 623 // sentinel character (such as '\0'). | 624 // sentinel character (such as '\0'). |
| 624 // static | 625 // static |
| 625 HttpVersion HttpResponseHeaders::ParseVersion( | 626 HttpVersion HttpResponseHeaders::ParseVersion( |
| 626 std::string::const_iterator line_begin, | 627 std::string::const_iterator line_begin, |
| 627 std::string::const_iterator line_end) { | 628 std::string::const_iterator line_end) { |
| 628 std::string::const_iterator p = line_begin; | 629 std::string::const_iterator p = line_begin; |
| 629 | 630 |
| 630 // RFC2616 sec 3.1: HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT | 631 // RFC2616 sec 3.1: HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT |
| 631 // TODO: (1*DIGIT apparently means one or more digits, but we only handle 1). | 632 // TODO: (1*DIGIT apparently means one or more digits, but we only handle 1). |
| 632 // TODO: handle leading zeros, which is allowed by the rfc1616 sec 3.1. | 633 // TODO: handle leading zeros, which is allowed by the rfc1616 sec 3.1. |
| 633 | 634 |
| 634 if ((line_end - p < 4) || !LowerCaseEqualsASCII(p, p + 4, "http")) { | 635 if ((line_end - p < 4) || !base::LowerCaseEqualsASCII(p, p + 4, "http")) { |
| 635 DVLOG(1) << "missing status line"; | 636 DVLOG(1) << "missing status line"; |
| 636 return HttpVersion(); | 637 return HttpVersion(); |
| 637 } | 638 } |
| 638 | 639 |
| 639 p += 4; | 640 p += 4; |
| 640 | 641 |
| 641 if (p >= line_end || *p != '/') { | 642 if (p >= line_end || *p != '/') { |
| 642 DVLOG(1) << "missing version"; | 643 DVLOG(1) << "missing version"; |
| 643 return HttpVersion(); | 644 return HttpVersion(); |
| 644 } | 645 } |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 738 | 739 |
| 739 size_t HttpResponseHeaders::FindHeader(size_t from, | 740 size_t HttpResponseHeaders::FindHeader(size_t from, |
| 740 const base::StringPiece& search) const { | 741 const base::StringPiece& search) const { |
| 741 for (size_t i = from; i < parsed_.size(); ++i) { | 742 for (size_t i = from; i < parsed_.size(); ++i) { |
| 742 if (parsed_[i].is_continuation()) | 743 if (parsed_[i].is_continuation()) |
| 743 continue; | 744 continue; |
| 744 const std::string::const_iterator& name_begin = parsed_[i].name_begin; | 745 const std::string::const_iterator& name_begin = parsed_[i].name_begin; |
| 745 const std::string::const_iterator& name_end = parsed_[i].name_end; | 746 const std::string::const_iterator& name_end = parsed_[i].name_end; |
| 746 if (static_cast<size_t>(name_end - name_begin) == search.size() && | 747 if (static_cast<size_t>(name_end - name_begin) == search.size() && |
| 747 std::equal(name_begin, name_end, search.begin(), | 748 std::equal(name_begin, name_end, search.begin(), |
| 748 base::CaseInsensitiveCompare<char>())) | 749 base::CaseInsensitiveCompareASCII<char>())) |
| 749 return i; | 750 return i; |
| 750 } | 751 } |
| 751 | 752 |
| 752 return std::string::npos; | 753 return std::string::npos; |
| 753 } | 754 } |
| 754 | 755 |
| 755 bool HttpResponseHeaders::GetCacheControlDirective(const StringPiece& directive, | 756 bool HttpResponseHeaders::GetCacheControlDirective(const StringPiece& directive, |
| 756 TimeDelta* result) const { | 757 TimeDelta* result) const { |
| 757 StringPiece name("cache-control"); | 758 StringPiece name("cache-control"); |
| 758 std::string value; | 759 std::string value; |
| 759 | 760 |
| 760 size_t directive_size = directive.size(); | 761 size_t directive_size = directive.size(); |
| 761 | 762 |
| 762 void* iter = NULL; | 763 void* iter = NULL; |
| 763 while (EnumerateHeader(&iter, name, &value)) { | 764 while (EnumerateHeader(&iter, name, &value)) { |
| 764 if (value.size() > directive_size + 1 && | 765 if (value.size() > directive_size + 1 && |
| 765 LowerCaseEqualsASCII(value.begin(), | 766 base::LowerCaseEqualsASCII( |
| 766 value.begin() + directive_size, | 767 value.begin(), value.begin() + directive_size, directive.begin()) && |
| 767 directive.begin()) && | |
| 768 value[directive_size] == '=') { | 768 value[directive_size] == '=') { |
| 769 int64 seconds; | 769 int64 seconds; |
| 770 base::StringToInt64( | 770 base::StringToInt64( |
| 771 StringPiece(value.begin() + directive_size + 1, value.end()), | 771 StringPiece(value.begin() + directive_size + 1, value.end()), |
| 772 &seconds); | 772 &seconds); |
| 773 *result = TimeDelta::FromSeconds(seconds); | 773 *result = TimeDelta::FromSeconds(seconds); |
| 774 return true; | 774 return true; |
| 775 } | 775 } |
| 776 } | 776 } |
| 777 | 777 |
| (...skipping 446 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1224 {"close", false}}; | 1224 {"close", false}}; |
| 1225 | 1225 |
| 1226 if (http_version_ < HttpVersion(1, 0)) | 1226 if (http_version_ < HttpVersion(1, 0)) |
| 1227 return false; | 1227 return false; |
| 1228 | 1228 |
| 1229 for (const char* header : kConnectionHeaders) { | 1229 for (const char* header : kConnectionHeaders) { |
| 1230 void* iterator = nullptr; | 1230 void* iterator = nullptr; |
| 1231 std::string token; | 1231 std::string token; |
| 1232 while (EnumerateHeader(&iterator, header, &token)) { | 1232 while (EnumerateHeader(&iterator, header, &token)) { |
| 1233 for (const KeepAliveToken& keep_alive_token : kKeepAliveTokens) { | 1233 for (const KeepAliveToken& keep_alive_token : kKeepAliveTokens) { |
| 1234 if (LowerCaseEqualsASCII(token, keep_alive_token.token)) | 1234 if (base::LowerCaseEqualsASCII(token, keep_alive_token.token)) |
| 1235 return keep_alive_token.keep_alive; | 1235 return keep_alive_token.keep_alive; |
| 1236 } | 1236 } |
| 1237 } | 1237 } |
| 1238 } | 1238 } |
| 1239 return http_version_ != HttpVersion(1, 0); | 1239 return http_version_ != HttpVersion(1, 0); |
| 1240 } | 1240 } |
| 1241 | 1241 |
| 1242 bool HttpResponseHeaders::HasStrongValidators() const { | 1242 bool HttpResponseHeaders::HasStrongValidators() const { |
| 1243 std::string etag_header; | 1243 std::string etag_header; |
| 1244 EnumerateHeader(NULL, "etag", &etag_header); | 1244 EnumerateHeader(NULL, "etag", &etag_header); |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1301 size_t space_position = content_range_spec.find(' '); | 1301 size_t space_position = content_range_spec.find(' '); |
| 1302 if (space_position == std::string::npos) | 1302 if (space_position == std::string::npos) |
| 1303 return false; | 1303 return false; |
| 1304 | 1304 |
| 1305 // Invalid header if it doesn't contain "bytes-unit". | 1305 // Invalid header if it doesn't contain "bytes-unit". |
| 1306 std::string::const_iterator content_range_spec_begin = | 1306 std::string::const_iterator content_range_spec_begin = |
| 1307 content_range_spec.begin(); | 1307 content_range_spec.begin(); |
| 1308 std::string::const_iterator content_range_spec_end = | 1308 std::string::const_iterator content_range_spec_end = |
| 1309 content_range_spec.begin() + space_position; | 1309 content_range_spec.begin() + space_position; |
| 1310 HttpUtil::TrimLWS(&content_range_spec_begin, &content_range_spec_end); | 1310 HttpUtil::TrimLWS(&content_range_spec_begin, &content_range_spec_end); |
| 1311 if (!LowerCaseEqualsASCII(content_range_spec_begin, | 1311 if (!base::LowerCaseEqualsASCII(content_range_spec_begin, |
| 1312 content_range_spec_end, | 1312 content_range_spec_end, "bytes")) { |
| 1313 "bytes")) { | |
| 1314 return false; | 1313 return false; |
| 1315 } | 1314 } |
| 1316 | 1315 |
| 1317 size_t slash_position = content_range_spec.find('/', space_position + 1); | 1316 size_t slash_position = content_range_spec.find('/', space_position + 1); |
| 1318 if (slash_position == std::string::npos) | 1317 if (slash_position == std::string::npos) |
| 1319 return false; | 1318 return false; |
| 1320 | 1319 |
| 1321 // Obtain the part behind the space and before slash. | 1320 // Obtain the part behind the space and before slash. |
| 1322 std::string::const_iterator byte_range_resp_spec_begin = | 1321 std::string::const_iterator byte_range_resp_spec_begin = |
| 1323 content_range_spec.begin() + space_position + 1; | 1322 content_range_spec.begin() + space_position + 1; |
| 1324 std::string::const_iterator byte_range_resp_spec_end = | 1323 std::string::const_iterator byte_range_resp_spec_end = |
| 1325 content_range_spec.begin() + slash_position; | 1324 content_range_spec.begin() + slash_position; |
| 1326 HttpUtil::TrimLWS(&byte_range_resp_spec_begin, &byte_range_resp_spec_end); | 1325 HttpUtil::TrimLWS(&byte_range_resp_spec_begin, &byte_range_resp_spec_end); |
| 1327 | 1326 |
| 1328 // Parse the byte-range-resp-spec part. | 1327 // Parse the byte-range-resp-spec part. |
| 1329 std::string byte_range_resp_spec(byte_range_resp_spec_begin, | 1328 std::string byte_range_resp_spec(byte_range_resp_spec_begin, |
| 1330 byte_range_resp_spec_end); | 1329 byte_range_resp_spec_end); |
| 1331 // If byte-range-resp-spec != "*". | 1330 // If byte-range-resp-spec != "*". |
| 1332 if (!LowerCaseEqualsASCII(byte_range_resp_spec, "*")) { | 1331 if (!base::LowerCaseEqualsASCII(byte_range_resp_spec, "*")) { |
| 1333 size_t minus_position = byte_range_resp_spec.find('-'); | 1332 size_t minus_position = byte_range_resp_spec.find('-'); |
| 1334 if (minus_position != std::string::npos) { | 1333 if (minus_position != std::string::npos) { |
| 1335 // Obtain first-byte-pos. | 1334 // Obtain first-byte-pos. |
| 1336 std::string::const_iterator first_byte_pos_begin = | 1335 std::string::const_iterator first_byte_pos_begin = |
| 1337 byte_range_resp_spec.begin(); | 1336 byte_range_resp_spec.begin(); |
| 1338 std::string::const_iterator first_byte_pos_end = | 1337 std::string::const_iterator first_byte_pos_end = |
| 1339 byte_range_resp_spec.begin() + minus_position; | 1338 byte_range_resp_spec.begin() + minus_position; |
| 1340 HttpUtil::TrimLWS(&first_byte_pos_begin, &first_byte_pos_end); | 1339 HttpUtil::TrimLWS(&first_byte_pos_begin, &first_byte_pos_end); |
| 1341 | 1340 |
| 1342 bool ok = base::StringToInt64(StringPiece(first_byte_pos_begin, | 1341 bool ok = base::StringToInt64(StringPiece(first_byte_pos_begin, |
| (...skipping 23 matching lines...) Expand all Loading... |
| 1366 } | 1365 } |
| 1367 | 1366 |
| 1368 // Parse the instance-length part. | 1367 // Parse the instance-length part. |
| 1369 // If instance-length == "*". | 1368 // If instance-length == "*". |
| 1370 std::string::const_iterator instance_length_begin = | 1369 std::string::const_iterator instance_length_begin = |
| 1371 content_range_spec.begin() + slash_position + 1; | 1370 content_range_spec.begin() + slash_position + 1; |
| 1372 std::string::const_iterator instance_length_end = | 1371 std::string::const_iterator instance_length_end = |
| 1373 content_range_spec.end(); | 1372 content_range_spec.end(); |
| 1374 HttpUtil::TrimLWS(&instance_length_begin, &instance_length_end); | 1373 HttpUtil::TrimLWS(&instance_length_begin, &instance_length_end); |
| 1375 | 1374 |
| 1376 if (LowerCaseEqualsASCII(instance_length_begin, instance_length_end, "*")) { | 1375 if (base::LowerCaseEqualsASCII(instance_length_begin, instance_length_end, |
| 1376 "*")) { |
| 1377 return false; | 1377 return false; |
| 1378 } else if (!base::StringToInt64(StringPiece(instance_length_begin, | 1378 } else if (!base::StringToInt64(StringPiece(instance_length_begin, |
| 1379 instance_length_end), | 1379 instance_length_end), |
| 1380 instance_length)) { | 1380 instance_length)) { |
| 1381 *instance_length = -1; | 1381 *instance_length = -1; |
| 1382 return false; | 1382 return false; |
| 1383 } | 1383 } |
| 1384 | 1384 |
| 1385 // We have all the values; let's verify that they make sense for a 206 | 1385 // We have all the values; let's verify that they make sense for a 206 |
| 1386 // response. | 1386 // response. |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1444 return true; | 1444 return true; |
| 1445 } | 1445 } |
| 1446 | 1446 |
| 1447 bool HttpResponseHeaders::IsChunkEncoded() const { | 1447 bool HttpResponseHeaders::IsChunkEncoded() const { |
| 1448 // Ignore spurious chunked responses from HTTP/1.0 servers and proxies. | 1448 // Ignore spurious chunked responses from HTTP/1.0 servers and proxies. |
| 1449 return GetHttpVersion() >= HttpVersion(1, 1) && | 1449 return GetHttpVersion() >= HttpVersion(1, 1) && |
| 1450 HasHeaderValue("Transfer-Encoding", "chunked"); | 1450 HasHeaderValue("Transfer-Encoding", "chunked"); |
| 1451 } | 1451 } |
| 1452 | 1452 |
| 1453 } // namespace net | 1453 } // namespace net |
| OLD | NEW |