| // 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); | |
| } |