summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/net/http_pipelining_compatibility_client.cc188
-rw-r--r--chrome/browser/net/http_pipelining_compatibility_client.h125
-rw-r--r--chrome/browser/net/http_pipelining_compatibility_client_unittest.cc440
-rw-r--r--chrome/chrome_browser.gypi2
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--chrome/test/data/http_pipelining/alphabet.txt1
-rw-r--r--chrome/test/data/http_pipelining/alphabet.txt.mock-http-headers1
-rw-r--r--net/base/net_error_list.h8
-rwxr-xr-xnet/tools/testserver/testserver.py16
9 files changed, 777 insertions, 5 deletions
diff --git a/chrome/browser/net/http_pipelining_compatibility_client.cc b/chrome/browser/net/http_pipelining_compatibility_client.cc
new file mode 100644
index 0000000..2a44b2c
--- /dev/null
+++ b/chrome/browser/net/http_pipelining_compatibility_client.cc
@@ -0,0 +1,188 @@
+// 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/histogram.h"
+#include "base/stringprintf.h"
+#include "net/base/load_flags.h"
+#include "net/disk_cache/histogram_macros.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_version.h"
+
+namespace chrome_browser_net {
+
+HttpPipeliningCompatibilityClient::HttpPipeliningCompatibilityClient()
+ : num_finished_(0) {
+}
+
+HttpPipeliningCompatibilityClient::~HttpPipeliningCompatibilityClient() {
+}
+
+void HttpPipeliningCompatibilityClient::Start(
+ const std::string& base_url,
+ std::vector<RequestInfo>& requests,
+ const net::CompletionCallback& callback,
+ net::URLRequestContext* url_request_context) {
+ finished_callback_ = callback;
+ for (size_t i = 0; i < requests.size(); ++i) {
+ requests_.push_back(new Request(i, base_url, requests[i], this,
+ url_request_context));
+ }
+}
+
+void HttpPipeliningCompatibilityClient::OnRequestFinished(int request_id,
+ Status status) {
+ // The CACHE_HISTOGRAM_* macros are used, because they allow dynamic metric
+ // names.
+ CACHE_HISTOGRAM_ENUMERATION(GetMetricName(request_id, "Status"),
+ status, STATUS_MAX);
+ ++num_finished_;
+ if (num_finished_ == 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);
+}
+
+HttpPipeliningCompatibilityClient::Request::Request(
+ int request_id,
+ const std::string& base_url,
+ const RequestInfo& info,
+ HttpPipeliningCompatibilityClient* client,
+ net::URLRequestContext* url_request_context)
+ : request_id_(request_id),
+ request_(GURL(base_url + info.filename), this),
+ info_(info),
+ client_(client),
+ finished_(false) {
+ request_.set_context(url_request_context);
+ // TODO(simonjam): Force pipelining.
+ request_.set_load_flags(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);
+ request_.Start();
+}
+
+HttpPipeliningCompatibilityClient::Request::~Request() {
+}
+
+void HttpPipeliningCompatibilityClient::Request::OnReceivedRedirect(
+ net::URLRequest* request,
+ const GURL& new_url,
+ bool* defer_redirect) {
+ *defer_redirect = true;
+ request->Cancel();
+ Finished(REDIRECTED);
+}
+
+void HttpPipeliningCompatibilityClient::Request::OnSSLCertificateError(
+ net::URLRequest* request,
+ const net::SSLInfo& ssl_info,
+ bool fatal) {
+ Finished(CERT_ERROR);
+}
+
+void HttpPipeliningCompatibilityClient::Request::OnResponseStarted(
+ net::URLRequest* request) {
+ if (finished_) {
+ return;
+ }
+ int response_code = request->GetResponseCode();
+ if (response_code > 0) {
+ client_->ReportResponseCode(request_id_, response_code);
+ }
+ if (response_code == 200) {
+ const net::HttpVersion required_version(1, 1);
+ if (request->response_info().headers->GetParsedHttpVersion() <
+ required_version) {
+ Finished(BAD_HTTP_VERSION);
+ } else {
+ read_buffer_ = new net::IOBuffer(info_.expected_response.length());
+ DoRead();
+ }
+ } else {
+ Finished(BAD_RESPONSE_CODE);
+ }
+}
+
+void HttpPipeliningCompatibilityClient::Request::OnReadCompleted(
+ net::URLRequest* request,
+ int bytes_read) {
+ if (bytes_read == 0) {
+ DoReadFinished();
+ } else if (bytes_read < 0) {
+ Finished(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(TOO_LARGE);
+ } else {
+ Finished(CONTENT_MISMATCH);
+ }
+ }
+}
+
+void HttpPipeliningCompatibilityClient::Request::DoRead() {
+ int bytes_read = 0;
+ if (request_.Read(read_buffer_.get(), info_.expected_response.length(),
+ &bytes_read)) {
+ OnReadCompleted(&request_, bytes_read);
+ }
+}
+
+void HttpPipeliningCompatibilityClient::Request::DoReadFinished() {
+ if (response_.length() != info_.expected_response.length()) {
+ if (info_.expected_response.find(response_) == 0) {
+ Finished(TOO_SMALL);
+ } else {
+ Finished(CONTENT_MISMATCH);
+ }
+ } else if (response_ == info_.expected_response) {
+ Finished(SUCCESS);
+ } else {
+ Finished(CONTENT_MISMATCH);
+ }
+}
+
+void HttpPipeliningCompatibilityClient::Request::Finished(Status result) {
+ if (finished_) {
+ return;
+ }
+ finished_ = true;
+ const net::URLRequestStatus& status = request_.status();
+ 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.
+ client_->ReportNetworkError(request_id_, status.error());
+ client_->OnRequestFinished(request_id_, NETWORK_ERROR);
+ return;
+ }
+ client_->OnRequestFinished(request_id_, result);
+}
+
+} // namespace chrome_browser_net
diff --git a/chrome/browser/net/http_pipelining_compatibility_client.h b/chrome/browser/net/http_pipelining_compatibility_client.h
new file mode 100644
index 0000000..1931b8c
--- /dev/null
+++ b/chrome/browser/net/http_pipelining_compatibility_client.h
@@ -0,0 +1,125 @@
+// 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.
+
+#ifndef CHROME_BROWSER_NET_HTTP_PIPELINING_COMPATIBILITY_CLIENT_H_
+#define CHROME_BROWSER_NET_HTTP_PIPELINING_COMPATIBILITY_CLIENT_H_
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "base/memory/scoped_vector.h"
+#include "net/base/completion_callback.h"
+#include "net/base/io_buffer.h"
+#include "net/url_request/url_request.h"
+
+namespace chrome_browser_net {
+
+// Class for performing a background test of users' Internet connections.
+// Fetches a collection of resources on a test server and verifies all were
+// received correctly. This will be used to determine whether or not proxies are
+// interfering with a user's ability to use HTTP pipelining. Results are
+// recorded with UMA.
+//
+// TODO(simonjam): Connect this to something. We should start with a field trial
+// that affects a subset of canary channel users. But first, we need a test
+// server.
+class HttpPipeliningCompatibilityClient {
+ public:
+ struct RequestInfo {
+ std::string filename; // The path relative to the test server's base_url.
+ std::string expected_response; // The expected body of the response.
+ };
+
+ enum Status {
+ SUCCESS,
+ REDIRECTED, // Response was redirected. We won't follow.
+ CERT_ERROR, // Any certificate problem.
+ BAD_RESPONSE_CODE, // Any non-200 response.
+ NETWORK_ERROR, // Any socket error reported by the network layer.
+ TOO_LARGE, // The response matched, but had extra data on the end.
+ TOO_SMALL, // The response was shorter than expected, but what we
+ // got matched.
+ CONTENT_MISMATCH, // The response didn't match the expected value.
+ BAD_HTTP_VERSION, // Any version older than HTTP/1.1.
+ STATUS_MAX,
+ };
+
+ HttpPipeliningCompatibilityClient();
+ ~HttpPipeliningCompatibilityClient();
+
+ // Launches the asynchronous URLRequests to fetch the URLs specified by
+ // |requests| combined with |base_url|. |base_url| should match the pattern
+ // "http://host/". |callback| is invoked once all the requests have completed.
+ // URLRequests are initiated in |url_request_context|. Results are recorded to
+ // UMA as they are received.
+ void Start(const std::string& base_url,
+ std::vector<RequestInfo>& requests,
+ const net::CompletionCallback& callback,
+ net::URLRequestContext* url_request_context);
+
+ private:
+ // There is one Request per RequestInfo passed in to Start() above.
+ class Request : public net::URLRequest::Delegate {
+ public:
+ Request(int request_id,
+ const std::string& base_url,
+ const RequestInfo& info,
+ HttpPipeliningCompatibilityClient* client,
+ net::URLRequestContext* url_request_context);
+ virtual ~Request();
+
+ // 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;
+
+ 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.
+ void DoReadFinished();
+
+ // Called when this request has determined its result. Returns the result to
+ // the |client_|.
+ void Finished(Status result);
+
+ const int request_id_;
+ net::URLRequest request_;
+ const RequestInfo info_;
+ HttpPipeliningCompatibilityClient* client_;
+ bool finished_;
+ scoped_refptr<net::IOBuffer> read_buffer_;
+ std::string response_;
+ };
+
+ // Called when a Request determines its result. Reports to UMA.
+ void OnRequestFinished(int request_id, Status status);
+
+ // Called when a Request encounters a network error. Reports to UMA.
+ void ReportNetworkError(int request_id, int error_code);
+
+ // Called when a Request determines its HTTP response code. Reports to UMA.
+ void ReportResponseCode(int request_id, int response_code);
+
+ // Returns the full UMA metric name based on |request_id| and |description|.
+ std::string GetMetricName(int request_id, const char* description);
+
+ ScopedVector<Request> requests_;
+ net::CompletionCallback finished_callback_;
+ size_t num_finished_;
+};
+
+} // namespace chrome_browser_net
+
+#endif // CHROME_BROWSER_NET_HTTP_PIPELINING_COMPATIBILITY_CLIENT_H_
diff --git a/chrome/browser/net/http_pipelining_compatibility_client_unittest.cc b/chrome/browser/net/http_pipelining_compatibility_client_unittest.cc
new file mode 100644
index 0000000..240d6ef
--- /dev/null
+++ b/chrome/browser/net/http_pipelining_compatibility_client_unittest.cc
@@ -0,0 +1,440 @@
+// 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 <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/message_loop.h"
+#include "base/metrics/histogram.h"
+#include "base/stl_util.h"
+#include "base/stringprintf.h"
+#include "chrome/test/base/test_url_request_context_getter.h"
+#include "content/test/test_browser_thread.h"
+#include "net/base/net_errors.h"
+#include "net/base/test_completion_callback.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "net/test/test_server.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chrome_browser_net {
+
+namespace {
+
+static const char* const kHistogramNames[] = {
+ "NetConnectivity.Pipeline.0.NetworkError",
+ "NetConnectivity.Pipeline.0.ResponseCode",
+ "NetConnectivity.Pipeline.0.Status",
+ "NetConnectivity.Pipeline.1.NetworkError",
+ "NetConnectivity.Pipeline.1.ResponseCode",
+ "NetConnectivity.Pipeline.1.Status",
+ "NetConnectivity.Pipeline.2.NetworkError",
+ "NetConnectivity.Pipeline.2.ResponseCode",
+ "NetConnectivity.Pipeline.2.Status",
+};
+
+enum HistogramField {
+ FIELD_NETWORK_ERROR,
+ FIELD_RESPONSE_CODE,
+ FIELD_STATUS,
+};
+
+class HttpPipeliningCompatibilityClientTest : public testing::Test {
+ public:
+ HttpPipeliningCompatibilityClientTest()
+ : test_server_(
+ net::TestServer::TYPE_HTTP,
+ FilePath(FILE_PATH_LITERAL("chrome/test/data/http_pipelining"))) {
+ }
+
+ protected:
+ virtual void SetUp() OVERRIDE {
+ ASSERT_TRUE(test_server_.Start());
+ context_ = new TestURLRequestContextGetter;
+ context_->AddRef();
+
+ for (size_t i = 0; i < arraysize(kHistogramNames); ++i) {
+ const char* name = kHistogramNames[i];
+ base::Histogram::SampleSet sample = GetHistogram(name);
+ if (sample.TotalCount() > 0) {
+ original_samples_[name] = sample;
+ }
+ }
+ }
+
+ virtual void TearDown() OVERRIDE {
+ content::BrowserThread::ReleaseSoon(content::BrowserThread::IO,
+ FROM_HERE, context_);
+ message_loop_.RunAllPending();
+ }
+
+ void RunTest(
+ std::vector<HttpPipeliningCompatibilityClient::RequestInfo> requests) {
+ HttpPipeliningCompatibilityClient client;
+ net::TestCompletionCallback callback;
+ client.Start(test_server_.GetURL("").spec(),
+ requests, callback.callback(),
+ context_->GetURLRequestContext());
+ callback.WaitForResult();
+
+ for (size_t i = 0; i < arraysize(kHistogramNames); ++i) {
+ const char* name = kHistogramNames[i];
+ base::Histogram::SampleSet sample = GetHistogram(name);
+ if (ContainsKey(original_samples_, name)) {
+ sample.Subtract(original_samples_[name]);
+ }
+ samples_[name] = sample;
+ }
+ }
+
+ base::Histogram::SampleSet GetHistogramValue(int request_id,
+ HistogramField field) {
+ const char* field_str = "";
+ switch (field) {
+ case FIELD_STATUS:
+ field_str = "Status";
+ break;
+
+ case FIELD_NETWORK_ERROR:
+ field_str = "NetworkError";
+ break;
+
+ case FIELD_RESPONSE_CODE:
+ field_str = "ResponseCode";
+ break;
+
+ default:
+ NOTREACHED();
+ break;
+ }
+
+ std::string name = base::StringPrintf("NetConnectivity.Pipeline.%d.%s",
+ request_id, field_str);
+ return samples_[name];
+ }
+
+ MessageLoopForIO message_loop_;
+ net::TestServer test_server_;
+ TestURLRequestContextGetter* context_;
+
+ private:
+ base::Histogram::SampleSet GetHistogram(const char* name) {
+ base::Histogram::SampleSet sample;
+ base::Histogram* histogram;
+ if (ContainsKey(histograms_, name)) {
+ histogram = histograms_[name];
+ histogram->SnapshotSample(&sample);
+ } else if (base::StatisticsRecorder::FindHistogram(name, &histogram)) {
+ histograms_[name] = histogram;
+ histogram->SnapshotSample(&sample);
+ }
+ return sample;
+ }
+
+ static std::map<std::string, base::Histogram*> histograms_;
+ std::map<std::string, base::Histogram::SampleSet> samples_;
+ std::map<std::string, base::Histogram::SampleSet> original_samples_;
+ base::StatisticsRecorder recorder_;
+};
+
+// static
+std::map<std::string, base::Histogram*>
+ HttpPipeliningCompatibilityClientTest::histograms_;
+
+TEST_F(HttpPipeliningCompatibilityClientTest, Success) {
+ HttpPipeliningCompatibilityClient::RequestInfo info;
+ info.filename = "files/alphabet.txt";
+ info.expected_response = "abcdefghijklmnopqrstuvwxyz";
+ std::vector<HttpPipeliningCompatibilityClient::RequestInfo> requests;
+ requests.push_back(info);
+
+ RunTest(requests);
+
+ base::Histogram::SampleSet status_sample =
+ GetHistogramValue(0, FIELD_STATUS);
+ EXPECT_EQ(1, status_sample.TotalCount());
+ EXPECT_EQ(1, status_sample.counts(
+ HttpPipeliningCompatibilityClient::SUCCESS));
+
+ base::Histogram::SampleSet network_sample =
+ GetHistogramValue(0, FIELD_NETWORK_ERROR);
+ EXPECT_EQ(0, network_sample.TotalCount());
+
+ base::Histogram::SampleSet response_sample =
+ GetHistogramValue(0, FIELD_RESPONSE_CODE);
+ EXPECT_EQ(1, response_sample.TotalCount());
+ EXPECT_EQ(1, response_sample.counts(200));
+}
+
+TEST_F(HttpPipeliningCompatibilityClientTest, TooSmall) {
+ HttpPipeliningCompatibilityClient::RequestInfo info;
+ info.filename = "files/alphabet.txt";
+ info.expected_response = "abcdefghijklmnopqrstuvwxyz26";
+ std::vector<HttpPipeliningCompatibilityClient::RequestInfo> requests;
+ requests.push_back(info);
+
+ RunTest(requests);
+
+ base::Histogram::SampleSet status_sample =
+ GetHistogramValue(0, FIELD_STATUS);
+ EXPECT_EQ(1, status_sample.TotalCount());
+ EXPECT_EQ(1, status_sample.counts(
+ HttpPipeliningCompatibilityClient::TOO_SMALL));
+
+ base::Histogram::SampleSet network_sample =
+ GetHistogramValue(0, FIELD_NETWORK_ERROR);
+ EXPECT_EQ(0, network_sample.TotalCount());
+
+ base::Histogram::SampleSet response_sample =
+ GetHistogramValue(0, FIELD_RESPONSE_CODE);
+ EXPECT_EQ(1, response_sample.TotalCount());
+ EXPECT_EQ(1, response_sample.counts(200));
+}
+
+TEST_F(HttpPipeliningCompatibilityClientTest, TooLarge) {
+ HttpPipeliningCompatibilityClient::RequestInfo info;
+ info.filename = "files/alphabet.txt";
+ info.expected_response = "abc";
+ std::vector<HttpPipeliningCompatibilityClient::RequestInfo> requests;
+ requests.push_back(info);
+
+ RunTest(requests);
+
+ base::Histogram::SampleSet status_sample =
+ GetHistogramValue(0, FIELD_STATUS);
+ EXPECT_EQ(1, status_sample.TotalCount());
+ EXPECT_EQ(1, status_sample.counts(
+ HttpPipeliningCompatibilityClient::TOO_LARGE));
+
+ base::Histogram::SampleSet network_sample =
+ GetHistogramValue(0, FIELD_NETWORK_ERROR);
+ EXPECT_EQ(0, network_sample.TotalCount());
+
+ base::Histogram::SampleSet response_sample =
+ GetHistogramValue(0, FIELD_RESPONSE_CODE);
+ EXPECT_EQ(1, response_sample.TotalCount());
+ EXPECT_EQ(1, response_sample.counts(200));
+}
+
+TEST_F(HttpPipeliningCompatibilityClientTest, Mismatch) {
+ HttpPipeliningCompatibilityClient::RequestInfo info;
+ info.filename = "files/alphabet.txt";
+ info.expected_response = "zyxwvutsrqponmlkjihgfedcba";
+ std::vector<HttpPipeliningCompatibilityClient::RequestInfo> requests;
+ requests.push_back(info);
+
+ RunTest(requests);
+
+ base::Histogram::SampleSet status_sample =
+ GetHistogramValue(0, FIELD_STATUS);
+ EXPECT_EQ(1, status_sample.TotalCount());
+ EXPECT_EQ(1, status_sample.counts(
+ HttpPipeliningCompatibilityClient::CONTENT_MISMATCH));
+
+ base::Histogram::SampleSet network_sample =
+ GetHistogramValue(0, FIELD_NETWORK_ERROR);
+ EXPECT_EQ(0, network_sample.TotalCount());
+
+ base::Histogram::SampleSet response_sample =
+ GetHistogramValue(0, FIELD_RESPONSE_CODE);
+ EXPECT_EQ(1, response_sample.TotalCount());
+ EXPECT_EQ(1, response_sample.counts(200));
+}
+
+TEST_F(HttpPipeliningCompatibilityClientTest, Redirect) {
+ HttpPipeliningCompatibilityClient::RequestInfo info;
+ info.filename = "server-redirect?http://foo.bar/asdf";
+ info.expected_response = "shouldn't matter";
+ std::vector<HttpPipeliningCompatibilityClient::RequestInfo> requests;
+ requests.push_back(info);
+
+ RunTest(requests);
+
+ base::Histogram::SampleSet status_sample =
+ GetHistogramValue(0, FIELD_STATUS);
+ EXPECT_EQ(1, status_sample.TotalCount());
+ EXPECT_EQ(1, status_sample.counts(
+ HttpPipeliningCompatibilityClient::REDIRECTED));
+
+ base::Histogram::SampleSet network_sample =
+ GetHistogramValue(0, FIELD_NETWORK_ERROR);
+ EXPECT_EQ(0, network_sample.TotalCount());
+
+ base::Histogram::SampleSet response_sample =
+ GetHistogramValue(0, FIELD_RESPONSE_CODE);
+ EXPECT_EQ(0, response_sample.TotalCount());
+}
+
+TEST_F(HttpPipeliningCompatibilityClientTest, AuthRequired) {
+ HttpPipeliningCompatibilityClient::RequestInfo info;
+ info.filename = "auth-basic";
+ info.expected_response = "shouldn't matter";
+ std::vector<HttpPipeliningCompatibilityClient::RequestInfo> requests;
+ requests.push_back(info);
+
+ RunTest(requests);
+
+ base::Histogram::SampleSet status_sample =
+ GetHistogramValue(0, FIELD_STATUS);
+ EXPECT_EQ(1, status_sample.TotalCount());
+ EXPECT_EQ(1, status_sample.counts(
+ HttpPipeliningCompatibilityClient::BAD_RESPONSE_CODE));
+
+ base::Histogram::SampleSet network_sample =
+ GetHistogramValue(0, FIELD_NETWORK_ERROR);
+ EXPECT_EQ(0, network_sample.TotalCount());
+
+ base::Histogram::SampleSet response_sample =
+ GetHistogramValue(0, FIELD_RESPONSE_CODE);
+ EXPECT_EQ(1, response_sample.TotalCount());
+ EXPECT_EQ(1, response_sample.counts(401));
+}
+
+TEST_F(HttpPipeliningCompatibilityClientTest, NoContent) {
+ HttpPipeliningCompatibilityClient::RequestInfo info;
+ info.filename = "nocontent";
+ info.expected_response = "shouldn't matter";
+ std::vector<HttpPipeliningCompatibilityClient::RequestInfo> requests;
+ requests.push_back(info);
+
+ RunTest(requests);
+
+ base::Histogram::SampleSet status_sample =
+ GetHistogramValue(0, FIELD_STATUS);
+ EXPECT_EQ(1, status_sample.TotalCount());
+ EXPECT_EQ(1, status_sample.counts(
+ HttpPipeliningCompatibilityClient::BAD_RESPONSE_CODE));
+
+ base::Histogram::SampleSet network_sample =
+ GetHistogramValue(0, FIELD_NETWORK_ERROR);
+ EXPECT_EQ(0, network_sample.TotalCount());
+
+ base::Histogram::SampleSet response_sample =
+ GetHistogramValue(0, FIELD_RESPONSE_CODE);
+ EXPECT_EQ(1, response_sample.TotalCount());
+ EXPECT_EQ(1, response_sample.counts(204));
+}
+
+TEST_F(HttpPipeliningCompatibilityClientTest, CloseSocket) {
+ HttpPipeliningCompatibilityClient::RequestInfo info;
+ info.filename = "close-socket";
+ info.expected_response = "shouldn't matter";
+ std::vector<HttpPipeliningCompatibilityClient::RequestInfo> requests;
+ requests.push_back(info);
+
+ RunTest(requests);
+
+ base::Histogram::SampleSet status_sample =
+ GetHistogramValue(0, FIELD_STATUS);
+ EXPECT_EQ(1, status_sample.TotalCount());
+ EXPECT_EQ(1, status_sample.counts(
+ HttpPipeliningCompatibilityClient::NETWORK_ERROR));
+
+ base::Histogram::SampleSet network_sample =
+ GetHistogramValue(0, FIELD_NETWORK_ERROR);
+ EXPECT_EQ(1, network_sample.TotalCount());
+ EXPECT_EQ(1, network_sample.counts(-net::ERR_EMPTY_RESPONSE));
+
+ base::Histogram::SampleSet response_sample =
+ GetHistogramValue(0, FIELD_RESPONSE_CODE);
+ EXPECT_EQ(0, response_sample.TotalCount());
+}
+
+TEST_F(HttpPipeliningCompatibilityClientTest, OldHttpVersion) {
+ HttpPipeliningCompatibilityClient::RequestInfo info;
+ info.filename = "http-1.0";
+ info.expected_response = "abcdefghijklmnopqrstuvwxyz";
+ std::vector<HttpPipeliningCompatibilityClient::RequestInfo> requests;
+ requests.push_back(info);
+
+ RunTest(requests);
+
+ base::Histogram::SampleSet status_sample =
+ GetHistogramValue(0, FIELD_STATUS);
+ EXPECT_EQ(1, status_sample.TotalCount());
+ EXPECT_EQ(1, status_sample.counts(
+ HttpPipeliningCompatibilityClient::BAD_HTTP_VERSION));
+
+ base::Histogram::SampleSet network_sample =
+ GetHistogramValue(0, FIELD_NETWORK_ERROR);
+ EXPECT_EQ(0, network_sample.TotalCount());
+
+ base::Histogram::SampleSet response_sample =
+ GetHistogramValue(0, FIELD_RESPONSE_CODE);
+ EXPECT_EQ(1, response_sample.TotalCount());
+ EXPECT_EQ(1, response_sample.counts(200));
+}
+
+TEST_F(HttpPipeliningCompatibilityClientTest, MultipleRequests) {
+ std::vector<HttpPipeliningCompatibilityClient::RequestInfo> requests;
+
+ HttpPipeliningCompatibilityClient::RequestInfo info1;
+ info1.filename = "files/alphabet.txt";
+ info1.expected_response = "abcdefghijklmnopqrstuvwxyz";
+ requests.push_back(info1);
+
+ HttpPipeliningCompatibilityClient::RequestInfo info2;
+ info2.filename = "close-socket";
+ info2.expected_response = "shouldn't matter";
+ requests.push_back(info2);
+
+ HttpPipeliningCompatibilityClient::RequestInfo info3;
+ info3.filename = "auth-basic";
+ info3.expected_response = "shouldn't matter";
+ requests.push_back(info3);
+
+ RunTest(requests);
+
+ base::Histogram::SampleSet status_sample1 =
+ GetHistogramValue(0, FIELD_STATUS);
+ EXPECT_EQ(1, status_sample1.TotalCount());
+ EXPECT_EQ(1, status_sample1.counts(
+ HttpPipeliningCompatibilityClient::SUCCESS));
+
+ base::Histogram::SampleSet network_sample1 =
+ GetHistogramValue(0, FIELD_NETWORK_ERROR);
+ EXPECT_EQ(0, network_sample1.TotalCount());
+
+ base::Histogram::SampleSet response_sample1 =
+ GetHistogramValue(0, FIELD_RESPONSE_CODE);
+ EXPECT_EQ(1, response_sample1.TotalCount());
+ EXPECT_EQ(1, response_sample1.counts(200));
+
+ base::Histogram::SampleSet status_sample2 =
+ GetHistogramValue(1, FIELD_STATUS);
+ EXPECT_EQ(1, status_sample2.TotalCount());
+ EXPECT_EQ(1, status_sample2.counts(
+ HttpPipeliningCompatibilityClient::NETWORK_ERROR));
+
+ base::Histogram::SampleSet network_sample2 =
+ GetHistogramValue(1, FIELD_NETWORK_ERROR);
+ EXPECT_EQ(1, network_sample2.TotalCount());
+ EXPECT_EQ(1, network_sample2.counts(-net::ERR_EMPTY_RESPONSE));
+
+ base::Histogram::SampleSet response_sample2 =
+ GetHistogramValue(1, FIELD_RESPONSE_CODE);
+ EXPECT_EQ(0, response_sample2.TotalCount());
+
+ base::Histogram::SampleSet status_sample3 =
+ GetHistogramValue(2, FIELD_STATUS);
+ EXPECT_EQ(1, status_sample3.TotalCount());
+ EXPECT_EQ(1, status_sample3.counts(
+ HttpPipeliningCompatibilityClient::BAD_RESPONSE_CODE));
+
+ base::Histogram::SampleSet network_sample3 =
+ GetHistogramValue(2, FIELD_NETWORK_ERROR);
+ EXPECT_EQ(0, network_sample3.TotalCount());
+
+ base::Histogram::SampleSet response_sample3 =
+ GetHistogramValue(2, FIELD_RESPONSE_CODE);
+ EXPECT_EQ(1, response_sample3.TotalCount());
+ EXPECT_EQ(1, response_sample3.counts(401));
+}
+
+} // anonymous namespace
+
+} // namespace chrome_browser_net
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 13897c9..81c969c 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -1674,6 +1674,8 @@
'browser/net/gaia/gaia_oauth_fetcher.h',
'browser/net/load_timing_observer.cc',
'browser/net/load_timing_observer.h',
+ 'browser/net/http_pipelining_compatibility_client.cc',
+ 'browser/net/http_pipelining_compatibility_client.h',
'browser/net/net_log_logger.cc',
'browser/net/net_log_logger.h',
'browser/net/net_pref_observer.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 0745992..a7910c4 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1531,6 +1531,7 @@
'browser/net/chrome_net_log_unittest.cc',
'browser/net/connection_tester_unittest.cc',
'browser/net/gaia/gaia_oauth_fetcher_unittest.cc',
+ 'browser/net/http_pipelining_compatibility_client_unittest.cc',
'browser/net/http_server_properties_manager_unittest.cc',
'browser/net/load_timing_observer_unittest.cc',
'browser/net/network_stats_unittest.cc',
diff --git a/chrome/test/data/http_pipelining/alphabet.txt b/chrome/test/data/http_pipelining/alphabet.txt
new file mode 100644
index 0000000..e85d5b4
--- /dev/null
+++ b/chrome/test/data/http_pipelining/alphabet.txt
@@ -0,0 +1 @@
+abcdefghijklmnopqrstuvwxyz \ No newline at end of file
diff --git a/chrome/test/data/http_pipelining/alphabet.txt.mock-http-headers b/chrome/test/data/http_pipelining/alphabet.txt.mock-http-headers
new file mode 100644
index 0000000..a919d52
--- /dev/null
+++ b/chrome/test/data/http_pipelining/alphabet.txt.mock-http-headers
@@ -0,0 +1 @@
+HTTP/1.1 200 OK
diff --git a/net/base/net_error_list.h b/net/base/net_error_list.h
index 37f5f31..8f27cd7 100644
--- a/net/base/net_error_list.h
+++ b/net/base/net_error_list.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// 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.
@@ -522,6 +522,9 @@ NET_ERROR(SPDY_SERVER_REFUSED_STREAM, -351)
// SPDY server didn't respond to the PING message.
NET_ERROR(SPDY_PING_FAILED, -352)
+// The request couldn't be completed on an HTTP pipeline. Client should retry.
+NET_ERROR(PIPELINE_EVICTION, -353)
+
// The cache does not have the requested entry.
NET_ERROR(CACHE_MISS, -400)
@@ -651,6 +654,3 @@ NET_ERROR(DNS_TIMED_OUT, -803)
// The entry was not found in cache, for cache-only lookups.
NET_ERROR(DNS_CACHE_MISS, -804)
-
-// FIXME: Take the next number.
-NET_ERROR(PIPELINE_EVICTION, -900)
diff --git a/net/tools/testserver/testserver.py b/net/tools/testserver/testserver.py
index f917789..12882b5 100755
--- a/net/tools/testserver/testserver.py
+++ b/net/tools/testserver/testserver.py
@@ -378,6 +378,7 @@ class TestPageHandler(BasePageHandler):
self.MultipartHandler,
self.MultipartSlowHandler,
self.GetSSLSessionCacheHandler,
+ self.CloseSocketHandler,
self.DefaultResponseHandler]
post_handlers = [
self.EchoTitleHandler,
@@ -922,6 +923,7 @@ class TestPageHandler(BasePageHandler):
return self._FileHandlerHelper(prefix)
def _FileHandlerHelper(self, prefix):
+ old_protocol_version = self.protocol_version
_, _, url_path, _, query, _ = urlparse.urlparse(self.path)
sub_path = url_path[len(prefix):]
entries = sub_path.split('/')
@@ -948,7 +950,9 @@ class TestPageHandler(BasePageHandler):
# "HTTP/1.1 200 OK"
response = f.readline()
- status_code = re.findall('HTTP/\d+.\d+ (\d+)', response)[0]
+ http_major, http_minor, status_code = re.findall(
+ 'HTTP/(\d+).(\d+) (\d+)', response)[0]
+ self.protocol_version = "HTTP/%s.%s" % (http_major, http_minor)
self.send_response(int(status_code))
for line in f:
@@ -990,6 +994,7 @@ class TestPageHandler(BasePageHandler):
if (self.command != 'HEAD'):
self.wfile.write(data)
+ self.protocol_version = old_protocol_version
return True
def SetCookieHandler(self):
@@ -1427,6 +1432,15 @@ class TestPageHandler(BasePageHandler):
' this request')
return True
+ def CloseSocketHandler(self):
+ """Closes the socket without sending anything."""
+
+ if not self._ShouldHandleRequest('/close-socket'):
+ return False
+
+ self.wfile.close()
+ return True
+
def DefaultResponseHandler(self):
"""This is the catch-all response handler for requests that aren't handled
by one of the special handlers above.