OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/extensions/api/dial/dial_service.h" | 5 #include "chrome/browser/extensions/api/dial/dial_service.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/basictypes.h" | 9 #include "base/basictypes.h" |
10 #include "base/callback.h" | 10 #include "base/callback.h" |
(...skipping 24 matching lines...) Expand all Loading... | |
35 using net::IPEndPoint; | 35 using net::IPEndPoint; |
36 using net::NetworkInterface; | 36 using net::NetworkInterface; |
37 using net::NetworkInterfaceList; | 37 using net::NetworkInterfaceList; |
38 using net::StringIOBuffer; | 38 using net::StringIOBuffer; |
39 using net::UDPSocket; | 39 using net::UDPSocket; |
40 | 40 |
41 namespace extensions { | 41 namespace extensions { |
42 | 42 |
43 namespace { | 43 namespace { |
44 | 44 |
45 // The total number of requests to make. | 45 // The total number of requests to make per discovery cycle. |
46 const int kDialNumRequests = 1; | 46 const int kDialMaxRequests = 4; |
47 | 47 |
48 // The interval to wait between successive requests. | 48 // The interval to wait between successive requests. |
49 const int kDialRequestIntervalMillis = 1000; | 49 const int kDialRequestIntervalMillis = 1000; |
50 | 50 |
51 // The timeout (MX) set for responses. | 51 // The timeout (MX) set for responses. |
52 const int kDialResponseTimeoutSecs = 2; | 52 const int kDialResponseTimeoutSecs = 2; |
53 | 53 |
54 // The multicast IP address for discovery. | 54 // The multicast IP address for discovery. |
55 const char kDialRequestAddress[] = "239.255.255.250"; | 55 const char kDialRequestAddress[] = "239.255.255.250"; |
56 | 56 |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
95 version.Version().c_str(), | 95 version.Version().c_str(), |
96 version.OSType().c_str())); | 96 version.OSType().c_str())); |
97 // 1500 is a good MTU value for most Ethernet LANs. | 97 // 1500 is a good MTU value for most Ethernet LANs. |
98 DCHECK(request.size() <= 1500); | 98 DCHECK(request.size() <= 1500); |
99 return request; | 99 return request; |
100 } | 100 } |
101 | 101 |
102 } // namespace | 102 } // namespace |
103 | 103 |
104 DialServiceImpl::DialServiceImpl(net::NetLog* net_log) | 104 DialServiceImpl::DialServiceImpl(net::NetLog* net_log) |
105 : is_writing_(false), is_reading_(false), discovery_active_(false), | 105 : is_writing_(false), |
106 num_requests_sent_(0) { | 106 is_reading_(false), |
107 discovery_active_(false), | |
108 num_requests_sent_(0), | |
109 max_requests_(kDialMaxRequests), | |
110 finish_delay_(TimeDelta::FromMilliseconds((kDialMaxRequests - 1) * | |
111 kDialRequestIntervalMillis) + | |
justinlin
2013/02/08 08:56:20
Align this with the "(" above.
mark a. foltz
2013/02/08 22:07:58
Done.
| |
112 TimeDelta::FromSeconds(kDialResponseTimeoutSecs)), | |
113 request_interval_(TimeDelta::FromMilliseconds(kDialRequestIntervalMillis)) | |
114 { | |
107 IPAddressNumber address; | 115 IPAddressNumber address; |
108 bool result = net::ParseIPLiteralToNumber(kDialRequestAddress, &address); | 116 bool result = net::ParseIPLiteralToNumber(kDialRequestAddress, &address); |
109 DCHECK(result); | 117 DCHECK(result); |
110 send_address_ = IPEndPoint(address, kDialRequestPort); | 118 send_address_ = IPEndPoint(address, kDialRequestPort); |
111 send_buffer_ = new StringIOBuffer(BuildRequest()); | 119 send_buffer_ = new StringIOBuffer(BuildRequest()); |
112 net_log_ = net_log; | 120 net_log_ = net_log; |
113 net_log_source_.type = net::NetLog::SOURCE_UDP_SOCKET; | 121 net_log_source_.type = net::NetLog::SOURCE_UDP_SOCKET; |
114 net_log_source_.id = net_log_->NextID(); | 122 net_log_source_.id = net_log_->NextID(); |
115 finish_delay_ = TimeDelta::FromMilliseconds((kDialNumRequests - 1) * | |
116 kDialRequestIntervalMillis) + | |
117 TimeDelta::FromSeconds(kDialResponseTimeoutSecs); | |
118 } | 123 } |
119 | 124 |
120 DialServiceImpl::~DialServiceImpl() { | 125 DialServiceImpl::~DialServiceImpl() { |
121 DCHECK(thread_checker_.CalledOnValidThread()); | 126 DCHECK(thread_checker_.CalledOnValidThread()); |
122 } | 127 } |
123 | 128 |
124 void DialServiceImpl::AddObserver(Observer* observer) { | 129 void DialServiceImpl::AddObserver(Observer* observer) { |
125 DCHECK(thread_checker_.CalledOnValidThread()); | 130 DCHECK(thread_checker_.CalledOnValidThread()); |
126 observer_list_.AddObserver(observer); | 131 observer_list_.AddObserver(observer); |
127 } | 132 } |
128 | 133 |
129 void DialServiceImpl::RemoveObserver(Observer* observer) { | 134 void DialServiceImpl::RemoveObserver(Observer* observer) { |
130 DCHECK(thread_checker_.CalledOnValidThread()); | 135 DCHECK(thread_checker_.CalledOnValidThread()); |
131 observer_list_.RemoveObserver(observer); | 136 observer_list_.RemoveObserver(observer); |
132 } | 137 } |
133 | 138 |
134 bool DialServiceImpl::HasObserver(Observer* observer) { | 139 bool DialServiceImpl::HasObserver(Observer* observer) { |
135 DCHECK(thread_checker_.CalledOnValidThread()); | 140 DCHECK(thread_checker_.CalledOnValidThread()); |
136 return observer_list_.HasObserver(observer); | 141 return observer_list_.HasObserver(observer); |
137 } | 142 } |
138 | 143 |
139 bool DialServiceImpl::Discover() { | 144 bool DialServiceImpl::Discover() { |
140 DCHECK(thread_checker_.CalledOnValidThread()); | 145 DCHECK(thread_checker_.CalledOnValidThread()); |
141 if (discovery_active_) | 146 if (discovery_active_) |
142 return false; | 147 return false; |
143 discovery_active_ = true; | 148 discovery_active_ = true; |
144 | 149 |
145 DVLOG(1) << "Discovery started."; | 150 DVLOG(1) << "Discovery started."; |
146 | 151 |
147 // TODO(mfoltz): Send multiple requests. | 152 StartDiscovery(); |
148 StartRequest(); | |
149 return true; | 153 return true; |
150 } | 154 } |
151 | 155 |
152 void DialServiceImpl::FinishDiscovery() { | 156 void DialServiceImpl::StartDiscovery() { |
153 DCHECK(thread_checker_.CalledOnValidThread()); | 157 DCHECK(thread_checker_.CalledOnValidThread()); |
154 DCHECK(discovery_active_); | 158 DCHECK(discovery_active_); |
155 DVLOG(1) << "Discovery finished."; | 159 if (socket_.get()) |
156 CloseSocket(); | 160 return; |
157 finish_timer_.Stop(); | 161 |
158 discovery_active_ = false; | 162 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind( |
159 num_requests_sent_ = 0; | 163 &DialServiceImpl::DoGetNetworkList, this)); |
160 FOR_EACH_OBSERVER(Observer, observer_list_, OnDiscoveryFinished(this)); | |
161 } | 164 } |
162 | 165 |
163 bool DialServiceImpl::BindAndWriteSocket( | 166 bool DialServiceImpl::BindSocketAndSendRequest( |
164 const NetworkInterface& bind_interface) { | 167 const IPAddressNumber& bind_ip_address) { |
165 DCHECK(thread_checker_.CalledOnValidThread()); | 168 DCHECK(thread_checker_.CalledOnValidThread()); |
166 DCHECK(!socket_.get()); | 169 DCHECK(!socket_.get()); |
167 | 170 |
168 net::RandIntCallback rand_cb = base::Bind(&base::RandInt); | 171 net::RandIntCallback rand_cb = base::Bind(&base::RandInt); |
169 socket_.reset(new UDPSocket(net::DatagramSocket::RANDOM_BIND, | 172 socket_.reset(new UDPSocket(net::DatagramSocket::RANDOM_BIND, |
170 rand_cb, | 173 rand_cb, |
171 net_log_, | 174 net_log_, |
172 net_log_source_)); | 175 net_log_source_)); |
173 socket_->AllowBroadcast(); | 176 socket_->AllowBroadcast(); |
174 | 177 |
175 // Schedule a timer to finish the discovery process (and close the socket). | 178 // Schedule a timer to finish the discovery process (and close the socket). |
176 finish_timer_.Start(FROM_HERE, | 179 if (finish_delay_ > TimeDelta::FromSeconds(0)) { |
177 finish_delay_, | 180 finish_timer_.Start(FROM_HERE, |
178 this, | 181 finish_delay_, |
179 &DialServiceImpl::FinishDiscovery); | 182 this, |
183 &DialServiceImpl::FinishDiscovery); | |
184 } | |
180 | 185 |
181 // 0 means bind a random port | 186 // 0 means bind a random port |
182 IPEndPoint address(bind_interface.address, 0); | 187 IPEndPoint address(bind_ip_address, 0); |
183 | 188 |
184 if (!CheckResult("Bind", socket_->Bind(address))) | 189 if (!CheckResult("Bind", socket_->Bind(address))) |
185 return false; | 190 return false; |
186 | 191 |
192 recv_buffer_ = new IOBufferWithSize(kDialRecvBufferSize); | |
193 if (!ReadSocket()) | |
194 return false; | |
195 SendOneRequest(); | |
196 return true; | |
197 } | |
198 | |
199 void DialServiceImpl::SendOneRequest() { | |
200 if (num_requests_sent_ == max_requests_) { | |
201 request_timer_.Stop(); | |
202 return; | |
203 } | |
204 num_requests_sent_++; | |
187 if (!socket_.get()) { | 205 if (!socket_.get()) { |
188 DLOG(WARNING) << "Socket not connected."; | 206 DLOG(WARNING) << "Socket not connected."; |
189 return false; | 207 return; |
190 } | 208 } |
191 | |
192 recv_buffer_ = new IOBufferWithSize(kDialRecvBufferSize); | |
193 ReadSocket(); | |
194 | |
195 if (is_writing_) { | 209 if (is_writing_) { |
196 DVLOG(1) << "Already writing."; | 210 DVLOG(1) << "Already writing."; |
197 return false; | 211 return; |
198 } | 212 } |
213 DVLOG(1) << "Sending request " << num_requests_sent_ << "/" | |
214 << max_requests_; | |
199 is_writing_ = true; | 215 is_writing_ = true; |
200 int result = socket_->SendTo( | 216 int result = socket_->SendTo( |
201 send_buffer_.get(), | 217 send_buffer_.get(), |
202 send_buffer_->size(), send_address_, | 218 send_buffer_->size(), send_address_, |
203 base::Bind(&DialServiceImpl::OnSocketWrite, this)); | 219 base::Bind(&DialServiceImpl::OnSocketWrite, this)); |
204 bool result_ok = CheckResult("SendTo", result); | 220 bool result_ok = CheckResult("SendTo", result); |
205 if (result_ok && result > 0) { | 221 if (result_ok && result > 0) { |
206 // Synchronous write. | 222 // Synchronous write. |
207 OnSocketWrite(result); | 223 OnSocketWrite(result); |
208 } | 224 } |
209 return result_ok; | 225 return; |
justinlin
2013/02/08 08:56:20
Don't need this return
mark a. foltz
2013/02/08 22:07:58
Done.
| |
210 } | |
211 | |
212 void DialServiceImpl::StartRequest() { | |
213 DCHECK(thread_checker_.CalledOnValidThread()); | |
214 DCHECK(discovery_active_); | |
215 if (socket_.get()) | |
216 return; | |
217 | |
218 // TODO(mfoltz): Add a net::NetworkChangeNotifier() to listen for connection | |
219 // type/IP address changes, and notify via observer. Also sanity check the | |
220 // connection type, i.e. !IsOffline && !IsCellular | |
221 // http://crbug.com/165290 | |
222 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind( | |
223 &DialServiceImpl::DoGetNetworkList, this)); | |
224 } | 226 } |
225 | 227 |
226 void DialServiceImpl::OnSocketWrite(int result) { | 228 void DialServiceImpl::OnSocketWrite(int result) { |
227 DCHECK(thread_checker_.CalledOnValidThread()); | 229 DCHECK(thread_checker_.CalledOnValidThread()); |
228 is_writing_ = false; | 230 is_writing_ = false; |
229 if (!CheckResult("OnSocketWrite", result)) | 231 if (!CheckResult("OnSocketWrite", result)) |
230 return; | 232 return; |
231 | |
232 if (result != send_buffer_->size()) { | 233 if (result != send_buffer_->size()) { |
233 DLOG(ERROR) << "Sent " << result << " chars, expected " | 234 DLOG(ERROR) << "Sent " << result << " chars, expected " |
234 << send_buffer_->size() << " chars"; | 235 << send_buffer_->size() << " chars"; |
235 } | 236 } |
236 // If discovery is inactive, no reason to notify observers. | 237 // If discovery is inactive, no reason to notify observers. |
237 if (!discovery_active_) { | 238 if (!discovery_active_) { |
238 DVLOG(1) << "Request sent after discovery finished. Ignoring."; | 239 DVLOG(1) << "Request sent after discovery finished. Ignoring."; |
239 return; | 240 return; |
240 } | 241 } |
241 FOR_EACH_OBSERVER(Observer, observer_list_, OnDiscoveryRequest(this)); | 242 FOR_EACH_OBSERVER(Observer, observer_list_, OnDiscoveryRequest(this)); |
242 num_requests_sent_++; | 243 // If we need to send additional requests, schedule a timer to do so. |
244 if (num_requests_sent_ < max_requests_ && num_requests_sent_ == 1) { | |
245 request_timer_.Start(FROM_HERE, | |
246 request_interval_, | |
247 this, | |
248 &DialServiceImpl::SendOneRequest); | |
249 } | |
243 } | 250 } |
244 | 251 |
245 bool DialServiceImpl::ReadSocket() { | 252 bool DialServiceImpl::ReadSocket() { |
246 DCHECK(thread_checker_.CalledOnValidThread()); | 253 DCHECK(thread_checker_.CalledOnValidThread()); |
247 if (!socket_.get()) { | 254 if (!socket_.get()) { |
248 DLOG(WARNING) << "Socket not connected."; | 255 DLOG(WARNING) << "Socket not connected."; |
249 return false; | 256 return false; |
250 } | 257 } |
251 | 258 |
252 if (is_reading_) { | 259 if (is_reading_) { |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
363 } else { | 370 } else { |
364 DVLOG(1) << "Malformed or missing " << kSsdpConfigIdHeader << ": " | 371 DVLOG(1) << "Malformed or missing " << kSsdpConfigIdHeader << ": " |
365 << config_id; | 372 << config_id; |
366 } | 373 } |
367 | 374 |
368 return true; | 375 return true; |
369 } | 376 } |
370 | 377 |
371 void DialServiceImpl::SendNetworkList(const NetworkInterfaceList& networks) { | 378 void DialServiceImpl::SendNetworkList(const NetworkInterfaceList& networks) { |
372 DCHECK(thread_checker_.CalledOnValidThread()); | 379 DCHECK(thread_checker_.CalledOnValidThread()); |
373 if (!networks.size()) { | |
374 DVLOG(1) << "No network interfaces found!"; | |
375 return; | |
376 } | |
377 | |
378 const NetworkInterface* interface = NULL; | 380 const NetworkInterface* interface = NULL; |
379 // Returns the first IPv4 address found. If there is a need for discovery | 381 // Returns the first IPv4 address found. If there is a need for discovery |
380 // across multiple networks, we could manage multiple sockets. | 382 // across multiple networks, we could manage multiple sockets. |
381 | 383 |
382 // TODO(mfoltz): Support IPV6 multicast. http://crbug.com/165286 | 384 // TODO(mfoltz): Support IPV6 multicast. http://crbug.com/165286 |
383 for (NetworkInterfaceList::const_iterator iter = networks.begin(); | 385 for (NetworkInterfaceList::const_iterator iter = networks.begin(); |
384 iter != networks.end(); ++iter) { | 386 iter != networks.end(); ++iter) { |
385 DVLOG(1) << "Found " << iter->name << ", " | 387 DVLOG(1) << "Found " << iter->name << ", " |
386 << net::IPAddressToString(iter->address); | 388 << net::IPAddressToString(iter->address); |
387 if (iter->address.size() == net::kIPv4AddressSize) { | 389 if (iter->address.size() == net::kIPv4AddressSize) { |
388 interface = &*iter; | 390 interface = &*iter; |
389 break; | 391 break; |
390 } | 392 } |
391 } | 393 } |
392 | 394 |
393 if (interface == NULL) { | 395 if (interface == NULL) { |
394 DVLOG(1) << "Could not find a valid interface to bind."; | 396 DVLOG(1) << "Could not find a valid interface to bind."; |
397 FinishDiscovery(); | |
395 } else { | 398 } else { |
396 BindAndWriteSocket(*interface); | 399 BindSocketAndSendRequest(interface->address); |
397 } | 400 } |
398 } | 401 } |
399 | 402 |
400 void DialServiceImpl::DoGetNetworkList() { | 403 void DialServiceImpl::DoGetNetworkList() { |
401 NetworkInterfaceList list; | 404 NetworkInterfaceList list; |
402 bool success = net::GetNetworkList(&list); | 405 bool success = net::GetNetworkList(&list); |
403 if (!success) { | 406 if (!success) { |
404 DVLOG(1) << "Could not retrieve network list!"; | 407 DVLOG(1) << "Could not retrieve network list!"; |
405 } | 408 } |
406 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind( | 409 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind( |
407 &DialServiceImpl::SendNetworkList, this, list)); | 410 &DialServiceImpl::SendNetworkList, this, list)); |
408 } | 411 } |
409 | 412 |
413 void DialServiceImpl::FinishDiscovery() { | |
414 DCHECK(thread_checker_.CalledOnValidThread()); | |
415 DCHECK(discovery_active_); | |
416 DVLOG(1) << "Discovery finished."; | |
417 CloseSocket(); | |
418 finish_timer_.Stop(); | |
419 request_timer_.Stop(); | |
420 discovery_active_ = false; | |
421 num_requests_sent_ = 0; | |
422 FOR_EACH_OBSERVER(Observer, observer_list_, OnDiscoveryFinished(this)); | |
justinlin
2013/02/08 08:56:20
Don't need to do this in the CL, but I kinda feel
mark a. foltz
2013/02/08 22:07:58
I agree with you that DialService is exclusively f
| |
423 } | |
424 | |
410 void DialServiceImpl::CloseSocket() { | 425 void DialServiceImpl::CloseSocket() { |
411 DCHECK(thread_checker_.CalledOnValidThread()); | 426 DCHECK(thread_checker_.CalledOnValidThread()); |
412 is_reading_ = false; | 427 is_reading_ = false; |
413 is_writing_ = false; | 428 is_writing_ = false; |
414 if (!socket_.get()) | 429 if (!socket_.get()) |
415 return; | 430 return; |
416 socket_.reset(); | 431 socket_.reset(); |
417 } | 432 } |
418 | 433 |
419 bool DialServiceImpl::CheckResult(const char* operation, int result) { | 434 bool DialServiceImpl::CheckResult(const char* operation, int result) { |
420 DCHECK(thread_checker_.CalledOnValidThread()); | 435 DCHECK(thread_checker_.CalledOnValidThread()); |
421 DVLOG(1) << "Operation " << operation << " result " << result; | 436 DVLOG(1) << "Operation " << operation << " result " << result; |
422 if (result < net::OK && result != net::ERR_IO_PENDING) { | 437 if (result < net::OK && result != net::ERR_IO_PENDING) { |
423 CloseSocket(); | 438 CloseSocket(); |
424 std::string error_str(net::ErrorToString(result)); | 439 std::string error_str(net::ErrorToString(result)); |
425 DVLOG(0) << "dial socket error: " << error_str; | 440 DVLOG(0) << "dial socket error: " << error_str; |
426 FOR_EACH_OBSERVER(Observer, observer_list_, OnError(this, error_str)); | 441 FOR_EACH_OBSERVER(Observer, observer_list_, OnError(this, error_str)); |
427 return false; | 442 return false; |
428 } | 443 } |
429 return true; | 444 return true; |
430 } | 445 } |
431 | 446 |
432 } // namespace extensions | 447 } // namespace extensions |
OLD | NEW |