| // 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 {} |