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 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 57const 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 64HttpProxySocketParams::~HttpProxySocketParams() {} 65 66#ifdef ANDROID 67bool 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 74void 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. 84static const int kHttpProxyConnectJobTimeoutInSeconds = 30; 85 86HttpProxyConnectJob::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 106HttpProxyConnectJob::~HttpProxyConnectJob() {} 107 108LoadState 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 126void 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 133void HttpProxyConnectJob::OnIOComplete(int result) { 134 int rv = DoLoop(result); 135 if (rv != ERR_IO_PENDING) 136 NotifyDelegateOfCompletion(rv); // Deletes |this| 137} 138 139int 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 185int 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 197int 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 211int 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 229int 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 274int 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 306int 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 315int 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 350int 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 377int 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 385HttpProxyClientSocketPool:: 386HttpProxyConnectJobFactory::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 406ConnectJob* 407HttpProxyClientSocketPool::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 421HttpProxyClientSocketPool::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 440HttpProxyClientSocketPool::~HttpProxyClientSocketPool() {} 441 442int 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 455void 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 466void HttpProxyClientSocketPool::CancelRequest( 467 const std::string& group_name, 468 ClientSocketHandle* handle) { 469 base_.CancelRequest(group_name, handle); 470} 471 472void HttpProxyClientSocketPool::ReleaseSocket(const std::string& group_name, 473 ClientSocket* socket, int id) { 474 base_.ReleaseSocket(group_name, socket, id); 475} 476 477void HttpProxyClientSocketPool::Flush() { 478 base_.Flush(); 479} 480 481void HttpProxyClientSocketPool::CloseIdleSockets() { 482 base_.CloseIdleSockets(); 483} 484 485int HttpProxyClientSocketPool::IdleSocketCount() const { 486 return base_.idle_socket_count(); 487} 488 489int HttpProxyClientSocketPool::IdleSocketCountInGroup( 490 const std::string& group_name) const { 491 return base_.IdleSocketCountInGroup(group_name); 492} 493 494LoadState HttpProxyClientSocketPool::GetLoadState( 495 const std::string& group_name, const ClientSocketHandle* handle) const { 496 return base_.GetLoadState(group_name, handle); 497} 498 499DictionaryValue* 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 521base::TimeDelta HttpProxyClientSocketPool::ConnectionTimeout() const { 522 return base_.ConnectionTimeout(); 523} 524 525ClientSocketPoolHistograms* HttpProxyClientSocketPool::histograms() const { 526 return base_.histograms(); 527} 528 529} // namespace net 530