Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(135)

Side by Side Diff: net/http/http_stream_factory_impl_job_controller.cc

Issue 2910463004: Revert "Revert CLs landed in HttpStreamFactoryImpl to track down a crasher" (Closed)
Patch Set: Fix Preconnect AltSvc Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2016 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/http/http_stream_factory_impl_job_controller.h" 5 #include "net/http/http_stream_factory_impl_job_controller.h"
6 6
7 #include <string> 7 #include <string>
8 #include <utility> 8 #include <utility>
9 9
10 #include "base/memory/ptr_util.h" 10 #include "base/memory/ptr_util.h"
(...skipping 10 matching lines...) Expand all
21 #include "net/log/net_log_capture_mode.h" 21 #include "net/log/net_log_capture_mode.h"
22 #include "net/log/net_log_event_type.h" 22 #include "net/log/net_log_event_type.h"
23 #include "net/log/net_log_source.h" 23 #include "net/log/net_log_source.h"
24 #include "net/log/net_log_with_source.h" 24 #include "net/log/net_log_with_source.h"
25 #include "net/proxy/proxy_server.h" 25 #include "net/proxy/proxy_server.h"
26 #include "net/spdy/chromium/spdy_session.h" 26 #include "net/spdy/chromium/spdy_session.h"
27 #include "url/url_constants.h" 27 #include "url/url_constants.h"
28 28
29 namespace net { 29 namespace net {
30 30
31 namespace {
32
33 // Returns parameters associated with the proxy resolution.
34 std::unique_ptr<base::Value> NetLogHttpStreamJobProxyServerResolved(
35 const ProxyServer& proxy_server,
36 NetLogCaptureMode /* capture_mode */) {
37 std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
38
39 dict->SetString("proxy_server", proxy_server.is_valid()
40 ? proxy_server.ToPacString()
41 : std::string());
42 return std::move(dict);
43 }
44
45 } // namespace
46
31 // The maximum time to wait for the alternate job to complete before resuming 47 // The maximum time to wait for the alternate job to complete before resuming
32 // the main job. 48 // the main job.
33 const int kMaxDelayTimeForMainJobSecs = 3; 49 const int kMaxDelayTimeForMainJobSecs = 3;
34 50
35 std::unique_ptr<base::Value> NetLogJobControllerCallback( 51 std::unique_ptr<base::Value> NetLogJobControllerCallback(
36 const GURL* url, 52 const GURL* url,
37 bool is_preconnect, 53 bool is_preconnect,
38 NetLogCaptureMode /* capture_mode */) { 54 NetLogCaptureMode /* capture_mode */) {
39 auto dict = base::MakeUnique<base::DictionaryValue>(); 55 auto dict = base::MakeUnique<base::DictionaryValue>();
40 dict->SetString("url", url->possibly_invalid_spec()); 56 dict->SetString("url", url->possibly_invalid_spec());
41 dict->SetBoolean("is_preconnect", is_preconnect); 57 dict->SetBoolean("is_preconnect", is_preconnect);
42 return std::move(dict); 58 return std::move(dict);
43 } 59 }
44 60
45 HttpStreamFactoryImpl::JobController::JobController( 61 HttpStreamFactoryImpl::JobController::JobController(
46 HttpStreamFactoryImpl* factory, 62 HttpStreamFactoryImpl* factory,
47 HttpStreamRequest::Delegate* delegate, 63 HttpStreamRequest::Delegate* delegate,
48 HttpNetworkSession* session, 64 HttpNetworkSession* session,
49 JobFactory* job_factory, 65 JobFactory* job_factory,
50 const HttpRequestInfo& request_info, 66 const HttpRequestInfo& request_info,
51 bool is_preconnect, 67 bool is_preconnect,
52 bool enable_ip_based_pooling, 68 bool enable_ip_based_pooling,
53 bool enable_alternative_services) 69 bool enable_alternative_services,
70 const SSLConfig& server_ssl_config,
71 const SSLConfig& proxy_ssl_config)
54 : factory_(factory), 72 : factory_(factory),
55 session_(session), 73 session_(session),
56 job_factory_(job_factory), 74 job_factory_(job_factory),
57 request_(nullptr), 75 request_(nullptr),
58 delegate_(delegate), 76 delegate_(delegate),
59 is_preconnect_(is_preconnect), 77 is_preconnect_(is_preconnect),
60 enable_ip_based_pooling_(enable_ip_based_pooling), 78 enable_ip_based_pooling_(enable_ip_based_pooling),
61 enable_alternative_services_(enable_alternative_services), 79 enable_alternative_services_(enable_alternative_services),
62 alternative_job_net_error_(OK), 80 alternative_job_net_error_(OK),
63 job_bound_(false), 81 job_bound_(false),
64 main_job_is_blocked_(false), 82 main_job_is_blocked_(false),
65 main_job_is_resumed_(false), 83 main_job_is_resumed_(false),
66 bound_job_(nullptr), 84 bound_job_(nullptr),
67 can_start_alternative_proxy_job_(false), 85 can_start_alternative_proxy_job_(true),
68 privacy_mode_(PRIVACY_MODE_DISABLED), 86 next_state_(STATE_RESOLVE_PROXY),
87 pac_request_(nullptr),
88 io_callback_(
89 base::Bind(&JobController::OnIOComplete, base::Unretained(this))),
90 request_info_(request_info),
91 server_ssl_config_(server_ssl_config),
92 proxy_ssl_config_(proxy_ssl_config),
93 num_streams_(0),
94 priority_(IDLE),
69 net_log_( 95 net_log_(
70 NetLogWithSource::Make(session->net_log(), 96 NetLogWithSource::Make(session->net_log(),
71 NetLogSourceType::HTTP_STREAM_JOB_CONTROLLER)), 97 NetLogSourceType::HTTP_STREAM_JOB_CONTROLLER)),
72 ptr_factory_(this) { 98 ptr_factory_(this) {
73 DCHECK(factory); 99 DCHECK(factory);
74 net_log_.BeginEvent(NetLogEventType::HTTP_STREAM_JOB_CONTROLLER, 100 net_log_.BeginEvent(NetLogEventType::HTTP_STREAM_JOB_CONTROLLER,
75 base::Bind(&NetLogJobControllerCallback, 101 base::Bind(&NetLogJobControllerCallback,
76 &request_info.url, is_preconnect)); 102 &request_info.url, is_preconnect));
77 } 103 }
78 104
79 HttpStreamFactoryImpl::JobController::~JobController() { 105 HttpStreamFactoryImpl::JobController::~JobController() {
80 main_job_.reset(); 106 main_job_.reset();
81 alternative_job_.reset(); 107 alternative_job_.reset();
82 bound_job_ = nullptr; 108 bound_job_ = nullptr;
109 if (pac_request_) {
110 // TODO(mmenke): Convert this to a DCHECK once https://crbug.com/723589 is
111 // resolved.
112 CHECK_EQ(STATE_RESOLVE_PROXY_COMPLETE, next_state_);
mmenke 2017/05/26 17:28:04 Think can change this back to a DCHECK, and remove
xunjieli 2017/05/26 17:35:15 Why? I thought we haven't figured out the root cau
113 session_->proxy_service()->CancelPacRequest(pac_request_);
114 }
83 net_log_.EndEvent(NetLogEventType::HTTP_STREAM_JOB_CONTROLLER); 115 net_log_.EndEvent(NetLogEventType::HTTP_STREAM_JOB_CONTROLLER);
84 } 116 }
85 117
86 bool HttpStreamFactoryImpl::JobController::for_websockets() { 118 bool HttpStreamFactoryImpl::JobController::for_websockets() {
87 return factory_->for_websockets_; 119 return factory_->for_websockets_;
88 } 120 }
89 121
90 HttpStreamFactoryImpl::Request* HttpStreamFactoryImpl::JobController::Start( 122 std::unique_ptr<HttpStreamFactoryImpl::Request>
91 const HttpRequestInfo& request_info, 123 HttpStreamFactoryImpl::JobController::Start(
92 HttpStreamRequest::Delegate* delegate, 124 HttpStreamRequest::Delegate* delegate,
93 WebSocketHandshakeStreamBase::CreateHelper* 125 WebSocketHandshakeStreamBase::CreateHelper*
94 websocket_handshake_stream_create_helper, 126 websocket_handshake_stream_create_helper,
95 const NetLogWithSource& source_net_log, 127 const NetLogWithSource& source_net_log,
96 HttpStreamRequest::StreamType stream_type, 128 HttpStreamRequest::StreamType stream_type,
97 RequestPriority priority, 129 RequestPriority priority) {
98 const SSLConfig& server_ssl_config,
99 const SSLConfig& proxy_ssl_config) {
100 DCHECK(factory_); 130 DCHECK(factory_);
101 DCHECK(!request_); 131 DCHECK(!request_);
102 132
103 privacy_mode_ = request_info.privacy_mode; 133 stream_type_ = stream_type;
134 priority_ = priority;
104 135
105 request_ = new Request(request_info.url, this, delegate, 136 auto request = base::MakeUnique<Request>(
106 websocket_handshake_stream_create_helper, 137 request_info_.url, this, delegate,
107 source_net_log, stream_type); 138 websocket_handshake_stream_create_helper, source_net_log, stream_type);
139 // Keep a raw pointer but release ownership of Request instance.
140 request_ = request.get();
141
108 // Associates |net_log_| with |source_net_log|. 142 // Associates |net_log_| with |source_net_log|.
109 source_net_log.AddEvent(NetLogEventType::HTTP_STREAM_JOB_CONTROLLER_BOUND, 143 source_net_log.AddEvent(NetLogEventType::HTTP_STREAM_JOB_CONTROLLER_BOUND,
110 net_log_.source().ToEventParametersCallback()); 144 net_log_.source().ToEventParametersCallback());
111 net_log_.AddEvent(NetLogEventType::HTTP_STREAM_JOB_CONTROLLER_BOUND, 145 net_log_.AddEvent(NetLogEventType::HTTP_STREAM_JOB_CONTROLLER_BOUND,
112 source_net_log.source().ToEventParametersCallback()); 146 source_net_log.source().ToEventParametersCallback());
113 147
114 CreateJobs(request_info, priority, server_ssl_config, proxy_ssl_config, 148 RunLoop(OK);
115 delegate, stream_type); 149 return request;
116
117 return request_;
118 } 150 }
119 151
120 void HttpStreamFactoryImpl::JobController::Preconnect( 152 void HttpStreamFactoryImpl::JobController::Preconnect(int num_streams) {
121 int num_streams,
122 const HttpRequestInfo& request_info,
123 const SSLConfig& server_ssl_config,
124 const SSLConfig& proxy_ssl_config) {
125 DCHECK(!main_job_); 153 DCHECK(!main_job_);
126 DCHECK(!alternative_job_); 154 DCHECK(!alternative_job_);
127 DCHECK(is_preconnect_); 155 DCHECK(is_preconnect_);
128 156
129 privacy_mode_ = request_info.privacy_mode; 157 stream_type_ = HttpStreamRequest::HTTP_STREAM;
158 num_streams_ = num_streams;
130 159
131 HostPortPair destination(HostPortPair::FromURL(request_info.url)); 160 RunLoop(OK);
132 GURL origin_url = ApplyHostMappingRules(request_info.url, &destination);
133
134 const AlternativeService alternative_service =
135 GetAlternativeServiceInfoFor(request_info, nullptr,
136 HttpStreamRequest::HTTP_STREAM)
137 .alternative_service;
138
139 if (alternative_service.protocol != kProtoUnknown) {
140 destination = alternative_service.host_port_pair();
141 ignore_result(ApplyHostMappingRules(request_info.url, &destination));
142 }
143
144 // Due to how the socket pools handle priorities and idle sockets, only IDLE
145 // priority currently makes sense for preconnects. The priority for
146 // preconnects is currently ignored (see RequestSocketsForPool()), but could
147 // be used at some point for proxy resolution or something.
148 main_job_.reset(job_factory_->CreateJob(
149 this, PRECONNECT, session_, request_info, IDLE, server_ssl_config,
150 proxy_ssl_config, destination, origin_url, alternative_service,
151 enable_ip_based_pooling_, session_->net_log()));
152 main_job_->Preconnect(num_streams);
153 } 161 }
154 162
155 LoadState HttpStreamFactoryImpl::JobController::GetLoadState() const { 163 LoadState HttpStreamFactoryImpl::JobController::GetLoadState() const {
156 DCHECK(request_); 164 DCHECK(request_);
157 DCHECK(main_job_ || alternative_job_); 165 if (next_state_ == STATE_RESOLVE_PROXY_COMPLETE)
166 return session_->proxy_service()->GetLoadState(pac_request_);
158 if (bound_job_) 167 if (bound_job_)
159 return bound_job_->GetLoadState(); 168 return bound_job_->GetLoadState();
160 169 if (main_job_)
161 // Just pick the first one. 170 return main_job_->GetLoadState();
162 return main_job_ ? main_job_->GetLoadState() 171 if (alternative_job_)
163 : alternative_job_->GetLoadState(); 172 return alternative_job_->GetLoadState();
173 // When proxy resolution fails, there is no job created and
174 // NotifyRequestFailed() is executed one message loop iteration later.
175 return LOAD_STATE_IDLE;
164 } 176 }
165 177
166 void HttpStreamFactoryImpl::JobController::OnRequestComplete() { 178 void HttpStreamFactoryImpl::JobController::OnRequestComplete() {
167 CancelJobs(); 179 CancelJobs();
168 DCHECK(request_); 180 DCHECK(request_);
169 request_ = nullptr; 181 request_ = nullptr;
170 if (bound_job_) { 182 if (bound_job_) {
171 if (bound_job_->job_type() == MAIN) { 183 if (bound_job_->job_type() == MAIN) {
172 main_job_.reset(); 184 main_job_.reset();
173 // |alternative_job_| can be non-null if |main_job_| is resumed after 185 // |alternative_job_| can be non-null if |main_job_| is resumed after
(...skipping 22 matching lines...) Expand all
196 if (alternative_job_) { 208 if (alternative_job_) {
197 alternative_job_->SetPriority(priority); 209 alternative_job_->SetPriority(priority);
198 } 210 }
199 } 211 }
200 212
201 void HttpStreamFactoryImpl::JobController::OnStreamReady( 213 void HttpStreamFactoryImpl::JobController::OnStreamReady(
202 Job* job, 214 Job* job,
203 const SSLConfig& used_ssl_config) { 215 const SSLConfig& used_ssl_config) {
204 DCHECK(job); 216 DCHECK(job);
205 217
206 factory_->OnStreamReady(job->proxy_info(), privacy_mode_); 218 factory_->OnStreamReady(job->proxy_info(), request_info_.privacy_mode);
207 219
208 if (IsJobOrphaned(job)) { 220 if (IsJobOrphaned(job)) {
209 // We have bound a job to the associated Request, |job| has been orphaned. 221 // We have bound a job to the associated Request, |job| has been orphaned.
210 OnOrphanedJobComplete(job); 222 OnOrphanedJobComplete(job);
211 return; 223 return;
212 } 224 }
213 std::unique_ptr<HttpStream> stream = job->ReleaseStream(); 225 std::unique_ptr<HttpStream> stream = job->ReleaseStream();
214 DCHECK(stream); 226 DCHECK(stream);
215 227
216 MarkRequestComplete(job->was_alpn_negotiated(), job->negotiated_protocol(), 228 MarkRequestComplete(job->was_alpn_negotiated(), job->negotiated_protocol(),
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
271 request_->OnWebSocketHandshakeStreamReady(used_ssl_config, used_proxy_info, 283 request_->OnWebSocketHandshakeStreamReady(used_ssl_config, used_proxy_info,
272 stream); 284 stream);
273 } 285 }
274 286
275 void HttpStreamFactoryImpl::JobController::OnStreamFailed( 287 void HttpStreamFactoryImpl::JobController::OnStreamFailed(
276 Job* job, 288 Job* job,
277 int status, 289 int status,
278 const SSLConfig& used_ssl_config) { 290 const SSLConfig& used_ssl_config) {
279 if (job->job_type() == ALTERNATIVE) { 291 if (job->job_type() == ALTERNATIVE) {
280 DCHECK_EQ(alternative_job_.get(), job); 292 DCHECK_EQ(alternative_job_.get(), job);
281 OnAlternativeJobFailed(status); 293 if (alternative_job_->alternative_proxy_server().is_valid()) {
294 OnAlternativeProxyJobFailed(status);
295 } else {
296 OnAlternativeServiceJobFailed(status);
297 }
282 } 298 }
283 299
284 MaybeResumeMainJob(job, base::TimeDelta()); 300 MaybeResumeMainJob(job, base::TimeDelta());
285 301
286 if (IsJobOrphaned(job)) { 302 if (IsJobOrphaned(job)) {
287 // We have bound a job to the associated Request, |job| has been orphaned. 303 // We have bound a job to the associated Request, |job| has been orphaned.
288 OnOrphanedJobComplete(job); 304 OnOrphanedJobComplete(job);
289 return; 305 return;
290 } 306 }
291 307
(...skipping 11 matching lines...) Expand all
303 } else { 319 } else {
304 DCHECK(job->job_type() == ALTERNATIVE); 320 DCHECK(job->job_type() == ALTERNATIVE);
305 alternative_job_.reset(); 321 alternative_job_.reset();
306 } 322 }
307 return; 323 return;
308 } else { 324 } else {
309 BindJob(job); 325 BindJob(job);
310 } 326 }
311 } 327 }
312 328
329 status = ReconsiderProxyAfterError(job, status);
330 if (next_state_ == STATE_RESOLVE_PROXY_COMPLETE) {
331 RunLoop(OK);
332 return;
333 }
313 request_->OnStreamFailed(status, used_ssl_config); 334 request_->OnStreamFailed(status, used_ssl_config);
314 } 335 }
315 336
316 void HttpStreamFactoryImpl::JobController::OnCertificateError( 337 void HttpStreamFactoryImpl::JobController::OnCertificateError(
317 Job* job, 338 Job* job,
318 int status, 339 int status,
319 const SSLConfig& used_ssl_config, 340 const SSLConfig& used_ssl_config,
320 const SSLInfo& ssl_info) { 341 const SSLInfo& ssl_info) {
321 MaybeResumeMainJob(job, base::TimeDelta()); 342 MaybeResumeMainJob(job, base::TimeDelta());
322 343
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
393 if (!request_) 414 if (!request_)
394 return; 415 return;
395 if (!bound_job_) 416 if (!bound_job_)
396 BindJob(job); 417 BindJob(job);
397 request_->OnNeedsProxyAuth(proxy_response, used_ssl_config, used_proxy_info, 418 request_->OnNeedsProxyAuth(proxy_response, used_ssl_config, used_proxy_info,
398 auth_controller); 419 auth_controller);
399 } 420 }
400 421
401 bool HttpStreamFactoryImpl::JobController::OnInitConnection( 422 bool HttpStreamFactoryImpl::JobController::OnInitConnection(
402 const ProxyInfo& proxy_info) { 423 const ProxyInfo& proxy_info) {
403 return factory_->OnInitConnection(*this, proxy_info, privacy_mode_); 424 return factory_->OnInitConnection(*this, proxy_info,
404 } 425 request_info_.privacy_mode);
405
406 void HttpStreamFactoryImpl::JobController::OnResolveProxyComplete(
407 Job* job,
408 const HttpRequestInfo& request_info,
409 RequestPriority priority,
410 const SSLConfig& server_ssl_config,
411 const SSLConfig& proxy_ssl_config,
412 HttpStreamRequest::StreamType stream_type) {
413 DCHECK(job);
414
415 ProxyServer alternative_proxy_server;
416 if (!ShouldCreateAlternativeProxyServerJob(job, job->proxy_info(),
417 request_info.url,
418 &alternative_proxy_server)) {
419 return;
420 }
421
422 DCHECK(main_job_);
423 DCHECK_EQ(MAIN, job->job_type());
424 DCHECK(!alternative_job_);
425 DCHECK(!main_job_is_blocked_);
426
427 HostPortPair destination(HostPortPair::FromURL(request_info.url));
428 GURL origin_url = ApplyHostMappingRules(request_info.url, &destination);
429
430 alternative_job_.reset(job_factory_->CreateJob(
431 this, ALTERNATIVE, session_, request_info, priority, server_ssl_config,
432 proxy_ssl_config, destination, origin_url, alternative_proxy_server,
433 enable_ip_based_pooling_, job->net_log().net_log()));
434
435 can_start_alternative_proxy_job_ = false;
436 main_job_is_blocked_ = true;
437
438 base::ThreadTaskRunnerHandle::Get()->PostTask(
439 FROM_HERE,
440 base::Bind(
441 &HttpStreamFactoryImpl::JobController::StartAlternativeProxyServerJob,
442 ptr_factory_.GetWeakPtr()));
443 } 426 }
444 427
445 void HttpStreamFactoryImpl::JobController::OnNewSpdySessionReady( 428 void HttpStreamFactoryImpl::JobController::OnNewSpdySessionReady(
446 Job* job, 429 Job* job,
447 const base::WeakPtr<SpdySession>& spdy_session, 430 const base::WeakPtr<SpdySession>& spdy_session,
448 bool direct) { 431 bool direct) {
449 DCHECK(job); 432 DCHECK(job);
450 DCHECK(job->using_spdy()); 433 DCHECK(job->using_spdy());
451 DCHECK(!is_preconnect_); 434 DCHECK(!is_preconnect_);
452 435
(...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after
681 return base::trace_event::EstimateMemoryUsage(main_job_) + 664 return base::trace_event::EstimateMemoryUsage(main_job_) +
682 base::trace_event::EstimateMemoryUsage(alternative_job_); 665 base::trace_event::EstimateMemoryUsage(alternative_job_);
683 } 666 }
684 667
685 WebSocketHandshakeStreamBase::CreateHelper* HttpStreamFactoryImpl:: 668 WebSocketHandshakeStreamBase::CreateHelper* HttpStreamFactoryImpl::
686 JobController::websocket_handshake_stream_create_helper() { 669 JobController::websocket_handshake_stream_create_helper() {
687 DCHECK(request_); 670 DCHECK(request_);
688 return request_->websocket_handshake_stream_create_helper(); 671 return request_->websocket_handshake_stream_create_helper();
689 } 672 }
690 673
691 void HttpStreamFactoryImpl::JobController::CreateJobs( 674 void HttpStreamFactoryImpl::JobController::OnIOComplete(int result) {
692 const HttpRequestInfo& request_info, 675 RunLoop(result);
693 RequestPriority priority, 676 }
694 const SSLConfig& server_ssl_config, 677
695 const SSLConfig& proxy_ssl_config, 678 void HttpStreamFactoryImpl::JobController::RunLoop(int result) {
696 HttpStreamRequest::Delegate* delegate, 679 int rv = DoLoop(result);
697 HttpStreamRequest::StreamType stream_type) { 680 if (rv == ERR_IO_PENDING)
681 return;
682 if (rv != OK) {
683 // DoLoop can only fail during proxy resolution step which happens before
684 // any jobs are created. Notify |request_| of the failure one message loop
685 // iteration later to avoid re-entrancy.
686 DCHECK(!main_job_);
687 DCHECK(!alternative_job_);
688 base::ThreadTaskRunnerHandle::Get()->PostTask(
689 FROM_HERE,
690 base::Bind(&HttpStreamFactoryImpl::JobController::NotifyRequestFailed,
691 ptr_factory_.GetWeakPtr(), rv));
692 }
693 }
694
695 int HttpStreamFactoryImpl::JobController::DoLoop(int rv) {
696 DCHECK_NE(next_state_, STATE_NONE);
697 do {
698 State state = next_state_;
699 next_state_ = STATE_NONE;
700 switch (state) {
701 case STATE_RESOLVE_PROXY:
702 DCHECK_EQ(OK, rv);
703 rv = DoResolveProxy();
704 break;
705 case STATE_RESOLVE_PROXY_COMPLETE:
706 rv = DoResolveProxyComplete(rv);
707 break;
708 case STATE_CREATE_JOBS:
709 DCHECK_EQ(OK, rv);
710 rv = DoCreateJobs();
711 break;
712 default:
713 NOTREACHED() << "bad state";
714 break;
715 }
716 } while (next_state_ != STATE_NONE && rv != ERR_IO_PENDING);
717 return rv;
718 }
719
720 int HttpStreamFactoryImpl::JobController::DoResolveProxy() {
721 // TODO(mmenke): Convert this to a DCHECK once https://crbug.com/723589 is
722 // resolved.
723 CHECK(!pac_request_);
mmenke 2017/05/26 17:28:04 DCHECK, remove TODO.
724 DCHECK(session_);
725
726 next_state_ = STATE_RESOLVE_PROXY_COMPLETE;
727
728 if (request_info_.load_flags & LOAD_BYPASS_PROXY) {
729 proxy_info_.UseDirect();
730 return OK;
731 }
732
733 HostPortPair destination(HostPortPair::FromURL(request_info_.url));
734 GURL origin_url = ApplyHostMappingRules(request_info_.url, &destination);
735
736 return session_->proxy_service()->ResolveProxy(
737 origin_url, request_info_.method, &proxy_info_, io_callback_,
738 &pac_request_, session_->params().proxy_delegate, net_log_);
739 }
740
741 int HttpStreamFactoryImpl::JobController::DoResolveProxyComplete(int rv) {
742 DCHECK_NE(ERR_IO_PENDING, rv);
743
744 pac_request_ = nullptr;
745 net_log_.AddEvent(
746 NetLogEventType::HTTP_STREAM_JOB_PROXY_SERVER_RESOLVED,
747 base::Bind(
748 &NetLogHttpStreamJobProxyServerResolved,
749 proxy_info_.is_empty() ? ProxyServer() : proxy_info_.proxy_server()));
750
751 if (rv != OK)
752 return rv;
753 // Remove unsupported proxies from the list.
754 int supported_proxies = ProxyServer::SCHEME_DIRECT |
755 ProxyServer::SCHEME_HTTP | ProxyServer::SCHEME_HTTPS |
756 ProxyServer::SCHEME_SOCKS4 |
757 ProxyServer::SCHEME_SOCKS5;
758 if (session_->IsQuicEnabled())
759 supported_proxies |= ProxyServer::SCHEME_QUIC;
760 proxy_info_.RemoveProxiesWithoutScheme(supported_proxies);
761
762 if (proxy_info_.is_empty()) {
763 // No proxies/direct to choose from.
764 return ERR_NO_SUPPORTED_PROXIES;
765 }
766
767 next_state_ = STATE_CREATE_JOBS;
768 return rv;
769 }
770
771 int HttpStreamFactoryImpl::JobController::DoCreateJobs() {
698 DCHECK(!main_job_); 772 DCHECK(!main_job_);
699 DCHECK(!alternative_job_); 773 DCHECK(!alternative_job_);
700 HostPortPair destination(HostPortPair::FromURL(request_info.url));
701 GURL origin_url = ApplyHostMappingRules(request_info.url, &destination);
702 774
703 main_job_.reset(job_factory_->CreateJob( 775 HostPortPair destination(HostPortPair::FromURL(request_info_.url));
704 this, MAIN, session_, request_info, priority, server_ssl_config, 776 GURL origin_url = ApplyHostMappingRules(request_info_.url, &destination);
705 proxy_ssl_config, destination, origin_url, enable_ip_based_pooling_,
706 net_log_.net_log()));
707 777
708 // Create an alternative job if alternative service is set up for this domain. 778 // Create an alternative job if alternative service is set up for this domain.
709 const AlternativeService alternative_service = 779 const AlternativeService alternative_service =
710 GetAlternativeServiceInfoFor(request_info, delegate, stream_type) 780 GetAlternativeServiceInfoFor(request_info_, delegate_, stream_type_)
711 .alternative_service; 781 .alternative_service;
712 782
783 if (is_preconnect_) {
784 // Due to how the socket pools handle priorities and idle sockets, only IDLE
785 // priority currently makes sense for preconnects. The priority for
786 // preconnects is currently ignored (see RequestSocketsForPool()), but could
787 // be used at some point for proxy resolution or something.
788 if (alternative_service.protocol != kProtoUnknown) {
789 HostPortPair alternative_destination(
790 alternative_service.host_port_pair());
791 ignore_result(
792 ApplyHostMappingRules(request_info_.url, &alternative_destination));
793 main_job_ = job_factory_->CreateAltSvcJob(
794 this, PRECONNECT, session_, request_info_, IDLE, proxy_info_,
795 server_ssl_config_, proxy_ssl_config_, alternative_destination,
796 origin_url, alternative_service, enable_ip_based_pooling_,
797 session_->net_log());
798 } else {
799 main_job_ = job_factory_->CreateMainJob(
800 this, PRECONNECT, session_, request_info_, IDLE, proxy_info_,
801 server_ssl_config_, proxy_ssl_config_, destination, origin_url,
802 enable_ip_based_pooling_, session_->net_log());
803 }
804 main_job_->Preconnect(num_streams_);
805 return OK;
806 }
807 main_job_ = job_factory_->CreateMainJob(
808 this, MAIN, session_, request_info_, priority_, proxy_info_,
809 server_ssl_config_, proxy_ssl_config_, destination, origin_url,
810 enable_ip_based_pooling_, net_log_.net_log());
811 // Alternative Service can only be set for HTTPS requests while Alternative
812 // Proxy is set for HTTP requests.
713 if (alternative_service.protocol != kProtoUnknown) { 813 if (alternative_service.protocol != kProtoUnknown) {
714 // Never share connection with other jobs for FTP requests. 814 // Never share connection with other jobs for FTP requests.
715 DVLOG(1) << "Selected alternative service (host: " 815 DVLOG(1) << "Selected alternative service (host: "
716 << alternative_service.host_port_pair().host() 816 << alternative_service.host_port_pair().host()
717 << " port: " << alternative_service.host_port_pair().port() << ")"; 817 << " port: " << alternative_service.host_port_pair().port() << ")";
718 818
719 DCHECK(!request_info.url.SchemeIs(url::kFtpScheme)); 819 DCHECK(!request_info_.url.SchemeIs(url::kFtpScheme));
720 HostPortPair alternative_destination(alternative_service.host_port_pair()); 820 HostPortPair alternative_destination(alternative_service.host_port_pair());
721 ignore_result( 821 ignore_result(
722 ApplyHostMappingRules(request_info.url, &alternative_destination)); 822 ApplyHostMappingRules(request_info_.url, &alternative_destination));
723 823
724 alternative_job_.reset(job_factory_->CreateJob( 824 alternative_job_ = job_factory_->CreateAltSvcJob(
725 this, ALTERNATIVE, session_, request_info, priority, server_ssl_config, 825 this, ALTERNATIVE, session_, request_info_, priority_, proxy_info_,
726 proxy_ssl_config, alternative_destination, origin_url, 826 server_ssl_config_, proxy_ssl_config_, alternative_destination,
727 alternative_service, enable_ip_based_pooling_, net_log_.net_log())); 827 origin_url, alternative_service, enable_ip_based_pooling_,
828 net_log_.net_log());
728 829
729 main_job_is_blocked_ = true; 830 main_job_is_blocked_ = true;
730 alternative_job_->Start(request_->stream_type()); 831 alternative_job_->Start(request_->stream_type());
731 } else { 832 } else {
732 can_start_alternative_proxy_job_ = true; 833 ProxyServer alternative_proxy_server;
834 if (ShouldCreateAlternativeProxyServerJob(proxy_info_, request_info_.url,
835 &alternative_proxy_server)) {
836 DCHECK(!main_job_is_blocked_);
837 ProxyInfo alternative_proxy_info;
838 alternative_proxy_info.UseProxyServer(alternative_proxy_server);
839
840 alternative_job_ = job_factory_->CreateAltProxyJob(
841 this, ALTERNATIVE, session_, request_info_, priority_,
842 alternative_proxy_info, server_ssl_config_, proxy_ssl_config_,
843 destination, origin_url, alternative_proxy_server,
844 enable_ip_based_pooling_, net_log_.net_log());
845
846 can_start_alternative_proxy_job_ = false;
847 main_job_is_blocked_ = true;
848 alternative_job_->Start(request_->stream_type());
849 }
733 } 850 }
734 // Even if |alternative_job| has already finished, it will not have notified 851 // Even if |alternative_job| has already finished, it will not have notified
735 // the request yet, since we defer that to the next iteration of the 852 // the request yet, since we defer that to the next iteration of the
736 // MessageLoop, so starting |main_job_| is always safe. 853 // MessageLoop, so starting |main_job_| is always safe.
737 main_job_->Start(request_->stream_type()); 854 main_job_->Start(request_->stream_type());
855 return OK;
738 } 856 }
739 857
740 void HttpStreamFactoryImpl::JobController::BindJob(Job* job) { 858 void HttpStreamFactoryImpl::JobController::BindJob(Job* job) {
741 DCHECK(request_); 859 DCHECK(request_);
742 DCHECK(job); 860 DCHECK(job);
743 DCHECK(job == alternative_job_.get() || job == main_job_.get()); 861 DCHECK(job == alternative_job_.get() || job == main_job_.get());
744 DCHECK(!job_bound_); 862 DCHECK(!job_bound_);
745 DCHECK(!bound_job_); 863 DCHECK(!bound_job_);
746 864
747 job_bound_ = true; 865 job_bound_ = true;
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
823 } 941 }
824 942
825 void HttpStreamFactoryImpl::JobController::MarkRequestComplete( 943 void HttpStreamFactoryImpl::JobController::MarkRequestComplete(
826 bool was_alpn_negotiated, 944 bool was_alpn_negotiated,
827 NextProto negotiated_protocol, 945 NextProto negotiated_protocol,
828 bool using_spdy) { 946 bool using_spdy) {
829 if (request_) 947 if (request_)
830 request_->Complete(was_alpn_negotiated, negotiated_protocol, using_spdy); 948 request_->Complete(was_alpn_negotiated, negotiated_protocol, using_spdy);
831 } 949 }
832 950
833 void HttpStreamFactoryImpl::JobController::OnAlternativeJobFailed( 951 void HttpStreamFactoryImpl::JobController::OnAlternativeServiceJobFailed(
834 int net_error) { 952 int net_error) {
835 DCHECK_EQ(alternative_job_->job_type(), ALTERNATIVE); 953 DCHECK_EQ(alternative_job_->job_type(), ALTERNATIVE);
836 DCHECK_NE(OK, net_error); 954 DCHECK_NE(OK, net_error);
955 DCHECK_NE(kProtoUnknown, alternative_job_->alternative_service().protocol);
837 956
838 alternative_job_net_error_ = net_error; 957 alternative_job_net_error_ = net_error;
839 958 failed_alternative_service_ = alternative_job_->alternative_service();
840 if (alternative_job_->alternative_proxy_server().is_valid()) {
841 failed_alternative_proxy_server_ =
842 alternative_job_->alternative_proxy_server();
843 } else {
844 DCHECK(!failed_alternative_proxy_server_.is_valid());
845 failed_alternative_service_ = alternative_job_->alternative_service();
846 }
847 959
848 if (IsJobOrphaned(alternative_job_.get())) { 960 if (IsJobOrphaned(alternative_job_.get())) {
849 // If |request_| is gone then it must have been successfully served by 961 // If |request_| is gone then it must have been successfully served by
850 // |main_job_|. 962 // |main_job_|.
851 // If |request_| is bound to a different job, then it is being 963 // If |request_| is bound to a different job, then it is being
852 // successfully serverd by the main job. 964 // successfully serverd by the main job.
853 ReportBrokenAlternativeService(); 965 ReportBrokenAlternativeService();
854 } 966 }
855 } 967 }
856 968
969 void HttpStreamFactoryImpl::JobController::OnAlternativeProxyJobFailed(
970 int net_error) {
971 DCHECK_EQ(alternative_job_->job_type(), ALTERNATIVE);
972 DCHECK_NE(OK, net_error);
973 DCHECK(alternative_job_->alternative_proxy_server().is_valid());
974
975 // Need to mark alt proxy as broken regardless whether the job is bound.
976 ProxyDelegate* proxy_delegate = session_->params().proxy_delegate;
977 if (proxy_delegate) {
978 proxy_delegate->OnAlternativeProxyBroken(
979 alternative_job_->alternative_proxy_server());
980 }
981 }
982
857 void HttpStreamFactoryImpl::JobController::ReportBrokenAlternativeService() { 983 void HttpStreamFactoryImpl::JobController::ReportBrokenAlternativeService() {
858 DCHECK(failed_alternative_service_.protocol != kProtoUnknown || 984 DCHECK(failed_alternative_service_.protocol != kProtoUnknown);
859 failed_alternative_proxy_server_.is_valid());
860 DCHECK_NE(OK, alternative_job_net_error_); 985 DCHECK_NE(OK, alternative_job_net_error_);
861 986
862 UMA_HISTOGRAM_SPARSE_SLOWLY("Net.AlternateServiceFailed", 987 int error_to_report = alternative_job_net_error_;
863 -alternative_job_net_error_); 988 alternative_job_net_error_ = OK;
989 UMA_HISTOGRAM_SPARSE_SLOWLY("Net.AlternateServiceFailed", -error_to_report);
864 990
865 if (alternative_job_net_error_ == ERR_NETWORK_CHANGED || 991 if (error_to_report == ERR_NETWORK_CHANGED ||
866 alternative_job_net_error_ == ERR_INTERNET_DISCONNECTED) { 992 error_to_report == ERR_INTERNET_DISCONNECTED) {
867 // No need to mark alternative service or proxy as broken. 993 // No need to mark alternative service or proxy as broken.
868 return; 994 return;
869 } 995 }
870 996
871 if (failed_alternative_proxy_server_.is_valid()) { 997 HistogramBrokenAlternateProtocolLocation(
872 ProxyDelegate* proxy_delegate = session_->params().proxy_delegate; 998 BROKEN_ALTERNATE_PROTOCOL_LOCATION_HTTP_STREAM_FACTORY_IMPL_JOB_ALT);
873 if (proxy_delegate) { 999 session_->http_server_properties()->MarkAlternativeServiceBroken(
874 proxy_delegate->OnAlternativeProxyBroken( 1000 failed_alternative_service_);
875 failed_alternative_proxy_server_);
876 }
877 } else {
878 HistogramBrokenAlternateProtocolLocation(
879 BROKEN_ALTERNATE_PROTOCOL_LOCATION_HTTP_STREAM_FACTORY_IMPL_JOB_ALT);
880 session_->http_server_properties()->MarkAlternativeServiceBroken(
881 failed_alternative_service_);
882 }
883 } 1001 }
884 1002
885 void HttpStreamFactoryImpl::JobController::MaybeNotifyFactoryOfCompletion() { 1003 void HttpStreamFactoryImpl::JobController::MaybeNotifyFactoryOfCompletion() {
886 if (!request_ && !main_job_ && !alternative_job_) { 1004 if (!request_ && !main_job_ && !alternative_job_) {
887 DCHECK(!bound_job_); 1005 DCHECK(!bound_job_);
888 factory_->OnJobControllerComplete(this); 1006 factory_->OnJobControllerComplete(this);
889 } 1007 }
890 } 1008 }
891 1009
1010 void HttpStreamFactoryImpl::JobController::NotifyRequestFailed(int rv) {
1011 if (!request_)
1012 return;
1013 request_->OnStreamFailed(rv, server_ssl_config_);
1014 }
1015
892 GURL HttpStreamFactoryImpl::JobController::ApplyHostMappingRules( 1016 GURL HttpStreamFactoryImpl::JobController::ApplyHostMappingRules(
893 const GURL& url, 1017 const GURL& url,
894 HostPortPair* endpoint) { 1018 HostPortPair* endpoint) {
895 const HostMappingRules* mapping_rules = session_->params().host_mapping_rules; 1019 const HostMappingRules* mapping_rules = session_->params().host_mapping_rules;
896 if (mapping_rules && mapping_rules->RewriteHost(endpoint)) { 1020 if (mapping_rules && mapping_rules->RewriteHost(endpoint)) {
897 url::Replacements<char> replacements; 1021 url::Replacements<char> replacements;
898 const std::string port_str = base::UintToString(endpoint->port()); 1022 const std::string port_str = base::UintToString(endpoint->port());
899 replacements.SetPort(port_str.c_str(), url::Component(0, port_str.size())); 1023 replacements.SetPort(port_str.c_str(), url::Component(0, port_str.size()));
900 replacements.SetHost(endpoint->host().c_str(), 1024 replacements.SetHost(endpoint->host().c_str(),
901 url::Component(0, endpoint->host().size())); 1025 url::Component(0, endpoint->host().size()));
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after
1035 1159
1036 // Ask delegate to mark QUIC as broken for the origin. 1160 // Ask delegate to mark QUIC as broken for the origin.
1037 if (quic_advertised && quic_all_broken && delegate != nullptr) 1161 if (quic_advertised && quic_all_broken && delegate != nullptr)
1038 delegate->OnQuicBroken(); 1162 delegate->OnQuicBroken();
1039 1163
1040 return first_alternative_service_info; 1164 return first_alternative_service_info;
1041 } 1165 }
1042 1166
1043 bool HttpStreamFactoryImpl::JobController:: 1167 bool HttpStreamFactoryImpl::JobController::
1044 ShouldCreateAlternativeProxyServerJob( 1168 ShouldCreateAlternativeProxyServerJob(
1045 Job* job,
1046 const ProxyInfo& proxy_info, 1169 const ProxyInfo& proxy_info,
1047 const GURL& url, 1170 const GURL& url,
1048 ProxyServer* alternative_proxy_server) const { 1171 ProxyServer* alternative_proxy_server) const {
1049 DCHECK(!alternative_proxy_server->is_valid()); 1172 DCHECK(!alternative_proxy_server->is_valid());
1050 1173
1051 if (!enable_alternative_services_) 1174 if (!enable_alternative_services_)
1052 return false; 1175 return false;
1053 1176
1054 if (!can_start_alternative_proxy_job_) { 1177 if (!can_start_alternative_proxy_job_) {
1055 // Either an alternative service job or an alternative proxy server job has 1178 // Either an alternative service job or an alternative proxy server job has
1056 // already been started. 1179 // already been started.
1057 return false; 1180 return false;
1058 } 1181 }
1059 1182
1060 if (job->job_type() == ALTERNATIVE) {
1061 // If |job| is using alternative service, then alternative proxy server
1062 // should not be used.
1063 return false;
1064 }
1065
1066 if (is_preconnect_ || job->job_type() == PRECONNECT) {
1067 // Preconnects should be fetched using only the main job to keep the
1068 // resource utilization down.
1069 return false;
1070 }
1071
1072 if (proxy_info.is_empty() || proxy_info.is_direct() || proxy_info.is_quic()) { 1183 if (proxy_info.is_empty() || proxy_info.is_direct() || proxy_info.is_quic()) {
1073 // Alternative proxy server job can be created only if |job| fetches the 1184 // Alternative proxy server job can be created only if |job| fetches the
1074 // |request_| through a non-QUIC proxy. 1185 // |request_| through a non-QUIC proxy.
1075 return false; 1186 return false;
1076 } 1187 }
1077 1188
1078 if (!url.SchemeIs(url::kHttpScheme)) { 1189 if (!url.SchemeIs(url::kHttpScheme)) {
1079 // Only HTTP URLs can be fetched through alternative proxy server, since the 1190 // Only HTTP URLs can be fetched through alternative proxy server, since the
1080 // alternative proxy server may not support fetching of URLs with other 1191 // alternative proxy server may not support fetching of URLs with other
1081 // schemes. 1192 // schemes.
1082 return false; 1193 return false;
1083 } 1194 }
1084 1195
1085 ProxyDelegate* proxy_delegate = session_->params().proxy_delegate; 1196 ProxyDelegate* proxy_delegate = session_->params().proxy_delegate;
1086 if (!proxy_delegate) 1197 if (!proxy_delegate)
1087 return false; 1198 return false;
1088
1089 proxy_delegate->GetAlternativeProxy(url, proxy_info.proxy_server(), 1199 proxy_delegate->GetAlternativeProxy(url, proxy_info.proxy_server(),
1090 alternative_proxy_server); 1200 alternative_proxy_server);
1091 1201
1092 if (!alternative_proxy_server->is_valid()) 1202 if (!alternative_proxy_server->is_valid())
1093 return false; 1203 return false;
1094 1204
1095 DCHECK(!(*alternative_proxy_server == proxy_info.proxy_server())); 1205 DCHECK(!(*alternative_proxy_server == proxy_info.proxy_server()));
1096 1206
1097 if (!alternative_proxy_server->is_https() && 1207 if (!alternative_proxy_server->is_https() &&
1098 !alternative_proxy_server->is_quic()) { 1208 !alternative_proxy_server->is_quic()) {
(...skipping 27 matching lines...) Expand all
1126 if (job->using_existing_quic_session()) { 1236 if (job->using_existing_quic_session()) {
1127 HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_NO_RACE, 1237 HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_NO_RACE,
1128 proxy_server_used); 1238 proxy_server_used);
1129 return; 1239 return;
1130 } 1240 }
1131 1241
1132 HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_WON_RACE, 1242 HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_WON_RACE,
1133 proxy_server_used); 1243 proxy_server_used);
1134 } 1244 }
1135 1245
1136 void HttpStreamFactoryImpl::JobController::StartAlternativeProxyServerJob() {
1137 if (!alternative_job_ || !request_)
1138 return;
1139 DCHECK(alternative_job_->alternative_proxy_server().is_valid());
1140 alternative_job_->Start(request_->stream_type());
1141 }
1142
1143 bool HttpStreamFactoryImpl::JobController::IsJobOrphaned(Job* job) const { 1246 bool HttpStreamFactoryImpl::JobController::IsJobOrphaned(Job* job) const {
1144 return !request_ || (job_bound_ && bound_job_ != job); 1247 return !request_ || (job_bound_ && bound_job_ != job);
1145 } 1248 }
1146 1249
1250 int HttpStreamFactoryImpl::JobController::ReconsiderProxyAfterError(Job* job,
1251 int error) {
1252 // ReconsiderProxyAfterError() should only be called when the last job fails.
1253 DCHECK(!(alternative_job_ && main_job_));
1254 // TODO(mmenke): Convert this to a DCHECK once https://crbug.com/723589 is
1255 // resolved.
1256 CHECK(!pac_request_);
1257 DCHECK(session_);
1258
1259 if (!job->should_reconsider_proxy())
1260 return error;
1261
1262 DCHECK(!job->alternative_proxy_server().is_valid());
1263
1264 // Do not bypass non-QUIC proxy on ERR_MSG_TOO_BIG.
1265 if (!proxy_info_.is_quic() && error == ERR_MSG_TOO_BIG)
1266 return error;
1267
1268 if (request_info_.load_flags & LOAD_BYPASS_PROXY)
1269 return error;
1270
1271 if (proxy_info_.is_https() && proxy_ssl_config_.send_client_cert) {
1272 session_->ssl_client_auth_cache()->Remove(
1273 proxy_info_.proxy_server().host_port_pair());
1274 }
1275
1276 HostPortPair destination(HostPortPair::FromURL(request_info_.url));
1277 GURL origin_url = ApplyHostMappingRules(request_info_.url, &destination);
1278
1279 int rv = session_->proxy_service()->ReconsiderProxyAfterError(
1280 origin_url, request_info_.method, error, &proxy_info_, io_callback_,
1281 &pac_request_, session_->params().proxy_delegate, net_log_);
1282 if (rv == OK || rv == ERR_IO_PENDING) {
1283 RemoveRequestFromSpdySessionRequestMap();
1284 // Abandon all Jobs and start over.
1285 job_bound_ = false;
1286 bound_job_ = nullptr;
1287 alternative_job_.reset();
1288 main_job_.reset();
1289 next_state_ = STATE_RESOLVE_PROXY_COMPLETE;
1290 } else {
1291 // If ReconsiderProxyAfterError() failed synchronously, it means
1292 // there was nothing left to fall-back to, so fail the transaction
1293 // with the last connection error we got.
1294 // TODO(eroman): This is a confusing contract, make it more obvious.
1295 rv = error;
1296 }
1297 return rv;
1298 }
1299
1147 } // namespace net 1300 } // namespace net
OLDNEW
« no previous file with comments | « net/http/http_stream_factory_impl_job_controller.h ('k') | net/http/http_stream_factory_impl_job_controller_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698