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. 4 5 #include "net/http/http_proxy_client_socket_pool.h" 6 7 #include <algorithm> 8 9 #include "base/compiler_specific.h" 10 #include "base/time/time.h" 11 #include "base/values.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_stream.h" 26 #include "net/ssl/ssl_cert_request_info.h" 27 #include "url/gurl.h" 28 29 namespace net { 30 31 HttpProxySocketParams::HttpProxySocketParams( 32 const scoped_refptr<TransportSocketParams>& transport_params, 33 const scoped_refptr<SSLSocketParams>& ssl_params, 34 const GURL& request_url, 35 const std::string& user_agent, 36 const HostPortPair& endpoint, 37 HttpAuthCache* http_auth_cache, 38 HttpAuthHandlerFactory* http_auth_handler_factory, 39 SpdySessionPool* spdy_session_pool, 40 bool tunnel) 41 : transport_params_(transport_params), 42 ssl_params_(ssl_params), 43 spdy_session_pool_(spdy_session_pool), 44 request_url_(request_url), 45 user_agent_(user_agent), 46 endpoint_(endpoint), 47 http_auth_cache_(tunnel ? http_auth_cache : NULL), 48 http_auth_handler_factory_(tunnel ? http_auth_handler_factory : NULL), 49 tunnel_(tunnel) { 50 DCHECK((transport_params.get() == NULL && ssl_params.get() != NULL) || 51 (transport_params.get() != NULL && ssl_params.get() == NULL)); 52 if (transport_params_.get()) { 53 ignore_limits_ = transport_params->ignore_limits(); 54 } else { 55 ignore_limits_ = ssl_params->ignore_limits(); 56 } 57 } 58 59 const HostResolver::RequestInfo& HttpProxySocketParams::destination() const { 60 if (transport_params_.get() == NULL) { 61 return ssl_params_->GetDirectConnectionParams()->destination(); 62 } else { 63 return transport_params_->destination(); 64 } 65 } 66 67 HttpProxySocketParams::~HttpProxySocketParams() {} 68 69 // HttpProxyConnectJobs will time out after this many seconds. Note this is on 70 // top of the timeout for the transport socket. 71 #if (defined(OS_ANDROID) || defined(OS_IOS)) && defined(SPDY_PROXY_AUTH_ORIGIN) 72 static const int kHttpProxyConnectJobTimeoutInSeconds = 10; 73 #else 74 static const int kHttpProxyConnectJobTimeoutInSeconds = 30; 75 #endif 76 77 78 HttpProxyConnectJob::HttpProxyConnectJob( 79 const std::string& group_name, 80 RequestPriority priority, 81 const scoped_refptr<HttpProxySocketParams>& params, 82 const base::TimeDelta& timeout_duration, 83 TransportClientSocketPool* transport_pool, 84 SSLClientSocketPool* ssl_pool, 85 HostResolver* host_resolver, 86 Delegate* delegate, 87 NetLog* net_log) 88 : ConnectJob(group_name, timeout_duration, priority, delegate, 89 BoundNetLog::Make(net_log, NetLog::SOURCE_CONNECT_JOB)), 90 weak_ptr_factory_(this), 91 params_(params), 92 transport_pool_(transport_pool), 93 ssl_pool_(ssl_pool), 94 resolver_(host_resolver), 95 callback_(base::Bind(&HttpProxyConnectJob::OnIOComplete, 96 weak_ptr_factory_.GetWeakPtr())), 97 using_spdy_(false), 98 protocol_negotiated_(kProtoUnknown) { 99 } 100 101 HttpProxyConnectJob::~HttpProxyConnectJob() {} 102 103 LoadState 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 121 void HttpProxyConnectJob::GetAdditionalErrorState(ClientSocketHandle * handle) { 122 if (error_response_info_.cert_request_info.get()) { 123 handle->set_ssl_error_response_info(error_response_info_); 124 handle->set_is_ssl_error(true); 125 } 126 } 127 128 void HttpProxyConnectJob::OnIOComplete(int result) { 129 int rv = DoLoop(result); 130 if (rv != ERR_IO_PENDING) 131 NotifyDelegateOfCompletion(rv); // Deletes |this| 132 } 133 134 int 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 180 int HttpProxyConnectJob::DoTransportConnect() { 181 next_state_ = STATE_TCP_CONNECT_COMPLETE; 182 transport_socket_handle_.reset(new ClientSocketHandle()); 183 return transport_socket_handle_->Init(group_name(), 184 params_->transport_params(), 185 priority(), 186 callback_, 187 transport_pool_, 188 net_log()); 189 } 190 191 int HttpProxyConnectJob::DoTransportConnectComplete(int result) { 192 if (result != OK) 193 return ERR_PROXY_CONNECTION_FAILED; 194 195 // Reset the timer to just the length of time allowed for HttpProxy handshake 196 // so that a fast TCP connection plus a slow HttpProxy failure doesn't take 197 // longer to timeout than it should. 198 ResetTimer(base::TimeDelta::FromSeconds( 199 kHttpProxyConnectJobTimeoutInSeconds)); 200 201 next_state_ = STATE_HTTP_PROXY_CONNECT; 202 return result; 203 } 204 205 int HttpProxyConnectJob::DoSSLConnect() { 206 if (params_->tunnel()) { 207 SpdySessionKey key(params_->destination().host_port_pair(), 208 ProxyServer::Direct(), 209 kPrivacyModeDisabled); 210 if (params_->spdy_session_pool()->FindAvailableSession(key, net_log())) { 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(), priority(), callback_, 220 ssl_pool_, net_log()); 221 } 222 223 int HttpProxyConnectJob::DoSSLConnectComplete(int result) { 224 if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) { 225 error_response_info_ = transport_socket_handle_->ssl_error_response_info(); 226 DCHECK(error_response_info_.cert_request_info.get()); 227 error_response_info_.cert_request_info->is_proxy = true; 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 transport_socket_handle_->socket()->Disconnect(); 237 return ERR_PROXY_CERTIFICATE_INVALID; 238 } 239 } 240 if (result < 0) { 241 if (transport_socket_handle_->socket()) 242 transport_socket_handle_->socket()->Disconnect(); 243 return ERR_PROXY_CONNECTION_FAILED; 244 } 245 246 SSLClientSocket* ssl = 247 static_cast<SSLClientSocket*>(transport_socket_handle_->socket()); 248 using_spdy_ = ssl->was_spdy_negotiated(); 249 protocol_negotiated_ = ssl->GetNegotiatedProtocol(); 250 251 // Reset the timer to just the length of time allowed for HttpProxy handshake 252 // so that a fast SSL connection plus a slow HttpProxy failure doesn't take 253 // longer to timeout than it should. 254 ResetTimer(base::TimeDelta::FromSeconds( 255 kHttpProxyConnectJobTimeoutInSeconds)); 256 // TODO(rch): If we ever decide to implement a "trusted" SPDY proxy 257 // (one that we speak SPDY over SSL to, but to which we send HTTPS 258 // request directly instead of through CONNECT tunnels, then we 259 // need to add a predicate to this if statement so we fall through 260 // to the else case. (HttpProxyClientSocket currently acts as 261 // a "trusted" SPDY proxy). 262 if (using_spdy_ && params_->tunnel()) { 263 next_state_ = STATE_SPDY_PROXY_CREATE_STREAM; 264 } else { 265 next_state_ = STATE_HTTP_PROXY_CONNECT; 266 } 267 return result; 268 } 269 270 int HttpProxyConnectJob::DoHttpProxyConnect() { 271 next_state_ = STATE_HTTP_PROXY_CONNECT_COMPLETE; 272 const HostResolver::RequestInfo& tcp_destination = params_->destination(); 273 const HostPortPair& proxy_server = tcp_destination.host_port_pair(); 274 275 // Add a HttpProxy connection on top of the tcp socket. 276 transport_socket_.reset( 277 new HttpProxyClientSocket(transport_socket_handle_.release(), 278 params_->request_url(), 279 params_->user_agent(), 280 params_->endpoint(), 281 proxy_server, 282 params_->http_auth_cache(), 283 params_->http_auth_handler_factory(), 284 params_->tunnel(), 285 using_spdy_, 286 protocol_negotiated_, 287 params_->ssl_params().get() != NULL)); 288 return transport_socket_->Connect(callback_); 289 } 290 291 int HttpProxyConnectJob::DoHttpProxyConnectComplete(int result) { 292 if (result == OK || result == ERR_PROXY_AUTH_REQUESTED || 293 result == ERR_HTTPS_PROXY_TUNNEL_RESPONSE) { 294 SetSocket(transport_socket_.PassAs<StreamSocket>()); 295 } 296 297 return result; 298 } 299 300 int HttpProxyConnectJob::DoSpdyProxyCreateStream() { 301 DCHECK(using_spdy_); 302 DCHECK(params_->tunnel()); 303 SpdySessionKey key(params_->destination().host_port_pair(), 304 ProxyServer::Direct(), 305 kPrivacyModeDisabled); 306 SpdySessionPool* spdy_pool = params_->spdy_session_pool(); 307 base::WeakPtr<SpdySession> spdy_session = 308 spdy_pool->FindAvailableSession(key, net_log()); 309 // It's possible that a session to the proxy has recently been created 310 if (spdy_session) { 311 if (transport_socket_handle_.get()) { 312 if (transport_socket_handle_->socket()) 313 transport_socket_handle_->socket()->Disconnect(); 314 transport_socket_handle_->Reset(); 315 } 316 } else { 317 // Create a session direct to the proxy itself 318 int rv = spdy_pool->CreateAvailableSessionFromSocket( 319 key, transport_socket_handle_.Pass(), 320 net_log(), OK, &spdy_session, /*using_ssl_*/ true); 321 if (rv < 0) 322 return rv; 323 } 324 325 next_state_ = STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE; 326 return spdy_stream_request_.StartRequest(SPDY_BIDIRECTIONAL_STREAM, 327 spdy_session, 328 params_->request_url(), 329 priority(), 330 spdy_session->net_log(), 331 callback_); 332 } 333 334 int HttpProxyConnectJob::DoSpdyProxyCreateStreamComplete(int result) { 335 if (result < 0) 336 return result; 337 338 next_state_ = STATE_HTTP_PROXY_CONNECT_COMPLETE; 339 base::WeakPtr<SpdyStream> stream = spdy_stream_request_.ReleaseStream(); 340 DCHECK(stream.get()); 341 // |transport_socket_| will set itself as |stream|'s delegate. 342 transport_socket_.reset( 343 new SpdyProxyClientSocket(stream, 344 params_->user_agent(), 345 params_->endpoint(), 346 params_->request_url(), 347 params_->destination().host_port_pair(), 348 net_log(), 349 params_->http_auth_cache(), 350 params_->http_auth_handler_factory())); 351 return transport_socket_->Connect(callback_); 352 } 353 354 int HttpProxyConnectJob::ConnectInternal() { 355 if (params_->transport_params().get()) { 356 next_state_ = STATE_TCP_CONNECT; 357 } else { 358 next_state_ = STATE_SSL_CONNECT; 359 } 360 return DoLoop(OK); 361 } 362 363 HttpProxyClientSocketPool:: 364 HttpProxyConnectJobFactory::HttpProxyConnectJobFactory( 365 TransportClientSocketPool* transport_pool, 366 SSLClientSocketPool* ssl_pool, 367 HostResolver* host_resolver, 368 NetLog* net_log) 369 : transport_pool_(transport_pool), 370 ssl_pool_(ssl_pool), 371 host_resolver_(host_resolver), 372 net_log_(net_log) { 373 base::TimeDelta max_pool_timeout = base::TimeDelta(); 374 375 #if (defined(OS_ANDROID) || defined(OS_IOS)) && defined(SPDY_PROXY_AUTH_ORIGIN) 376 #else 377 if (transport_pool_) 378 max_pool_timeout = transport_pool_->ConnectionTimeout(); 379 if (ssl_pool_) 380 max_pool_timeout = std::max(max_pool_timeout, 381 ssl_pool_->ConnectionTimeout()); 382 #endif 383 timeout_ = max_pool_timeout + 384 base::TimeDelta::FromSeconds(kHttpProxyConnectJobTimeoutInSeconds); 385 } 386 387 388 scoped_ptr<ConnectJob> 389 HttpProxyClientSocketPool::HttpProxyConnectJobFactory::NewConnectJob( 390 const std::string& group_name, 391 const PoolBase::Request& request, 392 ConnectJob::Delegate* delegate) const { 393 return scoped_ptr<ConnectJob>(new HttpProxyConnectJob(group_name, 394 request.priority(), 395 request.params(), 396 ConnectionTimeout(), 397 transport_pool_, 398 ssl_pool_, 399 host_resolver_, 400 delegate, 401 net_log_)); 402 } 403 404 base::TimeDelta 405 HttpProxyClientSocketPool::HttpProxyConnectJobFactory::ConnectionTimeout( 406 ) const { 407 return timeout_; 408 } 409 410 HttpProxyClientSocketPool::HttpProxyClientSocketPool( 411 int max_sockets, 412 int max_sockets_per_group, 413 ClientSocketPoolHistograms* histograms, 414 HostResolver* host_resolver, 415 TransportClientSocketPool* transport_pool, 416 SSLClientSocketPool* ssl_pool, 417 NetLog* net_log) 418 : transport_pool_(transport_pool), 419 ssl_pool_(ssl_pool), 420 base_(this, max_sockets, max_sockets_per_group, histograms, 421 ClientSocketPool::unused_idle_socket_timeout(), 422 ClientSocketPool::used_idle_socket_timeout(), 423 new HttpProxyConnectJobFactory(transport_pool, 424 ssl_pool, 425 host_resolver, 426 net_log)) { 427 // We should always have a |transport_pool_| except in unit tests. 428 if (transport_pool_) 429 base_.AddLowerLayeredPool(transport_pool_); 430 if (ssl_pool_) 431 base_.AddLowerLayeredPool(ssl_pool_); 432 } 433 434 HttpProxyClientSocketPool::~HttpProxyClientSocketPool() { 435 } 436 437 int HttpProxyClientSocketPool::RequestSocket( 438 const std::string& group_name, const void* socket_params, 439 RequestPriority priority, ClientSocketHandle* handle, 440 const CompletionCallback& callback, const BoundNetLog& net_log) { 441 const scoped_refptr<HttpProxySocketParams>* casted_socket_params = 442 static_cast<const scoped_refptr<HttpProxySocketParams>*>(socket_params); 443 444 return base_.RequestSocket(group_name, *casted_socket_params, priority, 445 handle, callback, net_log); 446 } 447 448 void HttpProxyClientSocketPool::RequestSockets( 449 const std::string& group_name, 450 const void* params, 451 int num_sockets, 452 const BoundNetLog& net_log) { 453 const scoped_refptr<HttpProxySocketParams>* casted_params = 454 static_cast<const scoped_refptr<HttpProxySocketParams>*>(params); 455 456 base_.RequestSockets(group_name, *casted_params, num_sockets, net_log); 457 } 458 459 void HttpProxyClientSocketPool::CancelRequest( 460 const std::string& group_name, 461 ClientSocketHandle* handle) { 462 base_.CancelRequest(group_name, handle); 463 } 464 465 void HttpProxyClientSocketPool::ReleaseSocket(const std::string& group_name, 466 scoped_ptr<StreamSocket> socket, 467 int id) { 468 base_.ReleaseSocket(group_name, socket.Pass(), id); 469 } 470 471 void HttpProxyClientSocketPool::FlushWithError(int error) { 472 base_.FlushWithError(error); 473 } 474 475 void HttpProxyClientSocketPool::CloseIdleSockets() { 476 base_.CloseIdleSockets(); 477 } 478 479 int HttpProxyClientSocketPool::IdleSocketCount() const { 480 return base_.idle_socket_count(); 481 } 482 483 int HttpProxyClientSocketPool::IdleSocketCountInGroup( 484 const std::string& group_name) const { 485 return base_.IdleSocketCountInGroup(group_name); 486 } 487 488 LoadState HttpProxyClientSocketPool::GetLoadState( 489 const std::string& group_name, const ClientSocketHandle* handle) const { 490 return base_.GetLoadState(group_name, handle); 491 } 492 493 base::DictionaryValue* HttpProxyClientSocketPool::GetInfoAsValue( 494 const std::string& name, 495 const std::string& type, 496 bool include_nested_pools) const { 497 base::DictionaryValue* dict = base_.GetInfoAsValue(name, type); 498 if (include_nested_pools) { 499 base::ListValue* list = new base::ListValue(); 500 if (transport_pool_) { 501 list->Append(transport_pool_->GetInfoAsValue("transport_socket_pool", 502 "transport_socket_pool", 503 true)); 504 } 505 if (ssl_pool_) { 506 list->Append(ssl_pool_->GetInfoAsValue("ssl_socket_pool", 507 "ssl_socket_pool", 508 true)); 509 } 510 dict->Set("nested_pools", list); 511 } 512 return dict; 513 } 514 515 base::TimeDelta HttpProxyClientSocketPool::ConnectionTimeout() const { 516 return base_.ConnectionTimeout(); 517 } 518 519 ClientSocketPoolHistograms* HttpProxyClientSocketPool::histograms() const { 520 return base_.histograms(); 521 } 522 523 bool HttpProxyClientSocketPool::IsStalled() const { 524 return base_.IsStalled(); 525 } 526 527 void HttpProxyClientSocketPool::AddHigherLayeredPool( 528 HigherLayeredPool* higher_pool) { 529 base_.AddHigherLayeredPool(higher_pool); 530 } 531 532 void HttpProxyClientSocketPool::RemoveHigherLayeredPool( 533 HigherLayeredPool* higher_pool) { 534 base_.RemoveHigherLayeredPool(higher_pool); 535 } 536 537 bool HttpProxyClientSocketPool::CloseOneIdleConnection() { 538 if (base_.CloseOneIdleSocket()) 539 return true; 540 return base_.CloseOneIdleConnectionInHigherLayeredPool(); 541 } 542 543 } // namespace net 544