OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "net/http/http_stream_factory_impl_job.h" | |
6 | |
7 #include <algorithm> | |
8 #include <string> | |
9 | |
10 #include "base/bind.h" | |
11 #include "base/bind_helpers.h" | |
12 #include "base/logging.h" | |
13 #include "base/profiler/scoped_tracker.h" | |
14 #include "base/stl_util.h" | |
15 #include "base/strings/string_util.h" | |
16 #include "base/strings/stringprintf.h" | |
17 #include "base/values.h" | |
18 #include "build/build_config.h" | |
19 #include "net/base/connection_type_histograms.h" | |
20 #include "net/base/net_log.h" | |
21 #include "net/base/net_util.h" | |
22 #include "net/http/http_basic_stream.h" | |
23 #include "net/http/http_network_session.h" | |
24 #include "net/http/http_proxy_client_socket.h" | |
25 #include "net/http/http_proxy_client_socket_pool.h" | |
26 #include "net/http/http_request_info.h" | |
27 #include "net/http/http_server_properties.h" | |
28 #include "net/http/http_stream_factory.h" | |
29 #include "net/http/http_stream_factory_impl_request.h" | |
30 #include "net/quic/quic_http_stream.h" | |
31 #include "net/socket/client_socket_handle.h" | |
32 #include "net/socket/client_socket_pool.h" | |
33 #include "net/socket/client_socket_pool_manager.h" | |
34 #include "net/socket/socks_client_socket_pool.h" | |
35 #include "net/socket/ssl_client_socket.h" | |
36 #include "net/socket/ssl_client_socket_pool.h" | |
37 #include "net/spdy/spdy_http_stream.h" | |
38 #include "net/spdy/spdy_session.h" | |
39 #include "net/spdy/spdy_session_pool.h" | |
40 #include "net/ssl/ssl_cert_request_info.h" | |
41 | |
42 namespace net { | |
43 | |
44 // Returns parameters associated with the start of a HTTP stream job. | |
45 base::Value* NetLogHttpStreamJobCallback(const GURL* original_url, | |
46 const GURL* url, | |
47 RequestPriority priority, | |
48 NetLog::LogLevel /* log_level */) { | |
49 base::DictionaryValue* dict = new base::DictionaryValue(); | |
50 dict->SetString("original_url", original_url->GetOrigin().spec()); | |
51 dict->SetString("url", url->GetOrigin().spec()); | |
52 dict->SetString("priority", RequestPriorityToString(priority)); | |
53 return dict; | |
54 } | |
55 | |
56 // Returns parameters associated with the Proto (with NPN negotiation) of a HTTP | |
57 // stream. | |
58 base::Value* NetLogHttpStreamProtoCallback( | |
59 const SSLClientSocket::NextProtoStatus status, | |
60 const std::string* proto, | |
61 NetLog::LogLevel /* log_level */) { | |
62 base::DictionaryValue* dict = new base::DictionaryValue(); | |
63 | |
64 dict->SetString("next_proto_status", | |
65 SSLClientSocket::NextProtoStatusToString(status)); | |
66 dict->SetString("proto", *proto); | |
67 return dict; | |
68 } | |
69 | |
70 HttpStreamFactoryImpl::Job::Job(HttpStreamFactoryImpl* stream_factory, | |
71 HttpNetworkSession* session, | |
72 const HttpRequestInfo& request_info, | |
73 RequestPriority priority, | |
74 const SSLConfig& server_ssl_config, | |
75 const SSLConfig& proxy_ssl_config, | |
76 NetLog* net_log) | |
77 : request_(NULL), | |
78 request_info_(request_info), | |
79 priority_(priority), | |
80 server_ssl_config_(server_ssl_config), | |
81 proxy_ssl_config_(proxy_ssl_config), | |
82 net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_HTTP_STREAM_JOB)), | |
83 io_callback_(base::Bind(&Job::OnIOComplete, base::Unretained(this))), | |
84 connection_(new ClientSocketHandle), | |
85 session_(session), | |
86 stream_factory_(stream_factory), | |
87 next_state_(STATE_NONE), | |
88 pac_request_(NULL), | |
89 blocking_job_(NULL), | |
90 waiting_job_(NULL), | |
91 using_ssl_(false), | |
92 using_spdy_(false), | |
93 using_quic_(false), | |
94 quic_request_(session_->quic_stream_factory()), | |
95 using_existing_quic_session_(false), | |
96 spdy_certificate_error_(OK), | |
97 establishing_tunnel_(false), | |
98 was_npn_negotiated_(false), | |
99 protocol_negotiated_(kProtoUnknown), | |
100 num_streams_(0), | |
101 spdy_session_direct_(false), | |
102 job_status_(STATUS_RUNNING), | |
103 other_job_status_(STATUS_RUNNING), | |
104 ptr_factory_(this) { | |
105 DCHECK(stream_factory); | |
106 DCHECK(session); | |
107 } | |
108 | |
109 HttpStreamFactoryImpl::Job::~Job() { | |
110 net_log_.EndEvent(NetLog::TYPE_HTTP_STREAM_JOB); | |
111 | |
112 // When we're in a partially constructed state, waiting for the user to | |
113 // provide certificate handling information or authentication, we can't reuse | |
114 // this stream at all. | |
115 if (next_state_ == STATE_WAITING_USER_ACTION) { | |
116 connection_->socket()->Disconnect(); | |
117 connection_.reset(); | |
118 } | |
119 | |
120 if (pac_request_) | |
121 session_->proxy_service()->CancelPacRequest(pac_request_); | |
122 | |
123 // The stream could be in a partial state. It is not reusable. | |
124 if (stream_.get() && next_state_ != STATE_DONE) | |
125 stream_->Close(true /* not reusable */); | |
126 } | |
127 | |
128 void HttpStreamFactoryImpl::Job::Start(Request* request) { | |
129 DCHECK(request); | |
130 request_ = request; | |
131 StartInternal(); | |
132 } | |
133 | |
134 int HttpStreamFactoryImpl::Job::Preconnect(int num_streams) { | |
135 DCHECK_GT(num_streams, 0); | |
136 base::WeakPtr<HttpServerProperties> http_server_properties = | |
137 session_->http_server_properties(); | |
138 if (http_server_properties && | |
139 http_server_properties->SupportsRequestPriority( | |
140 HostPortPair::FromURL(request_info_.url))) { | |
141 num_streams_ = 1; | |
142 } else { | |
143 num_streams_ = num_streams; | |
144 } | |
145 return StartInternal(); | |
146 } | |
147 | |
148 int HttpStreamFactoryImpl::Job::RestartTunnelWithProxyAuth( | |
149 const AuthCredentials& credentials) { | |
150 DCHECK(establishing_tunnel_); | |
151 next_state_ = STATE_RESTART_TUNNEL_AUTH; | |
152 stream_.reset(); | |
153 return RunLoop(OK); | |
154 } | |
155 | |
156 LoadState HttpStreamFactoryImpl::Job::GetLoadState() const { | |
157 switch (next_state_) { | |
158 case STATE_RESOLVE_PROXY_COMPLETE: | |
159 return session_->proxy_service()->GetLoadState(pac_request_); | |
160 case STATE_INIT_CONNECTION_COMPLETE: | |
161 case STATE_CREATE_STREAM_COMPLETE: | |
162 return using_quic_ ? LOAD_STATE_CONNECTING : connection_->GetLoadState(); | |
163 default: | |
164 return LOAD_STATE_IDLE; | |
165 } | |
166 } | |
167 | |
168 void HttpStreamFactoryImpl::Job::MarkAsAlternate( | |
169 const GURL& original_url, | |
170 AlternateProtocolInfo alternate) { | |
171 DCHECK(!original_url_.get()); | |
172 original_url_.reset(new GURL(original_url)); | |
173 alternate_protocol_ = alternate; | |
174 if (alternate.protocol == QUIC) { | |
175 DCHECK(session_->params().enable_quic); | |
176 using_quic_ = true; | |
177 } | |
178 } | |
179 | |
180 void HttpStreamFactoryImpl::Job::WaitFor(Job* job) { | |
181 DCHECK_EQ(STATE_NONE, next_state_); | |
182 DCHECK_EQ(STATE_NONE, job->next_state_); | |
183 DCHECK(!blocking_job_); | |
184 DCHECK(!job->waiting_job_); | |
185 blocking_job_ = job; | |
186 job->waiting_job_ = this; | |
187 } | |
188 | |
189 void HttpStreamFactoryImpl::Job::Resume(Job* job) { | |
190 DCHECK_EQ(blocking_job_, job); | |
191 blocking_job_ = NULL; | |
192 | |
193 // We know we're blocked if the next_state_ is STATE_WAIT_FOR_JOB_COMPLETE. | |
194 // Unblock |this|. | |
195 if (next_state_ == STATE_WAIT_FOR_JOB_COMPLETE) { | |
196 base::MessageLoop::current()->PostTask( | |
197 FROM_HERE, | |
198 base::Bind(&HttpStreamFactoryImpl::Job::OnIOComplete, | |
199 ptr_factory_.GetWeakPtr(), OK)); | |
200 } | |
201 } | |
202 | |
203 void HttpStreamFactoryImpl::Job::Orphan(const Request* request) { | |
204 DCHECK_EQ(request_, request); | |
205 request_ = NULL; | |
206 if (blocking_job_) { | |
207 // We've been orphaned, but there's a job we're blocked on. Don't bother | |
208 // racing, just cancel ourself. | |
209 DCHECK(blocking_job_->waiting_job_); | |
210 blocking_job_->waiting_job_ = NULL; | |
211 blocking_job_ = NULL; | |
212 if (stream_factory_->for_websockets_ && | |
213 connection_ && connection_->socket()) { | |
214 connection_->socket()->Disconnect(); | |
215 } | |
216 stream_factory_->OnOrphanedJobComplete(this); | |
217 } else if (stream_factory_->for_websockets_) { | |
218 // We cancel this job because a WebSocketHandshakeStream can't be created | |
219 // without a WebSocketHandshakeStreamBase::CreateHelper which is stored in | |
220 // the Request class and isn't accessible from this job. | |
221 if (connection_ && connection_->socket()) { | |
222 connection_->socket()->Disconnect(); | |
223 } | |
224 stream_factory_->OnOrphanedJobComplete(this); | |
225 } | |
226 } | |
227 | |
228 void HttpStreamFactoryImpl::Job::SetPriority(RequestPriority priority) { | |
229 priority_ = priority; | |
230 // TODO(akalin): Propagate this to |connection_| and maybe the | |
231 // preconnect state. | |
232 } | |
233 | |
234 bool HttpStreamFactoryImpl::Job::was_npn_negotiated() const { | |
235 return was_npn_negotiated_; | |
236 } | |
237 | |
238 NextProto HttpStreamFactoryImpl::Job::protocol_negotiated() const { | |
239 return protocol_negotiated_; | |
240 } | |
241 | |
242 bool HttpStreamFactoryImpl::Job::using_spdy() const { | |
243 return using_spdy_; | |
244 } | |
245 | |
246 const SSLConfig& HttpStreamFactoryImpl::Job::server_ssl_config() const { | |
247 return server_ssl_config_; | |
248 } | |
249 | |
250 const SSLConfig& HttpStreamFactoryImpl::Job::proxy_ssl_config() const { | |
251 return proxy_ssl_config_; | |
252 } | |
253 | |
254 const ProxyInfo& HttpStreamFactoryImpl::Job::proxy_info() const { | |
255 return proxy_info_; | |
256 } | |
257 | |
258 void HttpStreamFactoryImpl::Job::GetSSLInfo() { | |
259 DCHECK(using_ssl_); | |
260 DCHECK(!establishing_tunnel_); | |
261 DCHECK(connection_.get() && connection_->socket()); | |
262 SSLClientSocket* ssl_socket = | |
263 static_cast<SSLClientSocket*>(connection_->socket()); | |
264 ssl_socket->GetSSLInfo(&ssl_info_); | |
265 } | |
266 | |
267 SpdySessionKey HttpStreamFactoryImpl::Job::GetSpdySessionKey() const { | |
268 // In the case that we're using an HTTPS proxy for an HTTP url, | |
269 // we look for a SPDY session *to* the proxy, instead of to the | |
270 // origin server. | |
271 PrivacyMode privacy_mode = request_info_.privacy_mode; | |
272 if (IsHttpsProxyAndHttpUrl()) { | |
273 return SpdySessionKey(proxy_info_.proxy_server().host_port_pair(), | |
274 ProxyServer::Direct(), | |
275 privacy_mode); | |
276 } else { | |
277 return SpdySessionKey(origin_, | |
278 proxy_info_.proxy_server(), | |
279 privacy_mode); | |
280 } | |
281 } | |
282 | |
283 bool HttpStreamFactoryImpl::Job::CanUseExistingSpdySession() const { | |
284 // We need to make sure that if a spdy session was created for | |
285 // https://somehost/ that we don't use that session for http://somehost:443/. | |
286 // The only time we can use an existing session is if the request URL is | |
287 // https (the normal case) or if we're connection to a SPDY proxy, or | |
288 // if we're running with force_spdy_always_. crbug.com/133176 | |
289 // TODO(ricea): Add "wss" back to this list when SPDY WebSocket support is | |
290 // working. | |
291 return request_info_.url.SchemeIs("https") || | |
292 proxy_info_.proxy_server().is_https() || | |
293 session_->params().force_spdy_always; | |
294 } | |
295 | |
296 void HttpStreamFactoryImpl::Job::OnStreamReadyCallback() { | |
297 DCHECK(stream_.get()); | |
298 DCHECK(!IsPreconnecting()); | |
299 DCHECK(!stream_factory_->for_websockets_); | |
300 if (IsOrphaned()) { | |
301 stream_factory_->OnOrphanedJobComplete(this); | |
302 } else { | |
303 request_->Complete(was_npn_negotiated(), | |
304 protocol_negotiated(), | |
305 using_spdy(), | |
306 net_log_); | |
307 request_->OnStreamReady(this, server_ssl_config_, proxy_info_, | |
308 stream_.release()); | |
309 } | |
310 // |this| may be deleted after this call. | |
311 } | |
312 | |
313 void HttpStreamFactoryImpl::Job::OnWebSocketHandshakeStreamReadyCallback() { | |
314 DCHECK(websocket_stream_); | |
315 DCHECK(!IsPreconnecting()); | |
316 DCHECK(stream_factory_->for_websockets_); | |
317 // An orphaned WebSocket job will be closed immediately and | |
318 // never be ready. | |
319 DCHECK(!IsOrphaned()); | |
320 request_->Complete(was_npn_negotiated(), | |
321 protocol_negotiated(), | |
322 using_spdy(), | |
323 net_log_); | |
324 request_->OnWebSocketHandshakeStreamReady(this, | |
325 server_ssl_config_, | |
326 proxy_info_, | |
327 websocket_stream_.release()); | |
328 // |this| may be deleted after this call. | |
329 } | |
330 | |
331 void HttpStreamFactoryImpl::Job::OnNewSpdySessionReadyCallback() { | |
332 DCHECK(stream_.get()); | |
333 DCHECK(!IsPreconnecting()); | |
334 DCHECK(using_spdy()); | |
335 // Note: an event loop iteration has passed, so |new_spdy_session_| may be | |
336 // NULL at this point if the SpdySession closed immediately after creation. | |
337 base::WeakPtr<SpdySession> spdy_session = new_spdy_session_; | |
338 new_spdy_session_.reset(); | |
339 | |
340 // TODO(jgraettinger): Notify the factory, and let that notify |request_|, | |
341 // rather than notifying |request_| directly. | |
342 if (IsOrphaned()) { | |
343 if (spdy_session) { | |
344 stream_factory_->OnNewSpdySessionReady( | |
345 spdy_session, spdy_session_direct_, server_ssl_config_, proxy_info_, | |
346 was_npn_negotiated(), protocol_negotiated(), using_spdy(), net_log_); | |
347 } | |
348 stream_factory_->OnOrphanedJobComplete(this); | |
349 } else { | |
350 request_->OnNewSpdySessionReady( | |
351 this, stream_.Pass(), spdy_session, spdy_session_direct_); | |
352 } | |
353 // |this| may be deleted after this call. | |
354 } | |
355 | |
356 void HttpStreamFactoryImpl::Job::OnStreamFailedCallback(int result) { | |
357 DCHECK(!IsPreconnecting()); | |
358 if (IsOrphaned()) | |
359 stream_factory_->OnOrphanedJobComplete(this); | |
360 else | |
361 request_->OnStreamFailed(this, result, server_ssl_config_); | |
362 // |this| may be deleted after this call. | |
363 } | |
364 | |
365 void HttpStreamFactoryImpl::Job::OnCertificateErrorCallback( | |
366 int result, const SSLInfo& ssl_info) { | |
367 DCHECK(!IsPreconnecting()); | |
368 if (IsOrphaned()) | |
369 stream_factory_->OnOrphanedJobComplete(this); | |
370 else | |
371 request_->OnCertificateError(this, result, server_ssl_config_, ssl_info); | |
372 // |this| may be deleted after this call. | |
373 } | |
374 | |
375 void HttpStreamFactoryImpl::Job::OnNeedsProxyAuthCallback( | |
376 const HttpResponseInfo& response, | |
377 HttpAuthController* auth_controller) { | |
378 DCHECK(!IsPreconnecting()); | |
379 if (IsOrphaned()) | |
380 stream_factory_->OnOrphanedJobComplete(this); | |
381 else | |
382 request_->OnNeedsProxyAuth( | |
383 this, response, server_ssl_config_, proxy_info_, auth_controller); | |
384 // |this| may be deleted after this call. | |
385 } | |
386 | |
387 void HttpStreamFactoryImpl::Job::OnNeedsClientAuthCallback( | |
388 SSLCertRequestInfo* cert_info) { | |
389 DCHECK(!IsPreconnecting()); | |
390 if (IsOrphaned()) | |
391 stream_factory_->OnOrphanedJobComplete(this); | |
392 else | |
393 request_->OnNeedsClientAuth(this, server_ssl_config_, cert_info); | |
394 // |this| may be deleted after this call. | |
395 } | |
396 | |
397 void HttpStreamFactoryImpl::Job::OnHttpsProxyTunnelResponseCallback( | |
398 const HttpResponseInfo& response_info, | |
399 HttpStream* stream) { | |
400 DCHECK(!IsPreconnecting()); | |
401 if (IsOrphaned()) | |
402 stream_factory_->OnOrphanedJobComplete(this); | |
403 else | |
404 request_->OnHttpsProxyTunnelResponse( | |
405 this, response_info, server_ssl_config_, proxy_info_, stream); | |
406 // |this| may be deleted after this call. | |
407 } | |
408 | |
409 void HttpStreamFactoryImpl::Job::OnPreconnectsComplete() { | |
410 DCHECK(!request_); | |
411 if (new_spdy_session_.get()) { | |
412 stream_factory_->OnNewSpdySessionReady(new_spdy_session_, | |
413 spdy_session_direct_, | |
414 server_ssl_config_, | |
415 proxy_info_, | |
416 was_npn_negotiated(), | |
417 protocol_negotiated(), | |
418 using_spdy(), | |
419 net_log_); | |
420 } | |
421 stream_factory_->OnPreconnectsComplete(this); | |
422 // |this| may be deleted after this call. | |
423 } | |
424 | |
425 // static | |
426 int HttpStreamFactoryImpl::Job::OnHostResolution( | |
427 SpdySessionPool* spdy_session_pool, | |
428 const SpdySessionKey& spdy_session_key, | |
429 const AddressList& addresses, | |
430 const BoundNetLog& net_log) { | |
431 // It is OK to dereference spdy_session_pool, because the | |
432 // ClientSocketPoolManager will be destroyed in the same callback that | |
433 // destroys the SpdySessionPool. | |
434 return | |
435 spdy_session_pool->FindAvailableSession(spdy_session_key, net_log) ? | |
436 ERR_SPDY_SESSION_ALREADY_EXISTS : OK; | |
437 } | |
438 | |
439 void HttpStreamFactoryImpl::Job::OnIOComplete(int result) { | |
440 // TODO(pkasting): Remove ScopedTracker below once crbug.com/455884 is fixed. | |
441 tracked_objects::ScopedTracker tracking_profile( | |
442 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
443 "455884 HttpStreamFactoryImpl::Job::OnIOComplete")); | |
444 RunLoop(result); | |
445 } | |
446 | |
447 int HttpStreamFactoryImpl::Job::RunLoop(int result) { | |
448 result = DoLoop(result); | |
449 | |
450 if (result == ERR_IO_PENDING) | |
451 return result; | |
452 | |
453 // If there was an error, we should have already resumed the |waiting_job_|, | |
454 // if there was one. | |
455 DCHECK(result == OK || waiting_job_ == NULL); | |
456 | |
457 if (IsPreconnecting()) { | |
458 base::MessageLoop::current()->PostTask( | |
459 FROM_HERE, | |
460 base::Bind(&HttpStreamFactoryImpl::Job::OnPreconnectsComplete, | |
461 ptr_factory_.GetWeakPtr())); | |
462 return ERR_IO_PENDING; | |
463 } | |
464 | |
465 if (IsCertificateError(result)) { | |
466 // Retrieve SSL information from the socket. | |
467 GetSSLInfo(); | |
468 | |
469 next_state_ = STATE_WAITING_USER_ACTION; | |
470 base::MessageLoop::current()->PostTask( | |
471 FROM_HERE, | |
472 base::Bind(&HttpStreamFactoryImpl::Job::OnCertificateErrorCallback, | |
473 ptr_factory_.GetWeakPtr(), result, ssl_info_)); | |
474 return ERR_IO_PENDING; | |
475 } | |
476 | |
477 switch (result) { | |
478 case ERR_PROXY_AUTH_REQUESTED: { | |
479 UMA_HISTOGRAM_BOOLEAN("Net.ProxyAuthRequested.HasConnection", | |
480 connection_.get() != NULL); | |
481 if (!connection_.get()) | |
482 return ERR_PROXY_AUTH_REQUESTED_WITH_NO_CONNECTION; | |
483 CHECK(connection_->socket()); | |
484 CHECK(establishing_tunnel_); | |
485 | |
486 next_state_ = STATE_WAITING_USER_ACTION; | |
487 ProxyClientSocket* proxy_socket = | |
488 static_cast<ProxyClientSocket*>(connection_->socket()); | |
489 base::MessageLoop::current()->PostTask( | |
490 FROM_HERE, | |
491 base::Bind(&Job::OnNeedsProxyAuthCallback, ptr_factory_.GetWeakPtr(), | |
492 *proxy_socket->GetConnectResponseInfo(), | |
493 proxy_socket->GetAuthController())); | |
494 return ERR_IO_PENDING; | |
495 } | |
496 | |
497 case ERR_SSL_CLIENT_AUTH_CERT_NEEDED: | |
498 base::MessageLoop::current()->PostTask( | |
499 FROM_HERE, | |
500 base::Bind(&Job::OnNeedsClientAuthCallback, ptr_factory_.GetWeakPtr(), | |
501 connection_->ssl_error_response_info().cert_request_info)); | |
502 return ERR_IO_PENDING; | |
503 | |
504 case ERR_HTTPS_PROXY_TUNNEL_RESPONSE: { | |
505 DCHECK(connection_.get()); | |
506 DCHECK(connection_->socket()); | |
507 DCHECK(establishing_tunnel_); | |
508 | |
509 ProxyClientSocket* proxy_socket = | |
510 static_cast<ProxyClientSocket*>(connection_->socket()); | |
511 base::MessageLoop::current()->PostTask( | |
512 FROM_HERE, | |
513 base::Bind(&Job::OnHttpsProxyTunnelResponseCallback, | |
514 ptr_factory_.GetWeakPtr(), | |
515 *proxy_socket->GetConnectResponseInfo(), | |
516 proxy_socket->CreateConnectResponseStream())); | |
517 return ERR_IO_PENDING; | |
518 } | |
519 | |
520 case OK: | |
521 job_status_ = STATUS_SUCCEEDED; | |
522 MaybeMarkAlternateProtocolBroken(); | |
523 next_state_ = STATE_DONE; | |
524 if (new_spdy_session_.get()) { | |
525 base::MessageLoop::current()->PostTask( | |
526 FROM_HERE, | |
527 base::Bind(&Job::OnNewSpdySessionReadyCallback, | |
528 ptr_factory_.GetWeakPtr())); | |
529 } else if (stream_factory_->for_websockets_) { | |
530 DCHECK(websocket_stream_); | |
531 base::MessageLoop::current()->PostTask( | |
532 FROM_HERE, | |
533 base::Bind(&Job::OnWebSocketHandshakeStreamReadyCallback, | |
534 ptr_factory_.GetWeakPtr())); | |
535 } else { | |
536 DCHECK(stream_.get()); | |
537 base::MessageLoop::current()->PostTask( | |
538 FROM_HERE, | |
539 base::Bind(&Job::OnStreamReadyCallback, ptr_factory_.GetWeakPtr())); | |
540 } | |
541 return ERR_IO_PENDING; | |
542 | |
543 default: | |
544 if (job_status_ != STATUS_BROKEN) { | |
545 DCHECK_EQ(STATUS_RUNNING, job_status_); | |
546 job_status_ = STATUS_FAILED; | |
547 MaybeMarkAlternateProtocolBroken(); | |
548 } | |
549 base::MessageLoop::current()->PostTask( | |
550 FROM_HERE, | |
551 base::Bind(&Job::OnStreamFailedCallback, ptr_factory_.GetWeakPtr(), | |
552 result)); | |
553 return ERR_IO_PENDING; | |
554 } | |
555 } | |
556 | |
557 int HttpStreamFactoryImpl::Job::DoLoop(int result) { | |
558 DCHECK_NE(next_state_, STATE_NONE); | |
559 int rv = result; | |
560 do { | |
561 State state = next_state_; | |
562 next_state_ = STATE_NONE; | |
563 switch (state) { | |
564 case STATE_START: | |
565 DCHECK_EQ(OK, rv); | |
566 rv = DoStart(); | |
567 break; | |
568 case STATE_RESOLVE_PROXY: | |
569 DCHECK_EQ(OK, rv); | |
570 rv = DoResolveProxy(); | |
571 break; | |
572 case STATE_RESOLVE_PROXY_COMPLETE: | |
573 rv = DoResolveProxyComplete(rv); | |
574 break; | |
575 case STATE_WAIT_FOR_JOB: | |
576 DCHECK_EQ(OK, rv); | |
577 rv = DoWaitForJob(); | |
578 break; | |
579 case STATE_WAIT_FOR_JOB_COMPLETE: | |
580 rv = DoWaitForJobComplete(rv); | |
581 break; | |
582 case STATE_INIT_CONNECTION: | |
583 DCHECK_EQ(OK, rv); | |
584 rv = DoInitConnection(); | |
585 break; | |
586 case STATE_INIT_CONNECTION_COMPLETE: | |
587 rv = DoInitConnectionComplete(rv); | |
588 break; | |
589 case STATE_WAITING_USER_ACTION: | |
590 rv = DoWaitingUserAction(rv); | |
591 break; | |
592 case STATE_RESTART_TUNNEL_AUTH: | |
593 DCHECK_EQ(OK, rv); | |
594 rv = DoRestartTunnelAuth(); | |
595 break; | |
596 case STATE_RESTART_TUNNEL_AUTH_COMPLETE: | |
597 rv = DoRestartTunnelAuthComplete(rv); | |
598 break; | |
599 case STATE_CREATE_STREAM: | |
600 DCHECK_EQ(OK, rv); | |
601 rv = DoCreateStream(); | |
602 break; | |
603 case STATE_CREATE_STREAM_COMPLETE: | |
604 rv = DoCreateStreamComplete(rv); | |
605 break; | |
606 default: | |
607 NOTREACHED() << "bad state"; | |
608 rv = ERR_FAILED; | |
609 break; | |
610 } | |
611 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); | |
612 return rv; | |
613 } | |
614 | |
615 int HttpStreamFactoryImpl::Job::StartInternal() { | |
616 CHECK_EQ(STATE_NONE, next_state_); | |
617 next_state_ = STATE_START; | |
618 int rv = RunLoop(OK); | |
619 DCHECK_EQ(ERR_IO_PENDING, rv); | |
620 return rv; | |
621 } | |
622 | |
623 int HttpStreamFactoryImpl::Job::DoStart() { | |
624 origin_ = HostPortPair::FromURL(request_info_.url); | |
625 origin_url_ = stream_factory_->ApplyHostMappingRules( | |
626 request_info_.url, &origin_); | |
627 | |
628 net_log_.BeginEvent(NetLog::TYPE_HTTP_STREAM_JOB, | |
629 base::Bind(&NetLogHttpStreamJobCallback, | |
630 &request_info_.url, &origin_url_, | |
631 priority_)); | |
632 | |
633 // Don't connect to restricted ports. | |
634 bool is_port_allowed = IsPortAllowedByDefault(origin_.port()); | |
635 if (request_info_.url.SchemeIs("ftp")) { | |
636 // Never share connection with other jobs for FTP requests. | |
637 DCHECK(!waiting_job_); | |
638 | |
639 is_port_allowed = IsPortAllowedByFtp(origin_.port()); | |
640 } | |
641 if (!is_port_allowed && !IsPortAllowedByOverride(origin_.port())) { | |
642 if (waiting_job_) { | |
643 waiting_job_->Resume(this); | |
644 waiting_job_ = NULL; | |
645 } | |
646 return ERR_UNSAFE_PORT; | |
647 } | |
648 | |
649 next_state_ = STATE_RESOLVE_PROXY; | |
650 return OK; | |
651 } | |
652 | |
653 int HttpStreamFactoryImpl::Job::DoResolveProxy() { | |
654 DCHECK(!pac_request_); | |
655 DCHECK(session_); | |
656 | |
657 next_state_ = STATE_RESOLVE_PROXY_COMPLETE; | |
658 | |
659 if (request_info_.load_flags & LOAD_BYPASS_PROXY) { | |
660 proxy_info_.UseDirect(); | |
661 return OK; | |
662 } | |
663 | |
664 return session_->proxy_service()->ResolveProxy( | |
665 request_info_.url, request_info_.load_flags, &proxy_info_, io_callback_, | |
666 &pac_request_, session_->network_delegate(), net_log_); | |
667 } | |
668 | |
669 int HttpStreamFactoryImpl::Job::DoResolveProxyComplete(int result) { | |
670 pac_request_ = NULL; | |
671 | |
672 if (result == OK) { | |
673 // Remove unsupported proxies from the list. | |
674 proxy_info_.RemoveProxiesWithoutScheme( | |
675 ProxyServer::SCHEME_DIRECT | ProxyServer::SCHEME_QUIC | | |
676 ProxyServer::SCHEME_HTTP | ProxyServer::SCHEME_HTTPS | | |
677 ProxyServer::SCHEME_SOCKS4 | ProxyServer::SCHEME_SOCKS5); | |
678 | |
679 if (proxy_info_.is_empty()) { | |
680 // No proxies/direct to choose from. This happens when we don't support | |
681 // any of the proxies in the returned list. | |
682 result = ERR_NO_SUPPORTED_PROXIES; | |
683 } else if (using_quic_ && | |
684 (!proxy_info_.is_quic() && !proxy_info_.is_direct())) { | |
685 // QUIC can not be spoken to non-QUIC proxies. This error should not be | |
686 // user visible, because the non-alternate job should be resumed. | |
687 result = ERR_NO_SUPPORTED_PROXIES; | |
688 } | |
689 } | |
690 | |
691 if (result != OK) { | |
692 if (waiting_job_) { | |
693 waiting_job_->Resume(this); | |
694 waiting_job_ = NULL; | |
695 } | |
696 return result; | |
697 } | |
698 | |
699 if (blocking_job_) | |
700 next_state_ = STATE_WAIT_FOR_JOB; | |
701 else | |
702 next_state_ = STATE_INIT_CONNECTION; | |
703 return OK; | |
704 } | |
705 | |
706 bool HttpStreamFactoryImpl::Job::ShouldForceSpdySSL() const { | |
707 bool rv = session_->params().force_spdy_always && | |
708 session_->params().force_spdy_over_ssl; | |
709 return rv && !session_->HasSpdyExclusion(origin_); | |
710 } | |
711 | |
712 bool HttpStreamFactoryImpl::Job::ShouldForceSpdyWithoutSSL() const { | |
713 bool rv = session_->params().force_spdy_always && | |
714 !session_->params().force_spdy_over_ssl; | |
715 return rv && !session_->HasSpdyExclusion(origin_); | |
716 } | |
717 | |
718 bool HttpStreamFactoryImpl::Job::ShouldForceQuic() const { | |
719 return session_->params().enable_quic && | |
720 session_->params().origin_to_force_quic_on.Equals(origin_) && | |
721 proxy_info_.is_direct(); | |
722 } | |
723 | |
724 int HttpStreamFactoryImpl::Job::DoWaitForJob() { | |
725 DCHECK(blocking_job_); | |
726 next_state_ = STATE_WAIT_FOR_JOB_COMPLETE; | |
727 return ERR_IO_PENDING; | |
728 } | |
729 | |
730 int HttpStreamFactoryImpl::Job::DoWaitForJobComplete(int result) { | |
731 DCHECK(!blocking_job_); | |
732 DCHECK_EQ(OK, result); | |
733 next_state_ = STATE_INIT_CONNECTION; | |
734 return OK; | |
735 } | |
736 | |
737 int HttpStreamFactoryImpl::Job::DoInitConnection() { | |
738 DCHECK(!blocking_job_); | |
739 DCHECK(!connection_->is_initialized()); | |
740 DCHECK(proxy_info_.proxy_server().is_valid()); | |
741 next_state_ = STATE_INIT_CONNECTION_COMPLETE; | |
742 | |
743 using_ssl_ = request_info_.url.SchemeIs("https") || | |
744 request_info_.url.SchemeIs("wss") || ShouldForceSpdySSL(); | |
745 using_spdy_ = false; | |
746 | |
747 if (ShouldForceQuic()) | |
748 using_quic_ = true; | |
749 | |
750 if (proxy_info_.is_quic()) | |
751 using_quic_ = true; | |
752 | |
753 if (using_quic_) { | |
754 DCHECK(session_->params().enable_quic); | |
755 if (proxy_info_.is_quic() && !request_info_.url.SchemeIs("http")) { | |
756 NOTREACHED(); | |
757 // TODO(rch): support QUIC proxies for HTTPS urls. | |
758 return ERR_NOT_IMPLEMENTED; | |
759 } | |
760 HostPortPair destination = proxy_info_.is_quic() ? | |
761 proxy_info_.proxy_server().host_port_pair() : origin_; | |
762 next_state_ = STATE_INIT_CONNECTION_COMPLETE; | |
763 bool secure_quic = using_ssl_ || proxy_info_.is_quic(); | |
764 int rv = quic_request_.Request( | |
765 destination, secure_quic, request_info_.privacy_mode, | |
766 request_info_.method, net_log_, io_callback_); | |
767 if (rv == OK) { | |
768 using_existing_quic_session_ = true; | |
769 } else { | |
770 // OK, there's no available QUIC session. Let |waiting_job_| resume | |
771 // if it's paused. | |
772 if (waiting_job_) { | |
773 waiting_job_->Resume(this); | |
774 waiting_job_ = NULL; | |
775 } | |
776 } | |
777 return rv; | |
778 } | |
779 | |
780 // Check first if we have a spdy session for this group. If so, then go | |
781 // straight to using that. | |
782 SpdySessionKey spdy_session_key = GetSpdySessionKey(); | |
783 base::WeakPtr<SpdySession> spdy_session = | |
784 session_->spdy_session_pool()->FindAvailableSession( | |
785 spdy_session_key, net_log_); | |
786 if (spdy_session && CanUseExistingSpdySession()) { | |
787 // If we're preconnecting, but we already have a SpdySession, we don't | |
788 // actually need to preconnect any sockets, so we're done. | |
789 if (IsPreconnecting()) | |
790 return OK; | |
791 using_spdy_ = true; | |
792 next_state_ = STATE_CREATE_STREAM; | |
793 existing_spdy_session_ = spdy_session; | |
794 return OK; | |
795 } else if (request_ && !request_->HasSpdySessionKey() && | |
796 (using_ssl_ || ShouldForceSpdyWithoutSSL())) { | |
797 // Update the spdy session key for the request that launched this job. | |
798 request_->SetSpdySessionKey(spdy_session_key); | |
799 } | |
800 | |
801 // OK, there's no available SPDY session. Let |waiting_job_| resume if it's | |
802 // paused. | |
803 | |
804 if (waiting_job_) { | |
805 waiting_job_->Resume(this); | |
806 waiting_job_ = NULL; | |
807 } | |
808 | |
809 if (proxy_info_.is_http() || proxy_info_.is_https()) | |
810 establishing_tunnel_ = using_ssl_; | |
811 | |
812 bool want_spdy_over_npn = original_url_ != NULL; | |
813 | |
814 if (proxy_info_.is_https()) { | |
815 InitSSLConfig(proxy_info_.proxy_server().host_port_pair(), | |
816 &proxy_ssl_config_, | |
817 true /* is a proxy server */); | |
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; | |
821 } | |
822 if (using_ssl_) { | |
823 InitSSLConfig(origin_, &server_ssl_config_, | |
824 false /* not a proxy server */); | |
825 } | |
826 | |
827 base::WeakPtr<HttpServerProperties> http_server_properties = | |
828 session_->http_server_properties(); | |
829 if (http_server_properties) { | |
830 http_server_properties->MaybeForceHTTP11(origin_, &server_ssl_config_); | |
831 if (proxy_info_.is_http() || proxy_info_.is_https()) { | |
832 http_server_properties->MaybeForceHTTP11( | |
833 proxy_info_.proxy_server().host_port_pair(), &proxy_ssl_config_); | |
834 } | |
835 } | |
836 | |
837 if (IsPreconnecting()) { | |
838 DCHECK(!stream_factory_->for_websockets_); | |
839 return PreconnectSocketsForHttpRequest( | |
840 origin_url_, | |
841 request_info_.extra_headers, | |
842 request_info_.load_flags, | |
843 priority_, | |
844 session_, | |
845 proxy_info_, | |
846 ShouldForceSpdySSL(), | |
847 want_spdy_over_npn, | |
848 server_ssl_config_, | |
849 proxy_ssl_config_, | |
850 request_info_.privacy_mode, | |
851 net_log_, | |
852 num_streams_); | |
853 } | |
854 | |
855 // If we can't use a SPDY session, don't both checking for one after | |
856 // the hostname is resolved. | |
857 OnHostResolutionCallback resolution_callback = CanUseExistingSpdySession() ? | |
858 base::Bind(&Job::OnHostResolution, session_->spdy_session_pool(), | |
859 GetSpdySessionKey()) : | |
860 OnHostResolutionCallback(); | |
861 if (stream_factory_->for_websockets_) { | |
862 // TODO(ricea): Re-enable NPN when WebSockets over SPDY is supported. | |
863 SSLConfig websocket_server_ssl_config = server_ssl_config_; | |
864 websocket_server_ssl_config.next_protos.clear(); | |
865 return InitSocketHandleForWebSocketRequest( | |
866 origin_url_, request_info_.extra_headers, request_info_.load_flags, | |
867 priority_, session_, proxy_info_, ShouldForceSpdySSL(), | |
868 want_spdy_over_npn, websocket_server_ssl_config, proxy_ssl_config_, | |
869 request_info_.privacy_mode, net_log_, | |
870 connection_.get(), resolution_callback, io_callback_); | |
871 } | |
872 | |
873 return InitSocketHandleForHttpRequest( | |
874 origin_url_, request_info_.extra_headers, request_info_.load_flags, | |
875 priority_, session_, proxy_info_, ShouldForceSpdySSL(), | |
876 want_spdy_over_npn, server_ssl_config_, proxy_ssl_config_, | |
877 request_info_.privacy_mode, net_log_, | |
878 connection_.get(), resolution_callback, io_callback_); | |
879 } | |
880 | |
881 int HttpStreamFactoryImpl::Job::DoInitConnectionComplete(int result) { | |
882 if (IsPreconnecting()) { | |
883 if (using_quic_) | |
884 return result; | |
885 DCHECK_EQ(OK, result); | |
886 return OK; | |
887 } | |
888 | |
889 if (result == ERR_SPDY_SESSION_ALREADY_EXISTS) { | |
890 // We found a SPDY connection after resolving the host. This is | |
891 // probably an IP pooled connection. | |
892 SpdySessionKey spdy_session_key = GetSpdySessionKey(); | |
893 existing_spdy_session_ = | |
894 session_->spdy_session_pool()->FindAvailableSession( | |
895 spdy_session_key, net_log_); | |
896 if (existing_spdy_session_) { | |
897 using_spdy_ = true; | |
898 next_state_ = STATE_CREATE_STREAM; | |
899 } else { | |
900 // It is possible that the spdy session no longer exists. | |
901 ReturnToStateInitConnection(true /* close connection */); | |
902 } | |
903 return OK; | |
904 } | |
905 | |
906 // TODO(willchan): Make this a bit more exact. Maybe there are recoverable | |
907 // errors, such as ignoring certificate errors for Alternate-Protocol. | |
908 if (result < 0 && waiting_job_) { | |
909 waiting_job_->Resume(this); | |
910 waiting_job_ = NULL; | |
911 } | |
912 | |
913 // |result| may be the result of any of the stacked pools. The following | |
914 // logic is used when determining how to interpret an error. | |
915 // If |result| < 0: | |
916 // and connection_->socket() != NULL, then the SSL handshake ran and it | |
917 // is a potentially recoverable error. | |
918 // and connection_->socket == NULL and connection_->is_ssl_error() is true, | |
919 // then the SSL handshake ran with an unrecoverable error. | |
920 // otherwise, the error came from one of the other pools. | |
921 bool ssl_started = using_ssl_ && (result == OK || connection_->socket() || | |
922 connection_->is_ssl_error()); | |
923 | |
924 if (ssl_started && (result == OK || IsCertificateError(result))) { | |
925 if (using_quic_ && result == OK) { | |
926 was_npn_negotiated_ = true; | |
927 NextProto protocol_negotiated = | |
928 SSLClientSocket::NextProtoFromString("quic/1+spdy/3"); | |
929 protocol_negotiated_ = protocol_negotiated; | |
930 } else { | |
931 SSLClientSocket* ssl_socket = | |
932 static_cast<SSLClientSocket*>(connection_->socket()); | |
933 if (ssl_socket->WasNpnNegotiated()) { | |
934 was_npn_negotiated_ = true; | |
935 std::string proto; | |
936 SSLClientSocket::NextProtoStatus status = | |
937 ssl_socket->GetNextProto(&proto); | |
938 NextProto protocol_negotiated = | |
939 SSLClientSocket::NextProtoFromString(proto); | |
940 protocol_negotiated_ = protocol_negotiated; | |
941 net_log_.AddEvent( | |
942 NetLog::TYPE_HTTP_STREAM_REQUEST_PROTO, | |
943 base::Bind(&NetLogHttpStreamProtoCallback, | |
944 status, &proto)); | |
945 if (ssl_socket->was_spdy_negotiated()) | |
946 SwitchToSpdyMode(); | |
947 } | |
948 if (ShouldForceSpdySSL()) | |
949 SwitchToSpdyMode(); | |
950 } | |
951 } else if (proxy_info_.is_https() && connection_->socket() && | |
952 result == OK) { | |
953 ProxyClientSocket* proxy_socket = | |
954 static_cast<ProxyClientSocket*>(connection_->socket()); | |
955 if (proxy_socket->IsUsingSpdy()) { | |
956 was_npn_negotiated_ = true; | |
957 protocol_negotiated_ = proxy_socket->GetProtocolNegotiated(); | |
958 SwitchToSpdyMode(); | |
959 } | |
960 } | |
961 | |
962 // We may be using spdy without SSL | |
963 if (ShouldForceSpdyWithoutSSL()) | |
964 SwitchToSpdyMode(); | |
965 | |
966 if (result == ERR_PROXY_AUTH_REQUESTED || | |
967 result == ERR_HTTPS_PROXY_TUNNEL_RESPONSE) { | |
968 DCHECK(!ssl_started); | |
969 // Other state (i.e. |using_ssl_|) suggests that |connection_| will have an | |
970 // SSL socket, but there was an error before that could happen. This | |
971 // puts the in progress HttpProxy socket into |connection_| in order to | |
972 // complete the auth (or read the response body). The tunnel restart code | |
973 // is careful to remove it before returning control to the rest of this | |
974 // class. | |
975 connection_.reset(connection_->release_pending_http_proxy_connection()); | |
976 return result; | |
977 } | |
978 | |
979 if (!ssl_started && result < 0 && original_url_.get()) { | |
980 job_status_ = STATUS_BROKEN; | |
981 MaybeMarkAlternateProtocolBroken(); | |
982 return result; | |
983 } | |
984 | |
985 if (using_quic_) { | |
986 if (result < 0) { | |
987 job_status_ = STATUS_BROKEN; | |
988 MaybeMarkAlternateProtocolBroken(); | |
989 return result; | |
990 } | |
991 stream_ = quic_request_.ReleaseStream(); | |
992 next_state_ = STATE_NONE; | |
993 return OK; | |
994 } | |
995 | |
996 if (result < 0 && !ssl_started) | |
997 return ReconsiderProxyAfterError(result); | |
998 establishing_tunnel_ = false; | |
999 | |
1000 if (connection_->socket()) { | |
1001 LogHttpConnectedMetrics(*connection_); | |
1002 | |
1003 // We officially have a new connection. Record the type. | |
1004 if (!connection_->is_reused()) { | |
1005 ConnectionType type = using_spdy_ ? CONNECTION_SPDY : CONNECTION_HTTP; | |
1006 UpdateConnectionTypeHistograms(type); | |
1007 } | |
1008 } | |
1009 | |
1010 // Handle SSL errors below. | |
1011 if (using_ssl_) { | |
1012 DCHECK(ssl_started); | |
1013 if (IsCertificateError(result)) { | |
1014 if (using_spdy_ && original_url_.get() && | |
1015 original_url_->SchemeIs("http")) { | |
1016 // We ignore certificate errors for http over spdy. | |
1017 spdy_certificate_error_ = result; | |
1018 result = OK; | |
1019 } else { | |
1020 result = HandleCertificateError(result); | |
1021 if (result == OK && !connection_->socket()->IsConnectedAndIdle()) { | |
1022 ReturnToStateInitConnection(true /* close connection */); | |
1023 return result; | |
1024 } | |
1025 } | |
1026 } | |
1027 if (result < 0) | |
1028 return result; | |
1029 } | |
1030 | |
1031 next_state_ = STATE_CREATE_STREAM; | |
1032 return OK; | |
1033 } | |
1034 | |
1035 int HttpStreamFactoryImpl::Job::DoWaitingUserAction(int result) { | |
1036 // This state indicates that the stream request is in a partially | |
1037 // completed state, and we've called back to the delegate for more | |
1038 // information. | |
1039 | |
1040 // We're always waiting here for the delegate to call us back. | |
1041 return ERR_IO_PENDING; | |
1042 } | |
1043 | |
1044 int HttpStreamFactoryImpl::Job::SetSpdyHttpStream( | |
1045 base::WeakPtr<SpdySession> session, bool direct) { | |
1046 // TODO(ricea): Restore the code for WebSockets over SPDY once it's | |
1047 // implemented. | |
1048 if (stream_factory_->for_websockets_) | |
1049 return ERR_NOT_IMPLEMENTED; | |
1050 | |
1051 // TODO(willchan): Delete this code, because eventually, the | |
1052 // HttpStreamFactoryImpl will be creating all the SpdyHttpStreams, since it | |
1053 // will know when SpdySessions become available. | |
1054 | |
1055 bool use_relative_url = direct || request_info_.url.SchemeIs("https"); | |
1056 stream_.reset(new SpdyHttpStream(session, use_relative_url)); | |
1057 return OK; | |
1058 } | |
1059 | |
1060 int HttpStreamFactoryImpl::Job::DoCreateStream() { | |
1061 DCHECK(connection_->socket() || existing_spdy_session_.get() || using_quic_); | |
1062 | |
1063 next_state_ = STATE_CREATE_STREAM_COMPLETE; | |
1064 | |
1065 // We only set the socket motivation if we're the first to use | |
1066 // this socket. Is there a race for two SPDY requests? We really | |
1067 // need to plumb this through to the connect level. | |
1068 if (connection_->socket() && !connection_->is_reused()) | |
1069 SetSocketMotivation(); | |
1070 | |
1071 if (!using_spdy_) { | |
1072 // We may get ftp scheme when fetching ftp resources through proxy. | |
1073 bool using_proxy = (proxy_info_.is_http() || proxy_info_.is_https()) && | |
1074 (request_info_.url.SchemeIs("http") || | |
1075 request_info_.url.SchemeIs("ftp")); | |
1076 if (stream_factory_->for_websockets_) { | |
1077 DCHECK(request_); | |
1078 DCHECK(request_->websocket_handshake_stream_create_helper()); | |
1079 websocket_stream_.reset( | |
1080 request_->websocket_handshake_stream_create_helper() | |
1081 ->CreateBasicStream(connection_.Pass(), using_proxy)); | |
1082 } else { | |
1083 stream_.reset(new HttpBasicStream(connection_.release(), using_proxy)); | |
1084 } | |
1085 return OK; | |
1086 } | |
1087 | |
1088 CHECK(!stream_.get()); | |
1089 | |
1090 bool direct = true; | |
1091 const ProxyServer& proxy_server = proxy_info_.proxy_server(); | |
1092 PrivacyMode privacy_mode = request_info_.privacy_mode; | |
1093 if (IsHttpsProxyAndHttpUrl()) | |
1094 direct = false; | |
1095 | |
1096 if (existing_spdy_session_.get()) { | |
1097 // We picked up an existing session, so we don't need our socket. | |
1098 if (connection_->socket()) | |
1099 connection_->socket()->Disconnect(); | |
1100 connection_->Reset(); | |
1101 | |
1102 int set_result = SetSpdyHttpStream(existing_spdy_session_, direct); | |
1103 existing_spdy_session_.reset(); | |
1104 return set_result; | |
1105 } | |
1106 | |
1107 SpdySessionKey spdy_session_key(origin_, proxy_server, privacy_mode); | |
1108 if (IsHttpsProxyAndHttpUrl()) { | |
1109 // If we don't have a direct SPDY session, and we're using an HTTPS | |
1110 // proxy, then we might have a SPDY session to the proxy. | |
1111 // We never use privacy mode for connection to proxy server. | |
1112 spdy_session_key = SpdySessionKey(proxy_server.host_port_pair(), | |
1113 ProxyServer::Direct(), | |
1114 PRIVACY_MODE_DISABLED); | |
1115 } | |
1116 | |
1117 SpdySessionPool* spdy_pool = session_->spdy_session_pool(); | |
1118 base::WeakPtr<SpdySession> spdy_session = | |
1119 spdy_pool->FindAvailableSession(spdy_session_key, net_log_); | |
1120 | |
1121 if (spdy_session) { | |
1122 return SetSpdyHttpStream(spdy_session, direct); | |
1123 } | |
1124 | |
1125 spdy_session = | |
1126 spdy_pool->CreateAvailableSessionFromSocket(spdy_session_key, | |
1127 connection_.Pass(), | |
1128 net_log_, | |
1129 spdy_certificate_error_, | |
1130 using_ssl_); | |
1131 if (!spdy_session->HasAcceptableTransportSecurity()) { | |
1132 spdy_session->CloseSessionOnError( | |
1133 ERR_SPDY_INADEQUATE_TRANSPORT_SECURITY, ""); | |
1134 return ERR_SPDY_INADEQUATE_TRANSPORT_SECURITY; | |
1135 } | |
1136 | |
1137 new_spdy_session_ = spdy_session; | |
1138 spdy_session_direct_ = direct; | |
1139 const HostPortPair& host_port_pair = spdy_session_key.host_port_pair(); | |
1140 base::WeakPtr<HttpServerProperties> http_server_properties = | |
1141 session_->http_server_properties(); | |
1142 if (http_server_properties) | |
1143 http_server_properties->SetSupportsSpdy(host_port_pair, true); | |
1144 | |
1145 // Create a SpdyHttpStream attached to the session; | |
1146 // OnNewSpdySessionReadyCallback is not called until an event loop | |
1147 // iteration later, so if the SpdySession is closed between then, allow | |
1148 // reuse state from the underlying socket, sampled by SpdyHttpStream, | |
1149 // bubble up to the request. | |
1150 return SetSpdyHttpStream(new_spdy_session_, spdy_session_direct_); | |
1151 } | |
1152 | |
1153 int HttpStreamFactoryImpl::Job::DoCreateStreamComplete(int result) { | |
1154 if (result < 0) | |
1155 return result; | |
1156 | |
1157 session_->proxy_service()->ReportSuccess(proxy_info_, | |
1158 session_->network_delegate()); | |
1159 next_state_ = STATE_NONE; | |
1160 return OK; | |
1161 } | |
1162 | |
1163 int HttpStreamFactoryImpl::Job::DoRestartTunnelAuth() { | |
1164 next_state_ = STATE_RESTART_TUNNEL_AUTH_COMPLETE; | |
1165 ProxyClientSocket* proxy_socket = | |
1166 static_cast<ProxyClientSocket*>(connection_->socket()); | |
1167 return proxy_socket->RestartWithAuth(io_callback_); | |
1168 } | |
1169 | |
1170 int HttpStreamFactoryImpl::Job::DoRestartTunnelAuthComplete(int result) { | |
1171 if (result == ERR_PROXY_AUTH_REQUESTED) | |
1172 return result; | |
1173 | |
1174 if (result == OK) { | |
1175 // Now that we've got the HttpProxyClientSocket connected. We have | |
1176 // to release it as an idle socket into the pool and start the connection | |
1177 // process from the beginning. Trying to pass it in with the | |
1178 // SSLSocketParams might cause a deadlock since params are dispatched | |
1179 // interchangeably. This request won't necessarily get this http proxy | |
1180 // socket, but there will be forward progress. | |
1181 establishing_tunnel_ = false; | |
1182 ReturnToStateInitConnection(false /* do not close connection */); | |
1183 return OK; | |
1184 } | |
1185 | |
1186 return ReconsiderProxyAfterError(result); | |
1187 } | |
1188 | |
1189 void HttpStreamFactoryImpl::Job::ReturnToStateInitConnection( | |
1190 bool close_connection) { | |
1191 if (close_connection && connection_->socket()) | |
1192 connection_->socket()->Disconnect(); | |
1193 connection_->Reset(); | |
1194 | |
1195 if (request_) | |
1196 request_->RemoveRequestFromSpdySessionRequestMap(); | |
1197 | |
1198 next_state_ = STATE_INIT_CONNECTION; | |
1199 } | |
1200 | |
1201 void HttpStreamFactoryImpl::Job::SetSocketMotivation() { | |
1202 if (request_info_.motivation == HttpRequestInfo::PRECONNECT_MOTIVATED) | |
1203 connection_->socket()->SetSubresourceSpeculation(); | |
1204 else if (request_info_.motivation == HttpRequestInfo::OMNIBOX_MOTIVATED) | |
1205 connection_->socket()->SetOmniboxSpeculation(); | |
1206 // TODO(mbelshe): Add other motivations (like EARLY_LOAD_MOTIVATED). | |
1207 } | |
1208 | |
1209 bool HttpStreamFactoryImpl::Job::IsHttpsProxyAndHttpUrl() const { | |
1210 if (!proxy_info_.is_https()) | |
1211 return false; | |
1212 if (original_url_.get()) { | |
1213 // We currently only support Alternate-Protocol where the original scheme | |
1214 // is http. | |
1215 DCHECK(original_url_->SchemeIs("http")); | |
1216 return original_url_->SchemeIs("http"); | |
1217 } | |
1218 return request_info_.url.SchemeIs("http"); | |
1219 } | |
1220 | |
1221 // Sets several fields of ssl_config for the given origin_server based on the | |
1222 // proxy info and other factors. | |
1223 void HttpStreamFactoryImpl::Job::InitSSLConfig( | |
1224 const HostPortPair& origin_server, | |
1225 SSLConfig* ssl_config, | |
1226 bool is_proxy) const { | |
1227 if (proxy_info_.is_https() && ssl_config->send_client_cert) { | |
1228 // When connecting through an HTTPS proxy, disable TLS False Start so | |
1229 // that client authentication errors can be distinguished between those | |
1230 // originating from the proxy server (ERR_PROXY_CONNECTION_FAILED) and | |
1231 // those originating from the endpoint (ERR_SSL_PROTOCOL_ERROR / | |
1232 // ERR_BAD_SSL_CLIENT_AUTH_CERT). | |
1233 // TODO(rch): This assumes that the HTTPS proxy will only request a | |
1234 // client certificate during the initial handshake. | |
1235 // http://crbug.com/59292 | |
1236 ssl_config->false_start_enabled = false; | |
1237 } | |
1238 | |
1239 enum { | |
1240 FALLBACK_NONE = 0, // SSL version fallback did not occur. | |
1241 FALLBACK_SSL3 = 1, // Fell back to SSL 3.0. | |
1242 FALLBACK_TLS1 = 2, // Fell back to TLS 1.0. | |
1243 FALLBACK_TLS1_1 = 3, // Fell back to TLS 1.1. | |
1244 FALLBACK_MAX | |
1245 }; | |
1246 | |
1247 int fallback = FALLBACK_NONE; | |
1248 if (ssl_config->version_fallback) { | |
1249 switch (ssl_config->version_max) { | |
1250 case SSL_PROTOCOL_VERSION_SSL3: | |
1251 fallback = FALLBACK_SSL3; | |
1252 break; | |
1253 case SSL_PROTOCOL_VERSION_TLS1: | |
1254 fallback = FALLBACK_TLS1; | |
1255 break; | |
1256 case SSL_PROTOCOL_VERSION_TLS1_1: | |
1257 fallback = FALLBACK_TLS1_1; | |
1258 break; | |
1259 } | |
1260 } | |
1261 UMA_HISTOGRAM_ENUMERATION("Net.ConnectionUsedSSLVersionFallback", | |
1262 fallback, FALLBACK_MAX); | |
1263 | |
1264 // We also wish to measure the amount of fallback connections for a host that | |
1265 // we know implements TLS up to 1.2. Ideally there would be no fallback here | |
1266 // but high numbers of SSLv3 would suggest that SSLv3 fallback is being | |
1267 // caused by network middleware rather than buggy HTTPS servers. | |
1268 const std::string& host = origin_server.host(); | |
1269 if (!is_proxy && | |
1270 host.size() >= 10 && | |
1271 host.compare(host.size() - 10, 10, "google.com") == 0 && | |
1272 (host.size() == 10 || host[host.size()-11] == '.')) { | |
1273 UMA_HISTOGRAM_ENUMERATION("Net.GoogleConnectionUsedSSLVersionFallback", | |
1274 fallback, FALLBACK_MAX); | |
1275 } | |
1276 | |
1277 if (request_info_.load_flags & LOAD_VERIFY_EV_CERT) | |
1278 ssl_config->verify_ev_cert = true; | |
1279 | |
1280 // Disable Channel ID if privacy mode is enabled. | |
1281 if (request_info_.privacy_mode == PRIVACY_MODE_ENABLED) | |
1282 ssl_config->channel_id_enabled = false; | |
1283 } | |
1284 | |
1285 | |
1286 int HttpStreamFactoryImpl::Job::ReconsiderProxyAfterError(int error) { | |
1287 DCHECK(!pac_request_); | |
1288 DCHECK(session_); | |
1289 | |
1290 // A failure to resolve the hostname or any error related to establishing a | |
1291 // TCP connection could be grounds for trying a new proxy configuration. | |
1292 // | |
1293 // Why do this when a hostname cannot be resolved? Some URLs only make sense | |
1294 // to proxy servers. The hostname in those URLs might fail to resolve if we | |
1295 // are still using a non-proxy config. We need to check if a proxy config | |
1296 // now exists that corresponds to a proxy server that could load the URL. | |
1297 // | |
1298 switch (error) { | |
1299 case ERR_PROXY_CONNECTION_FAILED: | |
1300 case ERR_NAME_NOT_RESOLVED: | |
1301 case ERR_INTERNET_DISCONNECTED: | |
1302 case ERR_ADDRESS_UNREACHABLE: | |
1303 case ERR_CONNECTION_CLOSED: | |
1304 case ERR_CONNECTION_TIMED_OUT: | |
1305 case ERR_CONNECTION_RESET: | |
1306 case ERR_CONNECTION_REFUSED: | |
1307 case ERR_CONNECTION_ABORTED: | |
1308 case ERR_TIMED_OUT: | |
1309 case ERR_TUNNEL_CONNECTION_FAILED: | |
1310 case ERR_SOCKS_CONNECTION_FAILED: | |
1311 // This can happen in the case of trying to talk to a proxy using SSL, and | |
1312 // ending up talking to a captive portal that supports SSL instead. | |
1313 case ERR_PROXY_CERTIFICATE_INVALID: | |
1314 // This can happen when trying to talk SSL to a non-SSL server (Like a | |
1315 // captive portal). | |
1316 case ERR_SSL_PROTOCOL_ERROR: | |
1317 break; | |
1318 case ERR_SOCKS_CONNECTION_HOST_UNREACHABLE: | |
1319 // Remap the SOCKS-specific "host unreachable" error to a more | |
1320 // generic error code (this way consumers like the link doctor | |
1321 // know to substitute their error page). | |
1322 // | |
1323 // Note that if the host resolving was done by the SOCKS5 proxy, we can't | |
1324 // differentiate between a proxy-side "host not found" versus a proxy-side | |
1325 // "address unreachable" error, and will report both of these failures as | |
1326 // ERR_ADDRESS_UNREACHABLE. | |
1327 return ERR_ADDRESS_UNREACHABLE; | |
1328 default: | |
1329 return error; | |
1330 } | |
1331 | |
1332 if (request_info_.load_flags & LOAD_BYPASS_PROXY) { | |
1333 return error; | |
1334 } | |
1335 | |
1336 if (proxy_info_.is_https() && proxy_ssl_config_.send_client_cert) { | |
1337 session_->ssl_client_auth_cache()->Remove( | |
1338 proxy_info_.proxy_server().host_port_pair()); | |
1339 } | |
1340 | |
1341 int rv = session_->proxy_service()->ReconsiderProxyAfterError( | |
1342 request_info_.url, request_info_.load_flags, error, &proxy_info_, | |
1343 io_callback_, &pac_request_, session_->network_delegate(), net_log_); | |
1344 if (rv == OK || rv == ERR_IO_PENDING) { | |
1345 // If the error was during connection setup, there is no socket to | |
1346 // disconnect. | |
1347 if (connection_->socket()) | |
1348 connection_->socket()->Disconnect(); | |
1349 connection_->Reset(); | |
1350 if (request_) | |
1351 request_->RemoveRequestFromSpdySessionRequestMap(); | |
1352 next_state_ = STATE_RESOLVE_PROXY_COMPLETE; | |
1353 } else { | |
1354 // If ReconsiderProxyAfterError() failed synchronously, it means | |
1355 // there was nothing left to fall-back to, so fail the transaction | |
1356 // with the last connection error we got. | |
1357 // TODO(eroman): This is a confusing contract, make it more obvious. | |
1358 rv = error; | |
1359 } | |
1360 | |
1361 return rv; | |
1362 } | |
1363 | |
1364 int HttpStreamFactoryImpl::Job::HandleCertificateError(int error) { | |
1365 DCHECK(using_ssl_); | |
1366 DCHECK(IsCertificateError(error)); | |
1367 | |
1368 SSLClientSocket* ssl_socket = | |
1369 static_cast<SSLClientSocket*>(connection_->socket()); | |
1370 ssl_socket->GetSSLInfo(&ssl_info_); | |
1371 | |
1372 // Add the bad certificate to the set of allowed certificates in the | |
1373 // SSL config object. This data structure will be consulted after calling | |
1374 // RestartIgnoringLastError(). And the user will be asked interactively | |
1375 // before RestartIgnoringLastError() is ever called. | |
1376 SSLConfig::CertAndStatus bad_cert; | |
1377 | |
1378 // |ssl_info_.cert| may be NULL if we failed to create | |
1379 // X509Certificate for whatever reason, but normally it shouldn't | |
1380 // happen, unless this code is used inside sandbox. | |
1381 if (ssl_info_.cert.get() == NULL || | |
1382 !X509Certificate::GetDEREncoded(ssl_info_.cert->os_cert_handle(), | |
1383 &bad_cert.der_cert)) { | |
1384 return error; | |
1385 } | |
1386 bad_cert.cert_status = ssl_info_.cert_status; | |
1387 server_ssl_config_.allowed_bad_certs.push_back(bad_cert); | |
1388 | |
1389 int load_flags = request_info_.load_flags; | |
1390 if (session_->params().ignore_certificate_errors) | |
1391 load_flags |= LOAD_IGNORE_ALL_CERT_ERRORS; | |
1392 if (ssl_socket->IgnoreCertError(error, load_flags)) | |
1393 return OK; | |
1394 return error; | |
1395 } | |
1396 | |
1397 void HttpStreamFactoryImpl::Job::SwitchToSpdyMode() { | |
1398 if (HttpStreamFactory::spdy_enabled()) | |
1399 using_spdy_ = true; | |
1400 } | |
1401 | |
1402 // static | |
1403 void HttpStreamFactoryImpl::Job::LogHttpConnectedMetrics( | |
1404 const ClientSocketHandle& handle) { | |
1405 UMA_HISTOGRAM_ENUMERATION("Net.HttpSocketType", handle.reuse_type(), | |
1406 ClientSocketHandle::NUM_TYPES); | |
1407 | |
1408 switch (handle.reuse_type()) { | |
1409 case ClientSocketHandle::UNUSED: | |
1410 UMA_HISTOGRAM_CUSTOM_TIMES("Net.HttpConnectionLatency", | |
1411 handle.setup_time(), | |
1412 base::TimeDelta::FromMilliseconds(1), | |
1413 base::TimeDelta::FromMinutes(10), | |
1414 100); | |
1415 break; | |
1416 case ClientSocketHandle::UNUSED_IDLE: | |
1417 UMA_HISTOGRAM_CUSTOM_TIMES("Net.SocketIdleTimeBeforeNextUse_UnusedSocket", | |
1418 handle.idle_time(), | |
1419 base::TimeDelta::FromMilliseconds(1), | |
1420 base::TimeDelta::FromMinutes(6), | |
1421 100); | |
1422 break; | |
1423 case ClientSocketHandle::REUSED_IDLE: | |
1424 UMA_HISTOGRAM_CUSTOM_TIMES("Net.SocketIdleTimeBeforeNextUse_ReusedSocket", | |
1425 handle.idle_time(), | |
1426 base::TimeDelta::FromMilliseconds(1), | |
1427 base::TimeDelta::FromMinutes(6), | |
1428 100); | |
1429 break; | |
1430 default: | |
1431 NOTREACHED(); | |
1432 break; | |
1433 } | |
1434 } | |
1435 | |
1436 bool HttpStreamFactoryImpl::Job::IsPreconnecting() const { | |
1437 DCHECK_GE(num_streams_, 0); | |
1438 return num_streams_ > 0; | |
1439 } | |
1440 | |
1441 bool HttpStreamFactoryImpl::Job::IsOrphaned() const { | |
1442 return !IsPreconnecting() && !request_; | |
1443 } | |
1444 | |
1445 void HttpStreamFactoryImpl::Job::ReportJobSuccededForRequest() { | |
1446 if (using_existing_quic_session_) { | |
1447 // If an existing session was used, then no TCP connection was | |
1448 // started. | |
1449 HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_NO_RACE); | |
1450 } else if (original_url_) { | |
1451 // This job was the alternate protocol job, and hence won the race. | |
1452 HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_WON_RACE); | |
1453 } else { | |
1454 // This job was the normal job, and hence the alternate protocol job lost | |
1455 // the race. | |
1456 HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_LOST_RACE); | |
1457 } | |
1458 } | |
1459 | |
1460 void HttpStreamFactoryImpl::Job::MarkOtherJobComplete(const Job& job) { | |
1461 DCHECK_EQ(STATUS_RUNNING, other_job_status_); | |
1462 other_job_status_ = job.job_status_; | |
1463 other_job_alternate_protocol_ = job.alternate_protocol_; | |
1464 MaybeMarkAlternateProtocolBroken(); | |
1465 } | |
1466 | |
1467 void HttpStreamFactoryImpl::Job::MaybeMarkAlternateProtocolBroken() { | |
1468 if (job_status_ == STATUS_RUNNING || other_job_status_ == STATUS_RUNNING) | |
1469 return; | |
1470 | |
1471 bool is_alternate_protocol_job = original_url_.get() != NULL; | |
1472 if (is_alternate_protocol_job) { | |
1473 if (job_status_ == STATUS_BROKEN && other_job_status_ == STATUS_SUCCEEDED) { | |
1474 HistogramBrokenAlternateProtocolLocation( | |
1475 BROKEN_ALTERNATE_PROTOCOL_LOCATION_HTTP_STREAM_FACTORY_IMPL_JOB_ALT); | |
1476 session_->http_server_properties()->SetBrokenAlternateProtocol( | |
1477 HostPortPair::FromURL(*original_url_)); | |
1478 } | |
1479 return; | |
1480 } | |
1481 | |
1482 if (job_status_ == STATUS_SUCCEEDED && other_job_status_ == STATUS_BROKEN) { | |
1483 HistogramBrokenAlternateProtocolLocation( | |
1484 BROKEN_ALTERNATE_PROTOCOL_LOCATION_HTTP_STREAM_FACTORY_IMPL_JOB_MAIN); | |
1485 session_->http_server_properties()->SetBrokenAlternateProtocol( | |
1486 HostPortPair::FromURL(request_info_.url)); | |
1487 } | |
1488 } | |
1489 | |
1490 } // namespace net | |
OLD | NEW |