| Index: chrome/browser/net/http_pipelining_compatibility_client.cc
|
| diff --git a/chrome/browser/net/http_pipelining_compatibility_client.cc b/chrome/browser/net/http_pipelining_compatibility_client.cc
|
| deleted file mode 100644
|
| index 3f2ee54f800e3ea434c3657c8359839e97c9d86d..0000000000000000000000000000000000000000
|
| --- a/chrome/browser/net/http_pipelining_compatibility_client.cc
|
| +++ /dev/null
|
| @@ -1,542 +0,0 @@
|
| -// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -#include "chrome/browser/net/http_pipelining_compatibility_client.h"
|
| -
|
| -#include "base/metrics/field_trial.h"
|
| -#include "base/metrics/histogram.h"
|
| -#include "base/strings/string_number_conversions.h"
|
| -#include "base/strings/string_split.h"
|
| -#include "base/strings/stringprintf.h"
|
| -#include "chrome/browser/io_thread.h"
|
| -#include "chrome/common/chrome_version_info.h"
|
| -#include "content/public/browser/browser_thread.h"
|
| -#include "net/base/load_flags.h"
|
| -#include "net/base/network_change_notifier.h"
|
| -#include "net/base/request_priority.h"
|
| -#include "net/disk_cache/blockfile/histogram_macros.h"
|
| -#include "net/http/http_network_layer.h"
|
| -#include "net/http/http_network_session.h"
|
| -#include "net/http/http_response_headers.h"
|
| -#include "net/http/http_version.h"
|
| -#include "net/proxy/proxy_config.h"
|
| -#include "net/proxy/proxy_service.h"
|
| -#include "net/url_request/url_request_context.h"
|
| -#include "net/url_request/url_request_context_getter.h"
|
| -
|
| -namespace chrome_browser_net {
|
| -
|
| -static const int kCanaryRequestId = 999;
|
| -
|
| -namespace {
|
| -
|
| -// There is one Request per RequestInfo passed in to Start() above.
|
| -class Request : public internal::PipelineTestRequest,
|
| - public net::URLRequest::Delegate {
|
| - public:
|
| - Request(int request_id,
|
| - const std::string& base_url,
|
| - const RequestInfo& info,
|
| - internal::PipelineTestRequest::Delegate* delegate,
|
| - net::URLRequestContext* url_request_context);
|
| -
|
| - virtual ~Request() {}
|
| -
|
| - virtual void Start() OVERRIDE;
|
| -
|
| - protected:
|
| - // Called when this request has determined its result. Returns the result to
|
| - // the |client_|.
|
| - virtual void Finished(internal::PipelineTestRequest::Status result);
|
| -
|
| - const std::string& response() const { return response_; }
|
| -
|
| - internal::PipelineTestRequest::Delegate* delegate() { return delegate_; }
|
| -
|
| - private:
|
| - // Called when a response can be read. Reads bytes into |response_| until it
|
| - // consumes the entire response or it encounters an error.
|
| - void DoRead();
|
| -
|
| - // Called when all bytes have been received. Compares the |response_| to
|
| - // |info_|'s expected response.
|
| - virtual void DoReadFinished();
|
| -
|
| - // net::URLRequest::Delegate interface
|
| - virtual void OnReceivedRedirect(net::URLRequest* request,
|
| - const GURL& new_url,
|
| - bool* defer_redirect) OVERRIDE;
|
| - virtual void OnSSLCertificateError(net::URLRequest* request,
|
| - const net::SSLInfo& ssl_info,
|
| - bool fatal) OVERRIDE;
|
| - virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE;
|
| - virtual void OnReadCompleted(net::URLRequest* request,
|
| - int bytes_read) OVERRIDE;
|
| -
|
| - internal::PipelineTestRequest::Delegate* delegate_;
|
| - const int request_id_;
|
| - scoped_ptr<net::URLRequest> url_request_;
|
| - const RequestInfo info_;
|
| - scoped_refptr<net::IOBuffer> read_buffer_;
|
| - std::string response_;
|
| - int response_code_;
|
| -};
|
| -
|
| -Request::Request(int request_id,
|
| - const std::string& base_url,
|
| - const RequestInfo& info,
|
| - internal::PipelineTestRequest::Delegate* delegate,
|
| - net::URLRequestContext* url_request_context)
|
| - : delegate_(delegate),
|
| - request_id_(request_id),
|
| - url_request_(url_request_context->CreateRequest(GURL(base_url +
|
| - info.filename),
|
| - net::DEFAULT_PRIORITY,
|
| - this,
|
| - NULL)),
|
| - info_(info),
|
| - response_code_(0) {
|
| - url_request_->SetLoadFlags(net::LOAD_BYPASS_CACHE |
|
| - net::LOAD_DISABLE_CACHE |
|
| - net::LOAD_DO_NOT_SAVE_COOKIES |
|
| - net::LOAD_DO_NOT_SEND_COOKIES |
|
| - net::LOAD_DO_NOT_PROMPT_FOR_LOGIN |
|
| - net::LOAD_DO_NOT_SEND_AUTH_DATA);
|
| -}
|
| -
|
| -void Request::Start() {
|
| - url_request_->Start();
|
| -}
|
| -
|
| -void Request::OnReceivedRedirect(
|
| - net::URLRequest* request,
|
| - const GURL& new_url,
|
| - bool* defer_redirect) {
|
| - *defer_redirect = true;
|
| - request->Cancel();
|
| - Finished(STATUS_REDIRECTED);
|
| -}
|
| -
|
| -void Request::OnSSLCertificateError(
|
| - net::URLRequest* request,
|
| - const net::SSLInfo& ssl_info,
|
| - bool fatal) {
|
| - Finished(STATUS_CERT_ERROR);
|
| -}
|
| -
|
| -void Request::OnResponseStarted(net::URLRequest* request) {
|
| - response_code_ = request->GetResponseCode();
|
| - if (response_code_ != 200) {
|
| - Finished(STATUS_BAD_RESPONSE_CODE);
|
| - return;
|
| - }
|
| - const net::HttpVersion required_version(1, 1);
|
| - if (request->response_info().headers->GetParsedHttpVersion() <
|
| - required_version) {
|
| - Finished(STATUS_BAD_HTTP_VERSION);
|
| - return;
|
| - }
|
| - read_buffer_ = new net::IOBuffer(info_.expected_response.length());
|
| - DoRead();
|
| -}
|
| -
|
| -void Request::OnReadCompleted(net::URLRequest* request, int bytes_read) {
|
| - if (bytes_read == 0) {
|
| - DoReadFinished();
|
| - } else if (bytes_read < 0) {
|
| - Finished(STATUS_NETWORK_ERROR);
|
| - } else {
|
| - response_.append(read_buffer_->data(), bytes_read);
|
| - if (response_.length() <= info_.expected_response.length()) {
|
| - DoRead();
|
| - } else if (response_.find(info_.expected_response) == 0) {
|
| - Finished(STATUS_TOO_LARGE);
|
| - } else {
|
| - Finished(STATUS_CONTENT_MISMATCH);
|
| - }
|
| - }
|
| -}
|
| -
|
| -void Request::DoRead() {
|
| - int bytes_read = 0;
|
| - if (url_request_->Read(read_buffer_.get(), info_.expected_response.length(),
|
| - &bytes_read)) {
|
| - OnReadCompleted(url_request_.get(), bytes_read);
|
| - }
|
| -}
|
| -
|
| -void Request::DoReadFinished() {
|
| - if (response_.length() != info_.expected_response.length()) {
|
| - if (info_.expected_response.find(response_) == 0) {
|
| - Finished(STATUS_TOO_SMALL);
|
| - } else {
|
| - Finished(STATUS_CONTENT_MISMATCH);
|
| - }
|
| - } else if (response_ == info_.expected_response) {
|
| - Finished(STATUS_SUCCESS);
|
| - } else {
|
| - Finished(STATUS_CONTENT_MISMATCH);
|
| - }
|
| -}
|
| -
|
| -void Request::Finished(internal::PipelineTestRequest::Status result) {
|
| - const net::URLRequestStatus status = url_request_->status();
|
| - url_request_.reset();
|
| - if (response_code_ > 0) {
|
| - delegate()->ReportResponseCode(request_id_, response_code_);
|
| - }
|
| - if (status.status() == net::URLRequestStatus::FAILED) {
|
| - // Network errors trump all other status codes, because network errors can
|
| - // be detected by the network stack even with real content. If we determine
|
| - // that all pipelining errors can be detected by the network stack, then we
|
| - // don't need to worry about broken proxies.
|
| - delegate()->ReportNetworkError(request_id_, status.error());
|
| - delegate()->OnRequestFinished(request_id_, STATUS_NETWORK_ERROR);
|
| - } else {
|
| - delegate()->OnRequestFinished(request_id_, result);
|
| - }
|
| - // WARNING: We may be deleted at this point.
|
| -}
|
| -
|
| -// A special non-pipelined request sent before pipelining begins to test basic
|
| -// HTTP connectivity.
|
| -class CanaryRequest : public Request {
|
| - public:
|
| - CanaryRequest(int request_id,
|
| - const std::string& base_url,
|
| - const RequestInfo& info,
|
| - internal::PipelineTestRequest::Delegate* delegate,
|
| - net::URLRequestContext* url_request_context)
|
| - : Request(request_id, base_url, info, delegate, url_request_context) {
|
| - }
|
| -
|
| - virtual ~CanaryRequest() {}
|
| -
|
| - private:
|
| - virtual void Finished(
|
| - internal::PipelineTestRequest::Status result) OVERRIDE {
|
| - delegate()->OnCanaryFinished(result);
|
| - }
|
| -};
|
| -
|
| -// A special request that parses a /stats.txt response from the test server.
|
| -class StatsRequest : public Request {
|
| - public:
|
| - // Note that |info.expected_response| is only used to determine the correct
|
| - // length of the response. The exact string content isn't used.
|
| - StatsRequest(int request_id,
|
| - const std::string& base_url,
|
| - const RequestInfo& info,
|
| - internal::PipelineTestRequest::Delegate* delegate,
|
| - net::URLRequestContext* url_request_context)
|
| - : Request(request_id, base_url, info, delegate, url_request_context) {
|
| - }
|
| -
|
| - virtual ~StatsRequest() {}
|
| -
|
| - private:
|
| - virtual void DoReadFinished() OVERRIDE {
|
| - internal::PipelineTestRequest::Status status =
|
| - internal::ProcessStatsResponse(response());
|
| - Finished(status);
|
| - }
|
| -};
|
| -
|
| -class RequestFactory : public internal::PipelineTestRequest::Factory {
|
| - public:
|
| - virtual internal::PipelineTestRequest* NewRequest(
|
| - int request_id,
|
| - const std::string& base_url,
|
| - const RequestInfo& info,
|
| - internal::PipelineTestRequest::Delegate* delegate,
|
| - net::URLRequestContext* url_request_context,
|
| - internal::PipelineTestRequest::Type request_type) OVERRIDE {
|
| - switch (request_type) {
|
| - case internal::PipelineTestRequest::TYPE_PIPELINED:
|
| - return new Request(request_id, base_url, info, delegate,
|
| - url_request_context);
|
| -
|
| - case internal::PipelineTestRequest::TYPE_CANARY:
|
| - return new CanaryRequest(request_id, base_url, info, delegate,
|
| - url_request_context);
|
| -
|
| - case internal::PipelineTestRequest::TYPE_STATS:
|
| - return new StatsRequest(request_id, base_url, info, delegate,
|
| - url_request_context);
|
| -
|
| - default:
|
| - NOTREACHED();
|
| - return NULL;
|
| - }
|
| - }
|
| -};
|
| -
|
| -} // anonymous namespace
|
| -
|
| -HttpPipeliningCompatibilityClient::HttpPipeliningCompatibilityClient(
|
| - internal::PipelineTestRequest::Factory* factory)
|
| - : factory_(factory),
|
| - num_finished_(0),
|
| - num_succeeded_(0) {
|
| - if (!factory_.get()) {
|
| - factory_.reset(new RequestFactory);
|
| - }
|
| -}
|
| -
|
| -HttpPipeliningCompatibilityClient::~HttpPipeliningCompatibilityClient() {
|
| -}
|
| -
|
| -void HttpPipeliningCompatibilityClient::Start(
|
| - const std::string& base_url,
|
| - std::vector<RequestInfo>& requests,
|
| - Options options,
|
| - const net::CompletionCallback& callback,
|
| - net::URLRequestContext* url_request_context) {
|
| - net::HttpNetworkSession* old_session =
|
| - url_request_context->http_transaction_factory()->GetSession();
|
| - net::HttpNetworkSession::Params params = old_session->params();
|
| - params.force_http_pipelining = true;
|
| - scoped_refptr<net::HttpNetworkSession> session =
|
| - new net::HttpNetworkSession(params);
|
| - http_transaction_factory_.reset(
|
| - net::HttpNetworkLayer::CreateFactory(session.get()));
|
| -
|
| - url_request_context_.reset(new net::URLRequestContext);
|
| - url_request_context_->CopyFrom(url_request_context);
|
| - url_request_context_->set_http_transaction_factory(
|
| - http_transaction_factory_.get());
|
| -
|
| - finished_callback_ = callback;
|
| - for (size_t i = 0; i < requests.size(); ++i) {
|
| - requests_.push_back(factory_->NewRequest(
|
| - i, base_url, requests[i], this, url_request_context_.get(),
|
| - internal::PipelineTestRequest::TYPE_PIPELINED));
|
| - }
|
| - if (options == PIPE_TEST_COLLECT_SERVER_STATS ||
|
| - options == PIPE_TEST_CANARY_AND_STATS) {
|
| - RequestInfo info;
|
| - info.filename = "stats.txt";
|
| - // This is just to determine the expected length of the response.
|
| - // StatsRequest doesn't expect this exact value, but it does expect this
|
| - // exact length.
|
| - info.expected_response =
|
| - "were_all_requests_http_1_1:1,max_pipeline_depth:5";
|
| - requests_.push_back(factory_->NewRequest(
|
| - requests.size(), base_url, info, this, url_request_context_.get(),
|
| - internal::PipelineTestRequest::TYPE_STATS));
|
| - }
|
| - if (options == PIPE_TEST_RUN_CANARY_REQUEST ||
|
| - options == PIPE_TEST_CANARY_AND_STATS) {
|
| - RequestInfo info;
|
| - info.filename = "index.html";
|
| - info.expected_response =
|
| - "\nThis is a test server operated by Google. It's used by Google "
|
| - "Chrome to test\nproxies for compatibility with HTTP pipelining. More "
|
| - "information can be found\nhere:\n\nhttp://dev.chromium.org/developers/"
|
| - "design-documents/network-stack/http-pipelining\n\nSource code can be "
|
| - "found here:\n\nhttp://code.google.com/p/http-pipelining-test/\n";
|
| - canary_request_.reset(factory_->NewRequest(
|
| - kCanaryRequestId, base_url, info, this, url_request_context,
|
| - internal::PipelineTestRequest::TYPE_CANARY));
|
| - canary_request_->Start();
|
| - } else {
|
| - StartTestRequests();
|
| - }
|
| -}
|
| -
|
| -void HttpPipeliningCompatibilityClient::StartTestRequests() {
|
| - for (size_t i = 0; i < requests_.size(); ++i) {
|
| - requests_[i]->Start();
|
| - }
|
| -}
|
| -
|
| -void HttpPipeliningCompatibilityClient::OnCanaryFinished(
|
| - internal::PipelineTestRequest::Status status) {
|
| - canary_request_.reset();
|
| - bool success = (status == internal::PipelineTestRequest::STATUS_SUCCESS);
|
| - UMA_HISTOGRAM_BOOLEAN("NetConnectivity.Pipeline.CanarySuccess", success);
|
| - if (success) {
|
| - StartTestRequests();
|
| - } else {
|
| - finished_callback_.Run(0);
|
| - }
|
| -}
|
| -
|
| -void HttpPipeliningCompatibilityClient::OnRequestFinished(
|
| - int request_id, internal::PipelineTestRequest::Status status) {
|
| - // The CACHE_HISTOGRAM_* macros are used, because they allow dynamic metric
|
| - // names.
|
| - // TODO(gavinp): Clean up this dependency by moving the needed functionality
|
| - // into base/.
|
| - CACHE_HISTOGRAM_ENUMERATION(GetMetricName(request_id, "Status"),
|
| - status,
|
| - internal::PipelineTestRequest::STATUS_MAX);
|
| -
|
| - ++num_finished_;
|
| - if (status == internal::PipelineTestRequest::STATUS_SUCCESS) {
|
| - ++num_succeeded_;
|
| - }
|
| - if (num_finished_ == requests_.size()) {
|
| - UMA_HISTOGRAM_BOOLEAN("NetConnectivity.Pipeline.Success",
|
| - num_succeeded_ == requests_.size());
|
| - finished_callback_.Run(0);
|
| - }
|
| -}
|
| -
|
| -void HttpPipeliningCompatibilityClient::ReportNetworkError(int request_id,
|
| - int error_code) {
|
| - CACHE_HISTOGRAM_ENUMERATION(GetMetricName(request_id, "NetworkError"),
|
| - -error_code, 900);
|
| -}
|
| -
|
| -void HttpPipeliningCompatibilityClient::ReportResponseCode(int request_id,
|
| - int response_code) {
|
| - CACHE_HISTOGRAM_ENUMERATION(GetMetricName(request_id, "ResponseCode"),
|
| - response_code, 600);
|
| -}
|
| -
|
| -std::string HttpPipeliningCompatibilityClient::GetMetricName(
|
| - int request_id, const char* description) {
|
| - return base::StringPrintf("NetConnectivity.Pipeline.%d.%s",
|
| - request_id, description);
|
| -}
|
| -
|
| -namespace internal {
|
| -
|
| -internal::PipelineTestRequest::Status ProcessStatsResponse(
|
| - const std::string& response) {
|
| - bool were_all_requests_http_1_1 = false;
|
| - int max_pipeline_depth = 0;
|
| -
|
| - std::vector<std::pair<std::string, std::string> > kv_pairs;
|
| - base::SplitStringIntoKeyValuePairs(response, ':', ',', &kv_pairs);
|
| -
|
| - if (kv_pairs.size() != 2) {
|
| - return internal::PipelineTestRequest::STATUS_CORRUPT_STATS;
|
| - }
|
| -
|
| - for (size_t i = 0; i < kv_pairs.size(); ++i) {
|
| - const std::string& key = kv_pairs[i].first;
|
| - int value;
|
| - if (!base::StringToInt(kv_pairs[i].second, &value)) {
|
| - return internal::PipelineTestRequest::STATUS_CORRUPT_STATS;
|
| - }
|
| -
|
| - if (key == "were_all_requests_http_1_1") {
|
| - were_all_requests_http_1_1 = (value == 1);
|
| - } else if (key == "max_pipeline_depth") {
|
| - max_pipeline_depth = value;
|
| - } else {
|
| - return internal::PipelineTestRequest::STATUS_CORRUPT_STATS;
|
| - }
|
| - }
|
| -
|
| - UMA_HISTOGRAM_BOOLEAN("NetConnectivity.Pipeline.AllHTTP11",
|
| - were_all_requests_http_1_1);
|
| - UMA_HISTOGRAM_ENUMERATION("NetConnectivity.Pipeline.Depth",
|
| - max_pipeline_depth, 6);
|
| -
|
| - return internal::PipelineTestRequest::STATUS_SUCCESS;
|
| -}
|
| -
|
| -} // namespace internal
|
| -
|
| -namespace {
|
| -
|
| -void DeleteClient(IOThread* io_thread, int /* rv */) {
|
| - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
|
| - io_thread->globals()->http_pipelining_compatibility_client.reset();
|
| -}
|
| -
|
| -void CollectPipeliningCapabilityStatsOnIOThread(
|
| - const std::string& pipeline_test_server,
|
| - IOThread* io_thread) {
|
| - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
|
| -
|
| - net::URLRequestContext* url_request_context =
|
| - io_thread->globals()->system_request_context.get();
|
| - if (!url_request_context->proxy_service()->config().proxy_rules().empty()) {
|
| - // Pipelining with explicitly configured proxies is disabled for now.
|
| - return;
|
| - }
|
| -
|
| - const base::FieldTrial::Probability kDivisor = 100;
|
| - base::FieldTrial::Probability probability_to_run_test = 0;
|
| -
|
| - const char* kTrialName = "HttpPipeliningCompatibility";
|
| - base::FieldTrial* trial = base::FieldTrialList::Find(kTrialName);
|
| - if (trial) {
|
| - return;
|
| - }
|
| - // After May 4, 2012, the trial will disable itself.
|
| - trial = base::FieldTrialList::FactoryGetFieldTrial(
|
| - kTrialName, kDivisor, "disable_test", 2012, 5, 4,
|
| - base::FieldTrial::SESSION_RANDOMIZED, NULL);
|
| -
|
| - chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
|
| - if (channel == chrome::VersionInfo::CHANNEL_CANARY) {
|
| - probability_to_run_test = 100;
|
| - } else if (channel == chrome::VersionInfo::CHANNEL_DEV) {
|
| - probability_to_run_test = 100;
|
| - }
|
| -
|
| - int collect_stats_group = trial->AppendGroup("enable_test",
|
| - probability_to_run_test);
|
| - if (trial->group() != collect_stats_group) {
|
| - return;
|
| - }
|
| -
|
| - std::vector<RequestInfo> requests;
|
| -
|
| - RequestInfo info0;
|
| - info0.filename = "alphabet.txt";
|
| - info0.expected_response = "abcdefghijklmnopqrstuvwxyz";
|
| - requests.push_back(info0);
|
| -
|
| - RequestInfo info1;
|
| - info1.filename = "cached.txt";
|
| - info1.expected_response = "azbycxdwevfugthsirjqkplomn";
|
| - requests.push_back(info1);
|
| -
|
| - RequestInfo info2;
|
| - info2.filename = "reverse.txt";
|
| - info2.expected_response = "zyxwvutsrqponmlkjihgfedcba";
|
| - requests.push_back(info2);
|
| -
|
| - RequestInfo info3;
|
| - info3.filename = "chunked.txt";
|
| - info3.expected_response = "chunkedencodingisfun";
|
| - requests.push_back(info3);
|
| -
|
| - RequestInfo info4;
|
| - info4.filename = "cached.txt";
|
| - info4.expected_response = "azbycxdwevfugthsirjqkplomn";
|
| - requests.push_back(info4);
|
| -
|
| - HttpPipeliningCompatibilityClient* client =
|
| - new HttpPipeliningCompatibilityClient(NULL);
|
| - client->Start(pipeline_test_server, requests,
|
| - HttpPipeliningCompatibilityClient::PIPE_TEST_CANARY_AND_STATS,
|
| - base::Bind(&DeleteClient, io_thread),
|
| - url_request_context);
|
| - io_thread->globals()->http_pipelining_compatibility_client.reset(client);
|
| -}
|
| -
|
| -} // anonymous namespace
|
| -
|
| -void CollectPipeliningCapabilityStatsOnUIThread(
|
| - const std::string& pipeline_test_server, IOThread* io_thread) {
|
| - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
|
| - if (pipeline_test_server.empty())
|
| - return;
|
| -
|
| - content::BrowserThread::PostTask(
|
| - content::BrowserThread::IO,
|
| - FROM_HERE,
|
| - base::Bind(&CollectPipeliningCapabilityStatsOnIOThread,
|
| - pipeline_test_server,
|
| - io_thread));
|
| -}
|
| -
|
| -} // namespace chrome_browser_net
|
|
|