blob: f9be1f9b44d6b41e55a7f843c55f6a4f68ad5d0d [file] [log] [blame]
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. 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 '../error_handler.dart';
import '../frames/frames.dart';
import '../sync_errors.dart';
/// Responsible for pinging the other end and for handling pings from the
/// other end.
// TODO: We currently write unconditionally to the [FrameWriter]: we might want
// to consider be more aware what [Framewriter.bufferIndicator.wouldBuffer]
// says.
class PingHandler extends Object with TerminatableMixin {
final FrameWriter _frameWriter;
final Map<int, Completer> _remainingPings = {};
final Sink<int>? pingReceived;
final bool Function() isListeningToPings;
int _nextId = 1;
PingHandler(this._frameWriter, StreamController<int> pingStream)
: pingReceived = pingStream.sink,
isListeningToPings = (() => pingStream.hasListener);
@override
void onTerminated(Object? error) {
final remainingPings = _remainingPings.values.toList();
_remainingPings.clear();
for (final ping in remainingPings) {
ping.completeError(
error ?? 'Remaining ping completed with unspecified error');
}
}
void processPingFrame(PingFrame frame) {
ensureNotTerminatedSync(() {
if (frame.header.streamId != 0) {
throw ProtocolException('Ping frames must have a stream id of 0.');
}
if (!frame.hasAckFlag) {
if (isListeningToPings()) {
pingReceived?.add(frame.opaqueData);
}
_frameWriter.writePingFrame(frame.opaqueData, ack: true);
} else {
var c = _remainingPings.remove(frame.opaqueData);
if (c != null) {
c.complete();
} else {
// NOTE: It is not specified what happens when one gets an ACK for a
// ping we never sent. We be very strict and fail in this case.
throw ProtocolException(
'Received ping ack with unknown opaque data.');
}
}
});
}
Future ping() {
return ensureNotTerminatedAsync(() {
var c = Completer<void>();
var id = _nextId++;
_remainingPings[id] = c;
_frameWriter.writePingFrame(id);
return c.future;
});
}
}