Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2352)

Unified Diff: chrome/browser/local_discovery/service_discovery_client_mdns.cc

Issue 256913005: In browser process implementation of ServiceDiscoveryClient. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Tue 04/29/2014 14:32:52.76 Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/browser/local_discovery/service_discovery_client_mdns.h ('k') | chrome/chrome_browser.gypi » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/local_discovery/service_discovery_client_mdns.cc
diff --git a/chrome/browser/local_discovery/service_discovery_client_mdns.cc b/chrome/browser/local_discovery/service_discovery_client_mdns.cc
new file mode 100644
index 0000000000000000000000000000000000000000..9162d2163099e00e3ac17da7435f21efde650e0c
--- /dev/null
+++ b/chrome/browser/local_discovery/service_discovery_client_mdns.cc
@@ -0,0 +1,433 @@
+// Copyright 2014 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/local_discovery/service_discovery_client_mdns.h"
+
+#include "base/memory/scoped_vector.h"
+#include "base/metrics/histogram.h"
+#include "chrome/common/local_discovery/service_discovery_client_impl.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/dns/mdns_client.h"
+#include "net/udp/datagram_server_socket.h"
+
+namespace local_discovery {
+
+using content::BrowserThread;
+
+// Base class for objects returned by ServiceDiscoveryClient implementation.
+// Handles interaction of client code on UI thread end net code on mdns thread.
+class ServiceDiscoveryClientMdns::Proxy {
+ public:
+ typedef base::WeakPtr<Proxy> WeakPtr;
+
+ explicit Proxy(ServiceDiscoveryClientMdns* client)
+ : client_(client),
+ weak_ptr_factory_(this) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ client_->proxies_.insert(this);
+ }
+
+ virtual ~Proxy() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ client_->proxies_.erase(this);
+ }
+
+ // Notify proxies that mDNS layer is going to be destroyed.
+ virtual void OnMdnsDestroy() = 0;
+
+ // Notify proxies that new mDNS instance is ready.
+ virtual void OnNewMdnsReady() {}
+
+ // Run callback using this method to abort callback if instance of |Proxy|
+ // is deleted.
+ void RunCallback(const base::Closure& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ callback.Run();
+ }
+
+ protected:
+ bool PostToMdnsThread(const base::Closure& task) {
+ return client_->PostToMdnsThread(task);
+ }
+
+ static bool PostToUIThread(const base::Closure& task) {
+ return BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, task);
+ }
+
+ ServiceDiscoveryClient* client() {
+ return client_->client_.get();
+ }
+
+ WeakPtr GetWeakPtr() {
+ return weak_ptr_factory_.GetWeakPtr();
+ }
+
+ template<class T>
+ void DeleteOnMdnsThread(T* t) {
+ if (!t)
+ return;
+ if (!client_->mdns_runner_->DeleteSoon(FROM_HERE, t))
+ delete t;
+ }
+
+ private:
+ scoped_refptr<ServiceDiscoveryClientMdns> client_;
+ base::WeakPtrFactory<Proxy> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(Proxy);
+};
+
+namespace {
+
+const int kMaxRestartAttempts = 10;
+const int kRestartDelayOnNetworkChangeSeconds = 3;
+
+typedef base::Callback<void(bool)> MdnsInitCallback;
+
+class SocketFactory : public net::MDnsSocketFactory {
+ public:
+ explicit SocketFactory(const net::InterfaceIndexFamilyList& interfaces)
+ : interfaces_(interfaces) {}
+
+ // net::MDnsSocketFactory implementation:
+ virtual void CreateSockets(
+ ScopedVector<net::DatagramServerSocket>* sockets) OVERRIDE {
+ for (size_t i = 0; i < interfaces_.size(); ++i) {
+ DCHECK(interfaces_[i].second == net::ADDRESS_FAMILY_IPV4 ||
+ interfaces_[i].second == net::ADDRESS_FAMILY_IPV6);
+ scoped_ptr<net::DatagramServerSocket> socket(
+ CreateAndBindMDnsSocket(interfaces_[i].second, interfaces_[i].first));
+ if (socket)
+ sockets->push_back(socket.release());
+ }
+ }
+
+ private:
+ net::InterfaceIndexFamilyList interfaces_;
+};
+
+void InitMdns(const MdnsInitCallback& on_initialized,
+ const net::InterfaceIndexFamilyList& interfaces,
+ net::MDnsClient* mdns) {
+ SocketFactory socket_factory(interfaces);
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::Bind(on_initialized,
+ mdns->StartListening(&socket_factory)));
+}
+
+template<class T>
+class ProxyBase : public ServiceDiscoveryClientMdns::Proxy, public T {
+ public:
+ typedef base::WeakPtr<Proxy> WeakPtr;
+ typedef ProxyBase<T> Base;
+
+ explicit ProxyBase(ServiceDiscoveryClientMdns* client)
+ : Proxy(client) {
+ }
+
+ virtual ~ProxyBase() {
+ DeleteOnMdnsThread(implementation_.release());
+ }
+
+ virtual void OnMdnsDestroy() OVERRIDE {
+ DeleteOnMdnsThread(implementation_.release());
+ };
+
+ protected:
+ void set_implementation(scoped_ptr<T> implementation) {
+ implementation_ = implementation.Pass();
+ }
+
+ T* implementation() const {
+ return implementation_.get();
+ }
+
+ private:
+ scoped_ptr<T> implementation_;
+ DISALLOW_COPY_AND_ASSIGN(ProxyBase);
+};
+
+class ServiceWatcherProxy : public ProxyBase<ServiceWatcher> {
+ public:
+ ServiceWatcherProxy(ServiceDiscoveryClientMdns* client_mdns,
+ const std::string& service_type,
+ const ServiceWatcher::UpdatedCallback& callback)
+ : ProxyBase(client_mdns),
+ service_type_(service_type),
+ callback_(callback) {
+ // It's safe to call |CreateServiceWatcher| on UI thread, because
+ // |MDnsClient| is not used there. It's simplify implementation.
+ set_implementation(client()->CreateServiceWatcher(
+ service_type,
+ base::Bind(&ServiceWatcherProxy::OnCallback, GetWeakPtr(), callback)));
+ }
+
+ // ServiceWatcher methods.
+ virtual void Start() OVERRIDE {
+ if (implementation())
+ PostToMdnsThread(base::Bind(&ServiceWatcher::Start,
+ base::Unretained(implementation())));
+ }
+
+ virtual void DiscoverNewServices(bool force_update) OVERRIDE {
+ if (implementation())
+ PostToMdnsThread(base::Bind(&ServiceWatcher::DiscoverNewServices,
+ base::Unretained(implementation()),
+ force_update));
+ }
+
+ virtual void SetActivelyRefreshServices(
+ bool actively_refresh_services) OVERRIDE {
+ if (implementation())
+ PostToMdnsThread(base::Bind(&ServiceWatcher::SetActivelyRefreshServices,
+ base::Unretained(implementation()),
+ actively_refresh_services));
+ }
+
+ virtual std::string GetServiceType() const OVERRIDE {
+ return service_type_;
+ }
+
+ virtual void OnNewMdnsReady() OVERRIDE {
+ if (!implementation())
+ callback_.Run(ServiceWatcher::UPDATE_INVALIDATED, "");
+ }
+
+ private:
+ static void OnCallback(const WeakPtr& proxy,
+ const ServiceWatcher::UpdatedCallback& callback,
+ UpdateType a1,
+ const std::string& a2) {
+ DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
+ PostToUIThread(base::Bind(&Base::RunCallback, proxy,
+ base::Bind(callback, a1, a2)));
+ }
+ std::string service_type_;
+ ServiceWatcher::UpdatedCallback callback_;
+ DISALLOW_COPY_AND_ASSIGN(ServiceWatcherProxy);
+};
+
+class ServiceResolverProxy : public ProxyBase<ServiceResolver> {
+ public:
+ ServiceResolverProxy(ServiceDiscoveryClientMdns* client_mdns,
+ const std::string& service_name,
+ const ServiceResolver::ResolveCompleteCallback& callback)
+ : ProxyBase(client_mdns),
+ service_name_(service_name) {
+ // It's safe to call |CreateServiceResolver| on UI thread, because
+ // |MDnsClient| is not used there. It's simplify implementation.
+ set_implementation(client()->CreateServiceResolver(
+ service_name,
+ base::Bind(&ServiceResolverProxy::OnCallback, GetWeakPtr(), callback)));
+ }
+
+ // ServiceResolver methods.
+ virtual void StartResolving() OVERRIDE {
+ if (implementation())
+ PostToMdnsThread(base::Bind(&ServiceResolver::StartResolving,
+ base::Unretained(implementation())));
+ };
+
+ virtual std::string GetName() const OVERRIDE {
+ return service_name_;
+ }
+
+ private:
+ static void OnCallback(
+ const WeakPtr& proxy,
+ const ServiceResolver::ResolveCompleteCallback& callback,
+ RequestStatus a1,
+ const ServiceDescription& a2) {
+ DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
+ PostToUIThread(base::Bind(&Base::RunCallback, proxy,
+ base::Bind(callback, a1, a2)));
+ }
+
+ std::string service_name_;
+ DISALLOW_COPY_AND_ASSIGN(ServiceResolverProxy);
+};
+
+class LocalDomainResolverProxy : public ProxyBase<LocalDomainResolver> {
+ public:
+ LocalDomainResolverProxy(
+ ServiceDiscoveryClientMdns* client_mdns,
+ const std::string& domain,
+ net::AddressFamily address_family,
+ const LocalDomainResolver::IPAddressCallback& callback)
+ : ProxyBase(client_mdns) {
+ // It's safe to call |CreateLocalDomainResolver| on UI thread, because
+ // |MDnsClient| is not used there. It's simplify implementation.
+ set_implementation(client()->CreateLocalDomainResolver(
+ domain,
+ address_family,
+ base::Bind(
+ &LocalDomainResolverProxy::OnCallback, GetWeakPtr(), callback)));
+ }
+
+ // LocalDomainResolver methods.
+ virtual void Start() OVERRIDE {
+ if (implementation())
+ PostToMdnsThread(base::Bind(&LocalDomainResolver::Start,
+ base::Unretained(implementation())));
+ };
+
+ private:
+ static void OnCallback(const WeakPtr& proxy,
+ const LocalDomainResolver::IPAddressCallback& callback,
+ bool a1,
+ const net::IPAddressNumber& a2,
+ const net::IPAddressNumber& a3) {
+ DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
+ PostToUIThread(base::Bind(&Base::RunCallback, proxy,
+ base::Bind(callback, a1, a2, a3)));
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(LocalDomainResolverProxy);
+};
+
+} // namespace
+
+ServiceDiscoveryClientMdns::ServiceDiscoveryClientMdns()
+ : mdns_runner_(
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO)),
+ restart_attempts_(0),
+ need_dalay_mdns_tasks_(true),
+ weak_ptr_factory_(this) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
+ StartNewClient();
+}
+
+scoped_ptr<ServiceWatcher> ServiceDiscoveryClientMdns::CreateServiceWatcher(
+ const std::string& service_type,
+ const ServiceWatcher::UpdatedCallback& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ return scoped_ptr<ServiceWatcher>(
+ new ServiceWatcherProxy(this, service_type, callback));
+}
+
+scoped_ptr<ServiceResolver> ServiceDiscoveryClientMdns::CreateServiceResolver(
+ const std::string& service_name,
+ const ServiceResolver::ResolveCompleteCallback& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ return scoped_ptr<ServiceResolver>(
+ new ServiceResolverProxy(this, service_name, callback));
+}
+
+scoped_ptr<LocalDomainResolver>
+ServiceDiscoveryClientMdns::CreateLocalDomainResolver(
+ const std::string& domain,
+ net::AddressFamily address_family,
+ const LocalDomainResolver::IPAddressCallback& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ return scoped_ptr<LocalDomainResolver>(
+ new LocalDomainResolverProxy(this, domain, address_family, callback));
+}
+
+ServiceDiscoveryClientMdns::~ServiceDiscoveryClientMdns() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
+ DCHECK(proxies_.empty());
+ Reset();
+}
+
+void ServiceDiscoveryClientMdns::OnNetworkChanged(
+ net::NetworkChangeNotifier::ConnectionType type) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ // Only network changes resets counter.
+ restart_attempts_ = 0;
+ ScheduleStartNewClient();
+}
+
+void ServiceDiscoveryClientMdns::ScheduleStartNewClient() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ // Reset pointer to abort another restart request, if already scheduled.
+ weak_ptr_factory_.InvalidateWeakPtrs();
+ if (restart_attempts_ < kMaxRestartAttempts) {
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&ServiceDiscoveryClientMdns::StartNewClient,
+ weak_ptr_factory_.GetWeakPtr()),
+ base::TimeDelta::FromSeconds(
+ kRestartDelayOnNetworkChangeSeconds * (1 << restart_attempts_)));
+ } else {
+ ReportSuccess();
+ }
+}
+
+void ServiceDiscoveryClientMdns::StartNewClient() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ ++restart_attempts_;
+ Reset();
+ mdns_.reset(net::MDnsClient::CreateDefault().release());
+ client_.reset(new ServiceDiscoveryClientImpl(mdns_.get()));
+ BrowserThread::PostTaskAndReplyWithResult(
+ BrowserThread::FILE,
+ FROM_HERE,
+ base::Bind(&net::GetMDnsInterfacesToBind),
+ base::Bind(&ServiceDiscoveryClientMdns::OnInterfaceListReady,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void ServiceDiscoveryClientMdns::OnInterfaceListReady(
+ const net::InterfaceIndexFamilyList& interfaces) {
+ mdns_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&InitMdns,
+ base::Bind(&ServiceDiscoveryClientMdns::OnMdnsInitialized,
+ weak_ptr_factory_.GetWeakPtr()),
+ interfaces,
+ base::Unretained(mdns_.get())));
+ // Initialization is posted, no need to delay tasks.
+ need_dalay_mdns_tasks_ = false;
+ for (size_t i = 0; i < delayed_tasks_.size(); ++i)
+ mdns_runner_->PostTask(FROM_HERE, delayed_tasks_[i]);
+ delayed_tasks_.clear();
+}
+
+void ServiceDiscoveryClientMdns::OnMdnsInitialized(bool success) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (!success) {
+ ScheduleStartNewClient();
+ return;
+ }
+ ReportSuccess();
+
+ std::set<Proxy*> tmp_proxies(proxies_);
+ std::for_each(tmp_proxies.begin(), tmp_proxies.end(),
+ std::mem_fun(&Proxy::OnNewMdnsReady));
+}
+
+void ServiceDiscoveryClientMdns::ReportSuccess() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ UMA_HISTOGRAM_COUNTS_100("LocalDiscovery.ClientRestartAttempts",
+ restart_attempts_);
+}
+
+void ServiceDiscoveryClientMdns::Reset() {
+ need_dalay_mdns_tasks_ = true;
+ delayed_tasks_.clear();
+
+ weak_ptr_factory_.InvalidateWeakPtrs();
+
+ std::for_each(proxies_.begin(), proxies_.end(),
+ std::mem_fun(&Proxy::OnMdnsDestroy));
+ if (client_)
+ mdns_runner_->DeleteSoon(FROM_HERE, client_.release());
+ if (mdns_)
+ mdns_runner_->DeleteSoon(FROM_HERE, mdns_.release());
+}
+
+bool ServiceDiscoveryClientMdns::PostToMdnsThread(const base::Closure& task) {
+ // The first task on IO thread for each |mdns_| instance must be |InitMdns|.
+ // |OnInterfaceListReady| could be delayed by |GetMDnsInterfacesToBind|
+ // running on FILE thread, so |PostToMdnsThread| could to post task for
+ // |mdns_| that is not posted initialization for.
+ if (!need_dalay_mdns_tasks_)
+ return mdns_runner_->PostTask(FROM_HERE, task);
+ delayed_tasks_.push_back(task);
+ return true;
+}
+
+} // namespace local_discovery
« no previous file with comments | « chrome/browser/local_discovery/service_discovery_client_mdns.h ('k') | chrome/chrome_browser.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698