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
17namespace chromeos {
18
19class 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);
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
108LibCrosServiceLibraryImpl::LibCrosServiceLibraryImpl()
109    : service_connection_(NULL) {
110  if (!CrosLibrary::Get()->EnsureLoaded()) {
111    LOG(ERROR) << "Cros library has not been loaded.";
112  }
113}
114
115LibCrosServiceLibraryImpl::~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.
129void 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
152LibCrosServiceLibraryImpl::ServicingLibrary::ServicingLibrary(
153    LibCrosServiceConnection connection)
154    : service_connection_(connection) {
155}
156
157LibCrosServiceLibraryImpl::ServicingLibrary::~ServicingLibrary() {
158  ClearServiceConnection();
159}
160
161void LibCrosServiceLibraryImpl::ServicingLibrary::ClearServiceConnection() {
162  base::AutoLock lock(data_lock_);
163  service_connection_ = NULL;
164}
165
166//----------- LibCrosServiceLibraryImpl::NetworkProxyLibrary: public -----------
167
168LibCrosServiceLibraryImpl::NetworkProxyLibrary::NetworkProxyLibrary(
169    LibCrosServiceConnection connection)
170    : ServicingLibrary(connection) {
171  // Register callback for LibCrosService::ResolveNetworkProxy.
172  SetNetworkProxyResolver(&ResolveProxyHandler, this, service_connection_);
173}
174
175LibCrosServiceLibraryImpl::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.
189void 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.
200void 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.
242void 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
266LibCrosServiceLibraryImpl::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.
276void 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
288class LibCrosServiceLibraryStubImpl : public LibCrosServiceLibrary {
289 public:
290  LibCrosServiceLibraryStubImpl() {}
291  virtual ~LibCrosServiceLibraryStubImpl() {}
292
293  // LibCrosServiceLibrary overrides.
294  virtual void StartService() {}
295
296  DISALLOW_COPY_AND_ASSIGN(LibCrosServiceLibraryStubImpl);
297};
298
299//--------------------------- LibCrosServiceLibrary ----------------------------
300
301// Static.
302LibCrosServiceLibrary* 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.
313DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::LibCrosServiceLibraryImpl);
314DISABLE_RUNNABLE_METHOD_REFCOUNT(
315    chromeos::LibCrosServiceLibraryImpl::NetworkProxyLibrary);
316
317