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

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

Powered by Google App Engine
This is Rietveld 408576698