OLD | NEW |
---|---|
(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 set_implementation(client()->CreateServiceWatcher( | |
Noam Samuel
2014/04/28 20:40:51
Do we want this created on the mdns thread?
Vitaly Buka (NO REVIEWS)
2014/04/28 22:56:31
Updated comment.
On 2014/04/28 20:40:51, Noam Samu
| |
159 service_type, | |
160 base::Bind(&ServiceWatcherProxy::OnCallback, GetWeakPtr(), callback))); | |
161 } | |
162 | |
163 // ServiceWatcher methods. | |
164 virtual void Start() OVERRIDE { | |
165 if (implementation()) | |
166 PostToMdnsThread(base::Bind(&ServiceWatcher::Start, | |
167 base::Unretained(implementation()))); | |
168 } | |
169 | |
170 virtual void DiscoverNewServices(bool force_update) OVERRIDE { | |
171 if (implementation()) | |
172 PostToMdnsThread(base::Bind(&ServiceWatcher::DiscoverNewServices, | |
173 base::Unretained(implementation()), | |
174 force_update)); | |
175 } | |
176 | |
177 virtual void SetActivelyRefreshServices( | |
178 bool actively_refresh_services) OVERRIDE { | |
179 if (implementation()) | |
180 PostToMdnsThread(base::Bind(&ServiceWatcher::SetActivelyRefreshServices, | |
181 base::Unretained(implementation()), | |
182 actively_refresh_services)); | |
183 } | |
184 | |
185 virtual std::string GetServiceType() const OVERRIDE { | |
186 return service_type_; | |
187 } | |
188 | |
189 virtual void OnNewMdnsReady() OVERRIDE { | |
190 if (!implementation()) | |
191 callback_.Run(ServiceWatcher::UPDATE_INVALIDATED, ""); | |
192 } | |
193 | |
194 private: | |
195 static void OnCallback(const WeakPtr& proxy, | |
196 const ServiceWatcher::UpdatedCallback& callback, | |
197 UpdateType a1, | |
198 const std::string& a2) { | |
199 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
200 PostToUIThread(base::Bind(&Base::RunCallback, proxy, | |
201 base::Bind(callback, a1, a2))); | |
202 } | |
203 std::string service_type_; | |
204 ServiceWatcher::UpdatedCallback callback_; | |
205 DISALLOW_COPY_AND_ASSIGN(ServiceWatcherProxy); | |
206 }; | |
207 | |
208 class ServiceResolverProxy : public ProxyBase<ServiceResolver> { | |
209 public: | |
210 ServiceResolverProxy(ServiceDiscoveryClientMdns* client_mdns, | |
211 const std::string& service_name, | |
212 const ServiceResolver::ResolveCompleteCallback& callback) | |
213 : ProxyBase(client_mdns), | |
214 service_name_(service_name) { | |
215 set_implementation(client()->CreateServiceResolver( | |
Noam Samuel
2014/04/28 20:40:51
Ditto to 158
| |
216 service_name, | |
217 base::Bind(&ServiceResolverProxy::OnCallback, GetWeakPtr(), callback))); | |
218 } | |
219 | |
220 // ServiceResolver methods. | |
221 virtual void StartResolving() { | |
222 if (implementation()) | |
223 PostToMdnsThread(base::Bind(&ServiceResolver::StartResolving, | |
224 base::Unretained(implementation()))); | |
225 }; | |
226 | |
227 virtual std::string GetName() const OVERRIDE { | |
228 return service_name_; | |
229 } | |
230 | |
231 private: | |
232 static void OnCallback( | |
233 const WeakPtr& proxy, | |
234 const ServiceResolver::ResolveCompleteCallback& callback, | |
235 RequestStatus a1, | |
236 const ServiceDescription& a2) { | |
237 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
238 PostToUIThread(base::Bind(&Base::RunCallback, proxy, | |
239 base::Bind(callback, a1, a2))); | |
240 } | |
241 | |
242 std::string service_name_; | |
243 DISALLOW_COPY_AND_ASSIGN(ServiceResolverProxy); | |
244 }; | |
245 | |
246 class LocalDomainResolverProxy : public ProxyBase<LocalDomainResolver> { | |
247 public: | |
248 LocalDomainResolverProxy( | |
249 ServiceDiscoveryClientMdns* client_mdns, | |
250 const std::string& domain, | |
251 net::AddressFamily address_family, | |
252 const LocalDomainResolver::IPAddressCallback& callback) | |
253 : ProxyBase(client_mdns) { | |
254 set_implementation(client()->CreateLocalDomainResolver( | |
Noam Samuel
2014/04/28 20:40:51
Ditto to 158
| |
255 domain, | |
256 address_family, | |
257 base::Bind( | |
258 &LocalDomainResolverProxy::OnCallback, GetWeakPtr(), callback))); | |
259 } | |
260 | |
261 // LocalDomainResolver methods. | |
262 virtual void Start() OVERRIDE { | |
263 if (implementation()) | |
264 PostToMdnsThread(base::Bind(&LocalDomainResolver::Start, | |
265 base::Unretained(implementation()))); | |
266 }; | |
267 | |
268 private: | |
269 static void OnCallback(const WeakPtr& proxy, | |
270 const LocalDomainResolver::IPAddressCallback& callback, | |
271 bool a1, | |
272 const net::IPAddressNumber& a2, | |
273 const net::IPAddressNumber& a3) { | |
274 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
275 PostToUIThread(base::Bind(&Base::RunCallback, proxy, | |
276 base::Bind(callback, a1, a2, a3))); | |
277 } | |
278 | |
279 DISALLOW_COPY_AND_ASSIGN(LocalDomainResolverProxy); | |
280 }; | |
281 | |
282 } // namespace | |
283 | |
284 ServiceDiscoveryClientMdns::ServiceDiscoveryClientMdns() | |
285 : mdns_runner_( | |
286 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO)), | |
287 restart_attempts_(0), | |
288 need_dalay_mdns_tasks_(true), | |
289 weak_ptr_factory_(this) { | |
290 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
291 net::NetworkChangeNotifier::AddNetworkChangeObserver(this); | |
292 StartNewClient(); | |
293 } | |
294 | |
295 scoped_ptr<ServiceWatcher> ServiceDiscoveryClientMdns::CreateServiceWatcher( | |
296 const std::string& service_type, | |
297 const ServiceWatcher::UpdatedCallback& callback) { | |
298 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
299 return scoped_ptr<ServiceWatcher>( | |
300 new ServiceWatcherProxy(this, service_type, callback)); | |
301 } | |
302 | |
303 scoped_ptr<ServiceResolver> ServiceDiscoveryClientMdns::CreateServiceResolver( | |
304 const std::string& service_name, | |
305 const ServiceResolver::ResolveCompleteCallback& callback) { | |
306 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
307 return scoped_ptr<ServiceResolver>( | |
308 new ServiceResolverProxy(this, service_name, callback)); | |
309 } | |
310 | |
311 scoped_ptr<LocalDomainResolver> | |
312 ServiceDiscoveryClientMdns::CreateLocalDomainResolver( | |
313 const std::string& domain, | |
314 net::AddressFamily address_family, | |
315 const LocalDomainResolver::IPAddressCallback& callback) { | |
316 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
317 return scoped_ptr<LocalDomainResolver>( | |
318 new LocalDomainResolverProxy(this, domain, address_family, callback)); | |
319 } | |
320 | |
321 ServiceDiscoveryClientMdns::~ServiceDiscoveryClientMdns() { | |
322 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
323 net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this); | |
324 DCHECK(proxies_.empty()); | |
325 Reset(); | |
326 } | |
327 | |
328 void ServiceDiscoveryClientMdns::OnNetworkChanged( | |
329 net::NetworkChangeNotifier::ConnectionType type) { | |
330 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
331 // Only network changes resets counter. | |
332 restart_attempts_ = 0; | |
333 ScheduleStartNewClient(); | |
334 } | |
335 | |
336 void ServiceDiscoveryClientMdns::ScheduleStartNewClient() { | |
337 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
338 // Reset pointer to abort another restart request, if already scheduled. | |
339 weak_ptr_factory_.InvalidateWeakPtrs(); | |
340 if (restart_attempts_ < kMaxRestartAttempts) { | |
341 base::MessageLoop::current()->PostDelayedTask( | |
342 FROM_HERE, | |
343 base::Bind(&ServiceDiscoveryClientMdns::StartNewClient, | |
344 weak_ptr_factory_.GetWeakPtr()), | |
345 base::TimeDelta::FromSeconds( | |
346 kRestartDelayOnNetworkChangeSeconds * (1 << restart_attempts_))); | |
347 } else { | |
348 ReportSuccess(); | |
349 } | |
350 } | |
351 | |
352 void ServiceDiscoveryClientMdns::StartNewClient() { | |
353 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
354 ++restart_attempts_; | |
355 Reset(); | |
356 mdns_.reset(net::MDnsClient::CreateDefault().release()); | |
357 client_.reset(new ServiceDiscoveryClientImpl(mdns_.get())); | |
358 BrowserThread::PostTaskAndReplyWithResult( | |
359 BrowserThread::FILE, | |
360 FROM_HERE, | |
361 base::Bind(&net::GetMDnsInterfacesToBind), | |
362 base::Bind(&ServiceDiscoveryClientMdns::OnInterfaceListReady, | |
363 weak_ptr_factory_.GetWeakPtr())); | |
364 } | |
365 | |
366 void ServiceDiscoveryClientMdns::OnInterfaceListReady( | |
367 const net::InterfaceIndexFamilyList& interfaces) { | |
368 mdns_runner_->PostTask( | |
369 FROM_HERE, | |
370 base::Bind(&InitMdns, | |
371 base::Bind(&ServiceDiscoveryClientMdns::OnMdnsInitialized, | |
372 weak_ptr_factory_.GetWeakPtr()), | |
373 interfaces, | |
374 base::Unretained(mdns_.get()))); | |
375 // Initialization is posted, no need to delay tasks. | |
376 need_dalay_mdns_tasks_ = false; | |
377 for (size_t i = 0; i < delayed_tasks_.size(); ++i) | |
378 mdns_runner_->PostTask(FROM_HERE, delayed_tasks_[i]); | |
379 delayed_tasks_.clear(); | |
380 } | |
381 | |
382 void ServiceDiscoveryClientMdns::OnMdnsInitialized(bool success) { | |
383 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
384 if (!success) { | |
385 ScheduleStartNewClient(); | |
386 return; | |
387 } | |
388 ReportSuccess(); | |
389 | |
390 std::set<Proxy*> tmp_proxies(proxies_); | |
391 std::for_each(tmp_proxies.begin(), tmp_proxies.end(), | |
392 std::mem_fun(&Proxy::OnNewMdnsReady)); | |
393 } | |
394 | |
395 void ServiceDiscoveryClientMdns::ReportSuccess() { | |
396 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
397 UMA_HISTOGRAM_COUNTS_100("LocalDiscovery.ClientRestartAttempts", | |
398 restart_attempts_ /* +1 to match older data */); | |
Noam Samuel
2014/04/28 20:40:51
Confused. Should we be adding +1 so that the data
Vitaly Buka (NO REVIEWS)
2014/04/28 22:56:31
Outdated comment, it's already produce consistent
| |
399 } | |
400 | |
401 void ServiceDiscoveryClientMdns::Reset() { | |
402 need_dalay_mdns_tasks_ = true; | |
403 delayed_tasks_.clear(); | |
404 | |
405 weak_ptr_factory_.InvalidateWeakPtrs(); | |
406 | |
407 std::for_each(proxies_.begin(), proxies_.end(), | |
408 std::mem_fun(&Proxy::OnMdnsDestroy)); | |
409 if (client_) | |
410 mdns_runner_->DeleteSoon(FROM_HERE, client_.release()); | |
411 if (mdns_) | |
412 mdns_runner_->DeleteSoon(FROM_HERE, mdns_.release()); | |
413 } | |
414 | |
415 bool ServiceDiscoveryClientMdns::PostToMdnsThread(const base::Closure& task) { | |
416 // The first task on IO thread for each |mdns_| instance must be |InitMdns|. | |
417 // |OnInterfaceListReady| could be delayed by |GetMDnsInterfacesToBind| | |
418 // running on FILE thread, so |PostToMdnsThread| could to post task for | |
419 // |mdns_| that is not posted initialization for. | |
420 if (!need_dalay_mdns_tasks_) | |
421 return mdns_runner_->PostTask(FROM_HERE, task); | |
422 delayed_tasks_.push_back(task); | |
423 return true; | |
424 } | |
425 | |
426 } // namespace local_discovery | |
OLD | NEW |