1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h"
10 #include "base/command_line.h"
11 #include "base/single_thread_task_runner.h"
12 #include "base/strings/string_split.h"
13 #include "base/strings/string_tokenizer.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/time/time.h"
18 #include "base/values.h"
19 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h"
20 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
21 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.h"
22 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
23 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
24 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
25 #include "components/data_reduction_proxy/core/common/version.h"
26 #include "crypto/random.h"
27 #include "net/base/host_port_pair.h"
28 #include "net/base/load_flags.h"
29 #include "net/proxy/proxy_server.h"
30 #include "net/url_request/url_request.h"
32 #if !defined(OS_ANDROID) && !defined(OS_IOS)
33 #include "google_apis/google_api_keys.h"
36 namespace data_reduction_proxy
{
39 std::string
FormatOption(const std::string
& name
, const std::string
& value
) {
40 return name
+ "=" + value
;
43 bool ShouldForceDisableLoFi(const net::URLRequest
* request
) {
46 return (request
->load_flags() & net::LOAD_BYPASS_CACHE
) != 0;
51 const char kSessionHeaderOption
[] = "ps";
52 const char kCredentialsHeaderOption
[] = "sid";
53 const char kSecureSessionHeaderOption
[] = "s";
54 const char kBuildNumberHeaderOption
[] = "b";
55 const char kPatchNumberHeaderOption
[] = "p";
56 const char kClientHeaderOption
[] = "c";
57 const char kLoFiHeaderOption
[] = "q";
58 const char kExperimentsOption
[] = "exp";
60 // The empty version for the authentication protocol. Currently used by
62 #if defined(OS_ANDROID)
63 const char kAndroidWebViewProtocolVersion
[] = "";
66 #define CLIENT_ENUM(name, str_value) \
67 case name: return str_value;
68 const char* GetString(Client client
) {
78 bool DataReductionProxyRequestOptions::IsKeySetOnCommandLine() {
79 const base::CommandLine
& command_line
=
80 *base::CommandLine::ForCurrentProcess();
81 return command_line
.HasSwitch(
82 data_reduction_proxy::switches::kDataReductionProxyKey
);
86 std::string
DataReductionProxyRequestOptions::CreateLocalSessionKey(
87 const std::string
& session
,
88 const std::string
& credentials
) {
89 return base::StringPrintf("%s|%s", session
.c_str(), credentials
.c_str());
93 bool DataReductionProxyRequestOptions::ParseLocalSessionKey(
94 const std::string
& session_key
,
96 std::string
* credentials
) {
97 std::vector
<std::string
> auth_values
;
98 base::SplitString(session_key
, '|', &auth_values
);
99 if (auth_values
.size() == 2) {
100 *session
= auth_values
[0];
101 *credentials
= auth_values
[1];
108 DataReductionProxyRequestOptions::DataReductionProxyRequestOptions(
110 DataReductionProxyConfig
* config
)
111 : client_(GetString(client
)),
112 use_assigned_credentials_(false),
113 data_reduction_proxy_config_(config
) {
114 GetChromiumBuildAndPatch(ChromiumVersion(), &build_
, &patch_
);
115 // Constructed on the UI thread, but should be checked on the IO thread.
116 thread_checker_
.DetachFromThread();
119 DataReductionProxyRequestOptions::DataReductionProxyRequestOptions(
121 const std::string
& version
,
122 DataReductionProxyConfig
* config
)
123 : client_(GetString(client
)),
124 use_assigned_credentials_(false),
125 data_reduction_proxy_config_(config
) {
126 GetChromiumBuildAndPatch(version
, &build_
, &patch_
);
127 // Constructed on the UI thread, but should be checked on the IO thread.
128 thread_checker_
.DetachFromThread();
131 DataReductionProxyRequestOptions::~DataReductionProxyRequestOptions() {
134 void DataReductionProxyRequestOptions::Init() {
135 key_
= GetDefaultKey(),
142 std::string
DataReductionProxyRequestOptions::ChromiumVersion() const {
143 #if defined(PRODUCT_VERSION)
144 return PRODUCT_VERSION
;
146 return std::string();
150 void DataReductionProxyRequestOptions::GetChromiumBuildAndPatch(
151 const std::string
& version
,
153 std::string
* patch
) const {
154 std::vector
<std::string
> version_parts
;
155 base::SplitString(version
, '.', &version_parts
);
156 if (version_parts
.size() != 4)
158 *build
= version_parts
[2];
159 *patch
= version_parts
[3];
162 void DataReductionProxyRequestOptions::UpdateVersion() {
163 GetChromiumBuildAndPatch(version_
, &build_
, &patch_
);
164 RegenerateRequestHeaderValue();
167 void DataReductionProxyRequestOptions::UpdateLoFi(bool force_disable_lo_fi
) {
168 if (force_disable_lo_fi
) {
171 lofi_
= std::string();
172 RegenerateRequestHeaderValue();
175 // LoFi was not enabled, but now is. Add the header option.
176 if (lofi_
.empty() && DataReductionProxyParams::IsLoFiEnabled()) {
178 RegenerateRequestHeaderValue();
181 // LoFi was enabled, but no longer is. Remove the header option.
182 if (!lofi_
.empty() && !DataReductionProxyParams::IsLoFiEnabled()) {
183 lofi_
= std::string();
184 RegenerateRequestHeaderValue();
188 void DataReductionProxyRequestOptions::UpdateExperiments() {
189 std::string experiments
=
190 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
191 data_reduction_proxy::switches::kDataReductionProxyExperiment
);
192 if (experiments
.empty())
194 base::StringTokenizer
experiment_tokenizer(experiments
, ", ");
195 experiment_tokenizer
.set_quote_chars("\"");
196 while (experiment_tokenizer
.GetNext()) {
197 if (!experiment_tokenizer
.token().empty())
198 experiments_
.push_back(experiment_tokenizer
.token());
200 RegenerateRequestHeaderValue();
204 base::string16
DataReductionProxyRequestOptions::AuthHashForSalt(
206 const std::string
& key
) {
207 std::string salted_key
=
208 base::StringPrintf("%lld%s%lld",
209 static_cast<long long>(salt
),
211 static_cast<long long>(salt
));
212 return base::UTF8ToUTF16(base::MD5String(salted_key
));
215 base::Time
DataReductionProxyRequestOptions::Now() const {
216 return base::Time::Now();
219 void DataReductionProxyRequestOptions::RandBytes(void* output
,
220 size_t length
) const {
221 crypto::RandBytes(output
, length
);
224 void DataReductionProxyRequestOptions::MaybeAddRequestHeader(
225 net::URLRequest
* request
,
226 const net::ProxyServer
& proxy_server
,
227 net::HttpRequestHeaders
* request_headers
) {
228 DCHECK(thread_checker_
.CalledOnValidThread());
229 if (!proxy_server
.is_valid())
231 if (proxy_server
.is_direct())
233 MaybeAddRequestHeaderImpl(proxy_server
.host_port_pair(), false,
234 request_headers
, ShouldForceDisableLoFi(request
));
237 void DataReductionProxyRequestOptions::MaybeAddProxyTunnelRequestHandler(
238 const net::HostPortPair
& proxy_server
,
239 net::HttpRequestHeaders
* request_headers
) {
240 DCHECK(thread_checker_
.CalledOnValidThread());
241 MaybeAddRequestHeaderImpl(proxy_server
, true, request_headers
, false);
244 void DataReductionProxyRequestOptions::SetHeader(
245 net::HttpRequestHeaders
* headers
,
246 bool force_disable_lo_fi
) {
247 base::Time now
= Now();
248 // Authorization credentials must be regenerated if they are expired.
249 if (!use_assigned_credentials_
&& (now
> credentials_expiration_time_
))
251 UpdateLoFi(force_disable_lo_fi
);
252 const char kChromeProxyHeader
[] = "Chrome-Proxy";
253 std::string header_value
;
254 if (headers
->HasHeader(kChromeProxyHeader
)) {
255 headers
->GetHeader(kChromeProxyHeader
, &header_value
);
256 headers
->RemoveHeader(kChromeProxyHeader
);
257 header_value
+= ", ";
259 header_value
+= header_value_
;
260 headers
->SetHeader(kChromeProxyHeader
, header_value
);
263 void DataReductionProxyRequestOptions::ComputeCredentials(
264 const base::Time
& now
,
265 std::string
* session
,
266 std::string
* credentials
) const {
270 (now
- base::Time::UnixEpoch()).InMilliseconds() / 1000;
273 RandBytes(rand
, 3 * sizeof(rand
[0]));
274 *session
= base::StringPrintf("%lld-%u-%u-%u",
275 static_cast<long long>(timestamp
),
279 *credentials
= base::UTF16ToUTF8(AuthHashForSalt(timestamp
, key_
));
281 DVLOG(1) << "session: [" << *session
<< "] "
282 << "password: [" << *credentials
<< "]";
285 void DataReductionProxyRequestOptions::UpdateCredentials() {
286 base::Time now
= Now();
287 ComputeCredentials(now
, &session_
, &credentials_
);
288 credentials_expiration_time_
= now
+ base::TimeDelta::FromHours(24);
289 RegenerateRequestHeaderValue();
292 void DataReductionProxyRequestOptions::SetKeyOnIO(const std::string
& key
) {
293 DCHECK(thread_checker_
.CalledOnValidThread());
300 void DataReductionProxyRequestOptions::PopulateConfigResponse(
301 base::DictionaryValue
* response
) const {
302 DCHECK(thread_checker_
.CalledOnValidThread());
304 std::string credentials
;
305 base::Time now
= Now();
306 base::Time expiration_time
= now
+ base::TimeDelta::FromHours(24);
307 ComputeCredentials(now
, &session
, &credentials
);
308 response
->SetString("sessionKey",
309 CreateLocalSessionKey(session
, credentials
));
310 response
->SetString("expireTime",
311 config_parser::TimeToISO8601(expiration_time
));
314 void DataReductionProxyRequestOptions::SetCredentials(
315 const std::string
& session
,
316 const std::string
& credentials
) {
317 DCHECK(thread_checker_
.CalledOnValidThread());
319 credentials_
= credentials
;
320 secure_session_
.clear();
321 // Force skipping of credential regeneration. It should be handled by the
323 use_assigned_credentials_
= true;
324 RegenerateRequestHeaderValue();
327 void DataReductionProxyRequestOptions::SetSecureSession(
328 const std::string
& secure_session
) {
329 DCHECK(thread_checker_
.CalledOnValidThread());
331 credentials_
.clear();
332 secure_session_
= secure_session
;
333 // Force skipping of credential regeneration. It should be handled by the
335 use_assigned_credentials_
= true;
336 RegenerateRequestHeaderValue();
339 void DataReductionProxyRequestOptions::Invalidate() {
340 SetSecureSession(std::string());
343 std::string
DataReductionProxyRequestOptions::GetDefaultKey() const {
344 const base::CommandLine
& command_line
=
345 *base::CommandLine::ForCurrentProcess();
347 command_line
.GetSwitchValueASCII(switches::kDataReductionProxyKey
);
348 // Android and iOS get the default key from a preprocessor constant. All other
349 // platforms get the key from google_apis
350 #if defined(OS_ANDROID) || defined(OS_IOS)
351 #if defined(SPDY_PROXY_AUTH_VALUE)
353 key
= SPDY_PROXY_AUTH_VALUE
;
357 key
= google_apis::GetSpdyProxyAuthValue();
359 #endif // defined(OS_ANDROID) || defined(OS_IOS)
363 const std::string
& DataReductionProxyRequestOptions::GetSecureSession() const {
364 return secure_session_
;
367 void DataReductionProxyRequestOptions::MaybeAddRequestHeaderImpl(
368 const net::HostPortPair
& proxy_server
,
370 net::HttpRequestHeaders
* request_headers
,
371 bool force_disable_lo_fi
) {
372 if (proxy_server
.IsEmpty())
374 if (data_reduction_proxy_config_
&&
375 data_reduction_proxy_config_
->IsDataReductionProxy(proxy_server
, NULL
) &&
376 data_reduction_proxy_config_
->UsingHTTPTunnel(proxy_server
) ==
378 SetHeader(request_headers
, force_disable_lo_fi
);
382 void DataReductionProxyRequestOptions::RegenerateRequestHeaderValue() {
383 std::vector
<std::string
> headers
;
384 if (!session_
.empty())
385 headers
.push_back(FormatOption(kSessionHeaderOption
, session_
));
386 if (!credentials_
.empty())
387 headers
.push_back(FormatOption(kCredentialsHeaderOption
, credentials_
));
388 if (!secure_session_
.empty()) {
390 FormatOption(kSecureSessionHeaderOption
, secure_session_
));
392 if (!client_
.empty())
393 headers
.push_back(FormatOption(kClientHeaderOption
, client_
));
394 if (!build_
.empty() && !patch_
.empty()) {
395 headers
.push_back(FormatOption(kBuildNumberHeaderOption
, build_
));
396 headers
.push_back(FormatOption(kPatchNumberHeaderOption
, patch_
));
399 headers
.push_back(FormatOption(kLoFiHeaderOption
, lofi_
));
400 for (const auto& experiment
: experiments_
)
401 headers
.push_back(FormatOption(kExperimentsOption
, experiment
));
403 header_value_
= JoinString(headers
, ", ");
406 } // namespace data_reduction_proxy