blob: 09365c1f301bc00363c510cb03c9a6a071abace3 [file] [log] [blame]
Alex Deymoaea4c1c2015-08-19 20:24:43 -07001//
2// Copyright (C) 2011 The Android Open Source Project
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
Andrew de los Reyes000d8952011-03-02 15:21:14 -080016
17#include "update_engine/chrome_browser_proxy_resolver.h"
18
Alex Vakulenkod2779df2014-06-16 13:19:00 -070019#include <deque>
Andrew de los Reyes000d8952011-03-02 15:21:14 -080020#include <string>
21
Alex Vakulenko4906c1c2014-08-21 13:17:44 -070022#include <base/bind.h>
Chris Sosafc661a12013-02-26 14:43:21 -080023#include <base/strings/string_tokenizer.h>
Alex Deymoc4acdf42014-05-28 21:07:10 -070024#include <base/strings/string_util.h>
Andrew de los Reyes000d8952011-03-02 15:21:14 -080025
Alex Deymo39910dc2015-11-09 17:04:30 -080026#include "update_engine/common/utils.h"
Andrew de los Reyes000d8952011-03-02 15:21:14 -080027
28namespace chromeos_update_engine {
29
Chris Sosafc661a12013-02-26 14:43:21 -080030using base::StringTokenizer;
Alex Deymo60ca1a72015-06-18 18:19:15 -070031using base::TimeDelta;
Alex Vakulenko3f39d5c2015-10-13 09:27:13 -070032using brillo::MessageLoop;
Andrew de los Reyes000d8952011-03-02 15:21:14 -080033using std::deque;
Andrew de los Reyes000d8952011-03-02 15:21:14 -080034using std::string;
35
Andrew de los Reyes000d8952011-03-02 15:21:14 -080036const char kLibCrosServiceName[] = "org.chromium.LibCrosService";
Alex Deymo30534502015-07-20 15:06:33 -070037const char kLibCrosProxyResolveName[] = "ProxyResolved";
Andrew de los Reyes000d8952011-03-02 15:21:14 -080038const char kLibCrosProxyResolveSignalInterface[] =
Alex Deymo30534502015-07-20 15:06:33 -070039 "org.chromium.UpdateEngineLibcrosProxyResolvedInterface";
Andrew de los Reyes000d8952011-03-02 15:21:14 -080040
41namespace {
Gilad Arnoldb752fb32014-03-03 12:23:39 -080042
Andrew de los Reyes000d8952011-03-02 15:21:14 -080043const int kTimeout = 5; // seconds
44
Alex Vakulenkod2779df2014-06-16 13:19:00 -070045} // namespace
Andrew de los Reyes000d8952011-03-02 15:21:14 -080046
Gilad Arnold1b9d6ae2014-03-03 13:46:07 -080047ChromeBrowserProxyResolver::ChromeBrowserProxyResolver(
Alex Deymo30534502015-07-20 15:06:33 -070048 LibCrosProxy* libcros_proxy)
49 : libcros_proxy_(libcros_proxy), timeout_(kTimeout) {}
Andrew de los Reyes000d8952011-03-02 15:21:14 -080050
51bool ChromeBrowserProxyResolver::Init() {
Alex Deymo30534502015-07-20 15:06:33 -070052 libcros_proxy_->ue_proxy_resolved_interface()
53 ->RegisterProxyResolvedSignalHandler(
54 base::Bind(&ChromeBrowserProxyResolver::OnProxyResolvedSignal,
55 base::Unretained(this)),
56 base::Bind(&ChromeBrowserProxyResolver::OnSignalConnected,
57 base::Unretained(this)));
Andrew de los Reyes000d8952011-03-02 15:21:14 -080058 return true;
59}
60
61ChromeBrowserProxyResolver::~ChromeBrowserProxyResolver() {
Alex Deymo30534502015-07-20 15:06:33 -070062 // Kill outstanding timers.
Alex Deymoc00ec562017-02-05 04:36:02 +000063 for (const auto& it : callbacks_) {
64 MessageLoop::current()->CancelTask(it.second->timeout_id);
Andrew de los Reyes000d8952011-03-02 15:21:14 -080065 }
66}
67
Alex Deymoc00ec562017-02-05 04:36:02 +000068ProxyRequestId ChromeBrowserProxyResolver::GetProxiesForUrl(
69 const string& url, const ProxiesResolvedFn& callback) {
Alex Deymo30534502015-07-20 15:06:33 -070070 int timeout = timeout_;
Alex Vakulenko3f39d5c2015-10-13 09:27:13 -070071 brillo::ErrorPtr error;
Alex Deymo30534502015-07-20 15:06:33 -070072 if (!libcros_proxy_->service_interface_proxy()->ResolveNetworkProxy(
73 url.c_str(),
74 kLibCrosProxyResolveSignalInterface,
75 kLibCrosProxyResolveName,
76 &error)) {
77 LOG(WARNING) << "Can't resolve the proxy. Continuing with no proxy.";
Andrew de los Reyes518502a2011-03-14 14:19:39 -070078 timeout = 0;
Andrew de los Reyes000d8952011-03-02 15:21:14 -080079 }
Gilad Arnold1877c392012-02-10 11:34:33 -080080
Alex Deymoc00ec562017-02-05 04:36:02 +000081 std::unique_ptr<ProxyRequestData> request(new ProxyRequestData());
82 request->callback = callback;
83 ProxyRequestId timeout_id = MessageLoop::current()->PostDelayedTask(
Alex Deymo60ca1a72015-06-18 18:19:15 -070084 FROM_HERE,
85 base::Bind(&ChromeBrowserProxyResolver::HandleTimeout,
86 base::Unretained(this),
Alex Deymoc00ec562017-02-05 04:36:02 +000087 url,
88 request.get()),
Alex Deymo60ca1a72015-06-18 18:19:15 -070089 TimeDelta::FromSeconds(timeout));
Alex Deymoc00ec562017-02-05 04:36:02 +000090 request->timeout_id = timeout_id;
91 callbacks_.emplace(url, std::move(request));
92
93 // We re-use the timeout_id from the MessageLoop as the request id.
94 return timeout_id;
Andrew de los Reyes000d8952011-03-02 15:21:14 -080095}
96
Alex Deymoc00ec562017-02-05 04:36:02 +000097bool ChromeBrowserProxyResolver::CancelProxyRequest(ProxyRequestId request) {
98 // Finding the timeout_id in the callbacks_ structure requires a linear search
99 // but we expect this operation to not be so frequent and to have just a few
100 // proxy requests, so this should be fast enough.
101 for (auto it = callbacks_.begin(); it != callbacks_.end(); ++it) {
102 if (it->second->timeout_id == request) {
103 MessageLoop::current()->CancelTask(request);
104 callbacks_.erase(it);
105 return true;
106 }
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800107 }
Alex Deymoc00ec562017-02-05 04:36:02 +0000108 return false;
109}
110
111void ChromeBrowserProxyResolver::ProcessUrlResponse(
112 const string& source_url, const deque<string>& proxies) {
113 // Call all the occurrences of the |source_url| and erase them.
114 auto lower_end = callbacks_.lower_bound(source_url);
115 auto upper_end = callbacks_.upper_bound(source_url);
116 for (auto it = lower_end; it != upper_end; ++it) {
117 ProxyRequestData* request = it->second.get();
118 MessageLoop::current()->CancelTask(request->timeout_id);
119 request->callback.Run(proxies);
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800120 }
Alex Deymoc00ec562017-02-05 04:36:02 +0000121 callbacks_.erase(lower_end, upper_end);
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800122}
123
Alex Deymo30534502015-07-20 15:06:33 -0700124void ChromeBrowserProxyResolver::OnSignalConnected(const string& interface_name,
125 const string& signal_name,
126 bool successful) {
127 if (!successful) {
128 LOG(ERROR) << "Couldn't connect to the signal " << interface_name << "."
129 << signal_name;
130 }
131}
132
133void ChromeBrowserProxyResolver::OnProxyResolvedSignal(
134 const string& source_url,
135 const string& proxy_info,
136 const string& error_message) {
Alex Deymo30534502015-07-20 15:06:33 -0700137 if (!error_message.empty()) {
138 LOG(WARNING) << "ProxyResolved error: " << error_message;
139 }
Alex Deymoc00ec562017-02-05 04:36:02 +0000140 ProcessUrlResponse(source_url, ParseProxyString(proxy_info));
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800141}
142
Alex Deymoc00ec562017-02-05 04:36:02 +0000143void ChromeBrowserProxyResolver::HandleTimeout(string source_url,
144 ProxyRequestData* request) {
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800145 LOG(INFO) << "Timeout handler called. Seems Chrome isn't responding.";
Alex Deymoc00ec562017-02-05 04:36:02 +0000146 // Mark the timer_id that produced this callback as invalid to prevent
147 // canceling the timeout callback that already fired.
148 request->timeout_id = MessageLoop::kTaskIdNull;
149
150 deque<string> proxies = {kNoProxy};
151 ProcessUrlResponse(source_url, proxies);
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800152}
153
154deque<string> ChromeBrowserProxyResolver::ParseProxyString(
155 const string& input) {
156 deque<string> ret;
157 // Some of this code taken from
158 // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_server.cc and
159 // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_list.cc
160 StringTokenizer entry_tok(input, ";");
161 while (entry_tok.GetNext()) {
162 string token = entry_tok.token();
Ben Chan736fcb52014-05-21 18:28:22 -0700163 base::TrimWhitespaceASCII(token, base::TRIM_ALL, &token);
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800164
165 // Start by finding the first space (if any).
Alex Deymof329b932014-10-30 01:37:48 -0700166 string::iterator space;
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800167 for (space = token.begin(); space != token.end(); ++space) {
Alex Vakulenko0103c362016-01-20 07:56:15 -0800168 if (base::IsAsciiWhitespace(*space)) {
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800169 break;
170 }
171 }
172
Alex Vakulenko0103c362016-01-20 07:56:15 -0800173 string scheme = base::ToLowerASCII(string(token.begin(), space));
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800174 // Chrome uses "socks" to mean socks4 and "proxy" to mean http.
175 if (scheme == "socks")
176 scheme += "4";
177 else if (scheme == "proxy")
178 scheme = "http";
179 else if (scheme != "https" &&
180 scheme != "socks4" &&
181 scheme != "socks5" &&
182 scheme != "direct")
183 continue; // Invalid proxy scheme
184
185 string host_and_port = string(space, token.end());
Ben Chan736fcb52014-05-21 18:28:22 -0700186 base::TrimWhitespaceASCII(host_and_port, base::TRIM_ALL, &host_and_port);
Andrew de los Reyes000d8952011-03-02 15:21:14 -0800187 if (scheme != "direct" && host_and_port.empty())
188 continue; // Must supply host/port when non-direct proxy used.
189 ret.push_back(scheme + "://" + host_and_port);
190 }
191 if (ret.empty() || *ret.rbegin() != kNoProxy)
192 ret.push_back(kNoProxy);
193 return ret;
194}
195
196} // namespace chromeos_update_engine