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