// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/chromeos/cros/libcros_service_library.h" #include "base/synchronization/lock.h" #include "chrome/browser/chromeos/cros/cros_library.h" #include "chrome/browser/profiles/profile.h" #include "content/browser/browser_thread.h" #include "cros/chromeos_libcros_service.h" #include "net/base/net_errors.h" #include "net/proxy/proxy_service.h" #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_getter.h" namespace chromeos { class LibCrosServiceLibraryImpl : public LibCrosServiceLibrary { public: // Base class for all services of LibCrosService. // Each subclass should declare DISABLE_RUNNABLE_METHOD_REFCOUNT to disable // refcounting, which is okay since the subclass's object will exist as a // scoped_ptr in Singleton LibCrosServiceLibraryImpl, guaranteeing that its // lifetime is longer than that of any message loop. class ServicingLibrary { public: explicit ServicingLibrary(LibCrosServiceConnection service_connection); virtual ~ServicingLibrary(); // Clears service_connection_ (which is stored as weak pointer) so that it // can't be used anymore. virtual void ClearServiceConnection(); protected: LibCrosServiceConnection service_connection_; // Weak pointer. // Lock for data members to synchronize access on multiple threads. base::Lock data_lock_; private: DISALLOW_COPY_AND_ASSIGN(ServicingLibrary); }; // Library that provides network proxy service for LibCrosService. // For now, it only processes proxy resolution requests for ChromeOS clients. class NetworkProxyLibrary : public ServicingLibrary { public: explicit NetworkProxyLibrary(LibCrosServiceConnection connection); virtual ~NetworkProxyLibrary(); private: // Data being used in one proxy resolution. class Request { public: explicit Request(const std::string& source_url); virtual ~Request() {} // Callback on IO thread for when net::ProxyService::ResolveProxy // completes, synchronously or asynchronously. void OnCompletion(int result); net::CompletionCallbackImpl<Request> completion_callback_; std::string source_url_; // URL being resolved. int result_; // Result of proxy resolution. net::ProxyInfo proxy_info_; // ProxyInfo resolved for source_url_. std::string error_; // Error from proxy resolution. Task* notify_task_; // Task to notify of resolution result. private: DISALLOW_COPY_AND_ASSIGN(Request); }; // Static callback passed to LibCrosService to be invoked when ChromeOS // clients send network proxy resolution requests to the service running in // chrome executable. Called on UI thread from dbus request. static void ResolveProxyHandler(void* object, const char* source_url); void ResolveProxy(const std::string& source_url); // Wrapper on UI thread to call LibCrosService::NotifyNetworkProxyResolved. void NotifyProxyResolved(Request* request); std::vector<Request*> all_requests_; DISALLOW_COPY_AND_ASSIGN(NetworkProxyLibrary); }; LibCrosServiceLibraryImpl(); virtual ~LibCrosServiceLibraryImpl(); // LibCrosServiceLibrary implementation. // Starts LibCrosService running on dbus if not already started. virtual void StartService(); private: // Connection to LibCrosService. LibCrosServiceConnection service_connection_; // Libraries that form LibCrosService. scoped_ptr<NetworkProxyLibrary> network_proxy_lib_; DISALLOW_COPY_AND_ASSIGN(LibCrosServiceLibraryImpl); }; //---------------- LibCrosServiceLibraryImpl: public --------------------------- LibCrosServiceLibraryImpl::LibCrosServiceLibraryImpl() : service_connection_(NULL) { if (!CrosLibrary::Get()->EnsureLoaded()) { LOG(ERROR) << "Cros library has not been loaded."; } } LibCrosServiceLibraryImpl::~LibCrosServiceLibraryImpl() { if (service_connection_) { // Clear service connections in servicing libraries which held the former // as weak pointers. if (network_proxy_lib_.get()) network_proxy_lib_->ClearServiceConnection(); StopLibCrosService(service_connection_); VLOG(1) << "LibCrosService stopped."; service_connection_ = NULL; } } // Called on UI thread to start service for LibCrosService. void LibCrosServiceLibraryImpl::StartService() { // Make sure we're running on UI thread. if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, NewRunnableMethod(this, &LibCrosServiceLibraryImpl::StartService)); return; } if (service_connection_) // Service has already been started. return; // Starts LibCrosService; the returned connection is used for future // interactions with the service. service_connection_ = StartLibCrosService(); if (!service_connection_) { LOG(WARNING) << "Error starting LibCrosService"; return; } network_proxy_lib_.reset(new NetworkProxyLibrary(service_connection_)); VLOG(1) << "LibCrosService started."; } //------------- LibCrosServiceLibraryImpl::ServicingLibrary: public ------------ LibCrosServiceLibraryImpl::ServicingLibrary::ServicingLibrary( LibCrosServiceConnection connection) : service_connection_(connection) { } LibCrosServiceLibraryImpl::ServicingLibrary::~ServicingLibrary() { ClearServiceConnection(); } void LibCrosServiceLibraryImpl::ServicingLibrary::ClearServiceConnection() { base::AutoLock lock(data_lock_); service_connection_ = NULL; } //----------- LibCrosServiceLibraryImpl::NetworkProxyLibrary: public ----------- LibCrosServiceLibraryImpl::NetworkProxyLibrary::NetworkProxyLibrary( LibCrosServiceConnection connection) : ServicingLibrary(connection) { // Register callback for LibCrosService::ResolveNetworkProxy. SetNetworkProxyResolver(&ResolveProxyHandler, this, service_connection_); } LibCrosServiceLibraryImpl::NetworkProxyLibrary::~NetworkProxyLibrary() { base::AutoLock lock(data_lock_); if (!all_requests_.empty()) { for (size_t i = all_requests_.size() - 1; i >= 0; --i) { LOG(WARNING) << "Pending request for " << all_requests_[i]->source_url_; delete all_requests_[i]; } all_requests_.clear(); } } //----------- LibCrosServiceLibraryImpl::NetworkProxyLibrary: private ---------- // Static, called on UI thread from LibCrosService::ResolveProxy via dbus. void LibCrosServiceLibraryImpl::NetworkProxyLibrary::ResolveProxyHandler( void* object, const char* source_url) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); NetworkProxyLibrary* lib = static_cast<NetworkProxyLibrary*>(object); // source_url will be freed when this function returns, so make a copy of it. std::string url(source_url); BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, NewRunnableMethod(lib, &NetworkProxyLibrary::ResolveProxy, url)); } // Called on IO thread as task posted from ResolveProxyHandler on UI thread. void LibCrosServiceLibraryImpl::NetworkProxyLibrary::ResolveProxy( const std::string& source_url) { // Make sure we're running on IO thread. DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); // Create a request slot for this proxy resolution request. Request* request = new Request(source_url); request->notify_task_ = NewRunnableMethod(this, &NetworkProxyLibrary::NotifyProxyResolved, request); // Retrieve ProxyService from profile's request context. net::URLRequestContextGetter* getter = Profile::GetDefaultRequestContext(); net::ProxyService* proxy_service = NULL; if (getter) proxy_service = getter->GetURLRequestContext()->proxy_service(); // Check that we have valid proxy service and service_connection. if (!proxy_service) { request->error_ = "No proxy service in chrome"; } else { base::AutoLock lock(data_lock_); // Queue request slot. all_requests_.push_back(request); if (!service_connection_) request->error_ = "LibCrosService not started"; } if (request->error_ != "") { // Error string was just set. LOG(ERROR) << request->error_; request->result_ = net::OK; // Set to OK since error string is set. } else { VLOG(1) << "Starting networy proxy resolution for " << request->source_url_; request->result_ = proxy_service->ResolveProxy( GURL(request->source_url_), &request->proxy_info_, &request->completion_callback_, NULL, net::BoundNetLog()); } if (request->result_ != net::ERR_IO_PENDING) { VLOG(1) << "Network proxy resolution completed synchronously."; request->OnCompletion(request->result_); } } // Called on UI thread as task posted from Request::OnCompletion on IO thread. void LibCrosServiceLibraryImpl::NetworkProxyLibrary::NotifyProxyResolved( Request* request) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); base::AutoLock lock(data_lock_); if (service_connection_) { if (!NotifyNetworkProxyResolved(request->source_url_.c_str(), request->proxy_info_.ToPacString().c_str(), request->error_.c_str(), service_connection_)) { LOG(ERROR) << "LibCrosService has error with NotifyNetworkProxyResolved"; } else { VLOG(1) << "LibCrosService has notified proxy resoloution for " << request->source_url_; } } std::vector<Request*>::iterator iter = std::find(all_requests_.begin(), all_requests_.end(), request); DCHECK(iter != all_requests_.end()); all_requests_.erase(iter); delete request; } //---------- LibCrosServiceLibraryImpl::NetworkProxyLibrary::Request ----------- LibCrosServiceLibraryImpl::NetworkProxyLibrary::Request::Request( const std::string& source_url) : ALLOW_THIS_IN_INITIALIZER_LIST( completion_callback_(this, &Request::OnCompletion)), source_url_(source_url), result_(net::ERR_FAILED), notify_task_(NULL) { } // Called on IO thread when net::ProxyService::ResolveProxy has completed. void LibCrosServiceLibraryImpl::NetworkProxyLibrary::Request::OnCompletion( int result) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); result_ = result; if (result_ != net::OK) error_ = net::ErrorToString(result_); BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, notify_task_); notify_task_ = NULL; } //--------------------- LibCrosServiceLibraryStubImpl -------------------------- class LibCrosServiceLibraryStubImpl : public LibCrosServiceLibrary { public: LibCrosServiceLibraryStubImpl() {} virtual ~LibCrosServiceLibraryStubImpl() {} // LibCrosServiceLibrary overrides. virtual void StartService() {} DISALLOW_COPY_AND_ASSIGN(LibCrosServiceLibraryStubImpl); }; //--------------------------- LibCrosServiceLibrary ---------------------------- // Static. LibCrosServiceLibrary* LibCrosServiceLibrary::GetImpl(bool stub) { if (stub) return new LibCrosServiceLibraryStubImpl(); return new LibCrosServiceLibraryImpl(); } } // namespace chromeos // Allows InvokeLater without adding refcounting. This class is a Singleton and // won't be deleted until it's last InvokeLater is run, so are all its // scoped_ptred class members. DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::LibCrosServiceLibraryImpl); DISABLE_RUNNABLE_METHOD_REFCOUNT( chromeos::LibCrosServiceLibraryImpl::NetworkProxyLibrary);