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_job.h"
6
7 #include <algorithm>
8 #include <string>
9
10 #include "base/bind.h"
11 #include "base/callback_helpers.h"
12 #include "base/check_op.h"
13 #include "base/feature_list.h"
14 #include "base/location.h"
15 #include "base/metrics/histogram_macros.h"
16 #include "base/metrics/sparse_histogram.h"
17 #include "base/notreached.h"
18 #include "base/single_thread_task_runner.h"
19 #include "base/stl_util.h"
20 #include "base/strings/string_number_conversions.h"
21 #include "base/strings/string_util.h"
22 #include "base/threading/thread_task_runner_handle.h"
23 #include "base/trace_event/trace_event.h"
24 #include "base/values.h"
25 #include "build/build_config.h"
26 #include "net/base/port_util.h"
27 #include "net/base/proxy_delegate.h"
28 #include "net/base/trace_constants.h"
29 #include "net/cert/cert_verifier.h"
30 #include "net/http/bidirectional_stream_impl.h"
31 #include "net/http/http_basic_stream.h"
32 #include "net/http/http_network_session.h"
33 #include "net/http/http_proxy_client_socket.h"
34 #include "net/http/http_request_info.h"
35 #include "net/http/http_server_properties.h"
36 #include "net/http/http_stream_factory.h"
37 #include "net/http/proxy_fallback.h"
38 #include "net/log/net_log.h"
39 #include "net/log/net_log_capture_mode.h"
40 #include "net/log/net_log_event_type.h"
41 #include "net/log/net_log_source.h"
42 #include "net/log/net_log_source_type.h"
43 #include "net/quic/bidirectional_stream_quic_impl.h"
44 #include "net/quic/quic_http_stream.h"
45 #include "net/socket/client_socket_handle.h"
46 #include "net/socket/client_socket_pool_manager.h"
47 #include "net/socket/connect_job.h"
48 #include "net/socket/ssl_client_socket.h"
49 #include "net/socket/stream_socket.h"
50 #include "net/spdy/bidirectional_stream_spdy_impl.h"
51 #include "net/spdy/http2_push_promise_index.h"
52 #include "net/spdy/spdy_http_stream.h"
53 #include "net/spdy/spdy_session.h"
54 #include "net/ssl/ssl_cert_request_info.h"
55 #include "net/third_party/quiche/src/spdy/core/spdy_protocol.h"
56 #include "url/url_constants.h"
57
58 namespace net {
59
60 namespace {
61
62 // Experiment to preconnect only one connection if HttpServerProperties is
63 // not supported or initialized.
64 const base::Feature kLimitEarlyPreconnectsExperiment{
65 "LimitEarlyPreconnects", base::FEATURE_ENABLED_BY_DEFAULT};
66
67 } // namespace
68
69 // Returns parameters associated with the start of a HTTP stream job.
NetLogHttpStreamJobParams(const NetLogSource & source,const GURL & original_url,const GURL & url,bool expect_spdy,bool using_quic,RequestPriority priority)70 base::Value NetLogHttpStreamJobParams(const NetLogSource& source,
71 const GURL& original_url,
72 const GURL& url,
73 bool expect_spdy,
74 bool using_quic,
75 RequestPriority priority) {
76 base::Value dict(base::Value::Type::DICTIONARY);
77 if (source.IsValid())
78 source.AddToEventParameters(&dict);
79 dict.SetStringKey("original_url", original_url.GetOrigin().spec());
80 dict.SetStringKey("url", url.GetOrigin().spec());
81 dict.SetBoolKey("expect_spdy", expect_spdy);
82 dict.SetBoolKey("using_quic", using_quic);
83 dict.SetStringKey("priority", RequestPriorityToString(priority));
84 return dict;
85 }
86
87 // Returns parameters associated with the Proto (with NPN negotiation) of a HTTP
88 // stream.
NetLogHttpStreamProtoParams(NextProto negotiated_protocol)89 base::Value NetLogHttpStreamProtoParams(NextProto negotiated_protocol) {
90 base::Value dict(base::Value::Type::DICTIONARY);
91
92 dict.SetStringKey("proto", NextProtoToString(negotiated_protocol));
93 return dict;
94 }
95
Job(Delegate * delegate,JobType job_type,HttpNetworkSession * session,const HttpRequestInfo & request_info,RequestPriority priority,const ProxyInfo & proxy_info,const SSLConfig & server_ssl_config,const SSLConfig & proxy_ssl_config,HostPortPair destination,GURL origin_url,NextProto alternative_protocol,quic::ParsedQuicVersion quic_version,bool is_websocket,bool enable_ip_based_pooling,NetLog * net_log)96 HttpStreamFactory::Job::Job(Delegate* delegate,
97 JobType job_type,
98 HttpNetworkSession* session,
99 const HttpRequestInfo& request_info,
100 RequestPriority priority,
101 const ProxyInfo& proxy_info,
102 const SSLConfig& server_ssl_config,
103 const SSLConfig& proxy_ssl_config,
104 HostPortPair destination,
105 GURL origin_url,
106 NextProto alternative_protocol,
107 quic::ParsedQuicVersion quic_version,
108 bool is_websocket,
109 bool enable_ip_based_pooling,
110 NetLog* net_log)
111 : request_info_(request_info),
112 priority_(priority),
113 proxy_info_(proxy_info),
114 server_ssl_config_(server_ssl_config),
115 proxy_ssl_config_(proxy_ssl_config),
116 net_log_(
117 NetLogWithSource::Make(net_log, NetLogSourceType::HTTP_STREAM_JOB)),
118 io_callback_(
119 base::BindRepeating(&Job::OnIOComplete, base::Unretained(this))),
120 connection_(new ClientSocketHandle),
121 session_(session),
122 next_state_(STATE_NONE),
123 destination_(destination),
124 origin_url_(origin_url),
125 is_websocket_(is_websocket),
126 try_websocket_over_http2_(is_websocket_ &&
127 origin_url_.SchemeIs(url::kWssScheme) &&
128 proxy_info_.is_direct() &&
129 session_->params().enable_websocket_over_http2),
130 // Don't use IP connection pooling for HTTP over HTTPS proxies. It doesn't
131 // get us much, and testing it is more effort than its worth.
132 enable_ip_based_pooling_(
133 enable_ip_based_pooling &&
134 !(proxy_info_.proxy_server().is_secure_http_like() &&
135 origin_url_.SchemeIs(url::kHttpScheme))),
136 delegate_(delegate),
137 job_type_(job_type),
138 using_ssl_(origin_url_.SchemeIs(url::kHttpsScheme) ||
139 origin_url_.SchemeIs(url::kWssScheme)),
140 using_quic_(alternative_protocol == kProtoQUIC ||
141 (ShouldForceQuic(session,
142 destination,
143 origin_url,
144 proxy_info,
145 using_ssl_))),
146 quic_version_(quic_version),
147 expect_spdy_(alternative_protocol == kProtoHTTP2 && !using_quic_),
148 using_spdy_(false),
149 should_reconsider_proxy_(false),
150 quic_request_(session_->quic_stream_factory()),
151 expect_on_quic_host_resolution_(false),
152 using_existing_quic_session_(false),
153 establishing_tunnel_(false),
154 was_alpn_negotiated_(false),
155 negotiated_protocol_(kProtoUnknown),
156 num_streams_(0),
157 pushed_stream_id_(kNoPushedStreamFound),
158 spdy_session_direct_(
159 !(proxy_info.is_https() && origin_url_.SchemeIs(url::kHttpScheme))),
160 spdy_session_key_(
161 using_quic_ ? SpdySessionKey()
162 : GetSpdySessionKey(spdy_session_direct_,
163 proxy_info_.proxy_server(),
164 origin_url_,
165 request_info_.privacy_mode,
166 request_info_.socket_tag,
167 request_info_.network_isolation_key,
168 request_info_.disable_secure_dns)),
169 stream_type_(HttpStreamRequest::BIDIRECTIONAL_STREAM),
170 init_connection_already_resumed_(false) {
171 // QUIC can only be spoken to servers, never to proxies.
172 if (alternative_protocol == kProtoQUIC)
173 DCHECK(proxy_info_.is_direct());
174
175 // The Job is forced to use QUIC without a designated version, try the
176 // preferred QUIC version that is supported by default.
177 if (quic_version_ == quic::ParsedQuicVersion::Unsupported() &&
178 ShouldForceQuic(session, destination, origin_url, proxy_info,
179 using_ssl_)) {
180 quic_version_ =
181 session->context().quic_context->params()->supported_versions[0];
182 }
183
184 if (using_quic_)
185 DCHECK_NE(quic_version_, quic::ParsedQuicVersion::Unsupported());
186
187 DCHECK(session);
188 if (alternative_protocol != kProtoUnknown) {
189 // If the alternative service protocol is specified, then the job type must
190 // be either ALTERNATIVE or PRECONNECT.
191 DCHECK(job_type_ == ALTERNATIVE || job_type_ == PRECONNECT);
192 }
193
194 if (expect_spdy_) {
195 DCHECK(origin_url_.SchemeIs(url::kHttpsScheme));
196 }
197 if (using_quic_) {
198 DCHECK(session_->IsQuicEnabled());
199 }
200 if (job_type_ == PRECONNECT || is_websocket_) {
201 DCHECK(request_info_.socket_tag == SocketTag());
202 }
203 if (is_websocket_) {
204 DCHECK(origin_url_.SchemeIsWSOrWSS());
205 } else {
206 DCHECK(!origin_url_.SchemeIsWSOrWSS());
207 }
208 }
209
~Job()210 HttpStreamFactory::Job::~Job() {
211 net_log_.EndEvent(NetLogEventType::HTTP_STREAM_JOB);
212
213 // When we're in a partially constructed state, waiting for the user to
214 // provide certificate handling information or authentication, we can't reuse
215 // this stream at all.
216 if (next_state_ == STATE_WAITING_USER_ACTION) {
217 connection_->socket()->Disconnect();
218 connection_.reset();
219 }
220
221 // The stream could be in a partial state. It is not reusable.
222 if (stream_.get() && next_state_ != STATE_DONE)
223 stream_->Close(true /* not reusable */);
224 }
225
Start(HttpStreamRequest::StreamType stream_type)226 void HttpStreamFactory::Job::Start(HttpStreamRequest::StreamType stream_type) {
227 stream_type_ = stream_type;
228 StartInternal();
229 }
230
Preconnect(int num_streams)231 int HttpStreamFactory::Job::Preconnect(int num_streams) {
232 DCHECK_GT(num_streams, 0);
233 HttpServerProperties* http_server_properties =
234 session_->http_server_properties();
235 DCHECK(http_server_properties);
236 // Preconnect one connection if either of the following is true:
237 // (1) kLimitEarlyPreconnectsStreamExperiment is turned on,
238 // HttpServerProperties is not initialized, and url scheme is cryptographic.
239 // (2) The server supports H2 or QUIC.
240 bool connect_one_stream =
241 base::FeatureList::IsEnabled(kLimitEarlyPreconnectsExperiment) &&
242 !http_server_properties->IsInitialized() &&
243 request_info_.url.SchemeIsCryptographic();
244 if (connect_one_stream || http_server_properties->SupportsRequestPriority(
245 url::SchemeHostPort(request_info_.url),
246 request_info_.network_isolation_key)) {
247 num_streams_ = 1;
248 } else {
249 num_streams_ = num_streams;
250 }
251 return StartInternal();
252 }
253
RestartTunnelWithProxyAuth()254 int HttpStreamFactory::Job::RestartTunnelWithProxyAuth() {
255 DCHECK(establishing_tunnel_);
256 DCHECK(restart_with_auth_callback_);
257
258 std::move(restart_with_auth_callback_).Run();
259 return ERR_IO_PENDING;
260 }
261
GetLoadState() const262 LoadState HttpStreamFactory::Job::GetLoadState() const {
263 switch (next_state_) {
264 case STATE_INIT_CONNECTION_COMPLETE:
265 case STATE_CREATE_STREAM_COMPLETE:
266 return using_quic_ ? LOAD_STATE_CONNECTING : connection_->GetLoadState();
267 default:
268 return LOAD_STATE_IDLE;
269 }
270 }
271
Resume()272 void HttpStreamFactory::Job::Resume() {
273 DCHECK_EQ(job_type_, MAIN);
274 DCHECK_EQ(next_state_, STATE_WAIT_COMPLETE);
275 OnIOComplete(OK);
276 }
277
Orphan()278 void HttpStreamFactory::Job::Orphan() {
279 DCHECK_EQ(job_type_, ALTERNATIVE);
280 net_log_.AddEvent(NetLogEventType::HTTP_STREAM_JOB_ORPHANED);
281
282 // Watching for SPDY sessions isn't supported on orphaned jobs.
283 // TODO(mmenke): Fix that.
284 spdy_session_request_.reset();
285 }
286
SetPriority(RequestPriority priority)287 void HttpStreamFactory::Job::SetPriority(RequestPriority priority) {
288 priority_ = priority;
289 // Ownership of |connection_| is passed to the newly created stream
290 // or H2 session in DoCreateStream(), and the consumer is not
291 // notified immediately, so this call may occur when |connection_|
292 // is null.
293 //
294 // Note that streams are created without a priority associated with them,
295 // and it is up to the consumer to set their priority via
296 // HttpStream::InitializeStream(). So there is no need for this code
297 // to propagate priority changes to the newly created stream.
298 if (connection_ && connection_->is_initialized())
299 connection_->SetPriority(priority);
300 // TODO(akalin): Maybe Propagate this to the preconnect state.
301 }
302
was_alpn_negotiated() const303 bool HttpStreamFactory::Job::was_alpn_negotiated() const {
304 return was_alpn_negotiated_;
305 }
306
negotiated_protocol() const307 NextProto HttpStreamFactory::Job::negotiated_protocol() const {
308 return negotiated_protocol_;
309 }
310
using_spdy() const311 bool HttpStreamFactory::Job::using_spdy() const {
312 return using_spdy_;
313 }
314
EstimateMemoryUsage() const315 size_t HttpStreamFactory::Job::EstimateMemoryUsage() const {
316 StreamSocket::SocketMemoryStats stats;
317 if (connection_)
318 connection_->DumpMemoryStats(&stats);
319 return stats.total_size;
320 }
321
server_ssl_config() const322 const SSLConfig& HttpStreamFactory::Job::server_ssl_config() const {
323 return server_ssl_config_;
324 }
325
proxy_ssl_config() const326 const SSLConfig& HttpStreamFactory::Job::proxy_ssl_config() const {
327 return proxy_ssl_config_;
328 }
329
proxy_info() const330 const ProxyInfo& HttpStreamFactory::Job::proxy_info() const {
331 return proxy_info_;
332 }
333
resolve_error_info() const334 ResolveErrorInfo HttpStreamFactory::Job::resolve_error_info() const {
335 return resolve_error_info_;
336 }
337
GetSSLInfo(SSLInfo * ssl_info)338 void HttpStreamFactory::Job::GetSSLInfo(SSLInfo* ssl_info) {
339 DCHECK(using_ssl_);
340 DCHECK(!establishing_tunnel_);
341 DCHECK(connection_.get() && connection_->socket());
342 connection_->socket()->GetSSLInfo(ssl_info);
343 }
344
345 // static
ShouldForceQuic(HttpNetworkSession * session,const HostPortPair & destination,const GURL & origin_url,const ProxyInfo & proxy_info,bool using_ssl)346 bool HttpStreamFactory::Job::ShouldForceQuic(HttpNetworkSession* session,
347 const HostPortPair& destination,
348 const GURL& origin_url,
349 const ProxyInfo& proxy_info,
350 bool using_ssl) {
351 if (!session->IsQuicEnabled())
352 return false;
353 // If this is going through a QUIC proxy, only force QUIC for insecure
354 // requests. If the request is secure, a tunnel will be needed, and those are
355 // handled by the socket pools, using an HttpProxyConnectJob.
356 if (proxy_info.is_quic())
357 return !using_ssl;
358 const QuicParams* quic_params = session->context().quic_context->params();
359 return (base::Contains(quic_params->origins_to_force_quic_on,
360 HostPortPair()) ||
361 base::Contains(quic_params->origins_to_force_quic_on, destination)) &&
362 proxy_info.is_direct() && origin_url.SchemeIs(url::kHttpsScheme);
363 }
364
365 // static
GetSpdySessionKey(bool spdy_session_direct,const ProxyServer & proxy_server,const GURL & origin_url,PrivacyMode privacy_mode,const SocketTag & socket_tag,const NetworkIsolationKey & network_isolation_key,bool disable_secure_dns)366 SpdySessionKey HttpStreamFactory::Job::GetSpdySessionKey(
367 bool spdy_session_direct,
368 const ProxyServer& proxy_server,
369 const GURL& origin_url,
370 PrivacyMode privacy_mode,
371 const SocketTag& socket_tag,
372 const NetworkIsolationKey& network_isolation_key,
373 bool disable_secure_dns) {
374 // In the case that we're using an HTTPS proxy for an HTTP url, look for a
375 // HTTP/2 proxy session *to* the proxy, instead of to the origin server.
376 if (!spdy_session_direct) {
377 return SpdySessionKey(proxy_server.host_port_pair(), ProxyServer::Direct(),
378 PRIVACY_MODE_DISABLED,
379 SpdySessionKey::IsProxySession::kTrue, socket_tag,
380 network_isolation_key, disable_secure_dns);
381 }
382 return SpdySessionKey(HostPortPair::FromURL(origin_url), proxy_server,
383 privacy_mode, SpdySessionKey::IsProxySession::kFalse,
384 socket_tag, network_isolation_key, disable_secure_dns);
385 }
386
CanUseExistingSpdySession() const387 bool HttpStreamFactory::Job::CanUseExistingSpdySession() const {
388 DCHECK(!using_quic_);
389
390 if (proxy_info_.is_direct() &&
391 session_->http_server_properties()->RequiresHTTP11(
392 url::SchemeHostPort(request_info_.url),
393 request_info_.network_isolation_key)) {
394 return false;
395 }
396
397 if (is_websocket_)
398 return try_websocket_over_http2_;
399
400 DCHECK(origin_url_.SchemeIsHTTPOrHTTPS());
401
402 // We need to make sure that if a HTTP/2 session was created for
403 // https://somehost/ then we do not use that session for http://somehost:443/.
404 // The only time we can use an existing session is if the request URL is
405 // https (the normal case) or if we are connecting to a HTTP/2 proxy.
406 // https://crbug.com/133176
407 return origin_url_.SchemeIs(url::kHttpsScheme) ||
408 proxy_info_.proxy_server().is_https();
409 }
410
OnStreamReadyCallback()411 void HttpStreamFactory::Job::OnStreamReadyCallback() {
412 DCHECK(stream_.get());
413 DCHECK_NE(job_type_, PRECONNECT);
414 DCHECK(!is_websocket_ || try_websocket_over_http2_);
415
416 MaybeCopyConnectionAttemptsFromSocketOrHandle();
417
418 delegate_->OnStreamReady(this, server_ssl_config_);
419 // |this| may be deleted after this call.
420 }
421
OnWebSocketHandshakeStreamReadyCallback()422 void HttpStreamFactory::Job::OnWebSocketHandshakeStreamReadyCallback() {
423 DCHECK(websocket_stream_);
424 DCHECK_NE(job_type_, PRECONNECT);
425 DCHECK(is_websocket_);
426
427 MaybeCopyConnectionAttemptsFromSocketOrHandle();
428
429 delegate_->OnWebSocketHandshakeStreamReady(
430 this, server_ssl_config_, proxy_info_, std::move(websocket_stream_));
431 // |this| may be deleted after this call.
432 }
433
OnBidirectionalStreamImplReadyCallback()434 void HttpStreamFactory::Job::OnBidirectionalStreamImplReadyCallback() {
435 DCHECK(bidirectional_stream_impl_);
436
437 MaybeCopyConnectionAttemptsFromSocketOrHandle();
438
439 delegate_->OnBidirectionalStreamImplReady(this, server_ssl_config_,
440 proxy_info_);
441 // |this| may be deleted after this call.
442 }
443
OnStreamFailedCallback(int result)444 void HttpStreamFactory::Job::OnStreamFailedCallback(int result) {
445 DCHECK_NE(job_type_, PRECONNECT);
446
447 MaybeCopyConnectionAttemptsFromSocketOrHandle();
448
449 delegate_->OnStreamFailed(this, result, server_ssl_config_);
450 // |this| may be deleted after this call.
451 }
452
OnCertificateErrorCallback(int result,const SSLInfo & ssl_info)453 void HttpStreamFactory::Job::OnCertificateErrorCallback(
454 int result,
455 const SSLInfo& ssl_info) {
456 DCHECK_NE(job_type_, PRECONNECT);
457 DCHECK(!spdy_session_request_);
458
459 MaybeCopyConnectionAttemptsFromSocketOrHandle();
460
461 delegate_->OnCertificateError(this, result, server_ssl_config_, ssl_info);
462 // |this| may be deleted after this call.
463 }
464
OnNeedsProxyAuthCallback(const HttpResponseInfo & response,HttpAuthController * auth_controller,base::OnceClosure restart_with_auth_callback)465 void HttpStreamFactory::Job::OnNeedsProxyAuthCallback(
466 const HttpResponseInfo& response,
467 HttpAuthController* auth_controller,
468 base::OnceClosure restart_with_auth_callback) {
469 DCHECK_NE(job_type_, PRECONNECT);
470 DCHECK(establishing_tunnel_);
471 DCHECK(!restart_with_auth_callback_);
472
473 restart_with_auth_callback_ = std::move(restart_with_auth_callback);
474
475 // This is called out of band, so need to abort the SpdySessionRequest to
476 // prevent being passed a new session while waiting on proxy auth credentials.
477 spdy_session_request_.reset();
478
479 delegate_->OnNeedsProxyAuth(this, response, server_ssl_config_, proxy_info_,
480 auth_controller);
481 // |this| may be deleted after this call.
482 }
483
OnNeedsClientAuthCallback(SSLCertRequestInfo * cert_info)484 void HttpStreamFactory::Job::OnNeedsClientAuthCallback(
485 SSLCertRequestInfo* cert_info) {
486 DCHECK_NE(job_type_, PRECONNECT);
487 DCHECK(!spdy_session_request_);
488
489 delegate_->OnNeedsClientAuth(this, server_ssl_config_, cert_info);
490 // |this| may be deleted after this call.
491 }
492
OnPreconnectsComplete()493 void HttpStreamFactory::Job::OnPreconnectsComplete() {
494 delegate_->OnPreconnectsComplete(this);
495 // |this| may be deleted after this call.
496 }
497
OnIOComplete(int result)498 void HttpStreamFactory::Job::OnIOComplete(int result) {
499 TRACE_EVENT0(NetTracingCategory(), "HttpStreamFactory::Job::OnIOComplete");
500 RunLoop(result);
501 }
502
RunLoop(int result)503 void HttpStreamFactory::Job::RunLoop(int result) {
504 TRACE_EVENT0(NetTracingCategory(), "HttpStreamFactory::Job::RunLoop");
505 result = DoLoop(result);
506
507 if (result == ERR_IO_PENDING)
508 return;
509
510 // Stop watching for new SpdySessions, to avoid receiving a new SPDY session
511 // while doing anything other than waiting to establish a connection.
512 spdy_session_request_.reset();
513
514 if (job_type_ == PRECONNECT) {
515 base::ThreadTaskRunnerHandle::Get()->PostTask(
516 FROM_HERE,
517 base::BindOnce(&HttpStreamFactory::Job::OnPreconnectsComplete,
518 ptr_factory_.GetWeakPtr()));
519 return;
520 }
521
522 if (IsCertificateError(result)) {
523 // Retrieve SSL information from the socket.
524 SSLInfo ssl_info;
525 GetSSLInfo(&ssl_info);
526
527 next_state_ = STATE_WAITING_USER_ACTION;
528 base::ThreadTaskRunnerHandle::Get()->PostTask(
529 FROM_HERE,
530 base::BindOnce(&HttpStreamFactory::Job::OnCertificateErrorCallback,
531 ptr_factory_.GetWeakPtr(), result, ssl_info));
532 return;
533 }
534
535 switch (result) {
536 case ERR_SSL_CLIENT_AUTH_CERT_NEEDED:
537 base::ThreadTaskRunnerHandle::Get()->PostTask(
538 FROM_HERE,
539 base::BindOnce(
540 &Job::OnNeedsClientAuthCallback, ptr_factory_.GetWeakPtr(),
541 base::RetainedRef(connection_->ssl_cert_request_info())));
542 return;
543
544 case OK:
545 next_state_ = STATE_DONE;
546 if (is_websocket_) {
547 DCHECK(websocket_stream_);
548 base::ThreadTaskRunnerHandle::Get()->PostTask(
549 FROM_HERE,
550 base::BindOnce(&Job::OnWebSocketHandshakeStreamReadyCallback,
551 ptr_factory_.GetWeakPtr()));
552 } else if (stream_type_ == HttpStreamRequest::BIDIRECTIONAL_STREAM) {
553 if (!bidirectional_stream_impl_) {
554 base::ThreadTaskRunnerHandle::Get()->PostTask(
555 FROM_HERE, base::BindOnce(&Job::OnStreamFailedCallback,
556 ptr_factory_.GetWeakPtr(), ERR_FAILED));
557 } else {
558 base::ThreadTaskRunnerHandle::Get()->PostTask(
559 FROM_HERE,
560 base::BindOnce(&Job::OnBidirectionalStreamImplReadyCallback,
561 ptr_factory_.GetWeakPtr()));
562 }
563 } else {
564 DCHECK(stream_.get());
565 base::ThreadTaskRunnerHandle::Get()->PostTask(
566 FROM_HERE, base::BindOnce(&Job::OnStreamReadyCallback,
567 ptr_factory_.GetWeakPtr()));
568 }
569 return;
570
571 default:
572 base::ThreadTaskRunnerHandle::Get()->PostTask(
573 FROM_HERE, base::BindOnce(&Job::OnStreamFailedCallback,
574 ptr_factory_.GetWeakPtr(), result));
575 return;
576 }
577 }
578
DoLoop(int result)579 int HttpStreamFactory::Job::DoLoop(int result) {
580 DCHECK_NE(next_state_, STATE_NONE);
581 int rv = result;
582 do {
583 State state = next_state_;
584 next_state_ = STATE_NONE;
585 switch (state) {
586 case STATE_START:
587 DCHECK_EQ(OK, rv);
588 rv = DoStart();
589 break;
590 case STATE_WAIT:
591 DCHECK_EQ(OK, rv);
592 rv = DoWait();
593 break;
594 case STATE_WAIT_COMPLETE:
595 rv = DoWaitComplete(rv);
596 break;
597 case STATE_INIT_CONNECTION:
598 DCHECK_EQ(OK, rv);
599 rv = DoInitConnection();
600 break;
601 case STATE_INIT_CONNECTION_COMPLETE:
602 rv = DoInitConnectionComplete(rv);
603 break;
604 case STATE_WAITING_USER_ACTION:
605 rv = DoWaitingUserAction(rv);
606 break;
607 case STATE_CREATE_STREAM:
608 DCHECK_EQ(OK, rv);
609 rv = DoCreateStream();
610 break;
611 case STATE_CREATE_STREAM_COMPLETE:
612 rv = DoCreateStreamComplete(rv);
613 break;
614 default:
615 NOTREACHED() << "bad state";
616 rv = ERR_FAILED;
617 break;
618 }
619 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
620 return rv;
621 }
622
StartInternal()623 int HttpStreamFactory::Job::StartInternal() {
624 CHECK_EQ(STATE_NONE, next_state_);
625 next_state_ = STATE_START;
626 RunLoop(OK);
627 return ERR_IO_PENDING;
628 }
629
DoStart()630 int HttpStreamFactory::Job::DoStart() {
631 const NetLogWithSource* net_log = delegate_->GetNetLog();
632
633 if (net_log) {
634 net_log_.BeginEvent(NetLogEventType::HTTP_STREAM_JOB, [&] {
635 return NetLogHttpStreamJobParams(net_log->source(), request_info_.url,
636 origin_url_, expect_spdy_, using_quic_,
637 priority_);
638 });
639 net_log->AddEventReferencingSource(
640 NetLogEventType::HTTP_STREAM_REQUEST_STARTED_JOB, net_log_.source());
641 }
642
643 // Don't connect to restricted ports.
644 if (!IsPortAllowedForScheme(destination_.port(),
645 request_info_.url.scheme_piece())) {
646 return ERR_UNSAFE_PORT;
647 }
648
649 if (!session_->params().enable_quic_proxies_for_https_urls &&
650 proxy_info_.is_quic() && request_info_.url.SchemeIsCryptographic()) {
651 return ERR_NOT_IMPLEMENTED;
652 }
653
654 next_state_ = STATE_WAIT;
655 return OK;
656 }
657
DoWait()658 int HttpStreamFactory::Job::DoWait() {
659 next_state_ = STATE_WAIT_COMPLETE;
660 bool should_wait = delegate_->ShouldWait(this);
661 net_log_.AddEntryWithBoolParams(NetLogEventType::HTTP_STREAM_JOB_WAITING,
662 NetLogEventPhase::BEGIN, "should_wait",
663 should_wait);
664 if (should_wait)
665 return ERR_IO_PENDING;
666
667 return OK;
668 }
669
DoWaitComplete(int result)670 int HttpStreamFactory::Job::DoWaitComplete(int result) {
671 net_log_.EndEvent(NetLogEventType::HTTP_STREAM_JOB_WAITING);
672 DCHECK_EQ(OK, result);
673 next_state_ = STATE_INIT_CONNECTION;
674 return OK;
675 }
676
ResumeInitConnection()677 void HttpStreamFactory::Job::ResumeInitConnection() {
678 if (init_connection_already_resumed_)
679 return;
680 DCHECK_EQ(next_state_, STATE_INIT_CONNECTION);
681 net_log_.AddEvent(NetLogEventType::HTTP_STREAM_JOB_RESUME_INIT_CONNECTION);
682 init_connection_already_resumed_ = true;
683 OnIOComplete(OK);
684 }
685
DoInitConnection()686 int HttpStreamFactory::Job::DoInitConnection() {
687 net_log_.BeginEvent(NetLogEventType::HTTP_STREAM_JOB_INIT_CONNECTION);
688 int result = DoInitConnectionImpl();
689 if (!expect_on_quic_host_resolution_) {
690 delegate_->OnConnectionInitialized(this, result);
691 }
692 return result;
693 }
694
DoInitConnectionImpl()695 int HttpStreamFactory::Job::DoInitConnectionImpl() {
696 DCHECK(!connection_->is_initialized());
697
698 if (using_quic_ && !proxy_info_.is_quic() && !proxy_info_.is_direct()) {
699 // QUIC can not be spoken to non-QUIC proxies. This error should not be
700 // user visible, because the non-alternative Job should be resumed.
701 return ERR_NO_SUPPORTED_PROXIES;
702 }
703
704 DCHECK(proxy_info_.proxy_server().is_valid());
705 next_state_ = STATE_INIT_CONNECTION_COMPLETE;
706
707 if (proxy_info_.is_secure_http_like()) {
708 // Disable network fetches for HTTPS proxies, since the network requests
709 // are probably going to need to go through the proxy too.
710 proxy_ssl_config_.disable_cert_verification_network_fetches = true;
711 }
712 if (using_ssl_) {
713 // Prior to HTTP/2 and SPDY, some servers use TLS renegotiation to request
714 // TLS client authentication after the HTTP request was sent. Allow
715 // renegotiation for only those connections.
716 //
717 // Note that this does NOT implement the provision in
718 // https://http2.github.io/http2-spec/#rfc.section.9.2.1 which allows the
719 // server to request a renegotiation immediately before sending the
720 // connection preface as waiting for the preface would cost the round trip
721 // that False Start otherwise saves.
722 server_ssl_config_.renego_allowed_default = true;
723 server_ssl_config_.renego_allowed_for_protos.push_back(kProtoHTTP11);
724 }
725
726 if (using_quic_)
727 return DoInitConnectionImplQuic();
728
729 // Check first if there is a pushed stream matching the request, or an HTTP/2
730 // connection this request can pool to. If so, then go straight to using
731 // that.
732 if (CanUseExistingSpdySession()) {
733 if (!is_websocket_) {
734 session_->spdy_session_pool()->push_promise_index()->ClaimPushedStream(
735 spdy_session_key_, origin_url_, request_info_,
736 &existing_spdy_session_, &pushed_stream_id_);
737 }
738 if (!existing_spdy_session_) {
739 if (!spdy_session_request_) {
740 // If not currently watching for an H2 session, use
741 // SpdySessionPool::RequestSession() to check for a session, and start
742 // watching for one.
743 bool should_throttle_connect = ShouldThrottleConnectForSpdy();
744 base::RepeatingClosure resume_callback =
745 should_throttle_connect
746 ? base::BindRepeating(
747 &HttpStreamFactory::Job::ResumeInitConnection,
748 ptr_factory_.GetWeakPtr())
749 : base::RepeatingClosure();
750
751 bool is_blocking_request_for_session;
752 existing_spdy_session_ = session_->spdy_session_pool()->RequestSession(
753 spdy_session_key_, enable_ip_based_pooling_, is_websocket_,
754 net_log_, resume_callback, this, &spdy_session_request_,
755 &is_blocking_request_for_session);
756 if (!existing_spdy_session_ && should_throttle_connect &&
757 !is_blocking_request_for_session) {
758 net_log_.AddEvent(NetLogEventType::HTTP_STREAM_JOB_THROTTLED);
759 next_state_ = STATE_INIT_CONNECTION;
760 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
761 FROM_HERE, resume_callback,
762 base::TimeDelta::FromMilliseconds(kHTTP2ThrottleMs));
763 return ERR_IO_PENDING;
764 }
765 } else if (enable_ip_based_pooling_) {
766 // If already watching for an H2 session, still need to check for an
767 // existing connection that can be reused through IP pooling, as those
768 // don't post session available notifications.
769 //
770 // TODO(mmenke): Make sessions created through IP pooling invoke the
771 // callback.
772 existing_spdy_session_ =
773 session_->spdy_session_pool()->FindAvailableSession(
774 spdy_session_key_, enable_ip_based_pooling_, is_websocket_,
775 net_log_);
776 }
777 }
778 if (existing_spdy_session_) {
779 // Stop watching for SpdySessions.
780 spdy_session_request_.reset();
781
782 // If we're preconnecting, but we already have a SpdySession, we don't
783 // actually need to preconnect any sockets, so we're done.
784 if (job_type_ == PRECONNECT)
785 return OK;
786 using_spdy_ = true;
787 next_state_ = STATE_CREATE_STREAM;
788 return OK;
789 }
790 }
791
792 if (proxy_info_.is_http_like())
793 establishing_tunnel_ = using_ssl_;
794
795 HttpServerProperties* http_server_properties =
796 session_->http_server_properties();
797 if (http_server_properties) {
798 http_server_properties->MaybeForceHTTP11(
799 url::SchemeHostPort(request_info_.url),
800 request_info_.network_isolation_key, &server_ssl_config_);
801 if (proxy_info_.is_https()) {
802 http_server_properties->MaybeForceHTTP11(
803 url::SchemeHostPort(
804 url::kHttpsScheme,
805 proxy_info_.proxy_server().host_port_pair().host(),
806 proxy_info_.proxy_server().host_port_pair().port()),
807 request_info_.network_isolation_key, &proxy_ssl_config_);
808 }
809 }
810
811 if (job_type_ == PRECONNECT) {
812 DCHECK(!is_websocket_);
813 DCHECK(request_info_.socket_tag == SocketTag());
814 return PreconnectSocketsForHttpRequest(
815 GetSocketGroup(), destination_, request_info_.load_flags, priority_,
816 session_, proxy_info_, server_ssl_config_, proxy_ssl_config_,
817 request_info_.privacy_mode, request_info_.network_isolation_key,
818 request_info_.disable_secure_dns, net_log_, num_streams_);
819 }
820
821 ClientSocketPool::ProxyAuthCallback proxy_auth_callback =
822 base::BindRepeating(&HttpStreamFactory::Job::OnNeedsProxyAuthCallback,
823 base::Unretained(this));
824 if (is_websocket_) {
825 DCHECK(request_info_.socket_tag == SocketTag());
826 DCHECK(!request_info_.disable_secure_dns);
827 SSLConfig websocket_server_ssl_config = server_ssl_config_;
828 websocket_server_ssl_config.alpn_protos.clear();
829 return InitSocketHandleForWebSocketRequest(
830 GetSocketGroup(), destination_, request_info_.load_flags, priority_,
831 session_, proxy_info_, websocket_server_ssl_config, proxy_ssl_config_,
832 request_info_.privacy_mode, request_info_.network_isolation_key,
833 net_log_, connection_.get(), io_callback_, proxy_auth_callback);
834 }
835
836 return InitSocketHandleForHttpRequest(
837 GetSocketGroup(), destination_, request_info_.load_flags, priority_,
838 session_, proxy_info_, server_ssl_config_, proxy_ssl_config_,
839 request_info_.privacy_mode, request_info_.network_isolation_key,
840 request_info_.disable_secure_dns, request_info_.socket_tag, net_log_,
841 connection_.get(), io_callback_, proxy_auth_callback);
842 }
843
DoInitConnectionImplQuic()844 int HttpStreamFactory::Job::DoInitConnectionImplQuic() {
845 HostPortPair destination;
846 SSLConfig* ssl_config;
847 GURL url(request_info_.url);
848 if (proxy_info_.is_quic()) {
849 // A proxy's certificate is expected to be valid for the proxy hostname.
850 destination = proxy_info_.proxy_server().host_port_pair();
851 ssl_config = &proxy_ssl_config_;
852 GURL::Replacements replacements;
853 replacements.SetSchemeStr(url::kHttpsScheme);
854 replacements.SetHostStr(destination.host());
855 const std::string new_port = base::NumberToString(destination.port());
856 replacements.SetPortStr(new_port);
857 replacements.ClearUsername();
858 replacements.ClearPassword();
859 replacements.ClearPath();
860 replacements.ClearQuery();
861 replacements.ClearRef();
862 url = url.ReplaceComponents(replacements);
863 } else {
864 DCHECK(using_ssl_);
865 // The certificate of a QUIC alternative server is expected to be valid
866 // for the origin of the request (in addition to being valid for the
867 // server itself).
868 destination = destination_;
869 ssl_config = &server_ssl_config_;
870 }
871 int rv = quic_request_.Request(
872 destination, quic_version_, request_info_.privacy_mode, priority_,
873 request_info_.socket_tag, request_info_.network_isolation_key,
874 request_info_.disable_secure_dns, ssl_config->GetCertVerifyFlags(), url,
875 net_log_, &net_error_details_,
876 base::BindOnce(&Job::OnFailedOnDefaultNetwork, ptr_factory_.GetWeakPtr()),
877 io_callback_);
878 if (rv == OK) {
879 using_existing_quic_session_ = true;
880 } else if (rv == ERR_IO_PENDING) {
881 // There's no available QUIC session. Inform the delegate how long to
882 // delay the main job.
883 delegate_->MaybeSetWaitTimeForMainJob(
884 quic_request_.GetTimeDelayForWaitingJob());
885 expect_on_quic_host_resolution_ = quic_request_.WaitForHostResolution(
886 base::BindOnce(&Job::OnQuicHostResolution, base::Unretained(this)));
887 }
888 return rv;
889 }
890
OnQuicHostResolution(int result)891 void HttpStreamFactory::Job::OnQuicHostResolution(int result) {
892 DCHECK(expect_on_quic_host_resolution_);
893 expect_on_quic_host_resolution_ = false;
894 delegate_->OnConnectionInitialized(this, result);
895 }
896
OnFailedOnDefaultNetwork(int result)897 void HttpStreamFactory::Job::OnFailedOnDefaultNetwork(int result) {
898 DCHECK_EQ(job_type_, ALTERNATIVE);
899 DCHECK(using_quic_);
900 delegate_->OnFailedOnDefaultNetwork(this);
901 }
902
DoInitConnectionComplete(int result)903 int HttpStreamFactory::Job::DoInitConnectionComplete(int result) {
904 net_log_.EndEvent(NetLogEventType::HTTP_STREAM_JOB_INIT_CONNECTION);
905
906 // No need to continue waiting for a session, once a connection is
907 // established.
908 spdy_session_request_.reset();
909
910 if (job_type_ == PRECONNECT) {
911 if (using_quic_)
912 return result;
913 DCHECK_EQ(OK, result);
914 return OK;
915 }
916
917 resolve_error_info_ = connection_->resolve_error_info();
918
919 // |result| may be the result of any of the stacked pools. The following
920 // logic is used when determining how to interpret an error.
921 // If |result| < 0:
922 // and connection_->socket() != NULL, then the SSL handshake ran and it
923 // is a potentially recoverable error.
924 // and connection_->socket == NULL and connection_->is_ssl_error() is true,
925 // then the SSL handshake ran with an unrecoverable error.
926 // otherwise, the error came from one of the other pools.
927 bool ssl_started = using_ssl_ && (result == OK || connection_->socket() ||
928 connection_->is_ssl_error());
929
930 if (ssl_started && result == OK) {
931 if (using_quic_) {
932 was_alpn_negotiated_ = true;
933 negotiated_protocol_ = kProtoQUIC;
934 } else {
935 if (connection_->socket()->WasAlpnNegotiated()) {
936 was_alpn_negotiated_ = true;
937 negotiated_protocol_ = connection_->socket()->GetNegotiatedProtocol();
938 net_log_.AddEvent(NetLogEventType::HTTP_STREAM_REQUEST_PROTO, [&] {
939 return NetLogHttpStreamProtoParams(negotiated_protocol_);
940 });
941 if (negotiated_protocol_ == kProtoHTTP2) {
942 if (is_websocket_) {
943 // WebSocket is not supported over a fresh HTTP/2 connection.
944 return ERR_NOT_IMPLEMENTED;
945 }
946
947 using_spdy_ = true;
948 }
949 }
950 }
951 } else if (proxy_info_.is_secure_http_like() && connection_->socket() &&
952 result == OK) {
953 ProxyClientSocket* proxy_socket =
954 static_cast<ProxyClientSocket*>(connection_->socket());
955 // http://crbug.com/642354
956 if (!proxy_socket->IsConnected())
957 return ERR_CONNECTION_CLOSED;
958 if (proxy_socket->IsUsingSpdy()) {
959 was_alpn_negotiated_ = true;
960 negotiated_protocol_ = proxy_socket->GetProxyNegotiatedProtocol();
961 using_spdy_ = true;
962
963 // Using unencrypted websockets over an H2 proxy is not currently
964 // supported.
965 // TODO(mmenke): Should this case be treated like
966 // |try_websocket_over_http2_|, or should we force HTTP/1.1?
967 if (is_websocket_ && !try_websocket_over_http2_)
968 return ERR_NOT_IMPLEMENTED;
969 }
970 }
971
972 if (proxy_info_.is_quic() && using_quic_ && result < 0)
973 return ReconsiderProxyAfterError(result);
974
975 if (expect_spdy_ && !using_spdy_)
976 return ERR_ALPN_NEGOTIATION_FAILED;
977
978 if (!ssl_started && result < 0 && (expect_spdy_ || using_quic_))
979 return result;
980
981 if (using_quic_) {
982 if (result < 0)
983 return result;
984
985 if (stream_type_ == HttpStreamRequest::BIDIRECTIONAL_STREAM) {
986 std::unique_ptr<QuicChromiumClientSession::Handle> session =
987 quic_request_.ReleaseSessionHandle();
988 if (!session) {
989 // Quic session is closed before stream can be created.
990 return ERR_CONNECTION_CLOSED;
991 }
992 bidirectional_stream_impl_.reset(
993 new BidirectionalStreamQuicImpl(std::move(session)));
994 } else {
995 std::unique_ptr<QuicChromiumClientSession::Handle> session =
996 quic_request_.ReleaseSessionHandle();
997 if (!session) {
998 // Quic session is closed before stream can be created.
999 return ERR_CONNECTION_CLOSED;
1000 }
1001 stream_ = std::make_unique<QuicHttpStream>(std::move(session));
1002 }
1003 next_state_ = STATE_NONE;
1004 return OK;
1005 }
1006
1007 if (result < 0 && !ssl_started)
1008 return ReconsiderProxyAfterError(result);
1009
1010 establishing_tunnel_ = false;
1011
1012 // Handle SSL errors below.
1013 if (using_ssl_) {
1014 DCHECK(ssl_started);
1015 if (IsCertificateError(result)) {
1016 SSLInfo ssl_info;
1017 GetSSLInfo(&ssl_info);
1018 if (ssl_info.cert) {
1019 // Add the bad certificate to the set of allowed certificates in the
1020 // SSL config object. This data structure will be consulted after
1021 // calling RestartIgnoringLastError(). And the user will be asked
1022 // interactively before RestartIgnoringLastError() is ever called.
1023 server_ssl_config_.allowed_bad_certs.emplace_back(ssl_info.cert,
1024 ssl_info.cert_status);
1025 }
1026 }
1027 if (result < 0)
1028 return result;
1029 }
1030
1031 next_state_ = STATE_CREATE_STREAM;
1032 return OK;
1033 }
1034
DoWaitingUserAction(int result)1035 int HttpStreamFactory::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
SetSpdyHttpStreamOrBidirectionalStreamImpl(base::WeakPtr<SpdySession> session)1044 int HttpStreamFactory::Job::SetSpdyHttpStreamOrBidirectionalStreamImpl(
1045 base::WeakPtr<SpdySession> session) {
1046 DCHECK(using_spdy_);
1047
1048 if (is_websocket_) {
1049 DCHECK_NE(job_type_, PRECONNECT);
1050 DCHECK(delegate_->websocket_handshake_stream_create_helper());
1051
1052 if (!try_websocket_over_http2_) {
1053 // Plaintext WebSocket is not supported over HTTP/2 proxy,
1054 // see https://crbug.com/684681.
1055 return ERR_NOT_IMPLEMENTED;
1056 }
1057
1058 websocket_stream_ = delegate_->websocket_handshake_stream_create_helper()
1059 ->CreateHttp2Stream(session);
1060 return OK;
1061 }
1062 if (stream_type_ == HttpStreamRequest::BIDIRECTIONAL_STREAM) {
1063 bidirectional_stream_impl_ = std::make_unique<BidirectionalStreamSpdyImpl>(
1064 session, net_log_.source());
1065 return OK;
1066 }
1067
1068 // TODO(willchan): Delete this code, because eventually, the HttpStreamFactory
1069 // will be creating all the SpdyHttpStreams, since it will know when
1070 // SpdySessions become available.
1071
1072 stream_ = std::make_unique<SpdyHttpStream>(session, pushed_stream_id_,
1073 net_log_.source());
1074 return OK;
1075 }
1076
DoCreateStream()1077 int HttpStreamFactory::Job::DoCreateStream() {
1078 DCHECK(connection_->socket() || existing_spdy_session_.get());
1079 DCHECK(!using_quic_);
1080
1081 next_state_ = STATE_CREATE_STREAM_COMPLETE;
1082
1083 if (!using_spdy_) {
1084 DCHECK(!expect_spdy_);
1085 bool using_proxy = (proxy_info_.is_http_like()) &&
1086 request_info_.url.SchemeIs(url::kHttpScheme);
1087 if (is_websocket_) {
1088 DCHECK_NE(job_type_, PRECONNECT);
1089 DCHECK(delegate_->websocket_handshake_stream_create_helper());
1090 websocket_stream_ =
1091 delegate_->websocket_handshake_stream_create_helper()
1092 ->CreateBasicStream(std::move(connection_), using_proxy,
1093 session_->websocket_endpoint_lock_manager());
1094 } else {
1095 if (request_info_.upload_data_stream &&
1096 !request_info_.upload_data_stream->AllowHTTP1()) {
1097 return ERR_H2_OR_QUIC_REQUIRED;
1098 }
1099 stream_ = std::make_unique<HttpBasicStream>(std::move(connection_),
1100 using_proxy);
1101 }
1102 return OK;
1103 }
1104
1105 CHECK(!stream_.get());
1106
1107 // It is possible that a pushed stream has been opened by a server since last
1108 // time Job checked above.
1109 if (!existing_spdy_session_) {
1110 // WebSocket over HTTP/2 is only allowed to use existing HTTP/2 connections.
1111 // Therefore |using_spdy_| could not have been set unless a connection had
1112 // already been found.
1113 DCHECK(!is_websocket_);
1114
1115 session_->spdy_session_pool()->push_promise_index()->ClaimPushedStream(
1116 spdy_session_key_, origin_url_, request_info_, &existing_spdy_session_,
1117 &pushed_stream_id_);
1118 // It is also possible that an HTTP/2 connection has been established since
1119 // last time Job checked above.
1120 if (!existing_spdy_session_) {
1121 existing_spdy_session_ =
1122 session_->spdy_session_pool()->FindAvailableSession(
1123 spdy_session_key_, enable_ip_based_pooling_,
1124 /* is_websocket = */ false, net_log_);
1125 }
1126 }
1127 if (existing_spdy_session_) {
1128 // We picked up an existing session, so we don't need our socket.
1129 if (connection_->socket())
1130 connection_->socket()->Disconnect();
1131 connection_->Reset();
1132
1133 int set_result =
1134 SetSpdyHttpStreamOrBidirectionalStreamImpl(existing_spdy_session_);
1135 existing_spdy_session_.reset();
1136 return set_result;
1137 }
1138
1139 // Close idle sockets in this group, since subsequent requests will go over
1140 // |spdy_session|.
1141 if (connection_->socket()->IsConnected())
1142 connection_->CloseIdleSocketsInGroup("Switching to HTTP2 session");
1143
1144 // If |spdy_session_direct_| is false, then |proxy_info_| is guaranteed to
1145 // have a non-empty proxy list.
1146 bool is_trusted_proxy =
1147 !spdy_session_direct_ && proxy_info_.proxy_server().is_trusted_proxy();
1148
1149 base::WeakPtr<SpdySession> spdy_session =
1150 session_->spdy_session_pool()->CreateAvailableSessionFromSocketHandle(
1151 spdy_session_key_, is_trusted_proxy, std::move(connection_),
1152 net_log_);
1153
1154 if (!spdy_session->HasAcceptableTransportSecurity()) {
1155 spdy_session->CloseSessionOnError(ERR_HTTP2_INADEQUATE_TRANSPORT_SECURITY,
1156 "");
1157 return ERR_HTTP2_INADEQUATE_TRANSPORT_SECURITY;
1158 }
1159
1160 url::SchemeHostPort scheme_host_port(
1161 using_ssl_ ? url::kHttpsScheme : url::kHttpScheme,
1162 spdy_session_key_.host_port_pair().host(),
1163 spdy_session_key_.host_port_pair().port());
1164
1165 HttpServerProperties* http_server_properties =
1166 session_->http_server_properties();
1167 if (http_server_properties) {
1168 http_server_properties->SetSupportsSpdy(scheme_host_port,
1169 request_info_.network_isolation_key,
1170 true /* supports_spdy */);
1171 }
1172
1173 // Create a SpdyHttpStream or a BidirectionalStreamImpl attached to the
1174 // session.
1175 return SetSpdyHttpStreamOrBidirectionalStreamImpl(spdy_session);
1176 }
1177
DoCreateStreamComplete(int result)1178 int HttpStreamFactory::Job::DoCreateStreamComplete(int result) {
1179 if (result < 0)
1180 return result;
1181
1182 session_->proxy_resolution_service()->ReportSuccess(proxy_info_);
1183 next_state_ = STATE_NONE;
1184 return OK;
1185 }
1186
OnSpdySessionAvailable(base::WeakPtr<SpdySession> spdy_session)1187 void HttpStreamFactory::Job::OnSpdySessionAvailable(
1188 base::WeakPtr<SpdySession> spdy_session) {
1189 DCHECK(spdy_session);
1190
1191 // No need for the connection any more, since |spdy_session| can be used
1192 // instead, and there's no benefit from keeping the old ConnectJob in the
1193 // socket pool.
1194 if (connection_)
1195 connection_->ResetAndCloseSocket();
1196
1197 // Once a connection is initialized, or if there's any out-of-band callback,
1198 // like proxy auth challenge, the SpdySessionRequest is cancelled.
1199 DCHECK(next_state_ == STATE_INIT_CONNECTION ||
1200 next_state_ == STATE_INIT_CONNECTION_COMPLETE);
1201
1202 // Ignore calls to ResumeInitConnection() from either the timer or the
1203 // SpdySessionPool.
1204 init_connection_already_resumed_ = true;
1205
1206 // If this is a preconnect, nothing left do to.
1207 if (job_type_ == PRECONNECT) {
1208 OnPreconnectsComplete();
1209 return;
1210 }
1211
1212 using_spdy_ = true;
1213 existing_spdy_session_ = spdy_session;
1214 next_state_ = STATE_CREATE_STREAM;
1215
1216 // This will synchronously close |connection_|, so no need to worry about it
1217 // calling back into |this|.
1218 RunLoop(net::OK);
1219 }
1220
ReconsiderProxyAfterError(int error)1221 int HttpStreamFactory::Job::ReconsiderProxyAfterError(int error) {
1222 // Check if the error was a proxy failure.
1223 if (!CanFalloverToNextProxy(proxy_info_.proxy_server(), error, &error))
1224 return error;
1225
1226 should_reconsider_proxy_ = true;
1227 return error;
1228 }
1229
1230 ClientSocketPoolManager::SocketGroupType
GetSocketGroup() const1231 HttpStreamFactory::Job::GetSocketGroup() const {
1232 std::string scheme = origin_url_.scheme();
1233
1234 if (scheme == url::kHttpsScheme || scheme == url::kWssScheme)
1235 return ClientSocketPoolManager::SSL_GROUP;
1236
1237 DCHECK(scheme == url::kHttpScheme || scheme == url::kWsScheme);
1238 return ClientSocketPoolManager::NORMAL_GROUP;
1239 }
1240
1241 // If the connection succeeds, failed connection attempts leading up to the
1242 // success will be returned via the successfully connected socket. If the
1243 // connection fails, failed connection attempts will be returned via the
1244 // ClientSocketHandle. Check whether a socket was returned and copy the
1245 // connection attempts from the proper place.
MaybeCopyConnectionAttemptsFromSocketOrHandle()1246 void HttpStreamFactory::Job::MaybeCopyConnectionAttemptsFromSocketOrHandle() {
1247 if (!connection_)
1248 return;
1249
1250 ConnectionAttempts socket_attempts = connection_->connection_attempts();
1251 if (connection_->socket()) {
1252 connection_->socket()->GetConnectionAttempts(&socket_attempts);
1253 }
1254
1255 delegate_->AddConnectionAttemptsToRequest(this, socket_attempts);
1256 }
1257
1258 HttpStreamFactory::JobFactory::JobFactory() = default;
1259
1260 HttpStreamFactory::JobFactory::~JobFactory() = default;
1261
1262 std::unique_ptr<HttpStreamFactory::Job>
CreateMainJob(HttpStreamFactory::Job::Delegate * delegate,HttpStreamFactory::JobType job_type,HttpNetworkSession * session,const HttpRequestInfo & request_info,RequestPriority priority,const ProxyInfo & proxy_info,const SSLConfig & server_ssl_config,const SSLConfig & proxy_ssl_config,HostPortPair destination,GURL origin_url,bool is_websocket,bool enable_ip_based_pooling,NetLog * net_log)1263 HttpStreamFactory::JobFactory::CreateMainJob(
1264 HttpStreamFactory::Job::Delegate* delegate,
1265 HttpStreamFactory::JobType job_type,
1266 HttpNetworkSession* session,
1267 const HttpRequestInfo& request_info,
1268 RequestPriority priority,
1269 const ProxyInfo& proxy_info,
1270 const SSLConfig& server_ssl_config,
1271 const SSLConfig& proxy_ssl_config,
1272 HostPortPair destination,
1273 GURL origin_url,
1274 bool is_websocket,
1275 bool enable_ip_based_pooling,
1276 NetLog* net_log) {
1277 return std::make_unique<HttpStreamFactory::Job>(
1278 delegate, job_type, session, request_info, priority, proxy_info,
1279 server_ssl_config, proxy_ssl_config, destination, origin_url,
1280 kProtoUnknown, quic::ParsedQuicVersion::Unsupported(), is_websocket,
1281 enable_ip_based_pooling, net_log);
1282 }
1283
1284 std::unique_ptr<HttpStreamFactory::Job>
CreateAltSvcJob(HttpStreamFactory::Job::Delegate * delegate,HttpStreamFactory::JobType job_type,HttpNetworkSession * session,const HttpRequestInfo & request_info,RequestPriority priority,const ProxyInfo & proxy_info,const SSLConfig & server_ssl_config,const SSLConfig & proxy_ssl_config,HostPortPair destination,GURL origin_url,NextProto alternative_protocol,quic::ParsedQuicVersion quic_version,bool is_websocket,bool enable_ip_based_pooling,NetLog * net_log)1285 HttpStreamFactory::JobFactory::CreateAltSvcJob(
1286 HttpStreamFactory::Job::Delegate* delegate,
1287 HttpStreamFactory::JobType job_type,
1288 HttpNetworkSession* session,
1289 const HttpRequestInfo& request_info,
1290 RequestPriority priority,
1291 const ProxyInfo& proxy_info,
1292 const SSLConfig& server_ssl_config,
1293 const SSLConfig& proxy_ssl_config,
1294 HostPortPair destination,
1295 GURL origin_url,
1296 NextProto alternative_protocol,
1297 quic::ParsedQuicVersion quic_version,
1298 bool is_websocket,
1299 bool enable_ip_based_pooling,
1300 NetLog* net_log) {
1301 return std::make_unique<HttpStreamFactory::Job>(
1302 delegate, job_type, session, request_info, priority, proxy_info,
1303 server_ssl_config, proxy_ssl_config, destination, origin_url,
1304 alternative_protocol, quic_version, is_websocket, enable_ip_based_pooling,
1305 net_log);
1306 }
1307
ShouldThrottleConnectForSpdy() const1308 bool HttpStreamFactory::Job::ShouldThrottleConnectForSpdy() const {
1309 DCHECK(!using_quic_);
1310 DCHECK(!spdy_session_request_);
1311
1312 // If the job has previously been throttled, don't throttle it again.
1313 if (init_connection_already_resumed_)
1314 return false;
1315
1316 url::SchemeHostPort scheme_host_port(
1317 using_ssl_ ? url::kHttpsScheme : url::kHttpScheme,
1318 spdy_session_key_.host_port_pair().host(),
1319 spdy_session_key_.host_port_pair().port());
1320 // Only throttle the request if the server is believed to support H2.
1321 return session_->http_server_properties()->GetSupportsSpdy(
1322 scheme_host_port, request_info_.network_isolation_key);
1323 }
1324
1325 } // namespace net
1326