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// Make sure stdint.h includes SIZE_MAX. (See C89, p259, footnote 221.)
6#ifndef __STDC_LIMIT_MACROS
7#define __STDC_LIMIT_MACROS 1
8#endif
9
10#include "components/domain_reliability/config.h"
11
12#include <stdint.h>
13
14#include "base/json/json_reader.h"
15#include "base/json/json_value_converter.h"
16#include "base/rand_util.h"
17#include "base/strings/string_util.h"
18
19namespace {
20
21bool ConvertURL(const base::StringPiece& string_piece, GURL* url) {
22  *url = GURL(string_piece.as_string());
23  return url->is_valid();
24}
25
26bool IsValidSampleRate(double p) { return p >= 0.0 && p <= 1.0; }
27
28}  // namespace
29
30namespace domain_reliability {
31
32// static
33const size_t DomainReliabilityConfig::kInvalidResourceIndex = SIZE_MAX;
34
35DomainReliabilityConfig::Resource::Resource() {
36}
37DomainReliabilityConfig::Resource::~Resource() {}
38
39bool DomainReliabilityConfig::Resource::MatchesUrl(const GURL& url) const {
40  const std::string& spec = url.spec();
41
42  ScopedVector<std::string>::const_iterator it;
43  for (it = url_patterns.begin(); it != url_patterns.end(); it++) {
44    if (MatchPattern(spec, **it))
45      return true;
46  }
47
48  return false;
49}
50
51bool DomainReliabilityConfig::Resource::DecideIfShouldReportRequest(
52    bool success) const {
53  double sample_rate = success ? success_sample_rate : failure_sample_rate;
54  DCHECK(IsValidSampleRate(sample_rate));
55  return base::RandDouble() < sample_rate;
56}
57
58// static
59void DomainReliabilityConfig::Resource::RegisterJSONConverter(
60    base::JSONValueConverter<DomainReliabilityConfig::Resource>* converter) {
61  converter->RegisterStringField("resource_name", &Resource::name);
62  converter->RegisterRepeatedString("url_patterns", &Resource::url_patterns);
63  converter->RegisterDoubleField("success_sample_rate",
64                                 &Resource::success_sample_rate);
65  converter->RegisterDoubleField("failure_sample_rate",
66                                 &Resource::failure_sample_rate);
67}
68
69bool DomainReliabilityConfig::Resource::IsValid() const {
70  return !name.empty() && !url_patterns.empty() &&
71      IsValidSampleRate(success_sample_rate) &&
72      IsValidSampleRate(failure_sample_rate);
73}
74
75DomainReliabilityConfig::Collector::Collector() {}
76DomainReliabilityConfig::Collector::~Collector() {}
77
78// static
79void DomainReliabilityConfig::Collector::RegisterJSONConverter(
80    base::JSONValueConverter<DomainReliabilityConfig::Collector>* converter) {
81  converter->RegisterCustomField<GURL>("upload_url", &Collector::upload_url,
82                                       &ConvertURL);
83}
84
85bool DomainReliabilityConfig::Collector::IsValid() const {
86  return upload_url.is_valid();
87}
88
89DomainReliabilityConfig::DomainReliabilityConfig() : valid_until(0.0) {}
90DomainReliabilityConfig::~DomainReliabilityConfig() {}
91
92// static
93scoped_ptr<const DomainReliabilityConfig> DomainReliabilityConfig::FromJSON(
94    const base::StringPiece& json) {
95  scoped_ptr<base::Value> value(base::JSONReader::Read(json));
96  base::JSONValueConverter<DomainReliabilityConfig> converter;
97  DomainReliabilityConfig* config = new DomainReliabilityConfig();
98
99  // If we can parse and convert the JSON into a valid config, return that.
100  if (value && converter.Convert(*value, config) && config->IsValid())
101    return scoped_ptr<const DomainReliabilityConfig>(config);
102  else
103    return scoped_ptr<const DomainReliabilityConfig>();
104}
105
106bool DomainReliabilityConfig::IsValid() const {
107  if (valid_until == 0.0 || domain.empty() ||
108      resources.empty() || collectors.empty()) {
109    return false;
110  }
111
112  for (size_t i = 0; i < resources.size(); ++i) {
113    if (!resources[i]->IsValid())
114      return false;
115  }
116
117  for (size_t i = 0; i < collectors.size(); ++i) {
118    if (!collectors[i]->IsValid())
119      return false;
120  }
121
122  return true;
123}
124
125bool DomainReliabilityConfig::IsExpired(base::Time now) const {
126  DCHECK_NE(0.0, valid_until);
127  base::Time valid_until_time = base::Time::FromDoubleT(valid_until);
128  return now > valid_until_time;
129}
130
131size_t DomainReliabilityConfig::GetResourceIndexForUrl(const GURL& url) const {
132  // Removes username, password, and fragment.
133  GURL sanitized_url = url.GetAsReferrer();
134
135  for (size_t i = 0; i < resources.size(); ++i) {
136    if (resources[i]->MatchesUrl(sanitized_url))
137      return i;
138  }
139
140  return kInvalidResourceIndex;
141}
142
143// static
144void DomainReliabilityConfig::RegisterJSONConverter(
145    base::JSONValueConverter<DomainReliabilityConfig>* converter) {
146  converter->RegisterStringField("config_version",
147                                 &DomainReliabilityConfig::version);
148  converter->RegisterDoubleField("config_valid_until",
149                                 &DomainReliabilityConfig::valid_until);
150  converter->RegisterStringField("monitored_domain",
151                                 &DomainReliabilityConfig::domain);
152  converter->RegisterRepeatedMessage("monitored_resources",
153                                     &DomainReliabilityConfig::resources);
154  converter->RegisterRepeatedMessage("collectors",
155                                     &DomainReliabilityConfig::collectors);
156}
157
158}  // namespace domain_reliability
159