diff options
-rw-r--r-- | chrome/browser/android/cookies/cookies_fetcher.cc | 10 | ||||
-rw-r--r-- | chrome/browser/extensions/api/cookies/cookies_api.cc | 9 | ||||
-rw-r--r-- | content/browser/frame_host/render_frame_message_filter.cc | 6 | ||||
-rw-r--r-- | ios/net/cookies/cookie_store_ios_unittest.mm | 3 | ||||
-rw-r--r-- | net/cookies/cookie_monster.cc | 24 | ||||
-rw-r--r-- | net/cookies/cookie_monster.h | 2 | ||||
-rw-r--r-- | net/cookies/cookie_monster_unittest.cc | 32 | ||||
-rw-r--r-- | net/cookies/cookie_options.cc | 1 | ||||
-rw-r--r-- | net/cookies/cookie_options.h | 6 | ||||
-rw-r--r-- | net/cookies/cookie_store_unittest.h | 5 | ||||
-rw-r--r-- | net/url_request/url_request_http_job.cc | 2 | ||||
-rw-r--r-- | net/url_request/url_request_unittest.cc | 180 |
12 files changed, 269 insertions, 11 deletions
diff --git a/chrome/browser/android/cookies/cookies_fetcher.cc b/chrome/browser/android/cookies/cookies_fetcher.cc index d8c4bf1..29a6a64 100644 --- a/chrome/browser/android/cookies/cookies_fetcher.cc +++ b/chrome/browser/android/cookies/cookies_fetcher.cc @@ -5,10 +5,12 @@ #include "base/android/jni_android.h" #include "base/android/jni_string.h" #include "base/bind.h" +#include "base/command_line.h" #include "base/time/time.h" #include "chrome/browser/android/cookies/cookies_fetcher.h" #include "chrome/browser/profiles/profile_manager.h" #include "content/public/browser/browser_thread.h" +#include "content/public/common/content_switches.h" #include "jni/CookiesFetcher_jni.h" #include "net/cookies/cookie_monster.h" #include "net/cookies/cookie_store.h" @@ -154,10 +156,16 @@ void CookiesFetcher::RestoreToCookieJarInternal( net::CookieMonster* monster = store->GetCookieMonster(); base::Callback<void(bool success)> cb; + // TODO(estark): Remove kEnableExperimentalWebPlatformFeatures check + // when we decide whether to ship cookie + // prefixes. https://crbug.com/541511 monster->SetCookieWithDetailsAsync( cookie.Source(), cookie.Name(), cookie.Value(), cookie.Domain(), cookie.Path(), cookie.ExpiryDate(), cookie.IsSecure(), - cookie.IsHttpOnly(), cookie.IsFirstPartyOnly(), cookie.Priority(), cb); + cookie.IsHttpOnly(), cookie.IsFirstPartyOnly(), + base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableExperimentalWebPlatformFeatures), + cookie.Priority(), cb); } // JNI functions diff --git a/chrome/browser/extensions/api/cookies/cookies_api.cc b/chrome/browser/extensions/api/cookies/cookies_api.cc index 8f020919..558f90d 100644 --- a/chrome/browser/extensions/api/cookies/cookies_api.cc +++ b/chrome/browser/extensions/api/cookies/cookies_api.cc @@ -382,9 +382,8 @@ void CookiesSetFunction::SetCookieOnIOThread() { } cookie_monster->SetCookieWithDetailsAsync( - url_, - parsed_args_->details.name.get() ? *parsed_args_->details.name - : std::string(), + url_, parsed_args_->details.name.get() ? *parsed_args_->details.name + : std::string(), parsed_args_->details.value.get() ? *parsed_args_->details.value : std::string(), parsed_args_->details.domain.get() ? *parsed_args_->details.domain @@ -399,7 +398,9 @@ void CookiesSetFunction::SetCookieOnIOThread() { // TODO(mkwst): If we decide to ship First-party-only cookies, we'll need // to extend the extension API to support them. For the moment, we'll set // all cookies as non-First-party-only. - false, + false, store_browser_context_->GetURLRequestContext() + ->network_delegate() + ->AreExperimentalCookieFeaturesEnabled(), net::COOKIE_PRIORITY_DEFAULT, base::Bind(&CookiesSetFunction::PullCookie, this)); } diff --git a/content/browser/frame_host/render_frame_message_filter.cc b/content/browser/frame_host/render_frame_message_filter.cc index d7e9d9e..8b6c077 100644 --- a/content/browser/frame_host/render_frame_message_filter.cc +++ b/content/browser/frame_host/render_frame_message_filter.cc @@ -4,6 +4,7 @@ #include "content/browser/frame_host/render_frame_message_filter.h" +#include "base/command_line.h" #include "content/browser/bad_message.h" #include "content/browser/child_process_security_policy_impl.h" #include "content/browser/frame_host/render_frame_host_impl.h" @@ -14,6 +15,7 @@ #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" #include "content/public/common/content_constants.h" +#include "content/public/common/content_switches.h" #include "gpu/GLES2/gl2extchromium.h" #include "net/cookies/cookie_store.h" #include "net/url_request/url_request_context.h" @@ -342,6 +344,10 @@ void RenderFrameMessageFilter::OnSetCookie(int render_frame_id, url, first_party_for_cookies, cookie, resource_context_, render_process_id_, render_frame_id, &options)) { net::URLRequestContext* context = GetRequestContextForURL(url); + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableExperimentalWebPlatformFeatures)) { + options.set_enforce_prefixes(); + } // Pass a null callback since we don't care about when the 'set' completes. context->cookie_store()->SetCookieWithOptionsAsync( url, cookie, options, net::CookieStore::SetCookiesCallback()); diff --git a/ios/net/cookies/cookie_store_ios_unittest.mm b/ios/net/cookies/cookie_store_ios_unittest.mm index 39e30db..7db6f40 100644 --- a/ios/net/cookies/cookie_store_ios_unittest.mm +++ b/ios/net/cookies/cookie_store_ios_unittest.mm @@ -42,6 +42,7 @@ struct CookieStoreIOSTestTraits { static const bool filters_schemes = false; static const bool has_path_prefix_bug = true; static const int creation_time_granularity_in_ms = 1000; + static const int enforces_prefixes = true; base::MessageLoop loop_; }; @@ -58,6 +59,7 @@ struct InactiveCookieStoreIOSTestTraits { static const bool filters_schemes = false; static const bool has_path_prefix_bug = false; static const int creation_time_granularity_in_ms = 0; + static const int enforces_prefixes = true; base::MessageLoop loop_; }; @@ -168,6 +170,7 @@ struct RoundTripTestCookieStoreTraits { static const bool filters_schemes = false; static const bool has_path_prefix_bug = true; static const int creation_time_granularity_in_ms = 1000; + static const int enforces_prefixes = true; }; } // namespace net diff --git a/net/cookies/cookie_monster.cc b/net/cookies/cookie_monster.cc index 2292f8c..0f8ba79 100644 --- a/net/cookies/cookie_monster.cc +++ b/net/cookies/cookie_monster.cc @@ -331,6 +331,13 @@ void RunAsync(scoped_refptr<base::TaskRunner> proxy, proxy->PostTask(FROM_HERE, base::Bind(callback, cookie, removed)); } +bool CheckCookiePrefix(CanonicalCookie* cc, const CookieOptions& options) { + const char kSecurePrefix[] = "$Secure-"; + if (cc->Name().find(kSecurePrefix) == 0) + return cc->IsSecure() && cc->Source().SchemeIsCryptographic(); + return true; +} + } // namespace CookieMonster::CookieMonster(PersistentCookieStore* store, @@ -439,6 +446,7 @@ class CookieMonster::SetCookieWithDetailsTask : public CookieMonsterTask { bool secure, bool http_only, bool first_party_only, + bool enforce_prefixes, CookiePriority priority, const SetCookiesCallback& callback) : CookieMonsterTask(cookie_monster), @@ -451,6 +459,7 @@ class CookieMonster::SetCookieWithDetailsTask : public CookieMonsterTask { secure_(secure), http_only_(http_only), first_party_only_(first_party_only), + enforce_prefixes_(enforce_prefixes), priority_(priority), callback_(callback) {} @@ -470,6 +479,7 @@ class CookieMonster::SetCookieWithDetailsTask : public CookieMonsterTask { bool secure_; bool http_only_; bool first_party_only_; + bool enforce_prefixes_; CookiePriority priority_; SetCookiesCallback callback_; @@ -479,7 +489,7 @@ class CookieMonster::SetCookieWithDetailsTask : public CookieMonsterTask { void CookieMonster::SetCookieWithDetailsTask::Run() { bool success = this->cookie_monster()->SetCookieWithDetails( url_, name_, value_, domain_, path_, expiration_time_, secure_, - http_only_, first_party_only_, priority_); + http_only_, first_party_only_, enforce_prefixes_, priority_); if (!callback_.is_null()) { this->InvokeCallback(base::Bind(&SetCookiesCallback::Run, base::Unretained(&callback_), success)); @@ -928,11 +938,12 @@ void CookieMonster::SetCookieWithDetailsAsync( bool secure, bool http_only, bool first_party_only, + bool enforce_prefixes, CookiePriority priority, const SetCookiesCallback& callback) { scoped_refptr<SetCookieWithDetailsTask> task = new SetCookieWithDetailsTask( this, url, name, value, domain, path, expiration_time, secure, http_only, - first_party_only, priority, callback); + first_party_only, enforce_prefixes, priority, callback); DoCookieTaskForURL(task, url); } @@ -1112,6 +1123,7 @@ bool CookieMonster::SetCookieWithDetails(const GURL& url, bool secure, bool http_only, bool first_party_only, + bool enforce_prefixes, CookiePriority priority) { base::AutoLock autolock(lock_); @@ -1132,6 +1144,8 @@ bool CookieMonster::SetCookieWithDetails(const GURL& url, CookieOptions options; options.set_include_httponly(); options.set_include_first_party_only(); + if (enforce_prefixes) + options.set_enforce_prefixes(); return SetCanonicalCookie(&cc, creation_time, options); } @@ -1888,6 +1902,12 @@ bool CookieMonster::SetCanonicalCookie(scoped_ptr<CanonicalCookie>* cc, const std::string key(GetKey((*cc)->Domain())); bool already_expired = (*cc)->IsExpired(creation_time); + if (options.enforce_prefixes() && !CheckCookiePrefix(cc->get(), options)) { + VLOG(kVlogSetCookies) << "SetCookie() not storing cookie '" << (*cc)->Name() + << "' that violates prefix rules."; + return false; + } + if (DeleteAnyEquivalentCookie(key, **cc, options.exclude_httponly(), already_expired)) { VLOG(kVlogSetCookies) << "SetCookie() not clobbering httponly cookie"; diff --git a/net/cookies/cookie_monster.h b/net/cookies/cookie_monster.h index 72d09d0..8426d72 100644 --- a/net/cookies/cookie_monster.h +++ b/net/cookies/cookie_monster.h @@ -167,6 +167,7 @@ class NET_EXPORT CookieMonster : public CookieStore { bool secure, bool http_only, bool first_party, + bool enforce_prefixes, CookiePriority priority, const SetCookiesCallback& callback); @@ -464,6 +465,7 @@ class NET_EXPORT CookieMonster : public CookieStore { bool secure, bool http_only, bool first_party, + bool enforce_prefixes, CookiePriority priority); CookieList GetAllCookies(); diff --git a/net/cookies/cookie_monster_unittest.cc b/net/cookies/cookie_monster_unittest.cc index 839add8..82ae3a2 100644 --- a/net/cookies/cookie_monster_unittest.cc +++ b/net/cookies/cookie_monster_unittest.cc @@ -100,6 +100,7 @@ struct CookieMonsterTestTraits { static const bool filters_schemes = true; static const bool has_path_prefix_bug = false; static const int creation_time_granularity_in_ms = 0; + static const bool enforces_prefixes = true; }; INSTANTIATE_TYPED_TEST_CASE_P(CookieMonster, @@ -160,7 +161,7 @@ class CookieMonsterTest : public CookieStoreTest<CookieMonsterTestTraits> { ResultSavingCookieCallback<bool> callback; cm->SetCookieWithDetailsAsync( url, name, value, domain, path, expiration_time, secure, http_only, - first_party_only, priority, + first_party_only, false /* enforce prefixes */, priority, base::Bind(&ResultSavingCookieCallback<bool>::Run, base::Unretained(&callback))); RunFor(kTimeout); @@ -670,8 +671,8 @@ ACTION_P4(DeleteAllCreatedBetweenAction, ACTION_P3(SetCookieWithDetailsAction, cookie_monster, cc, callback) { cookie_monster->SetCookieWithDetailsAsync( cc.url, cc.name, cc.value, cc.domain, cc.path, cc.expiration_time, - cc.secure, cc.http_only, cc.first_party_only, cc.priority, - callback->AsCallback()); + cc.secure, cc.http_only, cc.first_party_only, + false /* enforce prefixes */, cc.priority, callback->AsCallback()); } ACTION_P2(GetAllCookiesAction, cookie_monster, callback) { @@ -2475,7 +2476,7 @@ class MultiThreadedCookieMonsterTest : public CookieMonsterTest { CookiePriority priority = COOKIE_PRIORITY_DEFAULT; cm->SetCookieWithDetailsAsync( url, name, value, domain, path, expiration_time, secure, http_only, - first_party_only, priority, + first_party_only, false /* enforce prefixes */, priority, base::Bind(&ResultSavingCookieCallback<bool>::Run, base::Unretained(callback))); } @@ -2966,6 +2967,29 @@ TEST_F(CookieMonsterTest, CookieSourceHistogram) { CookieMonster::COOKIE_SOURCE_NONSECURE_COOKIE_NONCRYPTOGRAPHIC_SCHEME, 1); } +TEST_F(CookieMonsterTest, SecureCookiePrefix) { + scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); + // A $Secure- cookie must be Secure. + EXPECT_FALSE(SetCookie(cm.get(), https_www_google_.url(), "$Secure-A=B")); + EXPECT_FALSE( + SetCookie(cm.get(), https_www_google_.url(), "$Secure-A=B; httponly")); + + // A typoed prefix does not have to be Secure. + EXPECT_TRUE( + SetCookie(cm.get(), https_www_google_.url(), "$secure-A=B; Secure")); + EXPECT_TRUE(SetCookie(cm.get(), https_www_google_.url(), "$secure-A=C;")); + EXPECT_TRUE( + SetCookie(cm.get(), https_www_google_.url(), "$SecureA=B; Secure")); + EXPECT_TRUE(SetCookie(cm.get(), https_www_google_.url(), "$SecureA=C;")); + + EXPECT_TRUE( + SetCookie(cm.get(), https_www_google_.url(), "$Secure-A=B; Secure")); + + // A $Secure- cookie can't be set on a non-secure origin. + EXPECT_FALSE( + SetCookie(cm.get(), http_www_google_.url(), "$Secure-A=B; Secure")); +} + class CookieMonsterNotificationTest : public CookieMonsterTest { public: CookieMonsterNotificationTest() diff --git a/net/cookies/cookie_options.cc b/net/cookies/cookie_options.cc index 5cacb6b..73b7641 100644 --- a/net/cookies/cookie_options.cc +++ b/net/cookies/cookie_options.cc @@ -11,6 +11,7 @@ namespace net { CookieOptions::CookieOptions() : exclude_httponly_(true), include_first_party_only_(false), + enforce_prefixes_(false), server_time_() {} } // namespace net diff --git a/net/cookies/cookie_options.h b/net/cookies/cookie_options.h index d3d7fd0..9b40886 100644 --- a/net/cookies/cookie_options.h +++ b/net/cookies/cookie_options.h @@ -35,6 +35,11 @@ class NET_EXPORT CookieOptions { void set_first_party_url(const GURL& url) { first_party_url_ = url; } GURL first_party_url() const { return first_party_url_; } + // TODO(estark): Remove once we decide whether to ship cookie + // prefixes. https://crbug.com/541511 + void set_enforce_prefixes() { enforce_prefixes_ = true; } + bool enforce_prefixes() const { return enforce_prefixes_; } + // |server_time| indicates what the server sending us the Cookie thought the // current time was when the cookie was produced. This is used to adjust for // clock skew between server and host. @@ -48,6 +53,7 @@ class NET_EXPORT CookieOptions { bool exclude_httponly_; bool include_first_party_only_; GURL first_party_url_; + bool enforce_prefixes_; base::Time server_time_; }; diff --git a/net/cookies/cookie_store_unittest.h b/net/cookies/cookie_store_unittest.h index b380fbc..f62f7ac 100644 --- a/net/cookies/cookie_store_unittest.h +++ b/net/cookies/cookie_store_unittest.h @@ -69,6 +69,9 @@ const char kValidCookieLine[] = "A=B; path=/"; // // Time to wait between two cookie insertions to ensure that cookies have // // different creation times. // static const int creation_time_granularity_in_ms; +// +// // The cookie store enforces cookie prefixes. +// static const bool enforces_prefixes; // }; template <class CookieStoreTestTraits> @@ -155,6 +158,8 @@ class CookieStoreTest : public testing::Test { CookieOptions options; if (!CookieStoreTestTraits::supports_http_only) options.set_include_httponly(); + if (CookieStoreTestTraits::enforces_prefixes) + options.set_enforce_prefixes(); return SetCookieWithOptions(cs, url, cookie_line, options); } diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc index 3cace90..263f502 100644 --- a/net/url_request/url_request_http_job.cc +++ b/net/url_request/url_request_http_job.cc @@ -749,6 +749,8 @@ void URLRequestHttpJob::SaveNextCookie() { CookieOptions options; options.set_include_httponly(); options.set_server_time(response_date_); + if (network_delegate()->AreExperimentalCookieFeaturesEnabled()) + options.set_enforce_prefixes(); CookieStore::SetCookiesCallback callback(base::Bind( &URLRequestHttpJob::OnCookieSaved, weak_factory_.GetWeakPtr(), diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc index f91a283..3fb6cb2 100644 --- a/net/url_request/url_request_unittest.cc +++ b/net/url_request/url_request_unittest.cc @@ -662,6 +662,11 @@ class MockCertificateReportSender std::string latest_report_; }; +class TestExperimentalFeaturesNetworkDelegate : public TestNetworkDelegate { + public: + bool OnAreExperimentalCookieFeaturesEnabled() const override { return true; } +}; + } // namespace // Inherit PlatformTest since we require the autorelease pool on Mac OS X. @@ -2722,6 +2727,181 @@ TEST_F(URLRequestTest, FirstPartyOnlyCookiesDisabled) { } } +// Tests that $Secure- cookies can't be set on non-secure origins. +TEST_F(URLRequestTest, SecureCookiePrefixOnNonsecureOrigin) { + LocalHttpTestServer test_server; + ASSERT_TRUE(test_server.Start()); + SpawnedTestServer test_server_https( + SpawnedTestServer::TYPE_HTTPS, SpawnedTestServer::kLocalhost, + base::FilePath(FILE_PATH_LITERAL("net/data/ssl"))); + ASSERT_TRUE(test_server_https.Start()); + + TestExperimentalFeaturesNetworkDelegate network_delegate; + TestURLRequestContext context(true); + context.set_network_delegate(&network_delegate); + context.Init(); + + // Try to set a Secure $Secure- cookie, with experimental features + // enabled. + { + TestDelegate d; + scoped_ptr<URLRequest> req(context.CreateRequest( + test_server.GetURL("set-cookie?$Secure-nonsecure-origin=1;Secure"), + DEFAULT_PRIORITY, &d)); + req->Start(); + base::RunLoop().Run(); + EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); + EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); + } + + // Verify that the cookie is not set. + { + TestDelegate d; + scoped_ptr<URLRequest> req(context.CreateRequest( + test_server_https.GetURL("echoheader?Cookie"), DEFAULT_PRIORITY, &d)); + req->Start(); + base::RunLoop().Run(); + + EXPECT_TRUE(d.data_received().find("$Secure-nonsecure-origin=1") == + std::string::npos); + EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); + EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); + } +} + +TEST_F(URLRequestTest, SecureCookiePrefixNonexperimental) { + SpawnedTestServer test_server( + SpawnedTestServer::TYPE_HTTPS, SpawnedTestServer::kLocalhost, + base::FilePath(FILE_PATH_LITERAL("net/data/ssl"))); + ASSERT_TRUE(test_server.Start()); + + TestNetworkDelegate network_delegate; + TestURLRequestContext context(true); + context.set_network_delegate(&network_delegate); + context.Init(); + + // Without experimental features, there should be no restrictions on + // $Secure- cookies. + + // Set a non-Secure cookie with the $Secure- prefix. + { + TestDelegate d; + scoped_ptr<URLRequest> req(context.CreateRequest( + test_server.GetURL("set-cookie?$Secure-nonsecure-not-experimental=1"), + DEFAULT_PRIORITY, &d)); + req->Start(); + base::RunLoop().Run(); + EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); + EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); + } + + // Set a Secure cookie with the $Secure- prefix. + { + TestDelegate d; + scoped_ptr<URLRequest> req(context.CreateRequest( + test_server.GetURL( + "set-cookie?$Secure-secure-not-experimental=1;Secure"), + DEFAULT_PRIORITY, &d)); + req->Start(); + base::RunLoop().Run(); + EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); + EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); + } + + // Verify that the cookies are set. Neither should have any + // restrictions because the experimental flag is off. + { + TestDelegate d; + scoped_ptr<URLRequest> req(context.CreateRequest( + test_server.GetURL("echoheader?Cookie"), DEFAULT_PRIORITY, &d)); + req->Start(); + base::RunLoop().Run(); + + EXPECT_TRUE(d.data_received().find("$Secure-secure-not-experimental=1") != + std::string::npos); + EXPECT_TRUE( + d.data_received().find("$Secure-nonsecure-not-experimental=1") != + std::string::npos); + EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); + EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); + } +} + +TEST_F(URLRequestTest, SecureCookiePrefixExperimentalNonsecure) { + SpawnedTestServer test_server( + SpawnedTestServer::TYPE_HTTPS, SpawnedTestServer::kLocalhost, + base::FilePath(FILE_PATH_LITERAL("net/data/ssl"))); + ASSERT_TRUE(test_server.Start()); + + TestExperimentalFeaturesNetworkDelegate network_delegate; + TestURLRequestContext context(true); + context.set_network_delegate(&network_delegate); + context.Init(); + + // Try to set a non-Secure $Secure- cookie, with experimental features + // enabled. + { + TestDelegate d; + scoped_ptr<URLRequest> req(context.CreateRequest( + test_server.GetURL("set-cookie?$Secure-foo=1"), DEFAULT_PRIORITY, &d)); + req->Start(); + base::RunLoop().Run(); + EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); + EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); + } + + // Verify that the cookie is not set. + { + TestDelegate d; + scoped_ptr<URLRequest> req(context.CreateRequest( + test_server.GetURL("echoheader?Cookie"), DEFAULT_PRIORITY, &d)); + req->Start(); + base::RunLoop().Run(); + + EXPECT_TRUE(d.data_received().find("$Secure-foo=1") == std::string::npos); + EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); + EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); + } +} + +TEST_F(URLRequestTest, SecureCookiePrefixExperimentalSecure) { + SpawnedTestServer test_server( + SpawnedTestServer::TYPE_HTTPS, SpawnedTestServer::kLocalhost, + base::FilePath(FILE_PATH_LITERAL("net/data/ssl"))); + ASSERT_TRUE(test_server.Start()); + + TestExperimentalFeaturesNetworkDelegate network_delegate; + TestURLRequestContext context(true); + context.set_network_delegate(&network_delegate); + context.Init(); + + // Try to set a Secure $Secure- cookie, with experimental features + // enabled. + { + TestDelegate d; + scoped_ptr<URLRequest> req(context.CreateRequest( + test_server.GetURL("set-cookie?$Secure-bar=1;Secure"), DEFAULT_PRIORITY, + &d)); + req->Start(); + base::RunLoop().Run(); + EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); + EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); + } + + // Verify that the cookie is set. + { + TestDelegate d; + scoped_ptr<URLRequest> req(context.CreateRequest( + test_server.GetURL("echoheader?Cookie"), DEFAULT_PRIORITY, &d)); + req->Start(); + base::RunLoop().Run(); + + EXPECT_TRUE(d.data_received().find("$Secure-bar=1") != std::string::npos); + EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); + EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); + } +} + // Tests that a request is cancelled while entering suspend mode. Uses mocks // rather than a spawned test server because the connection used to talk to // the test server is affected by entering suspend mode on Android. |