1 // Copyright (c) 2011 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 "chrome/browser/chromeos/cros/libcros_service_library.h"
6
7 #include "base/synchronization/lock.h"
8 #include "chrome/browser/chromeos/cros/cros_library.h"
9 #include "chrome/browser/profiles/profile.h"
10 #include "content/browser/browser_thread.h"
11 #include "cros/chromeos_libcros_service.h"
12 #include "net/base/net_errors.h"
13 #include "net/proxy/proxy_service.h"
14 #include "net/url_request/url_request_context.h"
15 #include "net/url_request/url_request_context_getter.h"
16
17 namespace chromeos {
18
19 class LibCrosServiceLibraryImpl : public LibCrosServiceLibrary {
20 public:
21 // Base class for all services of LibCrosService.
22 // Each subclass should declare DISABLE_RUNNABLE_METHOD_REFCOUNT to disable
23 // refcounting, which is okay since the subclass's object will exist as a
24 // scoped_ptr in Singleton LibCrosServiceLibraryImpl, guaranteeing that its
25 // lifetime is longer than that of any message loop.
26 class ServicingLibrary {
27 public:
28 explicit ServicingLibrary(LibCrosServiceConnection service_connection);
29 virtual ~ServicingLibrary();
30
31 // Clears service_connection_ (which is stored as weak pointer) so that it
32 // can't be used anymore.
33 virtual void ClearServiceConnection();
34
35 protected:
36 LibCrosServiceConnection service_connection_; // Weak pointer.
37 // Lock for data members to synchronize access on multiple threads.
38 base::Lock data_lock_;
39
40 private:
41 DISALLOW_COPY_AND_ASSIGN(ServicingLibrary);
42 };
43
44 // Library that provides network proxy service for LibCrosService.
45 // For now, it only processes proxy resolution requests for ChromeOS clients.
46 class NetworkProxyLibrary : public ServicingLibrary {
47 public:
48 explicit NetworkProxyLibrary(LibCrosServiceConnection connection);
49 virtual ~NetworkProxyLibrary();
50
51 private:
52 // Data being used in one proxy resolution.
53 class Request {
54 public:
55 explicit Request(const std::string& source_url);
~Request()56 virtual ~Request() {}
57
58 // Callback on IO thread for when net::ProxyService::ResolveProxy
59 // completes, synchronously or asynchronously.
60 void OnCompletion(int result);
61 net::CompletionCallbackImpl<Request> completion_callback_;
62
63 std::string source_url_; // URL being resolved.
64 int result_; // Result of proxy resolution.
65 net::ProxyInfo proxy_info_; // ProxyInfo resolved for source_url_.
66 std::string error_; // Error from proxy resolution.
67 Task* notify_task_; // Task to notify of resolution result.
68
69 private:
70 DISALLOW_COPY_AND_ASSIGN(Request);
71 };
72
73 // Static callback passed to LibCrosService to be invoked when ChromeOS
74 // clients send network proxy resolution requests to the service running in
75 // chrome executable. Called on UI thread from dbus request.
76 static void ResolveProxyHandler(void* object, const char* source_url);
77
78 void ResolveProxy(const std::string& source_url);
79
80 // Wrapper on UI thread to call LibCrosService::NotifyNetworkProxyResolved.
81 void NotifyProxyResolved(Request* request);
82
83 std::vector<Request*> all_requests_;
84
85 DISALLOW_COPY_AND_ASSIGN(NetworkProxyLibrary);
86 };
87
88 LibCrosServiceLibraryImpl();
89 virtual ~LibCrosServiceLibraryImpl();
90
91 // LibCrosServiceLibrary implementation.
92
93 // Starts LibCrosService running on dbus if not already started.
94 virtual void StartService();
95
96 private:
97 // Connection to LibCrosService.
98 LibCrosServiceConnection service_connection_;
99
100 // Libraries that form LibCrosService.
101 scoped_ptr<NetworkProxyLibrary> network_proxy_lib_;
102
103 DISALLOW_COPY_AND_ASSIGN(LibCrosServiceLibraryImpl);
104 };
105
106 //---------------- LibCrosServiceLibraryImpl: public ---------------------------
107
LibCrosServiceLibraryImpl()108 LibCrosServiceLibraryImpl::LibCrosServiceLibraryImpl()
109 : service_connection_(NULL) {
110 if (!CrosLibrary::Get()->EnsureLoaded()) {
111 LOG(ERROR) << "Cros library has not been loaded.";
112 }
113 }
114
~LibCrosServiceLibraryImpl()115 LibCrosServiceLibraryImpl::~LibCrosServiceLibraryImpl() {
116 if (service_connection_) {
117 // Clear service connections in servicing libraries which held the former
118 // as weak pointers.
119 if (network_proxy_lib_.get())
120 network_proxy_lib_->ClearServiceConnection();
121
122 StopLibCrosService(service_connection_);
123 VLOG(1) << "LibCrosService stopped.";
124 service_connection_ = NULL;
125 }
126 }
127
128 // Called on UI thread to start service for LibCrosService.
StartService()129 void LibCrosServiceLibraryImpl::StartService() {
130 // Make sure we're running on UI thread.
131 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
132 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
133 NewRunnableMethod(this,
134 &LibCrosServiceLibraryImpl::StartService));
135 return;
136 }
137 if (service_connection_) // Service has already been started.
138 return;
139 // Starts LibCrosService; the returned connection is used for future
140 // interactions with the service.
141 service_connection_ = StartLibCrosService();
142 if (!service_connection_) {
143 LOG(WARNING) << "Error starting LibCrosService";
144 return;
145 }
146 network_proxy_lib_.reset(new NetworkProxyLibrary(service_connection_));
147 VLOG(1) << "LibCrosService started.";
148 }
149
150 //------------- LibCrosServiceLibraryImpl::ServicingLibrary: public ------------
151
ServicingLibrary(LibCrosServiceConnection connection)152 LibCrosServiceLibraryImpl::ServicingLibrary::ServicingLibrary(
153 LibCrosServiceConnection connection)
154 : service_connection_(connection) {
155 }
156
~ServicingLibrary()157 LibCrosServiceLibraryImpl::ServicingLibrary::~ServicingLibrary() {
158 ClearServiceConnection();
159 }
160
ClearServiceConnection()161 void LibCrosServiceLibraryImpl::ServicingLibrary::ClearServiceConnection() {
162 base::AutoLock lock(data_lock_);
163 service_connection_ = NULL;
164 }
165
166 //----------- LibCrosServiceLibraryImpl::NetworkProxyLibrary: public -----------
167
NetworkProxyLibrary(LibCrosServiceConnection connection)168 LibCrosServiceLibraryImpl::NetworkProxyLibrary::NetworkProxyLibrary(
169 LibCrosServiceConnection connection)
170 : ServicingLibrary(connection) {
171 // Register callback for LibCrosService::ResolveNetworkProxy.
172 SetNetworkProxyResolver(&ResolveProxyHandler, this, service_connection_);
173 }
174
~NetworkProxyLibrary()175 LibCrosServiceLibraryImpl::NetworkProxyLibrary::~NetworkProxyLibrary() {
176 base::AutoLock lock(data_lock_);
177 if (!all_requests_.empty()) {
178 for (size_t i = all_requests_.size() - 1; i >= 0; --i) {
179 LOG(WARNING) << "Pending request for " << all_requests_[i]->source_url_;
180 delete all_requests_[i];
181 }
182 all_requests_.clear();
183 }
184 }
185
186 //----------- LibCrosServiceLibraryImpl::NetworkProxyLibrary: private ----------
187
188 // Static, called on UI thread from LibCrosService::ResolveProxy via dbus.
ResolveProxyHandler(void * object,const char * source_url)189 void LibCrosServiceLibraryImpl::NetworkProxyLibrary::ResolveProxyHandler(
190 void* object, const char* source_url) {
191 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
192 NetworkProxyLibrary* lib = static_cast<NetworkProxyLibrary*>(object);
193 // source_url will be freed when this function returns, so make a copy of it.
194 std::string url(source_url);
195 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
196 NewRunnableMethod(lib, &NetworkProxyLibrary::ResolveProxy, url));
197 }
198
199 // Called on IO thread as task posted from ResolveProxyHandler on UI thread.
ResolveProxy(const std::string & source_url)200 void LibCrosServiceLibraryImpl::NetworkProxyLibrary::ResolveProxy(
201 const std::string& source_url) {
202 // Make sure we're running on IO thread.
203 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
204
205 // Create a request slot for this proxy resolution request.
206 Request* request = new Request(source_url);
207 request->notify_task_ = NewRunnableMethod(this,
208 &NetworkProxyLibrary::NotifyProxyResolved, request);
209
210 // Retrieve ProxyService from profile's request context.
211 net::URLRequestContextGetter* getter = Profile::GetDefaultRequestContext();
212 net::ProxyService* proxy_service = NULL;
213 if (getter)
214 proxy_service = getter->GetURLRequestContext()->proxy_service();
215
216 // Check that we have valid proxy service and service_connection.
217 if (!proxy_service) {
218 request->error_ = "No proxy service in chrome";
219 } else {
220 base::AutoLock lock(data_lock_);
221 // Queue request slot.
222 all_requests_.push_back(request);
223 if (!service_connection_)
224 request->error_ = "LibCrosService not started";
225 }
226 if (request->error_ != "") { // Error string was just set.
227 LOG(ERROR) << request->error_;
228 request->result_ = net::OK; // Set to OK since error string is set.
229 } else {
230 VLOG(1) << "Starting networy proxy resolution for " << request->source_url_;
231 request->result_ = proxy_service->ResolveProxy(
232 GURL(request->source_url_), &request->proxy_info_,
233 &request->completion_callback_, NULL, net::BoundNetLog());
234 }
235 if (request->result_ != net::ERR_IO_PENDING) {
236 VLOG(1) << "Network proxy resolution completed synchronously.";
237 request->OnCompletion(request->result_);
238 }
239 }
240
241 // Called on UI thread as task posted from Request::OnCompletion on IO thread.
NotifyProxyResolved(Request * request)242 void LibCrosServiceLibraryImpl::NetworkProxyLibrary::NotifyProxyResolved(
243 Request* request) {
244 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
245 base::AutoLock lock(data_lock_);
246 if (service_connection_) {
247 if (!NotifyNetworkProxyResolved(request->source_url_.c_str(),
248 request->proxy_info_.ToPacString().c_str(),
249 request->error_.c_str(),
250 service_connection_)) {
251 LOG(ERROR) << "LibCrosService has error with NotifyNetworkProxyResolved";
252 } else {
253 VLOG(1) << "LibCrosService has notified proxy resoloution for "
254 << request->source_url_;
255 }
256 }
257 std::vector<Request*>::iterator iter =
258 std::find(all_requests_.begin(), all_requests_.end(), request);
259 DCHECK(iter != all_requests_.end());
260 all_requests_.erase(iter);
261 delete request;
262 }
263
264 //---------- LibCrosServiceLibraryImpl::NetworkProxyLibrary::Request -----------
265
Request(const std::string & source_url)266 LibCrosServiceLibraryImpl::NetworkProxyLibrary::Request::Request(
267 const std::string& source_url)
268 : ALLOW_THIS_IN_INITIALIZER_LIST(
269 completion_callback_(this, &Request::OnCompletion)),
270 source_url_(source_url),
271 result_(net::ERR_FAILED),
272 notify_task_(NULL) {
273 }
274
275 // Called on IO thread when net::ProxyService::ResolveProxy has completed.
OnCompletion(int result)276 void LibCrosServiceLibraryImpl::NetworkProxyLibrary::Request::OnCompletion(
277 int result) {
278 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
279 result_ = result;
280 if (result_ != net::OK)
281 error_ = net::ErrorToString(result_);
282 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, notify_task_);
283 notify_task_ = NULL;
284 }
285
286 //--------------------- LibCrosServiceLibraryStubImpl --------------------------
287
288 class LibCrosServiceLibraryStubImpl : public LibCrosServiceLibrary {
289 public:
LibCrosServiceLibraryStubImpl()290 LibCrosServiceLibraryStubImpl() {}
~LibCrosServiceLibraryStubImpl()291 virtual ~LibCrosServiceLibraryStubImpl() {}
292
293 // LibCrosServiceLibrary overrides.
StartService()294 virtual void StartService() {}
295
296 DISALLOW_COPY_AND_ASSIGN(LibCrosServiceLibraryStubImpl);
297 };
298
299 //--------------------------- LibCrosServiceLibrary ----------------------------
300
301 // Static.
GetImpl(bool stub)302 LibCrosServiceLibrary* LibCrosServiceLibrary::GetImpl(bool stub) {
303 if (stub)
304 return new LibCrosServiceLibraryStubImpl();
305 return new LibCrosServiceLibraryImpl();
306 }
307
308 } // namespace chromeos
309
310 // Allows InvokeLater without adding refcounting. This class is a Singleton and
311 // won't be deleted until it's last InvokeLater is run, so are all its
312 // scoped_ptred class members.
313 DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::LibCrosServiceLibraryImpl);
314 DISABLE_RUNNABLE_METHOD_REFCOUNT(
315 chromeos::LibCrosServiceLibraryImpl::NetworkProxyLibrary);
316
317