Index: pkg/mdns/lib/src/native_protocol_client.dart |
diff --git a/pkg/mdns/lib/src/native_protocol_client.dart b/pkg/mdns/lib/src/native_protocol_client.dart |
index 23854300ee9281e44c4dac70c3755474579346bb..28af6742b713696dd9883d380fbe9bc32a55afca 100644 |
--- a/pkg/mdns/lib/src/native_protocol_client.dart |
+++ b/pkg/mdns/lib/src/native_protocol_client.dart |
@@ -13,6 +13,64 @@ import 'package:mdns/src/constants.dart'; |
import 'package:mdns/src/lookup_resolver.dart'; |
import 'package:mdns/src/packet.dart'; |
+/// Cache for resource records that have been received. |
+/// |
+/// There can be multiple entries for the same name and type. |
+/// |
+/// The cached is updated with a list of records, because it needs to remove |
+/// all entries that correspond to name and type of the name/type combinations |
+/// of records that should be updated. For example, a host may remove one |
+/// of its IP addresses and report the remaining address as a response - then |
+/// we need to clear all previous entries for that host before updating the |
+/// cache. |
+class ResourceRecordCache { |
+ final List buffer; |
+ final int size; |
+ int position; |
+ |
+ ResourceRecordCache({int size: 32}) |
+ : buffer = new List(size), |
+ size = size, |
+ position = 0; |
+ |
+ void updateRecords(List<ResourceRecord> records) { |
+ // TODO(karlklose): include flush bit in the record and only flush if |
+ // necessary. |
+ // Clear the cache for all name/type combinations to be updated. |
+ for (int i = 0; i < size; i++) { |
+ ResourceRecord r = buffer[i % size]; |
+ if (r == null) continue; |
+ String name = r.name; |
+ int type = r.type; |
+ for (ResourceRecord record in records) { |
+ if (name == record.name && type == record.type) { |
+ buffer[i % size] = null; |
+ break; |
+ } |
+ } |
+ } |
+ // Add the new records. |
+ for (ResourceRecord record in records) { |
+ buffer[position] = record; |
+ position = (position + 1) % size; |
+ } |
+ } |
+ |
+ void lookup(String name, int type, List results) { |
+ int time = new DateTime.now().millisecondsSinceEpoch; |
+ for (int i = position + size; i >= position; i--) { |
+ int index = i % size; |
+ ResourceRecord record = buffer[index]; |
+ if (record == null) continue; |
+ if (record.validUntil < time) { |
+ buffer[index] = null; |
+ } else if (record.name == name && record.type == type) { |
+ results.add(record); |
+ } |
+ } |
+ } |
+} |
+ |
// Implementation of mDNS client using the native protocol. |
class NativeProtocolMDnsClient implements MDnsClient { |
bool _starting = false; |
@@ -20,6 +78,7 @@ class NativeProtocolMDnsClient implements MDnsClient { |
RawDatagramSocket _incoming; |
final List<RawDatagramSocket> _sockets = <RawDatagramSocket>[]; |
final LookupResolver _resolver = new LookupResolver(); |
+ ResourceRecordCache cache = new ResourceRecordCache(); |
/// Start the mDNS client. |
Future start() async { |
@@ -59,33 +118,48 @@ class NativeProtocolMDnsClient implements MDnsClient { |
_sockets.forEach((socket) => socket.close()); |
_incoming.close(); |
+ _resolver.clearPendingRequests(); |
+ |
_started = false; |
} |
- Future<InternetAddress> lookup( |
- String hostname, {Duration timeout: const Duration(seconds: 5)}) { |
+ Stream<ResourceRecord> lookup( |
+ int type, |
+ String name, |
+ {Duration timeout: const Duration(seconds: 5)}) { |
if (!_started) { |
throw new StateError('mDNS client is not started'); |
} |
+ // Look for entries in the cache. |
+ List<ResourceRecordCache> cached = <ResourceRecord>[]; |
+ cache.lookup(name, type, cached); |
+ if (cached.isNotEmpty) { |
+ StreamController controller = new StreamController(); |
+ cached.forEach(controller.add); |
+ controller.close(); |
+ return controller.stream; |
+ } |
+ |
// Add the pending request before sending the query. |
- var future = _resolver.addPendingRequest(hostname, timeout); |
+ var results = _resolver.addPendingRequest(type, name, timeout); |
// Send the request on all interfaces. |
- List<int> packet = encodeMDnsQuery(hostname); |
+ List<int> packet = encodeMDnsQuery(name, type); |
for (int i = 0; i < _sockets.length; i++) { |
_sockets[i].send(packet, mDnsAddress, mDnsPort); |
} |
- return future; |
+ return results; |
} |
// Process incoming datagrams. |
_handleIncoming(event) { |
if (event == RawSocketEvent.READ) { |
- var data = _incoming.receive(); |
- var response = decodeMDnsResponse(data.data); |
+ Datagram datagram = _incoming.receive(); |
+ List<ResourceRecord> response = decodeMDnsResponse(datagram.data); |
if (response != null) { |
+ cache.updateRecords(response); |
_resolver.handleResponse(response); |
} |
} |