Commit f44591a9 authored by treib@chromium.org's avatar treib@chromium.org

Enforce SafetyMode for YouTube if prefs::kForceSafeSearch is on, by modifying...

Enforce SafetyMode for YouTube if prefs::kForceSafeSearch is on, by modifying (or adding) YouTube's PrefCookie in the HTTP header.

This is essentially a followup to https://codereview.chromium.org/11186002/

BUG=344815

Review URL: https://codereview.chromium.org/354183002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@282546 0039d316-1c4b-4281-b951-d872f2087c98
parent afa3c72e
...@@ -34,14 +34,13 @@ ...@@ -34,14 +34,13 @@
using base::Time; using base::Time;
using content::ResourceType; using content::ResourceType;
using extensions::ExtensionWarning; using extensions::ExtensionWarning;
using net::cookie_util::ParsedRequestCookie;
using net::cookie_util::ParsedRequestCookies;
namespace extension_web_request_api_helpers { namespace extension_web_request_api_helpers {
namespace { namespace {
// A ParsedRequestCookie consists of the key and value of the cookie.
typedef std::pair<base::StringPiece, base::StringPiece> ParsedRequestCookie;
typedef std::vector<ParsedRequestCookie> ParsedRequestCookies;
typedef std::vector<linked_ptr<net::ParsedCookie> > ParsedResponseCookies; typedef std::vector<linked_ptr<net::ParsedCookie> > ParsedResponseCookies;
static const char* kResourceTypeStrings[] = { static const char* kResourceTypeStrings[] = {
...@@ -483,67 +482,6 @@ void MergeOnBeforeRequestResponses( ...@@ -483,67 +482,6 @@ void MergeOnBeforeRequestResponses(
MergeRedirectUrlOfResponses(deltas, new_url, conflicting_extensions, net_log); MergeRedirectUrlOfResponses(deltas, new_url, conflicting_extensions, net_log);
} }
// Assumes that |header_value| is the cookie header value of a HTTP Request
// following the cookie-string schema of RFC 6265, section 4.2.1, and returns
// cookie name/value pairs. If cookie values are presented in double quotes,
// these will appear in |parsed| as well. We can assume that the cookie header
// is written by Chromium and therefore, well-formed.
static void ParseRequestCookieLine(
const std::string& header_value,
ParsedRequestCookies* parsed_cookies) {
std::string::const_iterator i = header_value.begin();
while (i != header_value.end()) {
// Here we are at the beginning of a cookie.
// Eat whitespace.
while (i != header_value.end() && *i == ' ') ++i;
if (i == header_value.end()) return;
// Find cookie name.
std::string::const_iterator cookie_name_beginning = i;
while (i != header_value.end() && *i != '=') ++i;
base::StringPiece cookie_name(cookie_name_beginning, i);
// Find cookie value.
base::StringPiece cookie_value;
if (i != header_value.end()) { // Cookies may have no value.
++i; // Skip '='.
std::string::const_iterator cookie_value_beginning = i;
if (*i == '"') {
++i; // Skip '"'.
while (i != header_value.end() && *i != '"') ++i;
if (i == header_value.end()) return;
++i; // Skip '"'.
cookie_value = base::StringPiece(cookie_value_beginning, i);
// i points to character after '"', potentially a ';'
} else {
while (i != header_value.end() && *i != ';') ++i;
cookie_value = base::StringPiece(cookie_value_beginning, i);
// i points to ';' or end of string.
}
}
parsed_cookies->push_back(make_pair(cookie_name, cookie_value));
// Eat ';'
if (i != header_value.end()) ++i;
}
}
// Writes all cookies of |parsed_cookies| into a HTTP Request header value
// that belongs to the "Cookie" header.
static std::string SerializeRequestCookieLine(
const ParsedRequestCookies& parsed_cookies) {
std::string buffer;
for (ParsedRequestCookies::const_iterator i = parsed_cookies.begin();
i != parsed_cookies.end(); ++i) {
if (!buffer.empty())
buffer += "; ";
buffer += i->first.as_string();
if (!i->second.empty())
buffer += "=" + i->second.as_string();
}
return buffer;
}
static bool DoesRequestCookieMatchFilter( static bool DoesRequestCookieMatchFilter(
const ParsedRequestCookie& cookie, const ParsedRequestCookie& cookie,
RequestCookie* filter) { RequestCookie* filter) {
...@@ -681,7 +619,7 @@ void MergeCookiesInOnBeforeSendHeadersResponses( ...@@ -681,7 +619,7 @@ void MergeCookiesInOnBeforeSendHeadersResponses(
std::string cookie_header; std::string cookie_header;
request_headers->GetHeader(net::HttpRequestHeaders::kCookie, &cookie_header); request_headers->GetHeader(net::HttpRequestHeaders::kCookie, &cookie_header);
ParsedRequestCookies cookies; ParsedRequestCookies cookies;
ParseRequestCookieLine(cookie_header, &cookies); net::cookie_util::ParseRequestCookieLine(cookie_header, &cookies);
// Modify cookies. // Modify cookies.
bool modified = false; bool modified = false;
...@@ -691,7 +629,8 @@ void MergeCookiesInOnBeforeSendHeadersResponses( ...@@ -691,7 +629,8 @@ void MergeCookiesInOnBeforeSendHeadersResponses(
// Reassemble and store new cookie line. // Reassemble and store new cookie line.
if (modified) { if (modified) {
std::string new_cookie_header = SerializeRequestCookieLine(cookies); std::string new_cookie_header =
net::cookie_util::SerializeRequestCookieLine(cookies);
request_headers->SetHeader(net::HttpRequestHeaders::kCookie, request_headers->SetHeader(net::HttpRequestHeaders::kCookie,
new_cookie_header); new_cookie_header);
} }
......
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
#include "base/prefs/pref_member.h" #include "base/prefs/pref_member.h"
#include "base/prefs/pref_service.h" #include "base/prefs/pref_service.h"
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process.h"
#include "chrome/browser/content_settings/cookie_settings.h" #include "chrome/browser/content_settings/cookie_settings.h"
...@@ -25,19 +24,18 @@ ...@@ -25,19 +24,18 @@
#include "chrome/browser/net/chrome_extensions_network_delegate.h" #include "chrome/browser/net/chrome_extensions_network_delegate.h"
#include "chrome/browser/net/client_hints.h" #include "chrome/browser/net/client_hints.h"
#include "chrome/browser/net/connect_interceptor.h" #include "chrome/browser/net/connect_interceptor.h"
#include "chrome/browser/net/safe_search_util.h"
#include "chrome/browser/performance_monitor/performance_monitor.h" #include "chrome/browser/performance_monitor/performance_monitor.h"
#include "chrome/browser/prerender/prerender_tracker.h" #include "chrome/browser/prerender/prerender_tracker.h"
#include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/task_manager/task_manager.h" #include "chrome/browser/task_manager/task_manager.h"
#include "chrome/common/pref_names.h" #include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "components/data_reduction_proxy/browser/data_reduction_proxy_auth_request_handler.h" #include "components/data_reduction_proxy/browser/data_reduction_proxy_auth_request_handler.h"
#include "components/data_reduction_proxy/browser/data_reduction_proxy_metrics.h" #include "components/data_reduction_proxy/browser/data_reduction_proxy_metrics.h"
#include "components/data_reduction_proxy/browser/data_reduction_proxy_params.h" #include "components/data_reduction_proxy/browser/data_reduction_proxy_params.h"
#include "components/data_reduction_proxy/browser/data_reduction_proxy_protocol.h" #include "components/data_reduction_proxy/browser/data_reduction_proxy_protocol.h"
#include "components/data_reduction_proxy/browser/data_reduction_proxy_usage_stats.h" #include "components/data_reduction_proxy/browser/data_reduction_proxy_usage_stats.h"
#include "components/domain_reliability/monitor.h" #include "components/domain_reliability/monitor.h"
#include "components/google/core/browser/google_util.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_view_host.h" #include "content/public/browser/render_view_host.h"
...@@ -93,61 +91,6 @@ namespace { ...@@ -93,61 +91,6 @@ namespace {
const char kDNTHeader[] = "DNT"; const char kDNTHeader[] = "DNT";
// Returns whether a URL parameter, |first_parameter| (e.g. foo=bar), has the
// same key as the the |second_parameter| (e.g. foo=baz). Both parameters
// must be in key=value form.
bool HasSameParameterKey(const std::string& first_parameter,
const std::string& second_parameter) {
DCHECK(second_parameter.find("=") != std::string::npos);
// Prefix for "foo=bar" is "foo=".
std::string parameter_prefix = second_parameter.substr(
0, second_parameter.find("=") + 1);
return StartsWithASCII(first_parameter, parameter_prefix, false);
}
// Examines the query string containing parameters and adds the necessary ones
// so that SafeSearch is active. |query| is the string to examine and the
// return value is the |query| string modified such that SafeSearch is active.
std::string AddSafeSearchParameters(const std::string& query) {
std::vector<std::string> new_parameters;
std::string safe_parameter = chrome::kSafeSearchSafeParameter;
std::string ssui_parameter = chrome::kSafeSearchSsuiParameter;
std::vector<std::string> parameters;
base::SplitString(query, '&', &parameters);
std::vector<std::string>::iterator it;
for (it = parameters.begin(); it < parameters.end(); ++it) {
if (!HasSameParameterKey(*it, safe_parameter) &&
!HasSameParameterKey(*it, ssui_parameter)) {
new_parameters.push_back(*it);
}
}
new_parameters.push_back(safe_parameter);
new_parameters.push_back(ssui_parameter);
return JoinString(new_parameters, '&');
}
// If |request| is a request to Google Web Search the function
// enforces that the SafeSearch query parameters are set to active.
// Sets the query part of |new_url| with the new value of the parameters.
void ForceGoogleSafeSearch(net::URLRequest* request,
GURL* new_url) {
if (!google_util::IsGoogleSearchUrl(request->url()) &&
!google_util::IsGoogleHomePageUrl(request->url()))
return;
std::string query = request->url().query();
std::string new_query = AddSafeSearchParameters(query);
if (query == new_query)
return;
GURL::Replacements replacements;
replacements.SetQueryStr(new_query);
*new_url = request->url().ReplaceComponents(replacements);
}
// Gets called when the extensions finish work on the URL. If the extensions // Gets called when the extensions finish work on the URL. If the extensions
// did not do a redirect (so |new_url| is empty) then we enforce the // did not do a redirect (so |new_url| is empty) then we enforce the
// SafeSearch parameters. Otherwise we will get called again after the // SafeSearch parameters. Otherwise we will get called again after the
...@@ -158,7 +101,7 @@ void ForceGoogleSafeSearchCallbackWrapper( ...@@ -158,7 +101,7 @@ void ForceGoogleSafeSearchCallbackWrapper(
GURL* new_url, GURL* new_url,
int rv) { int rv) {
if (rv == net::OK && new_url->is_empty()) if (rv == net::OK && new_url->is_empty())
ForceGoogleSafeSearch(request, new_url); safe_search_util::ForceGoogleSafeSearch(request, new_url);
callback.Run(rv); callback.Run(rv);
} }
...@@ -467,7 +410,7 @@ int ChromeNetworkDelegate::OnBeforeURLRequest( ...@@ -467,7 +410,7 @@ int ChromeNetworkDelegate::OnBeforeURLRequest(
request, wrapped_callback, new_url); request, wrapped_callback, new_url);
if (force_safe_search && rv == net::OK && new_url->is_empty()) if (force_safe_search && rv == net::OK && new_url->is_empty())
ForceGoogleSafeSearch(request, new_url); safe_search_util::ForceGoogleSafeSearch(request, new_url);
if (connect_interceptor_) if (connect_interceptor_)
connect_interceptor_->WitnessURLRequest(request); connect_interceptor_->WitnessURLRequest(request);
...@@ -487,6 +430,11 @@ int ChromeNetworkDelegate::OnBeforeSendHeaders( ...@@ -487,6 +430,11 @@ int ChromeNetworkDelegate::OnBeforeSendHeaders(
net::URLRequest* request, net::URLRequest* request,
const net::CompletionCallback& callback, const net::CompletionCallback& callback,
net::HttpRequestHeaders* headers) { net::HttpRequestHeaders* headers) {
bool force_safe_search = force_google_safe_search_ &&
force_google_safe_search_->GetValue();
if (force_safe_search)
safe_search_util::ForceYouTubeSafetyMode(request, headers);
TRACE_EVENT_ASYNC_STEP_PAST0("net", "URLRequest", request, "SendRequest"); TRACE_EVENT_ASYNC_STEP_PAST0("net", "URLRequest", request, "SendRequest");
return extensions_delegate_->OnBeforeSendHeaders(request, callback, headers); return extensions_delegate_->OnBeforeSendHeaders(request, callback, headers);
} }
......
// 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.
#include "chrome/browser/net/safe_search_util.h"
#include <string>
#include <utility>
#include <vector>
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "chrome/common/url_constants.h"
#include "components/google/core/browser/google_util.h"
#include "net/cookies/cookie_util.h"
#include "net/http/http_request_headers.h"
#include "net/url_request/url_request.h"
#include "url/gurl.h"
namespace {
const char kYouTubePrefCookieName[] = "PREF";
// YouTube pref flags are stored in bit masks of 31 bits each, called "f1",
// "f2" etc. The Safety Mode flag is bit 58, so bit 27 in "f2".
const char kYouTubePrefCookieSafetyModeFlagsEntryName[] = "f2";
const int kYouTubePrefCookieSafetyModeFlagsEntryValue = (1 << 27);
// Returns whether a URL parameter, |first_parameter| (e.g. foo=bar), has the
// same key as the the |second_parameter| (e.g. foo=baz). Both parameters
// must be in key=value form.
bool HasSameParameterKey(const std::string& first_parameter,
const std::string& second_parameter) {
DCHECK(second_parameter.find("=") != std::string::npos);
// Prefix for "foo=bar" is "foo=".
std::string parameter_prefix = second_parameter.substr(
0, second_parameter.find("=") + 1);
return StartsWithASCII(first_parameter, parameter_prefix, false);
}
// Examines the query string containing parameters and adds the necessary ones
// so that SafeSearch is active. |query| is the string to examine and the
// return value is the |query| string modified such that SafeSearch is active.
std::string AddSafeSearchParameters(const std::string& query) {
std::vector<std::string> new_parameters;
std::string safe_parameter = chrome::kSafeSearchSafeParameter;
std::string ssui_parameter = chrome::kSafeSearchSsuiParameter;
std::vector<std::string> parameters;
base::SplitString(query, '&', &parameters);
std::vector<std::string>::iterator it;
for (it = parameters.begin(); it < parameters.end(); ++it) {
if (!HasSameParameterKey(*it, safe_parameter) &&
!HasSameParameterKey(*it, ssui_parameter)) {
new_parameters.push_back(*it);
}
}
new_parameters.push_back(safe_parameter);
new_parameters.push_back(ssui_parameter);
return JoinString(new_parameters, '&');
}
bool IsYouTubePrefCookie(const net::cookie_util::ParsedRequestCookie& cookie) {
return cookie.first == base::StringPiece(kYouTubePrefCookieName);
}
bool IsYouTubePrefCookieSafetyModeFlagsEntry(
const std::pair<std::string, std::string>& pref_entry) {
return pref_entry.first == kYouTubePrefCookieSafetyModeFlagsEntryName;
}
std::string JoinStringKeyValuePair(
const base::StringPairs::value_type& key_value,
char delimiter) {
return key_value.first + delimiter + key_value.second;
}
// Does the opposite of base::SplitStringIntoKeyValuePairs() from
// base/strings/string_util.h.
std::string JoinStringKeyValuePairs(const base::StringPairs& pairs,
char key_value_delimiter,
char key_value_pair_delimiter) {
if (pairs.empty())
return std::string();
base::StringPairs::const_iterator it = pairs.begin();
std::string result = JoinStringKeyValuePair(*it, key_value_delimiter);
++it;
for (; it != pairs.end(); ++it) {
result += key_value_pair_delimiter;
result += JoinStringKeyValuePair(*it, key_value_delimiter);
}
return result;
}
} // namespace
namespace safe_search_util {
// If |request| is a request to Google Web Search the function
// enforces that the SafeSearch query parameters are set to active.
// Sets the query part of |new_url| with the new value of the parameters.
void ForceGoogleSafeSearch(const net::URLRequest* request, GURL* new_url) {
if (!google_util::IsGoogleSearchUrl(request->url()) &&
!google_util::IsGoogleHomePageUrl(request->url()))
return;
std::string query = request->url().query();
std::string new_query = AddSafeSearchParameters(query);
if (query == new_query)
return;
GURL::Replacements replacements;
replacements.SetQueryStr(new_query);
*new_url = request->url().ReplaceComponents(replacements);
}
// If |request| is a request to YouTube, enforces YouTube's Safety Mode by
// adding/modifying YouTube's PrefCookie header.
void ForceYouTubeSafetyMode(const net::URLRequest* request,
net::HttpRequestHeaders* headers) {
if (!google_util::IsYoutubeDomainUrl(
request->url(),
google_util::ALLOW_SUBDOMAIN,
google_util::DISALLOW_NON_STANDARD_PORTS))
return;
// Get the cookie string from the headers and parse it into key/value pairs.
std::string cookie_string;
headers->GetHeader(base::StringPiece(net::HttpRequestHeaders::kCookie),
&cookie_string);
net::cookie_util::ParsedRequestCookies cookies;
net::cookie_util::ParseRequestCookieLine(cookie_string, &cookies);
// Find YouTube's pref cookie, or add it if it doesn't exist yet.
net::cookie_util::ParsedRequestCookies::iterator pref_it =
std::find_if(cookies.begin(), cookies.end(), IsYouTubePrefCookie);
if (pref_it == cookies.end()) {
cookies.push_back(std::make_pair(base::StringPiece(kYouTubePrefCookieName),
base::StringPiece()));
pref_it = cookies.end() - 1;
}
// The pref cookie's value may be quoted. If so, remove the quotes.
std::string pref_string = pref_it->second.as_string();
bool pref_string_quoted = false;
if (pref_string.size() >= 2 &&
pref_string[0] == '\"' &&
pref_string[pref_string.size() - 1] == '\"') {
pref_string_quoted = true;
pref_string = pref_string.substr(1, pref_string.length() - 2);
}
// The pref cookie's value consists of key/value pairs. Parse them.
base::StringPairs pref_values;
base::SplitStringIntoKeyValuePairs(pref_string, '=', '&', &pref_values);
// Find the "flags" entry that contains the Safety Mode flag, or add it if it
// doesn't exist.
base::StringPairs::iterator flag_it =
std::find_if(pref_values.begin(), pref_values.end(),
IsYouTubePrefCookieSafetyModeFlagsEntry);
int flag_value = 0;
if (flag_it == pref_values.end()) {
pref_values.push_back(
std::make_pair(std::string(kYouTubePrefCookieSafetyModeFlagsEntryName),
std::string()));
flag_it = pref_values.end() - 1;
} else {
base::HexStringToInt(base::StringPiece(flag_it->second), &flag_value);
}
// Set the Safety Mode bit.
flag_value |= kYouTubePrefCookieSafetyModeFlagsEntryValue;
// Finally, put it all back together and replace the original cookie string.
flag_it->second = base::StringPrintf("%x", flag_value);
pref_string = JoinStringKeyValuePairs(pref_values, '=', '&');
if (pref_string_quoted) {
pref_string = '\"' + pref_string + '\"';
}
pref_it->second = base::StringPiece(pref_string);
cookie_string = net::cookie_util::SerializeRequestCookieLine(cookies);
headers->SetHeader(base::StringPiece(net::HttpRequestHeaders::kCookie),
base::StringPiece(cookie_string));
}
} // namespace safe_search_util
// 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.
#ifndef CHROME_BROWSER_NET_SAFE_SEARCH_UTIL_H_
#define CHROME_BROWSER_NET_SAFE_SEARCH_UTIL_H_
class GURL;
namespace net {
class HttpRequestHeaders;
class URLRequest;
}
namespace safe_search_util {
// If |request| is a request to Google Web Search, enforces that the SafeSearch
// query parameters are set to active. Sets |new_url| to a copy of the request
// url in which the query part contains the new values of the parameters.
void ForceGoogleSafeSearch(const net::URLRequest* request, GURL* new_url);
// If |request| is a request to YouTube, enforces YouTube's Safety Mode by
// adding/modifying YouTube's PrefCookie header.
void ForceYouTubeSafetyMode(const net::URLRequest* request,
net::HttpRequestHeaders* headers);
} // namespace safe_search_util
#endif // CHROME_BROWSER_NET_SAFE_SEARCH_UTIL_H_
// 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.
#include "chrome/browser/net/safe_search_util.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/string_piece.h"
#include "net/http/http_request_headers.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
class SafeSearchUtilTest : public ::testing::Test {
protected:
SafeSearchUtilTest() {}
virtual ~SafeSearchUtilTest() {}
scoped_ptr<net::URLRequest> CreateYoutubeRequest() {
return context_.CreateRequest(GURL("http://www.youtube.com"),
net::DEFAULT_PRIORITY,
NULL,
NULL);
}
scoped_ptr<net::URLRequest> CreateNonYoutubeRequest() {
return context_.CreateRequest(GURL("http://www.notyoutube.com"),
net::DEFAULT_PRIORITY,
NULL,
NULL);
}
static void SetCookie(net::HttpRequestHeaders* headers,
const std::string& value) {
headers->SetHeader(base::StringPiece(net::HttpRequestHeaders::kCookie),
base::StringPiece(value));
}
static void CheckHeaders(net::URLRequest* request,
const std::string& header_string_original,
const std::string& header_string_expected) {
net::HttpRequestHeaders headers;
SetCookie(&headers, header_string_original);
safe_search_util::ForceYouTubeSafetyMode(request, &headers);
net::HttpRequestHeaders headers_expected;
SetCookie(&headers_expected, header_string_expected);
EXPECT_EQ(headers_expected.ToString(), headers.ToString());
}
base::MessageLoop message_loop_;
net::TestURLRequestContext context_;
};
// ForceGoogleSafeSearch is already tested quite extensively in
// ChromeNetworkDelegateSafeSearchTest (in chrome_network_delegate_unittest.cc),
// so we won't test it again here.
TEST_F(SafeSearchUtilTest, CreateYoutubePrefCookie) {
scoped_ptr<net::URLRequest> request = CreateYoutubeRequest();
CheckHeaders(request.get(),
"OtherCookie=value",
"OtherCookie=value; PREF=f2=8000000");
}
TEST_F(SafeSearchUtilTest, ModifyYoutubePrefCookie) {
scoped_ptr<net::URLRequest> request = CreateYoutubeRequest();
CheckHeaders(request.get(),
"PREF=f1=123; OtherCookie=value",
"PREF=f1=123&f2=8000000; OtherCookie=value");
CheckHeaders(request.get(),
"PREF=",
"PREF=f2=8000000");
CheckHeaders(request.get(),
"PREF=\"\"",
"PREF=\"f2=8000000\"");
CheckHeaders(request.get(),
"PREF=f1=123&f2=4321&foo=bar",
"PREF=f1=123&f2=8004321&foo=bar");
CheckHeaders(request.get(),
"PREF=\"f1=1&f2=4321\"; OtherCookie=value",
"PREF=\"f1=1&f2=8004321\"; OtherCookie=value");
}
TEST_F(SafeSearchUtilTest, DoesntTouchNonYoutubeURL) {
scoped_ptr<net::URLRequest> request = CreateNonYoutubeRequest();
CheckHeaders(request.get(),
"PREF=f2=0",
"PREF=f2=0");
}
...@@ -821,6 +821,8 @@ ...@@ -821,6 +821,8 @@
'browser/net/proxy_service_factory.h', 'browser/net/proxy_service_factory.h',
'browser/net/referrer.cc', 'browser/net/referrer.cc',
'browser/net/referrer.h', 'browser/net/referrer.h',
'browser/net/safe_search_util.cc',
'browser/net/safe_search_util.h',
'browser/net/service_providers_win.cc', 'browser/net/service_providers_win.cc',
'browser/net/service_providers_win.h', 'browser/net/service_providers_win.h',
'browser/net/spdyproxy/data_reduction_proxy_chrome_configurator.cc', 'browser/net/spdyproxy/data_reduction_proxy_chrome_configurator.cc',
......
...@@ -1130,6 +1130,7 @@ ...@@ -1130,6 +1130,7 @@
'browser/net/pref_proxy_config_tracker_impl_unittest.cc', 'browser/net/pref_proxy_config_tracker_impl_unittest.cc',
'browser/net/probe_message_unittest.cc', 'browser/net/probe_message_unittest.cc',
'browser/net/proxy_policy_handler_unittest.cc', 'browser/net/proxy_policy_handler_unittest.cc',
'browser/net/safe_search_util_unittest.cc',
'browser/net/spdyproxy/data_reduction_proxy_chrome_configurator_unittest.cc', 'browser/net/spdyproxy/data_reduction_proxy_chrome_configurator_unittest.cc',
'browser/net/spdyproxy/data_reduction_proxy_settings_unittest_android.cc', 'browser/net/spdyproxy/data_reduction_proxy_settings_unittest_android.cc',
'browser/net/sqlite_server_bound_cert_store_unittest.cc', 'browser/net/sqlite_server_bound_cert_store_unittest.cc',
......
...@@ -210,6 +210,59 @@ GURL CookieOriginToURL(const std::string& domain, bool is_https) { ...@@ -210,6 +210,59 @@ GURL CookieOriginToURL(const std::string& domain, bool is_https) {
return GURL(scheme + "://" + host); return GURL(scheme + "://" + host);
} }
void ParseRequestCookieLine(const std::string& header_value,
ParsedRequestCookies* parsed_cookies) {
std::string::const_iterator i = header_value.begin();
while (i != header_value.end()) {
// Here we are at the beginning of a cookie.
// Eat whitespace.
while (i != header_value.end() && *i == ' ') ++i;
if (i == header_value.end()) return;
// Find cookie name.
std::string::const_iterator cookie_name_beginning = i;
while (i != header_value.end() && *i != '=') ++i;
base::StringPiece cookie_name(cookie_name_beginning, i);
// Find cookie value.
base::StringPiece cookie_value;
if (i != header_value.end()) { // Cookies may have no value.
++i; // Skip '='.
std::string::const_iterator cookie_value_beginning = i;
if (*i == '"') {
++i; // Skip '"'.
while (i != header_value.end() && *i != '"') ++i;
if (i == header_value.end()) return;
++i; // Skip '"'.
cookie_value = base::StringPiece(cookie_value_beginning, i);
// i points to character after '"', potentially a ';'
} else {
while (i != header_value.end() && *i != ';') ++i;
cookie_value = base::StringPiece(cookie_value_beginning, i);
// i points to ';' or end of string.
}
}
parsed_cookies->push_back(std::make_pair(cookie_name, cookie_value));
// Eat ';'.
if (i != header_value.end()) ++i;
}
}
std::string SerializeRequestCookieLine(
const ParsedRequestCookies& parsed_cookies) {
std::string buffer;
for (ParsedRequestCookies::const_iterator i = parsed_cookies.begin();
i != parsed_cookies.end(); ++i) {
if (!buffer.empty())
buffer.append("; ");
buffer.append(i->first.begin(), i->first.end());
buffer.push_back('=');
buffer.append(i->second.begin(), i->second.end());
}
return buffer;
}
} // namespace cookie_utils } // namespace cookie_utils
} // namespace net } // namespace net
...@@ -6,7 +6,10 @@ ...@@ -6,7 +6,10 @@
#define NET_COOKIES_COOKIE_UTIL_H_ #define NET_COOKIES_COOKIE_UTIL_H_
#include <string> #include <string>
#include <utility>
#include <vector>
#include "base/strings/string_piece.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "net/base/net_export.h" #include "net/base/net_export.h"
...@@ -40,7 +43,25 @@ NET_EXPORT base::Time ParseCookieTime(const std::string& time_string); ...@@ -40,7 +43,25 @@ NET_EXPORT base::Time ParseCookieTime(const std::string& time_string);
// Convenience for converting a cookie origin (domain and https pair) to a URL. // Convenience for converting a cookie origin (domain and https pair) to a URL.
NET_EXPORT GURL CookieOriginToURL(const std::string& domain, bool is_https); NET_EXPORT GURL CookieOriginToURL(const std::string& domain, bool is_https);
} // namspace cookie_util // A ParsedRequestCookie consists of the key and value of the cookie.
typedef std::pair<base::StringPiece, base::StringPiece> ParsedRequestCookie;
typedef std::vector<ParsedRequestCookie> ParsedRequestCookies;
// Assumes that |header_value| is the cookie header value of a HTTP Request
// following the cookie-string schema of RFC 6265, section 4.2.1, and returns
// cookie name/value pairs. If cookie values are presented in double quotes,
// these will appear in |parsed_cookies| as well. Assumes that the cookie
// header is written by Chromium and therefore well-formed.
NET_EXPORT void ParseRequestCookieLine(const std::string& header_value,
ParsedRequestCookies* parsed_cookies);
// Writes all cookies of |parsed_cookies| into a HTTP Request header value
// that belongs to the "Cookie" header. The entries of |parsed_cookies| must
// already be appropriately escaped.
NET_EXPORT std::string SerializeRequestCookieLine(
const ParsedRequestCookies& parsed_cookies);
} // namespace cookie_util
} // namespace net } // namespace net
#endif // NET_COOKIES_COOKIE_UTIL_H_ #endif // NET_COOKIES_COOKIE_UTIL_H_
...@@ -2,10 +2,49 @@ ...@@ -2,10 +2,49 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include <string>
#include <utility>
#include <vector>
#include "base/basictypes.h" #include "base/basictypes.h"
#include "net/cookies/cookie_util.h" #include "net/cookies/cookie_util.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
namespace {
struct RequestCookieParsingTest {
std::string str;
std::vector<std::pair<std::string, std::string> > parsed;
};
net::cookie_util::ParsedRequestCookies MakeParsedRequestCookies(
const std::vector<std::pair<std::string, std::string> >& data) {
net::cookie_util::ParsedRequestCookies parsed;
for (size_t i = 0; i < data.size(); i++) {
parsed.push_back(std::make_pair(base::StringPiece(data[i].first),
base::StringPiece(data[i].second)));
}
return parsed;
}
void CheckParse(
const std::string& str,
const std::vector<std::pair<std::string, std::string> >& parsed_expected) {
net::cookie_util::ParsedRequestCookies parsed;
net::cookie_util::ParseRequestCookieLine(str, &parsed);
EXPECT_EQ(MakeParsedRequestCookies(parsed_expected), parsed);
}
void CheckSerialize(
const std::vector<std::pair<std::string, std::string> >& parsed,
const std::string& str_expected) {
net::cookie_util::ParsedRequestCookies prc =
MakeParsedRequestCookies(parsed);
EXPECT_EQ(str_expected, net::cookie_util::SerializeRequestCookieLine(prc));
}
} // namespace
TEST(CookieUtilTest, TestDomainIsHostOnly) { TEST(CookieUtilTest, TestDomainIsHostOnly) {
const struct { const struct {
const char* str; const char* str;
...@@ -109,3 +148,49 @@ TEST(CookieUtilTest, TestCookieDateParsing) { ...@@ -109,3 +148,49 @@ TEST(CookieUtilTest, TestCookieDateParsing) {
EXPECT_EQ(tests[i].epoch, parsed_time.ToTimeT()) << tests[i].str; EXPECT_EQ(tests[i].epoch, parsed_time.ToTimeT()) << tests[i].str;
} }
} }
TEST(CookieUtilTest, TestRequestCookieParsing) {
std::vector<RequestCookieParsingTest> tests;
// Simple case.
tests.push_back(RequestCookieParsingTest());
tests.back().str = "key=value";
tests.back().parsed.push_back(std::make_pair(std::string("key"),
std::string("value")));
// Multiple key/value pairs.
tests.push_back(RequestCookieParsingTest());
tests.back().str = "key1=value1; key2=value2";
tests.back().parsed.push_back(std::make_pair(std::string("key1"),
std::string("value1")));
tests.back().parsed.push_back(std::make_pair(std::string("key2"),
std::string("value2")));
// Empty value.
tests.push_back(RequestCookieParsingTest());
tests.back().str = "key=; otherkey=1234";
tests.back().parsed.push_back(std::make_pair(std::string("key"),
std::string()));
tests.back().parsed.push_back(std::make_pair(std::string("otherkey"),
std::string("1234")));
// Special characters (including equals signs) in value.
tests.push_back(RequestCookieParsingTest());
tests.back().str = "key=; a2=s=(./&t=:&u=a#$; a3=+~";
tests.back().parsed.push_back(std::make_pair(std::string("key"),
std::string()));
tests.back().parsed.push_back(std::make_pair(std::string("a2"),
std::string("s=(./&t=:&u=a#$")));
tests.back().parsed.push_back(std::make_pair(std::string("a3"),
std::string("+~")));
// Quoted value.
tests.push_back(RequestCookieParsingTest());
tests.back().str = "key=\"abcdef\"; otherkey=1234";
tests.back().parsed.push_back(std::make_pair(std::string("key"),
std::string("\"abcdef\"")));
tests.back().parsed.push_back(std::make_pair(std::string("otherkey"),
std::string("1234")));
for (size_t i = 0; i < tests.size(); i++) {
SCOPED_TRACE(testing::Message() << "Test " << i);
CheckParse(tests[i].str, tests[i].parsed);
CheckSerialize(tests[i].parsed, tests[i].str);
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment