Add ICU message format support
[chromium-blink-merge.git] / chrome / browser / local_discovery / service_discovery_client_mdns.cc
blob08ca55c9e0a63c3f67d2b3f45724c71c7dfd7f79
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.
5 #include "chrome/browser/local_discovery/service_discovery_client_mdns.h"
7 #include "base/location.h"
8 #include "base/memory/scoped_vector.h"
9 #include "base/metrics/histogram.h"
10 #include "base/single_thread_task_runner.h"
11 #include "base/thread_task_runner_handle.h"
12 #include "chrome/common/local_discovery/service_discovery_client_impl.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "net/dns/mdns_client.h"
15 #include "net/udp/datagram_server_socket.h"
17 namespace local_discovery {
19 using content::BrowserThread;
21 // Base class for objects returned by ServiceDiscoveryClient implementation.
22 // Handles interaction of client code on UI thread end net code on mdns thread.
23 class ServiceDiscoveryClientMdns::Proxy {
24 public:
25 typedef base::WeakPtr<Proxy> WeakPtr;
27 explicit Proxy(ServiceDiscoveryClientMdns* client)
28 : client_(client),
29 weak_ptr_factory_(this) {
30 DCHECK_CURRENTLY_ON(BrowserThread::UI);
31 client_->proxies_.AddObserver(this);
34 virtual ~Proxy() {
35 DCHECK_CURRENTLY_ON(BrowserThread::UI);
36 client_->proxies_.RemoveObserver(this);
39 // Returns true if object is not yet shutdown.
40 virtual bool IsValid() = 0;
42 // Notifies proxies that mDNS layer is going to be destroyed.
43 virtual void OnMdnsDestroy() = 0;
45 // Notifies proxies that new mDNS instance is ready.
46 virtual void OnNewMdnsReady() {
47 DCHECK(!client_->need_dalay_mdns_tasks_);
48 if (IsValid()) {
49 for (size_t i = 0; i < delayed_tasks_.size(); ++i)
50 client_->mdns_runner_->PostTask(FROM_HERE, delayed_tasks_[i]);
52 delayed_tasks_.clear();
55 // Runs callback using this method to abort callback if instance of |Proxy|
56 // is deleted.
57 void RunCallback(const base::Closure& callback) {
58 DCHECK_CURRENTLY_ON(BrowserThread::UI);
59 callback.Run();
62 protected:
63 void PostToMdnsThread(const base::Closure& task) {
64 DCHECK(IsValid());
65 // The first task on IO thread for each |mdns_| instance must be |InitMdns|.
66 // |OnInterfaceListReady| could be delayed by |GetMDnsInterfacesToBind|
67 // running on FILE thread, so |PostToMdnsThread| could be called to post
68 // task for |mdns_| that is not initialized yet.
69 if (!client_->need_dalay_mdns_tasks_) {
70 client_->mdns_runner_->PostTask(FROM_HERE, task);
71 return;
73 delayed_tasks_.push_back(task);
76 static bool PostToUIThread(const base::Closure& task) {
77 return BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, task);
80 ServiceDiscoveryClient* client() {
81 return client_->client_.get();
84 WeakPtr GetWeakPtr() {
85 return weak_ptr_factory_.GetWeakPtr();
88 template<class T>
89 void DeleteOnMdnsThread(T* t) {
90 if (!t)
91 return;
92 if (!client_->mdns_runner_->DeleteSoon(FROM_HERE, t))
93 delete t;
96 private:
97 scoped_refptr<ServiceDiscoveryClientMdns> client_;
98 // Delayed |mdns_runner_| tasks.
99 std::vector<base::Closure> delayed_tasks_;
100 base::WeakPtrFactory<Proxy> weak_ptr_factory_;
101 DISALLOW_COPY_AND_ASSIGN(Proxy);
104 namespace {
106 const int kMaxRestartAttempts = 10;
107 const int kRestartDelayOnNetworkChangeSeconds = 3;
109 typedef base::Callback<void(bool)> MdnsInitCallback;
111 class SocketFactory : public net::MDnsSocketFactory {
112 public:
113 explicit SocketFactory(const net::InterfaceIndexFamilyList& interfaces)
114 : interfaces_(interfaces) {}
116 // net::MDnsSocketFactory implementation:
117 void CreateSockets(
118 ScopedVector<net::DatagramServerSocket>* sockets) override {
119 for (size_t i = 0; i < interfaces_.size(); ++i) {
120 DCHECK(interfaces_[i].second == net::ADDRESS_FAMILY_IPV4 ||
121 interfaces_[i].second == net::ADDRESS_FAMILY_IPV6);
122 scoped_ptr<net::DatagramServerSocket> socket(
123 CreateAndBindMDnsSocket(interfaces_[i].second, interfaces_[i].first));
124 if (socket)
125 sockets->push_back(socket.release());
129 private:
130 net::InterfaceIndexFamilyList interfaces_;
133 void InitMdns(const MdnsInitCallback& on_initialized,
134 const net::InterfaceIndexFamilyList& interfaces,
135 net::MDnsClient* mdns) {
136 SocketFactory socket_factory(interfaces);
137 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
138 base::Bind(on_initialized,
139 mdns->StartListening(&socket_factory)));
142 template<class T>
143 class ProxyBase : public ServiceDiscoveryClientMdns::Proxy, public T {
144 public:
145 typedef ProxyBase<T> Base;
147 explicit ProxyBase(ServiceDiscoveryClientMdns* client)
148 : Proxy(client) {
151 ~ProxyBase() override {
152 DeleteOnMdnsThread(implementation_.release());
155 bool IsValid() override {
156 return !!implementation();
159 void OnMdnsDestroy() override {
160 DeleteOnMdnsThread(implementation_.release());
163 protected:
164 void set_implementation(scoped_ptr<T> implementation) {
165 implementation_ = implementation.Pass();
168 T* implementation() const {
169 return implementation_.get();
172 private:
173 scoped_ptr<T> implementation_;
174 DISALLOW_COPY_AND_ASSIGN(ProxyBase);
177 class ServiceWatcherProxy : public ProxyBase<ServiceWatcher> {
178 public:
179 ServiceWatcherProxy(ServiceDiscoveryClientMdns* client_mdns,
180 const std::string& service_type,
181 const ServiceWatcher::UpdatedCallback& callback)
182 : ProxyBase(client_mdns),
183 service_type_(service_type),
184 callback_(callback) {
185 // It's safe to call |CreateServiceWatcher| on UI thread, because
186 // |MDnsClient| is not used there. It's simplify implementation.
187 set_implementation(client()->CreateServiceWatcher(
188 service_type,
189 base::Bind(&ServiceWatcherProxy::OnCallback, GetWeakPtr(), callback)));
192 // ServiceWatcher methods.
193 void Start() override {
194 if (implementation()) {
195 PostToMdnsThread(base::Bind(&ServiceWatcher::Start,
196 base::Unretained(implementation())));
200 void DiscoverNewServices(bool force_update) override {
201 if (implementation()) {
202 PostToMdnsThread(base::Bind(&ServiceWatcher::DiscoverNewServices,
203 base::Unretained(implementation()),
204 force_update));
208 void SetActivelyRefreshServices(bool actively_refresh_services) override {
209 if (implementation()) {
210 PostToMdnsThread(base::Bind(&ServiceWatcher::SetActivelyRefreshServices,
211 base::Unretained(implementation()),
212 actively_refresh_services));
216 std::string GetServiceType() const override { return service_type_; }
218 void OnNewMdnsReady() override {
219 ProxyBase<ServiceWatcher>::OnNewMdnsReady();
220 if (!implementation())
221 callback_.Run(ServiceWatcher::UPDATE_INVALIDATED, "");
224 private:
225 static void OnCallback(const WeakPtr& proxy,
226 const ServiceWatcher::UpdatedCallback& callback,
227 UpdateType a1,
228 const std::string& a2) {
229 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
230 PostToUIThread(base::Bind(&Base::RunCallback, proxy,
231 base::Bind(callback, a1, a2)));
233 std::string service_type_;
234 ServiceWatcher::UpdatedCallback callback_;
235 DISALLOW_COPY_AND_ASSIGN(ServiceWatcherProxy);
238 class ServiceResolverProxy : public ProxyBase<ServiceResolver> {
239 public:
240 ServiceResolverProxy(ServiceDiscoveryClientMdns* client_mdns,
241 const std::string& service_name,
242 const ServiceResolver::ResolveCompleteCallback& callback)
243 : ProxyBase(client_mdns),
244 service_name_(service_name) {
245 // It's safe to call |CreateServiceResolver| on UI thread, because
246 // |MDnsClient| is not used there. It's simplify implementation.
247 set_implementation(client()->CreateServiceResolver(
248 service_name,
249 base::Bind(&ServiceResolverProxy::OnCallback, GetWeakPtr(), callback)));
252 // ServiceResolver methods.
253 void StartResolving() override {
254 if (implementation()) {
255 PostToMdnsThread(base::Bind(&ServiceResolver::StartResolving,
256 base::Unretained(implementation())));
260 std::string GetName() const override { return service_name_; }
262 private:
263 static void OnCallback(
264 const WeakPtr& proxy,
265 const ServiceResolver::ResolveCompleteCallback& callback,
266 RequestStatus a1,
267 const ServiceDescription& a2) {
268 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
269 PostToUIThread(base::Bind(&Base::RunCallback, proxy,
270 base::Bind(callback, a1, a2)));
273 std::string service_name_;
274 DISALLOW_COPY_AND_ASSIGN(ServiceResolverProxy);
277 class LocalDomainResolverProxy : public ProxyBase<LocalDomainResolver> {
278 public:
279 LocalDomainResolverProxy(
280 ServiceDiscoveryClientMdns* client_mdns,
281 const std::string& domain,
282 net::AddressFamily address_family,
283 const LocalDomainResolver::IPAddressCallback& callback)
284 : ProxyBase(client_mdns) {
285 // It's safe to call |CreateLocalDomainResolver| on UI thread, because
286 // |MDnsClient| is not used there. It's simplify implementation.
287 set_implementation(client()->CreateLocalDomainResolver(
288 domain,
289 address_family,
290 base::Bind(
291 &LocalDomainResolverProxy::OnCallback, GetWeakPtr(), callback)));
294 // LocalDomainResolver methods.
295 void Start() override {
296 if (implementation()) {
297 PostToMdnsThread(base::Bind(&LocalDomainResolver::Start,
298 base::Unretained(implementation())));
302 private:
303 static void OnCallback(const WeakPtr& proxy,
304 const LocalDomainResolver::IPAddressCallback& callback,
305 bool a1,
306 const net::IPAddressNumber& a2,
307 const net::IPAddressNumber& a3) {
308 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
309 PostToUIThread(base::Bind(&Base::RunCallback, proxy,
310 base::Bind(callback, a1, a2, a3)));
313 DISALLOW_COPY_AND_ASSIGN(LocalDomainResolverProxy);
316 } // namespace
318 ServiceDiscoveryClientMdns::ServiceDiscoveryClientMdns()
319 : mdns_runner_(
320 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO)),
321 restart_attempts_(0),
322 need_dalay_mdns_tasks_(true),
323 weak_ptr_factory_(this) {
324 DCHECK_CURRENTLY_ON(BrowserThread::UI);
325 net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
326 StartNewClient();
329 scoped_ptr<ServiceWatcher> ServiceDiscoveryClientMdns::CreateServiceWatcher(
330 const std::string& service_type,
331 const ServiceWatcher::UpdatedCallback& callback) {
332 DCHECK_CURRENTLY_ON(BrowserThread::UI);
333 return scoped_ptr<ServiceWatcher>(
334 new ServiceWatcherProxy(this, service_type, callback));
337 scoped_ptr<ServiceResolver> ServiceDiscoveryClientMdns::CreateServiceResolver(
338 const std::string& service_name,
339 const ServiceResolver::ResolveCompleteCallback& callback) {
340 DCHECK_CURRENTLY_ON(BrowserThread::UI);
341 return scoped_ptr<ServiceResolver>(
342 new ServiceResolverProxy(this, service_name, callback));
345 scoped_ptr<LocalDomainResolver>
346 ServiceDiscoveryClientMdns::CreateLocalDomainResolver(
347 const std::string& domain,
348 net::AddressFamily address_family,
349 const LocalDomainResolver::IPAddressCallback& callback) {
350 DCHECK_CURRENTLY_ON(BrowserThread::UI);
351 return scoped_ptr<LocalDomainResolver>(
352 new LocalDomainResolverProxy(this, domain, address_family, callback));
355 ServiceDiscoveryClientMdns::~ServiceDiscoveryClientMdns() {
356 DCHECK_CURRENTLY_ON(BrowserThread::UI);
357 net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
358 DestroyMdns();
361 void ServiceDiscoveryClientMdns::OnNetworkChanged(
362 net::NetworkChangeNotifier::ConnectionType type) {
363 DCHECK_CURRENTLY_ON(BrowserThread::UI);
364 // Only network changes resets counter.
365 restart_attempts_ = 0;
366 ScheduleStartNewClient();
369 void ServiceDiscoveryClientMdns::ScheduleStartNewClient() {
370 DCHECK_CURRENTLY_ON(BrowserThread::UI);
371 OnBeforeMdnsDestroy();
372 if (restart_attempts_ < kMaxRestartAttempts) {
373 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
374 FROM_HERE, base::Bind(&ServiceDiscoveryClientMdns::StartNewClient,
375 weak_ptr_factory_.GetWeakPtr()),
376 base::TimeDelta::FromSeconds(kRestartDelayOnNetworkChangeSeconds *
377 (1 << restart_attempts_)));
378 } else {
379 ReportSuccess();
383 void ServiceDiscoveryClientMdns::StartNewClient() {
384 DCHECK_CURRENTLY_ON(BrowserThread::UI);
385 ++restart_attempts_;
386 DestroyMdns();
387 mdns_.reset(net::MDnsClient::CreateDefault().release());
388 client_.reset(new ServiceDiscoveryClientImpl(mdns_.get()));
389 BrowserThread::PostTaskAndReplyWithResult(
390 BrowserThread::FILE,
391 FROM_HERE,
392 base::Bind(&net::GetMDnsInterfacesToBind),
393 base::Bind(&ServiceDiscoveryClientMdns::OnInterfaceListReady,
394 weak_ptr_factory_.GetWeakPtr()));
397 void ServiceDiscoveryClientMdns::OnInterfaceListReady(
398 const net::InterfaceIndexFamilyList& interfaces) {
399 mdns_runner_->PostTask(
400 FROM_HERE,
401 base::Bind(&InitMdns,
402 base::Bind(&ServiceDiscoveryClientMdns::OnMdnsInitialized,
403 weak_ptr_factory_.GetWeakPtr()),
404 interfaces,
405 base::Unretained(mdns_.get())));
408 void ServiceDiscoveryClientMdns::OnMdnsInitialized(bool success) {
409 DCHECK_CURRENTLY_ON(BrowserThread::UI);
410 if (!success) {
411 ScheduleStartNewClient();
412 return;
414 ReportSuccess();
416 // Initialization is done, no need to delay tasks.
417 need_dalay_mdns_tasks_ = false;
418 FOR_EACH_OBSERVER(Proxy, proxies_, OnNewMdnsReady());
421 void ServiceDiscoveryClientMdns::ReportSuccess() {
422 DCHECK_CURRENTLY_ON(BrowserThread::UI);
423 UMA_HISTOGRAM_COUNTS_100("LocalDiscovery.ClientRestartAttempts",
424 restart_attempts_);
427 void ServiceDiscoveryClientMdns::OnBeforeMdnsDestroy() {
428 need_dalay_mdns_tasks_ = true;
429 weak_ptr_factory_.InvalidateWeakPtrs();
430 FOR_EACH_OBSERVER(Proxy, proxies_, OnMdnsDestroy());
433 void ServiceDiscoveryClientMdns::DestroyMdns() {
434 OnBeforeMdnsDestroy();
435 // After calling |Proxy::OnMdnsDestroy| all references to client_ and mdns_
436 // should be destroyed.
437 if (client_)
438 mdns_runner_->DeleteSoon(FROM_HERE, client_.release());
439 if (mdns_)
440 mdns_runner_->DeleteSoon(FROM_HERE, mdns_.release());
443 } // namespace local_discovery