diff options
author | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
---|---|---|
committer | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
commit | 09911bf300f1a419907a9412154760efd0b7abc3 (patch) | |
tree | f131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/common/net | |
parent | 586acc5fe142f498261f52c66862fa417c3d52d2 (diff) | |
download | chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2 |
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/common/net')
-rw-r--r-- | chrome/common/net/cache_uitest.cc | 201 | ||||
-rw-r--r-- | chrome/common/net/cookie_monster_sqlite.cc | 378 | ||||
-rw-r--r-- | chrome/common/net/cookie_monster_sqlite.h | 76 | ||||
-rw-r--r-- | chrome/common/net/dns.h | 49 | ||||
-rw-r--r-- | chrome/common/net/url_request_intercept_job.cc | 234 | ||||
-rw-r--r-- | chrome/common/net/url_request_intercept_job.h | 88 | ||||
-rw-r--r-- | chrome/common/net/url_util_unittest.cc | 77 |
7 files changed, 1103 insertions, 0 deletions
diff --git a/chrome/common/net/cache_uitest.cc b/chrome/common/net/cache_uitest.cc new file mode 100644 index 0000000..36ad388 --- /dev/null +++ b/chrome/common/net/cache_uitest.cc @@ -0,0 +1,201 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <string> + +#include "base/string_util.h" +#include "chrome/test/ui/ui_test.h" +#include "chrome/test/automation/automation_proxy.h" +#include "chrome/test/automation/browser_proxy.h" +#include "chrome/test/automation/tab_proxy.h" +#include "net/url_request/url_request_unittest.h" + +// The CacheTest class extends the UITest class and provides functions to +// get a new tab and to run the tests on a particular path +// +// Typical usage: +// +// 1. Provide this class as the TestCase for TEST_F macro +// 2. Then run the cache test on a specific path using the function +// RunCacheTest +// +// For example: +// +// TEST_F(CacheTest, NoCacheMaxAge) { +// RunCacheTest(L"nocachetime/maxage", false, false); +// } +// +// Note that delays used in running the test is initialized to defaults +class CacheTest : public UITest { + protected: + + // Runs the cache test for the specified path. + // Can specify the test to check if a new tab is loaded from the cache + // and also if a delayed reload is required. A true value passed to the + // third parameter causes a delayed reload of the path in a new tab. + // The amount of delay is set by a class constant. + void RunCacheTest(const std::wstring &url, + bool expect_new_tab_cached, + bool expect_delayed_reload); + + private: + // Class constants + static const int kWaitForCacheUpdateMsec = 2000; + static const int kCacheWaitMultiplier = 4; // Used to increase delay + + // Appends a new tab to the test chrome window and loads the specified + // URL. The new tab will try to get the URL from the cache before requesting + // the server for it. + void GetNewTab(AutomationProxy* automationProxy, const GURL& tab_url); +}; + +// Runs the cache test for the specified path. +void CacheTest::RunCacheTest(const std::wstring &url, + bool expect_new_tab_cached, + bool expect_delayed_reload) { + TestServer server(L"chrome/test/data"); + GURL test_page(server.TestServerPageW(url)); + + NavigateToURL(test_page); + std::wstring original_time = GetActiveTabTitle(); + + Sleep(kWaitForCacheUpdateMsec); + + GetNewTab(automation(), test_page); + std::wstring revisit_time = GetActiveTabTitle(); + + if (expect_new_tab_cached) { + EXPECT_EQ(original_time, revisit_time); + }else { + EXPECT_NE(original_time, revisit_time); + } + + Sleep(kWaitForCacheUpdateMsec); + + // Force reload, overriding the caching behavior + NavigateToURL(test_page); + std::wstring reload_time = GetActiveTabTitle(); + + EXPECT_NE(revisit_time, reload_time); + + if (expect_delayed_reload) { + Sleep(kWaitForCacheUpdateMsec * kCacheWaitMultiplier); + + GetNewTab(automation(), test_page); + revisit_time = GetActiveTabTitle(); + + EXPECT_NE(reload_time, revisit_time); + } +} + +// Appends a new tab to the test chrome window and loads the specified URL. +void CacheTest::GetNewTab(AutomationProxy* automationProxy, + const GURL& tab_url) { + scoped_ptr<BrowserProxy> window_proxy(automationProxy->GetBrowserWindow(0)); + ASSERT_TRUE(window_proxy.get()); + ASSERT_TRUE(window_proxy->AppendTab(tab_url)); +} + +// Tests that a cached copy of the page is not used when max-age=0 headers +// are specified. +TEST_F(CacheTest, NoCacheMaxAge) { + RunCacheTest(L"nocachetime/maxage", false, false); +} + +// Tests that a cached copy of the page is not used when no-cache header +// is specified. +TEST_F(CacheTest, NoCache) { + RunCacheTest(L"nocachetime", false, false); +} + +// Tests that a cached copy of a page is used when its headers specify +// that it should be cached for 60 seconds. +TEST_F(CacheTest, Cache) { + RunCacheTest(L"cachetime", true, false); +} + +// Tests that a cached copy of the page is used when expires header +// specifies that the page has not yet expired. +TEST_F(CacheTest, Expires) { + RunCacheTest(L"cache/expires", true, false); +} + +// Tests that a cached copy of the page is used when proxy-revalidate header +// is specified and the page has not yet expired. +TEST_F(CacheTest, ProxyRevalidate) { + RunCacheTest(L"cache/proxy-revalidate", true, false); +} + +// Tests that a cached copy of the page is used when private header +// is specified and the page has not yet expired. +TEST_F(CacheTest, Private) { + RunCacheTest(L"cache/private", true, true); +} + +// Tests that a cached copy of the page is used when public header +// is specified and the page has not yet expired. +TEST_F(CacheTest, Public) { + RunCacheTest(L"cache/public", true, true); +} + +// Tests that a cached copy of the page is not used when s-maxage header +// is specified. +TEST_F(CacheTest, SMaxAge) { + RunCacheTest(L"cache/s-maxage", false, false); +} + +// Tests that a cached copy of the page is not used when must-revalidate header +// is specified. +TEST_F(CacheTest, MustRevalidate) { + RunCacheTest(L"cache/must-revalidate", false, false); +} + +// Tests that a cached copy of the page is not used when must-revalidate header +// is specified, even though the page has not yet expired. +TEST_F(CacheTest, MustRevalidateMaxAge) { + RunCacheTest(L"cache/must-revalidate/max-age", false, false); +} + +// Tests that a cached copy of the page is not used when no-store header +// is specified. +TEST_F(CacheTest, NoStore) { + RunCacheTest(L"cache/no-store", false, false); +} + +// Tests that a cached copy of the page is not used when no-store header +// is specified, even though the page has not yet expired. +TEST_F(CacheTest, NoStoreMaxAge) { + RunCacheTest(L"cache/no-store/max-age", false, false); +} + +// Tests that a cached copy of the page is not transformed when no-transform +// header is specified. +TEST_F(CacheTest, NoTransform) { + RunCacheTest(L"cache/no-transform", false, false); +} diff --git a/chrome/common/net/cookie_monster_sqlite.cc b/chrome/common/net/cookie_monster_sqlite.cc new file mode 100644 index 0000000..259b8f2 --- /dev/null +++ b/chrome/common/net/cookie_monster_sqlite.cc @@ -0,0 +1,378 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/common/net/cookie_monster_sqlite.h" + +#include "base/logging.h" +#include "base/ref_counted.h" +#include "base/string_util.h" +#include "base/thread.h" +#include "chrome/common/sqlite_compiled_statement.h" +#include "chrome/common/sqlite_utils.h" + +// This class is designed to be shared between any calling threads and the +// database thread. It batches operations and commits them on a timer. +class SQLitePersistentCookieStore::Backend + : public base::RefCountedThreadSafe<SQLitePersistentCookieStore::Backend> { + public: + // The passed database pointer must be already-initialized. This object will + // take ownership. + explicit Backend(sqlite3* db, MessageLoop* loop) + : db_(db), + background_loop_(loop), + cache_(new SqliteStatementCache(db)), + num_pending_(0) { + DCHECK(db_) << "Database must exist."; + } + + // You should call Close() before destructing this object. + ~Backend() { + DCHECK(!db_) << "Close should have already been called."; + DCHECK(num_pending_ == 0 && pending_.empty()); + } + + // Batch a cookie add + void AddCookie(const std::string& key, + const CookieMonster::CanonicalCookie& cc); + // Batch a cookie delete + void DeleteCookie(const CookieMonster::CanonicalCookie& cc); + // Commit and pending operations and close the database, must be called + // before the object is destructed. + void Close(); + + private: + class PendingOperation { + public: + typedef enum { + COOKIE_ADD, + COOKIE_DELETE, + } OperationType; + + PendingOperation(OperationType op, + const std::string& key, + const CookieMonster::CanonicalCookie& cc) + : op_(op), key_(key), cc_(cc) { } + + OperationType op() const { return op_; } + const std::string& key() const { return key_; } + const CookieMonster::CanonicalCookie& cc() const { return cc_; } + + private: + OperationType op_; + std::string key_; // Only used for OP_ADD + CookieMonster::CanonicalCookie cc_; + }; + + private: + // Batch a cookie operation (add or delete) + void BatchOperation(PendingOperation::OperationType op, + const std::string& key, + const CookieMonster::CanonicalCookie& cc); + // Commit our pending operations to the database. + void Commit(); + // Close() executed on the background thread. + void InternalBackgroundClose(); + + sqlite3* db_; + MessageLoop* background_loop_; + SqliteStatementCache* cache_; + + typedef std::list<PendingOperation*> PendingOperationsList; + PendingOperationsList pending_; + PendingOperationsList::size_type num_pending_; + Lock pending_lock_; // Guard pending_ and num_pending_ + + DISALLOW_EVIL_CONSTRUCTORS(Backend); +}; + +void SQLitePersistentCookieStore::Backend::AddCookie( + const std::string& key, + const CookieMonster::CanonicalCookie& cc) { + BatchOperation(PendingOperation::COOKIE_ADD, key, cc); +} + +void SQLitePersistentCookieStore::Backend::DeleteCookie( + const CookieMonster::CanonicalCookie& cc) { + BatchOperation(PendingOperation::COOKIE_DELETE, std::string(), cc); +} + +void SQLitePersistentCookieStore::Backend::BatchOperation( + PendingOperation::OperationType op, + const std::string& key, + const CookieMonster::CanonicalCookie& cc) { + // Commit every 30 seconds. + static const int kCommitIntervalMs = 30 * 1000; + // Commit right away if we have more than 512 outstanding operations. + static const int kCommitAfterBatchSize = 512; + DCHECK(MessageLoop::current() != background_loop_); + + // We do a full copy of the cookie here, and hopefully just here. + scoped_ptr<PendingOperation> po(new PendingOperation(op, key, cc)); + CHECK(po.get()); + + PendingOperationsList::size_type num_pending; + { + AutoLock locked(pending_lock_); + pending_.push_back(po.release()); + num_pending = ++num_pending_; + } + + // TODO(abarth): What if the DB thread is being destroyed on the UI thread? + if (num_pending == 1) { + // We've gotten our first entry for this batch, fire off the timer. + background_loop_->PostDelayedTask(FROM_HERE, + NewRunnableMethod(this, &Backend::Commit), kCommitIntervalMs); + } else if (num_pending == kCommitAfterBatchSize) { + // We've reached a big enough batch, fire off a commit now. + background_loop_->PostTask(FROM_HERE, + NewRunnableMethod(this, &Backend::Commit)); + } +} + +void SQLitePersistentCookieStore::Backend::Commit() { + DCHECK(MessageLoop::current() == background_loop_); + PendingOperationsList ops; + { + AutoLock locked(pending_lock_); + pending_.swap(ops); + num_pending_ = 0; + } + + // Maybe an old timer fired or we are already Close()'ed. + if (!db_ || ops.empty()) + return; + + SQLITE_UNIQUE_STATEMENT(add_smt, *cache_, + "INSERT INTO cookies VALUES (?,?,?,?,?,?,?,?)"); + if (!add_smt.is_valid()) { + NOTREACHED(); + return; + } + SQLITE_UNIQUE_STATEMENT(del_smt, *cache_, + "DELETE FROM cookies WHERE creation_utc=?"); + if (!del_smt.is_valid()) { + NOTREACHED(); + return; + } + + SQLTransaction transaction(db_); + transaction.Begin(); + for (PendingOperationsList::iterator it = ops.begin(); + it != ops.end(); ++it) { + // Free the cookies as we commit them to the database. + scoped_ptr<PendingOperation> po(*it); + switch (po->op()) { + case PendingOperation::COOKIE_ADD: + add_smt->reset(); + add_smt->bind_int64(0, po->cc().CreationDate().ToInternalValue()); + add_smt->bind_string(1, po->key()); + add_smt->bind_string(2, po->cc().Name()); + add_smt->bind_string(3, po->cc().Value()); + add_smt->bind_string(4, po->cc().Path()); + add_smt->bind_int64(5, po->cc().ExpiryDate().ToInternalValue()); + add_smt->bind_int(6, po->cc().IsSecure()); + add_smt->bind_int(7, po->cc().IsHttpOnly()); + if (add_smt->step() != SQLITE_DONE) { + NOTREACHED() << "Could not add a cookie to the DB."; + } + break; + case PendingOperation::COOKIE_DELETE: + del_smt->reset(); + del_smt->bind_int64(0, po->cc().CreationDate().ToInternalValue()); + if (del_smt->step() != SQLITE_DONE) { + NOTREACHED() << "Could not delete a cookie from the DB."; + } + break; + default: + NOTREACHED(); + break; + } + } + transaction.Commit(); +} + +// Fire off a close message to the background thread. We could still have a +// pending commit timer that will be holding a reference on us, but if/when +// this fires we will already have been cleaned up and it will be ignored. +void SQLitePersistentCookieStore::Backend::Close() { + DCHECK(MessageLoop::current() != background_loop_); + // Must close the backend on the background thread. + // TODO(abarth): What if the DB thread is being destroyed on the UI thread? + background_loop_->PostTask(FROM_HERE, + NewRunnableMethod(this, &Backend::InternalBackgroundClose)); +} + +void SQLitePersistentCookieStore::Backend::InternalBackgroundClose() { + DCHECK(MessageLoop::current() == background_loop_); + // Commit any pending operations + Commit(); + // We must destroy the cache before closing the database. + delete cache_; + cache_ = NULL; + sqlite3_close(db_); + db_ = NULL; +} + +SQLitePersistentCookieStore::SQLitePersistentCookieStore( + const std::wstring& path, MessageLoop* background_loop) + : path_(path), + background_loop_(background_loop) { + DCHECK(background_loop) << "SQLitePersistentCookieStore needs a MessageLoop"; +} + +SQLitePersistentCookieStore::~SQLitePersistentCookieStore() { + if (backend_.get()) { + backend_->Close(); + // Release our reference, it will probably still have a reference if the + // background thread has not run Close() yet. + backend_ = NULL; + } +} + +// Version number of the database. +static const int kCurrentVersionNumber = 2; + +namespace { + +// Initializes the cookies table, returning true on success. +bool InitTable(sqlite3* db) { + if (!DoesSqliteTableExist(db, "cookies")) { + if (sqlite3_exec(db, "CREATE TABLE cookies (" + "creation_utc INTEGER NOT NULL UNIQUE PRIMARY KEY," + "host_key TEXT NOT NULL," + "name TEXT NOT NULL," + "value TEXT NOT NULL," + "path TEXT NOT NULL," + // We only store persistent, so we know it expires + "expires_utc INTEGER NOT NULL," + "secure INTEGER NOT NULL," + "httponly INTEGER NOT NULL)", + NULL, NULL, NULL) != SQLITE_OK) + return false; + } + + // Try to create the index every time. Older versions did not have this index, + // so we want those people to get it. Ignore errors, since it may exist. + sqlite3_exec(db, "CREATE INDEX cookie_times ON cookies (creation_utc)", + NULL, NULL, NULL); + + return true; +} + +} // namespace + +bool SQLitePersistentCookieStore::Load( + std::vector<CookieMonster::KeyedCanonicalCookie>* cookies) { + DCHECK(!path_.empty()); + sqlite3* db; + if (sqlite3_open(WideToUTF8(path_).c_str(), &db) != SQLITE_OK) { + NOTREACHED() << "Unable to open cookie DB."; + return false; + } + + if (!EnsureDatabaseVersion(db) || !InitTable(db)) { + NOTREACHED() << "Unable to initialize cookie DB."; + sqlite3_close(db); + return false; + } + + // Slurp all the cookies into the out-vector. + SQLStatement smt; + if (smt.prepare(db, "SELECT * FROM cookies") != SQLITE_OK) { + NOTREACHED() << "select statement prep failed"; + sqlite3_close(db); + return false; + } + + // Step the statement and do the preload operation. Preload() requires that a + // statement be open on the database, so we oblige. We don't bother keeping + // this and reset the statement so we can read all the rows. + // + // The Preload call makes the read faster, since we will be reading most of + // the database, it's better to read it in one chunk than have a lot of seeks + // bringing it in organically. + if (smt.step() != SQLITE_ROW) + sqlite3Preload(db); + smt.reset(); + + while (smt.step() == SQLITE_ROW) { + std::string key = smt.column_string(1); + scoped_ptr<CookieMonster::CanonicalCookie> cc( + new CookieMonster::CanonicalCookie( + smt.column_string(2), // name + smt.column_string(3), // value + smt.column_string(4), // path + smt.column_int(6) != 0, // secure + smt.column_int(7) != 0, // httponly + Time::FromInternalValue(smt.column_int64(0)), // creation_utc + true, // has_expires + Time::FromInternalValue(smt.column_int64(5)))); // expires_utc + // Memory allocation failed. + if (!cc.get()) + break; + + DLOG_IF(WARNING, + cc->CreationDate() > Time::Now()) << L"CreationDate too recent"; + cookies->push_back( + CookieMonster::KeyedCanonicalCookie(smt.column_string(1), + cc.release())); + } + + // Create the backend, this will take ownership of the db pointer. + backend_ = new Backend(db, background_loop_); + + return true; +} + +bool SQLitePersistentCookieStore::EnsureDatabaseVersion(sqlite3* db) { + // Version check. + if (!meta_table_.Init(std::string(), kCurrentVersionNumber, db)) + return false; + + int compat_version = meta_table_.GetCompatibleVersionNumber(); + if (compat_version > kCurrentVersionNumber) { + DLOG(WARNING) << "Cookie DB version from the future: " << compat_version; + return false; + } + + return true; +} + +void SQLitePersistentCookieStore::AddCookie( + const std::string& key, + const CookieMonster::CanonicalCookie& cc) { + if (backend_.get()) + backend_->AddCookie(key, cc); +} + +void SQLitePersistentCookieStore::DeleteCookie( + const CookieMonster::CanonicalCookie& cc) { + if (backend_.get()) + backend_->DeleteCookie(cc); +} diff --git a/chrome/common/net/cookie_monster_sqlite.h b/chrome/common/net/cookie_monster_sqlite.h new file mode 100644 index 0000000..49de96a --- /dev/null +++ b/chrome/common/net/cookie_monster_sqlite.h @@ -0,0 +1,76 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// A sqlite implementation of a cookie monster persistent store. + +#ifndef CHROME_COMMON_NET_COOKIE_MONSTER_SQLITE_H__ +#define CHROME_COMMON_NET_COOKIE_MONSTER_SQLITE_H__ + +#include <string> +#include <vector> + +#include "base/ref_counted.h" +#include "base/message_loop.h" +#include "chrome/browser/meta_table_helper.h" +#include "net/base/cookie_monster.h" + +struct sqlite3; + +class SQLitePersistentCookieStore + : public CookieMonster::PersistentCookieStore { + public: + SQLitePersistentCookieStore(const std::wstring& path, + MessageLoop* background_loop); + ~SQLitePersistentCookieStore(); + + virtual bool Load(std::vector<CookieMonster::KeyedCanonicalCookie>*); + + virtual void AddCookie(const std::string&, + const CookieMonster::CanonicalCookie&); + virtual void DeleteCookie(const CookieMonster::CanonicalCookie&); + + private: + class Backend; + + // Database upgrade statements. + bool EnsureDatabaseVersion(sqlite3* db); + bool UpdateSchemaToVersion2(sqlite3* db); + + std::wstring path_; + scoped_refptr<Backend> backend_; + + // Background MessageLoop on which to access the backend_; + MessageLoop* background_loop_; + + MetaTableHelper meta_table_; + + DISALLOW_EVIL_CONSTRUCTORS(SQLitePersistentCookieStore); +}; + +#endif // CHROME_COMMON_NET_COOKIE_MONSTER_SQLITE_H__ diff --git a/chrome/common/net/dns.h b/chrome/common/net/dns.h new file mode 100644 index 0000000..9a178e3 --- /dev/null +++ b/chrome/common/net/dns.h @@ -0,0 +1,49 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This file has shared types used across IPC between render_dns_master.cc +// and dns_master.cc + + +#ifndef CHROME_COMMON_DNS_H__ +#define CHROME_COMMON_DNS_H__ + +#include <vector> + +namespace chrome_common_net{ + +// IPC messages are passed from the renderer to the browser in the form of +// Namelist instances. +// Each element of this vector is a hostname that needs to be looked up. +// The hostnames should never be empty strings. +typedef std::vector<std::string> NameList; + +} + +#endif // CHROME_COMMON_DNS_H__
\ No newline at end of file diff --git a/chrome/common/net/url_request_intercept_job.cc b/chrome/common/net/url_request_intercept_job.cc new file mode 100644 index 0000000..4da58d0c --- /dev/null +++ b/chrome/common/net/url_request_intercept_job.cc @@ -0,0 +1,234 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// This job type handles Chrome plugin network interception. When a plugin +// wants to intercept a request, a job of this type is created. The intercept +// job communicates with the plugin to retrieve the response headers and data. + +#include "chrome/common/net/url_request_intercept_job.h" + +#include "base/message_loop.h" +#include "base/string_util.h" +#include "chrome/common/chrome_plugin_lib.h" +#include "net/base/net_errors.h" + +// +// URLRequestInterceptJob +// + +URLRequestInterceptJob::URLRequestInterceptJob(URLRequest* request, + ChromePluginLib* plugin, + ScopableCPRequest* cprequest) + : URLRequestJob(request), + cprequest_(cprequest), + plugin_(plugin), + read_buffer_(NULL), + got_headers_(false) { + cprequest_->data = this; // see FromCPRequest(). + + NotificationService::current()->AddObserver( + this, NOTIFY_CHROME_PLUGIN_UNLOADED, + Source<ChromePluginLib>(plugin_)); +} + +URLRequestInterceptJob::~URLRequestInterceptJob() { + if (plugin_) { + plugin_->functions().request_funcs->end_request(cprequest_.get(), + CPERR_SUCCESS); + DetachPlugin(); + } +} + +void URLRequestInterceptJob::DetachPlugin() { + NotificationService::current()->RemoveObserver( + this, NOTIFY_CHROME_PLUGIN_UNLOADED, + Source<ChromePluginLib>(plugin_)); + plugin_ = NULL; +} + +void URLRequestInterceptJob::Start() { + // Start reading asynchronously so that all error reporting and data + // callbacks happen as they would for network requests. + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( + this, &URLRequestInterceptJob::StartAsync)); +} + +void URLRequestInterceptJob::Kill() { + if (plugin_) { + plugin_->functions().request_funcs->end_request(cprequest_.get(), + CPERR_CANCELLED); + DetachPlugin(); + } + URLRequestJob::Kill(); +} + +bool URLRequestInterceptJob::ReadRawData(char* dest, int dest_size, + int* bytes_read) { + DCHECK_NE(dest_size, 0); + DCHECK(bytes_read); + + if (!plugin_) + return false; + + int rv = plugin_->functions().request_funcs->read(cprequest_.get(), + dest, dest_size); + if (rv >= 0) { + *bytes_read = rv; + return true; + } + + if (rv == CPERR_IO_PENDING) { + read_buffer_ = dest; + read_buffer_size_ = dest_size; + SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); + } else { + // TODO(mpcomplete): better error code + NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, net::ERR_FAILED)); + } + + return false; +} + +bool URLRequestInterceptJob::GetMimeType(std::string* mime_type) { + return request_->response_headers()->GetMimeType(mime_type); +} + +bool URLRequestInterceptJob::GetCharset(std::string* charset) { + return request_->response_headers()->GetCharset(charset); +} + +bool URLRequestInterceptJob::GetContentEncoding(std::string* encoding_type) { + // TODO(darin): what if there are multiple content encodings? + return request_->response_headers()->EnumerateHeader(NULL, "Content-Encoding", + encoding_type); +} + +void URLRequestInterceptJob::GetResponseInfo(net::HttpResponseInfo* info) { + if (!plugin_) + return; + + std::string raw_headers; + int size = plugin_->functions().request_funcs->get_response_info( + cprequest_.get(), CPRESPONSEINFO_HTTP_RAW_HEADERS, NULL, 0); + int rv = size < 0 ? size : + plugin_->functions().request_funcs->get_response_info( + cprequest_.get(), CPRESPONSEINFO_HTTP_RAW_HEADERS, + WriteInto(&raw_headers, size+1), size); + if (rv != CPERR_SUCCESS) { + // TODO(mpcomplete): what should we do here? + raw_headers = "HTTP/1.1 404 Not Found" + '\0'; + } + + info->headers = new net::HttpResponseHeaders(raw_headers); + + if (request_->url().SchemeIsSecure()) { + // Make up a fake certificate for intercepted data since we don't have + // access to the real SSL info. + // TODO(mpcomplete): we should probably have the interception API transmit + // the SSL info, but the only consumer of this API (Gears) doesn't keep that + // around. We should change that. + const char* kCertIssuer = "Chrome Internal"; + const int kLifetimeDays = 100; + + DLOG(WARNING) << "Issuing a fake SSL certificate for interception of URL " + << request_->url(); + + info->ssl_info.cert = + new X509Certificate(request_->url().GetWithEmptyPath().spec(), + kCertIssuer, + Time::Now(), + Time::Now() + TimeDelta::FromDays(kLifetimeDays)); + info->ssl_info.cert_status = 0; + info->ssl_info.security_bits = 0; + } +} + +int URLRequestInterceptJob::GetResponseCode() { + if (!plugin_) + return -1; + + int status = 200; + plugin_->functions().request_funcs->get_response_info( + cprequest_.get(), CPRESPONSEINFO_HTTP_STATUS, &status, sizeof(status)); + + return status; +} + +bool URLRequestInterceptJob::IsRedirectResponse(GURL* location, + int* http_status_code) { + if (!request_->response_headers()) + return false; + + std::string value; + if (!request_->response_headers()->IsRedirect(&value)) + return false; + + *location = request_->url().Resolve(value); + *http_status_code = request_->response_headers()->response_code(); + return true; +} + +void URLRequestInterceptJob::StartAsync() { + // We may have been orphaned... + if (!request_ || !plugin_) + return; + + int rv = plugin_->functions().request_funcs->start_request(cprequest_.get()); + if (rv != CPERR_IO_PENDING) + OnStartCompleted(rv); +} + +void URLRequestInterceptJob::OnStartCompleted(int result) { + if (result != CPERR_SUCCESS) { + NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, + net::ERR_CONNECTION_FAILED)); + return; + } + + NotifyHeadersComplete(); +} + +void URLRequestInterceptJob::OnReadCompleted(int bytes_read) { + if (bytes_read < 0) { + NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, net::ERR_FAILED)); + return; + } + + SetStatus(URLRequestStatus()); // clear the async flag + NotifyReadComplete(bytes_read); +} + +void URLRequestInterceptJob::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(type == NOTIFY_CHROME_PLUGIN_UNLOADED); + DCHECK(plugin_ == Source<ChromePluginLib>(source).ptr()); + + DetachPlugin(); +} diff --git a/chrome/common/net/url_request_intercept_job.h b/chrome/common/net/url_request_intercept_job.h new file mode 100644 index 0000000..9eea7a6 --- /dev/null +++ b/chrome/common/net/url_request_intercept_job.h @@ -0,0 +1,88 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_COMMON_NET_URL_REQUEST_INTERCEPT_JOB_H__ +#define CHROME_COMMON_NET_URL_REQUEST_INTERCEPT_JOB_H__ + +#include "base/basictypes.h" +#include "net/url_request/url_request.h" +#include "net/url_request/url_request_job.h" +#include "chrome/browser/chrome_plugin_host.h" +#include "chrome/common/chrome_plugin_api.h" +#include "chrome/common/chrome_plugin_util.h" +#include "chrome/common/notification_service.h" + +class ChromePluginLib; + +// A request job that handles network requests intercepted by a Chrome plugin. +class URLRequestInterceptJob + : public URLRequestJob, public NotificationObserver { + public: + static URLRequestInterceptJob* FromCPRequest(CPRequest* request) { + return ScopableCPRequest::GetData<URLRequestInterceptJob*>(request); + } + + URLRequestInterceptJob(URLRequest* request, ChromePluginLib* plugin, + ScopableCPRequest* cprequest); + virtual ~URLRequestInterceptJob(); + + // Plugin callbacks. + void OnStartCompleted(int result); + void OnReadCompleted(int bytes_read); + + // URLRequestJob + virtual void Start(); + virtual void Kill(); + virtual bool ReadRawData(char* buf, int buf_size, int* bytes_read); + virtual bool GetMimeType(std::string* mime_type); + virtual bool GetCharset(std::string* charset); + virtual void GetResponseInfo(net::HttpResponseInfo* info); + virtual int GetResponseCode(); + virtual bool GetContentEncoding(std::string* encoding_type); + virtual bool IsRedirectResponse(GURL* location, int* http_status_code); + + // NotificationObserver + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + private: + void StartAsync(); + void DetachPlugin(); + + scoped_ptr<ScopableCPRequest> cprequest_; + ChromePluginLib* plugin_; + bool got_headers_; + char* read_buffer_; + int read_buffer_size_; + + DISALLOW_EVIL_CONSTRUCTORS(URLRequestInterceptJob); +}; + + +#endif // CHROME_COMMON_NET_URL_REQUEST_INTERCEPT_JOB_H__ diff --git a/chrome/common/net/url_util_unittest.cc b/chrome/common/net/url_util_unittest.cc new file mode 100644 index 0000000..28606f1 --- /dev/null +++ b/chrome/common/net/url_util_unittest.cc @@ -0,0 +1,77 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "base/basictypes.h" +#include "googleurl/src/url_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(URLUtil, Scheme) { + enum SchemeType { + NO_SCHEME = 0, + SCHEME = 1 << 0, + STANDARD_SCHEME = 1 << 1, + }; + const struct { + const wchar_t* url; + const char* try_scheme; + const bool scheme_matches; + const int flags; + } tests[] = { + {L" ", "hello", false, NO_SCHEME}, + {L"foo", NULL, false, NO_SCHEME}, + {L"google.com/foo:bar", NULL, false, NO_SCHEME}, + {L"Garbage:foo.com", "garbage", true, SCHEME}, + {L"Garbage:foo.com", "trash", false, SCHEME}, + {L"gopher:", "gopher", true, SCHEME | STANDARD_SCHEME}, + {L"About:blank", "about", true, SCHEME}, + {L"http://foo.com:123", "foo", false, SCHEME | STANDARD_SCHEME}, + {L"file://c/", "file", true, SCHEME | STANDARD_SCHEME}, + }; + + for (size_t i = 0; i < arraysize(tests); i++) { + int url_len = static_cast<int>(wcslen(tests[i].url)); + + url_parse::Component parsed_scheme; + bool has_scheme = url_parse::ExtractScheme(tests[i].url, url_len, + &parsed_scheme); + + EXPECT_EQ(!!(tests[i].flags & STANDARD_SCHEME), + url_util::IsStandard(tests[i].url, url_len)); + EXPECT_EQ(!!(tests[i].flags & STANDARD_SCHEME), + url_util::IsStandardScheme(tests[i].url, parsed_scheme.len)); + + url_parse::Component found_scheme; + EXPECT_EQ(tests[i].scheme_matches, + url_util::FindAndCompareScheme(tests[i].url, url_len, + tests[i].try_scheme, + &found_scheme)); + EXPECT_EQ(parsed_scheme.begin, found_scheme.begin); + EXPECT_EQ(parsed_scheme.len, found_scheme.len); + } +} |