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