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