1 // Copyright 2012 The Chromium Authors
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 <memory>
8 #include <utility>
9
10 #include "base/check_op.h"
11 #include "base/containers/contains.h"
12 #include "base/feature_list.h"
13 #include "base/functional/bind.h"
14 #include "base/functional/callback.h"
15 #include "base/location.h"
16 #include "base/notreached.h"
17 #include "base/strings/string_util.h"
18 #include "base/task/single_thread_task_runner.h"
19 #include "base/values.h"
20 #include "net/base/host_port_pair.h"
21 #include "net/base/load_flags.h"
22 #include "net/base/port_util.h"
23 #include "net/base/proxy_chain.h"
24 #include "net/base/proxy_delegate.h"
25 #include "net/base/session_usage.h"
26 #include "net/cert/cert_verifier.h"
27 #include "net/dns/public/secure_dns_policy.h"
28 #include "net/http/bidirectional_stream_impl.h"
29 #include "net/http/http_basic_stream.h"
30 #include "net/http/http_network_session.h"
31 #include "net/http/http_server_properties.h"
32 #include "net/http/http_stream_factory.h"
33 #include "net/http/proxy_fallback.h"
34 #include "net/log/net_log.h"
35 #include "net/log/net_log_event_type.h"
36 #include "net/log/net_log_source.h"
37 #include "net/log/net_log_source_type.h"
38 #include "net/proxy_resolution/proxy_resolution_service.h"
39 #include "net/quic/bidirectional_stream_quic_impl.h"
40 #include "net/quic/quic_http_stream.h"
41 #include "net/quic/quic_session_key.h"
42 #include "net/socket/client_socket_handle.h"
43 #include "net/socket/client_socket_pool_manager.h"
44 #include "net/socket/connect_job.h"
45 #include "net/socket/next_proto.h"
46 #include "net/socket/ssl_client_socket.h"
47 #include "net/socket/stream_socket.h"
48 #include "net/spdy/bidirectional_stream_spdy_impl.h"
49 #include "net/spdy/spdy_http_stream.h"
50 #include "net/spdy/spdy_session.h"
51 #include "net/ssl/ssl_cert_request_info.h"
52 #include "url/gurl.h"
53 #include "url/scheme_host_port.h"
54 #include "url/url_constants.h"
55
56 namespace net {
57
58 namespace {
59
60 // Experiment to preconnect only one connection if HttpServerProperties is
61 // not supported or initialized.
62 BASE_FEATURE(kLimitEarlyPreconnectsExperiment,
63 "LimitEarlyPreconnects",
64 base::FEATURE_ENABLED_BY_DEFAULT);
65
66 } // namespace
67
NetLogHttpStreamJobType(HttpStreamFactory::JobType job_type)68 const char* NetLogHttpStreamJobType(HttpStreamFactory::JobType job_type) {
69 switch (job_type) {
70 case HttpStreamFactory::MAIN:
71 return "main";
72 case HttpStreamFactory::ALTERNATIVE:
73 return "alternative";
74 case HttpStreamFactory::DNS_ALPN_H3:
75 return "dns_alpn_h3";
76 case HttpStreamFactory::PRECONNECT:
77 return "preconnect";
78 case HttpStreamFactory::PRECONNECT_DNS_ALPN_H3:
79 return "preconnect_dns_alpn_h3";
80 }
81 return "";
82 }
83
84 // Returns parameters associated with the ALPN protocol of a HTTP stream.
NetLogHttpStreamProtoParams(NextProto negotiated_protocol)85 base::Value::Dict NetLogHttpStreamProtoParams(NextProto negotiated_protocol) {
86 base::Value::Dict dict;
87
88 dict.Set("proto", NextProtoToString(negotiated_protocol));
89 return dict;
90 }
91
Job(Delegate * delegate,JobType job_type,HttpNetworkSession * session,const StreamRequestInfo & request_info,RequestPriority priority,const ProxyInfo & proxy_info,const std::vector<SSLConfig::CertAndStatus> & allowed_bad_certs,url::SchemeHostPort destination,GURL origin_url,NextProto alternative_protocol,quic::ParsedQuicVersion quic_version,bool is_websocket,bool enable_ip_based_pooling,NetLog * net_log)92 HttpStreamFactory::Job::Job(
93 Delegate* delegate,
94 JobType job_type,
95 HttpNetworkSession* session,
96 const StreamRequestInfo& request_info,
97 RequestPriority priority,
98 const ProxyInfo& proxy_info,
99 const std::vector<SSLConfig::CertAndStatus>& allowed_bad_certs,
100 url::SchemeHostPort destination,
101 GURL origin_url,
102 NextProto alternative_protocol,
103 quic::ParsedQuicVersion quic_version,
104 bool is_websocket,
105 bool enable_ip_based_pooling,
106 NetLog* net_log)
107 : request_info_(request_info),
108 priority_(priority),
109 proxy_info_(proxy_info),
110 allowed_bad_certs_(allowed_bad_certs),
111 net_log_(
112 NetLogWithSource::Make(net_log, NetLogSourceType::HTTP_STREAM_JOB)),
113 io_callback_(
114 base::BindRepeating(&Job::OnIOComplete, base::Unretained(this))),
115 connection_(std::make_unique<ClientSocketHandle>()),
116 session_(session),
117 destination_(std::move(destination)),
118 origin_url_(std::move(origin_url)),
119 is_websocket_(is_websocket),
120 try_websocket_over_http2_(is_websocket_ &&
121 origin_url_.SchemeIs(url::kWssScheme)),
122 // Only support IP-based pooling for non-proxied streams.
123 enable_ip_based_pooling_(enable_ip_based_pooling &&
124 proxy_info.is_direct()),
125 delegate_(delegate),
126 job_type_(job_type),
127 using_ssl_(origin_url_.SchemeIs(url::kHttpsScheme) ||
128 origin_url_.SchemeIs(url::kWssScheme)),
129 using_quic_(alternative_protocol == kProtoQUIC ||
130 (ShouldForceQuic(session,
131 destination_,
132 proxy_info,
133 using_ssl_,
134 is_websocket_)) ||
135 job_type == DNS_ALPN_H3 ||
136 job_type == PRECONNECT_DNS_ALPN_H3),
137 quic_version_(quic_version),
138 expect_spdy_(alternative_protocol == kProtoHTTP2 && !using_quic_),
139 quic_request_(session_->quic_session_pool()),
140 spdy_session_key_(using_quic_
141 ? SpdySessionKey()
142 : GetSpdySessionKey(proxy_info_.proxy_chain(),
143 origin_url_,
144 request_info_)) {
145 // Websocket `destination` schemes should be converted to HTTP(S).
146 DCHECK(base::EqualsCaseInsensitiveASCII(destination_.scheme(),
147 url::kHttpScheme) ||
148 base::EqualsCaseInsensitiveASCII(destination_.scheme(),
149 url::kHttpsScheme));
150
151 // This class is specific to a single `ProxyChain`, so `proxy_info_` must be
152 // non-empty. Entries beyond the first are ignored. It should simply take a
153 // `ProxyChain`, but the full `ProxyInfo` is passed back to
154 // `HttpNetworkTransaction`, which consumes additional fields.
155 DCHECK(!proxy_info_.is_empty());
156
157 // QUIC can only be spoken to servers, never to proxies.
158 if (alternative_protocol == kProtoQUIC) {
159 DCHECK(proxy_info_.is_direct());
160 }
161
162 // The Job is forced to use QUIC without a designated version, try the
163 // preferred QUIC version that is supported by default.
164 if (quic_version_ == quic::ParsedQuicVersion::Unsupported() &&
165 ShouldForceQuic(session, destination_, proxy_info, using_ssl_,
166 is_websocket_)) {
167 quic_version_ =
168 session->context().quic_context->params()->supported_versions[0];
169 }
170
171 if (using_quic_) {
172 DCHECK((quic_version_ != quic::ParsedQuicVersion::Unsupported()) ||
173 (job_type_ == DNS_ALPN_H3) || (job_type_ == PRECONNECT_DNS_ALPN_H3));
174 }
175
176 DCHECK(session);
177 if (alternative_protocol != kProtoUnknown) {
178 // If the alternative service protocol is specified, then the job type must
179 // be either ALTERNATIVE or PRECONNECT.
180 DCHECK(job_type_ == ALTERNATIVE || job_type_ == PRECONNECT);
181 }
182
183 if (expect_spdy_) {
184 DCHECK(origin_url_.SchemeIs(url::kHttpsScheme));
185 }
186 if (using_quic_) {
187 DCHECK(session_->IsQuicEnabled());
188 }
189 if (job_type_ == PRECONNECT || is_websocket_) {
190 DCHECK(request_info_.socket_tag == SocketTag());
191 }
192 if (is_websocket_) {
193 DCHECK(origin_url_.SchemeIsWSOrWSS());
194 } else {
195 DCHECK(!origin_url_.SchemeIsWSOrWSS());
196 }
197 }
198
~Job()199 HttpStreamFactory::Job::~Job() {
200 if (started_) {
201 net_log_.EndEvent(NetLogEventType::HTTP_STREAM_JOB);
202 }
203
204 // When we're in a partially constructed state, waiting for the user to
205 // provide certificate handling information or authentication, we can't reuse
206 // this stream at all.
207 if (next_state_ == STATE_WAITING_USER_ACTION) {
208 connection_->socket()->Disconnect();
209 connection_.reset();
210 }
211
212 // The stream could be in a partial state. It is not reusable.
213 if (stream_.get() && next_state_ != STATE_DONE) {
214 stream_->Close(true /* not reusable */);
215 }
216 }
217
Start(HttpStreamRequest::StreamType stream_type)218 void HttpStreamFactory::Job::Start(HttpStreamRequest::StreamType stream_type) {
219 started_ = true;
220 stream_type_ = stream_type;
221
222 const NetLogWithSource* delegate_net_log = delegate_->GetNetLog();
223 if (delegate_net_log) {
224 net_log_.BeginEvent(NetLogEventType::HTTP_STREAM_JOB, [&] {
225 base::Value::Dict dict;
226 const auto& source = delegate_net_log->source();
227 if (source.IsValid()) {
228 source.AddToEventParameters(dict);
229 }
230 dict.Set("logical_destination",
231 url::SchemeHostPort(origin_url_).Serialize());
232 dict.Set("destination", destination_.Serialize());
233 dict.Set("expect_spdy", expect_spdy_);
234 dict.Set("using_quic", using_quic_);
235 dict.Set("priority", RequestPriorityToString(priority_));
236 dict.Set("type", NetLogHttpStreamJobType(job_type_));
237 return dict;
238 });
239 delegate_net_log->AddEventReferencingSource(
240 NetLogEventType::HTTP_STREAM_REQUEST_STARTED_JOB, net_log_.source());
241 }
242
243 StartInternal();
244 }
245
Preconnect(int num_streams)246 int HttpStreamFactory::Job::Preconnect(int num_streams) {
247 DCHECK_GT(num_streams, 0);
248 HttpServerProperties* http_server_properties =
249 session_->http_server_properties();
250 DCHECK(http_server_properties);
251 // Preconnect one connection if either of the following is true:
252 // (1) kLimitEarlyPreconnectsStreamExperiment is turned on,
253 // HttpServerProperties is not initialized, and url scheme is cryptographic.
254 // (2) The server supports H2 or QUIC.
255 bool connect_one_stream =
256 base::FeatureList::IsEnabled(kLimitEarlyPreconnectsExperiment) &&
257 !http_server_properties->IsInitialized() &&
258 origin_url_.SchemeIsCryptographic();
259 if (connect_one_stream || http_server_properties->SupportsRequestPriority(
260 url::SchemeHostPort(origin_url_),
261 request_info_.network_anonymization_key)) {
262 num_streams_ = 1;
263 } else {
264 num_streams_ = num_streams;
265 }
266 return StartInternal();
267 }
268
RestartTunnelWithProxyAuth()269 int HttpStreamFactory::Job::RestartTunnelWithProxyAuth() {
270 DCHECK(establishing_tunnel_);
271 DCHECK(restart_with_auth_callback_);
272
273 std::move(restart_with_auth_callback_).Run();
274 return ERR_IO_PENDING;
275 }
276
GetLoadState() const277 LoadState HttpStreamFactory::Job::GetLoadState() const {
278 switch (next_state_) {
279 case STATE_INIT_CONNECTION_COMPLETE:
280 case STATE_CREATE_STREAM_COMPLETE:
281 return using_quic_ ? LOAD_STATE_CONNECTING : connection_->GetLoadState();
282 default:
283 return LOAD_STATE_IDLE;
284 }
285 }
286
Resume()287 void HttpStreamFactory::Job::Resume() {
288 DCHECK_EQ(job_type_, MAIN);
289 DCHECK_EQ(next_state_, STATE_WAIT_COMPLETE);
290 OnIOComplete(OK);
291 }
292
Orphan()293 void HttpStreamFactory::Job::Orphan() {
294 DCHECK(job_type_ == ALTERNATIVE || job_type_ == DNS_ALPN_H3);
295 net_log_.AddEvent(NetLogEventType::HTTP_STREAM_JOB_ORPHANED);
296
297 // Watching for SPDY sessions isn't supported on orphaned jobs.
298 // TODO(mmenke): Fix that.
299 spdy_session_request_.reset();
300 }
301
SetPriority(RequestPriority priority)302 void HttpStreamFactory::Job::SetPriority(RequestPriority priority) {
303 priority_ = priority;
304 // Ownership of |connection_| is passed to the newly created stream
305 // or H2 session in DoCreateStream(), and the consumer is not
306 // notified immediately, so this call may occur when |connection_|
307 // is null.
308 //
309 // Note that streams are created without a priority associated with them,
310 // and it is up to the consumer to set their priority via
311 // HttpStream::InitializeStream(). So there is no need for this code
312 // to propagate priority changes to the newly created stream.
313 if (connection_ && connection_->is_initialized()) {
314 connection_->SetPriority(priority);
315 }
316 // TODO(akalin): Maybe Propagate this to the preconnect state.
317 }
318
HasAvailableSpdySession() const319 bool HttpStreamFactory::Job::HasAvailableSpdySession() const {
320 return !using_quic_ && CanUseExistingSpdySession() &&
321 session_->spdy_session_pool()->HasAvailableSession(spdy_session_key_,
322 is_websocket_);
323 }
324
HasAvailableQuicSession() const325 bool HttpStreamFactory::Job::HasAvailableQuicSession() const {
326 if (!using_quic_) {
327 return false;
328 }
329 bool require_dns_https_alpn =
330 (job_type_ == DNS_ALPN_H3) || (job_type_ == PRECONNECT_DNS_ALPN_H3);
331
332 return quic_request_.CanUseExistingSession(
333 origin_url_, proxy_info_.proxy_chain(), request_info_.privacy_mode,
334 SessionUsage::kDestination, request_info_.socket_tag,
335 request_info_.network_anonymization_key, request_info_.secure_dns_policy,
336 require_dns_https_alpn, destination_);
337 }
338
TargettedSocketGroupHasActiveSocket() const339 bool HttpStreamFactory::Job::TargettedSocketGroupHasActiveSocket() const {
340 DCHECK(!using_quic_);
341 DCHECK(!is_websocket_);
342 ClientSocketPool* pool = session_->GetSocketPool(
343 HttpNetworkSession::NORMAL_SOCKET_POOL, proxy_info_.proxy_chain());
344 DCHECK(pool);
345 ClientSocketPool::GroupId connection_group(
346 destination_, request_info_.privacy_mode,
347 request_info_.network_anonymization_key, request_info_.secure_dns_policy,
348 disable_cert_verification_network_fetches());
349 return pool->HasActiveSocket(connection_group);
350 }
351
negotiated_protocol() const352 NextProto HttpStreamFactory::Job::negotiated_protocol() const {
353 return negotiated_protocol_;
354 }
355
using_spdy() const356 bool HttpStreamFactory::Job::using_spdy() const {
357 return negotiated_protocol_ == kProtoHTTP2;
358 }
359
disable_cert_verification_network_fetches() const360 bool HttpStreamFactory::Job::disable_cert_verification_network_fetches() const {
361 return !!(request_info_.load_flags & LOAD_DISABLE_CERT_NETWORK_FETCHES);
362 }
363
proxy_info() const364 const ProxyInfo& HttpStreamFactory::Job::proxy_info() const {
365 return proxy_info_;
366 }
367
resolve_error_info() const368 ResolveErrorInfo HttpStreamFactory::Job::resolve_error_info() const {
369 return resolve_error_info_;
370 }
371
GetSSLInfo(SSLInfo * ssl_info)372 void HttpStreamFactory::Job::GetSSLInfo(SSLInfo* ssl_info) {
373 DCHECK(using_ssl_);
374 DCHECK(!establishing_tunnel_);
375 DCHECK(connection_.get() && connection_->socket());
376 connection_->socket()->GetSSLInfo(ssl_info);
377 }
378
UsingHttpProxyWithoutTunnel() const379 bool HttpStreamFactory::Job::UsingHttpProxyWithoutTunnel() const {
380 return !using_quic_ && !using_ssl_ && !is_websocket_ &&
381 proxy_info_.proxy_chain().is_get_to_proxy_allowed();
382 }
383
384 // static
OriginToForceQuicOn(const QuicParams & quic_params,const url::SchemeHostPort & destination)385 bool HttpStreamFactory::Job::OriginToForceQuicOn(
386 const QuicParams& quic_params,
387 const url::SchemeHostPort& destination) {
388 // TODO(crbug.com/1206799): Consider converting `origins_to_force_quic_on` to
389 // use url::SchemeHostPort.
390 return (
391 base::Contains(quic_params.origins_to_force_quic_on, HostPortPair()) ||
392 base::Contains(quic_params.origins_to_force_quic_on,
393 HostPortPair::FromSchemeHostPort(destination)));
394 }
395
396 // static
ShouldForceQuic(HttpNetworkSession * session,const url::SchemeHostPort & destination,const ProxyInfo & proxy_info,bool using_ssl,bool is_websocket)397 bool HttpStreamFactory::Job::ShouldForceQuic(
398 HttpNetworkSession* session,
399 const url::SchemeHostPort& destination,
400 const ProxyInfo& proxy_info,
401 bool using_ssl,
402 bool is_websocket) {
403 if (!session->IsQuicEnabled()) {
404 return false;
405 }
406 if (is_websocket) {
407 return false;
408 }
409 // If a proxy is being used, the last proxy in the chain must be QUIC if we
410 // are to use QUIC on top of it.
411 if (!proxy_info.is_direct() && !proxy_info.proxy_chain().Last().is_quic()) {
412 return false;
413 }
414 return OriginToForceQuicOn(*session->context().quic_context->params(),
415 destination) &&
416 base::EqualsCaseInsensitiveASCII(destination.scheme(),
417 url::kHttpsScheme);
418 }
419
420 // static
GetSpdySessionKey(const ProxyChain & proxy_chain,const GURL & origin_url,const StreamRequestInfo & request_info)421 SpdySessionKey HttpStreamFactory::Job::GetSpdySessionKey(
422 const ProxyChain& proxy_chain,
423 const GURL& origin_url,
424 const StreamRequestInfo& request_info) {
425 // In the case that we'll be sending a GET request to the proxy, look for a
426 // HTTP/2 proxy session *to* the proxy, instead of to the origin server. The
427 // way HTTP over HTTPS proxies work is that the ConnectJob makes a SpdyProxy,
428 // and then the HttpStreamFactory detects it when it's added to the
429 // SpdySession pool, and uses it directly (completely ignoring the result of
430 // the ConnectJob, and in fact cancelling it). So we need to create the same
431 // key used by the HttpProxyConnectJob for the last proxy in the chain.
432 if (IsGetToProxy(proxy_chain, origin_url)) {
433 // For this to work as expected, the whole chain should be HTTPS.
434 for (const auto& proxy_server : proxy_chain.proxy_servers()) {
435 CHECK(proxy_server.is_https());
436 }
437 auto [last_proxy_partial_chain, last_proxy_server] =
438 proxy_chain.SplitLast();
439 const auto& last_proxy_host_port_pair = last_proxy_server.host_port_pair();
440 // Note that `disable_cert_network_fetches` must be true for proxies to
441 // avoid deadlock. See comment on
442 // `SSLConfig::disable_cert_verification_network_fetches`.
443 return SpdySessionKey(
444 last_proxy_host_port_pair, PRIVACY_MODE_DISABLED,
445 last_proxy_partial_chain, SessionUsage::kProxy, request_info.socket_tag,
446 request_info.network_anonymization_key, request_info.secure_dns_policy,
447 /*disable_cert_network_fetches=*/true);
448 }
449 return SpdySessionKey(
450 HostPortPair::FromURL(origin_url), request_info.privacy_mode, proxy_chain,
451 SessionUsage::kDestination, request_info.socket_tag,
452 request_info.network_anonymization_key, request_info.secure_dns_policy,
453 request_info.load_flags & LOAD_DISABLE_CERT_NETWORK_FETCHES);
454 }
455
456 // static
IsGetToProxy(const ProxyChain & proxy_chain,const GURL & origin_url)457 bool HttpStreamFactory::Job::IsGetToProxy(const ProxyChain& proxy_chain,
458 const GURL& origin_url) {
459 // Sending proxied GET requests to the last proxy server in the chain is no
460 // longer supported for QUIC.
461 return proxy_chain.is_get_to_proxy_allowed() &&
462 proxy_chain.Last().is_https() && origin_url.SchemeIs(url::kHttpScheme);
463 }
464
CanUseExistingSpdySession() const465 bool HttpStreamFactory::Job::CanUseExistingSpdySession() const {
466 DCHECK(!using_quic_);
467
468 if (proxy_info_.is_direct() &&
469 session_->http_server_properties()->RequiresHTTP11(
470 url::SchemeHostPort(origin_url_),
471 request_info_.network_anonymization_key)) {
472 return false;
473 }
474
475 if (is_websocket_) {
476 return try_websocket_over_http2_;
477 }
478
479 DCHECK(origin_url_.SchemeIsHTTPOrHTTPS());
480
481 // We need to make sure that if a HTTP/2 session was created for
482 // https://somehost/ then we do not use that session for http://somehost:443/.
483 // The only time we can use an existing session is if the request URL is
484 // https (the normal case) or if we are connecting to an HTTPS proxy to make
485 // a GET request for an HTTP destination. https://crbug.com/133176
486 if (origin_url_.SchemeIs(url::kHttpsScheme)) {
487 return true;
488 }
489 if (!proxy_info_.is_empty()) {
490 const ProxyChain& proxy_chain = proxy_info_.proxy_chain();
491 if (!proxy_chain.is_direct() && proxy_chain.is_get_to_proxy_allowed() &&
492 proxy_chain.Last().is_https()) {
493 return true;
494 }
495 }
496 return false;
497 }
498
OnStreamReadyCallback()499 void HttpStreamFactory::Job::OnStreamReadyCallback() {
500 DCHECK(stream_.get());
501 DCHECK_NE(job_type_, PRECONNECT);
502 DCHECK_NE(job_type_, PRECONNECT_DNS_ALPN_H3);
503 DCHECK(!is_websocket_ || try_websocket_over_http2_);
504
505 MaybeCopyConnectionAttemptsFromHandle();
506
507 delegate_->OnStreamReady(this);
508 // |this| may be deleted after this call.
509 }
510
OnWebSocketHandshakeStreamReadyCallback()511 void HttpStreamFactory::Job::OnWebSocketHandshakeStreamReadyCallback() {
512 DCHECK(websocket_stream_);
513 DCHECK_NE(job_type_, PRECONNECT);
514 DCHECK_NE(job_type_, PRECONNECT_DNS_ALPN_H3);
515 DCHECK(is_websocket_);
516
517 MaybeCopyConnectionAttemptsFromHandle();
518
519 delegate_->OnWebSocketHandshakeStreamReady(this, proxy_info_,
520 std::move(websocket_stream_));
521 // |this| may be deleted after this call.
522 }
523
OnBidirectionalStreamImplReadyCallback()524 void HttpStreamFactory::Job::OnBidirectionalStreamImplReadyCallback() {
525 DCHECK(bidirectional_stream_impl_);
526
527 MaybeCopyConnectionAttemptsFromHandle();
528
529 delegate_->OnBidirectionalStreamImplReady(this, proxy_info_);
530 // |this| may be deleted after this call.
531 }
532
OnStreamFailedCallback(int result)533 void HttpStreamFactory::Job::OnStreamFailedCallback(int result) {
534 DCHECK_NE(job_type_, PRECONNECT);
535 DCHECK_NE(job_type_, PRECONNECT_DNS_ALPN_H3);
536
537 MaybeCopyConnectionAttemptsFromHandle();
538
539 delegate_->OnStreamFailed(this, result);
540 // |this| may be deleted after this call.
541 }
542
OnCertificateErrorCallback(int result,const SSLInfo & ssl_info)543 void HttpStreamFactory::Job::OnCertificateErrorCallback(
544 int result,
545 const SSLInfo& ssl_info) {
546 DCHECK_NE(job_type_, PRECONNECT);
547 DCHECK_NE(job_type_, PRECONNECT_DNS_ALPN_H3);
548 DCHECK(!spdy_session_request_);
549
550 MaybeCopyConnectionAttemptsFromHandle();
551
552 delegate_->OnCertificateError(this, result, ssl_info);
553 // |this| may be deleted after this call.
554 }
555
OnNeedsProxyAuthCallback(const HttpResponseInfo & response,HttpAuthController * auth_controller,base::OnceClosure restart_with_auth_callback)556 void HttpStreamFactory::Job::OnNeedsProxyAuthCallback(
557 const HttpResponseInfo& response,
558 HttpAuthController* auth_controller,
559 base::OnceClosure restart_with_auth_callback) {
560 DCHECK_NE(job_type_, PRECONNECT);
561 DCHECK_NE(job_type_, PRECONNECT_DNS_ALPN_H3);
562 DCHECK(establishing_tunnel_);
563 DCHECK(!restart_with_auth_callback_);
564
565 restart_with_auth_callback_ = std::move(restart_with_auth_callback);
566
567 // This is called out of band, so need to abort the SpdySessionRequest to
568 // prevent being passed a new session while waiting on proxy auth credentials.
569 spdy_session_request_.reset();
570
571 delegate_->OnNeedsProxyAuth(this, response, proxy_info_, auth_controller);
572 // |this| may be deleted after this call.
573 }
574
OnNeedsClientAuthCallback(SSLCertRequestInfo * cert_info)575 void HttpStreamFactory::Job::OnNeedsClientAuthCallback(
576 SSLCertRequestInfo* cert_info) {
577 DCHECK_NE(job_type_, PRECONNECT);
578 DCHECK_NE(job_type_, PRECONNECT_DNS_ALPN_H3);
579 DCHECK(!spdy_session_request_);
580
581 delegate_->OnNeedsClientAuth(this, cert_info);
582 // |this| may be deleted after this call.
583 }
584
OnPreconnectsComplete(int result)585 void HttpStreamFactory::Job::OnPreconnectsComplete(int result) {
586 delegate_->OnPreconnectsComplete(this, result);
587 // |this| may be deleted after this call.
588 }
589
OnIOComplete(int result)590 void HttpStreamFactory::Job::OnIOComplete(int result) {
591 RunLoop(result);
592 }
593
RunLoop(int result)594 void HttpStreamFactory::Job::RunLoop(int result) {
595 result = DoLoop(result);
596
597 if (result == ERR_IO_PENDING) {
598 return;
599 }
600
601 // Stop watching for new SpdySessions, to avoid receiving a new SPDY session
602 // while doing anything other than waiting to establish a connection.
603 spdy_session_request_.reset();
604
605 if ((job_type_ == PRECONNECT) || (job_type_ == PRECONNECT_DNS_ALPN_H3)) {
606 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
607 FROM_HERE,
608 base::BindOnce(&HttpStreamFactory::Job::OnPreconnectsComplete,
609 ptr_factory_.GetWeakPtr(), result));
610 return;
611 }
612
613 if (IsCertificateError(result)) {
614 // Retrieve SSL information from the socket.
615 SSLInfo ssl_info;
616 GetSSLInfo(&ssl_info);
617
618 next_state_ = STATE_WAITING_USER_ACTION;
619 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
620 FROM_HERE,
621 base::BindOnce(&HttpStreamFactory::Job::OnCertificateErrorCallback,
622 ptr_factory_.GetWeakPtr(), result, ssl_info));
623 return;
624 }
625
626 switch (result) {
627 case ERR_SSL_CLIENT_AUTH_CERT_NEEDED:
628 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
629 FROM_HERE,
630 base::BindOnce(
631 &Job::OnNeedsClientAuthCallback, ptr_factory_.GetWeakPtr(),
632 base::RetainedRef(connection_->ssl_cert_request_info())));
633 return;
634
635 case OK:
636 next_state_ = STATE_DONE;
637 if (is_websocket_) {
638 DCHECK(websocket_stream_);
639 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
640 FROM_HERE,
641 base::BindOnce(&Job::OnWebSocketHandshakeStreamReadyCallback,
642 ptr_factory_.GetWeakPtr()));
643 } else if (stream_type_ == HttpStreamRequest::BIDIRECTIONAL_STREAM) {
644 if (!bidirectional_stream_impl_) {
645 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
646 FROM_HERE, base::BindOnce(&Job::OnStreamFailedCallback,
647 ptr_factory_.GetWeakPtr(), ERR_FAILED));
648 } else {
649 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
650 FROM_HERE,
651 base::BindOnce(&Job::OnBidirectionalStreamImplReadyCallback,
652 ptr_factory_.GetWeakPtr()));
653 }
654 } else {
655 DCHECK(stream_.get());
656 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
657 FROM_HERE, base::BindOnce(&Job::OnStreamReadyCallback,
658 ptr_factory_.GetWeakPtr()));
659 }
660 return;
661
662 default:
663 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
664 FROM_HERE, base::BindOnce(&Job::OnStreamFailedCallback,
665 ptr_factory_.GetWeakPtr(), result));
666 return;
667 }
668 }
669
DoLoop(int result)670 int HttpStreamFactory::Job::DoLoop(int result) {
671 DCHECK_NE(next_state_, STATE_NONE);
672 int rv = result;
673 do {
674 State state = next_state_;
675 next_state_ = STATE_NONE;
676 switch (state) {
677 case STATE_START:
678 DCHECK_EQ(OK, rv);
679 rv = DoStart();
680 break;
681 case STATE_WAIT:
682 DCHECK_EQ(OK, rv);
683 rv = DoWait();
684 break;
685 case STATE_WAIT_COMPLETE:
686 rv = DoWaitComplete(rv);
687 break;
688 case STATE_INIT_CONNECTION:
689 DCHECK_EQ(OK, rv);
690 rv = DoInitConnection();
691 break;
692 case STATE_INIT_CONNECTION_COMPLETE:
693 rv = DoInitConnectionComplete(rv);
694 break;
695 case STATE_WAITING_USER_ACTION:
696 rv = DoWaitingUserAction(rv);
697 break;
698 case STATE_CREATE_STREAM:
699 DCHECK_EQ(OK, rv);
700 rv = DoCreateStream();
701 break;
702 case STATE_CREATE_STREAM_COMPLETE:
703 rv = DoCreateStreamComplete(rv);
704 break;
705 default:
706 NOTREACHED() << "bad state";
707 rv = ERR_FAILED;
708 break;
709 }
710 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
711 return rv;
712 }
713
StartInternal()714 int HttpStreamFactory::Job::StartInternal() {
715 CHECK_EQ(STATE_NONE, next_state_);
716 next_state_ = STATE_START;
717 RunLoop(OK);
718 return ERR_IO_PENDING;
719 }
720
DoStart()721 int HttpStreamFactory::Job::DoStart() {
722 // Don't connect to restricted ports.
723 if (!IsPortAllowedForScheme(destination_.port(),
724 origin_url_.scheme_piece())) {
725 return ERR_UNSAFE_PORT;
726 }
727
728 next_state_ = STATE_WAIT;
729 return OK;
730 }
731
DoWait()732 int HttpStreamFactory::Job::DoWait() {
733 next_state_ = STATE_WAIT_COMPLETE;
734 bool should_wait = delegate_->ShouldWait(this);
735 net_log_.AddEntryWithBoolParams(NetLogEventType::HTTP_STREAM_JOB_WAITING,
736 NetLogEventPhase::BEGIN, "should_wait",
737 should_wait);
738 if (should_wait) {
739 return ERR_IO_PENDING;
740 }
741
742 return OK;
743 }
744
DoWaitComplete(int result)745 int HttpStreamFactory::Job::DoWaitComplete(int result) {
746 net_log_.EndEvent(NetLogEventType::HTTP_STREAM_JOB_WAITING);
747 DCHECK_EQ(OK, result);
748 next_state_ = STATE_INIT_CONNECTION;
749 return OK;
750 }
751
ResumeInitConnection()752 void HttpStreamFactory::Job::ResumeInitConnection() {
753 if (init_connection_already_resumed_) {
754 return;
755 }
756 DCHECK_EQ(next_state_, STATE_INIT_CONNECTION);
757 net_log_.AddEvent(NetLogEventType::HTTP_STREAM_JOB_RESUME_INIT_CONNECTION);
758 init_connection_already_resumed_ = true;
759 OnIOComplete(OK);
760 }
761
DoInitConnection()762 int HttpStreamFactory::Job::DoInitConnection() {
763 net_log_.BeginEvent(NetLogEventType::HTTP_STREAM_JOB_INIT_CONNECTION);
764 int result = DoInitConnectionImpl();
765 if (!expect_on_quic_session_created_ && !expect_on_quic_host_resolution_) {
766 delegate_->OnConnectionInitialized(this, result);
767 }
768 return result;
769 }
770
DoInitConnectionImpl()771 int HttpStreamFactory::Job::DoInitConnectionImpl() {
772 DCHECK(!connection_->is_initialized());
773
774 if (using_quic_ && !proxy_info_.is_direct() &&
775 !proxy_info_.proxy_chain().Last().is_quic()) {
776 // QUIC can not be spoken to non-QUIC proxies. This error should not be
777 // user visible, because the non-alternative Job should be resumed.
778 return ERR_NO_SUPPORTED_PROXIES;
779 }
780
781 DCHECK(proxy_info_.proxy_chain().IsValid());
782 next_state_ = STATE_INIT_CONNECTION_COMPLETE;
783
784 if (using_quic_) {
785 // TODO(mmenke): Clean this up. `disable_cert_verification_network_fetches`
786 // is enabled in ConnectJobFactory for H1/H2 connections. Also need to add
787 // it to the SpdySessionKey for H2 connections.
788 SSLConfig server_ssl_config;
789 server_ssl_config.disable_cert_verification_network_fetches =
790 disable_cert_verification_network_fetches();
791 return DoInitConnectionImplQuic(server_ssl_config.GetCertVerifyFlags());
792 }
793
794 // Check first if there is a pushed stream matching the request, or an HTTP/2
795 // connection this request can pool to. If so, then go straight to using
796 // that.
797 if (CanUseExistingSpdySession()) {
798 if (!existing_spdy_session_) {
799 if (!spdy_session_request_) {
800 // If not currently watching for an H2 session, use
801 // SpdySessionPool::RequestSession() to check for a session, and start
802 // watching for one.
803 bool should_throttle_connect = ShouldThrottleConnectForSpdy();
804 base::RepeatingClosure resume_callback =
805 should_throttle_connect
806 ? base::BindRepeating(
807 &HttpStreamFactory::Job::ResumeInitConnection,
808 ptr_factory_.GetWeakPtr())
809 : base::RepeatingClosure();
810
811 bool is_blocking_request_for_session;
812 existing_spdy_session_ = session_->spdy_session_pool()->RequestSession(
813 spdy_session_key_, enable_ip_based_pooling_, is_websocket_,
814 net_log_, resume_callback, this, &spdy_session_request_,
815 &is_blocking_request_for_session);
816 if (!existing_spdy_session_ && should_throttle_connect &&
817 !is_blocking_request_for_session) {
818 net_log_.AddEvent(NetLogEventType::HTTP_STREAM_JOB_THROTTLED);
819 next_state_ = STATE_INIT_CONNECTION;
820 base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
821 FROM_HERE, resume_callback, base::Milliseconds(kHTTP2ThrottleMs));
822 return ERR_IO_PENDING;
823 }
824 } else if (enable_ip_based_pooling_) {
825 // If already watching for an H2 session, still need to check for an
826 // existing connection that can be reused through IP pooling, as those
827 // don't post session available notifications.
828 //
829 // TODO(mmenke): Make sessions created through IP pooling invoke the
830 // callback.
831 existing_spdy_session_ =
832 session_->spdy_session_pool()->FindAvailableSession(
833 spdy_session_key_, enable_ip_based_pooling_, is_websocket_,
834 net_log_);
835 }
836 }
837 if (existing_spdy_session_) {
838 // Stop watching for SpdySessions.
839 spdy_session_request_.reset();
840
841 // If we're preconnecting, but we already have a SpdySession, we don't
842 // actually need to preconnect any sockets, so we're done.
843 if (job_type_ == PRECONNECT) {
844 return OK;
845 }
846 negotiated_protocol_ = kProtoHTTP2;
847 next_state_ = STATE_CREATE_STREAM;
848 return OK;
849 }
850 }
851
852 establishing_tunnel_ = !UsingHttpProxyWithoutTunnel();
853
854 if (job_type_ == PRECONNECT) {
855 DCHECK(!is_websocket_);
856 DCHECK(request_info_.socket_tag == SocketTag());
857
858 // The lifeime of the preconnect tasks is not controlled by |connection_|.
859 // It may outlives |this|. So we can't use |io_callback_| which holds
860 // base::Unretained(this).
861 auto callback =
862 base::BindOnce(&Job::OnIOComplete, ptr_factory_.GetWeakPtr());
863
864 return PreconnectSocketsForHttpRequest(
865 destination_, request_info_.load_flags, priority_, session_,
866 proxy_info_, allowed_bad_certs_, request_info_.privacy_mode,
867 request_info_.network_anonymization_key,
868 request_info_.secure_dns_policy, net_log_, num_streams_,
869 std::move(callback));
870 }
871
872 ClientSocketPool::ProxyAuthCallback proxy_auth_callback =
873 base::BindRepeating(&HttpStreamFactory::Job::OnNeedsProxyAuthCallback,
874 base::Unretained(this));
875 if (is_websocket_) {
876 DCHECK(request_info_.socket_tag == SocketTag());
877 DCHECK_EQ(SecureDnsPolicy::kAllow, request_info_.secure_dns_policy);
878 return InitSocketHandleForWebSocketRequest(
879 destination_, request_info_.load_flags, priority_, session_,
880 proxy_info_, allowed_bad_certs_, request_info_.privacy_mode,
881 request_info_.network_anonymization_key, net_log_, connection_.get(),
882 io_callback_, proxy_auth_callback);
883 }
884
885 return InitSocketHandleForHttpRequest(
886 destination_, request_info_.load_flags, priority_, session_, proxy_info_,
887 allowed_bad_certs_, request_info_.privacy_mode,
888 request_info_.network_anonymization_key, request_info_.secure_dns_policy,
889 request_info_.socket_tag, net_log_, connection_.get(), io_callback_,
890 proxy_auth_callback);
891 }
892
DoInitConnectionImplQuic(int server_cert_verifier_flags)893 int HttpStreamFactory::Job::DoInitConnectionImplQuic(
894 int server_cert_verifier_flags) {
895 url::SchemeHostPort destination;
896
897 bool require_dns_https_alpn =
898 (job_type_ == DNS_ALPN_H3) || (job_type_ == PRECONNECT_DNS_ALPN_H3);
899
900 ProxyChain proxy_chain = proxy_info_.proxy_chain();
901 if (!proxy_chain.is_direct()) {
902 // We only support proxying QUIC over QUIC. While MASQUE defines mechanisms
903 // to carry QUIC traffic over non-QUIC proxies, the performance of these
904 // mechanisms would be worse than simply using H/1 or H/2 to reach the
905 // destination. The error for an invalid condition should not be user
906 // visible, because the non-alternative Job should be resumed.
907 if (proxy_chain.AnyProxy(
908 [](const ProxyServer& s) { return !s.is_quic(); })) {
909 return ERR_NO_SUPPORTED_PROXIES;
910 }
911 }
912
913 std::optional<NetworkTrafficAnnotationTag> traffic_annotation =
914 proxy_info_.traffic_annotation().is_valid()
915 ? std::make_optional<NetworkTrafficAnnotationTag>(
916 proxy_info_.traffic_annotation())
917 : std::nullopt;
918
919 // The QuicSessionRequest will take care of connecting to any proxies in the
920 // proxy chain.
921 int rv = quic_request_.Request(
922 destination_, quic_version_, proxy_chain, std::move(traffic_annotation),
923 session_->context().http_user_agent_settings.get(),
924 SessionUsage::kDestination, request_info_.privacy_mode, priority_,
925 request_info_.socket_tag, request_info_.network_anonymization_key,
926 request_info_.secure_dns_policy, require_dns_https_alpn,
927 server_cert_verifier_flags, origin_url_, net_log_, &net_error_details_,
928 base::BindOnce(&Job::OnFailedOnDefaultNetwork, ptr_factory_.GetWeakPtr()),
929 io_callback_);
930 if (rv == OK) {
931 using_existing_quic_session_ = true;
932 } else if (rv == ERR_IO_PENDING) {
933 // There's no available QUIC session. Inform the delegate how long to
934 // delay the main job.
935 delegate_->MaybeSetWaitTimeForMainJob(
936 quic_request_.GetTimeDelayForWaitingJob());
937 // Set up to get notified of either host resolution completion or session
938 // creation, in order to call the delegate's `OnConnectionInitialized`
939 // callback.
940 expect_on_quic_host_resolution_ = quic_request_.WaitForHostResolution(
941 base::BindOnce(&Job::OnQuicHostResolution, base::Unretained(this)));
942 expect_on_quic_session_created_ = quic_request_.WaitForQuicSessionCreation(
943 base::BindOnce(&Job::OnQuicSessionCreated, ptr_factory_.GetWeakPtr()));
944 }
945 return rv;
946 }
947
OnQuicHostResolution(int result)948 void HttpStreamFactory::Job::OnQuicHostResolution(int result) {
949 DCHECK(expect_on_quic_host_resolution_);
950 expect_on_quic_host_resolution_ = false;
951 // If no `OnQuicSessionCreated` call is expected, then consider the
952 // connection "initialized" and inform the delegate. Note that
953 // `OnQuicHostResolution` is actually called somewhat _after_ host resolution
954 // is complete -- the `Job` has already run to the point where it can make no
955 // further progress.
956 if (!expect_on_quic_session_created_) {
957 delegate_->OnConnectionInitialized(this, result);
958 }
959 }
960
OnQuicSessionCreated(int result)961 void HttpStreamFactory::Job::OnQuicSessionCreated(int result) {
962 DCHECK(expect_on_quic_session_created_);
963 expect_on_quic_session_created_ = false;
964 delegate_->OnConnectionInitialized(this, result);
965 }
966
OnFailedOnDefaultNetwork(int result)967 void HttpStreamFactory::Job::OnFailedOnDefaultNetwork(int result) {
968 DCHECK(job_type_ == ALTERNATIVE || job_type_ == DNS_ALPN_H3);
969 DCHECK(using_quic_);
970 delegate_->OnFailedOnDefaultNetwork(this);
971 }
972
DoInitConnectionComplete(int result)973 int HttpStreamFactory::Job::DoInitConnectionComplete(int result) {
974 net_log_.EndEvent(NetLogEventType::HTTP_STREAM_JOB_INIT_CONNECTION);
975
976 establishing_tunnel_ = false;
977
978 // No need to continue waiting for a session, once a connection is
979 // established.
980 spdy_session_request_.reset();
981
982 if ((job_type_ == PRECONNECT) || (job_type_ == PRECONNECT_DNS_ALPN_H3)) {
983 if (using_quic_) {
984 return result;
985 }
986 DCHECK_EQ(OK, result);
987 return OK;
988 }
989
990 resolve_error_info_ = connection_->resolve_error_info();
991
992 // Determine the protocol (HTTP/1.1, HTTP/2, or HTTP/3). This covers both the
993 // origin and some proxy cases. First, if the URL is HTTPS (or WSS), we may
994 // negotiate HTTP/2 or HTTP/3 with the origin. Second, non-tunneled requests
995 // (i.e. HTTP URLs) through an HTTPS or QUIC proxy work by sending the request
996 // to the proxy directly. In that case, this logic also handles the proxy's
997 // negotiated protocol. HTTPS requests are always tunneled, so at most one of
998 // these applies.
999 //
1000 // Tunneled requests may also negotiate ALPN at the proxy, but
1001 // HttpProxyConnectJob handles ALPN. The resulting StreamSocket will not
1002 // report an ALPN protocol.
1003 if (result == OK) {
1004 if (using_quic_) {
1005 // TODO(davidben): Record these values consistently between QUIC and TCP
1006 // below. In the QUIC case, we only record it for origin connections. In
1007 // the TCP case, we also record it for non-tunneled, proxied requests.
1008 if (using_ssl_) {
1009 negotiated_protocol_ = kProtoQUIC;
1010 }
1011 } else if (connection_->socket()->GetNegotiatedProtocol() !=
1012 kProtoUnknown) {
1013 // Only connections that use TLS (either to the origin or via a GET to a
1014 // secure proxy) can negotiate ALPN.
1015 bool get_to_secure_proxy =
1016 IsGetToProxy(proxy_info_.proxy_chain(), origin_url_) &&
1017 proxy_info_.proxy_chain().Last().is_secure_http_like();
1018 DCHECK(using_ssl_ || get_to_secure_proxy);
1019 negotiated_protocol_ = connection_->socket()->GetNegotiatedProtocol();
1020 net_log_.AddEvent(NetLogEventType::HTTP_STREAM_REQUEST_PROTO, [&] {
1021 return NetLogHttpStreamProtoParams(negotiated_protocol_);
1022 });
1023 if (using_spdy()) {
1024 if (is_websocket_) {
1025 // WebSocket is not supported over a fresh HTTP/2 connection. This
1026 // should not be reachable. For the origin, we do not request HTTP/2
1027 // on fresh WebSockets connections, because not all HTTP/2 servers
1028 // implement RFC 8441. For proxies, WebSockets are always tunneled.
1029 //
1030 // TODO(davidben): This isn't a CHECK() because, previously, it was
1031 // reachable in https://crbug.com/828865. However, if reachable, it
1032 // means a bug in the socket pools. The socket pools have since been
1033 // cleaned up, so this may no longer be reachable. Restore the CHECK
1034 // and see if this is still needed.
1035 return ERR_NOT_IMPLEMENTED;
1036 }
1037 }
1038 }
1039 }
1040
1041 if (using_quic_ && result < 0 && !proxy_info_.is_direct() &&
1042 proxy_info_.proxy_chain().Last().is_quic()) {
1043 return ReconsiderProxyAfterError(result);
1044 }
1045
1046 if (expect_spdy_ && !using_spdy()) {
1047 return ERR_ALPN_NEGOTIATION_FAILED;
1048 }
1049
1050 // |result| may be the result of any of the stacked protocols. The following
1051 // logic is used when determining how to interpret an error.
1052 // If |result| < 0:
1053 // and connection_->socket() != NULL, then the SSL handshake ran and it
1054 // is a potentially recoverable error.
1055 // and connection_->socket == NULL and connection_->is_ssl_error() is true,
1056 // then the SSL handshake ran with an unrecoverable error.
1057 // otherwise, the error came from one of the other protocols.
1058 bool ssl_started = using_ssl_ && (result == OK || connection_->socket() ||
1059 connection_->is_ssl_error());
1060 if (!ssl_started && result < 0 && (expect_spdy_ || using_quic_)) {
1061 return result;
1062 }
1063
1064 if (using_quic_) {
1065 if (result < 0) {
1066 return result;
1067 }
1068
1069 if (stream_type_ == HttpStreamRequest::BIDIRECTIONAL_STREAM) {
1070 std::unique_ptr<QuicChromiumClientSession::Handle> session =
1071 quic_request_.ReleaseSessionHandle();
1072 if (!session) {
1073 // Quic session is closed before stream can be created.
1074 return ERR_CONNECTION_CLOSED;
1075 }
1076 bidirectional_stream_impl_ =
1077 std::make_unique<BidirectionalStreamQuicImpl>(std::move(session));
1078 } else {
1079 std::unique_ptr<QuicChromiumClientSession::Handle> session =
1080 quic_request_.ReleaseSessionHandle();
1081 if (!session) {
1082 // Quic session is closed before stream can be created.
1083 return ERR_CONNECTION_CLOSED;
1084 }
1085 auto dns_aliases =
1086 session->GetDnsAliasesForSessionKey(quic_request_.session_key());
1087 stream_ = std::make_unique<QuicHttpStream>(std::move(session),
1088 std::move(dns_aliases));
1089 }
1090 next_state_ = STATE_NONE;
1091 return OK;
1092 }
1093
1094 if (result < 0) {
1095 if (!ssl_started) {
1096 return ReconsiderProxyAfterError(result);
1097 }
1098 return result;
1099 }
1100
1101 next_state_ = STATE_CREATE_STREAM;
1102 return OK;
1103 }
1104
DoWaitingUserAction(int result)1105 int HttpStreamFactory::Job::DoWaitingUserAction(int result) {
1106 // This state indicates that the stream request is in a partially
1107 // completed state, and we've called back to the delegate for more
1108 // information.
1109
1110 // We're always waiting here for the delegate to call us back.
1111 return ERR_IO_PENDING;
1112 }
1113
SetSpdyHttpStreamOrBidirectionalStreamImpl(base::WeakPtr<SpdySession> session)1114 int HttpStreamFactory::Job::SetSpdyHttpStreamOrBidirectionalStreamImpl(
1115 base::WeakPtr<SpdySession> session) {
1116 DCHECK(using_spdy());
1117 auto dns_aliases = session_->spdy_session_pool()->GetDnsAliasesForSessionKey(
1118 spdy_session_key_);
1119
1120 if (is_websocket_) {
1121 DCHECK_NE(job_type_, PRECONNECT);
1122 DCHECK_NE(job_type_, PRECONNECT_DNS_ALPN_H3);
1123 DCHECK(delegate_->websocket_handshake_stream_create_helper());
1124
1125 if (!try_websocket_over_http2_) {
1126 // TODO(davidben): Is this reachable? We shouldn't receive a SpdySession
1127 // if not requested.
1128 return ERR_NOT_IMPLEMENTED;
1129 }
1130
1131 websocket_stream_ =
1132 delegate_->websocket_handshake_stream_create_helper()
1133 ->CreateHttp2Stream(session, std::move(dns_aliases));
1134 return OK;
1135 }
1136 if (stream_type_ == HttpStreamRequest::BIDIRECTIONAL_STREAM) {
1137 bidirectional_stream_impl_ = std::make_unique<BidirectionalStreamSpdyImpl>(
1138 session, net_log_.source());
1139 return OK;
1140 }
1141
1142 // TODO(willchan): Delete this code, because eventually, the HttpStreamFactory
1143 // will be creating all the SpdyHttpStreams, since it will know when
1144 // SpdySessions become available.
1145
1146 stream_ = std::make_unique<SpdyHttpStream>(session, net_log_.source(),
1147 std::move(dns_aliases));
1148 return OK;
1149 }
1150
DoCreateStream()1151 int HttpStreamFactory::Job::DoCreateStream() {
1152 DCHECK(connection_->socket() || existing_spdy_session_.get());
1153 DCHECK(!using_quic_);
1154
1155 next_state_ = STATE_CREATE_STREAM_COMPLETE;
1156
1157 if (!using_spdy()) {
1158 DCHECK(!expect_spdy_);
1159 bool is_for_get_to_http_proxy = UsingHttpProxyWithoutTunnel();
1160 if (is_websocket_) {
1161 DCHECK_NE(job_type_, PRECONNECT);
1162 DCHECK_NE(job_type_, PRECONNECT_DNS_ALPN_H3);
1163 DCHECK(delegate_->websocket_handshake_stream_create_helper());
1164 websocket_stream_ =
1165 delegate_->websocket_handshake_stream_create_helper()
1166 ->CreateBasicStream(std::move(connection_),
1167 is_for_get_to_http_proxy,
1168 session_->websocket_endpoint_lock_manager());
1169 } else {
1170 if (!request_info_.is_http1_allowed) {
1171 return ERR_H2_OR_QUIC_REQUIRED;
1172 }
1173 stream_ = std::make_unique<HttpBasicStream>(std::move(connection_),
1174 is_for_get_to_http_proxy);
1175 }
1176 return OK;
1177 }
1178
1179 CHECK(!stream_.get());
1180
1181 // It is also possible that an HTTP/2 connection has been established since
1182 // last time Job checked above.
1183 if (!existing_spdy_session_) {
1184 // WebSocket over HTTP/2 is only allowed to use existing HTTP/2 connections.
1185 // Therefore `using_spdy()` could not have been set unless a connection had
1186 // already been found.
1187 DCHECK(!is_websocket_);
1188
1189 existing_spdy_session_ =
1190 session_->spdy_session_pool()->FindAvailableSession(
1191 spdy_session_key_, enable_ip_based_pooling_,
1192 /* is_websocket = */ false, net_log_);
1193 }
1194 if (existing_spdy_session_) {
1195 // We picked up an existing session, so we don't need our socket.
1196 if (connection_->socket()) {
1197 connection_->socket()->Disconnect();
1198 }
1199 connection_->Reset();
1200
1201 int set_result =
1202 SetSpdyHttpStreamOrBidirectionalStreamImpl(existing_spdy_session_);
1203 existing_spdy_session_.reset();
1204 return set_result;
1205 }
1206
1207 // Close idle sockets in this group, since subsequent requests will go over
1208 // |spdy_session|.
1209 if (connection_->socket()->IsConnected()) {
1210 connection_->CloseIdleSocketsInGroup("Switching to HTTP2 session");
1211 }
1212
1213 base::WeakPtr<SpdySession> spdy_session;
1214 int rv =
1215 session_->spdy_session_pool()->CreateAvailableSessionFromSocketHandle(
1216 spdy_session_key_, std::move(connection_), net_log_, &spdy_session);
1217
1218 if (rv != OK) {
1219 return rv;
1220 }
1221
1222 url::SchemeHostPort scheme_host_port(
1223 using_ssl_ ? url::kHttpsScheme : url::kHttpScheme,
1224 spdy_session_key_.host_port_pair().host(),
1225 spdy_session_key_.host_port_pair().port());
1226
1227 HttpServerProperties* http_server_properties =
1228 session_->http_server_properties();
1229 if (http_server_properties) {
1230 http_server_properties->SetSupportsSpdy(
1231 scheme_host_port, request_info_.network_anonymization_key,
1232 true /* supports_spdy */);
1233 }
1234
1235 // Create a SpdyHttpStream or a BidirectionalStreamImpl attached to the
1236 // session.
1237 return SetSpdyHttpStreamOrBidirectionalStreamImpl(spdy_session);
1238 }
1239
DoCreateStreamComplete(int result)1240 int HttpStreamFactory::Job::DoCreateStreamComplete(int result) {
1241 if (result < 0) {
1242 return result;
1243 }
1244
1245 session_->proxy_resolution_service()->ReportSuccess(proxy_info_);
1246 next_state_ = STATE_NONE;
1247 return OK;
1248 }
1249
OnSpdySessionAvailable(base::WeakPtr<SpdySession> spdy_session)1250 void HttpStreamFactory::Job::OnSpdySessionAvailable(
1251 base::WeakPtr<SpdySession> spdy_session) {
1252 DCHECK(spdy_session);
1253
1254 // No need for the connection any more, since |spdy_session| can be used
1255 // instead, and there's no benefit from keeping the old ConnectJob in the
1256 // socket pool.
1257 if (connection_) {
1258 connection_->ResetAndCloseSocket();
1259 }
1260
1261 // Once a connection is initialized, or if there's any out-of-band callback,
1262 // like proxy auth challenge, the SpdySessionRequest is cancelled.
1263 DCHECK(next_state_ == STATE_INIT_CONNECTION ||
1264 next_state_ == STATE_INIT_CONNECTION_COMPLETE);
1265
1266 // Ignore calls to ResumeInitConnection() from either the timer or the
1267 // SpdySessionPool.
1268 init_connection_already_resumed_ = true;
1269
1270 // If this is a preconnect, nothing left do to.
1271 if (job_type_ == PRECONNECT) {
1272 OnPreconnectsComplete(OK);
1273 return;
1274 }
1275
1276 negotiated_protocol_ = kProtoHTTP2;
1277 existing_spdy_session_ = spdy_session;
1278 next_state_ = STATE_CREATE_STREAM;
1279
1280 // This will synchronously close |connection_|, so no need to worry about it
1281 // calling back into |this|.
1282 RunLoop(net::OK);
1283 }
1284
ReconsiderProxyAfterError(int error)1285 int HttpStreamFactory::Job::ReconsiderProxyAfterError(int error) {
1286 // Check if the error was a proxy failure.
1287 if (!CanFalloverToNextProxy(proxy_info_.proxy_chain(), error, &error,
1288 proxy_info_.is_for_ip_protection())) {
1289 return error;
1290 }
1291
1292 should_reconsider_proxy_ = true;
1293 return error;
1294 }
1295
MaybeCopyConnectionAttemptsFromHandle()1296 void HttpStreamFactory::Job::MaybeCopyConnectionAttemptsFromHandle() {
1297 if (!connection_) {
1298 return;
1299 }
1300
1301 delegate_->AddConnectionAttemptsToRequest(this,
1302 connection_->connection_attempts());
1303 }
1304
1305 HttpStreamFactory::JobFactory::JobFactory() = default;
1306
1307 HttpStreamFactory::JobFactory::~JobFactory() = default;
1308
1309 std::unique_ptr<HttpStreamFactory::Job>
CreateJob(HttpStreamFactory::Job::Delegate * delegate,HttpStreamFactory::JobType job_type,HttpNetworkSession * session,const StreamRequestInfo & request_info,RequestPriority priority,const ProxyInfo & proxy_info,const std::vector<SSLConfig::CertAndStatus> & allowed_bad_certs,url::SchemeHostPort destination,GURL origin_url,bool is_websocket,bool enable_ip_based_pooling,NetLog * net_log,NextProto alternative_protocol,quic::ParsedQuicVersion quic_version)1310 HttpStreamFactory::JobFactory::CreateJob(
1311 HttpStreamFactory::Job::Delegate* delegate,
1312 HttpStreamFactory::JobType job_type,
1313 HttpNetworkSession* session,
1314 const StreamRequestInfo& request_info,
1315 RequestPriority priority,
1316 const ProxyInfo& proxy_info,
1317 const std::vector<SSLConfig::CertAndStatus>& allowed_bad_certs,
1318 url::SchemeHostPort destination,
1319 GURL origin_url,
1320 bool is_websocket,
1321 bool enable_ip_based_pooling,
1322 NetLog* net_log,
1323 NextProto alternative_protocol,
1324 quic::ParsedQuicVersion quic_version) {
1325 return std::make_unique<HttpStreamFactory::Job>(
1326 delegate, job_type, session, request_info, priority, proxy_info,
1327 allowed_bad_certs, std::move(destination), origin_url,
1328 alternative_protocol, quic_version, is_websocket, enable_ip_based_pooling,
1329 net_log);
1330 }
1331
ShouldThrottleConnectForSpdy() const1332 bool HttpStreamFactory::Job::ShouldThrottleConnectForSpdy() const {
1333 DCHECK(!using_quic_);
1334 DCHECK(!spdy_session_request_);
1335
1336 // If the job has previously been throttled, don't throttle it again.
1337 if (init_connection_already_resumed_) {
1338 return false;
1339 }
1340
1341 url::SchemeHostPort scheme_host_port(
1342 using_ssl_ ? url::kHttpsScheme : url::kHttpScheme,
1343 spdy_session_key_.host_port_pair().host(),
1344 spdy_session_key_.host_port_pair().port());
1345 // Only throttle the request if the server is believed to support H2.
1346 return session_->http_server_properties()->GetSupportsSpdy(
1347 scheme_host_port, request_info_.network_anonymization_key);
1348 }
1349
1350 } // namespace net
1351