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