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 14 matching lines...) Expand all Loading... | |
| 25 #include "base/values.h" | 25 #include "base/values.h" |
| 26 #include "build/build_config.h" | 26 #include "build/build_config.h" |
| 27 #include "net/base/auth.h" | 27 #include "net/base/auth.h" |
| 28 #include "net/base/host_port_pair.h" | 28 #include "net/base/host_port_pair.h" |
| 29 #include "net/base/io_buffer.h" | 29 #include "net/base/io_buffer.h" |
| 30 #include "net/base/load_flags.h" | 30 #include "net/base/load_flags.h" |
| 31 #include "net/base/load_timing_info.h" | 31 #include "net/base/load_timing_info.h" |
| 32 #include "net/base/net_errors.h" | 32 #include "net/base/net_errors.h" |
| 33 #include "net/base/upload_data_stream.h" | 33 #include "net/base/upload_data_stream.h" |
| 34 #include "net/base/url_util.h" | 34 #include "net/base/url_util.h" |
| 35 #include "net/filter/source_stream.h" | |
| 35 #include "net/http/http_auth.h" | 36 #include "net/http/http_auth.h" |
| 36 #include "net/http/http_auth_handler.h" | 37 #include "net/http/http_auth_handler.h" |
| 37 #include "net/http/http_auth_handler_factory.h" | 38 #include "net/http/http_auth_handler_factory.h" |
| 38 #include "net/http/http_basic_stream.h" | 39 #include "net/http/http_basic_stream.h" |
| 39 #include "net/http/http_chunked_decoder.h" | 40 #include "net/http/http_chunked_decoder.h" |
| 40 #include "net/http/http_network_session.h" | 41 #include "net/http/http_network_session.h" |
| 41 #include "net/http/http_proxy_client_socket.h" | 42 #include "net/http/http_proxy_client_socket.h" |
| 42 #include "net/http/http_proxy_client_socket_pool.h" | 43 #include "net/http/http_proxy_client_socket_pool.h" |
| 43 #include "net/http/http_request_headers.h" | 44 #include "net/http/http_request_headers.h" |
| 44 #include "net/http/http_request_info.h" | 45 #include "net/http/http_request_info.h" |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 74 std::unique_ptr<base::Value> NetLogSSLCipherFallbackCallback( | 75 std::unique_ptr<base::Value> NetLogSSLCipherFallbackCallback( |
| 75 const GURL* url, | 76 const GURL* url, |
| 76 int net_error, | 77 int net_error, |
| 77 NetLogCaptureMode /* capture_mode */) { | 78 NetLogCaptureMode /* capture_mode */) { |
| 78 std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); | 79 std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); |
| 79 dict->SetString("host_and_port", GetHostAndPort(*url)); | 80 dict->SetString("host_and_port", GetHostAndPort(*url)); |
| 80 dict->SetInteger("net_error", net_error); | 81 dict->SetInteger("net_error", net_error); |
| 81 return std::move(dict); | 82 return std::move(dict); |
| 82 } | 83 } |
| 83 | 84 |
| 85 enum QualityParsingState { | |
| 86 BEFORE_QUALIFIER, // waiting for ";" | |
| 87 QUALIFIER, // waiting for "q" | |
| 88 EQUALS, // waiting for "=" | |
| 89 ZERO_OR_ONE, // waiting for "0" or "1" | |
| 90 PERIOD, // waiting for "." | |
| 91 DECIMALS, | |
| 92 DONE | |
| 93 }; | |
| 94 | |
| 95 // Tests is specified encoding is allowed by the given "Accept-Encoding" header. | |
| 96 // It is expected that header value is somewhat well-formatted (according to the | |
| 97 // RFC2616), and no "content-coding" is a prefix of another "content-coding". | |
| 98 bool IsAcceptableEncoding(const std::string& accept_encoding, | |
| 99 const std::string& encoding) { | |
| 100 size_t cursor = accept_encoding.find(encoding); | |
| 101 char c; | |
| 102 | |
| 103 // Encoding is not mentioned. | |
| 104 if (cursor == std::string::npos) | |
| 105 return false; | |
|
Randy Smith (Not in Mondays)
2017/03/16 17:50:44
Put a note in here about explicitly not worrying a
eustas
2017/03/20 13:05:18
This code is gone.
| |
| 106 | |
| 107 // Check that match position is not in the middle of another entry. | |
| 108 if (cursor != 0) { | |
| 109 c = accept_encoding[cursor - 1]; | |
| 110 if ((c != ' ') && (c != ',')) | |
|
Randy Smith (Not in Mondays)
2017/03/16 17:50:44
Do we need to check other whitespace? (Tab specif
eustas
2017/03/20 13:05:18
In new code whitespace is trimmed around all token
| |
| 111 return false; | |
| 112 } | |
| 113 cursor += encoding.size(); | |
| 114 | |
| 115 QualityParsingState state = BEFORE_QUALIFIER; | |
| 116 int ones = 0; | |
| 117 int sum = 0; | |
| 118 int digits = 0; | |
| 119 | |
| 120 for (; cursor < accept_encoding.size(); ++cursor) { | |
| 121 c = accept_encoding[cursor]; | |
| 122 switch (state) { | |
| 123 case BEFORE_QUALIFIER: | |
| 124 if (c == ' ') | |
| 125 break; | |
| 126 if (c == ',') | |
| 127 return true; | |
| 128 if (c == ';') { | |
| 129 state = QUALIFIER; | |
| 130 break; | |
| 131 } | |
| 132 return false; | |
| 133 | |
| 134 case QUALIFIER: | |
| 135 if (c == ' ') | |
| 136 break; | |
| 137 if (c == 'q' || c == 'Q') { | |
| 138 state = EQUALS; | |
| 139 break; | |
| 140 } | |
| 141 return false; | |
| 142 | |
| 143 case EQUALS: | |
| 144 if (c == '=') { | |
| 145 state = ZERO_OR_ONE; | |
| 146 break; | |
| 147 } | |
| 148 return false; | |
| 149 | |
| 150 case ZERO_OR_ONE: | |
| 151 if (c == '0' || c == '1') { | |
| 152 ones = c - '0'; | |
| 153 state = PERIOD; | |
| 154 break; | |
| 155 } | |
| 156 return false; | |
| 157 | |
| 158 case PERIOD: | |
| 159 if (c == '.') { | |
| 160 state = DECIMALS; | |
| 161 break; | |
| 162 } | |
| 163 return false; | |
|
Randy Smith (Not in Mondays)
2017/03/16 17:50:44
Is the thought that q=1 will never happen? I'd so
eustas
2017/03/20 13:05:18
Ooops. Fixed it,... and then rewritten it to a mor
| |
| 164 | |
| 165 case DECIMALS: | |
| 166 if (c == ',' || c == ' ') { | |
| 167 state = DONE; | |
| 168 break; | |
| 169 } | |
| 170 if (c >= '0' && c <= '9') { | |
| 171 sum += c - '0'; | |
| 172 digits++; | |
| 173 break; | |
| 174 } | |
| 175 return false; | |
| 176 | |
| 177 case DONE: | |
| 178 // assert(0) | |
| 179 break; | |
| 180 } | |
| 181 if (state == DONE) | |
| 182 break; | |
| 183 } | |
| 184 | |
| 185 // Before "q". | |
| 186 if (state == BEFORE_QUALIFIER) | |
| 187 return true; | |
| 188 | |
| 189 // Before value. | |
| 190 if (state == QUALIFIER || state == EQUALS || state == ZERO_OR_ONE) | |
| 191 return false; | |
| 192 | |
| 193 // assert(state == PERIOD || state == DECIMALS || state == DONE | |
|
Randy Smith (Not in Mondays)
2017/03/16 17:50:44
Why not make this a DCHECK and remove the comments
eustas
2017/03/20 13:05:18
Acknowledged.
| |
| 194 // Some value is read. | |
| 195 | |
| 196 if (digits > 3) | |
| 197 return false; | |
| 198 | |
| 199 if (ones == 0) | |
| 200 return (sum != 0); | |
| 201 | |
| 202 // Reject values like "1.x", where x != 0 | |
| 203 return (sum == 0); | |
| 204 } | |
| 205 | |
| 84 } // namespace | 206 } // namespace |
| 85 | 207 |
| 86 //----------------------------------------------------------------------------- | 208 //----------------------------------------------------------------------------- |
| 87 | 209 |
| 88 HttpNetworkTransaction::HttpNetworkTransaction(RequestPriority priority, | 210 HttpNetworkTransaction::HttpNetworkTransaction(RequestPriority priority, |
| 89 HttpNetworkSession* session) | 211 HttpNetworkSession* session) |
| 90 : pending_auth_target_(HttpAuth::AUTH_NONE), | 212 : pending_auth_target_(HttpAuth::AUTH_NONE), |
| 91 io_callback_(base::Bind(&HttpNetworkTransaction::OnIOComplete, | 213 io_callback_(base::Bind(&HttpNetworkTransaction::OnIOComplete, |
| 92 base::Unretained(this))), | 214 base::Unretained(this))), |
| 93 session_(session), | 215 session_(session), |
| (...skipping 444 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 538 const HttpResponseInfo& proxy_response, | 660 const HttpResponseInfo& proxy_response, |
| 539 const SSLConfig& used_ssl_config, | 661 const SSLConfig& used_ssl_config, |
| 540 const ProxyInfo& used_proxy_info, | 662 const ProxyInfo& used_proxy_info, |
| 541 HttpAuthController* auth_controller) { | 663 HttpAuthController* auth_controller) { |
| 542 DCHECK(stream_request_.get()); | 664 DCHECK(stream_request_.get()); |
| 543 DCHECK_EQ(STATE_CREATE_STREAM_COMPLETE, next_state_); | 665 DCHECK_EQ(STATE_CREATE_STREAM_COMPLETE, next_state_); |
| 544 | 666 |
| 545 establishing_tunnel_ = true; | 667 establishing_tunnel_ = true; |
| 546 response_.headers = proxy_response.headers; | 668 response_.headers = proxy_response.headers; |
| 547 response_.auth_challenge = proxy_response.auth_challenge; | 669 response_.auth_challenge = proxy_response.auth_challenge; |
| 670 | |
| 671 if (response_.headers.get() && !ContentEncodingsValid()) { | |
| 672 UMA_HISTOGRAM_ENUMERATION("Net.ContentDecodingFailed2.FilterType", | |
| 673 SourceStream::TYPE_UNKNOWN, | |
| 674 SourceStream::TYPE_MAX); | |
| 675 return ERR_CONTENT_DECODING_FAILED; | |
| 676 } | |
| 677 | |
| 548 headers_valid_ = true; | 678 headers_valid_ = true; |
| 549 server_ssl_config_ = used_ssl_config; | 679 server_ssl_config_ = used_ssl_config; |
| 550 proxy_info_ = used_proxy_info; | 680 proxy_info_ = used_proxy_info; |
| 551 | 681 |
| 552 auth_controllers_[HttpAuth::AUTH_PROXY] = auth_controller; | 682 auth_controllers_[HttpAuth::AUTH_PROXY] = auth_controller; |
| 553 pending_auth_target_ = HttpAuth::AUTH_PROXY; | 683 pending_auth_target_ = HttpAuth::AUTH_PROXY; |
| 554 | 684 |
| 555 DoCallback(OK); | 685 DoCallback(OK); |
| 556 } | 686 } |
| 557 | 687 |
| (...skipping 681 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1239 // bizarre for SPDY. Assuming this logic is useful at all. | 1369 // bizarre for SPDY. Assuming this logic is useful at all. |
| 1240 // TODO(davidben): Bubble the error code up so we do not cache? | 1370 // TODO(davidben): Bubble the error code up so we do not cache? |
| 1241 if (result == ERR_CONNECTION_CLOSED && response_.headers.get()) | 1371 if (result == ERR_CONNECTION_CLOSED && response_.headers.get()) |
| 1242 result = OK; | 1372 result = OK; |
| 1243 | 1373 |
| 1244 if (result < 0) | 1374 if (result < 0) |
| 1245 return HandleIOError(result); | 1375 return HandleIOError(result); |
| 1246 | 1376 |
| 1247 DCHECK(response_.headers.get()); | 1377 DCHECK(response_.headers.get()); |
| 1248 | 1378 |
| 1379 if (response_.headers.get() && !ContentEncodingsValid()) { | |
| 1380 UMA_HISTOGRAM_ENUMERATION("Net.ContentDecodingFailed2.FilterType", | |
| 1381 SourceStream::TYPE_UNKNOWN, | |
| 1382 SourceStream::TYPE_MAX); | |
| 1383 return ERR_CONTENT_DECODING_FAILED; | |
| 1384 } | |
| 1385 | |
| 1249 // On a 408 response from the server ("Request Timeout") on a stale socket, | 1386 // On a 408 response from the server ("Request Timeout") on a stale socket, |
| 1250 // retry the request. | 1387 // retry the request. |
| 1251 // Headers can be NULL because of http://crbug.com/384554. | 1388 // Headers can be NULL because of http://crbug.com/384554. |
| 1252 if (response_.headers.get() && response_.headers->response_code() == 408 && | 1389 if (response_.headers.get() && response_.headers->response_code() == 408 && |
| 1253 stream_->IsConnectionReused()) { | 1390 stream_->IsConnectionReused()) { |
| 1254 net_log_.AddEventWithNetErrorCode( | 1391 net_log_.AddEventWithNetErrorCode( |
| 1255 NetLogEventType::HTTP_TRANSACTION_RESTART_AFTER_ERROR, | 1392 NetLogEventType::HTTP_TRANSACTION_RESTART_AFTER_ERROR, |
| 1256 response_.headers->response_code()); | 1393 response_.headers->response_code()); |
| 1257 // This will close the socket - it would be weird to try and reuse it, even | 1394 // This will close the socket - it would be weird to try and reuse it, even |
| 1258 // if the server doesn't actually close it. | 1395 // 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() { | 1841 void HttpNetworkTransaction::CopyConnectionAttemptsFromStreamRequest() { |
| 1705 DCHECK(stream_request_); | 1842 DCHECK(stream_request_); |
| 1706 | 1843 |
| 1707 // Since the transaction can restart with auth credentials, it may create a | 1844 // Since the transaction can restart with auth credentials, it may create a |
| 1708 // stream more than once. Accumulate all of the connection attempts across | 1845 // stream more than once. Accumulate all of the connection attempts across |
| 1709 // those streams by appending them to the vector: | 1846 // those streams by appending them to the vector: |
| 1710 for (const auto& attempt : stream_request_->connection_attempts()) | 1847 for (const auto& attempt : stream_request_->connection_attempts()) |
| 1711 connection_attempts_.push_back(attempt); | 1848 connection_attempts_.push_back(attempt); |
| 1712 } | 1849 } |
| 1713 | 1850 |
| 1851 bool HttpNetworkTransaction::ContentEncodingsValid() const { | |
| 1852 HttpResponseHeaders* headers = GetResponseHeaders(); | |
| 1853 DCHECK(headers); | |
| 1854 | |
| 1855 // It is OK, if both "Accept-Encoding" and "Content-Encoding" headers are | |
| 1856 // missing. It is a widely used setup in unit-tests. | |
|
Randy Smith (Not in Mondays)
2017/03/16 17:50:44
Suggestion: Move this comment down to above the "r
eustas
2017/03/20 13:05:18
Replaced with set-matching code, so now it is more
| |
| 1857 std::string accept_encoding; | |
| 1858 request_headers_.GetHeader(HttpRequestHeaders::kAcceptEncoding, | |
| 1859 &accept_encoding); | |
| 1860 | |
| 1861 std::string encoding; | |
| 1862 size_t iter = 0; | |
| 1863 while (headers->EnumerateHeader(&iter, "Content-Encoding", &encoding)) { | |
| 1864 if (accept_encoding.empty()) | |
| 1865 return false; | |
| 1866 | |
| 1867 encoding = base::ToLowerASCII(encoding); | |
| 1868 // RFC2616 Section 3.5 recommendation: applications SHOULD consider "x-gzip" | |
| 1869 // and "x-compress" to be equivalent to "gzip" and "compress" respectively. | |
| 1870 if (encoding.compare("x-gzip") == 0) | |
| 1871 encoding = "gzip"; | |
| 1872 if (encoding.compare("x-compress") == 0) | |
| 1873 encoding = "compress"; | |
|
Randy Smith (Not in Mondays)
2017/03/16 17:50:44
Do we need to worry about these cases in the Accep
eustas
2017/03/20 13:05:18
Made it symmetric in new parser.
| |
| 1874 | |
| 1875 // Reject malformed encodings. | |
| 1876 if (encoding.find_first_of("=;, ") != std::string::npos) | |
| 1877 return false; | |
| 1878 | |
| 1879 if (!IsAcceptableEncoding(accept_encoding, encoding)) | |
| 1880 return false; | |
| 1881 } | |
| 1882 return true; | |
| 1883 } | |
| 1884 | |
| 1714 } // namespace net | 1885 } // namespace net |
| OLD | NEW |