Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2016 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_controller.h" | |
| 6 | |
| 7 #include "base/metrics/histogram_macros.h" | |
| 8 #include "base/strings/string_number_conversions.h" | |
| 9 #include "base/strings/string_util.h" | |
| 10 #include "net/base/host_mapping_rules.h" | |
| 11 #include "net/http/bidirectional_stream_impl.h" | |
| 12 #include "net/http/transport_security_state.h" | |
| 13 #include "net/spdy/spdy_session.h" | |
| 14 | |
| 15 namespace net { | |
| 16 | |
| 17 HttpStreamFactoryImpl::JobController::JobController( | |
| 18 HttpStreamFactoryImpl* factory, | |
| 19 HttpStreamRequest::Delegate* delegate, | |
| 20 HttpNetworkSession* session, | |
| 21 JobFactory* job_factory) | |
| 22 : factory_(factory), | |
| 23 session_(session), | |
| 24 job_factory_(job_factory), | |
| 25 request_(nullptr), | |
| 26 delegate_(delegate), | |
| 27 is_preconnect_(false), | |
| 28 job_bound_(false), | |
| 29 bound_job_(nullptr) { | |
| 30 DCHECK(factory); | |
| 31 } | |
| 32 | |
| 33 HttpStreamFactoryImpl::JobController::~JobController() { | |
| 34 main_job_.reset(); | |
| 35 alternative_job_.reset(); | |
| 36 bound_job_ = nullptr; | |
| 37 } | |
| 38 | |
| 39 bool HttpStreamFactoryImpl::JobController::for_websockets() { | |
| 40 return factory_->for_websockets_; | |
| 41 } | |
| 42 | |
| 43 HttpStreamFactoryImpl::Request* HttpStreamFactoryImpl::JobController::Start( | |
| 44 const HttpRequestInfo& request_info, | |
| 45 HttpStreamRequest::Delegate* delegate, | |
| 46 WebSocketHandshakeStreamBase::CreateHelper* | |
| 47 websocket_handshake_stream_create_helper, | |
| 48 const BoundNetLog& net_log, | |
| 49 HttpStreamRequest::StreamType stream_type, | |
| 50 RequestPriority priority, | |
| 51 const SSLConfig& server_ssl_config, | |
| 52 const SSLConfig& proxy_ssl_config) { | |
| 53 DCHECK(factory_); | |
| 54 DCHECK(!request_); | |
| 55 | |
| 56 request_ = new Request(request_info.url, this, delegate, | |
| 57 websocket_handshake_stream_create_helper, net_log, | |
| 58 stream_type); | |
| 59 | |
| 60 CreateJobs(request_info, priority, server_ssl_config, proxy_ssl_config, | |
| 61 delegate, stream_type, net_log); | |
| 62 | |
| 63 return request_; | |
| 64 } | |
| 65 | |
| 66 void HttpStreamFactoryImpl::JobController::Preconnect( | |
| 67 int num_streams, | |
| 68 const HttpRequestInfo& request_info, | |
| 69 const SSLConfig& server_ssl_config, | |
| 70 const SSLConfig& proxy_ssl_config) { | |
| 71 DCHECK(!main_job_); | |
| 72 DCHECK(!alternative_job_); | |
| 73 | |
| 74 is_preconnect_ = true; | |
| 75 HostPortPair destination(HostPortPair::FromURL(request_info.url)); | |
| 76 GURL origin_url = ApplyHostMappingRules(request_info.url, &destination); | |
| 77 | |
| 78 const AlternativeService alternative_service = GetAlternativeServiceFor( | |
| 79 request_info, nullptr, HttpStreamRequest::HTTP_STREAM); | |
| 80 | |
| 81 if (alternative_service.protocol != UNINITIALIZED_ALTERNATE_PROTOCOL) { | |
| 82 if (session_->params().quic_disable_preconnect_if_0rtt && | |
| 83 alternative_service.protocol == QUIC && | |
| 84 session_->quic_stream_factory()->ZeroRTTEnabledFor(QuicServerId( | |
| 85 alternative_service.host_port_pair(), request_info.privacy_mode))) { | |
| 86 MaybeNotifyFactoryOfCompletion(); | |
| 87 return; | |
| 88 } | |
| 89 destination = alternative_service.host_port_pair(); | |
| 90 ignore_result(ApplyHostMappingRules(request_info.url, &destination)); | |
| 91 } | |
| 92 | |
| 93 // Due to how the socket pools handle priorities and idle sockets, only IDLE | |
| 94 // priority currently makes sense for preconnects. The priority for | |
| 95 // preconnects is currently ignored (see RequestSocketsForPool()), but could | |
| 96 // be used at some point for proxy resolution or something. | |
| 97 main_job_.reset(job_factory_->CreateJob( | |
| 98 this, PRECONNECT, session_, request_info, IDLE, server_ssl_config, | |
| 99 proxy_ssl_config, destination, origin_url, alternative_service, | |
| 100 session_->net_log())); | |
| 101 main_job_->Preconnect(num_streams); | |
| 102 } | |
| 103 | |
| 104 LoadState HttpStreamFactoryImpl::JobController::GetLoadState() const { | |
| 105 DCHECK(request_); | |
| 106 DCHECK(main_job_ || alternative_job_); | |
| 107 if (bound_job_) | |
| 108 return bound_job_->GetLoadState(); | |
| 109 | |
| 110 // Just pick the first one. | |
| 111 if (main_job_) | |
| 112 return main_job_->GetLoadState(); | |
| 113 | |
| 114 return alternative_job_->GetLoadState(); | |
|
Ryan Hamilton
2016/06/17 23:14:36
How about:
return main_job_ ? main_job_->GetLoadSt
Zhongyi Shi
2016/06/17 23:32:23
Done.
| |
| 115 } | |
| 116 | |
| 117 void HttpStreamFactoryImpl::JobController::OnRequestComplete() { | |
| 118 CancelJobs(); | |
| 119 DCHECK(request_); | |
| 120 request_ = nullptr; | |
| 121 if (bound_job_) { | |
| 122 if (bound_job_->job_type() == MAIN) { | |
| 123 main_job_.reset(); | |
| 124 } else { | |
| 125 DCHECK(bound_job_->job_type() == ALTERNATIVE); | |
| 126 alternative_job_.reset(); | |
| 127 } | |
| 128 bound_job_ = nullptr; | |
| 129 } | |
| 130 MaybeNotifyFactoryOfCompletion(); | |
| 131 } | |
| 132 | |
| 133 int HttpStreamFactoryImpl::JobController::RestartTunnelWithProxyAuth( | |
| 134 const AuthCredentials& credentials) { | |
| 135 DCHECK(bound_job_); | |
| 136 return bound_job_->RestartTunnelWithProxyAuth(credentials); | |
| 137 } | |
| 138 | |
| 139 void HttpStreamFactoryImpl::JobController::SetPriority( | |
| 140 RequestPriority priority) { | |
| 141 if (main_job_) { | |
| 142 main_job_->SetPriority(priority); | |
| 143 } | |
| 144 if (alternative_job_) { | |
| 145 alternative_job_->SetPriority(priority); | |
| 146 } | |
| 147 } | |
| 148 | |
| 149 void HttpStreamFactoryImpl::JobController::OnStreamReady( | |
| 150 Job* job, | |
| 151 const SSLConfig& used_ssl_config, | |
| 152 const ProxyInfo& used_proxy_info) { | |
| 153 DCHECK(job); | |
| 154 | |
| 155 if (job_bound_ && bound_job_ != job) { | |
| 156 // We have bound a job to the associated Request, |job| has been orphaned. | |
| 157 OnOrphanedJobComplete(job); | |
| 158 return; | |
| 159 } | |
| 160 std::unique_ptr<HttpStream> stream = job->ReleaseStream(); | |
| 161 DCHECK(stream); | |
| 162 | |
| 163 MarkRequestComplete(job->was_npn_negotiated(), job->protocol_negotiated(), | |
| 164 job->using_spdy()); | |
| 165 | |
| 166 if (!request_) | |
| 167 return; | |
| 168 DCHECK(!factory_->for_websockets_); | |
| 169 DCHECK_EQ(HttpStreamRequest::HTTP_STREAM, request_->stream_type()); | |
| 170 OnJobSucceeded(job); | |
| 171 request_->OnStreamReady(used_ssl_config, used_proxy_info, stream.release()); | |
| 172 } | |
| 173 | |
| 174 void HttpStreamFactoryImpl::JobController::OnBidirectionalStreamImplReady( | |
| 175 Job* job, | |
| 176 const SSLConfig& used_ssl_config, | |
| 177 const ProxyInfo& used_proxy_info) { | |
| 178 DCHECK(job); | |
| 179 | |
| 180 if (job_bound_ && bound_job_ != job) { | |
| 181 // We have bound a job to the associated Request, |job| has been orphaned. | |
| 182 OnOrphanedJobComplete(job); | |
| 183 return; | |
| 184 } | |
| 185 | |
| 186 MarkRequestComplete(job->was_npn_negotiated(), job->protocol_negotiated(), | |
| 187 job->using_spdy()); | |
| 188 | |
| 189 if (!request_) | |
| 190 return; | |
| 191 std::unique_ptr<BidirectionalStreamImpl> stream = | |
| 192 job->ReleaseBidirectionalStream(); | |
| 193 DCHECK(stream); | |
| 194 DCHECK(!factory_->for_websockets_); | |
| 195 DCHECK_EQ(HttpStreamRequest::BIDIRECTIONAL_STREAM, request_->stream_type()); | |
| 196 | |
| 197 OnJobSucceeded(job); | |
| 198 request_->OnBidirectionalStreamImplReady(used_ssl_config, used_proxy_info, | |
| 199 stream.release()); | |
| 200 } | |
| 201 | |
| 202 void HttpStreamFactoryImpl::JobController::OnWebSocketHandshakeStreamReady( | |
| 203 Job* job, | |
| 204 const SSLConfig& used_ssl_config, | |
| 205 const ProxyInfo& used_proxy_info, | |
| 206 WebSocketHandshakeStreamBase* stream) { | |
| 207 DCHECK(job); | |
| 208 | |
| 209 MarkRequestComplete(job->was_npn_negotiated(), job->protocol_negotiated(), | |
| 210 job->using_spdy()); | |
| 211 | |
| 212 if (!request_) | |
| 213 return; | |
| 214 DCHECK(factory_->for_websockets_); | |
| 215 DCHECK_EQ(HttpStreamRequest::HTTP_STREAM, request_->stream_type()); | |
| 216 DCHECK(stream); | |
| 217 | |
| 218 OnJobSucceeded(job); | |
| 219 request_->OnWebSocketHandshakeStreamReady(used_ssl_config, used_proxy_info, | |
| 220 stream); | |
| 221 } | |
| 222 | |
| 223 void HttpStreamFactoryImpl::JobController::OnStreamFailed( | |
| 224 Job* job, | |
| 225 int status, | |
| 226 const SSLConfig& used_ssl_config, | |
| 227 SSLFailureState ssl_failure_state) { | |
| 228 if (job_bound_ && bound_job_ != job) { | |
| 229 // We have bound a job to the associated Request, |job| has been orphaned. | |
| 230 OnOrphanedJobComplete(job); | |
| 231 return; | |
| 232 } | |
| 233 | |
| 234 if (!request_) | |
| 235 return; | |
| 236 DCHECK_NE(OK, status); | |
| 237 DCHECK(job); | |
| 238 | |
| 239 if (!bound_job_) { | |
| 240 if (main_job_ && alternative_job_) { | |
| 241 // Hey, we've got other jobs! Maybe one of them will succeed, let's just | |
| 242 // ignore this failure. | |
| 243 factory_->request_map_.erase(job); | |
| 244 // Notify all the other jobs that this one failed. | |
| 245 if (job->job_type() == MAIN) { | |
| 246 alternative_job_->MarkOtherJobComplete(*job); | |
| 247 main_job_.reset(); | |
| 248 } else { | |
| 249 DCHECK(job->job_type() == ALTERNATIVE); | |
| 250 main_job_->MarkOtherJobComplete(*job); | |
| 251 alternative_job_.reset(); | |
| 252 } | |
| 253 return; | |
| 254 } else { | |
| 255 BindJob(job); | |
| 256 } | |
| 257 } | |
| 258 | |
| 259 request_->OnStreamFailed(status, used_ssl_config, ssl_failure_state); | |
| 260 } | |
| 261 | |
| 262 void HttpStreamFactoryImpl::JobController::OnCertificateError( | |
| 263 Job* job, | |
| 264 int status, | |
| 265 const SSLConfig& used_ssl_config, | |
| 266 const SSLInfo& ssl_info) { | |
| 267 if (job_bound_ && bound_job_ != job) { | |
| 268 // We have bound a job to the associated Request, |job| has been orphaned. | |
| 269 OnOrphanedJobComplete(job); | |
| 270 return; | |
| 271 } | |
| 272 | |
| 273 if (!request_) | |
| 274 return; | |
| 275 DCHECK_NE(OK, status); | |
| 276 if (!bound_job_) | |
| 277 BindJob(job); | |
| 278 | |
| 279 request_->OnCertificateError(status, used_ssl_config, ssl_info); | |
| 280 } | |
| 281 | |
| 282 void HttpStreamFactoryImpl::JobController::OnHttpsProxyTunnelResponse( | |
| 283 Job* job, | |
| 284 const HttpResponseInfo& response_info, | |
| 285 const SSLConfig& used_ssl_config, | |
| 286 const ProxyInfo& used_proxy_info, | |
| 287 HttpStream* stream) { | |
| 288 if (job_bound_ && bound_job_ != job) { | |
| 289 // We have bound a job to the associated Request, |job| has been orphaned. | |
| 290 OnOrphanedJobComplete(job); | |
| 291 return; | |
| 292 } | |
| 293 | |
| 294 if (!bound_job_) | |
| 295 BindJob(job); | |
| 296 if (!request_) | |
| 297 return; | |
| 298 request_->OnHttpsProxyTunnelResponse(response_info, used_ssl_config, | |
| 299 used_proxy_info, stream); | |
| 300 } | |
| 301 | |
| 302 void HttpStreamFactoryImpl::JobController::OnNeedsClientAuth( | |
| 303 Job* job, | |
| 304 const SSLConfig& used_ssl_config, | |
| 305 SSLCertRequestInfo* cert_info) { | |
| 306 if (job_bound_ && bound_job_ != job) { | |
| 307 // We have bound a job to the associated Request, |job| has been orphaned. | |
| 308 OnOrphanedJobComplete(job); | |
| 309 return; | |
| 310 } | |
| 311 if (!request_) | |
| 312 return; | |
| 313 if (!bound_job_) | |
| 314 BindJob(job); | |
| 315 | |
| 316 request_->OnNeedsClientAuth(used_ssl_config, cert_info); | |
| 317 } | |
| 318 | |
| 319 void HttpStreamFactoryImpl::JobController::OnNeedsProxyAuth( | |
| 320 Job* job, | |
| 321 const HttpResponseInfo& proxy_response, | |
| 322 const SSLConfig& used_ssl_config, | |
| 323 const ProxyInfo& used_proxy_info, | |
| 324 HttpAuthController* auth_controller) { | |
| 325 if (job_bound_ && bound_job_ != job) { | |
| 326 // We have bound a job to the associated Request, |job| has been orphaned. | |
| 327 OnOrphanedJobComplete(job); | |
| 328 return; | |
| 329 } | |
| 330 | |
| 331 if (!request_) | |
| 332 return; | |
| 333 if (!bound_job_) | |
| 334 BindJob(job); | |
| 335 request_->OnNeedsProxyAuth(proxy_response, used_ssl_config, used_proxy_info, | |
| 336 auth_controller); | |
| 337 } | |
| 338 | |
| 339 void HttpStreamFactoryImpl::JobController::OnNewSpdySessionReady( | |
| 340 Job* job, | |
| 341 const base::WeakPtr<SpdySession>& spdy_session, | |
| 342 bool direct) { | |
| 343 DCHECK(job); | |
| 344 DCHECK(job->using_spdy()); | |
| 345 | |
| 346 bool is_job_orphaned = job_bound_ && bound_job_ != job; | |
| 347 | |
| 348 // Cache these values in case the job gets deleted. | |
| 349 const SSLConfig used_ssl_config = job->server_ssl_config(); | |
| 350 const ProxyInfo used_proxy_info = job->proxy_info(); | |
| 351 const bool was_npn_negotiated = job->was_npn_negotiated(); | |
| 352 const NextProto protocol_negotiated = job->protocol_negotiated(); | |
| 353 const bool using_spdy = job->using_spdy(); | |
| 354 const BoundNetLog net_log = job->net_log(); | |
| 355 | |
| 356 // Cache this so we can still use it if the JobController is deleted. | |
| 357 HttpStreamFactoryImpl* factory = factory_; | |
| 358 | |
| 359 // Notify |request_|. | |
| 360 if (!is_preconnect_ && !is_job_orphaned) { | |
| 361 DCHECK(request_); | |
| 362 | |
| 363 // The first case is the usual case. | |
| 364 if (!job_bound_) { | |
| 365 BindJob(job); | |
| 366 } | |
| 367 | |
| 368 MarkRequestComplete(was_npn_negotiated, protocol_negotiated, using_spdy); | |
| 369 | |
| 370 std::unique_ptr<HttpStream> stream; | |
| 371 std::unique_ptr<BidirectionalStreamImpl> bidirectional_stream_impl; | |
| 372 | |
| 373 if (for_websockets()) { | |
| 374 // TODO(ricea): Re-instate this code when WebSockets over SPDY is | |
| 375 // implemented. | |
| 376 NOTREACHED(); | |
| 377 } else if (job->stream_type() == HttpStreamRequest::BIDIRECTIONAL_STREAM) { | |
| 378 bidirectional_stream_impl = job->ReleaseBidirectionalStream(); | |
| 379 DCHECK(bidirectional_stream_impl); | |
| 380 delegate_->OnBidirectionalStreamImplReady( | |
| 381 used_ssl_config, used_proxy_info, | |
| 382 bidirectional_stream_impl.release()); | |
| 383 } else { | |
| 384 stream = job->ReleaseStream(); | |
| 385 DCHECK(stream); | |
| 386 delegate_->OnStreamReady(used_ssl_config, used_proxy_info, | |
| 387 stream.release()); | |
| 388 } | |
| 389 } | |
| 390 | |
| 391 // Notify |factory_|. |request_| and |bounded_job_| might be deleted already. | |
| 392 if (spdy_session && spdy_session->IsAvailable()) { | |
| 393 factory->OnNewSpdySessionReady(spdy_session, direct, used_ssl_config, | |
| 394 used_proxy_info, was_npn_negotiated, | |
| 395 protocol_negotiated, using_spdy, net_log); | |
| 396 } | |
| 397 if (is_job_orphaned) { | |
| 398 OnOrphanedJobComplete(job); | |
| 399 } | |
| 400 } | |
| 401 | |
| 402 void HttpStreamFactoryImpl::JobController::OnPreconnectsComplete(Job* job) { | |
| 403 DCHECK_EQ(main_job_.get(), job); | |
| 404 main_job_.reset(); | |
| 405 factory_->OnPreconnectsCompleteInternal(); | |
| 406 MaybeNotifyFactoryOfCompletion(); | |
| 407 } | |
| 408 | |
| 409 void HttpStreamFactoryImpl::JobController::OnOrphanedJobComplete( | |
| 410 const Job* job) { | |
| 411 if (job->job_type() == MAIN) { | |
| 412 DCHECK_EQ(main_job_.get(), job); | |
| 413 main_job_.reset(); | |
| 414 } else { | |
| 415 DCHECK_EQ(alternative_job_.get(), job); | |
| 416 alternative_job_.reset(); | |
| 417 } | |
| 418 | |
| 419 MaybeNotifyFactoryOfCompletion(); | |
| 420 } | |
| 421 | |
| 422 void HttpStreamFactoryImpl::JobController::AddConnectionAttemptsToRequest( | |
| 423 Job* job, | |
| 424 const ConnectionAttempts& attempts) { | |
| 425 if (is_preconnect_ || (job_bound_ && bound_job_ != job)) | |
| 426 return; | |
| 427 | |
| 428 DCHECK(request_); | |
| 429 request_->AddConnectionAttempts(attempts); | |
| 430 } | |
| 431 | |
| 432 void HttpStreamFactoryImpl::JobController::SetSpdySessionKey( | |
| 433 Job* job, | |
| 434 const SpdySessionKey& spdy_session_key) { | |
| 435 if (is_preconnect_ || (job_bound_ && bound_job_ != job)) | |
| 436 return; | |
| 437 | |
| 438 DCHECK(request_); | |
| 439 if (!request_->HasSpdySessionKey()) { | |
| 440 RequestSet& request_set = | |
| 441 factory_->spdy_session_request_map_[spdy_session_key]; | |
| 442 DCHECK(!ContainsKey(request_set, request_)); | |
| 443 request_set.insert(request_); | |
| 444 request_->SetSpdySessionKey(spdy_session_key); | |
| 445 } | |
| 446 } | |
| 447 | |
| 448 void HttpStreamFactoryImpl::JobController:: | |
| 449 RemoveRequestFromSpdySessionRequestMapForJob(Job* job) { | |
| 450 if (is_preconnect_ || (job_bound_ && bound_job_ != job)) | |
| 451 return; | |
| 452 DCHECK(request_); | |
| 453 | |
| 454 RemoveRequestFromSpdySessionRequestMap(); | |
| 455 } | |
| 456 | |
| 457 void HttpStreamFactoryImpl::JobController:: | |
| 458 RemoveRequestFromSpdySessionRequestMap() { | |
| 459 const SpdySessionKey* spdy_session_key = request_->spdy_session_key(); | |
| 460 if (spdy_session_key) { | |
| 461 SpdySessionRequestMap& spdy_session_request_map = | |
| 462 factory_->spdy_session_request_map_; | |
| 463 DCHECK(ContainsKey(spdy_session_request_map, *spdy_session_key)); | |
| 464 RequestSet& request_set = spdy_session_request_map[*spdy_session_key]; | |
| 465 DCHECK(ContainsKey(request_set, request_)); | |
| 466 request_set.erase(request_); | |
| 467 if (request_set.empty()) | |
| 468 spdy_session_request_map.erase(*spdy_session_key); | |
| 469 request_->ResetSpdySessionKey(); | |
| 470 } | |
| 471 } | |
| 472 | |
| 473 const BoundNetLog* HttpStreamFactoryImpl::JobController::GetNetLog( | |
| 474 Job* job) const { | |
| 475 if (is_preconnect_ || (job_bound_ && bound_job_ != job)) | |
| 476 return nullptr; | |
| 477 DCHECK(request_); | |
| 478 return &request_->net_log(); | |
| 479 } | |
| 480 | |
| 481 WebSocketHandshakeStreamBase::CreateHelper* HttpStreamFactoryImpl:: | |
| 482 JobController::websocket_handshake_stream_create_helper() { | |
| 483 DCHECK(request_); | |
| 484 return request_->websocket_handshake_stream_create_helper(); | |
| 485 } | |
| 486 | |
| 487 void HttpStreamFactoryImpl::JobController::CreateJobs( | |
| 488 const HttpRequestInfo& request_info, | |
| 489 RequestPriority priority, | |
| 490 const SSLConfig& server_ssl_config, | |
| 491 const SSLConfig& proxy_ssl_config, | |
| 492 HttpStreamRequest::Delegate* delegate, | |
| 493 HttpStreamRequest::StreamType stream_type, | |
| 494 const BoundNetLog& net_log) { | |
| 495 DCHECK(!main_job_); | |
| 496 DCHECK(!alternative_job_); | |
| 497 HostPortPair destination(HostPortPair::FromURL(request_info.url)); | |
| 498 GURL origin_url = ApplyHostMappingRules(request_info.url, &destination); | |
| 499 | |
| 500 main_job_.reset(job_factory_->CreateJob( | |
| 501 this, MAIN, session_, request_info, priority, server_ssl_config, | |
| 502 proxy_ssl_config, destination, origin_url, net_log.net_log())); | |
| 503 AttachJob(main_job_.get()); | |
| 504 | |
| 505 // Create an alternative job if alternative service is set up for this domain. | |
| 506 const AlternativeService alternative_service = | |
| 507 GetAlternativeServiceFor(request_info, delegate, stream_type); | |
| 508 | |
| 509 if (alternative_service.protocol != UNINITIALIZED_ALTERNATE_PROTOCOL) { | |
| 510 // Never share connection with other jobs for FTP requests. | |
| 511 DVLOG(1) << "Selected alternative service (host: " | |
| 512 << alternative_service.host_port_pair().host() | |
| 513 << " port: " << alternative_service.host_port_pair().port() << ")"; | |
| 514 | |
| 515 DCHECK(!request_info.url.SchemeIs("ftp")); | |
| 516 HostPortPair alternative_destination(alternative_service.host_port_pair()); | |
| 517 ignore_result( | |
| 518 ApplyHostMappingRules(request_info.url, &alternative_destination)); | |
| 519 | |
| 520 alternative_job_.reset(job_factory_->CreateJob( | |
| 521 this, ALTERNATIVE, session_, request_info, priority, server_ssl_config, | |
| 522 proxy_ssl_config, alternative_destination, origin_url, | |
| 523 alternative_service, net_log.net_log())); | |
| 524 AttachJob(alternative_job_.get()); | |
| 525 | |
| 526 main_job_->WaitFor(alternative_job_.get()); | |
| 527 // Make sure to wait until we call WaitFor(), before starting | |
| 528 // |alternative_job|, otherwise |alternative_job| will not notify |job| | |
| 529 // appropriately. | |
| 530 alternative_job_->Start(request_->stream_type()); | |
| 531 } | |
| 532 // Even if |alternative_job| has already finished, it will not have notified | |
| 533 // the request yet, since we defer that to the next iteration of the | |
| 534 // MessageLoop, so starting |main_job_| is always safe. | |
| 535 main_job_->Start(request_->stream_type()); | |
| 536 } | |
| 537 | |
| 538 void HttpStreamFactoryImpl::JobController::AttachJob(Job* job) { | |
| 539 DCHECK(job); | |
| 540 factory_->request_map_[job] = request_; | |
| 541 } | |
| 542 | |
| 543 void HttpStreamFactoryImpl::JobController::BindJob(Job* job) { | |
| 544 DCHECK(request_); | |
| 545 DCHECK(job); | |
| 546 DCHECK(job == alternative_job_.get() || job == main_job_.get()); | |
| 547 DCHECK(!job_bound_); | |
| 548 DCHECK(!bound_job_); | |
| 549 | |
| 550 job_bound_ = true; | |
| 551 bound_job_ = job; | |
| 552 factory_->request_map_.erase(job); | |
| 553 | |
| 554 request_->net_log().AddEvent( | |
| 555 NetLog::TYPE_HTTP_STREAM_REQUEST_BOUND_TO_JOB, | |
| 556 job->net_log().source().ToEventParametersCallback()); | |
| 557 job->net_log().AddEvent( | |
| 558 NetLog::TYPE_HTTP_STREAM_JOB_BOUND_TO_REQUEST, | |
| 559 request_->net_log().source().ToEventParametersCallback()); | |
| 560 | |
| 561 OrphanUnboundJob(); | |
| 562 } | |
| 563 | |
| 564 void HttpStreamFactoryImpl::JobController::CancelJobs() { | |
| 565 DCHECK(request_); | |
| 566 RemoveRequestFromSpdySessionRequestMap(); | |
| 567 if (job_bound_) | |
| 568 return; | |
| 569 if (alternative_job_) { | |
| 570 factory_->request_map_.erase(alternative_job_.get()); | |
| 571 alternative_job_.reset(); | |
| 572 } | |
| 573 if (main_job_) { | |
| 574 factory_->request_map_.erase(main_job_.get()); | |
| 575 main_job_.reset(); | |
| 576 } | |
| 577 } | |
| 578 | |
| 579 void HttpStreamFactoryImpl::JobController::OrphanUnboundJob() { | |
| 580 DCHECK(request_); | |
| 581 RemoveRequestFromSpdySessionRequestMap(); | |
| 582 | |
| 583 DCHECK(bound_job_); | |
| 584 if (bound_job_->job_type() == MAIN && alternative_job_) { | |
| 585 factory_->request_map_.erase(alternative_job_.get()); | |
| 586 alternative_job_->Orphan(); | |
| 587 } else if (bound_job_->job_type() == ALTERNATIVE && main_job_) { | |
| 588 // Orphan main job. | |
| 589 factory_->request_map_.erase(main_job_.get()); | |
| 590 main_job_->Orphan(); | |
| 591 } | |
| 592 } | |
| 593 | |
| 594 void HttpStreamFactoryImpl::JobController::OnJobSucceeded(Job* job) { | |
| 595 // |job| should only be nullptr if we're being serviced by a late bound | |
| 596 // SpdySession (one that was not created by a job in our |jobs_| set). | |
| 597 if (!job) { | |
| 598 DCHECK(!bound_job_); | |
| 599 // NOTE(willchan): We do *NOT* call OrphanUnboundJob() here. The reason is | |
| 600 // because we *WANT* to cancel the unnecessary Jobs from other requests if | |
| 601 // another Job completes first. | |
| 602 // TODO(mbelshe): Revisit this when we implement ip connection pooling of | |
| 603 // SpdySessions. Do we want to orphan the jobs for a different hostname so | |
| 604 // they complete? Or do we want to prevent connecting a new SpdySession if | |
| 605 // we've already got one available for a different hostname where the ip | |
| 606 // address matches up? | |
| 607 CancelJobs(); | |
| 608 return; | |
| 609 } | |
| 610 if (!bound_job_) { | |
| 611 if (main_job_ && alternative_job_) { | |
| 612 job->ReportJobSucceededForRequest(); | |
| 613 // Notify all the other jobs that this one succeeded. | |
| 614 if (job->job_type() == MAIN) { | |
| 615 alternative_job_->MarkOtherJobComplete(*job); | |
| 616 } else { | |
| 617 DCHECK(job->job_type() == ALTERNATIVE); | |
| 618 main_job_->MarkOtherJobComplete(*job); | |
| 619 } | |
| 620 } | |
| 621 BindJob(job); | |
| 622 return; | |
| 623 } | |
| 624 DCHECK(bound_job_); | |
| 625 } | |
| 626 | |
| 627 void HttpStreamFactoryImpl::JobController::MarkRequestComplete( | |
| 628 bool was_npn_negotiated, | |
| 629 NextProto protocol_negotiated, | |
| 630 bool using_spdy) { | |
| 631 if (request_) | |
| 632 request_->Complete(was_npn_negotiated, protocol_negotiated, using_spdy); | |
| 633 } | |
| 634 | |
| 635 void HttpStreamFactoryImpl::JobController::MaybeNotifyFactoryOfCompletion() { | |
| 636 if (!request_ && !main_job_ && !alternative_job_) { | |
| 637 DCHECK(!bound_job_); | |
| 638 factory_->OnJobControllerComplete(this); | |
| 639 } | |
| 640 } | |
| 641 | |
| 642 GURL HttpStreamFactoryImpl::JobController::ApplyHostMappingRules( | |
| 643 const GURL& url, | |
| 644 HostPortPair* endpoint) { | |
| 645 const HostMappingRules* mapping_rules = session_->params().host_mapping_rules; | |
| 646 if (mapping_rules && mapping_rules->RewriteHost(endpoint)) { | |
| 647 url::Replacements<char> replacements; | |
| 648 const std::string port_str = base::UintToString(endpoint->port()); | |
| 649 replacements.SetPort(port_str.c_str(), url::Component(0, port_str.size())); | |
| 650 replacements.SetHost(endpoint->host().c_str(), | |
| 651 url::Component(0, endpoint->host().size())); | |
| 652 return url.ReplaceComponents(replacements); | |
| 653 } | |
| 654 return url; | |
| 655 } | |
| 656 | |
| 657 bool HttpStreamFactoryImpl::JobController::IsQuicWhitelistedForHost( | |
| 658 const std::string& host) { | |
| 659 bool whitelist_needed = false; | |
| 660 for (QuicVersion version : session_->params().quic_supported_versions) { | |
| 661 if (version <= QUIC_VERSION_30) { | |
| 662 whitelist_needed = true; | |
| 663 break; | |
| 664 } | |
| 665 } | |
| 666 | |
| 667 // The QUIC whitelist is not needed in QUIC versions after 30. | |
| 668 if (!whitelist_needed) | |
| 669 return true; | |
| 670 | |
| 671 if (session_->params().transport_security_state->IsGooglePinnedHost(host)) | |
| 672 return true; | |
| 673 | |
| 674 return ContainsKey(session_->params().quic_host_whitelist, | |
| 675 base::ToLowerASCII(host)); | |
| 676 } | |
| 677 | |
| 678 AlternativeService | |
| 679 HttpStreamFactoryImpl::JobController::GetAlternativeServiceFor( | |
| 680 const HttpRequestInfo& request_info, | |
| 681 HttpStreamRequest::Delegate* delegate, | |
| 682 HttpStreamRequest::StreamType stream_type) { | |
| 683 AlternativeService alternative_service = | |
| 684 GetAlternativeServiceForInternal(request_info, delegate, stream_type); | |
| 685 AlternativeServiceType type; | |
| 686 if (alternative_service.protocol == UNINITIALIZED_ALTERNATE_PROTOCOL) { | |
| 687 type = NO_ALTERNATIVE_SERVICE; | |
| 688 } else if (alternative_service.protocol == QUIC) { | |
| 689 if (request_info.url.host() == alternative_service.host) { | |
| 690 type = QUIC_SAME_DESTINATION; | |
| 691 } else { | |
| 692 type = QUIC_DIFFERENT_DESTINATION; | |
| 693 } | |
| 694 } else { | |
| 695 if (request_info.url.host() == alternative_service.host) { | |
| 696 type = NOT_QUIC_SAME_DESTINATION; | |
| 697 } else { | |
| 698 type = NOT_QUIC_DIFFERENT_DESTINATION; | |
| 699 } | |
| 700 } | |
| 701 UMA_HISTOGRAM_ENUMERATION("Net.AlternativeServiceTypeForRequest", type, | |
| 702 MAX_ALTERNATIVE_SERVICE_TYPE); | |
| 703 return alternative_service; | |
| 704 } | |
| 705 | |
| 706 AlternativeService | |
| 707 HttpStreamFactoryImpl::JobController::GetAlternativeServiceForInternal( | |
| 708 const HttpRequestInfo& request_info, | |
| 709 HttpStreamRequest::Delegate* delegate, | |
| 710 HttpStreamRequest::StreamType stream_type) { | |
| 711 GURL original_url = request_info.url; | |
| 712 | |
| 713 if (original_url.SchemeIs("ftp")) | |
| 714 return AlternativeService(); | |
| 715 | |
| 716 if (!session_->params().enable_alternative_service_for_insecure_origins && | |
| 717 !original_url.SchemeIs("https")) | |
| 718 return AlternativeService(); | |
| 719 | |
| 720 url::SchemeHostPort origin(original_url); | |
| 721 HttpServerProperties& http_server_properties = | |
| 722 *session_->http_server_properties(); | |
| 723 const AlternativeServiceVector alternative_service_vector = | |
| 724 http_server_properties.GetAlternativeServices(origin); | |
| 725 if (alternative_service_vector.empty()) | |
| 726 return AlternativeService(); | |
| 727 | |
| 728 bool quic_advertised = false; | |
| 729 bool quic_all_broken = true; | |
| 730 | |
| 731 const bool enable_different_host = | |
| 732 session_->params().enable_alternative_service_with_different_host; | |
| 733 | |
| 734 // First Alt-Svc that is not marked as broken. | |
| 735 AlternativeService first_alternative_service; | |
| 736 | |
| 737 for (const AlternativeService& alternative_service : | |
| 738 alternative_service_vector) { | |
| 739 DCHECK(IsAlternateProtocolValid(alternative_service.protocol)); | |
| 740 if (!quic_advertised && alternative_service.protocol == QUIC) | |
| 741 quic_advertised = true; | |
| 742 if (http_server_properties.IsAlternativeServiceBroken( | |
| 743 alternative_service)) { | |
| 744 HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_BROKEN); | |
| 745 continue; | |
| 746 } | |
| 747 | |
| 748 if (origin.host() != alternative_service.host && !enable_different_host) | |
| 749 continue; | |
| 750 | |
| 751 // Some shared unix systems may have user home directories (like | |
| 752 // http://foo.com/~mike) which allow users to emit headers. This is a bad | |
| 753 // idea already, but with Alternate-Protocol, it provides the ability for a | |
| 754 // single user on a multi-user system to hijack the alternate protocol. | |
| 755 // These systems also enforce ports <1024 as restricted ports. So don't | |
| 756 // allow protocol upgrades to user-controllable ports. | |
| 757 const int kUnrestrictedPort = 1024; | |
| 758 if (!session_->params().enable_user_alternate_protocol_ports && | |
| 759 (alternative_service.port >= kUnrestrictedPort && | |
| 760 origin.port() < kUnrestrictedPort)) | |
| 761 continue; | |
| 762 | |
| 763 if (alternative_service.protocol >= NPN_SPDY_MINIMUM_VERSION && | |
| 764 alternative_service.protocol <= NPN_SPDY_MAXIMUM_VERSION) { | |
| 765 if (!HttpStreamFactory::spdy_enabled()) | |
| 766 continue; | |
| 767 | |
| 768 // TODO(bnc): Re-enable when https://crbug.com/615413 is fixed. | |
| 769 if (origin.host() != alternative_service.host) | |
| 770 continue; | |
| 771 | |
| 772 // Cache this entry if we don't have a non-broken Alt-Svc yet. | |
| 773 if (first_alternative_service.protocol == | |
| 774 UNINITIALIZED_ALTERNATE_PROTOCOL) | |
| 775 first_alternative_service = alternative_service; | |
| 776 continue; | |
| 777 } | |
| 778 | |
| 779 DCHECK_EQ(QUIC, alternative_service.protocol); | |
| 780 quic_all_broken = false; | |
| 781 if (!session_->params().enable_quic) | |
| 782 continue; | |
| 783 | |
| 784 if (!IsQuicWhitelistedForHost(origin.host())) | |
| 785 continue; | |
| 786 | |
| 787 if (stream_type == HttpStreamRequest::BIDIRECTIONAL_STREAM && | |
| 788 session_->params().quic_disable_bidirectional_streams) { | |
| 789 continue; | |
| 790 } | |
| 791 | |
| 792 if (session_->quic_stream_factory()->IsQuicDisabled( | |
| 793 alternative_service.port)) | |
| 794 continue; | |
| 795 | |
| 796 if (!original_url.SchemeIs("https")) | |
| 797 continue; | |
| 798 | |
| 799 // Check whether there is an existing QUIC session to use for this origin. | |
| 800 HostPortPair mapped_origin(origin.host(), origin.port()); | |
| 801 ignore_result(ApplyHostMappingRules(original_url, &mapped_origin)); | |
| 802 QuicServerId server_id(mapped_origin, request_info.privacy_mode); | |
| 803 | |
| 804 HostPortPair destination(alternative_service.host_port_pair()); | |
| 805 ignore_result(ApplyHostMappingRules(original_url, &destination)); | |
| 806 | |
| 807 if (session_->quic_stream_factory()->CanUseExistingSession(server_id, | |
| 808 destination)) { | |
| 809 return alternative_service; | |
| 810 } | |
| 811 | |
| 812 // Cache this entry if we don't have a non-broken Alt-Svc yet. | |
| 813 if (first_alternative_service.protocol == UNINITIALIZED_ALTERNATE_PROTOCOL) | |
| 814 first_alternative_service = alternative_service; | |
| 815 } | |
| 816 | |
| 817 // Ask delegate to mark QUIC as broken for the origin. | |
| 818 if (quic_advertised && quic_all_broken && delegate != nullptr) | |
| 819 delegate->OnQuicBroken(); | |
| 820 | |
| 821 return first_alternative_service; | |
| 822 } | |
| 823 } | |
| OLD | NEW |