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 |