blob: 1af51c929a2ec3e79a7e0e5e1774a0448552963c [file] [log] [blame]
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import 'package:http2/src/connection_preface.dart';
import 'package:http2/src/frames/frames.dart';
import 'package:http2/src/hpack/hpack.dart';
import 'package:http2/src/settings/settings.dart';
import 'package:http2/transport.dart';
import 'package:test/test.dart';
void main() {
group('server-tests', () {
group('normal', () {
serverTest('gracefull-shutdown-for-unused-connection',
(ServerTransportConnection server,
FrameWriter clientWriter,
StreamIterator<Frame> clientReader,
Future<Frame> Function() nextFrame) async {
Future serverFun() async {
expect(await server.incomingStreams.toList(), isEmpty);
await server.finish();
}
Future clientFun() async {
expect(await nextFrame() is SettingsFrame, true);
clientWriter.writeSettingsAckFrame();
clientWriter.writeSettingsFrame([]);
expect(await nextFrame() is SettingsFrame, true);
// Tell the server to finish.
clientWriter.writeGoawayFrame(3, ErrorCode.NO_ERROR, []);
// Make sure the server ended the connection.
expect(await clientReader.moveNext(), false);
}
await Future.wait([serverFun(), clientFun()]);
});
});
group('client-errors', () {
serverTest('no-settings-frame-at-beginning',
(ServerTransportConnection server,
FrameWriter clientWriter,
StreamIterator<Frame> clientReader,
Future<Frame> Function() nextFrame) async {
Future serverFun() async {
// TODO: Do we want to get an error in this case?
expect(await server.incomingStreams.toList(), isEmpty);
await server.finish();
}
Future clientFun() async {
expect(await nextFrame() is SettingsFrame, true);
// Write headers frame to open a new stream
clientWriter.writeHeadersFrame(1, [], endStream: true);
// Make sure the client gets a [GoawayFrame] frame.
expect(
await nextFrame(),
isA<GoawayFrame>().having(
(f) => f.errorCode, 'errorCode', ErrorCode.PROTOCOL_ERROR));
// Make sure the server ended the connection.
expect(await clientReader.moveNext(), false);
}
await Future.wait([serverFun(), clientFun()]);
});
serverTest('data-frame-for-invalid-stream',
(ServerTransportConnection server,
FrameWriter clientWriter,
StreamIterator<Frame> clientReader,
Future<Frame> Function() nextFrame) async {
Future serverFun() async {
await server.incomingStreams.toList();
await server.finish();
}
Future clientFun() async {
expect(await nextFrame() is SettingsFrame, true);
clientWriter.writeSettingsAckFrame();
clientWriter.writeSettingsFrame([]);
expect(await nextFrame() is SettingsFrame, true);
// Write data frame to non-existent stream.
clientWriter.writeDataFrame(3, [1, 2, 3]);
// Make sure the client gets a [RstStreamFrame] frame.
var frame = await nextFrame();
expect(frame is WindowUpdateFrame, true);
expect(
await nextFrame(),
isA<RstStreamFrame>()
.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, []);
// Make sure the server ended the connection.
expect(await clientReader.moveNext(), false);
}
await Future.wait([serverFun(), clientFun()]);
});
serverTest('data-frame-after-stream-closed',
(ServerTransportConnection server,
FrameWriter clientWriter,
StreamIterator<Frame> clientReader,
Future<Frame> Function() nextFrame) async {
Future serverFun() async {
await server.incomingStreams.toList();
await server.finish();
}
Future clientFun() async {
expect(await nextFrame() is SettingsFrame, true);
clientWriter.writeSettingsAckFrame();
clientWriter.writeSettingsFrame([]);
expect(await nextFrame() is SettingsFrame, true);
clientWriter.writeHeadersFrame(3, [Header.ascii('a', 'b')],
endStream: true);
// Write data frame to non-existent stream (stream 3 was closed
// above).
clientWriter.writeDataFrame(3, [1, 2, 3]);
// Make sure the client gets a [RstStreamFrame] frame.
expect(
await nextFrame(),
isA<RstStreamFrame>()
.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, []);
// Make sure the server ended the connection.
expect(await clientReader.moveNext(), false);
}
await Future.wait([serverFun(), clientFun()]);
});
});
group('server-errors', () {
serverTest('server-resets-stream', (ServerTransportConnection server,
FrameWriter clientWriter,
StreamIterator<Frame> clientReader,
Future<Frame> Function() nextFrame) async {
Future serverFun() async {
var it = StreamIterator(server.incomingStreams);
expect(await it.moveNext(), true);
TransportStream stream = it.current;
stream.terminate();
expect(await it.moveNext(), false);
await server.finish();
}
Future clientFun() async {
expect(await nextFrame() is SettingsFrame, true);
clientWriter.writeSettingsAckFrame();
clientWriter.writeSettingsFrame([]);
expect(await nextFrame() is SettingsFrame, true);
clientWriter.writeHeadersFrame(1, [Header.ascii('a', 'b')],
endStream: false);
// Make sure the client gets a [RstStreamFrame] frame.
expect(
await nextFrame(),
isA<RstStreamFrame>()
.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, []);
// Make sure the server ended the connection.
expect(await clientReader.moveNext(), false);
}
await Future.wait([serverFun(), clientFun()]);
});
});
});
}
void serverTest(
String name,
void Function(
ServerTransportConnection,
FrameWriter,
StreamIterator<Frame> frameReader,
Future<Frame> Function() readNext)
func) {
return test(name, () {
var streams = ClientErrorStreams();
var clientReader = streams.clientConnectionFrameReader;
Future<Frame> readNext() async {
expect(await clientReader.moveNext(), true);
return clientReader.current;
}
return func(streams.serverConnection, streams.clientConnectionFrameWriter,
clientReader, readNext);
});
}
class ClientErrorStreams {
final StreamController<List<int>> writeA = StreamController();
final StreamController<List<int>> writeB = StreamController();
Stream<List<int>> get readA => writeA.stream;
Stream<List<int>> get readB => writeB.stream;
StreamIterator<Frame> get clientConnectionFrameReader {
var localSettings = ActiveSettings();
return StreamIterator(FrameReader(readA, localSettings).startDecoding());
}
FrameWriter get clientConnectionFrameWriter {
var encoder = HPackEncoder();
var peerSettings = ActiveSettings();
writeB.add(CONNECTION_PREFACE);
return FrameWriter(encoder, writeB, peerSettings);
}
ServerTransportConnection get serverConnection =>
ServerTransportConnection.viaStreams(readB, writeA);
}