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 22 using content::BrowserThread; 23 24 namespace chromeos { 25 26 // The ProxyResolverInterface implementation used in production. 27 class 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 186 ProxyResolutionServiceProvider::ProxyResolutionServiceProvider( 187 ProxyResolverInterface* resolver) 188 : resolver_(resolver), 189 origin_thread_id_(base::PlatformThread::CurrentId()), 190 weak_ptr_factory_(this) { 191 } 192 193 ProxyResolutionServiceProvider::~ProxyResolutionServiceProvider() { 194 } 195 196 void 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 213 void 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 224 bool ProxyResolutionServiceProvider::OnOriginThread() { 225 return base::PlatformThread::CurrentId() == origin_thread_id_; 226 } 227 228 void 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 257 void 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 269 ProxyResolutionServiceProvider* ProxyResolutionServiceProvider::Create() { 270 return new ProxyResolutionServiceProvider(new ProxyResolverImpl); 271 } 272 273 ProxyResolutionServiceProvider* 274 ProxyResolutionServiceProvider::CreateForTesting( 275 ProxyResolverInterface* resolver) { 276 return new ProxyResolutionServiceProvider(resolver); 277 } 278 279 ProxyResolverInterface::~ProxyResolverInterface() { 280 } 281 282 } // namespace chromeos 283