Chromium Code Reviews| 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) + | |
| 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 // TODO(mfoltz): Add a net::NetworkChangeNotifier() to listen for connection |
|
justinlin
2013/02/07 09:03:24
This TODO no longer needed.
mark a. foltz
2013/02/07 22:36:55
Done.
| |
| 159 num_requests_sent_ = 0; | 163 // type/IP address changes, and notify via observer. Also sanity check the |
| 160 FOR_EACH_OBSERVER(Observer, observer_list_, OnDiscoveryFinished(this)); | 164 // connection type, i.e. !IsOffline && !IsCellular |
| 165 // http://crbug.com/165290 | |
| 166 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind( | |
| 167 &DialServiceImpl::DoGetNetworkList, this)); | |
| 161 } | 168 } |
| 162 | 169 |
| 163 bool DialServiceImpl::BindAndWriteSocket( | 170 bool DialServiceImpl::BindSocketAndSendRequest( |
| 164 const NetworkInterface& bind_interface) { | 171 const IPAddressNumber& bind_ip_address) { |
| 165 DCHECK(thread_checker_.CalledOnValidThread()); | 172 DCHECK(thread_checker_.CalledOnValidThread()); |
| 166 DCHECK(!socket_.get()); | 173 DCHECK(!socket_.get()); |
| 167 | 174 |
| 168 net::RandIntCallback rand_cb = base::Bind(&base::RandInt); | 175 net::RandIntCallback rand_cb = base::Bind(&base::RandInt); |
| 169 socket_.reset(new UDPSocket(net::DatagramSocket::RANDOM_BIND, | 176 socket_.reset(new UDPSocket(net::DatagramSocket::RANDOM_BIND, |
| 170 rand_cb, | 177 rand_cb, |
| 171 net_log_, | 178 net_log_, |
| 172 net_log_source_)); | 179 net_log_source_)); |
| 173 socket_->AllowBroadcast(); | 180 socket_->AllowBroadcast(); |
| 174 | 181 |
| 175 // Schedule a timer to finish the discovery process (and close the socket). | 182 // Schedule a timer to finish the discovery process (and close the socket). |
| 176 finish_timer_.Start(FROM_HERE, | 183 if (finish_delay_ > TimeDelta::FromSeconds(0)) { |
| 177 finish_delay_, | 184 finish_timer_.Start(FROM_HERE, |
| 178 this, | 185 finish_delay_, |
| 179 &DialServiceImpl::FinishDiscovery); | 186 this, |
| 187 &DialServiceImpl::FinishDiscovery); | |
| 188 } | |
| 180 | 189 |
| 181 // 0 means bind a random port | 190 // 0 means bind a random port |
| 182 IPEndPoint address(bind_interface.address, 0); | 191 IPEndPoint address(bind_ip_address, 0); |
| 183 | 192 |
| 184 if (!CheckResult("Bind", socket_->Bind(address))) | 193 if (!CheckResult("Bind", socket_->Bind(address))) |
| 185 return false; | 194 return false; |
| 186 | 195 |
| 196 recv_buffer_ = new IOBufferWithSize(kDialRecvBufferSize); | |
| 197 if (!ReadSocket()) { | |
|
justinlin
2013/02/07 09:03:24
no braces
mark a. foltz
2013/02/07 22:36:55
Done.
| |
| 198 return false; | |
| 199 } | |
| 200 SendOneRequest(); | |
| 201 return true; | |
| 202 } | |
| 203 | |
| 204 void DialServiceImpl::SendOneRequest() { | |
| 205 if (num_requests_sent_ == max_requests_) { | |
| 206 request_timer_.Stop(); | |
| 207 return; | |
| 208 } | |
| 209 num_requests_sent_++; | |
| 187 if (!socket_.get()) { | 210 if (!socket_.get()) { |
| 188 DLOG(WARNING) << "Socket not connected."; | 211 DLOG(WARNING) << "Socket not connected."; |
| 189 return false; | 212 return; |
| 190 } | 213 } |
| 191 | |
| 192 recv_buffer_ = new IOBufferWithSize(kDialRecvBufferSize); | |
| 193 ReadSocket(); | |
| 194 | |
| 195 if (is_writing_) { | 214 if (is_writing_) { |
| 196 DVLOG(1) << "Already writing."; | 215 DVLOG(1) << "Already writing."; |
| 197 return false; | 216 return; |
| 198 } | 217 } |
| 218 DVLOG(1) << "Sending request " << num_requests_sent_ << "/" | |
| 219 << max_requests_; | |
| 199 is_writing_ = true; | 220 is_writing_ = true; |
| 200 int result = socket_->SendTo( | 221 int result = socket_->SendTo( |
| 201 send_buffer_.get(), | 222 send_buffer_.get(), |
| 202 send_buffer_->size(), send_address_, | 223 send_buffer_->size(), send_address_, |
| 203 base::Bind(&DialServiceImpl::OnSocketWrite, this)); | 224 base::Bind(&DialServiceImpl::OnSocketWrite, this)); |
| 204 bool result_ok = CheckResult("SendTo", result); | 225 bool result_ok = CheckResult("SendTo", result); |
| 205 if (result_ok && result > 0) { | 226 if (result_ok && result > 0) { |
| 206 // Synchronous write. | 227 // Synchronous write. |
| 207 OnSocketWrite(result); | 228 OnSocketWrite(result); |
| 208 } | 229 } |
| 209 return result_ok; | 230 return; |
| 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 } | 231 } |
| 225 | 232 |
| 226 void DialServiceImpl::OnSocketWrite(int result) { | 233 void DialServiceImpl::OnSocketWrite(int result) { |
| 227 DCHECK(thread_checker_.CalledOnValidThread()); | 234 DCHECK(thread_checker_.CalledOnValidThread()); |
| 228 is_writing_ = false; | 235 is_writing_ = false; |
| 229 if (!CheckResult("OnSocketWrite", result)) | 236 if (!CheckResult("OnSocketWrite", result)) |
| 230 return; | 237 return; |
| 231 | |
| 232 if (result != send_buffer_->size()) { | 238 if (result != send_buffer_->size()) { |
| 233 DLOG(ERROR) << "Sent " << result << " chars, expected " | 239 DLOG(ERROR) << "Sent " << result << " chars, expected " |
| 234 << send_buffer_->size() << " chars"; | 240 << send_buffer_->size() << " chars"; |
| 235 } | 241 } |
| 236 // If discovery is inactive, no reason to notify observers. | 242 // If discovery is inactive, no reason to notify observers. |
| 237 if (!discovery_active_) { | 243 if (!discovery_active_) { |
| 238 DVLOG(1) << "Request sent after discovery finished. Ignoring."; | 244 DVLOG(1) << "Request sent after discovery finished. Ignoring."; |
| 239 return; | 245 return; |
| 240 } | 246 } |
| 241 FOR_EACH_OBSERVER(Observer, observer_list_, OnDiscoveryRequest(this)); | 247 FOR_EACH_OBSERVER(Observer, observer_list_, OnDiscoveryRequest(this)); |
| 242 num_requests_sent_++; | 248 // If we need to send additional requests, schedule a timer to do so. |
| 249 if (num_requests_sent_ < max_requests_) { | |
| 250 if (request_interval_ <= TimeDelta::FromSeconds(0)) { | |
|
justinlin
2013/02/07 09:03:24
Is this check needed? Seems like scheduling a time
mark a. foltz
2013/02/07 22:36:55
Scheduling a zero-delay timer didn't result in a t
justinlin
2013/02/07 22:42:05
Ah, if it's for testing, then I think it's possibl
mark a. foltz
2013/02/08 00:04:50
Thanks for the tip. I wasn't able to use RunLoop
| |
| 251 SendOneRequest(); | |
| 252 } else if (num_requests_sent_ == 1) { | |
| 253 request_timer_.Start(FROM_HERE, | |
| 254 request_interval_, | |
| 255 this, | |
| 256 &DialServiceImpl::SendOneRequest); | |
| 257 } else { | |
| 258 DCHECK(request_timer_.IsRunning()); | |
|
justinlin
2013/02/07 09:03:24
I'm not sure this DCHECK is valid. Since the DialS
mark a. foltz
2013/02/07 22:36:55
Good point. Removed.
| |
| 259 } | |
| 260 } | |
| 243 } | 261 } |
| 244 | 262 |
| 245 bool DialServiceImpl::ReadSocket() { | 263 bool DialServiceImpl::ReadSocket() { |
| 246 DCHECK(thread_checker_.CalledOnValidThread()); | 264 DCHECK(thread_checker_.CalledOnValidThread()); |
| 247 if (!socket_.get()) { | 265 if (!socket_.get()) { |
| 248 DLOG(WARNING) << "Socket not connected."; | 266 DLOG(WARNING) << "Socket not connected."; |
| 249 return false; | 267 return false; |
| 250 } | 268 } |
| 251 | 269 |
| 252 if (is_reading_) { | 270 if (is_reading_) { |
| (...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 364 DVLOG(1) << "Malformed or missing " << kSsdpConfigIdHeader << ": " | 382 DVLOG(1) << "Malformed or missing " << kSsdpConfigIdHeader << ": " |
| 365 << config_id; | 383 << config_id; |
| 366 } | 384 } |
| 367 | 385 |
| 368 return true; | 386 return true; |
| 369 } | 387 } |
| 370 | 388 |
| 371 void DialServiceImpl::SendNetworkList(const NetworkInterfaceList& networks) { | 389 void DialServiceImpl::SendNetworkList(const NetworkInterfaceList& networks) { |
| 372 DCHECK(thread_checker_.CalledOnValidThread()); | 390 DCHECK(thread_checker_.CalledOnValidThread()); |
| 373 if (!networks.size()) { | 391 if (!networks.size()) { |
| 374 DVLOG(1) << "No network interfaces found!"; | 392 DVLOG(1) << "No valid network interfaces found!"; |
| 393 FinishDiscovery(); | |
| 375 return; | 394 return; |
| 376 } | 395 } |
| 377 | 396 |
| 378 const NetworkInterface* interface = NULL; | 397 const NetworkInterface* interface = NULL; |
| 379 // Returns the first IPv4 address found. If there is a need for discovery | 398 // Returns the first IPv4 address found. If there is a need for discovery |
| 380 // across multiple networks, we could manage multiple sockets. | 399 // across multiple networks, we could manage multiple sockets. |
| 381 | 400 |
| 382 // TODO(mfoltz): Support IPV6 multicast. http://crbug.com/165286 | 401 // TODO(mfoltz): Support IPV6 multicast. http://crbug.com/165286 |
| 383 for (NetworkInterfaceList::const_iterator iter = networks.begin(); | 402 for (NetworkInterfaceList::const_iterator iter = networks.begin(); |
| 384 iter != networks.end(); iter++) { | 403 iter != networks.end(); iter++) { |
| 385 DVLOG(1) << "Found " << iter->name << ", " | 404 DVLOG(1) << "Found " << iter->name << ", " |
| 386 << net::IPAddressToString(iter->address); | 405 << net::IPAddressToString(iter->address); |
| 387 if (iter->address.size() == net::kIPv4AddressSize) { | 406 if (iter->address.size() == net::kIPv4AddressSize) { |
| 388 interface = &*iter; | 407 interface = &*iter; |
| 389 break; | 408 break; |
| 390 } | 409 } |
| 391 } | 410 } |
| 392 | 411 |
| 393 if (interface == NULL) { | 412 if (interface == NULL) { |
| 394 DVLOG(1) << "Could not find a valid interface to bind."; | 413 DVLOG(1) << "Could not find a valid interface to bind."; |
| 414 FinishDiscovery(); | |
| 395 } else { | 415 } else { |
| 396 BindAndWriteSocket(*interface); | 416 BindSocketAndSendRequest(interface->address); |
| 397 } | 417 } |
| 398 } | 418 } |
| 399 | 419 |
| 400 void DialServiceImpl::DoGetNetworkList() { | 420 void DialServiceImpl::DoGetNetworkList() { |
| 401 NetworkInterfaceList list; | 421 NetworkInterfaceList list; |
| 402 bool success = net::GetNetworkList(&list); | 422 bool success = net::GetNetworkList(&list); |
| 403 if (!success) { | 423 if (!success) { |
| 404 DVLOG(1) << "Could not retrieve network list!"; | 424 DVLOG(1) << "Could not retrieve network list!"; |
| 405 return; | 425 list.clear(); |
|
justinlin
2013/02/07 09:03:24
This isn't needed as the vector is already initial
mark a. foltz
2013/02/07 22:36:55
Done. Will rebase.
| |
| 406 } | 426 } |
| 407 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind( | 427 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind( |
| 408 &DialServiceImpl::SendNetworkList, this, list)); | 428 &DialServiceImpl::SendNetworkList, this, list)); |
| 409 } | 429 } |
| 410 | 430 |
| 431 void DialServiceImpl::FinishDiscovery() { | |
| 432 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 433 DCHECK(discovery_active_); | |
| 434 DVLOG(1) << "Discovery finished."; | |
| 435 CloseSocket(); | |
| 436 finish_timer_.Stop(); | |
| 437 request_timer_.Stop(); | |
| 438 discovery_active_ = false; | |
| 439 num_requests_sent_ = 0; | |
| 440 FOR_EACH_OBSERVER(Observer, observer_list_, OnDiscoveryFinished(this)); | |
| 441 } | |
| 442 | |
| 411 void DialServiceImpl::CloseSocket() { | 443 void DialServiceImpl::CloseSocket() { |
| 412 DCHECK(thread_checker_.CalledOnValidThread()); | 444 DCHECK(thread_checker_.CalledOnValidThread()); |
| 413 is_reading_ = false; | 445 is_reading_ = false; |
| 414 is_writing_ = false; | 446 is_writing_ = false; |
| 415 if (!socket_.get()) | 447 if (!socket_.get()) |
| 416 return; | 448 return; |
| 417 socket_.reset(); | 449 socket_.reset(); |
| 418 } | 450 } |
| 419 | 451 |
| 420 bool DialServiceImpl::CheckResult(const char* operation, int result) { | 452 bool DialServiceImpl::CheckResult(const char* operation, int result) { |
| 421 DCHECK(thread_checker_.CalledOnValidThread()); | 453 DCHECK(thread_checker_.CalledOnValidThread()); |
| 422 DVLOG(1) << "Operation " << operation << " result " << result; | 454 DVLOG(1) << "Operation " << operation << " result " << result; |
| 423 if (result < net::OK && result != net::ERR_IO_PENDING) { | 455 if (result < net::OK && result != net::ERR_IO_PENDING) { |
| 424 CloseSocket(); | 456 CloseSocket(); |
| 425 std::string error_str(net::ErrorToString(result)); | 457 std::string error_str(net::ErrorToString(result)); |
| 426 DVLOG(0) << "dial socket error: " << error_str; | 458 DVLOG(0) << "dial socket error: " << error_str; |
| 427 FOR_EACH_OBSERVER(Observer, observer_list_, OnError(this, error_str)); | 459 FOR_EACH_OBSERVER(Observer, observer_list_, OnError(this, error_str)); |
| 428 return false; | 460 return false; |
| 429 } | 461 } |
| 430 return true; | 462 return true; |
| 431 } | 463 } |
| 432 | 464 |
| 433 } // namespace extensions | 465 } // namespace extensions |
| OLD | NEW |