1// Copyright (c) 2012 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/dbus/proxy_resolution_service_provider.h"
6
7#include "base/bind.h"
8#include "base/bind_helpers.h"
9#include "base/threading/platform_thread.h"
10#include "chrome/browser/profiles/profile_manager.h"
11#include "content/public/browser/browser_thread.h"
12#include "dbus/bus.h"
13#include "dbus/message.h"
14#include "dbus/exported_object.h"
15#include "net/base/load_flags.h"
16#include "net/base/net_errors.h"
17#include "net/proxy/proxy_service.h"
18#include "net/url_request/url_request_context.h"
19#include "net/url_request/url_request_context_getter.h"
20#include "third_party/cros_system_api/dbus/service_constants.h"
21
22using content::BrowserThread;
23
24namespace chromeos {
25
26// The ProxyResolverInterface implementation used in production.
27class ProxyResolverImpl : public ProxyResolverInterface {
28 public:
29  // Data being used in one proxy resolution.
30  class Request {
31   public:
32    explicit Request(const std::string& source_url)
33        : callback_(base::Bind(&Request::OnCompletion, base::Unretained(this))),
34          source_url_(source_url) {
35    }
36
37    virtual ~Request() {}
38
39    // Callback on IO thread for when net::ProxyService::ResolveProxy
40    // completes, synchronously or asynchronously.
41    void OnCompletion(int result) {
42      DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
43      // Generate the error message if the error message is not yet set,
44      // and there was an error.
45      if (error_.empty() && result != net::OK)
46        error_ = net::ErrorToString(result);
47      BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, notify_task_);
48    }
49
50    net::CompletionCallback callback_;
51
52    std::string source_url_;  // URL being resolved.
53    net::ProxyInfo proxy_info_;  // ProxyInfo resolved for source_url_.
54    std::string error_;  // Error from proxy resolution.
55    base::Closure notify_task_;  // Task to notify of resolution result.
56
57   private:
58    DISALLOW_COPY_AND_ASSIGN(Request);
59  };
60
61  ProxyResolverImpl()
62      : origin_thread_id_(base::PlatformThread::CurrentId()),
63        weak_ptr_factory_(this) {
64  }
65
66  virtual ~ProxyResolverImpl() {
67    DCHECK(OnOriginThread());
68
69    for (std::set<Request*>::iterator iter = all_requests_.begin();
70         iter != all_requests_.end(); ++iter) {
71      Request* request = *iter;
72      LOG(WARNING) << "Pending request for " << request->source_url_;
73      delete request;
74    }
75  }
76
77  // ProxyResolverInterface override.
78  virtual void ResolveProxy(
79      const std::string& source_url,
80      const std::string& signal_interface,
81      const std::string& signal_name,
82      scoped_refptr<dbus::ExportedObject> exported_object) OVERRIDE {
83    DCHECK(OnOriginThread());
84
85    // Create a request slot for this proxy resolution request.
86    Request* request = new Request(source_url);
87    request->notify_task_ = base::Bind(
88        &ProxyResolverImpl::NotifyProxyResolved,
89        weak_ptr_factory_.GetWeakPtr(),
90        signal_interface,
91        signal_name,
92        exported_object,
93        request);
94    all_requests_.insert(request);
95
96    // GetPrimaryUserProfile() and GetRequestContext() must be called on UI
97    // thread.
98    Profile* profile = ProfileManager::GetPrimaryUserProfile();
99    scoped_refptr<net::URLRequestContextGetter> getter =
100        profile->GetRequestContext();
101
102    BrowserThread::PostTask(
103        BrowserThread::IO, FROM_HERE,
104        base::Bind(&ProxyResolverImpl::ResolveProxyInternal,
105                   request,
106                   getter,
107                   exported_object));
108  }
109
110 private:
111  // Helper function for ResolveProxy().
112  static void ResolveProxyInternal(
113      Request* request,
114      scoped_refptr<net::URLRequestContextGetter> getter,
115      scoped_refptr<dbus::ExportedObject> exported_object) {
116    // Make sure we're running on IO thread.
117    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
118
119    // Check if we have the URLRequestContextGetter.
120    if (!getter.get()) {
121      request->error_ = "No URLRequestContextGetter";
122      request->OnCompletion(net::ERR_UNEXPECTED);
123      return;
124    }
125
126    // Retrieve ProxyService from profile's request context.
127    net::ProxyService* proxy_service =
128        getter->GetURLRequestContext()->proxy_service();
129    if (!proxy_service) {
130      request->error_ = "No proxy service in chrome";
131      request->OnCompletion(net::ERR_UNEXPECTED);
132      return;
133    }
134
135    VLOG(1) << "Starting network proxy resolution for "
136            << request->source_url_;
137    const int result = proxy_service->ResolveProxy(
138        GURL(request->source_url_), net::LOAD_NORMAL, &request->proxy_info_,
139        request->callback_, NULL, NULL, net::BoundNetLog());
140    if (result != net::ERR_IO_PENDING) {
141      VLOG(1) << "Network proxy resolution completed synchronously.";
142      request->OnCompletion(result);
143    }
144  }
145
146  // Called on UI thread as task posted from Request::OnCompletion on IO
147  // thread.
148  void NotifyProxyResolved(
149      const std::string& signal_interface,
150      const std::string& signal_name,
151      scoped_refptr<dbus::ExportedObject> exported_object,
152      Request* request) {
153    DCHECK(OnOriginThread());
154
155    // Send a signal to the client.
156    dbus::Signal signal(signal_interface, signal_name);
157    dbus::MessageWriter writer(&signal);
158    writer.AppendString(request->source_url_);
159    writer.AppendString(request->proxy_info_.ToPacString());
160    writer.AppendString(request->error_);
161    exported_object->SendSignal(&signal);
162    VLOG(1) << "Sending signal: " << signal.ToString();
163
164    std::set<Request*>::iterator iter = all_requests_.find(request);
165    if (iter == all_requests_.end()) {
166      LOG(ERROR) << "can't find request slot(" << request->source_url_
167                 << ") in proxy-resolution queue";
168    } else {
169      all_requests_.erase(iter);
170    }
171    delete request;
172  }
173
174  // Returns true if the current thread is on the origin thread.
175  bool OnOriginThread() {
176    return base::PlatformThread::CurrentId() == origin_thread_id_;
177  }
178
179  base::PlatformThreadId origin_thread_id_;
180  std::set<Request*> all_requests_;
181  base::WeakPtrFactory<ProxyResolverImpl> weak_ptr_factory_;
182
183  DISALLOW_COPY_AND_ASSIGN(ProxyResolverImpl);
184};
185
186ProxyResolutionServiceProvider::ProxyResolutionServiceProvider(
187    ProxyResolverInterface* resolver)
188    : resolver_(resolver),
189      origin_thread_id_(base::PlatformThread::CurrentId()),
190      weak_ptr_factory_(this) {
191}
192
193ProxyResolutionServiceProvider::~ProxyResolutionServiceProvider() {
194}
195
196void ProxyResolutionServiceProvider::Start(
197    scoped_refptr<dbus::ExportedObject> exported_object) {
198  DCHECK(OnOriginThread());
199  exported_object_ = exported_object;
200  VLOG(1) << "ProxyResolutionServiceProvider started";
201  exported_object_->ExportMethod(
202      kLibCrosServiceInterface,
203      kResolveNetworkProxy,
204      // Weak pointers can only bind to methods without return values,
205      // hence we cannot bind ResolveProxyInternal here. Instead we use a
206      // static function to solve this problem.
207      base::Bind(&ProxyResolutionServiceProvider::CallResolveProxyHandler,
208                 weak_ptr_factory_.GetWeakPtr()),
209      base::Bind(&ProxyResolutionServiceProvider::OnExported,
210                 weak_ptr_factory_.GetWeakPtr()));
211}
212
213void ProxyResolutionServiceProvider::OnExported(
214    const std::string& interface_name,
215    const std::string& method_name,
216    bool success) {
217  if (!success) {
218    LOG(ERROR) << "Failed to export " << interface_name << "."
219               << method_name;
220  }
221  VLOG(1) << "Method exported: " << interface_name << "." << method_name;
222}
223
224bool ProxyResolutionServiceProvider::OnOriginThread() {
225  return base::PlatformThread::CurrentId() == origin_thread_id_;
226}
227
228void ProxyResolutionServiceProvider::ResolveProxyHandler(
229    dbus::MethodCall* method_call,
230    dbus::ExportedObject::ResponseSender response_sender) {
231  DCHECK(OnOriginThread());
232  VLOG(1) << "Handing method call: " << method_call->ToString();
233  // The method call should contain the three string parameters.
234  dbus::MessageReader reader(method_call);
235  std::string source_url;
236  std::string signal_interface;
237  std::string signal_name;
238  if (!reader.PopString(&source_url) ||
239      !reader.PopString(&signal_interface) ||
240      !reader.PopString(&signal_name)) {
241    LOG(ERROR) << "Unexpected method call: " << method_call->ToString();
242    response_sender.Run(scoped_ptr<dbus::Response>());
243    return;
244  }
245
246  resolver_->ResolveProxy(source_url,
247                          signal_interface,
248                          signal_name,
249                          exported_object_);
250
251  // Send an empty response for now. We'll send a signal once the network proxy
252  // resolution is completed.
253  response_sender.Run(dbus::Response::FromMethodCall(method_call));
254}
255
256// static
257void ProxyResolutionServiceProvider::CallResolveProxyHandler(
258    base::WeakPtr<ProxyResolutionServiceProvider> provider_weak_ptr,
259    dbus::MethodCall* method_call,
260    dbus::ExportedObject::ResponseSender response_sender) {
261  if (!provider_weak_ptr) {
262    LOG(WARNING) << "Called after the object is deleted";
263    response_sender.Run(scoped_ptr<dbus::Response>());
264    return;
265  }
266  provider_weak_ptr->ResolveProxyHandler(method_call, response_sender);
267}
268
269ProxyResolutionServiceProvider* ProxyResolutionServiceProvider::Create() {
270  return new ProxyResolutionServiceProvider(new ProxyResolverImpl);
271}
272
273ProxyResolutionServiceProvider*
274ProxyResolutionServiceProvider::CreateForTesting(
275    ProxyResolverInterface* resolver) {
276  return new ProxyResolutionServiceProvider(resolver);
277}
278
279ProxyResolverInterface::~ProxyResolverInterface() {
280}
281
282}  // namespace chromeos
283