// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'dart:async'; import 'dart:collection'; import 'resource_record.dart'; /// Class for maintaining state about pending mDNS requests. base class PendingRequest extends LinkedListEntry { /// Creates a new PendingRequest. PendingRequest(this.type, this.domainName, this.controller); /// The [ResourceRecordType] of the request. final int type; /// The domain name to look up via mDNS. /// /// For example, `'_http._tcp.local` to look up HTTP services on the local /// domain. final String domainName; /// A StreamController managing the request. final StreamController controller; /// The timer for the request. Timer? timer; } /// Class for keeping track of pending lookups and processing incoming /// query responses. class LookupResolver { final LinkedList _pendingRequests = LinkedList(); /// Adds a request and returns a [Stream] of [ResourceRecord] responses. Stream addPendingRequest( int type, String name, Duration timeout) { final StreamController controller = StreamController(); final PendingRequest request = PendingRequest(type, name, controller); final Timer timer = Timer(timeout, () { request.unlink(); controller.close(); }); request.timer = timer; _pendingRequests.add(request); return controller.stream; } /// Parses [ResoureRecord]s received and delivers them to the appropriate /// listener(s) added via [addPendingRequest]. void handleResponse(List response) { for (final ResourceRecord r in response) { final int type = r.resourceRecordType; String name = r.name.toLowerCase(); if (name.endsWith('.')) { name = name.substring(0, name.length - 1); } bool responseMatches(PendingRequest request) { String requestName = request.domainName.toLowerCase(); // make, e.g. "_http" become "_http._tcp.local". if (!requestName.endsWith('local')) { if (!requestName.endsWith('._tcp.local') && !requestName.endsWith('._udp.local') && !requestName.endsWith('._tcp') && !requestName.endsWith('.udp')) { requestName += '._tcp'; } requestName += '.local'; } return requestName == name && request.type == type; } for (final PendingRequest pendingRequest in _pendingRequests) { if (responseMatches(pendingRequest)) { if (pendingRequest.controller.isClosed) { return; } pendingRequest.controller.add(r); } } } } /// Removes any pending requests and ends processing. void clearPendingRequests() { while (_pendingRequests.isNotEmpty) { final PendingRequest request = _pendingRequests.first; request.unlink(); request.timer?.cancel(); request.controller.close(); } } }