From 0e094cc8c0a8a30e095510d186da6654e3531a10 Mon Sep 17 00:00:00 2001 From: Moritz Date: Wed, 2 Apr 2025 08:02:02 +0200 Subject: [PATCH 1/3] Update to Dart `3.7.0` --- pkgs/http2/CHANGELOG.md | 2 +- pkgs/http2/example/display_headers.dart | 13 +- pkgs/http2/lib/multiprotocol_server.dart | 46 +- .../lib/src/artificial_server_socket.dart | 2 +- .../lib/src/async_utils/async_utils.dart | 11 +- pkgs/http2/lib/src/connection.dart | 207 ++++--- pkgs/http2/lib/src/connection_preface.dart | 21 +- .../src/flowcontrol/connection_queues.dart | 72 ++- .../lib/src/flowcontrol/queue_messages.dart | 22 +- .../lib/src/flowcontrol/stream_queues.dart | 46 +- .../lib/src/flowcontrol/window_handler.dart | 20 +- .../lib/src/frames/frame_defragmenter.dart | 16 +- pkgs/http2/lib/src/frames/frame_reader.dart | 150 +++-- pkgs/http2/lib/src/frames/frame_types.dart | 167 +++--- pkgs/http2/lib/src/frames/frame_writer.dart | 87 ++- pkgs/http2/lib/src/hpack/hpack.dart | 26 +- pkgs/http2/lib/src/hpack/huffman.dart | 8 +- pkgs/http2/lib/src/hpack/huffman_table.dart | 6 +- pkgs/http2/lib/src/ping/ping_handler.dart | 10 +- pkgs/http2/lib/src/settings/settings.dart | 41 +- .../http2/lib/src/streams/stream_handler.dart | 361 +++++++----- pkgs/http2/lib/transport.dart | 51 +- .../manual_test/out_of_stream_ids_test.dart | 13 +- pkgs/http2/pubspec.yaml | 12 +- pkgs/http2/test/client_test.dart | 534 +++++++++++------- .../http2/test/multiprotocol_server_test.dart | 76 ++- pkgs/http2/test/server_test.dart | 135 +++-- .../src/async_utils/async_utils_test.dart | 36 +- .../test/src/connection_preface_test.dart | 59 +- .../flowcontrol/connection_queues_test.dart | 40 +- pkgs/http2/test/src/flowcontrol/mocks.dart | 30 +- .../src/flowcontrol/stream_queues_test.dart | 50 +- .../src/flowcontrol/window_handler_test.dart | 39 +- .../src/frames/frame_defragmenter_test.dart | 61 +- .../test/src/frames/frame_reader_test.dart | 63 ++- .../src/frames/frame_writer_reader_test.dart | 95 +++- .../test/src/frames/frame_writer_test.dart | 8 +- pkgs/http2/test/src/hpack/hpack_test.dart | 126 +++-- .../test/src/hpack/huffman_table_test.dart | 2 +- .../test/src/ping/ping_handler_test.dart | 47 +- .../src/settings/settings_handler_test.dart | 60 +- pkgs/http2/test/src/streams/helper.dart | 9 +- .../test/src/streams/simple_flow_test.dart | 77 ++- .../test/src/streams/simple_push_test.dart | 60 +- pkgs/http2/test/src/streams/streams_test.dart | 294 ++++++---- pkgs/http2/test/transport_test.dart | 469 ++++++++------- 46 files changed, 2372 insertions(+), 1408 deletions(-) diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index 830df9de13..cd2d091452 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -1,4 +1,4 @@ -## 2.3.2-wip +## 3.0.0-wip ## 2.3.1 diff --git a/pkgs/http2/example/display_headers.dart b/pkgs/http2/example/display_headers.dart index 42dbf22b79..9ec1cb100e 100644 --- a/pkgs/http2/example/display_headers.dart +++ b/pkgs/http2/example/display_headers.dart @@ -54,11 +54,16 @@ void main(List args) async { Future connect(Uri uri) async { var useSSL = uri.scheme == 'https'; if (useSSL) { - var secureSocket = await SecureSocket.connect(uri.host, uri.port, - supportedProtocols: ['h2']); + var secureSocket = await SecureSocket.connect( + uri.host, + uri.port, + supportedProtocols: ['h2'], + ); if (secureSocket.selectedProtocol != 'h2') { - throw Exception('Failed to negogiate http/2 via alpn. Maybe server ' - "doesn't support http/2."); + throw Exception( + 'Failed to negogiate http/2 via alpn. Maybe server ' + "doesn't support http/2.", + ); } return secureSocket; } else { diff --git a/pkgs/http2/lib/multiprotocol_server.dart b/pkgs/http2/lib/multiprotocol_server.dart index 54fe6c0a60..0f837c6030 100644 --- a/pkgs/http2/lib/multiprotocol_server.dart +++ b/pkgs/http2/lib/multiprotocol_server.dart @@ -31,8 +31,10 @@ class MultiProtocolHttpServer { final _http2Connections = {}; MultiProtocolHttpServer._(this._serverSocket, this._settings) { - _http11Controller = - _ServerSocketController(_serverSocket.address, _serverSocket.port); + _http11Controller = _ServerSocketController( + _serverSocket.address, + _serverSocket.port, + ); _http11Server = HttpServer.listenOn(_http11Controller.stream); } @@ -45,8 +47,11 @@ class MultiProtocolHttpServer { /// /// See also [startServing]. static Future bind( - Object? address, int port, SecurityContext context, - {http2.ServerSettings? settings}) async { + Object? address, + int port, + SecurityContext context, { + http2.ServerSettings? settings, + }) async { context.setAlpnProtocols(['h2', 'h2-14', 'http/1.1'], true); var secureServer = await SecureServerSocket.bind(address, port, context); return MultiProtocolHttpServer._(secureServer, settings); @@ -63,21 +68,27 @@ class MultiProtocolHttpServer { /// /// It is expected that [callbackHttp11] and [callbackHttp2] will never throw /// an exception (i.e. these must take care of error handling themselves). - void startServing(void Function(HttpRequest) callbackHttp11, - void Function(http2.ServerTransportStream) callbackHttp2, - {void Function(dynamic error, StackTrace)? onError}) { + void startServing( + void Function(HttpRequest) callbackHttp11, + void Function(http2.ServerTransportStream) callbackHttp2, { + void Function(dynamic error, StackTrace)? onError, + }) { // 1. Start listening on the real [SecureServerSocket]. _serverSocket.listen((SecureSocket socket) { var protocol = socket.selectedProtocol; if (protocol == null || protocol == 'http/1.1') { _http11Controller.addHttp11Socket(socket); } else if (protocol == 'h2' || protocol == 'h2-14') { - var connection = http2.ServerTransportConnection.viaSocket(socket, - settings: _settings); + var connection = http2.ServerTransportConnection.viaSocket( + socket, + settings: _settings, + ); _http2Connections.add(connection); - connection.incomingStreams.listen(_http2Controller.add, - onError: onError, - onDone: () => _http2Connections.remove(connection)); + connection.incomingStreams.listen( + _http2Controller.add, + onError: onError, + onDone: () => _http2Connections.remove(connection), + ); } else { socket.destroy(); throw Exception('Unexpected negotiated ALPN protocol: $protocol.'); @@ -93,11 +104,12 @@ class MultiProtocolHttpServer { /// Closes this [MultiProtocolHttpServer]. /// /// Completes once everything has been successfully shut down. - Future close({bool force = false}) => - _serverSocket.close().whenComplete(() => Future.wait([ - _http11Server.close(force: force), - for (var c in _http2Connections) force ? c.terminate() : c.finish() - ])); + Future close({bool force = false}) => _serverSocket.close().whenComplete( + () => Future.wait([ + _http11Server.close(force: force), + for (var c in _http2Connections) force ? c.terminate() : c.finish(), + ]), + ); } /// An internal helper class. diff --git a/pkgs/http2/lib/src/artificial_server_socket.dart b/pkgs/http2/lib/src/artificial_server_socket.dart index 1a486fe062..47feb75126 100644 --- a/pkgs/http2/lib/src/artificial_server_socket.dart +++ b/pkgs/http2/lib/src/artificial_server_socket.dart @@ -14,7 +14,7 @@ import 'dart:io'; class ArtificialServerSocket extends StreamView implements ServerSocket { ArtificialServerSocket(this.address, this.port, Stream stream) - : super(stream); + : super(stream); // ######################################################################## // These are the methods of [ServerSocket] in addition to [Stream]. diff --git a/pkgs/http2/lib/src/async_utils/async_utils.dart b/pkgs/http2/lib/src/async_utils/async_utils.dart index 22a73e99ef..07aad5e933 100644 --- a/pkgs/http2/lib/src/async_utils/async_utils.dart +++ b/pkgs/http2/lib/src/async_utils/async_utils.dart @@ -68,8 +68,10 @@ class BufferedSink { // Currently `_doneFuture` will just complete normally if the sink // cancelled. }; - _doneFuture = - Future.wait([_controller.stream.pipe(dataSink), dataSink.done]); + _doneFuture = Future.wait([ + _controller.stream.pipe(dataSink), + dataSink.done, + ]); } /// The underlying sink. @@ -88,7 +90,7 @@ class BufferedBytesWriter { final BufferedSink _bufferedSink; BufferedBytesWriter(StreamSink> outgoing) - : _bufferedSink = BufferedSink(outgoing); + : _bufferedSink = BufferedSink(outgoing); /// An indicator whether the underlying sink is buffering at the moment. BufferIndicator get bufferIndicator => _bufferedSink.bufferIndicator; @@ -100,7 +102,8 @@ class BufferedBytesWriter { void add(List data) { if (_builder.length > 0) { throw StateError( - 'Cannot trigger an asynchronous write while there is buffered data.'); + 'Cannot trigger an asynchronous write while there is buffered data.', + ); } _bufferedSink.sink.add(data); } diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index 4e52e57f66..84cd8087e6 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -148,31 +148,41 @@ abstract class Connection { /// The state of this connection. late ConnectionState _state; - Connection(Stream> incoming, StreamSink> outgoing, - Settings settings, - {this.isClientConnection = true}) { + Connection( + Stream> incoming, + StreamSink> outgoing, + Settings settings, { + this.isClientConnection = true, + }) { _setupConnection(incoming, outgoing, settings); } /// Runs all setup necessary before new streams can be created with the remote /// peer. - void _setupConnection(Stream> incoming, - StreamSink> outgoing, Settings settingsObject) { + void _setupConnection( + Stream> incoming, + StreamSink> outgoing, + Settings settingsObject, + ) { // Setup frame reading. var incomingFrames = FrameReader(incoming, acknowledgedSettings).startDecoding(); - _frameReaderSubscription = incomingFrames.listen((Frame frame) { - _catchProtocolErrors(() => _handleFrameImpl(frame)); - }, onError: (error, stack) { - _terminate(ErrorCode.CONNECT_ERROR, causedByTransportError: true); - }, onDone: () { - // Ensure existing messages from lower levels are sent to the upper - // levels before we terminate everything. - _incomingQueue.forceDispatchIncomingMessages(); - _streams.forceDispatchIncomingMessages(); - - _terminate(ErrorCode.CONNECT_ERROR, causedByTransportError: true); - }); + _frameReaderSubscription = incomingFrames.listen( + (Frame frame) { + _catchProtocolErrors(() => _handleFrameImpl(frame)); + }, + onError: (error, stack) { + _terminate(ErrorCode.CONNECT_ERROR, causedByTransportError: true); + }, + onDone: () { + // Ensure existing messages from lower levels are sent to the upper + // levels before we terminate everything. + _incomingQueue.forceDispatchIncomingMessages(); + _streams.forceDispatchIncomingMessages(); + + _terminate(ErrorCode.CONNECT_ERROR, causedByTransportError: true); + }, + ); // Setup frame writing. _frameWriter = FrameWriter(_hpackContext.encoder, outgoing, peerSettings); @@ -181,8 +191,12 @@ abstract class Connection { }); // Setup handlers. - _settingsHandler = SettingsHandler(_hpackContext.encoder, _frameWriter, - acknowledgedSettings, peerSettings); + _settingsHandler = SettingsHandler( + _hpackContext.encoder, + _frameWriter, + acknowledgedSettings, + peerSettings, + ); _pingHandler = PingHandler(_frameWriter, _pingReceived); var settings = _decodeSettings(settingsObject); @@ -192,8 +206,10 @@ abstract class Connection { // TODO: The [error] can contain sensitive information we now expose via // a [Goaway] frame. We should somehow ensure we're only sending useful // but non-sensitive information. - _terminate(ErrorCode.PROTOCOL_ERROR, - message: 'Failed to set initial settings (error: $error).'); + _terminate( + ErrorCode.PROTOCOL_ERROR, + message: 'Failed to set initial settings (error: $error).', + ); }); _settingsHandler.onInitialWindowSizeChange.listen((int difference) { @@ -206,31 +222,39 @@ abstract class Connection { // size of the outgoing connection window. _connectionWindowHandler = OutgoingConnectionWindowHandler(_peerWindow); - var connectionWindowUpdater = - IncomingWindowHandler.connection(_frameWriter, _localWindow); + var connectionWindowUpdater = IncomingWindowHandler.connection( + _frameWriter, + _localWindow, + ); // Setup queues for outgoing/incoming messages on the connection level. - _outgoingQueue = - ConnectionMessageQueueOut(_connectionWindowHandler, _frameWriter); - _incomingQueue = - ConnectionMessageQueueIn(connectionWindowUpdater, _catchProtocolErrors); + _outgoingQueue = ConnectionMessageQueueOut( + _connectionWindowHandler, + _frameWriter, + ); + _incomingQueue = ConnectionMessageQueueIn( + connectionWindowUpdater, + _catchProtocolErrors, + ); if (isClientConnection) { _streams = StreamHandler.client( - _frameWriter, - _incomingQueue, - _outgoingQueue, - _settingsHandler.peerSettings, - _settingsHandler.acknowledgedSettings, - _activeStateHandler); + _frameWriter, + _incomingQueue, + _outgoingQueue, + _settingsHandler.peerSettings, + _settingsHandler.acknowledgedSettings, + _activeStateHandler, + ); } else { _streams = StreamHandler.server( - _frameWriter, - _incomingQueue, - _outgoingQueue, - _settingsHandler.peerSettings, - _settingsHandler.acknowledgedSettings, - _activeStateHandler); + _frameWriter, + _incomingQueue, + _outgoingQueue, + _settingsHandler.peerSettings, + _settingsHandler.acknowledgedSettings, + _activeStateHandler, + ); } // NOTE: We're not waiting until initial settings have been exchanged @@ -245,15 +269,17 @@ abstract class Connection { // By default a endpoint can make an unlimited number of concurrent streams. var concurrentStreamLimit = settings.concurrentStreamLimit; if (concurrentStreamLimit != null) { - settingsList.add(Setting( - Setting.SETTINGS_MAX_CONCURRENT_STREAMS, concurrentStreamLimit)); + settingsList.add( + Setting(Setting.SETTINGS_MAX_CONCURRENT_STREAMS, concurrentStreamLimit), + ); } // By default the stream level flow control window is 64 KiB. var streamWindowSize = settings.streamWindowSize; if (streamWindowSize != null) { - settingsList - .add(Setting(Setting.SETTINGS_INITIAL_WINDOW_SIZE, streamWindowSize)); + settingsList.add( + Setting(Setting.SETTINGS_INITIAL_WINDOW_SIZE, streamWindowSize), + ); } if (settings is ClientSettings) { @@ -274,7 +300,8 @@ abstract class Connection { Future ping() { return _pingHandler.ping().catchError((e, s) { return Future.error( - TransportException('The connection has been terminated.')); + TransportException('The connection has been terminated.'), + ); }, test: (e) => e is TerminatedException); } @@ -283,8 +310,12 @@ abstract class Connection { _finishing(active: true); // TODO: There is probably more we need to wait for. - return _streams.done.whenComplete(() => - Future.wait([_frameWriter.close(), _frameReaderSubscription.cancel()])); + return _streams.done.whenComplete( + () => Future.wait([ + _frameWriter.close(), + _frameReaderSubscription.cancel(), + ]), + ); } /// Terminates this connection forcefully. @@ -322,8 +353,10 @@ abstract class Connection { // we terminate the connection. if (_state.isInitialized) { if (frame is! SettingsFrame) { - _terminate(ErrorCode.PROTOCOL_ERROR, - message: 'Expected to first receive a settings frame.'); + _terminate( + ErrorCode.PROTOCOL_ERROR, + message: 'Expected to first receive a settings frame.', + ); return; } _state.state = ConnectionState.Operational; @@ -338,11 +371,13 @@ abstract class Connection { // [This needs to be done even if the frames get ignored, since the entire // connection shares one HPack compression context.] if (frame is HeadersFrame) { - frame.decodedHeaders = - _hpackContext.decoder.decode(frame.headerBlockFragment); + frame.decodedHeaders = _hpackContext.decoder.decode( + frame.headerBlockFragment, + ); } else if (frame is PushPromiseFrame) { - frame.decodedHeaders = - _hpackContext.decoder.decode(frame.headerBlockFragment); + frame.decodedHeaders = _hpackContext.decoder.decode( + frame.headerBlockFragment, + ); } if (_frameReceived.hasListener) { _frameReceived.add(null); @@ -363,7 +398,8 @@ abstract class Connection { // We can safely ignore these. } else { throw ProtocolException( - 'Cannot handle frame type ${frame.runtimeType} with stream-id 0.'); + 'Cannot handle frame type ${frame.runtimeType} with stream-id 0.', + ); } } else { _streams.processStreamFrame(_state, frame); @@ -392,10 +428,13 @@ abstract class Connection { _state.state = ConnectionState.Finishing; _state.finishingState |= ConnectionState.FinishingActive; - _outgoingQueue.enqueueMessage(GoawayMessage( + _outgoingQueue.enqueueMessage( + GoawayMessage( _streams.highestPeerInitiatedStream, ErrorCode.NO_ERROR, - message != null ? utf8.encode(message) : [])); + message != null ? utf8.encode(message) : [], + ), + ); } else { _state.state = ConnectionState.Finishing; _state.finishingState |= ConnectionState.FinishingPassive; @@ -407,18 +446,24 @@ abstract class Connection { /// Terminates this connection (if it is not already terminated). /// /// The returned future will never complete with an error. - Future _terminate(int errorCode, - {bool causedByTransportError = false, String? message}) { + Future _terminate( + int errorCode, { + bool causedByTransportError = false, + String? message, + }) { // TODO: When do we complete here? if (_state.state != ConnectionState.Terminated) { _state.state = ConnectionState.Terminated; var cancelFuture = Future.sync(_frameReaderSubscription.cancel); if (!causedByTransportError) { - _outgoingQueue.enqueueMessage(GoawayMessage( + _outgoingQueue.enqueueMessage( + GoawayMessage( _streams.highestPeerInitiatedStream, errorCode, - message != null ? utf8.encode(message) : [])); + message != null ? utf8.encode(message) : [], + ), + ); } var closeFuture = _frameWriter.close().catchError((e, s) { // We ignore any errors after writing to [GoawayFrame] @@ -428,7 +473,9 @@ abstract class Connection { // (e.g. if there is a pending connection.ping(), it's returned // Future will complete with this error). var exception = TransportConnectionException( - errorCode, 'Connection is being forcefully terminated.'); + errorCode, + 'Connection is being forcefully terminated.', + ); // Close all streams & stream queues _streams.terminate(exception); @@ -440,8 +487,10 @@ abstract class Connection { _pingHandler.terminate(exception); _settingsHandler.terminate(exception); - return Future.wait([cancelFuture, closeFuture]) - .catchError((_) => const []); + return Future.wait([ + cancelFuture, + closeFuture, + ]).catchError((_) => const []); } return Future.value(); } @@ -449,10 +498,13 @@ abstract class Connection { class ClientConnection extends Connection implements ClientTransportConnection { ClientConnection._(super.incoming, super.outgoing, super.settings) - : super(isClientConnection: true); + : super(isClientConnection: true); - factory ClientConnection(Stream> incoming, - StreamSink> outgoing, ClientSettings clientSettings) { + factory ClientConnection( + Stream> incoming, + StreamSink> outgoing, + ClientSettings clientSettings, + ) { outgoing.add(CONNECTION_PREFACE); return ClientConnection._(incoming, outgoing, clientSettings); } @@ -462,16 +514,20 @@ class ClientConnection extends Connection implements ClientTransportConnection { !_state.isFinishing && !_state.isTerminated && _streams.canOpenStream; @override - ClientTransportStream makeRequest(List
headers, - {bool endStream = false}) { + ClientTransportStream makeRequest( + List
headers, { + bool endStream = false, + }) { if (_state.isFinishing) { throw StateError( - 'The http/2 connection is finishing and can therefore not be used to ' - 'make new streams.'); + 'The http/2 connection is finishing and can therefore not be used to ' + 'make new streams.', + ); } else if (_state.isTerminated) { throw StateError( - 'The http/2 connection is no longer active and can therefore not be ' - 'used to make new streams.'); + 'The http/2 connection is no longer active and can therefore not be ' + 'used to make new streams.', + ); } var hStream = _streams.newStream(headers, endStream: endStream); if (_streams.ranOutOfStreamIds) { @@ -489,10 +545,13 @@ class ClientConnection extends Connection implements ClientTransportConnection { class ServerConnection extends Connection implements ServerTransportConnection { ServerConnection._(super.incoming, super.outgoing, super.settings) - : super(isClientConnection: false); + : super(isClientConnection: false); - factory ServerConnection(Stream> incoming, - StreamSink> outgoing, ServerSettings serverSettings) { + factory ServerConnection( + Stream> incoming, + StreamSink> outgoing, + ServerSettings serverSettings, + ) { var frameBytes = readConnectionPreface(incoming); return ServerConnection._(frameBytes, outgoing, serverSettings); } diff --git a/pkgs/http2/lib/src/connection_preface.dart b/pkgs/http2/lib/src/connection_preface.dart index 2f0b98c402..b747347cb4 100644 --- a/pkgs/http2/lib/src/connection_preface.dart +++ b/pkgs/http2/lib/src/connection_preface.dart @@ -33,7 +33,7 @@ const List CONNECTION_PREFACE = [ 0x0d, 0x0a, 0x0d, - 0x0a + 0x0a, ]; /// Reads the connection preface from [incoming]. @@ -96,14 +96,17 @@ Stream> readConnectionPreface(Stream> incoming) { } result.onListen = () { - subscription = - incoming.listen(onData, onError: result.addError, onDone: () { - if (!connectionPrefaceRead) { - terminate('EOS before connection preface could be read.'); - } else { - result.close(); - } - }); + subscription = incoming.listen( + onData, + onError: result.addError, + onDone: () { + if (!connectionPrefaceRead) { + terminate('EOS before connection preface could be read.'); + } else { + result.close(); + } + }, + ); result ..onPause = subscription.pause ..onResume = subscription.resume diff --git a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart index f1a499a903..0f4c86fd50 100644 --- a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart @@ -112,32 +112,47 @@ class ConnectionMessageQueueOut extends Object var message = _messages.first; if (message is HeadersMessage) { _messages.removeFirst(); - _frameWriter.writeHeadersFrame(message.streamId, message.headers, - endStream: message.endStream); + _frameWriter.writeHeadersFrame( + message.streamId, + message.headers, + endStream: message.endStream, + ); } else if (message is PushPromiseMessage) { _messages.removeFirst(); _frameWriter.writePushPromiseFrame( - message.streamId, message.promisedStreamId, message.headers); + message.streamId, + message.promisedStreamId, + message.headers, + ); } else if (message is DataMessage) { _messages.removeFirst(); if (_connectionWindow.peerWindowSize >= message.bytes.length) { _connectionWindow.decreaseWindow(message.bytes.length); - _frameWriter.writeDataFrame(message.streamId, message.bytes, - endStream: message.endStream); + _frameWriter.writeDataFrame( + message.streamId, + message.bytes, + endStream: message.endStream, + ); } else { // NOTE: We need to fragment the DataMessage. // TODO: Do not fragment if the number of bytes we can send is too low var len = _connectionWindow.peerWindowSize; var head = viewOrSublist(message.bytes, 0, len); - var tail = - viewOrSublist(message.bytes, len, message.bytes.length - len); + var tail = viewOrSublist( + message.bytes, + len, + message.bytes.length - len, + ); _connectionWindow.decreaseWindow(head.length); _frameWriter.writeDataFrame(message.streamId, head, endStream: false); - var tailMessage = - DataMessage(message.streamId, tail, message.endStream); + var tailMessage = DataMessage( + message.streamId, + tail, + message.endStream, + ); _messages.addFirst(tailMessage); } } else if (message is ResetStreamMessage) { @@ -146,7 +161,10 @@ class ConnectionMessageQueueOut extends Object } else if (message is GoawayMessage) { _messages.removeFirst(); _frameWriter.writeGoawayFrame( - message.lastStreamId, message.errorCode, message.debugData); + message.lastStreamId, + message.errorCode, + message.debugData, + ); } else { throw StateError('Unexpected message in queue: ${message.runtimeType}'); } @@ -195,7 +213,9 @@ class ConnectionMessageQueueIn extends Object int _count = 0; ConnectionMessageQueueIn( - this._windowUpdateHandler, this._catchProtocolErrors); + this._windowUpdateHandler, + this._catchProtocolErrors, + ); @override void onTerminated(Object? error) { @@ -224,8 +244,9 @@ class ConnectionMessageQueueIn extends Object void insertNewStreamMessageQueue(int streamId, StreamMessageQueueIn mq) { if (_stream2messageQueue.containsKey(streamId)) { throw ArgumentError( - 'Cannot register a SteramMessageQueueIn for the same streamId ' - 'multiple times'); + 'Cannot register a SteramMessageQueueIn for the same streamId ' + 'multiple times', + ); } var pendingMessages = Queue(); @@ -265,8 +286,11 @@ class ConnectionMessageQueueIn extends Object /// stream. void processHeadersFrame(HeadersFrame frame) { var streamId = frame.header.streamId; - var message = - HeadersMessage(streamId, frame.decodedHeaders, frame.hasEndStreamFlag); + var message = HeadersMessage( + streamId, + frame.decodedHeaders, + frame.hasEndStreamFlag, + ); // NOTE: Header frames do not affect flow control - only data frames do. _addMessage(streamId, message); } @@ -274,10 +298,17 @@ class ConnectionMessageQueueIn extends Object /// Processes an incoming [PushPromiseFrame] which is addressed to a specific /// stream. void processPushPromiseFrame( - PushPromiseFrame frame, ClientTransportStream pushedStream) { + PushPromiseFrame frame, + ClientTransportStream pushedStream, + ) { var streamId = frame.header.streamId; - var message = PushPromiseMessage(streamId, frame.decodedHeaders, - frame.promisedStreamId, pushedStream, false); + var message = PushPromiseMessage( + streamId, + frame.decodedHeaders, + frame.promisedStreamId, + pushedStream, + false, + ); // NOTE: // * Header frames do not affect flow control - only data frames do. @@ -307,7 +338,10 @@ class ConnectionMessageQueueIn extends Object } void _tryDispatch( - int streamId, StreamMessageQueueIn mq, Queue pendingMessages) { + int streamId, + StreamMessageQueueIn mq, + Queue pendingMessages, + ) { var bytesDeliveredToStream = 0; while (!mq.bufferIndicator.wouldBuffer && pendingMessages.isNotEmpty) { _count--; diff --git a/pkgs/http2/lib/src/flowcontrol/queue_messages.dart b/pkgs/http2/lib/src/flowcontrol/queue_messages.dart index 5c71228d45..735e46e0a3 100644 --- a/pkgs/http2/lib/src/flowcontrol/queue_messages.dart +++ b/pkgs/http2/lib/src/flowcontrol/queue_messages.dart @@ -20,7 +20,7 @@ class HeadersMessage extends Message { final List
headers; HeadersMessage(int streamId, this.headers, bool endStream) - : super(streamId, endStream); + : super(streamId, endStream); @override String toString() => @@ -31,7 +31,7 @@ class DataMessage extends Message { final List bytes; DataMessage(int streamId, this.bytes, bool endStream) - : super(streamId, endStream); + : super(streamId, endStream); @override String toString() => @@ -43,12 +43,17 @@ class PushPromiseMessage extends Message { final int promisedStreamId; final ClientTransportStream pushedStream; - PushPromiseMessage(int streamId, this.headers, this.promisedStreamId, - this.pushedStream, bool endStream) - : super(streamId, endStream); + PushPromiseMessage( + int streamId, + this.headers, + this.promisedStreamId, + this.pushedStream, + bool endStream, + ) : super(streamId, endStream); @override - String toString() => 'PushPromiseMessage(bytes: ${headers.length}, ' + String toString() => + 'PushPromiseMessage(bytes: ${headers.length}, ' 'promisedStreamId: $promisedStreamId, endStream: $endStream)'; } @@ -67,9 +72,10 @@ class GoawayMessage extends Message { final List debugData; GoawayMessage(this.lastStreamId, this.errorCode, this.debugData) - : super(0, false); + : super(0, false); @override - String toString() => 'GoawayMessage(lastStreamId: $lastStreamId, ' + String toString() => + 'GoawayMessage(lastStreamId: $lastStreamId, ' 'errorCode: $errorCode, debugData: ${debugData.length})'; } diff --git a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart index de7950a66e..b6d7d686aa 100644 --- a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart @@ -46,7 +46,10 @@ class StreamMessageQueueOut extends Object int writtenBytes = 0; StreamMessageQueueOut( - this.streamId, this.streamWindow, this.connectionMessageQueue) { + this.streamId, + this.streamWindow, + this.connectionMessageQueue, + ) { streamWindow.positiveWindow.bufferEmptyEvents.listen((_) { if (!wasTerminated) { _trySendData(); @@ -114,11 +117,17 @@ class StreamMessageQueueOut extends Object // TODO: Do not fragment if the number of bytes we can send is too low if (messageBytes.length > bytesAvailable) { var partA = viewOrSublist(messageBytes, 0, bytesAvailable); - var partB = viewOrSublist(messageBytes, bytesAvailable, - messageBytes.length - bytesAvailable); + var partB = viewOrSublist( + messageBytes, + bytesAvailable, + messageBytes.length - bytesAvailable, + ); var messageA = DataMessage(message.streamId, partA, false); - var messageB = - DataMessage(message.streamId, partB, message.endStream); + var messageB = DataMessage( + message.streamId, + partB, + message.endStream, + ); // Put the second fragment back into the front of the queue. _messages.addFirst(messageB); @@ -218,8 +227,10 @@ class StreamMessageQueueIn extends Object // NOTE: If server pushes were enabled, the client is responsible for // either rejecting or handling them. assert(!_serverPushStreamsC.isClosed); - var transportStreamPush = - TransportStreamPush(message.headers, message.pushedStream); + var transportStreamPush = TransportStreamPush( + message.headers, + message.pushedStream, + ); _serverPushStreamsC.add(transportStreamPush); return; } @@ -262,12 +273,14 @@ class StreamMessageQueueIn extends Object final message = _pendingMessages.removeFirst(); assert(!_incomingMessagesC.isClosed); if (message is HeadersMessage) { - _incomingMessagesC.add(HeadersStreamMessage(message.headers, - endStream: message.endStream)); + _incomingMessagesC.add( + HeadersStreamMessage(message.headers, endStream: message.endStream), + ); } else if (message is DataMessage) { if (message.bytes.isNotEmpty) { _incomingMessagesC.add( - DataStreamMessage(message.bytes, endStream: message.endStream)); + DataStreamMessage(message.bytes, endStream: message.endStream), + ); } } else { // This can never happen. @@ -293,12 +306,17 @@ class StreamMessageQueueIn extends Object if (message is HeadersMessage) { // NOTE: Header messages do not affect flow control - only // data messages do. - _incomingMessagesC.add(HeadersStreamMessage(message.headers, - endStream: message.endStream)); + _incomingMessagesC.add( + HeadersStreamMessage( + message.headers, + endStream: message.endStream, + ), + ); } else if (message is DataMessage) { if (message.bytes.isNotEmpty) { - _incomingMessagesC.add(DataStreamMessage(message.bytes, - endStream: message.endStream)); + _incomingMessagesC.add( + DataStreamMessage(message.bytes, endStream: message.endStream), + ); windowHandler.dataProcessed(message.bytes.length); } } else { diff --git a/pkgs/http2/lib/src/flowcontrol/window_handler.dart b/pkgs/http2/lib/src/flowcontrol/window_handler.dart index 27d0321694..a178c9b113 100644 --- a/pkgs/http2/lib/src/flowcontrol/window_handler.dart +++ b/pkgs/http2/lib/src/flowcontrol/window_handler.dart @@ -31,8 +31,9 @@ abstract class AbstractOutgoingWindowHandler { var increment = frame.windowSizeIncrement; if ((_peerWindow.size + increment) > Window.MAX_WINDOW_SIZE) { throw FlowControlException( - 'Window update received from remote peer would make flow control ' - 'window too large.'); + 'Window update received from remote peer would make flow control ' + 'window too large.', + ); } else { _peerWindow.modify(increment); } @@ -74,8 +75,9 @@ class OutgoingStreamWindowHandler extends AbstractOutgoingWindowHandler { void processInitialWindowSizeSettingChange(int difference) { if ((_peerWindow.size + difference) > Window.MAX_WINDOW_SIZE) { throw FlowControlException( - 'Window update received from remote peer would make flow control ' - 'window too large.'); + 'Window update received from remote peer would make flow control ' + 'window too large.', + ); } else { _peerWindow.modify(difference); if (_peerWindow.size <= 0) { @@ -102,10 +104,13 @@ class IncomingWindowHandler { final int _streamId; IncomingWindowHandler.stream( - this._frameWriter, this._localWindow, this._streamId); + this._frameWriter, + this._localWindow, + this._streamId, + ); IncomingWindowHandler.connection(this._frameWriter, this._localWindow) - : _streamId = 0; + : _streamId = 0; /// The current size for the incoming data window. /// @@ -141,7 +146,8 @@ class IncomingWindowHandler { // if (_localWindow.size < 0) { throw FlowControlException( - 'Connection level flow control window became negative.'); + 'Connection level flow control window became negative.', + ); } } diff --git a/pkgs/http2/lib/src/frames/frame_defragmenter.dart b/pkgs/http2/lib/src/frames/frame_defragmenter.dart index 5ab2c72426..f17fee04ae 100644 --- a/pkgs/http2/lib/src/frames/frame_defragmenter.dart +++ b/pkgs/http2/lib/src/frames/frame_defragmenter.dart @@ -34,7 +34,8 @@ class FrameDefragmenter { if (frame is ContinuationFrame) { if (_headersFrame!.header.streamId != frame.header.streamId) { throw ProtocolException( - 'Defragmentation: frames have different stream ids.'); + 'Defragmentation: frames have different stream ids.', + ); } _headersFrame = _headersFrame!.addBlockContinuation(frame); @@ -47,14 +48,16 @@ class FrameDefragmenter { } } else { throw ProtocolException( - 'Defragmentation: Incomplete frame must be followed by ' - 'continuation frame.'); + 'Defragmentation: Incomplete frame must be followed by ' + 'continuation frame.', + ); } } else if (_pushPromiseFrame != null) { if (frame is ContinuationFrame) { if (_pushPromiseFrame!.header.streamId != frame.header.streamId) { throw ProtocolException( - 'Defragmentation: frames have different stream ids.'); + 'Defragmentation: frames have different stream ids.', + ); } _pushPromiseFrame = _pushPromiseFrame!.addBlockContinuation(frame); @@ -67,8 +70,9 @@ class FrameDefragmenter { } } else { throw ProtocolException( - 'Defragmentation: Incomplete frame must be followed by ' - 'continuation frame.'); + 'Defragmentation: Incomplete frame must be followed by ' + 'continuation frame.', + ); } } else { if (frame is HeadersFrame) { diff --git a/pkgs/http2/lib/src/frames/frame_reader.dart b/pkgs/http2/lib/src/frames/frame_reader.dart index 7b692614aa..336e38f05b 100644 --- a/pkgs/http2/lib/src/frames/frame_reader.dart +++ b/pkgs/http2/lib/src/frames/frame_reader.dart @@ -47,7 +47,10 @@ class FrameReader { bufferedData.removeAt(0); } else { bufferedData[0] = viewOrSublist( - bufferedData[0], totalFrameLen, firstChunkLen - totalFrameLen); + bufferedData[0], + totalFrameLen, + firstChunkLen - totalFrameLen, + ); } bufferedLength -= totalFrameLen; @@ -68,45 +71,53 @@ class FrameReader { _framesController.close(); } - subscription = _inputStream.listen((List data) { - bufferedData.add(data); - bufferedLength += data.length; - - try { - while (true) { - header ??= tryReadHeader(); - if (header != null) { - if (header!.length > _localSettings.maxFrameSize) { - terminateWithError( - FrameSizeException('Incoming frame is too big.')); - return; - } - - var frame = tryReadFrame(header!); - - if (frame != null) { - _framesController.add(frame); - header = null; + subscription = _inputStream.listen( + (List data) { + bufferedData.add(data); + bufferedLength += data.length; + + try { + while (true) { + header ??= tryReadHeader(); + if (header != null) { + if (header!.length > _localSettings.maxFrameSize) { + terminateWithError( + FrameSizeException('Incoming frame is too big.'), + ); + return; + } + + var frame = tryReadFrame(header!); + + if (frame != null) { + _framesController.add(frame); + header = null; + } else { + break; + } } else { break; } - } else { - break; } + } catch (error, stack) { + terminateWithError(error, stack); } - } catch (error, stack) { + }, + onError: (Object error, StackTrace stack) { terminateWithError(error, stack); - } - }, onError: (Object error, StackTrace stack) { - terminateWithError(error, stack); - }, onDone: () { - if (bufferedLength == 0) { - _framesController.close(); - } else { - terminateWithError(FrameSizeException( - 'Incoming byte stream ended with incomplete frame')); - } - }); + }, + onDone: () { + if (bufferedLength == 0) { + _framesController.close(); + } else { + terminateWithError( + FrameSizeException( + 'Incoming byte stream ended with incomplete frame', + ), + ); + } + }, + ); _framesController ..onPause = subscription.pause @@ -188,29 +199,43 @@ class FrameReader { var headerBlockLen = frameEnd - offset - padLength; _checkFrameLengthCondition(headerBlockLen >= 0); var headerBlockFragment = viewOrSublist(bytes, offset, headerBlockLen); - return HeadersFrame(header, padLength, exclusiveDependency, - streamDependency, weight, headerBlockFragment); + return HeadersFrame( + header, + padLength, + exclusiveDependency, + streamDependency, + weight, + headerBlockFragment, + ); case FrameType.PRIORITY: _checkFrameLengthCondition( - (frameEnd - offset) == PriorityFrame.FIXED_FRAME_LENGTH, - message: 'Priority frame length must be exactly 5 bytes.'); + (frameEnd - offset) == PriorityFrame.FIXED_FRAME_LENGTH, + message: 'Priority frame length must be exactly 5 bytes.', + ); var exclusiveDependency = (bytes[offset] & 0x80) == 0x80; var streamDependency = readInt32(bytes, offset) & 0x7fffffff; var weight = bytes[offset + 4]; return PriorityFrame( - header, exclusiveDependency, streamDependency, weight); + header, + exclusiveDependency, + streamDependency, + weight, + ); case FrameType.RST_STREAM: _checkFrameLengthCondition( - (frameEnd - offset) == RstStreamFrame.FIXED_FRAME_LENGTH, - message: 'Rst frames must have a length of 4.'); + (frameEnd - offset) == RstStreamFrame.FIXED_FRAME_LENGTH, + message: 'Rst frames must have a length of 4.', + ); var errorCode = readInt32(bytes, offset); return RstStreamFrame(header, errorCode); case FrameType.SETTINGS: - _checkFrameLengthCondition((header.length % 6) == 0, - message: 'Settings frame length must be a multiple of 6 bytes.'); + _checkFrameLengthCondition( + (header.length % 6) == 0, + message: 'Settings frame length must be a multiple of 6 bytes.', + ); var count = header.length ~/ 6; var settings = []; @@ -221,8 +246,10 @@ class FrameReader { } var frame = SettingsFrame(header, settings); if (frame.hasAckFlag) { - _checkFrameLengthCondition(header.length == 0, - message: 'Settings frame length must 0 for ACKs.'); + _checkFrameLengthCondition( + header.length == 0, + message: 'Settings frame length must 0 for ACKs.', + ); } return frame; @@ -238,12 +265,17 @@ class FrameReader { _checkFrameLengthCondition(headerBlockLen >= 0); var headerBlockFragment = viewOrSublist(bytes, offset, headerBlockLen); return PushPromiseFrame( - header, padLength, promisedStreamId, headerBlockFragment); + header, + padLength, + promisedStreamId, + headerBlockFragment, + ); case FrameType.PING: _checkFrameLengthCondition( - (frameEnd - offset) == PingFrame.FIXED_FRAME_LENGTH, - message: 'Ping frames must have a length of 8.'); + (frameEnd - offset) == PingFrame.FIXED_FRAME_LENGTH, + message: 'Ping frames must have a length of 8.', + ); var opaqueData = readInt64(bytes, offset); return PingFrame(header, opaqueData); @@ -256,27 +288,35 @@ class FrameReader { case FrameType.WINDOW_UPDATE: _checkFrameLengthCondition( - (frameEnd - offset) == WindowUpdateFrame.FIXED_FRAME_LENGTH, - message: 'Window update frames must have a length of 4.'); + (frameEnd - offset) == WindowUpdateFrame.FIXED_FRAME_LENGTH, + message: 'Window update frames must have a length of 4.', + ); var windowSizeIncrement = readInt32(bytes, offset) & 0x7fffffff; return WindowUpdateFrame(header, windowSizeIncrement); case FrameType.CONTINUATION: - var headerBlockFragment = - viewOrSublist(bytes, offset, frameEnd - offset); + var headerBlockFragment = viewOrSublist( + bytes, + offset, + frameEnd - offset, + ); return ContinuationFrame(header, headerBlockFragment); default: // Unknown frames should be ignored according to spec. return UnknownFrame( - header, viewOrSublist(bytes, offset, frameEnd - offset)); + header, + viewOrSublist(bytes, offset, frameEnd - offset), + ); } } /// Checks that [condition] is `true` and raises an [FrameSizeException] /// otherwise. - void _checkFrameLengthCondition(bool condition, - {String message = 'Frame not long enough.'}) { + void _checkFrameLengthCondition( + bool condition, { + String message = 'Frame not long enough.', + }) { if (!condition) { throw FrameSizeException(message); } diff --git a/pkgs/http2/lib/src/frames/frame_types.dart b/pkgs/http2/lib/src/frames/frame_types.dart index 33c9e4aff1..9d8d3c4d55 100644 --- a/pkgs/http2/lib/src/frames/frame_types.dart +++ b/pkgs/http2/lib/src/frames/frame_types.dart @@ -44,8 +44,12 @@ class FrameHeader { FrameHeader(this.length, this.type, this.flags, this.streamId); - Map toJson() => - {'length': length, 'type': type, 'flags': flags, 'streamId': streamId}; + Map toJson() => { + 'length': length, + 'type': type, + 'flags': flags, + 'streamId': streamId, + }; } class Frame { @@ -73,12 +77,12 @@ class DataFrame extends Frame { bool get hasPaddedFlag => _isFlagSet(header.flags, FLAG_PADDED); @override - Map toJson() => super.toJson() - ..addAll({ - 'padLength': padLength, - 'bytes (length)': bytes.length, - 'bytes (up to 4 bytes)': bytes.length > 4 ? bytes.sublist(0, 4) : bytes, - }); + Map toJson() => + super.toJson()..addAll({ + 'padLength': padLength, + 'bytes (length)': bytes.length, + 'bytes (up to 4 bytes)': bytes.length > 4 ? bytes.sublist(0, 4) : bytes, + }); } class HeadersFrame extends Frame { @@ -120,30 +124,47 @@ class HeadersFrame extends Frame { var fragment = frame.headerBlockFragment; var flags = header.flags | frame.header.flags; var fh = FrameHeader( - header.length + fragment.length, header.type, flags, header.streamId); + header.length + fragment.length, + header.type, + flags, + header.streamId, + ); - var mergedHeaderBlockFragment = - Uint8List(headerBlockFragment.length + fragment.length); + var mergedHeaderBlockFragment = Uint8List( + headerBlockFragment.length + fragment.length, + ); mergedHeaderBlockFragment.setRange( - 0, headerBlockFragment.length, headerBlockFragment); + 0, + headerBlockFragment.length, + headerBlockFragment, + ); mergedHeaderBlockFragment.setRange( - headerBlockFragment.length, mergedHeaderBlockFragment.length, fragment); - - return HeadersFrame(fh, padLength, exclusiveDependency, streamDependency, - weight, mergedHeaderBlockFragment); + headerBlockFragment.length, + mergedHeaderBlockFragment.length, + fragment, + ); + + return HeadersFrame( + fh, + padLength, + exclusiveDependency, + streamDependency, + weight, + mergedHeaderBlockFragment, + ); } @override - Map toJson() => super.toJson() - ..addAll({ - 'padLength': padLength, - 'exclusiveDependency': exclusiveDependency, - 'streamDependency': streamDependency, - 'weight': weight, - 'headerBlockFragment (length)': headerBlockFragment.length - }); + Map toJson() => + super.toJson()..addAll({ + 'padLength': padLength, + 'exclusiveDependency': exclusiveDependency, + 'streamDependency': streamDependency, + 'weight': weight, + 'headerBlockFragment (length)': headerBlockFragment.length, + }); } class PriorityFrame extends Frame { @@ -161,12 +182,12 @@ class PriorityFrame extends Frame { ); @override - Map toJson() => super.toJson() - ..addAll({ - 'exclusiveDependency': exclusiveDependency, - 'streamDependency': streamDependency, - 'weight': weight, - }); + Map toJson() => + super.toJson()..addAll({ + 'exclusiveDependency': exclusiveDependency, + 'streamDependency': streamDependency, + 'weight': weight, + }); } class RstStreamFrame extends Frame { @@ -177,10 +198,7 @@ class RstStreamFrame extends Frame { RstStreamFrame(super.header, this.errorCode); @override - Map toJson() => super.toJson() - ..addAll({ - 'errorCode': errorCode, - }); + Map toJson() => super.toJson()..addAll({'errorCode': errorCode}); } class Setting { @@ -212,10 +230,9 @@ class SettingsFrame extends Frame { bool get hasAckFlag => _isFlagSet(header.flags, FLAG_ACK); @override - Map toJson() => super.toJson() - ..addAll({ - 'settings': settings.map((s) => s.toJson()).toList(), - }); + Map toJson() => + super.toJson() + ..addAll({'settings': settings.map((s) => s.toJson()).toList()}); } class PushPromiseFrame extends Frame { @@ -247,28 +264,43 @@ class PushPromiseFrame extends Frame { var fragment = frame.headerBlockFragment; var flags = header.flags | frame.header.flags; var fh = FrameHeader( - header.length + fragment.length, header.type, flags, header.streamId); + header.length + fragment.length, + header.type, + flags, + header.streamId, + ); - var mergedHeaderBlockFragment = - Uint8List(headerBlockFragment.length + fragment.length); + var mergedHeaderBlockFragment = Uint8List( + headerBlockFragment.length + fragment.length, + ); mergedHeaderBlockFragment.setRange( - 0, headerBlockFragment.length, headerBlockFragment); + 0, + headerBlockFragment.length, + headerBlockFragment, + ); mergedHeaderBlockFragment.setRange( - headerBlockFragment.length, mergedHeaderBlockFragment.length, fragment); + headerBlockFragment.length, + mergedHeaderBlockFragment.length, + fragment, + ); return PushPromiseFrame( - fh, padLength, promisedStreamId, mergedHeaderBlockFragment); + fh, + padLength, + promisedStreamId, + mergedHeaderBlockFragment, + ); } @override - Map toJson() => super.toJson() - ..addAll({ - 'padLength': padLength, - 'promisedStreamId': promisedStreamId, - 'headerBlockFragment (len)': headerBlockFragment.length, - }); + Map toJson() => + super.toJson()..addAll({ + 'padLength': padLength, + 'promisedStreamId': promisedStreamId, + 'headerBlockFragment (len)': headerBlockFragment.length, + }); } class PingFrame extends Frame { @@ -283,10 +315,7 @@ class PingFrame extends Frame { bool get hasAckFlag => _isFlagSet(header.flags, FLAG_ACK); @override - Map toJson() => super.toJson() - ..addAll({ - 'opaqueData': opaqueData, - }); + Map toJson() => super.toJson()..addAll({'opaqueData': opaqueData}); } class GoawayFrame extends Frame { @@ -297,12 +326,12 @@ class GoawayFrame extends Frame { GoawayFrame(super.header, this.lastStreamId, this.errorCode, this.debugData); @override - Map toJson() => super.toJson() - ..addAll({ - 'lastStreamId': lastStreamId, - 'errorCode': errorCode, - 'debugData (length)': debugData.length, - }); + Map toJson() => + super.toJson()..addAll({ + 'lastStreamId': lastStreamId, + 'errorCode': errorCode, + 'debugData (length)': debugData.length, + }); } class WindowUpdateFrame extends Frame { @@ -313,10 +342,8 @@ class WindowUpdateFrame extends Frame { WindowUpdateFrame(super.header, this.windowSizeIncrement); @override - Map toJson() => super.toJson() - ..addAll({ - 'windowSizeIncrement': windowSizeIncrement, - }); + Map toJson() => + super.toJson()..addAll({'windowSizeIncrement': windowSizeIncrement}); } class ContinuationFrame extends Frame { @@ -329,10 +356,9 @@ class ContinuationFrame extends Frame { bool get hasEndHeadersFlag => _isFlagSet(header.flags, FLAG_END_HEADERS); @override - Map toJson() => super.toJson() - ..addAll({ - 'headerBlockFragment (length)': headerBlockFragment.length, - }); + Map toJson() => + super.toJson() + ..addAll({'headerBlockFragment (length)': headerBlockFragment.length}); } class UnknownFrame extends Frame { @@ -341,8 +367,5 @@ class UnknownFrame extends Frame { UnknownFrame(super.header, this.data); @override - Map toJson() => super.toJson() - ..addAll({ - 'data (length)': data.length, - }); + Map toJson() => super.toJson()..addAll({'data (length)': data.length}); } diff --git a/pkgs/http2/lib/src/frames/frame_writer.dart b/pkgs/http2/lib/src/frames/frame_writer.dart index 50caad75c9..76d5e5402b 100644 --- a/pkgs/http2/lib/src/frames/frame_writer.dart +++ b/pkgs/http2/lib/src/frames/frame_writer.dart @@ -21,8 +21,10 @@ class FrameWriter { int _highestWrittenStreamId = 0; FrameWriter( - this._hpackEncoder, StreamSink> outgoing, this._peerSettings) - : _outWriter = BufferedBytesWriter(outgoing); + this._hpackEncoder, + StreamSink> outgoing, + this._peerSettings, + ) : _outWriter = BufferedBytesWriter(outgoing); /// A indicator whether writes would be buffered. BufferIndicator get bufferIndicator => _outWriter.bufferIndicator; @@ -34,8 +36,11 @@ class FrameWriter { void writeDataFrame(int streamId, List data, {bool endStream = false}) { while (data.length > _peerSettings.maxFrameSize) { var chunk = viewOrSublist(data, 0, _peerSettings.maxFrameSize); - data = viewOrSublist(data, _peerSettings.maxFrameSize, - data.length - _peerSettings.maxFrameSize); + data = viewOrSublist( + data, + _peerSettings.maxFrameSize, + data.length - _peerSettings.maxFrameSize, + ); _writeDataFrameNoFragment(streamId, chunk, false); } _writeDataFrameNoFragment(streamId, data, endStream); @@ -56,8 +61,11 @@ class FrameWriter { _writeData(buffer); } - void writeHeadersFrame(int streamId, List
headers, - {bool endStream = true}) { + void writeHeadersFrame( + int streamId, + List
headers, { + bool endStream = true, + }) { var fragment = _hpackEncoder.encode(headers); var maxSize = _peerSettings.maxFrameSize - HeadersFrame.MAX_CONSTANT_PAYLOAD; @@ -78,7 +86,11 @@ class FrameWriter { } void _writeHeadersFrameNoFragment( - int streamId, List fragment, bool endHeaders, bool endStream) { + int streamId, + List fragment, + bool endHeaders, + bool endStream, + ) { var type = FrameType.HEADERS; var flags = 0; if (endHeaders) flags |= HeadersFrame.FLAG_END_HEADERS; @@ -96,7 +108,10 @@ class FrameWriter { } void _writeContinuationFrame( - int streamId, List fragment, bool endHeaders) { + int streamId, + List fragment, + bool endHeaders, + ) { var type = FrameType.CONTINUATION; var flags = endHeaders ? ContinuationFrame.FLAG_END_HEADERS : 0; @@ -111,13 +126,18 @@ class FrameWriter { _writeData(buffer); } - void writePriorityFrame(int streamId, int streamDependency, int weight, - {bool exclusive = false}) { + void writePriorityFrame( + int streamId, + int streamDependency, + int weight, { + bool exclusive = false, + }) { var type = FrameType.PRIORITY; var flags = 0; - var buffer = - Uint8List(FRAME_HEADER_SIZE + PriorityFrame.FIXED_FRAME_LENGTH); + var buffer = Uint8List( + FRAME_HEADER_SIZE + PriorityFrame.FIXED_FRAME_LENGTH, + ); var offset = 0; _setFrameHeader(buffer, offset, type, flags, streamId, 5); @@ -137,8 +157,9 @@ class FrameWriter { var type = FrameType.RST_STREAM; var flags = 0; - var buffer = - Uint8List(FRAME_HEADER_SIZE + RstStreamFrame.FIXED_FRAME_LENGTH); + var buffer = Uint8List( + FRAME_HEADER_SIZE + RstStreamFrame.FIXED_FRAME_LENGTH, + ); var offset = 0; _setFrameHeader(buffer, offset, type, flags, streamId, 4); @@ -182,19 +203,30 @@ class FrameWriter { } void writePushPromiseFrame( - int streamId, int promisedStreamId, List
headers) { + int streamId, + int promisedStreamId, + List
headers, + ) { var fragment = _hpackEncoder.encode(headers); var maxSize = _peerSettings.maxFrameSize - PushPromiseFrame.MAX_CONSTANT_PAYLOAD; if (fragment.length < maxSize) { _writePushPromiseFrameNoFragmentation( - streamId, promisedStreamId, fragment, true); + streamId, + promisedStreamId, + fragment, + true, + ); } else { var chunk = fragment.sublist(0, maxSize); fragment = fragment.sublist(maxSize); _writePushPromiseFrameNoFragmentation( - streamId, promisedStreamId, chunk, false); + streamId, + promisedStreamId, + chunk, + false, + ); while (fragment.length > _peerSettings.maxFrameSize) { var chunk = fragment.sublist(0, _peerSettings.maxFrameSize); fragment = fragment.sublist(_peerSettings.maxFrameSize); @@ -205,7 +237,11 @@ class FrameWriter { } void _writePushPromiseFrameNoFragmentation( - int streamId, int promisedStreamId, List fragment, bool endHeaders) { + int streamId, + int promisedStreamId, + List fragment, + bool endHeaders, + ) { var type = FrameType.PUSH_PROMISE; var flags = endHeaders ? HeadersFrame.FLAG_END_HEADERS : 0; @@ -256,8 +292,9 @@ class FrameWriter { var type = FrameType.WINDOW_UPDATE; var flags = 0; - var buffer = - Uint8List(FRAME_HEADER_SIZE + WindowUpdateFrame.FIXED_FRAME_LENGTH); + var buffer = Uint8List( + FRAME_HEADER_SIZE + WindowUpdateFrame.FIXED_FRAME_LENGTH, + ); var offset = 0; _setFrameHeader(buffer, offset, type, flags, streamId, 4); @@ -280,8 +317,14 @@ class FrameWriter { /// The future which will complete once this writer is done. Future get doneFuture => _outWriter.doneFuture; - void _setFrameHeader(List bytes, int offset, int type, int flags, - int streamId, int length) { + void _setFrameHeader( + List bytes, + int offset, + int type, + int flags, + int streamId, + int length, + ) { setInt24(bytes, offset, length); bytes[3] = type; bytes[4] = flags; diff --git a/pkgs/http2/lib/src/hpack/hpack.dart b/pkgs/http2/lib/src/hpack/hpack.dart index de44266cdd..6ce7bf0513 100644 --- a/pkgs/http2/lib/src/hpack/hpack.dart +++ b/pkgs/http2/lib/src/hpack/hpack.dart @@ -31,9 +31,10 @@ class HPackContext { final HPackEncoder encoder = HPackEncoder(); final HPackDecoder decoder = HPackDecoder(); - HPackContext( - {int maxSendingHeaderTableSize = 4096, - int maxReceivingHeaderTableSize = 4096}) { + HPackContext({ + int maxSendingHeaderTableSize = 4096, + int maxReceivingHeaderTableSize = 4096, + }) { encoder.updateMaxSendingHeaderTableSize(maxSendingHeaderTableSize); decoder.updateMaxReceivingHeaderTableSize(maxReceivingHeaderTableSize); } @@ -140,16 +141,19 @@ class HPackDecoder { } else if (isWithoutIndexing) { headers.add(readHeaderFieldInternal(readInteger(4))); } else if (isNeverIndexing) { - headers - .add(readHeaderFieldInternal(readInteger(4), neverIndexed: true)); + headers.add( + readHeaderFieldInternal(readInteger(4), neverIndexed: true), + ); } else if (isDynamicTableSizeUpdate) { var newMaxSize = readInteger(5); if (newMaxSize <= _maxHeaderTableSize) { _table.updateMaxSize(newMaxSize); } else { - throw HPackDecodingException('Dynamic table size update failed: ' - 'A new value of $newMaxSize exceeds the limit of ' - '$_maxHeaderTableSize'); + throw HPackDecodingException( + 'Dynamic table size update failed: ' + 'A new value of $newMaxSize exceeds the limit of ' + '$_maxHeaderTableSize', + ); } } else { throw HPackDecodingException('Invalid encoding of headers.'); @@ -310,7 +314,8 @@ class IndexTable { Header lookup(int index) { if (index <= 0) { throw HPackDecodingException( - 'Invalid index (was: $index) for table lookup.'); + 'Invalid index (was: $index) for table lookup.', + ); } if (index < _staticTable.length) { return _staticTable[index]!; @@ -320,7 +325,8 @@ class IndexTable { return _dynamicTable[index]; } throw HPackDecodingException( - 'Invalid index (was: $index) for table lookup.'); + 'Invalid index (was: $index) for table lookup.', + ); } /// Adds a new header field to the dynamic table - and evicts entries as diff --git a/pkgs/http2/lib/src/hpack/huffman.dart b/pkgs/http2/lib/src/hpack/huffman.dart index aca4cde481..0f5324d0a4 100644 --- a/pkgs/http2/lib/src/hpack/huffman.dart +++ b/pkgs/http2/lib/src/hpack/huffman.dart @@ -53,8 +53,9 @@ class HuffmanDecoder { if (node.value != null) { if (node.value == EOS_BYTE) { throw HuffmanDecodingException( - 'More than 7 bit padding is not allowed. Found entire EOS ' - 'encoding'); + 'More than 7 bit padding is not allowed. Found entire EOS ' + 'encoding', + ); } buffer.addByte(node.value!); node = _root; @@ -67,7 +68,8 @@ class HuffmanDecoder { if (node != _root) { if (currentDepth > 7) { throw HuffmanDecodingException( - 'Incomplete encoding of a byte or more than 7 bit padding.'); + 'Incomplete encoding of a byte or more than 7 bit padding.', + ); } while (node.right != null) { diff --git a/pkgs/http2/lib/src/hpack/huffman_table.dart b/pkgs/http2/lib/src/hpack/huffman_table.dart index 488e1246d9..a61151bdcd 100644 --- a/pkgs/http2/lib/src/hpack/huffman_table.dart +++ b/pkgs/http2/lib/src/hpack/huffman_table.dart @@ -5,8 +5,10 @@ import 'huffman.dart'; /// The huffman codec for encoding/decoding HTTP/2 header blocks. -final HuffmanCodec http2HuffmanCodec = HuffmanCodec(HuffmanEncoder(_codeWords), - HuffmanDecoder(generateHuffmanTree(_codeWords))); +final HuffmanCodec http2HuffmanCodec = HuffmanCodec( + HuffmanEncoder(_codeWords), + HuffmanDecoder(generateHuffmanTree(_codeWords)), +); /// This is the integer representing the End-of-String symbol /// (it is not representable by a byte). diff --git a/pkgs/http2/lib/src/ping/ping_handler.dart b/pkgs/http2/lib/src/ping/ping_handler.dart index f9be1f9b44..d3dd7c056a 100644 --- a/pkgs/http2/lib/src/ping/ping_handler.dart +++ b/pkgs/http2/lib/src/ping/ping_handler.dart @@ -21,8 +21,8 @@ class PingHandler extends Object with TerminatableMixin { int _nextId = 1; PingHandler(this._frameWriter, StreamController pingStream) - : pingReceived = pingStream.sink, - isListeningToPings = (() => pingStream.hasListener); + : pingReceived = pingStream.sink, + isListeningToPings = (() => pingStream.hasListener); @override void onTerminated(Object? error) { @@ -30,7 +30,8 @@ class PingHandler extends Object with TerminatableMixin { _remainingPings.clear(); for (final ping in remainingPings) { ping.completeError( - error ?? 'Remaining ping completed with unspecified error'); + error ?? 'Remaining ping completed with unspecified error', + ); } } @@ -53,7 +54,8 @@ class PingHandler extends Object with TerminatableMixin { // 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.'); + 'Received ping ack with unknown opaque data.', + ); } } }); diff --git a/pkgs/http2/lib/src/settings/settings.dart b/pkgs/http2/lib/src/settings/settings.dart index 291c66856c..bc9942f9af 100644 --- a/pkgs/http2/lib/src/settings/settings.dart +++ b/pkgs/http2/lib/src/settings/settings.dart @@ -71,13 +71,14 @@ class ActiveSettings { /// enforced. The initial value of this setting is unlimited. int? maxHeaderListSize; - ActiveSettings( - {this.headerTableSize = 4096, - this.enablePush = true, - this.maxConcurrentStreams, - this.initialWindowSize = (1 << 16) - 1, - this.maxFrameSize = 1 << 14, - this.maxHeaderListSize}); + ActiveSettings({ + this.headerTableSize = 4096, + this.enablePush = true, + this.maxConcurrentStreams, + this.initialWindowSize = (1 << 16) - 1, + this.maxFrameSize = 1 << 14, + this.maxHeaderListSize, + }); } /// Handles remote and local connection [Setting]s. @@ -104,16 +105,21 @@ class SettingsHandler extends Object with TerminatableMixin { /// The peer settings, which we ACKed and are obeying. final ActiveSettings _peerSettings; - final _onInitialWindowSizeChangeController = - StreamController.broadcast(sync: true); + final _onInitialWindowSizeChangeController = StreamController.broadcast( + sync: true, + ); /// Events are fired when a SettingsFrame changes the initial size /// of stream windows. Stream get onInitialWindowSizeChange => _onInitialWindowSizeChangeController.stream; - SettingsHandler(this._hpackEncoder, this._frameWriter, - this._acknowledgedSettings, this._peerSettings); + SettingsHandler( + this._hpackEncoder, + this._frameWriter, + this._acknowledgedSettings, + this._peerSettings, + ); /// The settings for this endpoint of the connection which the remote peer /// has ACKed and uses. @@ -137,8 +143,9 @@ class SettingsHandler extends Object with TerminatableMixin { // which were never sent to the other side. We consider this definitly // an error. throw ProtocolException( - 'Received an acknowledged settings frame which did not have a ' - 'outstanding settings request.'); + 'Received an acknowledged settings frame which did not have a ' + 'outstanding settings request.', + ); } var settingChanges = _toBeAcknowledgedSettings.removeAt(0); var completer = _toBeAcknowledgedCompleters.removeAt(0); @@ -172,7 +179,10 @@ class SettingsHandler extends Object with TerminatableMixin { } void _modifySettings( - ActiveSettings base, List changes, bool peerSettings) { + ActiveSettings base, + List changes, + bool peerSettings, + ) { for (var setting in changes) { switch (setting.identifier) { case Setting.SETTINGS_ENABLE_PUSH: @@ -182,7 +192,8 @@ class SettingsHandler extends Object with TerminatableMixin { base.enablePush = true; } else { throw ProtocolException( - 'The push setting can be only set to 0 or 1.'); + 'The push setting can be only set to 0 or 1.', + ); } break; diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index 92a228df5e..92d734498a 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -66,20 +66,21 @@ class Http2StreamImpl extends TransportStream final ZoneUnaryCallback _canPushFun; final ZoneBinaryCallback> - _pushStreamFun; + _pushStreamFun; final ZoneUnaryCallback _terminateStreamFun; late StreamSubscription _outgoingCSubscription; Http2StreamImpl( - this.incomingQueue, - this.outgoingQueue, - this._outgoingC, - this.id, - this.windowHandler, - this._canPushFun, - this._pushStreamFun, - this._terminateStreamFun); + this.incomingQueue, + this.outgoingQueue, + this._outgoingC, + this.id, + this.windowHandler, + this._canPushFun, + this._pushStreamFun, + this._terminateStreamFun, + ); /// A stream of data and/or headers from the remote end. @override @@ -161,41 +162,62 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { final ActiveStateHandler _onActiveStateChanged; StreamHandler._( - this._frameWriter, - this.incomingQueue, - this.outgoingQueue, - this._peerSettings, - this._localSettings, - this._onActiveStateChanged, - this.nextStreamId, - this.lastRemoteStreamId); + this._frameWriter, + this.incomingQueue, + this.outgoingQueue, + this._peerSettings, + this._localSettings, + this._onActiveStateChanged, + this.nextStreamId, + this.lastRemoteStreamId, + ); factory StreamHandler.client( - FrameWriter writer, - ConnectionMessageQueueIn incomingQueue, - ConnectionMessageQueueOut outgoingQueue, - ActiveSettings peerSettings, - ActiveSettings localSettings, - ActiveStateHandler onActiveStateChanged) { - return StreamHandler._(writer, incomingQueue, outgoingQueue, peerSettings, - localSettings, onActiveStateChanged, 1, 0); + FrameWriter writer, + ConnectionMessageQueueIn incomingQueue, + ConnectionMessageQueueOut outgoingQueue, + ActiveSettings peerSettings, + ActiveSettings localSettings, + ActiveStateHandler onActiveStateChanged, + ) { + return StreamHandler._( + writer, + incomingQueue, + outgoingQueue, + peerSettings, + localSettings, + onActiveStateChanged, + 1, + 0, + ); } factory StreamHandler.server( - FrameWriter writer, - ConnectionMessageQueueIn incomingQueue, - ConnectionMessageQueueOut outgoingQueue, - ActiveSettings peerSettings, - ActiveSettings localSettings, - ActiveStateHandler onActiveStateChanged) { - return StreamHandler._(writer, incomingQueue, outgoingQueue, peerSettings, - localSettings, onActiveStateChanged, 2, -1); + FrameWriter writer, + ConnectionMessageQueueIn incomingQueue, + ConnectionMessageQueueOut outgoingQueue, + ActiveSettings peerSettings, + ActiveSettings localSettings, + ActiveStateHandler onActiveStateChanged, + ) { + return StreamHandler._( + writer, + incomingQueue, + outgoingQueue, + peerSettings, + localSettings, + onActiveStateChanged, + 2, + -1, + ); } @override void onTerminated(Object? error) { - _openStreams.values.toList().forEach((stream) => - _closeStreamAbnormally(stream, error, propagateException: true)); + _openStreams.values.toList().forEach( + (stream) => + _closeStreamAbnormally(stream, error, propagateException: true), + ); startClosing(); } @@ -219,14 +241,16 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { void processGoawayFrame(GoawayFrame frame) { var lastStreamId = frame.lastStreamId; - var streamIds = _openStreams.keys - .where((id) => id > lastStreamId && !_isPeerInitiatedStream(id)) - .toList(); + var streamIds = + _openStreams.keys + .where((id) => id > lastStreamId && !_isPeerInitiatedStream(id)) + .toList(); for (var id in streamIds) { var exception = StreamException( - id, - 'Remote end was telling us to stop. This stream was not processed ' - 'and can therefore be retried (on a new connection).'); + id, + 'Remote end was telling us to stop. This stream was not processed ' + 'and can therefore be retried (on a new connection).', + ); _closeStreamIdAbnormally(id, exception, propagateException: true); } } @@ -255,7 +279,8 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { if (MAX_STREAM_ID < nextStreamId) { throw StateError( - 'Cannot create new streams, since a wrap around would happen.'); + 'Cannot create new streams, since a wrap around would happen.', + ); } var streamId = nextStreamId; nextStreamId += 2; @@ -279,8 +304,10 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { // first frame for stream 7 is sent or received. if (remoteStreamId <= lastRemoteStreamId) { - throw ProtocolException('Remote tried to open new stream which is ' - 'not in "idle" state.'); + throw ProtocolException( + 'Remote tried to open new stream which is ' + 'not in "idle" state.', + ); } var sameDirection = (nextStreamId + remoteStreamId).isEven; @@ -302,26 +329,42 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { // * the underlying transport [outgoing] // - register incoming stream queue in connection-level queue - var outgoingStreamWindow = - Window(initialSize: _peerSettings.initialWindowSize); + var outgoingStreamWindow = Window( + initialSize: _peerSettings.initialWindowSize, + ); - var incomingStreamWindow = - Window(initialSize: _localSettings.initialWindowSize); + var incomingStreamWindow = Window( + initialSize: _localSettings.initialWindowSize, + ); var windowOutHandler = OutgoingStreamWindowHandler(outgoingStreamWindow); var windowInHandler = IncomingWindowHandler.stream( - _frameWriter, incomingStreamWindow, streamId); + _frameWriter, + incomingStreamWindow, + streamId, + ); var streamQueueIn = StreamMessageQueueIn(windowInHandler); - var streamQueueOut = - StreamMessageQueueOut(streamId, windowOutHandler, outgoingQueue); + var streamQueueOut = StreamMessageQueueOut( + streamId, + windowOutHandler, + outgoingQueue, + ); incomingQueue.insertNewStreamMessageQueue(streamId, streamQueueIn); var outgoingC = StreamController(); - var stream = Http2StreamImpl(streamQueueIn, streamQueueOut, outgoingC, - streamId, windowOutHandler, _canPush, _push, _terminateStream); + var stream = Http2StreamImpl( + streamQueueIn, + streamQueueOut, + outgoingC, + streamId, + windowOutHandler, + _canPush, + _push, + _terminateStream, + ); final wasIdle = _openStreams.isEmpty; _openStreams[stream.id] = stream; @@ -335,8 +378,9 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { // still sending us data, despite us not being interested in it, we will // reset the stream. if (stream.state == StreamState.HalfClosedLocal) { - stream.outgoingQueue - .enqueueMessage(ResetStreamMessage(stream.id, ErrorCode.CANCEL)); + stream.outgoingQueue.enqueueMessage( + ResetStreamMessage(stream.id, ErrorCode.CANCEL), + ); } }); @@ -355,7 +399,8 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { } bool _canPush(Http2StreamImpl stream) { - var openState = stream.state == StreamState.Open || + var openState = + stream.state == StreamState.Open || stream.state == StreamState.HalfClosedRemote; var pushEnabled = _peerSettings.enablePush; return openState && @@ -365,11 +410,15 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { } ServerTransportStream _push( - Http2StreamImpl stream, List
requestHeaders) { + Http2StreamImpl stream, + List
requestHeaders, + ) { if (stream.state != StreamState.Open && stream.state != StreamState.HalfClosedRemote) { - throw StateError('Cannot push based on a stream that is neither open ' - 'nor half-closed-remote.'); + throw StateError( + 'Cannot push based on a stream that is neither open ' + 'nor half-closed-remote.', + ); } if (!_peerSettings.enablePush) { @@ -381,8 +430,10 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { } if (_ranOutOfStreamIds()) { - throw StateError('There are no more stream ids left. Please use a ' - 'new connection.'); + throw StateError( + 'There are no more stream ids left. Please use a ' + 'new connection.', + ); } var pushStream = newLocalStream(); @@ -394,11 +445,15 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { // TODO: We should wait for us to send the headers frame before doing this // transition. _changeState(pushStream, StreamState.HalfClosedRemote); - pushStream.incomingQueue - .enqueueMessage(DataMessage(stream.id, const [], true)); + pushStream.incomingQueue.enqueueMessage( + DataMessage(stream.id, const [], true), + ); _frameWriter.writePushPromiseFrame( - stream.id, pushStream.id, requestHeaders); + stream.id, + pushStream.id, + requestHeaders, + ); return pushStream; } @@ -415,22 +470,25 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { } void _setupOutgoingMessageHandling(Http2StreamImpl stream) { - stream._outgoingCSubscription = - stream._outgoingC.stream.listen((StreamMessage msg) { - if (!wasTerminated) { - _handleNewOutgoingMessage(stream, msg); - } - }, onError: (error, stack) { - if (!wasTerminated) { - stream.terminate(); - } - }, onDone: () { - if (!wasTerminated) { - // Stream should already have been closed by the last frame, but we - // allow multiple close calls, just to make sure. - _handleOutgoingClose(stream); - } - }); + stream._outgoingCSubscription = stream._outgoingC.stream.listen( + (StreamMessage msg) { + if (!wasTerminated) { + _handleNewOutgoingMessage(stream, msg); + } + }, + onError: (error, stack) { + if (!wasTerminated) { + stream.terminate(); + } + }, + onDone: () { + if (!wasTerminated) { + // Stream should already have been closed by the last frame, but we + // allow multiple close calls, just to make sure. + _handleOutgoingClose(stream); + } + }, + ); stream.outgoingQueue.bufferIndicator.bufferEmptyEvents.listen((_) { if (stream._outgoingCSubscription.isPaused) { stream._outgoingCSubscription.resume(); @@ -442,7 +500,8 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { if (stream.state == StreamState.Idle) { if (msg is! HeadersStreamMessage) { var exception = TransportException( - 'The first message on a stream needs to be a headers frame.'); + 'The first message on a stream needs to be a headers frame.', + ); _closeStreamAbnormally(stream, exception); return; } @@ -479,17 +538,23 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { _processStreamFrameInternal(connectionState, frame); } on StreamClosedException catch (exception) { _frameWriter.writeRstStreamFrame( - exception.streamId, ErrorCode.STREAM_CLOSED); + exception.streamId, + ErrorCode.STREAM_CLOSED, + ); _closeStreamIdAbnormally(exception.streamId, exception); } on StreamException catch (exception) { _frameWriter.writeRstStreamFrame( - exception.streamId, ErrorCode.INTERNAL_ERROR); + exception.streamId, + ErrorCode.INTERNAL_ERROR, + ); _closeStreamIdAbnormally(exception.streamId, exception); } } void _processStreamFrameInternal( - ConnectionState connectionState, Frame frame) { + ConnectionState connectionState, + Frame frame, + ) { // If we initiated a close of the connection and the received frame belongs // to a stream id which is higher than the last peer-initiated stream we // processed, we'll ignore it. @@ -523,9 +588,10 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { var streamId = frame.header.streamId; var isServerStreamId = frame.header.streamId.isEven; var isLocalStream = isServerStreamId == isServer; - var isIdleStream = isLocalStream - ? streamId >= nextStreamId - : streamId > lastRemoteStreamId; + var isIdleStream = + isLocalStream + ? streamId >= nextStreamId + : streamId > lastRemoteStreamId; return isIdleStream; } @@ -533,8 +599,10 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { // Update highest stream id we received and processed (we update it // before processing, so if it was an error, the client will not // retry it). - _highestStreamIdReceived = - max(_highestStreamIdReceived, frame.header.streamId); + _highestStreamIdReceived = max( + _highestStreamIdReceived, + frame.header.streamId, + ); } if (frame is HeadersFrame) { @@ -548,15 +616,17 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { // A server cannot open new streams to the client. The only way // for a server to start a new stream is via a PUSH_PROMISE_FRAME. throw ProtocolException( - 'HTTP/2 clients cannot receive HEADER_FRAMEs as a connection' - 'attempt.'); + 'HTTP/2 clients cannot receive HEADER_FRAMEs as a connection' + 'attempt.', + ); } } else if (frame is WindowUpdateFrame) { if (frameBelongsToIdleStream()) { // We treat this as a protocol error even though not enforced // or specified by the HTTP/2 spec. throw ProtocolException( - 'Got a WINDOW_UPDATE_FRAME for an "idle" stream id.'); + 'Got a WINDOW_UPDATE_FRAME for an "idle" stream id.', + ); } else { // We must be able to receive window update frames for streams that // have been already closed. The specification does not mention @@ -568,7 +638,8 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { // [RstFrame]s for streams which haven't been established (known as // idle streams) must be treated as a connection error. throw ProtocolException( - 'Got a RST_STREAM_FRAME for an "idle" stream id.'); + 'Got a RST_STREAM_FRAME for an "idle" stream id.', + ); } else { // [RstFrame]s for already dead (known as "closed") streams should // be ignored. (If the stream was in "HalfClosedRemote" and we did @@ -591,8 +662,10 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { // TODO: When implementing priorities for HTTP/2 streams, these frames // need to be taken into account. } else if (frame is PushPromiseFrame) { - throw ProtocolException('Cannot push on a non-existent stream ' - '(stream ${frame.header.streamId} does not exist)'); + throw ProtocolException( + 'Cannot push on a non-existent stream ' + '(stream ${frame.header.streamId} does not exist)', + ); } else if (frame is DataFrame) { // http/2 spec: // However, after sending the RST_STREAM, the sending endpoint @@ -627,7 +700,8 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { _handleRstFrame(stream, frame); } else { throw ProtocolException( - 'Unsupported frame type ${frame.runtimeType}.'); + 'Unsupported frame type ${frame.runtimeType}.', + ); } } }); @@ -641,7 +715,9 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { if (stream.state != StreamState.Open && stream.state != StreamState.HalfClosedLocal) { throw StreamClosedException( - stream.id, 'Expected open state (was: ${stream.state}).'); + stream.id, + 'Expected open state (was: ${stream.state}).', + ); } incomingQueue.processHeadersFrame(frame); @@ -653,7 +729,9 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { if (stream.state != StreamState.Open && stream.state != StreamState.HalfClosedLocal) { throw StreamClosedException( - stream.id, 'Expected open state (was: ${stream.state}).'); + stream.id, + 'Expected open state (was: ${stream.state}).', + ); } incomingQueue.processDataFrame(frame); @@ -680,7 +758,8 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { void _handleRstFrame(Http2StreamImpl stream, RstStreamFrame frame) { stream._handleTerminated(frame.errorCode); var exception = StreamTransportException( - 'Stream was terminated by peer (errorCode: ${frame.errorCode}).'); + 'Stream was terminated by peer (errorCode: ${frame.errorCode}).', + ); _closeStreamAbnormally(stream, exception, propagateException: true); } @@ -697,9 +776,10 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { _openStreams.remove(stream.id); } else { throw StateError( - 'Got an end-of-stream from the remote end, but this stream is ' - 'neither in the Open nor in the HalfClosedLocal state. ' - 'This should never happen.'); + 'Got an end-of-stream from the remote end, but this stream is ' + 'neither in the Open nor in the HalfClosedLocal state. ' + 'This should never happen.', + ); } } @@ -707,16 +787,20 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { //// Process outgoing stream messages //////////////////////////////////////////////////////////////////////////// - void _sendHeaders(Http2StreamImpl stream, List
headers, - {bool endStream = false}) { + void _sendHeaders( + Http2StreamImpl stream, + List
headers, { + bool endStream = false, + }) { if (stream.state != StreamState.Idle && stream.state != StreamState.Open && stream.state != StreamState.HalfClosedRemote) { throw StateError('Idle state expected.'); } - stream.outgoingQueue - .enqueueMessage(HeadersMessage(stream.id, headers, endStream)); + stream.outgoingQueue.enqueueMessage( + HeadersMessage(stream.id, headers, endStream), + ); if (stream.state == StreamState.Idle) { _changeState(stream, StreamState.Open); @@ -727,15 +811,19 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { } } - void _sendData(Http2StreamImpl stream, List data, - {bool endStream = false}) { + void _sendData( + Http2StreamImpl stream, + List data, { + bool endStream = false, + }) { if (stream.state != StreamState.Open && stream.state != StreamState.HalfClosedRemote) { throw StateError('Open state expected (was: ${stream.state}).'); } - stream.outgoingQueue - .enqueueMessage(DataMessage(stream.id, data, endStream)); + stream.outgoingQueue.enqueueMessage( + DataMessage(stream.id, data, endStream), + ); if (endStream) { _endStream(stream); @@ -771,17 +859,26 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { onCheckForClose(); } - void _closeStreamIdAbnormally(int streamId, Exception exception, - {bool propagateException = false}) { + void _closeStreamIdAbnormally( + int streamId, + Exception exception, { + bool propagateException = false, + }) { var stream = _openStreams[streamId]; if (stream != null) { - _closeStreamAbnormally(stream, exception, - propagateException: propagateException); + _closeStreamAbnormally( + stream, + exception, + propagateException: propagateException, + ); } } - void _closeStreamAbnormally(Http2StreamImpl stream, Object? exception, - {bool propagateException = false}) { + void _closeStreamAbnormally( + Http2StreamImpl stream, + Object? exception, { + bool propagateException = false, + }) { incomingQueue.removeStreamMessageQueue(stream.id); if (stream.state != StreamState.Terminated) { @@ -831,21 +928,23 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { var from = stream.state; // In checked mode we'll test that the state transition is allowed. - assert((from == StreamState.Idle && to == StreamState.ReservedLocal) || - (from == StreamState.Idle && to == StreamState.ReservedRemote) || - (from == StreamState.Idle && to == StreamState.Open) || - (from == StreamState.Open && to == StreamState.HalfClosedLocal) || - (from == StreamState.Open && to == StreamState.HalfClosedRemote) || - (from == StreamState.Open && to == StreamState.Closed) || - (from == StreamState.HalfClosedLocal && to == StreamState.Closed) || - (from == StreamState.HalfClosedRemote && to == StreamState.Closed) || - (from == StreamState.ReservedLocal && - to == StreamState.HalfClosedRemote) || - (from == StreamState.ReservedLocal && to == StreamState.Closed) || - (from == StreamState.ReservedRemote && to == StreamState.Closed) || - (from == StreamState.ReservedRemote && - to == StreamState.HalfClosedLocal) || - (from != StreamState.Terminated && to == StreamState.Terminated)); + assert( + (from == StreamState.Idle && to == StreamState.ReservedLocal) || + (from == StreamState.Idle && to == StreamState.ReservedRemote) || + (from == StreamState.Idle && to == StreamState.Open) || + (from == StreamState.Open && to == StreamState.HalfClosedLocal) || + (from == StreamState.Open && to == StreamState.HalfClosedRemote) || + (from == StreamState.Open && to == StreamState.Closed) || + (from == StreamState.HalfClosedLocal && to == StreamState.Closed) || + (from == StreamState.HalfClosedRemote && to == StreamState.Closed) || + (from == StreamState.ReservedLocal && + to == StreamState.HalfClosedRemote) || + (from == StreamState.ReservedLocal && to == StreamState.Closed) || + (from == StreamState.ReservedRemote && to == StreamState.Closed) || + (from == StreamState.ReservedRemote && + to == StreamState.HalfClosedLocal) || + (from != StreamState.Terminated && to == StreamState.Terminated), + ); // If we initiated the stream and it became "open" or "closed" we need to // update the [_numberOfActiveStreams] counter. diff --git a/pkgs/http2/lib/transport.dart b/pkgs/http2/lib/transport.dart index 87a10f6799..2478763c34 100644 --- a/pkgs/http2/lib/transport.dart +++ b/pkgs/http2/lib/transport.dart @@ -36,10 +36,11 @@ class ClientSettings extends Settings { /// Whether the client allows pushes from the server (defaults to false). final bool allowServerPushes; - const ClientSettings( - {super.concurrentStreamLimit, - super.streamWindowSize, - this.allowServerPushes = false}); + const ClientSettings({ + super.concurrentStreamLimit, + super.streamWindowSize, + this.allowServerPushes = false, + }); } /// Represents a HTTP/2 connection. @@ -77,13 +78,17 @@ abstract class TransportConnection { } abstract class ClientTransportConnection extends TransportConnection { - factory ClientTransportConnection.viaSocket(Socket socket, - {ClientSettings? settings}) => + factory ClientTransportConnection.viaSocket( + Socket socket, { + ClientSettings? settings, + }) => ClientTransportConnection.viaStreams(socket, socket, settings: settings); factory ClientTransportConnection.viaStreams( - Stream> incoming, StreamSink> outgoing, - {ClientSettings? settings}) { + Stream> incoming, + StreamSink> outgoing, { + ClientSettings? settings, + }) { settings ??= const ClientSettings(); return ClientConnection(incoming, outgoing, settings); } @@ -93,21 +98,31 @@ abstract class ClientTransportConnection extends TransportConnection { bool get isOpen; /// Creates a new outgoing stream. - ClientTransportStream makeRequest(List
headers, - {bool endStream = false}); + ClientTransportStream makeRequest( + List
headers, { + bool endStream = false, + }); } abstract class ServerTransportConnection extends TransportConnection { - factory ServerTransportConnection.viaSocket(Socket socket, - {ServerSettings? settings}) { - return ServerTransportConnection.viaStreams(socket, socket, - settings: settings); + factory ServerTransportConnection.viaSocket( + Socket socket, { + ServerSettings? settings, + }) { + return ServerTransportConnection.viaStreams( + socket, + socket, + settings: settings, + ); } factory ServerTransportConnection.viaStreams( - Stream> incoming, StreamSink> outgoing, - {ServerSettings? settings = - const ServerSettings(concurrentStreamLimit: 1000)}) { + Stream> incoming, + StreamSink> outgoing, { + ServerSettings? settings = const ServerSettings( + concurrentStreamLimit: 1000, + ), + }) { settings ??= const ServerSettings(); return ServerConnection(incoming, outgoing, settings); } @@ -234,7 +249,7 @@ class TransportConnectionException extends TransportException { final int errorCode; TransportConnectionException(this.errorCode, String details) - : super('Connection error: $details (errorCode: $errorCode)'); + : super('Connection error: $details (errorCode: $errorCode)'); } /// An exception thrown when a HTTP/2 stream error occured. diff --git a/pkgs/http2/manual_test/out_of_stream_ids_test.dart b/pkgs/http2/manual_test/out_of_stream_ids_test.dart index acfbbd9cee..4368a14ab8 100644 --- a/pkgs/http2/manual_test/out_of_stream_ids_test.dart +++ b/pkgs/http2/manual_test/out_of_stream_ids_test.dart @@ -23,9 +23,10 @@ import '../test/transport_test.dart'; void main() { group('transport-test', () { - transportTest('client-runs-out-of-stream-ids', - (ClientTransportConnection client, - ServerTransportConnection server) async { + transportTest('client-runs-out-of-stream-ids', ( + ClientTransportConnection client, + ServerTransportConnection server, + ) async { Future serverFun() async { await for (ServerTransportStream stream in server.incomingStreams) { stream.sendHeaders([Header.ascii('x', 'y')], endStream: true); @@ -45,8 +46,10 @@ void main() { } expect(client.isOpen, false); - expect(() => client.makeRequest(headers), - throwsA(const TypeMatcher())); + expect( + () => client.makeRequest(headers), + throwsA(const TypeMatcher()), + ); await Future.delayed(const Duration(seconds: 1)); await client.finish(); diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index bb1c2ca16d..a39ecac51f 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -1,5 +1,5 @@ name: http2 -version: 2.3.2-wip +version: 3.0.0-wip description: A HTTP/2 implementation in Dart. repository: https://github.com/dart-lang/http/tree/master/pkgs/http2 @@ -9,10 +9,10 @@ topics: - protocols environment: - sdk: ^3.2.0 + sdk: ^3.7.0 dev_dependencies: - build_runner: ^2.3.0 - dart_flutter_team_lints: ^3.0.0 - mockito: ^5.3.2 - test: ^1.21.4 + build_runner: ^2.4.15 + dart_flutter_team_lints: ^3.5.1 + mockito: ^5.4.5 + test: ^1.25.15 diff --git a/pkgs/http2/test/client_test.dart b/pkgs/http2/test/client_test.dart index 726707f357..35a6392a77 100644 --- a/pkgs/http2/test/client_test.dart +++ b/pkgs/http2/test/client_test.dart @@ -19,11 +19,12 @@ 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 serverReader, - Future Function() nextFrame) async { + clientTest('gracefull-shutdown-for-unused-connection', ( + ClientTransportConnection client, + FrameWriter serverWriter, + StreamIterator serverReader, + Future Function() nextFrame, + ) async { var settingsDone = Completer(); Future serverFun() async { @@ -36,9 +37,13 @@ void main() { // Make sure we get the graceful shutdown message. expect( - await nextFrame(), - isA() - .having((f) => f.errorCode, 'errorCode', ErrorCode.NO_ERROR)); + await nextFrame(), + isA().having( + (f) => f.errorCode, + 'errorCode', + ErrorCode.NO_ERROR, + ), + ); // Make sure the client ended the connection. expect(await serverReader.moveNext(), false); @@ -62,11 +67,12 @@ void main() { }); group('connection-operational', () { - clientTest('on-connection-operational-fires', - (ClientTransportConnection client, - FrameWriter serverWriter, - StreamIterator serverReader, - Future Function() nextFrame) async { + clientTest('on-connection-operational-fires', ( + ClientTransportConnection client, + FrameWriter serverWriter, + StreamIterator serverReader, + Future Function() nextFrame, + ) async { final settingsDone = Completer(); Future serverFun() async { @@ -78,9 +84,13 @@ void main() { // Make sure we get the graceful shutdown message. expect( - await nextFrame(), - isA() - .having((f) => f.errorCode, 'errorCode', ErrorCode.NO_ERROR)); + await nextFrame(), + isA().having( + (f) => f.errorCode, + 'errorCode', + ErrorCode.NO_ERROR, + ), + ); // Make sure the client ended the connection. expect(await serverReader.moveNext(), false); @@ -88,8 +98,9 @@ void main() { Future clientFun() async { await settingsDone.future; - await client.onInitialPeerSettingsReceived - .timeout(const Duration(milliseconds: 20)); // Should complete + await client.onInitialPeerSettingsReceived.timeout( + const Duration(milliseconds: 20), + ); // Should complete expect(client.isOpen, true); @@ -104,11 +115,12 @@ void main() { await Future.wait([serverFun(), clientFun()]); }); - clientTest('on-connection-operational-does-not-fire', - (ClientTransportConnection client, - FrameWriter serverWriter, - StreamIterator serverReader, - Future Function() nextFrame) async { + clientTest('on-connection-operational-does-not-fire', ( + ClientTransportConnection client, + FrameWriter serverWriter, + StreamIterator serverReader, + Future Function() nextFrame, + ) async { final goawayReceived = Completer(); Future serverFun() async { serverWriter.writePingFrame(42); @@ -122,9 +134,11 @@ void main() { expect(client.isOpen, true); expect( - client.onInitialPeerSettingsReceived - .timeout(const Duration(seconds: 1)), - throwsA(isA())); + client.onInitialPeerSettingsReceived.timeout( + const Duration(seconds: 1), + ), + throwsA(isA()), + ); // We wait until the server received the error (it's actually later // than necessary, but we can't make a deterministic test otherwise). @@ -148,11 +162,12 @@ void main() { }); group('server-errors', () { - clientTest('no-settings-frame-at-beginning-immediate-error', - (ClientTransportConnection client, - FrameWriter serverWriter, - StreamIterator serverReader, - Future Function() nextFrame) async { + clientTest('no-settings-frame-at-beginning-immediate-error', ( + ClientTransportConnection client, + FrameWriter serverWriter, + StreamIterator serverReader, + Future Function() nextFrame, + ) async { var goawayReceived = Completer(); Future serverFun() async { serverWriter.writePingFrame(42); @@ -185,11 +200,12 @@ void main() { await Future.wait([serverFun(), clientFun()]); }); - clientTest('no-settings-frame-at-beginning-delayed-error', - (ClientTransportConnection client, - FrameWriter serverWriter, - StreamIterator serverReader, - Future Function() nextFrame) async { + clientTest('no-settings-frame-at-beginning-delayed-error', ( + ClientTransportConnection client, + FrameWriter serverWriter, + StreamIterator serverReader, + Future Function() nextFrame, + ) async { Future serverFun() async { expect(await nextFrame(), isA()); expect(await nextFrame(), isA()); @@ -215,11 +231,12 @@ void main() { await Future.wait([serverFun(), clientFun()]); }); - clientTest('data-frame-for-invalid-stream', - (ClientTransportConnection client, - FrameWriter serverWriter, - StreamIterator serverReader, - Future Function() nextFrame) async { + clientTest('data-frame-for-invalid-stream', ( + ClientTransportConnection client, + FrameWriter serverWriter, + StreamIterator serverReader, + Future Function() nextFrame, + ) async { var handshakeCompleter = Completer(); Future serverFun() async { @@ -232,9 +249,13 @@ void main() { var headers = await nextFrame() as HeadersFrame; expect( - await nextFrame(), - isA().having( - (p0) => p0.hasEndStreamFlag, 'Last data frame', true)); + await nextFrame(), + isA().having( + (p0) => p0.hasEndStreamFlag, + 'Last data frame', + true, + ), + ); // Write a data frame for a non-existent stream. var invalidStreamId = headers.header.streamId + 2; @@ -242,20 +263,34 @@ void main() { // Make sure the client sends a [RstStreamFrame] frame. expect( - await nextFrame(), - isA() - .having((p0) => p0.header.streamId, 'Connection update', 0)); + await nextFrame(), + isA().having( + (p0) => p0.header.streamId, + 'Connection update', + 0, + ), + ); expect( - await nextFrame(), - isA() - .having( - (f) => f.errorCode, 'errorCode', ErrorCode.STREAM_CLOSED) - .having((f) => f.header.streamId, 'header.streamId', - invalidStreamId)); + await nextFrame(), + isA() + .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); + serverWriter.writeDataFrame( + headers.header.streamId, + [], + endStream: true, + ); // Wait for the client finish. expect(await nextFrame(), isA()); @@ -276,11 +311,12 @@ void main() { await Future.wait([serverFun(), clientFun()]); }); - clientTest('data-frame-after-stream-closed', - (ClientTransportConnection client, - FrameWriter serverWriter, - StreamIterator serverReader, - Future Function() nextFrame) async { + clientTest('data-frame-after-stream-closed', ( + ClientTransportConnection client, + FrameWriter serverWriter, + StreamIterator serverReader, + Future Function() nextFrame, + ) async { var handshakeCompleter = Completer(); Future serverFun() async { @@ -293,9 +329,13 @@ void main() { var headers = await nextFrame() as HeadersFrame; expect( - await nextFrame(), - isA().having( - (p0) => p0.hasEndStreamFlag, 'Last data frame', true)); + await nextFrame(), + isA().having( + (p0) => p0.hasEndStreamFlag, + 'Last data frame', + true, + ), + ); var streamId = headers.header.streamId; @@ -312,36 +352,51 @@ void main() { // The two WindowUpdateFrames for the data1 DataFrame. expect( - await nextFrame(), - isA() - .having((p0) => p0.header.streamId, 'Stream update', 1) - .having((p0) => p0.windowSizeIncrement, 'Windowsize', - data1.length)); + await nextFrame(), + isA() + .having((p0) => p0.header.streamId, 'Stream update', 1) + .having( + (p0) => p0.windowSizeIncrement, + 'Windowsize', + data1.length, + ), + ); expect( - await nextFrame(), - isA() - .having((p0) => p0.header.streamId, 'Connection update', 0) - .having((p0) => p0.windowSizeIncrement, 'Windowsize', - data1.length)); + await nextFrame(), + isA() + .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() - .having((p0) => p0.header.streamId, 'Connection update', 0) - .having((p0) => p0.windowSizeIncrement, 'Windowsize', - data2.length)); + await nextFrame(), + isA() + .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() - .having( - (f) => f.errorCode, 'errorCode', ErrorCode.STREAM_CLOSED) - .having( - (f) => f.header.streamId, 'header.streamId', streamId)); + await nextFrame(), + isA() + .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()); @@ -358,8 +413,11 @@ void main() { expect(messages, hasLength(1)); expect( messages[0], - isA() - .having((p0) => p0.bytes, 'Same as `data1` above', [42, 42]), + isA().having( + (p0) => p0.bytes, + 'Same as `data1` above', + [42, 42], + ), ); await client.finish(); @@ -368,11 +426,12 @@ void main() { await Future.wait([serverFun(), clientFun()]); }); - clientTest('data-frame-received-after-stream-cancel', - (ClientTransportConnection client, - FrameWriter serverWriter, - StreamIterator serverReader, - Future Function() nextFrame) async { + clientTest('data-frame-received-after-stream-cancel', ( + ClientTransportConnection client, + FrameWriter serverWriter, + StreamIterator serverReader, + Future Function() nextFrame, + ) async { var handshakeCompleter = Completer(); var cancelDone = Completer(); var endDone = Completer(); @@ -387,9 +446,13 @@ void main() { var headers = await nextFrame() as HeadersFrame; expect( - await nextFrame(), - isA().having( - (p0) => p0.hasEndStreamFlag, 'Last data frame', true)); + await nextFrame(), + isA().having( + (p0) => p0.hasEndStreamFlag, + 'Last data frame', + true, + ), + ); var streamId = headers.header.streamId; // Write a data frame. @@ -402,28 +465,31 @@ void main() { // Await stream/connection window update frame. expect( - await nextFrame(), - isA() - .having((p0) => p0.header.streamId, 'Stream update', 1) - .having((p0) => p0.windowSizeIncrement, 'Windowsize', 1)); + await nextFrame(), + isA() + .having((p0) => p0.header.streamId, 'Stream update', 1) + .having((p0) => p0.windowSizeIncrement, 'Windowsize', 1), + ); expect( - await nextFrame(), - isA() - .having((p0) => p0.header.streamId, 'Connection update', 0) - .having((p0) => p0.windowSizeIncrement, 'Windowsize', 1)); + await nextFrame(), + isA() + .having((p0) => p0.header.streamId, 'Connection update', 0) + .having((p0) => p0.windowSizeIncrement, 'Windowsize', 1), + ); expect( - await nextFrame(), - isA() - .having((p0) => p0.header.streamId, 'Connection update', 0) - .having((p0) => p0.windowSizeIncrement, 'Windowsize', 1)); + await nextFrame(), + isA() + .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() - .having((f) => f.errorCode, 'errorCode', ErrorCode.CANCEL) - .having( - (f) => f.header.streamId, 'header.streamId', streamId)); + await nextFrame(), + isA() + .having((f) => f.errorCode, 'errorCode', ErrorCode.CANCEL) + .having((f) => f.header.streamId, 'header.streamId', streamId), + ); serverWriter.writeRstStreamFrame(streamId, ErrorCode.STREAM_CLOSED); @@ -445,8 +511,11 @@ void main() { var message = await stream.incomingMessages.first; expect( message, - isA() - .having((p0) => p0.bytes, 'Same sent data above', [42]), + isA().having( + (p0) => p0.bytes, + 'Same sent data above', + [42], + ), ); cancelDone.complete(); @@ -458,11 +527,12 @@ void main() { await Future.wait([serverFun(), clientFun()]); }); - clientTest('data-frame-received-after-stream-cancel-and-out-not-closed', - (ClientTransportConnection client, - FrameWriter serverWriter, - StreamIterator serverReader, - Future Function() nextFrame) async { + clientTest('data-frame-received-after-stream-cancel-and-out-not-closed', ( + ClientTransportConnection client, + FrameWriter serverWriter, + StreamIterator serverReader, + Future Function() nextFrame, + ) async { var handshakeCompleter = Completer(); var cancelDone = Completer(); var endDone = Completer(); @@ -493,26 +563,33 @@ void main() { // Await stream/connection window update frame. expect( - await nextFrame(), - isA() - .having((p0) => p0.header.streamId, 'Stream update', 1) - .having((p0) => p0.windowSizeIncrement, 'Windowsize', 1)); + await nextFrame(), + isA() + .having((p0) => p0.header.streamId, 'Stream update', 1) + .having((p0) => p0.windowSizeIncrement, 'Windowsize', 1), + ); expect( - await nextFrame(), - isA() - .having((p0) => p0.header.streamId, 'Connection update', 0) - .having((p0) => p0.windowSizeIncrement, 'Windowsize', 1)); + await nextFrame(), + isA() + .having((p0) => p0.header.streamId, 'Connection update', 0) + .having((p0) => p0.windowSizeIncrement, 'Windowsize', 1), + ); expect( - await nextFrame(), - isA() - .having((p0) => p0.header.streamId, 'Connection update', 0) - .having((p0) => p0.windowSizeIncrement, 'Windowsize', 1)); + await nextFrame(), + isA() + .having((p0) => p0.header.streamId, 'Connection update', 0) + .having((p0) => p0.windowSizeIncrement, 'Windowsize', 1), + ); await clientDone.future; expect( - await nextFrame(), - isA().having( - (p0) => p0.hasEndStreamFlag, 'Last data frame', true)); + await nextFrame(), + isA().having( + (p0) => p0.hasEndStreamFlag, + 'Last data frame', + true, + ), + ); // Wait for the client finish. expect(await serverReader.moveNext(), false); @@ -528,8 +605,11 @@ void main() { var message = await stream.incomingMessages.first; expect( message, - isA() - .having((p0) => p0.bytes, 'Same sent data above', [42]), + isA().having( + (p0) => p0.bytes, + 'Same sent data above', + [42], + ), ); cancelDone.complete(); @@ -545,11 +625,12 @@ void main() { await Future.wait([serverFun(), clientFun()]); }); - clientTest('client-reports-connection-error-on-push-to-nonexistent', - (ClientTransportConnection client, - FrameWriter serverWriter, - StreamIterator serverReader, - Future Function() nextFrame) async { + clientTest('client-reports-connection-error-on-push-to-nonexistent', ( + ClientTransportConnection client, + FrameWriter serverWriter, + StreamIterator serverReader, + Future Function() nextFrame, + ) async { var handshakeCompleter = Completer(); Future serverFun() async { @@ -562,25 +643,33 @@ void main() { var headers = await nextFrame() as HeadersFrame; expect( - await nextFrame(), - isA().having( - (p0) => p0.hasEndStreamFlag, 'Last data frame', true)); + await nextFrame(), + isA().having( + (p0) => p0.hasEndStreamFlag, + 'Last data frame', + true, + ), + ); var streamId = headers.header.streamId; // Write response. - serverWriter.writeHeadersFrame(streamId, [Header.ascii('a', 'b')], - endStream: true); + 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')]); + 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( + ascii.decode(frame.debugData), + contains('Cannot push on a non-existent stream'), + ); expect(await serverReader.moveNext(), false); await serverWriter.close(); } @@ -595,8 +684,11 @@ void main() { expect( messages[0], - isA().having((p0) => p0.headers.first, - 'Same sent headers above', isHeader('a', 'b')), + isA().having( + (p0) => p0.headers.first, + 'Same sent headers above', + isHeader('a', 'b'), + ), ); await client.finish(); @@ -605,11 +697,12 @@ void main() { await Future.wait([serverFun(), clientFun()]); }); - clientTest('client-reports-connection-error-on-push-to-non-open', - (ClientTransportConnection client, - FrameWriter serverWriter, - StreamIterator serverReader, - Future Function() nextFrame) async { + clientTest('client-reports-connection-error-on-push-to-non-open', ( + ClientTransportConnection client, + FrameWriter serverWriter, + StreamIterator serverReader, + Future Function() nextFrame, + ) async { var handshakeCompleter = Completer(); Future serverFun() async { @@ -628,15 +721,16 @@ void main() { // Push stream onto the existing (but half-closed) one. var pushStreamId = 2; - serverWriter.writePushPromiseFrame( - streamId, pushStreamId, [Header.ascii('a', 'b')]); + 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)')); + ascii.decode(frame.debugData), + contains('Expected open state (was: StreamState.HalfClosedRemote)'), + ); expect(await serverReader.moveNext(), false); await serverWriter.close(); } @@ -656,11 +750,12 @@ void main() { await Future.wait([serverFun(), clientFun()]); }); - clientTest('client-reports-flowcontrol-error-on-negative-window', - (ClientTransportConnection client, - FrameWriter serverWriter, - StreamIterator serverReader, - Future Function() nextFrame) async { + clientTest('client-reports-flowcontrol-error-on-negative-window', ( + ClientTransportConnection client, + FrameWriter serverWriter, + StreamIterator serverReader, + Future Function() nextFrame, + ) async { var handshakeCompleter = Completer(); Future serverFun() async { @@ -687,9 +782,12 @@ void main() { // 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.')); + ascii.decode(frame.debugData), + contains( + 'Connection level flow control window became ' + 'negative.', + ), + ); expect(await serverReader.moveNext(), false); await serverWriter.close(); } @@ -699,8 +797,9 @@ void main() { var stream = client.makeRequest([Header.ascii('a', 'b')]); var sub = stream.incomingMessages.listen( - expectAsync1((StreamMessage msg) {}, count: 0), - onError: expectAsync1((Object error) {})); + expectAsync1((StreamMessage msg) {}, count: 0), + onError: expectAsync1((Object error) {}), + ); sub.pause(); await Future.delayed(const Duration(milliseconds: 40)); sub.resume(); @@ -713,10 +812,12 @@ void main() { }); group('client-errors', () { - clientTest('client-resets-stream', (ClientTransportConnection client, - FrameWriter serverWriter, - StreamIterator serverReader, - Future Function() nextFrame) async { + clientTest('client-resets-stream', ( + ClientTransportConnection client, + FrameWriter serverWriter, + StreamIterator serverReader, + Future Function() nextFrame, + ) async { var settingsDone = Completer(); var headersDone = Completer(); @@ -741,15 +842,23 @@ void main() { // Make sure we got the stream reset. expect( - await nextFrame(), - isA().having( - (p0) => p0.errorCode, 'Stream reset', ErrorCode.CANCEL)); + await nextFrame(), + isA().having( + (p0) => p0.errorCode, + 'Stream reset', + ErrorCode.CANCEL, + ), + ); // Make sure we get the graceful shutdown message. expect( - await nextFrame(), - isA().having( - (p0) => p0.errorCode, 'Stream reset', ErrorCode.NO_ERROR)); + await nextFrame(), + isA().having( + (p0) => p0.errorCode, + 'Stream reset', + ErrorCode.NO_ERROR, + ), + ); // Make sure the client ended the connection. expect(await serverReader.moveNext(), false); @@ -759,8 +868,9 @@ void main() { await settingsDone.future; // Make a new stream and terminate it. - var stream = - client.makeRequest([Header.ascii('a', 'b')], endStream: false); + var stream = client.makeRequest([ + Header.ascii('a', 'b'), + ], endStream: false); await headersDone.future; stream.terminate(); @@ -776,11 +886,12 @@ void main() { await Future.wait([serverFun(), clientFun()]); }); - clientTest('goaway-terminates-nonprocessed-streams', - (ClientTransportConnection client, - FrameWriter serverWriter, - StreamIterator serverReader, - Future Function() nextFrame) async { + clientTest('goaway-terminates-nonprocessed-streams', ( + ClientTransportConnection client, + FrameWriter serverWriter, + StreamIterator serverReader, + Future Function() nextFrame, + ) async { var settingsDone = Completer(); Future serverFun() async { @@ -812,18 +923,25 @@ void main() { await settingsDone.future; // Make a new stream and terminate it. - var stream = - client.makeRequest([Header.ascii('a', 'b')], endStream: false); + 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 []; - }))); + stream.incomingMessages.toList().catchError( + expectAsync1((e) { + expect( + '$e', + contains( + 'This stream was not processed and can ' + 'therefore be retried', + ), + ); + return []; + }), + ), + ); expect(await stream.peerPushes.toList(), isEmpty); // Try to gracefully finish the connection. @@ -838,9 +956,13 @@ void main() { void clientTest( String name, - Future Function(ClientTransportConnection, FrameWriter, - StreamIterator frameReader, Future Function() readNext) - func, + Future Function( + ClientTransportConnection, + FrameWriter, + StreamIterator frameReader, + Future Function() readNext, + ) + func, ) { return test(name, () { var streams = ClientStreams(); @@ -851,8 +973,12 @@ void clientTest( return serverReader.current; } - return func(streams.clientConnection, streams.serverConnectionFrameWriter, - serverReader, readNext); + return func( + streams.clientConnection, + streams.serverConnectionFrameWriter, + serverReader, + readNext, + ); }); } @@ -866,8 +992,8 @@ class ClientStreams { var localSettings = ActiveSettings(); var streamAfterConnectionPreface = readConnectionPreface(readA); return StreamIterator( - FrameReader(streamAfterConnectionPreface, localSettings) - .startDecoding()); + FrameReader(streamAfterConnectionPreface, localSettings).startDecoding(), + ); } FrameWriter get serverConnectionFrameWriter { diff --git a/pkgs/http2/test/multiprotocol_server_test.dart b/pkgs/http2/test/multiprotocol_server_test.dart index 736e7da6b3..515716fc05 100644 --- a/pkgs/http2/test/multiprotocol_server_test.dart +++ b/pkgs/http2/test/multiprotocol_server_test.dart @@ -11,9 +11,13 @@ import 'package:http2/transport.dart'; import 'package:test/test.dart'; void main() { - var context = SecurityContext() - ..useCertificateChain('test/certificates/server_chain.pem') - ..usePrivateKey('test/certificates/server_key.pem', password: 'dartdart'); + var context = + SecurityContext() + ..useCertificateChain('test/certificates/server_chain.pem') + ..usePrivateKey( + 'test/certificates/server_key.pem', + password: 'dartdart', + ); group('multiprotocol-server', () { test('http/1.1', () async { @@ -22,16 +26,17 @@ void main() { var server = await MultiProtocolHttpServer.bind('localhost', 0, context); var requestNr = 0; server.startServing( - expectAsync1((HttpRequest request) async { - await handleHttp11Request(request, requestNr++); - if (requestNr == Count) { - await server.close(); - } - }, count: Count), - expectAsync1((ServerTransportStream stream) {}, count: 0)); + expectAsync1((HttpRequest request) async { + await handleHttp11Request(request, requestNr++); + if (requestNr == Count) { + await server.close(); + } + }, count: Count), + expectAsync1((ServerTransportStream stream) {}, count: 0), + ); var client = HttpClient(); - client.badCertificateCallback = (_, __, ___) => true; + client.badCertificateCallback = (_, _, _) => true; for (var i = 0; i < Count; i++) { await makeHttp11Request(server, client, i); } @@ -43,17 +48,21 @@ void main() { var server = await MultiProtocolHttpServer.bind('localhost', 0, context); var requestNr = 0; server.startServing( - expectAsync1((HttpRequest request) {}, count: 0), - expectAsync1((ServerTransportStream stream) async { - await handleHttp2Request(stream, requestNr++); - if (requestNr == Count) { - await server.close(); - } - }, count: Count)); - - var socket = await SecureSocket.connect('localhost', server.port, - onBadCertificate: (_) => true, - supportedProtocols: ['http/1.1', 'h2']); + expectAsync1((HttpRequest request) {}, count: 0), + expectAsync1((ServerTransportStream stream) async { + await handleHttp2Request(stream, requestNr++); + if (requestNr == Count) { + await server.close(); + } + }, count: Count), + ); + + var socket = await SecureSocket.connect( + 'localhost', + server.port, + onBadCertificate: (_) => true, + supportedProtocols: ['http/1.1', 'h2'], + ); var connection = ClientTransportConnection.viaSocket(socket); for (var i = 0; i < Count; i++) { await makeHttp2Request(server, connection, i); @@ -64,9 +73,13 @@ void main() { } Future makeHttp11Request( - MultiProtocolHttpServer server, HttpClient client, int i) async { - var request = - await client.getUrl(Uri.parse('https://localhost:${server.port}/abc$i')); + MultiProtocolHttpServer server, + HttpClient client, + int i, +) async { + var request = await client.getUrl( + Uri.parse('https://localhost:${server.port}/abc$i'), + ); var response = await request.close(); var body = await response.cast>().transform(utf8.decoder).join(''); expect(body, 'answer$i'); @@ -79,8 +92,11 @@ Future handleHttp11Request(HttpRequest request, int i) async { await request.response.close(); } -Future makeHttp2Request(MultiProtocolHttpServer server, - ClientTransportConnection connection, int i) async { +Future makeHttp2Request( + MultiProtocolHttpServer server, + ClientTransportConnection connection, + int i, +) async { expect(connection.isOpen, true); var headers = [ Header.ascii(':method', 'GET'), @@ -113,9 +129,9 @@ Future handleHttp2Request(ServerTransportStream stream, int i) async { expect(headers[':path'], '/abc$i'); expect(await si.moveNext(), false); - stream.outgoingMessages.add(HeadersStreamMessage([ - Header.ascii(':status', '200'), - ])); + stream.outgoingMessages.add( + HeadersStreamMessage([Header.ascii(':status', '200')]), + ); stream.outgoingMessages.add(DataStreamMessage(ascii.encode('answer$i'))); await stream.outgoingMessages.close(); diff --git a/pkgs/http2/test/server_test.dart b/pkgs/http2/test/server_test.dart index 1af51c929a..2aac5fe2e2 100644 --- a/pkgs/http2/test/server_test.dart +++ b/pkgs/http2/test/server_test.dart @@ -14,11 +14,12 @@ import 'package:test/test.dart'; void main() { group('server-tests', () { group('normal', () { - serverTest('gracefull-shutdown-for-unused-connection', - (ServerTransportConnection server, - FrameWriter clientWriter, - StreamIterator clientReader, - Future Function() nextFrame) async { + serverTest('gracefull-shutdown-for-unused-connection', ( + ServerTransportConnection server, + FrameWriter clientWriter, + StreamIterator clientReader, + Future Function() nextFrame, + ) async { Future serverFun() async { expect(await server.incomingStreams.toList(), isEmpty); await server.finish(); @@ -42,11 +43,12 @@ void main() { }); group('client-errors', () { - serverTest('no-settings-frame-at-beginning', - (ServerTransportConnection server, - FrameWriter clientWriter, - StreamIterator clientReader, - Future Function() nextFrame) async { + serverTest('no-settings-frame-at-beginning', ( + ServerTransportConnection server, + FrameWriter clientWriter, + StreamIterator clientReader, + Future Function() nextFrame, + ) async { Future serverFun() async { // TODO: Do we want to get an error in this case? expect(await server.incomingStreams.toList(), isEmpty); @@ -61,9 +63,13 @@ void main() { // Make sure the client gets a [GoawayFrame] frame. expect( - await nextFrame(), - isA().having( - (f) => f.errorCode, 'errorCode', ErrorCode.PROTOCOL_ERROR)); + await nextFrame(), + isA().having( + (f) => f.errorCode, + 'errorCode', + ErrorCode.PROTOCOL_ERROR, + ), + ); // Make sure the server ended the connection. expect(await clientReader.moveNext(), false); @@ -72,11 +78,12 @@ void main() { await Future.wait([serverFun(), clientFun()]); }); - serverTest('data-frame-for-invalid-stream', - (ServerTransportConnection server, - FrameWriter clientWriter, - StreamIterator clientReader, - Future Function() nextFrame) async { + serverTest('data-frame-for-invalid-stream', ( + ServerTransportConnection server, + FrameWriter clientWriter, + StreamIterator clientReader, + Future Function() nextFrame, + ) async { Future serverFun() async { await server.incomingStreams.toList(); await server.finish(); @@ -95,11 +102,15 @@ void main() { var frame = await nextFrame(); expect(frame is WindowUpdateFrame, true); expect( - await nextFrame(), - isA() - .having( - (f) => f.errorCode, 'errorCode', ErrorCode.STREAM_CLOSED) - .having((f) => f.header.streamId, 'header.streamId', 3)); + await nextFrame(), + isA() + .having( + (f) => f.errorCode, + 'errorCode', + ErrorCode.STREAM_CLOSED, + ) + .having((f) => f.header.streamId, 'header.streamId', 3), + ); // Tell the server to finish. clientWriter.writeGoawayFrame(3, ErrorCode.NO_ERROR, []); @@ -111,11 +122,12 @@ void main() { await Future.wait([serverFun(), clientFun()]); }); - serverTest('data-frame-after-stream-closed', - (ServerTransportConnection server, - FrameWriter clientWriter, - StreamIterator clientReader, - Future Function() nextFrame) async { + serverTest('data-frame-after-stream-closed', ( + ServerTransportConnection server, + FrameWriter clientWriter, + StreamIterator clientReader, + Future Function() nextFrame, + ) async { Future serverFun() async { await server.incomingStreams.toList(); await server.finish(); @@ -127,8 +139,9 @@ void main() { clientWriter.writeSettingsFrame([]); expect(await nextFrame() is SettingsFrame, true); - clientWriter.writeHeadersFrame(3, [Header.ascii('a', 'b')], - endStream: true); + clientWriter.writeHeadersFrame(3, [ + Header.ascii('a', 'b'), + ], endStream: true); // Write data frame to non-existent stream (stream 3 was closed // above). @@ -136,11 +149,15 @@ void main() { // Make sure the client gets a [RstStreamFrame] frame. expect( - await nextFrame(), - isA() - .having( - (f) => f.errorCode, 'errorCode', ErrorCode.STREAM_CLOSED) - .having((f) => f.header.streamId, 'header.streamId', 3)); + await nextFrame(), + isA() + .having( + (f) => f.errorCode, + 'errorCode', + ErrorCode.STREAM_CLOSED, + ) + .having((f) => f.header.streamId, 'header.streamId', 3), + ); // Tell the server to finish. clientWriter.writeGoawayFrame(3, ErrorCode.NO_ERROR, []); @@ -154,10 +171,12 @@ void main() { }); group('server-errors', () { - serverTest('server-resets-stream', (ServerTransportConnection server, - FrameWriter clientWriter, - StreamIterator clientReader, - Future Function() nextFrame) async { + serverTest('server-resets-stream', ( + ServerTransportConnection server, + FrameWriter clientWriter, + StreamIterator clientReader, + Future Function() nextFrame, + ) async { Future serverFun() async { var it = StreamIterator(server.incomingStreams); expect(await it.moveNext(), true); @@ -176,15 +195,17 @@ void main() { clientWriter.writeSettingsFrame([]); expect(await nextFrame() is SettingsFrame, true); - clientWriter.writeHeadersFrame(1, [Header.ascii('a', 'b')], - endStream: false); + clientWriter.writeHeadersFrame(1, [ + Header.ascii('a', 'b'), + ], endStream: false); // Make sure the client gets a [RstStreamFrame] frame. expect( - await nextFrame(), - isA() - .having((f) => f.errorCode, 'errorCode', ErrorCode.CANCEL) - .having((f) => f.header.streamId, 'header.streamId', 1)); + await nextFrame(), + isA() + .having((f) => f.errorCode, 'errorCode', ErrorCode.CANCEL) + .having((f) => f.header.streamId, 'header.streamId', 1), + ); // Tell the server to finish. clientWriter.writeGoawayFrame(3, ErrorCode.NO_ERROR, []); @@ -200,13 +221,15 @@ void main() { } void serverTest( - String name, - void Function( - ServerTransportConnection, - FrameWriter, - StreamIterator frameReader, - Future Function() readNext) - func) { + String name, + void Function( + ServerTransportConnection, + FrameWriter, + StreamIterator frameReader, + Future Function() readNext, + ) + func, +) { return test(name, () { var streams = ClientErrorStreams(); var clientReader = streams.clientConnectionFrameReader; @@ -216,8 +239,12 @@ void serverTest( return clientReader.current; } - return func(streams.serverConnection, streams.clientConnectionFrameWriter, - clientReader, readNext); + return func( + streams.serverConnection, + streams.clientConnectionFrameWriter, + clientReader, + readNext, + ); }); } diff --git a/pkgs/http2/test/src/async_utils/async_utils_test.dart b/pkgs/http2/test/src/async_utils/async_utils_test.dart index 6d1ebaadd5..3917febaa1 100644 --- a/pkgs/http2/test/src/async_utils/async_utils_test.dart +++ b/pkgs/http2/test/src/async_utils/async_utils_test.dart @@ -44,21 +44,27 @@ void main() { expect(bs.bufferIndicator.wouldBuffer, false); sub.pause(); - Timer.run(expectAsync0(() { - expect(bs.bufferIndicator.wouldBuffer, true); - bs.sink.add([1]); - - sub.resume(); - Timer.run(expectAsync0(() { - expect(bs.bufferIndicator.wouldBuffer, false); - bs.sink.add([2]); - - Timer.run(expectAsync0(() { - sub.cancel(); - expect(bs.bufferIndicator.wouldBuffer, false); - })); - })); - })); + Timer.run( + expectAsync0(() { + expect(bs.bufferIndicator.wouldBuffer, true); + bs.sink.add([1]); + + sub.resume(); + Timer.run( + expectAsync0(() { + expect(bs.bufferIndicator.wouldBuffer, false); + bs.sink.add([2]); + + Timer.run( + expectAsync0(() { + sub.cancel(); + expect(bs.bufferIndicator.wouldBuffer, false); + }), + ); + }), + ); + }), + ); }); test('buffered-bytes-writer', () async { diff --git a/pkgs/http2/test/src/connection_preface_test.dart b/pkgs/http2/test/src/connection_preface_test.dart index d2e6eb3f61..1188c148e9 100644 --- a/pkgs/http2/test/src/connection_preface_test.dart +++ b/pkgs/http2/test/src/connection_preface_test.dart @@ -16,8 +16,9 @@ void main() { for (var size = 1; size <= data.length; size++) { var c = StreamController>(); - var resultF = readConnectionPreface(c.stream) - .fold>([], (b, d) => b..addAll(d)); + var resultF = readConnectionPreface( + c.stream, + ).fold>([], (b, d) => b..addAll(d)); for (var i = 0; i < (size - 1 + data.length) ~/ size; i++) { var from = size * i; @@ -33,48 +34,66 @@ void main() { test('only-part-of-connection-sequence', () async { var c = StreamController>(); - var resultF = readConnectionPreface(c.stream) - .fold>([], (b, d) => b..addAll(d)); + var resultF = readConnectionPreface( + c.stream, + ).fold>([], (b, d) => b..addAll(d)); for (var i = 0; i < CONNECTION_PREFACE.length - 1; i++) { c.add([CONNECTION_PREFACE[i]]); } unawaited(c.close()); - unawaited(resultF.catchError(expectAsync2((Object error, Object _) { - expect(error, contains('EOS before connection preface could be read')); - return []; - }))); + unawaited( + resultF.catchError( + expectAsync2((Object error, Object _) { + expect( + error, + contains('EOS before connection preface could be read'), + ); + return []; + }), + ), + ); }); test('wrong-connection-sequence', () async { var c = StreamController>(); - var resultF = readConnectionPreface(c.stream) - .fold>([], (b, d) => b..addAll(d)); + var resultF = readConnectionPreface( + c.stream, + ).fold>([], (b, d) => b..addAll(d)); for (var i = 0; i < CONNECTION_PREFACE.length; i++) { c.add([0xff]); } unawaited(c.close()); - unawaited(resultF.catchError(expectAsync2((Object error, Object _) { - expect(error, contains('Connection preface does not match.')); - return []; - }))); + unawaited( + resultF.catchError( + expectAsync2((Object error, Object _) { + expect(error, contains('Connection preface does not match.')); + return []; + }), + ), + ); }); test('incoming-socket-error', () async { var c = StreamController>(); - var resultF = readConnectionPreface(c.stream) - .fold>([], (b, d) => b..addAll(d)); + var resultF = readConnectionPreface( + c.stream, + ).fold>([], (b, d) => b..addAll(d)); c.addError('hello world'); unawaited(c.close()); - unawaited(resultF.catchError(expectAsync2((Object error, Object _) { - expect(error, contains('hello world')); - return []; - }))); + unawaited( + resultF.catchError( + expectAsync2((Object error, Object _) { + expect(error, contains('hello world')); + return []; + }), + ), + ); }); }); } diff --git a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart index 1cde81be6a..137c684400 100644 --- a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart @@ -19,10 +19,12 @@ void main() { test('connection-message-queue-out', () { var fw = MockFrameWriter(); when(fw.bufferIndicator).thenReturn(BufferIndicator()); - when(fw.writeHeadersFrame(any, any, endStream: anyNamed('endStream'))) - .thenReturn(null); - when(fw.writeDataFrame(any, any, endStream: anyNamed('endStream'))) - .thenReturn(null); + when( + fw.writeHeadersFrame(any, any, endStream: anyNamed('endStream')), + ).thenReturn(null); + when( + fw.writeDataFrame(any, any, endStream: anyNamed('endStream')), + ).thenReturn(null); var windowMock = MockOutgoingWindowHandler(); var queue = ConnectionMessageQueueOut(windowMock, fw); @@ -70,8 +72,9 @@ void main() { expect(queue.pendingMessages, 1); verify(windowMock.decreaseWindow(1)).called(1); verify(fw.bufferIndicator).called(greaterThan(0)); - verify(fw.writeDataFrame(99, bytes.sublist(0, 1), endStream: false)) - .called(1); + verify( + fw.writeDataFrame(99, bytes.sublist(0, 1), endStream: false), + ).called(1); verifyNoMoreInteractions(windowMock); verifyNoMoreInteractions(fw); @@ -83,18 +86,23 @@ void main() { windowMock.peerWindowSize = bytes.length - 1; windowMock.positiveWindow.markUnBuffered(); verify(windowMock.decreaseWindow(bytes.length - 1)).called(1); - verify(fw.writeDataFrame(99, bytes.sublist(1), endStream: true)) - .called(1); + verify( + fw.writeDataFrame(99, bytes.sublist(1), endStream: true), + ).called(1); verifyNoMoreInteractions(windowMock); verify(fw.bufferIndicator).called(greaterThan(0)); verifyNoMoreInteractions(fw); queue.startClosing(); - queue.done.then(expectAsync1((_) { - expect(queue.pendingMessages, 0); - expect(() => queue.enqueueMessage(DataMessage(99, bytes, true)), - throwsA(const TypeMatcher())); - })); + queue.done.then( + expectAsync1((_) { + expect(queue.pendingMessages, 0); + expect( + () => queue.enqueueMessage(DataMessage(99, bytes, true)), + throwsA(const TypeMatcher()), + ); + }), + ); }); test('connection-message-queue-in', () { @@ -130,9 +138,9 @@ void main() { // specific queue. streamQueueMock.bufferIndicator.markUnBuffered(); verify(windowMock.dataProcessed(bytes.length)).called(1); - var capturedMessage = verify(streamQueueMock.enqueueMessage(captureAny)) - .captured - .single as DataMessage; + var capturedMessage = + verify(streamQueueMock.enqueueMessage(captureAny)).captured.single + as DataMessage; expect(capturedMessage.streamId, STREAM_ID); expect(capturedMessage.bytes, bytes); diff --git a/pkgs/http2/test/src/flowcontrol/mocks.dart b/pkgs/http2/test/src/flowcontrol/mocks.dart index c30048936c..775258f7a6 100644 --- a/pkgs/http2/test/src/flowcontrol/mocks.dart +++ b/pkgs/http2/test/src/flowcontrol/mocks.dart @@ -8,18 +8,22 @@ import 'package:http2/src/flowcontrol/window_handler.dart'; import 'package:http2/src/frames/frames.dart'; import 'package:mockito/annotations.dart'; -@GenerateMocks([ - FrameWriter, - IncomingWindowHandler, - OutgoingStreamWindowHandler, -], customMocks: [ - MockSpec(fallbackGenerators: { - #ensureNotTerminatedSync: ensureNotTerminatedSyncFallback, - }), - MockSpec(fallbackGenerators: { - #ensureNotTerminatedSync: ensureNotTerminatedSyncFallback, - }) -]) +@GenerateMocks( + [FrameWriter, IncomingWindowHandler, OutgoingStreamWindowHandler], + customMocks: [ + MockSpec( + fallbackGenerators: { + #ensureNotTerminatedSync: ensureNotTerminatedSyncFallback, + }, + ), + MockSpec( + fallbackGenerators: { + #ensureNotTerminatedSync: ensureNotTerminatedSyncFallback, + }, + ), + ], +) T ensureNotTerminatedSyncFallback(T Function()? f) => throw UnimplementedError( - 'Method cannot be stubbed; requires fallback values for return'); + 'Method cannot be stubbed; requires fallback values for return', + ); diff --git a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart index 0b5ae7a7a1..cbeb147a3a 100644 --- a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart @@ -25,8 +25,11 @@ void main() { when(windowMock.decreaseWindow(any)).thenReturn(null); windowMock.positiveWindow.markUnBuffered(); - var queue = - StreamMessageQueueOut(STREAM_ID, windowMock, connectionQueueMock); + var queue = StreamMessageQueueOut( + STREAM_ID, + windowMock, + connectionQueueMock, + ); expect(queue.bufferIndicator.wouldBuffer, isFalse); expect(queue.pendingMessages, 0); @@ -35,9 +38,9 @@ void main() { queue.enqueueMessage(DataMessage(STREAM_ID, BYTES, true)); verify(windowMock.decreaseWindow(BYTES.length)).called(1); final capturedMessage = - verify(connectionQueueMock.enqueueMessage(captureAny)) - .captured - .single; + verify( + connectionQueueMock.enqueueMessage(captureAny), + ).captured.single; expect(capturedMessage, const TypeMatcher()); var capturedDataMessage = capturedMessage as DataMessage; expect(capturedDataMessage.bytes, BYTES); @@ -51,8 +54,11 @@ void main() { when(windowMock.positiveWindow).thenReturn(BufferIndicator()); when(windowMock.decreaseWindow(any)).thenReturn(null); windowMock.positiveWindow.markUnBuffered(); - var queue = - StreamMessageQueueOut(STREAM_ID, windowMock, connectionQueueMock); + var queue = StreamMessageQueueOut( + STREAM_ID, + windowMock, + connectionQueueMock, + ); expect(queue.bufferIndicator.wouldBuffer, isFalse); expect(queue.pendingMessages, 0); @@ -83,8 +89,11 @@ void main() { var windowMock = MockOutgoingStreamWindowHandler(); when(windowMock.positiveWindow).thenReturn(BufferIndicator()); windowMock.positiveWindow.markUnBuffered(); - var queue = - StreamMessageQueueOut(STREAM_ID, windowMock, connectionQueueMock); + var queue = StreamMessageQueueOut( + STREAM_ID, + windowMock, + connectionQueueMock, + ); expect(queue.bufferIndicator.wouldBuffer, isFalse); expect(queue.pendingMessages, 0); @@ -108,17 +117,20 @@ void main() { var queue = StreamMessageQueueIn(windowMock); expect(queue.pendingMessages, 0); - queue.messages.listen(expectAsync1((StreamMessage message) { - expect(message, isA()); - - var dataMessage = message as DataStreamMessage; - expect(dataMessage.bytes, BYTES); - }), onDone: expectAsync0(() {})); + queue.messages.listen( + expectAsync1((StreamMessage message) { + expect(message, isA()); + + var dataMessage = message as DataStreamMessage; + expect(dataMessage.bytes, BYTES); + }), + onDone: expectAsync0(() {}), + ); queue.enqueueMessage(DataMessage(STREAM_ID, BYTES, true)); expect(queue.bufferIndicator.wouldBuffer, isFalse); verifyInOrder([ windowMock.gotData(BYTES.length), - windowMock.dataProcessed(BYTES.length) + windowMock.dataProcessed(BYTES.length), ]); verifyNoMoreInteractions(windowMock); }); @@ -132,8 +144,10 @@ void main() { when(windowMock.gotData(any)).thenReturn(null); var queue = StreamMessageQueueIn(windowMock); - var sub = queue.messages.listen(expectAsync1((_) {}, count: 0), - onDone: expectAsync0(() {}, count: 0)); + var sub = queue.messages.listen( + expectAsync1((_) {}, count: 0), + onDone: expectAsync0(() {}, count: 0), + ); sub.pause(); expect(queue.pendingMessages, 0); diff --git a/pkgs/http2/test/src/flowcontrol/window_handler_test.dart b/pkgs/http2/test/src/flowcontrol/window_handler_test.dart index 017dadd4ef..c788398755 100644 --- a/pkgs/http2/test/src/flowcontrol/window_handler_test.dart +++ b/pkgs/http2/test/src/flowcontrol/window_handler_test.dart @@ -13,9 +13,13 @@ import '../error_matchers.dart'; void main() { group('flowcontrol', () { void testAbstractOutgoingWindowHandler( - AbstractOutgoingWindowHandler handler, Window window, int initialSize) { - var sub = handler.positiveWindow.bufferEmptyEvents - .listen(expectAsync1((_) {}, count: 0)); + AbstractOutgoingWindowHandler handler, + Window window, + int initialSize, + ) { + var sub = handler.positiveWindow.bufferEmptyEvents.listen( + expectAsync1((_) {}, count: 0), + ); expect(handler.peerWindowSize, initialSize); expect(window.size, initialSize); @@ -40,10 +44,12 @@ void main() { expect(handler.positiveWindow.wouldBuffer, isFalse); handler.decreaseWindow(window.size); expect(handler.positiveWindow.wouldBuffer, isTrue); - sub = handler.positiveWindow.bufferEmptyEvents.listen(expectAsync1((_) { - expect(handler.peerWindowSize, 1); - expect(window.size, 1); - })); + sub = handler.positiveWindow.bufferEmptyEvents.listen( + expectAsync1((_) { + expect(handler.peerWindowSize, 1); + expect(window.size, 1); + }), + ); // Now we trigger the 1 byte window increase handler.processWindowUpdate(WindowUpdateFrame(frameHeader, 1)); @@ -52,8 +58,10 @@ void main() { // If the remote end sends us [WindowUpdateFrame]s which increase it above // the maximum size, we throw a [FlowControlException]. var frame = WindowUpdateFrame(frameHeader, Window.MAX_WINDOW_SIZE); - expect(() => handler.processWindowUpdate(frame), - throwsA(isFlowControlException)); + expect( + () => handler.processWindowUpdate(frame), + throwsA(isFlowControlException), + ); } test('outgoing-connection-window-handler', () { @@ -80,8 +88,9 @@ void main() { handler = OutgoingStreamWindowHandler(window); expect(handler.positiveWindow.wouldBuffer, isFalse); - final bufferEmpty = handler.positiveWindow.bufferEmptyEvents - .listen(expectAsync1((_) {}, count: 0)); + final bufferEmpty = handler.positiveWindow.bufferEmptyEvents.listen( + expectAsync1((_) {}, count: 0), + ); handler.processInitialWindowSizeSettingChange(-window.size); expect(handler.positiveWindow.wouldBuffer, isTrue); expect(handler.peerWindowSize, 0); @@ -93,9 +102,11 @@ void main() { expect(window.size, 1); expect( - () => handler.processInitialWindowSizeSettingChange( - Window.MAX_WINDOW_SIZE + 1), - throwsA(isFlowControlException)); + () => handler.processInitialWindowSizeSettingChange( + Window.MAX_WINDOW_SIZE + 1, + ), + throwsA(isFlowControlException), + ); }); test('incoming-window-handler', () { diff --git a/pkgs/http2/test/src/frames/frame_defragmenter_test.dart b/pkgs/http2/test/src/frames/frame_defragmenter_test.dart index 04abec1195..8d012eb842 100644 --- a/pkgs/http2/test/src/frames/frame_defragmenter_test.dart +++ b/pkgs/http2/test/src/frames/frame_defragmenter_test.dart @@ -15,27 +15,48 @@ void main() { return UnknownFrame(FrameHeader(0, 0, 0, 1), []); } - HeadersFrame headersFrame(List data, - {bool fragmented = false, int streamId = 1}) { + HeadersFrame headersFrame( + List data, { + bool fragmented = false, + int streamId = 1, + }) { var flags = fragmented ? 0 : HeadersFrame.FLAG_END_HEADERS; - var header = - FrameHeader(data.length, FrameType.HEADERS, flags, streamId); + var header = FrameHeader( + data.length, + FrameType.HEADERS, + flags, + streamId, + ); return HeadersFrame(header, 0, false, null, null, data); } - PushPromiseFrame pushPromiseFrame(List data, - {bool fragmented = false, int streamId = 1}) { + PushPromiseFrame pushPromiseFrame( + List data, { + bool fragmented = false, + int streamId = 1, + }) { var flags = fragmented ? 0 : HeadersFrame.FLAG_END_HEADERS; - var header = - FrameHeader(data.length, FrameType.PUSH_PROMISE, flags, streamId); + var header = FrameHeader( + data.length, + FrameType.PUSH_PROMISE, + flags, + streamId, + ); return PushPromiseFrame(header, 0, 44, data); } - ContinuationFrame continuationFrame(List data, - {bool fragmented = false, int streamId = 1}) { + ContinuationFrame continuationFrame( + List data, { + bool fragmented = false, + int streamId = 1, + }) { var flags = fragmented ? 0 : ContinuationFrame.FLAG_END_HEADERS; - var header = - FrameHeader(data.length, FrameType.CONTINUATION, flags, streamId); + var header = FrameHeader( + data.length, + FrameType.CONTINUATION, + flags, + streamId, + ); return ContinuationFrame(header, data); } @@ -85,7 +106,9 @@ void main() { expect(defrag.tryDefragmentFrame(f1), isNull); expect( - () => defrag.tryDefragmentFrame(f2), throwsA(isProtocolException)); + () => defrag.tryDefragmentFrame(f2), + throwsA(isProtocolException), + ); }); test('fragmented-push-promise-frame', () { @@ -96,7 +119,9 @@ void main() { expect(defrag.tryDefragmentFrame(f1), isNull); expect( - () => defrag.tryDefragmentFrame(f2), throwsA(isProtocolException)); + () => defrag.tryDefragmentFrame(f2), + throwsA(isProtocolException), + ); }); test('fragmented-headers-frame--no-continuation-frame', () { @@ -107,7 +132,9 @@ void main() { expect(defrag.tryDefragmentFrame(f1), isNull); expect( - () => defrag.tryDefragmentFrame(f2), throwsA(isProtocolException)); + () => defrag.tryDefragmentFrame(f2), + throwsA(isProtocolException), + ); }); test('fragmented-push-promise-no-continuation-frame', () { @@ -118,7 +145,9 @@ void main() { expect(defrag.tryDefragmentFrame(f1), isNull); expect( - () => defrag.tryDefragmentFrame(f2), throwsA(isProtocolException)); + () => defrag.tryDefragmentFrame(f2), + throwsA(isProtocolException), + ); }); test('push-without-headres-or-push-promise-frame', () { diff --git a/pkgs/http2/test/src/frames/frame_reader_test.dart b/pkgs/http2/test/src/frames/frame_reader_test.dart index 9cdf068c40..5d07e963a9 100644 --- a/pkgs/http2/test/src/frames/frame_reader_test.dart +++ b/pkgs/http2/test/src/frames/frame_reader_test.dart @@ -35,25 +35,28 @@ void main() { test('data-frame--max-frame-size', () { var body = List.filled(maxFrameSize, 0x42); - dataFrame(body).listen(expectAsync1((Frame frame) { - expect(frame, isA()); - expect(frame.header, hasLength(body.length)); - expect(frame.header.flags, 0); - var dataFrame = frame as DataFrame; - expect(dataFrame.hasEndStreamFlag, isFalse); - expect(dataFrame.hasPaddedFlag, isFalse); - expect(dataFrame.bytes, body); - }), - onError: - expectAsync2((Object error, StackTrace stack) {}, count: 0)); + dataFrame(body).listen( + expectAsync1((Frame frame) { + expect(frame, isA()); + expect(frame.header, hasLength(body.length)); + expect(frame.header.flags, 0); + var dataFrame = frame as DataFrame; + expect(dataFrame.hasEndStreamFlag, isFalse); + expect(dataFrame.hasPaddedFlag, isFalse); + expect(dataFrame.bytes, body); + }), + onError: expectAsync2((Object error, StackTrace stack) {}, count: 0), + ); }); test('data-frame--max-frame-size-plus-1', () { var body = List.filled(maxFrameSize + 1, 0x42); - dataFrame(body).listen(expectAsync1((_) {}, count: 0), - onError: expectAsync2((Object error, StackTrace stack) { - expect('$error', contains('Incoming frame is too big')); - })); + dataFrame(body).listen( + expectAsync1((_) {}, count: 0), + onError: expectAsync2((Object error, StackTrace stack) { + expect('$error', contains('Incoming frame is too big')); + }), + ); }); test('incomplete-header', () { @@ -66,10 +69,12 @@ void main() { ..add([1]) ..close(); - reader.startDecoding().listen(expectAsync1((_) {}, count: 0), - onError: expectAsync2((Object error, StackTrace stack) { - expect('$error', contains('incomplete frame')); - })); + reader.startDecoding().listen( + expectAsync1((_) {}, count: 0), + onError: expectAsync2((Object error, StackTrace stack) { + expect('$error', contains('incomplete frame')); + }), + ); }); test('incomplete-frame', () { @@ -87,10 +92,12 @@ void main() { ..add([0, 0, 255, 0, 0, 0, 0, 0, 1]) ..close(); - reader.startDecoding().listen(expectAsync1((_) {}, count: 0), - onError: expectAsync2((Object error, StackTrace stack) { - expect('$error', contains('incomplete frame')); - })); + reader.startDecoding().listen( + expectAsync1((_) {}, count: 0), + onError: expectAsync2((Object error, StackTrace stack) { + expect('$error', contains('incomplete frame')); + }), + ); }); test('connection-error', () { @@ -103,10 +110,12 @@ void main() { ..addError('hello world') ..close(); - reader.startDecoding().listen(expectAsync1((_) {}, count: 0), - onError: expectAsync2((Object error, StackTrace stack) { - expect('$error', contains('hello world')); - })); + reader.startDecoding().listen( + expectAsync1((_) {}, count: 0), + onError: expectAsync2((Object error, StackTrace stack) { + expect('$error', contains('hello world')); + }), + ); }); }); }); diff --git a/pkgs/http2/test/src/frames/frame_writer_reader_test.dart b/pkgs/http2/test/src/frames/frame_writer_reader_test.dart index 28bc59cddd..b3692326f9 100644 --- a/pkgs/http2/test/src/frames/frame_writer_reader_test.dart +++ b/pkgs/http2/test/src/frames/frame_writer_reader_test.dart @@ -14,8 +14,11 @@ import '../hpack/hpack_test.dart' show isHeader; void main() { group('frames', () { group('writer-reader', () { - writerReaderTest('data-frame', - (FrameWriter writer, FrameReader reader, HPackDecoder decoder) async { + writerReaderTest('data-frame', ( + FrameWriter writer, + FrameReader reader, + HPackDecoder decoder, + ) async { writer.writeDataFrame(99, [1, 2, 3], endStream: true); var frames = await finishWriting(writer, reader); @@ -30,8 +33,11 @@ void main() { expect(dataFrame.bytes, [1, 2, 3]); }); - writerReaderTest('headers-frame', - (FrameWriter writer, FrameReader reader, HPackDecoder decoder) async { + writerReaderTest('headers-frame', ( + FrameWriter writer, + FrameReader reader, + HPackDecoder decoder, + ) async { writer.writeHeadersFrame(99, [Header.ascii('a', 'b')], endStream: true); var frames = await finishWriting(writer, reader); @@ -54,8 +60,11 @@ void main() { expect(headers[0], isHeader('a', 'b')); }); - writerReaderTest('priority-frame', - (FrameWriter writer, FrameReader reader, HPackDecoder decoder) async { + writerReaderTest('priority-frame', ( + FrameWriter writer, + FrameReader reader, + HPackDecoder decoder, + ) async { writer.writePriorityFrame(99, 44, 33, exclusive: true); var frames = await finishWriting(writer, reader); @@ -69,8 +78,11 @@ void main() { expect(priorityFrame.weight, 33); }); - writerReaderTest('rst-frame', - (FrameWriter writer, FrameReader reader, HPackDecoder decoder) async { + writerReaderTest('rst-frame', ( + FrameWriter writer, + FrameReader reader, + HPackDecoder decoder, + ) async { writer.writeRstStreamFrame(99, 42); var frames = await finishWriting(writer, reader); @@ -82,8 +94,11 @@ void main() { expect(rstFrame.errorCode, 42); }); - writerReaderTest('settings-frame', - (FrameWriter writer, FrameReader reader, HPackDecoder decoder) async { + writerReaderTest('settings-frame', ( + FrameWriter writer, + FrameReader reader, + HPackDecoder decoder, + ) async { writer.writeSettingsFrame([Setting(Setting.SETTINGS_ENABLE_PUSH, 1)]); var frames = await finishWriting(writer, reader); @@ -95,12 +110,17 @@ void main() { expect(settingsFrame.header.streamId, 0); expect(settingsFrame.settings, hasLength(1)); expect( - settingsFrame.settings[0].identifier, Setting.SETTINGS_ENABLE_PUSH); + settingsFrame.settings[0].identifier, + Setting.SETTINGS_ENABLE_PUSH, + ); expect(settingsFrame.settings[0].value, 1); }); - writerReaderTest('settings-frame-ack', - (FrameWriter writer, FrameReader reader, HPackDecoder decoder) async { + writerReaderTest('settings-frame-ack', ( + FrameWriter writer, + FrameReader reader, + HPackDecoder decoder, + ) async { writer.writeSettingsAckFrame(); var frames = await finishWriting(writer, reader); @@ -113,8 +133,11 @@ void main() { expect(settingsFrame.settings, hasLength(0)); }); - writerReaderTest('push-promise-frame', - (FrameWriter writer, FrameReader reader, HPackDecoder decoder) async { + writerReaderTest('push-promise-frame', ( + FrameWriter writer, + FrameReader reader, + HPackDecoder decoder, + ) async { writer.writePushPromiseFrame(99, 44, [Header.ascii('a', 'b')]); var frames = await finishWriting(writer, reader); @@ -133,8 +156,11 @@ void main() { expect(headers[0], isHeader('a', 'b')); }); - writerReaderTest('ping-frame', - (FrameWriter writer, FrameReader reader, HPackDecoder decoder) async { + writerReaderTest('ping-frame', ( + FrameWriter writer, + FrameReader reader, + HPackDecoder decoder, + ) async { writer.writePingFrame(44, ack: true); var frames = await finishWriting(writer, reader); @@ -147,8 +173,11 @@ void main() { expect(pingFrame.hasAckFlag, isTrue); }); - writerReaderTest('goaway-frame', - (FrameWriter writer, FrameReader reader, HPackDecoder decoder) async { + writerReaderTest('goaway-frame', ( + FrameWriter writer, + FrameReader reader, + HPackDecoder decoder, + ) async { writer.writeGoawayFrame(44, 33, [1, 2, 3]); var frames = await finishWriting(writer, reader); @@ -162,8 +191,11 @@ void main() { expect(goawayFrame.debugData, [1, 2, 3]); }); - writerReaderTest('window-update-frame', - (FrameWriter writer, FrameReader reader, HPackDecoder decoder) async { + writerReaderTest('window-update-frame', ( + FrameWriter writer, + FrameReader reader, + HPackDecoder decoder, + ) async { writer.writeWindowUpdate(55, streamId: 99); var frames = await finishWriting(writer, reader); @@ -175,8 +207,11 @@ void main() { expect(windowUpdateFrame.windowSizeIncrement, 55); }); - writerReaderTest('frag-headers-frame', - (FrameWriter writer, FrameReader reader, HPackDecoder decoder) async { + writerReaderTest('frag-headers-frame', ( + FrameWriter writer, + FrameReader reader, + HPackDecoder decoder, + ) async { var headerName = [1]; var headerValue = List.filled(1 << 14, 0x42); var header = Header(headerName, headerValue); @@ -202,7 +237,7 @@ void main() { var headerBlock = [ ...headersFrame.headerBlockFragment, - ...contFrame.headerBlockFragment + ...contFrame.headerBlockFragment, ]; var headers = decoder.decode(headerBlock); @@ -214,8 +249,10 @@ void main() { }); } -void writerReaderTest(String name, - Future Function(FrameWriter, FrameReader, HPackDecoder) func) { +void writerReaderTest( + String name, + Future Function(FrameWriter, FrameReader, HPackDecoder) func, +) { test(name, () { var settings = ActiveSettings(); var context = HPackContext(); @@ -227,6 +264,8 @@ void writerReaderTest(String name, } Future> finishWriting(FrameWriter writer, FrameReader reader) { - return Future.wait([writer.close(), reader.startDecoding().toList()]) - .then((results) => results.last as List); + return Future.wait([ + writer.close(), + reader.startDecoding().toList(), + ]).then((results) => results.last as List); } diff --git a/pkgs/http2/test/src/frames/frame_writer_test.dart b/pkgs/http2/test/src/frames/frame_writer_test.dart index 24b822fef0..4550ed9176 100644 --- a/pkgs/http2/test/src/frames/frame_writer_test.dart +++ b/pkgs/http2/test/src/frames/frame_writer_test.dart @@ -18,9 +18,11 @@ void main() { var controller = StreamController>(); var writer = FrameWriter(context.encoder, controller, settings); - writer.doneFuture.then(expectAsync1((_) { - // We expect that the writer is done at this point. - })); + writer.doneFuture.then( + expectAsync1((_) { + // We expect that the writer is done at this point. + }), + ); // We cancel here the reading part (simulates a dying socket). controller.stream.listen((_) {}).cancel(); diff --git a/pkgs/http2/test/src/hpack/hpack_test.dart b/pkgs/http2/test/src/hpack/hpack_test.dart index aadf6cb17e..4882562338 100644 --- a/pkgs/http2/test/src/hpack/hpack_test.dart +++ b/pkgs/http2/test/src/hpack/hpack_test.dart @@ -33,7 +33,7 @@ void main() { 0x2e, 0x63, 0x6f, - 0x6d + 0x6d, ]); expect(headers, hasLength(4)); expect(headers[0], isHeader(':method', 'GET')); @@ -56,7 +56,7 @@ void main() { 0x61, 0x63, 0x68, - 0x65 + 0x65, ]); expect(headers, hasLength(5)); expect(headers[0], isHeader(':method', 'GET')); @@ -95,7 +95,7 @@ void main() { 0x61, 0x6c, 0x75, - 0x65 + 0x65, ]); expect(headers, hasLength(5)); expect(headers[0], isHeader(':method', 'GET')); @@ -127,7 +127,7 @@ void main() { 0xab, 0x90, 0xf4, - 0xff + 0xff, ]); expect(headers, hasLength(4)); expect(headers[0], isHeader(':method', 'GET')); @@ -148,7 +148,7 @@ void main() { 0x10, 0x64, 0x9c, - 0xbf + 0xbf, ]); expect(headers, hasLength(5)); expect(headers[0], isHeader(':method', 'GET')); @@ -182,7 +182,7 @@ void main() { 0xb8, 0xe8, 0xb4, - 0xbf + 0xbf, ]); expect(headers, hasLength(5)); expect(headers[0], isHeader(':method', 'GET')); @@ -267,7 +267,7 @@ void main() { 0x2e, 0x63, 0x6f, - 0x6d + 0x6d, ]); expect(headers, hasLength(4)); expect(headers[0], isHeader(':status', '302')); @@ -276,8 +276,16 @@ void main() { expect(headers[3], isHeader('location', 'https://www.example.com')); // Second response - headers = context.decoder - .decode([0x48, 0x03, 0x33, 0x30, 0x37, 0xc1, 0xc0, 0xbf]); + headers = context.decoder.decode([ + 0x48, + 0x03, + 0x33, + 0x30, + 0x37, + 0xc1, + 0xc0, + 0xbf, + ]); expect(headers, hasLength(4)); expect(headers[0], isHeader(':status', '307')); expect(headers[1], isHeader('cache-control', 'private')); @@ -383,7 +391,7 @@ void main() { 0x6f, 0x6e, 0x3d, - 0x31 + 0x31, ]); expect(headers, hasLength(6)); expect(headers[0], isHeader(':status', '200')); @@ -392,9 +400,12 @@ void main() { expect(headers[3], isHeader('location', 'https://www.example.com')); expect(headers[4], isHeader('content-encoding', 'gzip')); expect( - headers[5], - isHeader('set-cookie', - 'foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1')); + headers[5], + isHeader( + 'set-cookie', + 'foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1', + ), + ); }); test('C.6 response with huffman encoding', () { @@ -456,7 +467,7 @@ void main() { 0x82, 0xae, 0x43, - 0xd3 + 0xd3, ]); expect(headers, hasLength(4)); expect(headers[0], isHeader(':status', '302')); @@ -465,8 +476,16 @@ void main() { expect(headers[3], isHeader('location', 'https://www.example.com')); // Second response - headers = context.decoder - .decode([0x48, 0x83, 0x64, 0x0e, 0xff, 0xc1, 0xc0, 0xbf]); + headers = context.decoder.decode([ + 0x48, + 0x83, + 0x64, + 0x0e, + 0xff, + 0xc1, + 0xc0, + 0xbf, + ]); expect(headers, hasLength(4)); expect(headers[0], isHeader(':status', '307')); expect(headers[1], isHeader('cache-control', 'private')); @@ -553,7 +572,7 @@ void main() { 0x06, 0x3d, 0x50, - 0x07 + 0x07, ]); expect(headers, hasLength(6)); expect(headers[0], isHeader(':status', '200')); @@ -562,37 +581,48 @@ void main() { expect(headers[3], isHeader('location', 'https://www.example.com')); expect(headers[4], isHeader('content-encoding', 'gzip')); expect( - headers[5], - isHeader('set-cookie', - 'foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1')); + headers[5], + isHeader( + 'set-cookie', + 'foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1', + ), + ); }); }); group('negative-decoder-tests', () { test('invalid-integer-encoding', () { var context = HPackContext(); - expect(() => context.decoder.decode([1 << 6, 0xff]), - throwsA(isHPackDecodingException)); + expect( + () => context.decoder.decode([1 << 6, 0xff]), + throwsA(isHPackDecodingException), + ); }); test('index-out-of-table-size', () { var context = HPackContext(); - expect(() => context.decoder.decode([0x7f]), - throwsA(isHPackDecodingException)); + expect( + () => context.decoder.decode([0x7f]), + throwsA(isHPackDecodingException), + ); }); test('invalid-update-dynamic-table-size', () { var context = HPackContext(); - expect(() => context.decoder.decode([0x3f]), - throwsA(isHPackDecodingException)); + expect( + () => context.decoder.decode([0x3f]), + throwsA(isHPackDecodingException), + ); }); test('update-dynamic-table-size-too-high', () { var context = HPackContext(); // Tries to set dynamic table to 4097 (max is 4096 by default) var bytes = TestHelper.newInteger(0x20, 5, 4097); - expect(() => context.decoder.decode(bytes), - throwsA(isHPackDecodingException)); + expect( + () => context.decoder.decode(bytes), + throwsA(isHPackDecodingException), + ); }); }); @@ -609,8 +639,10 @@ void main() { test('update-dynamic-table-size-too-high', () { var context = HPackContext(); // Sets dynamic table to 4096 - expect(context.decoder.decode(TestHelper.newInteger(0x20, 5, 4096)), - []); + expect( + context.decoder.decode(TestHelper.newInteger(0x20, 5, 4096)), + [], + ); }); test('dynamic table entry', () { @@ -649,8 +681,9 @@ void main() { // We're reducing now the size by 1 byte, which should evict the last // entry. - headers = - context.decoder.decode(TestHelper.setDynamicTableSize(4096 - 1)); + headers = context.decoder.decode( + TestHelper.setDynamicTableSize(4096 - 1), + ); expect(headers, hasLength(0)); headers = context.decoder.decode(TestHelper.dynamicTableLookup(0)); @@ -662,8 +695,10 @@ void main() { TestHelper.expectHeader(headers[0], 1024, char0, charC); // Since we reduce the size by 1 byte, the last entry must be gone now. - expect(() => context.decoder.decode(TestHelper.dynamicTableLookup(2)), - throwsA(isHPackDecodingException)); + expect( + () => context.decoder.decode(TestHelper.dynamicTableLookup(2)), + throwsA(isHPackDecodingException), + ); }); }); @@ -671,18 +706,31 @@ void main() { test('simple-encoding', () { var context = HPackContext(); var headers = [Header.ascii('key', 'value')]; - expect(context.encoder.encode(headers), - [0x00, 0x03, 0x6b, 0x65, 0x79, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65]); + expect(context.encoder.encode(headers), [ + 0x00, + 0x03, + 0x6b, + 0x65, + 0x79, + 0x05, + 0x76, + 0x61, + 0x6c, + 0x75, + 0x65, + ]); }); test('simple-encoding-long-value', () { var context = HPackContext(); var headers = [ - Header([0x42], List.filled(300, 0x84)) + Header([0x42], List.filled(300, 0x84)), ]; - expect(context.decoder.decode(context.encoder.encode(headers)).first, - equalsHeader(headers.first)); + expect( + context.decoder.decode(context.encoder.encode(headers)).first, + equalsHeader(headers.first), + ); expect(context.encoder.encode(headers), [ // Literal Header Field with Incremental Indexing - Indexed Name diff --git a/pkgs/http2/test/src/hpack/huffman_table_test.dart b/pkgs/http2/test/src/hpack/huffman_table_test.dart index fa1c75be71..d5569f0a09 100644 --- a/pkgs/http2/test/src/hpack/huffman_table_test.dart +++ b/pkgs/http2/test/src/hpack/huffman_table_test.dart @@ -27,7 +27,7 @@ void main() { 0xab, 0x90, 0xf4, - 0xff + 0xff, ], 'no-cache': [0xa8, 0xeb, 0x10, 0x64, 0x9c, 0xbf], 'custom-key': [0x25, 0xa8, 0x49, 0xe9, 0x5b, 0xa9, 0x7d, 0x7f], diff --git a/pkgs/http2/test/src/ping/ping_handler_test.dart b/pkgs/http2/test/src/ping/ping_handler_test.dart index df02420870..2b10c7a083 100644 --- a/pkgs/http2/test/src/ping/ping_handler_test.dart +++ b/pkgs/http2/test/src/ping/ping_handler_test.dart @@ -20,10 +20,7 @@ void main() { var p1 = pingHandler.ping(); var p2 = pingHandler.ping(); - verifyInOrder([ - writer.writePingFrame(1), - writer.writePingFrame(2), - ]); + verifyInOrder([writer.writePingFrame(1), writer.writePingFrame(2)]); var header = FrameHeader(8, FrameType.PING, PingFrame.FLAG_ACK, 0); pingHandler.processPingFrame(PingFrame(header, 1)); @@ -46,7 +43,7 @@ void main() { verifyInOrder([ writer.writePingFrame(1, ack: true), - writer.writePingFrame(2, ack: true) + writer.writePingFrame(2, ack: true), ]); verifyNoMoreInteractions(writer); }); @@ -59,14 +56,20 @@ void main() { verify(writer.writePingFrame(1)).called(1); var header = FrameHeader(8, FrameType.PING, PingFrame.FLAG_ACK, 0); - expect(() => pingHandler.processPingFrame(PingFrame(header, 2)), - throwsA(isProtocolException)); + expect( + () => pingHandler.processPingFrame(PingFrame(header, 2)), + throwsA(isProtocolException), + ); // Ensure outstanding pings will be completed with an error once we call // `pingHandler.terminate()`. - unawaited(future.catchError(expectAsync2((Object error, Object _) { - expect(error, 'hello world'); - }))); + unawaited( + future.catchError( + expectAsync2((Object error, Object _) { + expect(error, 'hello world'); + }), + ), + ); pingHandler.terminate('hello world'); verifyNoMoreInteractions(writer); }); @@ -77,9 +80,11 @@ void main() { pingHandler.terminate('hello world'); expect( - () => pingHandler.processPingFrame(PingFrame( - FrameHeader(8, FrameType.PING, PingFrame.FLAG_ACK, 1), 1)), - throwsA(isTerminatedException)); + () => pingHandler.processPingFrame( + PingFrame(FrameHeader(8, FrameType.PING, PingFrame.FLAG_ACK, 1), 1), + ), + throwsA(isTerminatedException), + ); expect(pingHandler.ping(), throwsA(isTerminatedException)); verifyZeroInteractions(writer); }); @@ -89,8 +94,10 @@ void main() { var pingHandler = instantiateHandler(writer); var header = FrameHeader(8, FrameType.PING, PingFrame.FLAG_ACK, 1); - expect(() => pingHandler.processPingFrame(PingFrame(header, 1)), - throwsA(isProtocolException)); + expect( + () => pingHandler.processPingFrame(PingFrame(header, 1)), + throwsA(isProtocolException), + ); verifyZeroInteractions(writer); }); @@ -101,14 +108,8 @@ void main() { final pingStream = StreamController()..stream.listen(pings.add); PingHandler(writer, pingStream) - ..processPingFrame(PingFrame( - FrameHeader(8, FrameType.PING, 0, 0), - 1, - )) - ..processPingFrame(PingFrame( - FrameHeader(8, FrameType.PING, 0, 0), - 2, - )); + ..processPingFrame(PingFrame(FrameHeader(8, FrameType.PING, 0, 0), 1)) + ..processPingFrame(PingFrame(FrameHeader(8, FrameType.PING, 0, 0), 2)); await pingStream.close(); diff --git a/pkgs/http2/test/src/settings/settings_handler_test.dart b/pkgs/http2/test/src/settings/settings_handler_test.dart index 3d16a73ae8..7866aab925 100644 --- a/pkgs/http2/test/src/settings/settings_handler_test.dart +++ b/pkgs/http2/test/src/settings/settings_handler_test.dart @@ -19,7 +19,11 @@ void main() { test('successful-setting', () async { var writer = FrameWriterMock(); var sh = SettingsHandler( - HPackEncoder(), writer, ActiveSettings(), ActiveSettings()); + HPackEncoder(), + writer, + ActiveSettings(), + ActiveSettings(), + ); // Start changing settings. var changed = sh.changeSettings(pushSettings); @@ -30,8 +34,12 @@ void main() { expect(sh.acknowledgedSettings.enablePush, true); // Simulate remote end to respond with an ACK. - var header = - FrameHeader(0, FrameType.SETTINGS, SettingsFrame.FLAG_ACK, 0); + var header = FrameHeader( + 0, + FrameType.SETTINGS, + SettingsFrame.FLAG_ACK, + 0, + ); sh.handleSettingsFrame(SettingsFrame(header, [])); await changed; @@ -43,7 +51,11 @@ void main() { test('ack-remote-settings-change', () { var writer = FrameWriterMock(); var sh = SettingsHandler( - HPackEncoder(), writer, ActiveSettings(), ActiveSettings()); + HPackEncoder(), + writer, + ActiveSettings(), + ActiveSettings(), + ); // Check that settings haven't been applied. expect(sh.peerSettings.enablePush, true); @@ -61,22 +73,36 @@ void main() { test('invalid-remote-ack', () { var writer = FrameWriterMock(); var sh = SettingsHandler( - HPackEncoder(), writer, ActiveSettings(), ActiveSettings()); + HPackEncoder(), + writer, + ActiveSettings(), + ActiveSettings(), + ); // Simulates ACK even though we haven't sent any settings. - var header = - FrameHeader(0, FrameType.SETTINGS, SettingsFrame.FLAG_ACK, 0); + var header = FrameHeader( + 0, + FrameType.SETTINGS, + SettingsFrame.FLAG_ACK, + 0, + ); var settingsFrame = SettingsFrame(header, const []); - expect(() => sh.handleSettingsFrame(settingsFrame), - throwsA(isProtocolException)); + expect( + () => sh.handleSettingsFrame(settingsFrame), + throwsA(isProtocolException), + ); verifyZeroInteractions(writer); }); test('invalid-remote-settings-change', () { var writer = FrameWriterMock(); var sh = SettingsHandler( - HPackEncoder(), writer, ActiveSettings(), ActiveSettings()); + HPackEncoder(), + writer, + ActiveSettings(), + ActiveSettings(), + ); // Check that settings haven't been applied. expect(sh.peerSettings.enablePush, true); @@ -84,16 +110,22 @@ void main() { // Simulate remote end by setting the push setting. var header = FrameHeader(6, FrameType.SETTINGS, 0, 0); var settingsFrame = SettingsFrame(header, invalidPushSettings); - expect(() => sh.handleSettingsFrame(settingsFrame), - throwsA(isProtocolException)); + expect( + () => sh.handleSettingsFrame(settingsFrame), + throwsA(isProtocolException), + ); verifyZeroInteractions(writer); }); test('change-max-header-table-size', () { var writer = FrameWriterMock(); var mock = HPackEncoderMock(); - var sh = - SettingsHandler(mock, writer, ActiveSettings(), ActiveSettings()); + var sh = SettingsHandler( + mock, + writer, + ActiveSettings(), + ActiveSettings(), + ); // Simulate remote end by setting the push setting. var header = FrameHeader(6, FrameType.SETTINGS, 0, 0); diff --git a/pkgs/http2/test/src/streams/helper.dart b/pkgs/http2/test/src/streams/helper.dart index 726a7d6449..b7161c0bb9 100644 --- a/pkgs/http2/test/src/streams/helper.dart +++ b/pkgs/http2/test/src/streams/helper.dart @@ -20,10 +20,11 @@ void expectEmptyStream(Stream s) { } void streamTest( - String name, - Future Function(ClientTransportConnection, ServerTransportConnection) - func, - {ClientSettings? settings}) { + String name, + Future Function(ClientTransportConnection, ServerTransportConnection) + func, { + ClientSettings? settings, +}) { return test(name, () { var bidirect = BidirectionalConnection(); bidirect.settings = settings; diff --git a/pkgs/http2/test/src/streams/simple_flow_test.dart b/pkgs/http2/test/src/streams/simple_flow_test.dart index 25226d1a60..7789c51b4e 100644 --- a/pkgs/http2/test/src/streams/simple_flow_test.dart +++ b/pkgs/http2/test/src/streams/simple_flow_test.dart @@ -21,12 +21,19 @@ void main() { void Function(StreamMessage) headersTestFun(String type) { return expectAsync1((StreamMessage msg) { expect( - msg, - isA() - .having((m) => m.headers.first.name, 'headers.first.name', - expectedHeaders.first.name) - .having((m) => m.headers.first.value, 'headers.first.value', - expectedHeaders.first.value)); + msg, + isA() + .having( + (m) => m.headers.first.name, + 'headers.first.name', + expectedHeaders.first.name, + ) + .having( + (m) => m.headers.first.value, + 'headers.first.value', + expectedHeaders.first.value, + ), + ); }); } @@ -39,19 +46,29 @@ void main() { if (expectHeader) { expectHeader = false; expect( - msg, - isA() - .having((m) => m.headers.first.name, 'headers.first.name', - expectedHeaders.first.name) - .having((m) => m.headers.first.value, 'headers.first.value', - expectedHeaders.first.value)); + msg, + isA() + .having( + (m) => m.headers.first.name, + 'headers.first.name', + expectedHeaders.first.name, + ) + .having( + (m) => m.headers.first.value, + 'headers.first.value', + expectedHeaders.first.value, + ), + ); } else { expect(msg, isA()); var bytes = (msg as DataStreamMessage).bytes; expect( - bytes, - allBytes.sublist( - numBytesReceived, numBytesReceived + bytes.length)); + bytes, + allBytes.sublist( + numBytesReceived, + numBytesReceived + bytes.length, + ), + ); numBytesReceived += bytes.length; if (numBytesReceived > allBytes.length) { @@ -75,21 +92,27 @@ void main() { } } - streamTest('single-header-request--empty-response', - (ClientTransportConnection client, - ServerTransportConnection server) async { - server.incomingStreams - .listen(expectAsync1((TransportStream sStream) async { - sStream.incomingMessages - .listen(messageTestFun('server'), onDone: expectAsync0(() {})); - sStream.sendHeaders(expectedHeaders, endStream: true); - await serverReceivedAllBytes.future; - })); + streamTest('single-header-request--empty-response', ( + ClientTransportConnection client, + ServerTransportConnection server, + ) async { + server.incomingStreams.listen( + expectAsync1((TransportStream sStream) async { + sStream.incomingMessages.listen( + messageTestFun('server'), + onDone: expectAsync0(() {}), + ); + sStream.sendHeaders(expectedHeaders, endStream: true); + await serverReceivedAllBytes.future; + }), + ); TransportStream cStream = client.makeRequest(expectedHeaders); sendData(cStream); - cStream.incomingMessages - .listen(headersTestFun('client'), onDone: expectAsync0(() {})); + cStream.incomingMessages.listen( + headersTestFun('client'), + onDone: expectAsync0(() {}), + ); }); }); }); diff --git a/pkgs/http2/test/src/streams/simple_push_test.dart b/pkgs/http2/test/src/streams/simple_push_test.dart index d12dbb5d5d..0447970c64 100644 --- a/pkgs/http2/test/src/streams/simple_push_test.dart +++ b/pkgs/http2/test/src/streams/simple_push_test.dart @@ -55,35 +55,41 @@ void main() { return stream.outgoingMessages.done; } - streamTest('server-push', (ClientTransportConnection client, - ServerTransportConnection server) async { - server.incomingStreams - .listen(expectAsync1((ServerTransportStream sStream) async { - var pushStream = sStream.push(expectedHeaders); - pushStream.sendHeaders(expectedHeaders); - await sendData(pushStream, 'pushing "hello world" :)'); - - unawaited(sStream.incomingMessages.drain()); - sStream.sendHeaders(expectedHeaders, endStream: true); - - await serverReceivedAllBytes.future; - })); + streamTest('server-push', ( + ClientTransportConnection client, + ServerTransportConnection server, + ) async { + server.incomingStreams.listen( + expectAsync1((ServerTransportStream sStream) async { + var pushStream = sStream.push(expectedHeaders); + pushStream.sendHeaders(expectedHeaders); + await sendData(pushStream, 'pushing "hello world" :)'); + + unawaited(sStream.incomingMessages.drain()); + sStream.sendHeaders(expectedHeaders, endStream: true); + + await serverReceivedAllBytes.future; + }), + ); var cStream = client.makeRequest(expectedHeaders, endStream: true); - cStream.incomingMessages - .listen(headersTestFun(), onDone: expectAsync0(() {})); - cStream.peerPushes - .listen(expectAsync1((TransportStreamPush push) async { - testHeaders(push.requestHeaders); - - var iterator = StreamIterator(push.stream.incomingMessages); - var hasNext = await iterator.moveNext(); - expect(hasNext, isTrue); - testHeaders((iterator.current as HeadersStreamMessage).headers); - - var msg = await readData(iterator); - expect(msg, 'pushing "hello world" :)'); - })); + cStream.incomingMessages.listen( + headersTestFun(), + onDone: expectAsync0(() {}), + ); + cStream.peerPushes.listen( + expectAsync1((TransportStreamPush push) async { + testHeaders(push.requestHeaders); + + var iterator = StreamIterator(push.stream.incomingMessages); + var hasNext = await iterator.moveNext(); + expect(hasNext, isTrue); + testHeaders((iterator.current as HeadersStreamMessage).headers); + + var msg = await readData(iterator); + expect(msg, 'pushing "hello world" :)'); + }), + ); }, settings: const ClientSettings(allowServerPushes: true)); }); }); diff --git a/pkgs/http2/test/src/streams/streams_test.dart b/pkgs/http2/test/src/streams/streams_test.dart index 6bfa49c495..c1c1843124 100644 --- a/pkgs/http2/test/src/streams/streams_test.dart +++ b/pkgs/http2/test/src/streams/streams_test.dart @@ -11,42 +11,54 @@ import 'helper.dart'; void main() { group('streams', () { - streamTest('single-header-request--empty-response', - (ClientTransportConnection client, - ServerTransportConnection server) async { + streamTest('single-header-request--empty-response', ( + ClientTransportConnection client, + ServerTransportConnection server, + ) async { var expectedHeaders = [Header.ascii('key', 'value')]; - server.incomingStreams.listen(expectAsync1((TransportStream sStream) { - sStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { - expect(msg, isA()); - - var headersMsg = msg as HeadersStreamMessage; - expectHeadersEqual(headersMsg.headers, expectedHeaders); - }), onDone: expectAsync0(() {})); - sStream.outgoingMessages.close(); - })); + server.incomingStreams.listen( + expectAsync1((TransportStream sStream) { + sStream.incomingMessages.listen( + expectAsync1((StreamMessage msg) { + expect(msg, isA()); - TransportStream cStream = - client.makeRequest(expectedHeaders, endStream: true); + var headersMsg = msg as HeadersStreamMessage; + expectHeadersEqual(headersMsg.headers, expectedHeaders); + }), + onDone: expectAsync0(() {}), + ); + sStream.outgoingMessages.close(); + }), + ); + + TransportStream cStream = client.makeRequest( + expectedHeaders, + endStream: true, + ); expectEmptyStream(cStream.incomingMessages); }); - streamTest('multi-header-request--empty-response', - (ClientTransportConnection client, - ServerTransportConnection server) async { + streamTest('multi-header-request--empty-response', ( + ClientTransportConnection client, + ServerTransportConnection server, + ) async { var expectedHeaders = [Header.ascii('key', 'value')]; - server.incomingStreams.listen(expectAsync1((TransportStream sStream) { - sStream.incomingMessages.listen( + server.incomingStreams.listen( + expectAsync1((TransportStream sStream) { + sStream.incomingMessages.listen( expectAsync1((StreamMessage msg) { expect(msg, isA()); var headersMsg = msg as HeadersStreamMessage; expectHeadersEqual(headersMsg.headers, expectedHeaders); }, count: 3), - onDone: expectAsync0(() {})); - sStream.outgoingMessages.close(); - })); + onDone: expectAsync0(() {}), + ); + sStream.outgoingMessages.close(); + }), + ); TransportStream cStream = client.makeRequest(expectedHeaders); cStream.sendHeaders(expectedHeaders); @@ -54,21 +66,22 @@ void main() { expectEmptyStream(cStream.incomingMessages); }); - streamTest('multi-data-request--empty-response', - (ClientTransportConnection client, - ServerTransportConnection server) async { + streamTest('multi-data-request--empty-response', ( + ClientTransportConnection client, + ServerTransportConnection server, + ) async { var expectedHeaders = [Header.ascii('key', 'value')]; var chunks = [ [1], [2], - [3] + [3], ]; - server.incomingStreams - .listen(expectAsync1((TransportStream sStream) async { - var isFirst = true; - var receivedChunks = >[]; - sStream.incomingMessages.listen( + server.incomingStreams.listen( + expectAsync1((TransportStream sStream) async { + var isFirst = true; + var receivedChunks = >[]; + sStream.incomingMessages.listen( expectAsync1((StreamMessage msg) { if (isFirst) { isFirst = false; @@ -82,11 +95,14 @@ void main() { var dataMsg = msg as DataStreamMessage; receivedChunks.add(dataMsg.bytes); } - }, count: 1 + chunks.length), onDone: expectAsync0(() { - expect(receivedChunks, chunks); - })); - unawaited(sStream.outgoingMessages.close()); - })); + }, count: 1 + chunks.length), + onDone: expectAsync0(() { + expect(receivedChunks, chunks); + }), + ); + unawaited(sStream.outgoingMessages.close()); + }), + ); TransportStream cStream = client.makeRequest(expectedHeaders); chunks.forEach(cStream.sendData); @@ -94,106 +110,141 @@ void main() { expectEmptyStream(cStream.incomingMessages); }); - streamTest('single-header-request--single-headers-response', - (ClientTransportConnection client, - ServerTransportConnection server) async { + streamTest('single-header-request--single-headers-response', ( + ClientTransportConnection client, + ServerTransportConnection server, + ) async { var expectedHeaders = [Header.ascii('key', 'value')]; - server.incomingStreams.listen(expectAsync1((TransportStream sStream) { - sStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { + server.incomingStreams.listen( + expectAsync1((TransportStream sStream) { + sStream.incomingMessages.listen( + expectAsync1((StreamMessage msg) { + expect(msg, isA()); + + var headersMsg = msg as HeadersStreamMessage; + expectHeadersEqual(headersMsg.headers, expectedHeaders); + }), + onDone: expectAsync0(() {}), + ); + sStream.sendHeaders(expectedHeaders, endStream: true); + }), + ); + + TransportStream cStream = client.makeRequest( + expectedHeaders, + endStream: true, + ); + + cStream.incomingMessages.listen( + expectAsync1((StreamMessage msg) { expect(msg, isA()); var headersMsg = msg as HeadersStreamMessage; expectHeadersEqual(headersMsg.headers, expectedHeaders); - }), onDone: expectAsync0(() {})); - sStream.sendHeaders(expectedHeaders, endStream: true); - })); - - TransportStream cStream = - client.makeRequest(expectedHeaders, endStream: true); - - cStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { - expect(msg, isA()); - - var headersMsg = msg as HeadersStreamMessage; - expectHeadersEqual(headersMsg.headers, expectedHeaders); - }), onDone: expectAsync0(() {})); + }), + onDone: expectAsync0(() {}), + ); }); - streamTest('single-header-request--multi-headers-response', - (ClientTransportConnection client, - ServerTransportConnection server) async { + streamTest('single-header-request--multi-headers-response', ( + ClientTransportConnection client, + ServerTransportConnection server, + ) async { var expectedHeaders = [Header.ascii('key', 'value')]; - server.incomingStreams.listen(expectAsync1((TransportStream sStream) { - sStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { + server.incomingStreams.listen( + expectAsync1((TransportStream sStream) { + sStream.incomingMessages.listen( + expectAsync1((StreamMessage msg) { + expect(msg, isA()); + + var headersMsg = msg as HeadersStreamMessage; + expectHeadersEqual(headersMsg.headers, expectedHeaders); + }), + onDone: expectAsync0(() {}), + ); + + sStream.sendHeaders(expectedHeaders); + sStream.sendHeaders(expectedHeaders); + sStream.sendHeaders(expectedHeaders, endStream: true); + }), + ); + + TransportStream cStream = client.makeRequest( + expectedHeaders, + endStream: true, + ); + + cStream.incomingMessages.listen( + expectAsync1((StreamMessage msg) { expect(msg, isA()); var headersMsg = msg as HeadersStreamMessage; expectHeadersEqual(headersMsg.headers, expectedHeaders); - }), onDone: expectAsync0(() {})); - - sStream.sendHeaders(expectedHeaders); - sStream.sendHeaders(expectedHeaders); - sStream.sendHeaders(expectedHeaders, endStream: true); - })); - - TransportStream cStream = - client.makeRequest(expectedHeaders, endStream: true); - - cStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { - expect(msg, isA()); - - var headersMsg = msg as HeadersStreamMessage; - expectHeadersEqual(headersMsg.headers, expectedHeaders); - }, count: 3)); + }, count: 3), + ); }); - streamTest('single-header-request--multi-data-response', - (ClientTransportConnection client, - ServerTransportConnection server) async { + streamTest('single-header-request--multi-data-response', ( + ClientTransportConnection client, + ServerTransportConnection server, + ) async { var expectedHeaders = [Header.ascii('key', 'value')]; var chunks = [ [1], [2], - [3] + [3], ]; - server.incomingStreams.listen(expectAsync1((TransportStream sStream) { - sStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { - expect(msg, isA()); + server.incomingStreams.listen( + expectAsync1((TransportStream sStream) { + sStream.incomingMessages.listen( + expectAsync1((StreamMessage msg) { + expect(msg, isA()); - var headersMsg = msg as HeadersStreamMessage; - expectHeadersEqual(headersMsg.headers, expectedHeaders); - }), onDone: expectAsync0(() {})); + var headersMsg = msg as HeadersStreamMessage; + expectHeadersEqual(headersMsg.headers, expectedHeaders); + }), + onDone: expectAsync0(() {}), + ); - chunks.forEach(sStream.sendData); - sStream.outgoingMessages.close(); - })); + chunks.forEach(sStream.sendData); + sStream.outgoingMessages.close(); + }), + ); TransportStream cStream = client.makeRequest(expectedHeaders); unawaited(cStream.outgoingMessages.close()); var i = 0; - cStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { - expect( + cStream.incomingMessages.listen( + expectAsync1((StreamMessage msg) { + expect( msg, - isA() - .having((m) => m.bytes, 'bytes', chunks[i++])); - }, count: chunks.length)); + isA().having( + (m) => m.bytes, + 'bytes', + chunks[i++], + ), + ); + }, count: chunks.length), + ); }); }); - streamTest('single-data-request--data-trailer-response', - (ClientTransportConnection client, - ServerTransportConnection server) async { + streamTest('single-data-request--data-trailer-response', ( + ClientTransportConnection client, + ServerTransportConnection server, + ) async { var expectedHeaders = [Header.ascii('key', 'value')]; var chunk = [1]; - server.incomingStreams.listen(expectAsync1((TransportStream sStream) async { - var isFirst = true; - List? receivedChunk; - sStream.incomingMessages.listen( + server.incomingStreams.listen( + expectAsync1((TransportStream sStream) async { + var isFirst = true; + List? receivedChunk; + sStream.incomingMessages.listen( expectAsync1((StreamMessage msg) { if (isFirst) { isFirst = false; @@ -210,29 +261,34 @@ void main() { var dataMsg = msg as DataStreamMessage; receivedChunk = dataMsg.bytes; } - }, count: 2), onDone: expectAsync0(() { - expect(receivedChunk, chunk); - sStream.sendData([2]); - sStream.sendHeaders(expectedHeaders, endStream: true); - })); - })); + }, count: 2), + onDone: expectAsync0(() { + expect(receivedChunk, chunk); + sStream.sendData([2]); + sStream.sendHeaders(expectedHeaders, endStream: true); + }), + ); + }), + ); TransportStream cStream = client.makeRequest(expectedHeaders); cStream.sendData(chunk, endStream: true); var isFirst = true; - cStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { - if (isFirst) { - expect(msg, const TypeMatcher()); - final data = msg as DataStreamMessage; - expect(data.bytes, [2]); - isFirst = false; - } else { - expect(msg, const TypeMatcher()); - final trailer = msg as HeadersStreamMessage; - expect(trailer.endStream, true); - expectHeadersEqual(trailer.headers, expectedHeaders); - } - }, count: 2)); + cStream.incomingMessages.listen( + expectAsync1((StreamMessage msg) { + if (isFirst) { + expect(msg, const TypeMatcher()); + final data = msg as DataStreamMessage; + expect(data.bytes, [2]); + isFirst = false; + } else { + expect(msg, const TypeMatcher()); + final trailer = msg as HeadersStreamMessage; + expect(trailer.endStream, true); + expectHeadersEqual(trailer.headers, expectedHeaders); + } + }, count: 2), + ); }); } diff --git a/pkgs/http2/test/transport_test.dart b/pkgs/http2/test/transport_test.dart index 96c2c03e0c..72aedf9ae7 100644 --- a/pkgs/http2/test/transport_test.dart +++ b/pkgs/http2/test/transport_test.dart @@ -13,187 +13,241 @@ import 'src/hpack/hpack_test.dart' show isHeader; void main() { group('transport-test', () { - transportTest('ping', - (TransportConnection client, TransportConnection server) async { + transportTest('ping', ( + TransportConnection client, + TransportConnection server, + ) async { await client.ping(); await server.ping(); }); - transportTest('terminated-client-ping', - (TransportConnection client, TransportConnection server) async { - var clientError = - client.ping().catchError(expectAsync2((Object e, StackTrace s) { - expect(e, isA()); - })); + transportTest('terminated-client-ping', ( + TransportConnection client, + TransportConnection server, + ) async { + var clientError = client.ping().catchError( + expectAsync2((Object e, StackTrace s) { + expect(e, isA()); + }), + ); await client.terminate(); await clientError; // NOTE: Now the connection is dead and client/server should complete // with [TransportException]s when doing work (e.g. ping). - unawaited(client.ping().catchError(expectAsync2((Object e, StackTrace s) { - expect(e, isA()); - }))); - unawaited(server.ping().catchError(expectAsync2((Object e, StackTrace s) { - expect(e, isA()); - }))); + unawaited( + client.ping().catchError( + expectAsync2((Object e, StackTrace s) { + expect(e, isA()); + }), + ), + ); + unawaited( + server.ping().catchError( + expectAsync2((Object e, StackTrace s) { + expect(e, isA()); + }), + ), + ); }); - transportTest('terminated-server-ping', - (TransportConnection client, TransportConnection server) async { - var clientError = - client.ping().catchError(expectAsync2((Object e, StackTrace s) { - expect(e, isA()); - })); + transportTest('terminated-server-ping', ( + TransportConnection client, + TransportConnection server, + ) async { + var clientError = client.ping().catchError( + expectAsync2((Object e, StackTrace s) { + expect(e, isA()); + }), + ); await server.terminate(); await clientError; // NOTE: Now the connection is dead and the client/server should complete // with [TransportException]s when doing work (e.g. ping). - unawaited(client.ping().catchError(expectAsync2((Object e, StackTrace s) { - expect(e, isA()); - }))); - unawaited(server.ping().catchError(expectAsync2((Object e, StackTrace s) { - expect(e, isA()); - }))); + unawaited( + client.ping().catchError( + expectAsync2((Object e, StackTrace s) { + expect(e, isA()); + }), + ), + ); + unawaited( + server.ping().catchError( + expectAsync2((Object e, StackTrace s) { + expect(e, isA()); + }), + ), + ); }); const concurrentStreamLimit = 5; - transportTest('exhaust-concurrent-stream-limit', - (ClientTransportConnection client, - ServerTransportConnection server) async { - Future clientFun() async { - // We have to wait until the max-concurrent-streams [Setting] was - // transferred from server to client, which is asynchronous. - // The default is unlimited, which is why we have to wait for the server - // setting to arrive on the client. - // At the moment, delaying by 2 microtask cycles is enough. - await Future.value(); - await Future.value(); - - final streams = []; - for (var i = 0; i < concurrentStreamLimit; ++i) { - expect(client.isOpen, true); - streams.add(client.makeRequest([Header.ascii('a', 'b')])); - } - expect(client.isOpen, false); - for (final stream in streams) { - stream.sendData([], endStream: true); + transportTest( + 'exhaust-concurrent-stream-limit', + ( + ClientTransportConnection client, + ServerTransportConnection server, + ) async { + Future clientFun() async { + // We have to wait until the max-concurrent-streams [Setting] was + // transferred from server to client, which is asynchronous. + // The default is unlimited, which is why we have to wait for the + // server setting to arrive on the client. + // At the moment, delaying by 2 microtask cycles is enough. + await Future.value(); + await Future.value(); + + final streams = []; + for (var i = 0; i < concurrentStreamLimit; ++i) { + expect(client.isOpen, true); + streams.add(client.makeRequest([Header.ascii('a', 'b')])); + } + expect(client.isOpen, false); + for (final stream in streams) { + stream.sendData([], endStream: true); + } + await client.finish(); } - await client.finish(); - } - Future serverFun() async { - await for (final stream in server.incomingStreams) { - await stream.incomingMessages.toList(); - stream.sendHeaders([Header.ascii('a', 'b')], endStream: true); + Future serverFun() async { + await for (final stream in server.incomingStreams) { + await stream.incomingMessages.toList(); + stream.sendHeaders([Header.ascii('a', 'b')], endStream: true); + } + await server.finish(); } - await server.finish(); - } - await Future.wait([clientFun(), serverFun()]); - }, - serverSettings: - const ServerSettings(concurrentStreamLimit: concurrentStreamLimit)); - - transportTest('disabled-push', (ClientTransportConnection client, - ServerTransportConnection server) async { - server.incomingStreams - .listen(expectAsync1((ServerTransportStream stream) async { - expect(stream.canPush, false); - expect(() => stream.push([Header.ascii('a', 'b')]), - throwsA(const TypeMatcher())); - stream.sendHeaders([Header.ascii('x', 'y')], endStream: true); - })); - - var stream = - client.makeRequest([Header.ascii('a', 'b')], endStream: true); + await Future.wait([clientFun(), serverFun()]); + }, + serverSettings: const ServerSettings( + concurrentStreamLimit: concurrentStreamLimit, + ), + ); + + transportTest('disabled-push', ( + ClientTransportConnection client, + ServerTransportConnection server, + ) async { + server.incomingStreams.listen( + expectAsync1((ServerTransportStream stream) async { + expect(stream.canPush, false); + expect( + () => stream.push([Header.ascii('a', 'b')]), + throwsA(const TypeMatcher()), + ); + stream.sendHeaders([Header.ascii('x', 'y')], endStream: true); + }), + ); + + var stream = client.makeRequest([ + Header.ascii('a', 'b'), + ], endStream: true); var messages = await stream.incomingMessages.toList(); expect(messages, hasLength(1)); expect(messages[0] is HeadersStreamMessage, true); expect( - (messages[0] as HeadersStreamMessage).headers[0], isHeader('x', 'y')); + (messages[0] as HeadersStreamMessage).headers[0], + isHeader('x', 'y'), + ); expect(await stream.peerPushes.toList(), isEmpty); }); // By default, the stream concurrency level is set to this limit. const kDefaultStreamLimit = 100; - transportTest('enabled-push-100', (ClientTransportConnection client, - ServerTransportConnection server) async { - // To ensure the limit is kept up-to-date with closing/opening streams, we - // retry this. - const kRepetitions = 20; + transportTest( + 'enabled-push-100', + ( + ClientTransportConnection client, + ServerTransportConnection server, + ) async { + // To ensure the limit is kept up-to-date with closing/opening streams, we + // retry this. + const kRepetitions = 20; - Future serverFun() async { - await for (ServerTransportStream stream in server.incomingStreams) { - var pushes = []; - for (var i = 0; i < kDefaultStreamLimit; i++) { - expect(stream.canPush, true); - pushes.add(stream.push([Header.ascii('a', 'b')])); - } + Future serverFun() async { + await for (ServerTransportStream stream in server.incomingStreams) { + var pushes = []; + for (var i = 0; i < kDefaultStreamLimit; i++) { + expect(stream.canPush, true); + pushes.add(stream.push([Header.ascii('a', 'b')])); + } - // Now we should have reached the limit and we should not be able to - // create more pushes. - expect(stream.canPush, false); - expect(() => stream.push([Header.ascii('a', 'b')]), - throwsA(const TypeMatcher())); - - // Finish the pushes - for (var pushedStream in pushes) { - pushedStream - .sendHeaders([Header.ascii('e', 'nd')], endStream: true); - await pushedStream.incomingMessages.toList(); - } + // Now we should have reached the limit and we should not be able to + // create more pushes. + expect(stream.canPush, false); + expect( + () => stream.push([Header.ascii('a', 'b')]), + throwsA(const TypeMatcher()), + ); + + // Finish the pushes + for (var pushedStream in pushes) { + pushedStream.sendHeaders([ + Header.ascii('e', 'nd'), + ], endStream: true); + await pushedStream.incomingMessages.toList(); + } - // Finish the stream. - stream.sendHeaders([Header.ascii('x', 'y')], endStream: true); - expect(await stream.incomingMessages.toList(), hasLength(1)); + // Finish the stream. + stream.sendHeaders([Header.ascii('x', 'y')], endStream: true); + expect(await stream.incomingMessages.toList(), hasLength(1)); + } } - } - Future clientFun() async { - for (var i = 0; i < kRepetitions; i++) { - var stream = - client.makeRequest([Header.ascii('a', 'b')], endStream: true); - - Future expectPeerPushes() async { - var numberOfPushes = 0; - await for (TransportStreamPush pushedStream in stream.peerPushes) { - numberOfPushes++; - var messages = - await pushedStream.stream.incomingMessages.toList(); - expect(messages, hasLength(1)); - expect((messages[0] as HeadersStreamMessage).headers[0], - isHeader('e', 'nd')); - expect(await pushedStream.stream.peerPushes.toList(), isEmpty); + Future clientFun() async { + for (var i = 0; i < kRepetitions; i++) { + var stream = client.makeRequest([ + Header.ascii('a', 'b'), + ], endStream: true); + + Future expectPeerPushes() async { + var numberOfPushes = 0; + await for (TransportStreamPush pushedStream + in stream.peerPushes) { + numberOfPushes++; + var messages = + await pushedStream.stream.incomingMessages.toList(); + expect(messages, hasLength(1)); + expect( + (messages[0] as HeadersStreamMessage).headers[0], + isHeader('e', 'nd'), + ); + expect(await pushedStream.stream.peerPushes.toList(), isEmpty); + } + return numberOfPushes; } - return numberOfPushes; - } - // Wait for the end of the normal stream. - var messages = await stream.incomingMessages.toList(); - expect(messages, hasLength(1)); - expect(messages[0] is HeadersStreamMessage, true); - expect((messages[0] as HeadersStreamMessage).headers[0], - isHeader('x', 'y')); + // Wait for the end of the normal stream. + var messages = await stream.incomingMessages.toList(); + expect(messages, hasLength(1)); + expect(messages[0] is HeadersStreamMessage, true); + expect( + (messages[0] as HeadersStreamMessage).headers[0], + isHeader('x', 'y'), + ); - expect(await expectPeerPushes(), kDefaultStreamLimit); + expect(await expectPeerPushes(), kDefaultStreamLimit); + } } - } - - var serverFuture = serverFun(); - await clientFun(); - await client.terminate(); - await serverFuture; - }, - clientSettings: const ClientSettings( - concurrentStreamLimit: kDefaultStreamLimit, - allowServerPushes: true)); - - transportTest('early-shutdown', (ClientTransportConnection client, - ServerTransportConnection server) async { + var serverFuture = serverFun(); + + await clientFun(); + await client.terminate(); + await serverFuture; + }, + clientSettings: const ClientSettings( + concurrentStreamLimit: kDefaultStreamLimit, + allowServerPushes: true, + ), + ); + + transportTest('early-shutdown', ( + ClientTransportConnection client, + ServerTransportConnection server, + ) async { Future serverFun() async { await for (ServerTransportStream stream in server.incomingStreams) { stream.sendHeaders([Header.ascii('x', 'y')], endStream: true); @@ -214,19 +268,24 @@ void main() { await Future.wait([serverFun(), clientFun()]); }); - transportTest('client-terminates-stream', (ClientTransportConnection client, - ServerTransportConnection server) async { + transportTest('client-terminates-stream', ( + ClientTransportConnection client, + ServerTransportConnection server, + ) async { var readyForError = Completer(); Future serverFun() async { await for (ServerTransportStream stream in server.incomingStreams) { stream.sendHeaders([Header.ascii('x', 'y')], endStream: true); - stream.incomingMessages.listen(expectAsync1((msg) { - expect(msg, isA()); - readyForError.complete(); - }), onError: expectAsync1((Object error) { - expect('$error', contains('Stream was terminated by peer')); - })); + stream.incomingMessages.listen( + expectAsync1((msg) { + expect(msg, isA()); + readyForError.complete(); + }), + onError: expectAsync1((Object error) { + expect('$error', contains('Stream was terminated by peer')); + }), + ); } await server.finish(); } @@ -242,8 +301,10 @@ void main() { await Future.wait([serverFun(), clientFun()]); }); - transportTest('server-terminates-stream', (ClientTransportConnection client, - ServerTransportConnection server) async { + transportTest('server-terminates-stream', ( + ClientTransportConnection client, + ServerTransportConnection server, + ) async { Future serverFun() async { await for (ServerTransportStream stream in server.incomingStreams) { stream.terminate(); @@ -255,19 +316,22 @@ void main() { var headers = [Header.ascii('a', 'b')]; var stream = client.makeRequest(headers, endStream: true); var messageList = stream.incomingMessages.toList(); - await messageList.catchError(expectAsync1((Object error) { - expect('$error', contains('Stream was terminated by peer')); - return []; - })); + await messageList.catchError( + expectAsync1((Object error) { + expect('$error', contains('Stream was terminated by peer')); + return []; + }), + ); await client.finish(); } await Future.wait([serverFun(), clientFun()]); }); - transportTest('client-terminates-stream-after-half-close', - (ClientTransportConnection client, - ServerTransportConnection server) async { + transportTest('client-terminates-stream-after-half-close', ( + ClientTransportConnection client, + ServerTransportConnection server, + ) async { var readyForError = Completer(); Future serverFun() async { @@ -301,9 +365,10 @@ void main() { await Future.wait([serverFun(), clientFun()]); }); - transportTest('server-terminates-stream-after-half-close', - (ClientTransportConnection client, - ServerTransportConnection server) async { + transportTest('server-terminates-stream-after-half-close', ( + ClientTransportConnection client, + ServerTransportConnection server, + ) async { var readyForError = Completer(); Future serverFun() async { @@ -335,8 +400,10 @@ void main() { await Future.wait([serverFun(), clientFun()]); }); - transportTest('idle-handler', (ClientTransportConnection client, - ServerTransportConnection server) async { + transportTest('idle-handler', ( + ClientTransportConnection client, + ServerTransportConnection server, + ) async { Future serverFun() async { var activeCount = 0; var idleCount = 0; @@ -349,9 +416,11 @@ void main() { }, count: 6); await for (final stream in server.incomingStreams) { stream.sendHeaders([]); - unawaited(stream.incomingMessages - .toList() - .then((_) => stream.outgoingMessages.close())); + unawaited( + stream.incomingMessages.toList().then( + (_) => stream.outgoingMessages.close(), + ), + ); } await server.finish(); expect(activeCount, 3); @@ -369,7 +438,9 @@ void main() { } }, count: 6); final streams = List.generate( - 5, (_) => client.makeRequest([])); + 5, + (_) => client.makeRequest([]), + ); await Future.wait(streams.map((s) => s.outgoingMessages.close())); await Future.wait(streams.map((s) => s.incomingMessages.toList())); // This extra await is needed to allow the idle handler to run before @@ -403,11 +474,14 @@ void main() { final headers = [Header.ascii('a', 'b')]; Future testWindowSize( - ClientTransportConnection client, - ServerTransportConnection server, - int expectedStreamFlowcontrolWindow) async { - expect(expectedStreamFlowcontrolWindow, - lessThan(kChunkSize * kNumberOfMessages)); + ClientTransportConnection client, + ServerTransportConnection server, + int expectedStreamFlowcontrolWindow, + ) async { + expect( + expectedStreamFlowcontrolWindow, + lessThan(kChunkSize * kNumberOfMessages), + ); var serverSentBytes = 0; var flowcontrolWindowFull = Completer(); @@ -444,8 +518,10 @@ void main() { // of adding is [kChunkSize], it could be that we added // [kChunkSize - 1] bytes more than allowed, before getting // the pause event). - expect(serverSentBytes - kChunkSize + 1, - lessThan(expectedStreamFlowcontrolWindow)); + expect( + serverSentBytes - kChunkSize + 1, + lessThan(expectedStreamFlowcontrolWindow), + ); flowcontrolWindowFull.complete(); }) ..onResume = addData @@ -473,8 +549,10 @@ void main() { var dataMessage = message as DataStreamMessage; // We're just testing the first byte, to make the test faster. - expect(dataMessage.bytes[0], - ((byteNr ~/ kChunkSize) + (byteNr % kChunkSize)) % 256); + expect( + dataMessage.bytes[0], + ((byteNr ~/ kChunkSize) + (byteNr % kChunkSize)) % 256, + ); byteNr += dataMessage.bytes.length; } @@ -492,27 +570,34 @@ void main() { await Future.wait([serverFun(), clientFun()]); } - transportTest('fast-sender-receiver-paused--default-window-size', - (ClientTransportConnection client, - ServerTransportConnection server) async { + transportTest('fast-sender-receiver-paused--default-window-size', ( + ClientTransportConnection client, + ServerTransportConnection server, + ) async { await testWindowSize(client, server, Window().size); }); - transportTest('fast-sender-receiver-paused--10kb-window-size', - (ClientTransportConnection client, - ServerTransportConnection server) async { - await testWindowSize(client, server, 8096); - }, clientSettings: const ClientSettings(streamWindowSize: 8096)); + transportTest( + 'fast-sender-receiver-paused--10kb-window-size', + ( + ClientTransportConnection client, + ServerTransportConnection server, + ) async { + await testWindowSize(client, server, 8096); + }, + clientSettings: const ClientSettings(streamWindowSize: 8096), + ); }); }); } void transportTest( - String name, - Future Function(ClientTransportConnection, ServerTransportConnection) - func, - {ClientSettings? clientSettings, - ServerSettings? serverSettings}) { + String name, + Future Function(ClientTransportConnection, ServerTransportConnection) + func, { + ClientSettings? clientSettings, + ServerSettings? serverSettings, +}) { return test(name, () { var bidirectional = BidirectionalConnection(); bidirectional.clientSettings = clientSettings; @@ -535,10 +620,16 @@ class BidirectionalConnection { Stream> get readB => writeB.stream; ClientTransportConnection get clientConnection => - ClientTransportConnection.viaStreams(readA, writeB.sink, - settings: clientSettings); + ClientTransportConnection.viaStreams( + readA, + writeB.sink, + settings: clientSettings, + ); ServerTransportConnection get serverConnection => - ServerTransportConnection.viaStreams(readB, writeA.sink, - settings: serverSettings); + ServerTransportConnection.viaStreams( + readB, + writeA.sink, + settings: serverSettings, + ); } From c18d55d8b3c658fb9194d592800ec37fe44bf93d Mon Sep 17 00:00:00 2001 From: Moritz Date: Wed, 2 Apr 2025 08:05:59 +0200 Subject: [PATCH 2/3] Fix CI --- .github/workflows/http2.yaml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/http2.yaml b/.github/workflows/http2.yaml index 187686e6e2..c1362d3658 100644 --- a/.github/workflows/http2.yaml +++ b/.github/workflows/http2.yaml @@ -45,18 +45,14 @@ jobs: run: dart analyze --fatal-infos if: always() && steps.install.outcome == 'success' - # Run tests on a matrix consisting of two dimensions: - # 1. OS: ubuntu-latest, (macos-latest, windows-latest) - # 2. release channel: dev test: needs: analyze runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - # Add macos-latest and/or windows-latest if relevant for this package. os: [ubuntu-latest] - sdk: [3.2, dev] + sdk: [3.7, stable, dev] steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - uses: dart-lang/setup-dart@e51d8e571e22473a2ddebf0ef8a2123f0ab2c02c From 4afc7f40799368391d5f7e4720c6ea0bd8c0efbb Mon Sep 17 00:00:00 2001 From: Moritz Date: Wed, 2 Apr 2025 08:08:40 +0200 Subject: [PATCH 3/3] Add changelog entry --- pkgs/http2/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index cd2d091452..652fc543ee 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -1,5 +1,7 @@ ## 3.0.0-wip +- Require Dart SDK `3.7.0`. + ## 2.3.1 - Require Dart 3.2