1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/http/http_stream_factory_impl_job.h"
6
7 #include "base/logging.h"
8 #include "base/stl_util-inl.h"
9 #include "base/string_util.h"
10 #include "base/stringprintf.h"
11 #include "base/values.h"
12 #include "net/base/connection_type_histograms.h"
13 #include "net/base/net_log.h"
14 #include "net/base/net_util.h"
15 #include "net/base/ssl_cert_request_info.h"
16 #include "net/http/http_basic_stream.h"
17 #include "net/http/http_network_session.h"
18 #include "net/http/http_proxy_client_socket.h"
19 #include "net/http/http_proxy_client_socket_pool.h"
20 #include "net/http/http_request_info.h"
21 #include "net/http/http_stream_factory_impl_request.h"
22 #include "net/socket/client_socket_handle.h"
23 #include "net/socket/client_socket_pool.h"
24 #include "net/socket/socks_client_socket_pool.h"
25 #include "net/socket/ssl_client_socket.h"
26 #include "net/socket/ssl_client_socket_pool.h"
27 #include "net/spdy/spdy_http_stream.h"
28 #include "net/spdy/spdy_session.h"
29 #include "net/spdy/spdy_session_pool.h"
30
31 namespace net {
32
33 namespace {
34
35 } // namespace
36
Job(HttpStreamFactoryImpl * stream_factory,HttpNetworkSession * session,const HttpRequestInfo & request_info,const SSLConfig & ssl_config,const BoundNetLog & net_log)37 HttpStreamFactoryImpl::Job::Job(HttpStreamFactoryImpl* stream_factory,
38 HttpNetworkSession* session,
39 const HttpRequestInfo& request_info,
40 const SSLConfig& ssl_config,
41 const BoundNetLog& net_log)
42 : request_(NULL),
43 request_info_(request_info),
44 ssl_config_(ssl_config),
45 net_log_(BoundNetLog::Make(net_log.net_log(),
46 NetLog::SOURCE_HTTP_STREAM_JOB)),
47 ALLOW_THIS_IN_INITIALIZER_LIST(io_callback_(this, &Job::OnIOComplete)),
48 connection_(new ClientSocketHandle),
49 session_(session),
50 stream_factory_(stream_factory),
51 next_state_(STATE_NONE),
52 pac_request_(NULL),
53 blocking_job_(NULL),
54 dependent_job_(NULL),
55 using_ssl_(false),
56 using_spdy_(false),
57 force_spdy_always_(HttpStreamFactory::force_spdy_always()),
58 force_spdy_over_ssl_(HttpStreamFactory::force_spdy_over_ssl()),
59 spdy_certificate_error_(OK),
60 establishing_tunnel_(false),
61 was_npn_negotiated_(false),
62 num_streams_(0),
63 spdy_session_direct_(false),
64 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
65 DCHECK(stream_factory);
66 DCHECK(session);
67 }
68
~Job()69 HttpStreamFactoryImpl::Job::~Job() {
70 net_log_.EndEvent(NetLog::TYPE_HTTP_STREAM_JOB, NULL);
71
72 // When we're in a partially constructed state, waiting for the user to
73 // provide certificate handling information or authentication, we can't reuse
74 // this stream at all.
75 if (next_state_ == STATE_WAITING_USER_ACTION) {
76 connection_->socket()->Disconnect();
77 connection_.reset();
78 }
79
80 if (pac_request_)
81 session_->proxy_service()->CancelPacRequest(pac_request_);
82
83 // The stream could be in a partial state. It is not reusable.
84 if (stream_.get() && next_state_ != STATE_DONE)
85 stream_->Close(true /* not reusable */);
86 }
87
Start(Request * request)88 void HttpStreamFactoryImpl::Job::Start(Request* request) {
89 DCHECK(request);
90 request_ = request;
91 StartInternal();
92 }
93
Preconnect(int num_streams)94 int HttpStreamFactoryImpl::Job::Preconnect(int num_streams) {
95 DCHECK_GT(num_streams, 0);
96 num_streams_ = num_streams;
97 return StartInternal();
98 }
99
RestartTunnelWithProxyAuth(const string16 & username,const string16 & password)100 int HttpStreamFactoryImpl::Job::RestartTunnelWithProxyAuth(
101 const string16& username, const string16& password) {
102 DCHECK(establishing_tunnel_);
103 next_state_ = STATE_RESTART_TUNNEL_AUTH;
104 stream_.reset();
105 return RunLoop(OK);
106 }
107
GetLoadState() const108 LoadState HttpStreamFactoryImpl::Job::GetLoadState() const {
109 switch (next_state_) {
110 case STATE_RESOLVE_PROXY_COMPLETE:
111 return LOAD_STATE_RESOLVING_PROXY_FOR_URL;
112 case STATE_CREATE_STREAM_COMPLETE:
113 return connection_->GetLoadState();
114 case STATE_INIT_CONNECTION_COMPLETE:
115 return LOAD_STATE_SENDING_REQUEST;
116 default:
117 return LOAD_STATE_IDLE;
118 }
119 }
120
MarkAsAlternate(const GURL & original_url)121 void HttpStreamFactoryImpl::Job::MarkAsAlternate(const GURL& original_url) {
122 DCHECK(!original_url_.get());
123 original_url_.reset(new GURL(original_url));
124 }
125
WaitFor(Job * job)126 void HttpStreamFactoryImpl::Job::WaitFor(Job* job) {
127 DCHECK_EQ(STATE_NONE, next_state_);
128 DCHECK_EQ(STATE_NONE, job->next_state_);
129 DCHECK(!blocking_job_);
130 DCHECK(!job->dependent_job_);
131 blocking_job_ = job;
132 job->dependent_job_ = this;
133 }
134
Resume(Job * job)135 void HttpStreamFactoryImpl::Job::Resume(Job* job) {
136 DCHECK_EQ(blocking_job_, job);
137 blocking_job_ = NULL;
138
139 // We know we're blocked if the next_state_ is STATE_WAIT_FOR_JOB_COMPLETE.
140 // Unblock |this|.
141 if (next_state_ == STATE_WAIT_FOR_JOB_COMPLETE) {
142 MessageLoop::current()->PostTask(
143 FROM_HERE,
144 method_factory_.NewRunnableMethod(
145 &HttpStreamFactoryImpl::Job::OnIOComplete, OK));
146 }
147 }
148
Orphan(const Request * request)149 void HttpStreamFactoryImpl::Job::Orphan(const Request* request) {
150 DCHECK_EQ(request_, request);
151 request_ = NULL;
152 // We've been orphaned, but there's a job we're blocked on. Don't bother
153 // racing, just cancel ourself.
154 if (blocking_job_) {
155 DCHECK(blocking_job_->dependent_job_);
156 blocking_job_->dependent_job_ = NULL;
157 blocking_job_ = NULL;
158 stream_factory_->OnOrphanedJobComplete(this);
159 }
160 }
161
was_npn_negotiated() const162 bool HttpStreamFactoryImpl::Job::was_npn_negotiated() const {
163 return was_npn_negotiated_;
164 }
165
using_spdy() const166 bool HttpStreamFactoryImpl::Job::using_spdy() const {
167 return using_spdy_;
168 }
169
ssl_config() const170 const SSLConfig& HttpStreamFactoryImpl::Job::ssl_config() const {
171 return ssl_config_;
172 }
173
proxy_info() const174 const ProxyInfo& HttpStreamFactoryImpl::Job::proxy_info() const {
175 return proxy_info_;
176 }
177
GetSSLInfo()178 void HttpStreamFactoryImpl::Job::GetSSLInfo() {
179 DCHECK(using_ssl_);
180 DCHECK(!establishing_tunnel_);
181 DCHECK(connection_.get() && connection_->socket());
182 SSLClientSocket* ssl_socket =
183 static_cast<SSLClientSocket*>(connection_->socket());
184 ssl_socket->GetSSLInfo(&ssl_info_);
185 }
186
OnStreamReadyCallback()187 void HttpStreamFactoryImpl::Job::OnStreamReadyCallback() {
188 DCHECK(stream_.get());
189 DCHECK(!IsPreconnecting());
190 if (IsOrphaned()) {
191 stream_factory_->OnOrphanedJobComplete(this);
192 } else {
193 request_->Complete(was_npn_negotiated(),
194 using_spdy(),
195 net_log_.source());
196 request_->OnStreamReady(this, ssl_config_, proxy_info_, stream_.release());
197 }
198 // |this| may be deleted after this call.
199 }
200
OnSpdySessionReadyCallback()201 void HttpStreamFactoryImpl::Job::OnSpdySessionReadyCallback() {
202 DCHECK(!stream_.get());
203 DCHECK(!IsPreconnecting());
204 DCHECK(using_spdy());
205 DCHECK(new_spdy_session_);
206 scoped_refptr<SpdySession> spdy_session = new_spdy_session_;
207 new_spdy_session_ = NULL;
208 if (IsOrphaned()) {
209 stream_factory_->OnSpdySessionReady(
210 spdy_session, spdy_session_direct_, ssl_config_, proxy_info_,
211 was_npn_negotiated(), using_spdy(), net_log_.source());
212 stream_factory_->OnOrphanedJobComplete(this);
213 } else {
214 request_->OnSpdySessionReady(this, spdy_session, spdy_session_direct_);
215 }
216 // |this| may be deleted after this call.
217 }
218
OnStreamFailedCallback(int result)219 void HttpStreamFactoryImpl::Job::OnStreamFailedCallback(int result) {
220 DCHECK(!IsPreconnecting());
221 if (IsOrphaned())
222 stream_factory_->OnOrphanedJobComplete(this);
223 else
224 request_->OnStreamFailed(this, result, ssl_config_);
225 // |this| may be deleted after this call.
226 }
227
OnCertificateErrorCallback(int result,const SSLInfo & ssl_info)228 void HttpStreamFactoryImpl::Job::OnCertificateErrorCallback(
229 int result, const SSLInfo& ssl_info) {
230 DCHECK(!IsPreconnecting());
231 if (IsOrphaned())
232 stream_factory_->OnOrphanedJobComplete(this);
233 else
234 request_->OnCertificateError(this, result, ssl_config_, ssl_info);
235 // |this| may be deleted after this call.
236 }
237
OnNeedsProxyAuthCallback(const HttpResponseInfo & response,HttpAuthController * auth_controller)238 void HttpStreamFactoryImpl::Job::OnNeedsProxyAuthCallback(
239 const HttpResponseInfo& response,
240 HttpAuthController* auth_controller) {
241 DCHECK(!IsPreconnecting());
242 if (IsOrphaned())
243 stream_factory_->OnOrphanedJobComplete(this);
244 else
245 request_->OnNeedsProxyAuth(
246 this, response, ssl_config_, proxy_info_, auth_controller);
247 // |this| may be deleted after this call.
248 }
249
OnNeedsClientAuthCallback(SSLCertRequestInfo * cert_info)250 void HttpStreamFactoryImpl::Job::OnNeedsClientAuthCallback(
251 SSLCertRequestInfo* cert_info) {
252 DCHECK(!IsPreconnecting());
253 if (IsOrphaned())
254 stream_factory_->OnOrphanedJobComplete(this);
255 else
256 request_->OnNeedsClientAuth(this, ssl_config_, cert_info);
257 // |this| may be deleted after this call.
258 }
259
OnHttpsProxyTunnelResponseCallback(const HttpResponseInfo & response_info,HttpStream * stream)260 void HttpStreamFactoryImpl::Job::OnHttpsProxyTunnelResponseCallback(
261 const HttpResponseInfo& response_info,
262 HttpStream* stream) {
263 DCHECK(!IsPreconnecting());
264 if (IsOrphaned())
265 stream_factory_->OnOrphanedJobComplete(this);
266 else
267 request_->OnHttpsProxyTunnelResponse(
268 this, response_info, ssl_config_, proxy_info_, stream);
269 // |this| may be deleted after this call.
270 }
271
OnPreconnectsComplete()272 void HttpStreamFactoryImpl::Job::OnPreconnectsComplete() {
273 DCHECK(!request_);
274 if (new_spdy_session_) {
275 stream_factory_->OnSpdySessionReady(
276 new_spdy_session_, spdy_session_direct_, ssl_config_,
277 proxy_info_, was_npn_negotiated(), using_spdy(), net_log_.source());
278 }
279 stream_factory_->OnPreconnectsComplete(this);
280 // |this| may be deleted after this call.
281 }
282
OnIOComplete(int result)283 void HttpStreamFactoryImpl::Job::OnIOComplete(int result) {
284 RunLoop(result);
285 }
286
RunLoop(int result)287 int HttpStreamFactoryImpl::Job::RunLoop(int result) {
288 result = DoLoop(result);
289
290 if (result == ERR_IO_PENDING)
291 return result;
292
293 if (IsPreconnecting()) {
294 MessageLoop::current()->PostTask(
295 FROM_HERE,
296 method_factory_.NewRunnableMethod(
297 &HttpStreamFactoryImpl::Job::OnPreconnectsComplete));
298 return ERR_IO_PENDING;
299 }
300
301 if (IsCertificateError(result)) {
302 // Retrieve SSL information from the socket.
303 GetSSLInfo();
304
305 next_state_ = STATE_WAITING_USER_ACTION;
306 MessageLoop::current()->PostTask(
307 FROM_HERE,
308 method_factory_.NewRunnableMethod(
309 &HttpStreamFactoryImpl::Job::OnCertificateErrorCallback,
310 result, ssl_info_));
311 return ERR_IO_PENDING;
312 }
313
314 switch (result) {
315 case ERR_PROXY_AUTH_REQUESTED:
316 {
317 DCHECK(connection_.get());
318 DCHECK(connection_->socket());
319 DCHECK(establishing_tunnel_);
320
321 HttpProxyClientSocket* http_proxy_socket =
322 static_cast<HttpProxyClientSocket*>(connection_->socket());
323 const HttpResponseInfo* tunnel_auth_response =
324 http_proxy_socket->GetConnectResponseInfo();
325
326 next_state_ = STATE_WAITING_USER_ACTION;
327 MessageLoop::current()->PostTask(
328 FROM_HERE,
329 method_factory_.NewRunnableMethod(
330 &HttpStreamFactoryImpl::Job::OnNeedsProxyAuthCallback,
331 *tunnel_auth_response,
332 http_proxy_socket->auth_controller()));
333 }
334 return ERR_IO_PENDING;
335
336 case ERR_SSL_CLIENT_AUTH_CERT_NEEDED:
337 MessageLoop::current()->PostTask(
338 FROM_HERE,
339 method_factory_.NewRunnableMethod(
340 &HttpStreamFactoryImpl::Job::OnNeedsClientAuthCallback,
341 connection_->ssl_error_response_info().cert_request_info));
342 return ERR_IO_PENDING;
343
344 case ERR_HTTPS_PROXY_TUNNEL_RESPONSE:
345 {
346 DCHECK(connection_.get());
347 DCHECK(connection_->socket());
348 DCHECK(establishing_tunnel_);
349
350 ProxyClientSocket* proxy_socket =
351 static_cast<ProxyClientSocket*>(connection_->socket());
352 MessageLoop::current()->PostTask(
353 FROM_HERE,
354 method_factory_.NewRunnableMethod(
355 &HttpStreamFactoryImpl::Job::OnHttpsProxyTunnelResponseCallback,
356 *proxy_socket->GetConnectResponseInfo(),
357 proxy_socket->CreateConnectResponseStream()));
358 return ERR_IO_PENDING;
359 }
360
361 case OK:
362 next_state_ = STATE_DONE;
363 if (new_spdy_session_) {
364 MessageLoop::current()->PostTask(
365 FROM_HERE,
366 method_factory_.NewRunnableMethod(
367 &HttpStreamFactoryImpl::Job::OnSpdySessionReadyCallback));
368 } else {
369 MessageLoop::current()->PostTask(
370 FROM_HERE,
371 method_factory_.NewRunnableMethod(
372 &HttpStreamFactoryImpl::Job::OnStreamReadyCallback));
373 }
374 return ERR_IO_PENDING;
375
376 default:
377 MessageLoop::current()->PostTask(
378 FROM_HERE,
379 method_factory_.NewRunnableMethod(
380 &HttpStreamFactoryImpl::Job::OnStreamFailedCallback,
381 result));
382 return ERR_IO_PENDING;
383 }
384 return result;
385 }
386
DoLoop(int result)387 int HttpStreamFactoryImpl::Job::DoLoop(int result) {
388 DCHECK_NE(next_state_, STATE_NONE);
389 int rv = result;
390 do {
391 State state = next_state_;
392 next_state_ = STATE_NONE;
393 switch (state) {
394 case STATE_RESOLVE_PROXY:
395 DCHECK_EQ(OK, rv);
396 rv = DoResolveProxy();
397 break;
398 case STATE_RESOLVE_PROXY_COMPLETE:
399 rv = DoResolveProxyComplete(rv);
400 break;
401 case STATE_WAIT_FOR_JOB:
402 DCHECK_EQ(OK, rv);
403 rv = DoWaitForJob();
404 break;
405 case STATE_WAIT_FOR_JOB_COMPLETE:
406 rv = DoWaitForJobComplete(rv);
407 break;
408 case STATE_INIT_CONNECTION:
409 DCHECK_EQ(OK, rv);
410 rv = DoInitConnection();
411 break;
412 case STATE_INIT_CONNECTION_COMPLETE:
413 rv = DoInitConnectionComplete(rv);
414 break;
415 case STATE_WAITING_USER_ACTION:
416 rv = DoWaitingUserAction(rv);
417 break;
418 case STATE_RESTART_TUNNEL_AUTH:
419 DCHECK_EQ(OK, rv);
420 rv = DoRestartTunnelAuth();
421 break;
422 case STATE_RESTART_TUNNEL_AUTH_COMPLETE:
423 rv = DoRestartTunnelAuthComplete(rv);
424 break;
425 case STATE_CREATE_STREAM:
426 DCHECK_EQ(OK, rv);
427 rv = DoCreateStream();
428 break;
429 case STATE_CREATE_STREAM_COMPLETE:
430 rv = DoCreateStreamComplete(rv);
431 break;
432 default:
433 NOTREACHED() << "bad state";
434 rv = ERR_FAILED;
435 break;
436 }
437 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
438 return rv;
439 }
440
StartInternal()441 int HttpStreamFactoryImpl::Job::StartInternal() {
442 CHECK_EQ(STATE_NONE, next_state_);
443 net_log_.BeginEvent(NetLog::TYPE_HTTP_STREAM_JOB,
444 make_scoped_refptr(new NetLogStringParameter(
445 "url", request_info_.url.GetOrigin().spec())));
446 next_state_ = STATE_RESOLVE_PROXY;
447 int rv = RunLoop(OK);
448 DCHECK_EQ(ERR_IO_PENDING, rv);
449 return rv;
450 }
451
DoResolveProxy()452 int HttpStreamFactoryImpl::Job::DoResolveProxy() {
453 DCHECK(!pac_request_);
454
455 next_state_ = STATE_RESOLVE_PROXY_COMPLETE;
456
457 origin_ = HostPortPair(request_info_.url.HostNoBrackets(),
458 request_info_.url.EffectiveIntPort());
459
460 if (request_info_.load_flags & LOAD_BYPASS_PROXY) {
461 proxy_info_.UseDirect();
462 return OK;
463 }
464
465 return session_->proxy_service()->ResolveProxy(
466 request_info_.url, &proxy_info_, &io_callback_, &pac_request_,
467 net_log_);
468 }
469
DoResolveProxyComplete(int result)470 int HttpStreamFactoryImpl::Job::DoResolveProxyComplete(int result) {
471 pac_request_ = NULL;
472
473 if (result == OK) {
474 // Remove unsupported proxies from the list.
475 proxy_info_.RemoveProxiesWithoutScheme(
476 ProxyServer::SCHEME_DIRECT |
477 ProxyServer::SCHEME_HTTP | ProxyServer::SCHEME_HTTPS |
478 ProxyServer::SCHEME_SOCKS4 | ProxyServer::SCHEME_SOCKS5);
479
480 if (proxy_info_.is_empty()) {
481 // No proxies/direct to choose from. This happens when we don't support
482 // any of the proxies in the returned list.
483 result = ERR_NO_SUPPORTED_PROXIES;
484 }
485 }
486
487 if (result != OK) {
488 if (dependent_job_)
489 dependent_job_->Resume(this);
490 return result;
491 }
492
493 if (blocking_job_)
494 next_state_ = STATE_WAIT_FOR_JOB;
495 else
496 next_state_ = STATE_INIT_CONNECTION;
497 return OK;
498 }
499
ShouldForceSpdySSL() const500 bool HttpStreamFactoryImpl::Job::ShouldForceSpdySSL() const {
501 bool rv = force_spdy_always_ && force_spdy_over_ssl_;
502 return rv && !HttpStreamFactory::HasSpdyExclusion(origin_);
503 }
504
ShouldForceSpdyWithoutSSL() const505 bool HttpStreamFactoryImpl::Job::ShouldForceSpdyWithoutSSL() const {
506 bool rv = force_spdy_always_ && !force_spdy_over_ssl_;
507 return rv && !HttpStreamFactory::HasSpdyExclusion(origin_);
508 }
509
DoWaitForJob()510 int HttpStreamFactoryImpl::Job::DoWaitForJob() {
511 DCHECK(blocking_job_);
512 next_state_ = STATE_WAIT_FOR_JOB_COMPLETE;
513 return ERR_IO_PENDING;
514 }
515
DoWaitForJobComplete(int result)516 int HttpStreamFactoryImpl::Job::DoWaitForJobComplete(int result) {
517 DCHECK(!blocking_job_);
518 DCHECK_EQ(OK, result);
519 next_state_ = STATE_INIT_CONNECTION;
520 return OK;
521 }
522
DoInitConnection()523 int HttpStreamFactoryImpl::Job::DoInitConnection() {
524 DCHECK(!blocking_job_);
525 DCHECK(!connection_->is_initialized());
526 DCHECK(proxy_info_.proxy_server().is_valid());
527 next_state_ = STATE_INIT_CONNECTION_COMPLETE;
528
529 using_ssl_ = request_info_.url.SchemeIs("https") || ShouldForceSpdySSL();
530 using_spdy_ = false;
531
532 // Check first if we have a spdy session for this group. If so, then go
533 // straight to using that.
534 HostPortProxyPair spdy_session_key;
535 if (IsHttpsProxyAndHttpUrl()) {
536 spdy_session_key =
537 HostPortProxyPair(proxy_info_.proxy_server().host_port_pair(),
538 ProxyServer::Direct());
539 } else {
540 spdy_session_key = HostPortProxyPair(origin_, proxy_info_.proxy_server());
541 }
542 if (session_->spdy_session_pool()->HasSession(spdy_session_key)) {
543 // If we're preconnecting, but we already have a SpdySession, we don't
544 // actually need to preconnect any sockets, so we're done.
545 if (IsPreconnecting())
546 return OK;
547 using_spdy_ = true;
548 next_state_ = STATE_CREATE_STREAM;
549 return OK;
550 } else if (request_ && (using_ssl_ || ShouldForceSpdyWithoutSSL())) {
551 // Update the spdy session key for the request that launched this job.
552 request_->SetSpdySessionKey(spdy_session_key);
553 }
554
555 // OK, there's no available SPDY session. Let |dependent_job_| resume if it's
556 // paused.
557
558 if (dependent_job_) {
559 dependent_job_->Resume(this);
560 dependent_job_ = NULL;
561 }
562
563 if (proxy_info_.is_http() || proxy_info_.is_https())
564 establishing_tunnel_ = using_ssl_;
565
566 bool want_spdy_over_npn = original_url_.get() ? true : false;
567
568 SSLConfig ssl_config_for_proxy = ssl_config_;
569 if (proxy_info_.is_https()) {
570 InitSSLConfig(proxy_info_.proxy_server().host_port_pair(),
571 &ssl_config_for_proxy);
572 }
573 if (using_ssl_) {
574 InitSSLConfig(origin_, &ssl_config_);
575 }
576
577 if (IsPreconnecting()) {
578 return ClientSocketPoolManager::PreconnectSocketsForHttpRequest(
579 request_info_,
580 session_,
581 proxy_info_,
582 ShouldForceSpdySSL(),
583 want_spdy_over_npn,
584 ssl_config_,
585 ssl_config_for_proxy,
586 net_log_,
587 num_streams_);
588 } else {
589 return ClientSocketPoolManager::InitSocketHandleForHttpRequest(
590 request_info_,
591 session_,
592 proxy_info_,
593 ShouldForceSpdySSL(),
594 want_spdy_over_npn,
595 ssl_config_,
596 ssl_config_for_proxy,
597 net_log_,
598 connection_.get(),
599 &io_callback_);
600 }
601 }
602
DoInitConnectionComplete(int result)603 int HttpStreamFactoryImpl::Job::DoInitConnectionComplete(int result) {
604 if (IsPreconnecting()) {
605 DCHECK_EQ(OK, result);
606 return OK;
607 }
608
609 // TODO(willchan): Make this a bit more exact. Maybe there are recoverable
610 // errors, such as ignoring certificate errors for Alternate-Protocol.
611 if (result < 0 && dependent_job_) {
612 dependent_job_->Resume(this);
613 dependent_job_ = NULL;
614 }
615
616 // |result| may be the result of any of the stacked pools. The following
617 // logic is used when determining how to interpret an error.
618 // If |result| < 0:
619 // and connection_->socket() != NULL, then the SSL handshake ran and it
620 // is a potentially recoverable error.
621 // and connection_->socket == NULL and connection_->is_ssl_error() is true,
622 // then the SSL handshake ran with an unrecoverable error.
623 // otherwise, the error came from one of the other pools.
624 bool ssl_started = using_ssl_ && (result == OK || connection_->socket() ||
625 connection_->is_ssl_error());
626
627 if (ssl_started && (result == OK || IsCertificateError(result))) {
628 SSLClientSocket* ssl_socket =
629 static_cast<SSLClientSocket*>(connection_->socket());
630 if (ssl_socket->was_npn_negotiated()) {
631 was_npn_negotiated_ = true;
632 if (ssl_socket->was_spdy_negotiated())
633 SwitchToSpdyMode();
634 }
635 if (ShouldForceSpdySSL())
636 SwitchToSpdyMode();
637 } else if (proxy_info_.is_https() && connection_->socket() &&
638 result == OK) {
639 HttpProxyClientSocket* proxy_socket =
640 static_cast<HttpProxyClientSocket*>(connection_->socket());
641 if (proxy_socket->using_spdy()) {
642 was_npn_negotiated_ = true;
643 SwitchToSpdyMode();
644 }
645 }
646
647 // We may be using spdy without SSL
648 if (ShouldForceSpdyWithoutSSL())
649 SwitchToSpdyMode();
650
651 if (result == ERR_PROXY_AUTH_REQUESTED ||
652 result == ERR_HTTPS_PROXY_TUNNEL_RESPONSE) {
653 DCHECK(!ssl_started);
654 // Other state (i.e. |using_ssl_|) suggests that |connection_| will have an
655 // SSL socket, but there was an error before that could happen. This
656 // puts the in progress HttpProxy socket into |connection_| in order to
657 // complete the auth (or read the response body). The tunnel restart code
658 // is careful to remove it before returning control to the rest of this
659 // class.
660 connection_.reset(connection_->release_pending_http_proxy_connection());
661 return result;
662 }
663
664 if (!ssl_started && result < 0 && original_url_.get()) {
665 // Mark the alternate protocol as broken and fallback.
666 session_->mutable_alternate_protocols()->MarkBrokenAlternateProtocolFor(
667 HostPortPair::FromURL(*original_url_));
668 return result;
669 }
670
671 if (result < 0 && !ssl_started)
672 return ReconsiderProxyAfterError(result);
673 establishing_tunnel_ = false;
674
675 if (connection_->socket()) {
676 LogHttpConnectedMetrics(*connection_);
677
678 // We officially have a new connection. Record the type.
679 if (!connection_->is_reused()) {
680 ConnectionType type = using_spdy_ ? CONNECTION_SPDY : CONNECTION_HTTP;
681 UpdateConnectionTypeHistograms(type);
682 }
683 }
684
685 // Handle SSL errors below.
686 if (using_ssl_) {
687 DCHECK(ssl_started);
688 if (IsCertificateError(result)) {
689 if (using_spdy_ && original_url_.get() &&
690 original_url_->SchemeIs("http")) {
691 // We ignore certificate errors for http over spdy.
692 spdy_certificate_error_ = result;
693 result = OK;
694 } else {
695 result = HandleCertificateError(result);
696 if (result == OK && !connection_->socket()->IsConnectedAndIdle()) {
697 ReturnToStateInitConnection(true /* close connection */);
698 return result;
699 }
700 }
701 }
702 if (result < 0)
703 return result;
704 }
705
706 next_state_ = STATE_CREATE_STREAM;
707 return OK;
708 }
709
DoWaitingUserAction(int result)710 int HttpStreamFactoryImpl::Job::DoWaitingUserAction(int result) {
711 // This state indicates that the stream request is in a partially
712 // completed state, and we've called back to the delegate for more
713 // information.
714
715 // We're always waiting here for the delegate to call us back.
716 return ERR_IO_PENDING;
717 }
718
DoCreateStream()719 int HttpStreamFactoryImpl::Job::DoCreateStream() {
720 next_state_ = STATE_CREATE_STREAM_COMPLETE;
721
722 // We only set the socket motivation if we're the first to use
723 // this socket. Is there a race for two SPDY requests? We really
724 // need to plumb this through to the connect level.
725 if (connection_->socket() && !connection_->is_reused())
726 SetSocketMotivation();
727
728 const ProxyServer& proxy_server = proxy_info_.proxy_server();
729
730 if (!using_spdy_) {
731 bool using_proxy = (proxy_info_.is_http() || proxy_info_.is_https()) &&
732 request_info_.url.SchemeIs("http");
733 stream_.reset(new HttpBasicStream(connection_.release(), NULL,
734 using_proxy));
735 return OK;
736 }
737
738 CHECK(!stream_.get());
739
740 bool direct = true;
741 SpdySessionPool* spdy_pool = session_->spdy_session_pool();
742 scoped_refptr<SpdySession> spdy_session;
743
744 HostPortProxyPair pair(origin_, proxy_server);
745 if (spdy_pool->HasSession(pair)) {
746 // We have a SPDY session to the origin server. This might be a direct
747 // connection, or it might be a SPDY session through an HTTP or HTTPS proxy.
748 spdy_session = spdy_pool->Get(pair, net_log_);
749 } else if (IsHttpsProxyAndHttpUrl()) {
750 // If we don't have a direct SPDY session, and we're using an HTTPS
751 // proxy, then we might have a SPDY session to the proxy.
752 pair = HostPortProxyPair(proxy_server.host_port_pair(),
753 ProxyServer::Direct());
754 if (spdy_pool->HasSession(pair)) {
755 spdy_session = spdy_pool->Get(pair, net_log_);
756 }
757 direct = false;
758 }
759
760 if (spdy_session.get()) {
761 // We picked up an existing session, so we don't need our socket.
762 if (connection_->socket())
763 connection_->socket()->Disconnect();
764 connection_->Reset();
765 } else {
766 // SPDY can be negotiated using the TLS next protocol negotiation (NPN)
767 // extension, or just directly using SSL. Either way, |connection_| must
768 // contain an SSLClientSocket.
769 CHECK(connection_->socket());
770 int error = spdy_pool->GetSpdySessionFromSocket(
771 pair, connection_.release(), net_log_, spdy_certificate_error_,
772 &spdy_session, using_ssl_);
773 if (error != OK)
774 return error;
775 new_spdy_session_ = spdy_session;
776 spdy_session_direct_ = direct;
777 return OK;
778 }
779
780 if (spdy_session->IsClosed())
781 return ERR_CONNECTION_CLOSED;
782
783 // TODO(willchan): Delete this code, because eventually, the
784 // HttpStreamFactoryImpl will be creating all the SpdyHttpStreams, since it
785 // will know when SpdySessions become available. The above HasSession() checks
786 // will be able to be deleted too.
787
788 bool use_relative_url = direct || request_info_.url.SchemeIs("https");
789 stream_.reset(new SpdyHttpStream(spdy_session, use_relative_url));
790 return OK;
791 }
792
DoCreateStreamComplete(int result)793 int HttpStreamFactoryImpl::Job::DoCreateStreamComplete(int result) {
794 if (result < 0)
795 return result;
796
797 next_state_ = STATE_NONE;
798 return OK;
799 }
800
DoRestartTunnelAuth()801 int HttpStreamFactoryImpl::Job::DoRestartTunnelAuth() {
802 next_state_ = STATE_RESTART_TUNNEL_AUTH_COMPLETE;
803 HttpProxyClientSocket* http_proxy_socket =
804 static_cast<HttpProxyClientSocket*>(connection_->socket());
805 return http_proxy_socket->RestartWithAuth(&io_callback_);
806 }
807
DoRestartTunnelAuthComplete(int result)808 int HttpStreamFactoryImpl::Job::DoRestartTunnelAuthComplete(int result) {
809 if (result == ERR_PROXY_AUTH_REQUESTED)
810 return result;
811
812 if (result == OK) {
813 // Now that we've got the HttpProxyClientSocket connected. We have
814 // to release it as an idle socket into the pool and start the connection
815 // process from the beginning. Trying to pass it in with the
816 // SSLSocketParams might cause a deadlock since params are dispatched
817 // interchangeably. This request won't necessarily get this http proxy
818 // socket, but there will be forward progress.
819 establishing_tunnel_ = false;
820 ReturnToStateInitConnection(false /* do not close connection */);
821 return OK;
822 }
823
824 return ReconsiderProxyAfterError(result);
825 }
826
ReturnToStateInitConnection(bool close_connection)827 void HttpStreamFactoryImpl::Job::ReturnToStateInitConnection(
828 bool close_connection) {
829 if (close_connection && connection_->socket())
830 connection_->socket()->Disconnect();
831 connection_->Reset();
832
833 if (request_)
834 request_->RemoveRequestFromSpdySessionRequestMap();
835
836 next_state_ = STATE_INIT_CONNECTION;
837 }
838
SetSocketMotivation()839 void HttpStreamFactoryImpl::Job::SetSocketMotivation() {
840 if (request_info_.motivation == HttpRequestInfo::PRECONNECT_MOTIVATED)
841 connection_->socket()->SetSubresourceSpeculation();
842 else if (request_info_.motivation == HttpRequestInfo::OMNIBOX_MOTIVATED)
843 connection_->socket()->SetOmniboxSpeculation();
844 // TODO(mbelshe): Add other motivations (like EARLY_LOAD_MOTIVATED).
845 }
846
IsHttpsProxyAndHttpUrl()847 bool HttpStreamFactoryImpl::Job::IsHttpsProxyAndHttpUrl() {
848 if (!proxy_info_.is_https())
849 return false;
850 if (original_url_.get()) {
851 // We currently only support Alternate-Protocol where the original scheme
852 // is http.
853 DCHECK(original_url_->SchemeIs("http"));
854 return original_url_->SchemeIs("http");
855 }
856 return request_info_.url.SchemeIs("http");
857 }
858
859 // Sets several fields of ssl_config for the given origin_server based on the
860 // proxy info and other factors.
InitSSLConfig(const HostPortPair & origin_server,SSLConfig * ssl_config) const861 void HttpStreamFactoryImpl::Job::InitSSLConfig(
862 const HostPortPair& origin_server,
863 SSLConfig* ssl_config) const {
864 if (stream_factory_->IsTLSIntolerantServer(origin_server)) {
865 LOG(WARNING) << "Falling back to SSLv3 because host is TLS intolerant: "
866 << origin_server.ToString();
867 ssl_config->ssl3_fallback = true;
868 ssl_config->tls1_enabled = false;
869 }
870
871 if (proxy_info_.is_https() && ssl_config->send_client_cert) {
872 // When connecting through an HTTPS proxy, disable TLS False Start so
873 // that client authentication errors can be distinguished between those
874 // originating from the proxy server (ERR_PROXY_CONNECTION_FAILED) and
875 // those originating from the endpoint (ERR_SSL_PROTOCOL_ERROR /
876 // ERR_BAD_SSL_CLIENT_AUTH_CERT).
877 // TODO(rch): This assumes that the HTTPS proxy will only request a
878 // client certificate during the initial handshake.
879 // http://crbug.com/59292
880 ssl_config->false_start_enabled = false;
881 }
882
883 UMA_HISTOGRAM_ENUMERATION("Net.ConnectionUsedSSLv3Fallback",
884 static_cast<int>(ssl_config->ssl3_fallback), 2);
885
886 if (request_info_.load_flags & LOAD_VERIFY_EV_CERT)
887 ssl_config->verify_ev_cert = true;
888 }
889
890
ReconsiderProxyAfterError(int error)891 int HttpStreamFactoryImpl::Job::ReconsiderProxyAfterError(int error) {
892 DCHECK(!pac_request_);
893
894 // A failure to resolve the hostname or any error related to establishing a
895 // TCP connection could be grounds for trying a new proxy configuration.
896 //
897 // Why do this when a hostname cannot be resolved? Some URLs only make sense
898 // to proxy servers. The hostname in those URLs might fail to resolve if we
899 // are still using a non-proxy config. We need to check if a proxy config
900 // now exists that corresponds to a proxy server that could load the URL.
901 //
902 switch (error) {
903 case ERR_PROXY_CONNECTION_FAILED:
904 case ERR_NAME_NOT_RESOLVED:
905 case ERR_INTERNET_DISCONNECTED:
906 case ERR_ADDRESS_UNREACHABLE:
907 case ERR_CONNECTION_CLOSED:
908 case ERR_CONNECTION_RESET:
909 case ERR_CONNECTION_REFUSED:
910 case ERR_CONNECTION_ABORTED:
911 case ERR_TIMED_OUT:
912 case ERR_TUNNEL_CONNECTION_FAILED:
913 case ERR_SOCKS_CONNECTION_FAILED:
914 break;
915 case ERR_SOCKS_CONNECTION_HOST_UNREACHABLE:
916 // Remap the SOCKS-specific "host unreachable" error to a more
917 // generic error code (this way consumers like the link doctor
918 // know to substitute their error page).
919 //
920 // Note that if the host resolving was done by the SOCSK5 proxy, we can't
921 // differentiate between a proxy-side "host not found" versus a proxy-side
922 // "address unreachable" error, and will report both of these failures as
923 // ERR_ADDRESS_UNREACHABLE.
924 return ERR_ADDRESS_UNREACHABLE;
925 default:
926 return error;
927 }
928
929 if (request_info_.load_flags & LOAD_BYPASS_PROXY) {
930 return error;
931 }
932
933 if (proxy_info_.is_https() && ssl_config_.send_client_cert) {
934 session_->ssl_client_auth_cache()->Remove(
935 proxy_info_.proxy_server().host_port_pair().ToString());
936 }
937
938 int rv = session_->proxy_service()->ReconsiderProxyAfterError(
939 request_info_.url, &proxy_info_, &io_callback_, &pac_request_,
940 net_log_);
941 if (rv == OK || rv == ERR_IO_PENDING) {
942 // If the error was during connection setup, there is no socket to
943 // disconnect.
944 if (connection_->socket())
945 connection_->socket()->Disconnect();
946 connection_->Reset();
947 if (request_)
948 request_->RemoveRequestFromSpdySessionRequestMap();
949 next_state_ = STATE_RESOLVE_PROXY_COMPLETE;
950 } else {
951 // If ReconsiderProxyAfterError() failed synchronously, it means
952 // there was nothing left to fall-back to, so fail the transaction
953 // with the last connection error we got.
954 // TODO(eroman): This is a confusing contract, make it more obvious.
955 rv = error;
956 }
957
958 return rv;
959 }
960
HandleCertificateError(int error)961 int HttpStreamFactoryImpl::Job::HandleCertificateError(int error) {
962 DCHECK(using_ssl_);
963 DCHECK(IsCertificateError(error));
964
965 SSLClientSocket* ssl_socket =
966 static_cast<SSLClientSocket*>(connection_->socket());
967 ssl_socket->GetSSLInfo(&ssl_info_);
968
969 // Add the bad certificate to the set of allowed certificates in the
970 // SSL config object. This data structure will be consulted after calling
971 // RestartIgnoringLastError(). And the user will be asked interactively
972 // before RestartIgnoringLastError() is ever called.
973 SSLConfig::CertAndStatus bad_cert;
974 bad_cert.cert = ssl_info_.cert;
975 bad_cert.cert_status = ssl_info_.cert_status;
976 ssl_config_.allowed_bad_certs.push_back(bad_cert);
977
978 int load_flags = request_info_.load_flags;
979 if (HttpStreamFactory::ignore_certificate_errors())
980 load_flags |= LOAD_IGNORE_ALL_CERT_ERRORS;
981 if (ssl_socket->IgnoreCertError(error, load_flags))
982 return OK;
983 return error;
984 }
985
SwitchToSpdyMode()986 void HttpStreamFactoryImpl::Job::SwitchToSpdyMode() {
987 if (HttpStreamFactory::spdy_enabled())
988 using_spdy_ = true;
989 }
990
991 // static
LogHttpConnectedMetrics(const ClientSocketHandle & handle)992 void HttpStreamFactoryImpl::Job::LogHttpConnectedMetrics(
993 const ClientSocketHandle& handle) {
994 UMA_HISTOGRAM_ENUMERATION("Net.HttpSocketType", handle.reuse_type(),
995 ClientSocketHandle::NUM_TYPES);
996
997 switch (handle.reuse_type()) {
998 case ClientSocketHandle::UNUSED:
999 UMA_HISTOGRAM_CUSTOM_TIMES("Net.HttpConnectionLatency",
1000 handle.setup_time(),
1001 base::TimeDelta::FromMilliseconds(1),
1002 base::TimeDelta::FromMinutes(10),
1003 100);
1004 break;
1005 case ClientSocketHandle::UNUSED_IDLE:
1006 UMA_HISTOGRAM_CUSTOM_TIMES("Net.SocketIdleTimeBeforeNextUse_UnusedSocket",
1007 handle.idle_time(),
1008 base::TimeDelta::FromMilliseconds(1),
1009 base::TimeDelta::FromMinutes(6),
1010 100);
1011 break;
1012 case ClientSocketHandle::REUSED_IDLE:
1013 UMA_HISTOGRAM_CUSTOM_TIMES("Net.SocketIdleTimeBeforeNextUse_ReusedSocket",
1014 handle.idle_time(),
1015 base::TimeDelta::FromMilliseconds(1),
1016 base::TimeDelta::FromMinutes(6),
1017 100);
1018 break;
1019 default:
1020 NOTREACHED();
1021 break;
1022 }
1023 }
1024
IsPreconnecting() const1025 bool HttpStreamFactoryImpl::Job::IsPreconnecting() const {
1026 DCHECK_GE(num_streams_, 0);
1027 return num_streams_ > 0;
1028 }
1029
IsOrphaned() const1030 bool HttpStreamFactoryImpl::Job::IsOrphaned() const {
1031 return !IsPreconnecting() && !request_;
1032 }
1033
1034 } // namespace net
1035