1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/http/http_stream_factory_impl_job.h"
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/location.h"
13 #include "base/logging.h"
14 #include "base/metrics/histogram_macros.h"
15 #include "base/profiler/scoped_tracker.h"
16 #include "base/single_thread_task_runner.h"
17 #include "base/stl_util.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/string_util.h"
20 #include "base/strings/stringprintf.h"
21 #include "base/thread_task_runner_handle.h"
22 #include "base/values.h"
23 #include "build/build_config.h"
24 #include "net/base/connection_type_histograms.h"
25 #include "net/base/net_util.h"
26 #include "net/base/port_util.h"
27 #include "net/cert/cert_verifier.h"
28 #include "net/http/http_basic_stream.h"
29 #include "net/http/http_network_session.h"
30 #include "net/http/http_proxy_client_socket.h"
31 #include "net/http/http_proxy_client_socket_pool.h"
32 #include "net/http/http_request_info.h"
33 #include "net/http/http_server_properties.h"
34 #include "net/http/http_stream_factory.h"
35 #include "net/http/http_stream_factory_impl_request.h"
36 #include "net/log/net_log.h"
37 #include "net/quic/quic_http_stream.h"
38 #include "net/socket/client_socket_handle.h"
39 #include "net/socket/client_socket_pool.h"
40 #include "net/socket/client_socket_pool_manager.h"
41 #include "net/socket/socks_client_socket_pool.h"
42 #include "net/socket/ssl_client_socket.h"
43 #include "net/socket/ssl_client_socket_pool.h"
44 #include "net/spdy/spdy_http_stream.h"
45 #include "net/spdy/spdy_session.h"
46 #include "net/spdy/spdy_session_pool.h"
47 #include "net/ssl/ssl_cert_request_info.h"
48 #include "net/ssl/ssl_failure_state.h"
52 // Returns parameters associated with the start of a HTTP stream job.
53 scoped_ptr
<base::Value
> NetLogHttpStreamJobCallback(
54 const NetLog::Source
& source
,
55 const GURL
* original_url
,
57 const AlternativeService
* alternative_service
,
58 RequestPriority priority
,
59 NetLogCaptureMode
/* capture_mode */) {
60 scoped_ptr
<base::DictionaryValue
> dict(new base::DictionaryValue());
62 source
.AddToEventParameters(dict
.get());
63 dict
->SetString("original_url", original_url
->GetOrigin().spec());
64 dict
->SetString("url", url
->GetOrigin().spec());
65 dict
->SetString("alternative_service", alternative_service
->ToString());
66 dict
->SetString("priority", RequestPriorityToString(priority
));
70 // Returns parameters associated with the Proto (with NPN negotiation) of a HTTP
72 scoped_ptr
<base::Value
> NetLogHttpStreamProtoCallback(
73 const SSLClientSocket::NextProtoStatus status
,
74 const std::string
* proto
,
75 NetLogCaptureMode
/* capture_mode */) {
76 scoped_ptr
<base::DictionaryValue
> dict(new base::DictionaryValue());
78 dict
->SetString("next_proto_status",
79 SSLClientSocket::NextProtoStatusToString(status
));
80 dict
->SetString("proto", *proto
);
84 HttpStreamFactoryImpl::Job::Job(HttpStreamFactoryImpl
* stream_factory
,
85 HttpNetworkSession
* session
,
86 const HttpRequestInfo
& request_info
,
87 RequestPriority priority
,
88 const SSLConfig
& server_ssl_config
,
89 const SSLConfig
& proxy_ssl_config
,
101 HttpStreamFactoryImpl::Job::Job(HttpStreamFactoryImpl
* stream_factory
,
102 HttpNetworkSession
* session
,
103 const HttpRequestInfo
& request_info
,
104 RequestPriority priority
,
105 const SSLConfig
& server_ssl_config
,
106 const SSLConfig
& proxy_ssl_config
,
107 AlternativeService alternative_service
,
110 request_info_(request_info
),
112 server_ssl_config_(server_ssl_config
),
113 proxy_ssl_config_(proxy_ssl_config
),
114 net_log_(BoundNetLog::Make(net_log
, NetLog::SOURCE_HTTP_STREAM_JOB
)),
115 io_callback_(base::Bind(&Job::OnIOComplete
, base::Unretained(this))),
116 connection_(new ClientSocketHandle
),
118 stream_factory_(stream_factory
),
119 next_state_(STATE_NONE
),
121 alternative_service_(alternative_service
),
127 quic_request_(session_
->quic_stream_factory()),
128 using_existing_quic_session_(false),
129 spdy_certificate_error_(OK
),
130 establishing_tunnel_(false),
131 was_npn_negotiated_(false),
132 protocol_negotiated_(kProtoUnknown
),
134 spdy_session_direct_(false),
135 job_status_(STATUS_RUNNING
),
136 other_job_status_(STATUS_RUNNING
),
138 DCHECK(stream_factory
);
140 if (IsQuicAlternative()) {
141 DCHECK(session_
->params().enable_quic
);
146 HttpStreamFactoryImpl::Job::~Job() {
147 net_log_
.EndEvent(NetLog::TYPE_HTTP_STREAM_JOB
);
149 // When we're in a partially constructed state, waiting for the user to
150 // provide certificate handling information or authentication, we can't reuse
151 // this stream at all.
152 if (next_state_
== STATE_WAITING_USER_ACTION
) {
153 connection_
->socket()->Disconnect();
158 session_
->proxy_service()->CancelPacRequest(pac_request_
);
160 // The stream could be in a partial state. It is not reusable.
161 if (stream_
.get() && next_state_
!= STATE_DONE
)
162 stream_
->Close(true /* not reusable */);
165 void HttpStreamFactoryImpl::Job::Start(Request
* request
) {
171 int HttpStreamFactoryImpl::Job::Preconnect(int num_streams
) {
172 DCHECK_GT(num_streams
, 0);
173 base::WeakPtr
<HttpServerProperties
> http_server_properties
=
174 session_
->http_server_properties();
175 if (http_server_properties
&&
176 http_server_properties
->SupportsRequestPriority(
177 HostPortPair::FromURL(request_info_
.url
))) {
180 num_streams_
= num_streams
;
182 return StartInternal();
185 int HttpStreamFactoryImpl::Job::RestartTunnelWithProxyAuth(
186 const AuthCredentials
& credentials
) {
187 DCHECK(establishing_tunnel_
);
188 next_state_
= STATE_RESTART_TUNNEL_AUTH
;
193 LoadState
HttpStreamFactoryImpl::Job::GetLoadState() const {
194 switch (next_state_
) {
195 case STATE_RESOLVE_PROXY_COMPLETE
:
196 return session_
->proxy_service()->GetLoadState(pac_request_
);
197 case STATE_INIT_CONNECTION_COMPLETE
:
198 case STATE_CREATE_STREAM_COMPLETE
:
199 return using_quic_
? LOAD_STATE_CONNECTING
: connection_
->GetLoadState();
201 return LOAD_STATE_IDLE
;
205 void HttpStreamFactoryImpl::Job::WaitFor(Job
* job
) {
206 DCHECK_EQ(STATE_NONE
, next_state_
);
207 DCHECK_EQ(STATE_NONE
, job
->next_state_
);
208 DCHECK(!blocking_job_
);
209 DCHECK(!job
->waiting_job_
);
211 // Never share connection with other jobs for FTP requests.
212 DCHECK(!request_info_
.url
.SchemeIs("ftp"));
215 job
->waiting_job_
= this;
218 void HttpStreamFactoryImpl::Job::Resume(Job
* job
,
219 const base::TimeDelta
& delay
) {
220 DCHECK_EQ(blocking_job_
, job
);
221 blocking_job_
= NULL
;
223 // We know we're blocked if the next_state_ is STATE_WAIT_FOR_JOB_COMPLETE.
225 if (next_state_
== STATE_WAIT_FOR_JOB_COMPLETE
) {
226 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
227 FROM_HERE
, base::Bind(&HttpStreamFactoryImpl::Job::OnIOComplete
,
228 ptr_factory_
.GetWeakPtr(), OK
),
233 void HttpStreamFactoryImpl::Job::Orphan(const Request
* request
) {
234 DCHECK_EQ(request_
, request
);
236 net_log_
.AddEvent(NetLog::TYPE_HTTP_STREAM_JOB_ORPHANED
);
238 // We've been orphaned, but there's a job we're blocked on. Don't bother
239 // racing, just cancel ourself.
240 DCHECK(blocking_job_
->waiting_job_
);
241 blocking_job_
->waiting_job_
= NULL
;
242 blocking_job_
= NULL
;
243 if (stream_factory_
->for_websockets_
&&
244 connection_
&& connection_
->socket()) {
245 connection_
->socket()->Disconnect();
247 stream_factory_
->OnOrphanedJobComplete(this);
248 } else if (stream_factory_
->for_websockets_
) {
249 // We cancel this job because a WebSocketHandshakeStream can't be created
250 // without a WebSocketHandshakeStreamBase::CreateHelper which is stored in
251 // the Request class and isn't accessible from this job.
252 if (connection_
&& connection_
->socket()) {
253 connection_
->socket()->Disconnect();
255 stream_factory_
->OnOrphanedJobComplete(this);
259 void HttpStreamFactoryImpl::Job::SetPriority(RequestPriority priority
) {
260 priority_
= priority
;
261 // TODO(akalin): Propagate this to |connection_| and maybe the
265 bool HttpStreamFactoryImpl::Job::was_npn_negotiated() const {
266 return was_npn_negotiated_
;
269 NextProto
HttpStreamFactoryImpl::Job::protocol_negotiated() const {
270 return protocol_negotiated_
;
273 bool HttpStreamFactoryImpl::Job::using_spdy() const {
277 const SSLConfig
& HttpStreamFactoryImpl::Job::server_ssl_config() const {
278 return server_ssl_config_
;
281 const SSLConfig
& HttpStreamFactoryImpl::Job::proxy_ssl_config() const {
282 return proxy_ssl_config_
;
285 const ProxyInfo
& HttpStreamFactoryImpl::Job::proxy_info() const {
289 void HttpStreamFactoryImpl::Job::GetSSLInfo() {
291 DCHECK(!establishing_tunnel_
);
292 DCHECK(connection_
.get() && connection_
->socket());
293 SSLClientSocket
* ssl_socket
=
294 static_cast<SSLClientSocket
*>(connection_
->socket());
295 ssl_socket
->GetSSLInfo(&ssl_info_
);
298 SpdySessionKey
HttpStreamFactoryImpl::Job::GetSpdySessionKey() const {
299 // In the case that we're using an HTTPS proxy for an HTTP url,
300 // we look for a SPDY session *to* the proxy, instead of to the
302 if (IsHttpsProxyAndHttpUrl()) {
303 return SpdySessionKey(proxy_info_
.proxy_server().host_port_pair(),
304 ProxyServer::Direct(), PRIVACY_MODE_DISABLED
);
306 return SpdySessionKey(server_
, proxy_info_
.proxy_server(),
307 request_info_
.privacy_mode
);
310 bool HttpStreamFactoryImpl::Job::CanUseExistingSpdySession() const {
311 // We need to make sure that if a spdy session was created for
312 // https://somehost/ that we don't use that session for http://somehost:443/.
313 // The only time we can use an existing session is if the request URL is
314 // https (the normal case) or if we're connection to a SPDY proxy.
315 // https://crbug.com/133176
316 // TODO(ricea): Add "wss" back to this list when SPDY WebSocket support is
318 return origin_url_
.SchemeIs("https") ||
319 proxy_info_
.proxy_server().is_https() || IsSpdyAlternative();
322 void HttpStreamFactoryImpl::Job::OnStreamReadyCallback() {
323 DCHECK(stream_
.get());
324 DCHECK(!IsPreconnecting());
325 DCHECK(!stream_factory_
->for_websockets_
);
327 MaybeCopyConnectionAttemptsFromSocketOrHandle();
330 stream_factory_
->OnOrphanedJobComplete(this);
332 request_
->Complete(was_npn_negotiated(), protocol_negotiated(),
334 request_
->OnStreamReady(this, server_ssl_config_
, proxy_info_
,
337 // |this| may be deleted after this call.
340 void HttpStreamFactoryImpl::Job::OnWebSocketHandshakeStreamReadyCallback() {
341 DCHECK(websocket_stream_
);
342 DCHECK(!IsPreconnecting());
343 DCHECK(stream_factory_
->for_websockets_
);
344 // An orphaned WebSocket job will be closed immediately and
346 DCHECK(!IsOrphaned());
348 MaybeCopyConnectionAttemptsFromSocketOrHandle();
350 request_
->Complete(was_npn_negotiated(), protocol_negotiated(), using_spdy());
351 request_
->OnWebSocketHandshakeStreamReady(this,
354 websocket_stream_
.release());
355 // |this| may be deleted after this call.
358 void HttpStreamFactoryImpl::Job::OnNewSpdySessionReadyCallback() {
359 DCHECK(stream_
.get());
360 DCHECK(!IsPreconnecting());
361 DCHECK(using_spdy());
362 // Note: an event loop iteration has passed, so |new_spdy_session_| may be
363 // NULL at this point if the SpdySession closed immediately after creation.
364 base::WeakPtr
<SpdySession
> spdy_session
= new_spdy_session_
;
365 new_spdy_session_
.reset();
367 MaybeCopyConnectionAttemptsFromSocketOrHandle();
369 // TODO(jgraettinger): Notify the factory, and let that notify |request_|,
370 // rather than notifying |request_| directly.
373 stream_factory_
->OnNewSpdySessionReady(
374 spdy_session
, spdy_session_direct_
, server_ssl_config_
, proxy_info_
,
375 was_npn_negotiated(), protocol_negotiated(), using_spdy(), net_log_
);
377 stream_factory_
->OnOrphanedJobComplete(this);
379 request_
->OnNewSpdySessionReady(
380 this, stream_
.Pass(), spdy_session
, spdy_session_direct_
);
382 // |this| may be deleted after this call.
385 void HttpStreamFactoryImpl::Job::OnStreamFailedCallback(int result
) {
386 DCHECK(!IsPreconnecting());
388 MaybeCopyConnectionAttemptsFromSocketOrHandle();
391 stream_factory_
->OnOrphanedJobComplete(this);
393 SSLFailureState ssl_failure_state
=
394 connection_
? connection_
->ssl_failure_state() : SSL_FAILURE_NONE
;
395 request_
->OnStreamFailed(this, result
, server_ssl_config_
,
398 // |this| may be deleted after this call.
401 void HttpStreamFactoryImpl::Job::OnCertificateErrorCallback(
402 int result
, const SSLInfo
& ssl_info
) {
403 DCHECK(!IsPreconnecting());
405 MaybeCopyConnectionAttemptsFromSocketOrHandle();
408 stream_factory_
->OnOrphanedJobComplete(this);
410 request_
->OnCertificateError(this, result
, server_ssl_config_
, ssl_info
);
411 // |this| may be deleted after this call.
414 void HttpStreamFactoryImpl::Job::OnNeedsProxyAuthCallback(
415 const HttpResponseInfo
& response
,
416 HttpAuthController
* auth_controller
) {
417 DCHECK(!IsPreconnecting());
419 stream_factory_
->OnOrphanedJobComplete(this);
421 request_
->OnNeedsProxyAuth(
422 this, response
, server_ssl_config_
, proxy_info_
, auth_controller
);
423 // |this| may be deleted after this call.
426 void HttpStreamFactoryImpl::Job::OnNeedsClientAuthCallback(
427 SSLCertRequestInfo
* cert_info
) {
428 DCHECK(!IsPreconnecting());
430 stream_factory_
->OnOrphanedJobComplete(this);
432 request_
->OnNeedsClientAuth(this, server_ssl_config_
, cert_info
);
433 // |this| may be deleted after this call.
436 void HttpStreamFactoryImpl::Job::OnHttpsProxyTunnelResponseCallback(
437 const HttpResponseInfo
& response_info
,
438 HttpStream
* stream
) {
439 DCHECK(!IsPreconnecting());
441 stream_factory_
->OnOrphanedJobComplete(this);
443 request_
->OnHttpsProxyTunnelResponse(
444 this, response_info
, server_ssl_config_
, proxy_info_
, stream
);
445 // |this| may be deleted after this call.
448 void HttpStreamFactoryImpl::Job::OnPreconnectsComplete() {
450 if (new_spdy_session_
.get()) {
451 stream_factory_
->OnNewSpdySessionReady(new_spdy_session_
,
452 spdy_session_direct_
,
455 was_npn_negotiated(),
456 protocol_negotiated(),
460 stream_factory_
->OnPreconnectsComplete(this);
461 // |this| may be deleted after this call.
465 int HttpStreamFactoryImpl::Job::OnHostResolution(
466 SpdySessionPool
* spdy_session_pool
,
467 const SpdySessionKey
& spdy_session_key
,
468 const AddressList
& addresses
,
469 const BoundNetLog
& net_log
) {
470 // It is OK to dereference spdy_session_pool, because the
471 // ClientSocketPoolManager will be destroyed in the same callback that
472 // destroys the SpdySessionPool.
474 spdy_session_pool
->FindAvailableSession(spdy_session_key
, net_log
) ?
475 ERR_SPDY_SESSION_ALREADY_EXISTS
: OK
;
478 void HttpStreamFactoryImpl::Job::OnIOComplete(int result
) {
482 int HttpStreamFactoryImpl::Job::RunLoop(int result
) {
483 result
= DoLoop(result
);
485 if (result
== ERR_IO_PENDING
)
488 // If there was an error, we should have already resumed the |waiting_job_|,
490 DCHECK(result
== OK
|| waiting_job_
== NULL
);
492 if (IsPreconnecting()) {
493 base::ThreadTaskRunnerHandle::Get()->PostTask(
495 base::Bind(&HttpStreamFactoryImpl::Job::OnPreconnectsComplete
,
496 ptr_factory_
.GetWeakPtr()));
497 return ERR_IO_PENDING
;
500 if (IsCertificateError(result
)) {
501 // Retrieve SSL information from the socket.
504 next_state_
= STATE_WAITING_USER_ACTION
;
505 base::ThreadTaskRunnerHandle::Get()->PostTask(
507 base::Bind(&HttpStreamFactoryImpl::Job::OnCertificateErrorCallback
,
508 ptr_factory_
.GetWeakPtr(), result
, ssl_info_
));
509 return ERR_IO_PENDING
;
513 case ERR_PROXY_AUTH_REQUESTED
: {
514 UMA_HISTOGRAM_BOOLEAN("Net.ProxyAuthRequested.HasConnection",
515 connection_
.get() != NULL
);
516 if (!connection_
.get())
517 return ERR_PROXY_AUTH_REQUESTED_WITH_NO_CONNECTION
;
518 CHECK(connection_
->socket());
519 CHECK(establishing_tunnel_
);
521 next_state_
= STATE_WAITING_USER_ACTION
;
522 ProxyClientSocket
* proxy_socket
=
523 static_cast<ProxyClientSocket
*>(connection_
->socket());
524 base::ThreadTaskRunnerHandle::Get()->PostTask(
526 base::Bind(&Job::OnNeedsProxyAuthCallback
, ptr_factory_
.GetWeakPtr(),
527 *proxy_socket
->GetConnectResponseInfo(),
528 proxy_socket
->GetAuthController()));
529 return ERR_IO_PENDING
;
532 case ERR_SSL_CLIENT_AUTH_CERT_NEEDED
:
533 base::ThreadTaskRunnerHandle::Get()->PostTask(
535 base::Bind(&Job::OnNeedsClientAuthCallback
, ptr_factory_
.GetWeakPtr(),
536 connection_
->ssl_error_response_info().cert_request_info
));
537 return ERR_IO_PENDING
;
539 case ERR_HTTPS_PROXY_TUNNEL_RESPONSE
: {
540 DCHECK(connection_
.get());
541 DCHECK(connection_
->socket());
542 DCHECK(establishing_tunnel_
);
544 ProxyClientSocket
* proxy_socket
=
545 static_cast<ProxyClientSocket
*>(connection_
->socket());
546 base::ThreadTaskRunnerHandle::Get()->PostTask(
547 FROM_HERE
, base::Bind(&Job::OnHttpsProxyTunnelResponseCallback
,
548 ptr_factory_
.GetWeakPtr(),
549 *proxy_socket
->GetConnectResponseInfo(),
550 proxy_socket
->CreateConnectResponseStream()));
551 return ERR_IO_PENDING
;
555 job_status_
= STATUS_SUCCEEDED
;
556 MaybeMarkAlternativeServiceBroken();
557 next_state_
= STATE_DONE
;
558 if (new_spdy_session_
.get()) {
559 base::ThreadTaskRunnerHandle::Get()->PostTask(
560 FROM_HERE
, base::Bind(&Job::OnNewSpdySessionReadyCallback
,
561 ptr_factory_
.GetWeakPtr()));
562 } else if (stream_factory_
->for_websockets_
) {
563 DCHECK(websocket_stream_
);
564 base::ThreadTaskRunnerHandle::Get()->PostTask(
565 FROM_HERE
, base::Bind(&Job::OnWebSocketHandshakeStreamReadyCallback
,
566 ptr_factory_
.GetWeakPtr()));
568 DCHECK(stream_
.get());
569 base::ThreadTaskRunnerHandle::Get()->PostTask(
571 base::Bind(&Job::OnStreamReadyCallback
, ptr_factory_
.GetWeakPtr()));
573 return ERR_IO_PENDING
;
576 DCHECK(result
!= ERR_ALTERNATIVE_CERT_NOT_VALID_FOR_ORIGIN
||
577 IsSpdyAlternative() || IsQuicAlternative());
578 if (job_status_
!= STATUS_BROKEN
) {
579 DCHECK_EQ(STATUS_RUNNING
, job_status_
);
580 job_status_
= STATUS_FAILED
;
581 // TODO(bnc): If (result == ERR_ALTERNATIVE_CERT_NOT_VALID_FOR_ORIGIN),
582 // then instead of marking alternative service broken, mark (origin,
583 // alternative service) couple as invalid.
584 MaybeMarkAlternativeServiceBroken();
586 base::ThreadTaskRunnerHandle::Get()->PostTask(
587 FROM_HERE
, base::Bind(&Job::OnStreamFailedCallback
,
588 ptr_factory_
.GetWeakPtr(), result
));
589 return ERR_IO_PENDING
;
593 int HttpStreamFactoryImpl::Job::DoLoop(int result
) {
594 DCHECK_NE(next_state_
, STATE_NONE
);
597 State state
= next_state_
;
598 next_state_
= STATE_NONE
;
604 case STATE_RESOLVE_PROXY
:
606 rv
= DoResolveProxy();
608 case STATE_RESOLVE_PROXY_COMPLETE
:
609 rv
= DoResolveProxyComplete(rv
);
611 case STATE_WAIT_FOR_JOB
:
615 case STATE_WAIT_FOR_JOB_COMPLETE
:
616 rv
= DoWaitForJobComplete(rv
);
618 case STATE_INIT_CONNECTION
:
620 rv
= DoInitConnection();
622 case STATE_INIT_CONNECTION_COMPLETE
:
623 rv
= DoInitConnectionComplete(rv
);
625 case STATE_WAITING_USER_ACTION
:
626 rv
= DoWaitingUserAction(rv
);
628 case STATE_RESTART_TUNNEL_AUTH
:
630 rv
= DoRestartTunnelAuth();
632 case STATE_RESTART_TUNNEL_AUTH_COMPLETE
:
633 rv
= DoRestartTunnelAuthComplete(rv
);
635 case STATE_CREATE_STREAM
:
637 rv
= DoCreateStream();
639 case STATE_CREATE_STREAM_COMPLETE
:
640 rv
= DoCreateStreamComplete(rv
);
643 NOTREACHED() << "bad state";
647 } while (rv
!= ERR_IO_PENDING
&& next_state_
!= STATE_NONE
);
651 int HttpStreamFactoryImpl::Job::StartInternal() {
652 CHECK_EQ(STATE_NONE
, next_state_
);
653 next_state_
= STATE_START
;
654 int rv
= RunLoop(OK
);
655 DCHECK_EQ(ERR_IO_PENDING
, rv
);
659 int HttpStreamFactoryImpl::Job::DoStart() {
660 if (IsSpdyAlternative() || IsQuicAlternative()) {
661 server_
= alternative_service_
.host_port_pair();
663 server_
= HostPortPair::FromURL(request_info_
.url
);
666 stream_factory_
->ApplyHostMappingRules(request_info_
.url
, &server_
);
667 valid_spdy_session_pool_
.reset(new ValidSpdySessionPool(
668 session_
->spdy_session_pool(), origin_url_
, IsSpdyAlternative()));
671 NetLog::TYPE_HTTP_STREAM_JOB
,
672 base::Bind(&NetLogHttpStreamJobCallback
,
673 request_
? request_
->net_log().source() : NetLog::Source(),
674 &request_info_
.url
, &origin_url_
, &alternative_service_
,
677 request_
->net_log().AddEvent(NetLog::TYPE_HTTP_STREAM_REQUEST_STARTED_JOB
,
678 net_log_
.source().ToEventParametersCallback());
681 // Don't connect to restricted ports.
682 if (!IsPortAllowedForScheme(server_
.port(), request_info_
.url
.scheme())) {
684 waiting_job_
->Resume(this, base::TimeDelta());
687 return ERR_UNSAFE_PORT
;
690 next_state_
= STATE_RESOLVE_PROXY
;
694 int HttpStreamFactoryImpl::Job::DoResolveProxy() {
695 DCHECK(!pac_request_
);
698 next_state_
= STATE_RESOLVE_PROXY_COMPLETE
;
700 if (request_info_
.load_flags
& LOAD_BYPASS_PROXY
) {
701 proxy_info_
.UseDirect();
705 // TODO(rch): remove this code since Alt-Svc seems to prohibit it.
706 GURL url_for_proxy
= origin_url_
;
707 // For SPDY via Alt-Svc, set |alternative_service_url_| to
708 // https://<alternative host>:<alternative port>/...
709 // so the proxy resolution works with the actual destination, and so
710 // that the correct socket pool is used.
711 if (IsSpdyAlternative()) {
712 // TODO(rch): Figure out how to make QUIC iteract with PAC
713 // scripts. By not re-writing the URL, we will query the PAC script
714 // for the proxy to use to reach the original URL via TCP. But
715 // the alternate request will be going via UDP to a different port.
716 GURL::Replacements replacements
;
717 // new_port needs to be in scope here because GURL::Replacements references
718 // the memory contained by it directly.
719 const std::string new_port
= base::UintToString(alternative_service_
.port
);
720 replacements
.SetSchemeStr("https");
721 replacements
.SetPortStr(new_port
);
722 url_for_proxy
= url_for_proxy
.ReplaceComponents(replacements
);
725 return session_
->proxy_service()->ResolveProxy(
726 url_for_proxy
, request_info_
.load_flags
, &proxy_info_
, io_callback_
,
727 &pac_request_
, session_
->network_delegate(), net_log_
);
730 int HttpStreamFactoryImpl::Job::DoResolveProxyComplete(int result
) {
734 // Remove unsupported proxies from the list.
735 int supported_proxies
=
736 ProxyServer::SCHEME_DIRECT
| ProxyServer::SCHEME_HTTP
|
737 ProxyServer::SCHEME_HTTPS
| ProxyServer::SCHEME_SOCKS4
|
738 ProxyServer::SCHEME_SOCKS5
;
740 if (session_
->params().enable_quic_for_proxies
)
741 supported_proxies
|= ProxyServer::SCHEME_QUIC
;
743 proxy_info_
.RemoveProxiesWithoutScheme(supported_proxies
);
745 if (proxy_info_
.is_empty()) {
746 // No proxies/direct to choose from. This happens when we don't support
747 // any of the proxies in the returned list.
748 result
= ERR_NO_SUPPORTED_PROXIES
;
749 } else if (using_quic_
&&
750 (!proxy_info_
.is_quic() && !proxy_info_
.is_direct())) {
751 // QUIC can not be spoken to non-QUIC proxies. This error should not be
752 // user visible, because the non-alternative Job should be resumed.
753 result
= ERR_NO_SUPPORTED_PROXIES
;
759 waiting_job_
->Resume(this, base::TimeDelta());
766 next_state_
= STATE_WAIT_FOR_JOB
;
768 next_state_
= STATE_INIT_CONNECTION
;
772 bool HttpStreamFactoryImpl::Job::ShouldForceQuic() const {
773 return session_
->params().enable_quic
&&
774 session_
->params().origin_to_force_quic_on
.Equals(server_
) &&
775 proxy_info_
.is_direct();
778 int HttpStreamFactoryImpl::Job::DoWaitForJob() {
779 DCHECK(blocking_job_
);
780 next_state_
= STATE_WAIT_FOR_JOB_COMPLETE
;
781 return ERR_IO_PENDING
;
784 int HttpStreamFactoryImpl::Job::DoWaitForJobComplete(int result
) {
785 DCHECK(!blocking_job_
);
786 DCHECK_EQ(OK
, result
);
787 next_state_
= STATE_INIT_CONNECTION
;
791 int HttpStreamFactoryImpl::Job::DoInitConnection() {
792 // TODO(pkasting): Remove ScopedTracker below once crbug.com/462812 is fixed.
793 tracked_objects::ScopedTracker
tracking_profile(
794 FROM_HERE_WITH_EXPLICIT_FUNCTION(
795 "462812 HttpStreamFactoryImpl::Job::DoInitConnection"));
796 DCHECK(!blocking_job_
);
797 DCHECK(!connection_
->is_initialized());
798 DCHECK(proxy_info_
.proxy_server().is_valid());
799 next_state_
= STATE_INIT_CONNECTION_COMPLETE
;
801 using_ssl_
= origin_url_
.SchemeIs("https") || origin_url_
.SchemeIs("wss") ||
805 if (ShouldForceQuic())
808 DCHECK(!using_quic_
|| session_
->params().enable_quic
);
810 if (proxy_info_
.is_quic()) {
812 DCHECK(session_
->params().enable_quic_for_proxies
);
815 if (proxy_info_
.is_https() || proxy_info_
.is_quic()) {
816 InitSSLConfig(proxy_info_
.proxy_server().host_port_pair(),
817 &proxy_ssl_config_
, /*is_proxy=*/true);
818 // Disable revocation checking for HTTPS proxies since the revocation
819 // requests are probably going to need to go through the proxy too.
820 proxy_ssl_config_
.rev_checking_enabled
= false;
823 InitSSLConfig(server_
, &server_ssl_config_
, /*is_proxy=*/false);
827 if (proxy_info_
.is_quic() && !request_info_
.url
.SchemeIs("http")) {
829 // TODO(rch): support QUIC proxies for HTTPS urls.
830 return ERR_NOT_IMPLEMENTED
;
832 HostPortPair destination
;
833 std::string origin_host
;
835 SSLConfig
* ssl_config
;
836 if (proxy_info_
.is_quic()) {
837 // A proxy's certificate is expected to be valid for the proxy hostname.
838 destination
= proxy_info_
.proxy_server().host_port_pair();
839 origin_host
= destination
.host();
841 ssl_config
= &proxy_ssl_config_
;
843 // If QUIC is disabled on the destination port, return error.
844 if (session_
->quic_stream_factory()->IsQuicDisabled(destination
.port()))
845 return ERR_QUIC_PROTOCOL_ERROR
;
847 // The certificate of a QUIC alternative server is expected to be valid
848 // for the origin of the request (in addition to being valid for the
850 destination
= server_
;
851 origin_host
= origin_url_
.host();
852 secure_quic
= using_ssl_
;
853 ssl_config
= &server_ssl_config_
;
855 int rv
= quic_request_
.Request(
856 destination
, secure_quic
, request_info_
.privacy_mode
,
857 ssl_config
->GetCertVerifyFlags(), origin_host
, request_info_
.method
,
858 net_log_
, io_callback_
);
860 using_existing_quic_session_
= true;
862 // OK, there's no available QUIC session. Let |waiting_job_| resume
865 if (rv
== ERR_IO_PENDING
) {
866 // Start the |waiting_job_| after the delay returned by
867 // GetTimeDelayForWaitingJob().
869 // If QUIC request fails during handshake, then
870 // DoInitConnectionComplete() will start the |waiting_job_|.
871 waiting_job_
->Resume(this, quic_request_
.GetTimeDelayForWaitingJob());
873 // QUIC request has failed, resume the |waiting_job_|.
874 waiting_job_
->Resume(this, base::TimeDelta());
882 SpdySessionKey spdy_session_key
= GetSpdySessionKey();
884 // Check first if we have a spdy session for this group. If so, then go
885 // straight to using that.
886 if (CanUseExistingSpdySession()) {
887 base::WeakPtr
<SpdySession
> spdy_session
;
888 int result
= valid_spdy_session_pool_
->FindAvailableSession(
889 spdy_session_key
, net_log_
, &spdy_session
);
893 // If we're preconnecting, but we already have a SpdySession, we don't
894 // actually need to preconnect any sockets, so we're done.
895 if (IsPreconnecting())
898 next_state_
= STATE_CREATE_STREAM
;
899 existing_spdy_session_
= spdy_session
;
903 if (request_
&& !request_
->HasSpdySessionKey() && using_ssl_
) {
904 // Update the spdy session key for the request that launched this job.
905 request_
->SetSpdySessionKey(spdy_session_key
);
908 // OK, there's no available SPDY session. Let |waiting_job_| resume if it's
911 waiting_job_
->Resume(this, base::TimeDelta());
915 if (proxy_info_
.is_http() || proxy_info_
.is_https())
916 establishing_tunnel_
= using_ssl_
;
918 const bool expect_spdy
= IsSpdyAlternative();
920 base::WeakPtr
<HttpServerProperties
> http_server_properties
=
921 session_
->http_server_properties();
922 if (http_server_properties
) {
923 http_server_properties
->MaybeForceHTTP11(server_
, &server_ssl_config_
);
924 if (proxy_info_
.is_http() || proxy_info_
.is_https()) {
925 http_server_properties
->MaybeForceHTTP11(
926 proxy_info_
.proxy_server().host_port_pair(), &proxy_ssl_config_
);
930 if (IsPreconnecting()) {
931 DCHECK(!stream_factory_
->for_websockets_
);
932 return PreconnectSocketsForHttpRequest(
933 GetSocketGroup(), server_
, request_info_
.extra_headers
,
934 request_info_
.load_flags
, priority_
, session_
, proxy_info_
, expect_spdy
,
935 server_ssl_config_
, proxy_ssl_config_
, request_info_
.privacy_mode
,
936 net_log_
, num_streams_
);
939 // If we can't use a SPDY session, don't bother checking for one after
940 // the hostname is resolved.
941 OnHostResolutionCallback resolution_callback
=
942 CanUseExistingSpdySession()
943 ? base::Bind(&Job::OnHostResolution
, session_
->spdy_session_pool(),
945 : OnHostResolutionCallback();
946 if (stream_factory_
->for_websockets_
) {
947 // TODO(ricea): Re-enable NPN when WebSockets over SPDY is supported.
948 SSLConfig websocket_server_ssl_config
= server_ssl_config_
;
949 websocket_server_ssl_config
.next_protos
.clear();
950 return InitSocketHandleForWebSocketRequest(
951 GetSocketGroup(), server_
, request_info_
.extra_headers
,
952 request_info_
.load_flags
, priority_
, session_
, proxy_info_
, expect_spdy
,
953 websocket_server_ssl_config
, proxy_ssl_config_
,
954 request_info_
.privacy_mode
, net_log_
, connection_
.get(),
955 resolution_callback
, io_callback_
);
958 return InitSocketHandleForHttpRequest(
959 GetSocketGroup(), server_
, request_info_
.extra_headers
,
960 request_info_
.load_flags
, priority_
, session_
, proxy_info_
, expect_spdy
,
961 server_ssl_config_
, proxy_ssl_config_
, request_info_
.privacy_mode
,
962 net_log_
, connection_
.get(), resolution_callback
, io_callback_
);
965 int HttpStreamFactoryImpl::Job::DoInitConnectionComplete(int result
) {
966 if (using_quic_
&& result
< 0 && waiting_job_
) {
967 waiting_job_
->Resume(this, base::TimeDelta());
970 if (IsPreconnecting()) {
973 DCHECK_EQ(OK
, result
);
977 if (result
== ERR_SPDY_SESSION_ALREADY_EXISTS
) {
978 // We found a SPDY connection after resolving the host. This is
979 // probably an IP pooled connection.
980 SpdySessionKey spdy_session_key
= GetSpdySessionKey();
981 existing_spdy_session_
=
982 session_
->spdy_session_pool()->FindAvailableSession(
983 spdy_session_key
, net_log_
);
984 if (existing_spdy_session_
) {
986 next_state_
= STATE_CREATE_STREAM
;
988 // It is possible that the spdy session no longer exists.
989 ReturnToStateInitConnection(true /* close connection */);
994 if (proxy_info_
.is_quic() && using_quic_
) {
995 if (result
== ERR_QUIC_PROTOCOL_ERROR
||
996 result
== ERR_QUIC_HANDSHAKE_FAILED
|| result
== ERR_MSG_TOO_BIG
) {
998 return ReconsiderProxyAfterError(result
);
1000 // Mark QUIC proxy as bad if QUIC got disabled on the destination port.
1001 // Underlying QUIC layer would have closed the connection.
1002 HostPortPair destination
= proxy_info_
.proxy_server().host_port_pair();
1003 if (session_
->quic_stream_factory()->IsQuicDisabled(destination
.port())) {
1004 using_quic_
= false;
1005 return ReconsiderProxyAfterError(ERR_QUIC_PROTOCOL_ERROR
);
1009 // TODO(willchan): Make this a bit more exact. Maybe there are recoverable
1010 // errors, such as ignoring certificate errors for Alternate-Protocol.
1011 if (result
< 0 && waiting_job_
) {
1012 waiting_job_
->Resume(this, base::TimeDelta());
1013 waiting_job_
= NULL
;
1016 // |result| may be the result of any of the stacked pools. The following
1017 // logic is used when determining how to interpret an error.
1019 // and connection_->socket() != NULL, then the SSL handshake ran and it
1020 // is a potentially recoverable error.
1021 // and connection_->socket == NULL and connection_->is_ssl_error() is true,
1022 // then the SSL handshake ran with an unrecoverable error.
1023 // otherwise, the error came from one of the other pools.
1024 bool ssl_started
= using_ssl_
&& (result
== OK
|| connection_
->socket() ||
1025 connection_
->is_ssl_error());
1027 if (ssl_started
&& (result
== OK
|| IsCertificateError(result
))) {
1028 if (using_quic_
&& result
== OK
) {
1029 was_npn_negotiated_
= true;
1030 protocol_negotiated_
=
1031 SSLClientSocket::NextProtoFromString("quic/1+spdy/3");
1033 SSLClientSocket
* ssl_socket
=
1034 static_cast<SSLClientSocket
*>(connection_
->socket());
1035 if (ssl_socket
->WasNpnNegotiated()) {
1036 was_npn_negotiated_
= true;
1038 SSLClientSocket::NextProtoStatus status
=
1039 ssl_socket
->GetNextProto(&proto
);
1040 protocol_negotiated_
= SSLClientSocket::NextProtoFromString(proto
);
1042 NetLog::TYPE_HTTP_STREAM_REQUEST_PROTO
,
1043 base::Bind(&NetLogHttpStreamProtoCallback
,
1045 if (NextProtoIsSPDY(protocol_negotiated_
))
1049 } else if (proxy_info_
.is_https() && connection_
->socket() &&
1051 ProxyClientSocket
* proxy_socket
=
1052 static_cast<ProxyClientSocket
*>(connection_
->socket());
1053 if (proxy_socket
->IsUsingSpdy()) {
1054 was_npn_negotiated_
= true;
1055 protocol_negotiated_
= proxy_socket
->GetProtocolNegotiated();
1060 if (result
== ERR_PROXY_AUTH_REQUESTED
||
1061 result
== ERR_HTTPS_PROXY_TUNNEL_RESPONSE
) {
1062 DCHECK(!ssl_started
);
1063 // Other state (i.e. |using_ssl_|) suggests that |connection_| will have an
1064 // SSL socket, but there was an error before that could happen. This
1065 // puts the in progress HttpProxy socket into |connection_| in order to
1066 // complete the auth (or read the response body). The tunnel restart code
1067 // is careful to remove it before returning control to the rest of this
1069 connection_
.reset(connection_
->release_pending_http_proxy_connection());
1073 if (IsSpdyAlternative() && !using_spdy_
) {
1074 job_status_
= STATUS_BROKEN
;
1075 MaybeMarkAlternativeServiceBroken();
1076 return ERR_NPN_NEGOTIATION_FAILED
;
1079 if (!ssl_started
&& result
< 0 &&
1080 (IsSpdyAlternative() || IsQuicAlternative())) {
1081 job_status_
= STATUS_BROKEN
;
1082 // TODO(bnc): if (result == ERR_ALTERNATIVE_CERT_NOT_VALID_FOR_ORIGIN), then
1083 // instead of marking alternative service broken, mark (origin, alternative
1084 // service) couple as invalid.
1085 MaybeMarkAlternativeServiceBroken();
1091 job_status_
= STATUS_BROKEN
;
1092 MaybeMarkAlternativeServiceBroken();
1095 stream_
= quic_request_
.ReleaseStream();
1096 next_state_
= STATE_NONE
;
1100 if (result
< 0 && !ssl_started
)
1101 return ReconsiderProxyAfterError(result
);
1102 establishing_tunnel_
= false;
1104 if (connection_
->socket()) {
1105 // We officially have a new connection. Record the type.
1106 if (!connection_
->is_reused()) {
1107 ConnectionType type
= using_spdy_
? CONNECTION_SPDY
: CONNECTION_HTTP
;
1108 UpdateConnectionTypeHistograms(type
);
1112 // Handle SSL errors below.
1114 DCHECK(ssl_started
);
1115 if (IsCertificateError(result
)) {
1116 if (IsSpdyAlternative() && origin_url_
.SchemeIs("http")) {
1117 // We ignore certificate errors for http over spdy.
1118 spdy_certificate_error_
= result
;
1121 result
= HandleCertificateError(result
);
1122 if (result
== OK
&& !connection_
->socket()->IsConnectedAndIdle()) {
1123 ReturnToStateInitConnection(true /* close connection */);
1132 next_state_
= STATE_CREATE_STREAM
;
1136 int HttpStreamFactoryImpl::Job::DoWaitingUserAction(int result
) {
1137 // This state indicates that the stream request is in a partially
1138 // completed state, and we've called back to the delegate for more
1141 // We're always waiting here for the delegate to call us back.
1142 return ERR_IO_PENDING
;
1145 int HttpStreamFactoryImpl::Job::SetSpdyHttpStream(
1146 base::WeakPtr
<SpdySession
> session
, bool direct
) {
1147 // TODO(ricea): Restore the code for WebSockets over SPDY once it's
1149 if (stream_factory_
->for_websockets_
)
1150 return ERR_NOT_IMPLEMENTED
;
1152 // TODO(willchan): Delete this code, because eventually, the
1153 // HttpStreamFactoryImpl will be creating all the SpdyHttpStreams, since it
1154 // will know when SpdySessions become available.
1156 bool use_relative_url
= direct
|| request_info_
.url
.SchemeIs("https");
1157 stream_
.reset(new SpdyHttpStream(session
, use_relative_url
));
1161 int HttpStreamFactoryImpl::Job::DoCreateStream() {
1162 // TODO(pkasting): Remove ScopedTracker below once crbug.com/462811 is fixed.
1163 tracked_objects::ScopedTracker
tracking_profile(
1164 FROM_HERE_WITH_EXPLICIT_FUNCTION(
1165 "462811 HttpStreamFactoryImpl::Job::DoCreateStream"));
1166 DCHECK(connection_
->socket() || existing_spdy_session_
.get() || using_quic_
);
1167 DCHECK(!IsQuicAlternative());
1169 next_state_
= STATE_CREATE_STREAM_COMPLETE
;
1171 // We only set the socket motivation if we're the first to use
1172 // this socket. Is there a race for two SPDY requests? We really
1173 // need to plumb this through to the connect level.
1174 if (connection_
->socket() && !connection_
->is_reused())
1175 SetSocketMotivation();
1178 DCHECK(!IsSpdyAlternative());
1179 // We may get ftp scheme when fetching ftp resources through proxy.
1180 bool using_proxy
= (proxy_info_
.is_http() || proxy_info_
.is_https()) &&
1181 (request_info_
.url
.SchemeIs("http") ||
1182 request_info_
.url
.SchemeIs("ftp"));
1183 if (stream_factory_
->for_websockets_
) {
1185 DCHECK(request_
->websocket_handshake_stream_create_helper());
1186 websocket_stream_
.reset(
1187 request_
->websocket_handshake_stream_create_helper()
1188 ->CreateBasicStream(connection_
.Pass(), using_proxy
));
1190 stream_
.reset(new HttpBasicStream(connection_
.release(), using_proxy
));
1195 CHECK(!stream_
.get());
1197 bool direct
= !IsHttpsProxyAndHttpUrl();
1198 if (existing_spdy_session_
.get()) {
1199 // We picked up an existing session, so we don't need our socket.
1200 if (connection_
->socket())
1201 connection_
->socket()->Disconnect();
1202 connection_
->Reset();
1204 int set_result
= SetSpdyHttpStream(existing_spdy_session_
, direct
);
1205 existing_spdy_session_
.reset();
1209 SpdySessionKey spdy_session_key
= GetSpdySessionKey();
1210 base::WeakPtr
<SpdySession
> spdy_session
;
1211 int result
= valid_spdy_session_pool_
->FindAvailableSession(
1212 spdy_session_key
, net_log_
, &spdy_session
);
1217 return SetSpdyHttpStream(spdy_session
, direct
);
1220 result
= valid_spdy_session_pool_
->CreateAvailableSessionFromSocket(
1221 spdy_session_key
, connection_
.Pass(), net_log_
, spdy_certificate_error_
,
1222 using_ssl_
, &spdy_session
);
1227 if (!spdy_session
->HasAcceptableTransportSecurity()) {
1228 spdy_session
->CloseSessionOnError(
1229 ERR_SPDY_INADEQUATE_TRANSPORT_SECURITY
, "");
1230 return ERR_SPDY_INADEQUATE_TRANSPORT_SECURITY
;
1233 new_spdy_session_
= spdy_session
;
1234 spdy_session_direct_
= direct
;
1235 const HostPortPair
& host_port_pair
= spdy_session_key
.host_port_pair();
1236 base::WeakPtr
<HttpServerProperties
> http_server_properties
=
1237 session_
->http_server_properties();
1238 if (http_server_properties
)
1239 http_server_properties
->SetSupportsSpdy(host_port_pair
, true);
1241 // Create a SpdyHttpStream attached to the session;
1242 // OnNewSpdySessionReadyCallback is not called until an event loop
1243 // iteration later, so if the SpdySession is closed between then, allow
1244 // reuse state from the underlying socket, sampled by SpdyHttpStream,
1245 // bubble up to the request.
1246 return SetSpdyHttpStream(new_spdy_session_
, spdy_session_direct_
);
1249 int HttpStreamFactoryImpl::Job::DoCreateStreamComplete(int result
) {
1253 session_
->proxy_service()->ReportSuccess(proxy_info_
,
1254 session_
->network_delegate());
1255 next_state_
= STATE_NONE
;
1259 int HttpStreamFactoryImpl::Job::DoRestartTunnelAuth() {
1260 next_state_
= STATE_RESTART_TUNNEL_AUTH_COMPLETE
;
1261 ProxyClientSocket
* proxy_socket
=
1262 static_cast<ProxyClientSocket
*>(connection_
->socket());
1263 return proxy_socket
->RestartWithAuth(io_callback_
);
1266 int HttpStreamFactoryImpl::Job::DoRestartTunnelAuthComplete(int result
) {
1267 if (result
== ERR_PROXY_AUTH_REQUESTED
)
1271 // Now that we've got the HttpProxyClientSocket connected. We have
1272 // to release it as an idle socket into the pool and start the connection
1273 // process from the beginning. Trying to pass it in with the
1274 // SSLSocketParams might cause a deadlock since params are dispatched
1275 // interchangeably. This request won't necessarily get this http proxy
1276 // socket, but there will be forward progress.
1277 establishing_tunnel_
= false;
1278 ReturnToStateInitConnection(false /* do not close connection */);
1282 if (result
== ERR_CONNECTION_CLOSED
|| result
== ERR_CONNECTION_RESET
||
1283 result
== ERR_SOCKET_NOT_CONNECTED
) {
1284 // The server may have closed the connection while waiting for auth data.
1285 // Automatically try to use a new connection.
1286 establishing_tunnel_
= false;
1287 ReturnToStateInitConnection(true /* close connection */);
1291 return ReconsiderProxyAfterError(result
);
1294 void HttpStreamFactoryImpl::Job::ReturnToStateInitConnection(
1295 bool close_connection
) {
1296 if (close_connection
&& connection_
->socket())
1297 connection_
->socket()->Disconnect();
1298 connection_
->Reset();
1301 request_
->RemoveRequestFromSpdySessionRequestMap();
1303 next_state_
= STATE_INIT_CONNECTION
;
1306 void HttpStreamFactoryImpl::Job::SetSocketMotivation() {
1307 if (request_info_
.motivation
== HttpRequestInfo::PRECONNECT_MOTIVATED
)
1308 connection_
->socket()->SetSubresourceSpeculation();
1309 else if (request_info_
.motivation
== HttpRequestInfo::OMNIBOX_MOTIVATED
)
1310 connection_
->socket()->SetOmniboxSpeculation();
1311 // TODO(mbelshe): Add other motivations (like EARLY_LOAD_MOTIVATED).
1314 bool HttpStreamFactoryImpl::Job::IsHttpsProxyAndHttpUrl() const {
1315 if (!proxy_info_
.is_https())
1317 if (IsSpdyAlternative() || IsQuicAlternative()) {
1318 // We currently only support Alternate-Protocol where the original scheme
1320 DCHECK(origin_url_
.SchemeIs("http"));
1321 return origin_url_
.SchemeIs("http");
1323 return request_info_
.url
.SchemeIs("http");
1326 bool HttpStreamFactoryImpl::Job::IsSpdyAlternative() const {
1327 return alternative_service_
.protocol
>= NPN_SPDY_MINIMUM_VERSION
&&
1328 alternative_service_
.protocol
<= NPN_SPDY_MAXIMUM_VERSION
;
1331 bool HttpStreamFactoryImpl::Job::IsQuicAlternative() const {
1332 return alternative_service_
.protocol
== QUIC
;
1335 void HttpStreamFactoryImpl::Job::InitSSLConfig(const HostPortPair
& server
,
1336 SSLConfig
* ssl_config
,
1337 bool is_proxy
) const {
1339 // Prior to HTTP/2 and SPDY, some servers use TLS renegotiation to request
1340 // TLS client authentication after the HTTP request was sent. Allow
1341 // renegotiation for only those connections.
1343 // Note that this does NOT implement the provision in
1344 // https://http2.github.io/http2-spec/#rfc.section.9.2.1 which allows the
1345 // server to request a renegotiation immediately before sending the
1346 // connection preface as waiting for the preface would cost the round trip
1347 // that False Start otherwise saves.
1348 ssl_config
->renego_allowed_default
= true;
1349 ssl_config
->renego_allowed_for_protos
.push_back(kProtoHTTP11
);
1352 if (proxy_info_
.is_https() && ssl_config
->send_client_cert
) {
1353 // When connecting through an HTTPS proxy, disable TLS False Start so
1354 // that client authentication errors can be distinguished between those
1355 // originating from the proxy server (ERR_PROXY_CONNECTION_FAILED) and
1356 // those originating from the endpoint (ERR_SSL_PROTOCOL_ERROR /
1357 // ERR_BAD_SSL_CLIENT_AUTH_CERT).
1359 // This assumes the proxy will only request certificates on the initial
1360 // handshake; renegotiation on the proxy connection is unsupported.
1361 ssl_config
->false_start_enabled
= false;
1364 if (request_info_
.load_flags
& LOAD_VERIFY_EV_CERT
)
1365 ssl_config
->verify_ev_cert
= true;
1367 // Disable Channel ID if privacy mode is enabled.
1368 if (request_info_
.privacy_mode
== PRIVACY_MODE_ENABLED
)
1369 ssl_config
->channel_id_enabled
= false;
1373 int HttpStreamFactoryImpl::Job::ReconsiderProxyAfterError(int error
) {
1374 DCHECK(!pac_request_
);
1377 // A failure to resolve the hostname or any error related to establishing a
1378 // TCP connection could be grounds for trying a new proxy configuration.
1380 // Why do this when a hostname cannot be resolved? Some URLs only make sense
1381 // to proxy servers. The hostname in those URLs might fail to resolve if we
1382 // are still using a non-proxy config. We need to check if a proxy config
1383 // now exists that corresponds to a proxy server that could load the URL.
1386 case ERR_PROXY_CONNECTION_FAILED
:
1387 case ERR_NAME_NOT_RESOLVED
:
1388 case ERR_INTERNET_DISCONNECTED
:
1389 case ERR_ADDRESS_UNREACHABLE
:
1390 case ERR_CONNECTION_CLOSED
:
1391 case ERR_CONNECTION_TIMED_OUT
:
1392 case ERR_CONNECTION_RESET
:
1393 case ERR_CONNECTION_REFUSED
:
1394 case ERR_CONNECTION_ABORTED
:
1396 case ERR_TUNNEL_CONNECTION_FAILED
:
1397 case ERR_SOCKS_CONNECTION_FAILED
:
1398 // This can happen in the case of trying to talk to a proxy using SSL, and
1399 // ending up talking to a captive portal that supports SSL instead.
1400 case ERR_PROXY_CERTIFICATE_INVALID
:
1401 // This can happen when trying to talk SSL to a non-SSL server (Like a
1403 case ERR_QUIC_PROTOCOL_ERROR
:
1404 case ERR_QUIC_HANDSHAKE_FAILED
:
1405 case ERR_MSG_TOO_BIG
:
1406 case ERR_SSL_PROTOCOL_ERROR
:
1408 case ERR_SOCKS_CONNECTION_HOST_UNREACHABLE
:
1409 // Remap the SOCKS-specific "host unreachable" error to a more
1410 // generic error code (this way consumers like the link doctor
1411 // know to substitute their error page).
1413 // Note that if the host resolving was done by the SOCKS5 proxy, we can't
1414 // differentiate between a proxy-side "host not found" versus a proxy-side
1415 // "address unreachable" error, and will report both of these failures as
1416 // ERR_ADDRESS_UNREACHABLE.
1417 return ERR_ADDRESS_UNREACHABLE
;
1422 // Do not bypass non-QUIC proxy on ERR_MSG_TOO_BIG.
1423 if (!proxy_info_
.is_quic() && error
== ERR_MSG_TOO_BIG
)
1426 if (request_info_
.load_flags
& LOAD_BYPASS_PROXY
)
1429 if (proxy_info_
.is_https() && proxy_ssl_config_
.send_client_cert
) {
1430 session_
->ssl_client_auth_cache()->Remove(
1431 proxy_info_
.proxy_server().host_port_pair());
1434 int rv
= session_
->proxy_service()->ReconsiderProxyAfterError(
1435 request_info_
.url
, request_info_
.load_flags
, error
, &proxy_info_
,
1436 io_callback_
, &pac_request_
, session_
->network_delegate(), net_log_
);
1437 if (rv
== OK
|| rv
== ERR_IO_PENDING
) {
1438 // If the error was during connection setup, there is no socket to
1440 if (connection_
->socket())
1441 connection_
->socket()->Disconnect();
1442 connection_
->Reset();
1444 request_
->RemoveRequestFromSpdySessionRequestMap();
1445 next_state_
= STATE_RESOLVE_PROXY_COMPLETE
;
1447 // If ReconsiderProxyAfterError() failed synchronously, it means
1448 // there was nothing left to fall-back to, so fail the transaction
1449 // with the last connection error we got.
1450 // TODO(eroman): This is a confusing contract, make it more obvious.
1457 int HttpStreamFactoryImpl::Job::HandleCertificateError(int error
) {
1459 DCHECK(IsCertificateError(error
));
1461 SSLClientSocket
* ssl_socket
=
1462 static_cast<SSLClientSocket
*>(connection_
->socket());
1463 ssl_socket
->GetSSLInfo(&ssl_info_
);
1465 // Add the bad certificate to the set of allowed certificates in the
1466 // SSL config object. This data structure will be consulted after calling
1467 // RestartIgnoringLastError(). And the user will be asked interactively
1468 // before RestartIgnoringLastError() is ever called.
1469 SSLConfig::CertAndStatus bad_cert
;
1471 // |ssl_info_.cert| may be NULL if we failed to create
1472 // X509Certificate for whatever reason, but normally it shouldn't
1473 // happen, unless this code is used inside sandbox.
1474 if (ssl_info_
.cert
.get() == NULL
||
1475 !X509Certificate::GetDEREncoded(ssl_info_
.cert
->os_cert_handle(),
1476 &bad_cert
.der_cert
)) {
1479 bad_cert
.cert_status
= ssl_info_
.cert_status
;
1480 server_ssl_config_
.allowed_bad_certs
.push_back(bad_cert
);
1482 int load_flags
= request_info_
.load_flags
;
1483 if (session_
->params().ignore_certificate_errors
)
1484 load_flags
|= LOAD_IGNORE_ALL_CERT_ERRORS
;
1485 if (ssl_socket
->IgnoreCertError(error
, load_flags
))
1490 void HttpStreamFactoryImpl::Job::SwitchToSpdyMode() {
1491 if (HttpStreamFactory::spdy_enabled())
1495 bool HttpStreamFactoryImpl::Job::IsPreconnecting() const {
1496 DCHECK_GE(num_streams_
, 0);
1497 return num_streams_
> 0;
1500 bool HttpStreamFactoryImpl::Job::IsOrphaned() const {
1501 return !IsPreconnecting() && !request_
;
1504 void HttpStreamFactoryImpl::Job::ReportJobSucceededForRequest() {
1505 if (using_existing_quic_session_
) {
1506 // If an existing session was used, then no TCP connection was
1508 HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_NO_RACE
);
1509 } else if (IsSpdyAlternative() || IsQuicAlternative()) {
1510 // This Job was the alternative Job, and hence won the race.
1511 HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_WON_RACE
);
1513 // This Job was the normal Job, and hence the alternative Job lost the race.
1514 HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_LOST_RACE
);
1518 void HttpStreamFactoryImpl::Job::MarkOtherJobComplete(const Job
& job
) {
1519 DCHECK_EQ(STATUS_RUNNING
, other_job_status_
);
1520 other_job_status_
= job
.job_status_
;
1521 other_job_alternative_service_
= job
.alternative_service_
;
1522 MaybeMarkAlternativeServiceBroken();
1525 void HttpStreamFactoryImpl::Job::MaybeMarkAlternativeServiceBroken() {
1526 if (job_status_
== STATUS_RUNNING
|| other_job_status_
== STATUS_RUNNING
)
1529 if (IsSpdyAlternative() || IsQuicAlternative()) {
1530 if (job_status_
== STATUS_BROKEN
&& other_job_status_
== STATUS_SUCCEEDED
) {
1531 HistogramBrokenAlternateProtocolLocation(
1532 BROKEN_ALTERNATE_PROTOCOL_LOCATION_HTTP_STREAM_FACTORY_IMPL_JOB_ALT
);
1533 session_
->http_server_properties()->MarkAlternativeServiceBroken(
1534 alternative_service_
);
1539 if (job_status_
== STATUS_SUCCEEDED
&& other_job_status_
== STATUS_BROKEN
) {
1540 HistogramBrokenAlternateProtocolLocation(
1541 BROKEN_ALTERNATE_PROTOCOL_LOCATION_HTTP_STREAM_FACTORY_IMPL_JOB_MAIN
);
1542 session_
->http_server_properties()->MarkAlternativeServiceBroken(
1543 other_job_alternative_service_
);
1547 HttpStreamFactoryImpl::Job::ValidSpdySessionPool::ValidSpdySessionPool(
1548 SpdySessionPool
* spdy_session_pool
,
1550 bool is_spdy_alternative
)
1551 : spdy_session_pool_(spdy_session_pool
),
1552 origin_url_(origin_url
),
1553 is_spdy_alternative_(is_spdy_alternative
) {
1556 int HttpStreamFactoryImpl::Job::ValidSpdySessionPool::FindAvailableSession(
1557 const SpdySessionKey
& key
,
1558 const BoundNetLog
& net_log
,
1559 base::WeakPtr
<SpdySession
>* spdy_session
) {
1560 *spdy_session
= spdy_session_pool_
->FindAvailableSession(key
, net_log
);
1561 return CheckAlternativeServiceValidityForOrigin(*spdy_session
);
1564 int HttpStreamFactoryImpl::Job::ValidSpdySessionPool::
1565 CreateAvailableSessionFromSocket(const SpdySessionKey
& key
,
1566 scoped_ptr
<ClientSocketHandle
> connection
,
1567 const BoundNetLog
& net_log
,
1568 int certificate_error_code
,
1570 base::WeakPtr
<SpdySession
>* spdy_session
) {
1571 *spdy_session
= spdy_session_pool_
->CreateAvailableSessionFromSocket(
1572 key
, connection
.Pass(), net_log
, certificate_error_code
, is_secure
);
1573 return CheckAlternativeServiceValidityForOrigin(*spdy_session
);
1576 int HttpStreamFactoryImpl::Job::ValidSpdySessionPool::
1577 CheckAlternativeServiceValidityForOrigin(
1578 base::WeakPtr
<SpdySession
> spdy_session
) {
1579 // For an alternative Job, server_.host() might be different than
1580 // origin_url_.host(), therefore it needs to be verified that the former
1581 // provides a certificate that is valid for the latter.
1582 if (!is_spdy_alternative_
|| !spdy_session
||
1583 spdy_session
->VerifyDomainAuthentication(origin_url_
.host())) {
1586 return ERR_ALTERNATIVE_CERT_NOT_VALID_FOR_ORIGIN
;
1589 ClientSocketPoolManager::SocketGroupType
1590 HttpStreamFactoryImpl::Job::GetSocketGroup() const {
1591 std::string scheme
= origin_url_
.scheme();
1592 if (scheme
== "https" || scheme
== "wss" || IsSpdyAlternative())
1593 return ClientSocketPoolManager::SSL_GROUP
;
1595 if (scheme
== "ftp")
1596 return ClientSocketPoolManager::FTP_GROUP
;
1598 return ClientSocketPoolManager::NORMAL_GROUP
;
1601 // If the connection succeeds, failed connection attempts leading up to the
1602 // success will be returned via the successfully connected socket. If the
1603 // connection fails, failed connection attempts will be returned via the
1604 // ClientSocketHandle. Check whether a socket was returned and copy the
1605 // connection attempts from the proper place.
1606 void HttpStreamFactoryImpl::Job::
1607 MaybeCopyConnectionAttemptsFromSocketOrHandle() {
1608 if (IsOrphaned() || !connection_
)
1611 if (connection_
->socket()) {
1612 ConnectionAttempts socket_attempts
;
1613 connection_
->socket()->GetConnectionAttempts(&socket_attempts
);
1614 request_
->AddConnectionAttempts(socket_attempts
);
1616 request_
->AddConnectionAttempts(connection_
->connection_attempts());