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/data_reduction_proxy/browser/data_reduction_proxy_config_service.h"
6
7#include "base/bind.h"
8#include "base/memory/ref_counted.h"
9#include "base/message_loop/message_loop.h"
10#include "base/strings/string_util.h"
11
12namespace data_reduction_proxy {
13
14DataReductionProxyConfigService::DataReductionProxyConfigService(
15    scoped_ptr<net::ProxyConfigService> base_service)
16    : config_read_pending_(true),
17      registered_observer_(false),
18      enabled_(false),
19      restricted_(false) {
20  base_service_ = base_service.Pass();
21}
22
23DataReductionProxyConfigService::~DataReductionProxyConfigService() {
24  if (registered_observer_ && base_service_.get())
25    base_service_->RemoveObserver(this);
26}
27
28void DataReductionProxyConfigService::AddObserver(
29    net::ProxyConfigService::Observer* observer) {
30  RegisterObserver();
31  observers_.AddObserver(observer);
32}
33
34void DataReductionProxyConfigService::RemoveObserver(
35    net::ProxyConfigService::Observer* observer) {
36  observers_.RemoveObserver(observer);
37}
38
39net::ProxyConfigService::ConfigAvailability
40DataReductionProxyConfigService::GetLatestProxyConfig(
41    net::ProxyConfig* config) {
42  RegisterObserver();
43
44  if (enabled_) {
45    *config = config_;
46    return net::ProxyConfigService::CONFIG_VALID;
47  }
48
49  // Ask the base service if available.
50  net::ProxyConfig system_config;
51  ConfigAvailability system_availability =
52      net::ProxyConfigService::CONFIG_UNSET;
53  if (base_service_.get()) {
54    system_availability = base_service_->GetLatestProxyConfig(&system_config);
55    *config = system_config;
56  }
57  if (system_availability == net::ProxyConfigService::CONFIG_UNSET) {
58    *config = net::ProxyConfig::CreateDirect();
59  }
60
61  return net::ProxyConfigService::CONFIG_VALID;
62}
63
64void DataReductionProxyConfigService::OnLazyPoll() {
65  if (base_service_.get())
66    base_service_->OnLazyPoll();
67}
68
69void DataReductionProxyConfigService::UpdateProxyConfig(
70    bool enabled,
71    const net::ProxyConfig& config) {
72
73  config_read_pending_ = false;
74  enabled_ = enabled;
75  config_ = config;
76
77  if (!observers_.might_have_observers())
78    return;
79
80  // Evaluate the proxy configuration. If GetLatestProxyConfig returns
81  // CONFIG_PENDING, we are using the system proxy service, but it doesn't have
82  // a valid configuration yet. Once it is ready, OnProxyConfigChanged() will be
83  // called and broadcast the proxy configuration.
84  // Note: If a switch between a preference proxy configuration and the system
85  // proxy configuration occurs an unnecessary notification might get sent if
86  // the two configurations agree. This case should be rare however, so we don't
87  // handle that case specially.
88  net::ProxyConfig new_config;
89  ConfigAvailability availability = GetLatestProxyConfig(&new_config);
90  if (availability != CONFIG_PENDING) {
91    FOR_EACH_OBSERVER(net::ProxyConfigService::Observer, observers_,
92                      OnProxyConfigChanged(new_config, availability));
93  }
94}
95
96void DataReductionProxyConfigService::OnProxyConfigChanged(
97    const net::ProxyConfig& config,
98    ConfigAvailability availability) {
99
100  // Check whether the data reduction proxy is enabled. In this case that proxy
101  // configuration takes precedence and the change event from the delegate proxy
102  // service can be disregarded.
103  if (!enabled_) {
104    net::ProxyConfig actual_config;
105    availability = GetLatestProxyConfig(&actual_config);
106    FOR_EACH_OBSERVER(net::ProxyConfigService::Observer, observers_,
107                      OnProxyConfigChanged(actual_config, availability));
108  }
109}
110
111void DataReductionProxyConfigService::RegisterObserver() {
112  if (!registered_observer_ && base_service_.get()) {
113    base_service_->AddObserver(this);
114    registered_observer_ = true;
115  }
116}
117
118DataReductionProxyConfigTracker::DataReductionProxyConfigTracker(
119    DataReductionProxyConfigService* config_service,
120    base::TaskRunner* task_runner)
121    : config_service_(config_service),
122      task_runner_(task_runner) {
123  DCHECK(config_service);
124}
125
126DataReductionProxyConfigTracker::~DataReductionProxyConfigTracker() {
127}
128
129void DataReductionProxyConfigTracker::Enable(
130    bool primary_restricted,
131    bool fallback_restricted,
132    const std::string& primary_origin,
133    const std::string& fallback_origin,
134    const std::string& ssl_origin) {
135
136  std::vector<std::string> proxies;
137  if (!primary_restricted) {
138    std::string trimmed_primary;
139    base::TrimString(primary_origin, "/", &trimmed_primary);
140    if (!trimmed_primary.empty())
141      proxies.push_back(trimmed_primary);
142  }
143  if (!fallback_restricted) {
144    std::string trimmed_fallback;
145    base::TrimString(fallback_origin, "/", &trimmed_fallback);
146    if (!trimmed_fallback.empty())
147      proxies.push_back(trimmed_fallback);
148  }
149  if (proxies.empty()) {
150    std::string mode;
151    Disable();
152    return;
153  }
154
155  std::string trimmed_ssl;
156  base::TrimString(ssl_origin, "/", &trimmed_ssl);
157
158  std::string server = "http=" + JoinString(proxies, ",") + ",direct://;"
159      + (ssl_origin.empty() ? "" : ("https=" + ssl_origin + ",direct://;"));
160
161  net::ProxyConfig config;
162  config.proxy_rules().ParseFromString(server);
163  config.proxy_rules().bypass_rules.ParseFromString(
164      JoinString(bypass_rules_, ", "));
165  UpdateProxyConfigOnIOThread(true, config);
166}
167
168
169void DataReductionProxyConfigTracker::Disable() {
170  net::ProxyConfig config = net::ProxyConfig::CreateDirect();
171  UpdateProxyConfigOnIOThread(false, config);
172}
173
174void DataReductionProxyConfigTracker::AddHostPatternToBypass(
175    const std::string& pattern) {
176  bypass_rules_.push_back(pattern);
177}
178
179void DataReductionProxyConfigTracker::AddURLPatternToBypass(
180    const std::string& pattern) {
181  size_t pos = pattern.find("/");
182  if (pattern.find("/", pos + 1) == pos + 1)
183    pos = pattern.find("/", pos + 2);
184
185  std::string host_pattern;
186  if (pos != std::string::npos)
187    host_pattern = pattern.substr(0, pos);
188  else
189    host_pattern = pattern;
190
191  AddHostPatternToBypass(host_pattern);
192}
193
194void DataReductionProxyConfigTracker::UpdateProxyConfigOnIOThread(
195    bool enabled,
196    const net::ProxyConfig& config) {
197  task_runner_->PostTask(FROM_HERE,
198      base::Bind(
199          &DataReductionProxyConfigService::UpdateProxyConfig,
200          base::Unretained(config_service_),
201          enabled, config));
202}
203
204}  // namespace data_reduction_proxy
205