http_proxy_client_socket_pool.cc revision 5852652987a195e279633156fd0f937f6e12a918
1// Copyright (c) 2010 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. 4 5#include "net/http/http_proxy_client_socket_pool.h" 6 7#include <algorithm> 8 9#include "base/time.h" 10#include "base/values.h" 11#include "googleurl/src/gurl.h" 12#include "net/base/load_flags.h" 13#include "net/base/net_errors.h" 14#include "net/http/http_network_session.h" 15#include "net/http/http_proxy_client_socket.h" 16#include "net/socket/client_socket_factory.h" 17#include "net/socket/client_socket_handle.h" 18#include "net/socket/client_socket_pool_base.h" 19#include "net/socket/ssl_client_socket.h" 20#include "net/socket/ssl_client_socket_pool.h" 21#include "net/socket/tcp_client_socket_pool.h" 22#include "net/spdy/spdy_proxy_client_socket.h" 23#include "net/spdy/spdy_session.h" 24#include "net/spdy/spdy_session_pool.h" 25#include "net/spdy/spdy_settings_storage.h" 26#include "net/spdy/spdy_stream.h" 27 28namespace net { 29 30HttpProxySocketParams::HttpProxySocketParams( 31 const scoped_refptr<TCPSocketParams>& tcp_params, 32 const scoped_refptr<SSLSocketParams>& ssl_params, 33 const GURL& request_url, 34 const std::string& user_agent, 35 HostPortPair endpoint, 36 HttpAuthCache* http_auth_cache, 37 HttpAuthHandlerFactory* http_auth_handler_factory, 38 SpdySessionPool* spdy_session_pool, 39 SpdySettingsStorage* spdy_settings, 40 bool tunnel) 41 : tcp_params_(tcp_params), 42 ssl_params_(ssl_params), 43 spdy_session_pool_(spdy_session_pool), 44 spdy_settings_(spdy_settings), 45 request_url_(request_url), 46 user_agent_(user_agent), 47 endpoint_(endpoint), 48 http_auth_cache_(tunnel ? http_auth_cache : NULL), 49 http_auth_handler_factory_(tunnel ? http_auth_handler_factory : NULL), 50 tunnel_(tunnel) { 51 DCHECK((tcp_params == NULL && ssl_params != NULL) || 52 (tcp_params != NULL && ssl_params == NULL)); 53#ifdef ANDROID 54 if (tcp_params_) 55 ignore_limits_ = tcp_params->ignore_limits(); 56 else 57 ignore_limits_ = ssl_params->ignore_limits(); 58#endif 59} 60 61const HostResolver::RequestInfo& HttpProxySocketParams::destination() const { 62 if (tcp_params_ == NULL) 63 return ssl_params_->tcp_params()->destination(); 64 else 65 return tcp_params_->destination(); 66} 67 68HttpProxySocketParams::~HttpProxySocketParams() {} 69 70// HttpProxyConnectJobs will time out after this many seconds. Note this is on 71// top of the timeout for the transport socket. 72static const int kHttpProxyConnectJobTimeoutInSeconds = 30; 73 74HttpProxyConnectJob::HttpProxyConnectJob( 75 const std::string& group_name, 76 const scoped_refptr<HttpProxySocketParams>& params, 77 const base::TimeDelta& timeout_duration, 78 TCPClientSocketPool* tcp_pool, 79 SSLClientSocketPool* ssl_pool, 80 HostResolver* host_resolver, 81 Delegate* delegate, 82 NetLog* net_log) 83 : ConnectJob(group_name, timeout_duration, delegate, 84 BoundNetLog::Make(net_log, NetLog::SOURCE_CONNECT_JOB)), 85 params_(params), 86 tcp_pool_(tcp_pool), 87 ssl_pool_(ssl_pool), 88 resolver_(host_resolver), 89 ALLOW_THIS_IN_INITIALIZER_LIST( 90 callback_(this, &HttpProxyConnectJob::OnIOComplete)), 91 using_spdy_(false) { 92} 93 94HttpProxyConnectJob::~HttpProxyConnectJob() {} 95 96LoadState HttpProxyConnectJob::GetLoadState() const { 97 switch (next_state_) { 98 case STATE_TCP_CONNECT: 99 case STATE_TCP_CONNECT_COMPLETE: 100 case STATE_SSL_CONNECT: 101 case STATE_SSL_CONNECT_COMPLETE: 102 return transport_socket_handle_->GetLoadState(); 103 case STATE_HTTP_PROXY_CONNECT: 104 case STATE_HTTP_PROXY_CONNECT_COMPLETE: 105 case STATE_SPDY_PROXY_CREATE_STREAM: 106 case STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE: 107 return LOAD_STATE_ESTABLISHING_PROXY_TUNNEL; 108 default: 109 NOTREACHED(); 110 return LOAD_STATE_IDLE; 111 } 112} 113 114void HttpProxyConnectJob::GetAdditionalErrorState(ClientSocketHandle * handle) { 115 if (error_response_info_.cert_request_info) { 116 handle->set_ssl_error_response_info(error_response_info_); 117 handle->set_is_ssl_error(true); 118 } 119} 120 121void HttpProxyConnectJob::OnIOComplete(int result) { 122 int rv = DoLoop(result); 123 if (rv != ERR_IO_PENDING) 124 NotifyDelegateOfCompletion(rv); // Deletes |this| 125} 126 127int HttpProxyConnectJob::DoLoop(int result) { 128 DCHECK_NE(next_state_, STATE_NONE); 129 130 int rv = result; 131 do { 132 State state = next_state_; 133 next_state_ = STATE_NONE; 134 switch (state) { 135 case STATE_TCP_CONNECT: 136 DCHECK_EQ(OK, rv); 137 rv = DoTCPConnect(); 138 break; 139 case STATE_TCP_CONNECT_COMPLETE: 140 rv = DoTCPConnectComplete(rv); 141 break; 142 case STATE_SSL_CONNECT: 143 DCHECK_EQ(OK, rv); 144 rv = DoSSLConnect(); 145 break; 146 case STATE_SSL_CONNECT_COMPLETE: 147 rv = DoSSLConnectComplete(rv); 148 break; 149 case STATE_HTTP_PROXY_CONNECT: 150 DCHECK_EQ(OK, rv); 151 rv = DoHttpProxyConnect(); 152 break; 153 case STATE_HTTP_PROXY_CONNECT_COMPLETE: 154 rv = DoHttpProxyConnectComplete(rv); 155 break; 156 case STATE_SPDY_PROXY_CREATE_STREAM: 157 DCHECK_EQ(OK, rv); 158 rv = DoSpdyProxyCreateStream(); 159 break; 160 case STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE: 161 rv = DoSpdyProxyCreateStreamComplete(rv); 162 break; 163 default: 164 NOTREACHED() << "bad state"; 165 rv = ERR_FAILED; 166 break; 167 } 168 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); 169 170 return rv; 171} 172 173int HttpProxyConnectJob::DoTCPConnect() { 174 next_state_ = STATE_TCP_CONNECT_COMPLETE; 175 transport_socket_handle_.reset(new ClientSocketHandle()); 176 return transport_socket_handle_->Init( 177 group_name(), params_->tcp_params(), 178 params_->tcp_params()->destination().priority(), &callback_, tcp_pool_, 179 net_log()); 180} 181 182int HttpProxyConnectJob::DoTCPConnectComplete(int result) { 183 if (result != OK) 184 return ERR_PROXY_CONNECTION_FAILED; 185 186 // Reset the timer to just the length of time allowed for HttpProxy handshake 187 // so that a fast TCP connection plus a slow HttpProxy failure doesn't take 188 // longer to timeout than it should. 189 ResetTimer(base::TimeDelta::FromSeconds( 190 kHttpProxyConnectJobTimeoutInSeconds)); 191 192 next_state_ = STATE_HTTP_PROXY_CONNECT; 193 return result; 194} 195 196int HttpProxyConnectJob::DoSSLConnect() { 197 if (params_->tunnel()) { 198 HostPortProxyPair pair(params_->destination().host_port_pair(), 199 ProxyServer::Direct()); 200 if (params_->spdy_session_pool()->HasSession(pair)) { 201 using_spdy_ = true; 202 next_state_ = STATE_SPDY_PROXY_CREATE_STREAM; 203 return OK; 204 } 205 } 206 next_state_ = STATE_SSL_CONNECT_COMPLETE; 207 transport_socket_handle_.reset(new ClientSocketHandle()); 208 return transport_socket_handle_->Init( 209 group_name(), params_->ssl_params(), 210 params_->ssl_params()->tcp_params()->destination().priority(), 211 &callback_, ssl_pool_, net_log()); 212} 213 214int HttpProxyConnectJob::DoSSLConnectComplete(int result) { 215 if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) { 216 error_response_info_ = transport_socket_handle_->ssl_error_response_info(); 217 DCHECK(error_response_info_.cert_request_info.get()); 218 return result; 219 } 220 if (IsCertificateError(result)) { 221 if (params_->ssl_params()->load_flags() & LOAD_IGNORE_ALL_CERT_ERRORS) 222 result = OK; 223 else 224 // TODO(rch): allow the user to deal with proxy cert errors in the 225 // same way as server cert errors. 226 return ERR_PROXY_CERTIFICATE_INVALID; 227 } 228 if (result < 0) { 229 if (transport_socket_handle_->socket()) 230 transport_socket_handle_->socket()->Disconnect(); 231 return ERR_PROXY_CONNECTION_FAILED; 232 } 233 234 SSLClientSocket* ssl = 235 static_cast<SSLClientSocket*>(transport_socket_handle_->socket()); 236 using_spdy_ = ssl->was_spdy_negotiated(); 237 238 // Reset the timer to just the length of time allowed for HttpProxy handshake 239 // so that a fast SSL connection plus a slow HttpProxy failure doesn't take 240 // longer to timeout than it should. 241 ResetTimer(base::TimeDelta::FromSeconds( 242 kHttpProxyConnectJobTimeoutInSeconds)); 243 // TODO(rch): If we ever decide to implement a "trusted" SPDY proxy 244 // (one that we speak SPDY over SSL to, but to which we send HTTPS 245 // request directly instead of through CONNECT tunnels, then we 246 // need to add a predicate to this if statement so we fall through 247 // to the else case. (HttpProxyClientSocket currently acts as 248 // a "trusted" SPDY proxy). 249 if (using_spdy_ && params_->tunnel()) 250 next_state_ = STATE_SPDY_PROXY_CREATE_STREAM; 251 else 252 next_state_ = STATE_HTTP_PROXY_CONNECT; 253 return result; 254} 255 256#ifdef ANDROID 257// TODO(kristianm): Find out if Connect should block 258#endif 259int HttpProxyConnectJob::DoHttpProxyConnect() { 260 next_state_ = STATE_HTTP_PROXY_CONNECT_COMPLETE; 261 const HostResolver::RequestInfo& tcp_destination = params_->destination(); 262 const HostPortPair& proxy_server = tcp_destination.host_port_pair(); 263 264 // Add a HttpProxy connection on top of the tcp socket. 265 transport_socket_.reset( 266 new HttpProxyClientSocket(transport_socket_handle_.release(), 267 params_->request_url(), 268 params_->user_agent(), 269 params_->endpoint(), 270 proxy_server, 271 params_->http_auth_cache(), 272 params_->http_auth_handler_factory(), 273 params_->tunnel(), 274 using_spdy_, 275 params_->ssl_params() != NULL)); 276 return transport_socket_->Connect(&callback_ 277#ifdef ANDROID 278 , false 279#endif 280 ); 281} 282 283int HttpProxyConnectJob::DoHttpProxyConnectComplete(int result) { 284 if (result == OK || result == ERR_PROXY_AUTH_REQUESTED || 285 result == ERR_HTTPS_PROXY_TUNNEL_RESPONSE) { 286 set_socket(transport_socket_.release()); 287 } 288 289 return result; 290} 291 292int HttpProxyConnectJob::DoSpdyProxyCreateStream() { 293 DCHECK(using_spdy_); 294 DCHECK(params_->tunnel()); 295 296 HostPortProxyPair pair(params_->destination().host_port_pair(), 297 ProxyServer::Direct()); 298 SpdySessionPool* spdy_pool = params_->spdy_session_pool(); 299 scoped_refptr<SpdySession> spdy_session; 300 // It's possible that a session to the proxy has recently been created 301 if (spdy_pool->HasSession(pair)) { 302 if (transport_socket_handle_.get()) { 303 if (transport_socket_handle_->socket()) 304 transport_socket_handle_->socket()->Disconnect(); 305 transport_socket_handle_->Reset(); 306 } 307 spdy_session = spdy_pool->Get(pair, params_->spdy_settings(), net_log()); 308 } else { 309 // Create a session direct to the proxy itself 310 int rv = spdy_pool->GetSpdySessionFromSocket( 311 pair, params_->spdy_settings(), transport_socket_handle_.release(), 312 net_log(), OK, &spdy_session, /*using_ssl_*/ true); 313 if (rv < 0) 314 return rv; 315 } 316 317 next_state_ = STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE; 318 return spdy_session->CreateStream(params_->request_url(), 319 params_->destination().priority(), 320 &spdy_stream_, spdy_session->net_log(), 321 &callback_); 322} 323 324#ifdef ANDROID 325// TODO(kristianm): Find out if Connect should block 326#endif 327int HttpProxyConnectJob::DoSpdyProxyCreateStreamComplete(int result) { 328 if (result < 0) 329 return result; 330 331 next_state_ = STATE_HTTP_PROXY_CONNECT_COMPLETE; 332 transport_socket_.reset( 333 new SpdyProxyClientSocket(spdy_stream_, 334 params_->user_agent(), 335 params_->endpoint(), 336 params_->request_url(), 337 params_->destination().host_port_pair(), 338 params_->http_auth_cache(), 339 params_->http_auth_handler_factory())); 340 return transport_socket_->Connect(&callback_ 341#ifdef ANDROID 342 , false 343#endif 344 ); 345} 346 347int HttpProxyConnectJob::ConnectInternal() { 348 if (params_->tcp_params()) 349 next_state_ = STATE_TCP_CONNECT; 350 else 351 next_state_ = STATE_SSL_CONNECT; 352 return DoLoop(OK); 353} 354 355HttpProxyClientSocketPool:: 356HttpProxyConnectJobFactory::HttpProxyConnectJobFactory( 357 TCPClientSocketPool* tcp_pool, 358 SSLClientSocketPool* ssl_pool, 359 HostResolver* host_resolver, 360 NetLog* net_log) 361 : tcp_pool_(tcp_pool), 362 ssl_pool_(ssl_pool), 363 host_resolver_(host_resolver), 364 net_log_(net_log) { 365 base::TimeDelta max_pool_timeout = base::TimeDelta(); 366 if (tcp_pool_) 367 max_pool_timeout = tcp_pool_->ConnectionTimeout(); 368 if (ssl_pool_) 369 max_pool_timeout = std::max(max_pool_timeout, 370 ssl_pool_->ConnectionTimeout()); 371 timeout_ = max_pool_timeout + 372 base::TimeDelta::FromSeconds(kHttpProxyConnectJobTimeoutInSeconds); 373} 374 375 376ConnectJob* 377HttpProxyClientSocketPool::HttpProxyConnectJobFactory::NewConnectJob( 378 const std::string& group_name, 379 const PoolBase::Request& request, 380 ConnectJob::Delegate* delegate) const { 381 return new HttpProxyConnectJob(group_name, request.params(), 382 ConnectionTimeout(), tcp_pool_, ssl_pool_, 383 host_resolver_, delegate, net_log_); 384} 385 386HttpProxyClientSocketPool::HttpProxyClientSocketPool( 387 int max_sockets, 388 int max_sockets_per_group, 389 ClientSocketPoolHistograms* histograms, 390 HostResolver* host_resolver, 391 TCPClientSocketPool* tcp_pool, 392 SSLClientSocketPool* ssl_pool, 393 NetLog* net_log) 394 : tcp_pool_(tcp_pool), 395 ssl_pool_(ssl_pool), 396 base_(max_sockets, max_sockets_per_group, histograms, 397 base::TimeDelta::FromSeconds( 398 ClientSocketPool::unused_idle_socket_timeout()), 399 base::TimeDelta::FromSeconds(kUsedIdleSocketTimeout), 400 new HttpProxyConnectJobFactory(tcp_pool, ssl_pool, host_resolver, 401 net_log)) {} 402 403HttpProxyClientSocketPool::~HttpProxyClientSocketPool() {} 404 405int HttpProxyClientSocketPool::RequestSocket(const std::string& group_name, 406 const void* socket_params, 407 RequestPriority priority, 408 ClientSocketHandle* handle, 409 CompletionCallback* callback, 410 const BoundNetLog& net_log) { 411 const scoped_refptr<HttpProxySocketParams>* casted_socket_params = 412 static_cast<const scoped_refptr<HttpProxySocketParams>*>(socket_params); 413 414 return base_.RequestSocket(group_name, *casted_socket_params, priority, 415 handle, callback, net_log); 416} 417 418void HttpProxyClientSocketPool::RequestSockets( 419 const std::string& group_name, 420 const void* params, 421 int num_sockets, 422 const BoundNetLog& net_log) { 423 const scoped_refptr<HttpProxySocketParams>* casted_params = 424 static_cast<const scoped_refptr<HttpProxySocketParams>*>(params); 425 426 base_.RequestSockets(group_name, *casted_params, num_sockets, net_log); 427} 428 429void HttpProxyClientSocketPool::CancelRequest( 430 const std::string& group_name, 431 ClientSocketHandle* handle) { 432 base_.CancelRequest(group_name, handle); 433} 434 435void HttpProxyClientSocketPool::ReleaseSocket(const std::string& group_name, 436 ClientSocket* socket, int id) { 437 base_.ReleaseSocket(group_name, socket, id); 438} 439 440void HttpProxyClientSocketPool::Flush() { 441 base_.Flush(); 442} 443 444void HttpProxyClientSocketPool::CloseIdleSockets() { 445 base_.CloseIdleSockets(); 446} 447 448int HttpProxyClientSocketPool::IdleSocketCount() const { 449 return base_.idle_socket_count(); 450} 451 452int HttpProxyClientSocketPool::IdleSocketCountInGroup( 453 const std::string& group_name) const { 454 return base_.IdleSocketCountInGroup(group_name); 455} 456 457LoadState HttpProxyClientSocketPool::GetLoadState( 458 const std::string& group_name, const ClientSocketHandle* handle) const { 459 return base_.GetLoadState(group_name, handle); 460} 461 462DictionaryValue* HttpProxyClientSocketPool::GetInfoAsValue( 463 const std::string& name, 464 const std::string& type, 465 bool include_nested_pools) const { 466 DictionaryValue* dict = base_.GetInfoAsValue(name, type); 467 if (include_nested_pools) { 468 ListValue* list = new ListValue(); 469 if (tcp_pool_) { 470 list->Append(tcp_pool_->GetInfoAsValue("tcp_socket_pool", 471 "tcp_socket_pool", 472 true)); 473 } 474 if (ssl_pool_) { 475 list->Append(ssl_pool_->GetInfoAsValue("ssl_socket_pool", 476 "ssl_socket_pool", 477 true)); 478 } 479 dict->Set("nested_pools", list); 480 } 481 return dict; 482} 483 484base::TimeDelta HttpProxyClientSocketPool::ConnectionTimeout() const { 485 return base_.ConnectionTimeout(); 486} 487 488ClientSocketPoolHistograms* HttpProxyClientSocketPool::histograms() const { 489 return base_.histograms(); 490} 491 492} // namespace net 493