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