blob: 017dadd4efc1a0a619605d863b3586a0868e3cb5 [file] [log] [blame]
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import '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 {}