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