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

Side by Side Diff: chrome/browser/net/http_pipelining_compatibility_client.cc

Issue 275953002: Remove HTTP pipelining support. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix line endings Created 6 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 | Annotate | Revision Log
OLDNEW
(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 "chrome/browser/net/http_pipelining_compatibility_client.h"
6
7 #include "base/metrics/field_trial.h"
8 #include "base/metrics/histogram.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/string_split.h"
11 #include "base/strings/stringprintf.h"
12 #include "chrome/browser/io_thread.h"
13 #include "chrome/common/chrome_version_info.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "net/base/load_flags.h"
16 #include "net/base/network_change_notifier.h"
17 #include "net/base/request_priority.h"
18 #include "net/disk_cache/blockfile/histogram_macros.h"
19 #include "net/http/http_network_layer.h"
20 #include "net/http/http_network_session.h"
21 #include "net/http/http_response_headers.h"
22 #include "net/http/http_version.h"
23 #include "net/proxy/proxy_config.h"
24 #include "net/proxy/proxy_service.h"
25 #include "net/url_request/url_request_context.h"
26 #include "net/url_request/url_request_context_getter.h"
27
28 namespace chrome_browser_net {
29
30 static const int kCanaryRequestId = 999;
31
32 namespace {
33
34 // There is one Request per RequestInfo passed in to Start() above.
35 class Request : public internal::PipelineTestRequest,
36 public net::URLRequest::Delegate {
37 public:
38 Request(int request_id,
39 const std::string& base_url,
40 const RequestInfo& info,
41 internal::PipelineTestRequest::Delegate* delegate,
42 net::URLRequestContext* url_request_context);
43
44 virtual ~Request() {}
45
46 virtual void Start() OVERRIDE;
47
48 protected:
49 // Called when this request has determined its result. Returns the result to
50 // the |client_|.
51 virtual void Finished(internal::PipelineTestRequest::Status result);
52
53 const std::string& response() const { return response_; }
54
55 internal::PipelineTestRequest::Delegate* delegate() { return delegate_; }
56
57 private:
58 // Called when a response can be read. Reads bytes into |response_| until it
59 // consumes the entire response or it encounters an error.
60 void DoRead();
61
62 // Called when all bytes have been received. Compares the |response_| to
63 // |info_|'s expected response.
64 virtual void DoReadFinished();
65
66 // net::URLRequest::Delegate interface
67 virtual void OnReceivedRedirect(net::URLRequest* request,
68 const GURL& new_url,
69 bool* defer_redirect) OVERRIDE;
70 virtual void OnSSLCertificateError(net::URLRequest* request,
71 const net::SSLInfo& ssl_info,
72 bool fatal) OVERRIDE;
73 virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE;
74 virtual void OnReadCompleted(net::URLRequest* request,
75 int bytes_read) OVERRIDE;
76
77 internal::PipelineTestRequest::Delegate* delegate_;
78 const int request_id_;
79 scoped_ptr<net::URLRequest> url_request_;
80 const RequestInfo info_;
81 scoped_refptr<net::IOBuffer> read_buffer_;
82 std::string response_;
83 int response_code_;
84 };
85
86 Request::Request(int request_id,
87 const std::string& base_url,
88 const RequestInfo& info,
89 internal::PipelineTestRequest::Delegate* delegate,
90 net::URLRequestContext* url_request_context)
91 : delegate_(delegate),
92 request_id_(request_id),
93 url_request_(url_request_context->CreateRequest(GURL(base_url +
94 info.filename),
95 net::DEFAULT_PRIORITY,
96 this,
97 NULL)),
98 info_(info),
99 response_code_(0) {
100 url_request_->SetLoadFlags(net::LOAD_BYPASS_CACHE |
101 net::LOAD_DISABLE_CACHE |
102 net::LOAD_DO_NOT_SAVE_COOKIES |
103 net::LOAD_DO_NOT_SEND_COOKIES |
104 net::LOAD_DO_NOT_PROMPT_FOR_LOGIN |
105 net::LOAD_DO_NOT_SEND_AUTH_DATA);
106 }
107
108 void Request::Start() {
109 url_request_->Start();
110 }
111
112 void Request::OnReceivedRedirect(
113 net::URLRequest* request,
114 const GURL& new_url,
115 bool* defer_redirect) {
116 *defer_redirect = true;
117 request->Cancel();
118 Finished(STATUS_REDIRECTED);
119 }
120
121 void Request::OnSSLCertificateError(
122 net::URLRequest* request,
123 const net::SSLInfo& ssl_info,
124 bool fatal) {
125 Finished(STATUS_CERT_ERROR);
126 }
127
128 void Request::OnResponseStarted(net::URLRequest* request) {
129 response_code_ = request->GetResponseCode();
130 if (response_code_ != 200) {
131 Finished(STATUS_BAD_RESPONSE_CODE);
132 return;
133 }
134 const net::HttpVersion required_version(1, 1);
135 if (request->response_info().headers->GetParsedHttpVersion() <
136 required_version) {
137 Finished(STATUS_BAD_HTTP_VERSION);
138 return;
139 }
140 read_buffer_ = new net::IOBuffer(info_.expected_response.length());
141 DoRead();
142 }
143
144 void Request::OnReadCompleted(net::URLRequest* request, int bytes_read) {
145 if (bytes_read == 0) {
146 DoReadFinished();
147 } else if (bytes_read < 0) {
148 Finished(STATUS_NETWORK_ERROR);
149 } else {
150 response_.append(read_buffer_->data(), bytes_read);
151 if (response_.length() <= info_.expected_response.length()) {
152 DoRead();
153 } else if (response_.find(info_.expected_response) == 0) {
154 Finished(STATUS_TOO_LARGE);
155 } else {
156 Finished(STATUS_CONTENT_MISMATCH);
157 }
158 }
159 }
160
161 void Request::DoRead() {
162 int bytes_read = 0;
163 if (url_request_->Read(read_buffer_.get(), info_.expected_response.length(),
164 &bytes_read)) {
165 OnReadCompleted(url_request_.get(), bytes_read);
166 }
167 }
168
169 void Request::DoReadFinished() {
170 if (response_.length() != info_.expected_response.length()) {
171 if (info_.expected_response.find(response_) == 0) {
172 Finished(STATUS_TOO_SMALL);
173 } else {
174 Finished(STATUS_CONTENT_MISMATCH);
175 }
176 } else if (response_ == info_.expected_response) {
177 Finished(STATUS_SUCCESS);
178 } else {
179 Finished(STATUS_CONTENT_MISMATCH);
180 }
181 }
182
183 void Request::Finished(internal::PipelineTestRequest::Status result) {
184 const net::URLRequestStatus status = url_request_->status();
185 url_request_.reset();
186 if (response_code_ > 0) {
187 delegate()->ReportResponseCode(request_id_, response_code_);
188 }
189 if (status.status() == net::URLRequestStatus::FAILED) {
190 // Network errors trump all other status codes, because network errors can
191 // be detected by the network stack even with real content. If we determine
192 // that all pipelining errors can be detected by the network stack, then we
193 // don't need to worry about broken proxies.
194 delegate()->ReportNetworkError(request_id_, status.error());
195 delegate()->OnRequestFinished(request_id_, STATUS_NETWORK_ERROR);
196 } else {
197 delegate()->OnRequestFinished(request_id_, result);
198 }
199 // WARNING: We may be deleted at this point.
200 }
201
202 // A special non-pipelined request sent before pipelining begins to test basic
203 // HTTP connectivity.
204 class CanaryRequest : public Request {
205 public:
206 CanaryRequest(int request_id,
207 const std::string& base_url,
208 const RequestInfo& info,
209 internal::PipelineTestRequest::Delegate* delegate,
210 net::URLRequestContext* url_request_context)
211 : Request(request_id, base_url, info, delegate, url_request_context) {
212 }
213
214 virtual ~CanaryRequest() {}
215
216 private:
217 virtual void Finished(
218 internal::PipelineTestRequest::Status result) OVERRIDE {
219 delegate()->OnCanaryFinished(result);
220 }
221 };
222
223 // A special request that parses a /stats.txt response from the test server.
224 class StatsRequest : public Request {
225 public:
226 // Note that |info.expected_response| is only used to determine the correct
227 // length of the response. The exact string content isn't used.
228 StatsRequest(int request_id,
229 const std::string& base_url,
230 const RequestInfo& info,
231 internal::PipelineTestRequest::Delegate* delegate,
232 net::URLRequestContext* url_request_context)
233 : Request(request_id, base_url, info, delegate, url_request_context) {
234 }
235
236 virtual ~StatsRequest() {}
237
238 private:
239 virtual void DoReadFinished() OVERRIDE {
240 internal::PipelineTestRequest::Status status =
241 internal::ProcessStatsResponse(response());
242 Finished(status);
243 }
244 };
245
246 class RequestFactory : public internal::PipelineTestRequest::Factory {
247 public:
248 virtual internal::PipelineTestRequest* NewRequest(
249 int request_id,
250 const std::string& base_url,
251 const RequestInfo& info,
252 internal::PipelineTestRequest::Delegate* delegate,
253 net::URLRequestContext* url_request_context,
254 internal::PipelineTestRequest::Type request_type) OVERRIDE {
255 switch (request_type) {
256 case internal::PipelineTestRequest::TYPE_PIPELINED:
257 return new Request(request_id, base_url, info, delegate,
258 url_request_context);
259
260 case internal::PipelineTestRequest::TYPE_CANARY:
261 return new CanaryRequest(request_id, base_url, info, delegate,
262 url_request_context);
263
264 case internal::PipelineTestRequest::TYPE_STATS:
265 return new StatsRequest(request_id, base_url, info, delegate,
266 url_request_context);
267
268 default:
269 NOTREACHED();
270 return NULL;
271 }
272 }
273 };
274
275 } // anonymous namespace
276
277 HttpPipeliningCompatibilityClient::HttpPipeliningCompatibilityClient(
278 internal::PipelineTestRequest::Factory* factory)
279 : factory_(factory),
280 num_finished_(0),
281 num_succeeded_(0) {
282 if (!factory_.get()) {
283 factory_.reset(new RequestFactory);
284 }
285 }
286
287 HttpPipeliningCompatibilityClient::~HttpPipeliningCompatibilityClient() {
288 }
289
290 void HttpPipeliningCompatibilityClient::Start(
291 const std::string& base_url,
292 std::vector<RequestInfo>& requests,
293 Options options,
294 const net::CompletionCallback& callback,
295 net::URLRequestContext* url_request_context) {
296 net::HttpNetworkSession* old_session =
297 url_request_context->http_transaction_factory()->GetSession();
298 net::HttpNetworkSession::Params params = old_session->params();
299 params.force_http_pipelining = true;
300 scoped_refptr<net::HttpNetworkSession> session =
301 new net::HttpNetworkSession(params);
302 http_transaction_factory_.reset(
303 net::HttpNetworkLayer::CreateFactory(session.get()));
304
305 url_request_context_.reset(new net::URLRequestContext);
306 url_request_context_->CopyFrom(url_request_context);
307 url_request_context_->set_http_transaction_factory(
308 http_transaction_factory_.get());
309
310 finished_callback_ = callback;
311 for (size_t i = 0; i < requests.size(); ++i) {
312 requests_.push_back(factory_->NewRequest(
313 i, base_url, requests[i], this, url_request_context_.get(),
314 internal::PipelineTestRequest::TYPE_PIPELINED));
315 }
316 if (options == PIPE_TEST_COLLECT_SERVER_STATS ||
317 options == PIPE_TEST_CANARY_AND_STATS) {
318 RequestInfo info;
319 info.filename = "stats.txt";
320 // This is just to determine the expected length of the response.
321 // StatsRequest doesn't expect this exact value, but it does expect this
322 // exact length.
323 info.expected_response =
324 "were_all_requests_http_1_1:1,max_pipeline_depth:5";
325 requests_.push_back(factory_->NewRequest(
326 requests.size(), base_url, info, this, url_request_context_.get(),
327 internal::PipelineTestRequest::TYPE_STATS));
328 }
329 if (options == PIPE_TEST_RUN_CANARY_REQUEST ||
330 options == PIPE_TEST_CANARY_AND_STATS) {
331 RequestInfo info;
332 info.filename = "index.html";
333 info.expected_response =
334 "\nThis is a test server operated by Google. It's used by Google "
335 "Chrome to test\nproxies for compatibility with HTTP pipelining. More "
336 "information can be found\nhere:\n\nhttp://dev.chromium.org/developers/"
337 "design-documents/network-stack/http-pipelining\n\nSource code can be "
338 "found here:\n\nhttp://code.google.com/p/http-pipelining-test/\n";
339 canary_request_.reset(factory_->NewRequest(
340 kCanaryRequestId, base_url, info, this, url_request_context,
341 internal::PipelineTestRequest::TYPE_CANARY));
342 canary_request_->Start();
343 } else {
344 StartTestRequests();
345 }
346 }
347
348 void HttpPipeliningCompatibilityClient::StartTestRequests() {
349 for (size_t i = 0; i < requests_.size(); ++i) {
350 requests_[i]->Start();
351 }
352 }
353
354 void HttpPipeliningCompatibilityClient::OnCanaryFinished(
355 internal::PipelineTestRequest::Status status) {
356 canary_request_.reset();
357 bool success = (status == internal::PipelineTestRequest::STATUS_SUCCESS);
358 UMA_HISTOGRAM_BOOLEAN("NetConnectivity.Pipeline.CanarySuccess", success);
359 if (success) {
360 StartTestRequests();
361 } else {
362 finished_callback_.Run(0);
363 }
364 }
365
366 void HttpPipeliningCompatibilityClient::OnRequestFinished(
367 int request_id, internal::PipelineTestRequest::Status status) {
368 // The CACHE_HISTOGRAM_* macros are used, because they allow dynamic metric
369 // names.
370 // TODO(gavinp): Clean up this dependency by moving the needed functionality
371 // into base/.
372 CACHE_HISTOGRAM_ENUMERATION(GetMetricName(request_id, "Status"),
373 status,
374 internal::PipelineTestRequest::STATUS_MAX);
375
376 ++num_finished_;
377 if (status == internal::PipelineTestRequest::STATUS_SUCCESS) {
378 ++num_succeeded_;
379 }
380 if (num_finished_ == requests_.size()) {
381 UMA_HISTOGRAM_BOOLEAN("NetConnectivity.Pipeline.Success",
382 num_succeeded_ == requests_.size());
383 finished_callback_.Run(0);
384 }
385 }
386
387 void HttpPipeliningCompatibilityClient::ReportNetworkError(int request_id,
388 int error_code) {
389 CACHE_HISTOGRAM_ENUMERATION(GetMetricName(request_id, "NetworkError"),
390 -error_code, 900);
391 }
392
393 void HttpPipeliningCompatibilityClient::ReportResponseCode(int request_id,
394 int response_code) {
395 CACHE_HISTOGRAM_ENUMERATION(GetMetricName(request_id, "ResponseCode"),
396 response_code, 600);
397 }
398
399 std::string HttpPipeliningCompatibilityClient::GetMetricName(
400 int request_id, const char* description) {
401 return base::StringPrintf("NetConnectivity.Pipeline.%d.%s",
402 request_id, description);
403 }
404
405 namespace internal {
406
407 internal::PipelineTestRequest::Status ProcessStatsResponse(
408 const std::string& response) {
409 bool were_all_requests_http_1_1 = false;
410 int max_pipeline_depth = 0;
411
412 std::vector<std::pair<std::string, std::string> > kv_pairs;
413 base::SplitStringIntoKeyValuePairs(response, ':', ',', &kv_pairs);
414
415 if (kv_pairs.size() != 2) {
416 return internal::PipelineTestRequest::STATUS_CORRUPT_STATS;
417 }
418
419 for (size_t i = 0; i < kv_pairs.size(); ++i) {
420 const std::string& key = kv_pairs[i].first;
421 int value;
422 if (!base::StringToInt(kv_pairs[i].second, &value)) {
423 return internal::PipelineTestRequest::STATUS_CORRUPT_STATS;
424 }
425
426 if (key == "were_all_requests_http_1_1") {
427 were_all_requests_http_1_1 = (value == 1);
428 } else if (key == "max_pipeline_depth") {
429 max_pipeline_depth = value;
430 } else {
431 return internal::PipelineTestRequest::STATUS_CORRUPT_STATS;
432 }
433 }
434
435 UMA_HISTOGRAM_BOOLEAN("NetConnectivity.Pipeline.AllHTTP11",
436 were_all_requests_http_1_1);
437 UMA_HISTOGRAM_ENUMERATION("NetConnectivity.Pipeline.Depth",
438 max_pipeline_depth, 6);
439
440 return internal::PipelineTestRequest::STATUS_SUCCESS;
441 }
442
443 } // namespace internal
444
445 namespace {
446
447 void DeleteClient(IOThread* io_thread, int /* rv */) {
448 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
449 io_thread->globals()->http_pipelining_compatibility_client.reset();
450 }
451
452 void CollectPipeliningCapabilityStatsOnIOThread(
453 const std::string& pipeline_test_server,
454 IOThread* io_thread) {
455 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
456
457 net::URLRequestContext* url_request_context =
458 io_thread->globals()->system_request_context.get();
459 if (!url_request_context->proxy_service()->config().proxy_rules().empty()) {
460 // Pipelining with explicitly configured proxies is disabled for now.
461 return;
462 }
463
464 const base::FieldTrial::Probability kDivisor = 100;
465 base::FieldTrial::Probability probability_to_run_test = 0;
466
467 const char* kTrialName = "HttpPipeliningCompatibility";
468 base::FieldTrial* trial = base::FieldTrialList::Find(kTrialName);
469 if (trial) {
470 return;
471 }
472 // After May 4, 2012, the trial will disable itself.
473 trial = base::FieldTrialList::FactoryGetFieldTrial(
474 kTrialName, kDivisor, "disable_test", 2012, 5, 4,
475 base::FieldTrial::SESSION_RANDOMIZED, NULL);
476
477 chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
478 if (channel == chrome::VersionInfo::CHANNEL_CANARY) {
479 probability_to_run_test = 100;
480 } else if (channel == chrome::VersionInfo::CHANNEL_DEV) {
481 probability_to_run_test = 100;
482 }
483
484 int collect_stats_group = trial->AppendGroup("enable_test",
485 probability_to_run_test);
486 if (trial->group() != collect_stats_group) {
487 return;
488 }
489
490 std::vector<RequestInfo> requests;
491
492 RequestInfo info0;
493 info0.filename = "alphabet.txt";
494 info0.expected_response = "abcdefghijklmnopqrstuvwxyz";
495 requests.push_back(info0);
496
497 RequestInfo info1;
498 info1.filename = "cached.txt";
499 info1.expected_response = "azbycxdwevfugthsirjqkplomn";
500 requests.push_back(info1);
501
502 RequestInfo info2;
503 info2.filename = "reverse.txt";
504 info2.expected_response = "zyxwvutsrqponmlkjihgfedcba";
505 requests.push_back(info2);
506
507 RequestInfo info3;
508 info3.filename = "chunked.txt";
509 info3.expected_response = "chunkedencodingisfun";
510 requests.push_back(info3);
511
512 RequestInfo info4;
513 info4.filename = "cached.txt";
514 info4.expected_response = "azbycxdwevfugthsirjqkplomn";
515 requests.push_back(info4);
516
517 HttpPipeliningCompatibilityClient* client =
518 new HttpPipeliningCompatibilityClient(NULL);
519 client->Start(pipeline_test_server, requests,
520 HttpPipeliningCompatibilityClient::PIPE_TEST_CANARY_AND_STATS,
521 base::Bind(&DeleteClient, io_thread),
522 url_request_context);
523 io_thread->globals()->http_pipelining_compatibility_client.reset(client);
524 }
525
526 } // anonymous namespace
527
528 void CollectPipeliningCapabilityStatsOnUIThread(
529 const std::string& pipeline_test_server, IOThread* io_thread) {
530 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
531 if (pipeline_test_server.empty())
532 return;
533
534 content::BrowserThread::PostTask(
535 content::BrowserThread::IO,
536 FROM_HERE,
537 base::Bind(&CollectPipeliningCapabilityStatsOnIOThread,
538 pipeline_test_server,
539 io_thread));
540 }
541
542 } // namespace chrome_browser_net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698