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