blob: 726707f357fa3f7d4194a43c20beb17f1baaf5b1 [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 'dart:convert' show ascii;
import 'dart:typed_data';
import 'package:http2/src/connection_preface.dart';
import 'package:http2/src/flowcontrol/window.dart';
import 'package:http2/src/frames/frames.dart';
import 'package:http2/src/hpack/hpack.dart';
import 'package:http2/src/settings/settings.dart';
import 'package:http2/transport.dart';
import 'package:test/test.dart';
import 'src/hpack/hpack_test.dart' show isHeader;
void main() {
group('client-tests', () {
group('normal', () {
clientTest('gracefull-shutdown-for-unused-connection',
(ClientTransportConnection client,
FrameWriter serverWriter,
StreamIterator<Frame> serverReader,
Future<Frame> Function() nextFrame) async {
var settingsDone = Completer<void>();
Future serverFun() async {
serverWriter.writeSettingsFrame([]);
expect(await nextFrame(), isA<SettingsFrame>());
serverWriter.writeSettingsAckFrame();
expect(await nextFrame(), isA<SettingsFrame>());
settingsDone.complete();
// Make sure we get the graceful shutdown message.
expect(
await nextFrame(),
isA<GoawayFrame>()
.having((f) => f.errorCode, 'errorCode', ErrorCode.NO_ERROR));
// Make sure the client ended the connection.
expect(await serverReader.moveNext(), false);
}
Future clientFun() async {
await settingsDone.future;
expect(client.isOpen, true);
// Try to gracefully finish the connection.
var future = client.finish();
expect(client.isOpen, false);
await future;
}
await Future.wait([serverFun(), clientFun()]);
});
});
group('connection-operational', () {
clientTest('on-connection-operational-fires',
(ClientTransportConnection client,
FrameWriter serverWriter,
StreamIterator<Frame> serverReader,
Future<Frame> Function() nextFrame) async {
final settingsDone = Completer<void>();
Future serverFun() async {
serverWriter.writeSettingsFrame([]);
settingsDone.complete();
expect(await nextFrame(), isA<SettingsFrame>());
serverWriter.writeSettingsAckFrame();
expect(await nextFrame(), isA<SettingsFrame>());
// Make sure we get the graceful shutdown message.
expect(
await nextFrame(),
isA<GoawayFrame>()
.having((f) => f.errorCode, 'errorCode', ErrorCode.NO_ERROR));
// Make sure the client ended the connection.
expect(await serverReader.moveNext(), false);
}
Future clientFun() async {
await settingsDone.future;
await client.onInitialPeerSettingsReceived
.timeout(const Duration(milliseconds: 20)); // Should complete
expect(client.isOpen, true);
// Try to gracefully finish the connection.
var future = client.finish();
expect(client.isOpen, false);
await future;
}
await Future.wait([serverFun(), clientFun()]);
});
clientTest('on-connection-operational-does-not-fire',
(ClientTransportConnection client,
FrameWriter serverWriter,
StreamIterator<Frame> serverReader,
Future<Frame> Function() nextFrame) async {
final goawayReceived = Completer<void>();
Future serverFun() async {
serverWriter.writePingFrame(42);
expect(await nextFrame(), isA<SettingsFrame>());
expect(await nextFrame(), isA<GoawayFrame>());
goawayReceived.complete();
expect(await serverReader.moveNext(), false);
}
Future clientFun() async {
expect(client.isOpen, true);
expect(
client.onInitialPeerSettingsReceived
.timeout(const Duration(seconds: 1)),
throwsA(isA<TimeoutException>()));
// We wait until the server received the error (it's actually later
// than necessary, but we can't make a deterministic test otherwise).
await goawayReceived.future;
expect(client.isOpen, false);
String? error;
try {
client.makeRequest([Header.ascii('a', 'b')]);
} catch (e) {
error = '$e';
}
expect(error, contains('no longer active'));
await client.finish();
}
await Future.wait([serverFun(), clientFun()]);
});
});
group('server-errors', () {
clientTest('no-settings-frame-at-beginning-immediate-error',
(ClientTransportConnection client,
FrameWriter serverWriter,
StreamIterator<Frame> serverReader,
Future<Frame> Function() nextFrame) async {
var goawayReceived = Completer<void>();
Future serverFun() async {
serverWriter.writePingFrame(42);
expect(await nextFrame(), isA<SettingsFrame>());
expect(await nextFrame(), isA<GoawayFrame>());
goawayReceived.complete();
expect(await serverReader.moveNext(), false);
}
Future clientFun() async {
expect(client.isOpen, true);
// We wait until the server received the error (it's actually later
// than necessary, but we can't make a deterministic test otherwise).
await goawayReceived.future;
expect(client.isOpen, false);
String? error;
try {
client.makeRequest([Header.ascii('a', 'b')]);
} catch (e) {
error = '$e';
}
expect(error, contains('no longer active'));
await client.finish();
}
await Future.wait([serverFun(), clientFun()]);
});
clientTest('no-settings-frame-at-beginning-delayed-error',
(ClientTransportConnection client,
FrameWriter serverWriter,
StreamIterator<Frame> serverReader,
Future<Frame> Function() nextFrame) async {
Future serverFun() async {
expect(await nextFrame(), isA<SettingsFrame>());
expect(await nextFrame(), isA<HeadersFrame>());
serverWriter.writePingFrame(42);
expect(await nextFrame(), isA<GoawayFrame>());
expect(await serverReader.moveNext(), false);
}
Future clientFun() async {
expect(client.isOpen, true);
var stream = client.makeRequest([Header.ascii('a', 'b')]);
String? error;
try {
await stream.incomingMessages.toList();
} catch (e) {
error = '$e';
}
expect(error, contains('forcefully terminated'));
await client.finish();
}
await Future.wait([serverFun(), clientFun()]);
});
clientTest('data-frame-for-invalid-stream',
(ClientTransportConnection client,
FrameWriter serverWriter,
StreamIterator<Frame> serverReader,
Future<Frame> Function() nextFrame) async {
var handshakeCompleter = Completer<void>();
Future serverFun() async {
serverWriter.writeSettingsFrame([]);
expect(await nextFrame(), isA<SettingsFrame>());
serverWriter.writeSettingsAckFrame();
expect(await nextFrame(), isA<SettingsFrame>());
handshakeCompleter.complete();
var headers = await nextFrame() as HeadersFrame;
expect(
await nextFrame(),
isA<DataFrame>().having(
(p0) => p0.hasEndStreamFlag, 'Last data frame', true));
// Write a data frame for a non-existent stream.
var invalidStreamId = headers.header.streamId + 2;
serverWriter.writeDataFrame(invalidStreamId, [42]);
// Make sure the client sends a [RstStreamFrame] frame.
expect(
await nextFrame(),
isA<WindowUpdateFrame>()
.having((p0) => p0.header.streamId, 'Connection update', 0));
expect(
await nextFrame(),
isA<RstStreamFrame>()
.having(
(f) => f.errorCode, 'errorCode', ErrorCode.STREAM_CLOSED)
.having((f) => f.header.streamId, 'header.streamId',
invalidStreamId));
// Close the original stream.
serverWriter.writeDataFrame(headers.header.streamId, [],
endStream: true);
// Wait for the client finish.
expect(await nextFrame(), isA<GoawayFrame>());
expect(await serverReader.moveNext(), false);
await serverWriter.close();
}
Future clientFun() async {
await handshakeCompleter.future;
var stream = client.makeRequest([Header.ascii('a', 'b')]);
await stream.outgoingMessages.close();
expect(await stream.incomingMessages.toList(), isEmpty);
await client.finish();
}
await Future.wait([serverFun(), clientFun()]);
});
clientTest('data-frame-after-stream-closed',
(ClientTransportConnection client,
FrameWriter serverWriter,
StreamIterator<Frame> serverReader,
Future<Frame> Function() nextFrame) async {
var handshakeCompleter = Completer<void>();
Future serverFun() async {
serverWriter.writeSettingsFrame([]);
expect(await nextFrame(), isA<SettingsFrame>());
serverWriter.writeSettingsAckFrame();
expect(await nextFrame(), isA<SettingsFrame>());
handshakeCompleter.complete();
var headers = await nextFrame() as HeadersFrame;
expect(
await nextFrame(),
isA<DataFrame>().having(
(p0) => p0.hasEndStreamFlag, 'Last data frame', true));
var streamId = headers.header.streamId;
// Write a data frame for a non-existent stream.
var data1 = [42, 42];
serverWriter.writeDataFrame(streamId, data1, endStream: true);
// Write more data on the closed stream.
var data2 = [42];
serverWriter.writeDataFrame(streamId, data2);
// NOTE: The order of the window update frame / rst frame just
// happens to be like that ATM.
// The two WindowUpdateFrames for the data1 DataFrame.
expect(
await nextFrame(),
isA<WindowUpdateFrame>()
.having((p0) => p0.header.streamId, 'Stream update', 1)
.having((p0) => p0.windowSizeIncrement, 'Windowsize',
data1.length));
expect(
await nextFrame(),
isA<WindowUpdateFrame>()
.having((p0) => p0.header.streamId, 'Connection update', 0)
.having((p0) => p0.windowSizeIncrement, 'Windowsize',
data1.length));
// The [WindowUpdateFrame] for the frame on the closed stream, which
// should still update the connection.
expect(
await nextFrame(),
isA<WindowUpdateFrame>()
.having((p0) => p0.header.streamId, 'Connection update', 0)
.having((p0) => p0.windowSizeIncrement, 'Windowsize',
data2.length));
// Make sure we get a [RstStreamFrame] frame.
expect(
await nextFrame(),
isA<RstStreamFrame>()
.having(
(f) => f.errorCode, 'errorCode', ErrorCode.STREAM_CLOSED)
.having(
(f) => f.header.streamId, 'header.streamId', streamId));
// Wait for the client finish.
expect(await nextFrame(), isA<GoawayFrame>());
expect(await serverReader.moveNext(), false);
await serverWriter.close();
}
Future clientFun() async {
await handshakeCompleter.future;
var stream = client.makeRequest([Header.ascii('a', 'b')]);
await stream.outgoingMessages.close();
var messages = await stream.incomingMessages.toList();
expect(messages, hasLength(1));
expect(
messages[0],
isA<DataStreamMessage>()
.having((p0) => p0.bytes, 'Same as `data1` above', [42, 42]),
);
await client.finish();
}
await Future.wait([serverFun(), clientFun()]);
});
clientTest('data-frame-received-after-stream-cancel',
(ClientTransportConnection client,
FrameWriter serverWriter,
StreamIterator<Frame> serverReader,
Future<Frame> Function() nextFrame) async {
var handshakeCompleter = Completer<void>();
var cancelDone = Completer<void>();
var endDone = Completer<void>();
Future serverFun() async {
serverWriter.writeSettingsFrame([]);
expect(await nextFrame(), isA<SettingsFrame>());
serverWriter.writeSettingsAckFrame();
expect(await nextFrame(), isA<SettingsFrame>());
handshakeCompleter.complete();
var headers = await nextFrame() as HeadersFrame;
expect(
await nextFrame(),
isA<DataFrame>().having(
(p0) => p0.hasEndStreamFlag, 'Last data frame', true));
var streamId = headers.header.streamId;
// Write a data frame.
serverWriter.writeDataFrame(streamId, [42]);
await cancelDone.future;
serverWriter.writeDataFrame(streamId, [43]);
// NOTE: The order of the window update frame / rst frame just
// happens to be like that ATM.
// Await stream/connection window update frame.
expect(
await nextFrame(),
isA<WindowUpdateFrame>()
.having((p0) => p0.header.streamId, 'Stream update', 1)
.having((p0) => p0.windowSizeIncrement, 'Windowsize', 1));
expect(
await nextFrame(),
isA<WindowUpdateFrame>()
.having((p0) => p0.header.streamId, 'Connection update', 0)
.having((p0) => p0.windowSizeIncrement, 'Windowsize', 1));
expect(
await nextFrame(),
isA<WindowUpdateFrame>()
.having((p0) => p0.header.streamId, 'Connection update', 0)
.having((p0) => p0.windowSizeIncrement, 'Windowsize', 1));
// Make sure we get a [RstStreamFrame] frame.
expect(
await nextFrame(),
isA<RstStreamFrame>()
.having((f) => f.errorCode, 'errorCode', ErrorCode.CANCEL)
.having(
(f) => f.header.streamId, 'header.streamId', streamId));
serverWriter.writeRstStreamFrame(streamId, ErrorCode.STREAM_CLOSED);
endDone.complete();
// Wait for the client finish.
expect(await nextFrame(), isA<GoawayFrame>());
expect(await serverReader.moveNext(), false);
await serverWriter.close();
}
Future clientFun() async {
await handshakeCompleter.future;
var stream = client.makeRequest([Header.ascii('a', 'b')]);
await stream.outgoingMessages.close();
// first will cancel the stream
var message = await stream.incomingMessages.first;
expect(
message,
isA<DataStreamMessage>()
.having((p0) => p0.bytes, 'Same sent data above', [42]),
);
cancelDone.complete();
await endDone.future;
await client.finish();
}
await Future.wait([serverFun(), clientFun()]);
});
clientTest('data-frame-received-after-stream-cancel-and-out-not-closed',
(ClientTransportConnection client,
FrameWriter serverWriter,
StreamIterator<Frame> serverReader,
Future<Frame> Function() nextFrame) async {
var handshakeCompleter = Completer<void>();
var cancelDone = Completer<void>();
var endDone = Completer<void>();
var clientDone = Completer<void>();
Future serverFun() async {
serverWriter.writeSettingsFrame([]);
expect(await nextFrame(), isA<SettingsFrame>());
serverWriter.writeSettingsAckFrame();
expect(await nextFrame(), isA<SettingsFrame>());
handshakeCompleter.complete();
var headers = await nextFrame() as HeadersFrame;
var streamId = headers.header.streamId;
// Write a data frame.
serverWriter.writeDataFrame(streamId, [42]);
await cancelDone.future;
serverWriter.writeDataFrame(streamId, [43]);
serverWriter.writeRstStreamFrame(streamId, ErrorCode.STREAM_CLOSED);
endDone.complete();
// NOTE: The order of the window update frame / rst frame just
// happens to be like that ATM.
// Await stream/connection window update frame.
expect(
await nextFrame(),
isA<WindowUpdateFrame>()
.having((p0) => p0.header.streamId, 'Stream update', 1)
.having((p0) => p0.windowSizeIncrement, 'Windowsize', 1));
expect(
await nextFrame(),
isA<WindowUpdateFrame>()
.having((p0) => p0.header.streamId, 'Connection update', 0)
.having((p0) => p0.windowSizeIncrement, 'Windowsize', 1));
expect(
await nextFrame(),
isA<WindowUpdateFrame>()
.having((p0) => p0.header.streamId, 'Connection update', 0)
.having((p0) => p0.windowSizeIncrement, 'Windowsize', 1));
await clientDone.future;
expect(
await nextFrame(),
isA<DataFrame>().having(
(p0) => p0.hasEndStreamFlag, 'Last data frame', true));
// Wait for the client finish.
expect(await serverReader.moveNext(), false);
await serverWriter.close();
}
Future clientFun() async {
await handshakeCompleter.future;
var stream = client.makeRequest([Header.ascii('a', 'b')]);
// first will cancel the stream
var message = await stream.incomingMessages.first;
expect(
message,
isA<DataStreamMessage>()
.having((p0) => p0.bytes, 'Same sent data above', [42]),
);
cancelDone.complete();
await endDone.future;
await stream.outgoingMessages.close();
clientDone.complete();
await client.finish();
}
await Future.wait([serverFun(), clientFun()]);
});
clientTest('client-reports-connection-error-on-push-to-nonexistent',
(ClientTransportConnection client,
FrameWriter serverWriter,
StreamIterator<Frame> serverReader,
Future<Frame> Function() nextFrame) async {
var handshakeCompleter = Completer<void>();
Future serverFun() async {
serverWriter.writeSettingsFrame([]);
expect(await nextFrame(), isA<SettingsFrame>());
serverWriter.writeSettingsAckFrame();
expect(await nextFrame(), isA<SettingsFrame>());
handshakeCompleter.complete();
var headers = await nextFrame() as HeadersFrame;
expect(
await nextFrame(),
isA<DataFrame>().having(
(p0) => p0.hasEndStreamFlag, 'Last data frame', true));
var streamId = headers.header.streamId;
// Write response.
serverWriter.writeHeadersFrame(streamId, [Header.ascii('a', 'b')],
endStream: true);
// Push stream to the (non existing) one.
var pushStreamId = 2;
serverWriter.writePushPromiseFrame(
streamId, pushStreamId, [Header.ascii('a', 'b')]);
// Make sure we get a connection error.
var frame = await nextFrame() as GoawayFrame;
expect(ascii.decode(frame.debugData),
contains('Cannot push on a non-existent stream'));
expect(await serverReader.moveNext(), false);
await serverWriter.close();
}
Future clientFun() async {
await handshakeCompleter.future;
var stream = client.makeRequest([Header.ascii('a', 'b')]);
await stream.outgoingMessages.close();
var messages = await stream.incomingMessages.toList();
expect(messages, hasLength(1));
expect(
messages[0],
isA<HeadersStreamMessage>().having((p0) => p0.headers.first,
'Same sent headers above', isHeader('a', 'b')),
);
await client.finish();
}
await Future.wait([serverFun(), clientFun()]);
});
clientTest('client-reports-connection-error-on-push-to-non-open',
(ClientTransportConnection client,
FrameWriter serverWriter,
StreamIterator<Frame> serverReader,
Future<Frame> Function() nextFrame) async {
var handshakeCompleter = Completer<void>();
Future serverFun() async {
serverWriter.writeSettingsFrame([]);
expect(await nextFrame(), isA<SettingsFrame>());
serverWriter.writeSettingsAckFrame();
expect(await nextFrame(), isA<SettingsFrame>());
handshakeCompleter.complete();
var headers = await nextFrame() as HeadersFrame;
var streamId = headers.header.streamId;
// Write response.
serverWriter.writeDataFrame(streamId, [], endStream: true);
// Push stream onto the existing (but half-closed) one.
var pushStreamId = 2;
serverWriter.writePushPromiseFrame(
streamId, pushStreamId, [Header.ascii('a', 'b')]);
// Make sure we get a connection error.
var frame = await nextFrame() as GoawayFrame;
expect(
ascii.decode(frame.debugData),
contains(
'Expected open state (was: StreamState.HalfClosedRemote)'));
expect(await serverReader.moveNext(), false);
await serverWriter.close();
}
Future clientFun() async {
await handshakeCompleter.future;
var stream = client.makeRequest([Header.ascii('a', 'b')]);
// NOTE: We are not closing the outgoing part on purpose.
expect(await stream.incomingMessages.toList(), isEmpty);
expect(await stream.peerPushes.toList(), isEmpty);
await client.finish();
}
await Future.wait([serverFun(), clientFun()]);
});
clientTest('client-reports-flowcontrol-error-on-negative-window',
(ClientTransportConnection client,
FrameWriter serverWriter,
StreamIterator<Frame> serverReader,
Future<Frame> Function() nextFrame) async {
var handshakeCompleter = Completer<void>();
Future serverFun() async {
serverWriter.writeSettingsFrame([]);
expect(await nextFrame(), isA<SettingsFrame>());
serverWriter.writeSettingsAckFrame();
expect(await nextFrame(), isA<SettingsFrame>());
handshakeCompleter.complete();
var headers = await nextFrame() as HeadersFrame;
var streamId = headers.header.streamId;
// Write more than [kFlowControlWindowSize] bytes.
final kFlowControlWindowSize = Window().size;
var sentBytes = 0;
final bytes = Uint8List(1024);
while (sentBytes <= kFlowControlWindowSize) {
serverWriter.writeDataFrame(streamId, bytes);
sentBytes += bytes.length;
}
// Read the resulting [GoawayFrame] and assert the error message
// describes that the flow control window became negative.
var frame = await nextFrame() as GoawayFrame;
expect(
ascii.decode(frame.debugData),
contains('Connection level flow control window became '
'negative.'));
expect(await serverReader.moveNext(), false);
await serverWriter.close();
}
Future clientFun() async {
await handshakeCompleter.future;
var stream = client.makeRequest([Header.ascii('a', 'b')]);
var sub = stream.incomingMessages.listen(
expectAsync1((StreamMessage msg) {}, count: 0),
onError: expectAsync1((Object error) {}));
sub.pause();
await Future<void>.delayed(const Duration(milliseconds: 40));
sub.resume();
await client.finish();
}
await Future.wait([serverFun(), clientFun()]);
});
});
group('client-errors', () {
clientTest('client-resets-stream', (ClientTransportConnection client,
FrameWriter serverWriter,
StreamIterator<Frame> serverReader,
Future<Frame> Function() nextFrame) async {
var settingsDone = Completer<void>();
var headersDone = Completer<void>();
Future serverFun() async {
var decoder = HPackDecoder();
serverWriter.writeSettingsFrame([]);
expect(await nextFrame(), isA<SettingsFrame>());
serverWriter.writeSettingsAckFrame();
expect(await nextFrame(), isA<SettingsFrame>());
settingsDone.complete();
// Make sure we got the new stream.
var frame = await nextFrame() as HeadersFrame;
expect(frame.hasEndStreamFlag, false);
var decodedHeaders = decoder.decode(frame.headerBlockFragment);
expect(decodedHeaders, hasLength(1));
expect(decodedHeaders[0], isHeader('a', 'b'));
headersDone.complete();
// Make sure we got the stream reset.
expect(
await nextFrame(),
isA<RstStreamFrame>().having(
(p0) => p0.errorCode, 'Stream reset', ErrorCode.CANCEL));
// Make sure we get the graceful shutdown message.
expect(
await nextFrame(),
isA<GoawayFrame>().having(
(p0) => p0.errorCode, 'Stream reset', ErrorCode.NO_ERROR));
// Make sure the client ended the connection.
expect(await serverReader.moveNext(), false);
}
Future clientFun() async {
await settingsDone.future;
// Make a new stream and terminate it.
var stream =
client.makeRequest([Header.ascii('a', 'b')], endStream: false);
await headersDone.future;
stream.terminate();
// Make sure we don't get messages/pushes on the terminated stream.
expect(await stream.incomingMessages.toList(), isEmpty);
expect(await stream.peerPushes.toList(), isEmpty);
// Try to gracefully finish the connection.
await client.finish();
}
await Future.wait([serverFun(), clientFun()]);
});
clientTest('goaway-terminates-nonprocessed-streams',
(ClientTransportConnection client,
FrameWriter serverWriter,
StreamIterator<Frame> serverReader,
Future<Frame> Function() nextFrame) async {
var settingsDone = Completer<void>();
Future serverFun() async {
var decoder = HPackDecoder();
serverWriter.writeSettingsFrame([]);
expect(await nextFrame(), isA<SettingsFrame>());
serverWriter.writeSettingsAckFrame();
expect(await nextFrame(), isA<SettingsFrame>());
settingsDone.complete();
// Make sure we got the new stream.
var frame = await nextFrame() as HeadersFrame;
expect(frame.hasEndStreamFlag, false);
var decodedHeaders = decoder.decode(frame.headerBlockFragment);
expect(decodedHeaders, hasLength(1));
expect(decodedHeaders[0], isHeader('a', 'b'));
// Send the GoawayFrame.
serverWriter.writeGoawayFrame(0, ErrorCode.NO_ERROR, []);
// Since there are no open streams left, the other end should just
// close the connection.
expect(await serverReader.moveNext(), false);
}
Future clientFun() async {
await settingsDone.future;
// Make a new stream and terminate it.
var stream =
client.makeRequest([Header.ascii('a', 'b')], endStream: false);
// Make sure we don't get messages/pushes on the terminated stream.
unawaited(
stream.incomingMessages.toList().catchError(expectAsync1((e) {
expect(
'$e',
contains('This stream was not processed and can '
'therefore be retried'));
return <StreamMessage>[];
})));
expect(await stream.peerPushes.toList(), isEmpty);
// Try to gracefully finish the connection.
await client.finish();
}
await Future.wait([serverFun(), clientFun()]);
});
});
});
}
void clientTest(
String name,
Future Function(ClientTransportConnection, FrameWriter,
StreamIterator<Frame> frameReader, Future<Frame> Function() readNext)
func,
) {
return test(name, () {
var streams = ClientStreams();
var serverReader = streams.serverConnectionFrameReader;
Future<Frame> readNext() async {
expect(await serverReader.moveNext(), true);
return serverReader.current;
}
return func(streams.clientConnection, streams.serverConnectionFrameWriter,
serverReader, readNext);
});
}
class ClientStreams {
final StreamController<List<int>> writeA = StreamController();
final StreamController<List<int>> writeB = StreamController();
Stream<List<int>> get readA => writeA.stream;
Stream<List<int>> get readB => writeB.stream;
StreamIterator<Frame> get serverConnectionFrameReader {
var localSettings = ActiveSettings();
var streamAfterConnectionPreface = readConnectionPreface(readA);
return StreamIterator(
FrameReader(streamAfterConnectionPreface, localSettings)
.startDecoding());
}
FrameWriter get serverConnectionFrameWriter {
var encoder = HPackEncoder();
var peerSettings = ActiveSettings();
return FrameWriter(encoder, writeB, peerSettings);
}
ClientTransportConnection get clientConnection =>
ClientTransportConnection.viaStreams(readB, writeA);
}