| // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| import 'package:http2/src/flowcontrol/window.dart'; | |
| import 'package:http2/src/flowcontrol/window_handler.dart'; | |
| import 'package:http2/src/frames/frames.dart'; | |
| import 'package:mockito/mockito.dart'; | |
| import 'package:test/test.dart'; | |
| 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)); | |
| expect(handler.peerWindowSize, initialSize); | |
| expect(window.size, initialSize); | |
| // If we're sending data to the remote end, we need to subtract | |
| // the number of bytes from the outgoing connection (and stream) windows. | |
| handler.decreaseWindow(100); | |
| expect(handler.peerWindowSize, initialSize - 100); | |
| expect(window.size, initialSize - 100); | |
| // If we received a window update frame, the window should be increased | |
| // again. | |
| var frameHeader = FrameHeader(4, FrameType.WINDOW_UPDATE, 0, 0); | |
| handler.processWindowUpdate(WindowUpdateFrame(frameHeader, 100)); | |
| expect(handler.peerWindowSize, initialSize); | |
| expect(window.size, initialSize); | |
| sub.cancel(); | |
| // If we decrease the outgoing window size to 0 or below, and | |
| // increase it again, we expect to get an update event. | |
| 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); | |
| })); | |
| // Now we trigger the 1 byte window increase | |
| handler.processWindowUpdate(WindowUpdateFrame(frameHeader, 1)); | |
| sub.cancel(); | |
| // 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)); | |
| } | |
| test('outgoing-connection-window-handler', () { | |
| var window = Window(); | |
| var initialSize = window.size; | |
| var handler = OutgoingConnectionWindowHandler(window); | |
| testAbstractOutgoingWindowHandler(handler, window, initialSize); | |
| }); | |
| test('outgoing-stream-window-handler', () { | |
| var window = Window(); | |
| var initialSize = window.size; | |
| var handler = OutgoingStreamWindowHandler(window); | |
| testAbstractOutgoingWindowHandler(handler, window, initialSize); | |
| // Test stream specific functionality: If the connection window | |
| // gets increased/decreased via a [SettingsFrame], all stream | |
| // windows need to get updated as well. | |
| window = Window(); | |
| initialSize = window.size; | |
| handler = OutgoingStreamWindowHandler(window); | |
| expect(handler.positiveWindow.wouldBuffer, isFalse); | |
| final bufferEmpty = handler.positiveWindow.bufferEmptyEvents | |
| .listen(expectAsync1((_) {}, count: 0)); | |
| handler.processInitialWindowSizeSettingChange(-window.size); | |
| expect(handler.positiveWindow.wouldBuffer, isTrue); | |
| expect(handler.peerWindowSize, 0); | |
| expect(window.size, 0); | |
| bufferEmpty.onData(expectAsync1((_) {}, count: 1)); | |
| handler.processInitialWindowSizeSettingChange(1); | |
| expect(handler.positiveWindow.wouldBuffer, isFalse); | |
| expect(handler.peerWindowSize, 1); | |
| expect(window.size, 1); | |
| expect( | |
| () => handler.processInitialWindowSizeSettingChange( | |
| Window.MAX_WINDOW_SIZE + 1), | |
| throwsA(isFlowControlException)); | |
| }); | |
| test('incoming-window-handler', () { | |
| const STREAM_ID = 99; | |
| var fw = FrameWriterMock(); | |
| var window = Window(); | |
| var initialSize = window.size; | |
| var handler = IncomingWindowHandler.stream(fw, window, STREAM_ID); | |
| expect(handler.localWindowSize, initialSize); | |
| expect(window.size, initialSize); | |
| // If the remote end sends us now 100 bytes, it reduces the local | |
| // incoming window by 100 bytes. Once we handled these bytes, it, | |
| // will send a [WindowUpdateFrame] to the remote peer to ACK it. | |
| handler.gotData(100); | |
| expect(handler.localWindowSize, initialSize - 100); | |
| expect(window.size, initialSize - 100); | |
| // The data might sit in a queue. Once the user drains enough data of | |
| // the queue, we will start ACKing the data and the window becomes | |
| // positive again. | |
| handler.dataProcessed(100); | |
| expect(handler.localWindowSize, initialSize); | |
| expect(window.size, initialSize); | |
| verify(fw.writeWindowUpdate(100, streamId: STREAM_ID)).called(1); | |
| verifyNoMoreInteractions(fw); | |
| }); | |
| }); | |
| } | |
| class FrameWriterMock extends Mock implements FrameWriter {} |