summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/net/DEPS6
-rw-r--r--chrome/browser/net/sdch_browsertest.cc617
-rw-r--r--chrome/browser/profiles/off_the_record_profile_io_data.cc19
-rw-r--r--chrome/browser/profiles/profile_impl_io_data.cc19
-rw-r--r--chrome/chrome_tests.gypi2
-rw-r--r--net/base/sdch_dictionary_fetcher.cc2
-rw-r--r--net/base/sdch_dictionary_fetcher.h4
-rw-r--r--net/base/sdch_manager.cc25
-rw-r--r--net/base/sdch_manager.h8
-rw-r--r--net/filter/mock_filter_context.cc8
-rw-r--r--net/filter/mock_filter_context.h6
-rw-r--r--net/filter/sdch_filter_unittest.cc26
-rw-r--r--net/http/http_transaction_test_util.h2
-rw-r--r--net/url_request/url_request_context_storage.cc7
-rw-r--r--net/url_request/url_request_context_storage.h3
-rw-r--r--net/url_request/url_request_http_job_unittest.cc49
-rw-r--r--net/url_request/url_request_test_util.h5
-rw-r--r--sdch/sdch.gyp4
18 files changed, 774 insertions, 38 deletions
diff --git a/chrome/browser/net/DEPS b/chrome/browser/net/DEPS
index 8e71f2d..fc675bc 100644
--- a/chrome/browser/net/DEPS
+++ b/chrome/browser/net/DEPS
@@ -2,3 +2,9 @@ include_rules = [
# For nss_context_chromeos_browsertest.cc.
"+components/user_manager",
]
+
+specific_include_rules = {
+ ".*sdch_browsertest.cc": [
+ "+sdch/open-vcdiff",
+ ],
+}
diff --git a/chrome/browser/net/sdch_browsertest.cc b/chrome/browser/net/sdch_browsertest.cc
new file mode 100644
index 0000000..76567e2
--- /dev/null
+++ b/chrome/browser/net/sdch_browsertest.cc
@@ -0,0 +1,617 @@
+// Copyright 2014 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.
+
+// End-to-end SDCH tests. Uses the embedded test server to return SDCH
+// results
+
+#include "base/base64.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/command_line.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/weak_ptr.h"
+#include "base/path_service.h"
+#include "base/run_loop.h"
+#include "base/strings/string_tokenizer.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/browsing_data/browsing_data_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_remover.h"
+#include "chrome/browser/browsing_data/browsing_data_remover_test_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_tabstrip.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/test_utils.h"
+#include "crypto/sha2.h"
+#include "net/base/sdch_manager.h"
+#include "net/http/http_response_headers.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_fetcher_delegate.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "sdch/open-vcdiff/src/google/vcencoder.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_CHROMEOS)
+#include "chromeos/chromeos_switches.h"
+#endif
+
+namespace {
+
+typedef std::vector<net::test_server::HttpRequest> RequestVector;
+typedef std::map<std::string, std::string> HttpRequestHeaderMap;
+
+// Credit Alfred, Lord Tennyson
+static const char kSampleData[] = "<html><body><pre>"
+ "There lies the port; the vessel puffs her sail:\n"
+ "There gloom the dark, broad seas. My mariners,\n"
+ "Souls that have toil'd, and wrought, and thought with me—\n"
+ "That ever with a frolic welcome took\n"
+ "The thunder and the sunshine, and opposed\n"
+ "Free hearts, free foreheads—you and I are old;\n"
+ "Old age hath yet his honour and his toil;\n"
+ "Death closes all: but something ere the end,\n"
+ "Some work of noble note, may yet be done,\n"
+ "Not unbecoming men that strove with Gods.\n"
+ "The lights begin to twinkle from the rocks:\n"
+ "The long day wanes: the slow moon climbs: the deep\n"
+ "Moans round with many voices. Come, my friends,\n"
+ "'T is not too late to seek a newer world.\n"
+ "Push off, and sitting well in order smite\n"
+ "The sounding furrows; for my purpose holds\n"
+ "To sail beyond the sunset, and the baths\n"
+ "Of all the western stars, until I die.\n"
+ "It may be that the gulfs will wash us down:\n"
+ "It may be we shall touch the Happy Isles,\n"
+ "And see the great Achilles, whom we knew.\n"
+ "Tho' much is taken, much abides; and tho'\n"
+ "We are not now that strength which in old days\n"
+ "Moved earth and heaven, that which we are, we are;\n"
+ "One equal temper of heroic hearts,\n"
+ "Made weak by time and fate, but strong in will\n"
+ "To strive, to seek, to find, and not to yield.\n"
+ "</pre></body></html>";
+
+// Random selection of lines from above, to allow some encoding, but
+// not a trivial encoding.
+static const char kDictionaryContents[] =
+ "The thunder and the sunshine, and opposed\n"
+ "To sail beyond the sunset, and the baths\n"
+ "Of all the western stars, until I die.\n"
+ "Made weak by time and fate, but strong in will\n"
+ "Moans round with many voices. Come, my friends,\n"
+ "The lights begin to twinkle from the rocks:";
+
+static const char kDictionaryURLPath[] = "/dict";
+static const char kDataURLPath[] = "/data";
+
+// Scans in a case-insensitive way for |header| in |map|,
+// returning true if found and setting |*value| to the value
+// of that header. Does not handle multiple instances of the same
+// header.
+bool GetRequestHeader(const HttpRequestHeaderMap& map,
+ const char* header,
+ std::string* value) {
+ for (HttpRequestHeaderMap::const_iterator it = map.begin();
+ it != map.end(); ++it) {
+ if (!base::strcasecmp(it->first.c_str(), header)) {
+ *value = it->second;
+ return true;
+ }
+ }
+ return false;
+}
+
+// Do a URL-safe base64 encoding. See the SDCH spec "Dictionary Identifier"
+// section, and RFC 3548 section 4.
+void SafeBase64Encode(const std::string& input_value, std::string* output) {
+ DCHECK(output);
+ base::Base64Encode(input_value, output);
+ std::replace(output->begin(), output->end(), '+', '-');
+ std::replace(output->begin(), output->end(), '/', '_');
+}
+
+// Class that bundles responses for an EmbeddedTestServer().
+// Dictionary is at <domain>/dict, data at <domain>/data.
+// The data is sent SDCH encoded if that's allowed by protoocol.
+class SdchResponseHandler {
+ public:
+ // Do initial preparation so that SDCH requests can be handled.
+ explicit SdchResponseHandler(std::string domain)
+ : cache_sdch_response_(false),
+ weak_ptr_factory_(this) {
+ // Dictionary
+ sdch_dictionary_contents_ = "Domain: ";
+ sdch_dictionary_contents_ += domain;
+ sdch_dictionary_contents_ += "\n\n";
+ sdch_dictionary_contents_ += kDictionaryContents;
+
+ // Dictionary hash for client and server.
+ char binary_hash[32];
+ crypto::SHA256HashString(sdch_dictionary_contents_, binary_hash,
+ sizeof(binary_hash));
+ SafeBase64Encode(std::string(&binary_hash[0], 6), &dictionary_client_hash_);
+ SafeBase64Encode(std::string(&binary_hash[6], 6), &dictionary_server_hash_);
+
+ // Encoded response.
+ open_vcdiff::HashedDictionary vcdiff_dictionary(
+ kDictionaryContents, strlen(kDictionaryContents));
+ bool result = vcdiff_dictionary.Init();
+ DCHECK(result);
+ open_vcdiff::VCDiffStreamingEncoder encoder(&vcdiff_dictionary, 0, false);
+ encoded_data_ = dictionary_server_hash_;
+ encoded_data_ += '\0';
+ result = encoder.StartEncoding(&encoded_data_);
+ DCHECK(result);
+ result = encoder.EncodeChunk(
+ kSampleData, strlen(kSampleData), &encoded_data_);
+ DCHECK(result);
+ result = encoder.FinishEncoding(&encoded_data_);
+ DCHECK(result);
+ }
+
+ static bool ClientIsAdvertisingSdchEncoding(const HttpRequestHeaderMap& map) {
+ std::string value;
+ if (!GetRequestHeader(map, "accept-encoding", &value))
+ return false;
+ base::StringTokenizer tokenizer(value, " ,");
+ while (tokenizer.GetNext()) {
+ if (base::strcasecmp(tokenizer.token().c_str(), "sdch"))
+ return true;
+ }
+ return false;
+ }
+
+ bool ShouldRespondWithSdchEncoding(const HttpRequestHeaderMap& map) {
+ std::string value;
+ if (!GetRequestHeader(map, "avail-dictionary", &value))
+ return false;
+ return value == dictionary_client_hash_;
+ }
+
+ scoped_ptr<net::test_server::HttpResponse> HandleRequest(
+ const net::test_server::HttpRequest& request) {
+ request_vector_.push_back(request);
+
+ scoped_ptr<net::test_server::BasicHttpResponse> response(
+ new net::test_server::BasicHttpResponse);
+ if (request.relative_url == kDataURLPath) {
+ if (ShouldRespondWithSdchEncoding(request.headers)) {
+ // Note that chrome doesn't advertise accepting SDCH encoding
+ // for POSTs (because the meta-refresh hack would break a POST),
+ // but that's not for the server to enforce.
+ DCHECK_NE(encoded_data_, "");
+ response->set_content_type("text/html");
+ response->set_content(encoded_data_);
+ response->AddCustomHeader("Content-Encoding", "sdch");
+ // We allow tests to set caching on the sdch response,
+ // so that we can force an encoded response with no
+ // dictionary.
+ if (cache_sdch_response_)
+ response->AddCustomHeader("Cache-Control", "max-age=3600");
+ else
+ response->AddCustomHeader("Cache-Control", "no-store");
+ } else {
+ response->set_content_type("text/plain");
+ response->set_content(kSampleData);
+ if (ClientIsAdvertisingSdchEncoding(request.headers))
+ response->AddCustomHeader("Get-Dictionary", kDictionaryURLPath);
+ // We never cache the plain data response, to make it
+ // easy to refresh after we get the dictionary.
+ response->AddCustomHeader("Cache-Control", "no-store");
+ }
+ } else {
+ DCHECK_EQ(request.relative_url, kDictionaryURLPath);
+ DCHECK_NE(sdch_dictionary_contents_, "");
+ response->set_content_type("application/x-sdch-dictionary");
+ response->set_content(sdch_dictionary_contents_);
+ }
+ std::vector<base::Closure> callbacks;
+ callbacks.swap(callback_vector_);
+ for (std::vector<base::Closure>::iterator it = callbacks.begin();
+ it != callbacks.end(); ++it) {
+ it->Run();
+ }
+ return response.PassAs<net::test_server::HttpResponse>();
+ }
+
+ void WaitAndGetRequestVector(int num_requests,
+ base::Closure callback,
+ RequestVector* v) {
+ DCHECK_LT(0, num_requests);
+ if (static_cast<size_t>(num_requests) > request_vector_.size()) {
+ callback_vector_.push_back(
+ base::Bind(&SdchResponseHandler::WaitAndGetRequestVector,
+ weak_ptr_factory_.GetWeakPtr(), num_requests,
+ callback, v));
+ return;
+ }
+ *v = request_vector_;
+ content::BrowserThread::PostTask(
+ content::BrowserThread::UI, FROM_HERE, callback);
+ }
+
+ void set_cache_sdch_response(bool cache_sdch_response) {
+ cache_sdch_response_ = cache_sdch_response;
+ }
+
+ private:
+ bool cache_sdch_response_;
+ std::string encoded_data_;
+ std::string sdch_dictionary_contents_;
+ std::string dictionary_client_hash_;
+ std::string dictionary_server_hash_;
+ RequestVector request_vector_;
+ std::vector<base::Closure> callback_vector_;
+ base::WeakPtrFactory<SdchResponseHandler> weak_ptr_factory_;
+};
+
+class SdchBrowserTest : public InProcessBrowserTest, net::URLFetcherDelegate {
+ public:
+ static const char kTestHost[];
+
+ SdchBrowserTest()
+ : response_handler_(kTestHost),
+ url_request_context_getter_(NULL),
+ url_fetch_complete_(false),
+ waiting_(false) {}
+
+ // Helper functions for fetching data.
+
+ void FetchUrlDetailed(GURL url, net::URLRequestContextGetter* getter) {
+ url_fetch_complete_ = false;
+ fetcher_.reset(net::URLFetcher::Create(url, net::URLFetcher::GET, this));
+ fetcher_->SetRequestContext(getter);
+ fetcher_->Start();
+ if (!url_fetch_complete_) {
+ waiting_ = true;
+ content::RunMessageLoop();
+ waiting_ = false;
+ }
+ CHECK(url_fetch_complete_);
+ }
+
+ void FetchUrl(GURL url) {
+ FetchUrlDetailed(url, url_request_context_getter_);
+ }
+
+ const net::URLRequestStatus& FetcherStatus() const {
+ return fetcher_->GetStatus();
+ }
+
+ int FetcherResponseCode() const {
+ return (fetcher_->GetStatus().status() == net::URLRequestStatus::SUCCESS ?
+ fetcher_->GetResponseCode() : 0);
+ }
+
+ const net::HttpResponseHeaders* FetcherResponseHeaders() const {
+ return (fetcher_->GetStatus().status() == net::URLRequestStatus::SUCCESS ?
+ fetcher_->GetResponseHeaders() : NULL);
+ }
+
+ std::string FetcherResponseContents() const {
+ std::string contents;
+ if (fetcher_->GetStatus().status() == net::URLRequestStatus::SUCCESS)
+ CHECK(fetcher_->GetResponseAsString(&contents));
+ return contents;
+ }
+
+ // Get the data from the server. Return value is success/failure of the
+ // data operation, |*sdch_encoding_used| indicates whether or not the
+ // data was retrieved with sdch encoding.
+ // This is done through FetchUrl(), so the various helper functions
+ // will have valid status if it returns successfully.
+ bool GetDataDetailed(net::URLRequestContextGetter* getter,
+ bool* sdch_encoding_used) {
+ FetchUrlDetailed(
+ GURL(base::StringPrintf(
+ "http://%s:%d%s", kTestHost, test_server_port(), kDataURLPath)),
+ getter);
+ EXPECT_EQ(net::URLRequestStatus::SUCCESS, FetcherStatus().status())
+ << "Error code is " << FetcherStatus().error();
+ EXPECT_EQ(200, FetcherResponseCode());
+ EXPECT_EQ(kSampleData, FetcherResponseContents());
+
+ if (net::URLRequestStatus::SUCCESS != FetcherStatus().status() ||
+ 200 != FetcherResponseCode()) {
+ *sdch_encoding_used = false;
+ return false;
+ }
+
+ *sdch_encoding_used =
+ FetcherResponseHeaders()->HasHeaderValue("Content-Encoding", "sdch");
+
+ if (FetcherResponseContents() != kSampleData)
+ return false;
+
+ return true;
+ }
+
+ bool GetData(bool* sdch_encoding_used) {
+ return GetDataDetailed(
+ url_request_context_getter_, sdch_encoding_used);
+ }
+
+ // Client information and control.
+
+ int GetNumberOfDictionaryFetches(Profile* profile) {
+ int fetches = -1;
+ base::RunLoop run_loop;
+ content::BrowserThread::PostTaskAndReply(
+ content::BrowserThread::IO, FROM_HERE,
+ base::Bind(&SdchBrowserTest::GetNumberOfDictionaryFetchesOnIOThread,
+ base::Unretained(profile->GetRequestContext()),
+ &fetches),
+ run_loop.QuitClosure());
+ run_loop.Run();
+ DCHECK_NE(-1, fetches);
+ return fetches;
+ }
+
+ void BrowsingDataRemoveAndWait(int remove_mask) {
+ BrowsingDataRemover* remover = BrowsingDataRemover::CreateForPeriod(
+ browser()->profile(), BrowsingDataRemover::LAST_HOUR);
+ BrowsingDataRemoverCompletionObserver completion_observer(remover);
+ remover->Remove(remove_mask, BrowsingDataHelper::UNPROTECTED_WEB);
+ completion_observer.BlockUntilCompletion();
+ }
+
+ // Something of a cheat; nuke the dictionaries off the SdchManager without
+ // touching the cache (which browsing data remover would do).
+ void NukeSdchDictionaries() {
+ base::RunLoop run_loop;
+ content::BrowserThread::PostTaskAndReply(
+ content::BrowserThread::IO, FROM_HERE,
+ base::Bind(&SdchBrowserTest::NukeSdchDictionariesOnIOThread,
+ url_request_context_getter_),
+ run_loop.QuitClosure());
+ run_loop.Run();
+ }
+
+ // Create a second browser based on a second profile to work within
+ // multi-profile.
+ bool SetupSecondBrowser() {
+ base::FilePath user_data_dir;
+ PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
+
+ if (!second_profile_data_dir_.CreateUniqueTempDirUnderPath(user_data_dir))
+ return false;
+
+ second_profile_ = g_browser_process->profile_manager()->GetProfile(
+ second_profile_data_dir_.path());
+ if (!second_profile_) return false;
+
+ second_browser_ = new Browser(Browser::CreateParams(
+ second_profile_, browser()->host_desktop_type()));
+ if (!second_browser_) return false;
+
+ chrome::AddSelectedTabWithURL(second_browser_,
+ GURL(url::kAboutBlankURL),
+ content::PAGE_TRANSITION_AUTO_TOPLEVEL);
+ content::WaitForLoadStop(
+ second_browser_->tab_strip_model()->GetActiveWebContents());
+ second_browser_->window()->Show();
+
+ return true;
+ }
+
+ Browser* second_browser() { return second_browser_; }
+
+ // Server information and control.
+
+ void WaitAndGetTestVector(int num_requests, RequestVector* result) {
+ base::RunLoop run_loop;
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO, FROM_HERE,
+ base::Bind(&SdchResponseHandler::WaitAndGetRequestVector,
+ base::Unretained(&response_handler_),
+ num_requests,
+ run_loop.QuitClosure(),
+ result));
+ run_loop.Run();
+ }
+
+ int test_server_port() { return test_server_.port(); }
+
+ void SetSdchCacheability(bool cache_sdch_response) {
+ base::RunLoop run_loop;
+ content::BrowserThread::PostTaskAndReply(
+ content::BrowserThread::IO, FROM_HERE,
+ base::Bind(&SdchResponseHandler::set_cache_sdch_response,
+ base::Unretained(&response_handler_),
+ cache_sdch_response),
+ run_loop.QuitClosure());
+ run_loop.Run();
+ }
+
+ // Helper function for common test pattern.
+ //
+ // This function gets the data, confirms that the initial sending of the
+ // data included a dictionary advertisement, that that advertisement
+ // resulted in queueing a dictionary fetch, forces that fetch to
+ // go through, and confirms that a follow-on data load uses SDCH
+ // encoding. Returns true if the entire sequence of events occurred.
+ bool ForceSdchDictionaryLoad(Browser* browser) {
+ bool sdch_encoding_used = true;
+ bool data_gotten = GetDataDetailed(
+ browser->profile()->GetRequestContext(), &sdch_encoding_used);
+ EXPECT_TRUE(data_gotten);
+ if (!data_gotten) return false;
+ EXPECT_FALSE(sdch_encoding_used);
+
+ // Confirm that we were told to get the dictionary
+ const net::HttpResponseHeaders* headers = FetcherResponseHeaders();
+ std::string value;
+ bool have_dict_header =
+ headers->EnumerateHeader(NULL, "Get-Dictionary", &value);
+ EXPECT_TRUE(have_dict_header);
+ if (!have_dict_header) return false;
+
+ // If the above didn't result in a dictionary fetch being queued, the
+ // rest of the test will time out. Avoid that.
+ int num_fetches = GetNumberOfDictionaryFetches(browser->profile());
+ EXPECT_EQ(1, num_fetches);
+ if (1 != num_fetches) return false;
+
+ // Wait until the dictionary fetch actually happens.
+ RequestVector request_vector;
+ WaitAndGetTestVector(2, &request_vector);
+ EXPECT_EQ(request_vector[1].relative_url, kDictionaryURLPath);
+ if (request_vector[1].relative_url != kDictionaryURLPath) return false;
+
+ // Do a round trip to the server ignoring the encoding, presuming
+ // that if we've gotten data to this thread, the dictionary's made
+ // it into the SdchManager.
+ data_gotten = GetDataDetailed(
+ browser->profile()->GetRequestContext(), &sdch_encoding_used);
+ EXPECT_TRUE(data_gotten);
+ if (!data_gotten) return false;
+
+ // Now data fetches should be SDCH encoded.
+ sdch_encoding_used = false;
+ data_gotten = GetDataDetailed(
+ browser->profile()->GetRequestContext(), &sdch_encoding_used);
+ EXPECT_TRUE(data_gotten);
+ EXPECT_TRUE(sdch_encoding_used);
+
+ if (!data_gotten || !sdch_encoding_used) return false;
+
+ // Confirm the request vector looks at this point as expected.
+ WaitAndGetTestVector(4, &request_vector);
+ EXPECT_EQ(4u, request_vector.size());
+ EXPECT_EQ(request_vector[2].relative_url, kDataURLPath);
+ EXPECT_EQ(request_vector[3].relative_url, kDataURLPath);
+ return (4u == request_vector.size() &&
+ request_vector[2].relative_url == kDataURLPath &&
+ request_vector[3].relative_url == kDataURLPath);
+ }
+
+ private:
+ static void NukeSdchDictionariesOnIOThread(
+ net::URLRequestContextGetter* context_getter) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+ net::SdchManager* sdch_manager =
+ context_getter->GetURLRequestContext()->sdch_manager();
+ DCHECK(sdch_manager);
+ sdch_manager->ClearData();
+ }
+
+ static void GetNumberOfDictionaryFetchesOnIOThread(
+ net::URLRequestContextGetter* url_request_context_getter,
+ int* result) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+ net::SdchManager* sdch_manager =
+ url_request_context_getter->GetURLRequestContext()->sdch_manager();
+ DCHECK(sdch_manager);
+ *result = sdch_manager->GetFetchesCountForTesting();
+ }
+
+ // InProcessBrowserTest
+ virtual void SetUpCommandLine(base::CommandLine* command_line) OVERRIDE {
+ command_line->AppendSwitchASCII(
+ switches::kHostResolverRules,
+ "MAP " + std::string(kTestHost) + " 127.0.0.1");
+#if defined(OS_CHROMEOS)
+ command_line->AppendSwitch(
+ chromeos::switches::kIgnoreUserProfileMappingForTests);
+#endif
+ }
+
+ virtual void SetUpOnMainThread() OVERRIDE {
+ test_server_.RegisterRequestHandler(
+ base::Bind(&SdchResponseHandler::HandleRequest,
+ base::Unretained(&response_handler_)));
+ CHECK(test_server_.InitializeAndWaitUntilReady());
+ url_request_context_getter_ = browser()->profile()->GetRequestContext();
+ }
+
+ virtual void TearDownOnMainThread() OVERRIDE {
+ CHECK(test_server_.ShutdownAndWaitUntilComplete());
+ }
+
+ // URLFetcherDelegate
+ virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE {
+ url_fetch_complete_ = true;
+ if (waiting_)
+ base::MessageLoopForUI::current()->Quit();
+ }
+
+ SdchResponseHandler response_handler_;
+ net::test_server::EmbeddedTestServer test_server_;
+ scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
+ scoped_ptr<net::URLFetcher> fetcher_;
+ bool url_fetch_complete_;
+ bool waiting_;
+ base::ScopedTempDir second_profile_data_dir_;
+ Profile* second_profile_;
+ Browser* second_browser_;
+};
+
+const char SdchBrowserTest::kTestHost[] = "our.test.host.com";
+
+// Confirm that after getting a dictionary, calling the browsing
+// data remover renders it unusable. Also (in calling
+// ForceSdchDictionaryLoad()) servers as a smoke test for SDCH.
+IN_PROC_BROWSER_TEST_F(SdchBrowserTest, BrowsingDataRemover) {
+ ASSERT_TRUE(ForceSdchDictionaryLoad(browser()));
+
+ // Confirm browsing data remover without removing the cache leaves
+ // SDCH alone.
+ BrowsingDataRemoveAndWait(BrowsingDataRemover::REMOVE_ALL &
+ ~BrowsingDataRemover::REMOVE_CACHE);
+ bool sdch_encoding_used = false;
+ ASSERT_TRUE(GetData(&sdch_encoding_used));
+ EXPECT_TRUE(sdch_encoding_used);
+
+ // Confirm browsing data remover removing the cache clears SDCH state.
+ BrowsingDataRemoveAndWait(BrowsingDataRemover::REMOVE_CACHE);
+ sdch_encoding_used = false;
+ ASSERT_TRUE(GetData(&sdch_encoding_used));
+ EXPECT_FALSE(sdch_encoding_used);
+}
+
+// Confirm dictionaries not visible in other profiles.
+IN_PROC_BROWSER_TEST_F(SdchBrowserTest, Isolation) {
+ ASSERT_TRUE(ForceSdchDictionaryLoad(browser()));
+ ASSERT_TRUE(SetupSecondBrowser());
+
+ // Data fetches from incognito or separate profiles should not be SDCH
+ // encoded.
+ bool sdch_encoding_used = true;
+ Browser* incognito_browser = CreateIncognitoBrowser();
+ EXPECT_TRUE(GetDataDetailed(
+ incognito_browser->profile()->GetRequestContext(),
+ &sdch_encoding_used));
+ EXPECT_FALSE(sdch_encoding_used);
+
+ sdch_encoding_used = true;
+ EXPECT_TRUE(GetDataDetailed(
+ second_browser()->profile()->GetRequestContext(), &sdch_encoding_used));
+ EXPECT_FALSE(sdch_encoding_used);
+}
+
+// Confirm a dictionary loaded in incognito isn't visible in the main profile.
+IN_PROC_BROWSER_TEST_F(SdchBrowserTest, ReverseIsolation) {
+ Browser* incognito_browser = CreateIncognitoBrowser();
+ ASSERT_TRUE(ForceSdchDictionaryLoad(incognito_browser));
+
+ // Data fetches on main browser should not be SDCH encoded.
+ bool sdch_encoding_used = true;
+ ASSERT_TRUE(GetData(&sdch_encoding_used));
+ EXPECT_FALSE(sdch_encoding_used);
+}
+
+} // namespace
diff --git a/chrome/browser/profiles/off_the_record_profile_io_data.cc b/chrome/browser/profiles/off_the_record_profile_io_data.cc
index db57a91..28e0fa7 100644
--- a/chrome/browser/profiles/off_the_record_profile_io_data.cc
+++ b/chrome/browser/profiles/off_the_record_profile_io_data.cc
@@ -276,15 +276,16 @@ void OffTheRecordProfileIOData::InitializeInternal(
// Setup the SDCHManager for this profile.
sdch_manager_.reset(new net::SdchManager);
- sdch_manager_->set_sdch_fetcher(
- new net::SdchDictionaryFetcher(
- sdch_manager_.get(),
- // SdchDictionaryFetcher takes a reference to the Getter, and
- // hence implicitly takes ownership.
- new net::TrivialURLRequestContextGetter(
- main_context,
- content::BrowserThread::GetMessageLoopProxyForThread(
- content::BrowserThread::IO))));
+
+ scoped_refptr<net::URLRequestContextGetter> getter(
+ new net::TrivialURLRequestContextGetter(
+ main_context,
+ content::BrowserThread::GetMessageLoopProxyForThread(
+ content::BrowserThread::IO)));
+
+ sdch_manager_->set_sdch_fetcher(scoped_ptr<net::SdchFetcher>(
+ new net::SdchDictionaryFetcher(sdch_manager_.get(), getter)).Pass());
+
main_context->set_sdch_manager(sdch_manager_.get());
#if defined(ENABLE_EXTENSIONS)
diff --git a/chrome/browser/profiles/profile_impl_io_data.cc b/chrome/browser/profiles/profile_impl_io_data.cc
index c83d92e..d5abd72 100644
--- a/chrome/browser/profiles/profile_impl_io_data.cc
+++ b/chrome/browser/profiles/profile_impl_io_data.cc
@@ -579,15 +579,16 @@ void ProfileImplIOData::InitializeInternal(
// Setup the SDCHManager for this profile.
sdch_manager_.reset(new net::SdchManager);
- sdch_manager_->set_sdch_fetcher(
- new net::SdchDictionaryFetcher(
- sdch_manager_.get(),
- // SdchDictionaryFetcher takes a reference to the Getter, and
- // hence implicitly takes ownership.
- new net::TrivialURLRequestContextGetter(
- main_context,
- content::BrowserThread::GetMessageLoopProxyForThread(
- content::BrowserThread::IO))));
+
+ scoped_refptr<net::URLRequestContextGetter> getter(
+ new net::TrivialURLRequestContextGetter(
+ main_context,
+ content::BrowserThread::GetMessageLoopProxyForThread(
+ content::BrowserThread::IO)));
+
+ sdch_manager_->set_sdch_fetcher(scoped_ptr<net::SdchFetcher>(
+ new net::SdchDictionaryFetcher(sdch_manager_.get(), getter)).Pass());
+
main_context->set_sdch_manager(sdch_manager_.get());
// Create a media request context based on the main context, but using a
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 513a655..31f054a 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -823,6 +823,7 @@
'../media/media.gyp:media',
'../net/net.gyp:net',
'../net/net.gyp:net_test_support',
+ '../sdch/sdch.gyp:sdch',
'../skia/skia.gyp:skia',
'../sync/sync.gyp:sync',
'../sync/sync.gyp:test_support_sync_api',
@@ -1313,6 +1314,7 @@
'browser/net/nss_context_chromeos_browsertest.cc',
'browser/net/predictor_browsertest.cc',
'browser/net/proxy_browsertest.cc',
+ 'browser/net/sdch_browsertest.cc',
'browser/net/websocket_browsertest.cc',
'browser/notifications/login_state_notification_blocker_chromeos_browsertest.cc',
'browser/notifications/message_center_notifications_browsertest.cc',
diff --git a/net/base/sdch_dictionary_fetcher.cc b/net/base/sdch_dictionary_fetcher.cc
index aa2061e..f0a417e 100644
--- a/net/base/sdch_dictionary_fetcher.cc
+++ b/net/base/sdch_dictionary_fetcher.cc
@@ -16,7 +16,7 @@ namespace net {
SdchDictionaryFetcher::SdchDictionaryFetcher(
SdchManager* manager,
- URLRequestContextGetter* context)
+ scoped_refptr<URLRequestContextGetter> context)
: manager_(manager),
weak_factory_(this),
task_is_pending_(false),
diff --git a/net/base/sdch_dictionary_fetcher.h b/net/base/sdch_dictionary_fetcher.h
index b657b8f..82c0b10 100644
--- a/net/base/sdch_dictionary_fetcher.h
+++ b/net/base/sdch_dictionary_fetcher.h
@@ -29,11 +29,11 @@ class NET_EXPORT SdchDictionaryFetcher
public SdchFetcher,
public base::NonThreadSafe {
public:
- // Consumer must guarantee that the SdchManager pointer outlives
+ // The consumer must guarantee that |*manager| outlives
// this object. The current implementation guarantees this by
// the SdchManager owning this object.
SdchDictionaryFetcher(SdchManager* manager,
- URLRequestContextGetter* context);
+ scoped_refptr<URLRequestContextGetter> context);
virtual ~SdchDictionaryFetcher();
// Implementation of SdchFetcher class.
diff --git a/net/base/sdch_manager.cc b/net/base/sdch_manager.cc
index 19b1d76..bd68797 100644
--- a/net/base/sdch_manager.cc
+++ b/net/base/sdch_manager.cc
@@ -220,7 +220,8 @@ bool SdchManager::Dictionary::DomainMatch(const GURL& gurl,
}
//------------------------------------------------------------------------------
-SdchManager::SdchManager() {
+SdchManager::SdchManager()
+ : fetches_count_for_testing_(0) {
DCHECK(CalledOnValidThread());
}
@@ -250,9 +251,9 @@ void SdchManager::SdchErrorRecovery(ProblemCodes problem) {
UMA_HISTOGRAM_ENUMERATION("Sdch3.ProblemCodes_4", problem, MAX_PROBLEM_CODE);
}
-void SdchManager::set_sdch_fetcher(SdchFetcher* fetcher) {
+void SdchManager::set_sdch_fetcher(scoped_ptr<SdchFetcher> fetcher) {
DCHECK(CalledOnValidThread());
- fetcher_.reset(fetcher);
+ fetcher_ = fetcher.Pass();
}
// static
@@ -358,8 +359,10 @@ bool SdchManager::IsInSupportedDomain(const GURL& url) {
void SdchManager::FetchDictionary(const GURL& request_url,
const GURL& dictionary_url) {
DCHECK(CalledOnValidThread());
- if (CanFetchDictionary(request_url, dictionary_url) && fetcher_.get())
+ if (CanFetchDictionary(request_url, dictionary_url) && fetcher_.get()) {
+ ++fetches_count_for_testing_;
fetcher_->Schedule(dictionary_url);
+ }
}
bool SdchManager::CanFetchDictionary(const GURL& referring_url,
@@ -582,18 +585,8 @@ void SdchManager::UrlSafeBase64Encode(const std::string& input,
// Since this is only done during a dictionary load, and hashes are only 8
// characters, we just do the simple fixup, rather than rewriting the encoder.
base::Base64Encode(input, output);
- for (size_t i = 0; i < output->size(); ++i) {
- switch (output->data()[i]) {
- case '+':
- (*output)[i] = '-';
- continue;
- case '/':
- (*output)[i] = '_';
- continue;
- default:
- continue;
- }
- }
+ std::replace(output->begin(), output->end(), '+', '-');
+ std::replace(output->begin(), output->end(), '/', '_');
}
} // namespace net
diff --git a/net/base/sdch_manager.h b/net/base/sdch_manager.h
index 67ec167..85633f5 100644
--- a/net/base/sdch_manager.h
+++ b/net/base/sdch_manager.h
@@ -246,7 +246,7 @@ class NET_EXPORT SdchManager : public NON_EXPORTED_BASE(base::NonThreadSafe) {
static void SdchErrorRecovery(ProblemCodes problem);
// Register a fetcher that this class can use to obtain dictionaries.
- void set_sdch_fetcher(SdchFetcher* fetcher);
+ void set_sdch_fetcher(scoped_ptr<SdchFetcher> fetcher);
// Enables or disables SDCH compression.
static void EnableSdchSupport(bool enabled);
@@ -336,6 +336,10 @@ class NET_EXPORT SdchManager : public NON_EXPORTED_BASE(base::NonThreadSafe) {
void SetAllowLatencyExperiment(const GURL& url, bool enable);
+ int GetFetchesCountForTesting() const {
+ return fetches_count_for_testing_;
+ }
+
private:
struct BlacklistInfo {
BlacklistInfo()
@@ -376,6 +380,8 @@ class NET_EXPORT SdchManager : public NON_EXPORTED_BASE(base::NonThreadSafe) {
// round trip test has recently passed).
ExperimentSet allow_latency_experiment_;
+ int fetches_count_for_testing_;
+
DISALLOW_COPY_AND_ASSIGN(SdchManager);
};
diff --git a/net/filter/mock_filter_context.cc b/net/filter/mock_filter_context.cc
index eb3484c..ddf9fdc 100644
--- a/net/filter/mock_filter_context.cc
+++ b/net/filter/mock_filter_context.cc
@@ -12,12 +12,19 @@ MockFilterContext::MockFilterContext()
: is_cached_content_(false),
is_download_(false),
is_sdch_response_(false),
+ ok_to_call_get_url_(true),
response_code_(-1),
context_(new URLRequestContext()) {
}
MockFilterContext::~MockFilterContext() {}
+void MockFilterContext::NukeUnstableInterfaces() {
+ context_.reset();
+ ok_to_call_get_url_ = false;
+ request_time_ = base::Time();
+}
+
bool MockFilterContext::GetMimeType(std::string* mime_type) const {
*mime_type = mime_type_;
return true;
@@ -26,6 +33,7 @@ bool MockFilterContext::GetMimeType(std::string* mime_type) const {
// What URL was used to access this data?
// Return false if gurl is not present.
bool MockFilterContext::GetURL(GURL* gurl) const {
+ DCHECK(ok_to_call_get_url_);
*gurl = gurl_;
return true;
}
diff --git a/net/filter/mock_filter_context.h b/net/filter/mock_filter_context.h
index 9bb6235..91a64d22 100644
--- a/net/filter/mock_filter_context.h
+++ b/net/filter/mock_filter_context.h
@@ -36,6 +36,11 @@ class MockFilterContext : public FilterContext {
return context_.get();
}
+ // After a URLRequest's destructor is called, some interfaces may become
+ // unstable. This method is used to signal that state, so we can tag use
+ // of those interfaces as coding errors.
+ void NukeUnstableInterfaces();
+
virtual bool GetMimeType(std::string* mime_type) const OVERRIDE;
// What URL was used to access this data?
@@ -77,6 +82,7 @@ class MockFilterContext : public FilterContext {
bool is_cached_content_;
bool is_download_;
bool is_sdch_response_;
+ bool ok_to_call_get_url_;
int response_code_;
scoped_ptr<URLRequestContext> context_;
diff --git a/net/filter/sdch_filter_unittest.cc b/net/filter/sdch_filter_unittest.cc
index 28da615..88a9c39 100644
--- a/net/filter/sdch_filter_unittest.cc
+++ b/net/filter/sdch_filter_unittest.cc
@@ -165,6 +165,25 @@ TEST_F(SdchFilterTest, EmptyInputOk) {
filter_context()->SetURL(GURL(url_string));
scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
+ // With no input data, try to read output.
+ int output_bytes_or_buffer_size = sizeof(output_buffer);
+ Filter::FilterStatus status = filter->ReadData(output_buffer,
+ &output_bytes_or_buffer_size);
+
+ EXPECT_EQ(0, output_bytes_or_buffer_size);
+ EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA, status);
+}
+
+// Make sure that the filter context has everything that might be
+// nuked from it during URLRequest teardown before the SdchFilter
+// destructor.
+TEST_F(SdchFilterTest, SparseContextOk) {
+ std::vector<Filter::FilterType> filter_types;
+ filter_types.push_back(Filter::FILTER_TYPE_SDCH);
+ char output_buffer[20];
+ std::string url_string("http://ignore.com");
+ filter_context()->SetURL(GURL(url_string));
+ scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
// With no input data, try to read output.
int output_bytes_or_buffer_size = sizeof(output_buffer);
@@ -173,6 +192,13 @@ TEST_F(SdchFilterTest, EmptyInputOk) {
EXPECT_EQ(0, output_bytes_or_buffer_size);
EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA, status);
+
+ // Partially tear down context. Anything that goes through request()
+ // without checking it for null in the URLRequestJob::HttpFilterContext
+ // implementation is suspect. Everything that does check it for null should
+ // return null. This is to test for incorrectly relying on filter_context()
+ // from the SdchFilter destructor.
+ filter_context()->NukeUnstableInterfaces();
}
TEST_F(SdchFilterTest, PassThroughWhenTentative) {
diff --git a/net/http/http_transaction_test_util.h b/net/http/http_transaction_test_util.h
index d340fc9..0803ef5 100644
--- a/net/http/http_transaction_test_util.h
+++ b/net/http/http_transaction_test_util.h
@@ -28,6 +28,7 @@
namespace net {
class HttpRequestHeaders;
class IOBuffer;
+struct HttpRequestInfo;
}
//-----------------------------------------------------------------------------
@@ -227,6 +228,7 @@ class MockNetworkTransaction
return websocket_handshake_stream_create_helper_;
}
net::RequestPriority priority() const { return priority_; }
+ const net::HttpRequestInfo* request() const { return request_; }
private:
int StartInternal(const net::HttpRequestInfo* request,
diff --git a/net/url_request/url_request_context_storage.cc b/net/url_request/url_request_context_storage.cc
index e9399b1..ea10c7e 100644
--- a/net/url_request/url_request_context_storage.cc
+++ b/net/url_request/url_request_context_storage.cc
@@ -7,6 +7,7 @@
#include "base/logging.h"
#include "net/base/net_log.h"
#include "net/base/network_delegate.h"
+#include "net/base/sdch_manager.h"
#include "net/cert/cert_verifier.h"
#include "net/cookies/cookie_store.h"
#include "net/dns/host_resolver.h"
@@ -124,4 +125,10 @@ void URLRequestContextStorage::set_http_user_agent_settings(
http_user_agent_settings_.reset(http_user_agent_settings);
}
+void URLRequestContextStorage::set_sdch_manager(
+ scoped_ptr<SdchManager> sdch_manager) {
+ context_->set_sdch_manager(sdch_manager.get());
+ sdch_manager_ = sdch_manager.Pass();
+}
+
} // namespace net
diff --git a/net/url_request/url_request_context_storage.h b/net/url_request/url_request_context_storage.h
index 3d85ae0..5571b9a 100644
--- a/net/url_request/url_request_context_storage.h
+++ b/net/url_request/url_request_context_storage.h
@@ -25,6 +25,7 @@ class HttpUserAgentSettings;
class NetLog;
class NetworkDelegate;
class ProxyService;
+class SdchManager;
class SSLConfigService;
class TransportSecurityState;
class URLRequestContext;
@@ -67,6 +68,7 @@ class NET_EXPORT URLRequestContextStorage {
void set_throttler_manager(URLRequestThrottlerManager* throttler_manager);
void set_http_user_agent_settings(
HttpUserAgentSettings* http_user_agent_settings);
+ void set_sdch_manager(scoped_ptr<SdchManager> sdch_manager);
private:
// We use a raw pointer to prevent reference cycles, since
@@ -94,6 +96,7 @@ class NET_EXPORT URLRequestContextStorage {
scoped_ptr<HttpTransactionFactory> http_transaction_factory_;
scoped_ptr<URLRequestJobFactory> job_factory_;
scoped_ptr<URLRequestThrottlerManager> throttler_manager_;
+ scoped_ptr<SdchManager> sdch_manager_;
DISALLOW_COPY_AND_ASSIGN(URLRequestContextStorage);
};
diff --git a/net/url_request/url_request_http_job_unittest.cc b/net/url_request/url_request_http_job_unittest.cc
index 203d6d07..7b93b35 100644
--- a/net/url_request/url_request_http_job_unittest.cc
+++ b/net/url_request/url_request_http_job_unittest.cc
@@ -54,6 +54,37 @@ class URLRequestHttpJobTest : public ::testing::Test {
context_.set_http_transaction_factory(&network_layer_);
}
+ bool TransactionAcceptsSdchEncoding() {
+ base::WeakPtr<MockNetworkTransaction> transaction(
+ network_layer_.last_transaction());
+ EXPECT_TRUE(transaction);
+ if (!transaction) return false;
+
+ const HttpRequestInfo* request_info = transaction->request();
+ EXPECT_TRUE(request_info);
+ if (!request_info) return false;
+
+ std::string encoding_headers;
+ bool get_success = request_info->extra_headers.GetHeader(
+ "Accept-Encoding", &encoding_headers);
+ EXPECT_TRUE(get_success);
+ if (!get_success) return false;
+
+ // This check isn't wrapped with EXPECT* macros because different
+ // results from this function may be expected in different tests.
+ std::vector<std::string> tokens;
+ size_t num_tokens = Tokenize(encoding_headers, ",", &tokens);
+ for (size_t i = 0; i < num_tokens; i++) {
+ if (!base::strncasecmp(tokens[i].data(), "sdch", tokens[i].length()))
+ return true;
+ }
+ return false;
+ }
+
+ void EnableSdch() {
+ context_.SetSdchManager(scoped_ptr<SdchManager>(new SdchManager).Pass());
+ }
+
MockNetworkLayer network_layer_;
TestURLRequestContext context_;
TestDelegate delegate_;
@@ -125,6 +156,24 @@ TEST_F(URLRequestHttpJobTest, SetSubsequentTransactionPriority) {
EXPECT_EQ(LOW, network_layer_.last_transaction()->priority());
}
+// Confirm we do advertise SDCH encoding in the case of a GET.
+TEST_F(URLRequestHttpJobTest, SdchAdvertisementGet) {
+ EnableSdch();
+ req_.set_method("GET"); // Redundant with default.
+ scoped_refptr<TestURLRequestHttpJob> job(new TestURLRequestHttpJob(&req_));
+ job->Start();
+ EXPECT_TRUE(TransactionAcceptsSdchEncoding());
+}
+
+// Confirm we don't advertise SDCH encoding in the case of a POST.
+TEST_F(URLRequestHttpJobTest, SdchAdvertisementPost) {
+ EnableSdch();
+ req_.set_method("POST");
+ scoped_refptr<TestURLRequestHttpJob> job(new TestURLRequestHttpJob(&req_));
+ job->Start();
+ EXPECT_FALSE(TransactionAcceptsSdchEncoding());
+}
+
// This base class just serves to set up some things before the TestURLRequest
// constructor is called.
class URLRequestHttpJobWebSocketTestBase : public ::testing::Test {
diff --git a/net/url_request/url_request_test_util.h b/net/url_request/url_request_test_util.h
index 3e3a114..9536193 100644
--- a/net/url_request/url_request_test_util.h
+++ b/net/url_request/url_request_test_util.h
@@ -25,6 +25,7 @@
#include "net/base/net_errors.h"
#include "net/base/network_delegate.h"
#include "net/base/request_priority.h"
+#include "net/base/sdch_manager.h"
#include "net/cert/cert_verifier.h"
#include "net/cookies/cookie_monster.h"
#include "net/disk_cache/disk_cache.h"
@@ -72,6 +73,10 @@ class TestURLRequestContext : public URLRequestContext {
const HttpNetworkSession::Params& params) {
}
+ void SetSdchManager(scoped_ptr<SdchManager> sdch_manager) {
+ context_storage_.set_sdch_manager(sdch_manager.Pass());
+ }
+
private:
bool initialized_;
diff --git a/sdch/sdch.gyp b/sdch/sdch.gyp
index 233ac53..52a92f3 100644
--- a/sdch/sdch.gyp
+++ b/sdch/sdch.gyp
@@ -29,15 +29,19 @@
'open-vcdiff/src/encodetable.h',
'open-vcdiff/src/google/output_string.h',
'open-vcdiff/src/google/vcdecoder.h',
+ 'open-vcdiff/src/google/vcencoder.h',
'open-vcdiff/src/headerparser.cc',
'open-vcdiff/src/headerparser.h',
'open-vcdiff/src/instruction_map.cc',
'open-vcdiff/src/instruction_map.h',
+ 'open-vcdiff/src/jsonwriter.h',
+ 'open-vcdiff/src/jsonwriter.cc',
'open-vcdiff/src/rolling_hash.h',
'open-vcdiff/src/testing.h',
'open-vcdiff/src/varint_bigendian.cc',
'open-vcdiff/src/varint_bigendian.h',
'open-vcdiff/src/vcdecoder.cc',
+ 'open-vcdiff/src/vcencoder.cc',
'open-vcdiff/src/vcdiff_defs.h',
'open-vcdiff/src/vcdiffengine.cc',
'open-vcdiff/src/vcdiffengine.h',