Upstream version 11.40.271.0
[platform/framework/web/crosswalk.git] / src / components / variations / net / variations_http_header_provider.cc
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.
4
5 #include "components/variations/net/variations_http_header_provider.h"
6
7 #include <set>
8 #include <string>
9 #include <vector>
10
11 #include "base/base64.h"
12 #include "base/memory/singleton.h"
13 #include "base/metrics/histogram.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_split.h"
16 #include "base/strings/string_util.h"
17 #include "components/google/core/browser/google_util.h"
18 #include "components/variations/proto/client_variations.pb.h"
19 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
20 #include "net/http/http_request_headers.h"
21 #include "url/gurl.h"
22
23 namespace variations {
24
25 namespace {
26
27 const char* kSuffixesToSetHeadersFor[] = {
28   ".android.com",
29   ".doubleclick.com",
30   ".doubleclick.net",
31   ".ggpht.com",
32   ".googleadservices.com",
33   ".googleapis.com",
34   ".googlesyndication.com",
35   ".googleusercontent.com",
36   ".googlevideo.com",
37   ".gstatic.com",
38   ".ytimg.com",
39 };
40
41 const char kChromeUMAEnabled[] = "X-Chrome-UMA-Enabled";
42 const char kClientData[] = "X-Client-Data";
43
44 }  // namespace
45
46 VariationsHttpHeaderProvider* VariationsHttpHeaderProvider::GetInstance() {
47   return Singleton<VariationsHttpHeaderProvider>::get();
48 }
49
50 void VariationsHttpHeaderProvider::AppendHeaders(
51     const GURL& url,
52     bool incognito,
53     bool uma_enabled,
54     net::HttpRequestHeaders* headers) {
55   // Note the criteria for attaching client experiment headers:
56   // 1. We only transmit to Google owned domains which can evaluate experiments.
57   //    1a. These include hosts which have a standard postfix such as:
58   //         *.doubleclick.net or *.googlesyndication.com or
59   //         exactly www.googleadservices.com or
60   //         international TLD domains *.google.<TLD> or *.youtube.<TLD>.
61   // 2. Only transmit for non-Incognito profiles.
62   // 3. For the X-Chrome-UMA-Enabled bit, only set it if UMA is in fact enabled
63   //    for this install of Chrome.
64   // 4. For the X-Client-Data header, only include non-empty variation IDs.
65   if (incognito || !ShouldAppendHeaders(url))
66     return;
67
68   if (uma_enabled)
69     headers->SetHeaderIfMissing(kChromeUMAEnabled, "1");
70
71   // Lazily initialize the header, if not already done, before attempting to
72   // transmit it.
73   InitVariationIDsCacheIfNeeded();
74
75   std::string variation_ids_header_copy;
76   {
77     base::AutoLock scoped_lock(lock_);
78     variation_ids_header_copy = variation_ids_header_;
79   }
80
81   if (!variation_ids_header_copy.empty()) {
82     // Note that prior to M33 this header was named X-Chrome-Variations.
83     headers->SetHeaderIfMissing(kClientData, variation_ids_header_copy);
84   }
85 }
86
87 bool VariationsHttpHeaderProvider::SetDefaultVariationIds(
88     const std::string& variation_ids) {
89   default_variation_ids_set_.clear();
90   default_trigger_id_set_.clear();
91   std::vector<std::string> entries;
92   base::SplitString(variation_ids, ',', &entries);
93   for (std::vector<std::string>::const_iterator it = entries.begin();
94        it != entries.end(); ++it) {
95     if (it->empty()) {
96       default_variation_ids_set_.clear();
97       default_trigger_id_set_.clear();
98       return false;
99     }
100     bool trigger_id = StartsWithASCII(*it, "t", true);
101     // Remove the "t" prefix if it's there.
102     std::string entry = trigger_id ? it->substr(1) : *it;
103
104     int variation_id = 0;
105     if (!base::StringToInt(entry, &variation_id)) {
106       default_variation_ids_set_.clear();
107       default_trigger_id_set_.clear();
108       return false;
109     }
110     if (trigger_id)
111       default_trigger_id_set_.insert(variation_id);
112     else
113       default_variation_ids_set_.insert(variation_id);
114   }
115   return true;
116 }
117
118 VariationsHttpHeaderProvider::VariationsHttpHeaderProvider()
119     : variation_ids_cache_initialized_(false) {
120 }
121
122 VariationsHttpHeaderProvider::~VariationsHttpHeaderProvider() {
123 }
124
125 void VariationsHttpHeaderProvider::OnFieldTrialGroupFinalized(
126     const std::string& trial_name,
127     const std::string& group_name) {
128   VariationID new_id =
129       GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_name, group_name);
130   VariationID new_trigger_id = GetGoogleVariationID(
131       GOOGLE_WEB_PROPERTIES_TRIGGER, trial_name, group_name);
132   if (new_id == EMPTY_ID && new_trigger_id == EMPTY_ID)
133     return;
134
135   base::AutoLock scoped_lock(lock_);
136   if (new_id != EMPTY_ID)
137     variation_ids_set_.insert(new_id);
138   if (new_trigger_id != EMPTY_ID)
139     variation_trigger_ids_set_.insert(new_trigger_id);
140
141   UpdateVariationIDsHeaderValue();
142 }
143
144 void VariationsHttpHeaderProvider::OnSyntheticTrialsChanged(
145     const std::vector<metrics::SyntheticTrialGroup>& groups) {
146   base::AutoLock scoped_lock(lock_);
147
148   synthetic_variation_ids_set_.clear();
149   for (const metrics::SyntheticTrialGroup& group : groups) {
150     const VariationID id =
151         GetGoogleVariationIDFromHashes(GOOGLE_WEB_PROPERTIES, group.id);
152     if (id != EMPTY_ID)
153       synthetic_variation_ids_set_.insert(id);
154   }
155   UpdateVariationIDsHeaderValue();
156 }
157
158 void VariationsHttpHeaderProvider::InitVariationIDsCacheIfNeeded() {
159   base::AutoLock scoped_lock(lock_);
160   if (variation_ids_cache_initialized_)
161     return;
162
163   // Register for additional cache updates. This is done first to avoid a race
164   // that could cause registered FieldTrials to be missed.
165   DCHECK(base::MessageLoop::current());
166   base::FieldTrialList::AddObserver(this);
167
168   base::TimeTicks before_time = base::TimeTicks::Now();
169
170   base::FieldTrial::ActiveGroups initial_groups;
171   base::FieldTrialList::GetActiveFieldTrialGroups(&initial_groups);
172   for (base::FieldTrial::ActiveGroups::const_iterator it =
173            initial_groups.begin();
174        it != initial_groups.end(); ++it) {
175     const VariationID id =
176         GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, it->trial_name,
177                              it->group_name);
178     if (id != EMPTY_ID)
179       variation_ids_set_.insert(id);
180
181     const VariationID trigger_id =
182         GetGoogleVariationID(GOOGLE_WEB_PROPERTIES_TRIGGER, it->trial_name,
183                              it->group_name);
184     if (trigger_id != EMPTY_ID)
185       variation_trigger_ids_set_.insert(trigger_id);
186   }
187   UpdateVariationIDsHeaderValue();
188
189   UMA_HISTOGRAM_CUSTOM_COUNTS(
190       "Variations.HeaderConstructionTime",
191       (base::TimeTicks::Now() - before_time).InMicroseconds(),
192       0,
193       base::TimeDelta::FromSeconds(1).InMicroseconds(),
194       50);
195
196   variation_ids_cache_initialized_ = true;
197 }
198
199 void VariationsHttpHeaderProvider::UpdateVariationIDsHeaderValue() {
200   lock_.AssertAcquired();
201
202   // The header value is a serialized protobuffer of Variation IDs which is
203   // base64 encoded before transmitting as a string.
204   variation_ids_header_.clear();
205
206   if (variation_ids_set_.empty() && default_variation_ids_set_.empty() &&
207       variation_trigger_ids_set_.empty() && default_trigger_id_set_.empty() &&
208       synthetic_variation_ids_set_.empty()) {
209     return;
210   }
211
212   // This is the bottleneck for the creation of the header, so validate the size
213   // here. Force a hard maximum on the ID count in case the Variations server
214   // returns too many IDs and DOSs receiving servers with large requests.
215   const size_t total_id_count =
216       variation_ids_set_.size() + variation_trigger_ids_set_.size();
217   DCHECK_LE(total_id_count, 10U);
218   UMA_HISTOGRAM_COUNTS_100("Variations.Headers.ExperimentCount",
219                            total_id_count);
220   if (total_id_count > 20)
221     return;
222
223   // Merge the two sets of experiment ids.
224   std::set<VariationID> all_variation_ids_set = default_variation_ids_set_;
225   for (VariationID id : variation_ids_set_)
226     all_variation_ids_set.insert(id);
227   for (VariationID id : synthetic_variation_ids_set_)
228     all_variation_ids_set.insert(id);
229
230   std::set<VariationID> all_trigger_ids_set = default_trigger_id_set_;
231   for (VariationID id : variation_trigger_ids_set_)
232     all_trigger_ids_set.insert(id);
233
234   ClientVariations proto;
235   for (VariationID id : all_variation_ids_set)
236     proto.add_variation_id(id);
237   for (VariationID id : all_trigger_ids_set)
238     proto.add_trigger_variation_id(id);
239
240   std::string serialized;
241   proto.SerializeToString(&serialized);
242
243   std::string hashed;
244   base::Base64Encode(serialized, &hashed);
245   // If successful, swap the header value with the new one.
246   // Note that the list of IDs and the header could be temporarily out of sync
247   // if IDs are added as the header is recreated. The receiving servers are OK
248   // with such discrepancies.
249   variation_ids_header_ = hashed;
250 }
251
252 // static
253 bool VariationsHttpHeaderProvider::ShouldAppendHeaders(const GURL& url) {
254   if (google_util::IsGoogleDomainUrl(url, google_util::ALLOW_SUBDOMAIN,
255                                      google_util::ALLOW_NON_STANDARD_PORTS)) {
256     return true;
257   }
258
259   if (!url.is_valid() || !url.SchemeIsHTTPOrHTTPS())
260     return false;
261
262   // Some domains don't have international TLD extensions, so testing for them
263   // is very straight forward.
264   const std::string host = url.host();
265   for (size_t i = 0; i < arraysize(kSuffixesToSetHeadersFor); ++i) {
266     if (EndsWith(host, kSuffixesToSetHeadersFor[i], false))
267       return true;
268   }
269
270   return google_util::IsYoutubeDomainUrl(url, google_util::ALLOW_SUBDOMAIN,
271                                          google_util::ALLOW_NON_STANDARD_PORTS);
272 }
273
274 std::set<std::string> VariationsHttpHeaderProvider::GetVariationHeaderNames()
275     const {
276   std::set<std::string> headers;
277   headers.insert(kChromeUMAEnabled);
278   headers.insert(kClientData);
279   return headers;
280 }
281
282 }  // namespace variations