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