Revert of Add CompleteRead to SequencedSocketData and convert all but 3 tests (patchs...
[chromium-blink-merge.git] / net / http / http_proxy_client_socket.cc
blob2eec09c8bb34dfbfb569217d1c6456cb05d4bf6a
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
3 // found in the LICENSE file.
5 #include "net/http/http_proxy_client_socket.h"
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/profiler/scoped_tracker.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/stringprintf.h"
12 #include "net/base/auth.h"
13 #include "net/base/host_port_pair.h"
14 #include "net/base/io_buffer.h"
15 #include "net/base/net_util.h"
16 #include "net/base/proxy_delegate.h"
17 #include "net/http/http_basic_stream.h"
18 #include "net/http/http_network_session.h"
19 #include "net/http/http_request_info.h"
20 #include "net/http/http_response_headers.h"
21 #include "net/http/http_stream_parser.h"
22 #include "net/http/proxy_connect_redirect_http_stream.h"
23 #include "net/log/net_log.h"
24 #include "net/socket/client_socket_handle.h"
25 #include "url/gurl.h"
27 namespace net {
29 HttpProxyClientSocket::HttpProxyClientSocket(
30 ClientSocketHandle* transport_socket,
31 const std::string& user_agent,
32 const HostPortPair& endpoint,
33 const HostPortPair& proxy_server,
34 HttpAuthCache* http_auth_cache,
35 HttpAuthHandlerFactory* http_auth_handler_factory,
36 bool tunnel,
37 bool using_spdy,
38 NextProto protocol_negotiated,
39 ProxyDelegate* proxy_delegate,
40 bool is_https_proxy)
41 : io_callback_(base::Bind(&HttpProxyClientSocket::OnIOComplete,
42 base::Unretained(this))),
43 next_state_(STATE_NONE),
44 transport_(transport_socket),
45 endpoint_(endpoint),
46 auth_(tunnel ?
47 new HttpAuthController(HttpAuth::AUTH_PROXY,
48 GURL((is_https_proxy ? "https://" : "http://")
49 + proxy_server.ToString()),
50 http_auth_cache,
51 http_auth_handler_factory)
52 : NULL),
53 tunnel_(tunnel),
54 using_spdy_(using_spdy),
55 protocol_negotiated_(protocol_negotiated),
56 is_https_proxy_(is_https_proxy),
57 redirect_has_load_timing_info_(false),
58 proxy_server_(proxy_server),
59 proxy_delegate_(proxy_delegate),
60 net_log_(transport_socket->socket()->NetLog()) {
61 // Synthesize the bits of a request that we actually use.
62 request_.url = GURL("https://" + endpoint.ToString());
63 request_.method = "CONNECT";
64 if (!user_agent.empty())
65 request_.extra_headers.SetHeader(HttpRequestHeaders::kUserAgent,
66 user_agent);
69 HttpProxyClientSocket::~HttpProxyClientSocket() {
70 Disconnect();
73 int HttpProxyClientSocket::RestartWithAuth(const CompletionCallback& callback) {
74 DCHECK_EQ(STATE_NONE, next_state_);
75 DCHECK(user_callback_.is_null());
77 int rv = PrepareForAuthRestart();
78 if (rv != OK)
79 return rv;
81 rv = DoLoop(OK);
82 if (rv == ERR_IO_PENDING) {
83 if (!callback.is_null())
84 user_callback_ = callback;
87 return rv;
90 const scoped_refptr<HttpAuthController>&
91 HttpProxyClientSocket::GetAuthController() const {
92 return auth_;
95 bool HttpProxyClientSocket::IsUsingSpdy() const {
96 return using_spdy_;
99 NextProto HttpProxyClientSocket::GetProtocolNegotiated() const {
100 return protocol_negotiated_;
103 const HttpResponseInfo* HttpProxyClientSocket::GetConnectResponseInfo() const {
104 return response_.headers.get() ? &response_ : NULL;
107 HttpStream* HttpProxyClientSocket::CreateConnectResponseStream() {
108 return new ProxyConnectRedirectHttpStream(
109 redirect_has_load_timing_info_ ? &redirect_load_timing_info_ : NULL);
113 int HttpProxyClientSocket::Connect(const CompletionCallback& callback) {
114 DCHECK(transport_.get());
115 DCHECK(transport_->socket());
116 DCHECK(user_callback_.is_null());
118 // TODO(rch): figure out the right way to set up a tunnel with SPDY.
119 // This approach sends the complete HTTPS request to the proxy
120 // which allows the proxy to see "private" data. Instead, we should
121 // create an SSL tunnel to the origin server using the CONNECT method
122 // inside a single SPDY stream.
123 if (using_spdy_ || !tunnel_)
124 next_state_ = STATE_DONE;
125 if (next_state_ == STATE_DONE)
126 return OK;
128 DCHECK_EQ(STATE_NONE, next_state_);
129 next_state_ = STATE_GENERATE_AUTH_TOKEN;
131 int rv = DoLoop(OK);
132 if (rv == ERR_IO_PENDING)
133 user_callback_ = callback;
134 return rv;
137 void HttpProxyClientSocket::Disconnect() {
138 if (transport_.get())
139 transport_->socket()->Disconnect();
141 // Reset other states to make sure they aren't mistakenly used later.
142 // These are the states initialized by Connect().
143 next_state_ = STATE_NONE;
144 user_callback_.Reset();
147 bool HttpProxyClientSocket::IsConnected() const {
148 return next_state_ == STATE_DONE && transport_->socket()->IsConnected();
151 bool HttpProxyClientSocket::IsConnectedAndIdle() const {
152 return next_state_ == STATE_DONE &&
153 transport_->socket()->IsConnectedAndIdle();
156 const BoundNetLog& HttpProxyClientSocket::NetLog() const {
157 return net_log_;
160 void HttpProxyClientSocket::SetSubresourceSpeculation() {
161 if (transport_.get() && transport_->socket()) {
162 transport_->socket()->SetSubresourceSpeculation();
163 } else {
164 NOTREACHED();
168 void HttpProxyClientSocket::SetOmniboxSpeculation() {
169 if (transport_.get() && transport_->socket()) {
170 transport_->socket()->SetOmniboxSpeculation();
171 } else {
172 NOTREACHED();
176 bool HttpProxyClientSocket::WasEverUsed() const {
177 if (transport_.get() && transport_->socket()) {
178 return transport_->socket()->WasEverUsed();
180 NOTREACHED();
181 return false;
184 bool HttpProxyClientSocket::UsingTCPFastOpen() const {
185 if (transport_.get() && transport_->socket()) {
186 return transport_->socket()->UsingTCPFastOpen();
188 NOTREACHED();
189 return false;
192 bool HttpProxyClientSocket::WasNpnNegotiated() const {
193 if (transport_.get() && transport_->socket()) {
194 return transport_->socket()->WasNpnNegotiated();
196 NOTREACHED();
197 return false;
200 NextProto HttpProxyClientSocket::GetNegotiatedProtocol() const {
201 if (transport_.get() && transport_->socket()) {
202 return transport_->socket()->GetNegotiatedProtocol();
204 NOTREACHED();
205 return kProtoUnknown;
208 bool HttpProxyClientSocket::GetSSLInfo(SSLInfo* ssl_info) {
209 if (transport_.get() && transport_->socket()) {
210 return transport_->socket()->GetSSLInfo(ssl_info);
212 NOTREACHED();
213 return false;
216 int HttpProxyClientSocket::Read(IOBuffer* buf, int buf_len,
217 const CompletionCallback& callback) {
218 DCHECK(user_callback_.is_null());
219 if (next_state_ != STATE_DONE) {
220 // We're trying to read the body of the response but we're still trying
221 // to establish an SSL tunnel through the proxy. We can't read these
222 // bytes when establishing a tunnel because they might be controlled by
223 // an active network attacker. We don't worry about this for HTTP
224 // because an active network attacker can already control HTTP sessions.
225 // We reach this case when the user cancels a 407 proxy auth prompt.
226 // See http://crbug.com/8473.
227 DCHECK_EQ(407, response_.headers->response_code());
228 LogBlockedTunnelResponse();
230 return ERR_TUNNEL_CONNECTION_FAILED;
233 return transport_->socket()->Read(buf, buf_len, callback);
236 int HttpProxyClientSocket::Write(IOBuffer* buf, int buf_len,
237 const CompletionCallback& callback) {
238 DCHECK_EQ(STATE_DONE, next_state_);
239 DCHECK(user_callback_.is_null());
241 return transport_->socket()->Write(buf, buf_len, callback);
244 int HttpProxyClientSocket::SetReceiveBufferSize(int32 size) {
245 return transport_->socket()->SetReceiveBufferSize(size);
248 int HttpProxyClientSocket::SetSendBufferSize(int32 size) {
249 return transport_->socket()->SetSendBufferSize(size);
252 int HttpProxyClientSocket::GetPeerAddress(IPEndPoint* address) const {
253 return transport_->socket()->GetPeerAddress(address);
256 int HttpProxyClientSocket::GetLocalAddress(IPEndPoint* address) const {
257 return transport_->socket()->GetLocalAddress(address);
260 int HttpProxyClientSocket::PrepareForAuthRestart() {
261 if (!response_.headers.get())
262 return ERR_CONNECTION_RESET;
264 bool keep_alive = false;
265 if (response_.headers->IsKeepAlive() &&
266 http_stream_parser_->CanFindEndOfResponse()) {
267 if (!http_stream_parser_->IsResponseBodyComplete()) {
268 next_state_ = STATE_DRAIN_BODY;
269 drain_buf_ = new IOBuffer(kDrainBodyBufferSize);
270 return OK;
272 keep_alive = true;
275 // We don't need to drain the response body, so we act as if we had drained
276 // the response body.
277 return DidDrainBodyForAuthRestart(keep_alive);
280 int HttpProxyClientSocket::DidDrainBodyForAuthRestart(bool keep_alive) {
281 if (keep_alive && transport_->socket()->IsConnectedAndIdle()) {
282 next_state_ = STATE_GENERATE_AUTH_TOKEN;
283 transport_->set_reuse_type(ClientSocketHandle::REUSED_IDLE);
284 } else {
285 // This assumes that the underlying transport socket is a TCP socket,
286 // since only TCP sockets are restartable.
287 next_state_ = STATE_TCP_RESTART;
288 transport_->socket()->Disconnect();
291 // Reset the other member variables.
292 drain_buf_ = NULL;
293 parser_buf_ = NULL;
294 http_stream_parser_.reset();
295 request_line_.clear();
296 request_headers_.Clear();
297 response_ = HttpResponseInfo();
298 return OK;
301 void HttpProxyClientSocket::LogBlockedTunnelResponse() const {
302 ProxyClientSocket::LogBlockedTunnelResponse(
303 response_.headers->response_code(),
304 is_https_proxy_);
307 void HttpProxyClientSocket::DoCallback(int result) {
308 DCHECK_NE(ERR_IO_PENDING, result);
309 DCHECK(!user_callback_.is_null());
311 // Since Run() may result in Read being called,
312 // clear user_callback_ up front.
313 CompletionCallback c = user_callback_;
314 user_callback_.Reset();
315 c.Run(result);
318 void HttpProxyClientSocket::OnIOComplete(int result) {
319 DCHECK_NE(STATE_NONE, next_state_);
320 DCHECK_NE(STATE_DONE, next_state_);
321 int rv = DoLoop(result);
322 if (rv != ERR_IO_PENDING)
323 DoCallback(rv);
326 int HttpProxyClientSocket::DoLoop(int last_io_result) {
327 DCHECK_NE(next_state_, STATE_NONE);
328 DCHECK_NE(next_state_, STATE_DONE);
329 int rv = last_io_result;
330 do {
331 State state = next_state_;
332 next_state_ = STATE_NONE;
333 switch (state) {
334 case STATE_GENERATE_AUTH_TOKEN:
335 DCHECK_EQ(OK, rv);
336 rv = DoGenerateAuthToken();
337 break;
338 case STATE_GENERATE_AUTH_TOKEN_COMPLETE:
339 rv = DoGenerateAuthTokenComplete(rv);
340 break;
341 case STATE_SEND_REQUEST:
342 DCHECK_EQ(OK, rv);
343 net_log_.BeginEvent(
344 NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST);
345 rv = DoSendRequest();
346 break;
347 case STATE_SEND_REQUEST_COMPLETE:
348 rv = DoSendRequestComplete(rv);
349 net_log_.EndEventWithNetErrorCode(
350 NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST, rv);
351 break;
352 case STATE_READ_HEADERS:
353 DCHECK_EQ(OK, rv);
354 net_log_.BeginEvent(
355 NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_READ_HEADERS);
356 rv = DoReadHeaders();
357 break;
358 case STATE_READ_HEADERS_COMPLETE:
359 rv = DoReadHeadersComplete(rv);
360 net_log_.EndEventWithNetErrorCode(
361 NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_READ_HEADERS, rv);
362 break;
363 case STATE_DRAIN_BODY:
364 DCHECK_EQ(OK, rv);
365 rv = DoDrainBody();
366 break;
367 case STATE_DRAIN_BODY_COMPLETE:
368 rv = DoDrainBodyComplete(rv);
369 break;
370 case STATE_TCP_RESTART:
371 DCHECK_EQ(OK, rv);
372 rv = DoTCPRestart();
373 break;
374 case STATE_TCP_RESTART_COMPLETE:
375 rv = DoTCPRestartComplete(rv);
376 break;
377 case STATE_DONE:
378 break;
379 default:
380 NOTREACHED() << "bad state";
381 rv = ERR_UNEXPECTED;
382 break;
384 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE &&
385 next_state_ != STATE_DONE);
386 return rv;
389 int HttpProxyClientSocket::DoGenerateAuthToken() {
390 next_state_ = STATE_GENERATE_AUTH_TOKEN_COMPLETE;
391 return auth_->MaybeGenerateAuthToken(&request_, io_callback_, net_log_);
394 int HttpProxyClientSocket::DoGenerateAuthTokenComplete(int result) {
395 DCHECK_NE(ERR_IO_PENDING, result);
396 if (result == OK)
397 next_state_ = STATE_SEND_REQUEST;
398 return result;
401 int HttpProxyClientSocket::DoSendRequest() {
402 next_state_ = STATE_SEND_REQUEST_COMPLETE;
404 // This is constructed lazily (instead of within our Start method), so that
405 // we have proxy info available.
406 if (request_line_.empty()) {
407 DCHECK(request_headers_.IsEmpty());
408 HttpRequestHeaders authorization_headers;
409 if (auth_->HaveAuth())
410 auth_->AddAuthorizationHeader(&authorization_headers);
411 if (proxy_delegate_) {
412 proxy_delegate_->OnBeforeTunnelRequest(proxy_server_,
413 &authorization_headers);
415 std::string user_agent;
416 if (!request_.extra_headers.GetHeader(HttpRequestHeaders::kUserAgent,
417 &user_agent)) {
418 user_agent.clear();
420 BuildTunnelRequest(endpoint_, authorization_headers, user_agent,
421 &request_line_, &request_headers_);
423 net_log_.AddEvent(
424 NetLog::TYPE_HTTP_TRANSACTION_SEND_TUNNEL_HEADERS,
425 base::Bind(&HttpRequestHeaders::NetLogCallback,
426 base::Unretained(&request_headers_),
427 &request_line_));
430 parser_buf_ = new GrowableIOBuffer();
431 http_stream_parser_.reset(new HttpStreamParser(
432 transport_.get(), &request_, parser_buf_.get(), net_log_));
433 return http_stream_parser_->SendRequest(
434 request_line_, request_headers_, &response_, io_callback_);
437 int HttpProxyClientSocket::DoSendRequestComplete(int result) {
438 if (result < 0)
439 return result;
441 next_state_ = STATE_READ_HEADERS;
442 return OK;
445 int HttpProxyClientSocket::DoReadHeaders() {
446 next_state_ = STATE_READ_HEADERS_COMPLETE;
447 return http_stream_parser_->ReadResponseHeaders(io_callback_);
450 int HttpProxyClientSocket::DoReadHeadersComplete(int result) {
451 if (result < 0)
452 return result;
454 // Require the "HTTP/1.x" status line for SSL CONNECT.
455 if (response_.headers->GetParsedHttpVersion() < HttpVersion(1, 0))
456 return ERR_TUNNEL_CONNECTION_FAILED;
458 net_log_.AddEvent(
459 NetLog::TYPE_HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS,
460 base::Bind(&HttpResponseHeaders::NetLogCallback, response_.headers));
462 if (proxy_delegate_) {
463 proxy_delegate_->OnTunnelHeadersReceived(
464 HostPortPair::FromURL(request_.url),
465 proxy_server_,
466 *response_.headers);
469 switch (response_.headers->response_code()) {
470 case 200: // OK
471 if (http_stream_parser_->IsMoreDataBuffered())
472 // The proxy sent extraneous data after the headers.
473 return ERR_TUNNEL_CONNECTION_FAILED;
475 next_state_ = STATE_DONE;
476 return OK;
478 // We aren't able to CONNECT to the remote host through the proxy. We
479 // need to be very suspicious about the response because an active network
480 // attacker can force us into this state by masquerading as the proxy.
481 // The only safe thing to do here is to fail the connection because our
482 // client is expecting an SSL protected response.
483 // See http://crbug.com/7338.
485 case 302: // Found / Moved Temporarily
486 // Attempt to follow redirects from HTTPS proxies, but only if we can
487 // sanitize the response. This still allows a rogue HTTPS proxy to
488 // redirect an HTTPS site load to a similar-looking site, but no longer
489 // allows it to impersonate the site the user requested.
490 if (!is_https_proxy_ || !SanitizeProxyRedirect(&response_)) {
491 LogBlockedTunnelResponse();
492 return ERR_TUNNEL_CONNECTION_FAILED;
495 redirect_has_load_timing_info_ = transport_->GetLoadTimingInfo(
496 http_stream_parser_->IsConnectionReused(),
497 &redirect_load_timing_info_);
498 transport_.reset();
499 http_stream_parser_.reset();
500 return ERR_HTTPS_PROXY_TUNNEL_RESPONSE;
502 case 407: // Proxy Authentication Required
503 // We need this status code to allow proxy authentication. Our
504 // authentication code is smart enough to avoid being tricked by an
505 // active network attacker.
506 // The next state is intentionally not set as it should be STATE_NONE;
507 if (!SanitizeProxyAuth(&response_)) {
508 LogBlockedTunnelResponse();
509 return ERR_TUNNEL_CONNECTION_FAILED;
511 return HandleProxyAuthChallenge(auth_.get(), &response_, net_log_);
513 default:
514 // Ignore response to avoid letting the proxy impersonate the target
515 // server. (See http://crbug.com/137891.)
516 // We lose something by doing this. We have seen proxy 403, 404, and
517 // 501 response bodies that contain a useful error message. For
518 // example, Squid uses a 404 response to report the DNS error: "The
519 // domain name does not exist."
520 LogBlockedTunnelResponse();
521 return ERR_TUNNEL_CONNECTION_FAILED;
525 int HttpProxyClientSocket::DoDrainBody() {
526 DCHECK(drain_buf_.get());
527 DCHECK(transport_->is_initialized());
528 next_state_ = STATE_DRAIN_BODY_COMPLETE;
529 return http_stream_parser_->ReadResponseBody(
530 drain_buf_.get(), kDrainBodyBufferSize, io_callback_);
533 int HttpProxyClientSocket::DoDrainBodyComplete(int result) {
534 if (result < 0)
535 return result;
537 if (http_stream_parser_->IsResponseBodyComplete())
538 return DidDrainBodyForAuthRestart(true);
540 // Keep draining.
541 next_state_ = STATE_DRAIN_BODY;
542 return OK;
545 int HttpProxyClientSocket::DoTCPRestart() {
546 next_state_ = STATE_TCP_RESTART_COMPLETE;
547 return transport_->socket()->Connect(
548 base::Bind(&HttpProxyClientSocket::OnIOComplete, base::Unretained(this)));
551 int HttpProxyClientSocket::DoTCPRestartComplete(int result) {
552 // TODO(rvargas): Remove ScopedTracker below once crbug.com/462784 is fixed.
553 tracked_objects::ScopedTracker tracking_profile(
554 FROM_HERE_WITH_EXPLICIT_FUNCTION(
555 "462784 HttpProxyClientSocket::DoTCPRestartComplete"));
557 if (result != OK)
558 return result;
560 next_state_ = STATE_GENERATE_AUTH_TOKEN;
561 return result;
564 } // namespace net