Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(111)

Side by Side Diff: net/http/http_network_transaction.cc

Issue 2753453003: Reject unadvertised encodings (Closed)
Patch Set: Reject unadvertised encodings. Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698