Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / local_discovery / service_discovery_client_mdns.cc
1 // Copyright 2014 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/local_discovery/service_discovery_client_mdns.h"
6
7 #include "base/memory/scoped_vector.h"
8 #include "base/metrics/histogram.h"
9 #include "chrome/common/local_discovery/service_discovery_client_impl.h"
10 #include "content/public/browser/browser_thread.h"
11 #include "net/dns/mdns_client.h"
12 #include "net/udp/datagram_server_socket.h"
13
14 namespace local_discovery {
15
16 using content::BrowserThread;
17
18 // Base class for objects returned by ServiceDiscoveryClient implementation.
19 // Handles interaction of client code on UI thread end net code on mdns thread.
20 class ServiceDiscoveryClientMdns::Proxy {
21  public:
22   typedef base::WeakPtr<Proxy> WeakPtr;
23
24   explicit Proxy(ServiceDiscoveryClientMdns* client)
25       : client_(client),
26         weak_ptr_factory_(this) {
27     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
28     client_->proxies_.insert(this);
29   }
30
31   virtual ~Proxy() {
32     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
33     client_->proxies_.erase(this);
34   }
35
36   // Notify proxies that mDNS layer is going to be destroyed.
37   virtual void OnMdnsDestroy() = 0;
38
39   // Notify proxies that new mDNS instance is ready.
40   virtual void OnNewMdnsReady() {}
41
42   // Run callback using this method to abort callback if instance of |Proxy|
43   // is deleted.
44   void RunCallback(const base::Closure& callback) {
45     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
46     callback.Run();
47   }
48
49  protected:
50   bool PostToMdnsThread(const base::Closure& task) {
51     return client_->PostToMdnsThread(task);
52   }
53
54   static bool PostToUIThread(const base::Closure& task) {
55     return BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, task);
56   }
57
58   ServiceDiscoveryClient* client() {
59     return client_->client_.get();
60   }
61
62   WeakPtr GetWeakPtr() {
63     return weak_ptr_factory_.GetWeakPtr();
64   }
65
66   template<class T>
67   void DeleteOnMdnsThread(T* t) {
68     if (!t)
69       return;
70     if (!client_->mdns_runner_->DeleteSoon(FROM_HERE, t))
71       delete t;
72   }
73
74  private:
75   scoped_refptr<ServiceDiscoveryClientMdns> client_;
76   base::WeakPtrFactory<Proxy> weak_ptr_factory_;
77
78   DISALLOW_COPY_AND_ASSIGN(Proxy);
79 };
80
81 namespace {
82
83 const int kMaxRestartAttempts = 10;
84 const int kRestartDelayOnNetworkChangeSeconds = 3;
85
86 typedef base::Callback<void(bool)> MdnsInitCallback;
87
88 class SocketFactory : public net::MDnsSocketFactory {
89  public:
90   explicit SocketFactory(const net::InterfaceIndexFamilyList& interfaces)
91       : interfaces_(interfaces) {}
92
93   // net::MDnsSocketFactory implementation:
94   virtual void CreateSockets(
95       ScopedVector<net::DatagramServerSocket>* sockets) OVERRIDE {
96     for (size_t i = 0; i < interfaces_.size(); ++i) {
97       DCHECK(interfaces_[i].second == net::ADDRESS_FAMILY_IPV4 ||
98              interfaces_[i].second == net::ADDRESS_FAMILY_IPV6);
99       scoped_ptr<net::DatagramServerSocket> socket(
100           CreateAndBindMDnsSocket(interfaces_[i].second, interfaces_[i].first));
101       if (socket)
102         sockets->push_back(socket.release());
103     }
104   }
105
106  private:
107   net::InterfaceIndexFamilyList interfaces_;
108 };
109
110 void InitMdns(const MdnsInitCallback& on_initialized,
111               const net::InterfaceIndexFamilyList& interfaces,
112               net::MDnsClient* mdns) {
113   SocketFactory socket_factory(interfaces);
114   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
115                           base::Bind(on_initialized,
116                                      mdns->StartListening(&socket_factory)));
117 }
118
119 template<class T>
120 class ProxyBase : public ServiceDiscoveryClientMdns::Proxy, public T {
121  public:
122   typedef base::WeakPtr<Proxy> WeakPtr;
123   typedef ProxyBase<T> Base;
124
125   explicit ProxyBase(ServiceDiscoveryClientMdns* client)
126       : Proxy(client) {
127   }
128
129   virtual ~ProxyBase() {
130     DeleteOnMdnsThread(implementation_.release());
131   }
132
133   virtual void OnMdnsDestroy() OVERRIDE {
134     DeleteOnMdnsThread(implementation_.release());
135   };
136
137  protected:
138   void set_implementation(scoped_ptr<T> implementation) {
139     implementation_ = implementation.Pass();
140   }
141
142   T* implementation()  const {
143     return implementation_.get();
144   }
145
146  private:
147   scoped_ptr<T> implementation_;
148   DISALLOW_COPY_AND_ASSIGN(ProxyBase);
149 };
150
151 class ServiceWatcherProxy : public ProxyBase<ServiceWatcher> {
152  public:
153   ServiceWatcherProxy(ServiceDiscoveryClientMdns* client_mdns,
154                       const std::string& service_type,
155                       const ServiceWatcher::UpdatedCallback& callback)
156       : ProxyBase(client_mdns),
157         service_type_(service_type),
158         callback_(callback) {
159     // It's safe to call |CreateServiceWatcher| on UI thread, because
160     // |MDnsClient| is not used there. It's simplify implementation.
161     set_implementation(client()->CreateServiceWatcher(
162         service_type,
163         base::Bind(&ServiceWatcherProxy::OnCallback, GetWeakPtr(), callback)));
164   }
165
166   // ServiceWatcher methods.
167   virtual void Start() OVERRIDE {
168     if (implementation())
169       PostToMdnsThread(base::Bind(&ServiceWatcher::Start,
170                                   base::Unretained(implementation())));
171   }
172
173   virtual void DiscoverNewServices(bool force_update) OVERRIDE {
174     if (implementation())
175       PostToMdnsThread(base::Bind(&ServiceWatcher::DiscoverNewServices,
176                                   base::Unretained(implementation()),
177                                   force_update));
178   }
179
180   virtual void SetActivelyRefreshServices(
181       bool actively_refresh_services) OVERRIDE {
182     if (implementation())
183       PostToMdnsThread(base::Bind(&ServiceWatcher::SetActivelyRefreshServices,
184                                   base::Unretained(implementation()),
185                                   actively_refresh_services));
186   }
187
188   virtual std::string GetServiceType() const OVERRIDE {
189     return service_type_;
190   }
191
192   virtual void OnNewMdnsReady() OVERRIDE {
193     if (!implementation())
194       callback_.Run(ServiceWatcher::UPDATE_INVALIDATED, "");
195   }
196
197  private:
198   static void OnCallback(const WeakPtr& proxy,
199                          const ServiceWatcher::UpdatedCallback& callback,
200                          UpdateType a1,
201                          const std::string& a2) {
202     DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
203     PostToUIThread(base::Bind(&Base::RunCallback, proxy,
204                               base::Bind(callback, a1, a2)));
205   }
206   std::string service_type_;
207   ServiceWatcher::UpdatedCallback callback_;
208   DISALLOW_COPY_AND_ASSIGN(ServiceWatcherProxy);
209 };
210
211 class ServiceResolverProxy : public ProxyBase<ServiceResolver> {
212  public:
213   ServiceResolverProxy(ServiceDiscoveryClientMdns* client_mdns,
214                        const std::string& service_name,
215                        const ServiceResolver::ResolveCompleteCallback& callback)
216       : ProxyBase(client_mdns),
217         service_name_(service_name) {
218     // It's safe to call |CreateServiceResolver| on UI thread, because
219     // |MDnsClient| is not used there. It's simplify implementation.
220     set_implementation(client()->CreateServiceResolver(
221         service_name,
222         base::Bind(&ServiceResolverProxy::OnCallback, GetWeakPtr(), callback)));
223   }
224
225   // ServiceResolver methods.
226   virtual void StartResolving() OVERRIDE {
227     if (implementation())
228       PostToMdnsThread(base::Bind(&ServiceResolver::StartResolving,
229                                   base::Unretained(implementation())));
230   };
231
232   virtual std::string GetName() const OVERRIDE {
233     return service_name_;
234   }
235
236  private:
237   static void OnCallback(
238       const WeakPtr& proxy,
239       const ServiceResolver::ResolveCompleteCallback& callback,
240       RequestStatus a1,
241       const ServiceDescription& a2) {
242     DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
243     PostToUIThread(base::Bind(&Base::RunCallback, proxy,
244                               base::Bind(callback, a1, a2)));
245   }
246
247   std::string service_name_;
248   DISALLOW_COPY_AND_ASSIGN(ServiceResolverProxy);
249 };
250
251 class LocalDomainResolverProxy : public ProxyBase<LocalDomainResolver> {
252  public:
253   LocalDomainResolverProxy(
254       ServiceDiscoveryClientMdns* client_mdns,
255       const std::string& domain,
256       net::AddressFamily address_family,
257       const LocalDomainResolver::IPAddressCallback& callback)
258       : ProxyBase(client_mdns) {
259     // It's safe to call |CreateLocalDomainResolver| on UI thread, because
260     // |MDnsClient| is not used there. It's simplify implementation.
261     set_implementation(client()->CreateLocalDomainResolver(
262         domain,
263         address_family,
264         base::Bind(
265             &LocalDomainResolverProxy::OnCallback, GetWeakPtr(), callback)));
266   }
267
268   // LocalDomainResolver methods.
269   virtual void Start() OVERRIDE {
270     if (implementation())
271       PostToMdnsThread(base::Bind(&LocalDomainResolver::Start,
272                                   base::Unretained(implementation())));
273   };
274
275  private:
276   static void OnCallback(const WeakPtr& proxy,
277                          const LocalDomainResolver::IPAddressCallback& callback,
278                          bool a1,
279                          const net::IPAddressNumber& a2,
280                          const net::IPAddressNumber& a3) {
281     DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
282     PostToUIThread(base::Bind(&Base::RunCallback, proxy,
283                               base::Bind(callback, a1, a2, a3)));
284   }
285
286   DISALLOW_COPY_AND_ASSIGN(LocalDomainResolverProxy);
287 };
288
289 }  // namespace
290
291 ServiceDiscoveryClientMdns::ServiceDiscoveryClientMdns()
292     : mdns_runner_(
293           BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO)),
294       restart_attempts_(0),
295       need_dalay_mdns_tasks_(true),
296       weak_ptr_factory_(this) {
297   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
298   net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
299   StartNewClient();
300 }
301
302 scoped_ptr<ServiceWatcher> ServiceDiscoveryClientMdns::CreateServiceWatcher(
303     const std::string& service_type,
304     const ServiceWatcher::UpdatedCallback& callback) {
305   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
306   return scoped_ptr<ServiceWatcher>(
307       new ServiceWatcherProxy(this, service_type, callback));
308 }
309
310 scoped_ptr<ServiceResolver> ServiceDiscoveryClientMdns::CreateServiceResolver(
311     const std::string& service_name,
312     const ServiceResolver::ResolveCompleteCallback& callback) {
313   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
314   return scoped_ptr<ServiceResolver>(
315       new ServiceResolverProxy(this, service_name, callback));
316 }
317
318 scoped_ptr<LocalDomainResolver>
319 ServiceDiscoveryClientMdns::CreateLocalDomainResolver(
320     const std::string& domain,
321     net::AddressFamily address_family,
322     const LocalDomainResolver::IPAddressCallback& callback) {
323   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
324   return scoped_ptr<LocalDomainResolver>(
325       new LocalDomainResolverProxy(this, domain, address_family, callback));
326 }
327
328 ServiceDiscoveryClientMdns::~ServiceDiscoveryClientMdns() {
329   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
330   net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
331   DCHECK(proxies_.empty());
332   Reset();
333 }
334
335 void ServiceDiscoveryClientMdns::OnNetworkChanged(
336     net::NetworkChangeNotifier::ConnectionType type) {
337   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
338   // Only network changes resets counter.
339   restart_attempts_ = 0;
340   ScheduleStartNewClient();
341 }
342
343 void ServiceDiscoveryClientMdns::ScheduleStartNewClient() {
344   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
345   // Reset pointer to abort another restart request, if already scheduled.
346   weak_ptr_factory_.InvalidateWeakPtrs();
347   if (restart_attempts_ < kMaxRestartAttempts) {
348     base::MessageLoop::current()->PostDelayedTask(
349         FROM_HERE,
350         base::Bind(&ServiceDiscoveryClientMdns::StartNewClient,
351                    weak_ptr_factory_.GetWeakPtr()),
352         base::TimeDelta::FromSeconds(
353             kRestartDelayOnNetworkChangeSeconds * (1 << restart_attempts_)));
354   } else {
355     ReportSuccess();
356   }
357 }
358
359 void ServiceDiscoveryClientMdns::StartNewClient() {
360   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
361   ++restart_attempts_;
362   Reset();
363   mdns_.reset(net::MDnsClient::CreateDefault().release());
364   client_.reset(new ServiceDiscoveryClientImpl(mdns_.get()));
365   BrowserThread::PostTaskAndReplyWithResult(
366       BrowserThread::FILE,
367       FROM_HERE,
368       base::Bind(&net::GetMDnsInterfacesToBind),
369       base::Bind(&ServiceDiscoveryClientMdns::OnInterfaceListReady,
370                  weak_ptr_factory_.GetWeakPtr()));
371 }
372
373 void ServiceDiscoveryClientMdns::OnInterfaceListReady(
374     const net::InterfaceIndexFamilyList& interfaces) {
375   mdns_runner_->PostTask(
376       FROM_HERE,
377       base::Bind(&InitMdns,
378                  base::Bind(&ServiceDiscoveryClientMdns::OnMdnsInitialized,
379                             weak_ptr_factory_.GetWeakPtr()),
380                  interfaces,
381                  base::Unretained(mdns_.get())));
382   // Initialization is posted, no need to delay tasks.
383   need_dalay_mdns_tasks_ = false;
384   for (size_t i = 0; i < delayed_tasks_.size(); ++i)
385     mdns_runner_->PostTask(FROM_HERE, delayed_tasks_[i]);
386   delayed_tasks_.clear();
387 }
388
389 void ServiceDiscoveryClientMdns::OnMdnsInitialized(bool success) {
390   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
391   if (!success) {
392     ScheduleStartNewClient();
393     return;
394   }
395   ReportSuccess();
396
397   std::set<Proxy*> tmp_proxies(proxies_);
398   std::for_each(tmp_proxies.begin(), tmp_proxies.end(),
399                 std::mem_fun(&Proxy::OnNewMdnsReady));
400 }
401
402 void ServiceDiscoveryClientMdns::ReportSuccess() {
403   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
404   UMA_HISTOGRAM_COUNTS_100("LocalDiscovery.ClientRestartAttempts",
405                            restart_attempts_);
406 }
407
408 void ServiceDiscoveryClientMdns::Reset() {
409   need_dalay_mdns_tasks_ = true;
410   delayed_tasks_.clear();
411
412   weak_ptr_factory_.InvalidateWeakPtrs();
413
414   std::for_each(proxies_.begin(), proxies_.end(),
415                 std::mem_fun(&Proxy::OnMdnsDestroy));
416   if (client_)
417     mdns_runner_->DeleteSoon(FROM_HERE, client_.release());
418   if (mdns_)
419     mdns_runner_->DeleteSoon(FROM_HERE, mdns_.release());
420 }
421
422 bool ServiceDiscoveryClientMdns::PostToMdnsThread(const base::Closure& task) {
423   // The first task on IO thread for each |mdns_| instance must be |InitMdns|.
424   // |OnInterfaceListReady| could be delayed by |GetMDnsInterfacesToBind|
425   // running on FILE thread, so |PostToMdnsThread| could to post task for
426   // |mdns_| that is not posted initialization for.
427   if (!need_dalay_mdns_tasks_)
428     return mdns_runner_->PostTask(FROM_HERE, task);
429   delayed_tasks_.push_back(task);
430   return true;
431 }
432
433 }  // namespace local_discovery