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