| OLD | NEW |
| (Empty) |
| 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_stream_factory_impl_job.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <string> | |
| 9 | |
| 10 #include "base/bind.h" | |
| 11 #include "base/bind_helpers.h" | |
| 12 #include "base/logging.h" | |
| 13 #include "base/profiler/scoped_tracker.h" | |
| 14 #include "base/stl_util.h" | |
| 15 #include "base/strings/string_util.h" | |
| 16 #include "base/strings/stringprintf.h" | |
| 17 #include "base/values.h" | |
| 18 #include "build/build_config.h" | |
| 19 #include "net/base/connection_type_histograms.h" | |
| 20 #include "net/base/net_log.h" | |
| 21 #include "net/base/net_util.h" | |
| 22 #include "net/http/http_basic_stream.h" | |
| 23 #include "net/http/http_network_session.h" | |
| 24 #include "net/http/http_proxy_client_socket.h" | |
| 25 #include "net/http/http_proxy_client_socket_pool.h" | |
| 26 #include "net/http/http_request_info.h" | |
| 27 #include "net/http/http_server_properties.h" | |
| 28 #include "net/http/http_stream_factory.h" | |
| 29 #include "net/http/http_stream_factory_impl_request.h" | |
| 30 #include "net/quic/quic_http_stream.h" | |
| 31 #include "net/socket/client_socket_handle.h" | |
| 32 #include "net/socket/client_socket_pool.h" | |
| 33 #include "net/socket/client_socket_pool_manager.h" | |
| 34 #include "net/socket/socks_client_socket_pool.h" | |
| 35 #include "net/socket/ssl_client_socket.h" | |
| 36 #include "net/socket/ssl_client_socket_pool.h" | |
| 37 #include "net/spdy/spdy_http_stream.h" | |
| 38 #include "net/spdy/spdy_session.h" | |
| 39 #include "net/spdy/spdy_session_pool.h" | |
| 40 #include "net/ssl/ssl_cert_request_info.h" | |
| 41 | |
| 42 namespace net { | |
| 43 | |
| 44 // Returns parameters associated with the start of a HTTP stream job. | |
| 45 base::Value* NetLogHttpStreamJobCallback(const GURL* original_url, | |
| 46 const GURL* url, | |
| 47 RequestPriority priority, | |
| 48 NetLog::LogLevel /* log_level */) { | |
| 49 base::DictionaryValue* dict = new base::DictionaryValue(); | |
| 50 dict->SetString("original_url", original_url->GetOrigin().spec()); | |
| 51 dict->SetString("url", url->GetOrigin().spec()); | |
| 52 dict->SetString("priority", RequestPriorityToString(priority)); | |
| 53 return dict; | |
| 54 } | |
| 55 | |
| 56 // Returns parameters associated with the Proto (with NPN negotiation) of a HTTP | |
| 57 // stream. | |
| 58 base::Value* NetLogHttpStreamProtoCallback( | |
| 59 const SSLClientSocket::NextProtoStatus status, | |
| 60 const std::string* proto, | |
| 61 NetLog::LogLevel /* log_level */) { | |
| 62 base::DictionaryValue* dict = new base::DictionaryValue(); | |
| 63 | |
| 64 dict->SetString("next_proto_status", | |
| 65 SSLClientSocket::NextProtoStatusToString(status)); | |
| 66 dict->SetString("proto", *proto); | |
| 67 return dict; | |
| 68 } | |
| 69 | |
| 70 HttpStreamFactoryImpl::Job::Job(HttpStreamFactoryImpl* stream_factory, | |
| 71 HttpNetworkSession* session, | |
| 72 const HttpRequestInfo& request_info, | |
| 73 RequestPriority priority, | |
| 74 const SSLConfig& server_ssl_config, | |
| 75 const SSLConfig& proxy_ssl_config, | |
| 76 NetLog* net_log) | |
| 77 : request_(NULL), | |
| 78 request_info_(request_info), | |
| 79 priority_(priority), | |
| 80 server_ssl_config_(server_ssl_config), | |
| 81 proxy_ssl_config_(proxy_ssl_config), | |
| 82 net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_HTTP_STREAM_JOB)), | |
| 83 io_callback_(base::Bind(&Job::OnIOComplete, base::Unretained(this))), | |
| 84 connection_(new ClientSocketHandle), | |
| 85 session_(session), | |
| 86 stream_factory_(stream_factory), | |
| 87 next_state_(STATE_NONE), | |
| 88 pac_request_(NULL), | |
| 89 blocking_job_(NULL), | |
| 90 waiting_job_(NULL), | |
| 91 using_ssl_(false), | |
| 92 using_spdy_(false), | |
| 93 using_quic_(false), | |
| 94 quic_request_(session_->quic_stream_factory()), | |
| 95 using_existing_quic_session_(false), | |
| 96 spdy_certificate_error_(OK), | |
| 97 establishing_tunnel_(false), | |
| 98 was_npn_negotiated_(false), | |
| 99 protocol_negotiated_(kProtoUnknown), | |
| 100 num_streams_(0), | |
| 101 spdy_session_direct_(false), | |
| 102 job_status_(STATUS_RUNNING), | |
| 103 other_job_status_(STATUS_RUNNING), | |
| 104 ptr_factory_(this) { | |
| 105 DCHECK(stream_factory); | |
| 106 DCHECK(session); | |
| 107 } | |
| 108 | |
| 109 HttpStreamFactoryImpl::Job::~Job() { | |
| 110 net_log_.EndEvent(NetLog::TYPE_HTTP_STREAM_JOB); | |
| 111 | |
| 112 // When we're in a partially constructed state, waiting for the user to | |
| 113 // provide certificate handling information or authentication, we can't reuse | |
| 114 // this stream at all. | |
| 115 if (next_state_ == STATE_WAITING_USER_ACTION) { | |
| 116 connection_->socket()->Disconnect(); | |
| 117 connection_.reset(); | |
| 118 } | |
| 119 | |
| 120 if (pac_request_) | |
| 121 session_->proxy_service()->CancelPacRequest(pac_request_); | |
| 122 | |
| 123 // The stream could be in a partial state. It is not reusable. | |
| 124 if (stream_.get() && next_state_ != STATE_DONE) | |
| 125 stream_->Close(true /* not reusable */); | |
| 126 } | |
| 127 | |
| 128 void HttpStreamFactoryImpl::Job::Start(Request* request) { | |
| 129 DCHECK(request); | |
| 130 request_ = request; | |
| 131 StartInternal(); | |
| 132 } | |
| 133 | |
| 134 int HttpStreamFactoryImpl::Job::Preconnect(int num_streams) { | |
| 135 DCHECK_GT(num_streams, 0); | |
| 136 base::WeakPtr<HttpServerProperties> http_server_properties = | |
| 137 session_->http_server_properties(); | |
| 138 if (http_server_properties && | |
| 139 http_server_properties->SupportsRequestPriority( | |
| 140 HostPortPair::FromURL(request_info_.url))) { | |
| 141 num_streams_ = 1; | |
| 142 } else { | |
| 143 num_streams_ = num_streams; | |
| 144 } | |
| 145 return StartInternal(); | |
| 146 } | |
| 147 | |
| 148 int HttpStreamFactoryImpl::Job::RestartTunnelWithProxyAuth( | |
| 149 const AuthCredentials& credentials) { | |
| 150 DCHECK(establishing_tunnel_); | |
| 151 next_state_ = STATE_RESTART_TUNNEL_AUTH; | |
| 152 stream_.reset(); | |
| 153 return RunLoop(OK); | |
| 154 } | |
| 155 | |
| 156 LoadState HttpStreamFactoryImpl::Job::GetLoadState() const { | |
| 157 switch (next_state_) { | |
| 158 case STATE_RESOLVE_PROXY_COMPLETE: | |
| 159 return session_->proxy_service()->GetLoadState(pac_request_); | |
| 160 case STATE_INIT_CONNECTION_COMPLETE: | |
| 161 case STATE_CREATE_STREAM_COMPLETE: | |
| 162 return using_quic_ ? LOAD_STATE_CONNECTING : connection_->GetLoadState(); | |
| 163 default: | |
| 164 return LOAD_STATE_IDLE; | |
| 165 } | |
| 166 } | |
| 167 | |
| 168 void HttpStreamFactoryImpl::Job::MarkAsAlternate( | |
| 169 const GURL& original_url, | |
| 170 AlternateProtocolInfo alternate) { | |
| 171 DCHECK(!original_url_.get()); | |
| 172 original_url_.reset(new GURL(original_url)); | |
| 173 alternate_protocol_ = alternate; | |
| 174 if (alternate.protocol == QUIC) { | |
| 175 DCHECK(session_->params().enable_quic); | |
| 176 using_quic_ = true; | |
| 177 } | |
| 178 } | |
| 179 | |
| 180 void HttpStreamFactoryImpl::Job::WaitFor(Job* job) { | |
| 181 DCHECK_EQ(STATE_NONE, next_state_); | |
| 182 DCHECK_EQ(STATE_NONE, job->next_state_); | |
| 183 DCHECK(!blocking_job_); | |
| 184 DCHECK(!job->waiting_job_); | |
| 185 blocking_job_ = job; | |
| 186 job->waiting_job_ = this; | |
| 187 } | |
| 188 | |
| 189 void HttpStreamFactoryImpl::Job::Resume(Job* job) { | |
| 190 DCHECK_EQ(blocking_job_, job); | |
| 191 blocking_job_ = NULL; | |
| 192 | |
| 193 // We know we're blocked if the next_state_ is STATE_WAIT_FOR_JOB_COMPLETE. | |
| 194 // Unblock |this|. | |
| 195 if (next_state_ == STATE_WAIT_FOR_JOB_COMPLETE) { | |
| 196 base::MessageLoop::current()->PostTask( | |
| 197 FROM_HERE, | |
| 198 base::Bind(&HttpStreamFactoryImpl::Job::OnIOComplete, | |
| 199 ptr_factory_.GetWeakPtr(), OK)); | |
| 200 } | |
| 201 } | |
| 202 | |
| 203 void HttpStreamFactoryImpl::Job::Orphan(const Request* request) { | |
| 204 DCHECK_EQ(request_, request); | |
| 205 request_ = NULL; | |
| 206 if (blocking_job_) { | |
| 207 // We've been orphaned, but there's a job we're blocked on. Don't bother | |
| 208 // racing, just cancel ourself. | |
| 209 DCHECK(blocking_job_->waiting_job_); | |
| 210 blocking_job_->waiting_job_ = NULL; | |
| 211 blocking_job_ = NULL; | |
| 212 if (stream_factory_->for_websockets_ && | |
| 213 connection_ && connection_->socket()) { | |
| 214 connection_->socket()->Disconnect(); | |
| 215 } | |
| 216 stream_factory_->OnOrphanedJobComplete(this); | |
| 217 } else if (stream_factory_->for_websockets_) { | |
| 218 // We cancel this job because a WebSocketHandshakeStream can't be created | |
| 219 // without a WebSocketHandshakeStreamBase::CreateHelper which is stored in | |
| 220 // the Request class and isn't accessible from this job. | |
| 221 if (connection_ && connection_->socket()) { | |
| 222 connection_->socket()->Disconnect(); | |
| 223 } | |
| 224 stream_factory_->OnOrphanedJobComplete(this); | |
| 225 } | |
| 226 } | |
| 227 | |
| 228 void HttpStreamFactoryImpl::Job::SetPriority(RequestPriority priority) { | |
| 229 priority_ = priority; | |
| 230 // TODO(akalin): Propagate this to |connection_| and maybe the | |
| 231 // preconnect state. | |
| 232 } | |
| 233 | |
| 234 bool HttpStreamFactoryImpl::Job::was_npn_negotiated() const { | |
| 235 return was_npn_negotiated_; | |
| 236 } | |
| 237 | |
| 238 NextProto HttpStreamFactoryImpl::Job::protocol_negotiated() const { | |
| 239 return protocol_negotiated_; | |
| 240 } | |
| 241 | |
| 242 bool HttpStreamFactoryImpl::Job::using_spdy() const { | |
| 243 return using_spdy_; | |
| 244 } | |
| 245 | |
| 246 const SSLConfig& HttpStreamFactoryImpl::Job::server_ssl_config() const { | |
| 247 return server_ssl_config_; | |
| 248 } | |
| 249 | |
| 250 const SSLConfig& HttpStreamFactoryImpl::Job::proxy_ssl_config() const { | |
| 251 return proxy_ssl_config_; | |
| 252 } | |
| 253 | |
| 254 const ProxyInfo& HttpStreamFactoryImpl::Job::proxy_info() const { | |
| 255 return proxy_info_; | |
| 256 } | |
| 257 | |
| 258 void HttpStreamFactoryImpl::Job::GetSSLInfo() { | |
| 259 DCHECK(using_ssl_); | |
| 260 DCHECK(!establishing_tunnel_); | |
| 261 DCHECK(connection_.get() && connection_->socket()); | |
| 262 SSLClientSocket* ssl_socket = | |
| 263 static_cast<SSLClientSocket*>(connection_->socket()); | |
| 264 ssl_socket->GetSSLInfo(&ssl_info_); | |
| 265 } | |
| 266 | |
| 267 SpdySessionKey HttpStreamFactoryImpl::Job::GetSpdySessionKey() const { | |
| 268 // In the case that we're using an HTTPS proxy for an HTTP url, | |
| 269 // we look for a SPDY session *to* the proxy, instead of to the | |
| 270 // origin server. | |
| 271 PrivacyMode privacy_mode = request_info_.privacy_mode; | |
| 272 if (IsHttpsProxyAndHttpUrl()) { | |
| 273 return SpdySessionKey(proxy_info_.proxy_server().host_port_pair(), | |
| 274 ProxyServer::Direct(), | |
| 275 privacy_mode); | |
| 276 } else { | |
| 277 return SpdySessionKey(origin_, | |
| 278 proxy_info_.proxy_server(), | |
| 279 privacy_mode); | |
| 280 } | |
| 281 } | |
| 282 | |
| 283 bool HttpStreamFactoryImpl::Job::CanUseExistingSpdySession() const { | |
| 284 // We need to make sure that if a spdy session was created for | |
| 285 // https://somehost/ that we don't use that session for http://somehost:443/. | |
| 286 // The only time we can use an existing session is if the request URL is | |
| 287 // https (the normal case) or if we're connection to a SPDY proxy, or | |
| 288 // if we're running with force_spdy_always_. crbug.com/133176 | |
| 289 // TODO(ricea): Add "wss" back to this list when SPDY WebSocket support is | |
| 290 // working. | |
| 291 return request_info_.url.SchemeIs("https") || | |
| 292 proxy_info_.proxy_server().is_https() || | |
| 293 session_->params().force_spdy_always; | |
| 294 } | |
| 295 | |
| 296 void HttpStreamFactoryImpl::Job::OnStreamReadyCallback() { | |
| 297 DCHECK(stream_.get()); | |
| 298 DCHECK(!IsPreconnecting()); | |
| 299 DCHECK(!stream_factory_->for_websockets_); | |
| 300 if (IsOrphaned()) { | |
| 301 stream_factory_->OnOrphanedJobComplete(this); | |
| 302 } else { | |
| 303 request_->Complete(was_npn_negotiated(), | |
| 304 protocol_negotiated(), | |
| 305 using_spdy(), | |
| 306 net_log_); | |
| 307 request_->OnStreamReady(this, server_ssl_config_, proxy_info_, | |
| 308 stream_.release()); | |
| 309 } | |
| 310 // |this| may be deleted after this call. | |
| 311 } | |
| 312 | |
| 313 void HttpStreamFactoryImpl::Job::OnWebSocketHandshakeStreamReadyCallback() { | |
| 314 DCHECK(websocket_stream_); | |
| 315 DCHECK(!IsPreconnecting()); | |
| 316 DCHECK(stream_factory_->for_websockets_); | |
| 317 // An orphaned WebSocket job will be closed immediately and | |
| 318 // never be ready. | |
| 319 DCHECK(!IsOrphaned()); | |
| 320 request_->Complete(was_npn_negotiated(), | |
| 321 protocol_negotiated(), | |
| 322 using_spdy(), | |
| 323 net_log_); | |
| 324 request_->OnWebSocketHandshakeStreamReady(this, | |
| 325 server_ssl_config_, | |
| 326 proxy_info_, | |
| 327 websocket_stream_.release()); | |
| 328 // |this| may be deleted after this call. | |
| 329 } | |
| 330 | |
| 331 void HttpStreamFactoryImpl::Job::OnNewSpdySessionReadyCallback() { | |
| 332 DCHECK(stream_.get()); | |
| 333 DCHECK(!IsPreconnecting()); | |
| 334 DCHECK(using_spdy()); | |
| 335 // Note: an event loop iteration has passed, so |new_spdy_session_| may be | |
| 336 // NULL at this point if the SpdySession closed immediately after creation. | |
| 337 base::WeakPtr<SpdySession> spdy_session = new_spdy_session_; | |
| 338 new_spdy_session_.reset(); | |
| 339 | |
| 340 // TODO(jgraettinger): Notify the factory, and let that notify |request_|, | |
| 341 // rather than notifying |request_| directly. | |
| 342 if (IsOrphaned()) { | |
| 343 if (spdy_session) { | |
| 344 stream_factory_->OnNewSpdySessionReady( | |
| 345 spdy_session, spdy_session_direct_, server_ssl_config_, proxy_info_, | |
| 346 was_npn_negotiated(), protocol_negotiated(), using_spdy(), net_log_); | |
| 347 } | |
| 348 stream_factory_->OnOrphanedJobComplete(this); | |
| 349 } else { | |
| 350 request_->OnNewSpdySessionReady( | |
| 351 this, stream_.Pass(), spdy_session, spdy_session_direct_); | |
| 352 } | |
| 353 // |this| may be deleted after this call. | |
| 354 } | |
| 355 | |
| 356 void HttpStreamFactoryImpl::Job::OnStreamFailedCallback(int result) { | |
| 357 DCHECK(!IsPreconnecting()); | |
| 358 if (IsOrphaned()) | |
| 359 stream_factory_->OnOrphanedJobComplete(this); | |
| 360 else | |
| 361 request_->OnStreamFailed(this, result, server_ssl_config_); | |
| 362 // |this| may be deleted after this call. | |
| 363 } | |
| 364 | |
| 365 void HttpStreamFactoryImpl::Job::OnCertificateErrorCallback( | |
| 366 int result, const SSLInfo& ssl_info) { | |
| 367 DCHECK(!IsPreconnecting()); | |
| 368 if (IsOrphaned()) | |
| 369 stream_factory_->OnOrphanedJobComplete(this); | |
| 370 else | |
| 371 request_->OnCertificateError(this, result, server_ssl_config_, ssl_info); | |
| 372 // |this| may be deleted after this call. | |
| 373 } | |
| 374 | |
| 375 void HttpStreamFactoryImpl::Job::OnNeedsProxyAuthCallback( | |
| 376 const HttpResponseInfo& response, | |
| 377 HttpAuthController* auth_controller) { | |
| 378 DCHECK(!IsPreconnecting()); | |
| 379 if (IsOrphaned()) | |
| 380 stream_factory_->OnOrphanedJobComplete(this); | |
| 381 else | |
| 382 request_->OnNeedsProxyAuth( | |
| 383 this, response, server_ssl_config_, proxy_info_, auth_controller); | |
| 384 // |this| may be deleted after this call. | |
| 385 } | |
| 386 | |
| 387 void HttpStreamFactoryImpl::Job::OnNeedsClientAuthCallback( | |
| 388 SSLCertRequestInfo* cert_info) { | |
| 389 DCHECK(!IsPreconnecting()); | |
| 390 if (IsOrphaned()) | |
| 391 stream_factory_->OnOrphanedJobComplete(this); | |
| 392 else | |
| 393 request_->OnNeedsClientAuth(this, server_ssl_config_, cert_info); | |
| 394 // |this| may be deleted after this call. | |
| 395 } | |
| 396 | |
| 397 void HttpStreamFactoryImpl::Job::OnHttpsProxyTunnelResponseCallback( | |
| 398 const HttpResponseInfo& response_info, | |
| 399 HttpStream* stream) { | |
| 400 DCHECK(!IsPreconnecting()); | |
| 401 if (IsOrphaned()) | |
| 402 stream_factory_->OnOrphanedJobComplete(this); | |
| 403 else | |
| 404 request_->OnHttpsProxyTunnelResponse( | |
| 405 this, response_info, server_ssl_config_, proxy_info_, stream); | |
| 406 // |this| may be deleted after this call. | |
| 407 } | |
| 408 | |
| 409 void HttpStreamFactoryImpl::Job::OnPreconnectsComplete() { | |
| 410 DCHECK(!request_); | |
| 411 if (new_spdy_session_.get()) { | |
| 412 stream_factory_->OnNewSpdySessionReady(new_spdy_session_, | |
| 413 spdy_session_direct_, | |
| 414 server_ssl_config_, | |
| 415 proxy_info_, | |
| 416 was_npn_negotiated(), | |
| 417 protocol_negotiated(), | |
| 418 using_spdy(), | |
| 419 net_log_); | |
| 420 } | |
| 421 stream_factory_->OnPreconnectsComplete(this); | |
| 422 // |this| may be deleted after this call. | |
| 423 } | |
| 424 | |
| 425 // static | |
| 426 int HttpStreamFactoryImpl::Job::OnHostResolution( | |
| 427 SpdySessionPool* spdy_session_pool, | |
| 428 const SpdySessionKey& spdy_session_key, | |
| 429 const AddressList& addresses, | |
| 430 const BoundNetLog& net_log) { | |
| 431 // It is OK to dereference spdy_session_pool, because the | |
| 432 // ClientSocketPoolManager will be destroyed in the same callback that | |
| 433 // destroys the SpdySessionPool. | |
| 434 return | |
| 435 spdy_session_pool->FindAvailableSession(spdy_session_key, net_log) ? | |
| 436 ERR_SPDY_SESSION_ALREADY_EXISTS : OK; | |
| 437 } | |
| 438 | |
| 439 void HttpStreamFactoryImpl::Job::OnIOComplete(int result) { | |
| 440 // TODO(pkasting): Remove ScopedTracker below once crbug.com/455884 is fixed. | |
| 441 tracked_objects::ScopedTracker tracking_profile( | |
| 442 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 443 "455884 HttpStreamFactoryImpl::Job::OnIOComplete")); | |
| 444 RunLoop(result); | |
| 445 } | |
| 446 | |
| 447 int HttpStreamFactoryImpl::Job::RunLoop(int result) { | |
| 448 result = DoLoop(result); | |
| 449 | |
| 450 if (result == ERR_IO_PENDING) | |
| 451 return result; | |
| 452 | |
| 453 // If there was an error, we should have already resumed the |waiting_job_|, | |
| 454 // if there was one. | |
| 455 DCHECK(result == OK || waiting_job_ == NULL); | |
| 456 | |
| 457 if (IsPreconnecting()) { | |
| 458 base::MessageLoop::current()->PostTask( | |
| 459 FROM_HERE, | |
| 460 base::Bind(&HttpStreamFactoryImpl::Job::OnPreconnectsComplete, | |
| 461 ptr_factory_.GetWeakPtr())); | |
| 462 return ERR_IO_PENDING; | |
| 463 } | |
| 464 | |
| 465 if (IsCertificateError(result)) { | |
| 466 // Retrieve SSL information from the socket. | |
| 467 GetSSLInfo(); | |
| 468 | |
| 469 next_state_ = STATE_WAITING_USER_ACTION; | |
| 470 base::MessageLoop::current()->PostTask( | |
| 471 FROM_HERE, | |
| 472 base::Bind(&HttpStreamFactoryImpl::Job::OnCertificateErrorCallback, | |
| 473 ptr_factory_.GetWeakPtr(), result, ssl_info_)); | |
| 474 return ERR_IO_PENDING; | |
| 475 } | |
| 476 | |
| 477 switch (result) { | |
| 478 case ERR_PROXY_AUTH_REQUESTED: { | |
| 479 UMA_HISTOGRAM_BOOLEAN("Net.ProxyAuthRequested.HasConnection", | |
| 480 connection_.get() != NULL); | |
| 481 if (!connection_.get()) | |
| 482 return ERR_PROXY_AUTH_REQUESTED_WITH_NO_CONNECTION; | |
| 483 CHECK(connection_->socket()); | |
| 484 CHECK(establishing_tunnel_); | |
| 485 | |
| 486 next_state_ = STATE_WAITING_USER_ACTION; | |
| 487 ProxyClientSocket* proxy_socket = | |
| 488 static_cast<ProxyClientSocket*>(connection_->socket()); | |
| 489 base::MessageLoop::current()->PostTask( | |
| 490 FROM_HERE, | |
| 491 base::Bind(&Job::OnNeedsProxyAuthCallback, ptr_factory_.GetWeakPtr(), | |
| 492 *proxy_socket->GetConnectResponseInfo(), | |
| 493 proxy_socket->GetAuthController())); | |
| 494 return ERR_IO_PENDING; | |
| 495 } | |
| 496 | |
| 497 case ERR_SSL_CLIENT_AUTH_CERT_NEEDED: | |
| 498 base::MessageLoop::current()->PostTask( | |
| 499 FROM_HERE, | |
| 500 base::Bind(&Job::OnNeedsClientAuthCallback, ptr_factory_.GetWeakPtr(), | |
| 501 connection_->ssl_error_response_info().cert_request_info)); | |
| 502 return ERR_IO_PENDING; | |
| 503 | |
| 504 case ERR_HTTPS_PROXY_TUNNEL_RESPONSE: { | |
| 505 DCHECK(connection_.get()); | |
| 506 DCHECK(connection_->socket()); | |
| 507 DCHECK(establishing_tunnel_); | |
| 508 | |
| 509 ProxyClientSocket* proxy_socket = | |
| 510 static_cast<ProxyClientSocket*>(connection_->socket()); | |
| 511 base::MessageLoop::current()->PostTask( | |
| 512 FROM_HERE, | |
| 513 base::Bind(&Job::OnHttpsProxyTunnelResponseCallback, | |
| 514 ptr_factory_.GetWeakPtr(), | |
| 515 *proxy_socket->GetConnectResponseInfo(), | |
| 516 proxy_socket->CreateConnectResponseStream())); | |
| 517 return ERR_IO_PENDING; | |
| 518 } | |
| 519 | |
| 520 case OK: | |
| 521 job_status_ = STATUS_SUCCEEDED; | |
| 522 MaybeMarkAlternateProtocolBroken(); | |
| 523 next_state_ = STATE_DONE; | |
| 524 if (new_spdy_session_.get()) { | |
| 525 base::MessageLoop::current()->PostTask( | |
| 526 FROM_HERE, | |
| 527 base::Bind(&Job::OnNewSpdySessionReadyCallback, | |
| 528 ptr_factory_.GetWeakPtr())); | |
| 529 } else if (stream_factory_->for_websockets_) { | |
| 530 DCHECK(websocket_stream_); | |
| 531 base::MessageLoop::current()->PostTask( | |
| 532 FROM_HERE, | |
| 533 base::Bind(&Job::OnWebSocketHandshakeStreamReadyCallback, | |
| 534 ptr_factory_.GetWeakPtr())); | |
| 535 } else { | |
| 536 DCHECK(stream_.get()); | |
| 537 base::MessageLoop::current()->PostTask( | |
| 538 FROM_HERE, | |
| 539 base::Bind(&Job::OnStreamReadyCallback, ptr_factory_.GetWeakPtr())); | |
| 540 } | |
| 541 return ERR_IO_PENDING; | |
| 542 | |
| 543 default: | |
| 544 if (job_status_ != STATUS_BROKEN) { | |
| 545 DCHECK_EQ(STATUS_RUNNING, job_status_); | |
| 546 job_status_ = STATUS_FAILED; | |
| 547 MaybeMarkAlternateProtocolBroken(); | |
| 548 } | |
| 549 base::MessageLoop::current()->PostTask( | |
| 550 FROM_HERE, | |
| 551 base::Bind(&Job::OnStreamFailedCallback, ptr_factory_.GetWeakPtr(), | |
| 552 result)); | |
| 553 return ERR_IO_PENDING; | |
| 554 } | |
| 555 } | |
| 556 | |
| 557 int HttpStreamFactoryImpl::Job::DoLoop(int result) { | |
| 558 DCHECK_NE(next_state_, STATE_NONE); | |
| 559 int rv = result; | |
| 560 do { | |
| 561 State state = next_state_; | |
| 562 next_state_ = STATE_NONE; | |
| 563 switch (state) { | |
| 564 case STATE_START: | |
| 565 DCHECK_EQ(OK, rv); | |
| 566 rv = DoStart(); | |
| 567 break; | |
| 568 case STATE_RESOLVE_PROXY: | |
| 569 DCHECK_EQ(OK, rv); | |
| 570 rv = DoResolveProxy(); | |
| 571 break; | |
| 572 case STATE_RESOLVE_PROXY_COMPLETE: | |
| 573 rv = DoResolveProxyComplete(rv); | |
| 574 break; | |
| 575 case STATE_WAIT_FOR_JOB: | |
| 576 DCHECK_EQ(OK, rv); | |
| 577 rv = DoWaitForJob(); | |
| 578 break; | |
| 579 case STATE_WAIT_FOR_JOB_COMPLETE: | |
| 580 rv = DoWaitForJobComplete(rv); | |
| 581 break; | |
| 582 case STATE_INIT_CONNECTION: | |
| 583 DCHECK_EQ(OK, rv); | |
| 584 rv = DoInitConnection(); | |
| 585 break; | |
| 586 case STATE_INIT_CONNECTION_COMPLETE: | |
| 587 rv = DoInitConnectionComplete(rv); | |
| 588 break; | |
| 589 case STATE_WAITING_USER_ACTION: | |
| 590 rv = DoWaitingUserAction(rv); | |
| 591 break; | |
| 592 case STATE_RESTART_TUNNEL_AUTH: | |
| 593 DCHECK_EQ(OK, rv); | |
| 594 rv = DoRestartTunnelAuth(); | |
| 595 break; | |
| 596 case STATE_RESTART_TUNNEL_AUTH_COMPLETE: | |
| 597 rv = DoRestartTunnelAuthComplete(rv); | |
| 598 break; | |
| 599 case STATE_CREATE_STREAM: | |
| 600 DCHECK_EQ(OK, rv); | |
| 601 rv = DoCreateStream(); | |
| 602 break; | |
| 603 case STATE_CREATE_STREAM_COMPLETE: | |
| 604 rv = DoCreateStreamComplete(rv); | |
| 605 break; | |
| 606 default: | |
| 607 NOTREACHED() << "bad state"; | |
| 608 rv = ERR_FAILED; | |
| 609 break; | |
| 610 } | |
| 611 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); | |
| 612 return rv; | |
| 613 } | |
| 614 | |
| 615 int HttpStreamFactoryImpl::Job::StartInternal() { | |
| 616 CHECK_EQ(STATE_NONE, next_state_); | |
| 617 next_state_ = STATE_START; | |
| 618 int rv = RunLoop(OK); | |
| 619 DCHECK_EQ(ERR_IO_PENDING, rv); | |
| 620 return rv; | |
| 621 } | |
| 622 | |
| 623 int HttpStreamFactoryImpl::Job::DoStart() { | |
| 624 origin_ = HostPortPair::FromURL(request_info_.url); | |
| 625 origin_url_ = stream_factory_->ApplyHostMappingRules( | |
| 626 request_info_.url, &origin_); | |
| 627 | |
| 628 net_log_.BeginEvent(NetLog::TYPE_HTTP_STREAM_JOB, | |
| 629 base::Bind(&NetLogHttpStreamJobCallback, | |
| 630 &request_info_.url, &origin_url_, | |
| 631 priority_)); | |
| 632 | |
| 633 // Don't connect to restricted ports. | |
| 634 bool is_port_allowed = IsPortAllowedByDefault(origin_.port()); | |
| 635 if (request_info_.url.SchemeIs("ftp")) { | |
| 636 // Never share connection with other jobs for FTP requests. | |
| 637 DCHECK(!waiting_job_); | |
| 638 | |
| 639 is_port_allowed = IsPortAllowedByFtp(origin_.port()); | |
| 640 } | |
| 641 if (!is_port_allowed && !IsPortAllowedByOverride(origin_.port())) { | |
| 642 if (waiting_job_) { | |
| 643 waiting_job_->Resume(this); | |
| 644 waiting_job_ = NULL; | |
| 645 } | |
| 646 return ERR_UNSAFE_PORT; | |
| 647 } | |
| 648 | |
| 649 next_state_ = STATE_RESOLVE_PROXY; | |
| 650 return OK; | |
| 651 } | |
| 652 | |
| 653 int HttpStreamFactoryImpl::Job::DoResolveProxy() { | |
| 654 DCHECK(!pac_request_); | |
| 655 DCHECK(session_); | |
| 656 | |
| 657 next_state_ = STATE_RESOLVE_PROXY_COMPLETE; | |
| 658 | |
| 659 if (request_info_.load_flags & LOAD_BYPASS_PROXY) { | |
| 660 proxy_info_.UseDirect(); | |
| 661 return OK; | |
| 662 } | |
| 663 | |
| 664 return session_->proxy_service()->ResolveProxy( | |
| 665 request_info_.url, request_info_.load_flags, &proxy_info_, io_callback_, | |
| 666 &pac_request_, session_->network_delegate(), net_log_); | |
| 667 } | |
| 668 | |
| 669 int HttpStreamFactoryImpl::Job::DoResolveProxyComplete(int result) { | |
| 670 pac_request_ = NULL; | |
| 671 | |
| 672 if (result == OK) { | |
| 673 // Remove unsupported proxies from the list. | |
| 674 proxy_info_.RemoveProxiesWithoutScheme( | |
| 675 ProxyServer::SCHEME_DIRECT | ProxyServer::SCHEME_QUIC | | |
| 676 ProxyServer::SCHEME_HTTP | ProxyServer::SCHEME_HTTPS | | |
| 677 ProxyServer::SCHEME_SOCKS4 | ProxyServer::SCHEME_SOCKS5); | |
| 678 | |
| 679 if (proxy_info_.is_empty()) { | |
| 680 // No proxies/direct to choose from. This happens when we don't support | |
| 681 // any of the proxies in the returned list. | |
| 682 result = ERR_NO_SUPPORTED_PROXIES; | |
| 683 } else if (using_quic_ && | |
| 684 (!proxy_info_.is_quic() && !proxy_info_.is_direct())) { | |
| 685 // QUIC can not be spoken to non-QUIC proxies. This error should not be | |
| 686 // user visible, because the non-alternate job should be resumed. | |
| 687 result = ERR_NO_SUPPORTED_PROXIES; | |
| 688 } | |
| 689 } | |
| 690 | |
| 691 if (result != OK) { | |
| 692 if (waiting_job_) { | |
| 693 waiting_job_->Resume(this); | |
| 694 waiting_job_ = NULL; | |
| 695 } | |
| 696 return result; | |
| 697 } | |
| 698 | |
| 699 if (blocking_job_) | |
| 700 next_state_ = STATE_WAIT_FOR_JOB; | |
| 701 else | |
| 702 next_state_ = STATE_INIT_CONNECTION; | |
| 703 return OK; | |
| 704 } | |
| 705 | |
| 706 bool HttpStreamFactoryImpl::Job::ShouldForceSpdySSL() const { | |
| 707 bool rv = session_->params().force_spdy_always && | |
| 708 session_->params().force_spdy_over_ssl; | |
| 709 return rv && !session_->HasSpdyExclusion(origin_); | |
| 710 } | |
| 711 | |
| 712 bool HttpStreamFactoryImpl::Job::ShouldForceSpdyWithoutSSL() const { | |
| 713 bool rv = session_->params().force_spdy_always && | |
| 714 !session_->params().force_spdy_over_ssl; | |
| 715 return rv && !session_->HasSpdyExclusion(origin_); | |
| 716 } | |
| 717 | |
| 718 bool HttpStreamFactoryImpl::Job::ShouldForceQuic() const { | |
| 719 return session_->params().enable_quic && | |
| 720 session_->params().origin_to_force_quic_on.Equals(origin_) && | |
| 721 proxy_info_.is_direct(); | |
| 722 } | |
| 723 | |
| 724 int HttpStreamFactoryImpl::Job::DoWaitForJob() { | |
| 725 DCHECK(blocking_job_); | |
| 726 next_state_ = STATE_WAIT_FOR_JOB_COMPLETE; | |
| 727 return ERR_IO_PENDING; | |
| 728 } | |
| 729 | |
| 730 int HttpStreamFactoryImpl::Job::DoWaitForJobComplete(int result) { | |
| 731 DCHECK(!blocking_job_); | |
| 732 DCHECK_EQ(OK, result); | |
| 733 next_state_ = STATE_INIT_CONNECTION; | |
| 734 return OK; | |
| 735 } | |
| 736 | |
| 737 int HttpStreamFactoryImpl::Job::DoInitConnection() { | |
| 738 DCHECK(!blocking_job_); | |
| 739 DCHECK(!connection_->is_initialized()); | |
| 740 DCHECK(proxy_info_.proxy_server().is_valid()); | |
| 741 next_state_ = STATE_INIT_CONNECTION_COMPLETE; | |
| 742 | |
| 743 using_ssl_ = request_info_.url.SchemeIs("https") || | |
| 744 request_info_.url.SchemeIs("wss") || ShouldForceSpdySSL(); | |
| 745 using_spdy_ = false; | |
| 746 | |
| 747 if (ShouldForceQuic()) | |
| 748 using_quic_ = true; | |
| 749 | |
| 750 if (proxy_info_.is_quic()) | |
| 751 using_quic_ = true; | |
| 752 | |
| 753 if (using_quic_) { | |
| 754 DCHECK(session_->params().enable_quic); | |
| 755 if (proxy_info_.is_quic() && !request_info_.url.SchemeIs("http")) { | |
| 756 NOTREACHED(); | |
| 757 // TODO(rch): support QUIC proxies for HTTPS urls. | |
| 758 return ERR_NOT_IMPLEMENTED; | |
| 759 } | |
| 760 HostPortPair destination = proxy_info_.is_quic() ? | |
| 761 proxy_info_.proxy_server().host_port_pair() : origin_; | |
| 762 next_state_ = STATE_INIT_CONNECTION_COMPLETE; | |
| 763 bool secure_quic = using_ssl_ || proxy_info_.is_quic(); | |
| 764 int rv = quic_request_.Request( | |
| 765 destination, secure_quic, request_info_.privacy_mode, | |
| 766 request_info_.method, net_log_, io_callback_); | |
| 767 if (rv == OK) { | |
| 768 using_existing_quic_session_ = true; | |
| 769 } else { | |
| 770 // OK, there's no available QUIC session. Let |waiting_job_| resume | |
| 771 // if it's paused. | |
| 772 if (waiting_job_) { | |
| 773 waiting_job_->Resume(this); | |
| 774 waiting_job_ = NULL; | |
| 775 } | |
| 776 } | |
| 777 return rv; | |
| 778 } | |
| 779 | |
| 780 // Check first if we have a spdy session for this group. If so, then go | |
| 781 // straight to using that. | |
| 782 SpdySessionKey spdy_session_key = GetSpdySessionKey(); | |
| 783 base::WeakPtr<SpdySession> spdy_session = | |
| 784 session_->spdy_session_pool()->FindAvailableSession( | |
| 785 spdy_session_key, net_log_); | |
| 786 if (spdy_session && CanUseExistingSpdySession()) { | |
| 787 // If we're preconnecting, but we already have a SpdySession, we don't | |
| 788 // actually need to preconnect any sockets, so we're done. | |
| 789 if (IsPreconnecting()) | |
| 790 return OK; | |
| 791 using_spdy_ = true; | |
| 792 next_state_ = STATE_CREATE_STREAM; | |
| 793 existing_spdy_session_ = spdy_session; | |
| 794 return OK; | |
| 795 } else if (request_ && !request_->HasSpdySessionKey() && | |
| 796 (using_ssl_ || ShouldForceSpdyWithoutSSL())) { | |
| 797 // Update the spdy session key for the request that launched this job. | |
| 798 request_->SetSpdySessionKey(spdy_session_key); | |
| 799 } | |
| 800 | |
| 801 // OK, there's no available SPDY session. Let |waiting_job_| resume if it's | |
| 802 // paused. | |
| 803 | |
| 804 if (waiting_job_) { | |
| 805 waiting_job_->Resume(this); | |
| 806 waiting_job_ = NULL; | |
| 807 } | |
| 808 | |
| 809 if (proxy_info_.is_http() || proxy_info_.is_https()) | |
| 810 establishing_tunnel_ = using_ssl_; | |
| 811 | |
| 812 bool want_spdy_over_npn = original_url_ != NULL; | |
| 813 | |
| 814 if (proxy_info_.is_https()) { | |
| 815 InitSSLConfig(proxy_info_.proxy_server().host_port_pair(), | |
| 816 &proxy_ssl_config_, | |
| 817 true /* is a proxy server */); | |
| 818 // Disable revocation checking for HTTPS proxies since the revocation | |
| 819 // requests are probably going to need to go through the proxy too. | |
| 820 proxy_ssl_config_.rev_checking_enabled = false; | |
| 821 } | |
| 822 if (using_ssl_) { | |
| 823 InitSSLConfig(origin_, &server_ssl_config_, | |
| 824 false /* not a proxy server */); | |
| 825 } | |
| 826 | |
| 827 base::WeakPtr<HttpServerProperties> http_server_properties = | |
| 828 session_->http_server_properties(); | |
| 829 if (http_server_properties) { | |
| 830 http_server_properties->MaybeForceHTTP11(origin_, &server_ssl_config_); | |
| 831 if (proxy_info_.is_http() || proxy_info_.is_https()) { | |
| 832 http_server_properties->MaybeForceHTTP11( | |
| 833 proxy_info_.proxy_server().host_port_pair(), &proxy_ssl_config_); | |
| 834 } | |
| 835 } | |
| 836 | |
| 837 if (IsPreconnecting()) { | |
| 838 DCHECK(!stream_factory_->for_websockets_); | |
| 839 return PreconnectSocketsForHttpRequest( | |
| 840 origin_url_, | |
| 841 request_info_.extra_headers, | |
| 842 request_info_.load_flags, | |
| 843 priority_, | |
| 844 session_, | |
| 845 proxy_info_, | |
| 846 ShouldForceSpdySSL(), | |
| 847 want_spdy_over_npn, | |
| 848 server_ssl_config_, | |
| 849 proxy_ssl_config_, | |
| 850 request_info_.privacy_mode, | |
| 851 net_log_, | |
| 852 num_streams_); | |
| 853 } | |
| 854 | |
| 855 // If we can't use a SPDY session, don't both checking for one after | |
| 856 // the hostname is resolved. | |
| 857 OnHostResolutionCallback resolution_callback = CanUseExistingSpdySession() ? | |
| 858 base::Bind(&Job::OnHostResolution, session_->spdy_session_pool(), | |
| 859 GetSpdySessionKey()) : | |
| 860 OnHostResolutionCallback(); | |
| 861 if (stream_factory_->for_websockets_) { | |
| 862 // TODO(ricea): Re-enable NPN when WebSockets over SPDY is supported. | |
| 863 SSLConfig websocket_server_ssl_config = server_ssl_config_; | |
| 864 websocket_server_ssl_config.next_protos.clear(); | |
| 865 return InitSocketHandleForWebSocketRequest( | |
| 866 origin_url_, request_info_.extra_headers, request_info_.load_flags, | |
| 867 priority_, session_, proxy_info_, ShouldForceSpdySSL(), | |
| 868 want_spdy_over_npn, websocket_server_ssl_config, proxy_ssl_config_, | |
| 869 request_info_.privacy_mode, net_log_, | |
| 870 connection_.get(), resolution_callback, io_callback_); | |
| 871 } | |
| 872 | |
| 873 return InitSocketHandleForHttpRequest( | |
| 874 origin_url_, request_info_.extra_headers, request_info_.load_flags, | |
| 875 priority_, session_, proxy_info_, ShouldForceSpdySSL(), | |
| 876 want_spdy_over_npn, server_ssl_config_, proxy_ssl_config_, | |
| 877 request_info_.privacy_mode, net_log_, | |
| 878 connection_.get(), resolution_callback, io_callback_); | |
| 879 } | |
| 880 | |
| 881 int HttpStreamFactoryImpl::Job::DoInitConnectionComplete(int result) { | |
| 882 if (IsPreconnecting()) { | |
| 883 if (using_quic_) | |
| 884 return result; | |
| 885 DCHECK_EQ(OK, result); | |
| 886 return OK; | |
| 887 } | |
| 888 | |
| 889 if (result == ERR_SPDY_SESSION_ALREADY_EXISTS) { | |
| 890 // We found a SPDY connection after resolving the host. This is | |
| 891 // probably an IP pooled connection. | |
| 892 SpdySessionKey spdy_session_key = GetSpdySessionKey(); | |
| 893 existing_spdy_session_ = | |
| 894 session_->spdy_session_pool()->FindAvailableSession( | |
| 895 spdy_session_key, net_log_); | |
| 896 if (existing_spdy_session_) { | |
| 897 using_spdy_ = true; | |
| 898 next_state_ = STATE_CREATE_STREAM; | |
| 899 } else { | |
| 900 // It is possible that the spdy session no longer exists. | |
| 901 ReturnToStateInitConnection(true /* close connection */); | |
| 902 } | |
| 903 return OK; | |
| 904 } | |
| 905 | |
| 906 // TODO(willchan): Make this a bit more exact. Maybe there are recoverable | |
| 907 // errors, such as ignoring certificate errors for Alternate-Protocol. | |
| 908 if (result < 0 && waiting_job_) { | |
| 909 waiting_job_->Resume(this); | |
| 910 waiting_job_ = NULL; | |
| 911 } | |
| 912 | |
| 913 // |result| may be the result of any of the stacked pools. The following | |
| 914 // logic is used when determining how to interpret an error. | |
| 915 // If |result| < 0: | |
| 916 // and connection_->socket() != NULL, then the SSL handshake ran and it | |
| 917 // is a potentially recoverable error. | |
| 918 // and connection_->socket == NULL and connection_->is_ssl_error() is true, | |
| 919 // then the SSL handshake ran with an unrecoverable error. | |
| 920 // otherwise, the error came from one of the other pools. | |
| 921 bool ssl_started = using_ssl_ && (result == OK || connection_->socket() || | |
| 922 connection_->is_ssl_error()); | |
| 923 | |
| 924 if (ssl_started && (result == OK || IsCertificateError(result))) { | |
| 925 if (using_quic_ && result == OK) { | |
| 926 was_npn_negotiated_ = true; | |
| 927 NextProto protocol_negotiated = | |
| 928 SSLClientSocket::NextProtoFromString("quic/1+spdy/3"); | |
| 929 protocol_negotiated_ = protocol_negotiated; | |
| 930 } else { | |
| 931 SSLClientSocket* ssl_socket = | |
| 932 static_cast<SSLClientSocket*>(connection_->socket()); | |
| 933 if (ssl_socket->WasNpnNegotiated()) { | |
| 934 was_npn_negotiated_ = true; | |
| 935 std::string proto; | |
| 936 SSLClientSocket::NextProtoStatus status = | |
| 937 ssl_socket->GetNextProto(&proto); | |
| 938 NextProto protocol_negotiated = | |
| 939 SSLClientSocket::NextProtoFromString(proto); | |
| 940 protocol_negotiated_ = protocol_negotiated; | |
| 941 net_log_.AddEvent( | |
| 942 NetLog::TYPE_HTTP_STREAM_REQUEST_PROTO, | |
| 943 base::Bind(&NetLogHttpStreamProtoCallback, | |
| 944 status, &proto)); | |
| 945 if (ssl_socket->was_spdy_negotiated()) | |
| 946 SwitchToSpdyMode(); | |
| 947 } | |
| 948 if (ShouldForceSpdySSL()) | |
| 949 SwitchToSpdyMode(); | |
| 950 } | |
| 951 } else if (proxy_info_.is_https() && connection_->socket() && | |
| 952 result == OK) { | |
| 953 ProxyClientSocket* proxy_socket = | |
| 954 static_cast<ProxyClientSocket*>(connection_->socket()); | |
| 955 if (proxy_socket->IsUsingSpdy()) { | |
| 956 was_npn_negotiated_ = true; | |
| 957 protocol_negotiated_ = proxy_socket->GetProtocolNegotiated(); | |
| 958 SwitchToSpdyMode(); | |
| 959 } | |
| 960 } | |
| 961 | |
| 962 // We may be using spdy without SSL | |
| 963 if (ShouldForceSpdyWithoutSSL()) | |
| 964 SwitchToSpdyMode(); | |
| 965 | |
| 966 if (result == ERR_PROXY_AUTH_REQUESTED || | |
| 967 result == ERR_HTTPS_PROXY_TUNNEL_RESPONSE) { | |
| 968 DCHECK(!ssl_started); | |
| 969 // Other state (i.e. |using_ssl_|) suggests that |connection_| will have an | |
| 970 // SSL socket, but there was an error before that could happen. This | |
| 971 // puts the in progress HttpProxy socket into |connection_| in order to | |
| 972 // complete the auth (or read the response body). The tunnel restart code | |
| 973 // is careful to remove it before returning control to the rest of this | |
| 974 // class. | |
| 975 connection_.reset(connection_->release_pending_http_proxy_connection()); | |
| 976 return result; | |
| 977 } | |
| 978 | |
| 979 if (!ssl_started && result < 0 && original_url_.get()) { | |
| 980 job_status_ = STATUS_BROKEN; | |
| 981 MaybeMarkAlternateProtocolBroken(); | |
| 982 return result; | |
| 983 } | |
| 984 | |
| 985 if (using_quic_) { | |
| 986 if (result < 0) { | |
| 987 job_status_ = STATUS_BROKEN; | |
| 988 MaybeMarkAlternateProtocolBroken(); | |
| 989 return result; | |
| 990 } | |
| 991 stream_ = quic_request_.ReleaseStream(); | |
| 992 next_state_ = STATE_NONE; | |
| 993 return OK; | |
| 994 } | |
| 995 | |
| 996 if (result < 0 && !ssl_started) | |
| 997 return ReconsiderProxyAfterError(result); | |
| 998 establishing_tunnel_ = false; | |
| 999 | |
| 1000 if (connection_->socket()) { | |
| 1001 LogHttpConnectedMetrics(*connection_); | |
| 1002 | |
| 1003 // We officially have a new connection. Record the type. | |
| 1004 if (!connection_->is_reused()) { | |
| 1005 ConnectionType type = using_spdy_ ? CONNECTION_SPDY : CONNECTION_HTTP; | |
| 1006 UpdateConnectionTypeHistograms(type); | |
| 1007 } | |
| 1008 } | |
| 1009 | |
| 1010 // Handle SSL errors below. | |
| 1011 if (using_ssl_) { | |
| 1012 DCHECK(ssl_started); | |
| 1013 if (IsCertificateError(result)) { | |
| 1014 if (using_spdy_ && original_url_.get() && | |
| 1015 original_url_->SchemeIs("http")) { | |
| 1016 // We ignore certificate errors for http over spdy. | |
| 1017 spdy_certificate_error_ = result; | |
| 1018 result = OK; | |
| 1019 } else { | |
| 1020 result = HandleCertificateError(result); | |
| 1021 if (result == OK && !connection_->socket()->IsConnectedAndIdle()) { | |
| 1022 ReturnToStateInitConnection(true /* close connection */); | |
| 1023 return result; | |
| 1024 } | |
| 1025 } | |
| 1026 } | |
| 1027 if (result < 0) | |
| 1028 return result; | |
| 1029 } | |
| 1030 | |
| 1031 next_state_ = STATE_CREATE_STREAM; | |
| 1032 return OK; | |
| 1033 } | |
| 1034 | |
| 1035 int HttpStreamFactoryImpl::Job::DoWaitingUserAction(int result) { | |
| 1036 // This state indicates that the stream request is in a partially | |
| 1037 // completed state, and we've called back to the delegate for more | |
| 1038 // information. | |
| 1039 | |
| 1040 // We're always waiting here for the delegate to call us back. | |
| 1041 return ERR_IO_PENDING; | |
| 1042 } | |
| 1043 | |
| 1044 int HttpStreamFactoryImpl::Job::SetSpdyHttpStream( | |
| 1045 base::WeakPtr<SpdySession> session, bool direct) { | |
| 1046 // TODO(ricea): Restore the code for WebSockets over SPDY once it's | |
| 1047 // implemented. | |
| 1048 if (stream_factory_->for_websockets_) | |
| 1049 return ERR_NOT_IMPLEMENTED; | |
| 1050 | |
| 1051 // TODO(willchan): Delete this code, because eventually, the | |
| 1052 // HttpStreamFactoryImpl will be creating all the SpdyHttpStreams, since it | |
| 1053 // will know when SpdySessions become available. | |
| 1054 | |
| 1055 bool use_relative_url = direct || request_info_.url.SchemeIs("https"); | |
| 1056 stream_.reset(new SpdyHttpStream(session, use_relative_url)); | |
| 1057 return OK; | |
| 1058 } | |
| 1059 | |
| 1060 int HttpStreamFactoryImpl::Job::DoCreateStream() { | |
| 1061 DCHECK(connection_->socket() || existing_spdy_session_.get() || using_quic_); | |
| 1062 | |
| 1063 next_state_ = STATE_CREATE_STREAM_COMPLETE; | |
| 1064 | |
| 1065 // We only set the socket motivation if we're the first to use | |
| 1066 // this socket. Is there a race for two SPDY requests? We really | |
| 1067 // need to plumb this through to the connect level. | |
| 1068 if (connection_->socket() && !connection_->is_reused()) | |
| 1069 SetSocketMotivation(); | |
| 1070 | |
| 1071 if (!using_spdy_) { | |
| 1072 // We may get ftp scheme when fetching ftp resources through proxy. | |
| 1073 bool using_proxy = (proxy_info_.is_http() || proxy_info_.is_https()) && | |
| 1074 (request_info_.url.SchemeIs("http") || | |
| 1075 request_info_.url.SchemeIs("ftp")); | |
| 1076 if (stream_factory_->for_websockets_) { | |
| 1077 DCHECK(request_); | |
| 1078 DCHECK(request_->websocket_handshake_stream_create_helper()); | |
| 1079 websocket_stream_.reset( | |
| 1080 request_->websocket_handshake_stream_create_helper() | |
| 1081 ->CreateBasicStream(connection_.Pass(), using_proxy)); | |
| 1082 } else { | |
| 1083 stream_.reset(new HttpBasicStream(connection_.release(), using_proxy)); | |
| 1084 } | |
| 1085 return OK; | |
| 1086 } | |
| 1087 | |
| 1088 CHECK(!stream_.get()); | |
| 1089 | |
| 1090 bool direct = true; | |
| 1091 const ProxyServer& proxy_server = proxy_info_.proxy_server(); | |
| 1092 PrivacyMode privacy_mode = request_info_.privacy_mode; | |
| 1093 if (IsHttpsProxyAndHttpUrl()) | |
| 1094 direct = false; | |
| 1095 | |
| 1096 if (existing_spdy_session_.get()) { | |
| 1097 // We picked up an existing session, so we don't need our socket. | |
| 1098 if (connection_->socket()) | |
| 1099 connection_->socket()->Disconnect(); | |
| 1100 connection_->Reset(); | |
| 1101 | |
| 1102 int set_result = SetSpdyHttpStream(existing_spdy_session_, direct); | |
| 1103 existing_spdy_session_.reset(); | |
| 1104 return set_result; | |
| 1105 } | |
| 1106 | |
| 1107 SpdySessionKey spdy_session_key(origin_, proxy_server, privacy_mode); | |
| 1108 if (IsHttpsProxyAndHttpUrl()) { | |
| 1109 // If we don't have a direct SPDY session, and we're using an HTTPS | |
| 1110 // proxy, then we might have a SPDY session to the proxy. | |
| 1111 // We never use privacy mode for connection to proxy server. | |
| 1112 spdy_session_key = SpdySessionKey(proxy_server.host_port_pair(), | |
| 1113 ProxyServer::Direct(), | |
| 1114 PRIVACY_MODE_DISABLED); | |
| 1115 } | |
| 1116 | |
| 1117 SpdySessionPool* spdy_pool = session_->spdy_session_pool(); | |
| 1118 base::WeakPtr<SpdySession> spdy_session = | |
| 1119 spdy_pool->FindAvailableSession(spdy_session_key, net_log_); | |
| 1120 | |
| 1121 if (spdy_session) { | |
| 1122 return SetSpdyHttpStream(spdy_session, direct); | |
| 1123 } | |
| 1124 | |
| 1125 spdy_session = | |
| 1126 spdy_pool->CreateAvailableSessionFromSocket(spdy_session_key, | |
| 1127 connection_.Pass(), | |
| 1128 net_log_, | |
| 1129 spdy_certificate_error_, | |
| 1130 using_ssl_); | |
| 1131 if (!spdy_session->HasAcceptableTransportSecurity()) { | |
| 1132 spdy_session->CloseSessionOnError( | |
| 1133 ERR_SPDY_INADEQUATE_TRANSPORT_SECURITY, ""); | |
| 1134 return ERR_SPDY_INADEQUATE_TRANSPORT_SECURITY; | |
| 1135 } | |
| 1136 | |
| 1137 new_spdy_session_ = spdy_session; | |
| 1138 spdy_session_direct_ = direct; | |
| 1139 const HostPortPair& host_port_pair = spdy_session_key.host_port_pair(); | |
| 1140 base::WeakPtr<HttpServerProperties> http_server_properties = | |
| 1141 session_->http_server_properties(); | |
| 1142 if (http_server_properties) | |
| 1143 http_server_properties->SetSupportsSpdy(host_port_pair, true); | |
| 1144 | |
| 1145 // Create a SpdyHttpStream attached to the session; | |
| 1146 // OnNewSpdySessionReadyCallback is not called until an event loop | |
| 1147 // iteration later, so if the SpdySession is closed between then, allow | |
| 1148 // reuse state from the underlying socket, sampled by SpdyHttpStream, | |
| 1149 // bubble up to the request. | |
| 1150 return SetSpdyHttpStream(new_spdy_session_, spdy_session_direct_); | |
| 1151 } | |
| 1152 | |
| 1153 int HttpStreamFactoryImpl::Job::DoCreateStreamComplete(int result) { | |
| 1154 if (result < 0) | |
| 1155 return result; | |
| 1156 | |
| 1157 session_->proxy_service()->ReportSuccess(proxy_info_, | |
| 1158 session_->network_delegate()); | |
| 1159 next_state_ = STATE_NONE; | |
| 1160 return OK; | |
| 1161 } | |
| 1162 | |
| 1163 int HttpStreamFactoryImpl::Job::DoRestartTunnelAuth() { | |
| 1164 next_state_ = STATE_RESTART_TUNNEL_AUTH_COMPLETE; | |
| 1165 ProxyClientSocket* proxy_socket = | |
| 1166 static_cast<ProxyClientSocket*>(connection_->socket()); | |
| 1167 return proxy_socket->RestartWithAuth(io_callback_); | |
| 1168 } | |
| 1169 | |
| 1170 int HttpStreamFactoryImpl::Job::DoRestartTunnelAuthComplete(int result) { | |
| 1171 if (result == ERR_PROXY_AUTH_REQUESTED) | |
| 1172 return result; | |
| 1173 | |
| 1174 if (result == OK) { | |
| 1175 // Now that we've got the HttpProxyClientSocket connected. We have | |
| 1176 // to release it as an idle socket into the pool and start the connection | |
| 1177 // process from the beginning. Trying to pass it in with the | |
| 1178 // SSLSocketParams might cause a deadlock since params are dispatched | |
| 1179 // interchangeably. This request won't necessarily get this http proxy | |
| 1180 // socket, but there will be forward progress. | |
| 1181 establishing_tunnel_ = false; | |
| 1182 ReturnToStateInitConnection(false /* do not close connection */); | |
| 1183 return OK; | |
| 1184 } | |
| 1185 | |
| 1186 return ReconsiderProxyAfterError(result); | |
| 1187 } | |
| 1188 | |
| 1189 void HttpStreamFactoryImpl::Job::ReturnToStateInitConnection( | |
| 1190 bool close_connection) { | |
| 1191 if (close_connection && connection_->socket()) | |
| 1192 connection_->socket()->Disconnect(); | |
| 1193 connection_->Reset(); | |
| 1194 | |
| 1195 if (request_) | |
| 1196 request_->RemoveRequestFromSpdySessionRequestMap(); | |
| 1197 | |
| 1198 next_state_ = STATE_INIT_CONNECTION; | |
| 1199 } | |
| 1200 | |
| 1201 void HttpStreamFactoryImpl::Job::SetSocketMotivation() { | |
| 1202 if (request_info_.motivation == HttpRequestInfo::PRECONNECT_MOTIVATED) | |
| 1203 connection_->socket()->SetSubresourceSpeculation(); | |
| 1204 else if (request_info_.motivation == HttpRequestInfo::OMNIBOX_MOTIVATED) | |
| 1205 connection_->socket()->SetOmniboxSpeculation(); | |
| 1206 // TODO(mbelshe): Add other motivations (like EARLY_LOAD_MOTIVATED). | |
| 1207 } | |
| 1208 | |
| 1209 bool HttpStreamFactoryImpl::Job::IsHttpsProxyAndHttpUrl() const { | |
| 1210 if (!proxy_info_.is_https()) | |
| 1211 return false; | |
| 1212 if (original_url_.get()) { | |
| 1213 // We currently only support Alternate-Protocol where the original scheme | |
| 1214 // is http. | |
| 1215 DCHECK(original_url_->SchemeIs("http")); | |
| 1216 return original_url_->SchemeIs("http"); | |
| 1217 } | |
| 1218 return request_info_.url.SchemeIs("http"); | |
| 1219 } | |
| 1220 | |
| 1221 // Sets several fields of ssl_config for the given origin_server based on the | |
| 1222 // proxy info and other factors. | |
| 1223 void HttpStreamFactoryImpl::Job::InitSSLConfig( | |
| 1224 const HostPortPair& origin_server, | |
| 1225 SSLConfig* ssl_config, | |
| 1226 bool is_proxy) const { | |
| 1227 if (proxy_info_.is_https() && ssl_config->send_client_cert) { | |
| 1228 // When connecting through an HTTPS proxy, disable TLS False Start so | |
| 1229 // that client authentication errors can be distinguished between those | |
| 1230 // originating from the proxy server (ERR_PROXY_CONNECTION_FAILED) and | |
| 1231 // those originating from the endpoint (ERR_SSL_PROTOCOL_ERROR / | |
| 1232 // ERR_BAD_SSL_CLIENT_AUTH_CERT). | |
| 1233 // TODO(rch): This assumes that the HTTPS proxy will only request a | |
| 1234 // client certificate during the initial handshake. | |
| 1235 // http://crbug.com/59292 | |
| 1236 ssl_config->false_start_enabled = false; | |
| 1237 } | |
| 1238 | |
| 1239 enum { | |
| 1240 FALLBACK_NONE = 0, // SSL version fallback did not occur. | |
| 1241 FALLBACK_SSL3 = 1, // Fell back to SSL 3.0. | |
| 1242 FALLBACK_TLS1 = 2, // Fell back to TLS 1.0. | |
| 1243 FALLBACK_TLS1_1 = 3, // Fell back to TLS 1.1. | |
| 1244 FALLBACK_MAX | |
| 1245 }; | |
| 1246 | |
| 1247 int fallback = FALLBACK_NONE; | |
| 1248 if (ssl_config->version_fallback) { | |
| 1249 switch (ssl_config->version_max) { | |
| 1250 case SSL_PROTOCOL_VERSION_SSL3: | |
| 1251 fallback = FALLBACK_SSL3; | |
| 1252 break; | |
| 1253 case SSL_PROTOCOL_VERSION_TLS1: | |
| 1254 fallback = FALLBACK_TLS1; | |
| 1255 break; | |
| 1256 case SSL_PROTOCOL_VERSION_TLS1_1: | |
| 1257 fallback = FALLBACK_TLS1_1; | |
| 1258 break; | |
| 1259 } | |
| 1260 } | |
| 1261 UMA_HISTOGRAM_ENUMERATION("Net.ConnectionUsedSSLVersionFallback", | |
| 1262 fallback, FALLBACK_MAX); | |
| 1263 | |
| 1264 // We also wish to measure the amount of fallback connections for a host that | |
| 1265 // we know implements TLS up to 1.2. Ideally there would be no fallback here | |
| 1266 // but high numbers of SSLv3 would suggest that SSLv3 fallback is being | |
| 1267 // caused by network middleware rather than buggy HTTPS servers. | |
| 1268 const std::string& host = origin_server.host(); | |
| 1269 if (!is_proxy && | |
| 1270 host.size() >= 10 && | |
| 1271 host.compare(host.size() - 10, 10, "google.com") == 0 && | |
| 1272 (host.size() == 10 || host[host.size()-11] == '.')) { | |
| 1273 UMA_HISTOGRAM_ENUMERATION("Net.GoogleConnectionUsedSSLVersionFallback", | |
| 1274 fallback, FALLBACK_MAX); | |
| 1275 } | |
| 1276 | |
| 1277 if (request_info_.load_flags & LOAD_VERIFY_EV_CERT) | |
| 1278 ssl_config->verify_ev_cert = true; | |
| 1279 | |
| 1280 // Disable Channel ID if privacy mode is enabled. | |
| 1281 if (request_info_.privacy_mode == PRIVACY_MODE_ENABLED) | |
| 1282 ssl_config->channel_id_enabled = false; | |
| 1283 } | |
| 1284 | |
| 1285 | |
| 1286 int HttpStreamFactoryImpl::Job::ReconsiderProxyAfterError(int error) { | |
| 1287 DCHECK(!pac_request_); | |
| 1288 DCHECK(session_); | |
| 1289 | |
| 1290 // A failure to resolve the hostname or any error related to establishing a | |
| 1291 // TCP connection could be grounds for trying a new proxy configuration. | |
| 1292 // | |
| 1293 // Why do this when a hostname cannot be resolved? Some URLs only make sense | |
| 1294 // to proxy servers. The hostname in those URLs might fail to resolve if we | |
| 1295 // are still using a non-proxy config. We need to check if a proxy config | |
| 1296 // now exists that corresponds to a proxy server that could load the URL. | |
| 1297 // | |
| 1298 switch (error) { | |
| 1299 case ERR_PROXY_CONNECTION_FAILED: | |
| 1300 case ERR_NAME_NOT_RESOLVED: | |
| 1301 case ERR_INTERNET_DISCONNECTED: | |
| 1302 case ERR_ADDRESS_UNREACHABLE: | |
| 1303 case ERR_CONNECTION_CLOSED: | |
| 1304 case ERR_CONNECTION_TIMED_OUT: | |
| 1305 case ERR_CONNECTION_RESET: | |
| 1306 case ERR_CONNECTION_REFUSED: | |
| 1307 case ERR_CONNECTION_ABORTED: | |
| 1308 case ERR_TIMED_OUT: | |
| 1309 case ERR_TUNNEL_CONNECTION_FAILED: | |
| 1310 case ERR_SOCKS_CONNECTION_FAILED: | |
| 1311 // This can happen in the case of trying to talk to a proxy using SSL, and | |
| 1312 // ending up talking to a captive portal that supports SSL instead. | |
| 1313 case ERR_PROXY_CERTIFICATE_INVALID: | |
| 1314 // This can happen when trying to talk SSL to a non-SSL server (Like a | |
| 1315 // captive portal). | |
| 1316 case ERR_SSL_PROTOCOL_ERROR: | |
| 1317 break; | |
| 1318 case ERR_SOCKS_CONNECTION_HOST_UNREACHABLE: | |
| 1319 // Remap the SOCKS-specific "host unreachable" error to a more | |
| 1320 // generic error code (this way consumers like the link doctor | |
| 1321 // know to substitute their error page). | |
| 1322 // | |
| 1323 // Note that if the host resolving was done by the SOCKS5 proxy, we can't | |
| 1324 // differentiate between a proxy-side "host not found" versus a proxy-side | |
| 1325 // "address unreachable" error, and will report both of these failures as | |
| 1326 // ERR_ADDRESS_UNREACHABLE. | |
| 1327 return ERR_ADDRESS_UNREACHABLE; | |
| 1328 default: | |
| 1329 return error; | |
| 1330 } | |
| 1331 | |
| 1332 if (request_info_.load_flags & LOAD_BYPASS_PROXY) { | |
| 1333 return error; | |
| 1334 } | |
| 1335 | |
| 1336 if (proxy_info_.is_https() && proxy_ssl_config_.send_client_cert) { | |
| 1337 session_->ssl_client_auth_cache()->Remove( | |
| 1338 proxy_info_.proxy_server().host_port_pair()); | |
| 1339 } | |
| 1340 | |
| 1341 int rv = session_->proxy_service()->ReconsiderProxyAfterError( | |
| 1342 request_info_.url, request_info_.load_flags, error, &proxy_info_, | |
| 1343 io_callback_, &pac_request_, session_->network_delegate(), net_log_); | |
| 1344 if (rv == OK || rv == ERR_IO_PENDING) { | |
| 1345 // If the error was during connection setup, there is no socket to | |
| 1346 // disconnect. | |
| 1347 if (connection_->socket()) | |
| 1348 connection_->socket()->Disconnect(); | |
| 1349 connection_->Reset(); | |
| 1350 if (request_) | |
| 1351 request_->RemoveRequestFromSpdySessionRequestMap(); | |
| 1352 next_state_ = STATE_RESOLVE_PROXY_COMPLETE; | |
| 1353 } else { | |
| 1354 // If ReconsiderProxyAfterError() failed synchronously, it means | |
| 1355 // there was nothing left to fall-back to, so fail the transaction | |
| 1356 // with the last connection error we got. | |
| 1357 // TODO(eroman): This is a confusing contract, make it more obvious. | |
| 1358 rv = error; | |
| 1359 } | |
| 1360 | |
| 1361 return rv; | |
| 1362 } | |
| 1363 | |
| 1364 int HttpStreamFactoryImpl::Job::HandleCertificateError(int error) { | |
| 1365 DCHECK(using_ssl_); | |
| 1366 DCHECK(IsCertificateError(error)); | |
| 1367 | |
| 1368 SSLClientSocket* ssl_socket = | |
| 1369 static_cast<SSLClientSocket*>(connection_->socket()); | |
| 1370 ssl_socket->GetSSLInfo(&ssl_info_); | |
| 1371 | |
| 1372 // Add the bad certificate to the set of allowed certificates in the | |
| 1373 // SSL config object. This data structure will be consulted after calling | |
| 1374 // RestartIgnoringLastError(). And the user will be asked interactively | |
| 1375 // before RestartIgnoringLastError() is ever called. | |
| 1376 SSLConfig::CertAndStatus bad_cert; | |
| 1377 | |
| 1378 // |ssl_info_.cert| may be NULL if we failed to create | |
| 1379 // X509Certificate for whatever reason, but normally it shouldn't | |
| 1380 // happen, unless this code is used inside sandbox. | |
| 1381 if (ssl_info_.cert.get() == NULL || | |
| 1382 !X509Certificate::GetDEREncoded(ssl_info_.cert->os_cert_handle(), | |
| 1383 &bad_cert.der_cert)) { | |
| 1384 return error; | |
| 1385 } | |
| 1386 bad_cert.cert_status = ssl_info_.cert_status; | |
| 1387 server_ssl_config_.allowed_bad_certs.push_back(bad_cert); | |
| 1388 | |
| 1389 int load_flags = request_info_.load_flags; | |
| 1390 if (session_->params().ignore_certificate_errors) | |
| 1391 load_flags |= LOAD_IGNORE_ALL_CERT_ERRORS; | |
| 1392 if (ssl_socket->IgnoreCertError(error, load_flags)) | |
| 1393 return OK; | |
| 1394 return error; | |
| 1395 } | |
| 1396 | |
| 1397 void HttpStreamFactoryImpl::Job::SwitchToSpdyMode() { | |
| 1398 if (HttpStreamFactory::spdy_enabled()) | |
| 1399 using_spdy_ = true; | |
| 1400 } | |
| 1401 | |
| 1402 // static | |
| 1403 void HttpStreamFactoryImpl::Job::LogHttpConnectedMetrics( | |
| 1404 const ClientSocketHandle& handle) { | |
| 1405 UMA_HISTOGRAM_ENUMERATION("Net.HttpSocketType", handle.reuse_type(), | |
| 1406 ClientSocketHandle::NUM_TYPES); | |
| 1407 | |
| 1408 switch (handle.reuse_type()) { | |
| 1409 case ClientSocketHandle::UNUSED: | |
| 1410 UMA_HISTOGRAM_CUSTOM_TIMES("Net.HttpConnectionLatency", | |
| 1411 handle.setup_time(), | |
| 1412 base::TimeDelta::FromMilliseconds(1), | |
| 1413 base::TimeDelta::FromMinutes(10), | |
| 1414 100); | |
| 1415 break; | |
| 1416 case ClientSocketHandle::UNUSED_IDLE: | |
| 1417 UMA_HISTOGRAM_CUSTOM_TIMES("Net.SocketIdleTimeBeforeNextUse_UnusedSocket", | |
| 1418 handle.idle_time(), | |
| 1419 base::TimeDelta::FromMilliseconds(1), | |
| 1420 base::TimeDelta::FromMinutes(6), | |
| 1421 100); | |
| 1422 break; | |
| 1423 case ClientSocketHandle::REUSED_IDLE: | |
| 1424 UMA_HISTOGRAM_CUSTOM_TIMES("Net.SocketIdleTimeBeforeNextUse_ReusedSocket", | |
| 1425 handle.idle_time(), | |
| 1426 base::TimeDelta::FromMilliseconds(1), | |
| 1427 base::TimeDelta::FromMinutes(6), | |
| 1428 100); | |
| 1429 break; | |
| 1430 default: | |
| 1431 NOTREACHED(); | |
| 1432 break; | |
| 1433 } | |
| 1434 } | |
| 1435 | |
| 1436 bool HttpStreamFactoryImpl::Job::IsPreconnecting() const { | |
| 1437 DCHECK_GE(num_streams_, 0); | |
| 1438 return num_streams_ > 0; | |
| 1439 } | |
| 1440 | |
| 1441 bool HttpStreamFactoryImpl::Job::IsOrphaned() const { | |
| 1442 return !IsPreconnecting() && !request_; | |
| 1443 } | |
| 1444 | |
| 1445 void HttpStreamFactoryImpl::Job::ReportJobSuccededForRequest() { | |
| 1446 if (using_existing_quic_session_) { | |
| 1447 // If an existing session was used, then no TCP connection was | |
| 1448 // started. | |
| 1449 HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_NO_RACE); | |
| 1450 } else if (original_url_) { | |
| 1451 // This job was the alternate protocol job, and hence won the race. | |
| 1452 HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_WON_RACE); | |
| 1453 } else { | |
| 1454 // This job was the normal job, and hence the alternate protocol job lost | |
| 1455 // the race. | |
| 1456 HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_LOST_RACE); | |
| 1457 } | |
| 1458 } | |
| 1459 | |
| 1460 void HttpStreamFactoryImpl::Job::MarkOtherJobComplete(const Job& job) { | |
| 1461 DCHECK_EQ(STATUS_RUNNING, other_job_status_); | |
| 1462 other_job_status_ = job.job_status_; | |
| 1463 other_job_alternate_protocol_ = job.alternate_protocol_; | |
| 1464 MaybeMarkAlternateProtocolBroken(); | |
| 1465 } | |
| 1466 | |
| 1467 void HttpStreamFactoryImpl::Job::MaybeMarkAlternateProtocolBroken() { | |
| 1468 if (job_status_ == STATUS_RUNNING || other_job_status_ == STATUS_RUNNING) | |
| 1469 return; | |
| 1470 | |
| 1471 bool is_alternate_protocol_job = original_url_.get() != NULL; | |
| 1472 if (is_alternate_protocol_job) { | |
| 1473 if (job_status_ == STATUS_BROKEN && other_job_status_ == STATUS_SUCCEEDED) { | |
| 1474 HistogramBrokenAlternateProtocolLocation( | |
| 1475 BROKEN_ALTERNATE_PROTOCOL_LOCATION_HTTP_STREAM_FACTORY_IMPL_JOB_ALT); | |
| 1476 session_->http_server_properties()->SetBrokenAlternateProtocol( | |
| 1477 HostPortPair::FromURL(*original_url_)); | |
| 1478 } | |
| 1479 return; | |
| 1480 } | |
| 1481 | |
| 1482 if (job_status_ == STATUS_SUCCEEDED && other_job_status_ == STATUS_BROKEN) { | |
| 1483 HistogramBrokenAlternateProtocolLocation( | |
| 1484 BROKEN_ALTERNATE_PROTOCOL_LOCATION_HTTP_STREAM_FACTORY_IMPL_JOB_MAIN); | |
| 1485 session_->http_server_properties()->SetBrokenAlternateProtocol( | |
| 1486 HostPortPair::FromURL(request_info_.url)); | |
| 1487 } | |
| 1488 } | |
| 1489 | |
| 1490 } // namespace net | |
| OLD | NEW |