Chromium Code Reviews| 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 #include "net/http/http_network_transaction.h" | 5 #include "net/http/http_network_transaction.h" |
| 6 | 6 |
| 7 #include <memory> | 7 #include <memory> |
| 8 #include <set> | 8 #include <set> |
| 9 #include <utility> | 9 #include <utility> |
| 10 #include <vector> | 10 #include <vector> |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 74 std::unique_ptr<base::Value> NetLogSSLCipherFallbackCallback( | 74 std::unique_ptr<base::Value> NetLogSSLCipherFallbackCallback( |
| 75 const GURL* url, | 75 const GURL* url, |
| 76 int net_error, | 76 int net_error, |
| 77 NetLogCaptureMode /* capture_mode */) { | 77 NetLogCaptureMode /* capture_mode */) { |
| 78 std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); | 78 std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); |
| 79 dict->SetString("host_and_port", GetHostAndPort(*url)); | 79 dict->SetString("host_and_port", GetHostAndPort(*url)); |
| 80 dict->SetInteger("net_error", net_error); | 80 dict->SetInteger("net_error", net_error); |
| 81 return std::move(dict); | 81 return std::move(dict); |
| 82 } | 82 } |
| 83 | 83 |
| 84 enum QualityParsingState { | |
| 85 BEFORE_QUALIFIER, // waiting for ";" | |
| 86 QUALIFIER, // waiting for "q" | |
| 87 EQUALS, // waiting for "=" | |
| 88 ZERO_OR_ONE, // waiting for "0" or "1" | |
| 89 PERIOD, // waiting for "." | |
| 90 DECIMALS, | |
| 91 DONE | |
| 92 }; | |
| 93 | |
| 94 // Tests is specified encoding is allowed by the given "Accept-Encoding" header. | |
| 95 // It is expected that header value is somewhat well-formatted (according to the | |
| 96 // RFC2616), and no "content-coding" is a prefix of another "content-coding". | |
| 97 bool IsAcceptableEncoding(const std::string& accept_encoding, | |
|
Randy Smith (Not in Mondays)
2017/03/15 16:35:31
Confirming: This is here because there's no existi
eustas
2017/03/16 12:08:16
Yup, hasn't found parsers for "Accept-xxx" header.
Randy Smith (Not in Mondays)
2017/03/16 17:50:44
Stick in a TODO to that effect? (To move it somew
eustas
2017/03/20 13:05:18
Reworked and moved to HttpUtil
| |
| 98 const std::string& encoding) { | |
| 99 size_t cursor = accept_encoding.find(encoding); | |
| 100 char c; | |
| 101 | |
| 102 // Encoding is not mentioned. | |
| 103 if (cursor == std::string::npos) | |
| 104 return false; | |
| 105 | |
| 106 // Check that match position is not in the middle of another entry. | |
| 107 if (cursor != 0) { | |
| 108 c = accept_encoding[cursor - 1]; | |
| 109 if ((c != ' ') && (c != ',')) | |
| 110 return false; | |
| 111 } | |
| 112 cursor += encoding.size(); | |
| 113 | |
| 114 QualityParsingState state = BEFORE_QUALIFIER; | |
| 115 int ones = 0; | |
| 116 int sum = 0; | |
| 117 int digits = 0; | |
| 118 | |
| 119 for (; cursor < accept_encoding.size(); ++cursor) { | |
| 120 c = accept_encoding[cursor]; | |
| 121 switch (state) { | |
| 122 case BEFORE_QUALIFIER: | |
| 123 if (c == ' ') | |
| 124 break; | |
| 125 if (c == ',') | |
| 126 return true; | |
| 127 if (c == ';') { | |
| 128 state = QUALIFIER; | |
| 129 break; | |
| 130 } | |
| 131 return false; | |
| 132 | |
| 133 case QUALIFIER: | |
| 134 if (c == ' ') | |
| 135 break; | |
| 136 if (c == 'q' || c == 'Q') { | |
| 137 state = EQUALS; | |
| 138 break; | |
| 139 } | |
| 140 return false; | |
| 141 | |
| 142 case EQUALS: | |
| 143 if (c == '=') { | |
| 144 state = ZERO_OR_ONE; | |
| 145 break; | |
| 146 } | |
| 147 return false; | |
| 148 | |
| 149 case ZERO_OR_ONE: | |
| 150 if (c == '0' || c == '1') { | |
| 151 ones = c - '0'; | |
| 152 state = PERIOD; | |
| 153 break; | |
| 154 } | |
| 155 return false; | |
| 156 | |
| 157 case PERIOD: | |
| 158 if (c == '.') { | |
| 159 state = DECIMALS; | |
| 160 break; | |
| 161 } | |
| 162 return false; | |
| 163 | |
| 164 case DECIMALS: | |
| 165 if (c == ',' || c == ' ') { | |
| 166 state = DONE; | |
| 167 break; | |
| 168 } | |
| 169 if (c >= '0' && c <= '9') { | |
| 170 sum += c - '0'; | |
| 171 digits++; | |
| 172 break; | |
| 173 } | |
| 174 return false; | |
| 175 | |
| 176 case DONE: | |
| 177 // assert(0) | |
| 178 break; | |
| 179 } | |
| 180 if (state == DONE) | |
| 181 break; | |
| 182 } | |
| 183 | |
| 184 // Before "q". | |
| 185 if (state == BEFORE_QUALIFIER) | |
| 186 return true; | |
| 187 | |
| 188 // Before value. | |
| 189 if (state == QUALIFIER || state == EQUALS || state == ZERO_OR_ONE) | |
| 190 return false; | |
| 191 | |
| 192 // assert(state == PERIOD || state == DECIMALS || state == DONE | |
| 193 // Some value is read. | |
| 194 | |
| 195 if (digits > 3) | |
| 196 return false; | |
| 197 | |
| 198 if (ones == 0) | |
| 199 return (sum != 0); | |
| 200 | |
| 201 // Reject values like "1.x", where x != 0 | |
| 202 return (sum == 0); | |
| 203 } | |
| 204 | |
| 84 } // namespace | 205 } // namespace |
| 85 | 206 |
| 86 //----------------------------------------------------------------------------- | 207 //----------------------------------------------------------------------------- |
| 87 | 208 |
| 88 HttpNetworkTransaction::HttpNetworkTransaction(RequestPriority priority, | 209 HttpNetworkTransaction::HttpNetworkTransaction(RequestPriority priority, |
| 89 HttpNetworkSession* session) | 210 HttpNetworkSession* session) |
| 90 : pending_auth_target_(HttpAuth::AUTH_NONE), | 211 : pending_auth_target_(HttpAuth::AUTH_NONE), |
| 91 io_callback_(base::Bind(&HttpNetworkTransaction::OnIOComplete, | 212 io_callback_(base::Bind(&HttpNetworkTransaction::OnIOComplete, |
| 92 base::Unretained(this))), | 213 base::Unretained(this))), |
| 93 session_(session), | 214 session_(session), |
| (...skipping 444 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 538 const HttpResponseInfo& proxy_response, | 659 const HttpResponseInfo& proxy_response, |
| 539 const SSLConfig& used_ssl_config, | 660 const SSLConfig& used_ssl_config, |
| 540 const ProxyInfo& used_proxy_info, | 661 const ProxyInfo& used_proxy_info, |
| 541 HttpAuthController* auth_controller) { | 662 HttpAuthController* auth_controller) { |
| 542 DCHECK(stream_request_.get()); | 663 DCHECK(stream_request_.get()); |
| 543 DCHECK_EQ(STATE_CREATE_STREAM_COMPLETE, next_state_); | 664 DCHECK_EQ(STATE_CREATE_STREAM_COMPLETE, next_state_); |
| 544 | 665 |
| 545 establishing_tunnel_ = true; | 666 establishing_tunnel_ = true; |
| 546 response_.headers = proxy_response.headers; | 667 response_.headers = proxy_response.headers; |
| 547 response_.auth_challenge = proxy_response.auth_challenge; | 668 response_.auth_challenge = proxy_response.auth_challenge; |
| 669 // TODO(eustas): Should content encoding be checked here? | |
|
Randy Smith (Not in Mondays)
2017/03/15 16:35:32
Hmmm. I'm inclined to think yes. I'll ask Matt f
eustas
2017/03/16 12:08:16
Added similar check here.
| |
| 548 headers_valid_ = true; | 670 headers_valid_ = true; |
| 549 server_ssl_config_ = used_ssl_config; | 671 server_ssl_config_ = used_ssl_config; |
| 550 proxy_info_ = used_proxy_info; | 672 proxy_info_ = used_proxy_info; |
| 551 | 673 |
| 552 auth_controllers_[HttpAuth::AUTH_PROXY] = auth_controller; | 674 auth_controllers_[HttpAuth::AUTH_PROXY] = auth_controller; |
| 553 pending_auth_target_ = HttpAuth::AUTH_PROXY; | 675 pending_auth_target_ = HttpAuth::AUTH_PROXY; |
| 554 | 676 |
| 555 DoCallback(OK); | 677 DoCallback(OK); |
| 556 } | 678 } |
| 557 | 679 |
| 558 void HttpNetworkTransaction::OnNeedsClientAuth( | 680 void HttpNetworkTransaction::OnNeedsClientAuth( |
| 559 const SSLConfig& used_ssl_config, | 681 const SSLConfig& used_ssl_config, |
| 560 SSLCertRequestInfo* cert_info) { | 682 SSLCertRequestInfo* cert_info) { |
| 561 DCHECK_EQ(STATE_CREATE_STREAM_COMPLETE, next_state_); | 683 DCHECK_EQ(STATE_CREATE_STREAM_COMPLETE, next_state_); |
| 562 | 684 |
| 563 server_ssl_config_ = used_ssl_config; | 685 server_ssl_config_ = used_ssl_config; |
| 564 response_.cert_request_info = cert_info; | 686 response_.cert_request_info = cert_info; |
| 565 OnIOComplete(ERR_SSL_CLIENT_AUTH_CERT_NEEDED); | 687 OnIOComplete(ERR_SSL_CLIENT_AUTH_CERT_NEEDED); |
| 566 } | 688 } |
| 567 | 689 |
| 568 void HttpNetworkTransaction::OnHttpsProxyTunnelResponse( | 690 void HttpNetworkTransaction::OnHttpsProxyTunnelResponse( |
| 569 const HttpResponseInfo& response_info, | 691 const HttpResponseInfo& response_info, |
| 570 const SSLConfig& used_ssl_config, | 692 const SSLConfig& used_ssl_config, |
| 571 const ProxyInfo& used_proxy_info, | 693 const ProxyInfo& used_proxy_info, |
| 572 HttpStream* stream) { | 694 HttpStream* stream) { |
| 573 DCHECK_EQ(STATE_CREATE_STREAM_COMPLETE, next_state_); | 695 DCHECK_EQ(STATE_CREATE_STREAM_COMPLETE, next_state_); |
| 574 | 696 |
| 575 CopyConnectionAttemptsFromStreamRequest(); | 697 CopyConnectionAttemptsFromStreamRequest(); |
| 576 | 698 |
| 699 // TODO(eustas): Should content encoding be checked here? | |
| 577 headers_valid_ = true; | 700 headers_valid_ = true; |
| 578 response_ = response_info; | 701 response_ = response_info; |
| 579 server_ssl_config_ = used_ssl_config; | 702 server_ssl_config_ = used_ssl_config; |
| 580 proxy_info_ = used_proxy_info; | 703 proxy_info_ = used_proxy_info; |
| 581 if (stream_) { | 704 if (stream_) { |
| 582 total_received_bytes_ += stream_->GetTotalReceivedBytes(); | 705 total_received_bytes_ += stream_->GetTotalReceivedBytes(); |
| 583 total_sent_bytes_ += stream_->GetTotalSentBytes(); | 706 total_sent_bytes_ += stream_->GetTotalSentBytes(); |
| 584 } | 707 } |
| 585 stream_.reset(stream); | 708 stream_.reset(stream); |
| 586 stream_request_.reset(); // we're done with the stream request | 709 stream_request_.reset(); // we're done with the stream request |
| (...skipping 652 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1239 // bizarre for SPDY. Assuming this logic is useful at all. | 1362 // bizarre for SPDY. Assuming this logic is useful at all. |
| 1240 // TODO(davidben): Bubble the error code up so we do not cache? | 1363 // TODO(davidben): Bubble the error code up so we do not cache? |
| 1241 if (result == ERR_CONNECTION_CLOSED && response_.headers.get()) | 1364 if (result == ERR_CONNECTION_CLOSED && response_.headers.get()) |
| 1242 result = OK; | 1365 result = OK; |
| 1243 | 1366 |
| 1244 if (result < 0) | 1367 if (result < 0) |
| 1245 return HandleIOError(result); | 1368 return HandleIOError(result); |
| 1246 | 1369 |
| 1247 DCHECK(response_.headers.get()); | 1370 DCHECK(response_.headers.get()); |
| 1248 | 1371 |
| 1372 if (response_.headers.get() && !ContentEncodingsValid()) { | |
| 1373 // TODO(eustas): report in UMA? | |
|
Randy Smith (Not in Mondays)
2017/03/15 16:35:32
See comment about which error.
eustas
2017/03/16 12:08:16
Done.
| |
| 1374 // TODO(eustas): ignore, based on Finch flag? | |
|
Randy Smith (Not in Mondays)
2017/03/15 16:35:32
I'm inclined to think we can just watch the metric
eustas
2017/03/16 12:08:16
Done.
| |
| 1375 return ERR_INVALID_CONTENT_ENCODING; | |
| 1376 } | |
| 1377 | |
| 1249 // On a 408 response from the server ("Request Timeout") on a stale socket, | 1378 // On a 408 response from the server ("Request Timeout") on a stale socket, |
| 1250 // retry the request. | 1379 // retry the request. |
| 1251 // Headers can be NULL because of http://crbug.com/384554. | 1380 // Headers can be NULL because of http://crbug.com/384554. |
| 1252 if (response_.headers.get() && response_.headers->response_code() == 408 && | 1381 if (response_.headers.get() && response_.headers->response_code() == 408 && |
| 1253 stream_->IsConnectionReused()) { | 1382 stream_->IsConnectionReused()) { |
| 1254 net_log_.AddEventWithNetErrorCode( | 1383 net_log_.AddEventWithNetErrorCode( |
| 1255 NetLogEventType::HTTP_TRANSACTION_RESTART_AFTER_ERROR, | 1384 NetLogEventType::HTTP_TRANSACTION_RESTART_AFTER_ERROR, |
| 1256 response_.headers->response_code()); | 1385 response_.headers->response_code()); |
| 1257 // This will close the socket - it would be weird to try and reuse it, even | 1386 // This will close the socket - it would be weird to try and reuse it, even |
| 1258 // if the server doesn't actually close it. | 1387 // if the server doesn't actually close it. |
| (...skipping 445 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1704 void HttpNetworkTransaction::CopyConnectionAttemptsFromStreamRequest() { | 1833 void HttpNetworkTransaction::CopyConnectionAttemptsFromStreamRequest() { |
| 1705 DCHECK(stream_request_); | 1834 DCHECK(stream_request_); |
| 1706 | 1835 |
| 1707 // Since the transaction can restart with auth credentials, it may create a | 1836 // Since the transaction can restart with auth credentials, it may create a |
| 1708 // stream more than once. Accumulate all of the connection attempts across | 1837 // stream more than once. Accumulate all of the connection attempts across |
| 1709 // those streams by appending them to the vector: | 1838 // those streams by appending them to the vector: |
| 1710 for (const auto& attempt : stream_request_->connection_attempts()) | 1839 for (const auto& attempt : stream_request_->connection_attempts()) |
| 1711 connection_attempts_.push_back(attempt); | 1840 connection_attempts_.push_back(attempt); |
| 1712 } | 1841 } |
| 1713 | 1842 |
| 1843 bool HttpNetworkTransaction::ContentEncodingsValid() const { | |
| 1844 HttpResponseHeaders* headers = GetResponseHeaders(); | |
| 1845 DCHECK(headers); | |
| 1846 | |
| 1847 // It is OK, if both "Accept-Encoding" and "Content-Encoding" headers are | |
| 1848 // missing. It is a widely used setup in unit-tests. | |
|
Randy Smith (Not in Mondays)
2017/03/15 16:35:32
If this is specifically for unit tests, it's not v
eustas
2017/03/16 12:08:16
Pulled GetHeader out of loop.
| |
| 1849 | |
| 1850 bool accept_encoding_acquired = false; | |
| 1851 std::string accept_encoding; | |
| 1852 std::string encoding; | |
| 1853 size_t iter = 0; | |
| 1854 while (headers->EnumerateHeader(&iter, "Content-Encoding", &encoding)) { | |
| 1855 encoding = base::ToLowerASCII(encoding); | |
| 1856 // RFC2616 Section 3.5 recommendation: applications SHOULD consider "x-gzip" | |
| 1857 // and "x-compress" to be equivalent to "gzip" and "compress" respectively. | |
| 1858 if (encoding.compare("x-gzip") == 0) | |
| 1859 encoding = "gzip"; | |
| 1860 if (encoding.compare("x-compress") == 0) | |
| 1861 encoding = "compress"; | |
| 1862 | |
| 1863 // Reject malformed encodings. | |
| 1864 if (encoding.find_first_of("=;, ") != std::string::npos) | |
| 1865 return false; | |
| 1866 | |
| 1867 if (!accept_encoding_acquired) { | |
| 1868 if (!request_headers_.GetHeader(HttpRequestHeaders::kAcceptEncoding, | |
| 1869 &accept_encoding)) { | |
| 1870 // URLRequestHttpJob sets the header if no value is provided. | |
| 1871 // Other clients shoot themselves in a foot, if they don't. | |
| 1872 return false; | |
| 1873 } | |
| 1874 accept_encoding_acquired = true; | |
| 1875 } | |
| 1876 | |
| 1877 if (!IsAcceptableEncoding(accept_encoding, encoding)) | |
| 1878 return false; | |
| 1879 } | |
| 1880 return true; | |
| 1881 } | |
| 1882 | |
| 1714 } // namespace net | 1883 } // namespace net |
| OLD | NEW |