Removes placeholder message string for aria role option
[chromium-blink-merge.git] / net / http / http_stream_factory_impl_job.cc
blobe2332986bfb308cddfbd30ce97685ddfacc8b627
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"
7 #include <algorithm>
8 #include <string>
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/cert/cert_verifier.h"
27 #include "net/http/http_basic_stream.h"
28 #include "net/http/http_network_session.h"
29 #include "net/http/http_proxy_client_socket.h"
30 #include "net/http/http_proxy_client_socket_pool.h"
31 #include "net/http/http_request_info.h"
32 #include "net/http/http_server_properties.h"
33 #include "net/http/http_stream_factory.h"
34 #include "net/http/http_stream_factory_impl_request.h"
35 #include "net/log/net_log.h"
36 #include "net/quic/quic_http_stream.h"
37 #include "net/socket/client_socket_handle.h"
38 #include "net/socket/client_socket_pool.h"
39 #include "net/socket/client_socket_pool_manager.h"
40 #include "net/socket/socks_client_socket_pool.h"
41 #include "net/socket/ssl_client_socket.h"
42 #include "net/socket/ssl_client_socket_pool.h"
43 #include "net/spdy/spdy_http_stream.h"
44 #include "net/spdy/spdy_session.h"
45 #include "net/spdy/spdy_session_pool.h"
46 #include "net/ssl/ssl_cert_request_info.h"
47 #include "net/ssl/ssl_failure_state.h"
49 namespace net {
51 // Returns parameters associated with the start of a HTTP stream job.
52 scoped_ptr<base::Value> NetLogHttpStreamJobCallback(
53 const GURL* original_url,
54 const GURL* url,
55 const AlternativeService* alternative_service,
56 RequestPriority priority,
57 NetLogCaptureMode /* capture_mode */) {
58 scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
59 dict->SetString("original_url", original_url->GetOrigin().spec());
60 dict->SetString("url", url->GetOrigin().spec());
61 dict->SetString("alternative_service", alternative_service->ToString());
62 dict->SetString("priority", RequestPriorityToString(priority));
63 return dict.Pass();
66 // Returns parameters associated with the Proto (with NPN negotiation) of a HTTP
67 // stream.
68 scoped_ptr<base::Value> NetLogHttpStreamProtoCallback(
69 const SSLClientSocket::NextProtoStatus status,
70 const std::string* proto,
71 NetLogCaptureMode /* capture_mode */) {
72 scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
74 dict->SetString("next_proto_status",
75 SSLClientSocket::NextProtoStatusToString(status));
76 dict->SetString("proto", *proto);
77 return dict.Pass();
80 HttpStreamFactoryImpl::Job::Job(HttpStreamFactoryImpl* stream_factory,
81 HttpNetworkSession* session,
82 const HttpRequestInfo& request_info,
83 RequestPriority priority,
84 const SSLConfig& server_ssl_config,
85 const SSLConfig& proxy_ssl_config,
86 NetLog* net_log)
87 : Job(stream_factory,
88 session,
89 request_info,
90 priority,
91 server_ssl_config,
92 proxy_ssl_config,
93 AlternativeService(),
94 net_log) {
97 HttpStreamFactoryImpl::Job::Job(HttpStreamFactoryImpl* stream_factory,
98 HttpNetworkSession* session,
99 const HttpRequestInfo& request_info,
100 RequestPriority priority,
101 const SSLConfig& server_ssl_config,
102 const SSLConfig& proxy_ssl_config,
103 AlternativeService alternative_service,
104 NetLog* net_log)
105 : request_(NULL),
106 request_info_(request_info),
107 priority_(priority),
108 server_ssl_config_(server_ssl_config),
109 proxy_ssl_config_(proxy_ssl_config),
110 net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_HTTP_STREAM_JOB)),
111 io_callback_(base::Bind(&Job::OnIOComplete, base::Unretained(this))),
112 connection_(new ClientSocketHandle),
113 session_(session),
114 stream_factory_(stream_factory),
115 next_state_(STATE_NONE),
116 pac_request_(NULL),
117 alternative_service_(alternative_service),
118 blocking_job_(NULL),
119 waiting_job_(NULL),
120 using_ssl_(false),
121 using_spdy_(false),
122 using_quic_(false),
123 quic_request_(session_->quic_stream_factory()),
124 using_existing_quic_session_(false),
125 spdy_certificate_error_(OK),
126 establishing_tunnel_(false),
127 was_npn_negotiated_(false),
128 protocol_negotiated_(kProtoUnknown),
129 num_streams_(0),
130 spdy_session_direct_(false),
131 job_status_(STATUS_RUNNING),
132 other_job_status_(STATUS_RUNNING),
133 ptr_factory_(this) {
134 DCHECK(stream_factory);
135 DCHECK(session);
136 if (IsQuicAlternative()) {
137 DCHECK(session_->params().enable_quic);
138 using_quic_ = true;
142 HttpStreamFactoryImpl::Job::~Job() {
143 net_log_.EndEvent(NetLog::TYPE_HTTP_STREAM_JOB);
145 // When we're in a partially constructed state, waiting for the user to
146 // provide certificate handling information or authentication, we can't reuse
147 // this stream at all.
148 if (next_state_ == STATE_WAITING_USER_ACTION) {
149 connection_->socket()->Disconnect();
150 connection_.reset();
153 if (pac_request_)
154 session_->proxy_service()->CancelPacRequest(pac_request_);
156 // The stream could be in a partial state. It is not reusable.
157 if (stream_.get() && next_state_ != STATE_DONE)
158 stream_->Close(true /* not reusable */);
161 void HttpStreamFactoryImpl::Job::Start(Request* request) {
162 DCHECK(request);
163 request_ = request;
164 StartInternal();
167 int HttpStreamFactoryImpl::Job::Preconnect(int num_streams) {
168 DCHECK_GT(num_streams, 0);
169 base::WeakPtr<HttpServerProperties> http_server_properties =
170 session_->http_server_properties();
171 if (http_server_properties &&
172 http_server_properties->SupportsRequestPriority(
173 HostPortPair::FromURL(request_info_.url))) {
174 num_streams_ = 1;
175 } else {
176 num_streams_ = num_streams;
178 return StartInternal();
181 int HttpStreamFactoryImpl::Job::RestartTunnelWithProxyAuth(
182 const AuthCredentials& credentials) {
183 DCHECK(establishing_tunnel_);
184 next_state_ = STATE_RESTART_TUNNEL_AUTH;
185 stream_.reset();
186 return RunLoop(OK);
189 LoadState HttpStreamFactoryImpl::Job::GetLoadState() const {
190 switch (next_state_) {
191 case STATE_RESOLVE_PROXY_COMPLETE:
192 return session_->proxy_service()->GetLoadState(pac_request_);
193 case STATE_INIT_CONNECTION_COMPLETE:
194 case STATE_CREATE_STREAM_COMPLETE:
195 return using_quic_ ? LOAD_STATE_CONNECTING : connection_->GetLoadState();
196 default:
197 return LOAD_STATE_IDLE;
201 void HttpStreamFactoryImpl::Job::WaitFor(Job* job) {
202 DCHECK_EQ(STATE_NONE, next_state_);
203 DCHECK_EQ(STATE_NONE, job->next_state_);
204 DCHECK(!blocking_job_);
205 DCHECK(!job->waiting_job_);
207 // Never share connection with other jobs for FTP requests.
208 DCHECK(!request_info_.url.SchemeIs("ftp"));
210 blocking_job_ = job;
211 job->waiting_job_ = this;
214 void HttpStreamFactoryImpl::Job::Resume(Job* job) {
215 DCHECK_EQ(blocking_job_, job);
216 blocking_job_ = NULL;
218 // We know we're blocked if the next_state_ is STATE_WAIT_FOR_JOB_COMPLETE.
219 // Unblock |this|.
220 if (next_state_ == STATE_WAIT_FOR_JOB_COMPLETE) {
221 base::ThreadTaskRunnerHandle::Get()->PostTask(
222 FROM_HERE, base::Bind(&HttpStreamFactoryImpl::Job::OnIOComplete,
223 ptr_factory_.GetWeakPtr(), OK));
227 void HttpStreamFactoryImpl::Job::Orphan(const Request* request) {
228 DCHECK_EQ(request_, request);
229 request_ = NULL;
230 if (blocking_job_) {
231 // We've been orphaned, but there's a job we're blocked on. Don't bother
232 // racing, just cancel ourself.
233 DCHECK(blocking_job_->waiting_job_);
234 blocking_job_->waiting_job_ = NULL;
235 blocking_job_ = NULL;
236 if (stream_factory_->for_websockets_ &&
237 connection_ && connection_->socket()) {
238 connection_->socket()->Disconnect();
240 stream_factory_->OnOrphanedJobComplete(this);
241 } else if (stream_factory_->for_websockets_) {
242 // We cancel this job because a WebSocketHandshakeStream can't be created
243 // without a WebSocketHandshakeStreamBase::CreateHelper which is stored in
244 // the Request class and isn't accessible from this job.
245 if (connection_ && connection_->socket()) {
246 connection_->socket()->Disconnect();
248 stream_factory_->OnOrphanedJobComplete(this);
252 void HttpStreamFactoryImpl::Job::SetPriority(RequestPriority priority) {
253 priority_ = priority;
254 // TODO(akalin): Propagate this to |connection_| and maybe the
255 // preconnect state.
258 bool HttpStreamFactoryImpl::Job::was_npn_negotiated() const {
259 return was_npn_negotiated_;
262 NextProto HttpStreamFactoryImpl::Job::protocol_negotiated() const {
263 return protocol_negotiated_;
266 bool HttpStreamFactoryImpl::Job::using_spdy() const {
267 return using_spdy_;
270 const SSLConfig& HttpStreamFactoryImpl::Job::server_ssl_config() const {
271 return server_ssl_config_;
274 const SSLConfig& HttpStreamFactoryImpl::Job::proxy_ssl_config() const {
275 return proxy_ssl_config_;
278 const ProxyInfo& HttpStreamFactoryImpl::Job::proxy_info() const {
279 return proxy_info_;
282 void HttpStreamFactoryImpl::Job::GetSSLInfo() {
283 DCHECK(using_ssl_);
284 DCHECK(!establishing_tunnel_);
285 DCHECK(connection_.get() && connection_->socket());
286 SSLClientSocket* ssl_socket =
287 static_cast<SSLClientSocket*>(connection_->socket());
288 ssl_socket->GetSSLInfo(&ssl_info_);
291 SpdySessionKey HttpStreamFactoryImpl::Job::GetSpdySessionKey() const {
292 // In the case that we're using an HTTPS proxy for an HTTP url,
293 // we look for a SPDY session *to* the proxy, instead of to the
294 // origin server.
295 if (IsHttpsProxyAndHttpUrl()) {
296 return SpdySessionKey(proxy_info_.proxy_server().host_port_pair(),
297 ProxyServer::Direct(), PRIVACY_MODE_DISABLED);
299 return SpdySessionKey(server_, proxy_info_.proxy_server(),
300 request_info_.privacy_mode);
303 bool HttpStreamFactoryImpl::Job::CanUseExistingSpdySession() const {
304 // We need to make sure that if a spdy session was created for
305 // https://somehost/ that we don't use that session for http://somehost:443/.
306 // The only time we can use an existing session is if the request URL is
307 // https (the normal case) or if we're connection to a SPDY proxy.
308 // https://crbug.com/133176
309 // TODO(ricea): Add "wss" back to this list when SPDY WebSocket support is
310 // working.
311 return origin_url_.SchemeIs("https") ||
312 proxy_info_.proxy_server().is_https() || IsSpdyAlternative();
315 void HttpStreamFactoryImpl::Job::OnStreamReadyCallback() {
316 DCHECK(stream_.get());
317 DCHECK(!IsPreconnecting());
318 DCHECK(!stream_factory_->for_websockets_);
320 MaybeCopyConnectionAttemptsFromSocketOrHandle();
322 if (IsOrphaned()) {
323 stream_factory_->OnOrphanedJobComplete(this);
324 } else {
325 request_->Complete(was_npn_negotiated(),
326 protocol_negotiated(),
327 using_spdy(),
328 net_log_);
329 request_->OnStreamReady(this, server_ssl_config_, proxy_info_,
330 stream_.release());
332 // |this| may be deleted after this call.
335 void HttpStreamFactoryImpl::Job::OnWebSocketHandshakeStreamReadyCallback() {
336 DCHECK(websocket_stream_);
337 DCHECK(!IsPreconnecting());
338 DCHECK(stream_factory_->for_websockets_);
339 // An orphaned WebSocket job will be closed immediately and
340 // never be ready.
341 DCHECK(!IsOrphaned());
343 MaybeCopyConnectionAttemptsFromSocketOrHandle();
345 request_->Complete(was_npn_negotiated(),
346 protocol_negotiated(),
347 using_spdy(),
348 net_log_);
349 request_->OnWebSocketHandshakeStreamReady(this,
350 server_ssl_config_,
351 proxy_info_,
352 websocket_stream_.release());
353 // |this| may be deleted after this call.
356 void HttpStreamFactoryImpl::Job::OnNewSpdySessionReadyCallback() {
357 DCHECK(stream_.get());
358 DCHECK(!IsPreconnecting());
359 DCHECK(using_spdy());
360 // Note: an event loop iteration has passed, so |new_spdy_session_| may be
361 // NULL at this point if the SpdySession closed immediately after creation.
362 base::WeakPtr<SpdySession> spdy_session = new_spdy_session_;
363 new_spdy_session_.reset();
365 MaybeCopyConnectionAttemptsFromSocketOrHandle();
367 // TODO(jgraettinger): Notify the factory, and let that notify |request_|,
368 // rather than notifying |request_| directly.
369 if (IsOrphaned()) {
370 if (spdy_session) {
371 stream_factory_->OnNewSpdySessionReady(
372 spdy_session, spdy_session_direct_, server_ssl_config_, proxy_info_,
373 was_npn_negotiated(), protocol_negotiated(), using_spdy(), net_log_);
375 stream_factory_->OnOrphanedJobComplete(this);
376 } else {
377 request_->OnNewSpdySessionReady(
378 this, stream_.Pass(), spdy_session, spdy_session_direct_);
380 // |this| may be deleted after this call.
383 void HttpStreamFactoryImpl::Job::OnStreamFailedCallback(int result) {
384 DCHECK(!IsPreconnecting());
386 MaybeCopyConnectionAttemptsFromSocketOrHandle();
388 if (IsOrphaned()) {
389 stream_factory_->OnOrphanedJobComplete(this);
390 } else {
391 SSLFailureState ssl_failure_state =
392 connection_ ? connection_->ssl_failure_state() : SSL_FAILURE_NONE;
393 request_->OnStreamFailed(this, result, server_ssl_config_,
394 ssl_failure_state);
396 // |this| may be deleted after this call.
399 void HttpStreamFactoryImpl::Job::OnCertificateErrorCallback(
400 int result, const SSLInfo& ssl_info) {
401 DCHECK(!IsPreconnecting());
403 MaybeCopyConnectionAttemptsFromSocketOrHandle();
405 if (IsOrphaned())
406 stream_factory_->OnOrphanedJobComplete(this);
407 else
408 request_->OnCertificateError(this, result, server_ssl_config_, ssl_info);
409 // |this| may be deleted after this call.
412 void HttpStreamFactoryImpl::Job::OnNeedsProxyAuthCallback(
413 const HttpResponseInfo& response,
414 HttpAuthController* auth_controller) {
415 DCHECK(!IsPreconnecting());
416 if (IsOrphaned())
417 stream_factory_->OnOrphanedJobComplete(this);
418 else
419 request_->OnNeedsProxyAuth(
420 this, response, server_ssl_config_, proxy_info_, auth_controller);
421 // |this| may be deleted after this call.
424 void HttpStreamFactoryImpl::Job::OnNeedsClientAuthCallback(
425 SSLCertRequestInfo* cert_info) {
426 DCHECK(!IsPreconnecting());
427 if (IsOrphaned())
428 stream_factory_->OnOrphanedJobComplete(this);
429 else
430 request_->OnNeedsClientAuth(this, server_ssl_config_, cert_info);
431 // |this| may be deleted after this call.
434 void HttpStreamFactoryImpl::Job::OnHttpsProxyTunnelResponseCallback(
435 const HttpResponseInfo& response_info,
436 HttpStream* stream) {
437 DCHECK(!IsPreconnecting());
438 if (IsOrphaned())
439 stream_factory_->OnOrphanedJobComplete(this);
440 else
441 request_->OnHttpsProxyTunnelResponse(
442 this, response_info, server_ssl_config_, proxy_info_, stream);
443 // |this| may be deleted after this call.
446 void HttpStreamFactoryImpl::Job::OnPreconnectsComplete() {
447 DCHECK(!request_);
448 if (new_spdy_session_.get()) {
449 stream_factory_->OnNewSpdySessionReady(new_spdy_session_,
450 spdy_session_direct_,
451 server_ssl_config_,
452 proxy_info_,
453 was_npn_negotiated(),
454 protocol_negotiated(),
455 using_spdy(),
456 net_log_);
458 stream_factory_->OnPreconnectsComplete(this);
459 // |this| may be deleted after this call.
462 // static
463 int HttpStreamFactoryImpl::Job::OnHostResolution(
464 SpdySessionPool* spdy_session_pool,
465 const SpdySessionKey& spdy_session_key,
466 const AddressList& addresses,
467 const BoundNetLog& net_log) {
468 // It is OK to dereference spdy_session_pool, because the
469 // ClientSocketPoolManager will be destroyed in the same callback that
470 // destroys the SpdySessionPool.
471 return
472 spdy_session_pool->FindAvailableSession(spdy_session_key, net_log) ?
473 ERR_SPDY_SESSION_ALREADY_EXISTS : OK;
476 void HttpStreamFactoryImpl::Job::OnIOComplete(int result) {
477 RunLoop(result);
480 int HttpStreamFactoryImpl::Job::RunLoop(int result) {
481 result = DoLoop(result);
483 if (result == ERR_IO_PENDING)
484 return result;
486 // If there was an error, we should have already resumed the |waiting_job_|,
487 // if there was one.
488 DCHECK(result == OK || waiting_job_ == NULL);
490 if (IsPreconnecting()) {
491 base::ThreadTaskRunnerHandle::Get()->PostTask(
492 FROM_HERE,
493 base::Bind(&HttpStreamFactoryImpl::Job::OnPreconnectsComplete,
494 ptr_factory_.GetWeakPtr()));
495 return ERR_IO_PENDING;
498 if (IsCertificateError(result)) {
499 // Retrieve SSL information from the socket.
500 GetSSLInfo();
502 next_state_ = STATE_WAITING_USER_ACTION;
503 base::ThreadTaskRunnerHandle::Get()->PostTask(
504 FROM_HERE,
505 base::Bind(&HttpStreamFactoryImpl::Job::OnCertificateErrorCallback,
506 ptr_factory_.GetWeakPtr(), result, ssl_info_));
507 return ERR_IO_PENDING;
510 switch (result) {
511 case ERR_PROXY_AUTH_REQUESTED: {
512 UMA_HISTOGRAM_BOOLEAN("Net.ProxyAuthRequested.HasConnection",
513 connection_.get() != NULL);
514 if (!connection_.get())
515 return ERR_PROXY_AUTH_REQUESTED_WITH_NO_CONNECTION;
516 CHECK(connection_->socket());
517 CHECK(establishing_tunnel_);
519 next_state_ = STATE_WAITING_USER_ACTION;
520 ProxyClientSocket* proxy_socket =
521 static_cast<ProxyClientSocket*>(connection_->socket());
522 base::ThreadTaskRunnerHandle::Get()->PostTask(
523 FROM_HERE,
524 base::Bind(&Job::OnNeedsProxyAuthCallback, ptr_factory_.GetWeakPtr(),
525 *proxy_socket->GetConnectResponseInfo(),
526 proxy_socket->GetAuthController()));
527 return ERR_IO_PENDING;
530 case ERR_SSL_CLIENT_AUTH_CERT_NEEDED:
531 base::ThreadTaskRunnerHandle::Get()->PostTask(
532 FROM_HERE,
533 base::Bind(&Job::OnNeedsClientAuthCallback, ptr_factory_.GetWeakPtr(),
534 connection_->ssl_error_response_info().cert_request_info));
535 return ERR_IO_PENDING;
537 case ERR_HTTPS_PROXY_TUNNEL_RESPONSE: {
538 DCHECK(connection_.get());
539 DCHECK(connection_->socket());
540 DCHECK(establishing_tunnel_);
542 ProxyClientSocket* proxy_socket =
543 static_cast<ProxyClientSocket*>(connection_->socket());
544 base::ThreadTaskRunnerHandle::Get()->PostTask(
545 FROM_HERE, base::Bind(&Job::OnHttpsProxyTunnelResponseCallback,
546 ptr_factory_.GetWeakPtr(),
547 *proxy_socket->GetConnectResponseInfo(),
548 proxy_socket->CreateConnectResponseStream()));
549 return ERR_IO_PENDING;
552 case OK:
553 job_status_ = STATUS_SUCCEEDED;
554 MaybeMarkAlternativeServiceBroken();
555 next_state_ = STATE_DONE;
556 if (new_spdy_session_.get()) {
557 base::ThreadTaskRunnerHandle::Get()->PostTask(
558 FROM_HERE, base::Bind(&Job::OnNewSpdySessionReadyCallback,
559 ptr_factory_.GetWeakPtr()));
560 } else if (stream_factory_->for_websockets_) {
561 DCHECK(websocket_stream_);
562 base::ThreadTaskRunnerHandle::Get()->PostTask(
563 FROM_HERE, base::Bind(&Job::OnWebSocketHandshakeStreamReadyCallback,
564 ptr_factory_.GetWeakPtr()));
565 } else {
566 DCHECK(stream_.get());
567 base::ThreadTaskRunnerHandle::Get()->PostTask(
568 FROM_HERE,
569 base::Bind(&Job::OnStreamReadyCallback, ptr_factory_.GetWeakPtr()));
571 return ERR_IO_PENDING;
573 default:
574 DCHECK(result != ERR_ALTERNATIVE_CERT_NOT_VALID_FOR_ORIGIN ||
575 IsSpdyAlternative() || IsQuicAlternative());
576 if (job_status_ != STATUS_BROKEN) {
577 DCHECK_EQ(STATUS_RUNNING, job_status_);
578 job_status_ = STATUS_FAILED;
579 // TODO(bnc): If (result == ERR_ALTERNATIVE_CERT_NOT_VALID_FOR_ORIGIN),
580 // then instead of marking alternative service broken, mark (origin,
581 // alternative service) couple as invalid.
582 MaybeMarkAlternativeServiceBroken();
584 base::ThreadTaskRunnerHandle::Get()->PostTask(
585 FROM_HERE, base::Bind(&Job::OnStreamFailedCallback,
586 ptr_factory_.GetWeakPtr(), result));
587 return ERR_IO_PENDING;
591 int HttpStreamFactoryImpl::Job::DoLoop(int result) {
592 DCHECK_NE(next_state_, STATE_NONE);
593 int rv = result;
594 do {
595 State state = next_state_;
596 next_state_ = STATE_NONE;
597 switch (state) {
598 case STATE_START:
599 DCHECK_EQ(OK, rv);
600 rv = DoStart();
601 break;
602 case STATE_RESOLVE_PROXY:
603 DCHECK_EQ(OK, rv);
604 rv = DoResolveProxy();
605 break;
606 case STATE_RESOLVE_PROXY_COMPLETE:
607 rv = DoResolveProxyComplete(rv);
608 break;
609 case STATE_WAIT_FOR_JOB:
610 DCHECK_EQ(OK, rv);
611 rv = DoWaitForJob();
612 break;
613 case STATE_WAIT_FOR_JOB_COMPLETE:
614 rv = DoWaitForJobComplete(rv);
615 break;
616 case STATE_INIT_CONNECTION:
617 DCHECK_EQ(OK, rv);
618 rv = DoInitConnection();
619 break;
620 case STATE_INIT_CONNECTION_COMPLETE:
621 rv = DoInitConnectionComplete(rv);
622 break;
623 case STATE_WAITING_USER_ACTION:
624 rv = DoWaitingUserAction(rv);
625 break;
626 case STATE_RESTART_TUNNEL_AUTH:
627 DCHECK_EQ(OK, rv);
628 rv = DoRestartTunnelAuth();
629 break;
630 case STATE_RESTART_TUNNEL_AUTH_COMPLETE:
631 rv = DoRestartTunnelAuthComplete(rv);
632 break;
633 case STATE_CREATE_STREAM:
634 DCHECK_EQ(OK, rv);
635 rv = DoCreateStream();
636 break;
637 case STATE_CREATE_STREAM_COMPLETE:
638 rv = DoCreateStreamComplete(rv);
639 break;
640 default:
641 NOTREACHED() << "bad state";
642 rv = ERR_FAILED;
643 break;
645 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
646 return rv;
649 int HttpStreamFactoryImpl::Job::StartInternal() {
650 CHECK_EQ(STATE_NONE, next_state_);
651 next_state_ = STATE_START;
652 int rv = RunLoop(OK);
653 DCHECK_EQ(ERR_IO_PENDING, rv);
654 return rv;
657 int HttpStreamFactoryImpl::Job::DoStart() {
658 if (IsSpdyAlternative() || IsQuicAlternative()) {
659 server_ = alternative_service_.host_port_pair();
660 } else {
661 server_ = HostPortPair::FromURL(request_info_.url);
663 origin_url_ =
664 stream_factory_->ApplyHostMappingRules(request_info_.url, &server_);
665 valid_spdy_session_pool_.reset(new ValidSpdySessionPool(
666 session_->spdy_session_pool(), origin_url_, IsSpdyAlternative()));
668 net_log_.BeginEvent(
669 NetLog::TYPE_HTTP_STREAM_JOB,
670 base::Bind(&NetLogHttpStreamJobCallback, &request_info_.url, &origin_url_,
671 &alternative_service_, priority_));
673 // Don't connect to restricted ports.
674 if (!IsPortAllowedForScheme(server_.port(), request_info_.url.scheme())) {
675 if (waiting_job_) {
676 waiting_job_->Resume(this);
677 waiting_job_ = NULL;
679 return ERR_UNSAFE_PORT;
682 next_state_ = STATE_RESOLVE_PROXY;
683 return OK;
686 int HttpStreamFactoryImpl::Job::DoResolveProxy() {
687 DCHECK(!pac_request_);
688 DCHECK(session_);
690 next_state_ = STATE_RESOLVE_PROXY_COMPLETE;
692 if (request_info_.load_flags & LOAD_BYPASS_PROXY) {
693 proxy_info_.UseDirect();
694 return OK;
697 // TODO(rch): remove this code since Alt-Svc seems to prohibit it.
698 GURL url_for_proxy = origin_url_;
699 // For SPDY via Alt-Svc, set |alternative_service_url_| to
700 // https://<alternative host>:<alternative port>/...
701 // so the proxy resolution works with the actual destination, and so
702 // that the correct socket pool is used.
703 if (IsSpdyAlternative()) {
704 // TODO(rch): Figure out how to make QUIC iteract with PAC
705 // scripts. By not re-writing the URL, we will query the PAC script
706 // for the proxy to use to reach the original URL via TCP. But
707 // the alternate request will be going via UDP to a different port.
708 GURL::Replacements replacements;
709 // new_port needs to be in scope here because GURL::Replacements references
710 // the memory contained by it directly.
711 const std::string new_port = base::IntToString(alternative_service_.port);
712 replacements.SetSchemeStr("https");
713 replacements.SetPortStr(new_port);
714 url_for_proxy = url_for_proxy.ReplaceComponents(replacements);
717 return session_->proxy_service()->ResolveProxy(
718 url_for_proxy, request_info_.load_flags, &proxy_info_, io_callback_,
719 &pac_request_, session_->network_delegate(), net_log_);
722 int HttpStreamFactoryImpl::Job::DoResolveProxyComplete(int result) {
723 pac_request_ = NULL;
725 if (result == OK) {
726 // Remove unsupported proxies from the list.
727 int supported_proxies =
728 ProxyServer::SCHEME_DIRECT | ProxyServer::SCHEME_HTTP |
729 ProxyServer::SCHEME_HTTPS | ProxyServer::SCHEME_SOCKS4 |
730 ProxyServer::SCHEME_SOCKS5;
732 if (session_->params().enable_quic_for_proxies)
733 supported_proxies |= ProxyServer::SCHEME_QUIC;
735 proxy_info_.RemoveProxiesWithoutScheme(supported_proxies);
737 if (proxy_info_.is_empty()) {
738 // No proxies/direct to choose from. This happens when we don't support
739 // any of the proxies in the returned list.
740 result = ERR_NO_SUPPORTED_PROXIES;
741 } else if (using_quic_ &&
742 (!proxy_info_.is_quic() && !proxy_info_.is_direct())) {
743 // QUIC can not be spoken to non-QUIC proxies. This error should not be
744 // user visible, because the non-alternative Job should be resumed.
745 result = ERR_NO_SUPPORTED_PROXIES;
749 if (result != OK) {
750 if (waiting_job_) {
751 waiting_job_->Resume(this);
752 waiting_job_ = NULL;
754 return result;
757 if (blocking_job_)
758 next_state_ = STATE_WAIT_FOR_JOB;
759 else
760 next_state_ = STATE_INIT_CONNECTION;
761 return OK;
764 bool HttpStreamFactoryImpl::Job::ShouldForceQuic() const {
765 return session_->params().enable_quic &&
766 session_->params().origin_to_force_quic_on.Equals(server_) &&
767 proxy_info_.is_direct();
770 int HttpStreamFactoryImpl::Job::DoWaitForJob() {
771 DCHECK(blocking_job_);
772 next_state_ = STATE_WAIT_FOR_JOB_COMPLETE;
773 return ERR_IO_PENDING;
776 int HttpStreamFactoryImpl::Job::DoWaitForJobComplete(int result) {
777 DCHECK(!blocking_job_);
778 DCHECK_EQ(OK, result);
779 next_state_ = STATE_INIT_CONNECTION;
780 return OK;
783 int HttpStreamFactoryImpl::Job::DoInitConnection() {
784 // TODO(pkasting): Remove ScopedTracker below once crbug.com/462812 is fixed.
785 tracked_objects::ScopedTracker tracking_profile(
786 FROM_HERE_WITH_EXPLICIT_FUNCTION(
787 "462812 HttpStreamFactoryImpl::Job::DoInitConnection"));
788 DCHECK(!blocking_job_);
789 DCHECK(!connection_->is_initialized());
790 DCHECK(proxy_info_.proxy_server().is_valid());
791 next_state_ = STATE_INIT_CONNECTION_COMPLETE;
793 using_ssl_ = origin_url_.SchemeIs("https") || origin_url_.SchemeIs("wss") ||
794 IsSpdyAlternative();
795 using_spdy_ = false;
797 if (ShouldForceQuic())
798 using_quic_ = true;
800 DCHECK(!using_quic_ || session_->params().enable_quic);
802 if (proxy_info_.is_quic()) {
803 using_quic_ = true;
804 DCHECK(session_->params().enable_quic_for_proxies);
807 if (proxy_info_.is_https() || proxy_info_.is_quic()) {
808 InitSSLConfig(proxy_info_.proxy_server().host_port_pair(),
809 &proxy_ssl_config_, /*is_proxy=*/true);
810 // Disable revocation checking for HTTPS proxies since the revocation
811 // requests are probably going to need to go through the proxy too.
812 proxy_ssl_config_.rev_checking_enabled = false;
814 if (using_ssl_) {
815 InitSSLConfig(server_, &server_ssl_config_, /*is_proxy=*/false);
818 if (using_quic_) {
819 if (proxy_info_.is_quic() && !request_info_.url.SchemeIs("http")) {
820 NOTREACHED();
821 // TODO(rch): support QUIC proxies for HTTPS urls.
822 return ERR_NOT_IMPLEMENTED;
824 HostPortPair destination;
825 std::string origin_host;
826 bool secure_quic;
827 SSLConfig* ssl_config;
828 if (proxy_info_.is_quic()) {
829 // A proxy's certificate is expected to be valid for the proxy hostname.
830 destination = proxy_info_.proxy_server().host_port_pair();
831 origin_host = destination.host();
832 secure_quic = true;
833 ssl_config = &proxy_ssl_config_;
834 } else {
835 // The certificate of a QUIC alternative server is expected to be valid
836 // for the origin of the request (in addition to being valid for the
837 // server itself).
838 destination = server_;
839 origin_host = origin_url_.host();
840 secure_quic = using_ssl_;
841 ssl_config = &server_ssl_config_;
843 int rv = quic_request_.Request(
844 destination, secure_quic, request_info_.privacy_mode,
845 ssl_config->GetCertVerifyFlags(), origin_host, request_info_.method,
846 net_log_, io_callback_);
847 if (rv == OK) {
848 using_existing_quic_session_ = true;
849 } else {
850 // OK, there's no available QUIC session. Let |waiting_job_| resume
851 // if it's paused.
852 if (waiting_job_) {
853 waiting_job_->Resume(this);
854 waiting_job_ = NULL;
857 return rv;
860 SpdySessionKey spdy_session_key = GetSpdySessionKey();
862 // Check first if we have a spdy session for this group. If so, then go
863 // straight to using that.
864 if (CanUseExistingSpdySession()) {
865 base::WeakPtr<SpdySession> spdy_session;
866 int result = valid_spdy_session_pool_->FindAvailableSession(
867 spdy_session_key, net_log_, &spdy_session);
868 if (result != OK)
869 return result;
870 if (spdy_session) {
871 // If we're preconnecting, but we already have a SpdySession, we don't
872 // actually need to preconnect any sockets, so we're done.
873 if (IsPreconnecting())
874 return OK;
875 using_spdy_ = true;
876 next_state_ = STATE_CREATE_STREAM;
877 existing_spdy_session_ = spdy_session;
878 return OK;
881 if (request_ && !request_->HasSpdySessionKey() && using_ssl_) {
882 // Update the spdy session key for the request that launched this job.
883 request_->SetSpdySessionKey(spdy_session_key);
886 // OK, there's no available SPDY session. Let |waiting_job_| resume if it's
887 // paused.
888 if (waiting_job_) {
889 waiting_job_->Resume(this);
890 waiting_job_ = NULL;
893 if (proxy_info_.is_http() || proxy_info_.is_https())
894 establishing_tunnel_ = using_ssl_;
896 const bool expect_spdy = IsSpdyAlternative();
898 base::WeakPtr<HttpServerProperties> http_server_properties =
899 session_->http_server_properties();
900 if (http_server_properties) {
901 http_server_properties->MaybeForceHTTP11(server_, &server_ssl_config_);
902 if (proxy_info_.is_http() || proxy_info_.is_https()) {
903 http_server_properties->MaybeForceHTTP11(
904 proxy_info_.proxy_server().host_port_pair(), &proxy_ssl_config_);
908 if (IsPreconnecting()) {
909 DCHECK(!stream_factory_->for_websockets_);
910 return PreconnectSocketsForHttpRequest(
911 GetSocketGroup(), server_, request_info_.extra_headers,
912 request_info_.load_flags, priority_, session_, proxy_info_, expect_spdy,
913 server_ssl_config_, proxy_ssl_config_, request_info_.privacy_mode,
914 net_log_, num_streams_);
917 // If we can't use a SPDY session, don't bother checking for one after
918 // the hostname is resolved.
919 OnHostResolutionCallback resolution_callback =
920 CanUseExistingSpdySession()
921 ? base::Bind(&Job::OnHostResolution, session_->spdy_session_pool(),
922 spdy_session_key)
923 : OnHostResolutionCallback();
924 if (stream_factory_->for_websockets_) {
925 // TODO(ricea): Re-enable NPN when WebSockets over SPDY is supported.
926 SSLConfig websocket_server_ssl_config = server_ssl_config_;
927 websocket_server_ssl_config.next_protos.clear();
928 return InitSocketHandleForWebSocketRequest(
929 GetSocketGroup(), server_, request_info_.extra_headers,
930 request_info_.load_flags, priority_, session_, proxy_info_, expect_spdy,
931 websocket_server_ssl_config, proxy_ssl_config_,
932 request_info_.privacy_mode, net_log_, connection_.get(),
933 resolution_callback, io_callback_);
936 return InitSocketHandleForHttpRequest(
937 GetSocketGroup(), server_, request_info_.extra_headers,
938 request_info_.load_flags, priority_, session_, proxy_info_, expect_spdy,
939 server_ssl_config_, proxy_ssl_config_, request_info_.privacy_mode,
940 net_log_, connection_.get(), resolution_callback, io_callback_);
943 int HttpStreamFactoryImpl::Job::DoInitConnectionComplete(int result) {
944 if (IsPreconnecting()) {
945 if (using_quic_)
946 return result;
947 DCHECK_EQ(OK, result);
948 return OK;
951 if (result == ERR_SPDY_SESSION_ALREADY_EXISTS) {
952 // We found a SPDY connection after resolving the host. This is
953 // probably an IP pooled connection.
954 SpdySessionKey spdy_session_key = GetSpdySessionKey();
955 existing_spdy_session_ =
956 session_->spdy_session_pool()->FindAvailableSession(
957 spdy_session_key, net_log_);
958 if (existing_spdy_session_) {
959 using_spdy_ = true;
960 next_state_ = STATE_CREATE_STREAM;
961 } else {
962 // It is possible that the spdy session no longer exists.
963 ReturnToStateInitConnection(true /* close connection */);
965 return OK;
968 if (proxy_info_.is_quic() && using_quic_ &&
969 (result == ERR_QUIC_PROTOCOL_ERROR ||
970 result == ERR_QUIC_HANDSHAKE_FAILED)) {
971 using_quic_ = false;
972 return ReconsiderProxyAfterError(result);
975 // TODO(willchan): Make this a bit more exact. Maybe there are recoverable
976 // errors, such as ignoring certificate errors for Alternate-Protocol.
977 if (result < 0 && waiting_job_) {
978 waiting_job_->Resume(this);
979 waiting_job_ = NULL;
982 // |result| may be the result of any of the stacked pools. The following
983 // logic is used when determining how to interpret an error.
984 // If |result| < 0:
985 // and connection_->socket() != NULL, then the SSL handshake ran and it
986 // is a potentially recoverable error.
987 // and connection_->socket == NULL and connection_->is_ssl_error() is true,
988 // then the SSL handshake ran with an unrecoverable error.
989 // otherwise, the error came from one of the other pools.
990 bool ssl_started = using_ssl_ && (result == OK || connection_->socket() ||
991 connection_->is_ssl_error());
993 if (ssl_started && (result == OK || IsCertificateError(result))) {
994 if (using_quic_ && result == OK) {
995 was_npn_negotiated_ = true;
996 protocol_negotiated_ =
997 SSLClientSocket::NextProtoFromString("quic/1+spdy/3");
998 } else {
999 SSLClientSocket* ssl_socket =
1000 static_cast<SSLClientSocket*>(connection_->socket());
1001 if (ssl_socket->WasNpnNegotiated()) {
1002 was_npn_negotiated_ = true;
1003 std::string proto;
1004 SSLClientSocket::NextProtoStatus status =
1005 ssl_socket->GetNextProto(&proto);
1006 protocol_negotiated_ = SSLClientSocket::NextProtoFromString(proto);
1007 net_log_.AddEvent(
1008 NetLog::TYPE_HTTP_STREAM_REQUEST_PROTO,
1009 base::Bind(&NetLogHttpStreamProtoCallback,
1010 status, &proto));
1011 if (NextProtoIsSPDY(protocol_negotiated_))
1012 SwitchToSpdyMode();
1015 } else if (proxy_info_.is_https() && connection_->socket() &&
1016 result == OK) {
1017 ProxyClientSocket* proxy_socket =
1018 static_cast<ProxyClientSocket*>(connection_->socket());
1019 if (proxy_socket->IsUsingSpdy()) {
1020 was_npn_negotiated_ = true;
1021 protocol_negotiated_ = proxy_socket->GetProtocolNegotiated();
1022 SwitchToSpdyMode();
1026 if (result == ERR_PROXY_AUTH_REQUESTED ||
1027 result == ERR_HTTPS_PROXY_TUNNEL_RESPONSE) {
1028 DCHECK(!ssl_started);
1029 // Other state (i.e. |using_ssl_|) suggests that |connection_| will have an
1030 // SSL socket, but there was an error before that could happen. This
1031 // puts the in progress HttpProxy socket into |connection_| in order to
1032 // complete the auth (or read the response body). The tunnel restart code
1033 // is careful to remove it before returning control to the rest of this
1034 // class.
1035 connection_.reset(connection_->release_pending_http_proxy_connection());
1036 return result;
1039 if (IsSpdyAlternative() && !using_spdy_) {
1040 job_status_ = STATUS_BROKEN;
1041 MaybeMarkAlternativeServiceBroken();
1042 return ERR_NPN_NEGOTIATION_FAILED;
1045 if (!ssl_started && result < 0 &&
1046 (IsSpdyAlternative() || IsQuicAlternative())) {
1047 job_status_ = STATUS_BROKEN;
1048 // TODO(bnc): if (result == ERR_ALTERNATIVE_CERT_NOT_VALID_FOR_ORIGIN), then
1049 // instead of marking alternative service broken, mark (origin, alternative
1050 // service) couple as invalid.
1051 MaybeMarkAlternativeServiceBroken();
1052 return result;
1055 if (using_quic_) {
1056 if (result < 0) {
1057 job_status_ = STATUS_BROKEN;
1058 MaybeMarkAlternativeServiceBroken();
1059 return result;
1061 stream_ = quic_request_.ReleaseStream();
1062 next_state_ = STATE_NONE;
1063 return OK;
1066 if (result < 0 && !ssl_started)
1067 return ReconsiderProxyAfterError(result);
1068 establishing_tunnel_ = false;
1070 if (connection_->socket()) {
1071 // We officially have a new connection. Record the type.
1072 if (!connection_->is_reused()) {
1073 ConnectionType type = using_spdy_ ? CONNECTION_SPDY : CONNECTION_HTTP;
1074 UpdateConnectionTypeHistograms(type);
1078 // Handle SSL errors below.
1079 if (using_ssl_) {
1080 DCHECK(ssl_started);
1081 if (IsCertificateError(result)) {
1082 if (IsSpdyAlternative() && origin_url_.SchemeIs("http")) {
1083 // We ignore certificate errors for http over spdy.
1084 spdy_certificate_error_ = result;
1085 result = OK;
1086 } else {
1087 result = HandleCertificateError(result);
1088 if (result == OK && !connection_->socket()->IsConnectedAndIdle()) {
1089 ReturnToStateInitConnection(true /* close connection */);
1090 return result;
1094 if (result < 0)
1095 return result;
1098 next_state_ = STATE_CREATE_STREAM;
1099 return OK;
1102 int HttpStreamFactoryImpl::Job::DoWaitingUserAction(int result) {
1103 // This state indicates that the stream request is in a partially
1104 // completed state, and we've called back to the delegate for more
1105 // information.
1107 // We're always waiting here for the delegate to call us back.
1108 return ERR_IO_PENDING;
1111 int HttpStreamFactoryImpl::Job::SetSpdyHttpStream(
1112 base::WeakPtr<SpdySession> session, bool direct) {
1113 // TODO(ricea): Restore the code for WebSockets over SPDY once it's
1114 // implemented.
1115 if (stream_factory_->for_websockets_)
1116 return ERR_NOT_IMPLEMENTED;
1118 // TODO(willchan): Delete this code, because eventually, the
1119 // HttpStreamFactoryImpl will be creating all the SpdyHttpStreams, since it
1120 // will know when SpdySessions become available.
1122 bool use_relative_url = direct || request_info_.url.SchemeIs("https");
1123 stream_.reset(new SpdyHttpStream(session, use_relative_url));
1124 return OK;
1127 int HttpStreamFactoryImpl::Job::DoCreateStream() {
1128 // TODO(pkasting): Remove ScopedTracker below once crbug.com/462811 is fixed.
1129 tracked_objects::ScopedTracker tracking_profile(
1130 FROM_HERE_WITH_EXPLICIT_FUNCTION(
1131 "462811 HttpStreamFactoryImpl::Job::DoCreateStream"));
1132 DCHECK(connection_->socket() || existing_spdy_session_.get() || using_quic_);
1133 DCHECK(!IsQuicAlternative());
1135 next_state_ = STATE_CREATE_STREAM_COMPLETE;
1137 // We only set the socket motivation if we're the first to use
1138 // this socket. Is there a race for two SPDY requests? We really
1139 // need to plumb this through to the connect level.
1140 if (connection_->socket() && !connection_->is_reused())
1141 SetSocketMotivation();
1143 if (!using_spdy_) {
1144 DCHECK(!IsSpdyAlternative());
1145 // We may get ftp scheme when fetching ftp resources through proxy.
1146 bool using_proxy = (proxy_info_.is_http() || proxy_info_.is_https()) &&
1147 (request_info_.url.SchemeIs("http") ||
1148 request_info_.url.SchemeIs("ftp"));
1149 if (stream_factory_->for_websockets_) {
1150 DCHECK(request_);
1151 DCHECK(request_->websocket_handshake_stream_create_helper());
1152 websocket_stream_.reset(
1153 request_->websocket_handshake_stream_create_helper()
1154 ->CreateBasicStream(connection_.Pass(), using_proxy));
1155 } else {
1156 stream_.reset(new HttpBasicStream(connection_.release(), using_proxy));
1158 return OK;
1161 CHECK(!stream_.get());
1163 bool direct = !IsHttpsProxyAndHttpUrl();
1164 if (existing_spdy_session_.get()) {
1165 // We picked up an existing session, so we don't need our socket.
1166 if (connection_->socket())
1167 connection_->socket()->Disconnect();
1168 connection_->Reset();
1170 int set_result = SetSpdyHttpStream(existing_spdy_session_, direct);
1171 existing_spdy_session_.reset();
1172 return set_result;
1175 SpdySessionKey spdy_session_key = GetSpdySessionKey();
1176 base::WeakPtr<SpdySession> spdy_session;
1177 int result = valid_spdy_session_pool_->FindAvailableSession(
1178 spdy_session_key, net_log_, &spdy_session);
1179 if (result != OK) {
1180 return result;
1182 if (spdy_session) {
1183 return SetSpdyHttpStream(spdy_session, direct);
1186 result = valid_spdy_session_pool_->CreateAvailableSessionFromSocket(
1187 spdy_session_key, connection_.Pass(), net_log_, spdy_certificate_error_,
1188 using_ssl_, &spdy_session);
1189 if (result != OK) {
1190 return result;
1193 if (!spdy_session->HasAcceptableTransportSecurity()) {
1194 spdy_session->CloseSessionOnError(
1195 ERR_SPDY_INADEQUATE_TRANSPORT_SECURITY, "");
1196 return ERR_SPDY_INADEQUATE_TRANSPORT_SECURITY;
1199 new_spdy_session_ = spdy_session;
1200 spdy_session_direct_ = direct;
1201 const HostPortPair& host_port_pair = spdy_session_key.host_port_pair();
1202 base::WeakPtr<HttpServerProperties> http_server_properties =
1203 session_->http_server_properties();
1204 if (http_server_properties)
1205 http_server_properties->SetSupportsSpdy(host_port_pair, true);
1207 // Create a SpdyHttpStream attached to the session;
1208 // OnNewSpdySessionReadyCallback is not called until an event loop
1209 // iteration later, so if the SpdySession is closed between then, allow
1210 // reuse state from the underlying socket, sampled by SpdyHttpStream,
1211 // bubble up to the request.
1212 return SetSpdyHttpStream(new_spdy_session_, spdy_session_direct_);
1215 int HttpStreamFactoryImpl::Job::DoCreateStreamComplete(int result) {
1216 if (result < 0)
1217 return result;
1219 session_->proxy_service()->ReportSuccess(proxy_info_,
1220 session_->network_delegate());
1221 next_state_ = STATE_NONE;
1222 return OK;
1225 int HttpStreamFactoryImpl::Job::DoRestartTunnelAuth() {
1226 next_state_ = STATE_RESTART_TUNNEL_AUTH_COMPLETE;
1227 ProxyClientSocket* proxy_socket =
1228 static_cast<ProxyClientSocket*>(connection_->socket());
1229 return proxy_socket->RestartWithAuth(io_callback_);
1232 int HttpStreamFactoryImpl::Job::DoRestartTunnelAuthComplete(int result) {
1233 if (result == ERR_PROXY_AUTH_REQUESTED)
1234 return result;
1236 if (result == OK) {
1237 // Now that we've got the HttpProxyClientSocket connected. We have
1238 // to release it as an idle socket into the pool and start the connection
1239 // process from the beginning. Trying to pass it in with the
1240 // SSLSocketParams might cause a deadlock since params are dispatched
1241 // interchangeably. This request won't necessarily get this http proxy
1242 // socket, but there will be forward progress.
1243 establishing_tunnel_ = false;
1244 ReturnToStateInitConnection(false /* do not close connection */);
1245 return OK;
1248 return ReconsiderProxyAfterError(result);
1251 void HttpStreamFactoryImpl::Job::ReturnToStateInitConnection(
1252 bool close_connection) {
1253 if (close_connection && connection_->socket())
1254 connection_->socket()->Disconnect();
1255 connection_->Reset();
1257 if (request_)
1258 request_->RemoveRequestFromSpdySessionRequestMap();
1260 next_state_ = STATE_INIT_CONNECTION;
1263 void HttpStreamFactoryImpl::Job::SetSocketMotivation() {
1264 if (request_info_.motivation == HttpRequestInfo::PRECONNECT_MOTIVATED)
1265 connection_->socket()->SetSubresourceSpeculation();
1266 else if (request_info_.motivation == HttpRequestInfo::OMNIBOX_MOTIVATED)
1267 connection_->socket()->SetOmniboxSpeculation();
1268 // TODO(mbelshe): Add other motivations (like EARLY_LOAD_MOTIVATED).
1271 bool HttpStreamFactoryImpl::Job::IsHttpsProxyAndHttpUrl() const {
1272 if (!proxy_info_.is_https())
1273 return false;
1274 if (IsSpdyAlternative() || IsQuicAlternative()) {
1275 // We currently only support Alternate-Protocol where the original scheme
1276 // is http.
1277 DCHECK(origin_url_.SchemeIs("http"));
1278 return origin_url_.SchemeIs("http");
1280 return request_info_.url.SchemeIs("http");
1283 bool HttpStreamFactoryImpl::Job::IsSpdyAlternative() const {
1284 return alternative_service_.protocol >= NPN_SPDY_MINIMUM_VERSION &&
1285 alternative_service_.protocol <= NPN_SPDY_MAXIMUM_VERSION;
1288 bool HttpStreamFactoryImpl::Job::IsQuicAlternative() const {
1289 return alternative_service_.protocol == QUIC;
1292 void HttpStreamFactoryImpl::Job::InitSSLConfig(const HostPortPair& server,
1293 SSLConfig* ssl_config,
1294 bool is_proxy) const {
1295 if (!is_proxy) {
1296 // Prior to HTTP/2 and SPDY, some servers use TLS renegotiation to request
1297 // TLS client authentication after the HTTP request was sent. Allow
1298 // renegotiation for only those connections.
1300 // Note that this does NOT implement the provision in
1301 // https://http2.github.io/http2-spec/#rfc.section.9.2.1 which allows the
1302 // server to request a renegotiation immediately before sending the
1303 // connection preface as waiting for the preface would cost the round trip
1304 // that False Start otherwise saves.
1305 ssl_config->renego_allowed_default = true;
1306 ssl_config->renego_allowed_for_protos.push_back(kProtoHTTP11);
1309 if (proxy_info_.is_https() && ssl_config->send_client_cert) {
1310 // When connecting through an HTTPS proxy, disable TLS False Start so
1311 // that client authentication errors can be distinguished between those
1312 // originating from the proxy server (ERR_PROXY_CONNECTION_FAILED) and
1313 // those originating from the endpoint (ERR_SSL_PROTOCOL_ERROR /
1314 // ERR_BAD_SSL_CLIENT_AUTH_CERT).
1316 // This assumes the proxy will only request certificates on the initial
1317 // handshake; renegotiation on the proxy connection is unsupported.
1318 ssl_config->false_start_enabled = false;
1321 if (request_info_.load_flags & LOAD_VERIFY_EV_CERT)
1322 ssl_config->verify_ev_cert = true;
1324 // Disable Channel ID if privacy mode is enabled.
1325 if (request_info_.privacy_mode == PRIVACY_MODE_ENABLED)
1326 ssl_config->channel_id_enabled = false;
1330 int HttpStreamFactoryImpl::Job::ReconsiderProxyAfterError(int error) {
1331 DCHECK(!pac_request_);
1332 DCHECK(session_);
1334 // A failure to resolve the hostname or any error related to establishing a
1335 // TCP connection could be grounds for trying a new proxy configuration.
1337 // Why do this when a hostname cannot be resolved? Some URLs only make sense
1338 // to proxy servers. The hostname in those URLs might fail to resolve if we
1339 // are still using a non-proxy config. We need to check if a proxy config
1340 // now exists that corresponds to a proxy server that could load the URL.
1342 switch (error) {
1343 case ERR_PROXY_CONNECTION_FAILED:
1344 case ERR_NAME_NOT_RESOLVED:
1345 case ERR_INTERNET_DISCONNECTED:
1346 case ERR_ADDRESS_UNREACHABLE:
1347 case ERR_CONNECTION_CLOSED:
1348 case ERR_CONNECTION_TIMED_OUT:
1349 case ERR_CONNECTION_RESET:
1350 case ERR_CONNECTION_REFUSED:
1351 case ERR_CONNECTION_ABORTED:
1352 case ERR_TIMED_OUT:
1353 case ERR_TUNNEL_CONNECTION_FAILED:
1354 case ERR_SOCKS_CONNECTION_FAILED:
1355 // This can happen in the case of trying to talk to a proxy using SSL, and
1356 // ending up talking to a captive portal that supports SSL instead.
1357 case ERR_PROXY_CERTIFICATE_INVALID:
1358 // This can happen when trying to talk SSL to a non-SSL server (Like a
1359 // captive portal).
1360 case ERR_QUIC_PROTOCOL_ERROR:
1361 case ERR_QUIC_HANDSHAKE_FAILED:
1362 case ERR_SSL_PROTOCOL_ERROR:
1363 break;
1364 case ERR_SOCKS_CONNECTION_HOST_UNREACHABLE:
1365 // Remap the SOCKS-specific "host unreachable" error to a more
1366 // generic error code (this way consumers like the link doctor
1367 // know to substitute their error page).
1369 // Note that if the host resolving was done by the SOCKS5 proxy, we can't
1370 // differentiate between a proxy-side "host not found" versus a proxy-side
1371 // "address unreachable" error, and will report both of these failures as
1372 // ERR_ADDRESS_UNREACHABLE.
1373 return ERR_ADDRESS_UNREACHABLE;
1374 default:
1375 return error;
1378 if (request_info_.load_flags & LOAD_BYPASS_PROXY) {
1379 return error;
1382 if (proxy_info_.is_https() && proxy_ssl_config_.send_client_cert) {
1383 session_->ssl_client_auth_cache()->Remove(
1384 proxy_info_.proxy_server().host_port_pair());
1387 int rv = session_->proxy_service()->ReconsiderProxyAfterError(
1388 request_info_.url, request_info_.load_flags, error, &proxy_info_,
1389 io_callback_, &pac_request_, session_->network_delegate(), net_log_);
1390 if (rv == OK || rv == ERR_IO_PENDING) {
1391 // If the error was during connection setup, there is no socket to
1392 // disconnect.
1393 if (connection_->socket())
1394 connection_->socket()->Disconnect();
1395 connection_->Reset();
1396 if (request_)
1397 request_->RemoveRequestFromSpdySessionRequestMap();
1398 next_state_ = STATE_RESOLVE_PROXY_COMPLETE;
1399 } else {
1400 // If ReconsiderProxyAfterError() failed synchronously, it means
1401 // there was nothing left to fall-back to, so fail the transaction
1402 // with the last connection error we got.
1403 // TODO(eroman): This is a confusing contract, make it more obvious.
1404 rv = error;
1407 return rv;
1410 int HttpStreamFactoryImpl::Job::HandleCertificateError(int error) {
1411 DCHECK(using_ssl_);
1412 DCHECK(IsCertificateError(error));
1414 SSLClientSocket* ssl_socket =
1415 static_cast<SSLClientSocket*>(connection_->socket());
1416 ssl_socket->GetSSLInfo(&ssl_info_);
1418 // Add the bad certificate to the set of allowed certificates in the
1419 // SSL config object. This data structure will be consulted after calling
1420 // RestartIgnoringLastError(). And the user will be asked interactively
1421 // before RestartIgnoringLastError() is ever called.
1422 SSLConfig::CertAndStatus bad_cert;
1424 // |ssl_info_.cert| may be NULL if we failed to create
1425 // X509Certificate for whatever reason, but normally it shouldn't
1426 // happen, unless this code is used inside sandbox.
1427 if (ssl_info_.cert.get() == NULL ||
1428 !X509Certificate::GetDEREncoded(ssl_info_.cert->os_cert_handle(),
1429 &bad_cert.der_cert)) {
1430 return error;
1432 bad_cert.cert_status = ssl_info_.cert_status;
1433 server_ssl_config_.allowed_bad_certs.push_back(bad_cert);
1435 int load_flags = request_info_.load_flags;
1436 if (session_->params().ignore_certificate_errors)
1437 load_flags |= LOAD_IGNORE_ALL_CERT_ERRORS;
1438 if (ssl_socket->IgnoreCertError(error, load_flags))
1439 return OK;
1440 return error;
1443 void HttpStreamFactoryImpl::Job::SwitchToSpdyMode() {
1444 if (HttpStreamFactory::spdy_enabled())
1445 using_spdy_ = true;
1448 bool HttpStreamFactoryImpl::Job::IsPreconnecting() const {
1449 DCHECK_GE(num_streams_, 0);
1450 return num_streams_ > 0;
1453 bool HttpStreamFactoryImpl::Job::IsOrphaned() const {
1454 return !IsPreconnecting() && !request_;
1457 void HttpStreamFactoryImpl::Job::ReportJobSucceededForRequest() {
1458 if (using_existing_quic_session_) {
1459 // If an existing session was used, then no TCP connection was
1460 // started.
1461 HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_NO_RACE);
1462 } else if (IsSpdyAlternative() || IsQuicAlternative()) {
1463 // This Job was the alternative Job, and hence won the race.
1464 HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_WON_RACE);
1465 } else {
1466 // This Job was the normal Job, and hence the alternative Job lost the race.
1467 HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_LOST_RACE);
1471 void HttpStreamFactoryImpl::Job::MarkOtherJobComplete(const Job& job) {
1472 DCHECK_EQ(STATUS_RUNNING, other_job_status_);
1473 other_job_status_ = job.job_status_;
1474 other_job_alternative_service_ = job.alternative_service_;
1475 MaybeMarkAlternativeServiceBroken();
1478 void HttpStreamFactoryImpl::Job::MaybeMarkAlternativeServiceBroken() {
1479 if (job_status_ == STATUS_RUNNING || other_job_status_ == STATUS_RUNNING)
1480 return;
1482 if (IsSpdyAlternative() || IsQuicAlternative()) {
1483 if (job_status_ == STATUS_BROKEN && other_job_status_ == STATUS_SUCCEEDED) {
1484 HistogramBrokenAlternateProtocolLocation(
1485 BROKEN_ALTERNATE_PROTOCOL_LOCATION_HTTP_STREAM_FACTORY_IMPL_JOB_ALT);
1486 session_->http_server_properties()->MarkAlternativeServiceBroken(
1487 alternative_service_);
1489 return;
1492 if (job_status_ == STATUS_SUCCEEDED && other_job_status_ == STATUS_BROKEN) {
1493 HistogramBrokenAlternateProtocolLocation(
1494 BROKEN_ALTERNATE_PROTOCOL_LOCATION_HTTP_STREAM_FACTORY_IMPL_JOB_MAIN);
1495 session_->http_server_properties()->MarkAlternativeServiceBroken(
1496 other_job_alternative_service_);
1500 HttpStreamFactoryImpl::Job::ValidSpdySessionPool::ValidSpdySessionPool(
1501 SpdySessionPool* spdy_session_pool,
1502 GURL& origin_url,
1503 bool is_spdy_alternative)
1504 : spdy_session_pool_(spdy_session_pool),
1505 origin_url_(origin_url),
1506 is_spdy_alternative_(is_spdy_alternative) {
1509 int HttpStreamFactoryImpl::Job::ValidSpdySessionPool::FindAvailableSession(
1510 const SpdySessionKey& key,
1511 const BoundNetLog& net_log,
1512 base::WeakPtr<SpdySession>* spdy_session) {
1513 *spdy_session = spdy_session_pool_->FindAvailableSession(key, net_log);
1514 return CheckAlternativeServiceValidityForOrigin(*spdy_session);
1517 int HttpStreamFactoryImpl::Job::ValidSpdySessionPool::
1518 CreateAvailableSessionFromSocket(const SpdySessionKey& key,
1519 scoped_ptr<ClientSocketHandle> connection,
1520 const BoundNetLog& net_log,
1521 int certificate_error_code,
1522 bool is_secure,
1523 base::WeakPtr<SpdySession>* spdy_session) {
1524 *spdy_session = spdy_session_pool_->CreateAvailableSessionFromSocket(
1525 key, connection.Pass(), net_log, certificate_error_code, is_secure);
1526 return CheckAlternativeServiceValidityForOrigin(*spdy_session);
1529 int HttpStreamFactoryImpl::Job::ValidSpdySessionPool::
1530 CheckAlternativeServiceValidityForOrigin(
1531 base::WeakPtr<SpdySession> spdy_session) {
1532 // For an alternative Job, server_.host() might be different than
1533 // origin_url_.host(), therefore it needs to be verified that the former
1534 // provides a certificate that is valid for the latter.
1535 if (!is_spdy_alternative_ || !spdy_session ||
1536 spdy_session->VerifyDomainAuthentication(origin_url_.host())) {
1537 return OK;
1539 return ERR_ALTERNATIVE_CERT_NOT_VALID_FOR_ORIGIN;
1542 ClientSocketPoolManager::SocketGroupType
1543 HttpStreamFactoryImpl::Job::GetSocketGroup() const {
1544 std::string scheme = origin_url_.scheme();
1545 if (scheme == "https" || scheme == "wss" || IsSpdyAlternative())
1546 return ClientSocketPoolManager::SSL_GROUP;
1548 if (scheme == "ftp")
1549 return ClientSocketPoolManager::FTP_GROUP;
1551 return ClientSocketPoolManager::NORMAL_GROUP;
1554 // If the connection succeeds, failed connection attempts leading up to the
1555 // success will be returned via the successfully connected socket. If the
1556 // connection fails, failed connection attempts will be returned via the
1557 // ClientSocketHandle. Check whether a socket was returned and copy the
1558 // connection attempts from the proper place.
1559 void HttpStreamFactoryImpl::Job::
1560 MaybeCopyConnectionAttemptsFromSocketOrHandle() {
1561 if (IsOrphaned() || !connection_)
1562 return;
1564 if (connection_->socket()) {
1565 ConnectionAttempts socket_attempts;
1566 connection_->socket()->GetConnectionAttempts(&socket_attempts);
1567 request_->AddConnectionAttempts(socket_attempts);
1568 } else {
1569 request_->AddConnectionAttempts(connection_->connection_attempts());
1573 } // namespace net