From d3953a8d3d91b4a6c1d221705f63923d0f34022c Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Thu, 19 Mar 2015 15:35:51 +0100 Subject: [PATCH 001/172] Initial commit --- pkgs/http2/.gitignore | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 pkgs/http2/.gitignore diff --git a/pkgs/http2/.gitignore b/pkgs/http2/.gitignore new file mode 100644 index 0000000000..2ceb8eed93 --- /dev/null +++ b/pkgs/http2/.gitignore @@ -0,0 +1,14 @@ +# Don’t commit the following directories created by pub. +build/ +packages/ +.buildlog + +# Or the files created by dart2js. +*.dart.js +*.dart.precompiled.js +*.js_ +*.js.deps +*.js.map + +# Include when developing application packages. +pubspec.lock From b92d81c567ffc7bdeddaa4577d3411d5313227b9 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Thu, 19 Mar 2015 16:02:50 +0100 Subject: [PATCH 002/172] Initialize repository from oss-template & add codereview.settings file R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/173687013 --- pkgs/http2/.gitignore | 2 +- pkgs/http2/AUTHORS | 6 ++++++ pkgs/http2/CHANGELOG.md | 5 +++++ pkgs/http2/CONTRIBUTING.md | 33 +++++++++++++++++++++++++++++++++ pkgs/http2/LICENSE | 26 ++++++++++++++++++++++++++ pkgs/http2/README.md | 9 +++++++++ pkgs/http2/codereview.settings | 3 +++ pkgs/http2/pubspec.yaml | 12 ++++++++++++ 8 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 pkgs/http2/AUTHORS create mode 100644 pkgs/http2/CHANGELOG.md create mode 100644 pkgs/http2/CONTRIBUTING.md create mode 100644 pkgs/http2/LICENSE create mode 100644 pkgs/http2/README.md create mode 100644 pkgs/http2/codereview.settings create mode 100644 pkgs/http2/pubspec.yaml diff --git a/pkgs/http2/.gitignore b/pkgs/http2/.gitignore index 2ceb8eed93..d80b0673b7 100644 --- a/pkgs/http2/.gitignore +++ b/pkgs/http2/.gitignore @@ -1,6 +1,6 @@ # Don’t commit the following directories created by pub. build/ -packages/ +packages .buildlog # Or the files created by dart2js. diff --git a/pkgs/http2/AUTHORS b/pkgs/http2/AUTHORS new file mode 100644 index 0000000000..e8063a8cd6 --- /dev/null +++ b/pkgs/http2/AUTHORS @@ -0,0 +1,6 @@ +# Below is a list of people and organizations that have contributed +# to the project. Names should be added to the list like so: +# +# Name/Organization + +Google Inc. diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md new file mode 100644 index 0000000000..2a2d63cf8e --- /dev/null +++ b/pkgs/http2/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changelog + +## 0.0.1 + +- Initial version diff --git a/pkgs/http2/CONTRIBUTING.md b/pkgs/http2/CONTRIBUTING.md new file mode 100644 index 0000000000..6f5e0ea67d --- /dev/null +++ b/pkgs/http2/CONTRIBUTING.md @@ -0,0 +1,33 @@ +Want to contribute? Great! First, read this page (including the small print at +the end). + +### Before you contribute +Before we can use your code, you must sign the +[Google Individual Contributor License Agreement](https://cla.developers.google.com/about/google-individual) +(CLA), which you can do online. The CLA is necessary mainly because you own the +copyright to your changes, even after your contribution becomes part of our +codebase, so we need your permission to use and distribute your code. We also +need to be sure of various other things—for instance that you'll tell us if you +know that your code infringes on other people's patents. You don't have to sign +the CLA until after you've submitted your code for review and a member has +approved it, but you must do it before we can put your code into our codebase. + +Before you start working on a larger contribution, you should get in touch with +us first through the issue tracker with your idea so that we can help out and +possibly guide you. Coordinating up front makes it much easier to avoid +frustration later on. + +### Code reviews +All submissions, including submissions by project members, require review. + +### File headers +All files in the project must start with the following header. + + // 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. + +### The small print +Contributions made by corporations are covered by a different agreement than the +one above, the +[Software Grant and Corporate Contributor License Agreement](https://developers.google.com/open-source/cla/corporate). diff --git a/pkgs/http2/LICENSE b/pkgs/http2/LICENSE new file mode 100644 index 0000000000..de31e1a0a4 --- /dev/null +++ b/pkgs/http2/LICENSE @@ -0,0 +1,26 @@ +Copyright 2015, the Dart project authors. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/pkgs/http2/README.md b/pkgs/http2/README.md new file mode 100644 index 0000000000..d9b78d3ad8 --- /dev/null +++ b/pkgs/http2/README.md @@ -0,0 +1,9 @@ +# A HTTP/2 implementation in dart. + +This is a work in progress and experimental. + +## Features and bugs + +Please file feature requests and bugs at the [issue tracker][tracker]. + +[tracker]: https://github.com/dart-lang/http2/issues diff --git a/pkgs/http2/codereview.settings b/pkgs/http2/codereview.settings new file mode 100644 index 0000000000..bd94e0a834 --- /dev/null +++ b/pkgs/http2/codereview.settings @@ -0,0 +1,3 @@ +CODE_REVIEW_SERVER: https://chromereviews.googleplex.com +VIEW_VC: https://github.com/dart-lang/http2/commit/ +#CC_LIST: reviews@dartlang.org diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml new file mode 100644 index 0000000000..ab178af971 --- /dev/null +++ b/pkgs/http2/pubspec.yaml @@ -0,0 +1,12 @@ +name: http2 +version: 0.0.1 +description: A HTTP/2 implementation in dart. +author: Dart Team +homepage: https://github.com/dart-lang/http2 + +environment: + sdk: '>=1.0.0 <2.0.0' + +dev_dependencies: + unittest: '>=0.11.5 <0.12.0' + From e35a6436d4c6abf012b2926395cf38ca0ea4d036 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Fri, 27 Mar 2015 11:30:32 +0100 Subject: [PATCH 003/172] HPack: Add huffman encoding/decoding support R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/169287015 --- pkgs/http2/lib/src/hpack/huffman.dart | 190 ++++++++++++ pkgs/http2/lib/src/hpack/huffman_table.dart | 278 ++++++++++++++++++ .../test/src/hpack/huffman_table_test.dart | 155 ++++++++++ 3 files changed, 623 insertions(+) create mode 100644 pkgs/http2/lib/src/hpack/huffman.dart create mode 100644 pkgs/http2/lib/src/hpack/huffman_table.dart create mode 100644 pkgs/http2/test/src/hpack/huffman_table_test.dart diff --git a/pkgs/http2/lib/src/hpack/huffman.dart b/pkgs/http2/lib/src/hpack/huffman.dart new file mode 100644 index 0000000000..d1a4f6b20c --- /dev/null +++ b/pkgs/http2/lib/src/hpack/huffman.dart @@ -0,0 +1,190 @@ +// 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. + +library http2.src.hpack.huffman; + +import 'dart:io'; + +import 'huffman_table.dart'; + +class HuffmanDecodingException implements Exception { + final String _message; + + HuffmanDecodingException(this._message); + + String toString() => 'HuffmanDecodingException: $_message'; +} + + +/// A codec used for encoding/decoding using a huffman codec. +class HuffmanCodec { + final HuffmanEncoder _encoder; + final HuffmanDecoder _decoder; + + HuffmanCodec(this._encoder, this._decoder); + + List decode(List bytes) => _decoder.decode(bytes); + + List encode(List bytes) => _encoder.encode(bytes); +} + + +/// A huffman decoder based on a [HuffmanTreeNode]. +class HuffmanDecoder { + final HuffmanTreeNode _root; + + HuffmanDecoder(this._root); + + /// Decodes [bytes] using a huffman tree. + List decode(List bytes) { + var buffer = new BytesBuilder(); + + int currentByteOffset = 0; + HuffmanTreeNode node = _root; + int currentDepth = 0; + while (currentByteOffset < bytes.length) { + var byte = bytes[currentByteOffset]; + for (int currentBit = 7; currentBit >= 0; currentBit--) { + bool right = (byte >> currentBit) & 1 == 1; + if (right) { + node = node.right; + } else { + node = node.left; + } + currentDepth++; + if (node.value != null) { + if (node.value == EOS_BYTE) { + throw new HuffmanDecodingException( + 'More than 7 bit padding is not allowed. Found entire EOS ' + 'encoding'); + } + buffer.addByte(node.value); + node = _root; + currentDepth = 0; + } + } + currentByteOffset++; + } + + if (node != _root) { + if (currentDepth > 7) { + throw new HuffmanDecodingException( + 'Incomplete encoding of a byte or more than 7 bit padding.'); + } + + while (node.right != null) node = node.right; + + if (node.value != 256) { + throw new HuffmanDecodingException( + 'Incomplete encoding of a byte.'); + } + } + + return buffer.takeBytes(); + } +} + + +/// A huffman encoder based on a list of codewords. +class HuffmanEncoder { + final List _codewords; + + HuffmanEncoder(this._codewords); + + /// Encodes [bytes] using a list of codewords. + List encode(List bytes) { + var buffer = new BytesBuilder(); + + int currentByte = 0; + int currentBitOffset = 7; + + writeValue(int value, int numBits) { + int i = numBits - 1; + while (i >= 0) { + if (currentBitOffset == 7 && i >= 7) { + assert(currentByte == 0); + + buffer.addByte((value >> (i - 7)) & 0xff); + currentBitOffset = 7; + currentByte = 0; + i -= 8; + } else { + currentByte |= ((value >> i) & 1) << currentBitOffset; + + currentBitOffset--; + if (currentBitOffset == -1) { + buffer.addByte(currentByte); + currentBitOffset = 7; + currentByte = 0; + } + i--; + } + } + } + for (int i = 0; i < bytes.length; i++) { + var byte = bytes[i]; + var value = _codewords[byte]; + writeValue(value.encodedBytes, value.numBits); + } + + if (currentBitOffset < 7) { + writeValue(0xff, 1 + currentBitOffset); + } + + return buffer.takeBytes(); + } +} + + +/// Specifies the encoding of a specific value using huffman encoding. +class EncodedHuffmanValue { + /// An integer representation of the encoded bit-string. + final int encodedBytes; + + /// The number of bits in [encodedBytes]. + final int numBits; + + const EncodedHuffmanValue(this.encodedBytes, this.numBits); +} + + +/// A node in the huffman tree. +class HuffmanTreeNode { + HuffmanTreeNode left; + HuffmanTreeNode right; + int value; +} + + +/// Generates a huffman decoding tree. +HuffmanTreeNode generateHuffmanTree(List valueEncodings) { + HuffmanTreeNode root = new HuffmanTreeNode(); + + for (int byteOffset = 0; byteOffset < valueEncodings.length; byteOffset++) { + var entry = valueEncodings[byteOffset]; + + HuffmanTreeNode current = root; + for (int bitNr = 0; bitNr < entry.numBits; bitNr++) { + bool right = + ((entry.encodedBytes >> (entry.numBits - bitNr - 1)) & 1) == 1; + + if (right) { + if (current.right == null) { + current.right = new HuffmanTreeNode(); + } + current = current.right; + } else { + if (current.left == null) { + current.left = new HuffmanTreeNode(); + } + current = current.left; + } + } + + current.value = byteOffset; + } + + return root; +} + diff --git a/pkgs/http2/lib/src/hpack/huffman_table.dart b/pkgs/http2/lib/src/hpack/huffman_table.dart new file mode 100644 index 0000000000..5a794d8283 --- /dev/null +++ b/pkgs/http2/lib/src/hpack/huffman_table.dart @@ -0,0 +1,278 @@ +// 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. + +library http2.hpack.huffman_table; + +import 'huffman.dart'; + +/// The huffman codec for encoding/decoding HTTP/2 header blocks. +final HuffmanCodec http2HuffmanCodec = + new HuffmanCodec(new HuffmanEncoder(_codeWords), + new HuffmanDecoder(generateHuffmanTree(_codeWords))); + +/// This is the integer representing the End-of-String symbol +/// (it is not representable by a byte). +const int EOS_BYTE = 256; + +/// This list of byte encodings via huffman encoding was generated from the +/// HPACK specification. +const List _codeWords = const [ + const EncodedHuffmanValue(0x1ff8, 13), + const EncodedHuffmanValue(0x7fffd8, 23), + const EncodedHuffmanValue(0xfffffe2, 28), + const EncodedHuffmanValue(0xfffffe3, 28), + const EncodedHuffmanValue(0xfffffe4, 28), + const EncodedHuffmanValue(0xfffffe5, 28), + const EncodedHuffmanValue(0xfffffe6, 28), + const EncodedHuffmanValue(0xfffffe7, 28), + const EncodedHuffmanValue(0xfffffe8, 28), + const EncodedHuffmanValue(0xffffea, 24), + const EncodedHuffmanValue(0x3ffffffc, 30), + const EncodedHuffmanValue(0xfffffe9, 28), + const EncodedHuffmanValue(0xfffffea, 28), + const EncodedHuffmanValue(0x3ffffffd, 30), + const EncodedHuffmanValue(0xfffffeb, 28), + const EncodedHuffmanValue(0xfffffec, 28), + const EncodedHuffmanValue(0xfffffed, 28), + const EncodedHuffmanValue(0xfffffee, 28), + const EncodedHuffmanValue(0xfffffef, 28), + const EncodedHuffmanValue(0xffffff0, 28), + const EncodedHuffmanValue(0xffffff1, 28), + const EncodedHuffmanValue(0xffffff2, 28), + const EncodedHuffmanValue(0x3ffffffe, 30), + const EncodedHuffmanValue(0xffffff3, 28), + const EncodedHuffmanValue(0xffffff4, 28), + const EncodedHuffmanValue(0xffffff5, 28), + const EncodedHuffmanValue(0xffffff6, 28), + const EncodedHuffmanValue(0xffffff7, 28), + const EncodedHuffmanValue(0xffffff8, 28), + const EncodedHuffmanValue(0xffffff9, 28), + const EncodedHuffmanValue(0xffffffa, 28), + const EncodedHuffmanValue(0xffffffb, 28), + const EncodedHuffmanValue(0x14, 6), + const EncodedHuffmanValue(0x3f8, 10), + const EncodedHuffmanValue(0x3f9, 10), + const EncodedHuffmanValue(0xffa, 12), + const EncodedHuffmanValue(0x1ff9, 13), + const EncodedHuffmanValue(0x15, 6), + const EncodedHuffmanValue(0xf8, 8), + const EncodedHuffmanValue(0x7fa, 11), + const EncodedHuffmanValue(0x3fa, 10), + const EncodedHuffmanValue(0x3fb, 10), + const EncodedHuffmanValue(0xf9, 8), + const EncodedHuffmanValue(0x7fb, 11), + const EncodedHuffmanValue(0xfa, 8), + const EncodedHuffmanValue(0x16, 6), + const EncodedHuffmanValue(0x17, 6), + const EncodedHuffmanValue(0x18, 6), + const EncodedHuffmanValue(0x0, 5), + const EncodedHuffmanValue(0x1, 5), + const EncodedHuffmanValue(0x2, 5), + const EncodedHuffmanValue(0x19, 6), + const EncodedHuffmanValue(0x1a, 6), + const EncodedHuffmanValue(0x1b, 6), + const EncodedHuffmanValue(0x1c, 6), + const EncodedHuffmanValue(0x1d, 6), + const EncodedHuffmanValue(0x1e, 6), + const EncodedHuffmanValue(0x1f, 6), + const EncodedHuffmanValue(0x5c, 7), + const EncodedHuffmanValue(0xfb, 8), + const EncodedHuffmanValue(0x7ffc, 15), + const EncodedHuffmanValue(0x20, 6), + const EncodedHuffmanValue(0xffb, 12), + const EncodedHuffmanValue(0x3fc, 10), + const EncodedHuffmanValue(0x1ffa, 13), + const EncodedHuffmanValue(0x21, 6), + const EncodedHuffmanValue(0x5d, 7), + const EncodedHuffmanValue(0x5e, 7), + const EncodedHuffmanValue(0x5f, 7), + const EncodedHuffmanValue(0x60, 7), + const EncodedHuffmanValue(0x61, 7), + const EncodedHuffmanValue(0x62, 7), + const EncodedHuffmanValue(0x63, 7), + const EncodedHuffmanValue(0x64, 7), + const EncodedHuffmanValue(0x65, 7), + const EncodedHuffmanValue(0x66, 7), + const EncodedHuffmanValue(0x67, 7), + const EncodedHuffmanValue(0x68, 7), + const EncodedHuffmanValue(0x69, 7), + const EncodedHuffmanValue(0x6a, 7), + const EncodedHuffmanValue(0x6b, 7), + const EncodedHuffmanValue(0x6c, 7), + const EncodedHuffmanValue(0x6d, 7), + const EncodedHuffmanValue(0x6e, 7), + const EncodedHuffmanValue(0x6f, 7), + const EncodedHuffmanValue(0x70, 7), + const EncodedHuffmanValue(0x71, 7), + const EncodedHuffmanValue(0x72, 7), + const EncodedHuffmanValue(0xfc, 8), + const EncodedHuffmanValue(0x73, 7), + const EncodedHuffmanValue(0xfd, 8), + const EncodedHuffmanValue(0x1ffb, 13), + const EncodedHuffmanValue(0x7fff0, 19), + const EncodedHuffmanValue(0x1ffc, 13), + const EncodedHuffmanValue(0x3ffc, 14), + const EncodedHuffmanValue(0x22, 6), + const EncodedHuffmanValue(0x7ffd, 15), + const EncodedHuffmanValue(0x3, 5), + const EncodedHuffmanValue(0x23, 6), + const EncodedHuffmanValue(0x4, 5), + const EncodedHuffmanValue(0x24, 6), + const EncodedHuffmanValue(0x5, 5), + const EncodedHuffmanValue(0x25, 6), + const EncodedHuffmanValue(0x26, 6), + const EncodedHuffmanValue(0x27, 6), + const EncodedHuffmanValue(0x6, 5), + const EncodedHuffmanValue(0x74, 7), + const EncodedHuffmanValue(0x75, 7), + const EncodedHuffmanValue(0x28, 6), + const EncodedHuffmanValue(0x29, 6), + const EncodedHuffmanValue(0x2a, 6), + const EncodedHuffmanValue(0x7, 5), + const EncodedHuffmanValue(0x2b, 6), + const EncodedHuffmanValue(0x76, 7), + const EncodedHuffmanValue(0x2c, 6), + const EncodedHuffmanValue(0x8, 5), + const EncodedHuffmanValue(0x9, 5), + const EncodedHuffmanValue(0x2d, 6), + const EncodedHuffmanValue(0x77, 7), + const EncodedHuffmanValue(0x78, 7), + const EncodedHuffmanValue(0x79, 7), + const EncodedHuffmanValue(0x7a, 7), + const EncodedHuffmanValue(0x7b, 7), + const EncodedHuffmanValue(0x7ffe, 15), + const EncodedHuffmanValue(0x7fc, 11), + const EncodedHuffmanValue(0x3ffd, 14), + const EncodedHuffmanValue(0x1ffd, 13), + const EncodedHuffmanValue(0xffffffc, 28), + const EncodedHuffmanValue(0xfffe6, 20), + const EncodedHuffmanValue(0x3fffd2, 22), + const EncodedHuffmanValue(0xfffe7, 20), + const EncodedHuffmanValue(0xfffe8, 20), + const EncodedHuffmanValue(0x3fffd3, 22), + const EncodedHuffmanValue(0x3fffd4, 22), + const EncodedHuffmanValue(0x3fffd5, 22), + const EncodedHuffmanValue(0x7fffd9, 23), + const EncodedHuffmanValue(0x3fffd6, 22), + const EncodedHuffmanValue(0x7fffda, 23), + const EncodedHuffmanValue(0x7fffdb, 23), + const EncodedHuffmanValue(0x7fffdc, 23), + const EncodedHuffmanValue(0x7fffdd, 23), + const EncodedHuffmanValue(0x7fffde, 23), + const EncodedHuffmanValue(0xffffeb, 24), + const EncodedHuffmanValue(0x7fffdf, 23), + const EncodedHuffmanValue(0xffffec, 24), + const EncodedHuffmanValue(0xffffed, 24), + const EncodedHuffmanValue(0x3fffd7, 22), + const EncodedHuffmanValue(0x7fffe0, 23), + const EncodedHuffmanValue(0xffffee, 24), + const EncodedHuffmanValue(0x7fffe1, 23), + const EncodedHuffmanValue(0x7fffe2, 23), + const EncodedHuffmanValue(0x7fffe3, 23), + const EncodedHuffmanValue(0x7fffe4, 23), + const EncodedHuffmanValue(0x1fffdc, 21), + const EncodedHuffmanValue(0x3fffd8, 22), + const EncodedHuffmanValue(0x7fffe5, 23), + const EncodedHuffmanValue(0x3fffd9, 22), + const EncodedHuffmanValue(0x7fffe6, 23), + const EncodedHuffmanValue(0x7fffe7, 23), + const EncodedHuffmanValue(0xffffef, 24), + const EncodedHuffmanValue(0x3fffda, 22), + const EncodedHuffmanValue(0x1fffdd, 21), + const EncodedHuffmanValue(0xfffe9, 20), + const EncodedHuffmanValue(0x3fffdb, 22), + const EncodedHuffmanValue(0x3fffdc, 22), + const EncodedHuffmanValue(0x7fffe8, 23), + const EncodedHuffmanValue(0x7fffe9, 23), + const EncodedHuffmanValue(0x1fffde, 21), + const EncodedHuffmanValue(0x7fffea, 23), + const EncodedHuffmanValue(0x3fffdd, 22), + const EncodedHuffmanValue(0x3fffde, 22), + const EncodedHuffmanValue(0xfffff0, 24), + const EncodedHuffmanValue(0x1fffdf, 21), + const EncodedHuffmanValue(0x3fffdf, 22), + const EncodedHuffmanValue(0x7fffeb, 23), + const EncodedHuffmanValue(0x7fffec, 23), + const EncodedHuffmanValue(0x1fffe0, 21), + const EncodedHuffmanValue(0x1fffe1, 21), + const EncodedHuffmanValue(0x3fffe0, 22), + const EncodedHuffmanValue(0x1fffe2, 21), + const EncodedHuffmanValue(0x7fffed, 23), + const EncodedHuffmanValue(0x3fffe1, 22), + const EncodedHuffmanValue(0x7fffee, 23), + const EncodedHuffmanValue(0x7fffef, 23), + const EncodedHuffmanValue(0xfffea, 20), + const EncodedHuffmanValue(0x3fffe2, 22), + const EncodedHuffmanValue(0x3fffe3, 22), + const EncodedHuffmanValue(0x3fffe4, 22), + const EncodedHuffmanValue(0x7ffff0, 23), + const EncodedHuffmanValue(0x3fffe5, 22), + const EncodedHuffmanValue(0x3fffe6, 22), + const EncodedHuffmanValue(0x7ffff1, 23), + const EncodedHuffmanValue(0x3ffffe0, 26), + const EncodedHuffmanValue(0x3ffffe1, 26), + const EncodedHuffmanValue(0xfffeb, 20), + const EncodedHuffmanValue(0x7fff1, 19), + const EncodedHuffmanValue(0x3fffe7, 22), + const EncodedHuffmanValue(0x7ffff2, 23), + const EncodedHuffmanValue(0x3fffe8, 22), + const EncodedHuffmanValue(0x1ffffec, 25), + const EncodedHuffmanValue(0x3ffffe2, 26), + const EncodedHuffmanValue(0x3ffffe3, 26), + const EncodedHuffmanValue(0x3ffffe4, 26), + const EncodedHuffmanValue(0x7ffffde, 27), + const EncodedHuffmanValue(0x7ffffdf, 27), + const EncodedHuffmanValue(0x3ffffe5, 26), + const EncodedHuffmanValue(0xfffff1, 24), + const EncodedHuffmanValue(0x1ffffed, 25), + const EncodedHuffmanValue(0x7fff2, 19), + const EncodedHuffmanValue(0x1fffe3, 21), + const EncodedHuffmanValue(0x3ffffe6, 26), + const EncodedHuffmanValue(0x7ffffe0, 27), + const EncodedHuffmanValue(0x7ffffe1, 27), + const EncodedHuffmanValue(0x3ffffe7, 26), + const EncodedHuffmanValue(0x7ffffe2, 27), + const EncodedHuffmanValue(0xfffff2, 24), + const EncodedHuffmanValue(0x1fffe4, 21), + const EncodedHuffmanValue(0x1fffe5, 21), + const EncodedHuffmanValue(0x3ffffe8, 26), + const EncodedHuffmanValue(0x3ffffe9, 26), + const EncodedHuffmanValue(0xffffffd, 28), + const EncodedHuffmanValue(0x7ffffe3, 27), + const EncodedHuffmanValue(0x7ffffe4, 27), + const EncodedHuffmanValue(0x7ffffe5, 27), + const EncodedHuffmanValue(0xfffec, 20), + const EncodedHuffmanValue(0xfffff3, 24), + const EncodedHuffmanValue(0xfffed, 20), + const EncodedHuffmanValue(0x1fffe6, 21), + const EncodedHuffmanValue(0x3fffe9, 22), + const EncodedHuffmanValue(0x1fffe7, 21), + const EncodedHuffmanValue(0x1fffe8, 21), + const EncodedHuffmanValue(0x7ffff3, 23), + const EncodedHuffmanValue(0x3fffea, 22), + const EncodedHuffmanValue(0x3fffeb, 22), + const EncodedHuffmanValue(0x1ffffee, 25), + const EncodedHuffmanValue(0x1ffffef, 25), + const EncodedHuffmanValue(0xfffff4, 24), + const EncodedHuffmanValue(0xfffff5, 24), + const EncodedHuffmanValue(0x3ffffea, 26), + const EncodedHuffmanValue(0x7ffff4, 23), + const EncodedHuffmanValue(0x3ffffeb, 26), + const EncodedHuffmanValue(0x7ffffe6, 27), + const EncodedHuffmanValue(0x3ffffec, 26), + const EncodedHuffmanValue(0x3ffffed, 26), + const EncodedHuffmanValue(0x7ffffe7, 27), + const EncodedHuffmanValue(0x7ffffe8, 27), + const EncodedHuffmanValue(0x7ffffe9, 27), + const EncodedHuffmanValue(0x7ffffea, 27), + const EncodedHuffmanValue(0x7ffffeb, 27), + const EncodedHuffmanValue(0xffffffe, 28), + const EncodedHuffmanValue(0x7ffffec, 27), + const EncodedHuffmanValue(0x7ffffed, 27), + const EncodedHuffmanValue(0x7ffffee, 27), + const EncodedHuffmanValue(0x7ffffef, 27), + const EncodedHuffmanValue(0x7fffff0, 27), + const EncodedHuffmanValue(0x3ffffee, 26), + const EncodedHuffmanValue(0x3fffffff, 30), +]; diff --git a/pkgs/http2/test/src/hpack/huffman_table_test.dart b/pkgs/http2/test/src/hpack/huffman_table_test.dart new file mode 100644 index 0000000000..db31db7b26 --- /dev/null +++ b/pkgs/http2/test/src/hpack/huffman_table_test.dart @@ -0,0 +1,155 @@ +// 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:convert'; + +import 'package:unittest/unittest.dart'; +import 'package:http2/src/hpack/huffman.dart'; +import 'package:http2/src/hpack/huffman_table.dart'; + +main() { + group('hpack', () { + group('huffman', () { + final decode = http2HuffmanCodec.decode; + final encode = http2HuffmanCodec.encode; + + Map> hpackSpecTestCases = { + 'www.example.com' : [0xf1, 0xe3, 0xc2, 0xe5, 0xf2, 0x3a, 0x6b, 0xa0, + 0xab, 0x90, 0xf4, 0xff], + 'no-cache' : [0xa8, 0xeb, 0x10, 0x64, 0x9c, 0xbf], + 'custom-key' : [0x25, 0xa8, 0x49, 0xe9, 0x5b, 0xa9, 0x7d, 0x7f], + 'custom-value' : [0x25, 0xa8, 0x49, 0xe9, 0x5b, 0xb8, 0xe8, 0xb4, + 0xbf], + }; + + test('hpack-spec-testcases', () { + hpackSpecTestCases.forEach((String value, List encoding) { + expect(decode(encoding), ASCII.encode(value)); + expect(encode(ASCII.encode(value)), encoding); + }); + }); + + test('more-than-7-bit-padding', () { + var data = [ + // Just more-than-7-bitpadding + [0xff], + [0xff, 0xff], + [0xff, 0xff, 0xff], + [0xff, 0xff, 0xff, 0xff], + + // 0xf8 = '&' + more-than-7-bitpadding + [0xf8, 0xff], + [0xf8, 0xff, 0xff], + [0xf8, 0xff, 0xff, 0xff], + [0xf8, 0xff, 0xff, 0xff, 0xff], + + // ')' + entire EOS + [0xfe, 0xff, 0xff, 0xff, 0xff], + ]; + + for (var test in data) { + expect(() => decode(test), throwsHuffmanDecodingException); + } + }); + + test('incomplete-encoding', () { + var data = [ + // Incomplete encoding + [0xfe], + + // 0xf8 = '&' + Incomplete encoding + [0xf8, 0xfe], + ]; + + for (var test in data) { + expect(() => decode(test), throwsHuffmanDecodingException); + } + }); + + test('fuzzy-test', () { + var data = [ + [0xb8, 0xa4, 0x4e, 0xe3, 0xb1, 0x4d, 0x3d, 0x63, 0x16, 0x5b, 0x6a], + [0x71, 0x5f, 0xb3, 0xb1, 0x4b, 0x94, 0xe8, 0x2f, 0x4c, 0x3d, 0x04], + [0x95, 0x6d, 0x89, 0xfb, 0x91, 0x6a, 0x6c, 0x52, 0x64, 0x9a, 0xd1], + [0x64, 0x59, 0x79, 0x38, 0xd2, 0x09, 0xea, 0x94, 0x92, 0xda, 0x24], + [0xb0, 0x35, 0xfe, 0xa9, 0x96, 0xb5, 0xe1, 0xde, 0x0a, 0x82, 0x18], + [0x39, 0xe5, 0xdd, 0xba, 0x50, 0xd4, 0x33, 0xa7, 0xb9, 0x63, 0x21], + [0x26, 0x52, 0x7a, 0xaa, 0x52, 0x4d, 0x27, 0x81, 0xe4, 0xef, 0xcd], + [0x17, 0x9e, 0x09, 0xcc, 0xd0, 0x0f, 0x5e, 0x03, 0x45, 0xc9, 0xba], + [0x84, 0xfc, 0x75, 0xeb, 0xcc, 0x9e, 0xb6, 0x50, 0x3f, 0xf8, 0x00], + [0xb9, 0x24, 0x95, 0x13, 0x6d, 0x89, 0xb2, 0x89, 0x86, 0x02, 0xca], + [0xb7, 0xd5, 0x78, 0xfa, 0xa3, 0xa9, 0x90, 0x1b, 0x35, 0xb4, 0x72], + [0x62, 0x9a, 0x31, 0x0c, 0x32, 0x1c, 0x25, 0x2e, 0x1b, 0x56, 0x55], + [0xa9, 0x5d, 0xa8, 0xa4, 0xed, 0x91, 0xeb, 0xba, 0xa0, 0xf9, 0x82], + [0x59, 0x9c, 0xc3, 0x6f, 0x66, 0xec, 0x65, 0xe0, 0x95, 0x6e, 0x34], + [0x3d, 0xc7, 0x0d, 0x6c, 0x01, 0x7d, 0xf2, 0x03, 0x9b, 0xe3, 0xc1], + [0x1d, 0xc6, 0xa4, 0xd1, 0x59, 0x52, 0xce, 0x42, 0x3d, 0xf6, 0xe5], + [0x2d, 0xbd, 0xb6, 0x5c, 0xfb, 0x52, 0x65, 0x2e, 0x7f, 0x03, 0x61], + [0x22, 0x24, 0x50, 0x48, 0x65, 0x5a, 0xe0, 0x0d, 0xf9, 0x78, 0x8d], + [0x72, 0xeb, 0x1d, 0x31, 0xb7, 0xe3, 0xa8, 0x15, 0x1f, 0xf1, 0x43], + [0x45, 0xa4, 0x40, 0x5a, 0x9c, 0x98, 0xa8, 0x6e, 0xac, 0xba, 0x83], + [0x27, 0x55, 0x33, 0xa7, 0x79, 0x08, 0x29, 0x42, 0x6d, 0x89, 0xfc], + [0x3b, 0x65, 0x21, 0x7a, 0x24, 0x58, 0x58, 0x6a, 0x97, 0x6e, 0x7c], + [0x56, 0x41, 0xff, 0x08, 0xaf, 0x9d, 0x33, 0x12, 0xcd, 0xb5, 0x99], + [0x35, 0x48, 0x38, 0x46, 0x3f, 0xee, 0x15, 0x16, 0x8d, 0xf5, 0x16], + [0xcc, 0xc0, 0x1b, 0x1e, 0xf1, 0xae, 0xf7, 0x40, 0xca, 0xc7, 0x9d], + [0x93, 0xae, 0x93, 0xcf, 0x97, 0xdf, 0xba, 0xd6, 0xb2, 0xac, 0x2f], + [0x45, 0xe4, 0x5b, 0x73, 0x54, 0x4c, 0x6c, 0x95, 0xa9, 0xab, 0x7f], + [0x71, 0xac, 0xbf, 0xdf, 0xa4, 0x29, 0xe3, 0x17, 0x3f, 0x24, 0x2f], + [0x5e, 0xc0, 0xf2, 0xbf, 0x5d, 0xc0, 0x31, 0x2d, 0x97, 0x24, 0x1d], + [0x6d, 0x0b, 0x7c, 0x15, 0x68, 0x7c, 0xe1, 0x15, 0xbf, 0x4f, 0x85], + [0x0a, 0x59, 0xf2, 0x3e, 0x48, 0x1d, 0xac, 0xc8, 0x22, 0xb0, 0x37], + [0x3a, 0xe2, 0x9e, 0xec, 0xf9, 0x1e, 0x88, 0xfa, 0xbe, 0x00, 0xee], + [0xc7, 0x5a, 0x1f, 0xc8, 0x48, 0x23, 0x3b, 0x1a, 0x0f, 0xf3, 0x7c], + [0x43, 0x0d, 0x10, 0x03, 0xb2, 0xc6, 0xbd, 0xed, 0x03, 0x19, 0x49], + [0xc9, 0xc4, 0x0e, 0xf3, 0xc6, 0xf4, 0xc1, 0x71, 0xee, 0x96, 0xeb], + [0x18, 0x51, 0x07, 0x36, 0x1a, 0x13, 0x83, 0x69, 0x2b, 0x1b, 0x09], + [0xac, 0x23, 0xb7, 0x47, 0x2d, 0xeb, 0x39, 0xdc, 0x3e, 0xdb, 0x74], + [0x44, 0x60, 0x06, 0x28, 0x5e, 0x8f, 0xef, 0xfc, 0x70, 0x7b, 0x73], + [0xda, 0x38, 0x25, 0x76, 0xa9, 0x1a, 0x99, 0x9a, 0x52, 0xdf, 0x8c], + [0xd4, 0xc4, 0x99, 0x2b, 0x54, 0x88, 0xc9, 0x34, 0x80, 0x43, 0x15], + [0x11, 0xa1, 0xed, 0xe3, 0xb4, 0x88, 0xd5, 0x1d, 0x4a, 0x1b, 0x9f], + [0xfd, 0x2c, 0xb4, 0x6e, 0x65, 0xfb, 0x27, 0x9b, 0x65, 0x55, 0x19], + [0xb6, 0xa4, 0x67, 0x16, 0x8a, 0x59, 0xf5, 0xfc, 0x0f, 0x7e, 0x24], + [0x40, 0x8e, 0x5d, 0x84, 0x90, 0x76, 0x50, 0xdb, 0x72, 0x2a, 0x3b], + [0x7d, 0x1e, 0x9d, 0x2f, 0xad, 0xce, 0x60, 0x00, 0xf8, 0xbc, 0xfa], + [0xc1, 0x2d, 0x32, 0xbd, 0xa2, 0xe7, 0xed, 0x17, 0x48, 0xca, 0xb0], + [0xe6, 0x91, 0x6c, 0xa7, 0xdc, 0x83, 0x58, 0x19, 0x05, 0xb1, 0xa6], + [0xec, 0xb2, 0x16, 0xa3, 0x89, 0x7a, 0xcd, 0x44, 0xe9, 0x3a, 0x98], + [0xcf, 0xef, 0x78, 0x5b, 0x7a, 0xec, 0xa8, 0xfa, 0x6c, 0x78, 0x23], + [0x8b, 0x53, 0x89, 0x82, 0x21, 0x3e, 0xfc, 0xed, 0xe4, 0x6b, 0xa0], + [0xff, 0x28, 0x10, 0xb2, 0x24, 0xf9, 0xb5, 0x3e, 0x08, 0xb2, 0x50], + [0x5e, 0x57, 0x11, 0xff, 0x06, 0x1b, 0xc7, 0x0b, 0x28, 0x5b, 0x34], + [0x00, 0x4a, 0xcc, 0x4e, 0x8e, 0x07, 0xea, 0x93, 0x10, 0x1c, 0x87], + [0xab, 0xc7, 0x7e, 0x10, 0x64, 0x7f, 0xa4, 0x6c, 0xca, 0x93, 0x73], + [0xcf, 0x57, 0xc5, 0x15, 0xbc, 0x47, 0xed, 0x5b, 0x1e, 0xb5, 0x9b], + [0x8e, 0xa5, 0xf3, 0x07, 0xa0, 0x68, 0x1e, 0x9e, 0xea, 0x57, 0x3f], + [0xfe, 0xa7, 0x7f, 0x91, 0xc7, 0xa4, 0x15, 0x7c, 0xa2, 0x00, 0x4c], + [0xb9, 0x62, 0x28, 0xa5, 0x9b, 0x04, 0x98, 0xf9, 0xdd, 0x37, 0x42], + [0xfa, 0x40, 0x1c, 0xce, 0xa0, 0x75, 0x9d, 0xaf, 0xd2, 0x09, 0xae], + [0xa7, 0x8e, 0xdb, 0x1e, 0x8b, 0x94, 0x24, 0x47, 0xd8, 0x04, 0xd7], + [0x69, 0x95, 0x8a, 0x29, 0xbe, 0x9f, 0xfb, 0x71, 0x91, 0x9a, 0x40], + [0x82, 0xed, 0x1e, 0xf5, 0xac, 0x34, 0x17, 0xfe, 0x5f, 0xfd, 0xd3], + [0x81, 0xe6, 0xaa, 0x7b, 0x12, 0xf0, 0xb2, 0xb9, 0x47, 0x02, 0x3c], + [0x05, 0xc3, 0x6d, 0xd5, 0xf1, 0xa4, 0x93, 0xe2, 0x8b, 0x7c, 0xed], + ]; + for (var test in data) { + expect(decode(encode(test)), equals(test)); + } + }); + }); + }); +} + + +/// A matcher for HuffmanDecodingExceptions. +const Matcher isHuffmanDecodingException = const _HuffmanDecodingException(); + +class _HuffmanDecodingException extends TypeMatcher { + const _HuffmanDecodingException() : super("HuffmanDecodingException"); + bool matches(item, Map matchState) => item is HuffmanDecodingException; +} + +const Matcher throwsHuffmanDecodingException = + const Throws(isHuffmanDecodingException); From 3a14442115133778945eff91dbd7cf735d211364 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Tue, 7 Apr 2015 11:57:46 +0200 Subject: [PATCH 004/172] HPack: Add static/dynamic index tables & usage of huffman codec R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/173047013 --- pkgs/http2/lib/src/hpack/hpack.dart | 352 +++++++++++++++++ pkgs/http2/test/src/hpack/hpack_test.dart | 456 ++++++++++++++++++++++ 2 files changed, 808 insertions(+) create mode 100644 pkgs/http2/lib/src/hpack/hpack.dart create mode 100644 pkgs/http2/test/src/hpack/hpack_test.dart diff --git a/pkgs/http2/lib/src/hpack/hpack.dart b/pkgs/http2/lib/src/hpack/hpack.dart new file mode 100644 index 0000000000..26b32fbc1d --- /dev/null +++ b/pkgs/http2/lib/src/hpack/hpack.dart @@ -0,0 +1,352 @@ +// 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. + +/// Implements a [HPackContext] for encoding/decoding headers according to the +/// HPACK specificaiton. See here for more information: +/// https://tools.ietf.org/html/draft-ietf-httpbis-header-compression-10 +library http2.hpack; + +import 'dart:convert'; +import 'dart:io'; + +import 'huffman.dart'; +import 'huffman_table.dart'; + +/// Exception raised due to encoding/decoding errors. +class HPackDecodingException implements Exception { + final String _message; + + HPackDecodingException(this._message); + + String toString() => 'HPackDecodingException: $_message'; +} + + +/// A HPACK encoding/decoding context. +/// +/// This is a statefull class, so encoding/decoding changes internal state. +class HPackContext { + final HPackEncoder encoder = new HPackEncoder(); + final HPackDecoder decoder = new HPackDecoder(); + + HPackContext() { + // TODO: Inject these constants from Settings + // e.g. SETTINGS_HEADER_TABLE_SIZE + encoder.updateMaxSendingHeaderTableSize(4096); + decoder.updateMaxReceivingHeaderTableSize(4096); + } +} + + +/// A HTTP/2 header. +class Header { + final List name; + final List value; + final bool neverIndexed; + + Header(this.name, this.value, {this.neverIndexed: false}); + + factory Header.ascii(String name, String value) { + return new Header(ASCII.encode(name), ASCII.encode(value)); + } +} + + +/// A stateful HPACK decoder. +class HPackDecoder { + int _maxHeaderTableSize; + + final IndexTable _table = new IndexTable(); + + void updateMaxReceivingHeaderTableSize(int newMaximumSize) { + _maxHeaderTableSize = newMaximumSize; + } + + List
decode(List data) { + int offset = 0; + + int readInteger(int prefixBits) { + assert (prefixBits <= 8 && prefixBits > 0); + + var byte = data[offset++] & ((1 << prefixBits) - 1); + + int integer; + if (byte == ((1 << prefixBits) - 1)) { + // Length encodeded. + integer = 0; + int shift = 0; + while (true) { + bool done = (data[offset] & 0x80) != 0x80; + integer += (data[offset++] & 0x7f) << shift; + shift += 7; + if (done) break; + } + integer += (1 << prefixBits) - 1; + } else { + // In place length. + integer = byte; + } + + return integer; + } + + List readStringLiteral() { + bool isHuffmanEncoding = (data[offset] & 0x80) != 0; + int length = readInteger(7); + + // TODO: Use view's for Uint8list + var sublist = data.sublist(offset, offset + length); + offset += length; + if (isHuffmanEncoding) { + return http2HuffmanCodec.decode(sublist); + } else { + return sublist; + } + } + + Header readHeaderFieldInternal(int index, {bool neverIndexed: false}) { + var name, value; + if (index > 0) { + name = _table.lookup(index).name; + value = readStringLiteral(); + } else { + name = readStringLiteral(); + value = readStringLiteral(); + } + return new Header(name, value, neverIndexed: neverIndexed); + } + + try { + List
headers = []; + while (offset < data.length) { + int byte = data[offset]; + bool isIndexedField = (byte & 0x80) != 0; + bool isIncrementalIndexing = (byte & 0xc0) == 0x40; + + bool isWithoutIndexing = (byte & 0xf0) == 0; + bool isNeverIndexing = (byte & 0xf0) == 0x10; + bool isDynamicTableSizeUpdate = (byte & 0xe0) == 0x20; + + if (isIndexedField) { + int index = readInteger(7); + var field = _table.lookup(index); + headers.add(field); + } else if (isIncrementalIndexing) { + var field = readHeaderFieldInternal(readInteger(6)); + _table.addHeaderField(field); + headers.add(field); + } else if (isWithoutIndexing) { + headers.add(readHeaderFieldInternal(readInteger(4))); + } else if (isNeverIndexing) { + headers.add( + readHeaderFieldInternal(readInteger(4), neverIndexed: true)); + } else if (isDynamicTableSizeUpdate) { + int newMaxSize = readInteger(5); + if (newMaxSize <= _maxHeaderTableSize) { + _table.updateMaxSize(newMaxSize); + } else { + throw new HPackDecodingException( + 'Dynamic table size update failed: ' + 'A new value of $newMaxSize exceeds the limit of ' + '$_maxHeaderTableSize'); + } + } else { + throw new HPackDecodingException('Invalid encoding of headers.'); + } + } + return headers; + } on RangeError catch (e, s) { + throw new HPackDecodingException('$e'); + } on HuffmanDecodingException catch (e, s) { + throw new HPackDecodingException('$e'); + } + } +} + + +/// A stateful HPACK encoder. +// TODO: Currently we encode all headers: +// - without huffman encoding +// - without using the dynamic table +class HPackEncoder { + int _maxHeaderTableSize = 4096; + + final IndexTable _table = new IndexTable(); + + void updateMaxSendingHeaderTableSize(int newMaximumSize) { + _maxHeaderTableSize = newMaximumSize; + // TODO: Don't we need to write the updated size now in the stream? + } + + List encode(List
headers) { + var bytesBuilder = new BytesBuilder(); + int currentByte = 0; + + void writeInteger(int prefixBits, int value) { + assert (prefixBits <= 8); + + if (value < (1 << prefixBits) - 1) { + currentByte |= value; + bytesBuilder.addByte(currentByte); + } else { + // Length encodeded. + currentByte |= (1 << prefixBits) - 1; + value -= (1 << prefixBits) - 1; + bytesBuilder.addByte(currentByte); + bool done = false; + while (!done) { + currentByte = value & 0x7f; + value = value >> 7; + done = value == 0; + if (!done) currentByte |= 0x80; + bytesBuilder.addByte(currentByte); + } + } + currentByte = 0; + } + + void writeStringLiteral(List bytes) { + // TODO: Support huffman encoding. + currentByte = 0; // 1 would be huffman encoding + writeInteger(7, bytes.length); + bytesBuilder.add(bytes); + } + + void writeLiteralHeaderWithoutIndexing(Header header) { + bytesBuilder.addByte(0); + writeStringLiteral(header.name); + writeStringLiteral(header.value); + } + + for (var header in headers) { + writeLiteralHeaderWithoutIndexing(header); + } + + return bytesBuilder.takeBytes(); + } +} + +class IndexTable { + static final List
_staticTable = [ + null, + new Header(ASCII.encode(':authority'), const []), + new Header(ASCII.encode(':method'), ASCII.encode('GET')), + new Header(ASCII.encode(':method'), ASCII.encode('POST')), + new Header(ASCII.encode(':path'), ASCII.encode('/')), + new Header(ASCII.encode(':path'), ASCII.encode('/index.html')), + new Header(ASCII.encode(':scheme'), ASCII.encode('http')), + new Header(ASCII.encode(':scheme'), ASCII.encode('https')), + new Header(ASCII.encode(':status'), ASCII.encode('200')), + new Header(ASCII.encode(':status'), ASCII.encode('204')), + new Header(ASCII.encode(':status'), ASCII.encode('206')), + new Header(ASCII.encode(':status'), ASCII.encode('304')), + new Header(ASCII.encode(':status'), ASCII.encode('400')), + new Header(ASCII.encode(':status'), ASCII.encode('404')), + new Header(ASCII.encode(':status'), ASCII.encode('500')), + new Header(ASCII.encode('accept-charset'), const []), + new Header(ASCII.encode('accept-encoding'), + ASCII.encode('gzip, deflate')), + new Header(ASCII.encode('accept-language'), const []), + new Header(ASCII.encode('accept-ranges'), const []), + new Header(ASCII.encode('accept'), const []), + new Header(ASCII.encode('access-control-allow-origin'), const []), + new Header(ASCII.encode('age'), const []), + new Header(ASCII.encode('allow'), const []), + new Header(ASCII.encode('authorization'), const []), + new Header(ASCII.encode('cache-control'), const []), + new Header(ASCII.encode('content-disposition'), const []), + new Header(ASCII.encode('content-encoding'), const []), + new Header(ASCII.encode('content-language'), const []), + new Header(ASCII.encode('content-length'), const []), + new Header(ASCII.encode('content-location'), const []), + new Header(ASCII.encode('content-range'), const []), + new Header(ASCII.encode('content-type'), const []), + new Header(ASCII.encode('cookie'), const []), + new Header(ASCII.encode('date'), const []), + new Header(ASCII.encode('etag'), const []), + new Header(ASCII.encode('expect'), const []), + new Header(ASCII.encode('expires'), const []), + new Header(ASCII.encode('from'), const []), + new Header(ASCII.encode('host'), const []), + new Header(ASCII.encode('if-match'), const []), + new Header(ASCII.encode('if-modified-since'), const []), + new Header(ASCII.encode('if-none-match'), const []), + new Header(ASCII.encode('if-range'), const []), + new Header(ASCII.encode('if-unmodified-since'), const []), + new Header(ASCII.encode('last-modified'), const []), + new Header(ASCII.encode('link'), const []), + new Header(ASCII.encode('location'), const []), + new Header(ASCII.encode('max-forwards'), const []), + new Header(ASCII.encode('proxy-authenticate'), const []), + new Header(ASCII.encode('proxy-authorization'), const []), + new Header(ASCII.encode('range'), const []), + new Header(ASCII.encode('referer'), const []), + new Header(ASCII.encode('refresh'), const []), + new Header(ASCII.encode('retry-after'), const []), + new Header(ASCII.encode('server'), const []), + new Header(ASCII.encode('set-cookie'), const []), + new Header(ASCII.encode('strict-transport-security'), const []), + new Header(ASCII.encode('transfer-encoding'), const []), + new Header(ASCII.encode('user-agent'), const []), + new Header(ASCII.encode('vary'), const []), + new Header(ASCII.encode('via'), const []), + new Header(ASCII.encode('www-authenticate'), const []), + ]; + + final List
_dynamicTable = []; + + /// The maximum size the dynamic table can grow to before entries need to be + /// evicted. + int _maximumSize = 4096; + + /// The current size of the dynamic table. + int _currentSize = 0; + + IndexTable(); + + /// Updates the maximum size which the dynamic table can grow to. + void updateMaxSize(int newMaxDynTableSize) { + _maximumSize = newMaxDynTableSize; + _reduce(); + } + + /// Lookup an item by index. + Header lookup(int index) { + if (index <= 0) { + throw new HPackDecodingException( + 'Invalid index (was: $index) for table lookup.'); + } + if (index < _staticTable.length) { + return _staticTable[index]; + } + index -= _staticTable.length; + if (index < _dynamicTable.length) { + return _dynamicTable[index]; + } + throw new HPackDecodingException( + 'Invalid index (was: $index) for table lookup.'); + } + + /// Adds a new header field to the dynamic table - and evicts entries as + /// necessary. + void addHeaderField(Header header) { + _dynamicTable.insert(0, header); + _currentSize += _sizeOf(header); + _reduce(); + } + + /// Removes as many entries as required to be within the limit of + /// [_maximumSize]. + void _reduce() { + while (_currentSize > _maximumSize) { + Header h = _dynamicTable.removeLast(); + _currentSize -= _sizeOf(h); + } + } + + /// Returns the "size" a [header] has. + /// + /// This is specified to be the number of octets of name/value plus 32. + int _sizeOf(Header header) => header.name.length + header.value.length + 32; +} diff --git a/pkgs/http2/test/src/hpack/hpack_test.dart b/pkgs/http2/test/src/hpack/hpack_test.dart new file mode 100644 index 0000000000..ca3ee96ceb --- /dev/null +++ b/pkgs/http2/test/src/hpack/hpack_test.dart @@ -0,0 +1,456 @@ +// 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:unittest/unittest.dart'; +import 'package:http2/src/hpack/hpack.dart'; + +main() { + group('hpack', () { + group('hpack-spec-decoder', () { + test('C.3 request without huffman encoding', () { + var context = new HPackContext(); + List
headers; + + // First request + headers = context.decoder.decode([ + 0x82, 0x86, 0x84, 0x41, 0x0f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x78, + 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d]); + expect(headers, hasLength(4)); + expect(headers[0], isHeader(':method', 'GET')); + expect(headers[1], isHeader(':scheme', 'http')); + expect(headers[2], isHeader(':path', '/')); + expect(headers[3], isHeader(':authority', 'www.example.com')); + + // Second request + headers = context.decoder.decode([ + 0x82, 0x86, 0x84, 0xbe, 0x58, 0x08, 0x6e, 0x6f, 0x2d, 0x63, 0x61, + 0x63, 0x68, 0x65]); + expect(headers, hasLength(5)); + expect(headers[0], isHeader(':method', 'GET')); + expect(headers[1], isHeader(':scheme', 'http')); + expect(headers[2], isHeader(':path', '/')); + expect(headers[3], isHeader(':authority', 'www.example.com')); + expect(headers[4], isHeader('cache-control', 'no-cache')); + + // Third request + headers = context.decoder.decode([ + 0x82, 0x87, 0x85, 0xbf, 0x40, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, + 0x6d, 0x2d, 0x6b, 0x65, 0x79, 0x0c, 0x63, 0x75, 0x73, 0x74, 0x6f, + 0x6d, 0x2d, 0x76, 0x61, 0x6c, 0x75, 0x65]); + expect(headers, hasLength(5)); + expect(headers[0], isHeader(':method', 'GET')); + expect(headers[1], isHeader(':scheme', 'https')); + expect(headers[2], isHeader(':path', '/index.html')); + expect(headers[3], isHeader(':authority', 'www.example.com')); + expect(headers[4], isHeader('custom-key', 'custom-value')); + }); + + test('C.4 request with huffman encoding', () { + var context = new HPackContext(); + List
headers; + + // First request + headers = context.decoder.decode([ + 0x82, 0x86, 0x84, 0x41, 0x8c, 0xf1, 0xe3, 0xc2, 0xe5, 0xf2, 0x3a, + 0x6b, 0xa0, 0xab, 0x90, 0xf4, 0xff]); + expect(headers, hasLength(4)); + expect(headers[0], isHeader(':method', 'GET')); + expect(headers[1], isHeader(':scheme', 'http')); + expect(headers[2], isHeader(':path', '/')); + expect(headers[3], isHeader(':authority', 'www.example.com')); + + // Second request + headers = context.decoder.decode([ + 0x82, 0x86, 0x84, 0xbe, 0x58, 0x86, 0xa8, 0xeb, 0x10, 0x64, 0x9c, + 0xbf]); + expect(headers, hasLength(5)); + expect(headers[0], isHeader(':method', 'GET')); + expect(headers[1], isHeader(':scheme', 'http')); + expect(headers[2], isHeader(':path', '/')); + expect(headers[3], isHeader(':authority', 'www.example.com')); + expect(headers[4], isHeader('cache-control', 'no-cache')); + + // Third request + headers = context.decoder.decode([ + 0x82, 0x87, 0x85, 0xbf, 0x40, 0x88, 0x25, 0xa8, 0x49, 0xe9, 0x5b, + 0xa9, 0x7d, 0x7f, 0x89, 0x25, 0xa8, 0x49, 0xe9, 0x5b, 0xb8, 0xe8, + 0xb4, 0xbf]); + expect(headers, hasLength(5)); + expect(headers[0], isHeader(':method', 'GET')); + expect(headers[1], isHeader(':scheme', 'https')); + expect(headers[2], isHeader(':path', '/index.html')); + expect(headers[3], isHeader(':authority', 'www.example.com')); + expect(headers[4], isHeader('custom-key', 'custom-value')); + }); + + test('C.5 response without huffman encoding', () { + var context = new HPackContext(); + List
headers; + + // First response + headers = context.decoder.decode([ + 0x48, 0x03, 0x33, 0x30, 0x32, 0x58, 0x07, 0x70, 0x72, 0x69, 0x76, + 0x61, 0x74, 0x65, 0x61, 0x1d, 0x4d, 0x6f, 0x6e, 0x2c, 0x20, 0x32, + 0x31, 0x20, 0x4f, 0x63, 0x74, 0x20, 0x32, 0x30, 0x31, 0x33, 0x20, + 0x32, 0x30, 0x3a, 0x31, 0x33, 0x3a, 0x32, 0x31, 0x20, 0x47, 0x4d, + 0x54, 0x6e, 0x17, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, + 0x77, 0x77, 0x77, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, + 0x2e, 0x63, 0x6f, 0x6d]); + expect(headers, hasLength(4)); + expect(headers[0], isHeader(':status', '302')); + expect(headers[1], isHeader('cache-control', 'private')); + expect(headers[2], isHeader('date', 'Mon, 21 Oct 2013 20:13:21 GMT')); + expect(headers[3], isHeader('location', 'https://www.example.com')); + + // Second response + headers = context.decoder.decode([ + 0x48, 0x03, 0x33, 0x30, 0x37, 0xc1, 0xc0, 0xbf]); + expect(headers, hasLength(4)); + expect(headers[0], isHeader(':status', '307')); + expect(headers[1], isHeader('cache-control', 'private')); + expect(headers[2], isHeader('date', 'Mon, 21 Oct 2013 20:13:21 GMT')); + expect(headers[3], isHeader('location', 'https://www.example.com')); + + // Third response + headers = context.decoder.decode([ + 0x88, 0xc1, 0x61, 0x1d, 0x4d, 0x6f, 0x6e, 0x2c, 0x20, 0x32, 0x31, + 0x20, 0x4f, 0x63, 0x74, 0x20, 0x32, 0x30, 0x31, 0x33, 0x20, 0x32, + 0x30, 0x3a, 0x31, 0x33, 0x3a, 0x32, 0x32, 0x20, 0x47, 0x4d, 0x54, + 0xc0, 0x5a, 0x04, 0x67, 0x7a, 0x69, 0x70, 0x77, 0x38, 0x66, 0x6f, + 0x6f, 0x3d, 0x41, 0x53, 0x44, 0x4a, 0x4b, 0x48, 0x51, 0x4b, 0x42, + 0x5a, 0x58, 0x4f, 0x51, 0x57, 0x45, 0x4f, 0x50, 0x49, 0x55, 0x41, + 0x58, 0x51, 0x57, 0x45, 0x4f, 0x49, 0x55, 0x3b, 0x20, 0x6d, 0x61, + 0x78, 0x2d, 0x61, 0x67, 0x65, 0x3d, 0x33, 0x36, 0x30, 0x30, 0x3b, + 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31]); + expect(headers, hasLength(6)); + expect(headers[0], isHeader(':status', '200')); + expect(headers[1], isHeader('cache-control', 'private')); + expect(headers[2], isHeader('date', 'Mon, 21 Oct 2013 20:13:22 GMT')); + expect(headers[3], isHeader('location', 'https://www.example.com')); + expect(headers[4], isHeader('content-encoding', 'gzip')); + expect(headers[5], isHeader('set-cookie', + 'foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1')); + }); + + test('C.6 response with huffman encoding', () { + var context = new HPackContext(); + List
headers; + + // First response + headers = context.decoder.decode([ + 0x48, 0x82, 0x64, 0x02, 0x58, 0x85, 0xae, 0xc3, 0x77, 0x1a, 0x4b, + 0x61, 0x96, 0xd0, 0x7a, 0xbe, 0x94, 0x10, 0x54, 0xd4, 0x44, 0xa8, + 0x20, 0x05, 0x95, 0x04, 0x0b, 0x81, 0x66, 0xe0, 0x82, 0xa6, 0x2d, + 0x1b, 0xff, 0x6e, 0x91, 0x9d, 0x29, 0xad, 0x17, 0x18, 0x63, 0xc7, + 0x8f, 0x0b, 0x97, 0xc8, 0xe9, 0xae, 0x82, 0xae, 0x43, 0xd3]); + expect(headers, hasLength(4)); + expect(headers[0], isHeader(':status', '302')); + expect(headers[1], isHeader('cache-control', 'private')); + expect(headers[2], isHeader('date', 'Mon, 21 Oct 2013 20:13:21 GMT')); + expect(headers[3], isHeader('location', 'https://www.example.com')); + + // Second response + headers = context.decoder.decode([ + 0x48, 0x83, 0x64, 0x0e, 0xff, 0xc1, 0xc0, 0xbf]); + expect(headers, hasLength(4)); + expect(headers[0], isHeader(':status', '307')); + expect(headers[1], isHeader('cache-control', 'private')); + expect(headers[2], isHeader('date', 'Mon, 21 Oct 2013 20:13:21 GMT')); + expect(headers[3], isHeader('location', 'https://www.example.com')); + + // Third response + headers = context.decoder.decode([ + 0x88, 0xc1, 0x61, 0x96, 0xd0, 0x7a, 0xbe, 0x94, 0x10, 0x54, 0xd4, + 0x44, 0xa8, 0x20, 0x05, 0x95, 0x04, 0x0b, 0x81, 0x66, 0xe0, 0x84, + 0xa6, 0x2d, 0x1b, 0xff, 0xc0, 0x5a, 0x83, 0x9b, 0xd9, 0xab, 0x77, + 0xad, 0x94, 0xe7, 0x82, 0x1d, 0xd7, 0xf2, 0xe6, 0xc7, 0xb3, 0x35, + 0xdf, 0xdf, 0xcd, 0x5b, 0x39, 0x60, 0xd5, 0xaf, 0x27, 0x08, 0x7f, + 0x36, 0x72, 0xc1, 0xab, 0x27, 0x0f, 0xb5, 0x29, 0x1f, 0x95, 0x87, + 0x31, 0x60, 0x65, 0xc0, 0x03, 0xed, 0x4e, 0xe5, 0xb1, 0x06, 0x3d, + 0x50, 0x07]); + expect(headers, hasLength(6)); + expect(headers[0], isHeader(':status', '200')); + expect(headers[1], isHeader('cache-control', 'private')); + expect(headers[2], isHeader('date', 'Mon, 21 Oct 2013 20:13:22 GMT')); + expect(headers[3], isHeader('location', 'https://www.example.com')); + expect(headers[4], isHeader('content-encoding', 'gzip')); + expect(headers[5], isHeader('set-cookie', + 'foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1')); + }); + }); + + group('negative-decoder-tests', () { + test('invalid-integer-encoding', () { + var context = new HPackContext(); + expect(() => context.decoder.decode([1 << 6, 0xff]), + throwsHuffmanDecodingException); + }); + + test('index-out-of-table-size', () { + var context = new HPackContext(); + expect(() => context.decoder.decode([0x7f]), + throwsHuffmanDecodingException); + }); + + test('invalid-update-dynamic-table-size', () { + var context = new HPackContext(); + expect(() => context.decoder.decode([0x3f]), + throwsHuffmanDecodingException); + }); + + test('update-dynamic-table-size-too-high', () { + var context = new HPackContext(); + // Tries to set dynamic table to 4097 (max is 4096 by default) + var bytes = TestHelper.newInteger(0x20, 5, 4097); + expect(() => context.decoder.decode(bytes), + throwsHuffmanDecodingException); + }); + }); + + group('custom decoder tests', () { + const char0 = 0x30; + const char1 = 0x31; + const char2 = 0x31; + const char3 = 0x31; + const charA = 0x61; + const charB = 0x62; + const charC = 0x63; + const charD = 0x64; + + test('update-dynamic-table-size-too-high', () { + var context = new HPackContext(); + // Sets dynamic table to 4096 + expect(context.decoder.decode(TestHelper.newInteger(0x20, 5, 4096)), + []); + }); + + test('dynamic table entry', () { + List
headers; + var context = new HPackContext(); + + var buffer = []; + buffer.addAll(TestHelper.insertIntoDynamicTable(2048, char0, charA)); + buffer.addAll(TestHelper.insertIntoDynamicTable(2048, char1, charB)); + buffer.addAll(TestHelper.dynamicTableLookup(0)); + buffer.addAll(TestHelper.dynamicTableLookup(1)); + buffer.addAll(TestHelper.dynamicTableLookup(0)); + buffer.addAll(TestHelper.dynamicTableLookup(1)); + buffer.addAll(TestHelper.insertIntoDynamicTable(1024, char2, charC)); + buffer.addAll(TestHelper.insertIntoDynamicTable(1024, char3, charD)); + buffer.addAll(TestHelper.dynamicTableLookup(0)); + buffer.addAll(TestHelper.dynamicTableLookup(1)); + buffer.addAll(TestHelper.dynamicTableLookup(2)); + + headers = context.decoder.decode(buffer); + expect(headers, hasLength(11)); + TestHelper.expectHeader(headers[0], 2048, char0, charA); + TestHelper.expectHeader(headers[1], 2048, char1, charB); + + TestHelper.expectHeader(headers[2], 2048, char1, charB); + TestHelper.expectHeader(headers[3], 2048, char0, charA); + TestHelper.expectHeader(headers[4], 2048, char1, charB); + TestHelper.expectHeader(headers[5], 2048, char0, charA); + + TestHelper.expectHeader(headers[6], 1024, char2, charC); + TestHelper.expectHeader(headers[7], 1024, char3, charD); + + TestHelper.expectHeader(headers[8], 1024, char1, charD); + TestHelper.expectHeader(headers[9], 1024, char0, charC); + TestHelper.expectHeader(headers[10], 2048, char1, charB); + + // We're reducing now the size by 1 byte, which should evict the last + // entry. + headers = context.decoder.decode( + TestHelper.setDynamicTableSize(4096 - 1)); + expect(headers, hasLength(0)); + + headers = context.decoder.decode(TestHelper.dynamicTableLookup(0)); + expect(headers, hasLength(1)); + TestHelper.expectHeader(headers[0], 1024, char1, charD); + + headers = context.decoder.decode(TestHelper.dynamicTableLookup(1)); + expect(headers, hasLength(1)); + TestHelper.expectHeader(headers[0], 1024, char0, charC); + + // Since we reduce the size by 1 byte, the last entry must be gone now. + expect(() => context.decoder.decode(TestHelper.dynamicTableLookup(2)), + throwsA(isHPackDecodingException)); + }); + }); + + group('encoder-tests', () { + test('simple-encoding', () { + var context = new HPackContext(); + var headers = [new Header.ascii('key', 'value')]; + expect(context.encoder.encode(headers), + [0x00, 0x03, 0x6b, 0x65, 0x79, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65]); + }); + + test('simple-encoding-long-value', () { + var context = new HPackContext(); + var headers = [new Header([0x42], new List.filled(300, 0x84))]; + + expect(context.decoder.decode(context.encoder.encode(headers)).first, + equalsHeader(headers.first)); + + expect(context.encoder.encode(headers), + [ + // Literal Header Field with Incremental Indexing - Indexed Name + 0x00, + + // Key: Length + 0x01, + + // Key: Bytes + 0x42, + + // Value: (first 7 bits + rest) + 0x7f, 0xad, 0x01, + + // Value: Bytes + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + + + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + ]); + }); + }); + }); +} + + +class TestHelper { + static List setDynamicTableSize(int newSize) { + return TestHelper.newInteger(0x20, 5, newSize); + } + + static List newInteger(int currentByte, int prefixBits, int value) { + assert((currentByte & ((1 << prefixBits) - 1)) == 0); + var buffer = []; + if (value < ((1 << prefixBits) - 1)) { + currentByte |= value; + buffer.add(currentByte); + } else { + // Length encodeded. + currentByte |= (1 << prefixBits) - 1; + value -= (1 << prefixBits) - 1; + buffer.add(currentByte); + bool done = false; + while (!done) { + currentByte = value & 0x7f; + value = value >> 7; + done = value == 0; + if (!done) currentByte |= 0x80; + buffer.add(currentByte); + } + } + return buffer; + } + + static List insertIntoDynamicTable(int n, int nameChar, int valueChar) { + // NOTE: size(header) = 32 + header.name.length + header.value.length. + + var buffer = []; + + // Literal indexed (will be put into dynamic table) + buffer.addAll([0x40]); + + var name = [nameChar]; + buffer.addAll(newInteger(0, 7, name.length)); + buffer.addAll(name); + + var value = new List.filled(n - 32 - name.length, valueChar); + buffer.addAll(newInteger(0, 7, value.length)); + buffer.addAll(value); + + return buffer; + } + + static List dynamicTableLookup(int index) { + // There are 62 entries in the static table. + return newInteger(0x80, 7, 62 + index); + } + + static void expectHeader(Header h, int len, int nameChar, int valueChar) { + List data = h.value; + expect(data.length, len - 32 - 1); + for (int i = 0; i < data.length; i++) { + expect(data[i], valueChar); + } + } +} + + +/// A matcher for HuffmannDecodingExceptions. +const Matcher isHPackDecodingException = const _HPackDecodingException(); + +class _HPackDecodingException extends TypeMatcher { + const _HPackDecodingException() : super("HPackDecodingException"); + bool matches(item, Map matchState) => item is HPackDecodingException; +} + +const Matcher throwsHuffmanDecodingException = + const Throws(isHPackDecodingException); + + + +class _HeaderMatcher extends Matcher { + final Header header; + + _HeaderMatcher(this.header); + + Description describe(Description description) => description.add('Header'); + + bool matches(item, Map matchState) { + return + item is Header && + _compareLists(item.name, header.name) && + _compareLists(item.value, header.value); + } + + bool _compareLists(List a, List b) { + if (a == null && b == null) return true; + if (a == null && b != null) return false; + if (a.length != b.length) return false; + for (int i = 0; i < a.length; i++) { + if (a[i] != b[i]) return false; + } + return true; + } +} + +Matcher isHeader(String name, String value) + => new _HeaderMatcher(new Header.ascii(name, value)); + +Matcher equalsHeader(Header header) => new _HeaderMatcher(header); From 7932afcc005056f02b16b8e83e0954b8e4e649c3 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Wed, 8 Apr 2015 16:04:58 +0200 Subject: [PATCH 005/172] Framing: Add classes for all frames of a HTTP/2 connection. R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/175917013 --- pkgs/http2/lib/src/frames/frame_types.dart | 324 +++++++++++++++++++++ pkgs/http2/lib/src/frames/frame_utils.dart | 50 ++++ pkgs/http2/lib/src/frames/frames.dart | 12 + 3 files changed, 386 insertions(+) create mode 100644 pkgs/http2/lib/src/frames/frame_types.dart create mode 100644 pkgs/http2/lib/src/frames/frame_utils.dart create mode 100644 pkgs/http2/lib/src/frames/frames.dart diff --git a/pkgs/http2/lib/src/frames/frame_types.dart b/pkgs/http2/lib/src/frames/frame_types.dart new file mode 100644 index 0000000000..0ce5023e6b --- /dev/null +++ b/pkgs/http2/lib/src/frames/frame_types.dart @@ -0,0 +1,324 @@ +// 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. + +part of http2.src.frames; + +const int FRAME_HEADER_SIZE = 9; + +class FrameType { + static const int DATA = 0; + static const int HEADERS = 1; + static const int PRIORITY = 2; + static const int RST_STREAM = 3; + static const int SETTINGS = 4; + static const int PUSH_PROMISE = 5; + static const int PING = 6; + static const int GOAWAY = 7; + static const int WINDOW_UPDATE = 8; + static const int CONTINUATION = 9; +} + +class ErrorCode { + static const int NO_ERROR = 0; + static const int PROTOCOL_ERROR = 1; + static const int INTERNAL_ERROR = 2; + static const int FLOW_CONTROL_ERROR = 3; + static const int SETTINGS_TIMEOUT = 4; + static const int STREAM_CLOSED = 5; + static const int FRAME_SIZE_ERROR = 6; + static const int REFUSED_STREAM = 7; + static const int CANCEL = 8; + static const int COMPRESSION_ERROR = 9; + static const int CONNECT_ERROR = 10; + static const int ENHANCE_YOUR_CALM = 11; + static const int INADEQUATE_SECURITY = 12; + static const int HTTP_1_1_REQUIRED = 13; +} + +class FrameHeader { + final int length; + final int type; + final int flags; + final int streamId; + + FrameHeader(this.length, this.type, this.flags, this.streamId); + + Map toJson() => { + 'length' : length, + 'type' : type, + 'flags' : flags, + 'streamId' : streamId + }; +} + +class Frame { + static const int MAX_LEN = (1 << 24) - 1; + + final FrameHeader header; + + Frame(this.header); + + Map toJson() => { 'header' : header.toJson() }; +} + +class DataFrame extends Frame { + static const int FLAG_END_STREAM = 0x1; + static const int FLAG_PADDED = 0x8; + + /// The number of padding bytes. + final int padLength; + + final List bytes; + + DataFrame(FrameHeader header, this.padLength, this.bytes) + : super(header); + + bool get hasEndStreamFlag => _isFlagSet(header.flags, FLAG_END_STREAM); + bool get hasPaddedFlag => _isFlagSet(header.flags, FLAG_PADDED); + + Map toJson() => super.toJson()..addAll({ + 'padLength' : padLength, + 'bytes (length)': bytes.length, + 'bytes (up to 4 bytes)' : bytes.length > 4 ? bytes.sublist(0, 4) : bytes, + }); +} + +class HeadersFrame extends Frame { + static const int FLAG_END_STREAM = 0x1; + static const int FLAG_END_HEADERS = 0x4; + static const int FLAG_PADDED = 0x8; + static const int FLAG_PRIORITY = 0x20; + + // NOTE: This is the size a [HeadersFrame] can have in addition to padding + // and header block fragment data. + static const int MAX_CONSTANT_PAYLOAD = 6; + + /// The number of padding bytes (might be null). + final int padLength; + + final bool exclusiveDependency; + final int streamDependency; + final int weight; + final List headerBlockFragment; + + HeadersFrame(FrameHeader header, this.padLength, this.exclusiveDependency, + this.streamDependency, this.weight, this.headerBlockFragment) + : super(header); + + /// This will be set from the outside after decoding. + List
decodedHeaders; + + bool get hasEndStreamFlag => _isFlagSet(header.flags, FLAG_END_STREAM); + bool get hasEndHeadersFlag => _isFlagSet(header.flags, FLAG_END_HEADERS); + bool get hasPaddedFlag => _isFlagSet(header.flags, FLAG_PADDED); + bool get hasPriorityFlag => _isFlagSet(header.flags, FLAG_PRIORITY); + + HeadersFrame addBlockContinuation(ContinuationFrame frame) { + var fragment = frame.headerBlockFragment; + var flags = header.flags | frame.header.flags; + var fh = new FrameHeader(header.length + fragment.length, + header.type, flags, header.streamId); + + var mergedHeaderBlockFragment = new Uint8List( + headerBlockFragment.length + fragment.length); + + mergedHeaderBlockFragment.setRange( + 0, headerBlockFragment.length, + headerBlockFragment); + + mergedHeaderBlockFragment.setRange( + headerBlockFragment.length, mergedHeaderBlockFragment.length, + fragment); + + return new HeadersFrame(fh, padLength, exclusiveDependency, + streamDependency, weight, mergedHeaderBlockFragment); + } + + Map toJson() => super.toJson()..addAll({ + 'padLength' : padLength, + 'exclusiveDependency' : exclusiveDependency, + 'streamDependency' : streamDependency, + 'weight' : weight, + 'headerBlockFragment (length)': headerBlockFragment.length + }); +} + +class PriorityFrame extends Frame { + final bool exclusiveDependency; + final int streamDependency; + final int weight; + + PriorityFrame(FrameHeader header, this.exclusiveDependency, + this.streamDependency, this.weight) + : super(header); + + Map toJson() => super.toJson()..addAll({ + 'exclusiveDependency' : exclusiveDependency, + 'streamDependency' : streamDependency, + 'weight' : weight, + }); +} + +class RstStreamFrame extends Frame { + final int errorCode; + + RstStreamFrame(FrameHeader header, this.errorCode) + : super(header); + + Map toJson() => super.toJson()..addAll({ + 'errorCode' : errorCode, + }); +} + +class Setting { + static const int SETTINGS_HEADER_TABLE_SIZE = 1; + static const int SETTINGS_ENABLE_PUSH = 2; + static const int SETTINGS_MAX_CONCURRENT_STREAMS = 3; + static const int SETTINGS_INITIAL_WINDOW_SIZE = 4; + static const int SETTINGS_MAX_FRAME_SIZE = 5; + static const int SETTINGS_MAX_HEADER_LIST_SIZE = 6; + + final int identifier; + final int value; + + Setting(this.identifier, this.value); + + Map toJson() => {'identifier' : identifier, 'value' : value}; +} + +class SettingsFrame extends Frame { + static const int FLAG_ACK = 0x1; + + // A setting consist of a 2 byte identifier and a 4 byte value. + static const int SETTING_SIZE = 6; + + final List settings; + + SettingsFrame(FrameHeader header, this.settings) + : super(header); + + bool get hasAckFlag => _isFlagSet(header.flags, FLAG_ACK); + + Map toJson() => super.toJson()..addAll({ + 'settings' : settings.map((s) => s.toJson()).toList(), + }); +} + +class PushPromiseFrame extends Frame { + static const int FLAG_END_HEADERS = 0x4; + static const int FLAG_PADDED = 0x8; + + // NOTE: This is the size a [PushPromiseFrame] can have in addition to padding + // and header block fragment data. + static const int MAX_CONSTANT_PAYLOAD = 5; + + final int padLength; + final int promisedStreamId; + final List headerBlockFragment; + + /// This will be set from the outside after decoding. + List
decodedHeaders; + + PushPromiseFrame(FrameHeader header, this.padLength, this.promisedStreamId, + this.headerBlockFragment) + : super(header); + + bool get hasEndHeadersFlag => _isFlagSet(header.flags, FLAG_END_HEADERS); + bool get hasPaddedFlag => _isFlagSet(header.flags, FLAG_PADDED); + + PushPromiseFrame addBlockContinuation(ContinuationFrame frame) { + var fragment = frame.headerBlockFragment; + var flags = header.flags | frame.header.flags; + var fh = new FrameHeader(header.length + fragment.length, + header.type, flags, header.streamId); + + var mergedHeaderBlockFragment = new Uint8List( + headerBlockFragment.length + fragment.length); + + mergedHeaderBlockFragment.setRange( + 0, headerBlockFragment.length, + headerBlockFragment); + + mergedHeaderBlockFragment.setRange( + headerBlockFragment.length, mergedHeaderBlockFragment.length, + fragment); + + return new PushPromiseFrame( + fh, padLength, promisedStreamId, mergedHeaderBlockFragment); + } + + Map toJson() => super.toJson()..addAll({ + 'padLength' : padLength, + 'promisedStreamId' : promisedStreamId, + 'headerBlockFragment (len)' : headerBlockFragment.length, + }); +} + +class PingFrame extends Frame { + static const int FLAG_ACK = 0x1; + + final int opaqueData; + + PingFrame(FrameHeader header, this.opaqueData) + : super(header); + + bool get hasAckFlag => _isFlagSet(header.flags, FLAG_ACK); + + Map toJson() => super.toJson()..addAll({ + 'opaqueData' : opaqueData, + }); +} + +class GoawayFrame extends Frame { + final int lastStreamId; + final int errorCode; + final List debugData; + + GoawayFrame(FrameHeader header, this.lastStreamId, this.errorCode, + this.debugData) + : super(header); + + Map toJson() => super.toJson()..addAll({ + 'lastStreamId' : lastStreamId, + 'errorCode' : errorCode, + 'debugData (length)' : debugData.length, + }); +} + +class WindowUpdateFrame extends Frame { + final int windowSizeIncrement; + + WindowUpdateFrame(FrameHeader header, this.windowSizeIncrement) + : super(header); + + Map toJson() => super.toJson()..addAll({ + 'windowSizeIncrement' : windowSizeIncrement, + }); +} + +class ContinuationFrame extends Frame { + static const int FLAG_END_HEADERS = 0x4; + + final List headerBlockFragment; + + ContinuationFrame(FrameHeader header, this.headerBlockFragment) + : super(header); + + bool get hasEndHeadersFlag => _isFlagSet(header.flags, FLAG_END_HEADERS); + + Map toJson() => super.toJson()..addAll({ + 'headerBlockFragment (length)' : headerBlockFragment.length, + }); +} + +class UnknownFrame extends Frame { + final List data; + + UnknownFrame(FrameHeader header, this.data) + : super(header); + + Map toJson() => super.toJson()..addAll({ + 'data (length)' : data.length, + }); +} \ No newline at end of file diff --git a/pkgs/http2/lib/src/frames/frame_utils.dart b/pkgs/http2/lib/src/frames/frame_utils.dart new file mode 100644 index 0000000000..b32f23180d --- /dev/null +++ b/pkgs/http2/lib/src/frames/frame_utils.dart @@ -0,0 +1,50 @@ +// 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. + +part of http2.src.frames; + +int _readInt64(List bytes, int offset) { + int high = _readInt32(bytes, offset); + int low = _readInt32(bytes, offset + 4); + return high << 32 | low; +} + +int _readInt32(List bytes, int offset) { + return (bytes[offset] << 24) | (bytes[offset + 1] << 16) | + (bytes[offset + 2] << 8) | bytes[offset + 3]; +} + +int _readInt24(List bytes, int offset) { + return (bytes[offset] << 16) | (bytes[offset + 1] << 8) | bytes[offset + 2]; +} + +int _readInt16(List bytes, int offset) { + return (bytes[offset] << 8) | bytes[offset + 1]; +} + + +void _writeInt64(List bytes, int offset, int value) { + _writeInt32(bytes, offset, value >> 32); + _writeInt32(bytes, offset + 4, value & 0xffffffff); +} + +void _writeInt32(List bytes, int offset, int value) { + bytes[offset] = (value >> 24) & 0xff; + bytes[offset + 1] = (value >> 16) & 0xff; + bytes[offset + 2] = (value >> 8) & 0xff; + bytes[offset + 3] = value & 0xff; +} + +void _writeInt24(List bytes, int offset, int value) { + bytes[offset] = (value >> 16) & 0xff; + bytes[offset + 1] = (value >> 8) & 0xff; + bytes[offset + 2] = value & 0xff; +} + +void _writeInt16(List bytes, int offset, int value) { + bytes[offset] = (value >> 8) & 0xff; + bytes[offset + 1] = value & 0xff; +} + +bool _isFlagSet(int value, int bit) => value & bit == bit; diff --git a/pkgs/http2/lib/src/frames/frames.dart b/pkgs/http2/lib/src/frames/frames.dart new file mode 100644 index 0000000000..689074a0b0 --- /dev/null +++ b/pkgs/http2/lib/src/frames/frames.dart @@ -0,0 +1,12 @@ +// 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. + +library http2.src.frames; + +import 'dart:typed_data'; + +import '../hpack/hpack.dart'; + +part "frame_types.dart"; +part "frame_utils.dart"; From 2d5b148ff7de1a67e4183b724043612957154ef2 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Thu, 9 Apr 2015 14:19:11 +0200 Subject: [PATCH 006/172] Framing: Add FrameWriter, FrameReader R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/173557013 --- .../lib/src/async_utils/async_utils.dart | 139 ++++++++ pkgs/http2/lib/src/frames/frame_reader.dart | 294 +++++++++++++++++ pkgs/http2/lib/src/frames/frame_types.dart | 10 +- pkgs/http2/lib/src/frames/frame_utils.dart | 22 +- pkgs/http2/lib/src/frames/frame_writer.dart | 297 ++++++++++++++++++ pkgs/http2/lib/src/frames/frames.dart | 6 + pkgs/http2/lib/src/settings/settings.dart | 97 ++++++ .../src/async_utils/async_utils_test.dart | 85 +++++ .../test/src/frames/frame_reader_test.dart | 119 +++++++ .../src/frames/frame_writer_reader_test.dart | 233 ++++++++++++++ .../test/src/frames/frame_writer_test.dart | 35 +++ 11 files changed, 1329 insertions(+), 8 deletions(-) create mode 100644 pkgs/http2/lib/src/async_utils/async_utils.dart create mode 100644 pkgs/http2/lib/src/frames/frame_reader.dart create mode 100644 pkgs/http2/lib/src/frames/frame_writer.dart create mode 100644 pkgs/http2/lib/src/settings/settings.dart create mode 100644 pkgs/http2/test/src/async_utils/async_utils_test.dart create mode 100644 pkgs/http2/test/src/frames/frame_reader_test.dart create mode 100644 pkgs/http2/test/src/frames/frame_writer_reader_test.dart create mode 100644 pkgs/http2/test/src/frames/frame_writer_test.dart diff --git a/pkgs/http2/lib/src/async_utils/async_utils.dart b/pkgs/http2/lib/src/async_utils/async_utils.dart new file mode 100644 index 0000000000..b6401aac95 --- /dev/null +++ b/pkgs/http2/lib/src/async_utils/async_utils.dart @@ -0,0 +1,139 @@ +// 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. + +library http2.src.async_utils.async_utils; + +import 'dart:async'; +import 'dart:io'; + +/// An interface for `StreamSink`-like classes to indicate whether adding data +/// would be buffered and when the buffer is empty again. +class BufferIndicator { + final StreamController _controller = + new StreamController.broadcast(sync: true); + + /// A state variable indicating whether buffereing would occur at the moment. + bool _wouldBuffer = true; + + /// Indicates whether calling [add] would buffer the data if called. + /// + /// This can be used at a higher level as a way to do custom buffering and + /// possibly prioritization. + bool get wouldBuffer { + return _wouldBuffer; + } + + /// Signals that no buffering is happening at the moment. + void markUnBuffered() { + if (_wouldBuffer) { + _wouldBuffer = false; + _controller.add(null); + } + } + + /// Signals that buffering starts to happen. + void markBuffered() { + _wouldBuffer = true; + } + + /// A broadcast stream notifying users that the [add] method would not buffer + /// the data if called. + Stream get bufferEmptyEvents => _controller.stream; +} + + +/// Contains a [StreamSink] and a [BufferIndicator] to indicate whether writes +/// to the sink would cause buffering. +/// +/// It uses the `pause signal` from the `sink.addStream()` as an indicator +/// whether the underlying stream cannot handle more data and would buffer. +class BufferedSink { + /// The indicator whether the underlying sink is buffering at the moment. + final BufferIndicator bufferIndicator = new BufferIndicator(); + + /// A intermediate [StreamController] used to catch pause signals and to + /// propagate the change via [bufferIndicator]. + StreamController _controller; + + /// A future which completes once the sink has been closed. + Future _doneFuture; + + BufferedSink(StreamSink> dataSink) { + bufferIndicator.markBuffered(); + + _controller = new StreamController>( + onListen: () { + bufferIndicator.markUnBuffered(); + }, + onPause: () { + bufferIndicator.markBuffered(); + }, + onResume: () { + bufferIndicator.markUnBuffered(); + }, + onCancel: () { + // TODO: We may want to propagate cancel events as errors. + // Currently `_doneFuture` will just complete normally if the sink + // cancelled. + }, + sync: true); + _doneFuture = + Future.wait([_controller.stream.pipe(dataSink), dataSink.done]); + } + + /// The underlying sink. + StreamSink> get sink => _controller; + + /// The future which will complete once this sink has been closed. + Future get doneFuture => _doneFuture; +} + + +/// A small wrapper around [BufferedSink] which writes data in batches. +class BufferedBytesWriter { + /// A buffer which will be used for batching writes. + final BytesBuilder _builder = new BytesBuilder(copy: false); + + /// The underlying [BufferedSink]. + final BufferedSink _bufferedSink; + + BufferedBytesWriter(StreamSink> outgoing) + : _bufferedSink = new BufferedSink(outgoing); + + /// An indicator whether the underlying sink is buffering at the moment. + BufferIndicator get bufferIndicator => _bufferedSink.bufferIndicator; + + /// Adds [data] immediately to the underlying buffer. + /// + /// If there is buffered data which was added with [addBufferedData] and it + /// has not been flushed with [flushBufferedData] an error will be thrown. + void add(List data) { + if (_builder.length > 0) { + throw new StateError( + 'Cannot trigger an asynchronous write while there is buffered data.'); + } + _bufferedSink.sink.add(data); + } + + /// Queues up [bytes] to be written. + void addBufferedData(List bytes) { + _builder.add(bytes); + } + + /// Flushes all data which was enqueued by [addBufferedData]. + void flushBufferedData() { + if (_builder.length > 0) { + _bufferedSink.sink.add(_builder.takeBytes()); + } + } + + /// Closes this sink. + Future close() { + flushBufferedData(); + return _bufferedSink.sink.close().whenComplete(() => doneFuture); + } + + /// The future which will complete once this sink has been closed. + Future get doneFuture => _bufferedSink.doneFuture; +} diff --git a/pkgs/http2/lib/src/frames/frame_reader.dart b/pkgs/http2/lib/src/frames/frame_reader.dart new file mode 100644 index 0000000000..f623f95bd4 --- /dev/null +++ b/pkgs/http2/lib/src/frames/frame_reader.dart @@ -0,0 +1,294 @@ +// 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. + +part of http2.src.frames; + +/// Exception raised due to invalid frame encoding. +class FrameEncodingException implements Exception { + final String _message; + + FrameEncodingException(this._message); + + String toString() => 'FrameEncodingException: $_message'; +} + +/// Used for converting a `Stream>` to a `Stream`. +class FrameReader { + final Stream> _inputStream; + + /// Connection settings which this reader needs to ensure the remote end is + /// complying with. + Settings _localSettings; + + StreamSubscription> _subscription; + StreamController _framesController; + + FrameReader(this._inputStream, this._localSettings); + + /// Starts to listen on the input stream and decodes HTTP/2 transport frames. + Stream startDecoding() { + List> bufferedData = new List>(); + int bufferedLength = 0; + + FrameHeader tryReadHeader() { + if (bufferedLength >= FRAME_HEADER_SIZE) { + // Get at least FRAME_HEADER_SIZE bytes in the first byte array. + _mergeLists(bufferedData, FRAME_HEADER_SIZE); + + // Read the frame header from the first byte array. + return _readFrameHeader(bufferedData[0], 0); + } + return null; + } + + Frame tryReadFrame(FrameHeader header) { + int totalFrameLen = FRAME_HEADER_SIZE + header.length; + if (bufferedLength >= totalFrameLen) { + // Get the whole frame in the first byte array. + _mergeLists(bufferedData, totalFrameLen); + + // Read the frame. + Frame frame = _readFrame(header, bufferedData[0], FRAME_HEADER_SIZE); + + // Update bufferedData/bufferedLength + int firstChunkLen = bufferedData[0].length; + if (firstChunkLen == totalFrameLen) { + bufferedData.removeAt(0); + } else { + bufferedData[0] = _viewOrSublist( + bufferedData[0], totalFrameLen, firstChunkLen - totalFrameLen); + } + bufferedLength -= totalFrameLen; + + return frame; + } + return null; + } + + _framesController = new StreamController( + onListen: () { + FrameHeader header; + + void terminateWithError(error, [stack]) { + header = null; + _framesController.addError(error, stack); + _subscription.cancel(); + _framesController.close(); + } + + _subscription = _inputStream.listen((List data) { + bufferedData.add(data); + bufferedLength += data.length; + + try { + while (true) { + if (header == null) { + header = tryReadHeader(); + } + if (header != null) { + if (header.length > _localSettings.maxFrameSize) { + terminateWithError(new FrameEncodingException( + 'Incoming frame is too big.')); + return; + } + + Frame frame = tryReadFrame(header); + + if (frame != null) { + _framesController.add(frame); + header = null; + } else { + break; + } + } else { + break; + } + } + } catch (error, stack) { + terminateWithError(error, stack); + } + }, + onError: (error, stack) { + terminateWithError(error, stack); + }, + onDone: () { + if (bufferedLength == 0) { + _framesController.close(); + } else { + terminateWithError(new FrameEncodingException( + 'Incoming byte stream ended with incomplete frame')); + } + }); + }, + onPause: () => _subscription.pause(), + onResume: () => _subscription.resume()); + + return _framesController.stream; + } + + /// Combine combines/merges `List`s of `bufferedData` until + /// `numberOfBytes` have been accumulated. + /// + /// After calling `mergeLists`, `bufferedData[0]` will contain at least + /// `numberOfBytes` bytes. + void _mergeLists(List> bufferedData, int numberOfBytes) { + if (bufferedData[0].length < numberOfBytes) { + int numLists = 0; + int accumulatedLength = 0; + while (accumulatedLength < numberOfBytes && + numLists <= bufferedData.length) { + accumulatedLength += bufferedData[numLists++].length; + } + assert (accumulatedLength >= numberOfBytes); + var newList = new Uint8List(accumulatedLength); + int offset = 0; + for (int i = 0; i < numLists; i++) { + List data = bufferedData[i]; + newList.setRange(offset, offset + data.length, data); + offset += data.length; + } + bufferedData[0] = newList; + bufferedData.removeRange(1, numLists); + } + } + + /// Reads a FrameHeader] from [bytes], starting at [offset]. + FrameHeader _readFrameHeader(List bytes, int offset) { + int length = _readInt24(bytes, offset); + int type = bytes[offset + 3]; + int flags = bytes[offset + 4]; + int streamId = _readInt32(bytes, offset + 5) & 0x7fffffff; + + return new FrameHeader(length, type, flags, streamId); + } + + /// Reads a [Frame] from [bytes], starting at [frameOffset]. + Frame _readFrame(FrameHeader header, List bytes, int frameOffset) { + int frameEnd = frameOffset + header.length; + + int offset = frameOffset; + switch (header.type) { + case FrameType.DATA: + int padLength = 0; + if (_isFlagSet(header.flags, DataFrame.FLAG_PADDED)){ + _checkFrameLengthCondition((frameEnd - offset) >= 1); + padLength = bytes[offset++]; + } + int dataLen = frameEnd - offset - padLength; + _checkFrameLengthCondition(dataLen >= 0); + var dataBytes = _viewOrSublist(bytes, offset, dataLen); + return new DataFrame(header, padLength, dataBytes); + + case FrameType.HEADERS: + int padLength = 0; + if (_isFlagSet(header.flags, HeadersFrame.FLAG_PADDED)) { + _checkFrameLengthCondition((frameEnd - offset) >= 1); + padLength = bytes[offset++]; + } + int streamDependency; + bool exclusiveDependency = false; + int weight; + if (_isFlagSet(header.flags, HeadersFrame.FLAG_PRIORITY)) { + _checkFrameLengthCondition((frameEnd - offset) >= 5); + exclusiveDependency = (bytes[offset] & 0x80) == 0x80; + streamDependency = _readInt32(bytes, offset) & 0x7fffffff; + offset += 4; + weight = bytes[offset++]; + } + int headerBlockLen = frameEnd - offset - padLength; + _checkFrameLengthCondition(headerBlockLen >= 0); + var headerBlockFragment = _viewOrSublist( + bytes, offset, headerBlockLen); + return new HeadersFrame( + header, padLength, exclusiveDependency, streamDependency, + weight, headerBlockFragment); + + case FrameType.PRIORITY: + _checkFrameLengthCondition( + (frameEnd - offset) == PriorityFrame.FIXED_FRAME_LENGTH, + message: 'Priority frame length must be exactly 5 bytes.'); + bool exclusiveDependency = (bytes[offset] & 0x80) == 0x80; + int streamDependency = _readInt32(bytes, offset) & 0x7fffffff; + int weight = bytes[offset + 4]; + return new PriorityFrame( + header, exclusiveDependency, streamDependency, weight); + + case FrameType.RST_STREAM: + _checkFrameLengthCondition( + (frameEnd - offset) == RstStreamFrame.FIXED_FRAME_LENGTH, + message: 'Rst frames must have a length of 4.'); + int errorCode = _readInt32(bytes, offset); + return new RstStreamFrame(header, errorCode); + + case FrameType.SETTINGS: + _checkFrameLengthCondition( + (header.length % 6) == 0, + message: 'Settings frame length must be a multiple of 6 bytes.'); + + int count = header.length ~/ 6; + var settings = new List(count); + for (int i = 0; i < count; i++) { + int identifier = _readInt16(bytes, offset + 6 * i); + int value = _readInt32(bytes, offset + 6 * i + 2); + settings[i] = new Setting(identifier, value); + } + return new SettingsFrame(header, settings); + + case FrameType.PUSH_PROMISE: + int padLength = 0; + if (_isFlagSet(header.flags, PushPromiseFrame.FLAG_PADDED)) { + _checkFrameLengthCondition((frameEnd - offset) >= 1); + padLength = bytes[offset++]; + } + int promisedStreamId = _readInt32(bytes, offset) & 0x7fffffff; + offset += 4; + int headerBlockLen = frameEnd - offset - padLength; + _checkFrameLengthCondition(headerBlockLen >= 0); + var headerBlockFragment = _viewOrSublist( + bytes, offset, headerBlockLen); + return new PushPromiseFrame( + header, padLength, promisedStreamId, headerBlockFragment); + + case FrameType.PING: + _checkFrameLengthCondition( + (frameEnd - offset) == PingFrame.FIXED_FRAME_LENGTH, + message: 'Ping frames must have a length of 8.'); + var opaqueData = _readInt64(bytes, offset); + return new PingFrame(header, opaqueData); + + case FrameType.GOAWAY: + _checkFrameLengthCondition((frameEnd - offset) >= 8); + int lastStreamId = _readInt32(bytes, offset); + int errorCode = _readInt32(bytes, offset + 4); + var debugData = _viewOrSublist(bytes, offset + 8, header.length - 8); + return new GoawayFrame(header, lastStreamId, errorCode, debugData); + + case FrameType.WINDOW_UPDATE: + _checkFrameLengthCondition( + (frameEnd - offset) == WindowUpdateFrame.FIXED_FRAME_LENGTH, + message: 'Window update frames must have a length of 4.'); + int windowSizeIncrement = _readInt32(bytes, offset) & 0x7fffffff; + return new WindowUpdateFrame(header, windowSizeIncrement); + + case FrameType.CONTINUATION: + var headerBlockFragment = _viewOrSublist( + bytes, offset, frameEnd - offset); + return new ContinuationFrame(header, headerBlockFragment); + + default: + // Unknown frames should be ignored according to spec. + return new UnknownFrame( + header, _viewOrSublist(bytes, offset, frameEnd - offset)); + } + } + + /// Checks that [condition] is `true` and raises an [FrameEncodingException] + /// otherwise. + void _checkFrameLengthCondition(bool condition, + {String message: 'Frame not long enough.'}) { + if (!condition) { + throw new FrameEncodingException(message); + } + } +} diff --git a/pkgs/http2/lib/src/frames/frame_types.dart b/pkgs/http2/lib/src/frames/frame_types.dart index 0ce5023e6b..4022b5e517 100644 --- a/pkgs/http2/lib/src/frames/frame_types.dart +++ b/pkgs/http2/lib/src/frames/frame_types.dart @@ -145,6 +145,8 @@ class HeadersFrame extends Frame { } class PriorityFrame extends Frame { + static const int FIXED_FRAME_LENGTH = 5; + final bool exclusiveDependency; final int streamDependency; final int weight; @@ -161,6 +163,8 @@ class PriorityFrame extends Frame { } class RstStreamFrame extends Frame { + static const int FIXED_FRAME_LENGTH = 4; + final int errorCode; RstStreamFrame(FrameHeader header, this.errorCode) @@ -256,6 +260,8 @@ class PushPromiseFrame extends Frame { } class PingFrame extends Frame { + static const int FIXED_FRAME_LENGTH = 8; + static const int FLAG_ACK = 0x1; final int opaqueData; @@ -287,6 +293,8 @@ class GoawayFrame extends Frame { } class WindowUpdateFrame extends Frame { + static const int FIXED_FRAME_LENGTH = 4; + final int windowSizeIncrement; WindowUpdateFrame(FrameHeader header, this.windowSizeIncrement) @@ -321,4 +329,4 @@ class UnknownFrame extends Frame { Map toJson() => super.toJson()..addAll({ 'data (length)' : data.length, }); -} \ No newline at end of file +} diff --git a/pkgs/http2/lib/src/frames/frame_utils.dart b/pkgs/http2/lib/src/frames/frame_utils.dart index b32f23180d..f0ac415bb1 100644 --- a/pkgs/http2/lib/src/frames/frame_utils.dart +++ b/pkgs/http2/lib/src/frames/frame_utils.dart @@ -4,6 +4,14 @@ part of http2.src.frames; +List _viewOrSublist(List data, int offset, int length) { + if (data is Uint8List) { + return new Uint8List.view(data.buffer, data.offsetInBytes + offset, length); + } else { + return data.sublist(offset, offset + length); + } +} + int _readInt64(List bytes, int offset) { int high = _readInt32(bytes, offset); int low = _readInt32(bytes, offset + 4); @@ -24,27 +32,27 @@ int _readInt16(List bytes, int offset) { } -void _writeInt64(List bytes, int offset, int value) { - _writeInt32(bytes, offset, value >> 32); - _writeInt32(bytes, offset + 4, value & 0xffffffff); +void _setInt64(List bytes, int offset, int value) { + _setInt32(bytes, offset, value >> 32); + _setInt32(bytes, offset + 4, value & 0xffffffff); } -void _writeInt32(List bytes, int offset, int value) { +void _setInt32(List bytes, int offset, int value) { bytes[offset] = (value >> 24) & 0xff; bytes[offset + 1] = (value >> 16) & 0xff; bytes[offset + 2] = (value >> 8) & 0xff; bytes[offset + 3] = value & 0xff; } -void _writeInt24(List bytes, int offset, int value) { +void _setInt24(List bytes, int offset, int value) { bytes[offset] = (value >> 16) & 0xff; bytes[offset + 1] = (value >> 8) & 0xff; bytes[offset + 2] = value & 0xff; } -void _writeInt16(List bytes, int offset, int value) { +void _setInt16(List bytes, int offset, int value) { bytes[offset] = (value >> 8) & 0xff; bytes[offset + 1] = value & 0xff; } -bool _isFlagSet(int value, int bit) => value & bit == bit; +bool _isFlagSet(int value, int flag) => value & flag == flag; diff --git a/pkgs/http2/lib/src/frames/frame_writer.dart b/pkgs/http2/lib/src/frames/frame_writer.dart new file mode 100644 index 0000000000..6bf09e3484 --- /dev/null +++ b/pkgs/http2/lib/src/frames/frame_writer.dart @@ -0,0 +1,297 @@ +// 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. + +part of http2.src.frames; + +// TODO: Register for window update events. +// TODO: Register for setting update events. +// TODO: No support for writing padded information. +// TODO: No support for stream priorities. +class FrameWriter { + /// The HPack compression context. + final HPackEncoder _hpackEncoder; + + /// A buffered writer for outgoing bytes. + final BufferedBytesWriter _outWriter; + + /// Connection settings which this writer needs to respect. + Settings _peerSettings; + + /// This is the maximum over all stream id's we've written to the underlying + /// sink. + int _highestWrittenStreamId = 0; + + /// Whether this [FrameWriter] is closed. + bool _isClosed = false; + + FrameWriter(this._hpackEncoder, StreamSink> outgoing) + : _outWriter = new BufferedBytesWriter(outgoing); + + /// Used for initializing [FrameWriter]. + /// + /// This is necessary because of circular dependencies between + /// [SettingsHandler] and [FrameWriter]. + void initialize(Settings settings) { + _peerSettings = settings; + } + + /// A indicator whether writes would be buffered. + BufferIndicator get bufferIndicator => _outWriter.bufferIndicator; + + /// This is the maximum over all stream id's we've written to the underlying + /// sink. + int get highestWrittenStreamId => _highestWrittenStreamId; + + void writeDataFrame(int streamId, List data, {bool endStream: false}) { + while (data.length > _peerSettings.maxFrameSize) { + var chunk = _viewOrSublist(data, 0, _peerSettings.maxFrameSize); + data = _viewOrSublist(data, _peerSettings.maxFrameSize, + data.length - _peerSettings.maxFrameSize); + _writeDataFrameNoFragment(streamId, chunk, false); + } + _writeDataFrameNoFragment(streamId, data, endStream); + } + + void _writeDataFrameNoFragment(int streamId, List data, bool endStream) { + int type = FrameType.DATA; + int flags = endStream ? DataFrame.FLAG_END_STREAM : 0; + + var buffer = new Uint8List(FRAME_HEADER_SIZE + data.length); + int offset = 0; + + _setFrameHeader(buffer, offset, type, flags, streamId, data.length); + offset += FRAME_HEADER_SIZE; + + buffer.setRange(offset, offset + data.length, data); + + _writeData(buffer); + } + + void writeHeadersFrame(int streamId, List
headers, + {bool endStream: true}) { + var fragment = _hpackEncoder.encode(headers); + var maxSize = + _peerSettings.maxFrameSize - HeadersFrame.MAX_CONSTANT_PAYLOAD; + + if (fragment.length < maxSize) { + _writeHeadersFrameNoFragment(streamId, fragment, true, endStream); + } else { + var chunk = fragment.sublist(0, maxSize); + fragment = fragment.sublist(maxSize); + _writeHeadersFrameNoFragment(streamId, chunk, false, endStream); + while (fragment.length > _peerSettings.maxFrameSize) { + var chunk = fragment.sublist(0, _peerSettings.maxFrameSize); + fragment = fragment.sublist(_peerSettings.maxFrameSize); + _writeContinuationFrame(streamId, chunk, false); + } + _writeContinuationFrame(streamId, fragment, true); + } + } + + void _writeHeadersFrameNoFragment(int streamId, List fragment, + bool endHeaders, bool endStream) { + int type = FrameType.HEADERS; + int flags = 0; + if (endHeaders) flags |= HeadersFrame.FLAG_END_HEADERS; + if (endStream) flags |= HeadersFrame.FLAG_END_STREAM; + + var buffer = new Uint8List(FRAME_HEADER_SIZE + fragment.length); + int offset = 0; + + _setFrameHeader(buffer, offset, type, flags, streamId, fragment.length); + offset += FRAME_HEADER_SIZE; + + buffer.setRange(offset, buffer.length, fragment); + + _writeData(buffer); + } + + void _writeContinuationFrame(int streamId, List fragment, + bool endHeaders) { + int type = FrameType.CONTINUATION; + int flags = endHeaders ? ContinuationFrame.FLAG_END_HEADERS : 0; + + var buffer = new Uint8List(FRAME_HEADER_SIZE + fragment.length); + int offset = 0; + + _setFrameHeader(buffer, offset, type, flags, streamId, fragment.length); + offset += FRAME_HEADER_SIZE; + + buffer.setRange(offset, buffer.length, fragment); + + _writeData(buffer); + } + + void writePriorityFrame(int streamId, int streamDependency, + int weight, {bool exclusive: false}) { + int type = FrameType.PRIORITY; + int flags = 0; + + var buffer = new Uint8List( + FRAME_HEADER_SIZE + PriorityFrame.FIXED_FRAME_LENGTH); + int offset = 0; + + _setFrameHeader(buffer, offset, type, flags, streamId, 5); + offset += FRAME_HEADER_SIZE; + + if (exclusive) { + _setInt32(buffer, offset, (1 << 31) | streamDependency); + } else { + _setInt32(buffer, offset, streamDependency); + } + buffer[offset + 4] = weight; + + _writeData(buffer); + } + + void writeRstStreamFrame(int streamId, int errorCode) { + int type = FrameType.RST_STREAM; + int flags = 0; + + var buffer = new Uint8List( + FRAME_HEADER_SIZE + RstStreamFrame.FIXED_FRAME_LENGTH); + int offset = 0; + + _setFrameHeader(buffer, offset, type, flags, streamId, 4); + offset += FRAME_HEADER_SIZE; + + _setInt32(buffer, offset, errorCode); + + _writeData(buffer); + } + + void writeSettingsFrame(List settings, {bool ack: false}) { + int type = FrameType.SETTINGS; + int flags = ack ? SettingsFrame.FLAG_ACK : 0; + + var buffer = new Uint8List(FRAME_HEADER_SIZE + 6 * settings.length); + int offset = 0; + + _setFrameHeader(buffer, offset, type, flags, 0, 6 * settings.length); + offset += FRAME_HEADER_SIZE; + + for (int i = 0; i < settings.length; i++) { + var setting = settings[i]; + _setInt16(buffer, offset + 6 * i, setting.identifier); + _setInt32(buffer, offset + 6 * i + 2, setting.value); + } + + _writeData(buffer); + } + + void writePushPromiseFrame(int streamId, int promisedStreamId, + List
headers) { + var fragment = _hpackEncoder.encode(headers); + var maxSize = + _peerSettings.maxFrameSize - PushPromiseFrame.MAX_CONSTANT_PAYLOAD; + + if (fragment.length < maxSize) { + _writePushPromiseFrameNoFragmentation( + streamId, promisedStreamId, fragment, true); + } else { + var chunk = fragment.sublist(0, maxSize); + fragment = fragment.sublist(maxSize); + _writePushPromiseFrameNoFragmentation( + streamId, promisedStreamId, chunk, false); + while (fragment.length > _peerSettings.maxFrameSize) { + var chunk = fragment.sublist(0, _peerSettings.maxFrameSize); + fragment = fragment.sublist(_peerSettings.maxFrameSize); + _writeContinuationFrame(streamId, chunk, false); + } + _writeContinuationFrame(streamId, chunk, true); + } + } + + void _writePushPromiseFrameNoFragmentation( + int streamId, int promisedStreamId, List fragment, bool endHeaders) { + int type = FrameType.PUSH_PROMISE; + int flags = endHeaders ? HeadersFrame.FLAG_END_HEADERS : 0; + + var buffer = new Uint8List(FRAME_HEADER_SIZE + 4 + fragment.length); + int offset = 0; + + _setFrameHeader(buffer, offset, type, flags, streamId, 4 + fragment.length); + offset += FRAME_HEADER_SIZE; + + _setInt32(buffer, offset, promisedStreamId); + buffer.setRange(offset + 4, offset + 4 + fragment.length, fragment); + + _writeData(buffer); + } + + void writePingFrame(int opaqueData, {bool ack: false}) { + int type = FrameType.PING; + int flags = ack ? PingFrame.FLAG_ACK : 0; + + var buffer = new Uint8List( + FRAME_HEADER_SIZE + PingFrame.FIXED_FRAME_LENGTH); + int offset = 0; + + _setFrameHeader(buffer, 0, type, flags, 0, 8); + offset += FRAME_HEADER_SIZE; + + _setInt64(buffer, offset, opaqueData); + _writeData(buffer); + } + + void writeGoawayFrame(int lastStreamId, int errorCode, List debugData) { + int type = FrameType.GOAWAY; + int flags = 0; + + var buffer = new Uint8List(FRAME_HEADER_SIZE + 8 + debugData.length); + int offset = 0; + + _setFrameHeader(buffer, offset, type, flags, 0, 8 + debugData.length); + offset += FRAME_HEADER_SIZE; + + _setInt32(buffer, offset, lastStreamId); + _setInt32(buffer, offset + 4, errorCode); + buffer.setRange(offset + 8, buffer.length, debugData); + + _writeData(buffer); + } + + void writeWindowUpdate(int sizeIncrement, {int streamId: 0}) { + int type = FrameType.WINDOW_UPDATE; + int flags = 0; + + var buffer = new Uint8List( + FRAME_HEADER_SIZE + WindowUpdateFrame.FIXED_FRAME_LENGTH); + int offset = 0; + + _setFrameHeader(buffer, offset, type, flags, streamId, 4); + offset += FRAME_HEADER_SIZE; + + _setInt32(buffer, offset, sizeIncrement); + + _writeData(buffer); + } + + void _writeData(List bytes) { + if (_isClosed) { + // We do ignore any frames after this [FrameWriter] has been closed. + return; + } + + _outWriter.add(bytes); + } + + /// Closes the underlying sink and returns [doneFuture]. + Future close() { + return _outWriter.close().whenComplete(() => doneFuture); + } + + /// The future which will complete once this writer is done. + Future get doneFuture => _outWriter.doneFuture; + + void _setFrameHeader(List bytes, int offset, + int type, int flags, int streamId, int length) { + _setInt24(bytes, offset, length); + bytes[3] = type; + bytes[4] = flags; + _setInt32(bytes, 5, streamId); + + _highestWrittenStreamId = max(_highestWrittenStreamId, streamId); + } +} diff --git a/pkgs/http2/lib/src/frames/frames.dart b/pkgs/http2/lib/src/frames/frames.dart index 689074a0b0..bc7ce718a8 100644 --- a/pkgs/http2/lib/src/frames/frames.dart +++ b/pkgs/http2/lib/src/frames/frames.dart @@ -4,9 +4,15 @@ library http2.src.frames; +import 'dart:async'; import 'dart:typed_data'; +import 'dart:math' show max; +import '../async_utils/async_utils.dart'; import '../hpack/hpack.dart'; +import '../settings/settings.dart'; part "frame_types.dart"; part "frame_utils.dart"; +part "frame_reader.dart"; +part "frame_writer.dart"; diff --git a/pkgs/http2/lib/src/settings/settings.dart b/pkgs/http2/lib/src/settings/settings.dart new file mode 100644 index 0000000000..ab763db1e6 --- /dev/null +++ b/pkgs/http2/lib/src/settings/settings.dart @@ -0,0 +1,97 @@ +// 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. + +library http2.src.settings; + +/// The settings a remote peer can choose to set. +class Settings { + /// Allows the sender to inform the remote endpoint of the maximum size of the + /// header compression table used to decode header blocks, in octets. The + /// encoder can select any size equal to or less than this value by using + /// signaling specific to the header compression format inside a header block. + /// The initial value is 4,096 octets. + final int headerTableSize; + + /// This setting can be use to disable server push (Section 8.2). An endpoint + /// MUST NOT send a PUSH_PROMISE frame if it receives this parameter set to a + /// value of 0. An endpoint that has both set this parameter to 0 and had it + /// acknowledged MUST treat the receipt of a PUSH_PROMISE frame as a + /// connection error (Section 5.4.1) of type PROTOCOL_ERROR. + /// + /// The initial value is 1, which indicates that server push is permitted. + /// Any value other than 0 or 1 MUST be treated as a connection error + /// (Section 5.4.1) of type PROTOCOL_ERROR. + final bool enablePush; + + /// Indicates the maximum number of concurrent streams that the sender will + /// allow. This limit is directional: it applies to the number of streams that + /// the sender permits the receiver to create. Initially there is no limit to + /// this value. It is recommended that this value be no smaller than 100, so + /// as to not unnecessarily limit parallelism. + /// + /// A value of 0 for SETTINGS_MAX_CONCURRENT_STREAMS SHOULD NOT be treated as + /// special by endpoints. A zero value does prevent the creation of new + /// streams, however this can also happen for any limit that is exhausted with + /// active streams. Servers SHOULD only set a zero value for short durations; + /// if a server does not wish to accept requests, closing the connection is + /// more appropriate. + final int maxConcurrentStreams; + + /// Indicates the sender's initial window size (in octets) for stream level + /// flow control. The initial value is 2^16-1 (65,535) octets. + /// + /// This setting affects the window size of all streams, including existing + /// streams, see Section 6.9.2. + /// Values above the maximum flow control window size of 231-1 MUST be treated + /// as a connection error (Section 5.4.1) of type FLOW_CONTROL_ERROR. + final int initialWindowSize; + + /// Indicates the size of the largest frame payload that the sender is willing + /// to receive, in octets. + /// + /// The initial value is 2^14 (16,384) octets. The value advertised by an + /// endpoint MUST be between this initial value and the maximum allowed frame + /// size (2^24-1 or 16,777,215 octets), inclusive. Values outside this range + /// MUST be treated as a connection error (Section 5.4.1) of type + /// PROTOCOL_ERROR. + final int maxFrameSize; + + /// This advisory setting informs a peer of the maximum size of header list + /// that the sender is prepared to accept, in octets. The value is based on + /// the uncompressed size of header fields, including the length of the name + /// and value in octets plus an overhead of 32 octets for each header field. + /// + /// For any given request, a lower limit than what is advertised MAY be + /// enforced. The initial value of this setting is unlimited. + final int maxHeaderListSize; + + Settings({this.headerTableSize: 4096, + this.enablePush: true, + this.maxConcurrentStreams: null, + this.initialWindowSize: (1 << 16) - 1, + this.maxFrameSize: (1 << 14), + this.maxHeaderListSize: null}); + + Settings replace({int headerTableSize, + bool enablePush, + int maxConcurrentStreams, + int initialWindowSize, + int maxFrameSize, + int maxHeaderListSize}) { + if (headerTableSize == null) headerTableSize = this.headerTableSize; + if (enablePush == null) enablePush = this.enablePush; + if (maxConcurrentStreams == null) { + maxConcurrentStreams = this.maxConcurrentStreams; + } + if (initialWindowSize == null) initialWindowSize = this.initialWindowSize; + if (maxFrameSize == null) maxFrameSize = this.maxFrameSize; + if (maxHeaderListSize == null) maxHeaderListSize = this.maxHeaderListSize; + return new Settings(headerTableSize: headerTableSize, + enablePush: enablePush, + maxConcurrentStreams: maxConcurrentStreams, + initialWindowSize: initialWindowSize, + maxFrameSize: maxFrameSize, + maxHeaderListSize : maxHeaderListSize); + } +} diff --git a/pkgs/http2/test/src/async_utils/async_utils_test.dart b/pkgs/http2/test/src/async_utils/async_utils_test.dart new file mode 100644 index 0000000000..9940fbe805 --- /dev/null +++ b/pkgs/http2/test/src/async_utils/async_utils_test.dart @@ -0,0 +1,85 @@ +// 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:unittest/unittest.dart'; +import 'package:http2/src/async_utils/async_utils.dart'; + +main() { + group('async_utils', () { + test('buffer-indicator', () { + var bi = new BufferIndicator(); + bi.bufferEmptyEvents.listen(expectAsync((_) {}, count: 2)); + + expect(bi.wouldBuffer, true); + + bi.markUnBuffered(); + expect(bi.wouldBuffer, false); + + bi.markBuffered(); + expect(bi.wouldBuffer, true); + bi.markBuffered(); + expect(bi.wouldBuffer, true); + + bi.markUnBuffered(); + expect(bi.wouldBuffer, false); + bi.markUnBuffered(); + expect(bi.wouldBuffer, false); + + bi.markBuffered(); + expect(bi.wouldBuffer, true); + bi.markBuffered(); + expect(bi.wouldBuffer, true); + }); + + test('buffered-sink', () { + var c = new StreamController(); + var bs = new BufferedSink(c); + + expect(bs.bufferIndicator.wouldBuffer, true); + var sub = c.stream.listen(expectAsync((_) {}, count: 2)); + + expect(bs.bufferIndicator.wouldBuffer, false); + + sub.pause(); + Timer.run(expectAsync(() { + expect(bs.bufferIndicator.wouldBuffer, true); + bs.sink.add([1]); + + sub.resume(); + Timer.run(expectAsync(() { + expect(bs.bufferIndicator.wouldBuffer, false); + bs.sink.add([2]); + + Timer.run(expectAsync(() { + sub.cancel(); + expect(bs.bufferIndicator.wouldBuffer, false); + })); + })); + })); + }); + + test('buffered-bytes-writer', () async { + var c = new StreamController(); + var writer = new BufferedBytesWriter(c); + + expect(writer.bufferIndicator.wouldBuffer, true); + + var bytesFuture = c.stream.fold([], (b, d) => b..addAll(d)); + + expect(writer.bufferIndicator.wouldBuffer, false); + + writer.add([1, 2]); + writer.add([3, 4]); + + writer.addBufferedData([5, 6]); + expect(() => writer.add([7, 8]), throwsStateError); + + writer.addBufferedData([7, 8]); + await writer.close(); + expect(await bytesFuture, [1, 2, 3, 4, 5, 6, 7, 8]); + }); + }); +} diff --git a/pkgs/http2/test/src/frames/frame_reader_test.dart b/pkgs/http2/test/src/frames/frame_reader_test.dart new file mode 100644 index 0000000000..07dcabb47e --- /dev/null +++ b/pkgs/http2/test/src/frames/frame_reader_test.dart @@ -0,0 +1,119 @@ +// 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:unittest/unittest.dart'; + +import 'package:http2/src/hpack/hpack.dart'; +import 'package:http2/src/frames/frames.dart'; +import 'package:http2/src/settings/settings.dart'; + +import '../hpack/hpack_test.dart' show isHeader; + +main() { + group('frames', () { + group('frame-reader', () { + final int maxFrameSize = new Settings().maxFrameSize; + + Stream dataFrame(List body) { + var settings = new Settings(); + var context = new HPackContext(); + var controller = new StreamController>(); + var reader = new FrameReader(controller.stream, settings); + + // This is a DataFrame: + // - length: n + // - type: [0] + // - flags: [0] + // - stream id: [0, 0, 0, 1] + controller + ..add([0, (body.length >> 8) & 0xff, body.length & 0xff]) + ..add([0]) + ..add([0]) + ..add([0, 0, 0, 1]) + ..add(body) + ..close(); + return reader.startDecoding(); + } + + test('data-frame--max-frame-size', () { + var body = new List.filled(maxFrameSize, 0x42); + dataFrame(body).listen( + expectAsync((Frame frame) { + expect(frame is DataFrame, isTrue); + expect(frame.header.length, body.length); + expect(frame.header.flags, 0); + DataFrame dataFrame = frame; + expect(dataFrame.hasEndStreamFlag, isFalse); + expect(dataFrame.hasPaddedFlag, isFalse); + expect(dataFrame.bytes, body); + }), + onError: expectAsync((error, stack) {}, count: 0)); + }); + + test('data-frame--max-frame-size-plus-1', () { + var body = new List.filled(maxFrameSize + 1, 0x42); + dataFrame(body).listen( + expectAsync((_) {}, count: 0), + onError: expectAsync((error, stack) { + expect('$error', contains('Incoming frame is too big')); + })); + }); + + test('incomplete-header', () { + var settings = new Settings(); + + var context = new HPackContext(); + var controller = new StreamController>(); + var reader = new FrameReader(controller.stream, settings); + + controller..add([1])..close(); + + reader.startDecoding().listen( + expectAsync((_) {}, count: 0), + onError: expectAsync((error, stack) { + expect('$error', contains('incomplete frame')); + })); + }); + + test('incomplete-frame', () { + var settings = new Settings(); + + var context = new HPackContext(); + var controller = new StreamController>(); + var reader = new FrameReader(controller.stream, settings); + + // This is a DataFrame: + // - length: [0, 0, 255] + // - type: [0] + // - flags: [0] + // - stream id: [0, 0, 0, 1] + controller..add([0, 0, 255, 0, 0, 0, 0, 0, 1])..close(); + + reader.startDecoding().listen( + expectAsync((_) {}, count: 0), + onError: expectAsync((error, stack) { + expect('$error', contains('incomplete frame')); + })); + }); + + test('connection-error', () { + var settings = new Settings(); + + var context = new HPackContext(); + var controller = new StreamController>(); + var reader = new FrameReader(controller.stream, settings); + + controller..addError('hello world')..close(); + + reader.startDecoding().listen( + expectAsync((_) {}, count: 0), + onError: expectAsync((error, stack) { + expect('$error', contains('hello world')); + })); + }); + }); + }); +} diff --git a/pkgs/http2/test/src/frames/frame_writer_reader_test.dart b/pkgs/http2/test/src/frames/frame_writer_reader_test.dart new file mode 100644 index 0000000000..a8bd4e2df6 --- /dev/null +++ b/pkgs/http2/test/src/frames/frame_writer_reader_test.dart @@ -0,0 +1,233 @@ +// 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:unittest/unittest.dart'; + +import 'package:http2/src/hpack/hpack.dart'; +import 'package:http2/src/frames/frames.dart'; +import 'package:http2/src/settings/settings.dart'; + +import '../hpack/hpack_test.dart' show isHeader; + +main() { + group('frames', () { + group('writer-reader', () { + writerReaderTest('data-frame', (FrameWriter writer, + FrameReader reader, + HPackDecoder decoder) async { + writer.writeDataFrame(99, [1, 2, 3], endStream: true); + + var frames = await finishWriting(writer, reader); + expect(frames.length, 1); + expect(frames[0] is DataFrame, isTrue); + + DataFrame dataFrame = frames[0]; + expect(dataFrame.header.streamId, 99); + expect(dataFrame.hasPaddedFlag, isFalse); + expect(dataFrame.padLength, 0); + expect(dataFrame.hasEndStreamFlag, isTrue); + expect(dataFrame.bytes, [1, 2, 3]); + }); + + writerReaderTest('headers-frame', (FrameWriter writer, + FrameReader reader, + HPackDecoder decoder) async { + writer.writeHeadersFrame( + 99, [new Header.ascii('a', 'b')], endStream: true); + + var frames = await finishWriting(writer, reader); + expect(frames.length, 1); + expect(frames[0] is HeadersFrame, isTrue); + + HeadersFrame headersFrame = frames[0]; + expect(headersFrame.header.streamId, 99); + expect(headersFrame.hasPaddedFlag, isFalse); + expect(headersFrame.padLength, 0); + expect(headersFrame.hasEndStreamFlag, isTrue); + expect(headersFrame.hasEndHeadersFlag, isTrue); + expect(headersFrame.exclusiveDependency, false); + expect(headersFrame.hasPriorityFlag, false); + expect(headersFrame.streamDependency, isNull); + expect(headersFrame.weight, isNull); + + var headers = decoder.decode(headersFrame.headerBlockFragment); + expect(headers.length, 1); + expect(headers[0], isHeader('a', 'b')); + }); + + writerReaderTest('priority-frame', (FrameWriter writer, + FrameReader reader, + HPackDecoder decoder) async { + writer.writePriorityFrame(99, 44, 33, exclusive: true); + + var frames = await finishWriting(writer, reader); + expect(frames.length, 1); + expect(frames[0] is PriorityFrame, isTrue); + + PriorityFrame priorityFrame = frames[0]; + expect(priorityFrame.header.streamId, 99); + expect(priorityFrame.exclusiveDependency, isTrue); + expect(priorityFrame.streamDependency, 44); + expect(priorityFrame.weight, 33); + }); + + writerReaderTest('rst-frame', (FrameWriter writer, + FrameReader reader, + HPackDecoder decoder) async { + writer.writeRstStreamFrame(99, 42); + + var frames = await finishWriting(writer, reader); + expect(frames.length, 1); + expect(frames[0] is RstStreamFrame, isTrue); + + RstStreamFrame rstFrame = frames[0]; + expect(rstFrame.header.streamId, 99); + expect(rstFrame.errorCode, 42); + }); + + writerReaderTest('settings-frame', (FrameWriter writer, + FrameReader reader, + HPackDecoder decoder) async { + writer.writeSettingsFrame( + [new Setting(Setting.SETTINGS_ENABLE_PUSH, 1)], ack: true); + + var frames = await finishWriting(writer, reader); + expect(frames.length, 1); + expect(frames[0] is SettingsFrame, isTrue); + + SettingsFrame settingsFrame = frames[0]; + expect(settingsFrame.hasAckFlag, true); + expect(settingsFrame.header.streamId, 0); + expect(settingsFrame.settings.length, 1); + expect(settingsFrame.settings[0].identifier, + Setting.SETTINGS_ENABLE_PUSH); + expect(settingsFrame.settings[0].value, 1); + }); + + writerReaderTest('push-promise-frame', (FrameWriter writer, + FrameReader reader, + HPackDecoder decoder) async { + writer.writePushPromiseFrame(99, 44, [new Header.ascii('a', 'b')]); + + var frames = await finishWriting(writer, reader); + expect(frames.length, 1); + expect(frames[0] is PushPromiseFrame, isTrue); + + PushPromiseFrame pushPromiseFrame = frames[0]; + expect(pushPromiseFrame.header.streamId, 99); + expect(pushPromiseFrame.hasEndHeadersFlag, isTrue); + expect(pushPromiseFrame.hasPaddedFlag, isFalse); + expect(pushPromiseFrame.padLength, 0); + expect(pushPromiseFrame.promisedStreamId, 44); + + var headers = decoder.decode(pushPromiseFrame.headerBlockFragment); + expect(headers.length, 1); + expect(headers[0], isHeader('a', 'b')); + }); + + writerReaderTest('ping-frame', (FrameWriter writer, + FrameReader reader, + HPackDecoder decoder) async { + writer.writePingFrame(44, ack: true); + + var frames = await finishWriting(writer, reader); + expect(frames.length, 1); + expect(frames[0] is PingFrame, isTrue); + + PingFrame pingFrame = frames[0]; + expect(pingFrame.header.streamId, 0); + expect(pingFrame.opaqueData, 44); + expect(pingFrame.hasAckFlag, isTrue); + }); + + writerReaderTest('goaway-frame', (FrameWriter writer, + FrameReader reader, + HPackDecoder decoder) async { + writer.writeGoawayFrame(44, 33, [1, 2, 3]); + + var frames = await finishWriting(writer, reader); + expect(frames.length, 1); + expect(frames[0] is GoawayFrame, isTrue); + + GoawayFrame goawayFrame = frames[0]; + expect(goawayFrame.header.streamId, 0); + expect(goawayFrame.lastStreamId, 44); + expect(goawayFrame.errorCode, 33); + expect(goawayFrame.debugData, [1, 2, 3]); + }); + + writerReaderTest('window-update-frame', (FrameWriter writer, + FrameReader reader, + HPackDecoder decoder) async { + writer.writeWindowUpdate(55, streamId: 99); + + var frames = await finishWriting(writer, reader); + expect(frames.length, 1); + expect(frames[0] is WindowUpdateFrame, isTrue); + + WindowUpdateFrame windowUpdateFrame = frames[0]; + expect(windowUpdateFrame.header.streamId, 99); + expect(windowUpdateFrame.windowSizeIncrement, 55); + }); + + writerReaderTest('frag-headers-frame', (FrameWriter writer, + FrameReader reader, + HPackDecoder decoder) async { + var headerName = [1]; + var headerValue = new List.filled(1 << 14, 0x42); + var header = new Header(headerName, headerValue); + + writer.writeHeadersFrame(99, [header], endStream: true); + + var frames = await finishWriting(writer, reader); + expect(frames.length, 2); + expect(frames[0] is HeadersFrame, isTrue); + expect(frames[1] is ContinuationFrame, isTrue); + + HeadersFrame headersFrame = frames[0]; + expect(headersFrame.header.streamId, 99); + expect(headersFrame.hasPaddedFlag, isFalse); + expect(headersFrame.padLength, 0); + expect(headersFrame.hasEndHeadersFlag, isFalse); + expect(headersFrame.hasEndStreamFlag, isTrue); + expect(headersFrame.hasPriorityFlag, isFalse); + + ContinuationFrame contFrame = frames[1]; + expect(contFrame.header.streamId, 99); + expect(contFrame.hasEndHeadersFlag, isTrue); + + var headerBlock = [] + ..addAll(headersFrame.headerBlockFragment) + ..addAll(contFrame.headerBlockFragment); + + var headers = decoder.decode(headerBlock); + expect(headers.length, 1); + expect(headers[0].name, headerName); + expect(headers[0].value, headerValue); + }); + }); + }); +} + +writerReaderTest(String name, func(writer, reader, decoder)) { + test(name, () { + var settings = new Settings(); + + var context = new HPackContext(); + var controller = new StreamController>(); + var writer = new FrameWriter(context.encoder, controller); + var reader = new FrameReader(controller.stream, settings); + + writer.initialize(settings); + + return func(writer, reader, context.decoder); + }); +} + +Future> finishWriting(FrameWriter writer, FrameReader reader) { + return Future.wait([writer.close(), reader.startDecoding().toList()]) + .then((results) => results.last); +} diff --git a/pkgs/http2/test/src/frames/frame_writer_test.dart b/pkgs/http2/test/src/frames/frame_writer_test.dart new file mode 100644 index 0000000000..2d98c11e06 --- /dev/null +++ b/pkgs/http2/test/src/frames/frame_writer_test.dart @@ -0,0 +1,35 @@ +// 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:unittest/unittest.dart'; + +import 'package:http2/src/hpack/hpack.dart'; +import 'package:http2/src/frames/frames.dart'; +import 'package:http2/src/settings/settings.dart'; + +import '../hpack/hpack_test.dart' show isHeader; + +main() { + group('frames', () { + group('frame-writer', () { + test('connection-error', () { + var settings = new Settings(); + var context = new HPackContext(); + var controller = new StreamController>(); + var reader = new FrameReader(controller.stream, settings); + var writer = new FrameWriter(context.encoder, controller); + writer.initialize(settings); + + writer.doneFuture.then(expectAsync((_) { + // We expect that the writer is done at this point. + })); + + // We cancel here the reading part (simulates a dying socket). + controller.stream.listen((_) {}).cancel(); + }); + }); + }); +} From 32b274cb416cdae31aed41da6af0c32d18d98128 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Thu, 9 Apr 2015 16:56:51 +0200 Subject: [PATCH 007/172] Connection: Add readConnectionSequence() function R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/179147013 --- pkgs/http2/lib/src/connection_preface.dart | 97 +++++++++++++++++++ pkgs/http2/lib/src/frames/frame_reader.dart | 14 +-- pkgs/http2/lib/src/frames/frame_utils.dart | 2 +- pkgs/http2/lib/src/frames/frame_writer.dart | 4 +- .../test/src/connection_preface_test.dart | 79 +++++++++++++++ 5 files changed, 186 insertions(+), 10 deletions(-) create mode 100644 pkgs/http2/lib/src/connection_preface.dart create mode 100644 pkgs/http2/test/src/connection_preface_test.dart diff --git a/pkgs/http2/lib/src/connection_preface.dart b/pkgs/http2/lib/src/connection_preface.dart new file mode 100644 index 0000000000..194b67a78a --- /dev/null +++ b/pkgs/http2/lib/src/connection_preface.dart @@ -0,0 +1,97 @@ +// 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. + +library http2.src.connection_preface; + +import 'dart:async'; +import 'dart:math'; + +import 'frames/frames.dart'; + +/// This is a set of bytes with which a client connection begins in the normal +/// case. It can be used on a server to distinguish HTTP/1.1 and HTTP/2 clients. +const List CONNECTION_PREFACE = const [ + 0x50, 0x52, 0x49, 0x20, 0x2a, 0x20, 0x48, 0x54, 0x54, + 0x50, 0x2f, 0x32, 0x2e, 0x30, 0x0d, 0x0a, 0x0d, 0x0a, + 0x53, 0x4d, 0x0d, 0x0a, 0x0d, 0x0a]; + + +/// Reads the connection preface from [incoming]. +/// +/// The returned `Stream` will be a duplicate of `incoming` without the +/// connection preface. If an error occurs while reading the connection +/// preface, the returned stream will have only an error. +Stream> readConnectionPreface(Stream> incoming) { + StreamController result; + StreamSubscription subscription; + bool connectionPrefaceRead = false; + var prefaceBuffer = []; + bool terminated = false; + + terminate(error) { + if (!terminated) { + result.addError(error); + result.close(); + subscription.cancel(); + } + terminated = true; + } + + void compareConnectionPreface(List data) { + for (int i = 0; i < CONNECTION_PREFACE.length; i++) { + if (data[i] != CONNECTION_PREFACE[i]) { + terminate('Connection preface does not match.'); + break; + } + } + prefaceBuffer = null; + connectionPrefaceRead = true; + } + + void onData(List data) { + if (connectionPrefaceRead) { + // Forward data after reading preface. + result.add(data); + } else { + if (prefaceBuffer.isEmpty && data.length > CONNECTION_PREFACE.length) { + compareConnectionPreface(data); + data = data.sublist(CONNECTION_PREFACE.length); + } else if (prefaceBuffer.length < CONNECTION_PREFACE.length) { + int remaining = CONNECTION_PREFACE.length - prefaceBuffer.length; + + int end = min(data.length, remaining); + var part1 = viewOrSublist(data, 0, end); + var part2 = viewOrSublist(data, end, data.length - end); + prefaceBuffer.addAll(part1); + + if (prefaceBuffer.length == CONNECTION_PREFACE.length) { + compareConnectionPreface(prefaceBuffer); + } + data = part2; + } + if (data.length > 0) { + result.add(data); + } + } + } + + result = new StreamController( + onListen: () { + subscription = incoming.listen( + onData, + onError: (e,s) => result.addError(e, s), + onDone: () { + if (prefaceBuffer != null) { + terminate('EOS before connection preface could be read.'); + } else { + result.close(); + } + }); + }, + onPause: () => subscription.pause(), + onResume: () => subscription.resume(), + onCancel: () => subscription.cancel()); + + return result.stream; +} diff --git a/pkgs/http2/lib/src/frames/frame_reader.dart b/pkgs/http2/lib/src/frames/frame_reader.dart index f623f95bd4..aa4f534dcf 100644 --- a/pkgs/http2/lib/src/frames/frame_reader.dart +++ b/pkgs/http2/lib/src/frames/frame_reader.dart @@ -56,7 +56,7 @@ class FrameReader { if (firstChunkLen == totalFrameLen) { bufferedData.removeAt(0); } else { - bufferedData[0] = _viewOrSublist( + bufferedData[0] = viewOrSublist( bufferedData[0], totalFrameLen, firstChunkLen - totalFrameLen); } bufferedLength -= totalFrameLen; @@ -177,7 +177,7 @@ class FrameReader { } int dataLen = frameEnd - offset - padLength; _checkFrameLengthCondition(dataLen >= 0); - var dataBytes = _viewOrSublist(bytes, offset, dataLen); + var dataBytes = viewOrSublist(bytes, offset, dataLen); return new DataFrame(header, padLength, dataBytes); case FrameType.HEADERS: @@ -198,7 +198,7 @@ class FrameReader { } int headerBlockLen = frameEnd - offset - padLength; _checkFrameLengthCondition(headerBlockLen >= 0); - var headerBlockFragment = _viewOrSublist( + var headerBlockFragment = viewOrSublist( bytes, offset, headerBlockLen); return new HeadersFrame( header, padLength, exclusiveDependency, streamDependency, @@ -245,7 +245,7 @@ class FrameReader { offset += 4; int headerBlockLen = frameEnd - offset - padLength; _checkFrameLengthCondition(headerBlockLen >= 0); - var headerBlockFragment = _viewOrSublist( + var headerBlockFragment = viewOrSublist( bytes, offset, headerBlockLen); return new PushPromiseFrame( header, padLength, promisedStreamId, headerBlockFragment); @@ -261,7 +261,7 @@ class FrameReader { _checkFrameLengthCondition((frameEnd - offset) >= 8); int lastStreamId = _readInt32(bytes, offset); int errorCode = _readInt32(bytes, offset + 4); - var debugData = _viewOrSublist(bytes, offset + 8, header.length - 8); + var debugData = viewOrSublist(bytes, offset + 8, header.length - 8); return new GoawayFrame(header, lastStreamId, errorCode, debugData); case FrameType.WINDOW_UPDATE: @@ -272,14 +272,14 @@ class FrameReader { return new WindowUpdateFrame(header, windowSizeIncrement); case FrameType.CONTINUATION: - var headerBlockFragment = _viewOrSublist( + var headerBlockFragment = viewOrSublist( bytes, offset, frameEnd - offset); return new ContinuationFrame(header, headerBlockFragment); default: // Unknown frames should be ignored according to spec. return new UnknownFrame( - header, _viewOrSublist(bytes, offset, frameEnd - offset)); + header, viewOrSublist(bytes, offset, frameEnd - offset)); } } diff --git a/pkgs/http2/lib/src/frames/frame_utils.dart b/pkgs/http2/lib/src/frames/frame_utils.dart index f0ac415bb1..55dcc75451 100644 --- a/pkgs/http2/lib/src/frames/frame_utils.dart +++ b/pkgs/http2/lib/src/frames/frame_utils.dart @@ -4,7 +4,7 @@ part of http2.src.frames; -List _viewOrSublist(List data, int offset, int length) { +List viewOrSublist(List data, int offset, int length) { if (data is Uint8List) { return new Uint8List.view(data.buffer, data.offsetInBytes + offset, length); } else { diff --git a/pkgs/http2/lib/src/frames/frame_writer.dart b/pkgs/http2/lib/src/frames/frame_writer.dart index 6bf09e3484..14f013a8e1 100644 --- a/pkgs/http2/lib/src/frames/frame_writer.dart +++ b/pkgs/http2/lib/src/frames/frame_writer.dart @@ -45,8 +45,8 @@ class FrameWriter { void writeDataFrame(int streamId, List data, {bool endStream: false}) { while (data.length > _peerSettings.maxFrameSize) { - var chunk = _viewOrSublist(data, 0, _peerSettings.maxFrameSize); - data = _viewOrSublist(data, _peerSettings.maxFrameSize, + var chunk = viewOrSublist(data, 0, _peerSettings.maxFrameSize); + data = viewOrSublist(data, _peerSettings.maxFrameSize, data.length - _peerSettings.maxFrameSize); _writeDataFrameNoFragment(streamId, chunk, false); } diff --git a/pkgs/http2/test/src/connection_preface_test.dart b/pkgs/http2/test/src/connection_preface_test.dart new file mode 100644 index 0000000000..f2145e50f8 --- /dev/null +++ b/pkgs/http2/test/src/connection_preface_test.dart @@ -0,0 +1,79 @@ +// 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. + +library http2.test.connection_preface_test; + +import 'dart:async'; +import 'dart:math' show min; + +import 'package:unittest/unittest.dart'; +import 'package:http2/src/connection_preface.dart'; + +main() { + group('connection-preface', () { + test('successful', () async { + final frameBytes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + final data = new List.from(CONNECTION_PREFACE)..addAll(frameBytes); + + for (int size = 1; size <= data.length; size++) { + var c = new StreamController(); + var resultF = + readConnectionPreface(c.stream).fold([], (b, d) => b..addAll(d)); + + for (int i = 0; i < (size - 1 + data.length) ~/ size; i++) { + int from = size * i; + int to = min(size * (i + 1), data.length); + + c.add(data.sublist(from, to)); + } + c.close(); + + expect(await resultF, frameBytes); + } + }); + + test('only-part-of-connection-sequence', () async { + var c = new StreamController(); + var resultF = + readConnectionPreface(c.stream).fold([], (b, d) => b..addAll(d)); + + for (int i = 0; i < CONNECTION_PREFACE.length - 1; i++) { + c.add([CONNECTION_PREFACE[i]]); + } + c.close(); + + resultF.catchError(expectAsync((error, _) { + expect(error, contains('EOS before connection preface could be read')); + })); + }); + + test('wrong-connection-sequence', () async { + var c = new StreamController(); + var resultF = + readConnectionPreface(c.stream).fold([], (b, d) => b..addAll(d)); + + for (int i = 0; i < CONNECTION_PREFACE.length; i++) { + c.add([0xff]); + } + c.close(); + + resultF.catchError(expectAsync((error, _) { + expect(error, contains('Connection preface does not match.')); + })); + }); + + test('incoming-socket-error', () async { + var c = new StreamController(); + var resultF = + readConnectionPreface(c.stream).fold([], (b, d) => b..addAll(d)); + + c.addError('hello world'); + c.close(); + + resultF.catchError(expectAsync((error, _) { + expect(error, contains('hello world')); + })); + }); + }); +} From fdaeafb8b5e1567f5e097919bb9efaf2b3d60236 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Fri, 10 Apr 2015 12:44:52 +0200 Subject: [PATCH 008/172] Framing: Add a FrameDefragmenter class R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/180037013 --- .../lib/src/frames/frame_defragmenter.dart | 93 +++++++++++ pkgs/http2/lib/src/sync_errors.dart | 13 ++ .../src/frames/frame_defragmenter_test.dart | 147 ++++++++++++++++++ 3 files changed, 253 insertions(+) create mode 100644 pkgs/http2/lib/src/frames/frame_defragmenter.dart create mode 100644 pkgs/http2/lib/src/sync_errors.dart create mode 100644 pkgs/http2/test/src/frames/frame_defragmenter_test.dart diff --git a/pkgs/http2/lib/src/frames/frame_defragmenter.dart b/pkgs/http2/lib/src/frames/frame_defragmenter.dart new file mode 100644 index 0000000000..5c42662fce --- /dev/null +++ b/pkgs/http2/lib/src/frames/frame_defragmenter.dart @@ -0,0 +1,93 @@ +// 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. + +library http2.src.frames.frame_defragmenter; + +import 'frames.dart'; + +import '../sync_errors.dart'; + +/// Class used for defragmenting [HeadersFrame]s and [PushPromiseFrame]s. +// TODO: Somehow emit an error if too many continuation frames have been sent +// (since we're buffering all of them). +class FrameDefragmenter { + /// The current incomplete [HeadersFrame] fragment. + HeadersFrame _headersFrame; + + /// The current incomplete [PushPromiseFrame] fragment. + PushPromiseFrame _pushPromiseFrame; + + /// Tries to defragment [frame]. + /// + /// If the given [frame] is a [HeadersFrame] or a [PushPromiseFrame] which + /// needs de-fragmentation, it will be saved and `null` will be returned. + /// + /// If there is currently an incomplete [HeadersFrame] or [PushPromiseFrame] + /// saved, [frame] needs to be a [ContinuationFrame]. It will be added to the + /// saved frame. In case the defragmentation is complete, the defragmented + /// [HeadersFrame] or [PushPromiseFrame] will be returned. + /// + /// All other [Frame] types will be returned. + // TODO: Consider handling continuation frames without preceding + // headers/push-promise frame here instead of the call site? + Frame tryDefragmentFrame(Frame frame) { + if (_headersFrame != null) { + if (frame is ContinuationFrame) { + if (_headersFrame.header.streamId != frame.header.streamId) { + throw new ProtocolException( + 'Defragmentation: frames have different stream ids.'); + } + _headersFrame = _headersFrame.addBlockContinuation(frame); + + if (frame.hasEndHeadersFlag) { + var frame = _headersFrame; + _headersFrame = null; + return frame; + } else { + return null; + } + } else { + throw new ProtocolException( + 'Defragmentation: Incomplete frame must be followed by ' + 'continuation frame.'); + } + } else if (_pushPromiseFrame != null) { + if (frame is ContinuationFrame) { + if (_pushPromiseFrame.header.streamId != frame.header.streamId) { + throw new ProtocolException( + 'Defragmentation: frames have different stream ids.'); + } + _pushPromiseFrame = _pushPromiseFrame.addBlockContinuation(frame); + + if (frame.hasEndHeadersFlag) { + var frame = _pushPromiseFrame; + _pushPromiseFrame = null; + return frame; + } else { + return null; + } + } else { + throw new ProtocolException( + 'Defragmentation: Incomplete frame must be followed by ' + 'continuation frame.'); + } + } else { + if (frame is HeadersFrame) { + if (!frame.hasEndHeadersFlag) { + _headersFrame = frame; + return null; + } + } else if (frame is PushPromiseFrame) { + if (!frame.hasEndHeadersFlag) { + _pushPromiseFrame = frame; + return null; + } + } + } + + // If this frame is not relevant for header defragmentation, we pass it to + // the next stage. + return frame; + } +} diff --git a/pkgs/http2/lib/src/sync_errors.dart b/pkgs/http2/lib/src/sync_errors.dart new file mode 100644 index 0000000000..06b7cb5ef4 --- /dev/null +++ b/pkgs/http2/lib/src/sync_errors.dart @@ -0,0 +1,13 @@ +// 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. + +library http2.src.sync_errors; + +class ProtocolException implements Exception { + final String message; + + ProtocolException(this.message); + + String toString() => 'ProtocolError: $message'; +} diff --git a/pkgs/http2/test/src/frames/frame_defragmenter_test.dart b/pkgs/http2/test/src/frames/frame_defragmenter_test.dart new file mode 100644 index 0000000000..db12e12d79 --- /dev/null +++ b/pkgs/http2/test/src/frames/frame_defragmenter_test.dart @@ -0,0 +1,147 @@ +// 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:unittest/unittest.dart'; + +import 'package:http2/src/sync_errors.dart'; +import 'package:http2/src/frames/frames.dart'; +import 'package:http2/src/frames/frame_defragmenter.dart'; + +main() { + group('frames', () { + group('frame-defragmenter', () { + UnknownFrame unknownFrame() { + return new UnknownFrame(new FrameHeader(0, 0, 0, 1), []); + } + + HeadersFrame headersFrame(List data, + {bool fragmented: false, int streamId: 1}) { + int flags = fragmented ? 0 : HeadersFrame.FLAG_END_HEADERS; + var header = new FrameHeader( + data.length, FrameType.HEADERS, flags, streamId); + return new HeadersFrame(header, 0, false, null, null, data); + } + + PushPromiseFrame pushPromiseFrame(List data, + {bool fragmented: false, + int streamId: 1}) { + int flags = fragmented ? 0 : HeadersFrame.FLAG_END_HEADERS; + var header = new FrameHeader( + data.length, FrameType.PUSH_PROMISE, flags, streamId); + return new PushPromiseFrame(header, 0, 44, data); + } + + ContinuationFrame continuationFrame(List data, + {bool fragmented: false, + int streamId: 1}) { + int flags = fragmented ? 0 : ContinuationFrame.FLAG_END_HEADERS; + var header = new FrameHeader( + data.length, FrameType.CONTINUATION, flags, streamId); + return new ContinuationFrame(header, data); + } + + test('unknown-frame', () { + var defrag = new FrameDefragmenter(); + expect(defrag.tryDefragmentFrame(unknownFrame()) is UnknownFrame, true); + }); + + test('fragmented-headers-frame', () { + var defrag = new FrameDefragmenter(); + + var f1 = headersFrame([1, 2, 3], fragmented: true); + var f2 = continuationFrame([4, 5, 6], fragmented: true); + var f3 = continuationFrame([7, 8, 9], fragmented: false); + + expect(defrag.tryDefragmentFrame(f1), isNull); + expect(defrag.tryDefragmentFrame(f2), isNull); + HeadersFrame h = defrag.tryDefragmentFrame(f3); + expect(h.hasEndHeadersFlag, isTrue); + expect(h.hasEndStreamFlag, isFalse); + expect(h.hasPaddedFlag, isFalse); + expect(h.padLength, 0); + expect(h.headerBlockFragment, [1, 2, 3, 4, 5, 6, 7, 8, 9]); + }); + + test('fragmented-push-promise-frame', () { + var defrag = new FrameDefragmenter(); + + var f1 = pushPromiseFrame([1, 2, 3], fragmented: true); + var f2 = continuationFrame([4, 5, 6], fragmented: true); + var f3 = continuationFrame([7, 8, 9], fragmented: false); + + expect(defrag.tryDefragmentFrame(f1), isNull); + expect(defrag.tryDefragmentFrame(f2), isNull); + PushPromiseFrame h = defrag.tryDefragmentFrame(f3); + expect(h.hasEndHeadersFlag, isTrue); + expect(h.hasPaddedFlag, isFalse); + expect(h.padLength, 0); + expect(h.headerBlockFragment, [1, 2, 3, 4, 5, 6, 7, 8, 9]); + }); + + test('fragmented-headers-frame--wrong-id', () { + var defrag = new FrameDefragmenter(); + + var f1 = headersFrame([1, 2, 3], fragmented: true, streamId: 1); + var f2 = continuationFrame([4, 5, 6], fragmented: true, streamId: 2); + + expect(defrag.tryDefragmentFrame(f1), isNull); + expect(() => defrag.tryDefragmentFrame(f2), + throwsProtocolException); + }); + + test('fragmented-push-promise-frame', () { + var defrag = new FrameDefragmenter(); + + var f1 = pushPromiseFrame([1, 2, 3], fragmented: true, streamId: 1); + var f2 = continuationFrame([4, 5, 6], fragmented: true, streamId: 2); + + expect(defrag.tryDefragmentFrame(f1), isNull); + expect(() => defrag.tryDefragmentFrame(f2), + throwsProtocolException); + }); + + test('fragmented-headers-frame--no-continuation-frame', () { + var defrag = new FrameDefragmenter(); + + var f1 = headersFrame([1, 2, 3], fragmented: true); + var f2 = unknownFrame(); + + expect(defrag.tryDefragmentFrame(f1), isNull); + expect(() => defrag.tryDefragmentFrame(f2), + throwsProtocolException); + }); + + test('fragmented-push-promise-no-continuation-frame', () { + var defrag = new FrameDefragmenter(); + + var f1 = pushPromiseFrame([1, 2, 3], fragmented: true); + var f2 = unknownFrame(); + + expect(defrag.tryDefragmentFrame(f1), isNull); + expect(() => defrag.tryDefragmentFrame(f2), + throwsProtocolException); + }); + + test('push-without-headres-or-push-promise-frame', () { + var defrag = new FrameDefragmenter(); + + var f1 = continuationFrame([4, 5, 6], fragmented: true, streamId: 1); + expect(defrag.tryDefragmentFrame(f1), equals(f1)); + }); + }); + }); +} + + +/// A matcher for HuffmannDecodingExceptions. +const Matcher isProtocolException = const _ProtocolException(); + +class _ProtocolException extends TypeMatcher { + const _ProtocolException() : super("ProtocolException"); + bool matches(item, Map matchState) => item is ProtocolException; +} + +const Matcher throwsProtocolException = + const Throws(isProtocolException); + From cf437b9a6a57e9d130baab32648ba87ee8e06ccb Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Mon, 13 Apr 2015 12:51:03 +0200 Subject: [PATCH 009/172] Handler: Settings handler, tests for FrameReader of settings frames R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/182827013 --- pkgs/http2/lib/src/frames/frame_reader.dart | 25 ++- pkgs/http2/lib/src/frames/frame_writer.dart | 17 +- pkgs/http2/lib/src/frames/frames.dart | 1 + pkgs/http2/lib/src/settings/settings.dart | 151 +++++++++++++++--- pkgs/http2/lib/src/sync_errors.dart | 22 ++- pkgs/http2/test/src/error_matchers.dart | 30 ++++ .../src/frames/frame_defragmenter_test.dart | 16 +- .../src/frames/frame_writer_reader_test.dart | 21 ++- pkgs/http2/test/src/mock_utils.dart | 91 +++++++++++ .../src/settings/settings_handler_test.dart | 99 ++++++++++++ 10 files changed, 413 insertions(+), 60 deletions(-) create mode 100644 pkgs/http2/test/src/error_matchers.dart create mode 100644 pkgs/http2/test/src/mock_utils.dart create mode 100644 pkgs/http2/test/src/settings/settings_handler_test.dart diff --git a/pkgs/http2/lib/src/frames/frame_reader.dart b/pkgs/http2/lib/src/frames/frame_reader.dart index aa4f534dcf..c362452706 100644 --- a/pkgs/http2/lib/src/frames/frame_reader.dart +++ b/pkgs/http2/lib/src/frames/frame_reader.dart @@ -4,15 +4,6 @@ part of http2.src.frames; -/// Exception raised due to invalid frame encoding. -class FrameEncodingException implements Exception { - final String _message; - - FrameEncodingException(this._message); - - String toString() => 'FrameEncodingException: $_message'; -} - /// Used for converting a `Stream>` to a `Stream`. class FrameReader { final Stream> _inputStream; @@ -88,7 +79,7 @@ class FrameReader { } if (header != null) { if (header.length > _localSettings.maxFrameSize) { - terminateWithError(new FrameEncodingException( + terminateWithError(new FrameSizeException( 'Incoming frame is too big.')); return; } @@ -116,7 +107,7 @@ class FrameReader { if (bufferedLength == 0) { _framesController.close(); } else { - terminateWithError(new FrameEncodingException( + terminateWithError(new FrameSizeException( 'Incoming byte stream ended with incomplete frame')); } }); @@ -233,7 +224,13 @@ class FrameReader { int value = _readInt32(bytes, offset + 6 * i + 2); settings[i] = new Setting(identifier, value); } - return new SettingsFrame(header, settings); + var frame = new SettingsFrame(header, settings); + if (frame.hasAckFlag) { + _checkFrameLengthCondition( + header.length == 0, + message: 'Settings frame length must 0 for ACKs.'); + } + return frame; case FrameType.PUSH_PROMISE: int padLength = 0; @@ -283,12 +280,12 @@ class FrameReader { } } - /// Checks that [condition] is `true` and raises an [FrameEncodingException] + /// Checks that [condition] is `true` and raises an [FrameSizeException] /// otherwise. void _checkFrameLengthCondition(bool condition, {String message: 'Frame not long enough.'}) { if (!condition) { - throw new FrameEncodingException(message); + throw new FrameSizeException(message); } } } diff --git a/pkgs/http2/lib/src/frames/frame_writer.dart b/pkgs/http2/lib/src/frames/frame_writer.dart index 14f013a8e1..6597240ea6 100644 --- a/pkgs/http2/lib/src/frames/frame_writer.dart +++ b/pkgs/http2/lib/src/frames/frame_writer.dart @@ -161,9 +161,9 @@ class FrameWriter { _writeData(buffer); } - void writeSettingsFrame(List settings, {bool ack: false}) { + void writeSettingsFrame(List settings) { int type = FrameType.SETTINGS; - int flags = ack ? SettingsFrame.FLAG_ACK : 0; + int flags = 0; var buffer = new Uint8List(FRAME_HEADER_SIZE + 6 * settings.length); int offset = 0; @@ -180,6 +180,19 @@ class FrameWriter { _writeData(buffer); } + void writeSettingsAckFrame() { + int type = FrameType.SETTINGS; + int flags = SettingsFrame.FLAG_ACK; + + var buffer = new Uint8List(FRAME_HEADER_SIZE); + int offset = 0; + + _setFrameHeader(buffer, offset, type, flags, 0, 0); + offset += FRAME_HEADER_SIZE; + + _writeData(buffer); + } + void writePushPromiseFrame(int streamId, int promisedStreamId, List
headers) { var fragment = _hpackEncoder.encode(headers); diff --git a/pkgs/http2/lib/src/frames/frames.dart b/pkgs/http2/lib/src/frames/frames.dart index bc7ce718a8..40df8326e9 100644 --- a/pkgs/http2/lib/src/frames/frames.dart +++ b/pkgs/http2/lib/src/frames/frames.dart @@ -11,6 +11,7 @@ import 'dart:math' show max; import '../async_utils/async_utils.dart'; import '../hpack/hpack.dart'; import '../settings/settings.dart'; +import '../sync_errors.dart'; part "frame_types.dart"; part "frame_utils.dart"; diff --git a/pkgs/http2/lib/src/settings/settings.dart b/pkgs/http2/lib/src/settings/settings.dart index ab763db1e6..ac080ee28f 100644 --- a/pkgs/http2/lib/src/settings/settings.dart +++ b/pkgs/http2/lib/src/settings/settings.dart @@ -4,6 +4,11 @@ library http2.src.settings; +import 'dart:async'; + +import '../frames/frames.dart'; +import '../sync_errors.dart'; + /// The settings a remote peer can choose to set. class Settings { /// Allows the sender to inform the remote endpoint of the maximum size of the @@ -11,7 +16,7 @@ class Settings { /// encoder can select any size equal to or less than this value by using /// signaling specific to the header compression format inside a header block. /// The initial value is 4,096 octets. - final int headerTableSize; + int headerTableSize; /// This setting can be use to disable server push (Section 8.2). An endpoint /// MUST NOT send a PUSH_PROMISE frame if it receives this parameter set to a @@ -22,7 +27,7 @@ class Settings { /// The initial value is 1, which indicates that server push is permitted. /// Any value other than 0 or 1 MUST be treated as a connection error /// (Section 5.4.1) of type PROTOCOL_ERROR. - final bool enablePush; + bool enablePush; /// Indicates the maximum number of concurrent streams that the sender will /// allow. This limit is directional: it applies to the number of streams that @@ -36,7 +41,7 @@ class Settings { /// active streams. Servers SHOULD only set a zero value for short durations; /// if a server does not wish to accept requests, closing the connection is /// more appropriate. - final int maxConcurrentStreams; + int maxConcurrentStreams; /// Indicates the sender's initial window size (in octets) for stream level /// flow control. The initial value is 2^16-1 (65,535) octets. @@ -45,7 +50,7 @@ class Settings { /// streams, see Section 6.9.2. /// Values above the maximum flow control window size of 231-1 MUST be treated /// as a connection error (Section 5.4.1) of type FLOW_CONTROL_ERROR. - final int initialWindowSize; + int initialWindowSize; /// Indicates the size of the largest frame payload that the sender is willing /// to receive, in octets. @@ -55,7 +60,7 @@ class Settings { /// size (2^24-1 or 16,777,215 octets), inclusive. Values outside this range /// MUST be treated as a connection error (Section 5.4.1) of type /// PROTOCOL_ERROR. - final int maxFrameSize; + int maxFrameSize; /// This advisory setting informs a peer of the maximum size of header list /// that the sender is prepared to accept, in octets. The value is based on @@ -64,7 +69,7 @@ class Settings { /// /// For any given request, a lower limit than what is advertised MAY be /// enforced. The initial value of this setting is unlimited. - final int maxHeaderListSize; + int maxHeaderListSize; Settings({this.headerTableSize: 4096, this.enablePush: true, @@ -72,26 +77,122 @@ class Settings { this.initialWindowSize: (1 << 16) - 1, this.maxFrameSize: (1 << 14), this.maxHeaderListSize: null}); +} + + +/// Handles remote and local connection [Setting]s. +/// +/// Incoming [SettingFrame]s will be handled here to update the peer settings. +/// Changes to [_toBeAcknowledgedSettings] can be made, the peer will then be +/// notified of the setting changes it should use. +class SettingsHandler { + final FrameWriter _frameWriter; + + /// A list of outstanding setting changes. + final List> _toBeAcknowledgedSettings = []; + + /// A list of completers for outstanding setting changes. + final List _toBeAcknowledgedCompleters = []; + + /// The local settings, which the remote side ACKed to obey. + Settings _acknowledgedSettings = new Settings(); + + /// The peer settings, which we ACKed and are obeying. + Settings _peerSettings = new Settings(); + + SettingsHandler(this._frameWriter); + + /// The settings for this endpoint of the connection which the remote peer + /// has ACKed and uses. + Settings get acknowledgedSettings => _acknowledgedSettings; + + /// The settings for the remote endpoint of the connection which this + /// endpoint should use. + Settings get peerSettings => _peerSettings; + + /// Handles an incoming [SettingsFrame] which can be an ACK or a settings + /// change. + void handleSettingsFrame(SettingsFrame frame) { + assert (frame.header.streamId == 0); + + if (frame.hasAckFlag) { + assert (frame.header.length == 0); + + if (_toBeAcknowledgedSettings.isEmpty) { + // NOTE: The specification does not say anything about ACKed settings + // which were never sent to the other side. We consider this definitly + // an error. + throw new ProtocolException( + 'Received an acknowledged settings frame which did not have a ' + 'outstanding settings request.'); + } + List settingChanges = _toBeAcknowledgedSettings.removeAt(0); + Completer completer = _toBeAcknowledgedCompleters.removeAt(0); + _modifySettings(_acknowledgedSettings, settingChanges); + completer.complete(); + } else { + _modifySettings(_peerSettings, frame.settings); + _frameWriter.writeSettingsAckFrame(); + } + } + + Future changeSettings(List changes) { + // TODO: Have a timeout: When ACK doesn't get back in a reasonable time + // frame we should quit with ErrorCode.SETTINGS_TIMEOUT. + var completer = new Completer(); + _toBeAcknowledgedSettings.add(changes); + _toBeAcknowledgedCompleters.add(completer); + _frameWriter.writeSettingsFrame(changes); + return completer.future; + } + + void _modifySettings(Settings base, List changes) { + for (var setting in changes) { + switch (setting.identifier) { + case Setting.SETTINGS_ENABLE_PUSH: + if (setting.value == 0) { + base.enablePush = false; + } else if (setting.value == 1) { + base.enablePush = true; + } else { + throw new ProtocolException( + 'The push setting can be only set to 0 or 1.'); + } + break; + + case Setting.SETTINGS_HEADER_TABLE_SIZE: + // TODO: Propagate this signal to the HPackContext. + base.headerTableSize = setting.value; + break; + + case Setting.SETTINGS_MAX_HEADER_LIST_SIZE: + // TODO: Propagate this signal to the HPackContext. + base.maxHeaderListSize = setting.value; + break; + + case Setting.SETTINGS_MAX_CONCURRENT_STREAMS: + // NOTE: We will not force closing of existing streams if the limit is + // lower than the current number of open streams. But we will prevent + // new streams from being created if the number of existing streams + // is above this limit. + base.maxConcurrentStreams = setting.value; + break; + + case Setting.SETTINGS_INITIAL_WINDOW_SIZE: + if (setting.value < (1 << 31)) { + // TODO: + // var difference = setting.value - base.initialWindowSize; + // streamSet.processInitialWindowSizeSettingChange(difference); + base.initialWindowSize = setting.value; + } else { + throw new FlowControlException('Invalid initial window size.'); + } + break; - Settings replace({int headerTableSize, - bool enablePush, - int maxConcurrentStreams, - int initialWindowSize, - int maxFrameSize, - int maxHeaderListSize}) { - if (headerTableSize == null) headerTableSize = this.headerTableSize; - if (enablePush == null) enablePush = this.enablePush; - if (maxConcurrentStreams == null) { - maxConcurrentStreams = this.maxConcurrentStreams; + default: + // Spec says to ignore unkown settings. + break; + } } - if (initialWindowSize == null) initialWindowSize = this.initialWindowSize; - if (maxFrameSize == null) maxFrameSize = this.maxFrameSize; - if (maxHeaderListSize == null) maxHeaderListSize = this.maxHeaderListSize; - return new Settings(headerTableSize: headerTableSize, - enablePush: enablePush, - maxConcurrentStreams: maxConcurrentStreams, - initialWindowSize: initialWindowSize, - maxFrameSize: maxFrameSize, - maxHeaderListSize : maxHeaderListSize); } } diff --git a/pkgs/http2/lib/src/sync_errors.dart b/pkgs/http2/lib/src/sync_errors.dart index 06b7cb5ef4..eb758e1630 100644 --- a/pkgs/http2/lib/src/sync_errors.dart +++ b/pkgs/http2/lib/src/sync_errors.dart @@ -5,9 +5,25 @@ library http2.src.sync_errors; class ProtocolException implements Exception { - final String message; + final String _message; - ProtocolException(this.message); + ProtocolException(this._message); - String toString() => 'ProtocolError: $message'; + String toString() => 'ProtocolError: $_message'; +} + +class FlowControlException implements Exception { + final String _message; + + FlowControlException(this._message); + + String toString() => 'FlowControlException: $_message'; +} + +class FrameSizeException implements Exception { + final String _message; + + FrameSizeException(this._message); + + String toString() => 'FrameSizeException: $_message'; } diff --git a/pkgs/http2/test/src/error_matchers.dart b/pkgs/http2/test/src/error_matchers.dart new file mode 100644 index 0000000000..2b9588bf11 --- /dev/null +++ b/pkgs/http2/test/src/error_matchers.dart @@ -0,0 +1,30 @@ +// 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. + +library http2.test.error_matchers; + +import 'package:unittest/unittest.dart'; +import 'package:http2/src/sync_errors.dart'; + +const Matcher isProtocolException = const _ProtocolException(); + +class _ProtocolException extends TypeMatcher { + const _ProtocolException() : super("ProtocolException"); + bool matches(item, Map matchState) => item is ProtocolException; +} + +const Matcher throwsProtocolException = + const Throws(isProtocolException); + + +const Matcher isFrameSizeException = const _FrameSizeException(); + +class _FrameSizeException extends TypeMatcher { + const _FrameSizeException() : super("FrameSizeException"); + bool matches(item, Map matchState) => item is FrameSizeException; +} + +const Matcher throwsFrameSizeException = + const Throws(isFrameSizeException); + diff --git a/pkgs/http2/test/src/frames/frame_defragmenter_test.dart b/pkgs/http2/test/src/frames/frame_defragmenter_test.dart index db12e12d79..424f0e254b 100644 --- a/pkgs/http2/test/src/frames/frame_defragmenter_test.dart +++ b/pkgs/http2/test/src/frames/frame_defragmenter_test.dart @@ -4,10 +4,11 @@ import 'package:unittest/unittest.dart'; -import 'package:http2/src/sync_errors.dart'; import 'package:http2/src/frames/frames.dart'; import 'package:http2/src/frames/frame_defragmenter.dart'; +import '../error_matchers.dart'; + main() { group('frames', () { group('frame-defragmenter', () { @@ -132,16 +133,3 @@ main() { }); }); } - - -/// A matcher for HuffmannDecodingExceptions. -const Matcher isProtocolException = const _ProtocolException(); - -class _ProtocolException extends TypeMatcher { - const _ProtocolException() : super("ProtocolException"); - bool matches(item, Map matchState) => item is ProtocolException; -} - -const Matcher throwsProtocolException = - const Throws(isProtocolException); - diff --git a/pkgs/http2/test/src/frames/frame_writer_reader_test.dart b/pkgs/http2/test/src/frames/frame_writer_reader_test.dart index a8bd4e2df6..782cd0bd1f 100644 --- a/pkgs/http2/test/src/frames/frame_writer_reader_test.dart +++ b/pkgs/http2/test/src/frames/frame_writer_reader_test.dart @@ -12,6 +12,8 @@ import 'package:http2/src/settings/settings.dart'; import '../hpack/hpack_test.dart' show isHeader; +import '../error_matchers.dart'; + main() { group('frames', () { group('writer-reader', () { @@ -92,14 +94,14 @@ main() { FrameReader reader, HPackDecoder decoder) async { writer.writeSettingsFrame( - [new Setting(Setting.SETTINGS_ENABLE_PUSH, 1)], ack: true); + [new Setting(Setting.SETTINGS_ENABLE_PUSH, 1)]); var frames = await finishWriting(writer, reader); expect(frames.length, 1); expect(frames[0] is SettingsFrame, isTrue); SettingsFrame settingsFrame = frames[0]; - expect(settingsFrame.hasAckFlag, true); + expect(settingsFrame.hasAckFlag, false); expect(settingsFrame.header.streamId, 0); expect(settingsFrame.settings.length, 1); expect(settingsFrame.settings[0].identifier, @@ -107,6 +109,21 @@ main() { expect(settingsFrame.settings[0].value, 1); }); + writerReaderTest('settings-frame-ack', (FrameWriter writer, + FrameReader reader, + HPackDecoder decoder) async { + writer.writeSettingsAckFrame(); + + var frames = await finishWriting(writer, reader); + expect(frames.length, 1); + expect(frames[0] is SettingsFrame, isTrue); + + SettingsFrame settingsFrame = frames[0]; + expect(settingsFrame.hasAckFlag, true); + expect(settingsFrame.header.streamId, 0); + expect(settingsFrame.settings.length, 0); + }); + writerReaderTest('push-promise-frame', (FrameWriter writer, FrameReader reader, HPackDecoder decoder) async { diff --git a/pkgs/http2/test/src/mock_utils.dart b/pkgs/http2/test/src/mock_utils.dart new file mode 100644 index 0000000000..f4e1353c67 --- /dev/null +++ b/pkgs/http2/test/src/mock_utils.dart @@ -0,0 +1,91 @@ +// 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. + +library http2.test.mock_utils; + +import 'dart:mirrors'; + +import 'package:unittest/unittest.dart'; + +/// Used for mocking arbitrary classes. +/// +/// Usage happens in two steps: +/// a) Make a subclass which implements a certain interface, e.g. +/// class FrameWriterMock extends SmartMock implements FrameWriter { +/// dynamic noSuchMethod(_) => super.noSuchMethod(_); +/// } +/// b) Register method mocks with e.g. +/// var writer = new FrameWriterMock(); +/// writer.mock_writeSettingsFrame = (List settings, +/// {bool ack: true}) { +/// // Assert settings/ack & return maybe value. +/// } +/// var settingsHandler = new SettingsHandler(writer); +/// +/// // This will trigger a call on [writer] to the mocked method. +/// settingsHandler.changeSettings([]); +/// +/// a) should guarantee that we do not get any checked-mode exceptions. +/// b) allows one to pass the mock into functions/other objects and mock +/// methods. +/// +/// NOTE: If method signatures change, the test code must be changed and +/// the analyzer will not give any warnings if this is not done. +@proxy +class SmartMock { + final Map _registeredMethods = {}; + + dynamic noSuchMethod(Invocation invocation) { + var name = MirrorSystem.getName(invocation.memberName); + var positional = invocation.positionalArguments; + var named = invocation.namedArguments; + + handleCall() { + var function = _registeredMethods[name]; + if (function == null) { + throw new Exception('No mock registered for setter "$name".'); + } + return Function.apply(function, positional, named); + } + + handleRegistration(String name) { + if (positional[0] == null) { + _registeredMethods.remove(name); + } else { + _registeredMethods[name] = positional[0]; + } + } + + if (invocation.isSetter) { + if (name.startsWith('mock_')) { + name = name.substring('mock_'.length, name.length - 1); + return handleRegistration(name); + } else { + handleCall(); + } + } else { + handleCall(); + } + } +} + +/// This is a helper class for working around issues where `expectAsync` +/// cannot be used. +/// +/// For example, expectAsync is not capable for wrapping functions with named +/// arguments +/// (e.g. `expectAsync((List settings, {bool ack: true}) {})`). +class TestCounter { + final Function _complete = expectAsync(() {}); + + final int count; + int _got = 0; + + TestCounter({this.count: 1}); + + void got() { + _got++; + if (_got == count) _complete(); + } +} diff --git a/pkgs/http2/test/src/settings/settings_handler_test.dart b/pkgs/http2/test/src/settings/settings_handler_test.dart new file mode 100644 index 0000000000..dc8ae5d83b --- /dev/null +++ b/pkgs/http2/test/src/settings/settings_handler_test.dart @@ -0,0 +1,99 @@ +// 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:unittest/unittest.dart'; + +import 'package:http2/src/frames/frames.dart'; +import 'package:http2/src/settings/settings.dart'; + +import '../error_matchers.dart'; +import '../mock_utils.dart'; + +main() { + group('settings-handler', () { + var pushSettings = [new Setting(Setting.SETTINGS_ENABLE_PUSH, 0)]; + var invalidPushSettings = [new Setting(Setting.SETTINGS_ENABLE_PUSH, 2)]; + + test('successful-setting', () async { + var writer = new FrameWriterMock(); + var sh = new SettingsHandler(writer); + var tc = new TestCounter(); + + writer.mock_writeSettingsFrame = (List s, {bool ack: false}) { + expect(s, pushSettings); + expect(ack, false); + tc.got(); + }; + + // Start changing settings. + Future changed = sh.changeSettings(pushSettings); + + // Check that settings haven't been applied. + expect(sh.acknowledgedSettings.enablePush, true); + + // Simulate remote end to responsd with an ACK. + var header = new FrameHeader( + 0, FrameType.SETTINGS, SettingsFrame.FLAG_ACK, 0); + sh.handleSettingsFrame(new SettingsFrame(header, [])); + + await changed; + + // Check that settings have been applied. + expect(sh.acknowledgedSettings.enablePush, false); + }); + + test('ack-remote-settings-change', () { + var writer = new FrameWriterMock(); + var sh = new SettingsHandler(writer); + var tc = new TestCounter(); + + writer.mock_writeSettingsAckFrame = () { + tc.got(); + }; + + // Check that settings haven't been applied. + expect(sh.peerSettings.enablePush, true); + + // Simulate remote end by setting the push setting. + var header = new FrameHeader(6, FrameType.SETTINGS, 0, 0); + sh.handleSettingsFrame(new SettingsFrame(header, pushSettings)); + + // Check that settings have been applied. + expect(sh.peerSettings.enablePush, false); + }); + + test('invalid-remote-ack', () { + var writer = new FrameWriterMock(); + var sh = new SettingsHandler(writer); + + // Simulates ACK even though we haven't sent any settings. + var header = new FrameHeader( + 0, FrameType.SETTINGS, SettingsFrame.FLAG_ACK, 0); + var settingsFrame = new SettingsFrame(header, const []); + + expect(() => sh.handleSettingsFrame(settingsFrame), + throwsProtocolException); + }); + + test('invalid-remote-settings-change', () { + var writer = new FrameWriterMock(); + var sh = new SettingsHandler(writer); + + // Check that settings haven't been applied. + expect(sh.peerSettings.enablePush, true); + + // Simulate remote end by setting the push setting. + var header = new FrameHeader(6, FrameType.SETTINGS, 0, 0); + var settingsFrame = new SettingsFrame(header, invalidPushSettings); + expect(() => sh.handleSettingsFrame(settingsFrame), + throwsProtocolException); + }); + }); +} + +class FrameWriterMock extends SmartMock implements FrameWriter { + dynamic noSuchMethod(_) => super.noSuchMethod(_); +} From 4896b6bf4c21564a54fa49a7828babd5fae49123 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Mon, 13 Apr 2015 12:57:39 +0200 Subject: [PATCH 010/172] Handler: Implement PingHandler R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/184637013 --- pkgs/http2/lib/src/error_handler.dart | 24 ++++ pkgs/http2/lib/src/ping/ping_handler.dart | 70 +++++++++++ pkgs/http2/lib/src/sync_errors.dart | 4 + pkgs/http2/test/src/error_matchers.dart | 11 ++ .../test/src/ping/ping_handler_test.dart | 109 ++++++++++++++++++ 5 files changed, 218 insertions(+) create mode 100644 pkgs/http2/lib/src/error_handler.dart create mode 100644 pkgs/http2/lib/src/ping/ping_handler.dart create mode 100644 pkgs/http2/test/src/ping/ping_handler_test.dart diff --git a/pkgs/http2/lib/src/error_handler.dart b/pkgs/http2/lib/src/error_handler.dart new file mode 100644 index 0000000000..285fe240a2 --- /dev/null +++ b/pkgs/http2/lib/src/error_handler.dart @@ -0,0 +1,24 @@ +// 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. + +library http2.src.error_handler; + +/// Used by classes which may be terminated from the outside. +class TerminatableMixin { + bool _terminated = false; + + /// Terminates this stream message queue. Further operations on it will fail. + void terminate([error]) { + if (!wasTerminated) { + _terminated = true; + onTerminated(error); + } + } + + bool get wasTerminated => _terminated; + + void onTerminated(error) { + // Subclasses can override this method if they want. + } +} diff --git a/pkgs/http2/lib/src/ping/ping_handler.dart b/pkgs/http2/lib/src/ping/ping_handler.dart new file mode 100644 index 0000000000..7f5337d53f --- /dev/null +++ b/pkgs/http2/lib/src/ping/ping_handler.dart @@ -0,0 +1,70 @@ +// 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. + +library http2.src.ping.ping_handler; + +import 'dart:async'; + +import '../frames/frames.dart'; +import '../error_handler.dart'; +import '../sync_errors.dart'; + +/// Responsible for pinging the other end and for handling pings from the +/// other end. +// TODO: We currently write unconditionally to the [FrameWriter]: we might want +// to consider be more aware what [Framewriter.bufferIndicator.wouldBuffer] +// says. +class PingHandler extends Object with TerminatableMixin { + final FrameWriter _frameWriter; + final Map _remainingPings = {}; + int _nextId = 1; + + PingHandler(this._frameWriter); + + void onTerminated(error) { + var values = _remainingPings.values.toList(); + _remainingPings.clear(); + values.forEach((Completer c) => c.completeError(error)); + } + + void processPingFrame(PingFrame frame) { + if (wasTerminated) { + throw new TerminatedException(); + } + + if (frame.header.streamId != 0) { + throw new ProtocolException('Ping frames must have a stream id of 0.'); + } + + if (!frame.hasAckFlag) { + _frameWriter.writePingFrame(frame.opaqueData, ack: true); + } else { + Completer c = _remainingPings.remove(frame.opaqueData); + if (c != null) { + c.complete(); + } else { + // NOTE: It is not specified what happens when one gets an ACK for a + // ping we never sent. We be very strict and fail in this case. + throw new ProtocolException( + 'Received ping ack with unknown opaque data.'); + } + } + } + + Future ping() { + Completer c = new Completer(); + + if (wasTerminated) { + c.completeError(new TerminatedException()); + return c.future; + } + + var id = _nextId++; + _remainingPings[id] = c; + + _frameWriter.writePingFrame(id); + + return c.future; + } +} diff --git a/pkgs/http2/lib/src/sync_errors.dart b/pkgs/http2/lib/src/sync_errors.dart index eb758e1630..063e801892 100644 --- a/pkgs/http2/lib/src/sync_errors.dart +++ b/pkgs/http2/lib/src/sync_errors.dart @@ -27,3 +27,7 @@ class FrameSizeException implements Exception { String toString() => 'FrameSizeException: $_message'; } + +class TerminatedException implements Exception { + String toString() => 'TerminatedException: The object has been terminated.'; +} diff --git a/pkgs/http2/test/src/error_matchers.dart b/pkgs/http2/test/src/error_matchers.dart index 2b9588bf11..915c385666 100644 --- a/pkgs/http2/test/src/error_matchers.dart +++ b/pkgs/http2/test/src/error_matchers.dart @@ -28,3 +28,14 @@ class _FrameSizeException extends TypeMatcher { const Matcher throwsFrameSizeException = const Throws(isFrameSizeException); + +const Matcher isTerminatedException = const _TerminatedException(); + +class _TerminatedException extends TypeMatcher { + const _TerminatedException() : super("TerminatedException"); + bool matches(item, Map matchState) => item is TerminatedException; +} + +const Matcher throwsTerminatedException = + const Throws(isTerminatedException); + diff --git a/pkgs/http2/test/src/ping/ping_handler_test.dart b/pkgs/http2/test/src/ping/ping_handler_test.dart new file mode 100644 index 0000000000..b075f8bd26 --- /dev/null +++ b/pkgs/http2/test/src/ping/ping_handler_test.dart @@ -0,0 +1,109 @@ +// 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:unittest/unittest.dart'; + +import 'package:http2/src/frames/frames.dart'; +import 'package:http2/src/ping/ping_handler.dart'; + +import '../error_matchers.dart'; +import '../mock_utils.dart'; + +main() { + group('ping-handler', () { + test('successful-ping', () async { + var writer = new FrameWriterMock(); + var pingHandler = new PingHandler(writer); + var tc = new TestCounter(count: 2); + + int pingCount = 1; + writer.mock_writePingFrame = (int opaqueData, {bool ack: false}) { + expect(opaqueData, pingCount); + expect(ack, false); + pingCount++; + tc.got(); + }; + + Future p1 = pingHandler.ping(); + Future p2 = pingHandler.ping(); + + var header = new FrameHeader(8, FrameType.PING, PingFrame.FLAG_ACK, 0); + pingHandler.processPingFrame(new PingFrame(header, 1)); + var header2 = new FrameHeader(8, FrameType.PING, PingFrame.FLAG_ACK, 0); + pingHandler.processPingFrame(new PingFrame(header2, 2)); + + await p1; + await p2; + }); + + test('successful-ack-to-remote-ping', () async { + var writer = new FrameWriterMock(); + var pingHandler = new PingHandler(writer); + var tc = new TestCounter(count: 2); + + int pingCount = 1; + writer.mock_writePingFrame = (int opaqueData, {bool ack: false}) { + expect(opaqueData, pingCount); + expect(ack, true); + pingCount++; + tc.got(); + }; + + var header = new FrameHeader(8, FrameType.PING, 0, 0); + pingHandler.processPingFrame(new PingFrame(header, 1)); + var header2 = new FrameHeader(8, FrameType.PING, 0, 0); + pingHandler.processPingFrame(new PingFrame(header2, 2)); + }); + + test('ping-unknown-opaque-data', () async { + var writer = new FrameWriterMock(); + var pingHandler = new PingHandler(writer); + var tc = new TestCounter(); + + writer.mock_writePingFrame = (int opaqueData, {bool ack: false}) { + expect(opaqueData, 1); + tc.got(); + }; + + Future future = pingHandler.ping(); + + var header = new FrameHeader(8, FrameType.PING, PingFrame.FLAG_ACK, 0); + expect(() => pingHandler.processPingFrame(new PingFrame(header, 2)), + throwsProtocolException); + + // Ensure outstanding pings will be completed with an error once we call + // `pingHandler.terminate()`. + future.catchError(expectAsync((error, _) { + expect(error, 'hello world'); + })); + pingHandler.terminate('hello world'); + }); + + test('terminate-ping-handler', () async { + var writer = new FrameWriterMock(); + var pingHandler = new PingHandler(writer); + + pingHandler.terminate('hello world'); + expect(() => pingHandler.processPingFrame(null), + throwsTerminatedException); + expect(pingHandler.ping(), + throwsTerminatedException); + }); + + test('ping-non-zero-stream-id', () async { + var writer = new FrameWriterMock(); + var pingHandler = new PingHandler(writer); + + var header = new FrameHeader(8, FrameType.PING, PingFrame.FLAG_ACK, 1); + expect(() => pingHandler.processPingFrame(new PingFrame(header, 1)), + throwsProtocolException); + }); + }); +} + +class FrameWriterMock extends SmartMock implements FrameWriter { + dynamic noSuchMethod(_) => super.noSuchMethod(_); +} From 4d976b3cdf05373dc15bf8711c8ea1cb800266d4 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Wed, 15 Apr 2015 13:15:44 +0200 Subject: [PATCH 011/172] Introduce ensureNotTerminated{Sync,Async} functions, simplify settings initialization in frame writer R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/187717013 --- pkgs/http2/lib/src/error_handler.dart | 18 +++++ pkgs/http2/lib/src/frames/frame_writer.dart | 14 ++-- pkgs/http2/lib/src/ping/ping_handler.dart | 52 ++++++-------- pkgs/http2/lib/src/settings/settings.dart | 71 +++++++++++-------- .../src/frames/frame_writer_reader_test.dart | 6 +- .../test/src/frames/frame_writer_test.dart | 6 +- .../src/settings/settings_handler_test.dart | 8 +-- 7 files changed, 92 insertions(+), 83 deletions(-) diff --git a/pkgs/http2/lib/src/error_handler.dart b/pkgs/http2/lib/src/error_handler.dart index 285fe240a2..ead709841c 100644 --- a/pkgs/http2/lib/src/error_handler.dart +++ b/pkgs/http2/lib/src/error_handler.dart @@ -4,6 +4,10 @@ library http2.src.error_handler; +import 'dart:async'; + +import 'sync_errors.dart'; + /// Used by classes which may be terminated from the outside. class TerminatableMixin { bool _terminated = false; @@ -21,4 +25,18 @@ class TerminatableMixin { void onTerminated(error) { // Subclasses can override this method if they want. } + + dynamic ensureNotTerminatedSync(f()) { + if (wasTerminated) { + throw new TerminatedException(); + } + return f(); + } + + Future ensureNotTerminatedAsync(Future f()) { + if (wasTerminated) { + return new Future.error(new TerminatedException()); + } + return f(); + } } diff --git a/pkgs/http2/lib/src/frames/frame_writer.dart b/pkgs/http2/lib/src/frames/frame_writer.dart index 6597240ea6..72e5aa95c8 100644 --- a/pkgs/http2/lib/src/frames/frame_writer.dart +++ b/pkgs/http2/lib/src/frames/frame_writer.dart @@ -16,7 +16,7 @@ class FrameWriter { final BufferedBytesWriter _outWriter; /// Connection settings which this writer needs to respect. - Settings _peerSettings; + final Settings _peerSettings; /// This is the maximum over all stream id's we've written to the underlying /// sink. @@ -25,17 +25,11 @@ class FrameWriter { /// Whether this [FrameWriter] is closed. bool _isClosed = false; - FrameWriter(this._hpackEncoder, StreamSink> outgoing) + FrameWriter(this._hpackEncoder, + StreamSink> outgoing, + this._peerSettings) : _outWriter = new BufferedBytesWriter(outgoing); - /// Used for initializing [FrameWriter]. - /// - /// This is necessary because of circular dependencies between - /// [SettingsHandler] and [FrameWriter]. - void initialize(Settings settings) { - _peerSettings = settings; - } - /// A indicator whether writes would be buffered. BufferIndicator get bufferIndicator => _outWriter.bufferIndicator; diff --git a/pkgs/http2/lib/src/ping/ping_handler.dart b/pkgs/http2/lib/src/ping/ping_handler.dart index 7f5337d53f..5a35bea7f4 100644 --- a/pkgs/http2/lib/src/ping/ping_handler.dart +++ b/pkgs/http2/lib/src/ping/ping_handler.dart @@ -29,42 +29,34 @@ class PingHandler extends Object with TerminatableMixin { } void processPingFrame(PingFrame frame) { - if (wasTerminated) { - throw new TerminatedException(); - } - - if (frame.header.streamId != 0) { - throw new ProtocolException('Ping frames must have a stream id of 0.'); - } + ensureNotTerminatedSync(() { + if (frame.header.streamId != 0) { + throw new ProtocolException('Ping frames must have a stream id of 0.'); + } - if (!frame.hasAckFlag) { - _frameWriter.writePingFrame(frame.opaqueData, ack: true); - } else { - Completer c = _remainingPings.remove(frame.opaqueData); - if (c != null) { - c.complete(); + if (!frame.hasAckFlag) { + _frameWriter.writePingFrame(frame.opaqueData, ack: true); } else { - // NOTE: It is not specified what happens when one gets an ACK for a - // ping we never sent. We be very strict and fail in this case. - throw new ProtocolException( - 'Received ping ack with unknown opaque data.'); + Completer c = _remainingPings.remove(frame.opaqueData); + if (c != null) { + c.complete(); + } else { + // NOTE: It is not specified what happens when one gets an ACK for a + // ping we never sent. We be very strict and fail in this case. + throw new ProtocolException( + 'Received ping ack with unknown opaque data.'); + } } - } + }); } Future ping() { - Completer c = new Completer(); - - if (wasTerminated) { - c.completeError(new TerminatedException()); + return ensureNotTerminatedAsync(() { + Completer c = new Completer(); + var id = _nextId++; + _remainingPings[id] = c; + _frameWriter.writePingFrame(id); return c.future; - } - - var id = _nextId++; - _remainingPings[id] = c; - - _frameWriter.writePingFrame(id); - - return c.future; + }); } } diff --git a/pkgs/http2/lib/src/settings/settings.dart b/pkgs/http2/lib/src/settings/settings.dart index ac080ee28f..86e5c5d748 100644 --- a/pkgs/http2/lib/src/settings/settings.dart +++ b/pkgs/http2/lib/src/settings/settings.dart @@ -8,6 +8,7 @@ import 'dart:async'; import '../frames/frames.dart'; import '../sync_errors.dart'; +import '../error_handler.dart'; /// The settings a remote peer can choose to set. class Settings { @@ -85,7 +86,7 @@ class Settings { /// Incoming [SettingFrame]s will be handled here to update the peer settings. /// Changes to [_toBeAcknowledgedSettings] can be made, the peer will then be /// notified of the setting changes it should use. -class SettingsHandler { +class SettingsHandler extends Object with TerminatableMixin { final FrameWriter _frameWriter; /// A list of outstanding setting changes. @@ -100,7 +101,9 @@ class SettingsHandler { /// The peer settings, which we ACKed and are obeying. Settings _peerSettings = new Settings(); - SettingsHandler(this._frameWriter); + SettingsHandler(this._frameWriter, + this._acknowledgedSettings, + this._peerSettings); /// The settings for this endpoint of the connection which the remote peer /// has ACKed and uses. @@ -113,37 +116,47 @@ class SettingsHandler { /// Handles an incoming [SettingsFrame] which can be an ACK or a settings /// change. void handleSettingsFrame(SettingsFrame frame) { - assert (frame.header.streamId == 0); - - if (frame.hasAckFlag) { - assert (frame.header.length == 0); - - if (_toBeAcknowledgedSettings.isEmpty) { - // NOTE: The specification does not say anything about ACKed settings - // which were never sent to the other side. We consider this definitly - // an error. - throw new ProtocolException( - 'Received an acknowledged settings frame which did not have a ' - 'outstanding settings request.'); + ensureNotTerminatedSync(() { + assert (frame.header.streamId == 0); + + if (frame.hasAckFlag) { + assert (frame.header.length == 0); + + if (_toBeAcknowledgedSettings.isEmpty) { + // NOTE: The specification does not say anything about ACKed settings + // which were never sent to the other side. We consider this definitly + // an error. + throw new ProtocolException( + 'Received an acknowledged settings frame which did not have a ' + 'outstanding settings request.'); + } + List settingChanges = _toBeAcknowledgedSettings.removeAt(0); + Completer completer = _toBeAcknowledgedCompleters.removeAt(0); + _modifySettings(_acknowledgedSettings, settingChanges); + completer.complete(); + } else { + _modifySettings(_peerSettings, frame.settings); + _frameWriter.writeSettingsAckFrame(); } - List settingChanges = _toBeAcknowledgedSettings.removeAt(0); - Completer completer = _toBeAcknowledgedCompleters.removeAt(0); - _modifySettings(_acknowledgedSettings, settingChanges); - completer.complete(); - } else { - _modifySettings(_peerSettings, frame.settings); - _frameWriter.writeSettingsAckFrame(); - } + }); + } + + void onTerminated(error) { + _toBeAcknowledgedSettings.clear(); + _toBeAcknowledgedCompleters.forEach( + (Completer c) => c.completeError(error)); } Future changeSettings(List changes) { - // TODO: Have a timeout: When ACK doesn't get back in a reasonable time - // frame we should quit with ErrorCode.SETTINGS_TIMEOUT. - var completer = new Completer(); - _toBeAcknowledgedSettings.add(changes); - _toBeAcknowledgedCompleters.add(completer); - _frameWriter.writeSettingsFrame(changes); - return completer.future; + return ensureNotTerminatedAsync(() { + // TODO: Have a timeout: When ACK doesn't get back in a reasonable time + // frame we should quit with ErrorCode.SETTINGS_TIMEOUT. + var completer = new Completer(); + _toBeAcknowledgedSettings.add(changes); + _toBeAcknowledgedCompleters.add(completer); + _frameWriter.writeSettingsFrame(changes); + return completer.future; + }); } void _modifySettings(Settings base, List changes) { diff --git a/pkgs/http2/test/src/frames/frame_writer_reader_test.dart b/pkgs/http2/test/src/frames/frame_writer_reader_test.dart index 782cd0bd1f..888c4fb4c0 100644 --- a/pkgs/http2/test/src/frames/frame_writer_reader_test.dart +++ b/pkgs/http2/test/src/frames/frame_writer_reader_test.dart @@ -232,14 +232,10 @@ main() { writerReaderTest(String name, func(writer, reader, decoder)) { test(name, () { var settings = new Settings(); - var context = new HPackContext(); var controller = new StreamController>(); - var writer = new FrameWriter(context.encoder, controller); + var writer = new FrameWriter(context.encoder, controller, settings); var reader = new FrameReader(controller.stream, settings); - - writer.initialize(settings); - return func(writer, reader, context.decoder); }); } diff --git a/pkgs/http2/test/src/frames/frame_writer_test.dart b/pkgs/http2/test/src/frames/frame_writer_test.dart index 2d98c11e06..f52a2a8084 100644 --- a/pkgs/http2/test/src/frames/frame_writer_test.dart +++ b/pkgs/http2/test/src/frames/frame_writer_test.dart @@ -10,8 +10,6 @@ import 'package:http2/src/hpack/hpack.dart'; import 'package:http2/src/frames/frames.dart'; import 'package:http2/src/settings/settings.dart'; -import '../hpack/hpack_test.dart' show isHeader; - main() { group('frames', () { group('frame-writer', () { @@ -19,9 +17,7 @@ main() { var settings = new Settings(); var context = new HPackContext(); var controller = new StreamController>(); - var reader = new FrameReader(controller.stream, settings); - var writer = new FrameWriter(context.encoder, controller); - writer.initialize(settings); + var writer = new FrameWriter(context.encoder, controller, settings); writer.doneFuture.then(expectAsync((_) { // We expect that the writer is done at this point. diff --git a/pkgs/http2/test/src/settings/settings_handler_test.dart b/pkgs/http2/test/src/settings/settings_handler_test.dart index dc8ae5d83b..2b9cc35891 100644 --- a/pkgs/http2/test/src/settings/settings_handler_test.dart +++ b/pkgs/http2/test/src/settings/settings_handler_test.dart @@ -19,7 +19,7 @@ main() { test('successful-setting', () async { var writer = new FrameWriterMock(); - var sh = new SettingsHandler(writer); + var sh = new SettingsHandler(writer, new Settings(), new Settings()); var tc = new TestCounter(); writer.mock_writeSettingsFrame = (List s, {bool ack: false}) { @@ -47,7 +47,7 @@ main() { test('ack-remote-settings-change', () { var writer = new FrameWriterMock(); - var sh = new SettingsHandler(writer); + var sh = new SettingsHandler(writer, new Settings(), new Settings()); var tc = new TestCounter(); writer.mock_writeSettingsAckFrame = () { @@ -67,7 +67,7 @@ main() { test('invalid-remote-ack', () { var writer = new FrameWriterMock(); - var sh = new SettingsHandler(writer); + var sh = new SettingsHandler(writer, new Settings(), new Settings()); // Simulates ACK even though we haven't sent any settings. var header = new FrameHeader( @@ -80,7 +80,7 @@ main() { test('invalid-remote-settings-change', () { var writer = new FrameWriterMock(); - var sh = new SettingsHandler(writer); + var sh = new SettingsHandler(writer, new Settings(), new Settings()); // Check that settings haven't been applied. expect(sh.peerSettings.enablePush, true); From bd59f26babd6a481c704b7da9ecf59329a5ad3d7 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Wed, 20 May 2015 14:27:58 +0200 Subject: [PATCH 012/172] Connection: Add basic Connection class for handling ping/settings frames R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/195537013 --- pkgs/http2/lib/src/connection.dart | 273 ++++++++++++++++++++++++++++ pkgs/http2/lib/transport.dart | 55 ++++++ pkgs/http2/test/transport_test.dart | 75 ++++++++ 3 files changed, 403 insertions(+) create mode 100644 pkgs/http2/lib/src/connection.dart create mode 100644 pkgs/http2/lib/transport.dart create mode 100644 pkgs/http2/test/transport_test.dart diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart new file mode 100644 index 0000000000..1300cbad8a --- /dev/null +++ b/pkgs/http2/lib/src/connection.dart @@ -0,0 +1,273 @@ +// 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. + +library http2.src.conn; + +import 'dart:async'; +import 'dart:math'; + +import '../transport.dart'; +import 'frames/frame_defragmenter.dart'; +import 'frames/frames.dart'; +import 'hpack/hpack.dart'; +import 'ping/ping_handler.dart'; +import 'settings/settings.dart'; +import 'sync_errors.dart'; + +import 'connection_preface.dart'; + +enum ConnectionState { + /// The connection has been established, we're waiting for the settings frame + /// of the remote end. + Initialized, + + /// The connection has been established and is fully operational. + Operational, + + /// The connection is no longer accepting new streams or creating new streams. + Finishing, + + /// The connection has been terminated and cannot be used anymore. + Terminated, +} + +class Connection implements TransportConnection { + /// The settings the other end has acknowledged to use when communicating with + /// us. + final Settings acknowledgedSettings = new Settings(); + + /// The settings we have to obey communicating with the other side. + final Settings peerSettings = new Settings(); + + /// Whether this connection is a client connection. + final bool isClientConnection; + + /// The HPack context for this connection. + final HPackContext _hpackContext = new HPackContext(); + + /// Used for defragmenting PushPromise/Header frames. + final FrameDefragmenter _defragmenter = new FrameDefragmenter(); + + /// The outgoing frames of this connection; + FrameWriter _frameWriter; + + /// A subscription of incoming [Frame]s. + StreamSubscription _frameReaderSubscription; + + /// The ping handler used for making pings & handling remote pings. + PingHandler _pingHandler; + + /// The settings handler used for changing settings & for handling remote + /// setting changes. + SettingsHandler _settingsHandler; + + /// Represents the highest stream id this connection has received from the + /// other side. + int _highestStreamIdReceived = 0; + + /// The state of this connection. + ConnectionState _state; + + factory Connection.client(Stream> incoming, + StreamSink> outgoing) { + outgoing.add(CONNECTION_PREFACE); + return new Connection(incoming, outgoing, isClientConnection: true); + } + + factory Connection.server(Stream> incoming, + StreamSink> outgoing) { + var frameBytes = readConnectionPreface(incoming); + return new Connection(frameBytes, outgoing, isClientConnection: false); + } + + Connection(Stream> incoming, + StreamSink> outgoing, + {this.isClientConnection: true}) { + _setupConnection(incoming, outgoing); + } + + /// Runs all setup necessary before new streams can be created with the remote + /// peer. + void _setupConnection(Stream> incoming, + StreamSink> outgoing) { + // Setup frame reading. + var incomingFrames = new FrameReader( + incoming, acknowledgedSettings).startDecoding(); + _frameReaderSubscription = incomingFrames.listen( + _handleFrame, + onError: (stack, error) { + _terminate(ErrorCode.CONNECT_ERROR, causedByTransportError: true); + }, onDone: () { + _terminate(ErrorCode.CONNECT_ERROR, causedByTransportError: true); + }); + + // Setup frame writing. + _frameWriter = new FrameWriter( + _hpackContext.encoder, outgoing, peerSettings); + _frameWriter.doneFuture.then((_) { + _terminate(ErrorCode.CONNECT_ERROR, causedByTransportError: true); + }).catchError((error, stack) { + _terminate(ErrorCode.CONNECT_ERROR, causedByTransportError: true); + }); + + // Setup handlers. + _settingsHandler = new SettingsHandler( + _frameWriter, acknowledgedSettings, peerSettings); + _pingHandler = new PingHandler(_frameWriter); + + // Do the initial settings handshake. + _settingsHandler.changeSettings([]).catchError((_) { + _terminate(ErrorCode.PROTOCOL_ERROR); + }); + + // NOTE: We're not waiting until initial settings have been exchanged + // before we start using the connection (i.e. we don't wait for half a + // round-trip-time). + _state = ConnectionState.Initialized; + } + + /// Pings the remote peer (can e.g. be used for measuring latency). + Future ping() { + return _pingHandler.ping().catchError((e, s) { + return new Future.error( + new TransportException('The connection has been terminated.')); + }, test: (e) => e is TerminatedException); + } + + /// Finishes this connection. + void finish() { + _finishing(active: true); + } + + /// Terminates this connection forcefully. + Future terminate() { + return _terminate(ErrorCode.NO_ERROR); + } + + /// Handles an incoming [Frame] from the underlying [FrameReader]. + void _handleFrame(Frame frame) { + // The first frame from the other side must be a [SettingsFrame], otherwise + // we terminate the connection. + if (_state == ConnectionState.Initialized) { + if (frame is! SettingsFrame) { + _terminate(ErrorCode.PROTOCOL_ERROR); + return; + } + _state = ConnectionState.Operational; + } + + // Try to decode frame if it is a Headers/PushPromise frame. + try { + frame = _defragmenter.tryDefragmentFrame(frame); + if (frame == null) return; + } on ProtocolException { + _terminate(ErrorCode.PROTOCOL_ERROR); + return; + } + + // Try to decode headers if it's a Headers/PushPromise frame. + // [This needs to be done even if the frames get ignored, since the entire + // connection shares one HPack compression context.] + try { + if (frame is HeadersFrame) { + frame.decodedHeaders = + _hpackContext.decoder.decode(frame.headerBlockFragment); + } else if (frame is PushPromiseFrame) { + frame.decodedHeaders = + _hpackContext.decoder.decode(frame.headerBlockFragment); + } + } on HPackDecodingException { + _terminate(ErrorCode.PROTOCOL_ERROR); + } + + // Update highest stream id we received. + _highestStreamIdReceived = + max(_highestStreamIdReceived, frame.header.streamId); + + // Handle the frame as either a stream or a connection frame. + if (frame.header.streamId == 0) { + if (frame is SettingsFrame) { + _settingsHandler.handleSettingsFrame(frame); + } else if (frame is PingFrame) { + _pingHandler.processPingFrame(frame); + } else if (frame is WindowUpdateFrame) { + // TODO: Implement this. + throw new UnimplementedError(); + } else if (frame is GoawayFrame) { + // TODO: What to do about [frame.lastStreamIdReceived] ? + _finishing(active: false); + } else if (frame is UnknownFrame) { + // We can safely ignore these. + } else { + throw 'Unknown incoming frame ${frame.runtimeType}'; + } + } else { + // We will not process frames for stream id's which are higher than when + // we sent the [GoawayFrame]. + if (_state == ConnectionState.Finishing && + frame.header.streamId > highestSeenStreamId) { + return; + } + + // TODO: Hand frame over to StreamHandler. + } + } + + void _finishing({bool active: true}) { + // If this connection is already finishing or dead, we return. + if (_state == ConnectionState.Terminated || + _state == ConnectionState.Finishing) { + return; + } + + if (_state == ConnectionState.Initialized || + _state == ConnectionState.Operational) { + _state = ConnectionState.Finishing; + + // If we are actively finishing this connection, we'll send a + // GoawayFrame otherwise we'll just propagate the message. + if (active) { + _frameWriter.writeGoawayFrame( + highestSeenStreamId, ErrorCode.NO_ERROR, []); + } + + // TODO: Propagate to the [StreamSet] that we no longer process new + // streams. And make it return a Future which we can listen to until + // all streams are done. + } + } + + /// Terminates this connection (if it is not already terminated). + /// + /// The returned future will never complete with an error. + Future _terminate(int errorCode, {bool causedByTransportError: false}) { + // TODO: When do we complete here? + if (_state != ConnectionState.Terminated) { + _state = ConnectionState.Terminated; + + var cancelFuture = new Future.sync(_frameReaderSubscription.cancel); + if (!causedByTransportError) { + _frameWriter.writeGoawayFrame(highestSeenStreamId, errorCode, []); + } + var closeFuture = _frameWriter.close().catchError((e, s) { + // We ignore any errors after writing to [GoawayFrame] + }); + + // Close all lower level handlers with an error message. + // (e.g. if there is a pending connection.ping(), it's returned + // Future will complete with this error). + var exception = new TransportConnectionException( + errorCode, 'Connection is being forcefully terminated.'); + _pingHandler.terminate(exception); + _settingsHandler.terminate(exception); + + return Future.wait([cancelFuture, closeFuture]).catchError((_) {}); + } + return new Future.value(); + } + + /// The highest stream id which has been used anywhere. + int get highestSeenStreamId => + max(_highestStreamIdReceived, _frameWriter.highestWrittenStreamId); +} diff --git a/pkgs/http2/lib/transport.dart b/pkgs/http2/lib/transport.dart new file mode 100644 index 0000000000..761e2a6fa9 --- /dev/null +++ b/pkgs/http2/lib/transport.dart @@ -0,0 +1,55 @@ +// 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. + +library http2; + +import 'dart:async'; +import 'dart:io'; + +import 'src/connection.dart'; + +/// Represents a HTTP/2 connection. +abstract class TransportConnection { + factory TransportConnection.clientViaSocket(Socket socket) + => new TransportConnection.clientViaStreams(socket, socket); + + factory TransportConnection.serverViaSocket(Socket socket) + => new TransportConnection.serverViaStreams(socket, socket); + + factory TransportConnection.clientViaStreams(Stream> incoming, + Sink> outgoing) + => new Connection.client(incoming, outgoing); + + factory TransportConnection.serverViaStreams(Stream> incoming, + Sink> outgoing) + => new Connection.server(incoming, outgoing); + + /// Pings the other end. + Future ping(); + + /// Finish this connection. + /// + /// No new streams will be accepted or can be created. + void finish(); + + /// Terminates this connection forcefully. + Future terminate(); +} + +/// An exception thrown by the HTTP/2 implementation. +class TransportException implements Exception { + final String message; + + TransportException(this.message); + + String toString() => 'HTTP/2 error: $message'; +} + +/// An exception thrown when a HTTP/2 connection error occurred. +class TransportConnectionException extends TransportException { + final int errorCode; + + TransportConnectionException(this.errorCode, String details) + : super('Connection error: $details'); +} diff --git a/pkgs/http2/test/transport_test.dart b/pkgs/http2/test/transport_test.dart new file mode 100644 index 0000000000..4b672ae8b4 --- /dev/null +++ b/pkgs/http2/test/transport_test.dart @@ -0,0 +1,75 @@ +// 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:unittest/unittest.dart'; + +import 'package:http2/transport.dart'; + +main() { + group('transport-test', () { + transportTest('ping', (TransportConnection client, + TransportConnection server) async { + await client.ping(); + await server.ping(); + }); + transportTest('terminated-client-ping', (TransportConnection client, + TransportConnection server) async { + var clientError = client.ping().catchError(expectAsync((e, s) { + expect(e is TransportException, isTrue); + })); + await client.terminate(); + await clientError; + + // NOTE: Now the connection is dead and client/server should complete + // with [TransportException]s when doing work (e.g. ping). + client.ping().catchError(expectAsync((e, s) { + expect(e is TransportException, isTrue); + })); + server.ping().catchError(expectAsync((e, s) { + expect(e is TransportException, isTrue); + })); + }); + transportTest('terminated-server-ping', (TransportConnection client, + TransportConnection server) async { + var clientError = client.ping().catchError(expectAsync((e, s) { + expect(e is TransportException, isTrue); + })); + await server.terminate(); + await clientError; + + // NOTE: Now the connection is dead and the client/server should complete + // with [TransportException]s when doing work (e.g. ping). + client.ping().catchError(expectAsync((e, s) { + expect(e is TransportException, isTrue); + })); + server.ping().catchError(expectAsync((e, s) { + expect(e is TransportException, isTrue); + })); + }); + }); +} + +transportTest(String name, func(client, server)) { + return test(name, () { + var bidirectional = new BidirectionalConnection(); + var client = bidirectional.clientConnection; + var server = bidirectional.serverConnection; + return func(client, server); + }); +} + +class BidirectionalConnection { + final StreamController> writeA = new StreamController(); + final StreamController> writeB = new StreamController(); + Stream> get readA => writeA.stream; + Stream> get readB => writeB.stream; + + TransportConnection get clientConnection + => new TransportConnection.clientViaStreams(readA, writeB.sink); + + TransportConnection get serverConnection + => new TransportConnection.serverViaStreams(readB, writeA.sink); +} From fe485078335434f0b9286accb1bfb3edbbabcdc9 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Fri, 22 May 2015 18:21:03 +0200 Subject: [PATCH 013/172] Implement connection/stream level queues and window handlers for flow control R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/200807013 --- pkgs/http2/lib/src/connection.dart | 44 ++- .../src/flowcontrol/connection_queues.dart | 260 +++++++++++++++++ .../lib/src/flowcontrol/queue_messages.dart | 52 ++++ .../lib/src/flowcontrol/stream_queues.dart | 264 ++++++++++++++++++ pkgs/http2/lib/src/flowcontrol/window.dart | 40 +++ .../lib/src/flowcontrol/window_handler.dart | 145 ++++++++++ pkgs/http2/lib/src/hpack/hpack.dart | 4 +- pkgs/http2/lib/src/settings/settings.dart | 14 +- pkgs/http2/lib/transport.dart | 47 +++- pkgs/http2/test/src/error_matchers.dart | 10 + .../flowcontrol/connection_queues_test.dart | 181 ++++++++++++ .../src/flowcontrol/stream_queues_test.dart | 171 ++++++++++++ .../src/flowcontrol/window_handler_test.dart | 133 +++++++++ 13 files changed, 1356 insertions(+), 9 deletions(-) create mode 100644 pkgs/http2/lib/src/flowcontrol/connection_queues.dart create mode 100644 pkgs/http2/lib/src/flowcontrol/queue_messages.dart create mode 100644 pkgs/http2/lib/src/flowcontrol/stream_queues.dart create mode 100644 pkgs/http2/lib/src/flowcontrol/window.dart create mode 100644 pkgs/http2/lib/src/flowcontrol/window_handler.dart create mode 100644 pkgs/http2/test/src/flowcontrol/connection_queues_test.dart create mode 100644 pkgs/http2/test/src/flowcontrol/stream_queues_test.dart create mode 100644 pkgs/http2/test/src/flowcontrol/window_handler_test.dart diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index 1300cbad8a..c7e93fafb8 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -8,6 +8,9 @@ import 'dart:async'; import 'dart:math'; import '../transport.dart'; +import 'flowcontrol/connection_queues.dart'; +import 'flowcontrol/window.dart'; +import 'flowcontrol/window_handler.dart'; import 'frames/frame_defragmenter.dart'; import 'frames/frames.dart'; import 'hpack/hpack.dart'; @@ -46,6 +49,12 @@ class Connection implements TransportConnection { /// The HPack context for this connection. final HPackContext _hpackContext = new HPackContext(); + /// The flow window for this connection of the peer. + final Window _peerWindow = new Window(); + + /// The flow window for this connection of this end. + final Window _localWindow = new Window(); + /// Used for defragmenting PushPromise/Header frames. final FrameDefragmenter _defragmenter = new FrameDefragmenter(); @@ -55,6 +64,12 @@ class Connection implements TransportConnection { /// A subscription of incoming [Frame]s. StreamSubscription _frameReaderSubscription; + /// The incoming connection-level message queue. + ConnectionMessageQueueIn _incomingQueue; + + /// The outgoing connection-level message queue. + ConnectionMessageQueueOut _outgoingQueue; + /// The ping handler used for making pings & handling remote pings. PingHandler _pingHandler; @@ -62,6 +77,9 @@ class Connection implements TransportConnection { /// setting changes. SettingsHandler _settingsHandler; + /// The connection-level flow control window handler for outgoing messages. + OutgoingConnectionWindowHandler _connectionWindowHandler; + /// Represents the highest stream id this connection has received from the /// other side. int _highestStreamIdReceived = 0; @@ -121,6 +139,25 @@ class Connection implements TransportConnection { _terminate(ErrorCode.PROTOCOL_ERROR); }); + _settingsHandler.onInitialWindowSizeChange.listen((size) { + // TODO: Apply change to [StreamHandler] + }); + + + // Setup the connection window handler, which keeps track of the + // size of the outgoing connection window. + _connectionWindowHandler = + new OutgoingConnectionWindowHandler(_peerWindow); + + var connectionWindowUpdater = new IncomingWindowHandler.connection( + _frameWriter, _localWindow); + + // Setup queues for outgoing/incoming messages on the connection level. + _outgoingQueue = new ConnectionMessageQueueOut( + _connectionWindowHandler, _frameWriter); + _incomingQueue = new ConnectionMessageQueueIn( + connectionWindowUpdater); + // NOTE: We're not waiting until initial settings have been exchanged // before we start using the connection (i.e. we don't wait for half a // round-trip-time). @@ -192,8 +229,7 @@ class Connection implements TransportConnection { } else if (frame is PingFrame) { _pingHandler.processPingFrame(frame); } else if (frame is WindowUpdateFrame) { - // TODO: Implement this. - throw new UnimplementedError(); + _connectionWindowHandler.processWindowUpdate(frame); } else if (frame is GoawayFrame) { // TODO: What to do about [frame.lastStreamIdReceived] ? _finishing(active: false); @@ -259,6 +295,10 @@ class Connection implements TransportConnection { // Future will complete with this error). var exception = new TransportConnectionException( errorCode, 'Connection is being forcefully terminated.'); + + _incomingQueue.terminate(exception); + _outgoingQueue.terminate(exception); + _pingHandler.terminate(exception); _settingsHandler.terminate(exception); diff --git a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart new file mode 100644 index 0000000000..5a1e2d77b6 --- /dev/null +++ b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart @@ -0,0 +1,260 @@ +// 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. + +// TODO: Take priorities into account. +// TODO: Properly fragment large data frames, so they are not taking up too much +// bandwidth. +library http2.src.flowcontrol.connection_flow_controller; + +import 'dart:async'; +import 'dart:collection'; + +import '../error_handler.dart'; +import '../frames/frames.dart'; + +import 'stream_queues.dart'; +import 'queue_messages.dart'; +import 'window_handler.dart'; +import '../frames/frames.dart'; + +/// The last place before messages coming from the application get encoded and +/// send as [Frame]s. +/// +/// It will convert [Message]s from higher layers and send them via [Frame]s. +/// +/// - It will queue messages until the connection-level flow control window +/// allows sending the message and the underlying [StreamSink] is not +/// buffering. +/// - It will use a [FrameWriter] to write a new frame to the connection. +class ConnectionMessageQueueOut extends Object with TerminatableMixin { + /// The handler which will be used for increasing the connection-level flow + /// control window. + final OutgoingConnectionWindowHandler _connectionWindow; + + /// The buffered [Message]s which are to be delivered to the remote peer. + final Queue _messages = new Queue(); + + /// The [FrameWriter] used for writing Headers/Data/PushPromise frames. + final FrameWriter _frameWriter; + + ConnectionMessageQueueOut(this._connectionWindow, this._frameWriter) { + _frameWriter.bufferIndicator.bufferEmptyEvents.listen((_) { + _trySendMessages(); + }); + _connectionWindow.positiveWindow.bufferEmptyEvents.listen((_) { + _trySendMessages(); + }); + } + + /// The number of pending messages which haven't been written to the wire. + int get pendingMessages => _messages.length; + + /// Enqueues a new [Message] which should be delivered to the remote peer. + void enqueueMessage(Message message) { + if (!wasTerminated) { + _messages.addLast(message); + _trySendMessages(); + } + } + + void onTerminated(error) { + _messages.clear(); + } + + void _trySendMessages() { + if (!wasTerminated) { + _trySendMessage(); + + // If we have more messages and we can send them, we'll run them + // using `Timer.run()` to let other things get in-between. + if (_messages.length > 0 && + !_connectionWindow.positiveWindow.wouldBuffer) { + // TODO: If all the frame writer methods would return an integer of the + // number of bytes written, we could just say, we loop here until 10kb + // and after words, we'll make `Timer.run()`. + Timer.run(_trySendMessages); + } + } + } + + void _trySendMessage() { + if (!_frameWriter.bufferIndicator.wouldBuffer && _messages.length > 0) { + Message message = _messages.first; + if (message is HeadersMessage) { + _messages.removeFirst(); + _frameWriter.writeHeadersFrame( + message.streamId, message.headers, endStream: message.endStream); + } else if (message is PushPromiseMessage) { + _messages.removeFirst(); + _frameWriter.writePushPromiseFrame( + message.streamId, message.promisedStreamId, message.headers); + } else if (message is DataMessage) { + _messages.removeFirst(); + + if (_connectionWindow.peerWindowSize >= message.bytes.length) { + _connectionWindow.decreaseWindow(message.bytes.length); + _frameWriter.writeDataFrame( + message.streamId, message.bytes, endStream: message.endStream); + } else { + // NOTE: We need to fragment the DataMessage. + // TODO: Do not fragment if the number of bytes we can send is too low + int len = _connectionWindow.peerWindowSize; + var head = viewOrSublist(message.bytes, 0, len); + var tail = viewOrSublist( + message.bytes, len, message.bytes.length - len); + + _connectionWindow.decreaseWindow(head.length); + _frameWriter.writeDataFrame(message.streamId, head, endStream: false); + + var tailMessage = + new DataMessage(message.streamId, tail, message.endStream); + _messages.addFirst(tailMessage); + } + } else { + throw new StateError( + 'Unexpected message in queue: ${message.runtimeType}'); + } + } + } +} + +/// The first place an incoming stream message gets delivered to. +/// +/// The [ConnectionMessageQueueIn] will be given [Frame]s which were sent to +/// any stream on this connection. +/// +/// - It will extract the necessary data from the [Frame] and store it in a new +/// [Message] object. +/// - It will multiplex the created [Message]es to a stream-specific +/// [StreamMessageQueueIn]. +/// - If the [StreamMessageQueueIn] cannot accept more data, the data will be +/// buffered until it can. +/// - [DataMessage]s which have been successfully delivered to a stream-specific +/// [StreamMessageQueueIn] will increase the flow control window for the +/// connection. +/// +/// Incoming [DataFrame]s will decrease the flow control window the peer has +/// available. +class ConnectionMessageQueueIn extends Object with TerminatableMixin { + /// The handler which will be used for increasing the connection-level flow + /// control window. + final IncomingWindowHandler _windowUpdateHandler; + + /// A mapping from stream-id to the corresponding stream-specific + /// [StreamMessageQueueIn]. + final Map _stream2messageQueue = {}; + + /// A buffer for [Message]s which cannot be received by their + /// [StreamMessageQueueIn]. + final Map> _stream2pendingMessages = {}; + + /// The number of pending messages which haven't been delivered + /// to the stream-specific queue. (for debugging purposes) + int _count = 0; + + ConnectionMessageQueueIn(this._windowUpdateHandler); + + void onTerminated(error) { + // NOTE: The higher level will be shutdown first, so all streams + // should have been removed at this point. + assert(_stream2messageQueue.isEmpty); + assert(_stream2pendingMessages.isEmpty); + } + + /// The number of pending messages which haven't been delivered + /// to the stream-specific queue. (for debugging purposes) + int get pendingMessages => _count; + + /// Registers a stream specific [StreamMessageQueueIn] for a new stream id. + void insertNewStreamMessageQueue(int streamId, StreamMessageQueueIn mq) { + if (_stream2messageQueue.containsKey(streamId)) { + throw new ArgumentError( + 'Cannot register a SteramMessageQueueIn for the same streamId ' + 'multiple times'); + } + + var pendingMessages = new Queue(); + _stream2pendingMessages[streamId] = pendingMessages; + _stream2messageQueue[streamId] = mq; + + mq.bufferIndicator.bufferEmptyEvents.listen((_) { + _tryDispatch(streamId, mq, pendingMessages); + }); + } + + /// Removes a stream id and its message queue from this connection-level + /// message queue. + void removeStreamMessageQueue(int streamId) { + _stream2pendingMessages.remove(streamId); + _stream2messageQueue.remove(streamId); + } + + /// Processes an incoming [DataFrame] which is addressed to a specific stream. + void processDataFrame(DataFrame frame) { + var streamId = frame.header.streamId; + var message = + new DataMessage(streamId, frame.bytes, frame.hasEndStreamFlag); + + _windowUpdateHandler.gotData(message.bytes.length); + _addMessage(streamId, message); + } + + /// Processes an incoming [HeadersFrame] which is addressed to a specific + /// stream. + void processHeadersFrame(HeadersFrame frame) { + var streamId = frame.header.streamId; + var message = new HeadersMessage( + streamId, frame.decodedHeaders, frame.hasEndHeadersFlag); + // NOTE: Header frames do not affect flow control - only data frames do. + _addMessage(streamId, message); + } + + /// Processes an incoming [PushPromiseFrame] which is addressed to a specific + /// stream. + void processPushPromiseFrame(PushPromiseFrame frame) { + var streamId = frame.header.streamId; + var message = new PushPromiseMessage( + streamId, frame.decodedHeaders, frame.promisedStreamId, false); + // NOTE: Header frames do not affect flow control - only data frames do. + _addMessage(streamId, message); + } + + void _addMessage(int streamId, Message message) { + _count++; + + // FIXME: We need to do a runtime check here and + // raise a protocol error if we cannot find the registered stream. + var streamMQ = _stream2messageQueue[streamId]; + var pendingMessages = _stream2pendingMessages[streamId]; + pendingMessages.addLast(message); + _tryDispatch(streamId, streamMQ, pendingMessages); + } + + void _tryDispatch(int streamId, + StreamMessageQueueIn mq, + Queue pendingMessages) { + int bytesDeliveredToStream = 0; + while (!mq.bufferIndicator.wouldBuffer && pendingMessages.length > 0) { + _count--; + + var message = pendingMessages.removeFirst(); + if (message is DataMessage) { + bytesDeliveredToStream += message.bytes.length; + } + mq.enqueueMessage(message); + if (message.endStream) { + // FIXME: This should be turned into a check and we should + // raise a protocol error if we ever get a message on a stream + // which has been closed. + assert (pendingMessages.isEmpty); + + _stream2messageQueue.remove(streamId); + _stream2pendingMessages.remove(streamId); + } + } + if (bytesDeliveredToStream > 0) { + _windowUpdateHandler.dataProcessed(bytesDeliveredToStream); + } + } +} diff --git a/pkgs/http2/lib/src/flowcontrol/queue_messages.dart b/pkgs/http2/lib/src/flowcontrol/queue_messages.dart new file mode 100644 index 0000000000..d8217be90a --- /dev/null +++ b/pkgs/http2/lib/src/flowcontrol/queue_messages.dart @@ -0,0 +1,52 @@ +// 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. + +library http2.src.flowcontrol.queue_messages; + +import '../../transport.dart'; + +/// The subclasses of [Message] are objects that are coming from the +/// connection layer on top of frames. +/// +/// Messages on a HTTP/2 stream will be represented by a different class +/// hierarchy. +abstract class Message { + final int streamId; + final bool endStream; + + Message(this.streamId, this.endStream); +} + +class HeadersMessage extends Message { + final List
headers; + + HeadersMessage(int streamId, this.headers, bool endStream) + : super(streamId, endStream); + + String toString() + => 'HeadersMessage(headers: ${headers.length}, endStream: $endStream)'; +} + +class DataMessage extends Message { + final List bytes; + + DataMessage(int streamId, this.bytes, bool endStream) + : super(streamId, endStream); + + String toString() + => 'DataMessage(bytes: ${bytes.length}, endStream: $endStream)'; +} + +class PushPromiseMessage extends Message { + final List
headers; + final int promisedStreamId; + + PushPromiseMessage( + int streamId, this.headers, this.promisedStreamId, bool endStream) + : super(streamId, endStream); + + String toString() + => 'PushPromiseMessage(bytes: ${headers.length}, ' + 'promisedStreamId: $promisedStreamId, endStream: $endStream)'; +} diff --git a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart new file mode 100644 index 0000000000..21bf79c2e4 --- /dev/null +++ b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart @@ -0,0 +1,264 @@ +// 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. + +library http2.src.flowcontrol.stream_queues; + +import 'dart:async'; +import 'dart:collection'; + +import '../../transport.dart'; +import '../async_utils/async_utils.dart'; +import '../error_handler.dart'; +import '../frames/frames.dart'; + +import 'connection_queues.dart'; +import 'queue_messages.dart'; +import 'window_handler.dart'; + +/// This class will buffer any headers/data messages in the order they were +/// added. +/// +/// It will ensure that we never send more data than the remote flow control +/// window allows. +class StreamMessageQueueOut extends Object with TerminatableMixin { + /// The id of the stream this message queue belongs to. + final int streamId; + + /// The stream-level flow control handler. + final OutgoingStreamWindowHandler streamWindow; + + /// The underlying connection-level message queue. + final ConnectionMessageQueueOut connectionMessageQueue; + + /// A indicator for whether this queue is currently buffering. + final BufferIndicator bufferIndicator = new BufferIndicator(); + + /// Buffered [Message]s which will be written to the underlying connection + /// if the flow control window allows so. + final Queue _messages = new Queue(); + + /// Debugging data on how much data should be written to the underlying + /// connection message queue. + int toBeWrittenBytes = 0; + + /// Debugging data on how much data was written to the underlying connection + /// message queue. + int writtenBytes = 0; + + StreamMessageQueueOut(this.streamId, + this.streamWindow, + this.connectionMessageQueue) { + streamWindow.positiveWindow.bufferEmptyEvents.listen((_) { + if (!wasTerminated) { + _trySendData(); + } + }); + if (streamWindow.positiveWindow.wouldBuffer) { + bufferIndicator.markBuffered(); + } else { + bufferIndicator.markUnBuffered(); + } + } + + /// Debugging data about how many messages are pending to be written to the + /// connection message queue. + int get pendingMessages => _messages.length; + + /// Enqueues a new [Message] which is to be delivered to the connection + /// message queue. + void enqueueMessage(Message message) { + if (!wasTerminated) { + if (message is DataMessage) { + toBeWrittenBytes += message.bytes.length; + } + + _messages.addLast(message); + _trySendData(); + + if (_messages.length > 0) { + bufferIndicator.markBuffered(); + } + } + } + + /// Terminates this stream message queue. Further operations on it will fail. + void terminate([error]) { + super.terminate(error); + _messages.clear(); + } + + void _trySendData() { + int queueLenBefore = _messages.length; + + while (_messages.length > 0) { + Message message = _messages.first; + + if (message is HeadersMessage) { + _messages.removeFirst(); + connectionMessageQueue.enqueueMessage(message); + } else if (message is DataMessage) { + int bytesAvailable = streamWindow.peerWindowSize; + if (bytesAvailable > 0 || message.bytes.length == 0) { + _messages.removeFirst(); + + // Do we need to fragment? + DataMessage messageToSend = message; + List messageBytes = message.bytes; + // TODO: Do not fragment if the number of bytes we can send is too low + if (messageBytes.length > bytesAvailable) { + var partA = viewOrSublist(messageBytes, 0, bytesAvailable); + var partB = viewOrSublist( + messageBytes, bytesAvailable, + messageBytes.length - bytesAvailable); + var messageA = new DataMessage(message.streamId, partA, false); + var messageB = new DataMessage( + message.streamId, partB, message.endStream); + + // Put the second fragment back into the front of the queue. + _messages.addFirst(messageB); + + // Send the first fragment. + messageToSend = messageA; + } + + writtenBytes += messageToSend.bytes.length; + streamWindow.decreaseWindow(messageToSend.bytes.length); + connectionMessageQueue.enqueueMessage(messageToSend); + } else { + break; + } + } else { + throw new StateError('Unknown messages type: ${message.runtimeType}'); + } + } + + if (queueLenBefore > 0 && _messages.isEmpty) { + bufferIndicator.markUnBuffered(); + } + } +} + +/// Keeps a list of [Messages] which should be delivered to the +/// [TransportStream]. +/// +/// It will keep messages up to the stream flow control window size if the +/// [messages] listener is paused. +class StreamMessageQueueIn extends Object with TerminatableMixin { + /// The stream-level window our peer is using when sending us messages. + final IncomingWindowHandler windowHandler; + + /// A indicator whether this [StreamMessageQueueIn] is currently buffering. + final BufferIndicator bufferIndicator = new BufferIndicator(); + + /// The pending [Message]s which are to be delivered via the [messages] + /// stream. + final Queue _pendingMessages = new Queue(); + + /// The [StreamController] used for producing the [messages] stream. + StreamController _incomingMessagesC; + + /// The [StreamController] used for producing the [serverPushes] stream. + StreamController _serverPushStreamsC; + + StreamMessageQueueIn(this.windowHandler) { + // We start by marking it as buffered, since no one is listening yet and + // incoming messages will get buffered. + bufferIndicator.markBuffered(); + + _incomingMessagesC = new StreamController( + onListen: () { + if (_checkOpen()) { + _tryDispatch(); + _tryUpdateBufferIndicator(); + } + }, onPause: () { + _tryUpdateBufferIndicator(); + // TODO: Would we ever want to decrease the window size in this + // situation? + }, onResume: () { + if (_checkOpen()) { + _tryDispatch(); + _tryUpdateBufferIndicator(); + } + }, onCancel: () { + _pendingMessages.clear(); + _close(); + }); + + // FIXME/TODO/FIXME: This needs to change, we need to intercept all + // onListen/... callbacks. + _serverPushStreamsC = new StreamController(); + } + + /// Debugging data: the number of pending messages in this queue. + int get pendingMessages => _pendingMessages.length; + + /// The stream of [StreamMessage]s which come from the remote peer. + Stream get messages => _incomingMessagesC.stream; + + /// The stream of [ServerPush]es which come from the remote peer. + Stream get serverPushes => _serverPushStreamsC.stream; + + /// A lower layer enqueues a new [Message] which should be delivered to the + /// app. + void enqueueMessage(Message message) { + if (_checkOpen()) { + if (message is DataMessage) { + windowHandler.gotData(message.bytes.length); + } + _pendingMessages.add(message); + + _tryDispatch(); + _tryUpdateBufferIndicator(); + } + } + + void onTerminated(TransportException exception) { + _pendingMessages.clear(); + if (!_incomingMessagesC.isClosed) { + _incomingMessagesC.addError(exception); + _close(); + } + } + + void _tryDispatch() { + while (_incomingMessagesC.hasListener && + !_incomingMessagesC.isPaused && + !_incomingMessagesC.isClosed && + !wasTerminated && + _pendingMessages.isNotEmpty) { + var message = _pendingMessages.removeFirst(); + if (message is HeadersMessage) { + // NOTE: Header messages do not affect flow control - only + // data messages do. + _incomingMessagesC.add(new HeadersStreamMessage(message.headers)); + } else if (message is DataMessage) { + if (message.bytes.length > 0) { + _incomingMessagesC.add(new DataStreamMessage(message.bytes)); + windowHandler.dataProcessed(message.bytes.length); + } + } + if (message.endStream) { + _close(); + } + } + } + + void _tryUpdateBufferIndicator() { + if (_incomingMessagesC.isPaused || _pendingMessages.length > 0) { + bufferIndicator.markBuffered(); + } else if (bufferIndicator.wouldBuffer && !_incomingMessagesC.isPaused) { + bufferIndicator.markUnBuffered(); + } + } + + void _close() { + if (!_incomingMessagesC.isClosed) { + _incomingMessagesC.close(); + _serverPushStreamsC.close(); + } + } + + bool _checkOpen() => !_incomingMessagesC.isClosed && !wasTerminated; +} diff --git a/pkgs/http2/lib/src/flowcontrol/window.dart b/pkgs/http2/lib/src/flowcontrol/window.dart new file mode 100644 index 0000000000..baf193db73 --- /dev/null +++ b/pkgs/http2/lib/src/flowcontrol/window.dart @@ -0,0 +1,40 @@ +// 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. + +library http2.src.window; + +class Window { + static const int MAX_WINDOW_SIZE = (1 << 31) - 1; + + /// The size available in this window. + /// + /// The default flow control window for the entire connection and for new + /// streams is 65535). + /// + /// NOTE: This value can potentially become negative. + int _size; + + /// The size the window would normally have if there is no outstanding + /// data. + /// + /// NOTE: The peer can always increase a stream window above this default + /// limit. + int _defaultSize; + + Window({int initialSize: (1 << 16) - 1}) + : _size = initialSize, _defaultSize = initialSize; + + /// The current size of the flow control window. + int get size => _size; + + void modify(int difference) { + _size += difference; + } + + /// This method can be e.g. called after receiving a SettingsFrame + /// which changes the initial window size of all streams. + void modifyDefaultSize(int difference) { + _defaultSize += difference; + } +} diff --git a/pkgs/http2/lib/src/flowcontrol/window_handler.dart b/pkgs/http2/lib/src/flowcontrol/window_handler.dart new file mode 100644 index 0000000000..4187fa7ec4 --- /dev/null +++ b/pkgs/http2/lib/src/flowcontrol/window_handler.dart @@ -0,0 +1,145 @@ +// 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. + +library http2.src.window_handler; + +import '../async_utils/async_utils.dart'; +import '../sync_errors.dart'; +import '../frames/frames.dart'; + +import 'window.dart'; + +abstract class AbstractOutgoingWindowHandler { + /// The connection flow control window. + final Window _peerWindow; + + /// Indicates when the outgoing connection window turned positive and we can + /// send data frames again. + final BufferIndicator positiveWindow = new BufferIndicator(); + + AbstractOutgoingWindowHandler(this._peerWindow) { + if (_peerWindow.size > 0) { + positiveWindow.markUnBuffered(); + } + } + + /// The flow control window size we use for sending data. We are not allowed + /// to let this window be negative. + int get peerWindowSize => _peerWindow.size; + + /// Process a window update frame received from the remote end. + void processWindowUpdate(WindowUpdateFrame frame) { + int oldWindowSize = _peerWindow.size; + + int increment = frame.windowSizeIncrement; + if ((_peerWindow.size + increment) > Window.MAX_WINDOW_SIZE) { + throw new FlowControlException( + 'Window update received from remote peer would make flow control ' + 'window too large.'); + } else { + _peerWindow.modify(increment); + } + + // If we transitioned from an negative/empty window to a positive window + // we'll fire an event that more data frames can be sent now. + if (oldWindowSize <= 0 && _peerWindow.size > 0) { + positiveWindow.markUnBuffered(); + } + } + + /// Update the peer window by subtracting [numberOfBytes]. + /// + /// The remote peer will send us [WindowUpdateFrame]s which will increase + /// the window again at a later point in time. + void decreaseWindow(int numberOfBytes) { + _peerWindow.modify(-numberOfBytes); + if (_peerWindow.size <= 0) { + positiveWindow.markBuffered(); + } + } +} + + +/// Handles the connection window for outgoing data frames. +class OutgoingConnectionWindowHandler extends AbstractOutgoingWindowHandler { + OutgoingConnectionWindowHandler(Window window) : super(window); +} + + +/// Handles the window for outgoing messages to the peer. +class OutgoingStreamWindowHandler extends AbstractOutgoingWindowHandler { + OutgoingStreamWindowHandler(Window window) : super(window); + + /// Update the peer window by adding [difference] to it. + /// + /// + /// The remote peer has send a new [SettingsFrame] which updated the default + /// stream level [Setting.SETTINGS_INITIAL_WINDOW_SIZE]. This causes all + /// existing streams to update the flow stream-level flow control window. + void processInitialWindowSizeSettingChange(int difference) { + if ((_peerWindow.size + difference) > Window.MAX_WINDOW_SIZE) { + throw new FlowControlException( + 'Window update received from remote peer would make flow control ' + 'window too large.'); + } else { + _peerWindow.modify(difference); + if (_peerWindow.size <= 0) { + positiveWindow.markBuffered(); + } + } + } +} + + +/// Mirrors the flow control window the remote end is using. +class IncomingWindowHandler { + /// The [FrameWriter] used for writing [WindowUpdateFrame]s to the wire. + final FrameWriter _frameWriter; + + /// The mirror of the [Window] the remote end sees. + /// + /// If [_localWindow ] turns negative, it means the remote peer sent us more + /// data than we allowed it to send. + final Window _localWindow; + + /// The stream id this window handler is for (is `0` for connection level). + final int _streamId; + + IncomingWindowHandler.stream( + this._frameWriter, this._localWindow, this._streamId); + + IncomingWindowHandler.connection( + this._frameWriter, this._localWindow) : _streamId = 0; + + /// The current size for the incoming data window. + /// + /// (This should never get negative, otherwise the peer send us more data + /// than we told it to send.) + int get localWindowSize => _localWindow.size; + + /// Signals that we received [numberOfBytes] from the remote peer. + void gotData(int numberOfBytes) { + _localWindow.modify(-numberOfBytes); + + // If this turns negative, it means the remote end send us more data + // then we announced we can handle (i.e. the remote window size must be + // negative). + // FIXME/TODO: We should in such a case terminate the connection. + } + + /// Tell the peer we received [numberOfBytes] bytes. It will increase it's + /// sending window then. + /// + // TODO/FIXME: If we pause and don't want to get more data, we have to + // - either stop sending window update frames + // - or decreasing the window size + void dataProcessed(int numberOfBytes) { + _localWindow.modify(numberOfBytes); + + // TODO: This can be optimized by delaying the window update to + // send one update with a bigger difference than multiple small update + // frames. + _frameWriter.writeWindowUpdate(numberOfBytes, streamId: _streamId); + } +} diff --git a/pkgs/http2/lib/src/hpack/hpack.dart b/pkgs/http2/lib/src/hpack/hpack.dart index 26b32fbc1d..417c868758 100644 --- a/pkgs/http2/lib/src/hpack/hpack.dart +++ b/pkgs/http2/lib/src/hpack/hpack.dart @@ -156,9 +156,9 @@ class HPackDecoder { } } return headers; - } on RangeError catch (e, s) { + } on RangeError catch (e) { throw new HPackDecodingException('$e'); - } on HuffmanDecodingException catch (e, s) { + } on HuffmanDecodingException catch (e) { throw new HPackDecodingException('$e'); } } diff --git a/pkgs/http2/lib/src/settings/settings.dart b/pkgs/http2/lib/src/settings/settings.dart index 86e5c5d748..3279883f4d 100644 --- a/pkgs/http2/lib/src/settings/settings.dart +++ b/pkgs/http2/lib/src/settings/settings.dart @@ -101,6 +101,14 @@ class SettingsHandler extends Object with TerminatableMixin { /// The peer settings, which we ACKed and are obeying. Settings _peerSettings = new Settings(); + StreamController _onInitialWindowSizeChangeController = + new StreamController.broadcast(sync: true); + + /// Events are fired when a SettingsFrame changes the initial size + /// of stream windows. + Stream get onInitialWindowSizeChange + => _onInitialWindowSizeChangeController.stream; + SettingsHandler(this._frameWriter, this._acknowledgedSettings, this._peerSettings); @@ -193,9 +201,7 @@ class SettingsHandler extends Object with TerminatableMixin { case Setting.SETTINGS_INITIAL_WINDOW_SIZE: if (setting.value < (1 << 31)) { - // TODO: - // var difference = setting.value - base.initialWindowSize; - // streamSet.processInitialWindowSizeSettingChange(difference); + _onInitialWindowSizeChangeController.add(setting.value); base.initialWindowSize = setting.value; } else { throw new FlowControlException('Invalid initial window size.'); @@ -203,7 +209,7 @@ class SettingsHandler extends Object with TerminatableMixin { break; default: - // Spec says to ignore unkown settings. + // Spec says to ignore unknown settings. break; } } diff --git a/pkgs/http2/lib/transport.dart b/pkgs/http2/lib/transport.dart index 761e2a6fa9..d5516247d5 100644 --- a/pkgs/http2/lib/transport.dart +++ b/pkgs/http2/lib/transport.dart @@ -2,12 +2,15 @@ // 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. -library http2; +library http2.transport; import 'dart:async'; import 'dart:io'; import 'src/connection.dart'; +import 'src/hpack/hpack.dart' show Header; + +export 'src/hpack/hpack.dart' show Header; /// Represents a HTTP/2 connection. abstract class TransportConnection { @@ -37,6 +40,48 @@ abstract class TransportConnection { Future terminate(); } +/// Represents a HTTP/2 stream. +abstract class TransportStream { + // TODO: Populate this class. +} +/// Represents a message which can be sent over a HTTP/2 stream. +abstract class StreamMessage {} + + +/// Represents a data message which can be sent over a HTTP/2 stream. +class DataStreamMessage implements StreamMessage { + final List bytes; + + DataStreamMessage(this.bytes); + + String toString() => 'DataStreamMessage(${bytes.length} bytes)'; +} + + +/// Represents a headers message which can be sent over a HTTP/2 stream. +class HeadersStreamMessage implements StreamMessage { + final List
headers; + + HeadersStreamMessage(this.headers); + + String toString() => 'HeadersStreamMessage(${headers.length} headers)'; +} + + +/// Represents a remote stream push. +class TransportStreamPush { + /// The request headers which [stream] is the response to. + final List
requestHeaders; + + /// The remote stream push. + final TransportStream stream; + + TransportStreamPush(this.requestHeaders, this.stream); + + String toString() => + 'TransportStreamPush(${requestHeaders.length} request headers headers)'; +} + /// An exception thrown by the HTTP/2 implementation. class TransportException implements Exception { final String message; diff --git a/pkgs/http2/test/src/error_matchers.dart b/pkgs/http2/test/src/error_matchers.dart index 915c385666..0ecc098dd7 100644 --- a/pkgs/http2/test/src/error_matchers.dart +++ b/pkgs/http2/test/src/error_matchers.dart @@ -39,3 +39,13 @@ class _TerminatedException extends TypeMatcher { const Matcher throwsTerminatedException = const Throws(isTerminatedException); + +const Matcher isFlowControlException = const _FlowControlException(); + +class _FlowControlException extends TypeMatcher { + const _FlowControlException() : super("FlowControlException"); + bool matches(item, Map matchState) => item is FlowControlException; +} + +const Matcher throwsFlowControlException = + const Throws(isFlowControlException); diff --git a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart new file mode 100644 index 0000000000..a9c2ab147c --- /dev/null +++ b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart @@ -0,0 +1,181 @@ +// 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:unittest/unittest.dart'; + +import 'package:http2/transport.dart'; +import 'package:http2/src/async_utils/async_utils.dart'; +import 'package:http2/src/frames/frames.dart'; +import 'package:http2/src/flowcontrol/window.dart'; +import 'package:http2/src/flowcontrol/window_handler.dart'; +import 'package:http2/src/flowcontrol/connection_queues.dart'; +import 'package:http2/src/flowcontrol/stream_queues.dart'; +import 'package:http2/src/flowcontrol/queue_messages.dart'; + +import '../error_matchers.dart'; +import '../mock_utils.dart'; + +main() { + group('flowcontrol', () { + test('connection-message-queue-out', () { + var fw = new MockFrameWriter(); + var windowMock = new MockOutgoingWindowHandler(); + var queue = new ConnectionMessageQueueOut(windowMock, fw); + + fw.bufferIndicator.markUnBuffered(); + + expect(queue.pendingMessages, 0); + + var headers = [new Header.ascii('a', 'b')]; + var bytes = [1, 2, 3]; + + // Send [HeadersMessage]. + var c = new TestCounter(); + fw.mock_writeHeadersFrame = (int streamId, + List
sendingHeaders, + {bool endStream}) { + expect(streamId, 99); + expect(sendingHeaders, headers); + expect(endStream, false); + c.got(); + }; + queue.enqueueMessage(new HeadersMessage(99, headers, false)); + expect(queue.pendingMessages, 0); + + fw.mock_writeHeadersFrame = null; + + // Send [DataMessage]. + c = new TestCounter(count: 2); + windowMock.peerWindowSize = bytes.length; + windowMock.mock_decreaseWindow = (int difference) { + expect(difference, bytes.length); + c.got(); + }; + fw.mock_writeDataFrame = (int streamId, + List
sendingBytes, + {bool endStream}) { + expect(streamId, 99); + expect(sendingBytes, bytes); + expect(endStream, true); + c.got(); + }; + queue.enqueueMessage(new DataMessage(99, bytes, true)); + expect(queue.pendingMessages, 0); + + fw.mock_writeDataFrame = null; + + // Send [DataMessage] if the connection window is too small. + // Should trigger fragmentation and should write 1 byte. + c = new TestCounter(count: 2); + windowMock.peerWindowSize = 1; + windowMock.mock_decreaseWindow = (int difference) { + expect(difference, 1); + c.got(); + }; + fw.mock_writeDataFrame = (int streamId, List
sendingBytes, + {bool endStream}) { + expect(streamId, 99); + expect(sendingBytes, bytes.sublist(0, 1)); + expect(endStream, false); + c.got(); + }; + queue.enqueueMessage(new DataMessage(99, bytes, true)); + expect(queue.pendingMessages, 1); + + // Now mark it as unbuffered. This should write the rest of the + // [bytes.length - 1] bytes. + c = new TestCounter(count: 2); + windowMock.mock_decreaseWindow = (int difference) { + expect(difference, bytes.length - 1); + c.got(); + }; + fw.mock_writeDataFrame = (int streamId, List
sendingBytes, + {bool endStream}) { + expect(streamId, 99); + expect(sendingBytes, bytes.sublist(1)); + expect(endStream, true); + c.got(); + }; + windowMock.peerWindowSize = bytes.length - 1; + windowMock.positiveWindow.markUnBuffered(); + + // Terminate it now, ensure messages get cleared and we no longer + // enqueue messages. + queue.terminate(); + expect(queue.pendingMessages, 0); + fw = new MockFrameWriter(); + windowMock = new MockOutgoingWindowHandler(); + queue.enqueueMessage(new DataMessage(99, bytes, true)); + expect(queue.pendingMessages, 0); + }); + + test('connection-message-queue-in', () { + const STREAM_ID = 99; + final bytes = [1, 2, 3]; + + var windowMock = new MockIncomingWindowHandler(); + + var queue = new ConnectionMessageQueueIn(windowMock); + expect(queue.pendingMessages, 0); + + var streamQueueMock = new MockStreamMessageQueueIn(); + queue.insertNewStreamMessageQueue(STREAM_ID, streamQueueMock); + + // Insert a [DataFrame] and let it be buffered. + var header = new FrameHeader(0, 0, 0, STREAM_ID); + var c = new TestCounter(); + windowMock.mock_gotData = (int diff) { + expect(diff, bytes.length); + c.got(); + }; + queue.processDataFrame(new DataFrame(header, 0, bytes)); + expect(queue.pendingMessages, 1); + + // Indicate that the stream queue has space, and make sure + // the data is propagated from the connection to the stream + // specific queue. + c = new TestCounter(count: 2); + windowMock.mock_gotData = null; + windowMock.mock_dataProcessed = (int diff) { + expect(diff, bytes.length); + c.got(); + }; + streamQueueMock.mock_enqueueMessage = (DataMessage message) { + expect(message.streamId, STREAM_ID); + expect(message.bytes, bytes); + c.got(); + }; + streamQueueMock.bufferIndicator.markUnBuffered(); + + // TODO: Write tests for adding HeadersFrame/PushPromiseFrame. + }); + }); +} + +class MockFrameWriter extends SmartMock implements FrameWriter { + BufferIndicator bufferIndicator = new BufferIndicator(); + + dynamic noSuchMethod(_) => super.noSuchMethod(_); +} + +class MockStreamMessageQueueIn extends SmartMock + implements StreamMessageQueueIn { + BufferIndicator bufferIndicator = new BufferIndicator(); + + dynamic noSuchMethod(_) => super.noSuchMethod(_); +} + +class MockIncomingWindowHandler extends SmartMock + implements IncomingWindowHandler { + dynamic noSuchMethod(_) => super.noSuchMethod(_); +} + +class MockOutgoingWindowHandler extends SmartMock + implements OutgoingConnectionWindowHandler, + OutgoingStreamWindowHandler { + BufferIndicator positiveWindow = new BufferIndicator(); + int peerWindowSize = new Window().size; + + dynamic noSuchMethod(_) => super.noSuchMethod(_); +} diff --git a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart new file mode 100644 index 0000000000..c89beabf76 --- /dev/null +++ b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart @@ -0,0 +1,171 @@ +// 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:unittest/unittest.dart'; + +import 'package:http2/transport.dart'; +import 'package:http2/src/async_utils/async_utils.dart'; +import 'package:http2/src/flowcontrol/queue_messages.dart'; +import 'package:http2/src/flowcontrol/stream_queues.dart'; +import 'package:http2/src/flowcontrol/window_handler.dart'; +import 'package:http2/src/flowcontrol/connection_queues.dart'; +import 'package:http2/src/frames/frames.dart'; + +import '../error_matchers.dart'; +import '../mock_utils.dart'; + +main() { + group('flowcontrol', () { + const STREAM_ID = 99; + const BYTES = const [1, 2, 3]; + + group('stream-message-queue-out', () { + test('window-big-enough', () { + var connectionQueueMock = new MockConnectionMessageQueueOut(); + var windowMock = new MockOutgoingStreamWindowHandler(); + + windowMock.positiveWindow.markUnBuffered(); + var queue = new StreamMessageQueueOut( + STREAM_ID, windowMock, connectionQueueMock); + + expect(queue.bufferIndicator.wouldBuffer, isFalse); + expect(queue.pendingMessages, 0); + + windowMock.peerWindowSize = BYTES.length; + windowMock.mock_decreaseWindow = expectAsync((int difference) { + expect(difference, BYTES.length); + }); + connectionQueueMock.mock_enqueueMessage = + expectAsync((Message message) { + expect(message is DataMessage, isTrue); + DataMessage dataMessage = message; + expect(dataMessage.bytes, BYTES); + expect(dataMessage.endStream, isTrue); + }); + queue.enqueueMessage(new DataMessage(STREAM_ID, BYTES, true)); + }); + + test('window-smaller-than-necessary', () { + var connectionQueueMock = new MockConnectionMessageQueueOut(); + var windowMock = new MockOutgoingStreamWindowHandler(); + + windowMock.positiveWindow.markUnBuffered(); + var queue = new StreamMessageQueueOut( + STREAM_ID, windowMock, connectionQueueMock); + + expect(queue.bufferIndicator.wouldBuffer, isFalse); + expect(queue.pendingMessages, 0); + + // We set the window size fixed to 1, which means all the data messages + // will get fragmented to 1 byte. + windowMock.peerWindowSize = 1; + windowMock.mock_decreaseWindow = expectAsync((int difference) { + expect(difference, 1); + }, count: BYTES.length); + int counter = 0; + connectionQueueMock.mock_enqueueMessage = + expectAsync((Message message) { + expect(message is DataMessage, isTrue); + DataMessage dataMessage = message; + expect(dataMessage.bytes, BYTES.sublist(counter, counter + 1)); + counter++; + expect(dataMessage.endStream, counter == BYTES.length); + }, count: BYTES.length); + queue.enqueueMessage(new DataMessage(STREAM_ID, BYTES, true)); + + expect(queue.pendingMessages, 0); + }); + + test('window-empty', () { + var connectionQueueMock = new MockConnectionMessageQueueOut(); + var windowMock = new MockOutgoingStreamWindowHandler(); + + windowMock.positiveWindow.markUnBuffered(); + var queue = new StreamMessageQueueOut( + STREAM_ID, windowMock, connectionQueueMock); + + expect(queue.bufferIndicator.wouldBuffer, isFalse); + expect(queue.pendingMessages, 0); + + windowMock.peerWindowSize = 0; + windowMock.mock_decreaseWindow = expectAsync((_) {}, count: 0); + connectionQueueMock.mock_enqueueMessage = + expectAsync((_) {}, count: 0); + queue.enqueueMessage(new DataMessage(STREAM_ID, BYTES, true)); + expect(queue.bufferIndicator.wouldBuffer, isTrue); + expect(queue.pendingMessages, 1); + }); + }); + + group('stream-message-queue-in', () { + test('data-end-of-stream', () { + var windowMock = new MockIncomingWindowHandler(); + var queue = new StreamMessageQueueIn(windowMock); + + expect(queue.pendingMessages, 0); + queue.messages.listen(expectAsync((StreamMessage message) { + expect(message is DataStreamMessage, isTrue); + + DataStreamMessage dataMessage = message; + expect(dataMessage.bytes, BYTES); + }), onDone: expectAsync(() {})); + windowMock.mock_gotData = expectAsync((int difference) { + expect(difference, BYTES.length); + }); + windowMock.mock_dataProcessed = expectAsync((int difference) { + expect(difference, BYTES.length); + }); + queue.enqueueMessage(new DataMessage(STREAM_ID, BYTES, true)); + expect(queue.bufferIndicator.wouldBuffer, isFalse); + }); + }); + + test('data-end-of-stream--paused', () { + const STREAM_ID = 99; + final bytes = [1, 2, 3]; + + var windowMock = new MockIncomingWindowHandler(); + var queue = new StreamMessageQueueIn(windowMock); + + var sub = queue.messages.listen( + expectAsync((_) {}, count: 0), onDone: expectAsync(() {}, count: 0)); + sub.pause(); + + // We assert that we got the data, but it wasn't processed. + windowMock.mock_gotData = expectAsync((int difference) { + expect(difference, bytes.length); + }); + windowMock.mock_dataProcessed = expectAsync((_) {}, count: 0); + + expect(queue.pendingMessages, 0); + queue.enqueueMessage(new DataMessage(STREAM_ID, bytes, true)); + expect(queue.pendingMessages, 1); + expect(queue.bufferIndicator.wouldBuffer, isTrue); + }); + + // TODO: Add tests for Headers/HeadersPush messages. + }); +} + + +class MockConnectionMessageQueueOut extends SmartMock + implements ConnectionMessageQueueOut { + dynamic noSuchMethod(_) => super.noSuchMethod(_); +} + + +class MockIncomingWindowHandler extends SmartMock + implements IncomingWindowHandler { + dynamic noSuchMethod(_) => super.noSuchMethod(_); +} + +class MockOutgoingStreamWindowHandler extends SmartMock + implements OutgoingStreamWindowHandler { + final BufferIndicator positiveWindow = new BufferIndicator(); + int peerWindowSize; + + dynamic noSuchMethod(_) => super.noSuchMethod(_); +} diff --git a/pkgs/http2/test/src/flowcontrol/window_handler_test.dart b/pkgs/http2/test/src/flowcontrol/window_handler_test.dart new file mode 100644 index 0000000000..10c900ddb9 --- /dev/null +++ b/pkgs/http2/test/src/flowcontrol/window_handler_test.dart @@ -0,0 +1,133 @@ +// 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:unittest/unittest.dart'; + +import 'package:http2/src/flowcontrol/window.dart'; +import 'package:http2/src/flowcontrol/window_handler.dart'; +import 'package:http2/src/frames/frames.dart'; + +import '../error_matchers.dart'; +import '../mock_utils.dart'; + +main() { + group('flowcontrol', () { + void testAbstractOutgoingWindowHandler( + AbstractOutgoingWindowHandler handler, + Window window, + int initialSize) { + var sub = handler.positiveWindow.bufferEmptyEvents.listen( + expectAsync((_) {}, 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 = new FrameHeader(4, FrameType.WINDOW_UPDATE, 0, 0); + handler.processWindowUpdate(new 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(expectAsync((_) { + expect(handler.peerWindowSize, 1); + expect(window.size, 1); + })); + + // Now we trigger the 1 byte window increase + handler.processWindowUpdate(new 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 = new WindowUpdateFrame(frameHeader, Window.MAX_WINDOW_SIZE); + expect(() => handler.processWindowUpdate(frame), + throwsA(isFlowControlException)); + } + + test('outgoing-connection-window-handler', () { + var window = new Window(); + int initialSize = window.size; + var handler = new OutgoingConnectionWindowHandler(window); + + testAbstractOutgoingWindowHandler(handler, window, initialSize); + }); + + test('outgoing-stream-window-handler', () { + var window = new Window(); + int initialSize = window.size; + var handler = new 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 = new Window(); + initialSize = window.size; + handler = new OutgoingStreamWindowHandler(window); + + expect(handler.positiveWindow.wouldBuffer, isFalse); + handler.positiveWindow.bufferEmptyEvents.listen( + expectAsync((_) {}, count: 0)); + handler.processInitialWindowSizeSettingChange(-window.size); + expect(handler.positiveWindow.wouldBuffer, isTrue); + expect(handler.peerWindowSize, 0); + expect(window.size, 0); + + expect(() => handler.processInitialWindowSizeSettingChange( + Window.MAX_WINDOW_SIZE + 1), throwsA(isFlowControlException)); + }); + + test('incoming-window-handler', () { + const STREAM_ID = 99; + + var fw = new FrameWriterMock(); + var window = new Window(); + int initialSize = window.size; + var handler = new 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. + var c = new TestCounter(); + fw.mock_writeWindowUpdate = (int numberOfBytes, {int streamId}) { + expect(numberOfBytes, 100); + expect(streamId, STREAM_ID); + c.got(); + }; + handler.dataProcessed(100); + expect(handler.localWindowSize, initialSize); + expect(window.size, initialSize); + }); + }); +} + +class FrameWriterMock extends SmartMock implements FrameWriter { + dynamic noSuchMethod(_) => super.noSuchMethod(_); +} From b3e3db5694fbdd2b85ff7c952f56fd9d050f5254 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Wed, 27 May 2015 11:21:51 +0200 Subject: [PATCH 014/172] Add StreamsHandler, TransportStream and HTTP/2 stream state management R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/199947013 --- pkgs/http2/lib/src/connection.dart | 73 ++- .../src/flowcontrol/connection_queues.dart | 4 +- .../http2/lib/src/streams/stream_handler.dart | 485 ++++++++++++++++++ pkgs/http2/lib/transport.dart | 91 +++- pkgs/http2/test/src/streams/helper.dart | 58 +++ .../test/src/streams/simple_flow_test.dart | 92 ++++ .../test/src/streams/simple_push_test.dart | 90 ++++ pkgs/http2/test/src/streams/streams_test.dart | 173 +++++++ pkgs/http2/test/transport_test.dart | 4 +- 9 files changed, 1038 insertions(+), 32 deletions(-) create mode 100644 pkgs/http2/lib/src/streams/stream_handler.dart create mode 100644 pkgs/http2/test/src/streams/helper.dart create mode 100644 pkgs/http2/test/src/streams/simple_flow_test.dart create mode 100644 pkgs/http2/test/src/streams/simple_push_test.dart create mode 100644 pkgs/http2/test/src/streams/streams_test.dart diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index c7e93fafb8..6dbdb2c0df 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -16,6 +16,7 @@ import 'frames/frames.dart'; import 'hpack/hpack.dart'; import 'ping/ping_handler.dart'; import 'settings/settings.dart'; +import 'streams/stream_handler.dart'; import 'sync_errors.dart'; import 'connection_preface.dart'; @@ -35,7 +36,7 @@ enum ConnectionState { Terminated, } -class Connection implements TransportConnection { +abstract class Connection { /// The settings the other end has acknowledged to use when communicating with /// us. final Settings acknowledgedSettings = new Settings(); @@ -77,6 +78,9 @@ class Connection implements TransportConnection { /// setting changes. SettingsHandler _settingsHandler; + /// The set of active streams this connection has. + StreamHandler _streams; + /// The connection-level flow control window handler for outgoing messages. OutgoingConnectionWindowHandler _connectionWindowHandler; @@ -87,18 +91,6 @@ class Connection implements TransportConnection { /// The state of this connection. ConnectionState _state; - factory Connection.client(Stream> incoming, - StreamSink> outgoing) { - outgoing.add(CONNECTION_PREFACE); - return new Connection(incoming, outgoing, isClientConnection: true); - } - - factory Connection.server(Stream> incoming, - StreamSink> outgoing) { - var frameBytes = readConnectionPreface(incoming); - return new Connection(frameBytes, outgoing, isClientConnection: false); - } - Connection(Stream> incoming, StreamSink> outgoing, {this.isClientConnection: true}) { @@ -158,6 +150,16 @@ class Connection implements TransportConnection { _incomingQueue = new ConnectionMessageQueueIn( connectionWindowUpdater); + if (isClientConnection) { + _streams = new StreamHandler.client( + _frameWriter, _incomingQueue, _outgoingQueue, + _settingsHandler.peerSettings, _settingsHandler.acknowledgedSettings); + } else { + _streams = new StreamHandler.server( + _frameWriter, _incomingQueue, _outgoingQueue, + _settingsHandler.peerSettings, _settingsHandler.acknowledgedSettings); + } + // NOTE: We're not waiting until initial settings have been exchanged // before we start using the connection (i.e. we don't wait for half a // round-trip-time). @@ -219,10 +221,12 @@ class Connection implements TransportConnection { } // Update highest stream id we received. + // TODO: This should only be done for "processed" streams. + // So we should ask the StreamSet for what Ids it has processed. _highestStreamIdReceived = max(_highestStreamIdReceived, frame.header.streamId); - // Handle the frame as either a stream or a connection frame. + // Handle the frame as either a connection or a stream frame. if (frame.header.streamId == 0) { if (frame is SettingsFrame) { _settingsHandler.handleSettingsFrame(frame); @@ -241,12 +245,13 @@ class Connection implements TransportConnection { } else { // We will not process frames for stream id's which are higher than when // we sent the [GoawayFrame]. + // TODO/FIXME: Isn't this the responsibility of the StreamSet. It + // should also send RST/... if (_state == ConnectionState.Finishing && frame.header.streamId > highestSeenStreamId) { return; } - - // TODO: Hand frame over to StreamHandler. + _streams.processStreamFrame(frame); } } @@ -296,6 +301,10 @@ class Connection implements TransportConnection { var exception = new TransportConnectionException( errorCode, 'Connection is being forcefully terminated.'); + // Close all streams & stream queues + _streams.terminate(exception); + + // Close the connection queues _incomingQueue.terminate(exception); _outgoingQueue.terminate(exception); @@ -311,3 +320,35 @@ class Connection implements TransportConnection { int get highestSeenStreamId => max(_highestStreamIdReceived, _frameWriter.highestWrittenStreamId); } + + +class ClientConnection extends Connection implements ClientTransportConnection { + ClientConnection._(Stream> incoming, + StreamSink> outgoing) + : super(incoming, outgoing, isClientConnection: true); + + factory ClientConnection(Stream> incoming, + StreamSink> outgoing) { + outgoing.add(CONNECTION_PREFACE); + return new ClientConnection._(incoming, outgoing); + } + + TransportStream makeRequest(List
headers, {bool endStream: false}) { + TransportStream hStream = _streams.newStream(headers, endStream: endStream); + return hStream; + } +} + +class ServerConnection extends Connection implements ServerTransportConnection { + ServerConnection._(Stream> incoming, + StreamSink> outgoing) + : super(incoming, outgoing, isClientConnection: false); + + factory ServerConnection(Stream> incoming, + StreamSink> outgoing) { + var frameBytes = readConnectionPreface(incoming); + return new ServerConnection._(frameBytes, outgoing); + } + + Stream get incomingStreams => _streams.incomingStreams; +} diff --git a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart index 5a1e2d77b6..d4197c282d 100644 --- a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart @@ -86,7 +86,7 @@ class ConnectionMessageQueueOut extends Object with TerminatableMixin { _frameWriter.writeHeadersFrame( message.streamId, message.headers, endStream: message.endStream); } else if (message is PushPromiseMessage) { - _messages.removeFirst(); + _messages.removeFirst(); _frameWriter.writePushPromiseFrame( message.streamId, message.promisedStreamId, message.headers); } else if (message is DataMessage) { @@ -205,7 +205,7 @@ class ConnectionMessageQueueIn extends Object with TerminatableMixin { void processHeadersFrame(HeadersFrame frame) { var streamId = frame.header.streamId; var message = new HeadersMessage( - streamId, frame.decodedHeaders, frame.hasEndHeadersFlag); + streamId, frame.decodedHeaders, frame.hasEndStreamFlag); // NOTE: Header frames do not affect flow control - only data frames do. _addMessage(streamId, message); } diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart new file mode 100644 index 0000000000..233d9ef39d --- /dev/null +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -0,0 +1,485 @@ +// 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. + +library http2.src.stream_handler; + +import 'dart:async'; + +import '../../transport.dart'; + +import '../flowcontrol/connection_queues.dart'; +import '../flowcontrol/stream_queues.dart'; +import '../flowcontrol/queue_messages.dart'; +import '../flowcontrol/window.dart'; +import '../flowcontrol/window_handler.dart'; +import '../frames/frames.dart'; +import '../hpack/hpack.dart'; +import '../settings/settings.dart'; +import '../error_handler.dart'; + +/// Represents the current state of a stream. +enum StreamState { + ReservedLocal, + ReservedRemote, + Idle, + Open, + HalfClosedLocal, + HalfClosedRemote, + Closed, + + /// The [Terminated] state is an artificial state and signals that this stream + /// has been forcefully terminated. + Terminated, +} + +/// Represents a HTTP/2 stream. +class Http2StreamImpl extends TransportStream + implements ClientTransportStream, + ServerTransportStream { + /// The id of this stream. + /// + /// * odd numbered streams are client streams + /// * even numbered streams are opened from the server + final int id; + + // The queue for incoming [StreamMessage]s. + final StreamMessageQueueIn incomingQueue; + + // The queue for outgoing [StreamMessage]s. + final StreamMessageQueueOut outgoingQueue; + + // The stream controller to which the application can + // add outgoing messages. + final StreamController _outgoingC; + + final OutgoingStreamWindowHandler windowHandler; + + // The state of this stream. + StreamState state = StreamState.Idle; + + final Function _pushStreamFun; + + StreamSubscription _outgoingCSubscription; + + Http2StreamImpl(this.incomingQueue, + this.outgoingQueue, + this._outgoingC, + this.id, + this.windowHandler, + this._pushStreamFun); + + /// A stream of data and/or headers from the remote end. + Stream get incomingMessages => incomingQueue.messages; + + /// A sink for writing data and/or headers to the remote end. + StreamSink get outgoingMessages => _outgoingC.sink; + + /// Streams which the server pushed to this endpoint. + Stream get peerPushes => incomingQueue.serverPushes; + + /// Pushes a new stream to a client. + /// + /// The [requestHeaders] are the headers to which the pushed stream + /// responds to. + TransportStream push(List
requestHeaders) + => _pushStreamFun(this, requestHeaders); + + void terminate() { + _outgoingCSubscription.cancel(); + } +} + +/// Handles [Frame]s with a non-zero stream-id. +/// +/// It keeps track of open streams, their state, their queues, forwards +/// messages from the connectionn level to stream level and vise versa. +class StreamHandler extends Object with TerminatableMixin { + static const int MAX_STREAM_ID = (1 << 31) - 1; + final FrameWriter _frameWriter; + final ConnectionMessageQueueIn incomingQueue; + final ConnectionMessageQueueOut outgoingQueue; + + final StreamController _newStreamsC = new StreamController(); + + final Settings _peerSettings; + final Settings _localSettings; + + final Map _openStreams = {}; + int nextStreamId; + int lastRemoteStreamId; + + StreamHandler._(this._frameWriter, this.incomingQueue, this.outgoingQueue, + this._peerSettings, this._localSettings, + this.nextStreamId, this.lastRemoteStreamId); + + factory StreamHandler.client(FrameWriter writer, + ConnectionMessageQueueIn incomingQueue, + ConnectionMessageQueueOut outgoingQueue, + Settings peerSettings, Settings localSettings) { + return new StreamHandler._( + writer, incomingQueue, outgoingQueue, peerSettings, localSettings, + 1, 0); + } + + factory StreamHandler.server(FrameWriter writer, + ConnectionMessageQueueIn incomingQueue, + ConnectionMessageQueueOut outgoingQueue, + Settings peerSettings, Settings localSettings) { + return new StreamHandler._( + writer, incomingQueue, outgoingQueue, peerSettings, localSettings, + 2, -1); + } + + void onTerminated(exception) { + _openStreams.values.toList().forEach( + (stream) => _closeStreamAbnormally(stream, exception)); + + // Signal that there are no more incoming connections (server case). + // FIXME: Should we do this always (even in client case?) + _newStreamsC..addError(exception)..close(); + } + + Stream get incomingStreams => _newStreamsC.stream; + + List get openStreams => _openStreams.values.toList(); + + void processInitialWindowSizeSettingChange(int difference) { + // If the initialFlowWindow size was changed via a SettingsFrame, all + // existing streams must be updated to reflect this change. + _openStreams.values.forEach((Http2StreamImpl stream) { + stream.windowHandler.processInitialWindowSizeSettingChange(difference); + }); + } + + + //////////////////////////////////////////////////////////////////////////// + //// New local/remote Stream handling + //////////////////////////////////////////////////////////////////////////// + + TransportStream newStream(List
headers, {bool endStream: false}) { + return ensureNotTerminatedSync(() { + var stream = newLocalStream(); + _sendHeaders(stream, headers); + if (endStream) { + _handleOutgoingClose(stream); + } + return stream; + }); + } + + TransportStream newLocalStream() { + return ensureNotTerminatedSync(() { + if (MAX_STREAM_ID < (nextStreamId + 2)) { + throw new StateError( + 'Cannot create new streams, since a wrap around would happen.'); + } + int streamId = nextStreamId; + nextStreamId += 2; + return _newStreamInternal(streamId); + }); + } + + TransportStream newRemoteStream(int remoteStreamId) { + return ensureNotTerminatedSync(() { + assert (MAX_STREAM_ID < remoteStreamId); + if (remoteStreamId != (lastRemoteStreamId + 2)) { + // FIXME: Is this check ok? Can't there be holes in the streams? + throw new StateError( + 'Remote end tries to create new stream which is not 2 higher than ' + 'last one.'); + } + bool sameDirection = (nextStreamId + remoteStreamId) % 2 == 0; + assert (remoteStreamId < lastRemoteStreamId); + assert (!sameDirection); + + lastRemoteStreamId = remoteStreamId; + return _newStreamInternal(remoteStreamId); + }); + } + + Http2StreamImpl _newStreamInternal(int streamId) { + // For each new stream we must: + // - setup sending/receiving [Window]s with correct initial size + // - setup sending/receiving WindowHandlers which take care of + // updating the windows. + // - setup incoming/outgoing stream queues, which buffer data + // that is not handled by + // * the application [incoming] + // * the underlying transport [outgoing] + // - register incoming stream queue in connection-level queue + + var outgoingStreamWindow = new Window( + initialSize: _peerSettings.initialWindowSize); + + var incomingStreamWindow = new Window( + initialSize: _localSettings.initialWindowSize); + + var windowOutHandler = new OutgoingStreamWindowHandler( + outgoingStreamWindow); + + var windowInHandler = new IncomingWindowHandler.stream( + _frameWriter, incomingStreamWindow, streamId); + + var streamQueueIn = new StreamMessageQueueIn(windowInHandler); + var streamQueueOut = new StreamMessageQueueOut( + streamId, windowOutHandler, outgoingQueue); + + incomingQueue.insertNewStreamMessageQueue(streamId, streamQueueIn); + + var _outgoingC = new StreamController(); + var stream = new Http2StreamImpl( + streamQueueIn, streamQueueOut, _outgoingC, streamId, windowOutHandler, + this._push); + _openStreams[stream.id] = stream; + + _setupOutgoingMessageHandling(stream); + + return stream; + } + + TransportStream _push(Http2StreamImpl stream, List
requestHeaders) { + if (stream.state != StreamState.Open && + stream.state != StreamState.HalfClosedRemote) { + throw new StateError('Cannot push based on a stream that is neither open ' + 'nor half-closed-remote.'); + } + + Http2StreamImpl pushStream = newLocalStream(); + pushStream.incomingQueue.enqueueMessage( + new DataMessage(stream.id, const [], true)); + pushStream.state = StreamState.HalfClosedRemote; + + _frameWriter.writePushPromiseFrame( + stream.id, pushStream.id, requestHeaders); + + return pushStream; + } + + void _setupOutgoingMessageHandling(Http2StreamImpl stream) { + stream._outgoingCSubscription = + stream._outgoingC.stream.listen((StreamMessage msg) { + if (!wasTerminated) { + _handleNewOutgoingMessage(stream, msg); + } + }, onError: (error, stack) { + if (!wasTerminated) { + stream.terminate(); + } + }, onDone: () { + if (!wasTerminated) { + // TODO: We should really allow the endStream flag to be send with + // the last headers/data frame. Should we add it to [StreamMessage]? + _handleOutgoingClose(stream); + } + }); + stream.outgoingQueue.bufferIndicator.bufferEmptyEvents.listen((_) { + if (stream._outgoingCSubscription.isPaused) { + stream._outgoingCSubscription.resume(); + } + }); + } + + void _handleNewOutgoingMessage(Http2StreamImpl stream, StreamMessage msg) { + if (stream.state == StreamState.Idle) { + if (msg is! HeadersStreamMessage) { + var exception = new TransportException( + 'The first message on a stream needs to be a headers frame.'); + _closeStreamAbnormally(stream, exception); + return; + } + stream.state = StreamState.Open; + } + + if (msg is DataStreamMessage) { + _sendData(stream, msg.bytes, endStream: false); + } else if (msg is HeadersStreamMessage) { + _sendHeaders(stream, msg.headers, endStream: false); + } + + if (stream.outgoingQueue.bufferIndicator.wouldBuffer && + !stream._outgoingCSubscription.isPaused) { + stream._outgoingCSubscription.pause(); + } + } + + void _handleOutgoingClose(Http2StreamImpl stream) { + // We allow multiple close calls. + if (stream.state != StreamState.Closed) { + _sendData(stream, const [], endStream: true); + } + } + + //////////////////////////////////////////////////////////////////////////// + //// Process incoming stream frames + //////////////////////////////////////////////////////////////////////////// + + void processStreamFrame(Frame frame) { + return ensureNotTerminatedSync(() { + var stream = _openStreams[frame.header.streamId]; + if (stream == null) { + if (frame is HeadersFrame) { + Http2StreamImpl newStream = newRemoteStream(frame.header.streamId); + var halfClosed = frame.hasEndStreamFlag; + if (halfClosed) { + newStream.state = StreamState.HalfClosedRemote; + } else { + newStream.state = StreamState.Open; + } + + _handleHeadersFrame(newStream, frame); + _newStreamsC.add(newStream); + } else if (frame is WindowUpdateFrame) { + // FIXME/TODO: Can we ignore these? + } else { + throw new StateError('No open stream found & was not headers frame.'); + } + } else { + if (frame is HeadersFrame) { + _handleHeadersFrame(stream, frame); + } else if (frame is DataFrame) { + _handleDataFrame(stream, frame); + } else if (frame is PushPromiseFrame) { + _handlePushPromiseFrame(stream, frame); + } else if (frame is WindowUpdateFrame) { + _handleWindowUpdate(stream, frame); + } else { + throw new StateError('Unsupported frame type ${frame.runtimeType}.'); + } + } + }); + } + + void _handleHeadersFrame(Http2StreamImpl stream, HeadersFrame frame) { + if (stream.state == StreamState.ReservedRemote) { + stream.state = StreamState.HalfClosedLocal; + } + + if (stream.state != StreamState.Open && + stream.state != StreamState.HalfClosedLocal) { + throw new StateError('Expected open state (was: ${stream.state}).'); + } + + incomingQueue.processHeadersFrame(frame); + + if (frame.hasEndStreamFlag) _handleEndOfStreamRemote(stream); + } + + void _handleDataFrame(Http2StreamImpl stream, DataFrame frame) { + if (stream.state != StreamState.Open && + stream.state != StreamState.HalfClosedLocal) { + throw new StateError('Expected open state (was: ${stream.state}).'); + } + + incomingQueue.processDataFrame(frame); + + if (frame.hasEndStreamFlag) _handleEndOfStreamRemote(stream); + } + + void _handlePushPromiseFrame(Http2StreamImpl stream, PushPromiseFrame frame) { + if (stream.state != StreamState.Open && + stream.state != StreamState.HalfClosedLocal) { + throw new StateError('Expected open state (was: ${stream.state}).'); + } + + var pushStream = newRemoteStream(frame.promisedStreamId); + pushStream.state = StreamState.ReservedRemote; + + incomingQueue.processPushPromiseFrame(frame); + } + + void _handleWindowUpdate(Http2StreamImpl stream, WindowUpdateFrame frame) { + stream.windowHandler.processWindowUpdate(frame); + } + + void _handleEndOfStreamRemote(Http2StreamImpl stream) { + if (stream.state == StreamState.Open) { + stream.state = StreamState.HalfClosedRemote; + } else if (stream.state == StreamState.HalfClosedLocal) { + stream.state = StreamState.Closed; + // TODO: We have to make sure that we + // - remove the stream for data structures which only care about the + // state + // - keep the stream in data structures which need to be emptied + // (e.g. MessageQueues which are not empty yet). + _openStreams.remove(stream.id); + } else { + throw new StateError( + 'Got an end-of-stream from the remote end, but this stream is ' + 'neither in the Open nor in the HalfClosedLocal state.'); + } + } + + + //////////////////////////////////////////////////////////////////////////// + //// Process outgoing stream messages + //////////////////////////////////////////////////////////////////////////// + + void _sendHeaders(Http2StreamImpl stream, List
headers, + {bool endStream: false}) { + if (stream.state != StreamState.Idle && + stream.state != StreamState.Open && + stream.state != StreamState.HalfClosedRemote) { + throw new StateError('Idle state expected.'); + } + + stream.outgoingQueue.enqueueMessage( + new HeadersMessage(stream.id, headers, endStream)); + + if (stream.state == StreamState.Idle) { + stream.state = StreamState.Open; + } + + if (endStream) { + _endStream(stream); + } + } + + void _sendData(Http2StreamImpl stream, List data, + {bool endStream: false}) { + if (stream.state != StreamState.Open && + stream.state != StreamState.HalfClosedRemote) { + throw new StateError('Open state expected (was: ${stream.state}).'); + } + + stream.outgoingQueue.enqueueMessage( + new DataMessage(stream.id, data, endStream)); + + if (endStream) { + _endStream(stream); + } + } + + void _endStream(Http2StreamImpl stream) { + if (stream.state == StreamState.Open) { + stream.state = StreamState.HalfClosedLocal; + } else if (stream.state == StreamState.HalfClosedRemote) { + stream.state = StreamState.Closed; + } else { + throw new StateError('unknown state transition'); + } + if (stream.state == StreamState.Closed) { + _cleanupClosedStream(stream); + } + } + + //////////////////////////////////////////////////////////////////////////// + //// Stream closing + //////////////////////////////////////////////////////////////////////////// + + void _cleanupClosedStream(Http2StreamImpl stream) { + incomingQueue.removeStreamMessageQueue(stream.id); + _openStreams.remove(stream.id); + } + + void _closeStreamAbnormally(Http2StreamImpl stream, Exception exception) { + incomingQueue.removeStreamMessageQueue(stream.id); + + stream.state = StreamState.Terminated; + stream.incomingQueue.terminate(exception); + stream._outgoingCSubscription.cancel(); + + // NOTE: we're not adding an error here. + stream.outgoingQueue.terminate(); + } +} diff --git a/pkgs/http2/lib/transport.dart b/pkgs/http2/lib/transport.dart index d5516247d5..6083d29973 100644 --- a/pkgs/http2/lib/transport.dart +++ b/pkgs/http2/lib/transport.dart @@ -14,22 +14,41 @@ export 'src/hpack/hpack.dart' show Header; /// Represents a HTTP/2 connection. abstract class TransportConnection { - factory TransportConnection.clientViaSocket(Socket socket) - => new TransportConnection.clientViaStreams(socket, socket); + /// Pings the other end. + Future ping(); - factory TransportConnection.serverViaSocket(Socket socket) - => new TransportConnection.serverViaStreams(socket, socket); + /// Finish this connection. + /// + /// No new streams will be accepted or can be created. + void finish(); - factory TransportConnection.clientViaStreams(Stream> incoming, - Sink> outgoing) - => new Connection.client(incoming, outgoing); + /// Terminates this connection forcefully. + Future terminate(); +} - factory TransportConnection.serverViaStreams(Stream> incoming, +abstract class ClientTransportConnection extends TransportConnection { + factory ClientTransportConnection.viaSocket(Socket socket) + => new ClientTransportConnection.viaStreams(socket, socket); + + factory ClientTransportConnection.viaStreams(Stream> incoming, Sink> outgoing) - => new Connection.server(incoming, outgoing); + => new ClientConnection(incoming, outgoing); - /// Pings the other end. - Future ping(); + /// Creates a new outgoing stream. + ClientTransportStream makeRequest(List
headers, + {bool endStream: false}); +} + +abstract class ServerTransportConnection extends TransportConnection { + factory ServerTransportConnection.viaSocket(Socket socket) + => new ServerTransportConnection.viaStreams(socket, socket); + + factory ServerTransportConnection.viaStreams(Stream> incoming, + Sink> outgoing) + => new ServerConnection(incoming, outgoing); + + /// Incoming HTTP/2 streams. + Stream get incomingStreams; /// Finish this connection. /// @@ -42,8 +61,50 @@ abstract class TransportConnection { /// Represents a HTTP/2 stream. abstract class TransportStream { - // TODO: Populate this class. + /// The id of this stream. + /// + /// * odd numbered streams are client streams + /// * even numbered streams are opened from the server + int get id; + + /// A stream of data and/or headers from the remote end. + Stream get incomingMessages; + + /// A sink for writing data and/or headers to the remote end. + StreamSink get outgoingMessages; + + /// Terminates this HTTP/2 stream in an un-normal way. + /// + /// For normal termination, one can cancel the [StreamSubscription] from + /// `incoming.listen()` and close the `outgoing` [StreamSink]. + /// + /// Terminating this HTTP/2 stream will free up all resources associated with + /// it locally and will notify the remote end that this stream is no longer + /// used. + void terminate(); + + // For convenience only. + void sendHeaders(List
headers, {bool endStream: false}) { + outgoingMessages.add(new HeadersStreamMessage(headers)); + if (endStream) outgoingMessages.close(); + } + + void sendData(List bytes, {bool endStream: false}) { + outgoingMessages.add(new DataStreamMessage(bytes)); + if (endStream) outgoingMessages.close(); + } } + +abstract class ClientTransportStream extends TransportStream { + /// Streams which the remote end pushed to this endpoint. + Stream get peerPushes; +} + +abstract class ServerTransportStream extends TransportStream { + /// Pushes a new stream to the remote peer. + TransportStream push(List
requestHeaders); +} + /// Represents a message which can be sent over a HTTP/2 stream. abstract class StreamMessage {} @@ -98,3 +159,9 @@ class TransportConnectionException extends TransportException { TransportConnectionException(this.errorCode, String details) : super('Connection error: $details'); } + +/// An exception thrown when a HTTP/2 connection error occured. +class StreamTransportException extends TransportException { + StreamTransportException(String details) + : super('Stream error: $details'); +} diff --git a/pkgs/http2/test/src/streams/helper.dart b/pkgs/http2/test/src/streams/helper.dart new file mode 100644 index 0000000000..a60a5ebafa --- /dev/null +++ b/pkgs/http2/test/src/streams/helper.dart @@ -0,0 +1,58 @@ +// 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. + +library http2.test.end2end_test; + +import 'dart:async'; + +import 'package:unittest/unittest.dart'; + +import 'package:http2/transport.dart'; +import 'package:http2/src/frames/frames.dart'; +import 'package:http2/src/connection.dart'; +import 'package:http2/src/settings/settings.dart'; + +expectHeadersEqual(List
headers, List
expectedHeaders) { + expect(headers, hasLength(expectedHeaders.length)); + for (int i = 0; i < expectedHeaders.length; i++) { + expect(headers[i].name, expectedHeaders[i].name); + expect(headers[i].value, expectedHeaders[i].value); + } +} + +expectEmptyStream(Stream s) { + s.listen(expectAsync((_) {}, count: 0), onDone: expectAsync(() {})); +} + +streamTest(String name, func(client, server)) { + return test(name, () { + var bidirect = new BidirectionalConnection(); + var client = bidirect.clientConnection; + var server = bidirect.serverConnection; + return func(client, server); + }); +} + +framesTest(String name, func(frameWriter, frameStream)) { + return test(name, () { + var c = new StreamController(); + var fw = new FrameWriter(null, c, new Settings()); + var frameStream = new FrameReader(c.stream, new Settings()); + + return func(fw, frameStream); + }); +} + +class BidirectionalConnection { + final StreamController> writeA = new StreamController(); + final StreamController> writeB = new StreamController(); + Stream> get readA => writeA.stream; + Stream> get readB => writeB.stream; + + ClientTransportConnection get clientConnection + => new ClientTransportConnection.viaStreams(readA, writeB); + + ServerTransportConnection get serverConnection + => new ServerTransportConnection.viaStreams(readB, writeA); +} diff --git a/pkgs/http2/test/src/streams/simple_flow_test.dart b/pkgs/http2/test/src/streams/simple_flow_test.dart new file mode 100644 index 0000000000..d5356c90e1 --- /dev/null +++ b/pkgs/http2/test/src/streams/simple_flow_test.dart @@ -0,0 +1,92 @@ +// 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. + +library http2.test.streams.simple_flow_test; + +import 'dart:async'; + +import 'package:unittest/unittest.dart'; +import 'package:http2/transport.dart'; + +import 'helper.dart'; + +main() { + group('streams', () { + group('flowcontrol', () { + const int numOfOneKB = 1000; + + var expectedHeaders = [new Header.ascii('key', 'value')]; + var allBytes = new List.generate(numOfOneKB * 1024, (i) => i % 256); + allBytes.addAll(new List.generate(42, (i) => 42)); + + headersTestFun(String type) { + return expectAsync((StreamMessage msg) { + expect(msg is HeadersStreamMessage, isTrue); + expect((msg as HeadersStreamMessage).headers.first.name, + expectedHeaders.first.name); + expect((msg as HeadersStreamMessage).headers.first.value, + expectedHeaders.first.value); + }); + } + + Completer serverReceivedAllBytes = new Completer(); + + messageTestFun(String type) { + bool expectHeader = true; + int numBytesReceived = 0; + return (StreamMessage msg) { + if (expectHeader) { + expectHeader = false; + expect(msg is HeadersStreamMessage, isTrue); + expect((msg as HeadersStreamMessage).headers.first.name, + expectedHeaders.first.name); + expect((msg as HeadersStreamMessage).headers.first.value, + expectedHeaders.first.value); + } else { + expect(msg is DataStreamMessage, isTrue); + var bytes = (msg as DataStreamMessage).bytes; + expect(bytes, allBytes.sublist( + numBytesReceived, numBytesReceived + bytes.length)); + numBytesReceived += bytes.length; + + if (numBytesReceived == allBytes) { + if (serverReceivedAllBytes.isCompleted) { + throw new Exception('Got more messages than expected'); + } + serverReceivedAllBytes.complete(); + } + } + }; + } + + sendData(TransportStream cStream) { + for (int i = 0; i < (allBytes.length + 1023) ~/ 1024; i++) { + int end = 1024 * (i + 1); + bool isLast = end > allBytes.length; + if (isLast) { + end = allBytes.length; + } + cStream.sendData(allBytes.sublist(1024 * i, end), endStream: isLast); + } + } + + streamTest('single-header-request--empty-response', + (ClientTransportConnection client, + ServerTransportConnection server) async { + server.incomingStreams.listen( + expectAsync((TransportStream sStream) async { + sStream.incomingMessages.listen( + messageTestFun('server'), onDone: expectAsync(() { })); + sStream.sendHeaders(expectedHeaders, endStream: true); + expect(await serverReceivedAllBytes.future, completes); + })); + + TransportStream cStream = client.makeRequest(expectedHeaders); + sendData(cStream); + cStream.incomingMessages.listen( + headersTestFun('client'), onDone: expectAsync(() {})); + }); + }); + }); +} diff --git a/pkgs/http2/test/src/streams/simple_push_test.dart b/pkgs/http2/test/src/streams/simple_push_test.dart new file mode 100644 index 0000000000..7af6ae9e21 --- /dev/null +++ b/pkgs/http2/test/src/streams/simple_push_test.dart @@ -0,0 +1,90 @@ +// 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. + +library http2.test.streams.simple_push_test; + +import 'dart:async'; +import 'dart:convert'; + +import 'package:unittest/unittest.dart'; +import 'package:http2/transport.dart'; + +import 'helper.dart'; + +main() { + group('streams', () { + group('server-push', () { + const int numOfOneKB = 1000; + + var expectedHeaders = [new Header.ascii('key', 'value')]; + var allBytes = new List.generate(numOfOneKB * 1024, (i) => i % 256); + allBytes.addAll(new List.generate(42, (i) => 42)); + + headersTestFun() { + return expectAsync((StreamMessage msg) { + expect(msg is HeadersStreamMessage, isTrue); + expect((msg as HeadersStreamMessage).headers.first.name, + expectedHeaders.first.name); + expect((msg as HeadersStreamMessage).headers.first.value, + expectedHeaders.first.value); + }); + } + + Completer serverReceivedAllBytes = new Completer(); + + Future readData(StreamIterator iterator) async { + var all = []; + + while (await iterator.moveNext()) { + var msg = iterator.current; + expect(msg is DataStreamMessage, isTrue); + all.addAll(msg.bytes); + } + + return UTF8.decode(all); + } + + + Future sendData(TransportStream stream, String data) { + stream.outgoingMessages + ..add(new DataStreamMessage(UTF8.encode(data))) + ..close(); + return stream.outgoingMessages.done; + } + + streamTest('server-push', + (ClientTransportConnection client, + ServerTransportConnection server) async { + server.incomingStreams.listen( + expectAsync((ServerTransportStream sStream) async { + sStream.incomingMessages.drain(); + + sStream.sendHeaders(expectedHeaders, endStream: true); + + var pushStream = sStream.push(expectedHeaders); + pushStream.sendHeaders(expectedHeaders); + await sendData(pushStream, 'pushing "hello world" :)'); + + expect(await serverReceivedAllBytes.future, completes); + })); + + ClientTransportStream cStream = + client.makeRequest(expectedHeaders, endStream: true); + cStream.incomingMessages.listen( + headersTestFun(), onDone: expectAsync(() { })); + cStream.peerPushes.listen(expectAsync((TransportStreamPush push) async { + expect(push.requestHeaders, expectedHeaders); + + var iterator = new StreamIterator(push.stream.incomingMessages); + bool hasNext = await iterator.moveNext(); + expect(hasNext, isTrue); + headersTestFun()(iterator.current); + + String msg = await readData(iterator); + expect(msg, 'pushing "hello iworld" :)'); + })); + }); + }); + }); +} diff --git a/pkgs/http2/test/src/streams/streams_test.dart b/pkgs/http2/test/src/streams/streams_test.dart new file mode 100644 index 0000000000..f5b258c61c --- /dev/null +++ b/pkgs/http2/test/src/streams/streams_test.dart @@ -0,0 +1,173 @@ +// 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. + +library http2.test.end2end_test; + +import 'package:unittest/unittest.dart'; +import 'package:http2/transport.dart'; + +import 'helper.dart'; + +main() { + group('streams', () { + streamTest('single-header-request--empty-response', + (ClientTransportConnection client, + ServerTransportConnection server) async { + var expectedHeaders = [new Header.ascii('key', 'value')]; + + server.incomingStreams.listen(expectAsync((TransportStream sStream) { + sStream.incomingMessages.listen(expectAsync((StreamMessage msg) { + expect(msg is HeadersStreamMessage, isTrue); + + HeadersStreamMessage headersMsg = msg; + expectHeadersEqual(headersMsg.headers, expectedHeaders); + }), onDone: expectAsync(() {})); + sStream.outgoingMessages.close(); + })); + + TransportStream cStream = + client.makeRequest(expectedHeaders, endStream: true); + expectEmptyStream(cStream.incomingMessages); + }); + + streamTest('multi-header-request--empty-response', + (ClientTransportConnection client, + ServerTransportConnection server) async { + var expectedHeaders = [new Header.ascii('key', 'value')]; + + server.incomingStreams.listen(expectAsync((TransportStream sStream) { + sStream.incomingMessages.listen(expectAsync((StreamMessage msg) { + expect(msg is HeadersStreamMessage, isTrue); + + HeadersStreamMessage headersMsg = msg; + expectHeadersEqual(headersMsg.headers, expectedHeaders); + }, count: 3), onDone: expectAsync(() {})); + sStream.outgoingMessages.close(); + })); + + TransportStream cStream = client.makeRequest(expectedHeaders); + cStream.sendHeaders(expectedHeaders); + cStream.sendHeaders(expectedHeaders, endStream: true); + expectEmptyStream(cStream.incomingMessages); + }); + + streamTest('multi-data-request--empty-response', + (ClientTransportConnection client, + ServerTransportConnection server) async { + var expectedHeaders = [new Header.ascii('key', 'value')]; + var chunks = [[1], [2], [3]]; + + server.incomingStreams.listen( + expectAsync((TransportStream sStream) async { + bool isFirst = true; + var receivedChunks = []; + sStream.incomingMessages.listen(expectAsync((StreamMessage msg) { + if (isFirst) { + isFirst = false; + expect(msg is HeadersStreamMessage, isTrue); + + HeadersStreamMessage headersMsg = msg; + expectHeadersEqual(headersMsg.headers, expectedHeaders); + } else { + expect(msg is DataStreamMessage, isTrue); + + DataStreamMessage dataMsg = msg; + receivedChunks.add(dataMsg.bytes); + } + }, count: 1 + chunks.length), onDone: expectAsync(() { + expect(receivedChunks, chunks); + })); + sStream.outgoingMessages.close(); + })); + + TransportStream cStream = client.makeRequest(expectedHeaders); + chunks.forEach(cStream.sendData); + cStream.outgoingMessages.close(); + expectEmptyStream(cStream.incomingMessages); + }); + + streamTest('single-header-request--single-headers-response', + (ClientTransportConnection client, + ServerTransportConnection server) async { + var expectedHeaders = [new Header.ascii('key', 'value')]; + + server.incomingStreams.listen(expectAsync((TransportStream sStream) { + sStream.incomingMessages.listen(expectAsync((StreamMessage msg) { + expect(msg is HeadersStreamMessage, isTrue); + + HeadersStreamMessage headersMsg = msg; + expectHeadersEqual(headersMsg.headers, expectedHeaders); + }), onDone: expectAsync(() {})); + sStream.sendHeaders(expectedHeaders, endStream: true); + })); + + TransportStream cStream = + client.makeRequest(expectedHeaders, endStream: true); + + cStream.incomingMessages.listen(expectAsync((StreamMessage msg) { + expect(msg is HeadersStreamMessage, isTrue); + + HeadersStreamMessage headersMsg = msg; + expectHeadersEqual(headersMsg.headers, expectedHeaders); + }), onDone: expectAsync(() {})); + }); + + streamTest('single-header-request--multi-headers-response', + (ClientTransportConnection client, + ServerTransportConnection server) async { + var expectedHeaders = [new Header.ascii('key', 'value')]; + + server.incomingStreams.listen(expectAsync((TransportStream sStream) { + sStream.incomingMessages.listen(expectAsync((StreamMessage msg) { + expect(msg is HeadersStreamMessage, isTrue); + + HeadersStreamMessage headersMsg = msg; + expectHeadersEqual(headersMsg.headers, expectedHeaders); + }), onDone: expectAsync(() {})); + + sStream.sendHeaders(expectedHeaders); + sStream.sendHeaders(expectedHeaders); + sStream.sendHeaders(expectedHeaders, endStream: true); + })); + + TransportStream cStream = + client.makeRequest(expectedHeaders, endStream: true); + + cStream.incomingMessages.listen(expectAsync((StreamMessage msg) { + expect(msg is HeadersStreamMessage, isTrue); + + HeadersStreamMessage headersMsg = msg; + expectHeadersEqual(headersMsg.headers, expectedHeaders); + }, count: 3)); + }); + + streamTest('single-header-request--multi-data-response', + (ClientTransportConnection client, + ServerTransportConnection server) async { + var expectedHeaders = [new Header.ascii('key', 'value')]; + var chunks = [[1], [2], [3]]; + + server.incomingStreams.listen(expectAsync((TransportStream sStream) { + sStream.incomingMessages.listen(expectAsync((StreamMessage msg) { + expect(msg is HeadersStreamMessage, isTrue); + + HeadersStreamMessage headersMsg = msg; + expectHeadersEqual(headersMsg.headers, expectedHeaders); + }), onDone: expectAsync(() {})); + + chunks.forEach(sStream.sendData); + sStream.outgoingMessages.close(); + })); + + TransportStream cStream = client.makeRequest(expectedHeaders); + cStream.outgoingMessages.close(); + + int i = 0; + cStream.incomingMessages.listen(expectAsync((StreamMessage msg) { + expect(msg is DataStreamMessage, isTrue); + expect((msg as DataStreamMessage).bytes, chunks[i++]); + }, count: chunks.length)); + }); + }); +} diff --git a/pkgs/http2/test/transport_test.dart b/pkgs/http2/test/transport_test.dart index 4b672ae8b4..a080872397 100644 --- a/pkgs/http2/test/transport_test.dart +++ b/pkgs/http2/test/transport_test.dart @@ -68,8 +68,8 @@ class BidirectionalConnection { Stream> get readB => writeB.stream; TransportConnection get clientConnection - => new TransportConnection.clientViaStreams(readA, writeB.sink); + => new ClientTransportConnection.viaStreams(readA, writeB.sink); TransportConnection get serverConnection - => new TransportConnection.serverViaStreams(readB, writeA.sink); + => new ServerTransportConnection.viaStreams(readB, writeA.sink); } From 56f9a79721aec5babcd21f013f6ba567a4d51bde Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Thu, 28 May 2015 16:07:42 +0200 Subject: [PATCH 015/172] Make server pushes work in client/server R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/202947013 --- .../src/flowcontrol/connection_queues.dart | 8 ++- .../lib/src/flowcontrol/queue_messages.dart | 5 +- .../lib/src/flowcontrol/stream_queues.dart | 55 ++++++++++---- .../http2/lib/src/streams/stream_handler.dart | 72 +++++++++++++++---- .../test/src/streams/simple_push_test.dart | 26 ++++--- 5 files changed, 121 insertions(+), 45 deletions(-) diff --git a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart index d4197c282d..e9790771f1 100644 --- a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart @@ -10,6 +10,8 @@ library http2.src.flowcontrol.connection_flow_controller; import 'dart:async'; import 'dart:collection'; +import '../../transport.dart'; + import '../error_handler.dart'; import '../frames/frames.dart'; @@ -212,10 +214,12 @@ class ConnectionMessageQueueIn extends Object with TerminatableMixin { /// Processes an incoming [PushPromiseFrame] which is addressed to a specific /// stream. - void processPushPromiseFrame(PushPromiseFrame frame) { + void processPushPromiseFrame(PushPromiseFrame frame, + TransportStream pushedStream) { var streamId = frame.header.streamId; var message = new PushPromiseMessage( - streamId, frame.decodedHeaders, frame.promisedStreamId, false); + streamId, frame.decodedHeaders, frame.promisedStreamId, pushedStream, + false); // NOTE: Header frames do not affect flow control - only data frames do. _addMessage(streamId, message); } diff --git a/pkgs/http2/lib/src/flowcontrol/queue_messages.dart b/pkgs/http2/lib/src/flowcontrol/queue_messages.dart index d8217be90a..d3331a3f05 100644 --- a/pkgs/http2/lib/src/flowcontrol/queue_messages.dart +++ b/pkgs/http2/lib/src/flowcontrol/queue_messages.dart @@ -41,10 +41,11 @@ class DataMessage extends Message { class PushPromiseMessage extends Message { final List
headers; final int promisedStreamId; + final TransportStream pushedStream; PushPromiseMessage( - int streamId, this.headers, this.promisedStreamId, bool endStream) - : super(streamId, endStream); + int streamId, this.headers, this.promisedStreamId, this.pushedStream, + bool endStream) : super(streamId, endStream); String toString() => 'PushPromiseMessage(bytes: ${headers.length}, ' diff --git a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart index 21bf79c2e4..465cc56f5d 100644 --- a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart @@ -223,28 +223,53 @@ class StreamMessageQueueIn extends Object with TerminatableMixin { } void _tryDispatch() { - while (_incomingMessagesC.hasListener && - !_incomingMessagesC.isPaused && - !_incomingMessagesC.isClosed && - !wasTerminated && + while (!wasTerminated && _pendingMessages.isNotEmpty) { - var message = _pendingMessages.removeFirst(); - if (message is HeadersMessage) { - // NOTE: Header messages do not affect flow control - only - // data messages do. - _incomingMessagesC.add(new HeadersStreamMessage(message.headers)); - } else if (message is DataMessage) { - if (message.bytes.length > 0) { - _incomingMessagesC.add(new DataStreamMessage(message.bytes)); - windowHandler.dataProcessed(message.bytes.length); + bool handled = false; + + var message = _pendingMessages.first; + if (message is HeadersMessage || message is DataMessage) { + assert (!_incomingMessagesC.isClosed); + if (_incomingMessagesC.hasListener && + !_incomingMessagesC.isPaused) { + _pendingMessages.removeFirst(); + if (message is HeadersMessage) { + // NOTE: Header messages do not affect flow control - only + // data messages do. + _incomingMessagesC.add(new HeadersStreamMessage(message.headers)); + } else if (message is DataMessage) { + if (message.bytes.length > 0) { + _incomingMessagesC.add(new DataStreamMessage(message.bytes)); + windowHandler.dataProcessed(message.bytes.length); + } + } + if (message.endStream) { + _close(); + } + handled = true; + } + } else if (message is PushPromiseMessage) { + assert (!_serverPushStreamsC.isClosed); + if (_serverPushStreamsC.hasListener && + !_serverPushStreamsC.isPaused) { + _pendingMessages.removeFirst(); + + var transportStreamPush = new TransportStreamPush( + message.headers, message.pushedStream); + _serverPushStreamsC.add(transportStreamPush); + if (message.endStream) { + _close(); + } + handled = true; } } - if (message.endStream) { - _close(); + if (!handled) { + break; } } } + void _tryUpdateBufferIndicator() { if (_incomingMessagesC.isPaused || _pendingMessages.length > 0) { bufferIndicator.markBuffered(); diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index 233d9ef39d..000e11d4c8 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -17,6 +17,7 @@ import '../frames/frames.dart'; import '../hpack/hpack.dart'; import '../settings/settings.dart'; import '../error_handler.dart'; +import '../sync_errors.dart'; /// Represents the current state of a stream. enum StreamState { @@ -94,6 +95,7 @@ class Http2StreamImpl extends TransportStream /// /// It keeps track of open streams, their state, their queues, forwards /// messages from the connectionn level to stream level and vise versa. +// TODO: Respect SETTINGS_MAX_CONCURRENT_STREAMS class StreamHandler extends Object with TerminatableMixin { static const int MAX_STREAM_ID = (1 << 31) - 1; final FrameWriter _frameWriter; @@ -109,6 +111,8 @@ class StreamHandler extends Object with TerminatableMixin { int nextStreamId; int lastRemoteStreamId; + bool get isServer => nextStreamId.isEven; + StreamHandler._(this._frameWriter, this.incomingQueue, this.outgoingQueue, this._peerSettings, this._localSettings, this.nextStreamId, this.lastRemoteStreamId); @@ -182,7 +186,7 @@ class StreamHandler extends Object with TerminatableMixin { TransportStream newRemoteStream(int remoteStreamId) { return ensureNotTerminatedSync(() { - assert (MAX_STREAM_ID < remoteStreamId); + assert (remoteStreamId < MAX_STREAM_ID); if (remoteStreamId != (lastRemoteStreamId + 2)) { // FIXME: Is this check ok? Can't there be holes in the streams? throw new StateError( @@ -190,7 +194,7 @@ class StreamHandler extends Object with TerminatableMixin { 'last one.'); } bool sameDirection = (nextStreamId + remoteStreamId) % 2 == 0; - assert (remoteStreamId < lastRemoteStreamId); + assert (lastRemoteStreamId < remoteStreamId ); assert (!sameDirection); lastRemoteStreamId = remoteStreamId; @@ -246,9 +250,13 @@ class StreamHandler extends Object with TerminatableMixin { } Http2StreamImpl pushStream = newLocalStream(); + + // NOTE: Since there was no real request from the client, we simulate it + // by adding a synthetic `endStream = true` Data message into the incoming + // queue. + pushStream.state = StreamState.HalfClosedRemote; pushStream.incomingQueue.enqueueMessage( new DataMessage(stream.id, const [], true)); - pushStream.state = StreamState.HalfClosedRemote; _frameWriter.writePushPromiseFrame( stream.id, pushStream.id, requestHeaders); @@ -315,22 +323,56 @@ class StreamHandler extends Object with TerminatableMixin { //////////////////////////////////////////////////////////////////////////// void processStreamFrame(Frame frame) { + // TODO: Consider splitting this method into client/server handling. return ensureNotTerminatedSync(() { var stream = _openStreams[frame.header.streamId]; if (stream == null) { + bool frameBelongsToIdleStream() { + int streamId = frame.header.streamId; + bool isServerStreamId = frame.header.streamId.isEven; + bool isLocalStream = isServerStreamId == isServer; + bool isIdleStream = isLocalStream ? + streamId >= nextStreamId : streamId > lastRemoteStreamId; + return isIdleStream; + } + if (frame is HeadersFrame) { - Http2StreamImpl newStream = newRemoteStream(frame.header.streamId); - var halfClosed = frame.hasEndStreamFlag; - if (halfClosed) { - newStream.state = StreamState.HalfClosedRemote; - } else { + if (isServer) { + Http2StreamImpl newStream = newRemoteStream(frame.header.streamId); newStream.state = StreamState.Open; - } - _handleHeadersFrame(newStream, frame); - _newStreamsC.add(newStream); + _handleHeadersFrame(newStream, frame); + _newStreamsC.add(newStream); + } else { + // A server cannot open new streams to the client. The only way + // for a server to start a new stream is via a PUSH_PROMISE_FRAME. + throw new ProtocolException( + 'HTTP/2 clients cannot receive HEADER_FRAMEs as a connection' + 'attempt.'); + } } else if (frame is WindowUpdateFrame) { - // FIXME/TODO: Can we ignore these? + if (frameBelongsToIdleStream()) { + // We treat this as a protocol error even though not enforced + // or specified by the HTTP/2 spec. + throw new ProtocolException( + 'Got a WINDOW_UPDATE_FRAME for an "idle" stream id.'); + } else { + // We must be able to receive window update frames for streams that + // have been already closed. The specification does not mention + // what happens if the streamId is belonging to an "idle" / unused + // stream. + } + } else if (frame is RstStreamFrame) { + if (frameBelongsToIdleStream()) { + // [RstFrame]s for streams which haven't been established (known as + // idle streams) must be treated as a connection error. + throw new ProtocolException( + 'Got a RST_STREAM_FRAME for an "idle" stream id.'); + } else { + // [RstFrame]s for already dead (known as "closed") streams should + // be ignored. (If the stream was in "HalfClosedRemote" and we did + // send an endStream=true, it will be removed from the stream set). + } } else { throw new StateError('No open stream found & was not headers frame.'); } @@ -382,10 +424,10 @@ class StreamHandler extends Object with TerminatableMixin { throw new StateError('Expected open state (was: ${stream.state}).'); } - var pushStream = newRemoteStream(frame.promisedStreamId); - pushStream.state = StreamState.ReservedRemote; + var pushedStream = newRemoteStream(frame.promisedStreamId); + pushedStream.state = StreamState.ReservedRemote; - incomingQueue.processPushPromiseFrame(frame); + incomingQueue.processPushPromiseFrame(frame, pushedStream); } void _handleWindowUpdate(Http2StreamImpl stream, WindowUpdateFrame frame) { diff --git a/pkgs/http2/test/src/streams/simple_push_test.dart b/pkgs/http2/test/src/streams/simple_push_test.dart index 7af6ae9e21..c6a3b5a459 100644 --- a/pkgs/http2/test/src/streams/simple_push_test.dart +++ b/pkgs/http2/test/src/streams/simple_push_test.dart @@ -21,13 +21,18 @@ main() { var allBytes = new List.generate(numOfOneKB * 1024, (i) => i % 256); allBytes.addAll(new List.generate(42, (i) => 42)); + testHeaders(List
headers) { + expect(headers.length, expectedHeaders.length); + for (int i = 0; i < headers.length; i++) { + expect(headers[i].name, expectedHeaders[i].name); + expect(headers[i].value, expectedHeaders[i].value); + } + } + headersTestFun() { return expectAsync((StreamMessage msg) { expect(msg is HeadersStreamMessage, isTrue); - expect((msg as HeadersStreamMessage).headers.first.name, - expectedHeaders.first.name); - expect((msg as HeadersStreamMessage).headers.first.value, - expectedHeaders.first.value); + testHeaders((msg as HeadersStreamMessage).headers); }); } @@ -58,14 +63,13 @@ main() { ServerTransportConnection server) async { server.incomingStreams.listen( expectAsync((ServerTransportStream sStream) async { - sStream.incomingMessages.drain(); - - sStream.sendHeaders(expectedHeaders, endStream: true); - var pushStream = sStream.push(expectedHeaders); pushStream.sendHeaders(expectedHeaders); await sendData(pushStream, 'pushing "hello world" :)'); + sStream.incomingMessages.drain(); + sStream.sendHeaders(expectedHeaders, endStream: true); + expect(await serverReceivedAllBytes.future, completes); })); @@ -74,15 +78,15 @@ main() { cStream.incomingMessages.listen( headersTestFun(), onDone: expectAsync(() { })); cStream.peerPushes.listen(expectAsync((TransportStreamPush push) async { - expect(push.requestHeaders, expectedHeaders); + testHeaders(push.requestHeaders); var iterator = new StreamIterator(push.stream.incomingMessages); bool hasNext = await iterator.moveNext(); expect(hasNext, isTrue); - headersTestFun()(iterator.current); + testHeaders(iterator.current.headers); String msg = await readData(iterator); - expect(msg, 'pushing "hello iworld" :)'); + expect(msg, 'pushing "hello world" :)'); })); }); }); From 0d29b027296709d2b9192a99f792c34f8d218379 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Thu, 28 May 2015 16:42:31 +0200 Subject: [PATCH 016/172] Add proof-of-concept HTTP/2 client and test google/twitter R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/203637013 --- pkgs/http2/lib/client.dart | 136 ++++++++++++++++++++++ pkgs/http2/lib/src/connection.dart | 31 +++-- pkgs/http2/lib/transport.dart | 12 +- pkgs/http2/test/client_websites_test.dart | 132 +++++++++++++++++++++ 4 files changed, 299 insertions(+), 12 deletions(-) create mode 100644 pkgs/http2/lib/client.dart create mode 100644 pkgs/http2/test/client_websites_test.dart diff --git a/pkgs/http2/lib/client.dart b/pkgs/http2/lib/client.dart new file mode 100644 index 0000000000..7c99970563 --- /dev/null +++ b/pkgs/http2/lib/client.dart @@ -0,0 +1,136 @@ +// 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. + +library http2.client; + +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'transport.dart'; + +class Request { + final String method; + final Uri uri; + + Request(this.method, this.uri); +} + +class Response { + final Map> headers; + final Stream> stream; + final Stream serverPushes; + + Response(this.headers, this.stream, this.serverPushes); +} + +class ServerPush { + final Map> requestHeaders; + final Future response; + + ServerPush(this.requestHeaders, this.response); +} + + +class ClientConnection { + final ClientTransportConnection connection; + + ClientConnection(Socket socket, {bool allowServerPushes: false}) + : connection = new ClientTransportConnection.viaSocket( + socket, allowServerPushes: allowServerPushes); + + Future makeRequest(Request request) { + var path = request.uri.path; + if (path.isEmpty) path = '/'; + + var headers = [ + new Header.ascii(':method', request.method), + new Header.ascii(':path', path), + new Header.ascii(':scheme', request.uri.scheme), + new Header.ascii(':authority', '${request.uri.host}'), + ]; + + return _handleStream(connection.makeRequest(headers, endStream: true)); + } + + Future close() { + return connection.terminate(); + } + + Future _handleStream(ClientTransportStream stream) { + var completer = new Completer(); + bool isFirst = true; + var controller = new StreamController(); + var serverPushController = new StreamController(sync: true); + stream.incomingMessages.listen((StreamMessage msg) { + if (isFirst) { + isFirst = false; + var headerMap = _convertHeaders((msg as HeadersStreamMessage).headers); + completer.complete(new Response( + headerMap, controller.stream, serverPushController.stream)); + } else { + controller.add((msg as DataStreamMessage).bytes); + } + }, onDone: controller.close); + _handlePeerPushes(stream.peerPushes).pipe(serverPushController); + return completer.future; + } + + Stream _handlePeerPushes( + Stream serverPushes) { + var pushesController = new StreamController(); + serverPushes.listen((TransportStreamPush push) { + var responseCompleter = new Completer(); + var serverPush = new ServerPush( + _convertHeaders(push.requestHeaders), responseCompleter.future); + + pushesController.add(serverPush); + + bool isFirst = true; + var dataController = new StreamController(); + push.stream.incomingMessages.listen((StreamMessage msg) { + if (isFirst) { + isFirst = false; + var headerMap = + _convertHeaders((msg as HeadersStreamMessage).headers); + var response = new Response( + headerMap, dataController.stream, new Stream.fromIterable([])); + responseCompleter.complete(response); + } else { + dataController.add((msg as DataStreamMessage).bytes); + } + }, onDone: dataController.close); + }, onDone: pushesController.close); + return pushesController.stream; + } + + Map> _convertHeaders(List
headers) { + var headerMap = {}; + for (var header in headers) { + headerMap.putIfAbsent(ASCII.decode(header.name), () => []) + .add(ASCII.decode(header.value)); + } + return headerMap; + } + +} + +Future connect( + Uri uri, {bool allowServerPushes: false}) async { + const List Http2AlpnProtocols = + const ['h2-14', 'h2-15', 'h2-16', 'h2-17', 'h2']; + + bool useSSL = uri.scheme == 'https'; + if (useSSL) { + SecureSocket socket = await SecureSocket.connect( + uri.host, uri.port, supportedProtocols: Http2AlpnProtocols); + if (!Http2AlpnProtocols.contains(socket.selectedProtocol)) { + throw new Exception('Server does not support HTTP/2.'); + } + return new ClientConnection(socket, allowServerPushes: allowServerPushes); + } else { + Socket socket = await Socket.connect(uri.host, uri.port); + return new ClientConnection(socket, allowServerPushes: allowServerPushes); + } +} diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index 6dbdb2c0df..34b016b8c2 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -47,6 +47,9 @@ abstract class Connection { /// Whether this connection is a client connection. final bool isClientConnection; + /// Whether server side pushes are allowed. + final bool pushEnabled; + /// The HPack context for this connection. final HPackContext _hpackContext = new HPackContext(); @@ -93,7 +96,8 @@ abstract class Connection { Connection(Stream> incoming, StreamSink> outgoing, - {this.isClientConnection: true}) { + {this.isClientConnection: true, + this.pushEnabled}) { _setupConnection(incoming, outgoing); } @@ -126,8 +130,13 @@ abstract class Connection { _frameWriter, acknowledgedSettings, peerSettings); _pingHandler = new PingHandler(_frameWriter); - // Do the initial settings handshake. - _settingsHandler.changeSettings([]).catchError((_) { + // Do the initial settings handshake (possibly with pushes disabled). + var settings = []; + if (isClientConnection && !pushEnabled) { + // By default the server is allowed to do server pushes. + settings.add(new Setting(Setting.SETTINGS_ENABLE_PUSH, 0)); + } + _settingsHandler.changeSettings(settings).catchError((_) { _terminate(ErrorCode.PROTOCOL_ERROR); }); @@ -324,13 +333,18 @@ abstract class Connection { class ClientConnection extends Connection implements ClientTransportConnection { ClientConnection._(Stream> incoming, - StreamSink> outgoing) - : super(incoming, outgoing, isClientConnection: true); + StreamSink> outgoing, + bool pushEnabled) + : super(incoming, + outgoing, + isClientConnection: true, + pushEnabled: pushEnabled); factory ClientConnection(Stream> incoming, - StreamSink> outgoing) { + StreamSink> outgoing, + {bool allowServerPushes: true}) { outgoing.add(CONNECTION_PREFACE); - return new ClientConnection._(incoming, outgoing); + return new ClientConnection._(incoming, outgoing, allowServerPushes); } TransportStream makeRequest(List
headers, {bool endStream: false}) { @@ -342,7 +356,8 @@ class ClientConnection extends Connection implements ClientTransportConnection { class ServerConnection extends Connection implements ServerTransportConnection { ServerConnection._(Stream> incoming, StreamSink> outgoing) - : super(incoming, outgoing, isClientConnection: false); + : super( + incoming, outgoing, isClientConnection: false, pushEnabled: false); factory ServerConnection(Stream> incoming, StreamSink> outgoing) { diff --git a/pkgs/http2/lib/transport.dart b/pkgs/http2/lib/transport.dart index 6083d29973..50a172e507 100644 --- a/pkgs/http2/lib/transport.dart +++ b/pkgs/http2/lib/transport.dart @@ -27,12 +27,16 @@ abstract class TransportConnection { } abstract class ClientTransportConnection extends TransportConnection { - factory ClientTransportConnection.viaSocket(Socket socket) - => new ClientTransportConnection.viaStreams(socket, socket); + factory ClientTransportConnection.viaSocket(Socket socket, + {bool allowServerPushes: true}) + => new ClientTransportConnection.viaStreams( + socket, socket, allowServerPushes: allowServerPushes); factory ClientTransportConnection.viaStreams(Stream> incoming, - Sink> outgoing) - => new ClientConnection(incoming, outgoing); + Sink> outgoing, + {bool allowServerPushes: true}) + => new ClientConnection( + incoming, outgoing, allowServerPushes: allowServerPushes); /// Creates a new outgoing stream. ClientTransportStream makeRequest(List
headers, diff --git a/pkgs/http2/test/client_websites_test.dart b/pkgs/http2/test/client_websites_test.dart new file mode 100644 index 0000000000..9efbacb840 --- /dev/null +++ b/pkgs/http2/test/client_websites_test.dart @@ -0,0 +1,132 @@ +// 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. + +library http2.test.client_websites_test; + +import 'dart:async'; +import 'dart:convert'; + +import 'package:http2/client.dart'; +import 'package:unittest/unittest.dart'; + +main() async { + group('end2end', () { + test('google', () async { + var uri = Uri.parse("https://www.google.com/"); + ClientConnection connection = await connect(uri); + Response response = await connection.makeRequest(new Request('GET', uri)); + dumpHeaders(uri, response.headers); + + String body = await response.stream.transform(UTF8.decoder).join(''); + connection.close(); + + body = body.toLowerCase(); + expect(body, contains('')); + expect(body, contains('www.google')); + }); + + test('twitter', () async { + var uri = Uri.parse("https://twitter.com/"); + ClientConnection connection = await connect(uri); + Response response = await connection.makeRequest(new Request('GET', uri)); + dumpHeaders(uri, response.headers); + + String body = await response.stream.transform(UTF8.decoder).join(''); + connection.close(); + + expect(body, contains('')); + expect(body, contains('twitter.com')); + }); + + test('nghttp2.org - server push enabled', () async { + var uri = Uri.parse("https://nghttp2.org/"); + + ClientConnection connection = await connect(uri, allowServerPushes: true); + var request = new Request('GET', uri); + Response response = await connection.makeRequest(request); + dumpHeaders(uri, response.headers); + + Future accumulateResponse(stream) async { + return await stream.transform(UTF8.decoder).join(''); + } + + Future> accumulatePushes() async { + var futures = []; + return response.serverPushes.listen((ServerPush push) { + futures.add(push.response.then((Response response) { + dumpHeaders(uri, push.requestHeaders, + msg: '**push** Request headers for push request.'); + dumpHeaders(uri, response.headers, + msg: '**push** Response headers for server push ' + 'request.'); + + return accumulateResponse(response.stream).then((String body) { + return [push.requestHeaders[':path'].join(''), body]; + }); + })); + }).asFuture().then((_) => Future.wait(futures)); + } + + var results = await Future.wait( + [accumulateResponse(response.stream), accumulatePushes()]); + + var body = results[0]; + expect(body, contains('')); + expect(body, contains('nghttp2')); + + // TODO: Find out why the push we get is a 502, it should be 200 with a + // css file. WTF? + var pushes = results[1]; + expect(pushes, hasLength(1)); + expect(pushes[0][0], '/stylesheets/screen.css'); + expect(pushes[0][1], contains('Bad Gateway')); + await connection.close(); + }); + + test('nghttp2.org - server push disabled', () async { + var uri = Uri.parse("https://nghttp2.org/"); + + ClientConnection connection = await connect( + uri, allowServerPushes: false); + var request = new Request('GET', uri); + Response response = await connection.makeRequest(request); + dumpHeaders(uri, response.headers); + + Future accumulateResponse(stream) async { + return await stream.transform(UTF8.decoder).join(''); + } + + Future> accumulatePushes() async { + var futures = []; + return response.serverPushes.listen((ServerPush push) { + futures.add(push.response.then( + (Response response) => response.stream.drain())); + }).asFuture().then((_) => Future.wait(futures)); + } + + var results = await Future.wait( + [accumulateResponse(response.stream), accumulatePushes()]); + + var body = results[0]; + expect(body, contains('')); + expect(body, contains('nghttp2')); + + var pushes = results[1]; + expect(pushes, hasLength(0)); + await connection.close(); + }); + }); +} + +dumpHeaders(Uri uri, Map> headers, + {String msg: 'Response headers.'}) { + print(''); + print('[$uri] $msg'); + for (var key in headers.keys.toList()..sort()) { + var spaces = ' ' * (20 - key.length); + print('$key $spaces ${headers[key].join(', ')}'); + } + print(''); +} + From 3eaf51e4b9420fb92c3c24514006c3d257f1ca4e Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Mon, 29 Jun 2015 15:44:25 +0200 Subject: [PATCH 017/172] Ignore stream priority frames for "idle" streams, do not enforce stream id increment of 2 R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/215317013. --- .../http2/lib/src/streams/stream_handler.dart | 38 ++++++++++++++++--- pkgs/http2/lib/src/sync_errors.dart | 1 + 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index 000e11d4c8..2fde747a22 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -187,14 +187,24 @@ class StreamHandler extends Object with TerminatableMixin { TransportStream newRemoteStream(int remoteStreamId) { return ensureNotTerminatedSync(() { assert (remoteStreamId < MAX_STREAM_ID); - if (remoteStreamId != (lastRemoteStreamId + 2)) { - // FIXME: Is this check ok? Can't there be holes in the streams? - throw new StateError( - 'Remote end tries to create new stream which is not 2 higher than ' - 'last one.'); + // NOTE: We cannot enforce that a new stream id is 2 higher than the last + // used stream id. Meaning there can be "holes" in the sense that stream + // ids are not used: + // + // http/2 spec: + // The first use of a new stream identifier implicitly closes all + // streams in the "idle" state that might have been initiated by that + // peer with a lower-valued stream identifier. For example, if a client + // sends a HEADERS frame on stream 7 without ever sending a frame on + // stream 5, then stream 5 transitions to the "closed" state when the + // first frame for stream 7 is sent or received. + + if (remoteStreamId < lastRemoteStreamId) { + throw new ProtocolException('Remote tried to open new stream which is ' + 'not in "idle" state.'); } + bool sameDirection = (nextStreamId + remoteStreamId) % 2 == 0; - assert (lastRemoteStreamId < remoteStreamId ); assert (!sameDirection); lastRemoteStreamId = remoteStreamId; @@ -373,6 +383,22 @@ class StreamHandler extends Object with TerminatableMixin { // be ignored. (If the stream was in "HalfClosedRemote" and we did // send an endStream=true, it will be removed from the stream set). } + } else if (frame is PriorityFrame) { + // http/2 spec: + // The PRIORITY frame can be sent for a stream in the "idle" or + // "closed" states. This allows for the reprioritization of a + // group of dependentstreams by altering the priority of an + // unused or closed parentstream. + // + // As long as we do not handle stream priorities, we can safely ignore + // such frames on idle streams. + // + // NOTE: Firefox for example sends [PriorityFrame]s even without + // opening any streams (e.g. streams 3,5,7,9,11 [PriorityFrame]s and + // stream 13 is the first real stream opened by a [HeadersFrame]. + // + // TODO: When implementing priorities for HTTP/2 streams, these frames + // need to be taken into account. } else { throw new StateError('No open stream found & was not headers frame.'); } diff --git a/pkgs/http2/lib/src/sync_errors.dart b/pkgs/http2/lib/src/sync_errors.dart index 063e801892..a963152c11 100644 --- a/pkgs/http2/lib/src/sync_errors.dart +++ b/pkgs/http2/lib/src/sync_errors.dart @@ -31,3 +31,4 @@ class FrameSizeException implements Exception { class TerminatedException implements Exception { String toString() => 'TerminatedException: The object has been terminated.'; } + From 0d75c0bffe176c9d94957b6dcc00164ddf55d44b Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Mon, 29 Jun 2015 15:53:04 +0200 Subject: [PATCH 018/172] Added temporarily a package:http2/debug.dart library It allows one to dump all incoming/outgoing frames. R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/221727013. --- pkgs/http2/lib/debug.dart | 145 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 pkgs/http2/lib/debug.dart diff --git a/pkgs/http2/lib/debug.dart b/pkgs/http2/lib/debug.dart new file mode 100644 index 0000000000..7b5c6636c1 --- /dev/null +++ b/pkgs/http2/lib/debug.dart @@ -0,0 +1,145 @@ +// 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. + +library http2.debug; + +import 'dart:async'; +import 'dart:io'; +import 'dart:io' show stderr; +import 'dart:convert'; + +import 'src/connection.dart'; +import 'src/connection_preface.dart'; +import 'src/frames/frames.dart'; +import 'src/settings/settings.dart'; + +import 'transport.dart'; + +final jsonEncoder = new JsonEncoder.withIndent(' '); + + +TransportConnection debugPrintingConnection(Socket socket, + {bool isServer: true, + bool verbose: true}) { + var connection; + + var incoming = decodeVerbose(socket, isServer, verbose: verbose); + var outgoing = decodeOutgoingVerbose(socket, isServer, verbose: verbose); + if (isServer) { + connection = new ServerTransportConnection.viaStreams(incoming, outgoing); + } else { + connection = new ClientTransportConnection.viaStreams(incoming, outgoing); + } + return connection; +} + +Stream> decodeVerbose(Stream> inc, + bool isServer, + {bool verbose: true}) { + String name = isServer ? 'server' : 'client'; + + var sc = new StreamController(); + var sDebug = new StreamController(); + + _pipeAndCopy(inc, sc, sDebug); + + if (!isServer) { + _decodeFrames(sDebug.stream).listen((frame) { + print('[$name/stream:${frame.header.streamId}] ' + 'Incoming ${frame.runtimeType}:'); + if (verbose) { + print(jsonEncoder.convert(frame.toJson())); + print(''); + } + }, onError: (e, s) { + print('[$name] Stream error: $e.'); + }, onDone: () { + print('[$name] Closed.'); + }); + } else { + var s3 = readConnectionPreface(sDebug.stream); + _decodeFrames(s3).listen((frame) { + print('[$name/stream:${frame.header.streamId}] ' + 'Incoming ${frame.runtimeType}:'); + if (verbose) { + print(jsonEncoder.convert(frame.toJson())); + print(''); + } + }, onError: (e, s) { + print('[$name] Stream error: $e.'); + }, onDone: () { + print('[$name] Closed.'); + }); + } + + return sc.stream; +} + +StreamSink> decodeOutgoingVerbose(StreamSink> sink, + bool isServer, + {bool verbose: true}) { + String name = isServer ? 'server' : 'client'; + + var proxySink = new StreamController(); + var copy = new StreamController(); + + if (!isServer) { + _decodeFrames(readConnectionPreface(copy.stream)) + .listen((Frame frame) { + print('[$name/stream:${frame.header.streamId}] ' + 'Outgoing ${frame.runtimeType}:'); + if (verbose) { + print(jsonEncoder.convert(frame.toJson())); + print(''); + } + }, onError: (e, s) { + print('[$name] Outgoing stream error: $e'); + }, onDone: () { + print('[$name] Closing.'); + }); + } else { + _decodeFrames(copy.stream).listen((Frame frame) { + print('[$name/stream:${frame.header.streamId}] ' + 'Outgoing ${frame.runtimeType}:'); + if (verbose) { + print(jsonEncoder.convert(frame.toJson())); + print(''); + } + }, onError: (e, s) { + print('[$name] Outgoing stream error: $e'); + }, onDone: () { + print('[$name] Closing.'); + proxySink.close(); + }); + } + + _pipeAndCopy(proxySink.stream, sink, copy); + + return proxySink; +} + +Stream _decodeFrames(Stream> bytes) { + var settings = new Settings(); + var decoder = new FrameReader(bytes, settings); + return decoder.startDecoding(); +} + +Future _pipeAndCopy(Stream from, StreamSink to, StreamSink to2) { + var c = new Completer(); + from.listen((List data) { + to.add(data); + to2.add(data); + }, onError: (e, s) { + to.addError(e, s); + to2.addError(e, s); + }, onDone: () { + Future.wait([to.close(), to2.close()]) + .then(c.complete).catchError(c.completeError); + }); + return c.future; +} + +void print(String s) { + stderr.writeln(s); +} From a778364c4e48d0c26965720c14f76f821bd67985 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Mon, 6 Jul 2015 17:17:16 +0200 Subject: [PATCH 019/172] Add experimental HTTP/2 server for testing server-push with browsers. R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/221977013. --- pkgs/http2/experimental/pkcert/README | 16 +++ pkgs/http2/experimental/pkcert/cert9.db | Bin 0 -> 12288 bytes pkgs/http2/experimental/pkcert/key4.db | Bin 0 -> 20480 bytes pkgs/http2/experimental/server.dart | 165 ++++++++++++++++++++++++ 4 files changed, 181 insertions(+) create mode 100644 pkgs/http2/experimental/pkcert/README create mode 100644 pkgs/http2/experimental/pkcert/cert9.db create mode 100644 pkgs/http2/experimental/pkcert/key4.db create mode 100644 pkgs/http2/experimental/server.dart diff --git a/pkgs/http2/experimental/pkcert/README b/pkgs/http2/experimental/pkcert/README new file mode 100644 index 0000000000..fe764a9cc3 --- /dev/null +++ b/pkgs/http2/experimental/pkcert/README @@ -0,0 +1,16 @@ +This is a certificate database used by Dart for testing purposes. + +It is created as a certificate database by NSS (Network Security Services), +a library from Mozilla, using the certutil tool. It uses a cert9.db file, +rather than a cert8.db file, so the database directory must be specified with +"sql:" in front of the directory path, or the environment variable +NSS_DEFAULT_DB_TYPE must be set to "sql". + +The password for the key database is "dartdart". + +The database contains a root certificate from Equifax, used to verify the +client https connection to www.google.dk. It contains a self-signed +certificate for a local certificate authority myauthority_cert, and a +server certificate for localhost called localhost_cert, signed by +myauthority_cert. It contains the key for localhost_cert, but +not the key for myauthority_cert. diff --git a/pkgs/http2/experimental/pkcert/cert9.db b/pkgs/http2/experimental/pkcert/cert9.db new file mode 100644 index 0000000000000000000000000000000000000000..497fca668ed41d875948a64130060131e7fdb649 GIT binary patch literal 12288 zcmeHN3sh9c8NT<J0GlR>Sp@ojwriuD6iQrO$*oGGMr+WmNJ|S(*BbQk7J&+k4EB!- zmPYvpga%73#Zq^Df;2obWR8DijC6Kzj5IndBqBOkIyX!jI5%uYXh>j`)IVTuWR!Z_7j7TF4 zhGyGAC=88Y7#iU)Gy-C1gv8JYilGq}LnAPTjz=ly9_t@Qma#HMmSJQWMwVe@8Ag_2 zWEn=5<63fDOO9*FaVYQ$JTOe4VBo0HAFTF%#?ygZ7VQS3e1!Ox2nLcD)6`{aa|=wQ(`nF zt{bmY$`x`Yg5*Sm%ZaFCoQPWBMASu2ME&GML?9<37FiLK!iingFg;vpS2dNnnySkk zD4N}YqS+lNn%#k-85fFXcc5s-g`yQMnlq?jSc)bNC!*QIiD(LOVnUaw>k{9zhV#d{IY-vn8vQ@}cQzx?L|5^gZ{vUu-pas-`BCz(^b^=>%8v`~5 zegp%ffe%T)%!p|x0f^=SXZxhpnsnpR>fCmVYj}l&gW}rVMg{tH8pHIQ~ ze+&2!1OXltZI_LKLBxQcKp>I0k}>)f3A&8<Popx}j4tV3a?MK5)S zk(z9xFCQaBV+4ant20fw}fj~0C+;x3gTDmUPqU3jLmsR#`R?Tqd z{~-c2fkWV3a07IJ22eYQhTrC@je%c01L&w0js$9w7Bf!ti)^%DXGo6M7?vid83jV% zL>?q!;R8SZ*WaIw|03-iua0jmbM)(%6;ulY-t%EiO1|wi#A4V%-0i2;114+jde zttRo>_1y90lR{iXf;Ie6ZFboY1$`;_Nb!LDVW-wV>el=-Uzc1g)y@*GpHRXc`M6qn z{?6Td=gtmk9lPu8C9jr`jS9wTvJ;45xM$zVdhw*Y@v0ZfGiZQ4wkOhi>?+r^Wg{Mk*M zaqsob0Zs4E40|bL!Iv#h!`W!%AxrKH01C* z*FeZs_aX0PFNY&;9`fV2c1(ISZmT-3;K|GjZHl7$?e|}6zHsT|6%$t6n-h~&{)hAp z4Zb@XZ@T{Jy|C;|$&{^G%U>DqxczjcwM4{m09lS2o@wJAn_OC7`=U+A@eY)!RC&F(Y-Tt^E;#@I1(>cokJsOAeOd2Q z_tiVw@WZIICA4Lzti{A8;%~ixAfz0{=T8<{pkr;T^?-f>}=VrosYi%r-*$7nF*7h-(^}T z#I)nN`aEezzo=kohg)^OXB4eO%tvrH%ltI?g+oJcI0@>$@$9&)C@dqYj-M_GSeNNg zV)!m&_u8?}TiWmB0s-E++=;w}Jpw27Pa8PFz6HUdhTBs2CrB7FmBQP;D{P{bJ)(xb zn6L_)7)B-c>I$3KZ13-_=>CGHf!-|p3Yre2B>t|x_`KJF-Q(v;>O<<{>gpNe_CwA)6--NqWlH=POYdj2{QL?DJ zw}YBGmN%8#H(zR)TqBes03KmaIV^4eG6jxp0{m$>n3-4p%8O&A3^R=K$IJ|aF|S;C zgb}v#h%ede``>JFiW@BTzuYLGFoa^88l^e_hbP0#Kpe`UtHipgIanlpt(G7&oewR4 z7lZ!*9Na)B<&*h3YVAki2ziU}(XEwTGO>>$DfQTnsUZA;f3hif-rK*nkny6~@ zdS}6h#OljLZTi49iGOTK^EU&A5=#aQ{b6R(7Lti1!k>b`a%>a)pS>u{Pp(t`wJBZc zVbSL=>TlEPK*t2^l}bmOa+v+GDS3yws&mU5P#j)8n#a^$+}f%~L10`C!T@h!z##Az z_(C~Z+LjbpQsDoX0?PmzI5?04^Z)>-41)+T0~|!4`5zA33IjiXVmm4er{_kY;1P0e z1fm1U$<@`s#nyx1_6G~G*-%5(NCRo4s-~lXoGXD8v$aAR7;5RN8k!=vX_z97^|ZDd zYasRYkel`Ow&-YWHbSbZ=^GkJAPE@Cj-~84$}SDsPzIQT1+(PAQ+e=I9y$e!$jg8U z9!zAxL=H^k!9)Q}2w*~_Bnl7|W+sIYF(3vOGZP0y!D2udEC$5EVn84)21LSQKqxE* z#KK}gFf7IrOlRrM@Pm+XGd6^bgOG6$G7dtC2^EEC%LVyB588L~tgWyC2{`;G;#PBavz=58gTTl>AUI#7a97%2j zb%Gn=`*HbC3%T#Pi&@4QY8dFKZq|@MQXV-wQl@$EfD@NmM#sHb5Dv3-vLbo89(17O zS`ggboS}1z@Awwj@74-R%W2f;=(xF2aC4}Tr9HvcYHmfzFiTKNPhDdd5^{p8$+-eZ zG0F-JC%%am!Z3j_90){`!(5SNKVd-5-!P=bx6sgW^P}JvGt}LQb|g!;ISl%rFkENc zzoB3;SaE3t9rsq$f0AGh(E>YX1DgRlZr<+_*t)v9lU(M!v_Hk3@%|QC9y>e#_ricN z;3Lrc7v57#&`Sy|De#XeKo6`0AK+3R06<5?>=8b*{$J|<|Ja3D3cRGi-=F~X{+|m2 zdVpM@_HQ7v6k_QYaD|sjh^kWk6M|m(N=>0!G>^Te=Mra_`z#qZWVMDOz za3?qhegdu!X9Law-iTsE0-%Wa2ABZ?h&IF#gcV{L{2kB$fIp}B_iaLeD%{o9+QO3L z;%0%tNfWI|GI+`v^r}UHpQ>A)kD8l^=cVSx6M3k)i89>O+;{>PHMcB=b3V7U#mqY$ z9FBvk8^bOLs6evVlf1vHg8NZ0Sqq<$J7fx*&+J3*< zv53^wPgmITh%{DnJ9C-0dxTB+k?Y(?Wrj&ZtnYV;DHR0U=ef$!hkc&Ve!$ogJN zt*?q93cgc@f7$(nmK&@xUomd;(2nL$O6aX{VqT`Ze)E;DC|q{#Iz5oKW~okj?OlDM zAE2$AvB#{GQNmUDBS)vz!Ea~U9^FpY(9F2W%%9=XY{B_Lbk#F4tL(%M*mmQT^?7UG zx(~0rxhv>c8!PhE+B1gpMjZ)n1KzrajvSR}eHgrl%o)q_r~!(~zyFm-pv?WF=WBXc z&4pTl^ny0VjpNQ=D_Dcon_LdkI^!c5tn2CpYVFrH$4=g3dq(!M_UW=bP)55xd3{fP z472K~OSPYc%jS(b$)Cboksl$5%nLB+X=u8{8>S@*MGaT1^<_5eIPuQQyzyr2po(MV zqX#=Z3``q~J$25Pm_Ot>wL#U%AvN}PSD|CAdarzb{dyq%2po;fXFx@Tw~u#tz1^L# zzOiZEsK>9Kk`f6N3Bbk16%+;9?Ae1iQ-Y#?QO`0JacBc4CjFDGdBSQU*L&Jh!5+u_4+T2WxkJN6nw^{SpkCQD_!PFs~r6T@`$h=WG z*j(a|#gsnSNyE~Fyc=CupzZS-ih84_>)e?^TTk@cE~%V^1g*5pSB?+E$uibM$uxCe zC7AJ>-!~UrE%0z3eaN4rQ`_WsM)ifE(T&)*n{-AE-s6%N!`sj|rP+kS_DzG*fhB+T`@O+TOxJvN;4ZE{s6oOXe z$VwPE^FveIFQXy`Lk_^~rel~LbZD6|`WV%@Q$~yo4N{tv$G5?cWL0JN=C706(huh?fpi(Ez_&Y}McV7igb{_g*n!Cf< zo?O^BZn_Xjq^Ws2!%OFksiWPWKH5}gWoPDb0lY)vwJwsQ11>q6DZME9FX&1CT}bfv z|8%f5Fv_PI?u1}R+yQp~9)XNv`uWEFWfixWd*VTzxT%Th^>=!4bpxK_op`zP?gSp^ zyc0DZPs`rzXtY>JZk)VdWdKc^mHFkuP~Wc_Tb=g{%F3;6tPf5*gsN%|878Bcsd8M* zEz#=XsOGl9$>^#1sIoaRyg1<7dD!!=otJpm$fxd)8ZKUlW3ThE=JPqFGDo}Y!qW1j z=mxXIcIxifoxaQ>Q=L&q-js}H`Q^U-Wi7oJAy))fU9Kn*70XBH%1`=SBRt678Hi1O z-cgp;oowCT9kme2dIN8EA59f9oo;dexk%}(j?qxn7C)0uJ%ud~qL{_1$Y@sTBp0J( zQ28FWm_ExY7j`~hw|dqJV>_Q7%k7iw$xV{CNx|0<3z6iHn0Pll(Q;Cs2NBkgWm58L zHLT_03fI?sliGK&dUw6aD7K%k_RROZAKz#eBV|~}f4=g~=N9hVH57j0jpZpmh^SK3yQpV5)z8!DV|m>n0V)$K@Z zdKDkP*K9+D=j+LftHO*0+HMOLs}tjT9X38%A@?8G{~Je)PAPW>-3YwCCXvfCv)WsE zw4TyD%KATDl=q@Opd`n&BCB^*^l97;i-s2@P44lXr_DH1548H9)?{z|uPX&N5+@+yM+!Q z8Y@Vz%7&&=L=?u6w?sd4C1=&w)kn4oxA8{l)V&Hk{Tz1llFYnOolzG*`*U3zC7d5; ztf^tl^I)B3fTG4vU$b#77Va?AcpsY1Y1ls~>#(Ou?h5Yv;SJ!&K|pk##!FW!sSoPvP^E;-b9FTnQ+pJlmLpgXI&UTb=5*C;DV4VHMXaALxO zysv2bEywL+2oi}bBxQd-B>U78V`Wr%Bi5I%Cso3%VuD{hGu}{o(0ZaX!9pf-o7j}y z1Ct)(H-*d|T$)CDt~IDNK>-E>#&rIsk9(!ah3&RNdqgXCTlbgN@a?~~#r1N*$md3h zZNwV*yz>P2C4(13!2v-m=92Au5DLa&hbx?*c_KLwr}%W2V=5`InuFy}5!E7R%+Qk1Hu2E4zbyHsup-njW-Yq14Y%o79(l-sE1AE$O5lBrMtB_Aq(LLP=AxkGo|?+a z+S~#n>&Dpf5RUrW+1-pa*r_gl=7jiHV>BF!W&MwAye1rIUFMDYxcJypwL7X6qEBykz-Z$De{ zQC(3f`yMYjvtiYiEQLxFgNv(L5^z~EGzAauxIbxMK5tYB?v{022irp%_askqeRH|b zQPuDkipt4TBnmV?ype)Gt?+z&+$J_B@4iYDqwHSwe9aQewFl2*COe%?CCJJMzZe^} zuim5018MtKHk)oRLn~S5nQJ2Wdpl`6c{-u_|M!2D{(F1l%=dpZ3@}IJA=uy!uu2&E z=dq8B=K1C8;4iP#h>Lh?waLE^AG2k}6QV({!-j{Cv~DKHouT#2I;GN}lU;sK{2)bG zs!!HD>NZ#w2dXqXS(@y+kVs!7y-STrj^?ujPYYw)#$6(t$!Kn>B#APMASqlPGMHjx zV4Ui`H#GL#khbhe?Z^8r>C(Hk(OH}c+3vOw$$>)yalT!y5A~EyKHYSEpe~gf^+x>) zOMASecHNa`^^0UQ7gds!|I}Le8Ks@kwvPJ&to%MveY)EP2e`W*1T-=*ORx2^_}b;) zE6uzR$)e}Cs*c`EnhfUWCmEZ_Z_S#PYRBNBBmH#=sq28KG%}i#DoNSJe6v1u`I@V~ zuVd3IpOm}P-(FmJ+4DQHgHwB!&E7~P2FUZ!3l%LWlK|F5r(}k3LcxzC(6*EeM>Z7Ta(vaFM*vw+$H9j4H zkG|i{7#MweG5ufYmuCF@7IslxtDP|3S8zEec4Kia7wc5r9kT>l^^8i#^-4^p<3ceU zMhi6~Hln@Fs=G)9lXNv7wTkWJ>vLMu28OlnM#uJKeC){|C8GtYl3Y~R$_o~|Ka4tF z{v|jqo88>_ICods-U#QXI&8ILJP3`0Aq$Z_euG84K*KAgxF~v}Dnd5tEf!i?@Ti9dP6X<+%x~O}nSsFesv-jSncnOFDUye{6 zNi%)ulH}Rc)p&d*Ru?~uGR#(L+KYMs_fh9r(HmkFcbtso|Api)!@a^Hx|YU;`0@V4 zJi7Bw`UAM^julmF7$e^CK48vV-+@$*QEUrg6xC-~eI%H6TscSI=2r1i)c z*VQ&TbJX^Ooq$)-8hP_w3*8V=NA3;P3wNlvtT-R+-)Y*hYWaEvQR_oHt2`TOGZ=RE zk`UzV|tbE3ndy>%r8)9Wd5PjIZ_vEJRXQ+U>GR_VPqm zBf4!!zBGZmuH^<}9Hv2AQ9Ql?xMCSONDsfF~?C1}Ogg-Gh}y(sU$sijPPn@xW` YhA_ZB7~6eff4%HI!j$j}Ngkp90nk=@-T(jq literal 0 HcmV?d00001 diff --git a/pkgs/http2/experimental/server.dart b/pkgs/http2/experimental/server.dart new file mode 100644 index 0000000000..2856e299e3 --- /dev/null +++ b/pkgs/http2/experimental/server.dart @@ -0,0 +1,165 @@ +// 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. + +library http2.experimental.server; + +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:http2/debug.dart' hide print; +import 'package:http2/transport.dart'; + +const bool DEBUGGING = false; + +const String HOSTNAME = 'localhost'; +const int PORT = 7777; + +main() async { + var database = Platform.script.resolve('pkcert').toFilePath(); + SecureSocket.initialize(database: database, password: 'dartdart'); + + var server = await SecureServerSocket.bind( + HOSTNAME, PORT, 'localhost_cert', supportedProtocols: ['h2']); + print('HTTP/2 server listening on https://$HOSTNAME:$PORT'); + + runZoned(() { + server.listen(handleClient); + }, onError: (e, s) { + print("Unexpected error: $e"); + print("Unexpected error - stack: $s"); + }); +} + +handleClient(SecureSocket socket) { + dumpInfo('main', 'Got new https client'); + + var connection; + if (DEBUGGING) { + connection = debugPrintingConnection(socket); + } else { + connection = new ServerTransportConnection.viaSocket(socket); + } + + connection.incomingStreams.listen((ServerTransportStream stream) async { + dumpInfo('main', 'Got new HTTP/2 stream on connection: ${stream.id}'); + + String path; + stream.incomingMessages.listen((StreamMessage msg) async { + dumpInfo('${stream.id}', 'Got new incoming message'); + if (msg is HeadersStreamMessage) { + dumpHeaders('${stream.id}', msg.headers); + if (path == null) { + path = pathFromHeaders(msg.headers); + if (path == null) throw 'no path given'; + + if (path == '/') { + sendHtml(stream); + } else if (['/iframe', '/iframe2'].contains(path)) { + sendIFrameHtml(stream, path); + } else { + send404(stream, path); + } + } + } else if (msg is DataStreamMessage) { + dumpData('${stream.id}', msg.bytes); + } + }); + }); +} + +void dumpHeaders(String prefix, List
headers) { + for (int i = 0 ; i < headers.length; i++) { + String key = ASCII.decode(headers[i].name); + String value = ASCII.decode(headers[i].value); + print('[$prefix] $key: $value'); + } +} + +String pathFromHeaders(List
headers) { + for (int i = 0 ; i < headers.length; i++) { + if (ASCII.decode(headers[i].name) == ':path') { + return ASCII.decode(headers[i].value); + } + } + throw new Exception('Expected a :path header, but did not find one.'); +} + +void dumpData(String prefix, List data) { + print('[$prefix] Got ${data.length} bytes.'); +} + +void dumpInfo(String prefix, String msg) { + print('[$prefix] $msg'); +} + +Future sendHtml(TransportStream stream) async { + push(stream, '/iframe', sendIFrameHtml); + push(stream, '/iframe2', sendIFrameHtml); + push(stream, '/favicon.ico', send404); + + stream.sendHeaders([ + new Header.ascii(':status', '200'), + new Header.ascii('content-type', 'text/html; charset=utf-8'), + ]); + stream.sendData(ASCII.encode(''' + + hello + +

head

+ first
+
+ second
+
+ + +''')); + return stream.outgoingMessages.close(); +} + +Future push(ServerTransportStream stream, + String path, + Future sendResponse(stream, path)) async { + var requestHeaders = [ + new Header.ascii(':authority', '$HOSTNAME:$PORT'), + new Header.ascii(':method', 'GET'), + new Header.ascii(':path', path), + new Header.ascii(':scheme', 'https'), + ]; + + var pushStream = stream.push(requestHeaders); + await sendResponse(pushStream, path); +} + +Future sendIFrameHtml(TransportStream stream, String path) async { + stream.sendHeaders([ + new Header.ascii(':status', '200'), + new Header.ascii('content-type', 'text/html; charset=utf-8'), + ]); + stream.sendData(ASCII.encode(''' + + Content for '$path' inside an IFrame. + +

Content for '$path' inside an IFrame.

+ + +''')); + await stream.outgoingMessages.close(); +} + +Future send404(TransportStream stream, String path) async { + stream.sendHeaders([ + new Header.ascii(':status', '404'), + new Header.ascii('content-type', 'text/html; charset=utf-8'), + ]); + stream.sendData(ASCII.encode(''' + + Path '$path' was not found on this server. + +

Path '$path' was not found on this server.

+ + +''')); + return stream.outgoingMessages.close(); +} From 337f00acad40b692d492f4719378a0b562c2a053 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Wed, 5 Aug 2015 10:58:56 +0200 Subject: [PATCH 020/172] Move highest stream id handling to StreamHandler, update working test BUG= R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/227477013 . --- pkgs/http2/lib/src/connection.dart | 32 +++------------- .../src/flowcontrol/connection_queues.dart | 6 +++ .../http2/lib/src/streams/stream_handler.dart | 37 ++++++++++++++++++- pkgs/http2/test/client_websites_test.dart | 4 +- .../flowcontrol/connection_queues_test.dart | 18 +++++++++ 5 files changed, 67 insertions(+), 30 deletions(-) diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index 34b016b8c2..d18cffb183 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -87,10 +87,6 @@ abstract class Connection { /// The connection-level flow control window handler for outgoing messages. OutgoingConnectionWindowHandler _connectionWindowHandler; - /// Represents the highest stream id this connection has received from the - /// other side. - int _highestStreamIdReceived = 0; - /// The state of this connection. ConnectionState _state; @@ -205,7 +201,7 @@ abstract class Connection { _state = ConnectionState.Operational; } - // Try to decode frame if it is a Headers/PushPromise frame. + // Try to defragment [frame] if it is a Headers/PushPromise frame. try { frame = _defragmenter.tryDefragmentFrame(frame); if (frame == null) return; @@ -227,14 +223,9 @@ abstract class Connection { } } on HPackDecodingException { _terminate(ErrorCode.PROTOCOL_ERROR); + return; } - // Update highest stream id we received. - // TODO: This should only be done for "processed" streams. - // So we should ask the StreamSet for what Ids it has processed. - _highestStreamIdReceived = - max(_highestStreamIdReceived, frame.header.streamId); - // Handle the frame as either a connection or a stream frame. if (frame.header.streamId == 0) { if (frame is SettingsFrame) { @@ -252,15 +243,7 @@ abstract class Connection { throw 'Unknown incoming frame ${frame.runtimeType}'; } } else { - // We will not process frames for stream id's which are higher than when - // we sent the [GoawayFrame]. - // TODO/FIXME: Isn't this the responsibility of the StreamSet. It - // should also send RST/... - if (_state == ConnectionState.Finishing && - frame.header.streamId > highestSeenStreamId) { - return; - } - _streams.processStreamFrame(frame); + _streams.processStreamFrame(_state, frame); } } @@ -279,7 +262,7 @@ abstract class Connection { // GoawayFrame otherwise we'll just propagate the message. if (active) { _frameWriter.writeGoawayFrame( - highestSeenStreamId, ErrorCode.NO_ERROR, []); + _streams.highestPeerInitiatedStream, ErrorCode.NO_ERROR, []); } // TODO: Propagate to the [StreamSet] that we no longer process new @@ -298,7 +281,8 @@ abstract class Connection { var cancelFuture = new Future.sync(_frameReaderSubscription.cancel); if (!causedByTransportError) { - _frameWriter.writeGoawayFrame(highestSeenStreamId, errorCode, []); + _frameWriter.writeGoawayFrame( + _streams.highestPeerInitiatedStream, errorCode, []); } var closeFuture = _frameWriter.close().catchError((e, s) { // We ignore any errors after writing to [GoawayFrame] @@ -324,10 +308,6 @@ abstract class Connection { } return new Future.value(); } - - /// The highest stream id which has been used anywhere. - int get highestSeenStreamId => - max(_highestStreamIdReceived, _frameWriter.highestWrittenStreamId); } diff --git a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart index e9790771f1..c4c3a50d47 100644 --- a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart @@ -202,6 +202,12 @@ class ConnectionMessageQueueIn extends Object with TerminatableMixin { _addMessage(streamId, message); } + /// If a [DataFrame] will be ignored, this method will take the minimal + /// action necessary. + void processIgnoredDataFrame(DataFrame frame) { + _windowUpdateHandler.gotData(frame.bytes.length); + } + /// Processes an incoming [HeadersFrame] which is addressed to a specific /// stream. void processHeadersFrame(HeadersFrame frame) { diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index 2fde747a22..eb7e59c6cb 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -5,9 +5,11 @@ library http2.src.stream_handler; import 'dart:async'; +import 'dart:math'; import '../../transport.dart'; +import '../connection.dart'; import '../flowcontrol/connection_queues.dart'; import '../flowcontrol/stream_queues.dart'; import '../flowcontrol/queue_messages.dart'; @@ -111,6 +113,12 @@ class StreamHandler extends Object with TerminatableMixin { int nextStreamId; int lastRemoteStreamId; + int _highestStreamIdReceived = 0; + + /// Represents the highest stream id this connection has received from the + /// remote side. + int get highestPeerInitiatedStream => _highestStreamIdReceived; + bool get isServer => nextStreamId.isEven; StreamHandler._(this._frameWriter, this.incomingQueue, this.outgoingQueue, @@ -332,7 +340,20 @@ class StreamHandler extends Object with TerminatableMixin { //// Process incoming stream frames //////////////////////////////////////////////////////////////////////////// - void processStreamFrame(Frame frame) { + void processStreamFrame(ConnectionState connectionState, Frame frame) { + // If we initiated a close of the connection and the received frame belongs + // to a stream id which is higher than the last peer-initiated stream we + // processed, we'll ignore it. + if (connectionState == ConnectionState.Finishing && + frame.header.streamId > highestPeerInitiatedStream) { + // Even if the frame will be ignored, we still need to process it in a + // minimal way to ensure the connection window will be updated. + if (frame is DataFrame) { + incomingQueue.processIgnoredDataFrame(frame); + } + return null; + } + // TODO: Consider splitting this method into client/server handling. return ensureNotTerminatedSync(() { var stream = _openStreams[frame.header.streamId]; @@ -345,6 +366,20 @@ class StreamHandler extends Object with TerminatableMixin { streamId >= nextStreamId : streamId > lastRemoteStreamId; return isIdleStream; } + bool isPeerInitiatedStream() { + int streamId = frame.header.streamId; + bool isServerStreamId = frame.header.streamId.isEven; + bool isLocalStream = isServerStreamId == isServer; + return !isLocalStream; + } + + if (isPeerInitiatedStream()) { + // Update highest stream id we received and processed (we update it + // before processing, so if it was an error, the client will not + // retry it). + _highestStreamIdReceived = + max(_highestStreamIdReceived, frame.header.streamId); + } if (frame is HeadersFrame) { if (isServer) { diff --git a/pkgs/http2/test/client_websites_test.dart b/pkgs/http2/test/client_websites_test.dart index 9efbacb840..3d65ac48b6 100644 --- a/pkgs/http2/test/client_websites_test.dart +++ b/pkgs/http2/test/client_websites_test.dart @@ -75,12 +75,10 @@ main() async { expect(body, contains('')); expect(body, contains('nghttp2')); - // TODO: Find out why the push we get is a 502, it should be 200 with a - // css file. WTF? var pushes = results[1]; expect(pushes, hasLength(1)); expect(pushes[0][0], '/stylesheets/screen.css'); - expect(pushes[0][1], contains('Bad Gateway')); + expect(pushes[0][1], contains('audio,video{')); await connection.close(); }); diff --git a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart index a9c2ab147c..8e6082b07f 100644 --- a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart @@ -150,6 +150,24 @@ main() { // TODO: Write tests for adding HeadersFrame/PushPromiseFrame. }); + + test('connection-ignored-message-queue-in', () { + const STREAM_ID = 99; + final bytes = [1, 2, 3]; + + var windowMock = new MockIncomingWindowHandler(); + var queue = new ConnectionMessageQueueIn(windowMock); + + // Insert a [DataFrame] and let it be buffered. + var header = new FrameHeader(0, 0, 0, STREAM_ID); + var c = new TestCounter(); + windowMock.mock_gotData = (int diff) { + expect(diff, bytes.length); + c.got(); + }; + queue.processIgnoredDataFrame(new DataFrame(header, 0, bytes)); + expect(queue.pendingMessages, 0); + }); }); } From 34109c8ed2a9c4836376d7582e92c3f412f1ac66 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Wed, 5 Aug 2015 11:16:05 +0200 Subject: [PATCH 021/172] Restructure error handling slightly for processing of incoming frames R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/229487013 . --- pkgs/http2/lib/src/connection.dart | 50 ++++++++++------ .../http2/lib/src/streams/stream_handler.dart | 59 +++++++++++++++---- pkgs/http2/lib/src/sync_errors.dart | 15 +++++ 3 files changed, 94 insertions(+), 30 deletions(-) diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index d18cffb183..5be88803d4 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -191,6 +191,27 @@ abstract class Connection { /// Handles an incoming [Frame] from the underlying [FrameReader]. void _handleFrame(Frame frame) { + try { + _handleFrameImpl(frame); + } on ProtocolException catch (error) { + _terminate(ErrorCode.PROTOCOL_ERROR); + } on FlowControlException { + _terminate(ErrorCode.FLOW_CONTROL_ERROR); + } on FrameSizeException { + _terminate(ErrorCode.FRAME_SIZE_ERROR); + } on HPackDecodingException { + _terminate(ErrorCode.PROTOCOL_ERROR); + } on TerminatedException { + // We tried to perform an action even though the connection was already + // terminated. + // TODO: Can this even happen and if so, how should we propagate this + // error? + } catch (error) { + _terminate(ErrorCode.INTERNAL_ERROR); + } + } + + void _handleFrameImpl(Frame frame) { // The first frame from the other side must be a [SettingsFrame], otherwise // we terminate the connection. if (_state == ConnectionState.Initialized) { @@ -202,28 +223,18 @@ abstract class Connection { } // Try to defragment [frame] if it is a Headers/PushPromise frame. - try { - frame = _defragmenter.tryDefragmentFrame(frame); - if (frame == null) return; - } on ProtocolException { - _terminate(ErrorCode.PROTOCOL_ERROR); - return; - } + frame = _defragmenter.tryDefragmentFrame(frame); + if (frame == null) return; // Try to decode headers if it's a Headers/PushPromise frame. // [This needs to be done even if the frames get ignored, since the entire // connection shares one HPack compression context.] - try { - if (frame is HeadersFrame) { - frame.decodedHeaders = - _hpackContext.decoder.decode(frame.headerBlockFragment); - } else if (frame is PushPromiseFrame) { - frame.decodedHeaders = - _hpackContext.decoder.decode(frame.headerBlockFragment); - } - } on HPackDecodingException { - _terminate(ErrorCode.PROTOCOL_ERROR); - return; + if (frame is HeadersFrame) { + frame.decodedHeaders = + _hpackContext.decoder.decode(frame.headerBlockFragment); + } else if (frame is PushPromiseFrame) { + frame.decodedHeaders = + _hpackContext.decoder.decode(frame.headerBlockFragment); } // Handle the frame as either a connection or a stream frame. @@ -240,7 +251,8 @@ abstract class Connection { } else if (frame is UnknownFrame) { // We can safely ignore these. } else { - throw 'Unknown incoming frame ${frame.runtimeType}'; + throw new ProtocolException( + 'Cannot handle frame type ${frame.runtimeType} with stream-id 0.'); } } else { _streams.processStreamFrame(_state, frame); diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index eb7e59c6cb..8e0a9c98f2 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -96,7 +96,7 @@ class Http2StreamImpl extends TransportStream /// Handles [Frame]s with a non-zero stream-id. /// /// It keeps track of open streams, their state, their queues, forwards -/// messages from the connectionn level to stream level and vise versa. +/// messages from the connection level to stream level and vise versa. // TODO: Respect SETTINGS_MAX_CONCURRENT_STREAMS class StreamHandler extends Object with TerminatableMixin { static const int MAX_STREAM_ID = (1 << 31) - 1; @@ -207,7 +207,7 @@ class StreamHandler extends Object with TerminatableMixin { // stream 5, then stream 5 transitions to the "closed" state when the // first frame for stream 7 is sent or received. - if (remoteStreamId < lastRemoteStreamId) { + if (remoteStreamId <= lastRemoteStreamId) { throw new ProtocolException('Remote tried to open new stream which is ' 'not in "idle" state.'); } @@ -341,6 +341,21 @@ class StreamHandler extends Object with TerminatableMixin { //////////////////////////////////////////////////////////////////////////// void processStreamFrame(ConnectionState connectionState, Frame frame) { + try { + _processStreamFrameInternal(connectionState, frame); + } on StreamClosedException catch (exception) { + _frameWriter.writeRstStreamFrame( + exception.streamId, ErrorCode.STREAM_CLOSED); + _closeStreamIdAbnormally(exception.streamId, exception); + } on StreamException catch (exception) { + _frameWriter.writeRstStreamFrame( + exception.streamId, ErrorCode.INTERNAL_ERROR); + _closeStreamIdAbnormally(exception.streamId, exception); + } + } + + void _processStreamFrameInternal(ConnectionState connectionState, + Frame frame) { // If we initiated a close of the connection and the received frame belongs // to a stream id which is higher than the last peer-initiated stream we // processed, we'll ignore it. @@ -422,8 +437,8 @@ class StreamHandler extends Object with TerminatableMixin { // http/2 spec: // The PRIORITY frame can be sent for a stream in the "idle" or // "closed" states. This allows for the reprioritization of a - // group of dependentstreams by altering the priority of an - // unused or closed parentstream. + // group of dependent streams by altering the priority of an + // unused or closed parent stream. // // As long as we do not handle stream priorities, we can safely ignore // such frames on idle streams. @@ -435,7 +450,9 @@ class StreamHandler extends Object with TerminatableMixin { // TODO: When implementing priorities for HTTP/2 streams, these frames // need to be taken into account. } else { - throw new StateError('No open stream found & was not headers frame.'); + throw new StreamException(frame.header.streamId, + 'No open stream found and was not a headers frame opening a ' + 'new stream.'); } } else { if (frame is HeadersFrame) { @@ -447,7 +464,8 @@ class StreamHandler extends Object with TerminatableMixin { } else if (frame is WindowUpdateFrame) { _handleWindowUpdate(stream, frame); } else { - throw new StateError('Unsupported frame type ${frame.runtimeType}.'); + throw new ProtocolException( + 'Unsupported frame type ${frame.runtimeType}.'); } } }); @@ -460,7 +478,8 @@ class StreamHandler extends Object with TerminatableMixin { if (stream.state != StreamState.Open && stream.state != StreamState.HalfClosedLocal) { - throw new StateError('Expected open state (was: ${stream.state}).'); + throw new StreamClosedException( + stream.id, 'Expected open state (was: ${stream.state}).'); } incomingQueue.processHeadersFrame(frame); @@ -471,7 +490,8 @@ class StreamHandler extends Object with TerminatableMixin { void _handleDataFrame(Http2StreamImpl stream, DataFrame frame) { if (stream.state != StreamState.Open && stream.state != StreamState.HalfClosedLocal) { - throw new StateError('Expected open state (was: ${stream.state}).'); + throw new StreamClosedException( + stream.id, 'Expected open state (was: ${stream.state}).'); } incomingQueue.processDataFrame(frame); @@ -482,7 +502,9 @@ class StreamHandler extends Object with TerminatableMixin { void _handlePushPromiseFrame(Http2StreamImpl stream, PushPromiseFrame frame) { if (stream.state != StreamState.Open && stream.state != StreamState.HalfClosedLocal) { - throw new StateError('Expected open state (was: ${stream.state}).'); + // FIXME(kustermann): We need to RST the promised stream. + throw new StreamClosedException( + stream.id, 'Expected open state (was: ${stream.state}).'); } var pushedStream = newRemoteStream(frame.promisedStreamId); @@ -509,7 +531,8 @@ class StreamHandler extends Object with TerminatableMixin { } else { throw new StateError( 'Got an end-of-stream from the remote end, but this stream is ' - 'neither in the Open nor in the HalfClosedLocal state.'); + 'neither in the Open nor in the HalfClosedLocal state. ' + 'This should never happen.'); } } @@ -559,7 +582,8 @@ class StreamHandler extends Object with TerminatableMixin { } else if (stream.state == StreamState.HalfClosedRemote) { stream.state = StreamState.Closed; } else { - throw new StateError('unknown state transition'); + throw new StateError( + 'Invalid state transition. This should never happen.'); } if (stream.state == StreamState.Closed) { _cleanupClosedStream(stream); @@ -571,10 +595,21 @@ class StreamHandler extends Object with TerminatableMixin { //////////////////////////////////////////////////////////////////////////// void _cleanupClosedStream(Http2StreamImpl stream) { + // FIXME: We can only do this once all the data has been flushed. + // Otherwise we won't propagate [WindowUpdateFrame]s to the stream, which + // then never writes the remaining data out. + incomingQueue.removeStreamMessageQueue(stream.id); _openStreams.remove(stream.id); } + void _closeStreamIdAbnormally(int streamId, Exception exception) { + Http2StreamImpl stream = _openStreams[streamId]; + if (stream != null) { + _closeStreamAbnormally(stream, exception); + } + } + void _closeStreamAbnormally(Http2StreamImpl stream, Exception exception) { incomingQueue.removeStreamMessageQueue(stream.id); @@ -584,5 +619,7 @@ class StreamHandler extends Object with TerminatableMixin { // NOTE: we're not adding an error here. stream.outgoingQueue.terminate(); + + _openStreams.remove(stream.id); } } diff --git a/pkgs/http2/lib/src/sync_errors.dart b/pkgs/http2/lib/src/sync_errors.dart index a963152c11..f8afa055b7 100644 --- a/pkgs/http2/lib/src/sync_errors.dart +++ b/pkgs/http2/lib/src/sync_errors.dart @@ -32,3 +32,18 @@ class TerminatedException implements Exception { String toString() => 'TerminatedException: The object has been terminated.'; } +class StreamException implements Exception { + final String _message; + final int streamId; + + StreamException(this.streamId, this._message); + + String toString() => 'StreamException(stream id: $streamId): $_message'; +} + +class StreamClosedException extends StreamException { + StreamClosedException(int streamId, [String message = '']) + : super(streamId, message); + + String toString() => 'StreamClosedException(stream id: $streamId): $_message'; +} From 064053316360a185777174e10a1c62cd0c56625d Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Wed, 5 Aug 2015 11:59:32 +0200 Subject: [PATCH 022/172] Add error details to goaway frames, add first error test R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/236647013 . --- pkgs/http2/lib/src/connection.dart | 43 +++++---- .../http2/lib/src/streams/stream_handler.dart | 3 +- pkgs/http2/lib/transport.dart | 2 +- pkgs/http2/test/client_errors_test.dart | 95 +++++++++++++++++++ 4 files changed, 125 insertions(+), 18 deletions(-) create mode 100644 pkgs/http2/test/client_errors_test.dart diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index 5be88803d4..1fdc07dbca 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -6,6 +6,7 @@ library http2.src.conn; import 'dart:async'; import 'dart:math'; +import 'dart:convert'; import '../transport.dart'; import 'flowcontrol/connection_queues.dart'; @@ -132,8 +133,12 @@ abstract class Connection { // By default the server is allowed to do server pushes. settings.add(new Setting(Setting.SETTINGS_ENABLE_PUSH, 0)); } - _settingsHandler.changeSettings(settings).catchError((_) { - _terminate(ErrorCode.PROTOCOL_ERROR); + _settingsHandler.changeSettings(settings).catchError((error) { + // TODO: The [error] can contain sensitive information we now expose via + // a [Goaway] frame. We should somehow ensure we're only sending useful + // but non-sensitive information. + _terminate(ErrorCode.PROTOCOL_ERROR, + message: 'Failed to set initial settings (error: $error).'); }); _settingsHandler.onInitialWindowSizeChange.listen((size) { @@ -194,20 +199,20 @@ abstract class Connection { try { _handleFrameImpl(frame); } on ProtocolException catch (error) { - _terminate(ErrorCode.PROTOCOL_ERROR); - } on FlowControlException { - _terminate(ErrorCode.FLOW_CONTROL_ERROR); - } on FrameSizeException { - _terminate(ErrorCode.FRAME_SIZE_ERROR); - } on HPackDecodingException { - _terminate(ErrorCode.PROTOCOL_ERROR); - } on TerminatedException { + _terminate(ErrorCode.PROTOCOL_ERROR, message: '$error'); + } on FlowControlException catch (error) { + _terminate(ErrorCode.FLOW_CONTROL_ERROR, message: '$error'); + } on FrameSizeException catch (error) { + _terminate(ErrorCode.FRAME_SIZE_ERROR, message: '$error'); + } on HPackDecodingException catch (error) { + _terminate(ErrorCode.PROTOCOL_ERROR, message: '$error'); + } on TerminatedException catch (error) { // We tried to perform an action even though the connection was already // terminated. // TODO: Can this even happen and if so, how should we propagate this // error? } catch (error) { - _terminate(ErrorCode.INTERNAL_ERROR); + _terminate(ErrorCode.INTERNAL_ERROR, message: '$error'); } } @@ -216,7 +221,8 @@ abstract class Connection { // we terminate the connection. if (_state == ConnectionState.Initialized) { if (frame is! SettingsFrame) { - _terminate(ErrorCode.PROTOCOL_ERROR); + _terminate(ErrorCode.PROTOCOL_ERROR, + message: 'Expected to first receive a settings frame.'); return; } _state = ConnectionState.Operational; @@ -259,7 +265,7 @@ abstract class Connection { } } - void _finishing({bool active: true}) { + void _finishing({bool active: true, String message}) { // If this connection is already finishing or dead, we return. if (_state == ConnectionState.Terminated || _state == ConnectionState.Finishing) { @@ -274,7 +280,9 @@ abstract class Connection { // GoawayFrame otherwise we'll just propagate the message. if (active) { _frameWriter.writeGoawayFrame( - _streams.highestPeerInitiatedStream, ErrorCode.NO_ERROR, []); + _streams.highestPeerInitiatedStream, + ErrorCode.NO_ERROR, + message != null ? UTF8.encode(message) : []); } // TODO: Propagate to the [StreamSet] that we no longer process new @@ -286,7 +294,8 @@ abstract class Connection { /// Terminates this connection (if it is not already terminated). /// /// The returned future will never complete with an error. - Future _terminate(int errorCode, {bool causedByTransportError: false}) { + Future _terminate(int errorCode, + {bool causedByTransportError: false, String message}) { // TODO: When do we complete here? if (_state != ConnectionState.Terminated) { _state = ConnectionState.Terminated; @@ -294,7 +303,9 @@ abstract class Connection { var cancelFuture = new Future.sync(_frameReaderSubscription.cancel); if (!causedByTransportError) { _frameWriter.writeGoawayFrame( - _streams.highestPeerInitiatedStream, errorCode, []); + _streams.highestPeerInitiatedStream, + errorCode, + message != null ? UTF8.encode(message) : []); } var closeFuture = _frameWriter.close().catchError((e, s) { // We ignore any errors after writing to [GoawayFrame] diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index 8e0a9c98f2..b46b73adf8 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -89,6 +89,7 @@ class Http2StreamImpl extends TransportStream => _pushStreamFun(this, requestHeaders); void terminate() { + // TODO/FIXME: We need to send an RST to the other side. _outgoingCSubscription.cancel(); } } @@ -450,7 +451,7 @@ class StreamHandler extends Object with TerminatableMixin { // TODO: When implementing priorities for HTTP/2 streams, these frames // need to be taken into account. } else { - throw new StreamException(frame.header.streamId, + throw new StreamClosedException(frame.header.streamId, 'No open stream found and was not a headers frame opening a ' 'new stream.'); } diff --git a/pkgs/http2/lib/transport.dart b/pkgs/http2/lib/transport.dart index 50a172e507..125f9a0256 100644 --- a/pkgs/http2/lib/transport.dart +++ b/pkgs/http2/lib/transport.dart @@ -48,7 +48,7 @@ abstract class ServerTransportConnection extends TransportConnection { => new ServerTransportConnection.viaStreams(socket, socket); factory ServerTransportConnection.viaStreams(Stream> incoming, - Sink> outgoing) + Sink> outgoing) => new ServerConnection(incoming, outgoing); /// Incoming HTTP/2 streams. diff --git a/pkgs/http2/test/client_errors_test.dart b/pkgs/http2/test/client_errors_test.dart new file mode 100644 index 0000000000..94f34de5e9 --- /dev/null +++ b/pkgs/http2/test/client_errors_test.dart @@ -0,0 +1,95 @@ +// 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. + +library http2.test.client_errors_test; + +import 'dart:async'; +import 'dart:convert'; + +import 'package:unittest/unittest.dart'; + +import 'package:http2/transport.dart'; +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'; + +main() { + group('client-errors', () { + clientErrorTest('no-settings-frame-at-beginning', + (ServerTransportConnection server, + FrameWriter clientWriter, + StreamIterator clientReader, + Future nextFrame()) async { + + Future serverFun() async { + // Make sure the server reports the connection error on the stream of + // incoming connections. + await server.incomingStreams.toList().catchError(expectAsync((error) { + expect('$error', contains('Connection error')); + })); + + await server.terminate(); + } + + 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. + var frame = await nextFrame(); + expect(frame is GoawayFrame, true); + expect((frame as GoawayFrame).errorCode, ErrorCode.PROTOCOL_ERROR); + + // Make sure the server ended the connection. + expect(await clientReader.moveNext(), false); + } + + await Future.wait([serverFun(), clientFun()]); + }); + }); +} + +clientErrorTest(String name, + func(serverConnection, frameWriter, frameReader, readNext)) { + return test(name, () { + var streams = new ClientErrorStreams(); + var clientReader = streams.clientConnectionFrameReader; + + Future readNext() async { + expect(await clientReader.moveNext(), true); + return clientReader.current; + } + + return func(streams.serverConnection, + streams.clientConnectionFrameWriter, + clientReader, + readNext); + }); +} + +class ClientErrorStreams { + final StreamController> writeA = new StreamController(); + final StreamController> writeB = new StreamController(); + Stream> get readA => writeA.stream; + Stream> get readB => writeB.stream; + + StreamIterator get clientConnectionFrameReader { + Settings localSettings = new Settings(); + return new StreamIterator( + new FrameReader(readA, localSettings).startDecoding()); + } + + FrameWriter get clientConnectionFrameWriter { + var encoder = new HPackEncoder(); + Settings peerSettings = new Settings(); + writeB.add(CONNECTION_PREFACE); + return new FrameWriter(encoder, writeB, peerSettings); + } + + ServerTransportConnection get serverConnection + => new ServerTransportConnection.viaStreams(readB, writeA); +} From 9f03242cd8cd293f8b31bde5af63389df72d8dbb Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Wed, 12 Aug 2015 16:13:39 +0200 Subject: [PATCH 023/172] Add ClosableMixin and use it in stream/connection in/out queues R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/236107013 . --- pkgs/http2/lib/src/error_handler.dart | 48 ++++++++ .../src/flowcontrol/connection_queues.dart | 43 ++++++- .../lib/src/flowcontrol/stream_queues.dart | 105 ++++++++++-------- .../flowcontrol/connection_queues_test.dart | 18 ++- 4 files changed, 153 insertions(+), 61 deletions(-) diff --git a/pkgs/http2/lib/src/error_handler.dart b/pkgs/http2/lib/src/error_handler.dart index ead709841c..d3f0f60d9e 100644 --- a/pkgs/http2/lib/src/error_handler.dart +++ b/pkgs/http2/lib/src/error_handler.dart @@ -40,3 +40,51 @@ class TerminatableMixin { return f(); } } + +/// Used by classes which may be closed. +class ClosableMixin { + bool _closing = false; + Completer _completer = new Completer(); + + Future get done => _completer.future; + + bool get isClosing => _closing; + bool get wasClosed => _completer.isCompleted; + + void startClosing() { + if (!_closing) { + _closing = true; + + onClosing(); + onCheckForClose(); + } + } + + void onCheckForClose() { + // Subclasses can override this method if they want. + } + + void onClosing() { + // Subclasses can override this method if they want. + } + + dynamic ensureNotClosingSync(f()) { + if (isClosing) { + throw new StateError('Was in the process of closing.'); + } + return f(); + } + + void closeWithValue([value]) { + if (!wasClosed) { + _completer.complete(value); + } + } + + void closeWithError(error) { + if (!wasClosed) { + _completer.complete(error); + } + } +} + diff --git a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart index c4c3a50d47..8de8513f52 100644 --- a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart @@ -29,7 +29,11 @@ import '../frames/frames.dart'; /// allows sending the message and the underlying [StreamSink] is not /// buffering. /// - It will use a [FrameWriter] to write a new frame to the connection. -class ConnectionMessageQueueOut extends Object with TerminatableMixin { +// TODO: Make [StreamsHandler] call [connectionOut.startClosing()] once +// * all streams have been closed +// * the connection state is finishing +class ConnectionMessageQueueOut extends Object + with TerminatableMixin, ClosableMixin { /// The handler which will be used for increasing the connection-level flow /// control window. final OutgoingConnectionWindowHandler _connectionWindow; @@ -54,14 +58,23 @@ class ConnectionMessageQueueOut extends Object with TerminatableMixin { /// Enqueues a new [Message] which should be delivered to the remote peer. void enqueueMessage(Message message) { - if (!wasTerminated) { - _messages.addLast(message); - _trySendMessages(); - } + ensureNotClosingSync(() { + if (!wasTerminated) { + _messages.addLast(message); + _trySendMessages(); + } + }); } void onTerminated(error) { _messages.clear(); + closeWithError(error); + } + + void onCheckForClose() { + if (isClosing && _messages.length == 0) { + closeWithValue(); + } } void _trySendMessages() { @@ -76,6 +89,8 @@ class ConnectionMessageQueueOut extends Object with TerminatableMixin { // number of bytes written, we could just say, we loop here until 10kb // and after words, we'll make `Timer.run()`. Timer.run(_trySendMessages); + } else { + onCheckForClose(); } } } @@ -138,7 +153,11 @@ class ConnectionMessageQueueOut extends Object with TerminatableMixin { /// /// Incoming [DataFrame]s will decrease the flow control window the peer has /// available. -class ConnectionMessageQueueIn extends Object with TerminatableMixin { +// TODO: Make [StreamsHandler] call [connectionOut.startClosing()] once +// * all streams have been closed +// * the connection state is finishing +class ConnectionMessageQueueIn extends Object + with TerminatableMixin, ClosableMixin { /// The handler which will be used for increasing the connection-level flow /// control window. final IncomingWindowHandler _windowUpdateHandler; @@ -162,6 +181,16 @@ class ConnectionMessageQueueIn extends Object with TerminatableMixin { // should have been removed at this point. assert(_stream2messageQueue.isEmpty); assert(_stream2pendingMessages.isEmpty); + closeWithError(error); + } + + void onCheckForClose() { + if (isClosing) { + assert (_stream2messageQueue.isEmpty == _stream2pendingMessages.isEmpty); + if (_stream2messageQueue.isEmpty) { + closeWithValue(); + } + } } /// The number of pending messages which haven't been delivered @@ -266,5 +295,7 @@ class ConnectionMessageQueueIn extends Object with TerminatableMixin { if (bytesDeliveredToStream > 0) { _windowUpdateHandler.dataProcessed(bytesDeliveredToStream); } + + onCheckForClose(); } } diff --git a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart index 465cc56f5d..3f643474bd 100644 --- a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart @@ -21,7 +21,8 @@ import 'window_handler.dart'; /// /// It will ensure that we never send more data than the remote flow control /// window allows. -class StreamMessageQueueOut extends Object with TerminatableMixin { +class StreamMessageQueueOut extends Object + with TerminatableMixin, ClosableMixin { /// The id of the stream this message queue belongs to. final int streamId; @@ -68,30 +69,40 @@ class StreamMessageQueueOut extends Object with TerminatableMixin { /// Enqueues a new [Message] which is to be delivered to the connection /// message queue. void enqueueMessage(Message message) { - if (!wasTerminated) { - if (message is DataMessage) { - toBeWrittenBytes += message.bytes.length; - } + ensureNotClosingSync(() { + if (!wasTerminated) { + if (message.endStream) startClosing(); + + if (message is DataMessage) { + toBeWrittenBytes += message.bytes.length; + } - _messages.addLast(message); - _trySendData(); + _messages.addLast(message); + _trySendData(); - if (_messages.length > 0) { - bufferIndicator.markBuffered(); + if (_messages.length > 0) { + bufferIndicator.markBuffered(); + } } - } + }); } - /// Terminates this stream message queue. Further operations on it will fail. - void terminate([error]) { - super.terminate(error); + void onTerminated(error) { _messages.clear(); + closeWithError(error); + } + + void onCheckForClose() { + if (isClosing && _messages.isEmpty) closeWithValue(); + } void _trySendData() { int queueLenBefore = _messages.length; while (_messages.length > 0) { + assert(!isClosing); + Message message = _messages.first; if (message is HeadersMessage) { @@ -132,10 +143,11 @@ class StreamMessageQueueOut extends Object with TerminatableMixin { throw new StateError('Unknown messages type: ${message.runtimeType}'); } } - if (queueLenBefore > 0 && _messages.isEmpty) { bufferIndicator.markUnBuffered(); } + + onCheckForClose(); } } @@ -144,7 +156,8 @@ class StreamMessageQueueOut extends Object with TerminatableMixin { /// /// It will keep messages up to the stream flow control window size if the /// [messages] listener is paused. -class StreamMessageQueueIn extends Object with TerminatableMixin { +class StreamMessageQueueIn extends Object + with TerminatableMixin, ClosableMixin { /// The stream-level window our peer is using when sending us messages. final IncomingWindowHandler windowHandler; @@ -168,7 +181,7 @@ class StreamMessageQueueIn extends Object with TerminatableMixin { _incomingMessagesC = new StreamController( onListen: () { - if (_checkOpen()) { + if (!wasClosed && !wasTerminated) { _tryDispatch(); _tryUpdateBufferIndicator(); } @@ -177,13 +190,14 @@ class StreamMessageQueueIn extends Object with TerminatableMixin { // TODO: Would we ever want to decrease the window size in this // situation? }, onResume: () { - if (_checkOpen()) { + if (!wasClosed && !wasTerminated) { _tryDispatch(); _tryUpdateBufferIndicator(); } }, onCancel: () { _pendingMessages.clear(); - _close(); + startClosing(); + onCloseCheck(); }); // FIXME/TODO/FIXME: This needs to change, we need to intercept all @@ -203,22 +217,35 @@ class StreamMessageQueueIn extends Object with TerminatableMixin { /// A lower layer enqueues a new [Message] which should be delivered to the /// app. void enqueueMessage(Message message) { - if (_checkOpen()) { + ensureNotClosingSync(() { + if (!wasTerminated) { if (message is DataMessage) { - windowHandler.gotData(message.bytes.length); - } - _pendingMessages.add(message); + windowHandler.gotData(message.bytes.length); + } + _pendingMessages.add(message); + if (message.endStream) startClosing(); - _tryDispatch(); - _tryUpdateBufferIndicator(); - } + _tryDispatch(); + _tryUpdateBufferIndicator(); + } + }); } - void onTerminated(TransportException exception) { + void onTerminated(exception) { _pendingMessages.clear(); - if (!_incomingMessagesC.isClosed) { + if (!wasClosed) { _incomingMessagesC.addError(exception); - _close(); + _incomingMessagesC.close(); + _serverPushStreamsC.close(); + closeWithError(exception); + } + } + + void onCloseCheck() { + if (isClosing && !wasClosed && _pendingMessages.isEmpty) { + _incomingMessagesC.close(); + _serverPushStreamsC.close(); + closeWithValue(); } } @@ -243,9 +270,6 @@ class StreamMessageQueueIn extends Object with TerminatableMixin { windowHandler.dataProcessed(message.bytes.length); } } - if (message.endStream) { - _close(); - } handled = true; } } else if (message is PushPromiseMessage) { @@ -257,19 +281,19 @@ class StreamMessageQueueIn extends Object with TerminatableMixin { var transportStreamPush = new TransportStreamPush( message.headers, message.pushedStream); _serverPushStreamsC.add(transportStreamPush); - if (message.endStream) { - _close(); - } handled = true; } } - if (!handled) { + if (handled) { + if (message.endStream) { + onCloseCheck(); + } + } else { break; } } } - void _tryUpdateBufferIndicator() { if (_incomingMessagesC.isPaused || _pendingMessages.length > 0) { bufferIndicator.markBuffered(); @@ -277,13 +301,4 @@ class StreamMessageQueueIn extends Object with TerminatableMixin { bufferIndicator.markUnBuffered(); } } - - void _close() { - if (!_incomingMessagesC.isClosed) { - _incomingMessagesC.close(); - _serverPushStreamsC.close(); - } - } - - bool _checkOpen() => !_incomingMessagesC.isClosed && !wasTerminated; } diff --git a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart index 8e6082b07f..f695d937b9 100644 --- a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart @@ -57,10 +57,10 @@ main() { {bool endStream}) { expect(streamId, 99); expect(sendingBytes, bytes); - expect(endStream, true); + expect(endStream, false); c.got(); }; - queue.enqueueMessage(new DataMessage(99, bytes, true)); + queue.enqueueMessage(new DataMessage(99, bytes, false)); expect(queue.pendingMessages, 0); fw.mock_writeDataFrame = null; @@ -100,14 +100,12 @@ main() { windowMock.peerWindowSize = bytes.length - 1; windowMock.positiveWindow.markUnBuffered(); - // Terminate it now, ensure messages get cleared and we no longer - // enqueue messages. - queue.terminate(); - expect(queue.pendingMessages, 0); - fw = new MockFrameWriter(); - windowMock = new MockOutgoingWindowHandler(); - queue.enqueueMessage(new DataMessage(99, bytes, true)); - expect(queue.pendingMessages, 0); + queue.startClosing(); + queue.done.then(expectAsync((_) { + expect(queue.pendingMessages, 0); + expect(() => queue.enqueueMessage(new DataMessage(99, bytes, true)), + throws); + })); }); test('connection-message-queue-in', () { From a3fd95ad462628e08671fda59a1fc5f69f2df1cf Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Thu, 13 Aug 2015 12:19:27 +0200 Subject: [PATCH 024/172] Implement (first version) of graceful shutdown. R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/241757013 . --- pkgs/http2/lib/client.dart | 2 +- pkgs/http2/lib/src/connection.dart | 14 +- .../http2/lib/src/streams/stream_handler.dart | 22 +- pkgs/http2/lib/transport.dart | 14 +- pkgs/http2/test/client_errors_test.dart | 95 --------- pkgs/http2/test/server_tests.dart | 196 ++++++++++++++++++ 6 files changed, 227 insertions(+), 116 deletions(-) delete mode 100644 pkgs/http2/test/client_errors_test.dart create mode 100644 pkgs/http2/test/server_tests.dart diff --git a/pkgs/http2/lib/client.dart b/pkgs/http2/lib/client.dart index 7c99970563..9c87a2b59b 100644 --- a/pkgs/http2/lib/client.dart +++ b/pkgs/http2/lib/client.dart @@ -55,7 +55,7 @@ class ClientConnection { } Future close() { - return connection.terminate(); + return connection.finish(); } Future _handleStream(ClientTransportStream stream) { diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index 1fdc07dbca..f6cec4d22e 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -185,8 +185,16 @@ abstract class Connection { } /// Finishes this connection. - void finish() { + Future finish() { _finishing(active: true); + + // TODO: There is probably more we need to wait for. + return _streams.done.whenComplete(() { + var futures = [_frameWriter.close()]; + var f = _frameReaderSubscription.cancel(); + if (f != null) futures.add(f); + return Future.wait(futures); + }); } /// Terminates this connection forcefully. @@ -285,9 +293,7 @@ abstract class Connection { message != null ? UTF8.encode(message) : []); } - // TODO: Propagate to the [StreamSet] that we no longer process new - // streams. And make it return a Future which we can listen to until - // all streams are done. + _streams.startClosing(); } } diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index b46b73adf8..1577eecf41 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -99,7 +99,8 @@ class Http2StreamImpl extends TransportStream /// It keeps track of open streams, their state, their queues, forwards /// messages from the connection level to stream level and vise versa. // TODO: Respect SETTINGS_MAX_CONCURRENT_STREAMS -class StreamHandler extends Object with TerminatableMixin { +// TODO: Handle stream/connection queue errors & forward to connection object. +class StreamHandler extends Object with TerminatableMixin, ClosableMixin { static const int MAX_STREAM_ID = (1 << 31) - 1; final FrameWriter _frameWriter; final ConnectionMessageQueueIn incomingQueue; @@ -147,10 +148,7 @@ class StreamHandler extends Object with TerminatableMixin { void onTerminated(exception) { _openStreams.values.toList().forEach( (stream) => _closeStreamAbnormally(stream, exception)); - - // Signal that there are no more incoming connections (server case). - // FIXME: Should we do this always (even in client case?) - _newStreamsC..addError(exception)..close(); + startClosing(); } Stream get incomingStreams => _newStreamsC.stream; @@ -602,6 +600,8 @@ class StreamHandler extends Object with TerminatableMixin { incomingQueue.removeStreamMessageQueue(stream.id); _openStreams.remove(stream.id); + + onCheckForClose(); } void _closeStreamIdAbnormally(int streamId, Exception exception) { @@ -622,5 +622,17 @@ class StreamHandler extends Object with TerminatableMixin { stream.outgoingQueue.terminate(); _openStreams.remove(stream.id); + + onCheckForClose(); + } + + void onClosing() { + _newStreamsC.close(); + } + + void onCheckForClose() { + if (isClosing && _openStreams.isEmpty) { + closeWithValue(); + } } } diff --git a/pkgs/http2/lib/transport.dart b/pkgs/http2/lib/transport.dart index 125f9a0256..9100238e58 100644 --- a/pkgs/http2/lib/transport.dart +++ b/pkgs/http2/lib/transport.dart @@ -20,7 +20,7 @@ abstract class TransportConnection { /// Finish this connection. /// /// No new streams will be accepted or can be created. - void finish(); + Future finish(); /// Terminates this connection forcefully. Future terminate(); @@ -33,7 +33,7 @@ abstract class ClientTransportConnection extends TransportConnection { socket, socket, allowServerPushes: allowServerPushes); factory ClientTransportConnection.viaStreams(Stream> incoming, - Sink> outgoing, + StreamSink> outgoing, {bool allowServerPushes: true}) => new ClientConnection( incoming, outgoing, allowServerPushes: allowServerPushes); @@ -48,19 +48,11 @@ abstract class ServerTransportConnection extends TransportConnection { => new ServerTransportConnection.viaStreams(socket, socket); factory ServerTransportConnection.viaStreams(Stream> incoming, - Sink> outgoing) + StreamSink> outgoing) => new ServerConnection(incoming, outgoing); /// Incoming HTTP/2 streams. Stream get incomingStreams; - - /// Finish this connection. - /// - /// No new streams will be accepted or can be created. - void finish(); - - /// Terminates this connection forcefully. - Future terminate(); } /// Represents a HTTP/2 stream. diff --git a/pkgs/http2/test/client_errors_test.dart b/pkgs/http2/test/client_errors_test.dart deleted file mode 100644 index 94f34de5e9..0000000000 --- a/pkgs/http2/test/client_errors_test.dart +++ /dev/null @@ -1,95 +0,0 @@ -// 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. - -library http2.test.client_errors_test; - -import 'dart:async'; -import 'dart:convert'; - -import 'package:unittest/unittest.dart'; - -import 'package:http2/transport.dart'; -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'; - -main() { - group('client-errors', () { - clientErrorTest('no-settings-frame-at-beginning', - (ServerTransportConnection server, - FrameWriter clientWriter, - StreamIterator clientReader, - Future nextFrame()) async { - - Future serverFun() async { - // Make sure the server reports the connection error on the stream of - // incoming connections. - await server.incomingStreams.toList().catchError(expectAsync((error) { - expect('$error', contains('Connection error')); - })); - - await server.terminate(); - } - - 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. - var frame = await nextFrame(); - expect(frame is GoawayFrame, true); - expect((frame as GoawayFrame).errorCode, ErrorCode.PROTOCOL_ERROR); - - // Make sure the server ended the connection. - expect(await clientReader.moveNext(), false); - } - - await Future.wait([serverFun(), clientFun()]); - }); - }); -} - -clientErrorTest(String name, - func(serverConnection, frameWriter, frameReader, readNext)) { - return test(name, () { - var streams = new ClientErrorStreams(); - var clientReader = streams.clientConnectionFrameReader; - - Future readNext() async { - expect(await clientReader.moveNext(), true); - return clientReader.current; - } - - return func(streams.serverConnection, - streams.clientConnectionFrameWriter, - clientReader, - readNext); - }); -} - -class ClientErrorStreams { - final StreamController> writeA = new StreamController(); - final StreamController> writeB = new StreamController(); - Stream> get readA => writeA.stream; - Stream> get readB => writeB.stream; - - StreamIterator get clientConnectionFrameReader { - Settings localSettings = new Settings(); - return new StreamIterator( - new FrameReader(readA, localSettings).startDecoding()); - } - - FrameWriter get clientConnectionFrameWriter { - var encoder = new HPackEncoder(); - Settings peerSettings = new Settings(); - writeB.add(CONNECTION_PREFACE); - return new FrameWriter(encoder, writeB, peerSettings); - } - - ServerTransportConnection get serverConnection - => new ServerTransportConnection.viaStreams(readB, writeA); -} diff --git a/pkgs/http2/test/server_tests.dart b/pkgs/http2/test/server_tests.dart new file mode 100644 index 0000000000..767afbdfbb --- /dev/null +++ b/pkgs/http2/test/server_tests.dart @@ -0,0 +1,196 @@ +// 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. + +library http2.test.client_errors_test; + +import 'dart:async'; + +import 'package:unittest/unittest.dart'; + +import 'package:http2/transport.dart'; +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'; + +main() { + group('server-tests', () { + group('normal', () { + serverTest('gracefull-shutdown-for-unused-connection', + (ServerTransportConnection server, + FrameWriter clientWriter, + StreamIterator clientReader, + Future 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 clientReader, + Future 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. + var frame = await nextFrame(); + expect(frame is GoawayFrame, true); + expect((frame as GoawayFrame).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 clientReader, + Future 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 RstStreamFrame, true); + expect((frame as RstStreamFrame).errorCode, ErrorCode.STREAM_CLOSED); + expect((frame as RstStreamFrame).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 clientReader, + Future 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, [new 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. + var frame = await nextFrame(); + expect(frame is RstStreamFrame, true); + expect((frame as RstStreamFrame).errorCode, ErrorCode.STREAM_CLOSED); + expect((frame as RstStreamFrame).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(String name, + func(serverConnection, frameWriter, frameReader, readNext)) { + return test(name, () { + var streams = new ClientErrorStreams(); + var clientReader = streams.clientConnectionFrameReader; + + Future readNext() async { + expect(await clientReader.moveNext(), true); + return clientReader.current; + } + + return func(streams.serverConnection, + streams.clientConnectionFrameWriter, + clientReader, + readNext); + }); +} + +class ClientErrorStreams { + final StreamController> writeA = new StreamController(); + final StreamController> writeB = new StreamController(); + Stream> get readA => writeA.stream; + Stream> get readB => writeB.stream; + + StreamIterator get clientConnectionFrameReader { + Settings localSettings = new Settings(); + return new StreamIterator( + new FrameReader(readA, localSettings).startDecoding()); + } + + FrameWriter get clientConnectionFrameWriter { + var encoder = new HPackEncoder(); + Settings peerSettings = new Settings(); + writeB.add(CONNECTION_PREFACE); + return new FrameWriter(encoder, writeB, peerSettings); + } + + ServerTransportConnection get serverConnection + => new ServerTransportConnection.viaStreams(readB, writeA); +} From b2df920f07104731eecca9f219ffe2712f4d071f Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Thu, 13 Aug 2015 14:59:25 +0200 Subject: [PATCH 025/172] Extract some byte related functions into separate libarary: Use it in hpack.dart R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/236287013 . --- pkgs/http2/lib/src/byte_utils.dart | 59 +++++++++++++++++++ pkgs/http2/lib/src/connection_preface.dart | 2 +- .../src/flowcontrol/connection_queues.dart | 3 +- .../lib/src/flowcontrol/stream_queues.dart | 2 +- pkgs/http2/lib/src/frames/frame_reader.dart | 24 ++++---- pkgs/http2/lib/src/frames/frame_utils.dart | 51 ---------------- pkgs/http2/lib/src/frames/frame_writer.dart | 26 ++++---- pkgs/http2/lib/src/frames/frames.dart | 1 + pkgs/http2/lib/src/hpack/hpack.dart | 4 +- .../src/flowcontrol/stream_queues_test.dart | 4 -- pkgs/http2/test/src/mock_utils.dart | 4 +- 11 files changed, 92 insertions(+), 88 deletions(-) create mode 100644 pkgs/http2/lib/src/byte_utils.dart diff --git a/pkgs/http2/lib/src/byte_utils.dart b/pkgs/http2/lib/src/byte_utils.dart new file mode 100644 index 0000000000..5ed20473d4 --- /dev/null +++ b/pkgs/http2/lib/src/byte_utils.dart @@ -0,0 +1,59 @@ +// 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. + +library http2.src.byte_utils; + +import 'dart:typed_data'; + +List viewOrSublist(List data, int offset, int length) { + if (data is Uint8List) { + return new Uint8List.view(data.buffer, data.offsetInBytes + offset, length); + } else { + return data.sublist(offset, offset + length); + } +} + +int readInt64(List bytes, int offset) { + int high = readInt32(bytes, offset); + int low = readInt32(bytes, offset + 4); + return high << 32 | low; +} + +int readInt32(List bytes, int offset) { + return (bytes[offset] << 24) | (bytes[offset + 1] << 16) | + (bytes[offset + 2] << 8) | bytes[offset + 3]; +} + +int readInt24(List bytes, int offset) { + return (bytes[offset] << 16) | (bytes[offset + 1] << 8) | bytes[offset + 2]; +} + +int readInt16(List bytes, int offset) { + return (bytes[offset] << 8) | bytes[offset + 1]; +} + + +void setInt64(List bytes, int offset, int value) { + setInt32(bytes, offset, value >> 32); + setInt32(bytes, offset + 4, value & 0xffffffff); +} + +void setInt32(List bytes, int offset, int value) { + bytes[offset] = (value >> 24) & 0xff; + bytes[offset + 1] = (value >> 16) & 0xff; + bytes[offset + 2] = (value >> 8) & 0xff; + bytes[offset + 3] = value & 0xff; +} + +void setInt24(List bytes, int offset, int value) { + bytes[offset] = (value >> 16) & 0xff; + bytes[offset + 1] = (value >> 8) & 0xff; + bytes[offset + 2] = value & 0xff; +} + +void setInt16(List bytes, int offset, int value) { + bytes[offset] = (value >> 8) & 0xff; + bytes[offset + 1] = value & 0xff; +} + diff --git a/pkgs/http2/lib/src/connection_preface.dart b/pkgs/http2/lib/src/connection_preface.dart index 194b67a78a..9e604ab6d0 100644 --- a/pkgs/http2/lib/src/connection_preface.dart +++ b/pkgs/http2/lib/src/connection_preface.dart @@ -7,7 +7,7 @@ library http2.src.connection_preface; import 'dart:async'; import 'dart:math'; -import 'frames/frames.dart'; +import 'byte_utils.dart'; /// This is a set of bytes with which a client connection begins in the normal /// case. It can be used on a server to distinguish HTTP/1.1 and HTTP/2 clients. diff --git a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart index 8de8513f52..de3a56cd07 100644 --- a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart @@ -12,13 +12,14 @@ import 'dart:collection'; import '../../transport.dart'; +import '../byte_utils.dart'; import '../error_handler.dart'; import '../frames/frames.dart'; import 'stream_queues.dart'; import 'queue_messages.dart'; import 'window_handler.dart'; -import '../frames/frames.dart'; + /// The last place before messages coming from the application get encoded and /// send as [Frame]s. diff --git a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart index 3f643474bd..9603fc1252 100644 --- a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart @@ -9,8 +9,8 @@ import 'dart:collection'; import '../../transport.dart'; import '../async_utils/async_utils.dart'; +import '../byte_utils.dart'; import '../error_handler.dart'; -import '../frames/frames.dart'; import 'connection_queues.dart'; import 'queue_messages.dart'; diff --git a/pkgs/http2/lib/src/frames/frame_reader.dart b/pkgs/http2/lib/src/frames/frame_reader.dart index c362452706..ded54b9fa6 100644 --- a/pkgs/http2/lib/src/frames/frame_reader.dart +++ b/pkgs/http2/lib/src/frames/frame_reader.dart @@ -146,10 +146,10 @@ class FrameReader { /// Reads a FrameHeader] from [bytes], starting at [offset]. FrameHeader _readFrameHeader(List bytes, int offset) { - int length = _readInt24(bytes, offset); + int length = readInt24(bytes, offset); int type = bytes[offset + 3]; int flags = bytes[offset + 4]; - int streamId = _readInt32(bytes, offset + 5) & 0x7fffffff; + int streamId = readInt32(bytes, offset + 5) & 0x7fffffff; return new FrameHeader(length, type, flags, streamId); } @@ -183,7 +183,7 @@ class FrameReader { if (_isFlagSet(header.flags, HeadersFrame.FLAG_PRIORITY)) { _checkFrameLengthCondition((frameEnd - offset) >= 5); exclusiveDependency = (bytes[offset] & 0x80) == 0x80; - streamDependency = _readInt32(bytes, offset) & 0x7fffffff; + streamDependency = readInt32(bytes, offset) & 0x7fffffff; offset += 4; weight = bytes[offset++]; } @@ -200,7 +200,7 @@ class FrameReader { (frameEnd - offset) == PriorityFrame.FIXED_FRAME_LENGTH, message: 'Priority frame length must be exactly 5 bytes.'); bool exclusiveDependency = (bytes[offset] & 0x80) == 0x80; - int streamDependency = _readInt32(bytes, offset) & 0x7fffffff; + int streamDependency = readInt32(bytes, offset) & 0x7fffffff; int weight = bytes[offset + 4]; return new PriorityFrame( header, exclusiveDependency, streamDependency, weight); @@ -209,7 +209,7 @@ class FrameReader { _checkFrameLengthCondition( (frameEnd - offset) == RstStreamFrame.FIXED_FRAME_LENGTH, message: 'Rst frames must have a length of 4.'); - int errorCode = _readInt32(bytes, offset); + int errorCode = readInt32(bytes, offset); return new RstStreamFrame(header, errorCode); case FrameType.SETTINGS: @@ -220,8 +220,8 @@ class FrameReader { int count = header.length ~/ 6; var settings = new List(count); for (int i = 0; i < count; i++) { - int identifier = _readInt16(bytes, offset + 6 * i); - int value = _readInt32(bytes, offset + 6 * i + 2); + int identifier = readInt16(bytes, offset + 6 * i); + int value = readInt32(bytes, offset + 6 * i + 2); settings[i] = new Setting(identifier, value); } var frame = new SettingsFrame(header, settings); @@ -238,7 +238,7 @@ class FrameReader { _checkFrameLengthCondition((frameEnd - offset) >= 1); padLength = bytes[offset++]; } - int promisedStreamId = _readInt32(bytes, offset) & 0x7fffffff; + int promisedStreamId = readInt32(bytes, offset) & 0x7fffffff; offset += 4; int headerBlockLen = frameEnd - offset - padLength; _checkFrameLengthCondition(headerBlockLen >= 0); @@ -251,13 +251,13 @@ class FrameReader { _checkFrameLengthCondition( (frameEnd - offset) == PingFrame.FIXED_FRAME_LENGTH, message: 'Ping frames must have a length of 8.'); - var opaqueData = _readInt64(bytes, offset); + var opaqueData = readInt64(bytes, offset); return new PingFrame(header, opaqueData); case FrameType.GOAWAY: _checkFrameLengthCondition((frameEnd - offset) >= 8); - int lastStreamId = _readInt32(bytes, offset); - int errorCode = _readInt32(bytes, offset + 4); + int lastStreamId = readInt32(bytes, offset); + int errorCode = readInt32(bytes, offset + 4); var debugData = viewOrSublist(bytes, offset + 8, header.length - 8); return new GoawayFrame(header, lastStreamId, errorCode, debugData); @@ -265,7 +265,7 @@ class FrameReader { _checkFrameLengthCondition( (frameEnd - offset) == WindowUpdateFrame.FIXED_FRAME_LENGTH, message: 'Window update frames must have a length of 4.'); - int windowSizeIncrement = _readInt32(bytes, offset) & 0x7fffffff; + int windowSizeIncrement = readInt32(bytes, offset) & 0x7fffffff; return new WindowUpdateFrame(header, windowSizeIncrement); case FrameType.CONTINUATION: diff --git a/pkgs/http2/lib/src/frames/frame_utils.dart b/pkgs/http2/lib/src/frames/frame_utils.dart index 55dcc75451..73a3173423 100644 --- a/pkgs/http2/lib/src/frames/frame_utils.dart +++ b/pkgs/http2/lib/src/frames/frame_utils.dart @@ -4,55 +4,4 @@ part of http2.src.frames; -List viewOrSublist(List data, int offset, int length) { - if (data is Uint8List) { - return new Uint8List.view(data.buffer, data.offsetInBytes + offset, length); - } else { - return data.sublist(offset, offset + length); - } -} - -int _readInt64(List bytes, int offset) { - int high = _readInt32(bytes, offset); - int low = _readInt32(bytes, offset + 4); - return high << 32 | low; -} - -int _readInt32(List bytes, int offset) { - return (bytes[offset] << 24) | (bytes[offset + 1] << 16) | - (bytes[offset + 2] << 8) | bytes[offset + 3]; -} - -int _readInt24(List bytes, int offset) { - return (bytes[offset] << 16) | (bytes[offset + 1] << 8) | bytes[offset + 2]; -} - -int _readInt16(List bytes, int offset) { - return (bytes[offset] << 8) | bytes[offset + 1]; -} - - -void _setInt64(List bytes, int offset, int value) { - _setInt32(bytes, offset, value >> 32); - _setInt32(bytes, offset + 4, value & 0xffffffff); -} - -void _setInt32(List bytes, int offset, int value) { - bytes[offset] = (value >> 24) & 0xff; - bytes[offset + 1] = (value >> 16) & 0xff; - bytes[offset + 2] = (value >> 8) & 0xff; - bytes[offset + 3] = value & 0xff; -} - -void _setInt24(List bytes, int offset, int value) { - bytes[offset] = (value >> 16) & 0xff; - bytes[offset + 1] = (value >> 8) & 0xff; - bytes[offset + 2] = value & 0xff; -} - -void _setInt16(List bytes, int offset, int value) { - bytes[offset] = (value >> 8) & 0xff; - bytes[offset + 1] = value & 0xff; -} - bool _isFlagSet(int value, int flag) => value & flag == flag; diff --git a/pkgs/http2/lib/src/frames/frame_writer.dart b/pkgs/http2/lib/src/frames/frame_writer.dart index 72e5aa95c8..4bf58ccf10 100644 --- a/pkgs/http2/lib/src/frames/frame_writer.dart +++ b/pkgs/http2/lib/src/frames/frame_writer.dart @@ -4,8 +4,6 @@ part of http2.src.frames; -// TODO: Register for window update events. -// TODO: Register for setting update events. // TODO: No support for writing padded information. // TODO: No support for stream priorities. class FrameWriter { @@ -130,9 +128,9 @@ class FrameWriter { offset += FRAME_HEADER_SIZE; if (exclusive) { - _setInt32(buffer, offset, (1 << 31) | streamDependency); + setInt32(buffer, offset, (1 << 31) | streamDependency); } else { - _setInt32(buffer, offset, streamDependency); + setInt32(buffer, offset, streamDependency); } buffer[offset + 4] = weight; @@ -150,7 +148,7 @@ class FrameWriter { _setFrameHeader(buffer, offset, type, flags, streamId, 4); offset += FRAME_HEADER_SIZE; - _setInt32(buffer, offset, errorCode); + setInt32(buffer, offset, errorCode); _writeData(buffer); } @@ -167,8 +165,8 @@ class FrameWriter { for (int i = 0; i < settings.length; i++) { var setting = settings[i]; - _setInt16(buffer, offset + 6 * i, setting.identifier); - _setInt32(buffer, offset + 6 * i + 2, setting.value); + setInt16(buffer, offset + 6 * i, setting.identifier); + setInt32(buffer, offset + 6 * i + 2, setting.value); } _writeData(buffer); @@ -221,7 +219,7 @@ class FrameWriter { _setFrameHeader(buffer, offset, type, flags, streamId, 4 + fragment.length); offset += FRAME_HEADER_SIZE; - _setInt32(buffer, offset, promisedStreamId); + setInt32(buffer, offset, promisedStreamId); buffer.setRange(offset + 4, offset + 4 + fragment.length, fragment); _writeData(buffer); @@ -238,7 +236,7 @@ class FrameWriter { _setFrameHeader(buffer, 0, type, flags, 0, 8); offset += FRAME_HEADER_SIZE; - _setInt64(buffer, offset, opaqueData); + setInt64(buffer, offset, opaqueData); _writeData(buffer); } @@ -252,8 +250,8 @@ class FrameWriter { _setFrameHeader(buffer, offset, type, flags, 0, 8 + debugData.length); offset += FRAME_HEADER_SIZE; - _setInt32(buffer, offset, lastStreamId); - _setInt32(buffer, offset + 4, errorCode); + setInt32(buffer, offset, lastStreamId); + setInt32(buffer, offset + 4, errorCode); buffer.setRange(offset + 8, buffer.length, debugData); _writeData(buffer); @@ -270,7 +268,7 @@ class FrameWriter { _setFrameHeader(buffer, offset, type, flags, streamId, 4); offset += FRAME_HEADER_SIZE; - _setInt32(buffer, offset, sizeIncrement); + setInt32(buffer, offset, sizeIncrement); _writeData(buffer); } @@ -294,10 +292,10 @@ class FrameWriter { void _setFrameHeader(List bytes, int offset, int type, int flags, int streamId, int length) { - _setInt24(bytes, offset, length); + setInt24(bytes, offset, length); bytes[3] = type; bytes[4] = flags; - _setInt32(bytes, 5, streamId); + setInt32(bytes, 5, streamId); _highestWrittenStreamId = max(_highestWrittenStreamId, streamId); } diff --git a/pkgs/http2/lib/src/frames/frames.dart b/pkgs/http2/lib/src/frames/frames.dart index 40df8326e9..9843165db0 100644 --- a/pkgs/http2/lib/src/frames/frames.dart +++ b/pkgs/http2/lib/src/frames/frames.dart @@ -9,6 +9,7 @@ import 'dart:typed_data'; import 'dart:math' show max; import '../async_utils/async_utils.dart'; +import '../byte_utils.dart'; import '../hpack/hpack.dart'; import '../settings/settings.dart'; import '../sync_errors.dart'; diff --git a/pkgs/http2/lib/src/hpack/hpack.dart b/pkgs/http2/lib/src/hpack/hpack.dart index 417c868758..b277857b75 100644 --- a/pkgs/http2/lib/src/hpack/hpack.dart +++ b/pkgs/http2/lib/src/hpack/hpack.dart @@ -12,6 +12,7 @@ import 'dart:io'; import 'huffman.dart'; import 'huffman_table.dart'; +import '../byte_utils.dart'; /// Exception raised due to encoding/decoding errors. class HPackDecodingException implements Exception { @@ -95,8 +96,7 @@ class HPackDecoder { bool isHuffmanEncoding = (data[offset] & 0x80) != 0; int length = readInteger(7); - // TODO: Use view's for Uint8list - var sublist = data.sublist(offset, offset + length); + var sublist = viewOrSublist(data, offset, length); offset += length; if (isHuffmanEncoding) { return http2HuffmanCodec.decode(sublist); diff --git a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart index c89beabf76..27e01313ba 100644 --- a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart @@ -2,8 +2,6 @@ // 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:unittest/unittest.dart'; import 'package:http2/transport.dart'; @@ -12,9 +10,7 @@ import 'package:http2/src/flowcontrol/queue_messages.dart'; import 'package:http2/src/flowcontrol/stream_queues.dart'; import 'package:http2/src/flowcontrol/window_handler.dart'; import 'package:http2/src/flowcontrol/connection_queues.dart'; -import 'package:http2/src/frames/frames.dart'; -import '../error_matchers.dart'; import '../mock_utils.dart'; main() { diff --git a/pkgs/http2/test/src/mock_utils.dart b/pkgs/http2/test/src/mock_utils.dart index f4e1353c67..8a61a13a24 100644 --- a/pkgs/http2/test/src/mock_utils.dart +++ b/pkgs/http2/test/src/mock_utils.dart @@ -62,10 +62,10 @@ class SmartMock { name = name.substring('mock_'.length, name.length - 1); return handleRegistration(name); } else { - handleCall(); + return handleCall(); } } else { - handleCall(); + return handleCall(); } } } From 6e678e4b92e73e7abce427b6874ac6583781b681 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Fri, 21 Aug 2015 15:35:57 +0200 Subject: [PATCH 026/172] Implement server-side termination of a stream BUG= R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/246727013 . --- .../lib/src/flowcontrol/stream_queues.dart | 4 +- .../http2/lib/src/streams/stream_handler.dart | 27 +++++++---- pkgs/http2/test/server_tests.dart | 46 +++++++++++++++++++ 3 files changed, 68 insertions(+), 9 deletions(-) diff --git a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart index 9603fc1252..63abf32f50 100644 --- a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart @@ -234,7 +234,9 @@ class StreamMessageQueueIn extends Object void onTerminated(exception) { _pendingMessages.clear(); if (!wasClosed) { - _incomingMessagesC.addError(exception); + if (exception != null) { + _incomingMessagesC.addError(exception); + } _incomingMessagesC.close(); _serverPushStreamsC.close(); closeWithError(exception); diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index 1577eecf41..a0b0ba9f97 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -62,6 +62,7 @@ class Http2StreamImpl extends TransportStream StreamState state = StreamState.Idle; final Function _pushStreamFun; + final Function _terminateStreamFun; StreamSubscription _outgoingCSubscription; @@ -70,7 +71,8 @@ class Http2StreamImpl extends TransportStream this._outgoingC, this.id, this.windowHandler, - this._pushStreamFun); + this._pushStreamFun, + this._terminateStreamFun); /// A stream of data and/or headers from the remote end. Stream get incomingMessages => incomingQueue.messages; @@ -88,10 +90,7 @@ class Http2StreamImpl extends TransportStream TransportStream push(List
requestHeaders) => _pushStreamFun(this, requestHeaders); - void terminate() { - // TODO/FIXME: We need to send an RST to the other side. - _outgoingCSubscription.cancel(); - } + void terminate() => _terminateStreamFun(this); } /// Handles [Frame]s with a non-zero stream-id. @@ -251,7 +250,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { var _outgoingC = new StreamController(); var stream = new Http2StreamImpl( streamQueueIn, streamQueueOut, _outgoingC, streamId, windowOutHandler, - this._push); + this._push, this._terminateStream); _openStreams[stream.id] = stream; _setupOutgoingMessageHandling(stream); @@ -281,6 +280,17 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { return pushStream; } + void _terminateStream(Http2StreamImpl stream) { + if (stream.state == StreamState.Open || + stream.state == StreamState.HalfClosedLocal || + stream.state == StreamState.HalfClosedRemote || + stream.state == StreamState.ReservedLocal || + stream.state == StreamState.ReservedRemote) { + _frameWriter.writeRstStreamFrame(stream.id, ErrorCode.CANCEL); + _closeStreamAbnormally(stream, null, propagateException: false); + } + } + void _setupOutgoingMessageHandling(Http2StreamImpl stream) { stream._outgoingCSubscription = stream._outgoingC.stream.listen((StreamMessage msg) { @@ -611,11 +621,12 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { } } - void _closeStreamAbnormally(Http2StreamImpl stream, Exception exception) { + void _closeStreamAbnormally(Http2StreamImpl stream, Exception exception, + {bool propagateException: false}) { incomingQueue.removeStreamMessageQueue(stream.id); stream.state = StreamState.Terminated; - stream.incomingQueue.terminate(exception); + stream.incomingQueue.terminate(propagateException ? exception : null); stream._outgoingCSubscription.cancel(); // NOTE: we're not adding an error here. diff --git a/pkgs/http2/test/server_tests.dart b/pkgs/http2/test/server_tests.dart index 767afbdfbb..49e71fa1cd 100644 --- a/pkgs/http2/test/server_tests.dart +++ b/pkgs/http2/test/server_tests.dart @@ -151,6 +151,52 @@ main() { await Future.wait([serverFun(), clientFun()]); }); }); + + group('server-errors', () { + serverTest('server-resets-stream', + (ServerTransportConnection server, + FrameWriter clientWriter, + StreamIterator clientReader, + Future nextFrame()) async { + + Future serverFun() async { + var it = new 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, [new Header.ascii('a', 'b')], endStream: false); + + // Make sure the client gets a [RstStreamFrame] frame. + var frame = await nextFrame(); + expect(frame is RstStreamFrame, true); + expect((frame as RstStreamFrame).errorCode, ErrorCode.CANCEL); + expect((frame as RstStreamFrame).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()]); + }); + }); }); } From b54e62ef771441cd0a4e61856157b9f539b08480 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Fri, 21 Aug 2015 15:43:14 +0200 Subject: [PATCH 027/172] Add client tests for client behavior R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/246747013 . --- pkgs/http2/test/client_tests.dart | 160 ++++++++++++++++++++++++++++++ pkgs/http2/test/server_tests.dart | 2 +- 2 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 pkgs/http2/test/client_tests.dart diff --git a/pkgs/http2/test/client_tests.dart b/pkgs/http2/test/client_tests.dart new file mode 100644 index 0000000000..e885bb4a0a --- /dev/null +++ b/pkgs/http2/test/client_tests.dart @@ -0,0 +1,160 @@ +// 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. + +library http2.test.client_tests; + +import 'dart:async'; + +import 'package:unittest/unittest.dart'; + +import 'package:http2/transport.dart'; +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 'src/hpack/hpack_test.dart' show isHeader; + +main() { + group('server-tests', () { + group('normal', () { + clientTest('gracefull-shutdown-for-unused-connection', + (ClientTransportConnection client, + FrameWriter serverWriter, + StreamIterator serverReader, + Future nextFrame()) async { + + var settingsDone = new Completer(); + + Future serverFun() async { + serverWriter.writeSettingsFrame([]); + expect(await nextFrame() is SettingsFrame, true); + serverWriter.writeSettingsAckFrame(); + expect(await nextFrame() is SettingsFrame, true); + + settingsDone.complete(); + + // Make sure we get the graceful shutdown message. + var frame = await nextFrame(); + expect(frame is GoawayFrame, true); + expect((frame as GoawayFrame).errorCode, ErrorCode.NO_ERROR); + + // Make sure the client ended the connection. + expect(await serverReader.moveNext(), false); + } + + Future clientFun() async { + await settingsDone.future; + + // Try to gracefully finish the connection. + await client.finish(); + } + + await Future.wait([serverFun(), clientFun()]); + }); + }); + + group('client-errors', () { + clientTest('client-resets-stream', + (ClientTransportConnection client, + FrameWriter serverWriter, + StreamIterator serverReader, + Future nextFrame()) async { + + var settingsDone = new Completer(); + + Future serverFun() async { + var decoder = new HPackDecoder(); + + serverWriter.writeSettingsFrame([]); + expect(await nextFrame() is SettingsFrame, true); + serverWriter.writeSettingsAckFrame(); + expect(await nextFrame() is SettingsFrame, true); + + settingsDone.complete(); + + // Make sure we got the new stream. + HeadersFrame frame = await nextFrame(); + expect(frame.hasEndStreamFlag, false); + var decodedHeaders = decoder.decode(frame.headerBlockFragment); + expect(decodedHeaders, hasLength(1)); + expect(decodedHeaders[0], isHeader('a', 'b')); + + // Make sure we got the stream reset. + frame = await nextFrame(); + expect(frame is RstStreamFrame, true); + expect((frame as RstStreamFrame).errorCode, ErrorCode.CANCEL); + + // Make sure we get the graceful shutdown message. + frame = await nextFrame(); + expect(frame is GoawayFrame, true); + expect((frame as GoawayFrame).errorCode, ErrorCode.NO_ERROR); + + // Make sure the client ended the connection. + expect(await serverReader.moveNext(), false); + } + + Future clientFun() async { + await settingsDone.future; + + // Make a new stream and terminate it. + var stream = client.makeRequest( + [new Header.ascii('a', 'b')], endStream: false); + stream.terminate(); + + // Make sure we don't get messages/pushes on the terminated stream. + expect(await stream.incomingMessages.toList(), isEmpty); + expect(await stream.peerPushes.toList(), isEmpty); + + // Try to gracefully finish the connection. + await client.finish(); + } + + await Future.wait([serverFun(), clientFun()]); + }); + }); + }); +} + +clientTest(String name, + func(clientConnection, frameWriter, frameReader, readNext)) { + return test(name, () { + var streams = new ClientStreams(); + var serverReader = streams.serverConnectionFrameReader; + + Future readNext() async { + expect(await serverReader.moveNext(), true); + return serverReader.current; + } + + return func(streams.clientConnection, + streams.serverConnectionFrameWriter, + serverReader, + readNext); + }); +} + +class ClientStreams { + final StreamController> writeA = new StreamController(); + final StreamController> writeB = new StreamController(); + Stream> get readA => writeA.stream; + Stream> get readB => writeB.stream; + + StreamIterator get serverConnectionFrameReader { + Settings localSettings = new Settings(); + var streamAfterConnectionPreface = readConnectionPreface(readA); + return new StreamIterator( + new FrameReader(streamAfterConnectionPreface, localSettings) + .startDecoding()); + } + + FrameWriter get serverConnectionFrameWriter { + var encoder = new HPackEncoder(); + Settings peerSettings = new Settings(); + return new FrameWriter(encoder, writeB, peerSettings); + } + + ClientTransportConnection get clientConnection + => new ClientTransportConnection.viaStreams(readB, writeA); +} diff --git a/pkgs/http2/test/server_tests.dart b/pkgs/http2/test/server_tests.dart index 49e71fa1cd..65f031a256 100644 --- a/pkgs/http2/test/server_tests.dart +++ b/pkgs/http2/test/server_tests.dart @@ -2,7 +2,7 @@ // 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. -library http2.test.client_errors_test; +library http2.test.server_tests; import 'dart:async'; From e38c84fbec2b51ef88456822d4fa31ff64c932ee Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Thu, 27 Aug 2015 10:09:21 +0200 Subject: [PATCH 028/172] Require sdk with boringssl support, remove sqlite db R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/245287013 . --- pkgs/http2/experimental/pkcert/README | 16 ------- pkgs/http2/experimental/pkcert/cert9.db | Bin 12288 -> 0 bytes pkgs/http2/experimental/pkcert/key4.db | Bin 20480 -> 0 bytes pkgs/http2/experimental/server.dart | 14 ++++-- pkgs/http2/experimental/server_chain.pem | 57 +++++++++++++++++++++++ pkgs/http2/experimental/server_key.pem | 29 ++++++++++++ pkgs/http2/pubspec.yaml | 2 +- 7 files changed, 96 insertions(+), 22 deletions(-) delete mode 100644 pkgs/http2/experimental/pkcert/README delete mode 100644 pkgs/http2/experimental/pkcert/cert9.db delete mode 100644 pkgs/http2/experimental/pkcert/key4.db create mode 100644 pkgs/http2/experimental/server_chain.pem create mode 100644 pkgs/http2/experimental/server_key.pem diff --git a/pkgs/http2/experimental/pkcert/README b/pkgs/http2/experimental/pkcert/README deleted file mode 100644 index fe764a9cc3..0000000000 --- a/pkgs/http2/experimental/pkcert/README +++ /dev/null @@ -1,16 +0,0 @@ -This is a certificate database used by Dart for testing purposes. - -It is created as a certificate database by NSS (Network Security Services), -a library from Mozilla, using the certutil tool. It uses a cert9.db file, -rather than a cert8.db file, so the database directory must be specified with -"sql:" in front of the directory path, or the environment variable -NSS_DEFAULT_DB_TYPE must be set to "sql". - -The password for the key database is "dartdart". - -The database contains a root certificate from Equifax, used to verify the -client https connection to www.google.dk. It contains a self-signed -certificate for a local certificate authority myauthority_cert, and a -server certificate for localhost called localhost_cert, signed by -myauthority_cert. It contains the key for localhost_cert, but -not the key for myauthority_cert. diff --git a/pkgs/http2/experimental/pkcert/cert9.db b/pkgs/http2/experimental/pkcert/cert9.db deleted file mode 100644 index 497fca668ed41d875948a64130060131e7fdb649..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeHN3sh9c8NT<J0GlR>Sp@ojwriuD6iQrO$*oGGMr+WmNJ|S(*BbQk7J&+k4EB!- zmPYvpga%73#Zq^Df;2obWR8DijC6Kzj5IndBqBOkIyX!jI5%uYXh>j`)IVTuWR!Z_7j7TF4 zhGyGAC=88Y7#iU)Gy-C1gv8JYilGq}LnAPTjz=ly9_t@Qma#HMmSJQWMwVe@8Ag_2 zWEn=5<63fDOO9*FaVYQ$JTOe4VBo0HAFTF%#?ygZ7VQS3e1!Ox2nLcD)6`{aa|=wQ(`nF zt{bmY$`x`Yg5*Sm%ZaFCoQPWBMASu2ME&GML?9<37FiLK!iingFg;vpS2dNnnySkk zD4N}YqS+lNn%#k-85fFXcc5s-g`yQMnlq?jSc)bNC!*QIiD(LOVnUaw>k{9zhV#d{IY-vn8vQ@}cQzx?L|5^gZ{vUu-pas-`BCz(^b^=>%8v`~5 zegp%ffe%T)%!p|x0f^=SXZxhpnsnpR>fCmVYj}l&gW}rVMg{tH8pHIQ~ ze+&2!1OXltZI_LKLBxQcKp>I0k}>)f3A&8<Popx}j4tV3a?MK5)S zk(z9xFCQaBV+4ant20fw}fj~0C+;x3gTDmUPqU3jLmsR#`R?Tqd z{~-c2fkWV3a07IJ22eYQhTrC@je%c01L&w0js$9w7Bf!ti)^%DXGo6M7?vid83jV% zL>?q!;R8SZ*WaIw|03-iua0jmbM)(%6;ulY-t%EiO1|wi#A4V%-0i2;114+jde zttRo>_1y90lR{iXf;Ie6ZFboY1$`;_Nb!LDVW-wV>el=-Uzc1g)y@*GpHRXc`M6qn z{?6Td=gtmk9lPu8C9jr`jS9wTvJ;45xM$zVdhw*Y@v0ZfGiZQ4wkOhi>?+r^Wg{Mk*M zaqsob0Zs4E40|bL!Iv#h!`W!%AxrKH01C* z*FeZs_aX0PFNY&;9`fV2c1(ISZmT-3;K|GjZHl7$?e|}6zHsT|6%$t6n-h~&{)hAp z4Zb@XZ@T{Jy|C;|$&{^G%U>DqxczjcwM4{m09lS2o@wJAn_OC7`=U+A@eY)!RC&F(Y-Tt^E;#@I1(>cokJsOAeOd2Q z_tiVw@WZIICA4Lzti{A8;%~ixAfz0{=T8<{pkr;T^?-f>}=VrosYi%r-*$7nF*7h-(^}T z#I)nN`aEezzo=kohg)^OXB4eO%tvrH%ltI?g+oJcI0@>$@$9&)C@dqYj-M_GSeNNg zV)!m&_u8?}TiWmB0s-E++=;w}Jpw27Pa8PFz6HUdhTBs2CrB7FmBQP;D{P{bJ)(xb zn6L_)7)B-c>I$3KZ13-_=>CGHf!-|p3Yre2B>t|x_`KJF-Q(v;>O<<{>gpNe_CwA)6--NqWlH=POYdj2{QL?DJ zw}YBGmN%8#H(zR)TqBes03KmaIV^4eG6jxp0{m$>n3-4p%8O&A3^R=K$IJ|aF|S;C zgb}v#h%ede``>JFiW@BTzuYLGFoa^88l^e_hbP0#Kpe`UtHipgIanlpt(G7&oewR4 z7lZ!*9Na)B<&*h3YVAki2ziU}(XEwTGO>>$DfQTnsUZA;f3hif-rK*nkny6~@ zdS}6h#OljLZTi49iGOTK^EU&A5=#aQ{b6R(7Lti1!k>b`a%>a)pS>u{Pp(t`wJBZc zVbSL=>TlEPK*t2^l}bmOa+v+GDS3yws&mU5P#j)8n#a^$+}f%~L10`C!T@h!z##Az z_(C~Z+LjbpQsDoX0?PmzI5?04^Z)>-41)+T0~|!4`5zA33IjiXVmm4er{_kY;1P0e z1fm1U$<@`s#nyx1_6G~G*-%5(NCRo4s-~lXoGXD8v$aAR7;5RN8k!=vX_z97^|ZDd zYasRYkel`Ow&-YWHbSbZ=^GkJAPE@Cj-~84$}SDsPzIQT1+(PAQ+e=I9y$e!$jg8U z9!zAxL=H^k!9)Q}2w*~_Bnl7|W+sIYF(3vOGZP0y!D2udEC$5EVn84)21LSQKqxE* z#KK}gFf7IrOlRrM@Pm+XGd6^bgOG6$G7dtC2^EEC%LVyB588L~tgWyC2{`;G;#PBavz=58gTTl>AUI#7a97%2j zb%Gn=`*HbC3%T#Pi&@4QY8dFKZq|@MQXV-wQl@$EfD@NmM#sHb5Dv3-vLbo89(17O zS`ggboS}1z@Awwj@74-R%W2f;=(xF2aC4}Tr9HvcYHmfzFiTKNPhDdd5^{p8$+-eZ zG0F-JC%%am!Z3j_90){`!(5SNKVd-5-!P=bx6sgW^P}JvGt}LQb|g!;ISl%rFkENc zzoB3;SaE3t9rsq$f0AGh(E>YX1DgRlZr<+_*t)v9lU(M!v_Hk3@%|QC9y>e#_ricN z;3Lrc7v57#&`Sy|De#XeKo6`0AK+3R06<5?>=8b*{$J|<|Ja3D3cRGi-=F~X{+|m2 zdVpM@_HQ7v6k_QYaD|sjh^kWk6M|m(N=>0!G>^Te=Mra_`z#qZWVMDOz za3?qhegdu!X9Law-iTsE0-%Wa2ABZ?h&IF#gcV{L{2kB$fIp}B_iaLeD%{o9+QO3L z;%0%tNfWI|GI+`v^r}UHpQ>A)kD8l^=cVSx6M3k)i89>O+;{>PHMcB=b3V7U#mqY$ z9FBvk8^bOLs6evVlf1vHg8NZ0Sqq<$J7fx*&+J3*< zv53^wPgmITh%{DnJ9C-0dxTB+k?Y(?Wrj&ZtnYV;DHR0U=ef$!hkc&Ve!$ogJN zt*?q93cgc@f7$(nmK&@xUomd;(2nL$O6aX{VqT`Ze)E;DC|q{#Iz5oKW~okj?OlDM zAE2$AvB#{GQNmUDBS)vz!Ea~U9^FpY(9F2W%%9=XY{B_Lbk#F4tL(%M*mmQT^?7UG zx(~0rxhv>c8!PhE+B1gpMjZ)n1KzrajvSR}eHgrl%o)q_r~!(~zyFm-pv?WF=WBXc z&4pTl^ny0VjpNQ=D_Dcon_LdkI^!c5tn2CpYVFrH$4=g3dq(!M_UW=bP)55xd3{fP z472K~OSPYc%jS(b$)Cboksl$5%nLB+X=u8{8>S@*MGaT1^<_5eIPuQQyzyr2po(MV zqX#=Z3``q~J$25Pm_Ot>wL#U%AvN}PSD|CAdarzb{dyq%2po;fXFx@Tw~u#tz1^L# zzOiZEsK>9Kk`f6N3Bbk16%+;9?Ae1iQ-Y#?QO`0JacBc4CjFDGdBSQU*L&Jh!5+u_4+T2WxkJN6nw^{SpkCQD_!PFs~r6T@`$h=WG z*j(a|#gsnSNyE~Fyc=CupzZS-ih84_>)e?^TTk@cE~%V^1g*5pSB?+E$uibM$uxCe zC7AJ>-!~UrE%0z3eaN4rQ`_WsM)ifE(T&)*n{-AE-s6%N!`sj|rP+kS_DzG*fhB+T`@O+TOxJvN;4ZE{s6oOXe z$VwPE^FveIFQXy`Lk_^~rel~LbZD6|`WV%@Q$~yo4N{tv$G5?cWL0JN=C706(huh?fpi(Ez_&Y}McV7igb{_g*n!Cf< zo?O^BZn_Xjq^Ws2!%OFksiWPWKH5}gWoPDb0lY)vwJwsQ11>q6DZME9FX&1CT}bfv z|8%f5Fv_PI?u1}R+yQp~9)XNv`uWEFWfixWd*VTzxT%Th^>=!4bpxK_op`zP?gSp^ zyc0DZPs`rzXtY>JZk)VdWdKc^mHFkuP~Wc_Tb=g{%F3;6tPf5*gsN%|878Bcsd8M* zEz#=XsOGl9$>^#1sIoaRyg1<7dD!!=otJpm$fxd)8ZKUlW3ThE=JPqFGDo}Y!qW1j z=mxXIcIxifoxaQ>Q=L&q-js}H`Q^U-Wi7oJAy))fU9Kn*70XBH%1`=SBRt678Hi1O z-cgp;oowCT9kme2dIN8EA59f9oo;dexk%}(j?qxn7C)0uJ%ud~qL{_1$Y@sTBp0J( zQ28FWm_ExY7j`~hw|dqJV>_Q7%k7iw$xV{CNx|0<3z6iHn0Pll(Q;Cs2NBkgWm58L zHLT_03fI?sliGK&dUw6aD7K%k_RROZAKz#eBV|~}f4=g~=N9hVH57j0jpZpmh^SK3yQpV5)z8!DV|m>n0V)$K@Z zdKDkP*K9+D=j+LftHO*0+HMOLs}tjT9X38%A@?8G{~Je)PAPW>-3YwCCXvfCv)WsE zw4TyD%KATDl=q@Opd`n&BCB^*^l97;i-s2@P44lXr_DH1548H9)?{z|uPX&N5+@+yM+!Q z8Y@Vz%7&&=L=?u6w?sd4C1=&w)kn4oxA8{l)V&Hk{Tz1llFYnOolzG*`*U3zC7d5; ztf^tl^I)B3fTG4vU$b#77Va?AcpsY1Y1ls~>#(Ou?h5Yv;SJ!&K|pk##!FW!sSoPvP^E;-b9FTnQ+pJlmLpgXI&UTb=5*C;DV4VHMXaALxO zysv2bEywL+2oi}bBxQd-B>U78V`Wr%Bi5I%Cso3%VuD{hGu}{o(0ZaX!9pf-o7j}y z1Ct)(H-*d|T$)CDt~IDNK>-E>#&rIsk9(!ah3&RNdqgXCTlbgN@a?~~#r1N*$md3h zZNwV*yz>P2C4(13!2v-m=92Au5DLa&hbx?*c_KLwr}%W2V=5`InuFy}5!E7R%+Qk1Hu2E4zbyHsup-njW-Yq14Y%o79(l-sE1AE$O5lBrMtB_Aq(LLP=AxkGo|?+a z+S~#n>&Dpf5RUrW+1-pa*r_gl=7jiHV>BF!W&MwAye1rIUFMDYxcJypwL7X6qEBykz-Z$De{ zQC(3f`yMYjvtiYiEQLxFgNv(L5^z~EGzAauxIbxMK5tYB?v{022irp%_askqeRH|b zQPuDkipt4TBnmV?ype)Gt?+z&+$J_B@4iYDqwHSwe9aQewFl2*COe%?CCJJMzZe^} zuim5018MtKHk)oRLn~S5nQJ2Wdpl`6c{-u_|M!2D{(F1l%=dpZ3@}IJA=uy!uu2&E z=dq8B=K1C8;4iP#h>Lh?waLE^AG2k}6QV({!-j{Cv~DKHouT#2I;GN}lU;sK{2)bG zs!!HD>NZ#w2dXqXS(@y+kVs!7y-STrj^?ujPYYw)#$6(t$!Kn>B#APMASqlPGMHjx zV4Ui`H#GL#khbhe?Z^8r>C(Hk(OH}c+3vOw$$>)yalT!y5A~EyKHYSEpe~gf^+x>) zOMASecHNa`^^0UQ7gds!|I}Le8Ks@kwvPJ&to%MveY)EP2e`W*1T-=*ORx2^_}b;) zE6uzR$)e}Cs*c`EnhfUWCmEZ_Z_S#PYRBNBBmH#=sq28KG%}i#DoNSJe6v1u`I@V~ zuVd3IpOm}P-(FmJ+4DQHgHwB!&E7~P2FUZ!3l%LWlK|F5r(}k3LcxzC(6*EeM>Z7Ta(vaFM*vw+$H9j4H zkG|i{7#MweG5ufYmuCF@7IslxtDP|3S8zEec4Kia7wc5r9kT>l^^8i#^-4^p<3ceU zMhi6~Hln@Fs=G)9lXNv7wTkWJ>vLMu28OlnM#uJKeC){|C8GtYl3Y~R$_o~|Ka4tF z{v|jqo88>_ICods-U#QXI&8ILJP3`0Aq$Z_euG84K*KAgxF~v}Dnd5tEf!i?@Ti9dP6X<+%x~O}nSsFesv-jSncnOFDUye{6 zNi%)ulH}Rc)p&d*Ru?~uGR#(L+KYMs_fh9r(HmkFcbtso|Api)!@a^Hx|YU;`0@V4 zJi7Bw`UAM^julmF7$e^CK48vV-+@$*QEUrg6xC-~eI%H6TscSI=2r1i)c z*VQ&TbJX^Ooq$)-8hP_w3*8V=NA3;P3wNlvtT-R+-)Y*hYWaEvQR_oHt2`TOGZ=RE zk`UzV|tbE3ndy>%r8)9Wd5PjIZ_vEJRXQ+U>GR_VPqm zBf4!!zBGZmuH^<}9Hv2AQ9Ql?xMCSONDsfF~?C1}Ogg-Gh}y(sU$sijPPn@xW` YhA_ZB7~6eff4%HI!j$j}Ngkp90nk=@-T(jq diff --git a/pkgs/http2/experimental/server.dart b/pkgs/http2/experimental/server.dart index 2856e299e3..12af09d692 100644 --- a/pkgs/http2/experimental/server.dart +++ b/pkgs/http2/experimental/server.dart @@ -17,11 +17,15 @@ const String HOSTNAME = 'localhost'; const int PORT = 7777; main() async { - var database = Platform.script.resolve('pkcert').toFilePath(); - SecureSocket.initialize(database: database, password: 'dartdart'); + String localFile(path) => Platform.script.resolve(path).toFilePath(); - var server = await SecureServerSocket.bind( - HOSTNAME, PORT, 'localhost_cert', supportedProtocols: ['h2']); + var privateKeyFile = Platform.script.resolve('privatekey.pem').toFilePath(); + var context = new SecurityContext() + ..usePrivateKey(localFile('server_key.pem'), password: 'dartdart') + ..useCertificateChain(localFile('server_chain.pem')) + ..setAlpnProtocols(['h2'], true); + + var server = await SecureServerSocket.bind(HOSTNAME, PORT, context); print('HTTP/2 server listening on https://$HOSTNAME:$PORT'); runZoned(() { @@ -43,7 +47,7 @@ handleClient(SecureSocket socket) { } connection.incomingStreams.listen((ServerTransportStream stream) async { - dumpInfo('main', 'Got new HTTP/2 stream on connection: ${stream.id}'); + dumpInfo('main', 'Got new HTTP/2 stream with id: ${stream.id}'); String path; stream.incomingMessages.listen((StreamMessage msg) async { diff --git a/pkgs/http2/experimental/server_chain.pem b/pkgs/http2/experimental/server_chain.pem new file mode 100644 index 0000000000..4163fe7ddd --- /dev/null +++ b/pkgs/http2/experimental/server_chain.pem @@ -0,0 +1,57 @@ +-----BEGIN CERTIFICATE----- +MIIDKTCCAhGgAwIBAgIJAOWmjTS+OnTEMA0GCSqGSIb3DQEBCwUAMBcxFTATBgNV +BAMMDGludGVybWVkaWF0ZTAeFw0xNTA1MTgwOTAwNDBaFw0yMzA4MDQwOTAwNDBa +MBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBALlcwQJuzd+xH8QFgfJSn5tRlvhkldSX98cE7NiA602NBbnAVyUrkRXq +Ni75lgt0kwjYfA9z674m8WSVbgpLPintPCla9CYky1TH0keIs8Rz6cGWHryWEHiu +EDuljQynu2b3sAFuHu9nfWurbJwZnFakBKpdQ9m4EyOZCHC/jHYY7HacKSXg1Cki +we2ca0BWDrcqy8kLy0dZ5oC6IZG8O8drAK8f3f44CRYw59D3sOKBrKXaabpvyEcb +N7Wk2HDBVwHpUJo1reVwtbM8dhqQayYSD8oXnGpP3RQNu/e2rzlXRyq/BfcDY1JI +7TbC4t/7/N4EcPSpGsTcSOC9A7FpzvECAwEAAaN7MHkwCQYDVR0TBAIwADAsBglg +hkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0O +BBYEFCnwiEMMFZh7NhCr+qA8K0w4Q+AOMB8GA1UdIwQYMBaAFB0h1Evsaw2vfrmS +YuoCTmC4EE6ZMA0GCSqGSIb3DQEBCwUAA4IBAQAcFmHMaXRxyoNaeOowQ6iQWoZd +AUbvG7SHr7I6Pi2aqdqofsKWts7Ytm5WsS0M2nN+sW504houu0iCPeJJX8RQw2q4 +CCcNOs9IXk+2uMzlpocHpv+yYoUiD5DxgWh7eghQMLyMpf8FX3Gy4VazeuXznHOM +4gE4L417xkDzYOzqVTp0FTyAPUv6G2euhNCD6TMru9REcRhYul+K9kocjA5tt2KG +MH6y28LXbLyq4YJUxSUU9gY/xlnbbZS48KDqEcdYC9zjW9nQ0qS+XQuQuFIcwjJ5 +V4kAUYxDu6FoTpyQjgsrmBbZlKNxH7Nj4NDlcdJhp/zeSKHqWa5hSWjjKIxp +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDAjCCAeqgAwIBAgIJAOWmjTS+OnTDMA0GCSqGSIb3DQEBCwUAMBgxFjAUBgNV +BAMMDXJvb3RhdXRob3JpdHkwHhcNMTUwNTE4MDkwMDQwWhcNMjMwODA0MDkwMDQw +WjAXMRUwEwYDVQQDDAxpbnRlcm1lZGlhdGUwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDSrAO1CoPvUllgLOzDm5nG0skDF7vh1DUgAIDVGz0ecD0JFbQx +EF79pju/6MbtpTW2FYvRp11t/G7rGtX923ybOHY/1MNFQrdIvPlO1VV7IGKjoMwP +DNeb0fIGjHoE9QxaDxR8NX8xQbItpsw+TUtRfc9SLkR+jaYJfVRoM21BOncZbSHE +YKiZlEbpecB/+EtwVpgvl+8mPD5U07Fi4fp/lza3WXInXQPyiTVllIEJCt4PKmlu +MocNaJOW38bysL7i0PzDpVZtOxLHOTaW68yF3FckIHNCaA7k1ABEEEegjFMmIao7 +B9w7A0jvr4jZVvNmui5Djjn+oJxwEVVgyf8LAgMBAAGjUDBOMB0GA1UdDgQWBBQd +IdRL7GsNr365kmLqAk5guBBOmTAfBgNVHSMEGDAWgBRk81s9d0ZbiZhh44KckwPb +oTc0XzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBZQTK0plfdB5PC +cC5icut4EmrByJa1RbU7ayuEE70e7hla6KVmVjVdCBGltI4jBYwfhKbRItHiAJ/8 +x+XZKBG8DLPFuDb7lAa1ObhAYF7YThUFPQYaBhfzKcWrdmWDBFpvNv6E0Mm364dZ +e7Yxmbe5S4agkYPoxEzgEYmcUk9jbjdR6eTbs8laG169ljrECXfEU9RiAcqz5iSX +NLSewqB47hn3B9qgKcQn+PsgO2j7M+rfklhNgeGJeWmy7j6clSOuCsIjWHU0RLQ4 +0W3SB/rpEAJ7fgQbYUPTIUNALSOWi/o1tDX2mXPRjBoxqAv7I+vYk1lZPmSzkyRh +FKvRDxsW +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDAzCCAeugAwIBAgIJAJ0MomS4Ck+8MA0GCSqGSIb3DQEBCwUAMBgxFjAUBgNV +BAMMDXJvb3RhdXRob3JpdHkwHhcNMTUwNTE4MDkwMDQwWhcNMjMwODA0MDkwMDQw +WjAYMRYwFAYDVQQDDA1yb290YXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAts1ijtBV92S2cOvpUMOSTp9c6A34nIGr0T5Nhz6XiqRVT+gv +dQgmkdKJQjbvR60y6jzltYFsI2MpGVXY8h/oAL81D/k7PDB2aREgyBfTPAhBHyGw +siR+2xYt5b/Zs99q5RdRqQNzNpLPJriIKvUsRyQWy1UiG2s7pRXQeA8qB0XtJdCj +kFIi+G2bDsaffspGeDOCqt7t+yqvRXfSES0c/l7DIHaiMbbp4//ZNML3RNgAjPz2 +hCezZ+wOYajOIyoSPK8IgICrhYFYxvgWxwbLDBEfC5B3jOQsySe10GoRAKZz1gBV +DmgReu81tYJmdgkc9zknnQtIFdA0ex+GvZlfWQIDAQABo1AwTjAdBgNVHQ4EFgQU +ZPNbPXdGW4mYYeOCnJMD26E3NF8wHwYDVR0jBBgwFoAUZPNbPXdGW4mYYeOCnJMD +26E3NF8wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEATzkZ97K777uZ +lQcduNX3ey4IbCiEzFA2zO5Blj+ilfIwNbZXNOgm/lqNvVGDYs6J1apJJe30vL3X +J+t2zsZWzzQzb9uIU37zYemt6m0fHrSrx/iy5lGNqt3HMfqEcOqSCOIK3PCTMz2/ +uyGe1iw33PVeWsm1JUybQ9IrU/huJjbgOHU4wab+8SJCM49ipArp68Fr6j4lcEaE +4rfRg1ZsvxiOyUB3qPn6wyL/JB8kOJ+QCBe498376eaem8AEFk0kQRh6hDaWtq/k +t6IIXQLjx+EBDVP/veK0UnVhKRP8YTOoV8ZiG1NcdlJmX/Uk7iAfevP7CkBfSN8W +r6AL284qtw== +-----END CERTIFICATE----- diff --git a/pkgs/http2/experimental/server_key.pem b/pkgs/http2/experimental/server_key.pem new file mode 100644 index 0000000000..1fd2324045 --- /dev/null +++ b/pkgs/http2/experimental/server_key.pem @@ -0,0 +1,29 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIE5DAcBgoqhkiG9w0BDAEBMA4ECL7L6rj6uEHGAgIIAASCBMLbucyfqAkgCbhP +xNSHYllPMAv/dsIjtnsBwepCXPGkCBCuOAw/2FaCHjN9hBqL5V7fkrKeaemhm2YE +ycPtlHJYPDf3kEkyMjdZ9rIY6kePGfQizs2uJPcXj4YPyQ4HsfVXpOicKfQrouf5 +Mze9bGzeMN065q3iP4dYUMwHAyZYteXCsanQNHlqvsWli0W+H8St8fdsXefZhnv1 +qVatKWdNdWQ9t5MuljgNU2Vv56sHKEYXI0yLxk2QUMk8KlJfnmt8foYUsnPUXHmc +gIjLKwwVkpdololnEHSNu0cEOUPowjgJru+uMpn7vdNl7TPEQ9jbEgdNg4JwoYzU +0nao8WzjaSp7kzvZz0VFwKnk5AjstGvvuAWckADdq23QElbn/mF7AG1m/TBpYxzF +gTt37UdndS/AcvVznWVVrRP5iTSIawdIwvqI4s7rqsoE0GCcak+RhchgAz2gWKkS +oODUo0JL6pPVbJ3l4ebbaO6c99nDVc8dViPtc1EkStJEJ2O4kI4xgLSCr4Y9ahKn +oAaoSkX7Xxq3aQm+BzqSpLjdGL8atsqR/YVOIHYIl3gThvP0NfZGx1xHyvO5mCdZ +kHxSA7tKWxauZ3eQ2clbnzeRsl4El0WMHy/5K1ovene4v7sunmoXVtghBC8hK6eh +zMO9orex2PNQ/VQC7HCvtytunOVx1lkSBoNo7hR70igg6rW9H7UyoAoBOwMpT1xa +J6V62nqruTKOqFNfur7aHJGpHGtDb5/ickHeYCyPTvmGp67u4wChzKReeg02oECe +d1E5FKAcIa8s9TVOB6Z+HvTRNQZu2PsI6TJnjQRowvY9DAHiWTlJZBBY/pko3hxX +TsIeybpvRdEHpDWv86/iqtw1hv9CUxS/8ZTWUgBo+osShHW79FeDASr9FC4/Zn76 +ZDERTgV4YWlW/klVWcG2lFo7jix+OPXAB+ZQavLhlN1xdWBcIz1AUWjAM4hdPylW +HCX4PB9CQIPl2E7F+Y2p6nMcMWSJVBi5UIH7E9LfaBguXSzMmTk2Fw5p1aOQ6wfN +goVAMVwi8ppAVs741PfHdZ295xMmK/1LCxz5DeAdD/tsA/SYfT753GotioDuC7im +EyJ5JyvTr5I6RFFBuqt3NlUb3Hp16wP3B2x9DZiB6jxr0l341/NHgsyeBXkuIy9j +ON2mvpBPCJhS8kgWo3G0UyyKnx64tcgpGuSvZhGwPz843B6AbYyE6pMRfSWRMkMS +YZYa+VNKhR4ixdj07ocFZEWLVjCH7kxkE8JZXKt8jKYmkWd0lS1QVjgaKlO6lRa3 +q6SPJkhW6pvqobvcqVNXwi1XuzpZeEbuh0B7OTekFTTxx5g9XeDl56M8SVQ1KEhT +Q1t7H2Nba18WCB7cf+6PN0F0K0Jz1Kq7ZWaqEI/grX1m4RQuvNF5807sB/QKMO/Z +Gz3NXvHg5xTJRd/567lxPGkor0cE7qD1EZfmJ2HrBYXQ91bhgA7LToBuMZo6ZRXH +QfsanjbP4FPLMiGdQigLjj3A35L/f4sQOOVac/sRaFnm7pzcxsMvyVU/YtvGcjYE +xaOOVnamg661Wo0wksXoDjeSz/JIyyKO3Gwp1FSm2wGLjjy/Ehmqcqy8rvHuf07w +AUukhVtTNn4= +-----END ENCRYPTED PRIVATE KEY----- diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index ab178af971..88e514691f 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -5,7 +5,7 @@ author: Dart Team homepage: https://github.com/dart-lang/http2 environment: - sdk: '>=1.0.0 <2.0.0' + sdk: '>=1.13.0-edge.1015c7f4ffb04c8a1ce541d2101893b19d0e44fd <2.0.0' dev_dependencies: unittest: '>=0.11.5 <0.12.0' From 6b96abe81edf4c7d6c7808a7b59fde5fb8e64940 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Mon, 31 Aug 2015 12:59:28 +0200 Subject: [PATCH 029/172] Use package:test, make client website tests work with gzip responses. BUG= R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/248147013 . --- .../lib/src/flowcontrol/stream_queues.dart | 2 -- pkgs/http2/pubspec.yaml | 2 +- pkgs/http2/test/client_tests.dart | 4 +-- pkgs/http2/test/client_websites_test.dart | 27 +++++++++---------- pkgs/http2/test/server_tests.dart | 2 +- .../src/async_utils/async_utils_test.dart | 2 +- .../test/src/connection_preface_test.dart | 2 +- pkgs/http2/test/src/error_matchers.dart | 2 +- .../flowcontrol/connection_queues_test.dart | 2 +- .../src/flowcontrol/stream_queues_test.dart | 2 +- .../src/flowcontrol/window_handler_test.dart | 2 +- .../src/frames/frame_defragmenter_test.dart | 2 +- .../test/src/frames/frame_reader_test.dart | 2 +- .../src/frames/frame_writer_reader_test.dart | 2 +- .../test/src/frames/frame_writer_test.dart | 2 +- pkgs/http2/test/src/hpack/hpack_test.dart | 2 +- .../test/src/hpack/huffman_table_test.dart | 2 +- pkgs/http2/test/src/mock_utils.dart | 2 +- .../test/src/ping/ping_handler_test.dart | 2 +- .../src/settings/settings_handler_test.dart | 2 +- pkgs/http2/test/src/streams/helper.dart | 2 +- .../test/src/streams/simple_flow_test.dart | 2 +- .../test/src/streams/simple_push_test.dart | 2 +- pkgs/http2/test/src/streams/streams_test.dart | 2 +- pkgs/http2/test/transport_test.dart | 2 +- 25 files changed, 37 insertions(+), 40 deletions(-) diff --git a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart index 63abf32f50..5841357867 100644 --- a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart @@ -101,8 +101,6 @@ class StreamMessageQueueOut extends Object int queueLenBefore = _messages.length; while (_messages.length > 0) { - assert(!isClosing); - Message message = _messages.first; if (message is HeadersMessage) { diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index 88e514691f..8f4d4d3dc8 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -8,5 +8,5 @@ environment: sdk: '>=1.13.0-edge.1015c7f4ffb04c8a1ce541d2101893b19d0e44fd <2.0.0' dev_dependencies: - unittest: '>=0.11.5 <0.12.0' + test: '>=0.12.0 <0.13.0' diff --git a/pkgs/http2/test/client_tests.dart b/pkgs/http2/test/client_tests.dart index e885bb4a0a..c7b5d9f737 100644 --- a/pkgs/http2/test/client_tests.dart +++ b/pkgs/http2/test/client_tests.dart @@ -6,7 +6,7 @@ library http2.test.client_tests; import 'dart:async'; -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; import 'package:http2/transport.dart'; import 'package:http2/src/connection_preface.dart'; @@ -17,7 +17,7 @@ import 'package:http2/src/settings/settings.dart'; import 'src/hpack/hpack_test.dart' show isHeader; main() { - group('server-tests', () { + group('client-tests', () { group('normal', () { clientTest('gracefull-shutdown-for-unused-connection', (ClientTransportConnection client, diff --git a/pkgs/http2/test/client_websites_test.dart b/pkgs/http2/test/client_websites_test.dart index 3d65ac48b6..a7c91b61cd 100644 --- a/pkgs/http2/test/client_websites_test.dart +++ b/pkgs/http2/test/client_websites_test.dart @@ -6,9 +6,10 @@ library http2.test.client_websites_test; import 'dart:async'; import 'dart:convert'; +import 'dart:io'; import 'package:http2/client.dart'; -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; main() async { group('end2end', () { @@ -32,7 +33,7 @@ main() async { Response response = await connection.makeRequest(new Request('GET', uri)); dumpHeaders(uri, response.headers); - String body = await response.stream.transform(UTF8.decoder).join(''); + String body = await readBody(response); connection.close(); expect(body, contains('')); @@ -47,10 +48,6 @@ main() async { Response response = await connection.makeRequest(request); dumpHeaders(uri, response.headers); - Future accumulateResponse(stream) async { - return await stream.transform(UTF8.decoder).join(''); - } - Future> accumulatePushes() async { var futures = []; return response.serverPushes.listen((ServerPush push) { @@ -61,7 +58,7 @@ main() async { msg: '**push** Response headers for server push ' 'request.'); - return accumulateResponse(response.stream).then((String body) { + return readBody(response).then((String body) { return [push.requestHeaders[':path'].join(''), body]; }); })); @@ -69,7 +66,7 @@ main() async { } var results = await Future.wait( - [accumulateResponse(response.stream), accumulatePushes()]); + [readBody(response), accumulatePushes()]); var body = results[0]; expect(body, contains('')); @@ -91,10 +88,6 @@ main() async { Response response = await connection.makeRequest(request); dumpHeaders(uri, response.headers); - Future accumulateResponse(stream) async { - return await stream.transform(UTF8.decoder).join(''); - } - Future> accumulatePushes() async { var futures = []; return response.serverPushes.listen((ServerPush push) { @@ -103,8 +96,7 @@ main() async { }).asFuture().then((_) => Future.wait(futures)); } - var results = await Future.wait( - [accumulateResponse(response.stream), accumulatePushes()]); + var results = await Future.wait([readBody(response), accumulatePushes()]); var body = results[0]; expect(body, contains('')); @@ -128,3 +120,10 @@ dumpHeaders(Uri uri, Map> headers, print(''); } +Future readBody(Response response) async { + var stream = response.stream; + if (response.headers['content-encoding']?.join('') == 'gzip') { + stream = stream.transform(GZIP.decoder); + } + return await stream.transform(UTF8.decoder).join(''); +} diff --git a/pkgs/http2/test/server_tests.dart b/pkgs/http2/test/server_tests.dart index 65f031a256..b6041ff927 100644 --- a/pkgs/http2/test/server_tests.dart +++ b/pkgs/http2/test/server_tests.dart @@ -6,7 +6,7 @@ library http2.test.server_tests; import 'dart:async'; -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; import 'package:http2/transport.dart'; import 'package:http2/src/connection_preface.dart'; diff --git a/pkgs/http2/test/src/async_utils/async_utils_test.dart b/pkgs/http2/test/src/async_utils/async_utils_test.dart index 9940fbe805..1c0e58ef94 100644 --- a/pkgs/http2/test/src/async_utils/async_utils_test.dart +++ b/pkgs/http2/test/src/async_utils/async_utils_test.dart @@ -4,7 +4,7 @@ import 'dart:async'; -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; import 'package:http2/src/async_utils/async_utils.dart'; main() { diff --git a/pkgs/http2/test/src/connection_preface_test.dart b/pkgs/http2/test/src/connection_preface_test.dart index f2145e50f8..c59ab139fa 100644 --- a/pkgs/http2/test/src/connection_preface_test.dart +++ b/pkgs/http2/test/src/connection_preface_test.dart @@ -7,7 +7,7 @@ library http2.test.connection_preface_test; import 'dart:async'; import 'dart:math' show min; -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; import 'package:http2/src/connection_preface.dart'; main() { diff --git a/pkgs/http2/test/src/error_matchers.dart b/pkgs/http2/test/src/error_matchers.dart index 0ecc098dd7..3ffc454834 100644 --- a/pkgs/http2/test/src/error_matchers.dart +++ b/pkgs/http2/test/src/error_matchers.dart @@ -4,7 +4,7 @@ library http2.test.error_matchers; -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; import 'package:http2/src/sync_errors.dart'; const Matcher isProtocolException = const _ProtocolException(); diff --git a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart index f695d937b9..b0e03f2d10 100644 --- a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart @@ -2,7 +2,7 @@ // 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:unittest/unittest.dart'; +import 'package:test/test.dart'; import 'package:http2/transport.dart'; import 'package:http2/src/async_utils/async_utils.dart'; diff --git a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart index 27e01313ba..fe96f00c72 100644 --- a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart @@ -2,7 +2,7 @@ // 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:unittest/unittest.dart'; +import 'package:test/test.dart'; import 'package:http2/transport.dart'; import 'package:http2/src/async_utils/async_utils.dart'; diff --git a/pkgs/http2/test/src/flowcontrol/window_handler_test.dart b/pkgs/http2/test/src/flowcontrol/window_handler_test.dart index 10c900ddb9..2f44dc1200 100644 --- a/pkgs/http2/test/src/flowcontrol/window_handler_test.dart +++ b/pkgs/http2/test/src/flowcontrol/window_handler_test.dart @@ -2,7 +2,7 @@ // 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:unittest/unittest.dart'; +import 'package:test/test.dart'; import 'package:http2/src/flowcontrol/window.dart'; import 'package:http2/src/flowcontrol/window_handler.dart'; diff --git a/pkgs/http2/test/src/frames/frame_defragmenter_test.dart b/pkgs/http2/test/src/frames/frame_defragmenter_test.dart index 424f0e254b..ec39517718 100644 --- a/pkgs/http2/test/src/frames/frame_defragmenter_test.dart +++ b/pkgs/http2/test/src/frames/frame_defragmenter_test.dart @@ -2,7 +2,7 @@ // 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:unittest/unittest.dart'; +import 'package:test/test.dart'; import 'package:http2/src/frames/frames.dart'; import 'package:http2/src/frames/frame_defragmenter.dart'; diff --git a/pkgs/http2/test/src/frames/frame_reader_test.dart b/pkgs/http2/test/src/frames/frame_reader_test.dart index 07dcabb47e..20a25cf393 100644 --- a/pkgs/http2/test/src/frames/frame_reader_test.dart +++ b/pkgs/http2/test/src/frames/frame_reader_test.dart @@ -4,7 +4,7 @@ import 'dart:async'; -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; import 'package:http2/src/hpack/hpack.dart'; import 'package:http2/src/frames/frames.dart'; diff --git a/pkgs/http2/test/src/frames/frame_writer_reader_test.dart b/pkgs/http2/test/src/frames/frame_writer_reader_test.dart index 888c4fb4c0..bb09632254 100644 --- a/pkgs/http2/test/src/frames/frame_writer_reader_test.dart +++ b/pkgs/http2/test/src/frames/frame_writer_reader_test.dart @@ -4,7 +4,7 @@ import 'dart:async'; -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; import 'package:http2/src/hpack/hpack.dart'; import 'package:http2/src/frames/frames.dart'; diff --git a/pkgs/http2/test/src/frames/frame_writer_test.dart b/pkgs/http2/test/src/frames/frame_writer_test.dart index f52a2a8084..4033f9546b 100644 --- a/pkgs/http2/test/src/frames/frame_writer_test.dart +++ b/pkgs/http2/test/src/frames/frame_writer_test.dart @@ -4,7 +4,7 @@ import 'dart:async'; -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; import 'package:http2/src/hpack/hpack.dart'; import 'package:http2/src/frames/frames.dart'; diff --git a/pkgs/http2/test/src/hpack/hpack_test.dart b/pkgs/http2/test/src/hpack/hpack_test.dart index ca3ee96ceb..1c06a8f172 100644 --- a/pkgs/http2/test/src/hpack/hpack_test.dart +++ b/pkgs/http2/test/src/hpack/hpack_test.dart @@ -2,7 +2,7 @@ // 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:unittest/unittest.dart'; +import 'package:test/test.dart'; import 'package:http2/src/hpack/hpack.dart'; main() { diff --git a/pkgs/http2/test/src/hpack/huffman_table_test.dart b/pkgs/http2/test/src/hpack/huffman_table_test.dart index db31db7b26..21054711f3 100644 --- a/pkgs/http2/test/src/hpack/huffman_table_test.dart +++ b/pkgs/http2/test/src/hpack/huffman_table_test.dart @@ -4,7 +4,7 @@ import 'dart:convert'; -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; import 'package:http2/src/hpack/huffman.dart'; import 'package:http2/src/hpack/huffman_table.dart'; diff --git a/pkgs/http2/test/src/mock_utils.dart b/pkgs/http2/test/src/mock_utils.dart index 8a61a13a24..d7110de0cb 100644 --- a/pkgs/http2/test/src/mock_utils.dart +++ b/pkgs/http2/test/src/mock_utils.dart @@ -6,7 +6,7 @@ library http2.test.mock_utils; import 'dart:mirrors'; -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; /// Used for mocking arbitrary classes. /// diff --git a/pkgs/http2/test/src/ping/ping_handler_test.dart b/pkgs/http2/test/src/ping/ping_handler_test.dart index b075f8bd26..c4a9bff2b3 100644 --- a/pkgs/http2/test/src/ping/ping_handler_test.dart +++ b/pkgs/http2/test/src/ping/ping_handler_test.dart @@ -4,7 +4,7 @@ import 'dart:async'; -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; import 'package:http2/src/frames/frames.dart'; import 'package:http2/src/ping/ping_handler.dart'; diff --git a/pkgs/http2/test/src/settings/settings_handler_test.dart b/pkgs/http2/test/src/settings/settings_handler_test.dart index 2b9cc35891..cdfa547aad 100644 --- a/pkgs/http2/test/src/settings/settings_handler_test.dart +++ b/pkgs/http2/test/src/settings/settings_handler_test.dart @@ -4,7 +4,7 @@ import 'dart:async'; -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; import 'package:http2/src/frames/frames.dart'; import 'package:http2/src/settings/settings.dart'; diff --git a/pkgs/http2/test/src/streams/helper.dart b/pkgs/http2/test/src/streams/helper.dart index a60a5ebafa..bcb6fda735 100644 --- a/pkgs/http2/test/src/streams/helper.dart +++ b/pkgs/http2/test/src/streams/helper.dart @@ -6,7 +6,7 @@ library http2.test.end2end_test; import 'dart:async'; -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; import 'package:http2/transport.dart'; import 'package:http2/src/frames/frames.dart'; diff --git a/pkgs/http2/test/src/streams/simple_flow_test.dart b/pkgs/http2/test/src/streams/simple_flow_test.dart index d5356c90e1..d6cab374bd 100644 --- a/pkgs/http2/test/src/streams/simple_flow_test.dart +++ b/pkgs/http2/test/src/streams/simple_flow_test.dart @@ -6,7 +6,7 @@ library http2.test.streams.simple_flow_test; import 'dart:async'; -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; import 'package:http2/transport.dart'; import 'helper.dart'; diff --git a/pkgs/http2/test/src/streams/simple_push_test.dart b/pkgs/http2/test/src/streams/simple_push_test.dart index c6a3b5a459..e1980d23df 100644 --- a/pkgs/http2/test/src/streams/simple_push_test.dart +++ b/pkgs/http2/test/src/streams/simple_push_test.dart @@ -7,7 +7,7 @@ library http2.test.streams.simple_push_test; import 'dart:async'; import 'dart:convert'; -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; import 'package:http2/transport.dart'; import 'helper.dart'; diff --git a/pkgs/http2/test/src/streams/streams_test.dart b/pkgs/http2/test/src/streams/streams_test.dart index f5b258c61c..f019ce15d2 100644 --- a/pkgs/http2/test/src/streams/streams_test.dart +++ b/pkgs/http2/test/src/streams/streams_test.dart @@ -4,7 +4,7 @@ library http2.test.end2end_test; -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; import 'package:http2/transport.dart'; import 'helper.dart'; diff --git a/pkgs/http2/test/transport_test.dart b/pkgs/http2/test/transport_test.dart index a080872397..bdf603e408 100644 --- a/pkgs/http2/test/transport_test.dart +++ b/pkgs/http2/test/transport_test.dart @@ -4,7 +4,7 @@ import 'dart:async'; -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; import 'package:http2/transport.dart'; From 9a6ddc690322235f90e7ccc01ad9944746042656 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Mon, 31 Aug 2015 13:02:06 +0200 Subject: [PATCH 030/172] Add ClientConnection.isOpen to signal if new requests can be made, added tests R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/247057013 . --- pkgs/http2/lib/src/connection.dart | 40 ++++++---- pkgs/http2/lib/src/error_handler.dart | 2 +- .../http2/lib/src/streams/stream_handler.dart | 6 +- pkgs/http2/lib/transport.dart | 4 + pkgs/http2/test/client_tests.dart | 73 ++++++++++++++++++- 5 files changed, 107 insertions(+), 18 deletions(-) diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index f6cec4d22e..7ae6427d80 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -280,21 +280,21 @@ abstract class Connection { return; } - if (_state == ConnectionState.Initialized || - _state == ConnectionState.Operational) { - _state = ConnectionState.Finishing; - - // If we are actively finishing this connection, we'll send a - // GoawayFrame otherwise we'll just propagate the message. - if (active) { - _frameWriter.writeGoawayFrame( - _streams.highestPeerInitiatedStream, - ErrorCode.NO_ERROR, - message != null ? UTF8.encode(message) : []); - } - - _streams.startClosing(); + assert(_state == ConnectionState.Initialized || + _state == ConnectionState.Operational); + + _state = ConnectionState.Finishing; + + // If we are actively finishing this connection, we'll send a + // GoawayFrame otherwise we'll just propagate the message. + if (active) { + _frameWriter.writeGoawayFrame( + _streams.highestPeerInitiatedStream, + ErrorCode.NO_ERROR, + message != null ? UTF8.encode(message) : []); } + + _streams.startClosing(); } /// Terminates this connection (if it is not already terminated). @@ -356,7 +356,19 @@ class ClientConnection extends Connection implements ClientTransportConnection { return new ClientConnection._(incoming, outgoing, allowServerPushes); } + bool get isOpen => _state != ConnectionState.Finishing && + _state != ConnectionState.Terminated; + TransportStream makeRequest(List
headers, {bool endStream: false}) { + if (_state == ConnectionState.Finishing) { + throw new StateError( + 'The http/2 connection is finishing and can therefore not be used to ' + 'make new streams.'); + } else if (_state == ConnectionState.Terminated) { + throw new StateError( + 'The http/2 connection is no longer active and can therefore not be ' + 'used to make new streams.'); + } TransportStream hStream = _streams.newStream(headers, endStream: endStream); return hStream; } diff --git a/pkgs/http2/lib/src/error_handler.dart b/pkgs/http2/lib/src/error_handler.dart index d3f0f60d9e..06c9ac28ac 100644 --- a/pkgs/http2/lib/src/error_handler.dart +++ b/pkgs/http2/lib/src/error_handler.dart @@ -56,8 +56,8 @@ class ClosableMixin { _closing = true; onClosing(); - onCheckForClose(); } + onCheckForClose(); } void onCheckForClose() { diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index a0b0ba9f97..0ef6d92b58 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -146,7 +146,8 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { void onTerminated(exception) { _openStreams.values.toList().forEach( - (stream) => _closeStreamAbnormally(stream, exception)); + (stream) => _closeStreamAbnormally(stream, exception, + propagateException: true)); startClosing(); } @@ -511,7 +512,8 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { void _handlePushPromiseFrame(Http2StreamImpl stream, PushPromiseFrame frame) { if (stream.state != StreamState.Open && stream.state != StreamState.HalfClosedLocal) { - // FIXME(kustermann): We need to RST the promised stream. + _frameWriter.writeRstStreamFrame( + frame.promisedStreamId, ErrorCode.REFUSED_STREAM); throw new StreamClosedException( stream.id, 'Expected open state (was: ${stream.state}).'); } diff --git a/pkgs/http2/lib/transport.dart b/pkgs/http2/lib/transport.dart index 9100238e58..1a950498d3 100644 --- a/pkgs/http2/lib/transport.dart +++ b/pkgs/http2/lib/transport.dart @@ -38,6 +38,10 @@ abstract class ClientTransportConnection extends TransportConnection { => new ClientConnection( incoming, outgoing, allowServerPushes: allowServerPushes); + /// Whether this connection is open and can be used to make new requests + /// via [makeRequest]. + bool get isOpen; + /// Creates a new outgoing stream. ClientTransportStream makeRequest(List
headers, {bool endStream: false}); diff --git a/pkgs/http2/test/client_tests.dart b/pkgs/http2/test/client_tests.dart index c7b5d9f737..3bc9cadf3a 100644 --- a/pkgs/http2/test/client_tests.dart +++ b/pkgs/http2/test/client_tests.dart @@ -47,7 +47,78 @@ main() { Future clientFun() async { await settingsDone.future; + expect(client.isOpen, true); + // Try to gracefully finish the connection. + var future = client.finish(); + + expect(client.isOpen, false); + + await future; + } + + await Future.wait([serverFun(), clientFun()]); + }); + }); + + group('server-errors', () { + clientTest('no-settings-frame-at-beginning-immediate-error', + (ClientTransportConnection client, + FrameWriter serverWriter, + StreamIterator serverReader, + Future nextFrame()) async { + + var goawayReceived = new Completer(); + Future serverFun() async { + serverWriter.writePingFrame(42); + expect(await nextFrame() is SettingsFrame, true); + expect(await nextFrame() is GoawayFrame, true); + goawayReceived.complete(); + expect(await serverReader.moveNext(), false); + } + + Future clientFun() async { + expect(client.isOpen, true); + + // We wait until the server received the error (it's actually later + // than necessary, but we can't make a deterministic test otherwise). + await goawayReceived.future; + + expect(client.isOpen, false); + + var error; + try { + client.makeRequest([new Header.ascii('a', 'b')]); + } catch (e) { error = '$e'; } + expect(error, contains('no longer active')); + + await client.finish(); + } + + await Future.wait([serverFun(), clientFun()]); + }); + + clientTest('no-settings-frame-at-beginning-delayed-error', + (ClientTransportConnection client, + FrameWriter serverWriter, + StreamIterator serverReader, + Future nextFrame()) async { + + Future serverFun() async { + expect(await nextFrame() is SettingsFrame, true); + expect(await nextFrame() is HeadersFrame, true); + serverWriter.writePingFrame(42); + expect(await nextFrame() is GoawayFrame, true); + expect(await serverReader.moveNext(), false); + } + + Future clientFun() async { + expect(client.isOpen, true); + var stream = client.makeRequest([new Header.ascii('a', 'b')]); + + var error = await stream.incomingMessages.toList() + .catchError((e) => '$e'); + expect(error, contains('forcefully terminated')); await client.finish(); } @@ -132,7 +203,7 @@ clientTest(String name, streams.serverConnectionFrameWriter, serverReader, readNext); - }); + })); } class ClientStreams { From a448d85946d3746115851ed27b78183cffdafa57 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Tue, 1 Sep 2015 10:24:56 +0200 Subject: [PATCH 031/172] Add more tests which test client behavior if server is misbehaving The cases include: * server sends frame with invalid stream * server sends frame after stream was closed * server pushes on non-open/half-open stream * server pushes on non-existent stream BUG= R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/244417013 . --- .../http2/lib/src/streams/stream_handler.dart | 10 +- pkgs/http2/test/client_tests.dart | 228 +++++++++++++++++- 2 files changed, 232 insertions(+), 6 deletions(-) diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index 0ef6d92b58..c433b302c7 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -459,6 +459,10 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { // // TODO: When implementing priorities for HTTP/2 streams, these frames // need to be taken into account. + } else if (frame is PushPromiseFrame) { + throw new ProtocolException( + 'Cannot push on a non-existent stream ' + '(stream ${frame.header.streamId} does not exist)'); } else { throw new StreamClosedException(frame.header.streamId, 'No open stream found and was not a headers frame opening a ' @@ -512,10 +516,8 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { void _handlePushPromiseFrame(Http2StreamImpl stream, PushPromiseFrame frame) { if (stream.state != StreamState.Open && stream.state != StreamState.HalfClosedLocal) { - _frameWriter.writeRstStreamFrame( - frame.promisedStreamId, ErrorCode.REFUSED_STREAM); - throw new StreamClosedException( - stream.id, 'Expected open state (was: ${stream.state}).'); + throw new ProtocolException( + 'Expected open state (was: ${stream.state}).'); } var pushedStream = newRemoteStream(frame.promisedStreamId); diff --git a/pkgs/http2/test/client_tests.dart b/pkgs/http2/test/client_tests.dart index 3bc9cadf3a..f76dfc8757 100644 --- a/pkgs/http2/test/client_tests.dart +++ b/pkgs/http2/test/client_tests.dart @@ -5,6 +5,7 @@ library http2.test.client_tests; import 'dart:async'; +import 'dart:convert'; import 'package:test/test.dart'; @@ -124,6 +125,229 @@ main() { await Future.wait([serverFun(), clientFun()]); }); + + clientTest('data-frame-for-invalid-stream', + (ClientTransportConnection client, + FrameWriter serverWriter, + StreamIterator serverReader, + Future nextFrame()) async { + + var handshakeCompleter = new Completer(); + + Future serverFun() async { + serverWriter.writeSettingsFrame([]); + expect(await nextFrame() is SettingsFrame, true); + serverWriter.writeSettingsAckFrame(); + expect(await nextFrame() is SettingsFrame, true); + + handshakeCompleter.complete(); + + HeadersFrame headers = await nextFrame(); + DataFrame finFrame = await nextFrame(); + expect(finFrame.hasEndStreamFlag, true); + + // Write a data frame for a non-existent stream. + int invalidStreamId = headers.header.streamId + 2; + serverWriter.writeDataFrame(invalidStreamId, [42]); + + // Make sure the client sends a [RstStreamFrame] frame. + var frame = await nextFrame(); + expect(frame is RstStreamFrame, true); + expect((frame as RstStreamFrame).errorCode, ErrorCode.STREAM_CLOSED); + expect((frame as RstStreamFrame).header.streamId, invalidStreamId); + + // Close the original stream. + serverWriter.writeDataFrame( + headers.header.streamId, [], endStream: true); + + // Wait for the client finish. + expect(await nextFrame() is GoawayFrame, true); + expect(await serverReader.moveNext(), false); + await serverWriter.close(); + } + + Future clientFun() async { + await handshakeCompleter.future; + + var stream = client.makeRequest([new Header.ascii('a', 'b')]); + await stream.outgoingMessages.close(); + expect(await stream.incomingMessages.toList(), isEmpty); + + await client.finish(); + } + + await Future.wait([serverFun(), clientFun()]); + }); + + clientTest('data-frame-after-stream-closed', + (ClientTransportConnection client, + FrameWriter serverWriter, + StreamIterator serverReader, + Future nextFrame()) async { + + var handshakeCompleter = new Completer(); + + Future serverFun() async { + serverWriter.writeSettingsFrame([]); + expect(await nextFrame() is SettingsFrame, true); + serverWriter.writeSettingsAckFrame(); + expect(await nextFrame() is SettingsFrame, true); + + handshakeCompleter.complete(); + + HeadersFrame headers = await nextFrame(); + DataFrame finFrame = await nextFrame(); + expect(finFrame.hasEndStreamFlag, true); + + int streamId = headers.header.streamId; + + // Write a data frame for a non-existent stream. + serverWriter.writeDataFrame(streamId, [42], endStream: true); + + // Write more data on the closed stream. + serverWriter.writeDataFrame(streamId, [42]); + + // NOTE: The order of the window update frame / rst frame just + // happens to be like that ATM. + + // Await stream/connection window update frame. + var win = await nextFrame(); + expect(win.header.streamId, 1); + expect(win.windowSizeIncrement, 1); + win = await nextFrame(); + expect(win.header.streamId, 0); + expect(win.windowSizeIncrement, 1); + + // Make sure we get a [RstStreamFrame] frame. + var frame = await nextFrame(); + expect(frame is RstStreamFrame, true); + expect((frame as RstStreamFrame).errorCode, ErrorCode.STREAM_CLOSED); + expect((frame as RstStreamFrame).header.streamId, streamId); + + // Wait for the client finish. + expect(await nextFrame() is GoawayFrame, true); + expect(await serverReader.moveNext(), false); + await serverWriter.close(); + } + + Future clientFun() async { + await handshakeCompleter.future; + + var stream = client.makeRequest([new Header.ascii('a', 'b')]); + await stream.outgoingMessages.close(); + var messages = await stream.incomingMessages.toList(); + expect(messages, hasLength(1)); + expect((messages[0] as DataStreamMessage).bytes, [42]); + + await client.finish(); + } + + await Future.wait([serverFun(), clientFun()]); + }); + + clientTest('client-reports-connection-error-on-push-to-nonexistent', + (ClientTransportConnection client, + FrameWriter serverWriter, + StreamIterator serverReader, + Future nextFrame()) async { + + var handshakeCompleter = new Completer(); + + Future serverFun() async { + serverWriter.writeSettingsFrame([]); + expect(await nextFrame() is SettingsFrame, true); + serverWriter.writeSettingsAckFrame(); + expect(await nextFrame() is SettingsFrame, true); + + handshakeCompleter.complete(); + + HeadersFrame headers = await nextFrame(); + DataFrame finFrame = await nextFrame(); + expect(finFrame.hasEndStreamFlag, true); + + int streamId = headers.header.streamId; + + // Write response. + serverWriter.writeHeadersFrame( + streamId, [new Header.ascii('a', 'b')], endStream: true); + + // Push stream to the (non existing) one. + int pushStreamId = 2; + serverWriter.writePushPromiseFrame( + streamId, pushStreamId, [new Header.ascii('a', 'b')]); + + // Make sure we get a connection error. + GoawayFrame frame = await nextFrame(); + expect(ASCII.decode(frame.debugData), + contains('Cannot push on a non-existent stream')); + expect(await serverReader.moveNext(), false); + await serverWriter.close(); + } + + Future clientFun() async { + await handshakeCompleter.future; + + var stream = client.makeRequest([new Header.ascii('a', 'b')]); + await stream.outgoingMessages.close(); + var messages = await stream.incomingMessages.toList(); + expect(messages, hasLength(1)); + expect((messages[0] as HeadersStreamMessage).headers.first, + isHeader('a', 'b')); + await client.finish(); + } + + await Future.wait([serverFun(), clientFun()]); + }); + + clientTest('client-reports-connection-error-on-push-to-non-open', + (ClientTransportConnection client, + FrameWriter serverWriter, + StreamIterator serverReader, + Future nextFrame()) async { + + var handshakeCompleter = new Completer(); + + Future serverFun() async { + serverWriter.writeSettingsFrame([]); + expect(await nextFrame() is SettingsFrame, true); + serverWriter.writeSettingsAckFrame(); + expect(await nextFrame() is SettingsFrame, true); + + handshakeCompleter.complete(); + + HeadersFrame headers = await nextFrame(); + int streamId = headers.header.streamId; + + // Write response. + serverWriter.writeDataFrame(streamId, [], endStream: true); + + // Push stream onto the existing (but half-closed) one. + int pushStreamId = 2; + serverWriter.writePushPromiseFrame( + streamId, pushStreamId, [new Header.ascii('a', 'b')]); + + // Make sure we get a connection error. + GoawayFrame frame = await nextFrame(); + expect(ASCII.decode(frame.debugData), + contains('Expected open state (was: StreamState.HalfClosedRemote)')); + expect(await serverReader.moveNext(), false); + await serverWriter.close(); + } + + Future clientFun() async { + await handshakeCompleter.future; + + var stream = client.makeRequest([new Header.ascii('a', 'b')]); + + // NOTE: We are not closing the outgoing part on purpose. + expect(await stream.incomingMessages.toList(), isEmpty); + expect(await stream.peerPushes.toList(), isEmpty); + + await client.finish(); + } + + await Future.wait([serverFun(), clientFun()]); + }); }); group('client-errors', () { @@ -146,7 +370,7 @@ main() { settingsDone.complete(); // Make sure we got the new stream. - HeadersFrame frame = await nextFrame(); + var frame = await nextFrame(); expect(frame.hasEndStreamFlag, false); var decodedHeaders = decoder.decode(frame.headerBlockFragment); expect(decodedHeaders, hasLength(1)); @@ -203,7 +427,7 @@ clientTest(String name, streams.serverConnectionFrameWriter, serverReader, readNext); - })); + }); } class ClientStreams { From 74bcb728a590c4b40e37ca189bedda7b9ead94e7 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Tue, 1 Sep 2015 11:18:42 +0200 Subject: [PATCH 032/172] Introduce {Client,Server}Settings, enforce max concurrent streams setting This CL: * Renames Settings to ActiveSettings. [ActiveSettings] are the settings used internally for sending/receiving. * Adds {Client,Server}Settings class: This allows a client to specify the settings the server should obey (allow pushes, maximum server streams) and allows the server to specify settings the client should obey (maximum concurrent streams). * It allows us to re-order server push messages earlier. This has two consequences: The client *needs* to handle all pushes (if it enabled pushes), a push is decoupled from the normal data stream. * Adds tests which test: - If pushes were disabled, a push will be RSTed on the client if received. - The number of pushes is restricted and will be enforced. R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/247197013 . --- pkgs/http2/experimental/server.dart | 1 - pkgs/http2/lib/client.dart | 24 +++- pkgs/http2/lib/debug.dart | 2 +- pkgs/http2/lib/src/connection.dart | 66 +++++---- .../src/flowcontrol/connection_queues.dart | 21 ++- .../lib/src/flowcontrol/stream_queues.dart | 34 +++-- pkgs/http2/lib/src/frames/frame_reader.dart | 2 +- pkgs/http2/lib/src/frames/frame_writer.dart | 2 +- pkgs/http2/lib/src/settings/settings.dart | 24 ++-- .../http2/lib/src/streams/stream_handler.dart | 129 +++++++++++++++--- pkgs/http2/lib/transport.dart | 72 ++++++++-- pkgs/http2/test/client_tests.dart | 4 +- pkgs/http2/test/server_tests.dart | 4 +- .../test/src/frames/frame_reader_test.dart | 10 +- .../src/frames/frame_writer_reader_test.dart | 2 +- .../test/src/frames/frame_writer_test.dart | 2 +- .../src/settings/settings_handler_test.dart | 9 +- pkgs/http2/test/src/streams/helper.dart | 11 +- .../test/src/streams/simple_push_test.dart | 2 +- pkgs/http2/test/transport_test.dart | 119 +++++++++++++++- 20 files changed, 417 insertions(+), 123 deletions(-) diff --git a/pkgs/http2/experimental/server.dart b/pkgs/http2/experimental/server.dart index 12af09d692..d319bbf5ed 100644 --- a/pkgs/http2/experimental/server.dart +++ b/pkgs/http2/experimental/server.dart @@ -19,7 +19,6 @@ const int PORT = 7777; main() async { String localFile(path) => Platform.script.resolve(path).toFilePath(); - var privateKeyFile = Platform.script.resolve('privatekey.pem').toFilePath(); var context = new SecurityContext() ..usePrivateKey(localFile('server_key.pem'), password: 'dartdart') ..useCertificateChain(localFile('server_chain.pem')) diff --git a/pkgs/http2/lib/client.dart b/pkgs/http2/lib/client.dart index 9c87a2b59b..a1ed835f7d 100644 --- a/pkgs/http2/lib/client.dart +++ b/pkgs/http2/lib/client.dart @@ -36,9 +36,12 @@ class ServerPush { class ClientConnection { final ClientTransportConnection connection; - ClientConnection(Socket socket, {bool allowServerPushes: false}) + /// Assumes the protocol on [socket] was negogiated to be http/2. + /// + /// If [settings] are omitted, the default [ClientSettings] will be used. + ClientConnection(Socket socket, {ClientSettings settings}) : connection = new ClientTransportConnection.viaSocket( - socket, allowServerPushes: allowServerPushes); + socket, settings: settings); Future makeRequest(Request request) { var path = request.uri.path; @@ -116,21 +119,30 @@ class ClientConnection { } -Future connect( - Uri uri, {bool allowServerPushes: false}) async { +/// Tries to connect to [uri] via a secure socket connection and establishes a +/// http/2 connection. +/// +/// If [allowServerPushes] is `true`, server pushes need to be handled by the +/// client. The maximum number of concurrent server pushes can be configured via +/// [maxConcurrentPushes] (default is `null` meaning no limit). +Future connect(Uri uri, + {bool allowServerPushes: false, + int maxConcurrentPushes: null}) async { const List Http2AlpnProtocols = const ['h2-14', 'h2-15', 'h2-16', 'h2-17', 'h2']; bool useSSL = uri.scheme == 'https'; + var settings = new ClientSettings( + maxConcurrentPushes, allowServerPushes); if (useSSL) { SecureSocket socket = await SecureSocket.connect( uri.host, uri.port, supportedProtocols: Http2AlpnProtocols); if (!Http2AlpnProtocols.contains(socket.selectedProtocol)) { throw new Exception('Server does not support HTTP/2.'); } - return new ClientConnection(socket, allowServerPushes: allowServerPushes); + return new ClientConnection(socket, settings: settings); } else { Socket socket = await Socket.connect(uri.host, uri.port); - return new ClientConnection(socket, allowServerPushes: allowServerPushes); + return new ClientConnection(socket, settings: settings); } } diff --git a/pkgs/http2/lib/debug.dart b/pkgs/http2/lib/debug.dart index 7b5c6636c1..2a84594806 100644 --- a/pkgs/http2/lib/debug.dart +++ b/pkgs/http2/lib/debug.dart @@ -120,7 +120,7 @@ StreamSink> decodeOutgoingVerbose(StreamSink> sink, } Stream _decodeFrames(Stream> bytes) { - var settings = new Settings(); + var settings = new ActiveSettings(); var decoder = new FrameReader(bytes, settings); return decoder.startDecoding(); } diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index 7ae6427d80..d8494c836b 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -5,10 +5,9 @@ library http2.src.conn; import 'dart:async'; -import 'dart:math'; import 'dart:convert'; -import '../transport.dart'; +import '../transport.dart' hide Settings; import 'flowcontrol/connection_queues.dart'; import 'flowcontrol/window.dart'; import 'flowcontrol/window_handler.dart'; @@ -40,17 +39,14 @@ enum ConnectionState { abstract class Connection { /// The settings the other end has acknowledged to use when communicating with /// us. - final Settings acknowledgedSettings = new Settings(); + final ActiveSettings acknowledgedSettings = new ActiveSettings(); /// The settings we have to obey communicating with the other side. - final Settings peerSettings = new Settings(); + final ActiveSettings peerSettings = new ActiveSettings(); /// Whether this connection is a client connection. final bool isClientConnection; - /// Whether server side pushes are allowed. - final bool pushEnabled; - /// The HPack context for this connection. final HPackContext _hpackContext = new HPackContext(); @@ -93,15 +89,16 @@ abstract class Connection { Connection(Stream> incoming, StreamSink> outgoing, - {this.isClientConnection: true, - this.pushEnabled}) { - _setupConnection(incoming, outgoing); + List settings, + {this.isClientConnection: true}) { + _setupConnection(incoming, outgoing, settings); } /// Runs all setup necessary before new streams can be created with the remote /// peer. void _setupConnection(Stream> incoming, - StreamSink> outgoing) { + StreamSink> outgoing, + List settings) { // Setup frame reading. var incomingFrames = new FrameReader( incoming, acknowledgedSettings).startDecoding(); @@ -128,11 +125,6 @@ abstract class Connection { _pingHandler = new PingHandler(_frameWriter); // Do the initial settings handshake (possibly with pushes disabled). - var settings = []; - if (isClientConnection && !pushEnabled) { - // By default the server is allowed to do server pushes. - settings.add(new Setting(Setting.SETTINGS_ENABLE_PUSH, 0)); - } _settingsHandler.changeSettings(settings).catchError((error) { // TODO: The [error] can contain sensitive information we now expose via // a [Goaway] frame. We should somehow ensure we're only sending useful @@ -343,17 +335,30 @@ abstract class Connection { class ClientConnection extends Connection implements ClientTransportConnection { ClientConnection._(Stream> incoming, StreamSink> outgoing, - bool pushEnabled) + List settings) : super(incoming, outgoing, - isClientConnection: true, - pushEnabled: pushEnabled); + settings, + isClientConnection: true); factory ClientConnection(Stream> incoming, StreamSink> outgoing, - {bool allowServerPushes: true}) { + ClientSettings clientSettings) { + var settings = []; + + // By default the server is allowed to do server pushes. + if (!clientSettings.allowServerPushes) { + settings.add(new Setting(Setting.SETTINGS_ENABLE_PUSH, 0)); + } + // By default the client is allowed to make/push an unlimited number of + // concurrent streams. + if (clientSettings.concurrentStreamLimit != null) { + settings.add(new Setting(Setting.SETTINGS_MAX_CONCURRENT_STREAMS, + clientSettings.concurrentStreamLimit)); + } + outgoing.add(CONNECTION_PREFACE); - return new ClientConnection._(incoming, outgoing, allowServerPushes); + return new ClientConnection._(incoming, outgoing, settings); } bool get isOpen => _state != ConnectionState.Finishing && @@ -376,14 +381,25 @@ class ClientConnection extends Connection implements ClientTransportConnection { class ServerConnection extends Connection implements ServerTransportConnection { ServerConnection._(Stream> incoming, - StreamSink> outgoing) + StreamSink> outgoing, + List settings) : super( - incoming, outgoing, isClientConnection: false, pushEnabled: false); + incoming, outgoing, settings, isClientConnection: false); factory ServerConnection(Stream> incoming, - StreamSink> outgoing) { + StreamSink> outgoing, + ServerSettings serverSettings) { + var settings = []; + + // By default the client is allowed to make an unlimited number of + // concurrent streams. + if (serverSettings.concurrentStreamLimit != null) { + settings.add(new Setting(Setting.SETTINGS_MAX_CONCURRENT_STREAMS, + serverSettings.concurrentStreamLimit)); + } + var frameBytes = readConnectionPreface(incoming); - return new ServerConnection._(frameBytes, outgoing); + return new ServerConnection._(frameBytes, outgoing, settings); } Stream get incomingStreams => _streams.incomingStreams; diff --git a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart index de3a56cd07..c84e8bf506 100644 --- a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart @@ -256,21 +256,34 @@ class ConnectionMessageQueueIn extends Object var message = new PushPromiseMessage( streamId, frame.decodedHeaders, frame.promisedStreamId, pushedStream, false); - // NOTE: Header frames do not affect flow control - only data frames do. - _addMessage(streamId, message); + + // NOTE: + // * Header frames do not affect flow control - only data frames do. + // * At this point we might reorder a push message earlier than + // data/headers messages. + _addPushMessage(streamId, message); } void _addMessage(int streamId, Message message) { _count++; - // FIXME: We need to do a runtime check here and - // raise a protocol error if we cannot find the registered stream. + // TODO: Do we need to do a runtime check here and + // raise a protocol error if we cannot find the registered stream? var streamMQ = _stream2messageQueue[streamId]; var pendingMessages = _stream2pendingMessages[streamId]; pendingMessages.addLast(message); _tryDispatch(streamId, streamMQ, pendingMessages); } + void _addPushMessage(int streamId, PushPromiseMessage message) { + _count++; + + // TODO: Do we need to do a runtime check here and + // raise a protocol error if we cannot find the registered stream? + var streamMQ = _stream2messageQueue[streamId]; + streamMQ.enqueueMessage(message); + } + void _tryDispatch(int streamId, StreamMessageQueueIn mq, Queue pendingMessages) { diff --git a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart index 5841357867..1c5bc9415f 100644 --- a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart @@ -198,9 +198,13 @@ class StreamMessageQueueIn extends Object onCloseCheck(); }); - // FIXME/TODO/FIXME: This needs to change, we need to intercept all - // onListen/... callbacks. - _serverPushStreamsC = new StreamController(); + _serverPushStreamsC = new StreamController( + onListen: () { + if (!wasClosed && !wasTerminated) { + _tryDispatch(); + _tryUpdateBufferIndicator(); + } + }); } /// Debugging data: the number of pending messages in this queue. @@ -217,6 +221,16 @@ class StreamMessageQueueIn extends Object void enqueueMessage(Message message) { ensureNotClosingSync(() { if (!wasTerminated) { + if (message is PushPromiseMessage) { + // NOTE: If server pushes were enabled, the client is responsible for + // either rejecting or handling them. + assert (!_serverPushStreamsC.isClosed); + var transportStreamPush = new TransportStreamPush( + message.headers, message.pushedStream); + _serverPushStreamsC.add(transportStreamPush); + return; + } + if (message is DataMessage) { windowHandler.gotData(message.bytes.length); } @@ -269,20 +283,12 @@ class StreamMessageQueueIn extends Object _incomingMessagesC.add(new DataStreamMessage(message.bytes)); windowHandler.dataProcessed(message.bytes.length); } + } else { + // This can never happen. + assert(false); } handled = true; } - } else if (message is PushPromiseMessage) { - assert (!_serverPushStreamsC.isClosed); - if (_serverPushStreamsC.hasListener && - !_serverPushStreamsC.isPaused) { - _pendingMessages.removeFirst(); - - var transportStreamPush = new TransportStreamPush( - message.headers, message.pushedStream); - _serverPushStreamsC.add(transportStreamPush); - handled = true; - } } if (handled) { if (message.endStream) { diff --git a/pkgs/http2/lib/src/frames/frame_reader.dart b/pkgs/http2/lib/src/frames/frame_reader.dart index ded54b9fa6..256da5dc80 100644 --- a/pkgs/http2/lib/src/frames/frame_reader.dart +++ b/pkgs/http2/lib/src/frames/frame_reader.dart @@ -10,7 +10,7 @@ class FrameReader { /// Connection settings which this reader needs to ensure the remote end is /// complying with. - Settings _localSettings; + ActiveSettings _localSettings; StreamSubscription> _subscription; StreamController _framesController; diff --git a/pkgs/http2/lib/src/frames/frame_writer.dart b/pkgs/http2/lib/src/frames/frame_writer.dart index 4bf58ccf10..dc441dd1e4 100644 --- a/pkgs/http2/lib/src/frames/frame_writer.dart +++ b/pkgs/http2/lib/src/frames/frame_writer.dart @@ -14,7 +14,7 @@ class FrameWriter { final BufferedBytesWriter _outWriter; /// Connection settings which this writer needs to respect. - final Settings _peerSettings; + final ActiveSettings _peerSettings; /// This is the maximum over all stream id's we've written to the underlying /// sink. diff --git a/pkgs/http2/lib/src/settings/settings.dart b/pkgs/http2/lib/src/settings/settings.dart index 3279883f4d..ec55c5cc30 100644 --- a/pkgs/http2/lib/src/settings/settings.dart +++ b/pkgs/http2/lib/src/settings/settings.dart @@ -11,7 +11,7 @@ import '../sync_errors.dart'; import '../error_handler.dart'; /// The settings a remote peer can choose to set. -class Settings { +class ActiveSettings { /// Allows the sender to inform the remote endpoint of the maximum size of the /// header compression table used to decode header blocks, in octets. The /// encoder can select any size equal to or less than this value by using @@ -72,12 +72,12 @@ class Settings { /// enforced. The initial value of this setting is unlimited. int maxHeaderListSize; - Settings({this.headerTableSize: 4096, - this.enablePush: true, - this.maxConcurrentStreams: null, - this.initialWindowSize: (1 << 16) - 1, - this.maxFrameSize: (1 << 14), - this.maxHeaderListSize: null}); + ActiveSettings({this.headerTableSize: 4096, + this.enablePush: true, + this.maxConcurrentStreams: null, + this.initialWindowSize: (1 << 16) - 1, + this.maxFrameSize: (1 << 14), + this.maxHeaderListSize: null}); } @@ -96,10 +96,10 @@ class SettingsHandler extends Object with TerminatableMixin { final List _toBeAcknowledgedCompleters = []; /// The local settings, which the remote side ACKed to obey. - Settings _acknowledgedSettings = new Settings(); + ActiveSettings _acknowledgedSettings; /// The peer settings, which we ACKed and are obeying. - Settings _peerSettings = new Settings(); + ActiveSettings _peerSettings; StreamController _onInitialWindowSizeChangeController = new StreamController.broadcast(sync: true); @@ -115,11 +115,11 @@ class SettingsHandler extends Object with TerminatableMixin { /// The settings for this endpoint of the connection which the remote peer /// has ACKed and uses. - Settings get acknowledgedSettings => _acknowledgedSettings; + ActiveSettings get acknowledgedSettings => _acknowledgedSettings; /// The settings for the remote endpoint of the connection which this /// endpoint should use. - Settings get peerSettings => _peerSettings; + ActiveSettings get peerSettings => _peerSettings; /// Handles an incoming [SettingsFrame] which can be an ACK or a settings /// change. @@ -167,7 +167,7 @@ class SettingsHandler extends Object with TerminatableMixin { }); } - void _modifySettings(Settings base, List changes) { + void _modifySettings(ActiveSettings base, List changes) { for (var setting in changes) { switch (setting.identifier) { case Setting.SETTINGS_ENABLE_PUSH: diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index c433b302c7..263c6eb370 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -61,6 +61,7 @@ class Http2StreamImpl extends TransportStream // The state of this stream. StreamState state = StreamState.Idle; + final Function _canPushFun; final Function _pushStreamFun; final Function _terminateStreamFun; @@ -71,6 +72,7 @@ class Http2StreamImpl extends TransportStream this._outgoingC, this.id, this.windowHandler, + this._canPushFun, this._pushStreamFun, this._terminateStreamFun); @@ -83,6 +85,8 @@ class Http2StreamImpl extends TransportStream /// Streams which the server pushed to this endpoint. Stream get peerPushes => incomingQueue.serverPushes; + bool get canPush => _canPushFun(this); + /// Pushes a new stream to a client. /// /// The [requestHeaders] are the headers to which the pushed stream @@ -107,8 +111,8 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { final StreamController _newStreamsC = new StreamController(); - final Settings _peerSettings; - final Settings _localSettings; + final ActiveSettings _peerSettings; + final ActiveSettings _localSettings; final Map _openStreams = {}; int nextStreamId; @@ -129,7 +133,8 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { factory StreamHandler.client(FrameWriter writer, ConnectionMessageQueueIn incomingQueue, ConnectionMessageQueueOut outgoingQueue, - Settings peerSettings, Settings localSettings) { + ActiveSettings peerSettings, + ActiveSettings localSettings) { return new StreamHandler._( writer, incomingQueue, outgoingQueue, peerSettings, localSettings, 1, 0); @@ -138,7 +143,8 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { factory StreamHandler.server(FrameWriter writer, ConnectionMessageQueueIn incomingQueue, ConnectionMessageQueueOut outgoingQueue, - Settings peerSettings, Settings localSettings) { + ActiveSettings peerSettings, + ActiveSettings localSettings) { return new StreamHandler._( writer, incomingQueue, outgoingQueue, peerSettings, localSettings, 2, -1); @@ -181,6 +187,8 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { TransportStream newLocalStream() { return ensureNotTerminatedSync(() { + assert(_canCreateNewStream()); + if (MAX_STREAM_ID < (nextStreamId + 2)) { throw new StateError( 'Cannot create new streams, since a wrap around would happen.'); @@ -251,7 +259,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { var _outgoingC = new StreamController(); var stream = new Http2StreamImpl( streamQueueIn, streamQueueOut, _outgoingC, streamId, windowOutHandler, - this._push, this._terminateStream); + this._canPush, this._push, this._terminateStream); _openStreams[stream.id] = stream; _setupOutgoingMessageHandling(stream); @@ -259,6 +267,13 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { return stream; } + bool _canPush(Http2StreamImpl stream) { + bool openState = (stream.state == StreamState.Open || + stream.state == StreamState.HalfClosedRemote); + bool pushEnabled = this._peerSettings.enablePush; + return openState && pushEnabled && _canCreateNewStream(); + } + TransportStream _push(Http2StreamImpl stream, List
requestHeaders) { if (stream.state != StreamState.Open && stream.state != StreamState.HalfClosedRemote) { @@ -266,12 +281,23 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { 'nor half-closed-remote.'); } + if (!_peerSettings.enablePush) { + throw new StateError('Client did disable server pushes.'); + } + + if (!_canCreateNewStream()) { + throw new StateError('Maximum number of streams reached.'); + } + Http2StreamImpl pushStream = newLocalStream(); // NOTE: Since there was no real request from the client, we simulate it // by adding a synthetic `endStream = true` Data message into the incoming // queue. - pushStream.state = StreamState.HalfClosedRemote; + _changeState(pushStream, StreamState.ReservedLocal); + // TODO: We should wait for us to send the headers frame before doing this + // transition. + _changeState(pushStream, StreamState.HalfClosedRemote); pushStream.incomingQueue.enqueueMessage( new DataMessage(stream.id, const [], true)); @@ -324,7 +350,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { _closeStreamAbnormally(stream, exception); return; } - stream.state = StreamState.Open; + _changeState(stream, StreamState.Open); } if (msg is DataStreamMessage) { @@ -392,7 +418,6 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { return isIdleStream; } bool isPeerInitiatedStream() { - int streamId = frame.header.streamId; bool isServerStreamId = frame.header.streamId.isEven; bool isLocalStream = isServerStreamId == isServer; return !isLocalStream; @@ -409,7 +434,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { if (frame is HeadersFrame) { if (isServer) { Http2StreamImpl newStream = newRemoteStream(frame.header.streamId); - newStream.state = StreamState.Open; + _changeState(newStream, StreamState.Open); _handleHeadersFrame(newStream, frame); _newStreamsC.add(newStream); @@ -487,7 +512,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { void _handleHeadersFrame(Http2StreamImpl stream, HeadersFrame frame) { if (stream.state == StreamState.ReservedRemote) { - stream.state = StreamState.HalfClosedLocal; + _changeState(stream, StreamState.HalfClosedLocal); } if (stream.state != StreamState.Open && @@ -521,7 +546,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { } var pushedStream = newRemoteStream(frame.promisedStreamId); - pushedStream.state = StreamState.ReservedRemote; + _changeState(pushedStream, StreamState.ReservedRemote); incomingQueue.processPushPromiseFrame(frame, pushedStream); } @@ -532,9 +557,9 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { void _handleEndOfStreamRemote(Http2StreamImpl stream) { if (stream.state == StreamState.Open) { - stream.state = StreamState.HalfClosedRemote; + _changeState(stream, StreamState.HalfClosedRemote); } else if (stream.state == StreamState.HalfClosedLocal) { - stream.state = StreamState.Closed; + _changeState(stream, StreamState.Closed); // TODO: We have to make sure that we // - remove the stream for data structures which only care about the // state @@ -566,7 +591,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { new HeadersMessage(stream.id, headers, endStream)); if (stream.state == StreamState.Idle) { - stream.state = StreamState.Open; + _changeState(stream, StreamState.Open); } if (endStream) { @@ -591,9 +616,9 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { void _endStream(Http2StreamImpl stream) { if (stream.state == StreamState.Open) { - stream.state = StreamState.HalfClosedLocal; + _changeState(stream, StreamState.HalfClosedLocal); } else if (stream.state == StreamState.HalfClosedRemote) { - stream.state = StreamState.Closed; + _changeState(stream, StreamState.Closed); } else { throw new StateError( 'Invalid state transition. This should never happen.'); @@ -629,7 +654,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { {bool propagateException: false}) { incomingQueue.removeStreamMessageQueue(stream.id); - stream.state = StreamState.Terminated; + _changeState(stream, StreamState.Terminated); stream.incomingQueue.terminate(propagateException ? exception : null); stream._outgoingCSubscription.cancel(); @@ -650,4 +675,74 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { closeWithValue(); } } + + //////////////////////////////////////////////////////////////////////////// + //// State transitioning & Counting of active streams + //////////////////////////////////////////////////////////////////////////// + + /// The number of streams which we initiated and which are in one of the open + /// states (i.e. [StreamState.Open], [StreamState.HalfClosedLocal] or + /// [StreamState.HalfClosedRemote]) + int _numberOfActiveStreams = 0; + + bool _canCreateNewStream() { + int limit = _peerSettings.maxConcurrentStreams; + return limit == null || _numberOfActiveStreams < limit; + } + + void _changeState(Http2StreamImpl stream, StreamState to) { + StreamState from = stream.state; + + // In checked mode we'll test that the state transition is allowed. + assert( + (from == StreamState.Idle && to == StreamState.ReservedLocal) || + (from == StreamState.Idle && to == StreamState.ReservedRemote) || + (from == StreamState.Idle && to == StreamState.Open) || + (from == StreamState.Open && to == StreamState.HalfClosedLocal) || + (from == StreamState.Open && to == StreamState.HalfClosedRemote) || + (from == StreamState.Open && to == StreamState.Closed) || + (from == StreamState.HalfClosedLocal && to == StreamState.Closed) || + (from == StreamState.HalfClosedRemote && to == StreamState.Closed) || + (from == StreamState.ReservedLocal && + to == StreamState.HalfClosedRemote) || + (from == StreamState.ReservedLocal && to == StreamState.Closed) || + (from == StreamState.ReservedRemote && to == StreamState.Closed) || + (from == StreamState.ReservedRemote && + to == StreamState.HalfClosedLocal) || + (from != StreamState.Terminated && to == StreamState.Terminated)); + + // If we initiated the stream and it became "open" or "closed" we need to + // update the [_numberOfActiveStreams] counter. + if (_didInitiateStream(stream)) { + switch (stream.state) { + case StreamState.ReservedLocal: + case StreamState.ReservedRemote: + case StreamState.Idle: + if (to == StreamState.Open || + to == StreamState.HalfClosedLocal || + to == StreamState.HalfClosedRemote) { + _numberOfActiveStreams++; + } + break; + case StreamState.Open: + case StreamState.HalfClosedLocal: + case StreamState.HalfClosedRemote: + if (to == StreamState.Closed || to == StreamState.Terminated) { + _numberOfActiveStreams--; + } + break; + case StreamState.Closed: + case StreamState.Terminated: + // There is nothing to do here. + break; + } + } + stream.state = to; + } + + bool _didInitiateStream(Http2StreamImpl stream) { + int id = stream.id; + return (isServer && id.isEven) || (!isServer && id.isOdd); + } } + diff --git a/pkgs/http2/lib/transport.dart b/pkgs/http2/lib/transport.dart index 1a950498d3..aca553c919 100644 --- a/pkgs/http2/lib/transport.dart +++ b/pkgs/http2/lib/transport.dart @@ -12,6 +12,29 @@ import 'src/hpack/hpack.dart' show Header; export 'src/hpack/hpack.dart' show Header; +/// Settings for a [TransportConnection]. +abstract class Settings { + /// The maximum number of concurrent streams the remote end can open. + final int concurrentStreamLimit; + + const Settings(this.concurrentStreamLimit); +} + +/// Settings for a [TransportConnection] a server can make. +class ServerSettings extends Settings { + const ServerSettings(int concurrentStreamLimit) + : super(concurrentStreamLimit); +} + +/// Settings for a [TransportConnection] a client can make. +class ClientSettings extends Settings { + /// Whether the client allows pushes from the server. + final bool allowServerPushes; + + const ClientSettings(int concurrentStreamLimit, this.allowServerPushes) + : super(concurrentStreamLimit); +} + /// Represents a HTTP/2 connection. abstract class TransportConnection { /// Pings the other end. @@ -28,15 +51,17 @@ abstract class TransportConnection { abstract class ClientTransportConnection extends TransportConnection { factory ClientTransportConnection.viaSocket(Socket socket, - {bool allowServerPushes: true}) + {ClientSettings settings}) => new ClientTransportConnection.viaStreams( - socket, socket, allowServerPushes: allowServerPushes); - - factory ClientTransportConnection.viaStreams(Stream> incoming, - StreamSink> outgoing, - {bool allowServerPushes: true}) - => new ClientConnection( - incoming, outgoing, allowServerPushes: allowServerPushes); + socket, socket, settings: settings); + + factory ClientTransportConnection.viaStreams( + Stream> incoming, + StreamSink> outgoing, + {ClientSettings settings}) { + if (settings == null) settings = const ClientSettings(null, false); + return new ClientConnection(incoming, outgoing, settings); + } /// Whether this connection is open and can be used to make new requests /// via [makeRequest]. @@ -48,12 +73,19 @@ abstract class ClientTransportConnection extends TransportConnection { } abstract class ServerTransportConnection extends TransportConnection { - factory ServerTransportConnection.viaSocket(Socket socket) - => new ServerTransportConnection.viaStreams(socket, socket); + factory ServerTransportConnection.viaSocket(Socket socket, + {ServerSettings settings}) { + return new ServerTransportConnection.viaStreams( + socket, socket, settings: settings); + } - factory ServerTransportConnection.viaStreams(Stream> incoming, - StreamSink> outgoing) - => new ServerConnection(incoming, outgoing); + factory ServerTransportConnection.viaStreams( + Stream> incoming, + StreamSink> outgoing, + {ServerSettings settings: const ServerSettings(1000)}) { + if (settings == null) settings = const ServerSettings(null); + return new ServerConnection(incoming, outgoing, settings); + } /// Incoming HTTP/2 streams. Stream get incomingStreams; @@ -97,12 +129,22 @@ abstract class TransportStream { abstract class ClientTransportStream extends TransportStream { /// Streams which the remote end pushed to this endpoint. + /// + /// If peer pushes were enabled, the client is responsible to either + /// handle or reject any peer push. Stream get peerPushes; } abstract class ServerTransportStream extends TransportStream { + /// Whether a method to [push] will succeed. Requirements for this getter to + /// return `true` are: + /// * this stream must be in the Open or HalfClosedRemote state + /// * the client needs to have the "enable push" settings enabled + /// * the number of active streams has not reached the maximum + bool get canPush; + /// Pushes a new stream to the remote peer. - TransportStream push(List
requestHeaders); + ServerTransportStream push(List
requestHeaders); } /// Represents a message which can be sent over a HTTP/2 stream. @@ -135,7 +177,7 @@ class TransportStreamPush { final List
requestHeaders; /// The remote stream push. - final TransportStream stream; + final ClientTransportStream stream; TransportStreamPush(this.requestHeaders, this.stream); diff --git a/pkgs/http2/test/client_tests.dart b/pkgs/http2/test/client_tests.dart index f76dfc8757..32108757d3 100644 --- a/pkgs/http2/test/client_tests.dart +++ b/pkgs/http2/test/client_tests.dart @@ -437,7 +437,7 @@ class ClientStreams { Stream> get readB => writeB.stream; StreamIterator get serverConnectionFrameReader { - Settings localSettings = new Settings(); + ActiveSettings localSettings = new ActiveSettings(); var streamAfterConnectionPreface = readConnectionPreface(readA); return new StreamIterator( new FrameReader(streamAfterConnectionPreface, localSettings) @@ -446,7 +446,7 @@ class ClientStreams { FrameWriter get serverConnectionFrameWriter { var encoder = new HPackEncoder(); - Settings peerSettings = new Settings(); + ActiveSettings peerSettings = new ActiveSettings(); return new FrameWriter(encoder, writeB, peerSettings); } diff --git a/pkgs/http2/test/server_tests.dart b/pkgs/http2/test/server_tests.dart index b6041ff927..1d9c5e161b 100644 --- a/pkgs/http2/test/server_tests.dart +++ b/pkgs/http2/test/server_tests.dart @@ -225,14 +225,14 @@ class ClientErrorStreams { Stream> get readB => writeB.stream; StreamIterator get clientConnectionFrameReader { - Settings localSettings = new Settings(); + ActiveSettings localSettings = new ActiveSettings(); return new StreamIterator( new FrameReader(readA, localSettings).startDecoding()); } FrameWriter get clientConnectionFrameWriter { var encoder = new HPackEncoder(); - Settings peerSettings = new Settings(); + ActiveSettings peerSettings = new ActiveSettings(); writeB.add(CONNECTION_PREFACE); return new FrameWriter(encoder, writeB, peerSettings); } diff --git a/pkgs/http2/test/src/frames/frame_reader_test.dart b/pkgs/http2/test/src/frames/frame_reader_test.dart index 20a25cf393..817c1df92c 100644 --- a/pkgs/http2/test/src/frames/frame_reader_test.dart +++ b/pkgs/http2/test/src/frames/frame_reader_test.dart @@ -15,10 +15,10 @@ import '../hpack/hpack_test.dart' show isHeader; main() { group('frames', () { group('frame-reader', () { - final int maxFrameSize = new Settings().maxFrameSize; + final int maxFrameSize = new ActiveSettings().maxFrameSize; Stream dataFrame(List body) { - var settings = new Settings(); + var settings = new ActiveSettings(); var context = new HPackContext(); var controller = new StreamController>(); var reader = new FrameReader(controller.stream, settings); @@ -63,7 +63,7 @@ main() { }); test('incomplete-header', () { - var settings = new Settings(); + var settings = new ActiveSettings(); var context = new HPackContext(); var controller = new StreamController>(); @@ -79,7 +79,7 @@ main() { }); test('incomplete-frame', () { - var settings = new Settings(); + var settings = new ActiveSettings(); var context = new HPackContext(); var controller = new StreamController>(); @@ -100,7 +100,7 @@ main() { }); test('connection-error', () { - var settings = new Settings(); + var settings = new ActiveSettings(); var context = new HPackContext(); var controller = new StreamController>(); diff --git a/pkgs/http2/test/src/frames/frame_writer_reader_test.dart b/pkgs/http2/test/src/frames/frame_writer_reader_test.dart index bb09632254..11ce7cbb26 100644 --- a/pkgs/http2/test/src/frames/frame_writer_reader_test.dart +++ b/pkgs/http2/test/src/frames/frame_writer_reader_test.dart @@ -231,7 +231,7 @@ main() { writerReaderTest(String name, func(writer, reader, decoder)) { test(name, () { - var settings = new Settings(); + var settings = new ActiveSettings(); var context = new HPackContext(); var controller = new StreamController>(); var writer = new FrameWriter(context.encoder, controller, settings); diff --git a/pkgs/http2/test/src/frames/frame_writer_test.dart b/pkgs/http2/test/src/frames/frame_writer_test.dart index 4033f9546b..d523e8209e 100644 --- a/pkgs/http2/test/src/frames/frame_writer_test.dart +++ b/pkgs/http2/test/src/frames/frame_writer_test.dart @@ -14,7 +14,7 @@ main() { group('frames', () { group('frame-writer', () { test('connection-error', () { - var settings = new Settings(); + var settings = new ActiveSettings(); var context = new HPackContext(); var controller = new StreamController>(); var writer = new FrameWriter(context.encoder, controller, settings); diff --git a/pkgs/http2/test/src/settings/settings_handler_test.dart b/pkgs/http2/test/src/settings/settings_handler_test.dart index cdfa547aad..66c2622f1d 100644 --- a/pkgs/http2/test/src/settings/settings_handler_test.dart +++ b/pkgs/http2/test/src/settings/settings_handler_test.dart @@ -19,7 +19,8 @@ main() { test('successful-setting', () async { var writer = new FrameWriterMock(); - var sh = new SettingsHandler(writer, new Settings(), new Settings()); + var sh = new SettingsHandler( + writer, new ActiveSettings(), new ActiveSettings()); var tc = new TestCounter(); writer.mock_writeSettingsFrame = (List s, {bool ack: false}) { @@ -47,7 +48,7 @@ main() { test('ack-remote-settings-change', () { var writer = new FrameWriterMock(); - var sh = new SettingsHandler(writer, new Settings(), new Settings()); + var sh = new SettingsHandler(writer, new ActiveSettings(), new ActiveSettings()); var tc = new TestCounter(); writer.mock_writeSettingsAckFrame = () { @@ -67,7 +68,7 @@ main() { test('invalid-remote-ack', () { var writer = new FrameWriterMock(); - var sh = new SettingsHandler(writer, new Settings(), new Settings()); + var sh = new SettingsHandler(writer, new ActiveSettings(), new ActiveSettings()); // Simulates ACK even though we haven't sent any settings. var header = new FrameHeader( @@ -80,7 +81,7 @@ main() { test('invalid-remote-settings-change', () { var writer = new FrameWriterMock(); - var sh = new SettingsHandler(writer, new Settings(), new Settings()); + var sh = new SettingsHandler(writer, new ActiveSettings(), new ActiveSettings()); // Check that settings haven't been applied. expect(sh.peerSettings.enablePush, true); diff --git a/pkgs/http2/test/src/streams/helper.dart b/pkgs/http2/test/src/streams/helper.dart index bcb6fda735..e05b1bfa99 100644 --- a/pkgs/http2/test/src/streams/helper.dart +++ b/pkgs/http2/test/src/streams/helper.dart @@ -25,9 +25,10 @@ expectEmptyStream(Stream s) { s.listen(expectAsync((_) {}, count: 0), onDone: expectAsync(() {})); } -streamTest(String name, func(client, server)) { +streamTest(String name, func(client, server), {ClientSettings settings}) { return test(name, () { var bidirect = new BidirectionalConnection(); + bidirect.settings = settings; var client = bidirect.clientConnection; var server = bidirect.serverConnection; return func(client, server); @@ -37,21 +38,23 @@ streamTest(String name, func(client, server)) { framesTest(String name, func(frameWriter, frameStream)) { return test(name, () { var c = new StreamController(); - var fw = new FrameWriter(null, c, new Settings()); - var frameStream = new FrameReader(c.stream, new Settings()); + var fw = new FrameWriter(null, c, new ActiveSettings()); + var frameStream = new FrameReader(c.stream, new ActiveSettings()); return func(fw, frameStream); }); } class BidirectionalConnection { + ClientSettings settings; final StreamController> writeA = new StreamController(); final StreamController> writeB = new StreamController(); Stream> get readA => writeA.stream; Stream> get readB => writeB.stream; ClientTransportConnection get clientConnection - => new ClientTransportConnection.viaStreams(readA, writeB); + => new ClientTransportConnection.viaStreams( + readA, writeB, settings: settings); ServerTransportConnection get serverConnection => new ServerTransportConnection.viaStreams(readB, writeA); diff --git a/pkgs/http2/test/src/streams/simple_push_test.dart b/pkgs/http2/test/src/streams/simple_push_test.dart index e1980d23df..0d29ce905a 100644 --- a/pkgs/http2/test/src/streams/simple_push_test.dart +++ b/pkgs/http2/test/src/streams/simple_push_test.dart @@ -88,7 +88,7 @@ main() { String msg = await readData(iterator); expect(msg, 'pushing "hello world" :)'); })); - }); + }, settings: new ClientSettings(null, true)); }); }); } diff --git a/pkgs/http2/test/transport_test.dart b/pkgs/http2/test/transport_test.dart index bdf603e408..c47088e2a4 100644 --- a/pkgs/http2/test/transport_test.dart +++ b/pkgs/http2/test/transport_test.dart @@ -5,9 +5,10 @@ import 'dart:async'; import 'package:test/test.dart'; - import 'package:http2/transport.dart'; +import 'src/hpack/hpack_test.dart' show isHeader; + main() { group('transport-test', () { transportTest('ping', (TransportConnection client, @@ -15,6 +16,7 @@ main() { await client.ping(); await server.ping(); }); + transportTest('terminated-client-ping', (TransportConnection client, TransportConnection server) async { var clientError = client.ping().catchError(expectAsync((e, s) { @@ -32,6 +34,7 @@ main() { expect(e is TransportException, isTrue); })); }); + transportTest('terminated-server-ping', (TransportConnection client, TransportConnection server) async { var clientError = client.ping().catchError(expectAsync((e, s) { @@ -49,12 +52,109 @@ main() { expect(e is TransportException, isTrue); })); }); + + transportTest('disabled-push', (ClientTransportConnection client, + ServerTransportConnection server) async { + server.incomingStreams.listen( + expectAsync((ServerTransportStream stream) async { + expect(stream.canPush, false); + expect(() => stream.push([new Header.ascii('a', 'b')]), throws); + stream.sendHeaders([new Header.ascii('x', 'y')], endStream: true); + })); + + var stream = client.makeRequest( + [new Header.ascii('a', 'b')], endStream: true); + + var messages = await stream.incomingMessages.toList(); + expect(messages, hasLength(1)); + expect(messages[0] is HeadersStreamMessage, true); + expect((messages[0] as HeadersStreamMessage).headers[0], + isHeader('x', 'y')); + + expect(await stream.peerPushes.toList(), isEmpty); + }); + + // By default, the stream concurrency level is set to this limit. + const int kDefaultStreamLimit = 100; + transportTest('enabled-push-100', (ClientTransportConnection client, + ServerTransportConnection server) async { + // To ensure the limit is kept up-to-date with closing/opening streams, we + // retry this. + const int kRepetitions = 20; + + Future serverFun() async { + await for (ServerTransportStream stream in server.incomingStreams) { + var pushes = []; + for (int i = 0; i < kDefaultStreamLimit; i++) { + expect(stream.canPush, true); + pushes.add(stream.push([new Header.ascii('a', 'b')])); + } + + // Now we should have reached the limit and we should not be able to + // create more pushes. + expect(stream.canPush, false); + expect(() => stream.push([new Header.ascii('a', 'b')]), throws); + + // Finish the pushes + for (ServerTransportStream pushedStream in pushes) { + pushedStream.sendHeaders( + [new Header.ascii('e', 'nd')], endStream: true); + await pushedStream.incomingMessages.toList(); + } + + // Finish the stream. + stream.sendHeaders([new Header.ascii('x', 'y')], endStream: true); + expect(await stream.incomingMessages.toList(), hasLength(1)); + } + } + + Future clientFun() async { + for (int i = 0; i < kRepetitions; i++) { + var stream = client.makeRequest( + [new Header.ascii('a', 'b')], endStream: true); + + Future expectPeerPushes() async { + int numberOfPushes = 0; + await for (TransportStreamPush pushedStream in stream.peerPushes) { + numberOfPushes++; + var messages = + await pushedStream.stream.incomingMessages.toList(); + expect(messages, hasLength(1)); + expect((messages[0] as HeadersStreamMessage).headers[0], + isHeader('e', 'nd')); + expect(await pushedStream.stream.peerPushes.toList(), isEmpty); + } + return numberOfPushes; + } + + // Wait for the end of the normal stream. + var messages = await stream.incomingMessages.toList(); + expect(messages, hasLength(1)); + expect(messages[0] is HeadersStreamMessage, true); + expect((messages[0] as HeadersStreamMessage).headers[0], + isHeader('x', 'y')); + + expect(await expectPeerPushes(), kDefaultStreamLimit); + } + } + + var serverFuture = serverFun(); + + await clientFun(); + await client.terminate(); + await serverFuture; + }, clientSettings: new ClientSettings(kDefaultStreamLimit, true)); }); } -transportTest(String name, func(client, server)) { +transportTest(String name, + func(client, server), + {ClientSettings clientSettings, + ServerSettings serverSettings}) { return test(name, () { var bidirectional = new BidirectionalConnection(); + bidirectional.clientSettings = clientSettings; + bidirectional.serverSettings = serverSettings; var client = bidirectional.clientConnection; var server = bidirectional.serverConnection; return func(client, server); @@ -62,14 +162,21 @@ transportTest(String name, func(client, server)) { } class BidirectionalConnection { + ClientSettings clientSettings; + ServerSettings serverSettings; + final StreamController> writeA = new StreamController(); final StreamController> writeB = new StreamController(); Stream> get readA => writeA.stream; Stream> get readB => writeB.stream; - TransportConnection get clientConnection - => new ClientTransportConnection.viaStreams(readA, writeB.sink); + ClientTransportConnection get clientConnection + => new ClientTransportConnection.viaStreams( + readA, + writeB.sink, settings: clientSettings); - TransportConnection get serverConnection - => new ServerTransportConnection.viaStreams(readB, writeA.sink); + ServerTransportConnection get serverConnection + => new ServerTransportConnection.viaStreams( + readB, writeA.sink, settings: serverSettings); } + From c01501e1e280b1cd7bfc74be319773996e7acb7e Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Tue, 1 Sep 2015 11:23:51 +0200 Subject: [PATCH 033/172] Forward SETTINGS_HEADER_TABLE_SIZE to hpack encoder R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/250007013 . --- pkgs/http2/lib/src/connection.dart | 4 +- pkgs/http2/lib/src/hpack/hpack.dart | 14 ++++--- pkgs/http2/lib/src/settings/settings.dart | 23 +++++++---- pkgs/http2/test/src/mock_utils.dart | 2 +- .../src/settings/settings_handler_test.dart | 38 +++++++++++++++++-- 5 files changed, 62 insertions(+), 19 deletions(-) diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index d8494c836b..7b8923fc93 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -121,7 +121,9 @@ abstract class Connection { // Setup handlers. _settingsHandler = new SettingsHandler( - _frameWriter, acknowledgedSettings, peerSettings); + _hpackContext.encoder, + _frameWriter, + acknowledgedSettings, peerSettings); _pingHandler = new PingHandler(_frameWriter); // Do the initial settings handshake (possibly with pushes disabled). diff --git a/pkgs/http2/lib/src/hpack/hpack.dart b/pkgs/http2/lib/src/hpack/hpack.dart index b277857b75..d9e5e2bfe1 100644 --- a/pkgs/http2/lib/src/hpack/hpack.dart +++ b/pkgs/http2/lib/src/hpack/hpack.dart @@ -31,11 +31,12 @@ class HPackContext { final HPackEncoder encoder = new HPackEncoder(); final HPackDecoder decoder = new HPackDecoder(); - HPackContext() { - // TODO: Inject these constants from Settings - // e.g. SETTINGS_HEADER_TABLE_SIZE - encoder.updateMaxSendingHeaderTableSize(4096); - decoder.updateMaxReceivingHeaderTableSize(4096); + HPackContext({int maxSendingHeaderTableSize: 4096, + int maxReceivingHeaderTableSize: 4096}) { + encoder.updateMaxSendingHeaderTableSize( + maxSendingHeaderTableSize); + decoder.updateMaxReceivingHeaderTableSize( + maxReceivingHeaderTableSize); } } @@ -175,8 +176,9 @@ class HPackEncoder { final IndexTable _table = new IndexTable(); void updateMaxSendingHeaderTableSize(int newMaximumSize) { + // TODO: Once we start encoding via dynamic table we need to let the other + // side know the maximum table size we're using. _maxHeaderTableSize = newMaximumSize; - // TODO: Don't we need to write the updated size now in the stream? } List encode(List
headers) { diff --git a/pkgs/http2/lib/src/settings/settings.dart b/pkgs/http2/lib/src/settings/settings.dart index ec55c5cc30..a695a2a8a7 100644 --- a/pkgs/http2/lib/src/settings/settings.dart +++ b/pkgs/http2/lib/src/settings/settings.dart @@ -7,6 +7,7 @@ library http2.src.settings; import 'dart:async'; import '../frames/frames.dart'; +import '../hpack/hpack.dart'; import '../sync_errors.dart'; import '../error_handler.dart'; @@ -87,6 +88,10 @@ class ActiveSettings { /// Changes to [_toBeAcknowledgedSettings] can be made, the peer will then be /// notified of the setting changes it should use. class SettingsHandler extends Object with TerminatableMixin { + /// Certain settings changes can change the maximum allowed dynamic table + /// size used by the HPack encoder. + final HPackEncoder _hpackEncoder; + final FrameWriter _frameWriter; /// A list of outstanding setting changes. @@ -96,10 +101,10 @@ class SettingsHandler extends Object with TerminatableMixin { final List _toBeAcknowledgedCompleters = []; /// The local settings, which the remote side ACKed to obey. - ActiveSettings _acknowledgedSettings; + final ActiveSettings _acknowledgedSettings; /// The peer settings, which we ACKed and are obeying. - ActiveSettings _peerSettings; + final ActiveSettings _peerSettings; StreamController _onInitialWindowSizeChangeController = new StreamController.broadcast(sync: true); @@ -109,7 +114,8 @@ class SettingsHandler extends Object with TerminatableMixin { Stream get onInitialWindowSizeChange => _onInitialWindowSizeChangeController.stream; - SettingsHandler(this._frameWriter, + SettingsHandler(this._hpackEncoder, + this._frameWriter, this._acknowledgedSettings, this._peerSettings); @@ -140,10 +146,10 @@ class SettingsHandler extends Object with TerminatableMixin { } List settingChanges = _toBeAcknowledgedSettings.removeAt(0); Completer completer = _toBeAcknowledgedCompleters.removeAt(0); - _modifySettings(_acknowledgedSettings, settingChanges); + _modifySettings(_acknowledgedSettings, settingChanges, false); completer.complete(); } else { - _modifySettings(_peerSettings, frame.settings); + _modifySettings(_peerSettings, frame.settings, true); _frameWriter.writeSettingsAckFrame(); } }); @@ -167,7 +173,8 @@ class SettingsHandler extends Object with TerminatableMixin { }); } - void _modifySettings(ActiveSettings base, List changes) { + void _modifySettings(ActiveSettings base, List changes, + bool peerSettings) { for (var setting in changes) { switch (setting.identifier) { case Setting.SETTINGS_ENABLE_PUSH: @@ -182,8 +189,10 @@ class SettingsHandler extends Object with TerminatableMixin { break; case Setting.SETTINGS_HEADER_TABLE_SIZE: - // TODO: Propagate this signal to the HPackContext. base.headerTableSize = setting.value; + if (peerSettings) { + _hpackEncoder.updateMaxSendingHeaderTableSize(base.headerTableSize); + } break; case Setting.SETTINGS_MAX_HEADER_LIST_SIZE: diff --git a/pkgs/http2/test/src/mock_utils.dart b/pkgs/http2/test/src/mock_utils.dart index d7110de0cb..888a20304d 100644 --- a/pkgs/http2/test/src/mock_utils.dart +++ b/pkgs/http2/test/src/mock_utils.dart @@ -21,7 +21,7 @@ import 'package:test/test.dart'; /// {bool ack: true}) { /// // Assert settings/ack & return maybe value. /// } -/// var settingsHandler = new SettingsHandler(writer); +/// var settingsHandler = new SettingsHandler(hpackEncoder, writer); /// /// // This will trigger a call on [writer] to the mocked method. /// settingsHandler.changeSettings([]); diff --git a/pkgs/http2/test/src/settings/settings_handler_test.dart b/pkgs/http2/test/src/settings/settings_handler_test.dart index 66c2622f1d..14dcb572a5 100644 --- a/pkgs/http2/test/src/settings/settings_handler_test.dart +++ b/pkgs/http2/test/src/settings/settings_handler_test.dart @@ -7,6 +7,7 @@ import 'dart:async'; import 'package:test/test.dart'; import 'package:http2/src/frames/frames.dart'; +import 'package:http2/src/hpack/hpack.dart'; import 'package:http2/src/settings/settings.dart'; import '../error_matchers.dart'; @@ -16,11 +17,13 @@ main() { group('settings-handler', () { var pushSettings = [new Setting(Setting.SETTINGS_ENABLE_PUSH, 0)]; var invalidPushSettings = [new Setting(Setting.SETTINGS_ENABLE_PUSH, 2)]; + var setMaxTable256 = [new Setting(Setting.SETTINGS_HEADER_TABLE_SIZE, 256)]; test('successful-setting', () async { var writer = new FrameWriterMock(); var sh = new SettingsHandler( - writer, new ActiveSettings(), new ActiveSettings()); + new HPackEncoder(), writer, + new ActiveSettings(), new ActiveSettings()); var tc = new TestCounter(); writer.mock_writeSettingsFrame = (List s, {bool ack: false}) { @@ -48,7 +51,9 @@ main() { test('ack-remote-settings-change', () { var writer = new FrameWriterMock(); - var sh = new SettingsHandler(writer, new ActiveSettings(), new ActiveSettings()); + var sh = new SettingsHandler( + new HPackEncoder(), writer, + new ActiveSettings(), new ActiveSettings()); var tc = new TestCounter(); writer.mock_writeSettingsAckFrame = () { @@ -68,7 +73,9 @@ main() { test('invalid-remote-ack', () { var writer = new FrameWriterMock(); - var sh = new SettingsHandler(writer, new ActiveSettings(), new ActiveSettings()); + var sh = new SettingsHandler( + new HPackEncoder(), writer, + new ActiveSettings(), new ActiveSettings()); // Simulates ACK even though we haven't sent any settings. var header = new FrameHeader( @@ -81,7 +88,9 @@ main() { test('invalid-remote-settings-change', () { var writer = new FrameWriterMock(); - var sh = new SettingsHandler(writer, new ActiveSettings(), new ActiveSettings()); + var sh = new SettingsHandler( + new HPackEncoder(), writer, + new ActiveSettings(), new ActiveSettings()); // Check that settings haven't been applied. expect(sh.peerSettings.enablePush, true); @@ -92,9 +101,30 @@ main() { expect(() => sh.handleSettingsFrame(settingsFrame), throwsProtocolException); }); + + test('change-max-header-table-size', () { + var writer = new FrameWriterMock(); + var mock = new HPackEncoderMock(); + var sh = new SettingsHandler( + mock, writer, + new ActiveSettings(), new ActiveSettings()); + + // Simulate remote end by setting the push setting. + var header = new FrameHeader(6, FrameType.SETTINGS, 0, 0); + var settingsFrame = new SettingsFrame(header, setMaxTable256); + mock.mock_updateMaxSendingHeaderTableSize = expectAsync((int newSize) { + expect(newSize, 256); + }); + writer.mock_writeSettingsAckFrame = expectAsync(() { }); + sh.handleSettingsFrame(settingsFrame); + }); }); } class FrameWriterMock extends SmartMock implements FrameWriter { dynamic noSuchMethod(_) => super.noSuchMethod(_); } + +class HPackEncoderMock extends SmartMock implements HPackEncoder { + dynamic noSuchMethod(_) => super.noSuchMethod(_); +} From fa37e8a47284043a68d63905c5eb9e8a44e2942d Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Wed, 2 Sep 2015 12:41:07 +0200 Subject: [PATCH 034/172] Wait for stream in/out queues to be closed before removing a stream from the global table R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/247277013 . --- .../http2/lib/src/streams/stream_handler.dart | 57 +++++++++++++------ pkgs/http2/test/transport_test.dart | 23 ++++++++ 2 files changed, 62 insertions(+), 18 deletions(-) diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index 263c6eb370..cd00ac9022 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -174,6 +174,12 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { //// New local/remote Stream handling //////////////////////////////////////////////////////////////////////////// + bool _isPeerInitiatedStream(int streamId) { + bool isServerStreamId = streamId.isEven; + bool isLocalStream = isServerStreamId == isServer; + return !isLocalStream; + } + TransportStream newStream(List
headers, {bool endStream: false}) { return ensureNotTerminatedSync(() { var stream = newLocalStream(); @@ -264,6 +270,13 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { _setupOutgoingMessageHandling(stream); + // NOTE: We are not interested whether the streams were normally finished + // or abnormally terminated. Therefore we use 'catchError((_) {})'! + var streamDone = [streamQueueIn.done, streamQueueOut.done]; + Future.wait(streamDone).catchError((_) {}).whenComplete(() { + _cleanupClosedStream(stream); + }); + return stream; } @@ -395,7 +408,21 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { // If we initiated a close of the connection and the received frame belongs // to a stream id which is higher than the last peer-initiated stream we // processed, we'll ignore it. + // http/2 spec: + // After sending a GOAWAY frame, the sender can discard frames for + // streams initiated by the receiver with identifiers higher than the + // identified last stream. However, any frames that alter connection + // state cannot be completely ignored. For instance, HEADERS, + // PUSH_PROMISE, and CONTINUATION frames MUST be minimally processed to + // ensure the state maintained for header compression is consistent + // (see Section 4.3); similarly, DATA frames MUST be counted toward + // the connection flow-control window. Failure to process these + // frames can cause flow control or header compression state to become + // unsynchronized. + // TODO: It is not completely clear what "discard" means. I would assume we + // should send an [RstFrame] back with [ErrorCode.REFUSED_STREAM]. if (connectionState == ConnectionState.Finishing && + _isPeerInitiatedStream(frame.header.streamId) && frame.header.streamId > highestPeerInitiatedStream) { // Even if the frame will be ignored, we still need to process it in a // minimal way to ensure the connection window will be updated. @@ -417,13 +444,8 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { streamId >= nextStreamId : streamId > lastRemoteStreamId; return isIdleStream; } - bool isPeerInitiatedStream() { - bool isServerStreamId = frame.header.streamId.isEven; - bool isLocalStream = isServerStreamId == isServer; - return !isLocalStream; - } - if (isPeerInitiatedStream()) { + if (_isPeerInitiatedStream(frame.header.streamId)) { // Update highest stream id we received and processed (we update it // before processing, so if it was an error, the client will not // retry it). @@ -623,9 +645,6 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { throw new StateError( 'Invalid state transition. This should never happen.'); } - if (stream.state == StreamState.Closed) { - _cleanupClosedStream(stream); - } } //////////////////////////////////////////////////////////////////////////// @@ -633,13 +652,14 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { //////////////////////////////////////////////////////////////////////////// void _cleanupClosedStream(Http2StreamImpl stream) { - // FIXME: We can only do this once all the data has been flushed. - // Otherwise we won't propagate [WindowUpdateFrame]s to the stream, which - // then never writes the remaining data out. - + // NOTE: This function should only be called once + // * all incoming data has been delivered to the application + // * all outgoing data has been added to the connection queue. incomingQueue.removeStreamMessageQueue(stream.id); _openStreams.remove(stream.id); - + if (stream.state != StreamState.Terminated) { + _changeState(stream, StreamState.Terminated); + } onCheckForClose(); } @@ -661,8 +681,6 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { // NOTE: we're not adding an error here. stream.outgoingQueue.terminate(); - _openStreams.remove(stream.id); - onCheckForClose(); } @@ -714,6 +732,9 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { // If we initiated the stream and it became "open" or "closed" we need to // update the [_numberOfActiveStreams] counter. if (_didInitiateStream(stream)) { + // NOTE: We wait until the stream is completely done. + // (If we waited only until `StreamState.Closed` then we might still have + // the endStream header/data message buffered, but not yet sent out). switch (stream.state) { case StreamState.ReservedLocal: case StreamState.ReservedRemote: @@ -727,11 +748,11 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { case StreamState.Open: case StreamState.HalfClosedLocal: case StreamState.HalfClosedRemote: - if (to == StreamState.Closed || to == StreamState.Terminated) { + case StreamState.Closed: + if (to == StreamState.Terminated) { _numberOfActiveStreams--; } break; - case StreamState.Closed: case StreamState.Terminated: // There is nothing to do here. break; diff --git a/pkgs/http2/test/transport_test.dart b/pkgs/http2/test/transport_test.dart index c47088e2a4..726f243690 100644 --- a/pkgs/http2/test/transport_test.dart +++ b/pkgs/http2/test/transport_test.dart @@ -144,6 +144,29 @@ main() { await client.terminate(); await serverFuture; }, clientSettings: new ClientSettings(kDefaultStreamLimit, true)); + + transportTest('early-shutdown', + (ClientTransportConnection client, + ServerTransportConnection server) async { + Future serverFun() async { + await for (ServerTransportStream stream in server.incomingStreams) { + stream.sendHeaders([new Header.ascii('x', 'y')], endStream: true); + expect(await stream.incomingMessages.toList(), hasLength(1)); + } + await server.finish(); + } + + Future clientFun() async { + var headers = [new Header.ascii('a', 'b')]; + var stream = client.makeRequest(headers, endStream: true); + var finishFuture = client.finish(); + var messages = await stream.incomingMessages.toList(); + expect(messages, hasLength(1)); + await finishFuture; + } + + await Future.wait([serverFun(), clientFun()]); + }); }); } From 01a3051aa9d5e0729373bd778aa4208c7a2f71f7 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Wed, 2 Sep 2015 12:43:13 +0200 Subject: [PATCH 035/172] Make ClientConnection.isOpen and ServerTransportStream.canPush now if we run out of stream ids. R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/247207013 . --- pkgs/http2/lib/src/connection.dart | 3 + .../http2/lib/src/streams/stream_handler.dart | 20 +++++-- .../manual_test/out_of_stream_ids_test.dart | 59 +++++++++++++++++++ 3 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 pkgs/http2/manual_test/out_of_stream_ids_test.dart diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index 7b8923fc93..5de85e3c1c 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -377,6 +377,9 @@ class ClientConnection extends Connection implements ClientTransportConnection { 'used to make new streams.'); } TransportStream hStream = _streams.newStream(headers, endStream: endStream); + if (_streams.ranOutOfStreamIds) { + _finishing(active: true, message: 'Ran out of stream ids'); + } return hStream; } } diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index cd00ac9022..42c597eb4c 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -101,10 +101,10 @@ class Http2StreamImpl extends TransportStream /// /// It keeps track of open streams, their state, their queues, forwards /// messages from the connection level to stream level and vise versa. -// TODO: Respect SETTINGS_MAX_CONCURRENT_STREAMS // TODO: Handle stream/connection queue errors & forward to connection object. class StreamHandler extends Object with TerminatableMixin, ClosableMixin { static const int MAX_STREAM_ID = (1 << 31) - 1; + final FrameWriter _frameWriter; final ConnectionMessageQueueIn incomingQueue; final ConnectionMessageQueueOut outgoingQueue; @@ -126,6 +126,8 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { bool get isServer => nextStreamId.isEven; + bool get ranOutOfStreamIds => _ranOutOfStreamIds(); + StreamHandler._(this._frameWriter, this.incomingQueue, this.outgoingQueue, this._peerSettings, this._localSettings, this.nextStreamId, this.lastRemoteStreamId); @@ -195,7 +197,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { return ensureNotTerminatedSync(() { assert(_canCreateNewStream()); - if (MAX_STREAM_ID < (nextStreamId + 2)) { + if (MAX_STREAM_ID < nextStreamId) { throw new StateError( 'Cannot create new streams, since a wrap around would happen.'); } @@ -207,7 +209,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { TransportStream newRemoteStream(int remoteStreamId) { return ensureNotTerminatedSync(() { - assert (remoteStreamId < MAX_STREAM_ID); + assert (remoteStreamId <= MAX_STREAM_ID); // NOTE: We cannot enforce that a new stream id is 2 higher than the last // used stream id. Meaning there can be "holes" in the sense that stream // ids are not used: @@ -284,7 +286,8 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { bool openState = (stream.state == StreamState.Open || stream.state == StreamState.HalfClosedRemote); bool pushEnabled = this._peerSettings.enablePush; - return openState && pushEnabled && _canCreateNewStream(); + return openState && pushEnabled && _canCreateNewStream() && + !_ranOutOfStreamIds(); } TransportStream _push(Http2StreamImpl stream, List
requestHeaders) { @@ -302,6 +305,11 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { throw new StateError('Maximum number of streams reached.'); } + if (_ranOutOfStreamIds()) { + throw new StateError('There are no more stream ids left. Please use a ' + 'new connection.'); + } + Http2StreamImpl pushStream = newLocalStream(); // NOTE: Since there was no real request from the client, we simulate it @@ -708,6 +716,10 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { return limit == null || _numberOfActiveStreams < limit; } + bool _ranOutOfStreamIds() { + return nextStreamId > MAX_STREAM_ID; + } + void _changeState(Http2StreamImpl stream, StreamState to) { StreamState from = stream.state; diff --git a/pkgs/http2/manual_test/out_of_stream_ids_test.dart b/pkgs/http2/manual_test/out_of_stream_ids_test.dart new file mode 100644 index 0000000000..8de19af718 --- /dev/null +++ b/pkgs/http2/manual_test/out_of_stream_ids_test.dart @@ -0,0 +1,59 @@ +// 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. + +/// --------------------------------------------------------------------------- +/// In order to run this test one needs to change the following line in +/// ../lib/src/streams/stream_handler.dart +/// +/// - static const int MAX_STREAM_ID = (1 << 31) - 1; +/// + static const int MAX_STREAM_ID = (1 << 5) - 1; +/// +/// --------------------------------------------------------------------------- + +import 'dart:async'; + +import 'package:test/test.dart'; +import 'package:http2/transport.dart'; + +import '../test/transport_test.dart'; + +main() { + group('transport-test', () { + transportTest('client-runs-out-of-stream-ids', + (ClientTransportConnection client, + ServerTransportConnection server) async { + Future serverFun() async { + await for (ServerTransportStream stream in server.incomingStreams) { + stream.sendHeaders([new Header.ascii('x', 'y')], endStream: true); + expect(await stream.incomingMessages.toList(), hasLength(1)); + } + await server.finish(); + } + + Future clientFun() async { + var headers = [new Header.ascii('a', 'b')]; + + const kMaxStreamId = (1 << 5) - 1; + for (int i = 1; i <= kMaxStreamId; i += 2) { + var stream = client.makeRequest(headers, endStream: true); + var messages = await stream.incomingMessages.toList(); + expect(messages, hasLength(1)); + } + + expect(client.isOpen, false); + expect(() => client.makeRequest(headers), throws); + + await new Future.delayed(const Duration(seconds: 1)); + await client.finish(); + } + + var serverFuture = serverFun(); + var clientFuture = clientFun(); + + await serverFuture; + await clientFuture; + }); + }); +} + From 809f9fbf30296ab621e9364f952dff4aca3d440b Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Thu, 3 Sep 2015 16:52:11 +0200 Subject: [PATCH 036/172] Implement termination of streams after receiving a Goaway frame The remote end indicates the highest stream id (initiated by the receiver of the Goaway frame) which it processed. This means the receiver of the goaway frame knows that all streams above that "last processed stream id" will be ignored by the server and these requests can therefore be retried. Currently we report a normal StreamError with a special sentence, that could be made a special error in the future. R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/250127013 . --- pkgs/http2/lib/src/connection.dart | 75 ++++++++++++++----- .../http2/lib/src/streams/stream_handler.dart | 22 ++++-- pkgs/http2/test/client_tests.dart | 55 ++++++++++++++ 3 files changed, 127 insertions(+), 25 deletions(-) diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index 5de85e3c1c..d506e8126d 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -21,19 +21,44 @@ import 'sync_errors.dart'; import 'connection_preface.dart'; -enum ConnectionState { +class ConnectionState { /// The connection has been established, we're waiting for the settings frame /// of the remote end. - Initialized, + static const int Initialized = 1; /// The connection has been established and is fully operational. - Operational, + static const int Operational = 2; /// The connection is no longer accepting new streams or creating new streams. - Finishing, + static const int Finishing = 3; /// The connection has been terminated and cannot be used anymore. - Terminated, + static const int Terminated = 4; + + /// Whether we actively were finishing the connection. + static const int FinishingActive = 1; + + /// Whether we passively were finishing the connection. + static const int FinishingPassive = 2; + + int state = Initialized; + int finishingState = 0; + + ConnectionState(); + + bool get isInitialized => state == ConnectionState.Initialized; + + bool get isOperational => state == ConnectionState.Operational; + + bool get isFinishing => state == ConnectionState.Finishing; + + bool get isTerminated => state == ConnectionState.Terminated; + + bool get activeFinishing + => state == Finishing && (finishingState & FinishingActive) != 0; + + bool get passiveFinishing + => state == Finishing && (finishingState & FinishingPassive) != 0; } abstract class Connection { @@ -167,7 +192,7 @@ abstract class Connection { // NOTE: We're not waiting until initial settings have been exchanged // before we start using the connection (i.e. we don't wait for half a // round-trip-time). - _state = ConnectionState.Initialized; + _state = new ConnectionState(); } /// Pings the remote peer (can e.g. be used for measuring latency). @@ -221,13 +246,13 @@ abstract class Connection { void _handleFrameImpl(Frame frame) { // The first frame from the other side must be a [SettingsFrame], otherwise // we terminate the connection. - if (_state == ConnectionState.Initialized) { + if (_state.isInitialized) { if (frame is! SettingsFrame) { _terminate(ErrorCode.PROTOCOL_ERROR, message: 'Expected to first receive a settings frame.'); return; } - _state = ConnectionState.Operational; + _state.state = ConnectionState.Operational; } // Try to defragment [frame] if it is a Headers/PushPromise frame. @@ -254,7 +279,7 @@ abstract class Connection { } else if (frame is WindowUpdateFrame) { _connectionWindowHandler.processWindowUpdate(frame); } else if (frame is GoawayFrame) { - // TODO: What to do about [frame.lastStreamIdReceived] ? + _streams.processGoawayFrame(frame); _finishing(active: false); } else if (frame is UnknownFrame) { // We can safely ignore these. @@ -268,24 +293,34 @@ abstract class Connection { } void _finishing({bool active: true, String message}) { - // If this connection is already finishing or dead, we return. - if (_state == ConnectionState.Terminated || - _state == ConnectionState.Finishing) { + // If this connection is already dead, we return. + if (_state.isTerminated) return; + + // If this connection is already finishing, we make sure to store the + // passive bit, since this information is used by [StreamHandler]. + // + // Vice versa should not matter: If we started passively finishing, an + // active finish should be a NOP. + if (_state.isFinishing) { + if (!active) _state.finishingState |= ConnectionState.FinishingPassive; return; } - assert(_state == ConnectionState.Initialized || - _state == ConnectionState.Operational); - - _state = ConnectionState.Finishing; + assert(_state.isInitialized || _state.isOperational); // If we are actively finishing this connection, we'll send a // GoawayFrame otherwise we'll just propagate the message. if (active) { + _state.state = ConnectionState.Finishing; + _state.finishingState |= ConnectionState.FinishingActive; + _frameWriter.writeGoawayFrame( _streams.highestPeerInitiatedStream, ErrorCode.NO_ERROR, message != null ? UTF8.encode(message) : []); + } else { + _state.state = ConnectionState.Finishing; + _state.finishingState |= ConnectionState.FinishingPassive; } _streams.startClosing(); @@ -297,8 +332,8 @@ abstract class Connection { Future _terminate(int errorCode, {bool causedByTransportError: false, String message}) { // TODO: When do we complete here? - if (_state != ConnectionState.Terminated) { - _state = ConnectionState.Terminated; + if (_state.state != ConnectionState.Terminated) { + _state.state = ConnectionState.Terminated; var cancelFuture = new Future.sync(_frameReaderSubscription.cancel); if (!causedByTransportError) { @@ -367,11 +402,11 @@ class ClientConnection extends Connection implements ClientTransportConnection { _state != ConnectionState.Terminated; TransportStream makeRequest(List
headers, {bool endStream: false}) { - if (_state == ConnectionState.Finishing) { + if (_state.isFinishing) { throw new StateError( 'The http/2 connection is finishing and can therefore not be used to ' 'make new streams.'); - } else if (_state == ConnectionState.Terminated) { + } else if (_state.isTerminated) { throw new StateError( 'The http/2 connection is no longer active and can therefore not be ' 'used to make new streams.'); diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index 42c597eb4c..0f74b7534b 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -171,6 +171,18 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { }); } + void processGoawayFrame(GoawayFrame frame) { + var lastStreamId = frame.lastStreamId; + var streamIds = _openStreams.keys + .where((id) => id > lastStreamId && !_isPeerInitiatedStream(id)) + .toList(); + for (int id in streamIds) { + var exception = new StreamException(id, + 'Remote end was telling us to stop. This stream was not processed ' + 'and can therefore be retried (on a new connection).'); + _closeStreamIdAbnormally(id, exception, propagateException: true); + } + } //////////////////////////////////////////////////////////////////////////// //// New local/remote Stream handling @@ -427,9 +439,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { // the connection flow-control window. Failure to process these // frames can cause flow control or header compression state to become // unsynchronized. - // TODO: It is not completely clear what "discard" means. I would assume we - // should send an [RstFrame] back with [ErrorCode.REFUSED_STREAM]. - if (connectionState == ConnectionState.Finishing && + if (connectionState.activeFinishing && _isPeerInitiatedStream(frame.header.streamId) && frame.header.streamId > highestPeerInitiatedStream) { // Even if the frame will be ignored, we still need to process it in a @@ -671,10 +681,12 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { onCheckForClose(); } - void _closeStreamIdAbnormally(int streamId, Exception exception) { + void _closeStreamIdAbnormally(int streamId, Exception exception, + {bool propagateException: false}) { Http2StreamImpl stream = _openStreams[streamId]; if (stream != null) { - _closeStreamAbnormally(stream, exception); + _closeStreamAbnormally( + stream, exception, propagateException: propagateException); } } diff --git a/pkgs/http2/test/client_tests.dart b/pkgs/http2/test/client_tests.dart index 32108757d3..a35a72935d 100644 --- a/pkgs/http2/test/client_tests.dart +++ b/pkgs/http2/test/client_tests.dart @@ -408,6 +408,61 @@ main() { await Future.wait([serverFun(), clientFun()]); }); + + clientTest('goaway-terminates-nonprocessed-streams', + (ClientTransportConnection client, + FrameWriter serverWriter, + StreamIterator serverReader, + Future nextFrame()) async { + + var settingsDone = new Completer(); + + Future serverFun() async { + var decoder = new HPackDecoder(); + + serverWriter.writeSettingsFrame([]); + expect(await nextFrame() is SettingsFrame, true); + serverWriter.writeSettingsAckFrame(); + expect(await nextFrame() is SettingsFrame, true); + + settingsDone.complete(); + + // Make sure we got the new stream. + var frame = await nextFrame(); + expect(frame.hasEndStreamFlag, false); + var decodedHeaders = decoder.decode(frame.headerBlockFragment); + expect(decodedHeaders, hasLength(1)); + expect(decodedHeaders[0], isHeader('a', 'b')); + + // Send the GoawayFrame. + serverWriter.writeGoawayFrame( + 0, ErrorCode.NO_ERROR, []); + + // Since there are no open streams left, the other end should just + // close the connection. + expect(await serverReader.moveNext(), false); + } + + Future clientFun() async { + await settingsDone.future; + + // Make a new stream and terminate it. + var stream = client.makeRequest( + [new Header.ascii('a', 'b')], endStream: false); + + // Make sure we don't get messages/pushes on the terminated stream. + stream.incomingMessages.toList().catchError(expectAsync((e) { + expect('$e', contains('This stream was not processed and can ' + 'therefore be retried.')); + })); + expect(await stream.peerPushes.toList(), isEmpty); + + // Try to gracefully finish the connection. + await client.finish(); + } + + await Future.wait([serverFun(), clientFun()]); + }); }); }); } From 2eeed529ba8b8774f4a59e90a234d083d16a0b60 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Thu, 3 Sep 2015 16:57:22 +0200 Subject: [PATCH 037/172] Add first transport test for asserting flowcontrol window decrease pauses writer R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/252907013 . --- .../http2/lib/src/streams/stream_handler.dart | 2 + pkgs/http2/test/transport_test.dart | 101 ++++++++++++++++++ 2 files changed, 103 insertions(+) diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index 0f74b7534b..57582f8686 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -542,6 +542,8 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { _handlePushPromiseFrame(stream, frame); } else if (frame is WindowUpdateFrame) { _handleWindowUpdate(stream, frame); + } else if (frame is RstStreamFrame) { + // TODO } else { throw new ProtocolException( 'Unsupported frame type ${frame.runtimeType}.'); diff --git a/pkgs/http2/test/transport_test.dart b/pkgs/http2/test/transport_test.dart index 726f243690..0fa2e55d83 100644 --- a/pkgs/http2/test/transport_test.dart +++ b/pkgs/http2/test/transport_test.dart @@ -3,9 +3,11 @@ // BSD-style license that can be found in the LICENSE FILE. import 'dart:async'; +import 'dart:typed_data'; import 'package:test/test.dart'; import 'package:http2/transport.dart'; +import 'package:http2/src/flowcontrol/window.dart'; import 'src/hpack/hpack_test.dart' show isHeader; @@ -167,6 +169,105 @@ main() { await Future.wait([serverFun(), clientFun()]); }); + + group('flow-control', () { + const int kChunkSize = 1024; + const int kNumberOfMessages = 1000; + final int kStreamWindowSize = new Window().size; + final headers = [new Header.ascii('a', 'b')]; + + transportTest('fast-sender-receiver-paused', + (ClientTransportConnection client, + ServerTransportConnection server) async { + expect(kStreamWindowSize, lessThan(kChunkSize * kNumberOfMessages)); + + int serverSentBytes = 0; + Completer flowcontrolWindowFull = new Completer(); + + Future serverFun() async { + await for (ServerTransportStream stream in server.incomingStreams) { + stream.sendHeaders([new Header.ascii('x', 'y')]); + + int messageNr = 0; + StreamController controller; + addData() { + if (!controller.isPaused) { + if (messageNr < kNumberOfMessages) { + var messageBytes = new Uint8List(kChunkSize); + for (int j = 0; j < messageBytes.length; j++) { + messageBytes[j] = (messageNr + j) % 256; + } + controller.add(new DataStreamMessage(messageBytes)); + + messageNr++; + serverSentBytes += messageBytes.length; + + Timer.run(addData); + } else { + if (!controller.isClosed) controller.close(); + } + } + } + + controller = new StreamController( + onListen: () { + addData(); + }, + onPause: expectAsync(() { + // Assert that we're now at the place (since the granularity + // of adding is [kChunkSize], it could be that we added + // [kChunkSize - 1] bytes more than allowed, before getting + // the pause event). + expect((serverSentBytes - kChunkSize + 1) < kStreamWindowSize, + true); + flowcontrolWindowFull.complete(); + }), + onResume: () { + addData(); + }, + onCancel: () {}); + + await stream.outgoingMessages.addStream(controller.stream); + await stream.outgoingMessages.close(); + await stream.incomingMessages.toList(); + } + await server.finish(); + } + + Future clientFun() async { + var stream = client.makeRequest(headers, endStream: true); + + bool gotHeadersFrame = false; + int byteNr = 0; + + var sub = stream.incomingMessages.listen((message) { + if (!gotHeadersFrame) { + expect(message is HeadersStreamMessage, true); + gotHeadersFrame = true; + } else { + expect(message is DataStreamMessage, true); + DataStreamMessage dataMessage = message; + + // We're just testing the first byte, to make the test faster. + expect(dataMessage.bytes[0], + ((byteNr ~/ kChunkSize) + (byteNr % kChunkSize)) % 256); + + byteNr += dataMessage.bytes.length; + } + }); + + // We pause immediately, making the server fill the stream flowcontrol + // window. + sub.pause(); + + await flowcontrolWindowFull.future; + sub.resume(); + await client.finish(); + } + + await Future.wait([serverFun(), clientFun()]); + }); + }); }); } From 117fa660bb86e366f7ecd22463333c827d11ef74 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Thu, 3 Sep 2015 17:13:44 +0200 Subject: [PATCH 038/172] Add support for SETTINGS_INITIAL_WINDOW_SIZE setting changes. This CL also makes {Client,Server,}Settings accept named arguments instead of positional ones. R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/253857013 . --- pkgs/http2/lib/client.dart | 3 +- pkgs/http2/lib/src/connection.dart | 73 +++++++++++-------- pkgs/http2/lib/src/settings/settings.dart | 3 +- pkgs/http2/lib/transport.dart | 31 +++++--- .../test/src/streams/simple_push_test.dart | 2 +- pkgs/http2/test/transport_test.dart | 32 ++++++-- 6 files changed, 92 insertions(+), 52 deletions(-) diff --git a/pkgs/http2/lib/client.dart b/pkgs/http2/lib/client.dart index a1ed835f7d..a9d32e0ebe 100644 --- a/pkgs/http2/lib/client.dart +++ b/pkgs/http2/lib/client.dart @@ -133,7 +133,8 @@ Future connect(Uri uri, bool useSSL = uri.scheme == 'https'; var settings = new ClientSettings( - maxConcurrentPushes, allowServerPushes); + concurrentStreamLimit: maxConcurrentPushes, + allowServerPushes: allowServerPushes); if (useSSL) { SecureSocket socket = await SecureSocket.connect( uri.host, uri.port, supportedProtocols: Http2AlpnProtocols); diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index d506e8126d..60d8ab9557 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -7,7 +7,7 @@ library http2.src.conn; import 'dart:async'; import 'dart:convert'; -import '../transport.dart' hide Settings; +import '../transport.dart'; import 'flowcontrol/connection_queues.dart'; import 'flowcontrol/window.dart'; import 'flowcontrol/window_handler.dart'; @@ -114,7 +114,7 @@ abstract class Connection { Connection(Stream> incoming, StreamSink> outgoing, - List settings, + Settings settings, {this.isClientConnection: true}) { _setupConnection(incoming, outgoing, settings); } @@ -123,7 +123,7 @@ abstract class Connection { /// peer. void _setupConnection(Stream> incoming, StreamSink> outgoing, - List settings) { + Settings settingsObject) { // Setup frame reading. var incomingFrames = new FrameReader( incoming, acknowledgedSettings).startDecoding(); @@ -151,6 +151,8 @@ abstract class Connection { acknowledgedSettings, peerSettings); _pingHandler = new PingHandler(_frameWriter); + var settings = _decodeSettings(settingsObject); + // Do the initial settings handshake (possibly with pushes disabled). _settingsHandler.changeSettings(settings).catchError((error) { // TODO: The [error] can contain sensitive information we now expose via @@ -160,8 +162,8 @@ abstract class Connection { message: 'Failed to set initial settings (error: $error).'); }); - _settingsHandler.onInitialWindowSizeChange.listen((size) { - // TODO: Apply change to [StreamHandler] + _settingsHandler.onInitialWindowSizeChange.listen((int difference) { + _streams.processInitialWindowSizeSettingChange(difference); }); @@ -195,6 +197,36 @@ abstract class Connection { _state = new ConnectionState(); } + List _decodeSettings(Settings settings) { + var settingsList = []; + + // By default a endpoitn can make an unlimited number of concurrent streams. + if (settings.concurrentStreamLimit != null) { + settingsList.add(new Setting(Setting.SETTINGS_MAX_CONCURRENT_STREAMS, + settings.concurrentStreamLimit)); + } + + // By default the stream level flow control window is 64 KiB. + if (settings.streamWindowSize != null) { + settingsList.add(new Setting(Setting.SETTINGS_INITIAL_WINDOW_SIZE, + settings.streamWindowSize)); + } + + if (settings is ClientSettings) { + // By default the server is allowed to do server pushes. + if (settings.allowServerPushes == null || + settings.allowServerPushes == false) { + settingsList.add(new Setting(Setting.SETTINGS_ENABLE_PUSH, 0)); + } + } else if (settings is ServerSettings) { + // No special server settings at the moment. + } else { + assert(false); + } + + return settingsList; + } + /// Pings the remote peer (can e.g. be used for measuring latency). Future ping() { return _pingHandler.ping().catchError((e, s) { @@ -372,7 +404,7 @@ abstract class Connection { class ClientConnection extends Connection implements ClientTransportConnection { ClientConnection._(Stream> incoming, StreamSink> outgoing, - List settings) + Settings settings) : super(incoming, outgoing, settings, @@ -381,21 +413,8 @@ class ClientConnection extends Connection implements ClientTransportConnection { factory ClientConnection(Stream> incoming, StreamSink> outgoing, ClientSettings clientSettings) { - var settings = []; - - // By default the server is allowed to do server pushes. - if (!clientSettings.allowServerPushes) { - settings.add(new Setting(Setting.SETTINGS_ENABLE_PUSH, 0)); - } - // By default the client is allowed to make/push an unlimited number of - // concurrent streams. - if (clientSettings.concurrentStreamLimit != null) { - settings.add(new Setting(Setting.SETTINGS_MAX_CONCURRENT_STREAMS, - clientSettings.concurrentStreamLimit)); - } - outgoing.add(CONNECTION_PREFACE); - return new ClientConnection._(incoming, outgoing, settings); + return new ClientConnection._(incoming, outgoing, clientSettings); } bool get isOpen => _state != ConnectionState.Finishing && @@ -422,25 +441,17 @@ class ClientConnection extends Connection implements ClientTransportConnection { class ServerConnection extends Connection implements ServerTransportConnection { ServerConnection._(Stream> incoming, StreamSink> outgoing, - List settings) + Settings settings) : super( incoming, outgoing, settings, isClientConnection: false); factory ServerConnection(Stream> incoming, StreamSink> outgoing, ServerSettings serverSettings) { - var settings = []; - - // By default the client is allowed to make an unlimited number of - // concurrent streams. - if (serverSettings.concurrentStreamLimit != null) { - settings.add(new Setting(Setting.SETTINGS_MAX_CONCURRENT_STREAMS, - serverSettings.concurrentStreamLimit)); - } - var frameBytes = readConnectionPreface(incoming); - return new ServerConnection._(frameBytes, outgoing, settings); + return new ServerConnection._(frameBytes, outgoing, serverSettings); } Stream get incomingStreams => _streams.incomingStreams; } + diff --git a/pkgs/http2/lib/src/settings/settings.dart b/pkgs/http2/lib/src/settings/settings.dart index a695a2a8a7..398226a010 100644 --- a/pkgs/http2/lib/src/settings/settings.dart +++ b/pkgs/http2/lib/src/settings/settings.dart @@ -210,7 +210,8 @@ class SettingsHandler extends Object with TerminatableMixin { case Setting.SETTINGS_INITIAL_WINDOW_SIZE: if (setting.value < (1 << 31)) { - _onInitialWindowSizeChangeController.add(setting.value); + int difference = setting.value - base.initialWindowSize; + _onInitialWindowSizeChangeController.add(difference); base.initialWindowSize = setting.value; } else { throw new FlowControlException('Invalid initial window size.'); diff --git a/pkgs/http2/lib/transport.dart b/pkgs/http2/lib/transport.dart index aca553c919..58f95d2d5e 100644 --- a/pkgs/http2/lib/transport.dart +++ b/pkgs/http2/lib/transport.dart @@ -14,25 +14,35 @@ export 'src/hpack/hpack.dart' show Header; /// Settings for a [TransportConnection]. abstract class Settings { - /// The maximum number of concurrent streams the remote end can open. + /// The maximum number of concurrent streams the remote end can open + /// (defaults to being unlimited). final int concurrentStreamLimit; - const Settings(this.concurrentStreamLimit); + /// The default stream window size the remote peer can use when creating new + /// streams (defaults to 65535 bytes). + final int streamWindowSize; + + const Settings({this.concurrentStreamLimit, this.streamWindowSize}); } /// Settings for a [TransportConnection] a server can make. class ServerSettings extends Settings { - const ServerSettings(int concurrentStreamLimit) - : super(concurrentStreamLimit); + const ServerSettings({int concurrentStreamLimit, + int streamWindowSize}) + : super(concurrentStreamLimit: concurrentStreamLimit, + streamWindowSize: streamWindowSize); } /// Settings for a [TransportConnection] a client can make. class ClientSettings extends Settings { - /// Whether the client allows pushes from the server. + /// Whether the client allows pushes from the server (defaults to false). final bool allowServerPushes; - const ClientSettings(int concurrentStreamLimit, this.allowServerPushes) - : super(concurrentStreamLimit); + const ClientSettings({int concurrentStreamLimit, + int streamWindowSize, + this.allowServerPushes: false}) + : super(concurrentStreamLimit: concurrentStreamLimit, + streamWindowSize: streamWindowSize); } /// Represents a HTTP/2 connection. @@ -59,7 +69,7 @@ abstract class ClientTransportConnection extends TransportConnection { Stream> incoming, StreamSink> outgoing, {ClientSettings settings}) { - if (settings == null) settings = const ClientSettings(null, false); + if (settings == null) settings = const ClientSettings(); return new ClientConnection(incoming, outgoing, settings); } @@ -82,8 +92,9 @@ abstract class ServerTransportConnection extends TransportConnection { factory ServerTransportConnection.viaStreams( Stream> incoming, StreamSink> outgoing, - {ServerSettings settings: const ServerSettings(1000)}) { - if (settings == null) settings = const ServerSettings(null); + {ServerSettings settings: const ServerSettings( + concurrentStreamLimit: 1000)}) { + if (settings == null) settings = const ServerSettings(); return new ServerConnection(incoming, outgoing, settings); } diff --git a/pkgs/http2/test/src/streams/simple_push_test.dart b/pkgs/http2/test/src/streams/simple_push_test.dart index 0d29ce905a..d75ec61f9b 100644 --- a/pkgs/http2/test/src/streams/simple_push_test.dart +++ b/pkgs/http2/test/src/streams/simple_push_test.dart @@ -88,7 +88,7 @@ main() { String msg = await readData(iterator); expect(msg, 'pushing "hello world" :)'); })); - }, settings: new ClientSettings(null, true)); + }, settings: new ClientSettings(allowServerPushes: true)); }); }); } diff --git a/pkgs/http2/test/transport_test.dart b/pkgs/http2/test/transport_test.dart index 0fa2e55d83..63244df32a 100644 --- a/pkgs/http2/test/transport_test.dart +++ b/pkgs/http2/test/transport_test.dart @@ -145,7 +145,9 @@ main() { await clientFun(); await client.terminate(); await serverFuture; - }, clientSettings: new ClientSettings(kDefaultStreamLimit, true)); + }, clientSettings: new ClientSettings( + concurrentStreamLimit: kDefaultStreamLimit, + allowServerPushes: true)); transportTest('early-shutdown', (ClientTransportConnection client, @@ -173,13 +175,15 @@ main() { group('flow-control', () { const int kChunkSize = 1024; const int kNumberOfMessages = 1000; - final int kStreamWindowSize = new Window().size; final headers = [new Header.ascii('a', 'b')]; - transportTest('fast-sender-receiver-paused', - (ClientTransportConnection client, - ServerTransportConnection server) async { - expect(kStreamWindowSize, lessThan(kChunkSize * kNumberOfMessages)); + + Future testWindowSize(ClientTransportConnection client, + ServerTransportConnection server, + int expectedStreamFlowcontrolWindow) async { + + expect(expectedStreamFlowcontrolWindow, + lessThan(kChunkSize * kNumberOfMessages)); int serverSentBytes = 0; Completer flowcontrolWindowFull = new Completer(); @@ -218,8 +222,8 @@ main() { // of adding is [kChunkSize], it could be that we added // [kChunkSize - 1] bytes more than allowed, before getting // the pause event). - expect((serverSentBytes - kChunkSize + 1) < kStreamWindowSize, - true); + expect((serverSentBytes - kChunkSize + 1), + lessThan(expectedStreamFlowcontrolWindow)); flowcontrolWindowFull.complete(); }), onResume: () { @@ -266,7 +270,19 @@ main() { } await Future.wait([serverFun(), clientFun()]); + } + + transportTest('fast-sender-receiver-paused--default-window-size', + (ClientTransportConnection client, + ServerTransportConnection server) async { + await testWindowSize(client, server, new Window().size); }); + + transportTest('fast-sender-receiver-paused--10kb-window-size', + (ClientTransportConnection client, + ServerTransportConnection server) async { + await testWindowSize(client, server, 8096); + }, clientSettings: new ClientSettings(streamWindowSize: 8096)); }); }); } From 91ae35af467a5a1738d93dca27f7fdf1e236b5d6 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Fri, 4 Sep 2015 10:20:10 +0200 Subject: [PATCH 039/172] Handling of malicious peer which sends more data than flow control window allows R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/253887013 . --- pkgs/http2/lib/src/connection.dart | 19 +++--- .../src/flowcontrol/connection_queues.dart | 10 +++- .../lib/src/flowcontrol/window_handler.dart | 24 +++++++- .../{client_tests.dart => client_test.dart} | 59 ++++++++++++++++++- .../{server_tests.dart => server_test.dart} | 0 .../flowcontrol/connection_queues_test.dart | 4 +- 6 files changed, 102 insertions(+), 14 deletions(-) rename pkgs/http2/test/{client_tests.dart => client_test.dart} (89%) rename pkgs/http2/test/{server_tests.dart => server_test.dart} (100%) diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index 60d8ab9557..b542ffc88f 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -128,7 +128,9 @@ abstract class Connection { var incomingFrames = new FrameReader( incoming, acknowledgedSettings).startDecoding(); _frameReaderSubscription = incomingFrames.listen( - _handleFrame, + (Frame frame) { + _catchProtocolErrors(() => _handleFrameImpl(frame)); + }, onError: (stack, error) { _terminate(ErrorCode.CONNECT_ERROR, causedByTransportError: true); }, onDone: () { @@ -163,7 +165,9 @@ abstract class Connection { }); _settingsHandler.onInitialWindowSizeChange.listen((int difference) { - _streams.processInitialWindowSizeSettingChange(difference); + _catchProtocolErrors(() { + _streams.processInitialWindowSizeSettingChange(difference); + }); }); @@ -179,7 +183,7 @@ abstract class Connection { _outgoingQueue = new ConnectionMessageQueueOut( _connectionWindowHandler, _frameWriter); _incomingQueue = new ConnectionMessageQueueIn( - connectionWindowUpdater); + connectionWindowUpdater, _catchProtocolErrors); if (isClientConnection) { _streams = new StreamHandler.client( @@ -253,10 +257,10 @@ abstract class Connection { return _terminate(ErrorCode.NO_ERROR); } - /// Handles an incoming [Frame] from the underlying [FrameReader]. - void _handleFrame(Frame frame) { + /// Invokes the passed in closure and catches any exceptions. + void _catchProtocolErrors(void fn()) { try { - _handleFrameImpl(frame); + fn(); } on ProtocolException catch (error) { _terminate(ErrorCode.PROTOCOL_ERROR, message: '$error'); } on FlowControlException catch (error) { @@ -417,8 +421,7 @@ class ClientConnection extends Connection implements ClientTransportConnection { return new ClientConnection._(incoming, outgoing, clientSettings); } - bool get isOpen => _state != ConnectionState.Finishing && - _state != ConnectionState.Terminated; + bool get isOpen => !_state.isFinishing && !_state.isTerminated; TransportStream makeRequest(List
headers, {bool endStream: false}) { if (_state.isFinishing) { diff --git a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart index c84e8bf506..65deddff75 100644 --- a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart @@ -163,6 +163,9 @@ class ConnectionMessageQueueIn extends Object /// control window. final IncomingWindowHandler _windowUpdateHandler; + /// Catches any protocol errors and acts upon them. + final Function _catchProtocolErrors; + /// A mapping from stream-id to the corresponding stream-specific /// [StreamMessageQueueIn]. final Map _stream2messageQueue = {}; @@ -175,7 +178,8 @@ class ConnectionMessageQueueIn extends Object /// to the stream-specific queue. (for debugging purposes) int _count = 0; - ConnectionMessageQueueIn(this._windowUpdateHandler); + ConnectionMessageQueueIn(this._windowUpdateHandler, + this._catchProtocolErrors); void onTerminated(error) { // NOTE: The higher level will be shutdown first, so all streams @@ -211,7 +215,9 @@ class ConnectionMessageQueueIn extends Object _stream2messageQueue[streamId] = mq; mq.bufferIndicator.bufferEmptyEvents.listen((_) { - _tryDispatch(streamId, mq, pendingMessages); + _catchProtocolErrors(() { + _tryDispatch(streamId, mq, pendingMessages); + }); }); } diff --git a/pkgs/http2/lib/src/flowcontrol/window_handler.dart b/pkgs/http2/lib/src/flowcontrol/window_handler.dart index 4187fa7ec4..bd0330ba1d 100644 --- a/pkgs/http2/lib/src/flowcontrol/window_handler.dart +++ b/pkgs/http2/lib/src/flowcontrol/window_handler.dart @@ -125,7 +125,29 @@ class IncomingWindowHandler { // If this turns negative, it means the remote end send us more data // then we announced we can handle (i.e. the remote window size must be // negative). - // FIXME/TODO: We should in such a case terminate the connection. + // + // NOTE: [_localWindow.size] tracks the amount of data we advertised that we + // can handle. The value can change in three situations: + // + // a) We received data from the remote end (we can handle now less data) + // => This is handled by [gotData]. + // + // b) We processed data from the remote end (we can handle now more data) + // => This is handled by [dataProcessed]. + // + // c) We increase/decrease the initial steram window size after the + // stream was created (newer streams will start with the changed + // initial stream window size). + // => This is not an issue, because we don't support changing the + // initial window size later on -- only during the initial + // settings exchange. Since streams (and therefore instances + // of [IncomingWindowHandler]) are only created after sending out + // our initial settings. + // + if (_localWindow.size < 0) { + throw new FlowControlException( + 'Connection level flow control window became negative.'); + } } /// Tell the peer we received [numberOfBytes] bytes. It will increase it's diff --git a/pkgs/http2/test/client_tests.dart b/pkgs/http2/test/client_test.dart similarity index 89% rename from pkgs/http2/test/client_tests.dart rename to pkgs/http2/test/client_test.dart index a35a72935d..e95b289a53 100644 --- a/pkgs/http2/test/client_tests.dart +++ b/pkgs/http2/test/client_test.dart @@ -6,10 +6,12 @@ library http2.test.client_tests; import 'dart:async'; import 'dart:convert'; +import 'dart:typed_data'; import 'package:test/test.dart'; import 'package:http2/transport.dart'; +import 'package:http2/src/flowcontrol/window.dart'; import 'package:http2/src/connection_preface.dart'; import 'package:http2/src/frames/frames.dart'; import 'package:http2/src/hpack/hpack.dart'; @@ -348,6 +350,61 @@ main() { await Future.wait([serverFun(), clientFun()]); }); + + clientTest('client-reports-flowcontrol-error-on-negative-window', + (ClientTransportConnection client, + FrameWriter serverWriter, + StreamIterator serverReader, + Future nextFrame()) async { + + var handshakeCompleter = new Completer(); + + Future serverFun() async { + serverWriter.writeSettingsFrame([]); + expect(await nextFrame() is SettingsFrame, true); + serverWriter.writeSettingsAckFrame(); + expect(await nextFrame() is SettingsFrame, true); + + handshakeCompleter.complete(); + + HeadersFrame headers = await nextFrame(); + int streamId = headers.header.streamId; + + // Write more than [kFlowControlWindowSize] bytes. + final int kFlowControlWindowSize = new Window().size; + int sentBytes = 0; + final bytes = new Uint8List(1024); + while (sentBytes <= kFlowControlWindowSize) { + serverWriter.writeDataFrame(streamId, bytes); + sentBytes += bytes.length; + } + + // Read the resulting [GoawayFrame] and assert the error message + // describes that the flow control window became negative. + GoawayFrame frame = await nextFrame(); + expect(ASCII.decode(frame.debugData), + contains('Connection level flow control window became ' + 'negative.')); + expect(await serverReader.moveNext(), false); + await serverWriter.close(); + } + + Future clientFun() async { + await handshakeCompleter.future; + + var stream = client.makeRequest([new Header.ascii('a', 'b')]); + var sub = stream.incomingMessages.listen( + expectAsync((StreamMessage msg) {}, count: 0), + onError: expectAsync((error) {})); + sub.pause(); + await new Future.delayed(const Duration(milliseconds: 40)); + sub.resume(); + + await client.finish(); + } + + await Future.wait([serverFun(), clientFun()]); + }); }); group('client-errors', () { @@ -453,7 +510,7 @@ main() { // Make sure we don't get messages/pushes on the terminated stream. stream.incomingMessages.toList().catchError(expectAsync((e) { expect('$e', contains('This stream was not processed and can ' - 'therefore be retried.')); + 'therefore be retried')); })); expect(await stream.peerPushes.toList(), isEmpty); diff --git a/pkgs/http2/test/server_tests.dart b/pkgs/http2/test/server_test.dart similarity index 100% rename from pkgs/http2/test/server_tests.dart rename to pkgs/http2/test/server_test.dart diff --git a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart index b0e03f2d10..ab58c79959 100644 --- a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart @@ -114,7 +114,7 @@ main() { var windowMock = new MockIncomingWindowHandler(); - var queue = new ConnectionMessageQueueIn(windowMock); + var queue = new ConnectionMessageQueueIn(windowMock, (f) => f()); expect(queue.pendingMessages, 0); var streamQueueMock = new MockStreamMessageQueueIn(); @@ -154,7 +154,7 @@ main() { final bytes = [1, 2, 3]; var windowMock = new MockIncomingWindowHandler(); - var queue = new ConnectionMessageQueueIn(windowMock); + var queue = new ConnectionMessageQueueIn(windowMock, (f) => f()); // Insert a [DataFrame] and let it be buffered. var header = new FrameHeader(0, 0, 0, STREAM_ID); From a20504ba0a6c933d9dc54cf1a6b70016196763d2 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Mon, 7 Sep 2015 10:02:11 +0200 Subject: [PATCH 040/172] Implement handling of RstStreamFrame R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/249407013 . --- .../src/flowcontrol/connection_queues.dart | 3 -- .../http2/lib/src/streams/stream_handler.dart | 12 ++++- pkgs/http2/test/transport_test.dart | 53 +++++++++++++++++++ 3 files changed, 63 insertions(+), 5 deletions(-) diff --git a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart index 65deddff75..41b56c5930 100644 --- a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart @@ -303,9 +303,6 @@ class ConnectionMessageQueueIn extends Object } mq.enqueueMessage(message); if (message.endStream) { - // FIXME: This should be turned into a check and we should - // raise a protocol error if we ever get a message on a stream - // which has been closed. assert (pendingMessages.isEmpty); _stream2messageQueue.remove(streamId); diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index 57582f8686..3cdec55bd6 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -543,7 +543,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { } else if (frame is WindowUpdateFrame) { _handleWindowUpdate(stream, frame); } else if (frame is RstStreamFrame) { - // TODO + _handleRstFrame(stream, frame); } else { throw new ProtocolException( 'Unsupported frame type ${frame.runtimeType}.'); @@ -597,6 +597,12 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { stream.windowHandler.processWindowUpdate(frame); } + void _handleRstFrame(Http2StreamImpl stream, RstStreamFrame frame) { + var exception = new StreamException( + stream.id, 'Stream was terminated by peer.'); + _closeStreamAbnormally(stream, exception, propagateException: true); + } + void _handleEndOfStreamRemote(Http2StreamImpl stream) { if (stream.state == StreamState.Open) { _changeState(stream, StreamState.HalfClosedRemote); @@ -696,7 +702,9 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { {bool propagateException: false}) { incomingQueue.removeStreamMessageQueue(stream.id); - _changeState(stream, StreamState.Terminated); + if (stream.state != StreamState.Terminated) { + _changeState(stream, StreamState.Terminated); + } stream.incomingQueue.terminate(propagateException ? exception : null); stream._outgoingCSubscription.cancel(); diff --git a/pkgs/http2/test/transport_test.dart b/pkgs/http2/test/transport_test.dart index 63244df32a..970a99a3fe 100644 --- a/pkgs/http2/test/transport_test.dart +++ b/pkgs/http2/test/transport_test.dart @@ -172,6 +172,59 @@ main() { await Future.wait([serverFun(), clientFun()]); }); + transportTest('client-terminates-stream', + (ClientTransportConnection client, + ServerTransportConnection server) async { + + var readyForError = new Completer(); + + Future serverFun() async { + await for (ServerTransportStream stream in server.incomingStreams) { + stream.sendHeaders([new Header.ascii('x', 'y')], endStream: true); + stream.incomingMessages.listen(expectAsync((msg) { + expect(msg is HeadersStreamMessage, true); + readyForError.complete(); + }), onError: expectAsync((error) { + expect('$error', contains('Stream was terminated by peer')); + })); + } + await server.finish(); + } + + Future clientFun() async { + var headers = [new Header.ascii('a', 'b')]; + var stream = client.makeRequest(headers, endStream: false); + await readyForError.future; + stream.terminate(); + await client.finish(); + } + + await Future.wait([serverFun(), clientFun()]); + }); + + transportTest('server-terminates-stream', + (ClientTransportConnection client, + ServerTransportConnection server) async { + + Future serverFun() async { + await for (ServerTransportStream stream in server.incomingStreams) { + stream.terminate(); + } + await server.finish(); + } + + Future clientFun() async { + var headers = [new Header.ascii('a', 'b')]; + var stream = client.makeRequest(headers, endStream: true); + await stream.incomingMessages.toList().catchError(expectAsync((error) { + expect('$error', contains('Stream was terminated by peer')); + })); + await client.finish(); + } + + await Future.wait([serverFun(), clientFun()]); + }); + group('flow-control', () { const int kChunkSize = 1024; const int kNumberOfMessages = 1000; From 14d38cdf7d251301c6e423d818164d2eaa23ff4a Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Mon, 7 Sep 2015 10:02:53 +0200 Subject: [PATCH 041/172] Move package:http2/{client,debug}.dart to package:http2/testing BUG= R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/251047013 . --- pkgs/http2/experimental/server.dart | 2 +- pkgs/http2/lib/{ => testing}/client.dart | 0 pkgs/http2/lib/{ => testing}/debug.dart | 0 pkgs/http2/pubspec.yaml | 4 ++-- pkgs/http2/test/client_websites_test.dart | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) rename pkgs/http2/lib/{ => testing}/client.dart (100%) rename pkgs/http2/lib/{ => testing}/debug.dart (100%) diff --git a/pkgs/http2/experimental/server.dart b/pkgs/http2/experimental/server.dart index d319bbf5ed..7fb8b77db8 100644 --- a/pkgs/http2/experimental/server.dart +++ b/pkgs/http2/experimental/server.dart @@ -8,7 +8,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; -import 'package:http2/debug.dart' hide print; +import 'package:http2/src/debug.dart' hide print; import 'package:http2/transport.dart'; const bool DEBUGGING = false; diff --git a/pkgs/http2/lib/client.dart b/pkgs/http2/lib/testing/client.dart similarity index 100% rename from pkgs/http2/lib/client.dart rename to pkgs/http2/lib/testing/client.dart diff --git a/pkgs/http2/lib/debug.dart b/pkgs/http2/lib/testing/debug.dart similarity index 100% rename from pkgs/http2/lib/debug.dart rename to pkgs/http2/lib/testing/debug.dart diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index 8f4d4d3dc8..8fdab25996 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -1,11 +1,11 @@ name: http2 -version: 0.0.1 +version: 0.1.0 description: A HTTP/2 implementation in dart. author: Dart Team homepage: https://github.com/dart-lang/http2 environment: - sdk: '>=1.13.0-edge.1015c7f4ffb04c8a1ce541d2101893b19d0e44fd <2.0.0' + sdk: '>=1.13.0-dev.1 <2.0.0' dev_dependencies: test: '>=0.12.0 <0.13.0' diff --git a/pkgs/http2/test/client_websites_test.dart b/pkgs/http2/test/client_websites_test.dart index a7c91b61cd..c8ca6d616f 100644 --- a/pkgs/http2/test/client_websites_test.dart +++ b/pkgs/http2/test/client_websites_test.dart @@ -8,7 +8,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; -import 'package:http2/client.dart'; +import 'package:http2/src/testing/client.dart'; import 'package:test/test.dart'; main() async { From 94d181d423276cdb657a00170ec58c41b366dd64 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Mon, 7 Sep 2015 16:45:18 +0200 Subject: [PATCH 042/172] Add documentation to the transport library, and move testing/ to src/testing BUG= R=sgjesse@google.com Review URL: https://chromereviews.googleplex.com/250607015 . --- pkgs/http2/lib/{ => src}/testing/client.dart | 4 +- pkgs/http2/lib/{ => src}/testing/debug.dart | 9 +- pkgs/http2/lib/transport.dart | 96 +++++++++++++++++++- 3 files changed, 100 insertions(+), 9 deletions(-) rename pkgs/http2/lib/{ => src}/testing/client.dart (99%) rename pkgs/http2/lib/{ => src}/testing/debug.dart (96%) diff --git a/pkgs/http2/lib/testing/client.dart b/pkgs/http2/lib/src/testing/client.dart similarity index 99% rename from pkgs/http2/lib/testing/client.dart rename to pkgs/http2/lib/src/testing/client.dart index a9d32e0ebe..7fee8ccd8a 100644 --- a/pkgs/http2/lib/testing/client.dart +++ b/pkgs/http2/lib/src/testing/client.dart @@ -8,7 +8,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; -import 'transport.dart'; +import '../../transport.dart'; class Request { final String method; @@ -32,7 +32,6 @@ class ServerPush { ServerPush(this.requestHeaders, this.response); } - class ClientConnection { final ClientTransportConnection connection; @@ -116,7 +115,6 @@ class ClientConnection { } return headerMap; } - } /// Tries to connect to [uri] via a secure socket connection and establishes a diff --git a/pkgs/http2/lib/testing/debug.dart b/pkgs/http2/lib/src/testing/debug.dart similarity index 96% rename from pkgs/http2/lib/testing/debug.dart rename to pkgs/http2/lib/src/testing/debug.dart index 2a84594806..61fc7de4d1 100644 --- a/pkgs/http2/lib/testing/debug.dart +++ b/pkgs/http2/lib/src/testing/debug.dart @@ -9,12 +9,11 @@ import 'dart:io'; import 'dart:io' show stderr; import 'dart:convert'; -import 'src/connection.dart'; -import 'src/connection_preface.dart'; -import 'src/frames/frames.dart'; -import 'src/settings/settings.dart'; +import '../connection_preface.dart'; +import '../frames/frames.dart'; +import '../settings/settings.dart'; -import 'transport.dart'; +import '../../transport.dart'; final jsonEncoder = new JsonEncoder.withIndent(' '); diff --git a/pkgs/http2/lib/transport.dart b/pkgs/http2/lib/transport.dart index 58f95d2d5e..35e3a1efea 100644 --- a/pkgs/http2/lib/transport.dart +++ b/pkgs/http2/lib/transport.dart @@ -2,6 +2,100 @@ // 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. +/// This library provides an http/2 interface on top of a bidirectional stream +/// of bytes. +/// +/// The client and server sides can be created via [ClientTransportStream] and +/// [ServerTransportStream] respectively. Both sides can be configured via +/// settings (see [ClientSettings] and [ServerSettings]). The settings will be +/// communicated to the remote peer (if necessary) and will be valid during the +/// entire lifetime of the connection. +/// +/// A http/2 transport allows a client to open a bidirectional stream (see +/// [ClientTransportConnection.makeRequest]) and a server can open (or push) a +/// unidirectional stream to the client via [ServerTransportStream.push]. +/// +/// In both cases (unidirectional and bidirectional stream), one can send +/// headers and data to the other side (via [HeadersStreamMessage] and +/// [DataStreamMessage]). These messages are ordered and will arrive in the same +/// order as they were sent (data messages may be split up into multiple smaller +/// chunks or might be combined). +/// +/// In the most common case, each direction will send one [HeadersStreamMessage] +/// followed by zero or more [DataStreamMessage]s. +/// +/// Establishing a bidirectional stream of bytes to a server is up to the user +/// of this library. There are 3 common ways to achive this +/// +/// * connect to a server via SSL and use the ALPN (SSL) protocol extension +/// to negogiate with the server to speak http/2 (the ALPN protocol +/// identifier for http/2 is `h2`) +/// +/// * have prior knowledge about the server - i.e. know ahead of time that +/// the server will speak http/2 via an unencrypted tcp connection +/// +/// * use a http/1.1 connection and upgrade it to http/2 +/// +/// The first way is the most common way and can be done in Dart by using +/// `dart:io`s secure socket implementation (by using a `SecurityContext` and +/// including 'h2' in the list of protocols used for ALPN). +/// +/// Here is a simple example on how to connect to a http/2 capable server and +/// requesting a resource: +/// +/// import 'dart:async'; +/// import 'dart:convert'; +/// import 'dart:io'; +/// +/// import 'package:http2/transport.dart'; +/// +/// main() async { +/// var uri = Uri.parse("https://www.google.com/"); +/// +/// var socket = await connect(uri); +/// +/// // The default client settings will disable server pushes. We +/// // therefore do not need to deal with [stream.peerPushes]. +/// var transport = new ClientTransportConnection.viaSocket(socket); +/// +/// var headers = [ +/// new Header.ascii(':method', 'GET'), +/// new Header.ascii(':path', uri.path), +/// new Header.ascii(':scheme', uri.scheme), +/// new Header.ascii(':authority', uri.host), +/// ]; +/// +/// var stream = transport.makeRequest(headers, endStream: true); +/// await for (var message in stream.incomingMessages) { +/// if (message is HeadersStreamMessage) { +/// for (var header in message.headers) { +/// var name = UTF8.decode(header.name); +/// var value = UTF8.decode(header.value); +/// print('$name: $value'); +/// } +/// } else if (message is DataStreamMessage) { +/// // Use [message.bytes] (but respect 'content-encoding' header) +/// } +/// } +/// await transport.finish(); +/// } +/// +/// Future connect(Uri uri) async { +/// bool useSSL = uri.scheme == 'https'; +/// if (useSSL) { +/// var secureSocket = await SecureSocket.connect( +/// uri.host, uri.port, supportedProtocols: ['h2']); +/// if (secureSocket.selectedProtocol != 'h2') { +/// throw new Exception( +/// "Failed to negogiate http/2 via alpn. Maybe server " +/// "doesn't support http/2."); +/// } +/// return secureSocket; +/// } else { +/// return await Socket.connect(uri.host, uri.port); +/// } +/// } +/// library http2.transport; import 'dart:async'; @@ -213,7 +307,7 @@ class TransportConnectionException extends TransportException { : super('Connection error: $details'); } -/// An exception thrown when a HTTP/2 connection error occured. +/// An exception thrown when a HTTP/2 stream error occured. class StreamTransportException extends TransportException { StreamTransportException(String details) : super('Stream error: $details'); From de70f6b32db779243099dada5dab7ffb4fa1ff34 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 8 Sep 2015 09:57:39 +0100 Subject: [PATCH 043/172] ignoring .pub and .packages also update code review settings R=kustermann@google.com Review URL: https://codereview.chromium.org//1311003005 . --- pkgs/http2/.gitignore | 2 ++ pkgs/http2/codereview.settings | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pkgs/http2/.gitignore b/pkgs/http2/.gitignore index d80b0673b7..a19c373b84 100644 --- a/pkgs/http2/.gitignore +++ b/pkgs/http2/.gitignore @@ -1,5 +1,7 @@ # Don’t commit the following directories created by pub. build/ +.packages +.pub/ packages .buildlog diff --git a/pkgs/http2/codereview.settings b/pkgs/http2/codereview.settings index bd94e0a834..69e23ea6d8 100644 --- a/pkgs/http2/codereview.settings +++ b/pkgs/http2/codereview.settings @@ -1,3 +1,3 @@ -CODE_REVIEW_SERVER: https://chromereviews.googleplex.com +CODE_REVIEW_SERVER: https://codereview.chromium.org/ VIEW_VC: https://github.com/dart-lang/http2/commit/ -#CC_LIST: reviews@dartlang.org +CC_LIST: reviews@dartlang.org From d3c35ac9c35ce20821ece75a5af99c78e8fac940 Mon Sep 17 00:00:00 2001 From: Seth Ladd Date: Fri, 18 Sep 2015 11:33:05 -0700 Subject: [PATCH 044/172] link to more info BUG= R=kevmoo@google.com Review URL: https://codereview.chromium.org//1349343002 . --- pkgs/http2/README.md | 7 ++- pkgs/http2/example/display_headers.dart | 63 +++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 pkgs/http2/example/display_headers.dart diff --git a/pkgs/http2/README.md b/pkgs/http2/README.md index d9b78d3ad8..886090f373 100644 --- a/pkgs/http2/README.md +++ b/pkgs/http2/README.md @@ -1,9 +1,12 @@ -# A HTTP/2 implementation in dart. +# HTTP/2 for Dart -This is a work in progress and experimental. +Note: This is a work in progress and experimental. + +See the [API docs][api] for more details and sample code. ## Features and bugs Please file feature requests and bugs at the [issue tracker][tracker]. [tracker]: https://github.com/dart-lang/http2/issues +[api]: http://www.dartdocs.org/documentation/http2/latest diff --git a/pkgs/http2/example/display_headers.dart b/pkgs/http2/example/display_headers.dart new file mode 100644 index 0000000000..6cf9be2dbb --- /dev/null +++ b/pkgs/http2/example/display_headers.dart @@ -0,0 +1,63 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:http2/transport.dart'; + +main(List args) async { + if (args == null || args.length != 1) { + print('Usage: dart display_headers.dart '); + exit(1); + } + + var uriArg = args[0]; + + if (!uriArg.startsWith('https://')) { + print('URI must start with https://'); + exit(1); + } + + var uri = Uri.parse(uriArg); + + var socket = await connect(uri); + + // The default client settings will disable server pushes. We + // therefore do not need to deal with [stream.peerPushes]. + var transport = new ClientTransportConnection.viaSocket(socket); + + var headers = [ + new Header.ascii(':method', 'GET'), + new Header.ascii(':path', uri.path), + new Header.ascii(':scheme', uri.scheme), + new Header.ascii(':authority', uri.host), + ]; + + var stream = transport.makeRequest(headers, endStream: true); + await for (var message in stream.incomingMessages) { + if (message is HeadersStreamMessage) { + for (var header in message.headers) { + var name = UTF8.decode(header.name); + var value = UTF8.decode(header.value); + print('$name: $value'); + } + } else if (message is DataStreamMessage) { + // Use [message.bytes] (but respect 'content-encoding' header) + } + } + await transport.finish(); +} + +Future connect(Uri uri) async { + bool useSSL = uri.scheme == 'https'; + if (useSSL) { + var secureSocket = await SecureSocket.connect(uri.host, uri.port, + supportedProtocols: ['h2']); + if (secureSocket.selectedProtocol != 'h2') { + throw new Exception("Failed to negogiate http/2 via alpn. Maybe server " + "doesn't support http/2."); + } + return secureSocket; + } else { + return await Socket.connect(uri.host, uri.port); + } +} From 82b4f9a328b54de262fcad2eac7e3261c194e560 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Fri, 11 Dec 2015 13:14:21 +0100 Subject: [PATCH 045/172] Update example server after moving debug.dart Closes dart-lang/http2#6 R=sgjesse@google.com Review URL: https://codereview.chromium.org//1519053002 . --- pkgs/http2/experimental/server.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/http2/experimental/server.dart b/pkgs/http2/experimental/server.dart index 7fb8b77db8..0bccf41af3 100644 --- a/pkgs/http2/experimental/server.dart +++ b/pkgs/http2/experimental/server.dart @@ -8,7 +8,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; -import 'package:http2/src/debug.dart' hide print; +import 'package:http2/src/testing/debug.dart' hide print; import 'package:http2/transport.dart'; const bool DEBUGGING = false; From 9539f10b162b94568466113f7ad375052ca7e91e Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Mon, 11 Jan 2016 12:55:36 +0100 Subject: [PATCH 046/172] Add a MultiProtocolHttpServer which calls two different callbacks depending on the HTTP version R=sgjesse@google.com Review URL: https://codereview.chromium.org//1573913002 . --- pkgs/http2/lib/multiprotocol_server.dart | 123 +++++++++++++++ .../lib/src/artificial_server_socket.dart | 145 ++++++++++++++++++ pkgs/http2/lib/src/connection.dart | 2 +- pkgs/http2/pubspec.yaml | 4 +- pkgs/http2/test/certificates/server_chain.pem | 57 +++++++ pkgs/http2/test/certificates/server_key.pem | 29 ++++ .../http2/test/multiprotocol_server_test.dart | 141 +++++++++++++++++ 7 files changed, 498 insertions(+), 3 deletions(-) create mode 100644 pkgs/http2/lib/multiprotocol_server.dart create mode 100644 pkgs/http2/lib/src/artificial_server_socket.dart create mode 100644 pkgs/http2/test/certificates/server_chain.pem create mode 100644 pkgs/http2/test/certificates/server_key.pem create mode 100644 pkgs/http2/test/multiprotocol_server_test.dart diff --git a/pkgs/http2/lib/multiprotocol_server.dart b/pkgs/http2/lib/multiprotocol_server.dart new file mode 100644 index 0000000000..72d91a406a --- /dev/null +++ b/pkgs/http2/lib/multiprotocol_server.dart @@ -0,0 +1,123 @@ +// Copyright (c) 2016 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. + +library http2.multiprotocol_server; + +import 'dart:async'; +import 'dart:io'; + +import 'package:http2/transport.dart' as http2; + +import 'src/artificial_server_socket.dart'; + +/// Handles protocol negotiation with HTTP/1.1 and HTTP/2 clients. +/// +/// Given a (host, port) pair and a [SecurityContext], [MultiProtocolHttpServer] +/// will negotiate with the client whether HTTP/1.1 or HTTP/2 should be spoken. +/// +/// The user must supply 2 callback functions to [startServing], which: +/// * one handles HTTP/1.1 clients (called with a [HttpRequest]) +/// * one handles HTTP/2 clients (called with a [http2.ServerTransportStream]) +class MultiProtocolHttpServer { + final SecureServerSocket _serverSocket; + + _ServerSocketController _http11Controller; + HttpServer _http11Server; + + StreamController _http2Controller; + Stream _http2Server; + Set _http2Connections = new Set(); + + MultiProtocolHttpServer._(this._serverSocket) { + _http11Controller = new _ServerSocketController( + _serverSocket.address, _serverSocket.port); + _http11Server = new HttpServer.listenOn(_http11Controller.stream); + + _http2Controller = new StreamController(); + _http2Server = _http2Controller.stream; + } + + /// Binds a new [SecureServerSocket] with a security [context] at [port] and + /// [address] (see [SecureServerSocket.bind] for a description of supported + /// types for [address]). + /// + /// See also [startServing]. + static Future bind(address, + int port, + SecurityContext context) async { + context.setAlpnProtocols(['h2', 'http/1.1'], true); + var secureServer = await SecureServerSocket.bind(address, port, context); + return new MultiProtocolHttpServer._(secureServer); + } + + /// The port this multi-protocol HTTP server runs on. + int get port => _serverSocket.port; + + /// The address this multi-protocol HTTP server runs on. + InternetAddress get address => _serverSocket.address; + + /// Starts listening for HTTP/1.1 and HTTP/2 clients and calls the given + /// callbacks for new clients. + /// + /// It is expected that [callbackHttp11] and [callbackHttp2] will never throw + /// an exception (i.e. these must take care of error handling themselves). + void startServing(void callbackHttp11(HttpRequest request), + void callbackHttp2(http2.ServerTransportStream stream), + {void onError(error, stack)}) { + // 1. Start listening on the real [SecureServerSocket]. + _serverSocket.listen((SecureSocket socket) { + var protocol = socket.selectedProtocol; + if (protocol == null || protocol == 'http/1.1') { + _http11Controller.addHttp11Socket(socket); + } else if (protocol == 'h2') { + var connection = new http2.ServerTransportConnection.viaSocket(socket); + _http2Connections.add(connection); + connection.incomingStreams.listen( + _http2Controller.add, + onError: onError, + onDone: () => _http2Connections.remove(connection)); + } else { + socket.destroy(); + throw new Exception( + "Unexpected negotiated ALPN protocol: $protocol."); + } + }, onError: onError); + + // 2. Drain all incoming http/1.1 and http/2 connections and call the + // respective handlers. + _http11Server.listen(callbackHttp11); + _http2Server.listen(callbackHttp2); + } + + /// Closes this [MultiProtocolHttpServer]. + /// + /// Completes once everything has been successfully shut down. + Future close({bool force: false}) { + return _serverSocket.close().whenComplete(() { + Future done1 = _http11Server.close(force: force); + Future done2 = Future.wait(_http2Connections.map( + (c) => force ? c.terminate() : c.finish())); + return Future.wait([done1, done2]); + }); + } +} + +/// An internal helper class. +class _ServerSocketController { + final InternetAddress address; + final int port; + final StreamController _controller = new StreamController(); + + _ServerSocketController(this.address, this.port); + + ArtificialServerSocket get stream { + return new ArtificialServerSocket(address, port, _controller.stream); + } + + void addHttp11Socket(Socket socket) { + _controller.add(socket); + } + + Future close() => _controller.close(); +} diff --git a/pkgs/http2/lib/src/artificial_server_socket.dart b/pkgs/http2/lib/src/artificial_server_socket.dart new file mode 100644 index 0000000000..4d03e772f7 --- /dev/null +++ b/pkgs/http2/lib/src/artificial_server_socket.dart @@ -0,0 +1,145 @@ +// Copyright (c) 2016 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. + +library http2.artificial_server_socket; + +import 'dart:async'; +import 'dart:io'; + +/// Custom implementation of the [ServerSocket] interface. +/// +/// This class can be used to create a [ServerSocket] using [Stream] and +/// a [InternetAddress] and `port` (an example use case is to filter [Socket]s +/// and keep the [ServerSocket] interface for APIs that expect it, +/// e.g. `new HttpServer.listenOn()`). +class ArtificialServerSocket extends Object + with StreamMethodsMixin + implements ServerSocket { + final Stream _stream; + + ArtificialServerSocket(this.address, this.port, this._stream); + + // ######################################################################## + // These are the methods of [ServerSocket] in addition to [Stream]. + // ######################################################################## + + final InternetAddress address; + + final int port; + + @deprecated + ServerSocketReference get reference => null; + + /// Closing of an [ArtificialServerSocket] is not possible and an exception + /// will be thrown when calling this method. + Future close() async { + throw new Exception("Did not expect close() to be called."); + } +} + +/// A mixin used to implement the [Stream] interface. +/// +/// This class can be used as a mixin and will delegate all methods on [Stream] +/// to another [_stream] instance. +abstract class StreamMethodsMixin implements Stream { + Stream get _stream; + + Future any(bool test(T element)) => _stream.any(test); + + Stream asBroadcastStream( + {void onListen(StreamSubscription subscription), + void onCancel(StreamSubscription subscription)}) { + return _stream.asBroadcastStream(onListen: onListen, onCancel: onCancel); + } + + Stream asyncExpand(Stream convert(T)) => _stream.asyncExpand(convert); + + Stream asyncMap(convert(T event)) => _stream.asyncExpand(convert); + + Future contains(Object needle) => _stream.contains(needle); + + Stream distinct([bool equals(T previous, T next)]) { + return _stream.distinct(equals); + } + + Future drain([futureValue]) => _stream.drain(); + + Future elementAt(int index) => _stream.elementAt(index); + + Future every(bool test(T)) => _stream.every(test); + + Stream expand(Iterable convert(T)) => _stream.expand(convert); + + Future get first => _stream.first; + + Future firstWhere(bool test(T element), {Object defaultValue()}) { + return _stream.firstWhere(test); + } + + Future fold(initialValue, combine(previous, T element)) { + return _stream.fold(initialValue, combine); + } + + Future forEach(void action(T element)) => _stream.forEach(action); + + Stream handleError(Function onError, {bool test(error)}) { + return _stream.handleError(onError, test: test); + } + + bool get isBroadcast => _stream.isBroadcast; + + Future get isEmpty => _stream.isEmpty; + + Future join([String separator = ""]) => _stream.join(separator); + + Future get last => _stream.last; + + Future lastWhere(bool test(T element), {Object defaultValue()}) { + return _stream.lastWhere(test, defaultValue: defaultValue); + } + + Future get length => _stream.length; + + StreamSubscription listen(void onData(T event), + {Function onError, + void onDone(), + bool cancelOnError}) { + return _stream.listen(onData, + onError: onError, onDone: onDone, cancelOnError: cancelOnError); + } + + Stream map(convert(T event)) => _stream.map(convert); + + Future pipe(StreamConsumer consumer) => _stream.pipe(consumer); + + Future reduce(T combine(T previous, T element)) { + return _stream.reduce(combine); + } + + Future get single => _stream.single; + + Future singleWhere(bool test(T)) => _stream.singleWhere(test); + + Stream skip(int count) => _stream.skip(count); + + Stream skipWhile(bool test(T)) => _stream.skipWhile(test); + + Stream take(int count) => _stream.take(count); + + Stream takeWhile(bool test(T)) => _stream.takeWhile(test); + + Stream timeout(Duration timeLimit, {void onTimeout(EventSink sink)}) { + return _stream.timeout(timeLimit, onTimeout: onTimeout); + } + + Future> toList() => _stream.toList(); + + Future> toSet() => _stream.toSet(); + + Stream transform(StreamTransformer streamTransformer) { + return _stream.transform(streamTransformer); + } + + Stream where(bool test(T)) => _stream.where(test); +} diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index b542ffc88f..dad5aea50c 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -131,7 +131,7 @@ abstract class Connection { (Frame frame) { _catchProtocolErrors(() => _handleFrameImpl(frame)); }, - onError: (stack, error) { + onError: (error, stack) { _terminate(ErrorCode.CONNECT_ERROR, causedByTransportError: true); }, onDone: () { _terminate(ErrorCode.CONNECT_ERROR, causedByTransportError: true); diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index 8fdab25996..1cbcd1fcab 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -1,6 +1,6 @@ name: http2 -version: 0.1.0 -description: A HTTP/2 implementation in dart. +version: 0.1.1 +description: A HTTP/2 implementation in Dart. author: Dart Team homepage: https://github.com/dart-lang/http2 diff --git a/pkgs/http2/test/certificates/server_chain.pem b/pkgs/http2/test/certificates/server_chain.pem new file mode 100644 index 0000000000..4163fe7ddd --- /dev/null +++ b/pkgs/http2/test/certificates/server_chain.pem @@ -0,0 +1,57 @@ +-----BEGIN CERTIFICATE----- +MIIDKTCCAhGgAwIBAgIJAOWmjTS+OnTEMA0GCSqGSIb3DQEBCwUAMBcxFTATBgNV +BAMMDGludGVybWVkaWF0ZTAeFw0xNTA1MTgwOTAwNDBaFw0yMzA4MDQwOTAwNDBa +MBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBALlcwQJuzd+xH8QFgfJSn5tRlvhkldSX98cE7NiA602NBbnAVyUrkRXq +Ni75lgt0kwjYfA9z674m8WSVbgpLPintPCla9CYky1TH0keIs8Rz6cGWHryWEHiu +EDuljQynu2b3sAFuHu9nfWurbJwZnFakBKpdQ9m4EyOZCHC/jHYY7HacKSXg1Cki +we2ca0BWDrcqy8kLy0dZ5oC6IZG8O8drAK8f3f44CRYw59D3sOKBrKXaabpvyEcb +N7Wk2HDBVwHpUJo1reVwtbM8dhqQayYSD8oXnGpP3RQNu/e2rzlXRyq/BfcDY1JI +7TbC4t/7/N4EcPSpGsTcSOC9A7FpzvECAwEAAaN7MHkwCQYDVR0TBAIwADAsBglg +hkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0O +BBYEFCnwiEMMFZh7NhCr+qA8K0w4Q+AOMB8GA1UdIwQYMBaAFB0h1Evsaw2vfrmS +YuoCTmC4EE6ZMA0GCSqGSIb3DQEBCwUAA4IBAQAcFmHMaXRxyoNaeOowQ6iQWoZd +AUbvG7SHr7I6Pi2aqdqofsKWts7Ytm5WsS0M2nN+sW504houu0iCPeJJX8RQw2q4 +CCcNOs9IXk+2uMzlpocHpv+yYoUiD5DxgWh7eghQMLyMpf8FX3Gy4VazeuXznHOM +4gE4L417xkDzYOzqVTp0FTyAPUv6G2euhNCD6TMru9REcRhYul+K9kocjA5tt2KG +MH6y28LXbLyq4YJUxSUU9gY/xlnbbZS48KDqEcdYC9zjW9nQ0qS+XQuQuFIcwjJ5 +V4kAUYxDu6FoTpyQjgsrmBbZlKNxH7Nj4NDlcdJhp/zeSKHqWa5hSWjjKIxp +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDAjCCAeqgAwIBAgIJAOWmjTS+OnTDMA0GCSqGSIb3DQEBCwUAMBgxFjAUBgNV +BAMMDXJvb3RhdXRob3JpdHkwHhcNMTUwNTE4MDkwMDQwWhcNMjMwODA0MDkwMDQw +WjAXMRUwEwYDVQQDDAxpbnRlcm1lZGlhdGUwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDSrAO1CoPvUllgLOzDm5nG0skDF7vh1DUgAIDVGz0ecD0JFbQx +EF79pju/6MbtpTW2FYvRp11t/G7rGtX923ybOHY/1MNFQrdIvPlO1VV7IGKjoMwP +DNeb0fIGjHoE9QxaDxR8NX8xQbItpsw+TUtRfc9SLkR+jaYJfVRoM21BOncZbSHE +YKiZlEbpecB/+EtwVpgvl+8mPD5U07Fi4fp/lza3WXInXQPyiTVllIEJCt4PKmlu +MocNaJOW38bysL7i0PzDpVZtOxLHOTaW68yF3FckIHNCaA7k1ABEEEegjFMmIao7 +B9w7A0jvr4jZVvNmui5Djjn+oJxwEVVgyf8LAgMBAAGjUDBOMB0GA1UdDgQWBBQd +IdRL7GsNr365kmLqAk5guBBOmTAfBgNVHSMEGDAWgBRk81s9d0ZbiZhh44KckwPb +oTc0XzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBZQTK0plfdB5PC +cC5icut4EmrByJa1RbU7ayuEE70e7hla6KVmVjVdCBGltI4jBYwfhKbRItHiAJ/8 +x+XZKBG8DLPFuDb7lAa1ObhAYF7YThUFPQYaBhfzKcWrdmWDBFpvNv6E0Mm364dZ +e7Yxmbe5S4agkYPoxEzgEYmcUk9jbjdR6eTbs8laG169ljrECXfEU9RiAcqz5iSX +NLSewqB47hn3B9qgKcQn+PsgO2j7M+rfklhNgeGJeWmy7j6clSOuCsIjWHU0RLQ4 +0W3SB/rpEAJ7fgQbYUPTIUNALSOWi/o1tDX2mXPRjBoxqAv7I+vYk1lZPmSzkyRh +FKvRDxsW +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDAzCCAeugAwIBAgIJAJ0MomS4Ck+8MA0GCSqGSIb3DQEBCwUAMBgxFjAUBgNV +BAMMDXJvb3RhdXRob3JpdHkwHhcNMTUwNTE4MDkwMDQwWhcNMjMwODA0MDkwMDQw +WjAYMRYwFAYDVQQDDA1yb290YXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAts1ijtBV92S2cOvpUMOSTp9c6A34nIGr0T5Nhz6XiqRVT+gv +dQgmkdKJQjbvR60y6jzltYFsI2MpGVXY8h/oAL81D/k7PDB2aREgyBfTPAhBHyGw +siR+2xYt5b/Zs99q5RdRqQNzNpLPJriIKvUsRyQWy1UiG2s7pRXQeA8qB0XtJdCj +kFIi+G2bDsaffspGeDOCqt7t+yqvRXfSES0c/l7DIHaiMbbp4//ZNML3RNgAjPz2 +hCezZ+wOYajOIyoSPK8IgICrhYFYxvgWxwbLDBEfC5B3jOQsySe10GoRAKZz1gBV +DmgReu81tYJmdgkc9zknnQtIFdA0ex+GvZlfWQIDAQABo1AwTjAdBgNVHQ4EFgQU +ZPNbPXdGW4mYYeOCnJMD26E3NF8wHwYDVR0jBBgwFoAUZPNbPXdGW4mYYeOCnJMD +26E3NF8wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEATzkZ97K777uZ +lQcduNX3ey4IbCiEzFA2zO5Blj+ilfIwNbZXNOgm/lqNvVGDYs6J1apJJe30vL3X +J+t2zsZWzzQzb9uIU37zYemt6m0fHrSrx/iy5lGNqt3HMfqEcOqSCOIK3PCTMz2/ +uyGe1iw33PVeWsm1JUybQ9IrU/huJjbgOHU4wab+8SJCM49ipArp68Fr6j4lcEaE +4rfRg1ZsvxiOyUB3qPn6wyL/JB8kOJ+QCBe498376eaem8AEFk0kQRh6hDaWtq/k +t6IIXQLjx+EBDVP/veK0UnVhKRP8YTOoV8ZiG1NcdlJmX/Uk7iAfevP7CkBfSN8W +r6AL284qtw== +-----END CERTIFICATE----- diff --git a/pkgs/http2/test/certificates/server_key.pem b/pkgs/http2/test/certificates/server_key.pem new file mode 100644 index 0000000000..1fd2324045 --- /dev/null +++ b/pkgs/http2/test/certificates/server_key.pem @@ -0,0 +1,29 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIE5DAcBgoqhkiG9w0BDAEBMA4ECL7L6rj6uEHGAgIIAASCBMLbucyfqAkgCbhP +xNSHYllPMAv/dsIjtnsBwepCXPGkCBCuOAw/2FaCHjN9hBqL5V7fkrKeaemhm2YE +ycPtlHJYPDf3kEkyMjdZ9rIY6kePGfQizs2uJPcXj4YPyQ4HsfVXpOicKfQrouf5 +Mze9bGzeMN065q3iP4dYUMwHAyZYteXCsanQNHlqvsWli0W+H8St8fdsXefZhnv1 +qVatKWdNdWQ9t5MuljgNU2Vv56sHKEYXI0yLxk2QUMk8KlJfnmt8foYUsnPUXHmc +gIjLKwwVkpdololnEHSNu0cEOUPowjgJru+uMpn7vdNl7TPEQ9jbEgdNg4JwoYzU +0nao8WzjaSp7kzvZz0VFwKnk5AjstGvvuAWckADdq23QElbn/mF7AG1m/TBpYxzF +gTt37UdndS/AcvVznWVVrRP5iTSIawdIwvqI4s7rqsoE0GCcak+RhchgAz2gWKkS +oODUo0JL6pPVbJ3l4ebbaO6c99nDVc8dViPtc1EkStJEJ2O4kI4xgLSCr4Y9ahKn +oAaoSkX7Xxq3aQm+BzqSpLjdGL8atsqR/YVOIHYIl3gThvP0NfZGx1xHyvO5mCdZ +kHxSA7tKWxauZ3eQ2clbnzeRsl4El0WMHy/5K1ovene4v7sunmoXVtghBC8hK6eh +zMO9orex2PNQ/VQC7HCvtytunOVx1lkSBoNo7hR70igg6rW9H7UyoAoBOwMpT1xa +J6V62nqruTKOqFNfur7aHJGpHGtDb5/ickHeYCyPTvmGp67u4wChzKReeg02oECe +d1E5FKAcIa8s9TVOB6Z+HvTRNQZu2PsI6TJnjQRowvY9DAHiWTlJZBBY/pko3hxX +TsIeybpvRdEHpDWv86/iqtw1hv9CUxS/8ZTWUgBo+osShHW79FeDASr9FC4/Zn76 +ZDERTgV4YWlW/klVWcG2lFo7jix+OPXAB+ZQavLhlN1xdWBcIz1AUWjAM4hdPylW +HCX4PB9CQIPl2E7F+Y2p6nMcMWSJVBi5UIH7E9LfaBguXSzMmTk2Fw5p1aOQ6wfN +goVAMVwi8ppAVs741PfHdZ295xMmK/1LCxz5DeAdD/tsA/SYfT753GotioDuC7im +EyJ5JyvTr5I6RFFBuqt3NlUb3Hp16wP3B2x9DZiB6jxr0l341/NHgsyeBXkuIy9j +ON2mvpBPCJhS8kgWo3G0UyyKnx64tcgpGuSvZhGwPz843B6AbYyE6pMRfSWRMkMS +YZYa+VNKhR4ixdj07ocFZEWLVjCH7kxkE8JZXKt8jKYmkWd0lS1QVjgaKlO6lRa3 +q6SPJkhW6pvqobvcqVNXwi1XuzpZeEbuh0B7OTekFTTxx5g9XeDl56M8SVQ1KEhT +Q1t7H2Nba18WCB7cf+6PN0F0K0Jz1Kq7ZWaqEI/grX1m4RQuvNF5807sB/QKMO/Z +Gz3NXvHg5xTJRd/567lxPGkor0cE7qD1EZfmJ2HrBYXQ91bhgA7LToBuMZo6ZRXH +QfsanjbP4FPLMiGdQigLjj3A35L/f4sQOOVac/sRaFnm7pzcxsMvyVU/YtvGcjYE +xaOOVnamg661Wo0wksXoDjeSz/JIyyKO3Gwp1FSm2wGLjjy/Ehmqcqy8rvHuf07w +AUukhVtTNn4= +-----END ENCRYPTED PRIVATE KEY----- diff --git a/pkgs/http2/test/multiprotocol_server_test.dart b/pkgs/http2/test/multiprotocol_server_test.dart new file mode 100644 index 0000000000..632d63394c --- /dev/null +++ b/pkgs/http2/test/multiprotocol_server_test.dart @@ -0,0 +1,141 @@ +// 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. + +library http2.test.client_tests; + +import 'dart:async'; +import 'dart:io'; +import 'dart:convert'; + +import 'package:test/test.dart'; + +import 'package:http2/transport.dart'; +import 'package:http2/multiprotocol_server.dart'; + +main() { + SecurityContext context = new SecurityContext() + ..useCertificateChain('test/certificates/server_chain.pem') + ..usePrivateKey('test/certificates/server_key.pem', + password: 'dartdart'); + + group('multiprotocol-server', () { + test('http/1.1', () async { + const Count = 2; + + var server = await MultiProtocolHttpServer.bind('localhost', 0, context); + int requestNr = 0; + server.startServing( + expectAsync((HttpRequest request) async { + await handleHttp11Request(request, requestNr++); + if (requestNr == Count) { + await server.close(); + } + }, count: Count), + expectAsync((ServerTransportStream stream) { + }, count: 0)); + + var client = new HttpClient(); + client.badCertificateCallback = (_, __, ___) => true; + for (int i = 0; i < Count; i++) { + await makeHttp11Request(server, client, i); + } + }); + + test('http/2', () async { + const Count = 2; + + var server = await MultiProtocolHttpServer.bind('localhost', 0, context); + int requestNr = 0; + server.startServing( + expectAsync((HttpRequest request) { + }, count: 0), + expectAsync((ServerTransportStream stream) async { + await handleHttp2Request(stream, requestNr++); + if (requestNr == Count) { + await server.close(); + } + }, count: Count)); + + var socket = await SecureSocket.connect( + 'localhost', server.port, + onBadCertificate: (_) => true, + supportedProtocols: ['http/1.1', 'h2']); + var connection = new ClientTransportConnection.viaSocket(socket); + for (int i = 0; i < Count; i++) { + await makeHttp2Request(server, connection, i); + } + await connection.finish(); + }); + }); +} + +Future makeHttp11Request(MultiProtocolHttpServer server, + HttpClient client, + int i) async { + var request = await client.getUrl( + Uri.parse('https://localhost:${server.port}/abc$i')); + var response = await request.close(); + var body = await response.transform(UTF8.decoder).join(''); + expect(body, 'answer$i'); +} + +Future handleHttp11Request(HttpRequest request, int i) async { + expect(request.uri.path, '/abc$i'); + await request.drain(); + request.response.write('answer$i'); + await request.response.close(); +} + +Future makeHttp2Request(MultiProtocolHttpServer server, + ClientTransportConnection connection, + int i) async { + expect(connection.isOpen, true); + var headers = [ + new Header.ascii(':method', 'GET'), + new Header.ascii(':scheme', 'https'), + new Header.ascii(':authority', 'localhost:${server.port}'), + new Header.ascii(':path', '/abc$i'), + ]; + + var stream = connection.makeRequest(headers, endStream: true); + var si = new StreamIterator(stream.incomingMessages); + + expect(await si.moveNext(), true); + expect(si.current is HeadersStreamMessage, true); + var responseHeaders = getHeaders(si.current); + expect(responseHeaders[':status'], '200'); + + expect(await si.moveNext(), true); + expect(si.current is DataStreamMessage, true); + expect(ASCII.decode(si.current.bytes), 'answer$i'); + + expect(await si.moveNext(), false); +} + +Future handleHttp2Request(ServerTransportStream stream, int i) async { + var si = new StreamIterator(stream.incomingMessages); + + expect(await si.moveNext(), true); + expect(si.current is HeadersStreamMessage, true); + var headers = getHeaders(si.current); + + expect(headers[':path'], '/abc$i'); + expect(await si.moveNext(), false); + + stream.outgoingMessages.add(new HeadersStreamMessage([ + new Header.ascii(':status', '200'), + ])); + + stream.outgoingMessages.add(new DataStreamMessage(ASCII.encode('answer$i'))); + await stream.outgoingMessages.close(); +} + +Map getHeaders(HeadersStreamMessage headers) { + var map = {}; + for (var h in headers.headers) { + map.putIfAbsent(ASCII.decode(h.name), () => ASCII.decode(h.value)); + } + return map; +} + From ab902b06876fe3fee4735f0b44cff8686e7aeb68 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Mon, 11 Jan 2016 16:08:38 +0100 Subject: [PATCH 047/172] Try to send EndStreamFlag together with headers (if possible) Some servers (e.g. https://twitter.com) will respond with "status: 400" if one doesn't send the EOS flag with the headers frame (it's not a protocol violation, since it responds with a valid "status: 400" response, but it still different than what other servers, like GWS do). R=sgjesse@google.com Review URL: https://codereview.chromium.org//1578753002 . --- pkgs/http2/lib/src/streams/stream_handler.dart | 5 +---- pkgs/http2/test/client_websites_test.dart | 2 ++ 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index 3cdec55bd6..5a78aaa4d5 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -197,10 +197,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { TransportStream newStream(List
headers, {bool endStream: false}) { return ensureNotTerminatedSync(() { var stream = newLocalStream(); - _sendHeaders(stream, headers); - if (endStream) { - _handleOutgoingClose(stream); - } + _sendHeaders(stream, headers, endStream: endStream); return stream; }); } diff --git a/pkgs/http2/test/client_websites_test.dart b/pkgs/http2/test/client_websites_test.dart index c8ca6d616f..bed148b88f 100644 --- a/pkgs/http2/test/client_websites_test.dart +++ b/pkgs/http2/test/client_websites_test.dart @@ -124,6 +124,8 @@ Future readBody(Response response) async { var stream = response.stream; if (response.headers['content-encoding']?.join('') == 'gzip') { stream = stream.transform(GZIP.decoder); + } else if (response.headers['content-encoding']?.join('') == 'deflate') { + stream = stream.transform(ZLIB.decoder); } return await stream.transform(UTF8.decoder).join(''); } From dbb7a431125666e8dfd29740f212962e0a95990f Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Tue, 2 Feb 2016 18:41:43 +0100 Subject: [PATCH 048/172] Fix performance issue when underlying socket is not writable R=sgjesse@google.com Review URL: https://codereview.chromium.org//1642143002 . --- pkgs/http2/CHANGELOG.md | 14 +++ .../src/flowcontrol/connection_queues.dart | 100 ++++++++++-------- pkgs/http2/pubspec.yaml | 2 +- .../flowcontrol/connection_queues_test.dart | 2 +- 4 files changed, 72 insertions(+), 46 deletions(-) diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index 2a2d63cf8e..51817d9832 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 0.1.1+1 + +* Fixing a performance issue in case the underlying socket is not writeable + +## 0.1.1 + +* Adding support for MultiProtocolHttpServer in the + `package:http2/multiprotocol_server.dart` library + +## 0.1.0 + +* First version of a HTTP/2 transport implementation in the + `package:http2/transport.dart` library + ## 0.0.1 - Initial version diff --git a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart index 41b56c5930..b64b7a2874 100644 --- a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart @@ -80,59 +80,71 @@ class ConnectionMessageQueueOut extends Object void _trySendMessages() { if (!wasTerminated) { - _trySendMessage(); + // We can make progress if + // * there is at least one message to send + // * the underlying frame writer / sink / socket doesn't block + // * either one + // * the next message is a non-flow control message (e.g. headers) + // * the connection window is positive - // If we have more messages and we can send them, we'll run them - // using `Timer.run()` to let other things get in-between. if (_messages.length > 0 && - !_connectionWindow.positiveWindow.wouldBuffer) { - // TODO: If all the frame writer methods would return an integer of the - // number of bytes written, we could just say, we loop here until 10kb - // and after words, we'll make `Timer.run()`. - Timer.run(_trySendMessages); - } else { - onCheckForClose(); + !_frameWriter.bufferIndicator.wouldBuffer && + (!_connectionWindow.positiveWindow.wouldBuffer || + _messages.first is! DataMessage)) { + _trySendMessage(); + + // If we have more messages and we can send them, we'll run them + // using `Timer.run()` to let other things get in-between. + if (_messages.length > 0 && + !_frameWriter.bufferIndicator.wouldBuffer && + (!_connectionWindow.positiveWindow.wouldBuffer || + _messages.first is! DataMessage)) { + // TODO: If all the frame writer methods would return the + // number of bytes written, we could just say, we loop here until 10kb + // and after words, we'll make `Timer.run()`. + Timer.run(_trySendMessages); + } else { + onCheckForClose(); + } } } } void _trySendMessage() { - if (!_frameWriter.bufferIndicator.wouldBuffer && _messages.length > 0) { - Message message = _messages.first; - if (message is HeadersMessage) { - _messages.removeFirst(); - _frameWriter.writeHeadersFrame( - message.streamId, message.headers, endStream: message.endStream); - } else if (message is PushPromiseMessage) { - _messages.removeFirst(); - _frameWriter.writePushPromiseFrame( - message.streamId, message.promisedStreamId, message.headers); - } else if (message is DataMessage) { - _messages.removeFirst(); - - if (_connectionWindow.peerWindowSize >= message.bytes.length) { - _connectionWindow.decreaseWindow(message.bytes.length); - _frameWriter.writeDataFrame( - message.streamId, message.bytes, endStream: message.endStream); - } else { - // NOTE: We need to fragment the DataMessage. - // TODO: Do not fragment if the number of bytes we can send is too low - int len = _connectionWindow.peerWindowSize; - var head = viewOrSublist(message.bytes, 0, len); - var tail = viewOrSublist( - message.bytes, len, message.bytes.length - len); - - _connectionWindow.decreaseWindow(head.length); - _frameWriter.writeDataFrame(message.streamId, head, endStream: false); - - var tailMessage = - new DataMessage(message.streamId, tail, message.endStream); - _messages.addFirst(tailMessage); - } + Message message = _messages.first; + if (message is HeadersMessage) { + _messages.removeFirst(); + _frameWriter.writeHeadersFrame( + message.streamId, message.headers, endStream: message.endStream); + } else if (message is PushPromiseMessage) { + _messages.removeFirst(); + _frameWriter.writePushPromiseFrame( + message.streamId, message.promisedStreamId, message.headers); + } else if (message is DataMessage) { + _messages.removeFirst(); + + if (_connectionWindow.peerWindowSize >= message.bytes.length) { + _connectionWindow.decreaseWindow(message.bytes.length); + _frameWriter.writeDataFrame( + message.streamId, message.bytes, endStream: message.endStream); } else { - throw new StateError( - 'Unexpected message in queue: ${message.runtimeType}'); + // NOTE: We need to fragment the DataMessage. + // TODO: Do not fragment if the number of bytes we can send is too low + int len = _connectionWindow.peerWindowSize; + var head = viewOrSublist(message.bytes, 0, len); + var tail = viewOrSublist( + message.bytes, len, message.bytes.length - len); + + _connectionWindow.decreaseWindow(head.length); + _frameWriter.writeDataFrame(message.streamId, head, endStream: false); + + var tailMessage = + new DataMessage(message.streamId, tail, message.endStream); + _messages.addFirst(tailMessage); } + } else { + throw new StateError( + 'Unexpected message in queue: ${message.runtimeType}'); } } } diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index 1cbcd1fcab..a209324da2 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -1,5 +1,5 @@ name: http2 -version: 0.1.1 +version: 0.1.1+1 description: A HTTP/2 implementation in Dart. author: Dart Team homepage: https://github.com/dart-lang/http2 diff --git a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart index ab58c79959..6a172dba4e 100644 --- a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart @@ -13,7 +13,6 @@ import 'package:http2/src/flowcontrol/connection_queues.dart'; import 'package:http2/src/flowcontrol/stream_queues.dart'; import 'package:http2/src/flowcontrol/queue_messages.dart'; -import '../error_matchers.dart'; import '../mock_utils.dart'; main() { @@ -48,6 +47,7 @@ main() { // Send [DataMessage]. c = new TestCounter(count: 2); windowMock.peerWindowSize = bytes.length; + windowMock.positiveWindow.markUnBuffered(); windowMock.mock_decreaseWindow = (int difference) { expect(difference, bytes.length); c.got(); From 34e2a87d234efb5bc24769d760c2c7eb1cbdaf34 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Tue, 2 Feb 2016 18:44:11 +0100 Subject: [PATCH 049/172] Allow users of MultiProtocolHttpServer to pass http.ServerSettings R=sgjesse@google.com Review URL: https://codereview.chromium.org//1645993002 . --- pkgs/http2/CHANGELOG.md | 2 ++ pkgs/http2/lib/multiprotocol_server.dart | 21 +++++++++++++-------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index 51817d9832..cdfbb3f7c9 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -3,6 +3,8 @@ ## 0.1.1+1 * Fixing a performance issue in case the underlying socket is not writeable +* Allow clients of MultiProtocolHttpServer to supply [http.ServerSettings] +* Allow the draft version 'h2-14' in the ALPN protocol negogiation. ## 0.1.1 diff --git a/pkgs/http2/lib/multiprotocol_server.dart b/pkgs/http2/lib/multiprotocol_server.dart index 72d91a406a..580cec57dc 100644 --- a/pkgs/http2/lib/multiprotocol_server.dart +++ b/pkgs/http2/lib/multiprotocol_server.dart @@ -21,6 +21,7 @@ import 'src/artificial_server_socket.dart'; /// * one handles HTTP/2 clients (called with a [http2.ServerTransportStream]) class MultiProtocolHttpServer { final SecureServerSocket _serverSocket; + final http2.ServerSettings _settings; _ServerSocketController _http11Controller; HttpServer _http11Server; @@ -29,7 +30,7 @@ class MultiProtocolHttpServer { Stream _http2Server; Set _http2Connections = new Set(); - MultiProtocolHttpServer._(this._serverSocket) { + MultiProtocolHttpServer._(this._serverSocket, this._settings) { _http11Controller = new _ServerSocketController( _serverSocket.address, _serverSocket.port); _http11Server = new HttpServer.listenOn(_http11Controller.stream); @@ -42,13 +43,16 @@ class MultiProtocolHttpServer { /// [address] (see [SecureServerSocket.bind] for a description of supported /// types for [address]). /// + /// Optionally [settings] can be supplied which will be used for HTTP/2 + /// clients. + /// /// See also [startServing]. - static Future bind(address, - int port, - SecurityContext context) async { - context.setAlpnProtocols(['h2', 'http/1.1'], true); + static Future bind( + address, int port, SecurityContext context, + {http2.ServerSettings settings}) async { + context.setAlpnProtocols(['h2', 'h2-14', 'http/1.1'], true); var secureServer = await SecureServerSocket.bind(address, port, context); - return new MultiProtocolHttpServer._(secureServer); + return new MultiProtocolHttpServer._(secureServer, settings); } /// The port this multi-protocol HTTP server runs on. @@ -70,8 +74,9 @@ class MultiProtocolHttpServer { var protocol = socket.selectedProtocol; if (protocol == null || protocol == 'http/1.1') { _http11Controller.addHttp11Socket(socket); - } else if (protocol == 'h2') { - var connection = new http2.ServerTransportConnection.viaSocket(socket); + } else if (protocol == 'h2' || protocol == 'h2-14') { + var connection = new http2.ServerTransportConnection.viaSocket( + socket, settings: _settings); _http2Connections.add(connection); connection.incomingStreams.listen( _http2Controller.add, From b3ab39b8068e91104da227da313003155b375c51 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Fri, 17 Mar 2017 16:10:06 +0100 Subject: [PATCH 050/172] Include errorCode in exception message. R=vegorov@google.com Review-Url: https://codereview.chromium.org//2741343021 . --- pkgs/http2/CHANGELOG.md | 4 ++++ pkgs/http2/lib/transport.dart | 5 +++-- pkgs/http2/pubspec.yaml | 3 +-- pkgs/http2/test/client_test.dart | 13 +++++++++++-- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index cdfbb3f7c9..cc099dbfcc 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 0.1.1+2 + +* Add errorCode to exception toString message. + ## 0.1.1+1 * Fixing a performance issue in case the underlying socket is not writeable diff --git a/pkgs/http2/lib/transport.dart b/pkgs/http2/lib/transport.dart index 35e3a1efea..a23aff4d4e 100644 --- a/pkgs/http2/lib/transport.dart +++ b/pkgs/http2/lib/transport.dart @@ -303,8 +303,9 @@ class TransportException implements Exception { class TransportConnectionException extends TransportException { final int errorCode; - TransportConnectionException(this.errorCode, String details) - : super('Connection error: $details'); + TransportConnectionException(int errorCode, String details) + : errorCode = errorCode, + super('Connection error: $details (errorCode: $errorCode)'); } /// An exception thrown when a HTTP/2 stream error occured. diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index a209324da2..c5eeb302ac 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -1,5 +1,5 @@ name: http2 -version: 0.1.1+1 +version: 0.1.1+2 description: A HTTP/2 implementation in Dart. author: Dart Team homepage: https://github.com/dart-lang/http2 @@ -9,4 +9,3 @@ environment: dev_dependencies: test: '>=0.12.0 <0.13.0' - diff --git a/pkgs/http2/test/client_test.dart b/pkgs/http2/test/client_test.dart index e95b289a53..f477fff245 100644 --- a/pkgs/http2/test/client_test.dart +++ b/pkgs/http2/test/client_test.dart @@ -119,8 +119,12 @@ main() { expect(client.isOpen, true); var stream = client.makeRequest([new Header.ascii('a', 'b')]); - var error = await stream.incomingMessages.toList() - .catchError((e) => '$e'); + String error; + try { + await stream.incomingMessages.toList(); + } catch (e) { + error = '$e'; + } expect(error, contains('forcefully terminated')); await client.finish(); } @@ -415,6 +419,7 @@ main() { Future nextFrame()) async { var settingsDone = new Completer(); + var headersDone = new Completer(); Future serverFun() async { var decoder = new HPackDecoder(); @@ -433,6 +438,8 @@ main() { expect(decodedHeaders, hasLength(1)); expect(decodedHeaders[0], isHeader('a', 'b')); + headersDone.complete(); + // Make sure we got the stream reset. frame = await nextFrame(); expect(frame is RstStreamFrame, true); @@ -453,6 +460,8 @@ main() { // Make a new stream and terminate it. var stream = client.makeRequest( [new Header.ascii('a', 'b')], endStream: false); + + await headersDone.future; stream.terminate(); // Make sure we don't get messages/pushes on the terminated stream. From aaffcc4c910adc209bb4454209bc44a3dc682c05 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Thu, 30 Mar 2017 13:38:37 +0200 Subject: [PATCH 051/172] Add .test_config and disable some tests on MacOS Review-Url: https://codereview.chromium.org//2785973003 . --- pkgs/http2/.test_config | 5 +++++ pkgs/http2/test/client_websites_test.dart | 8 ++++++++ pkgs/http2/test/multiprotocol_server_test.dart | 4 ++++ 3 files changed, 17 insertions(+) create mode 100644 pkgs/http2/.test_config diff --git a/pkgs/http2/.test_config b/pkgs/http2/.test_config new file mode 100644 index 0000000000..2fa4b96b0e --- /dev/null +++ b/pkgs/http2/.test_config @@ -0,0 +1,5 @@ +{ + "test_package": { + "platforms" : ["vm"] + } +} diff --git a/pkgs/http2/test/client_websites_test.dart b/pkgs/http2/test/client_websites_test.dart index bed148b88f..b223887190 100644 --- a/pkgs/http2/test/client_websites_test.dart +++ b/pkgs/http2/test/client_websites_test.dart @@ -25,6 +25,8 @@ main() async { body = body.toLowerCase(); expect(body, contains('')); expect(body, contains('www.google')); + }, onPlatform: { + 'mac-os' : new Skip('ALPN not supported on MacOS'), }); test('twitter', () async { @@ -38,6 +40,8 @@ main() async { expect(body, contains('')); expect(body, contains('twitter.com')); + }, onPlatform: { + 'mac-os' : new Skip('ALPN not supported on MacOS'), }); test('nghttp2.org - server push enabled', () async { @@ -77,6 +81,8 @@ main() async { expect(pushes[0][0], '/stylesheets/screen.css'); expect(pushes[0][1], contains('audio,video{')); await connection.close(); + }, onPlatform: { + 'mac-os' : new Skip('ALPN not supported on MacOS'), }); test('nghttp2.org - server push disabled', () async { @@ -105,6 +111,8 @@ main() async { var pushes = results[1]; expect(pushes, hasLength(0)); await connection.close(); + }, onPlatform: { + 'mac-os' : new Skip('ALPN not supported on MacOS'), }); }); } diff --git a/pkgs/http2/test/multiprotocol_server_test.dart b/pkgs/http2/test/multiprotocol_server_test.dart index 632d63394c..70ff665a0f 100644 --- a/pkgs/http2/test/multiprotocol_server_test.dart +++ b/pkgs/http2/test/multiprotocol_server_test.dart @@ -40,6 +40,8 @@ main() { for (int i = 0; i < Count; i++) { await makeHttp11Request(server, client, i); } + }, onPlatform: { + 'mac-os' : new Skip('ALPN not supported on MacOS'), }); test('http/2', () async { @@ -66,6 +68,8 @@ main() { await makeHttp2Request(server, connection, i); } await connection.finish(); + }, onPlatform: { + 'mac-os' : new Skip('ALPN not supported on MacOS'), }); }); } From fdf60e26dec5c96f4f81de85cccc7aa7c5780418 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Fri, 31 Mar 2017 10:15:06 +0200 Subject: [PATCH 052/172] Make end2end client tests more resilient to different html responses Review-Url: https://codereview.chromium.org//2788853002 . --- pkgs/http2/test/client_websites_test.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkgs/http2/test/client_websites_test.dart b/pkgs/http2/test/client_websites_test.dart index b223887190..1f6adf38bb 100644 --- a/pkgs/http2/test/client_websites_test.dart +++ b/pkgs/http2/test/client_websites_test.dart @@ -19,11 +19,12 @@ main() async { Response response = await connection.makeRequest(new Request('GET', uri)); dumpHeaders(uri, response.headers); - String body = await response.stream.transform(UTF8.decoder).join(''); + final utf8Decoder = new Utf8Decoder(allowMalformed: true); + String body = await response.stream.transform(utf8Decoder).join(''); connection.close(); body = body.toLowerCase(); - expect(body, contains('')); + expect(body, contains(' Date: Thu, 22 Jun 2017 16:44:03 +0200 Subject: [PATCH 053/172] Allow closing the stream with the last frame. (dart-lang/http2#8) * Allow closing the stream with the last frame. This is needed for at least the Go gRPC client, which expects the trailer frame to be sent with endStream==true. * Address review feedback. Set endStream flag on incoming StreamMessages. Added test. --- .../lib/src/flowcontrol/stream_queues.dart | 8 ++- .../http2/lib/src/streams/stream_handler.dart | 12 +++-- pkgs/http2/lib/transport.dart | 21 +++++--- pkgs/http2/test/src/streams/streams_test.dart | 52 +++++++++++++++++++ 4 files changed, 79 insertions(+), 14 deletions(-) diff --git a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart index 1c5bc9415f..0fb4a73542 100644 --- a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart @@ -277,10 +277,14 @@ class StreamMessageQueueIn extends Object if (message is HeadersMessage) { // NOTE: Header messages do not affect flow control - only // data messages do. - _incomingMessagesC.add(new HeadersStreamMessage(message.headers)); + _incomingMessagesC.add( + new HeadersStreamMessage( + message.headers, endStream: message.endStream)); } else if (message is DataMessage) { if (message.bytes.length > 0) { - _incomingMessagesC.add(new DataStreamMessage(message.bytes)); + _incomingMessagesC.add( + new DataStreamMessage( + message.bytes, endStream: message.endStream)); windowHandler.dataProcessed(message.bytes.length); } } else { diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index 5a78aaa4d5..9405df0cd3 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -360,8 +360,8 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { } }, onDone: () { if (!wasTerminated) { - // TODO: We should really allow the endStream flag to be send with - // the last headers/data frame. Should we add it to [StreamMessage]? + // Stream should already have been closed by the last frame, but we + // allow multiple close calls, just to make sure. _handleOutgoingClose(stream); } }); @@ -384,9 +384,9 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { } if (msg is DataStreamMessage) { - _sendData(stream, msg.bytes, endStream: false); + _sendData(stream, msg.bytes, endStream: msg.endStream); } else if (msg is HeadersStreamMessage) { - _sendHeaders(stream, msg.headers, endStream: false); + _sendHeaders(stream, msg.headers, endStream: msg.endStream); } if (stream.outgoingQueue.bufferIndicator.wouldBuffer && @@ -397,7 +397,9 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { void _handleOutgoingClose(Http2StreamImpl stream) { // We allow multiple close calls. - if (stream.state != StreamState.Closed) { + if (stream.state != StreamState.HalfClosedLocal && + stream.state != StreamState.Closed && + stream.state != StreamState.Terminated) { _sendData(stream, const [], endStream: true); } } diff --git a/pkgs/http2/lib/transport.dart b/pkgs/http2/lib/transport.dart index a23aff4d4e..46840e60cc 100644 --- a/pkgs/http2/lib/transport.dart +++ b/pkgs/http2/lib/transport.dart @@ -222,12 +222,13 @@ abstract class TransportStream { // For convenience only. void sendHeaders(List
headers, {bool endStream: false}) { - outgoingMessages.add(new HeadersStreamMessage(headers)); + outgoingMessages.add( + new HeadersStreamMessage(headers, endStream: endStream)); if (endStream) outgoingMessages.close(); } void sendData(List bytes, {bool endStream: false}) { - outgoingMessages.add(new DataStreamMessage(bytes)); + outgoingMessages.add(new DataStreamMessage(bytes, endStream: endStream)); if (endStream) outgoingMessages.close(); } } @@ -253,24 +254,30 @@ abstract class ServerTransportStream extends TransportStream { } /// Represents a message which can be sent over a HTTP/2 stream. -abstract class StreamMessage {} +abstract class StreamMessage { + final bool endStream; + + StreamMessage({bool endStream}) : this.endStream = endStream ?? false; +} /// Represents a data message which can be sent over a HTTP/2 stream. -class DataStreamMessage implements StreamMessage { +class DataStreamMessage extends StreamMessage { final List bytes; - DataStreamMessage(this.bytes); + DataStreamMessage(this.bytes, {bool endStream}) + : super(endStream: endStream); String toString() => 'DataStreamMessage(${bytes.length} bytes)'; } /// Represents a headers message which can be sent over a HTTP/2 stream. -class HeadersStreamMessage implements StreamMessage { +class HeadersStreamMessage extends StreamMessage { final List
headers; - HeadersStreamMessage(this.headers); + HeadersStreamMessage(this.headers, {bool endStream}) + : super(endStream: endStream); String toString() => 'HeadersStreamMessage(${headers.length} headers)'; } diff --git a/pkgs/http2/test/src/streams/streams_test.dart b/pkgs/http2/test/src/streams/streams_test.dart index f019ce15d2..a1d274cc2b 100644 --- a/pkgs/http2/test/src/streams/streams_test.dart +++ b/pkgs/http2/test/src/streams/streams_test.dart @@ -170,4 +170,56 @@ main() { }, count: chunks.length)); }); }); + + streamTest('single-data-request--data-trailer-response', + (ClientTransportConnection client, + ServerTransportConnection server) async { + var expectedHeaders = [new Header.ascii('key', 'value')]; + var chunk = [1]; + + server.incomingStreams.listen(expectAsync((TransportStream sStream) async { + bool isFirst = true; + var receivedChunk; + sStream.incomingMessages.listen( + expectAsync((StreamMessage msg) { + if (isFirst) { + isFirst = false; + expect(msg is HeadersStreamMessage, isTrue); + expect(msg.endStream, false); + + HeadersStreamMessage headersMsg = msg; + expectHeadersEqual(headersMsg.headers, expectedHeaders); + } else { + expect(msg is DataStreamMessage, isTrue); + expect(msg.endStream, true); + expect(receivedChunk, null); + + DataStreamMessage dataMsg = msg; + receivedChunk = dataMsg.bytes; + } + }, count: 2), onDone: expectAsync(() { + expect(receivedChunk, chunk); + sStream.sendData([2]); + sStream.sendHeaders(expectedHeaders, endStream: true); + })); + })); + + TransportStream cStream = client.makeRequest(expectedHeaders); + cStream.sendData(chunk, endStream: true); + + bool isFirst = true; + cStream.incomingMessages.listen(expectAsync((StreamMessage msg) { + if (isFirst) { + expect(msg, new isInstanceOf()); + final data = msg as DataStreamMessage; + expect(data.bytes, [2]); + isFirst = false; + } else { + expect(msg, new isInstanceOf()); + final trailer = msg as HeadersStreamMessage; + expect(trailer.endStream, true); + expectHeadersEqual(trailer.headers, expectedHeaders); + } + }, count: 2)); + }); } From fea8d606edd38d2fef57c82e4360d4446dd1660a Mon Sep 17 00:00:00 2001 From: Jakob Andersen Date: Thu, 29 Jun 2017 16:50:46 +0200 Subject: [PATCH 054/172] Fix hints and lints. (dart-lang/http2#10) Mostly updating tests to no longer use deprecated methods. After this, only two hints remain: One with a TODO, and one for default window size which isn't used. I wasn't sure what to do about those, so I left them for now. --- .../lib/src/artificial_server_socket.dart | 3 -- .../manual_test/out_of_stream_ids_test.dart | 3 +- pkgs/http2/test/client_test.dart | 6 +-- .../http2/test/multiprotocol_server_test.dart | 8 +-- .../src/async_utils/async_utils_test.dart | 10 ++-- .../test/src/connection_preface_test.dart | 6 +-- pkgs/http2/test/src/error_matchers.dart | 12 ----- .../flowcontrol/connection_queues_test.dart | 14 ++--- .../src/flowcontrol/stream_queues_test.dart | 36 ++++++------- .../src/flowcontrol/window_handler_test.dart | 10 ++-- .../src/frames/frame_defragmenter_test.dart | 8 +-- .../test/src/frames/frame_reader_test.dart | 27 ++++------ .../src/frames/frame_writer_reader_test.dart | 2 - .../test/src/frames/frame_writer_test.dart | 2 +- pkgs/http2/test/src/hpack/hpack_test.dart | 13 ++--- .../test/src/hpack/huffman_table_test.dart | 7 +-- pkgs/http2/test/src/mock_utils.dart | 2 +- .../test/src/ping/ping_handler_test.dart | 14 +++-- .../src/settings/settings_handler_test.dart | 16 +++--- pkgs/http2/test/src/streams/helper.dart | 3 +- .../test/src/streams/simple_flow_test.dart | 8 +-- .../test/src/streams/simple_push_test.dart | 8 +-- pkgs/http2/test/src/streams/streams_test.dart | 52 +++++++++---------- pkgs/http2/test/transport_test.dart | 28 +++++----- 24 files changed, 123 insertions(+), 175 deletions(-) diff --git a/pkgs/http2/lib/src/artificial_server_socket.dart b/pkgs/http2/lib/src/artificial_server_socket.dart index 4d03e772f7..1d1f181f1b 100644 --- a/pkgs/http2/lib/src/artificial_server_socket.dart +++ b/pkgs/http2/lib/src/artificial_server_socket.dart @@ -28,9 +28,6 @@ class ArtificialServerSocket extends Object final int port; - @deprecated - ServerSocketReference get reference => null; - /// Closing of an [ArtificialServerSocket] is not possible and an exception /// will be thrown when calling this method. Future close() async { diff --git a/pkgs/http2/manual_test/out_of_stream_ids_test.dart b/pkgs/http2/manual_test/out_of_stream_ids_test.dart index 8de19af718..24c62a1d21 100644 --- a/pkgs/http2/manual_test/out_of_stream_ids_test.dart +++ b/pkgs/http2/manual_test/out_of_stream_ids_test.dart @@ -42,7 +42,8 @@ main() { } expect(client.isOpen, false); - expect(() => client.makeRequest(headers), throws); + expect(() => client.makeRequest(headers), + throwsA(new isInstanceOf())); await new Future.delayed(const Duration(seconds: 1)); await client.finish(); diff --git a/pkgs/http2/test/client_test.dart b/pkgs/http2/test/client_test.dart index f477fff245..4af36bca5d 100644 --- a/pkgs/http2/test/client_test.dart +++ b/pkgs/http2/test/client_test.dart @@ -398,8 +398,8 @@ main() { var stream = client.makeRequest([new Header.ascii('a', 'b')]); var sub = stream.incomingMessages.listen( - expectAsync((StreamMessage msg) {}, count: 0), - onError: expectAsync((error) {})); + expectAsync1((StreamMessage msg) {}, count: 0), + onError: expectAsync1((error) {})); sub.pause(); await new Future.delayed(const Duration(milliseconds: 40)); sub.resume(); @@ -517,7 +517,7 @@ main() { [new Header.ascii('a', 'b')], endStream: false); // Make sure we don't get messages/pushes on the terminated stream. - stream.incomingMessages.toList().catchError(expectAsync((e) { + stream.incomingMessages.toList().catchError(expectAsync1((e) { expect('$e', contains('This stream was not processed and can ' 'therefore be retried')); })); diff --git a/pkgs/http2/test/multiprotocol_server_test.dart b/pkgs/http2/test/multiprotocol_server_test.dart index 70ff665a0f..f8f560a0aa 100644 --- a/pkgs/http2/test/multiprotocol_server_test.dart +++ b/pkgs/http2/test/multiprotocol_server_test.dart @@ -26,13 +26,13 @@ main() { var server = await MultiProtocolHttpServer.bind('localhost', 0, context); int requestNr = 0; server.startServing( - expectAsync((HttpRequest request) async { + expectAsync1((HttpRequest request) async { await handleHttp11Request(request, requestNr++); if (requestNr == Count) { await server.close(); } }, count: Count), - expectAsync((ServerTransportStream stream) { + expectAsync1((ServerTransportStream stream) { }, count: 0)); var client = new HttpClient(); @@ -50,9 +50,9 @@ main() { var server = await MultiProtocolHttpServer.bind('localhost', 0, context); int requestNr = 0; server.startServing( - expectAsync((HttpRequest request) { + expectAsync1((HttpRequest request) { }, count: 0), - expectAsync((ServerTransportStream stream) async { + expectAsync1((ServerTransportStream stream) async { await handleHttp2Request(stream, requestNr++); if (requestNr == Count) { await server.close(); diff --git a/pkgs/http2/test/src/async_utils/async_utils_test.dart b/pkgs/http2/test/src/async_utils/async_utils_test.dart index 1c0e58ef94..1af6ceba58 100644 --- a/pkgs/http2/test/src/async_utils/async_utils_test.dart +++ b/pkgs/http2/test/src/async_utils/async_utils_test.dart @@ -11,7 +11,7 @@ main() { group('async_utils', () { test('buffer-indicator', () { var bi = new BufferIndicator(); - bi.bufferEmptyEvents.listen(expectAsync((_) {}, count: 2)); + bi.bufferEmptyEvents.listen(expectAsync1((_) {}, count: 2)); expect(bi.wouldBuffer, true); @@ -39,21 +39,21 @@ main() { var bs = new BufferedSink(c); expect(bs.bufferIndicator.wouldBuffer, true); - var sub = c.stream.listen(expectAsync((_) {}, count: 2)); + var sub = c.stream.listen(expectAsync1((_) {}, count: 2)); expect(bs.bufferIndicator.wouldBuffer, false); sub.pause(); - Timer.run(expectAsync(() { + Timer.run(expectAsync0(() { expect(bs.bufferIndicator.wouldBuffer, true); bs.sink.add([1]); sub.resume(); - Timer.run(expectAsync(() { + Timer.run(expectAsync0(() { expect(bs.bufferIndicator.wouldBuffer, false); bs.sink.add([2]); - Timer.run(expectAsync(() { + Timer.run(expectAsync0(() { sub.cancel(); expect(bs.bufferIndicator.wouldBuffer, false); })); diff --git a/pkgs/http2/test/src/connection_preface_test.dart b/pkgs/http2/test/src/connection_preface_test.dart index c59ab139fa..83d99948fa 100644 --- a/pkgs/http2/test/src/connection_preface_test.dart +++ b/pkgs/http2/test/src/connection_preface_test.dart @@ -43,7 +43,7 @@ main() { } c.close(); - resultF.catchError(expectAsync((error, _) { + resultF.catchError(expectAsync2((error, _) { expect(error, contains('EOS before connection preface could be read')); })); }); @@ -58,7 +58,7 @@ main() { } c.close(); - resultF.catchError(expectAsync((error, _) { + resultF.catchError(expectAsync2((error, _) { expect(error, contains('Connection preface does not match.')); })); }); @@ -71,7 +71,7 @@ main() { c.addError('hello world'); c.close(); - resultF.catchError(expectAsync((error, _) { + resultF.catchError(expectAsync2((error, _) { expect(error, contains('hello world')); })); }); diff --git a/pkgs/http2/test/src/error_matchers.dart b/pkgs/http2/test/src/error_matchers.dart index 3ffc454834..365d34c5ef 100644 --- a/pkgs/http2/test/src/error_matchers.dart +++ b/pkgs/http2/test/src/error_matchers.dart @@ -14,9 +14,6 @@ class _ProtocolException extends TypeMatcher { bool matches(item, Map matchState) => item is ProtocolException; } -const Matcher throwsProtocolException = - const Throws(isProtocolException); - const Matcher isFrameSizeException = const _FrameSizeException(); @@ -25,9 +22,6 @@ class _FrameSizeException extends TypeMatcher { bool matches(item, Map matchState) => item is FrameSizeException; } -const Matcher throwsFrameSizeException = - const Throws(isFrameSizeException); - const Matcher isTerminatedException = const _TerminatedException(); @@ -36,9 +30,6 @@ class _TerminatedException extends TypeMatcher { bool matches(item, Map matchState) => item is TerminatedException; } -const Matcher throwsTerminatedException = - const Throws(isTerminatedException); - const Matcher isFlowControlException = const _FlowControlException(); @@ -46,6 +37,3 @@ class _FlowControlException extends TypeMatcher { const _FlowControlException() : super("FlowControlException"); bool matches(item, Map matchState) => item is FlowControlException; } - -const Matcher throwsFlowControlException = - const Throws(isFlowControlException); diff --git a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart index 6a172dba4e..2d1b1243f5 100644 --- a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart @@ -101,10 +101,10 @@ main() { windowMock.positiveWindow.markUnBuffered(); queue.startClosing(); - queue.done.then(expectAsync((_) { + queue.done.then(expectAsync1((_) { expect(queue.pendingMessages, 0); expect(() => queue.enqueueMessage(new DataMessage(99, bytes, true)), - throws); + throwsA(new isInstanceOf())); })); }); @@ -171,27 +171,19 @@ main() { class MockFrameWriter extends SmartMock implements FrameWriter { BufferIndicator bufferIndicator = new BufferIndicator(); - - dynamic noSuchMethod(_) => super.noSuchMethod(_); } class MockStreamMessageQueueIn extends SmartMock implements StreamMessageQueueIn { BufferIndicator bufferIndicator = new BufferIndicator(); - - dynamic noSuchMethod(_) => super.noSuchMethod(_); } class MockIncomingWindowHandler extends SmartMock - implements IncomingWindowHandler { - dynamic noSuchMethod(_) => super.noSuchMethod(_); -} + implements IncomingWindowHandler { } class MockOutgoingWindowHandler extends SmartMock implements OutgoingConnectionWindowHandler, OutgoingStreamWindowHandler { BufferIndicator positiveWindow = new BufferIndicator(); int peerWindowSize = new Window().size; - - dynamic noSuchMethod(_) => super.noSuchMethod(_); } diff --git a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart index fe96f00c72..b7c66dd7b3 100644 --- a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart @@ -31,11 +31,11 @@ main() { expect(queue.pendingMessages, 0); windowMock.peerWindowSize = BYTES.length; - windowMock.mock_decreaseWindow = expectAsync((int difference) { + windowMock.mock_decreaseWindow = expectAsync1((int difference) { expect(difference, BYTES.length); }); connectionQueueMock.mock_enqueueMessage = - expectAsync((Message message) { + expectAsync1((Message message) { expect(message is DataMessage, isTrue); DataMessage dataMessage = message; expect(dataMessage.bytes, BYTES); @@ -58,12 +58,12 @@ main() { // We set the window size fixed to 1, which means all the data messages // will get fragmented to 1 byte. windowMock.peerWindowSize = 1; - windowMock.mock_decreaseWindow = expectAsync((int difference) { + windowMock.mock_decreaseWindow = expectAsync1((int difference) { expect(difference, 1); }, count: BYTES.length); int counter = 0; connectionQueueMock.mock_enqueueMessage = - expectAsync((Message message) { + expectAsync1((Message message) { expect(message is DataMessage, isTrue); DataMessage dataMessage = message; expect(dataMessage.bytes, BYTES.sublist(counter, counter + 1)); @@ -87,9 +87,9 @@ main() { expect(queue.pendingMessages, 0); windowMock.peerWindowSize = 0; - windowMock.mock_decreaseWindow = expectAsync((_) {}, count: 0); + windowMock.mock_decreaseWindow = expectAsync1((_) {}, count: 0); connectionQueueMock.mock_enqueueMessage = - expectAsync((_) {}, count: 0); + expectAsync1((_) {}, count: 0); queue.enqueueMessage(new DataMessage(STREAM_ID, BYTES, true)); expect(queue.bufferIndicator.wouldBuffer, isTrue); expect(queue.pendingMessages, 1); @@ -102,16 +102,16 @@ main() { var queue = new StreamMessageQueueIn(windowMock); expect(queue.pendingMessages, 0); - queue.messages.listen(expectAsync((StreamMessage message) { + queue.messages.listen(expectAsync1((StreamMessage message) { expect(message is DataStreamMessage, isTrue); DataStreamMessage dataMessage = message; expect(dataMessage.bytes, BYTES); - }), onDone: expectAsync(() {})); - windowMock.mock_gotData = expectAsync((int difference) { + }), onDone: expectAsync0(() {})); + windowMock.mock_gotData = expectAsync1((int difference) { expect(difference, BYTES.length); }); - windowMock.mock_dataProcessed = expectAsync((int difference) { + windowMock.mock_dataProcessed = expectAsync1((int difference) { expect(difference, BYTES.length); }); queue.enqueueMessage(new DataMessage(STREAM_ID, BYTES, true)); @@ -127,14 +127,14 @@ main() { var queue = new StreamMessageQueueIn(windowMock); var sub = queue.messages.listen( - expectAsync((_) {}, count: 0), onDone: expectAsync(() {}, count: 0)); + expectAsync1((_) {}, count: 0), onDone: expectAsync0(() {}, count: 0)); sub.pause(); // We assert that we got the data, but it wasn't processed. - windowMock.mock_gotData = expectAsync((int difference) { + windowMock.mock_gotData = expectAsync1((int difference) { expect(difference, bytes.length); }); - windowMock.mock_dataProcessed = expectAsync((_) {}, count: 0); + windowMock.mock_dataProcessed = expectAsync1((_) {}, count: 0); expect(queue.pendingMessages, 0); queue.enqueueMessage(new DataMessage(STREAM_ID, bytes, true)); @@ -148,20 +148,14 @@ main() { class MockConnectionMessageQueueOut extends SmartMock - implements ConnectionMessageQueueOut { - dynamic noSuchMethod(_) => super.noSuchMethod(_); -} + implements ConnectionMessageQueueOut { } class MockIncomingWindowHandler extends SmartMock - implements IncomingWindowHandler { - dynamic noSuchMethod(_) => super.noSuchMethod(_); -} + implements IncomingWindowHandler { } class MockOutgoingStreamWindowHandler extends SmartMock implements OutgoingStreamWindowHandler { final BufferIndicator positiveWindow = new BufferIndicator(); int peerWindowSize; - - dynamic noSuchMethod(_) => super.noSuchMethod(_); } diff --git a/pkgs/http2/test/src/flowcontrol/window_handler_test.dart b/pkgs/http2/test/src/flowcontrol/window_handler_test.dart index 2f44dc1200..b6a1f6164b 100644 --- a/pkgs/http2/test/src/flowcontrol/window_handler_test.dart +++ b/pkgs/http2/test/src/flowcontrol/window_handler_test.dart @@ -18,7 +18,7 @@ main() { Window window, int initialSize) { var sub = handler.positiveWindow.bufferEmptyEvents.listen( - expectAsync((_) {}, count: 0)); + expectAsync1((_) {}, count: 0)); expect(handler.peerWindowSize, initialSize); expect(window.size, initialSize); @@ -43,7 +43,7 @@ main() { expect(handler.positiveWindow.wouldBuffer, isFalse); handler.decreaseWindow(window.size); expect(handler.positiveWindow.wouldBuffer, isTrue); - sub = handler.positiveWindow.bufferEmptyEvents.listen(expectAsync((_) { + sub = handler.positiveWindow.bufferEmptyEvents.listen(expectAsync1((_) { expect(handler.peerWindowSize, 1); expect(window.size, 1); })); @@ -84,7 +84,7 @@ main() { expect(handler.positiveWindow.wouldBuffer, isFalse); handler.positiveWindow.bufferEmptyEvents.listen( - expectAsync((_) {}, count: 0)); + expectAsync1((_) {}, count: 0)); handler.processInitialWindowSizeSettingChange(-window.size); expect(handler.positiveWindow.wouldBuffer, isTrue); expect(handler.peerWindowSize, 0); @@ -128,6 +128,4 @@ main() { }); } -class FrameWriterMock extends SmartMock implements FrameWriter { - dynamic noSuchMethod(_) => super.noSuchMethod(_); -} +class FrameWriterMock extends SmartMock implements FrameWriter { } diff --git a/pkgs/http2/test/src/frames/frame_defragmenter_test.dart b/pkgs/http2/test/src/frames/frame_defragmenter_test.dart index ec39517718..c3eedb46bd 100644 --- a/pkgs/http2/test/src/frames/frame_defragmenter_test.dart +++ b/pkgs/http2/test/src/frames/frame_defragmenter_test.dart @@ -88,7 +88,7 @@ main() { expect(defrag.tryDefragmentFrame(f1), isNull); expect(() => defrag.tryDefragmentFrame(f2), - throwsProtocolException); + throwsA(isProtocolException)); }); test('fragmented-push-promise-frame', () { @@ -99,7 +99,7 @@ main() { expect(defrag.tryDefragmentFrame(f1), isNull); expect(() => defrag.tryDefragmentFrame(f2), - throwsProtocolException); + throwsA(isProtocolException)); }); test('fragmented-headers-frame--no-continuation-frame', () { @@ -110,7 +110,7 @@ main() { expect(defrag.tryDefragmentFrame(f1), isNull); expect(() => defrag.tryDefragmentFrame(f2), - throwsProtocolException); + throwsA(isProtocolException)); }); test('fragmented-push-promise-no-continuation-frame', () { @@ -121,7 +121,7 @@ main() { expect(defrag.tryDefragmentFrame(f1), isNull); expect(() => defrag.tryDefragmentFrame(f2), - throwsProtocolException); + throwsA(isProtocolException)); }); test('push-without-headres-or-push-promise-frame', () { diff --git a/pkgs/http2/test/src/frames/frame_reader_test.dart b/pkgs/http2/test/src/frames/frame_reader_test.dart index 817c1df92c..1b80241619 100644 --- a/pkgs/http2/test/src/frames/frame_reader_test.dart +++ b/pkgs/http2/test/src/frames/frame_reader_test.dart @@ -6,12 +6,9 @@ import 'dart:async'; import 'package:test/test.dart'; -import 'package:http2/src/hpack/hpack.dart'; import 'package:http2/src/frames/frames.dart'; import 'package:http2/src/settings/settings.dart'; -import '../hpack/hpack_test.dart' show isHeader; - main() { group('frames', () { group('frame-reader', () { @@ -19,7 +16,6 @@ main() { Stream dataFrame(List body) { var settings = new ActiveSettings(); - var context = new HPackContext(); var controller = new StreamController>(); var reader = new FrameReader(controller.stream, settings); @@ -41,7 +37,7 @@ main() { test('data-frame--max-frame-size', () { var body = new List.filled(maxFrameSize, 0x42); dataFrame(body).listen( - expectAsync((Frame frame) { + expectAsync1((Frame frame) { expect(frame is DataFrame, isTrue); expect(frame.header.length, body.length); expect(frame.header.flags, 0); @@ -50,14 +46,14 @@ main() { expect(dataFrame.hasPaddedFlag, isFalse); expect(dataFrame.bytes, body); }), - onError: expectAsync((error, stack) {}, count: 0)); + onError: expectAsync2((error, stack) {}, count: 0)); }); test('data-frame--max-frame-size-plus-1', () { var body = new List.filled(maxFrameSize + 1, 0x42); dataFrame(body).listen( - expectAsync((_) {}, count: 0), - onError: expectAsync((error, stack) { + expectAsync1((_) {}, count: 0), + onError: expectAsync2((error, stack) { expect('$error', contains('Incoming frame is too big')); })); }); @@ -65,15 +61,14 @@ main() { test('incomplete-header', () { var settings = new ActiveSettings(); - var context = new HPackContext(); var controller = new StreamController>(); var reader = new FrameReader(controller.stream, settings); controller..add([1])..close(); reader.startDecoding().listen( - expectAsync((_) {}, count: 0), - onError: expectAsync((error, stack) { + expectAsync1((_) {}, count: 0), + onError: expectAsync2((error, stack) { expect('$error', contains('incomplete frame')); })); }); @@ -81,7 +76,6 @@ main() { test('incomplete-frame', () { var settings = new ActiveSettings(); - var context = new HPackContext(); var controller = new StreamController>(); var reader = new FrameReader(controller.stream, settings); @@ -93,8 +87,8 @@ main() { controller..add([0, 0, 255, 0, 0, 0, 0, 0, 1])..close(); reader.startDecoding().listen( - expectAsync((_) {}, count: 0), - onError: expectAsync((error, stack) { + expectAsync1((_) {}, count: 0), + onError: expectAsync2((error, stack) { expect('$error', contains('incomplete frame')); })); }); @@ -102,15 +96,14 @@ main() { test('connection-error', () { var settings = new ActiveSettings(); - var context = new HPackContext(); var controller = new StreamController>(); var reader = new FrameReader(controller.stream, settings); controller..addError('hello world')..close(); reader.startDecoding().listen( - expectAsync((_) {}, count: 0), - onError: expectAsync((error, stack) { + expectAsync1((_) {}, count: 0), + onError: expectAsync2((error, stack) { expect('$error', contains('hello world')); })); }); diff --git a/pkgs/http2/test/src/frames/frame_writer_reader_test.dart b/pkgs/http2/test/src/frames/frame_writer_reader_test.dart index 11ce7cbb26..b65004602f 100644 --- a/pkgs/http2/test/src/frames/frame_writer_reader_test.dart +++ b/pkgs/http2/test/src/frames/frame_writer_reader_test.dart @@ -12,8 +12,6 @@ import 'package:http2/src/settings/settings.dart'; import '../hpack/hpack_test.dart' show isHeader; -import '../error_matchers.dart'; - main() { group('frames', () { group('writer-reader', () { diff --git a/pkgs/http2/test/src/frames/frame_writer_test.dart b/pkgs/http2/test/src/frames/frame_writer_test.dart index d523e8209e..e7ff6ae91a 100644 --- a/pkgs/http2/test/src/frames/frame_writer_test.dart +++ b/pkgs/http2/test/src/frames/frame_writer_test.dart @@ -19,7 +19,7 @@ main() { var controller = new StreamController>(); var writer = new FrameWriter(context.encoder, controller, settings); - writer.doneFuture.then(expectAsync((_) { + writer.doneFuture.then(expectAsync1((_) { // We expect that the writer is done at this point. })); diff --git a/pkgs/http2/test/src/hpack/hpack_test.dart b/pkgs/http2/test/src/hpack/hpack_test.dart index 1c06a8f172..02e8d02a1c 100644 --- a/pkgs/http2/test/src/hpack/hpack_test.dart +++ b/pkgs/http2/test/src/hpack/hpack_test.dart @@ -184,19 +184,19 @@ main() { test('invalid-integer-encoding', () { var context = new HPackContext(); expect(() => context.decoder.decode([1 << 6, 0xff]), - throwsHuffmanDecodingException); + throwsA(isHPackDecodingException)); }); test('index-out-of-table-size', () { var context = new HPackContext(); expect(() => context.decoder.decode([0x7f]), - throwsHuffmanDecodingException); + throwsA(isHPackDecodingException)); }); test('invalid-update-dynamic-table-size', () { var context = new HPackContext(); expect(() => context.decoder.decode([0x3f]), - throwsHuffmanDecodingException); + throwsA(isHPackDecodingException)); }); test('update-dynamic-table-size-too-high', () { @@ -204,7 +204,7 @@ main() { // Tries to set dynamic table to 4097 (max is 4096 by default) var bytes = TestHelper.newInteger(0x20, 5, 4097); expect(() => context.decoder.decode(bytes), - throwsHuffmanDecodingException); + throwsA(isHPackDecodingException)); }); }); @@ -420,11 +420,6 @@ class _HPackDecodingException extends TypeMatcher { bool matches(item, Map matchState) => item is HPackDecodingException; } -const Matcher throwsHuffmanDecodingException = - const Throws(isHPackDecodingException); - - - class _HeaderMatcher extends Matcher { final Header header; diff --git a/pkgs/http2/test/src/hpack/huffman_table_test.dart b/pkgs/http2/test/src/hpack/huffman_table_test.dart index 21054711f3..a700130c3e 100644 --- a/pkgs/http2/test/src/hpack/huffman_table_test.dart +++ b/pkgs/http2/test/src/hpack/huffman_table_test.dart @@ -49,7 +49,7 @@ main() { ]; for (var test in data) { - expect(() => decode(test), throwsHuffmanDecodingException); + expect(() => decode(test), throwsA(isHuffmanDecodingException)); } }); @@ -63,7 +63,7 @@ main() { ]; for (var test in data) { - expect(() => decode(test), throwsHuffmanDecodingException); + expect(() => decode(test), throwsA(isHuffmanDecodingException)); } }); @@ -150,6 +150,3 @@ class _HuffmanDecodingException extends TypeMatcher { const _HuffmanDecodingException() : super("HuffmanDecodingException"); bool matches(item, Map matchState) => item is HuffmanDecodingException; } - -const Matcher throwsHuffmanDecodingException = - const Throws(isHuffmanDecodingException); diff --git a/pkgs/http2/test/src/mock_utils.dart b/pkgs/http2/test/src/mock_utils.dart index 888a20304d..e62a4f7dc8 100644 --- a/pkgs/http2/test/src/mock_utils.dart +++ b/pkgs/http2/test/src/mock_utils.dart @@ -77,7 +77,7 @@ class SmartMock { /// arguments /// (e.g. `expectAsync((List settings, {bool ack: true}) {})`). class TestCounter { - final Function _complete = expectAsync(() {}); + final Function _complete = expectAsync0(() {}); final int count; int _got = 0; diff --git a/pkgs/http2/test/src/ping/ping_handler_test.dart b/pkgs/http2/test/src/ping/ping_handler_test.dart index c4a9bff2b3..f420754faa 100644 --- a/pkgs/http2/test/src/ping/ping_handler_test.dart +++ b/pkgs/http2/test/src/ping/ping_handler_test.dart @@ -72,11 +72,11 @@ main() { var header = new FrameHeader(8, FrameType.PING, PingFrame.FLAG_ACK, 0); expect(() => pingHandler.processPingFrame(new PingFrame(header, 2)), - throwsProtocolException); + throwsA(isProtocolException)); // Ensure outstanding pings will be completed with an error once we call // `pingHandler.terminate()`. - future.catchError(expectAsync((error, _) { + future.catchError(expectAsync2((error, _) { expect(error, 'hello world'); })); pingHandler.terminate('hello world'); @@ -88,9 +88,9 @@ main() { pingHandler.terminate('hello world'); expect(() => pingHandler.processPingFrame(null), - throwsTerminatedException); + throwsA(isTerminatedException)); expect(pingHandler.ping(), - throwsTerminatedException); + throwsA(isTerminatedException)); }); test('ping-non-zero-stream-id', () async { @@ -99,11 +99,9 @@ main() { var header = new FrameHeader(8, FrameType.PING, PingFrame.FLAG_ACK, 1); expect(() => pingHandler.processPingFrame(new PingFrame(header, 1)), - throwsProtocolException); + throwsA(isProtocolException)); }); }); } -class FrameWriterMock extends SmartMock implements FrameWriter { - dynamic noSuchMethod(_) => super.noSuchMethod(_); -} +class FrameWriterMock extends SmartMock implements FrameWriter { } diff --git a/pkgs/http2/test/src/settings/settings_handler_test.dart b/pkgs/http2/test/src/settings/settings_handler_test.dart index 14dcb572a5..fb2a5c1847 100644 --- a/pkgs/http2/test/src/settings/settings_handler_test.dart +++ b/pkgs/http2/test/src/settings/settings_handler_test.dart @@ -83,7 +83,7 @@ main() { var settingsFrame = new SettingsFrame(header, const []); expect(() => sh.handleSettingsFrame(settingsFrame), - throwsProtocolException); + throwsA(isProtocolException)); }); test('invalid-remote-settings-change', () { @@ -99,7 +99,7 @@ main() { var header = new FrameHeader(6, FrameType.SETTINGS, 0, 0); var settingsFrame = new SettingsFrame(header, invalidPushSettings); expect(() => sh.handleSettingsFrame(settingsFrame), - throwsProtocolException); + throwsA(isProtocolException)); }); test('change-max-header-table-size', () { @@ -112,19 +112,15 @@ main() { // Simulate remote end by setting the push setting. var header = new FrameHeader(6, FrameType.SETTINGS, 0, 0); var settingsFrame = new SettingsFrame(header, setMaxTable256); - mock.mock_updateMaxSendingHeaderTableSize = expectAsync((int newSize) { + mock.mock_updateMaxSendingHeaderTableSize = expectAsync1((int newSize) { expect(newSize, 256); }); - writer.mock_writeSettingsAckFrame = expectAsync(() { }); + writer.mock_writeSettingsAckFrame = expectAsync0(() { }); sh.handleSettingsFrame(settingsFrame); }); }); } -class FrameWriterMock extends SmartMock implements FrameWriter { - dynamic noSuchMethod(_) => super.noSuchMethod(_); -} +class FrameWriterMock extends SmartMock implements FrameWriter { } -class HPackEncoderMock extends SmartMock implements HPackEncoder { - dynamic noSuchMethod(_) => super.noSuchMethod(_); -} +class HPackEncoderMock extends SmartMock implements HPackEncoder { } diff --git a/pkgs/http2/test/src/streams/helper.dart b/pkgs/http2/test/src/streams/helper.dart index e05b1bfa99..7e370d9637 100644 --- a/pkgs/http2/test/src/streams/helper.dart +++ b/pkgs/http2/test/src/streams/helper.dart @@ -10,7 +10,6 @@ import 'package:test/test.dart'; import 'package:http2/transport.dart'; import 'package:http2/src/frames/frames.dart'; -import 'package:http2/src/connection.dart'; import 'package:http2/src/settings/settings.dart'; expectHeadersEqual(List
headers, List
expectedHeaders) { @@ -22,7 +21,7 @@ expectHeadersEqual(List
headers, List
expectedHeaders) { } expectEmptyStream(Stream s) { - s.listen(expectAsync((_) {}, count: 0), onDone: expectAsync(() {})); + s.listen(expectAsync1((_) {}, count: 0), onDone: expectAsync0(() {})); } streamTest(String name, func(client, server), {ClientSettings settings}) { diff --git a/pkgs/http2/test/src/streams/simple_flow_test.dart b/pkgs/http2/test/src/streams/simple_flow_test.dart index d6cab374bd..347e84eb0f 100644 --- a/pkgs/http2/test/src/streams/simple_flow_test.dart +++ b/pkgs/http2/test/src/streams/simple_flow_test.dart @@ -21,7 +21,7 @@ main() { allBytes.addAll(new List.generate(42, (i) => 42)); headersTestFun(String type) { - return expectAsync((StreamMessage msg) { + return expectAsync1((StreamMessage msg) { expect(msg is HeadersStreamMessage, isTrue); expect((msg as HeadersStreamMessage).headers.first.name, expectedHeaders.first.name); @@ -75,9 +75,9 @@ main() { (ClientTransportConnection client, ServerTransportConnection server) async { server.incomingStreams.listen( - expectAsync((TransportStream sStream) async { + expectAsync1((TransportStream sStream) async { sStream.incomingMessages.listen( - messageTestFun('server'), onDone: expectAsync(() { })); + messageTestFun('server'), onDone: expectAsync0(() { })); sStream.sendHeaders(expectedHeaders, endStream: true); expect(await serverReceivedAllBytes.future, completes); })); @@ -85,7 +85,7 @@ main() { TransportStream cStream = client.makeRequest(expectedHeaders); sendData(cStream); cStream.incomingMessages.listen( - headersTestFun('client'), onDone: expectAsync(() {})); + headersTestFun('client'), onDone: expectAsync0(() {})); }); }); }); diff --git a/pkgs/http2/test/src/streams/simple_push_test.dart b/pkgs/http2/test/src/streams/simple_push_test.dart index d75ec61f9b..844dbf6a0f 100644 --- a/pkgs/http2/test/src/streams/simple_push_test.dart +++ b/pkgs/http2/test/src/streams/simple_push_test.dart @@ -30,7 +30,7 @@ main() { } headersTestFun() { - return expectAsync((StreamMessage msg) { + return expectAsync1((StreamMessage msg) { expect(msg is HeadersStreamMessage, isTrue); testHeaders((msg as HeadersStreamMessage).headers); }); @@ -62,7 +62,7 @@ main() { (ClientTransportConnection client, ServerTransportConnection server) async { server.incomingStreams.listen( - expectAsync((ServerTransportStream sStream) async { + expectAsync1((ServerTransportStream sStream) async { var pushStream = sStream.push(expectedHeaders); pushStream.sendHeaders(expectedHeaders); await sendData(pushStream, 'pushing "hello world" :)'); @@ -76,8 +76,8 @@ main() { ClientTransportStream cStream = client.makeRequest(expectedHeaders, endStream: true); cStream.incomingMessages.listen( - headersTestFun(), onDone: expectAsync(() { })); - cStream.peerPushes.listen(expectAsync((TransportStreamPush push) async { + headersTestFun(), onDone: expectAsync0(() { })); + cStream.peerPushes.listen(expectAsync1((TransportStreamPush push) async { testHeaders(push.requestHeaders); var iterator = new StreamIterator(push.stream.incomingMessages); diff --git a/pkgs/http2/test/src/streams/streams_test.dart b/pkgs/http2/test/src/streams/streams_test.dart index a1d274cc2b..5f88a8ebea 100644 --- a/pkgs/http2/test/src/streams/streams_test.dart +++ b/pkgs/http2/test/src/streams/streams_test.dart @@ -16,13 +16,13 @@ main() { ServerTransportConnection server) async { var expectedHeaders = [new Header.ascii('key', 'value')]; - server.incomingStreams.listen(expectAsync((TransportStream sStream) { - sStream.incomingMessages.listen(expectAsync((StreamMessage msg) { + server.incomingStreams.listen(expectAsync1((TransportStream sStream) { + sStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { expect(msg is HeadersStreamMessage, isTrue); HeadersStreamMessage headersMsg = msg; expectHeadersEqual(headersMsg.headers, expectedHeaders); - }), onDone: expectAsync(() {})); + }), onDone: expectAsync0(() {})); sStream.outgoingMessages.close(); })); @@ -36,13 +36,13 @@ main() { ServerTransportConnection server) async { var expectedHeaders = [new Header.ascii('key', 'value')]; - server.incomingStreams.listen(expectAsync((TransportStream sStream) { - sStream.incomingMessages.listen(expectAsync((StreamMessage msg) { + server.incomingStreams.listen(expectAsync1((TransportStream sStream) { + sStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { expect(msg is HeadersStreamMessage, isTrue); HeadersStreamMessage headersMsg = msg; expectHeadersEqual(headersMsg.headers, expectedHeaders); - }, count: 3), onDone: expectAsync(() {})); + }, count: 3), onDone: expectAsync0(() {})); sStream.outgoingMessages.close(); })); @@ -59,10 +59,10 @@ main() { var chunks = [[1], [2], [3]]; server.incomingStreams.listen( - expectAsync((TransportStream sStream) async { + expectAsync1((TransportStream sStream) async { bool isFirst = true; var receivedChunks = []; - sStream.incomingMessages.listen(expectAsync((StreamMessage msg) { + sStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { if (isFirst) { isFirst = false; expect(msg is HeadersStreamMessage, isTrue); @@ -75,7 +75,7 @@ main() { DataStreamMessage dataMsg = msg; receivedChunks.add(dataMsg.bytes); } - }, count: 1 + chunks.length), onDone: expectAsync(() { + }, count: 1 + chunks.length), onDone: expectAsync0(() { expect(receivedChunks, chunks); })); sStream.outgoingMessages.close(); @@ -92,25 +92,25 @@ main() { ServerTransportConnection server) async { var expectedHeaders = [new Header.ascii('key', 'value')]; - server.incomingStreams.listen(expectAsync((TransportStream sStream) { - sStream.incomingMessages.listen(expectAsync((StreamMessage msg) { + server.incomingStreams.listen(expectAsync1((TransportStream sStream) { + sStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { expect(msg is HeadersStreamMessage, isTrue); HeadersStreamMessage headersMsg = msg; expectHeadersEqual(headersMsg.headers, expectedHeaders); - }), onDone: expectAsync(() {})); + }), onDone: expectAsync0(() {})); sStream.sendHeaders(expectedHeaders, endStream: true); })); TransportStream cStream = client.makeRequest(expectedHeaders, endStream: true); - cStream.incomingMessages.listen(expectAsync((StreamMessage msg) { + cStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { expect(msg is HeadersStreamMessage, isTrue); HeadersStreamMessage headersMsg = msg; expectHeadersEqual(headersMsg.headers, expectedHeaders); - }), onDone: expectAsync(() {})); + }), onDone: expectAsync0(() {})); }); streamTest('single-header-request--multi-headers-response', @@ -118,13 +118,13 @@ main() { ServerTransportConnection server) async { var expectedHeaders = [new Header.ascii('key', 'value')]; - server.incomingStreams.listen(expectAsync((TransportStream sStream) { - sStream.incomingMessages.listen(expectAsync((StreamMessage msg) { + server.incomingStreams.listen(expectAsync1((TransportStream sStream) { + sStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { expect(msg is HeadersStreamMessage, isTrue); HeadersStreamMessage headersMsg = msg; expectHeadersEqual(headersMsg.headers, expectedHeaders); - }), onDone: expectAsync(() {})); + }), onDone: expectAsync0(() {})); sStream.sendHeaders(expectedHeaders); sStream.sendHeaders(expectedHeaders); @@ -134,7 +134,7 @@ main() { TransportStream cStream = client.makeRequest(expectedHeaders, endStream: true); - cStream.incomingMessages.listen(expectAsync((StreamMessage msg) { + cStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { expect(msg is HeadersStreamMessage, isTrue); HeadersStreamMessage headersMsg = msg; @@ -148,13 +148,13 @@ main() { var expectedHeaders = [new Header.ascii('key', 'value')]; var chunks = [[1], [2], [3]]; - server.incomingStreams.listen(expectAsync((TransportStream sStream) { - sStream.incomingMessages.listen(expectAsync((StreamMessage msg) { + server.incomingStreams.listen(expectAsync1((TransportStream sStream) { + sStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { expect(msg is HeadersStreamMessage, isTrue); HeadersStreamMessage headersMsg = msg; expectHeadersEqual(headersMsg.headers, expectedHeaders); - }), onDone: expectAsync(() {})); + }), onDone: expectAsync0(() {})); chunks.forEach(sStream.sendData); sStream.outgoingMessages.close(); @@ -164,7 +164,7 @@ main() { cStream.outgoingMessages.close(); int i = 0; - cStream.incomingMessages.listen(expectAsync((StreamMessage msg) { + cStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { expect(msg is DataStreamMessage, isTrue); expect((msg as DataStreamMessage).bytes, chunks[i++]); }, count: chunks.length)); @@ -177,11 +177,11 @@ main() { var expectedHeaders = [new Header.ascii('key', 'value')]; var chunk = [1]; - server.incomingStreams.listen(expectAsync((TransportStream sStream) async { + server.incomingStreams.listen(expectAsync1((TransportStream sStream) async { bool isFirst = true; var receivedChunk; sStream.incomingMessages.listen( - expectAsync((StreamMessage msg) { + expectAsync1((StreamMessage msg) { if (isFirst) { isFirst = false; expect(msg is HeadersStreamMessage, isTrue); @@ -197,7 +197,7 @@ main() { DataStreamMessage dataMsg = msg; receivedChunk = dataMsg.bytes; } - }, count: 2), onDone: expectAsync(() { + }, count: 2), onDone: expectAsync0(() { expect(receivedChunk, chunk); sStream.sendData([2]); sStream.sendHeaders(expectedHeaders, endStream: true); @@ -208,7 +208,7 @@ main() { cStream.sendData(chunk, endStream: true); bool isFirst = true; - cStream.incomingMessages.listen(expectAsync((StreamMessage msg) { + cStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { if (isFirst) { expect(msg, new isInstanceOf()); final data = msg as DataStreamMessage; diff --git a/pkgs/http2/test/transport_test.dart b/pkgs/http2/test/transport_test.dart index 970a99a3fe..734af63dd3 100644 --- a/pkgs/http2/test/transport_test.dart +++ b/pkgs/http2/test/transport_test.dart @@ -21,7 +21,7 @@ main() { transportTest('terminated-client-ping', (TransportConnection client, TransportConnection server) async { - var clientError = client.ping().catchError(expectAsync((e, s) { + var clientError = client.ping().catchError(expectAsync2((e, s) { expect(e is TransportException, isTrue); })); await client.terminate(); @@ -29,17 +29,17 @@ main() { // NOTE: Now the connection is dead and client/server should complete // with [TransportException]s when doing work (e.g. ping). - client.ping().catchError(expectAsync((e, s) { + client.ping().catchError(expectAsync2((e, s) { expect(e is TransportException, isTrue); })); - server.ping().catchError(expectAsync((e, s) { + server.ping().catchError(expectAsync2((e, s) { expect(e is TransportException, isTrue); })); }); transportTest('terminated-server-ping', (TransportConnection client, TransportConnection server) async { - var clientError = client.ping().catchError(expectAsync((e, s) { + var clientError = client.ping().catchError(expectAsync2((e, s) { expect(e is TransportException, isTrue); })); await server.terminate(); @@ -47,10 +47,10 @@ main() { // NOTE: Now the connection is dead and the client/server should complete // with [TransportException]s when doing work (e.g. ping). - client.ping().catchError(expectAsync((e, s) { + client.ping().catchError(expectAsync2((e, s) { expect(e is TransportException, isTrue); })); - server.ping().catchError(expectAsync((e, s) { + server.ping().catchError(expectAsync2((e, s) { expect(e is TransportException, isTrue); })); }); @@ -58,9 +58,10 @@ main() { transportTest('disabled-push', (ClientTransportConnection client, ServerTransportConnection server) async { server.incomingStreams.listen( - expectAsync((ServerTransportStream stream) async { + expectAsync1((ServerTransportStream stream) async { expect(stream.canPush, false); - expect(() => stream.push([new Header.ascii('a', 'b')]), throws); + expect(() => stream.push([new Header.ascii('a', 'b')]), + throwsA(new isInstanceOf())); stream.sendHeaders([new Header.ascii('x', 'y')], endStream: true); })); @@ -95,7 +96,8 @@ main() { // Now we should have reached the limit and we should not be able to // create more pushes. expect(stream.canPush, false); - expect(() => stream.push([new Header.ascii('a', 'b')]), throws); + expect(() => stream.push([new Header.ascii('a', 'b')]), + throwsA(new isInstanceOf())); // Finish the pushes for (ServerTransportStream pushedStream in pushes) { @@ -181,10 +183,10 @@ main() { Future serverFun() async { await for (ServerTransportStream stream in server.incomingStreams) { stream.sendHeaders([new Header.ascii('x', 'y')], endStream: true); - stream.incomingMessages.listen(expectAsync((msg) { + stream.incomingMessages.listen(expectAsync1((msg) { expect(msg is HeadersStreamMessage, true); readyForError.complete(); - }), onError: expectAsync((error) { + }), onError: expectAsync1((error) { expect('$error', contains('Stream was terminated by peer')); })); } @@ -216,7 +218,7 @@ main() { Future clientFun() async { var headers = [new Header.ascii('a', 'b')]; var stream = client.makeRequest(headers, endStream: true); - await stream.incomingMessages.toList().catchError(expectAsync((error) { + await stream.incomingMessages.toList().catchError(expectAsync1((error) { expect('$error', contains('Stream was terminated by peer')); })); await client.finish(); @@ -270,7 +272,7 @@ main() { onListen: () { addData(); }, - onPause: expectAsync(() { + onPause: expectAsync0(() { // Assert that we're now at the place (since the granularity // of adding is [kChunkSize], it could be that we added // [kChunkSize - 1] bytes more than allowed, before getting From 17b3e7247850375ba789b6bf6f8f542c71df2598 Mon Sep 17 00:00:00 2001 From: Jakob Andersen Date: Mon, 3 Jul 2017 15:27:12 +0200 Subject: [PATCH 055/172] Add hook for receiving stream RST error. (dart-lang/http2#9) * Add hook for receiving stream RST error. Normally, a stream RST is delivered as an error on the incoming stream, but if the incoming stream has already been consumed, there is no good way to detect that the stream has been terminated. Added a `onCancel` hook, allowing user code to register a callback that is invoked when a RST frame is received. The gRPC code uses this to detect when a call has been canceled. * Review feedback. Renamed onCancel to onTerminated and made it a setter. Updated SDK dependency for new Function syntax. * Missed one. * Added test for server termination. Made _handleTerminate private. --- .../http2/lib/src/streams/stream_handler.dart | 23 ++++++ pkgs/http2/lib/transport.dart | 5 ++ pkgs/http2/pubspec.yaml | 2 +- pkgs/http2/test/transport_test.dart | 72 +++++++++++++++++++ 4 files changed, 101 insertions(+), 1 deletion(-) diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index 9405df0cd3..c9ece4640e 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -61,6 +61,13 @@ class Http2StreamImpl extends TransportStream // The state of this stream. StreamState state = StreamState.Idle; + // Error code from RST_STREAM frame, if the stream has been terminated + // remotely. + int _terminatedErrorCode; + + // Termination handler. Invoked if the stream receives an RST_STREAM frame. + void Function(int) _onTerminated; + final Function _canPushFun; final Function _pushStreamFun; final Function _terminateStreamFun; @@ -95,6 +102,20 @@ class Http2StreamImpl extends TransportStream => _pushStreamFun(this, requestHeaders); void terminate() => _terminateStreamFun(this); + + set onTerminated(void handler(int)) { + _onTerminated = handler; + if (_terminatedErrorCode != null && _onTerminated != null) { + _onTerminated(_terminatedErrorCode); + } + } + + void _handleTerminated(int errorCode) { + _terminatedErrorCode = errorCode; + if (_onTerminated != null) { + _onTerminated(_terminatedErrorCode); + } + } } /// Handles [Frame]s with a non-zero stream-id. @@ -597,6 +618,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { } void _handleRstFrame(Http2StreamImpl stream, RstStreamFrame frame) { + stream._handleTerminated(frame.errorCode); var exception = new StreamException( stream.id, 'Stream was terminated by peer.'); _closeStreamAbnormally(stream, exception, propagateException: true); @@ -706,6 +728,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { } stream.incomingQueue.terminate(propagateException ? exception : null); stream._outgoingCSubscription.cancel(); + stream._outgoingC.close(); // NOTE: we're not adding an error here. stream.outgoingQueue.terminate(); diff --git a/pkgs/http2/lib/transport.dart b/pkgs/http2/lib/transport.dart index 46840e60cc..7e29178568 100644 --- a/pkgs/http2/lib/transport.dart +++ b/pkgs/http2/lib/transport.dart @@ -210,6 +210,11 @@ abstract class TransportStream { /// A sink for writing data and/or headers to the remote end. StreamSink get outgoingMessages; + /// Set the termination handler on this stream. + /// + /// The handler will be called if the stream receives an RST_STREAM frame. + set onTerminated(void value(int)); + /// Terminates this HTTP/2 stream in an un-normal way. /// /// For normal termination, one can cancel the [StreamSubscription] from diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index c5eeb302ac..1d3121aa32 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -5,7 +5,7 @@ author: Dart Team homepage: https://github.com/dart-lang/http2 environment: - sdk: '>=1.13.0-dev.1 <2.0.0' + sdk: '>=1.24.0-dev.6.7 <2.0.0' dev_dependencies: test: '>=0.12.0 <0.13.0' diff --git a/pkgs/http2/test/transport_test.dart b/pkgs/http2/test/transport_test.dart index 734af63dd3..11fde21bab 100644 --- a/pkgs/http2/test/transport_test.dart +++ b/pkgs/http2/test/transport_test.dart @@ -227,6 +227,78 @@ main() { await Future.wait([serverFun(), clientFun()]); }); + transportTest('client-terminates-stream-after-half-close', + (ClientTransportConnection client, + ServerTransportConnection server) async { + + var readyForError = new Completer(); + + Future serverFun() async { + await for (ServerTransportStream stream in server.incomingStreams) { + stream.onTerminated = expectAsync1((errorCode) { + expect(errorCode, 8); + }, count: 1); + stream.sendHeaders([new Header.ascii('x', 'y')], endStream: false); + stream.incomingMessages.listen( + expectAsync1((msg) { + expect(msg is HeadersStreamMessage, true); + }), + onError: expectAsync1((_) {}, count: 0), + onDone: expectAsync0(() { + readyForError.complete(); + }, count: 1), + ); + } + await server.finish(); + } + + Future clientFun() async { + var headers = [new Header.ascii('a', 'b')]; + var stream = client.makeRequest(headers, endStream: true); + await stream.outgoingMessages.close(); + await readyForError.future; + stream.terminate(); + await client.finish(); + } + + await Future.wait([serverFun(), clientFun()]); + }); + + transportTest('server-terminates-stream-after-half-close', + (ClientTransportConnection client, + ServerTransportConnection server) async { + + var readyForError = new Completer(); + + Future serverFun() async { + await for (ServerTransportStream stream in server.incomingStreams) { + stream.sendHeaders([new Header.ascii('x', 'y')], endStream: false); + stream.incomingMessages.listen( + expectAsync1((msg) async { + expect(msg is HeadersStreamMessage, true); + await readyForError.future; + stream.terminate(); + }), + onError: expectAsync1((_) {}, count: 0), + onDone: expectAsync0(() {}, count: 1), + ); + } + await server.finish(); + } + + Future clientFun() async { + var headers = [new Header.ascii('a', 'b')]; + var stream = client.makeRequest(headers, endStream: false); + stream.onTerminated = expectAsync1((errorCode) { + expect(errorCode, 8); + }, count: 1); + readyForError.complete(); + await client.finish(); + } + + await Future.wait([serverFun(), clientFun()]); + }); + group('flow-control', () { const int kChunkSize = 1024; const int kNumberOfMessages = 1000; From 5099c35945f98613818f6c11dd91f0fa35a2a904 Mon Sep 17 00:00:00 2001 From: Jakob Andersen Date: Wed, 5 Jul 2017 17:43:24 +0200 Subject: [PATCH 056/172] Update change log and pubspec for release 0.1.2. (dart-lang/http2#11) --- pkgs/http2/CHANGELOG.md | 7 +++++++ pkgs/http2/pubspec.yaml | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index cc099dbfcc..1fa469cf1e 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 0.1.2 + +* The endStream bit is now set on the requested frame, instead of on an empty + data frame following it. +* Added an `onTerminated` hook that is called when a TransportStream receives + a RST_STREAM frame. + ## 0.1.1+2 * Add errorCode to exception toString message. diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index 1d3121aa32..ab62edd0c3 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -1,5 +1,5 @@ name: http2 -version: 0.1.1+2 +version: 0.1.2 description: A HTTP/2 implementation in Dart. author: Dart Team homepage: https://github.com/dart-lang/http2 From 6dacf783679c6514c23494c55139990cd18b8ce9 Mon Sep 17 00:00:00 2001 From: Jakob Andersen Date: Wed, 12 Jul 2017 19:16:36 +0200 Subject: [PATCH 057/172] Window updates. (dart-lang/http2#12) Make sure the positiveWindow is marked un-buffered if the window opens due to an increase in initial window size. Otherwise, even sub-sequent WindowUpdate frames wouldn't cause a bufferEmpty event to get emitted. Also, if the connection preface doesn't match, don't try to add data to the terminated connection. --- pkgs/http2/CHANGELOG.md | 5 +++++ pkgs/http2/lib/src/connection_preface.dart | 9 +++++---- pkgs/http2/lib/src/flowcontrol/window.dart | 15 +-------------- .../http2/lib/src/flowcontrol/window_handler.dart | 6 +++--- .../test/src/flowcontrol/window_handler_test.dart | 7 ++++++- 5 files changed, 20 insertions(+), 22 deletions(-) diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index 1fa469cf1e..44e918ccf8 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## Unreleased + +* Fixed a bug where a closed window would not open correctly due to an increase + in initial window size. + ## 0.1.2 * The endStream bit is now set on the requested frame, instead of on an empty diff --git a/pkgs/http2/lib/src/connection_preface.dart b/pkgs/http2/lib/src/connection_preface.dart index 9e604ab6d0..22c9e29b63 100644 --- a/pkgs/http2/lib/src/connection_preface.dart +++ b/pkgs/http2/lib/src/connection_preface.dart @@ -38,15 +38,16 @@ Stream> readConnectionPreface(Stream> incoming) { terminated = true; } - void compareConnectionPreface(List data) { + bool compareConnectionPreface(List data) { for (int i = 0; i < CONNECTION_PREFACE.length; i++) { if (data[i] != CONNECTION_PREFACE[i]) { terminate('Connection preface does not match.'); - break; + return false; } } prefaceBuffer = null; connectionPrefaceRead = true; + return true; } void onData(List data) { @@ -55,7 +56,7 @@ Stream> readConnectionPreface(Stream> incoming) { result.add(data); } else { if (prefaceBuffer.isEmpty && data.length > CONNECTION_PREFACE.length) { - compareConnectionPreface(data); + if (!compareConnectionPreface(data)) return; data = data.sublist(CONNECTION_PREFACE.length); } else if (prefaceBuffer.length < CONNECTION_PREFACE.length) { int remaining = CONNECTION_PREFACE.length - prefaceBuffer.length; @@ -66,7 +67,7 @@ Stream> readConnectionPreface(Stream> incoming) { prefaceBuffer.addAll(part1); if (prefaceBuffer.length == CONNECTION_PREFACE.length) { - compareConnectionPreface(prefaceBuffer); + if (!compareConnectionPreface(prefaceBuffer)) return; } data = part2; } diff --git a/pkgs/http2/lib/src/flowcontrol/window.dart b/pkgs/http2/lib/src/flowcontrol/window.dart index baf193db73..aa45cb6080 100644 --- a/pkgs/http2/lib/src/flowcontrol/window.dart +++ b/pkgs/http2/lib/src/flowcontrol/window.dart @@ -15,15 +15,8 @@ class Window { /// NOTE: This value can potentially become negative. int _size; - /// The size the window would normally have if there is no outstanding - /// data. - /// - /// NOTE: The peer can always increase a stream window above this default - /// limit. - int _defaultSize; - Window({int initialSize: (1 << 16) - 1}) - : _size = initialSize, _defaultSize = initialSize; + : _size = initialSize; /// The current size of the flow control window. int get size => _size; @@ -31,10 +24,4 @@ class Window { void modify(int difference) { _size += difference; } - - /// This method can be e.g. called after receiving a SettingsFrame - /// which changes the initial window size of all streams. - void modifyDefaultSize(int difference) { - _defaultSize += difference; - } } diff --git a/pkgs/http2/lib/src/flowcontrol/window_handler.dart b/pkgs/http2/lib/src/flowcontrol/window_handler.dart index bd0330ba1d..937c2ba210 100644 --- a/pkgs/http2/lib/src/flowcontrol/window_handler.dart +++ b/pkgs/http2/lib/src/flowcontrol/window_handler.dart @@ -30,8 +30,6 @@ abstract class AbstractOutgoingWindowHandler { /// Process a window update frame received from the remote end. void processWindowUpdate(WindowUpdateFrame frame) { - int oldWindowSize = _peerWindow.size; - int increment = frame.windowSizeIncrement; if ((_peerWindow.size + increment) > Window.MAX_WINDOW_SIZE) { throw new FlowControlException( @@ -43,7 +41,7 @@ abstract class AbstractOutgoingWindowHandler { // If we transitioned from an negative/empty window to a positive window // we'll fire an event that more data frames can be sent now. - if (oldWindowSize <= 0 && _peerWindow.size > 0) { + if (positiveWindow.wouldBuffer && _peerWindow.size > 0) { positiveWindow.markUnBuffered(); } } @@ -86,6 +84,8 @@ class OutgoingStreamWindowHandler extends AbstractOutgoingWindowHandler { _peerWindow.modify(difference); if (_peerWindow.size <= 0) { positiveWindow.markBuffered(); + } else if (positiveWindow.wouldBuffer) { + positiveWindow.markUnBuffered(); } } } diff --git a/pkgs/http2/test/src/flowcontrol/window_handler_test.dart b/pkgs/http2/test/src/flowcontrol/window_handler_test.dart index b6a1f6164b..5c0b8f88df 100644 --- a/pkgs/http2/test/src/flowcontrol/window_handler_test.dart +++ b/pkgs/http2/test/src/flowcontrol/window_handler_test.dart @@ -83,12 +83,17 @@ main() { handler = new OutgoingStreamWindowHandler(window); expect(handler.positiveWindow.wouldBuffer, isFalse); - handler.positiveWindow.bufferEmptyEvents.listen( + 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)); From 76bfd603c56e4b76c15d29915bcd26e56e288635 Mon Sep 17 00:00:00 2001 From: Jakob Roland Andersen Date: Tue, 18 Jul 2017 10:36:36 +0200 Subject: [PATCH 058/172] Release 0.1.3 --- pkgs/http2/CHANGELOG.md | 2 +- pkgs/http2/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index 44e918ccf8..fad6f7425d 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## 0.1.3 * Fixed a bug where a closed window would not open correctly due to an increase in initial window size. diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index ab62edd0c3..69dc9b921a 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -1,5 +1,5 @@ name: http2 -version: 0.1.2 +version: 0.1.3 description: A HTTP/2 implementation in Dart. author: Dart Team homepage: https://github.com/dart-lang/http2 From 4d86ea3120b5c46a1079f377a1cead7c24193a84 Mon Sep 17 00:00:00 2001 From: Jakob Andersen Date: Mon, 2 Oct 2017 09:37:27 +0200 Subject: [PATCH 059/172] Add onActiveStateChanged callback to Connection. (dart-lang/http2#13) The callback is invoked when the connection goes from idle (0 active streams) to active (at least 1 active stream), and when the connection goes from active to idle. This can be used to implement an idle connection timeout. --- pkgs/http2/CHANGELOG.md | 6 ++ pkgs/http2/lib/src/connection.dart | 15 ++++- .../http2/lib/src/streams/stream_handler.dart | 43 +++++++++---- pkgs/http2/lib/transport.dart | 10 ++- pkgs/http2/pubspec.yaml | 2 +- pkgs/http2/test/transport_test.dart | 64 ++++++++++++++++++- 6 files changed, 120 insertions(+), 20 deletions(-) diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index fad6f7425d..e4b49a5baf 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 0.1.4 + +* Added an `onActiveStateChanged` callback to `Connection`, which is invoked when + the connection changes state from idle to active or from active to idle. This + can be used to implement an idle connection timeout. + ## 0.1.3 * Fixed a bug where a closed window would not open correctly due to an increase diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index dad5aea50c..5ff58c57b0 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -72,6 +72,9 @@ abstract class Connection { /// Whether this connection is a client connection. final bool isClientConnection; + /// Active state handler for this connection. + void Function(bool isActive) onActiveStateChanged; + /// The HPack context for this connection. final HPackContext _hpackContext = new HPackContext(); @@ -188,11 +191,13 @@ abstract class Connection { if (isClientConnection) { _streams = new StreamHandler.client( _frameWriter, _incomingQueue, _outgoingQueue, - _settingsHandler.peerSettings, _settingsHandler.acknowledgedSettings); + _settingsHandler.peerSettings, _settingsHandler.acknowledgedSettings, + _activeStateHandler); } else { _streams = new StreamHandler.server( _frameWriter, _incomingQueue, _outgoingQueue, - _settingsHandler.peerSettings, _settingsHandler.acknowledgedSettings); + _settingsHandler.peerSettings, _settingsHandler.acknowledgedSettings, + _activeStateHandler); } // NOTE: We're not waiting until initial settings have been exchanged @@ -257,6 +262,12 @@ abstract class Connection { return _terminate(ErrorCode.NO_ERROR); } + void _activeStateHandler(bool isActive) { + if (onActiveStateChanged != null) { + onActiveStateChanged(isActive); + } + } + /// Invokes the passed in closure and catches any exceptions. void _catchProtocolErrors(void fn()) { try { diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index c9ece4640e..ead8d07eb0 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -149,28 +149,35 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { bool get ranOutOfStreamIds => _ranOutOfStreamIds(); + final void Function(bool isActive) _onActiveStateChanged; + StreamHandler._(this._frameWriter, this.incomingQueue, this.outgoingQueue, this._peerSettings, this._localSettings, - this.nextStreamId, this.lastRemoteStreamId); - - factory StreamHandler.client(FrameWriter writer, - ConnectionMessageQueueIn incomingQueue, - ConnectionMessageQueueOut outgoingQueue, - ActiveSettings peerSettings, - ActiveSettings localSettings) { + this._onActiveStateChanged, this.nextStreamId, + this.lastRemoteStreamId); + + factory StreamHandler.client( + FrameWriter writer, + ConnectionMessageQueueIn incomingQueue, + ConnectionMessageQueueOut outgoingQueue, + ActiveSettings peerSettings, + ActiveSettings localSettings, + void Function(bool isActive) onActiveStateChanged) { return new StreamHandler._( writer, incomingQueue, outgoingQueue, peerSettings, localSettings, - 1, 0); + onActiveStateChanged, 1, 0); } - factory StreamHandler.server(FrameWriter writer, - ConnectionMessageQueueIn incomingQueue, - ConnectionMessageQueueOut outgoingQueue, - ActiveSettings peerSettings, - ActiveSettings localSettings) { + factory StreamHandler.server( + FrameWriter writer, + ConnectionMessageQueueIn incomingQueue, + ConnectionMessageQueueOut outgoingQueue, + ActiveSettings peerSettings, + ActiveSettings localSettings, + void Function(bool isActive) onActiveStateChanged) { return new StreamHandler._( writer, incomingQueue, outgoingQueue, peerSettings, localSettings, - 2, -1); + onActiveStateChanged, 2, -1); } void onTerminated(exception) { @@ -298,6 +305,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { var stream = new Http2StreamImpl( streamQueueIn, streamQueueOut, _outgoingC, streamId, windowOutHandler, this._canPush, this._push, this._terminateStream); + final wasIdle = _openStreams.isEmpty; _openStreams[stream.id] = stream; _setupOutgoingMessageHandling(stream); @@ -309,6 +317,10 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { _cleanupClosedStream(stream); }); + if (wasIdle) { + _onActiveStateChanged(true); + } + return stream; } @@ -707,6 +719,9 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { if (stream.state != StreamState.Terminated) { _changeState(stream, StreamState.Terminated); } + if (_openStreams.isEmpty) { + _onActiveStateChanged(false); + } onCheckForClose(); } diff --git a/pkgs/http2/lib/transport.dart b/pkgs/http2/lib/transport.dart index 7e29178568..90ac383f02 100644 --- a/pkgs/http2/lib/transport.dart +++ b/pkgs/http2/lib/transport.dart @@ -144,6 +144,14 @@ abstract class TransportConnection { /// Pings the other end. Future ping(); + /// Sets the active state callback. + /// + /// This callback is invoked with [true] when the number of active streams + /// goes from 0 to 1 (the connection goes from idle to active), and with + /// [false] when the number of active streams becomes 0 (the connection goes + /// from active to idle). + set onActiveStateChanged(void Function(bool isActive) callback); + /// Finish this connection. /// /// No new streams will be accepted or can be created. @@ -210,7 +218,7 @@ abstract class TransportStream { /// A sink for writing data and/or headers to the remote end. StreamSink get outgoingMessages; - /// Set the termination handler on this stream. + /// Sets the termination handler on this stream. /// /// The handler will be called if the stream receives an RST_STREAM frame. set onTerminated(void value(int)); diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index 69dc9b921a..3c529f6ad2 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -1,5 +1,5 @@ name: http2 -version: 0.1.3 +version: 0.1.4 description: A HTTP/2 implementation in Dart. author: Dart Team homepage: https://github.com/dart-lang/http2 diff --git a/pkgs/http2/test/transport_test.dart b/pkgs/http2/test/transport_test.dart index 11fde21bab..670d2c3c47 100644 --- a/pkgs/http2/test/transport_test.dart +++ b/pkgs/http2/test/transport_test.dart @@ -207,7 +207,6 @@ main() { transportTest('server-terminates-stream', (ClientTransportConnection client, ServerTransportConnection server) async { - Future serverFun() async { await for (ServerTransportStream stream in server.incomingStreams) { stream.terminate(); @@ -267,7 +266,6 @@ main() { transportTest('server-terminates-stream-after-half-close', (ClientTransportConnection client, ServerTransportConnection server) async { - var readyForError = new Completer(); Future serverFun() async { @@ -299,6 +297,68 @@ main() { await Future.wait([serverFun(), clientFun()]); }); + transportTest('idle-handler', + (ClientTransportConnection client, + ServerTransportConnection server) async { + Future serverFun() async { + int activeCount = 0; + int idleCount = 0; + server.onActiveStateChanged = expectAsync1((active) { + if (active) { + activeCount++; + } else { + idleCount++; + } + }, count: 6); + await for (final stream in server.incomingStreams) { + stream.sendHeaders([]); + stream.incomingMessages.toList().then( + (_) => stream.outgoingMessages.close()); + } + await server.finish(); + expect(activeCount, 3); + expect(idleCount, 3); + } + + Future clientFun() async { + int activeCount = 0; + int idleCount = 0; + client.onActiveStateChanged = expectAsync1((active) { + if (active) { + activeCount++; + } else { + idleCount++; + } + }, count: 6); + final streams = new List.generate( + 5, (_) => client.makeRequest([])); + await Future.wait(streams.map((s) => s.outgoingMessages.close())); + await Future.wait(streams.map((s) => s.incomingMessages.toList())); + // This extra await is needed to allow the idle handler to run before + // verifying the idleCount, because the stream cleanup runs + // asynchronously after the stream is closed. + await new Future.value(); + expect(activeCount, 1); + expect(idleCount, 1); + + var stream = client.makeRequest([]); + await stream.outgoingMessages.close(); + await stream.incomingMessages.toList(); + await new Future.value(); + + stream = client.makeRequest([]); + await stream.outgoingMessages.close(); + await stream.incomingMessages.toList(); + await new Future.value(); + + await client.finish(); + expect(activeCount, 3); + expect(idleCount, 3); + } + + await Future.wait([clientFun(), serverFun()]); + }); + group('flow-control', () { const int kChunkSize = 1024; const int kNumberOfMessages = 1000; From 63bc30ea8f09642359c7ae50399ddfbaa450f676 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 3 Oct 2017 13:23:58 -0700 Subject: [PATCH 060/172] Add travis support --- pkgs/http2/.travis.yml | 17 +++++++++++++++++ pkgs/http2/pubspec.yaml | 4 ++-- 2 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 pkgs/http2/.travis.yml diff --git a/pkgs/http2/.travis.yml b/pkgs/http2/.travis.yml new file mode 100644 index 0000000000..f80a1f1986 --- /dev/null +++ b/pkgs/http2/.travis.yml @@ -0,0 +1,17 @@ +language: dart +dart: + - dev + - stable + +dart_task: + - test + - dartfmt + - dartanalyzer + +# Only building master means that we don't run two builds for each pull request. +branches: + only: [master] + +cache: + directories: + - $HOME/.pub-cache diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index 3c529f6ad2..2f22d3b878 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -1,11 +1,11 @@ name: http2 -version: 0.1.4 +version: 0.1.5-dev description: A HTTP/2 implementation in Dart. author: Dart Team homepage: https://github.com/dart-lang/http2 environment: - sdk: '>=1.24.0-dev.6.7 <2.0.0' + sdk: '>=1.24.0 <2.0.0' dev_dependencies: test: '>=0.12.0 <0.13.0' From ced4807c6422a1c1549f50cfcc5d35351175ba9a Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 3 Oct 2017 13:24:30 -0700 Subject: [PATCH 061/172] dartfmt --- pkgs/http2/example/display_headers.dart | 4 +- pkgs/http2/experimental/server.dart | 13 +- pkgs/http2/lib/multiprotocol_server.dart | 24 +- .../lib/src/artificial_server_socket.dart | 10 +- .../lib/src/async_utils/async_utils.dart | 2 - pkgs/http2/lib/src/byte_utils.dart | 8 +- pkgs/http2/lib/src/connection.dart | 122 ++-- pkgs/http2/lib/src/connection_preface.dart | 36 +- pkgs/http2/lib/src/error_handler.dart | 1 - .../src/flowcontrol/connection_queues.dart | 43 +- .../lib/src/flowcontrol/queue_messages.dart | 19 +- .../lib/src/flowcontrol/stream_queues.dart | 93 ++- pkgs/http2/lib/src/flowcontrol/window.dart | 3 +- .../lib/src/flowcontrol/window_handler.dart | 7 +- pkgs/http2/lib/src/frames/frame_reader.dart | 107 ++- pkgs/http2/lib/src/frames/frame_types.dart | 166 +++-- pkgs/http2/lib/src/frames/frame_writer.dart | 47 +- pkgs/http2/lib/src/hpack/hpack.dart | 150 ++--- pkgs/http2/lib/src/hpack/huffman.dart | 11 +- pkgs/http2/lib/src/hpack/huffman_table.dart | 518 +++++++-------- pkgs/http2/lib/src/settings/settings.dart | 36 +- .../http2/lib/src/streams/stream_handler.dart | 177 ++--- pkgs/http2/lib/src/testing/client.dart | 27 +- pkgs/http2/lib/src/testing/debug.dart | 32 +- pkgs/http2/lib/transport.dart | 57 +- .../manual_test/out_of_stream_ids_test.dart | 7 +- pkgs/http2/test/client_test.dart | 141 ++-- pkgs/http2/test/client_websites_test.dart | 57 +- .../http2/test/multiprotocol_server_test.dart | 43 +- pkgs/http2/test/server_test.dart | 60 +- pkgs/http2/test/src/error_matchers.dart | 3 - .../flowcontrol/connection_queues_test.dart | 27 +- .../src/flowcontrol/stream_queues_test.dart | 12 +- .../src/flowcontrol/window_handler_test.dart | 22 +- .../src/frames/frame_defragmenter_test.dart | 28 +- .../test/src/frames/frame_reader_test.dart | 56 +- .../src/frames/frame_writer_reader_test.dart | 75 +-- pkgs/http2/test/src/hpack/hpack_test.dart | 624 ++++++++++++++---- .../test/src/hpack/huffman_table_test.dart | 180 ++--- .../test/src/ping/ping_handler_test.dart | 11 +- .../src/settings/settings_handler_test.dart | 33 +- pkgs/http2/test/src/streams/helper.dart | 10 +- .../test/src/streams/simple_flow_test.dart | 28 +- .../test/src/streams/simple_push_test.dart | 17 +- pkgs/http2/test/src/streams/streams_test.dart | 83 ++- pkgs/http2/test/transport_test.dart | 173 +++-- 46 files changed, 1858 insertions(+), 1545 deletions(-) diff --git a/pkgs/http2/example/display_headers.dart b/pkgs/http2/example/display_headers.dart index 6cf9be2dbb..9e34e48e6d 100644 --- a/pkgs/http2/example/display_headers.dart +++ b/pkgs/http2/example/display_headers.dart @@ -50,8 +50,8 @@ main(List args) async { Future connect(Uri uri) async { bool useSSL = uri.scheme == 'https'; if (useSSL) { - var secureSocket = await SecureSocket.connect(uri.host, uri.port, - supportedProtocols: ['h2']); + var secureSocket = await SecureSocket + .connect(uri.host, uri.port, supportedProtocols: ['h2']); if (secureSocket.selectedProtocol != 'h2') { throw new Exception("Failed to negogiate http/2 via alpn. Maybe server " "doesn't support http/2."); diff --git a/pkgs/http2/experimental/server.dart b/pkgs/http2/experimental/server.dart index 0bccf41af3..266a1b7805 100644 --- a/pkgs/http2/experimental/server.dart +++ b/pkgs/http2/experimental/server.dart @@ -73,7 +73,7 @@ handleClient(SecureSocket socket) { } void dumpHeaders(String prefix, List
headers) { - for (int i = 0 ; i < headers.length; i++) { + for (int i = 0; i < headers.length; i++) { String key = ASCII.decode(headers[i].name); String value = ASCII.decode(headers[i].value); print('[$prefix] $key: $value'); @@ -81,7 +81,7 @@ void dumpHeaders(String prefix, List
headers) { } String pathFromHeaders(List
headers) { - for (int i = 0 ; i < headers.length; i++) { + for (int i = 0; i < headers.length; i++) { if (ASCII.decode(headers[i].name) == ':path') { return ASCII.decode(headers[i].value); } @@ -103,8 +103,8 @@ Future sendHtml(TransportStream stream) async { push(stream, '/favicon.ico', send404); stream.sendHeaders([ - new Header.ascii(':status', '200'), - new Header.ascii('content-type', 'text/html; charset=utf-8'), + new Header.ascii(':status', '200'), + new Header.ascii('content-type', 'text/html; charset=utf-8'), ]); stream.sendData(ASCII.encode(''' @@ -121,9 +121,8 @@ Future sendHtml(TransportStream stream) async { return stream.outgoingMessages.close(); } -Future push(ServerTransportStream stream, - String path, - Future sendResponse(stream, path)) async { +Future push(ServerTransportStream stream, String path, + Future sendResponse(stream, path)) async { var requestHeaders = [ new Header.ascii(':authority', '$HOSTNAME:$PORT'), new Header.ascii(':method', 'GET'), diff --git a/pkgs/http2/lib/multiprotocol_server.dart b/pkgs/http2/lib/multiprotocol_server.dart index 580cec57dc..0f54f885de 100644 --- a/pkgs/http2/lib/multiprotocol_server.dart +++ b/pkgs/http2/lib/multiprotocol_server.dart @@ -26,13 +26,13 @@ class MultiProtocolHttpServer { _ServerSocketController _http11Controller; HttpServer _http11Server; - StreamController _http2Controller; + StreamController _http2Controller; Stream _http2Server; Set _http2Connections = new Set(); MultiProtocolHttpServer._(this._serverSocket, this._settings) { - _http11Controller = new _ServerSocketController( - _serverSocket.address, _serverSocket.port); + _http11Controller = + new _ServerSocketController(_serverSocket.address, _serverSocket.port); _http11Server = new HttpServer.listenOn(_http11Controller.stream); _http2Controller = new StreamController(); @@ -67,25 +67,23 @@ class MultiProtocolHttpServer { /// It is expected that [callbackHttp11] and [callbackHttp2] will never throw /// an exception (i.e. these must take care of error handling themselves). void startServing(void callbackHttp11(HttpRequest request), - void callbackHttp2(http2.ServerTransportStream stream), - {void onError(error, stack)}) { + void callbackHttp2(http2.ServerTransportStream stream), + {void onError(error, stack)}) { // 1. Start listening on the real [SecureServerSocket]. _serverSocket.listen((SecureSocket socket) { var protocol = socket.selectedProtocol; if (protocol == null || protocol == 'http/1.1') { _http11Controller.addHttp11Socket(socket); } else if (protocol == 'h2' || protocol == 'h2-14') { - var connection = new http2.ServerTransportConnection.viaSocket( - socket, settings: _settings); + var connection = new http2.ServerTransportConnection.viaSocket(socket, + settings: _settings); _http2Connections.add(connection); - connection.incomingStreams.listen( - _http2Controller.add, + connection.incomingStreams.listen(_http2Controller.add, onError: onError, onDone: () => _http2Connections.remove(connection)); } else { socket.destroy(); - throw new Exception( - "Unexpected negotiated ALPN protocol: $protocol."); + throw new Exception("Unexpected negotiated ALPN protocol: $protocol."); } }, onError: onError); @@ -101,8 +99,8 @@ class MultiProtocolHttpServer { Future close({bool force: false}) { return _serverSocket.close().whenComplete(() { Future done1 = _http11Server.close(force: force); - Future done2 = Future.wait(_http2Connections.map( - (c) => force ? c.terminate() : c.finish())); + Future done2 = Future.wait( + _http2Connections.map((c) => force ? c.terminate() : c.finish())); return Future.wait([done1, done2]); }); } diff --git a/pkgs/http2/lib/src/artificial_server_socket.dart b/pkgs/http2/lib/src/artificial_server_socket.dart index 1d1f181f1b..0d894cb16f 100644 --- a/pkgs/http2/lib/src/artificial_server_socket.dart +++ b/pkgs/http2/lib/src/artificial_server_socket.dart @@ -14,8 +14,8 @@ import 'dart:io'; /// and keep the [ServerSocket] interface for APIs that expect it, /// e.g. `new HttpServer.listenOn()`). class ArtificialServerSocket extends Object - with StreamMethodsMixin - implements ServerSocket { + with StreamMethodsMixin + implements ServerSocket { final Stream _stream; ArtificialServerSocket(this.address, this.port, this._stream); @@ -99,11 +99,9 @@ abstract class StreamMethodsMixin implements Stream { Future get length => _stream.length; StreamSubscription listen(void onData(T event), - {Function onError, - void onDone(), - bool cancelOnError}) { + {Function onError, void onDone(), bool cancelOnError}) { return _stream.listen(onData, - onError: onError, onDone: onDone, cancelOnError: cancelOnError); + onError: onError, onDone: onDone, cancelOnError: cancelOnError); } Stream map(convert(T event)) => _stream.map(convert); diff --git a/pkgs/http2/lib/src/async_utils/async_utils.dart b/pkgs/http2/lib/src/async_utils/async_utils.dart index b6401aac95..e6e6a75302 100644 --- a/pkgs/http2/lib/src/async_utils/async_utils.dart +++ b/pkgs/http2/lib/src/async_utils/async_utils.dart @@ -42,7 +42,6 @@ class BufferIndicator { Stream get bufferEmptyEvents => _controller.stream; } - /// Contains a [StreamSink] and a [BufferIndicator] to indicate whether writes /// to the sink would cause buffering. /// @@ -89,7 +88,6 @@ class BufferedSink { Future get doneFuture => _doneFuture; } - /// A small wrapper around [BufferedSink] which writes data in batches. class BufferedBytesWriter { /// A buffer which will be used for batching writes. diff --git a/pkgs/http2/lib/src/byte_utils.dart b/pkgs/http2/lib/src/byte_utils.dart index 5ed20473d4..756e741605 100644 --- a/pkgs/http2/lib/src/byte_utils.dart +++ b/pkgs/http2/lib/src/byte_utils.dart @@ -21,8 +21,10 @@ int readInt64(List bytes, int offset) { } int readInt32(List bytes, int offset) { - return (bytes[offset] << 24) | (bytes[offset + 1] << 16) | - (bytes[offset + 2] << 8) | bytes[offset + 3]; + return (bytes[offset] << 24) | + (bytes[offset + 1] << 16) | + (bytes[offset + 2] << 8) | + bytes[offset + 3]; } int readInt24(List bytes, int offset) { @@ -33,7 +35,6 @@ int readInt16(List bytes, int offset) { return (bytes[offset] << 8) | bytes[offset + 1]; } - void setInt64(List bytes, int offset, int value) { setInt32(bytes, offset, value >> 32); setInt32(bytes, offset + 4, value & 0xffffffff); @@ -56,4 +57,3 @@ void setInt16(List bytes, int offset, int value) { bytes[offset] = (value >> 8) & 0xff; bytes[offset + 1] = value & 0xff; } - diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index 5ff58c57b0..0285e53655 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -54,11 +54,11 @@ class ConnectionState { bool get isTerminated => state == ConnectionState.Terminated; - bool get activeFinishing - => state == Finishing && (finishingState & FinishingActive) != 0; + bool get activeFinishing => + state == Finishing && (finishingState & FinishingActive) != 0; - bool get passiveFinishing - => state == Finishing && (finishingState & FinishingPassive) != 0; + bool get passiveFinishing => + state == Finishing && (finishingState & FinishingPassive) != 0; } abstract class Connection { @@ -115,34 +115,30 @@ abstract class Connection { /// The state of this connection. ConnectionState _state; - Connection(Stream> incoming, - StreamSink> outgoing, - Settings settings, - {this.isClientConnection: true}) { + Connection(Stream> incoming, StreamSink> outgoing, + Settings settings, + {this.isClientConnection: true}) { _setupConnection(incoming, outgoing, settings); } /// Runs all setup necessary before new streams can be created with the remote /// peer. void _setupConnection(Stream> incoming, - StreamSink> outgoing, - Settings settingsObject) { + StreamSink> outgoing, Settings settingsObject) { // Setup frame reading. - var incomingFrames = new FrameReader( - incoming, acknowledgedSettings).startDecoding(); - _frameReaderSubscription = incomingFrames.listen( - (Frame frame) { - _catchProtocolErrors(() => _handleFrameImpl(frame)); - }, - onError: (error, stack) { - _terminate(ErrorCode.CONNECT_ERROR, causedByTransportError: true); - }, onDone: () { - _terminate(ErrorCode.CONNECT_ERROR, causedByTransportError: true); - }); + var incomingFrames = + new FrameReader(incoming, acknowledgedSettings).startDecoding(); + _frameReaderSubscription = incomingFrames.listen((Frame frame) { + _catchProtocolErrors(() => _handleFrameImpl(frame)); + }, onError: (error, stack) { + _terminate(ErrorCode.CONNECT_ERROR, causedByTransportError: true); + }, onDone: () { + _terminate(ErrorCode.CONNECT_ERROR, causedByTransportError: true); + }); // Setup frame writing. - _frameWriter = new FrameWriter( - _hpackContext.encoder, outgoing, peerSettings); + _frameWriter = + new FrameWriter(_hpackContext.encoder, outgoing, peerSettings); _frameWriter.doneFuture.then((_) { _terminate(ErrorCode.CONNECT_ERROR, causedByTransportError: true); }).catchError((error, stack) { @@ -150,9 +146,7 @@ abstract class Connection { }); // Setup handlers. - _settingsHandler = new SettingsHandler( - _hpackContext.encoder, - _frameWriter, + _settingsHandler = new SettingsHandler(_hpackContext.encoder, _frameWriter, acknowledgedSettings, peerSettings); _pingHandler = new PingHandler(_frameWriter); @@ -164,7 +158,7 @@ abstract class Connection { // a [Goaway] frame. We should somehow ensure we're only sending useful // but non-sensitive information. _terminate(ErrorCode.PROTOCOL_ERROR, - message: 'Failed to set initial settings (error: $error).'); + message: 'Failed to set initial settings (error: $error).'); }); _settingsHandler.onInitialWindowSizeChange.listen((int difference) { @@ -173,30 +167,34 @@ abstract class Connection { }); }); - // Setup the connection window handler, which keeps track of the // size of the outgoing connection window. - _connectionWindowHandler = - new OutgoingConnectionWindowHandler(_peerWindow); + _connectionWindowHandler = new OutgoingConnectionWindowHandler(_peerWindow); - var connectionWindowUpdater = new IncomingWindowHandler.connection( - _frameWriter, _localWindow); + var connectionWindowUpdater = + new IncomingWindowHandler.connection(_frameWriter, _localWindow); // Setup queues for outgoing/incoming messages on the connection level. - _outgoingQueue = new ConnectionMessageQueueOut( - _connectionWindowHandler, _frameWriter); + _outgoingQueue = + new ConnectionMessageQueueOut(_connectionWindowHandler, _frameWriter); _incomingQueue = new ConnectionMessageQueueIn( connectionWindowUpdater, _catchProtocolErrors); if (isClientConnection) { _streams = new StreamHandler.client( - _frameWriter, _incomingQueue, _outgoingQueue, - _settingsHandler.peerSettings, _settingsHandler.acknowledgedSettings, + _frameWriter, + _incomingQueue, + _outgoingQueue, + _settingsHandler.peerSettings, + _settingsHandler.acknowledgedSettings, _activeStateHandler); } else { _streams = new StreamHandler.server( - _frameWriter, _incomingQueue, _outgoingQueue, - _settingsHandler.peerSettings, _settingsHandler.acknowledgedSettings, + _frameWriter, + _incomingQueue, + _outgoingQueue, + _settingsHandler.peerSettings, + _settingsHandler.acknowledgedSettings, _activeStateHandler); } @@ -212,13 +210,13 @@ abstract class Connection { // By default a endpoitn can make an unlimited number of concurrent streams. if (settings.concurrentStreamLimit != null) { settingsList.add(new Setting(Setting.SETTINGS_MAX_CONCURRENT_STREAMS, - settings.concurrentStreamLimit)); + settings.concurrentStreamLimit)); } // By default the stream level flow control window is 64 KiB. if (settings.streamWindowSize != null) { - settingsList.add(new Setting(Setting.SETTINGS_INITIAL_WINDOW_SIZE, - settings.streamWindowSize)); + settingsList.add(new Setting( + Setting.SETTINGS_INITIAL_WINDOW_SIZE, settings.streamWindowSize)); } if (settings is ClientSettings) { @@ -296,7 +294,7 @@ abstract class Connection { if (_state.isInitialized) { if (frame is! SettingsFrame) { _terminate(ErrorCode.PROTOCOL_ERROR, - message: 'Expected to first receive a settings frame.'); + message: 'Expected to first receive a settings frame.'); return; } _state.state = ConnectionState.Operational; @@ -361,10 +359,8 @@ abstract class Connection { _state.state = ConnectionState.Finishing; _state.finishingState |= ConnectionState.FinishingActive; - _frameWriter.writeGoawayFrame( - _streams.highestPeerInitiatedStream, - ErrorCode.NO_ERROR, - message != null ? UTF8.encode(message) : []); + _frameWriter.writeGoawayFrame(_streams.highestPeerInitiatedStream, + ErrorCode.NO_ERROR, message != null ? UTF8.encode(message) : []); } else { _state.state = ConnectionState.Finishing; _state.finishingState |= ConnectionState.FinishingPassive; @@ -377,17 +373,15 @@ abstract class Connection { /// /// The returned future will never complete with an error. Future _terminate(int errorCode, - {bool causedByTransportError: false, String message}) { + {bool causedByTransportError: false, String message}) { // TODO: When do we complete here? if (_state.state != ConnectionState.Terminated) { _state.state = ConnectionState.Terminated; var cancelFuture = new Future.sync(_frameReaderSubscription.cancel); if (!causedByTransportError) { - _frameWriter.writeGoawayFrame( - _streams.highestPeerInitiatedStream, - errorCode, - message != null ? UTF8.encode(message) : []); + _frameWriter.writeGoawayFrame(_streams.highestPeerInitiatedStream, + errorCode, message != null ? UTF8.encode(message) : []); } var closeFuture = _frameWriter.close().catchError((e, s) { // We ignore any errors after writing to [GoawayFrame] @@ -415,19 +409,13 @@ abstract class Connection { } } - class ClientConnection extends Connection implements ClientTransportConnection { - ClientConnection._(Stream> incoming, - StreamSink> outgoing, - Settings settings) - : super(incoming, - outgoing, - settings, - isClientConnection: true); + ClientConnection._(Stream> incoming, StreamSink> outgoing, + Settings settings) + : super(incoming, outgoing, settings, isClientConnection: true); factory ClientConnection(Stream> incoming, - StreamSink> outgoing, - ClientSettings clientSettings) { + StreamSink> outgoing, ClientSettings clientSettings) { outgoing.add(CONNECTION_PREFACE); return new ClientConnection._(incoming, outgoing, clientSettings); } @@ -453,19 +441,15 @@ class ClientConnection extends Connection implements ClientTransportConnection { } class ServerConnection extends Connection implements ServerTransportConnection { - ServerConnection._(Stream> incoming, - StreamSink> outgoing, - Settings settings) - : super( - incoming, outgoing, settings, isClientConnection: false); + ServerConnection._(Stream> incoming, StreamSink> outgoing, + Settings settings) + : super(incoming, outgoing, settings, isClientConnection: false); factory ServerConnection(Stream> incoming, - StreamSink> outgoing, - ServerSettings serverSettings) { + StreamSink> outgoing, ServerSettings serverSettings) { var frameBytes = readConnectionPreface(incoming); return new ServerConnection._(frameBytes, outgoing, serverSettings); } Stream get incomingStreams => _streams.incomingStreams; } - diff --git a/pkgs/http2/lib/src/connection_preface.dart b/pkgs/http2/lib/src/connection_preface.dart index 22c9e29b63..695832b742 100644 --- a/pkgs/http2/lib/src/connection_preface.dart +++ b/pkgs/http2/lib/src/connection_preface.dart @@ -12,10 +12,31 @@ import 'byte_utils.dart'; /// This is a set of bytes with which a client connection begins in the normal /// case. It can be used on a server to distinguish HTTP/1.1 and HTTP/2 clients. const List CONNECTION_PREFACE = const [ - 0x50, 0x52, 0x49, 0x20, 0x2a, 0x20, 0x48, 0x54, 0x54, - 0x50, 0x2f, 0x32, 0x2e, 0x30, 0x0d, 0x0a, 0x0d, 0x0a, - 0x53, 0x4d, 0x0d, 0x0a, 0x0d, 0x0a]; - + 0x50, + 0x52, + 0x49, + 0x20, + 0x2a, + 0x20, + 0x48, + 0x54, + 0x54, + 0x50, + 0x2f, + 0x32, + 0x2e, + 0x30, + 0x0d, + 0x0a, + 0x0d, + 0x0a, + 0x53, + 0x4d, + 0x0d, + 0x0a, + 0x0d, + 0x0a +]; /// Reads the connection preface from [incoming]. /// @@ -79,9 +100,8 @@ Stream> readConnectionPreface(Stream> incoming) { result = new StreamController( onListen: () { - subscription = incoming.listen( - onData, - onError: (e,s) => result.addError(e, s), + subscription = incoming.listen(onData, + onError: (e, s) => result.addError(e, s), onDone: () { if (prefaceBuffer != null) { terminate('EOS before connection preface could be read.'); @@ -89,7 +109,7 @@ Stream> readConnectionPreface(Stream> incoming) { result.close(); } }); - }, + }, onPause: () => subscription.pause(), onResume: () => subscription.resume(), onCancel: () => subscription.cancel()); diff --git a/pkgs/http2/lib/src/error_handler.dart b/pkgs/http2/lib/src/error_handler.dart index 06c9ac28ac..ae2bf96291 100644 --- a/pkgs/http2/lib/src/error_handler.dart +++ b/pkgs/http2/lib/src/error_handler.dart @@ -87,4 +87,3 @@ class ClosableMixin { } } } - diff --git a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart index b64b7a2874..076a037a3c 100644 --- a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart @@ -20,7 +20,6 @@ import 'stream_queues.dart'; import 'queue_messages.dart'; import 'window_handler.dart'; - /// The last place before messages coming from the application get encoded and /// send as [Frame]s. /// @@ -34,7 +33,7 @@ import 'window_handler.dart'; // * all streams have been closed // * the connection state is finishing class ConnectionMessageQueueOut extends Object - with TerminatableMixin, ClosableMixin { + with TerminatableMixin, ClosableMixin { /// The handler which will be used for increasing the connection-level flow /// control window. final OutgoingConnectionWindowHandler _connectionWindow; @@ -90,7 +89,7 @@ class ConnectionMessageQueueOut extends Object if (_messages.length > 0 && !_frameWriter.bufferIndicator.wouldBuffer && (!_connectionWindow.positiveWindow.wouldBuffer || - _messages.first is! DataMessage)) { + _messages.first is! DataMessage)) { _trySendMessage(); // If we have more messages and we can send them, we'll run them @@ -98,7 +97,7 @@ class ConnectionMessageQueueOut extends Object if (_messages.length > 0 && !_frameWriter.bufferIndicator.wouldBuffer && (!_connectionWindow.positiveWindow.wouldBuffer || - _messages.first is! DataMessage)) { + _messages.first is! DataMessage)) { // TODO: If all the frame writer methods would return the // number of bytes written, we could just say, we loop here until 10kb // and after words, we'll make `Timer.run()`. @@ -114,8 +113,8 @@ class ConnectionMessageQueueOut extends Object Message message = _messages.first; if (message is HeadersMessage) { _messages.removeFirst(); - _frameWriter.writeHeadersFrame( - message.streamId, message.headers, endStream: message.endStream); + _frameWriter.writeHeadersFrame(message.streamId, message.headers, + endStream: message.endStream); } else if (message is PushPromiseMessage) { _messages.removeFirst(); _frameWriter.writePushPromiseFrame( @@ -125,15 +124,15 @@ class ConnectionMessageQueueOut extends Object if (_connectionWindow.peerWindowSize >= message.bytes.length) { _connectionWindow.decreaseWindow(message.bytes.length); - _frameWriter.writeDataFrame( - message.streamId, message.bytes, endStream: message.endStream); + _frameWriter.writeDataFrame(message.streamId, message.bytes, + endStream: message.endStream); } else { // NOTE: We need to fragment the DataMessage. // TODO: Do not fragment if the number of bytes we can send is too low int len = _connectionWindow.peerWindowSize; var head = viewOrSublist(message.bytes, 0, len); - var tail = viewOrSublist( - message.bytes, len, message.bytes.length - len); + var tail = + viewOrSublist(message.bytes, len, message.bytes.length - len); _connectionWindow.decreaseWindow(head.length); _frameWriter.writeDataFrame(message.streamId, head, endStream: false); @@ -170,7 +169,7 @@ class ConnectionMessageQueueOut extends Object // * all streams have been closed // * the connection state is finishing class ConnectionMessageQueueIn extends Object - with TerminatableMixin, ClosableMixin { + with TerminatableMixin, ClosableMixin { /// The handler which will be used for increasing the connection-level flow /// control window. final IncomingWindowHandler _windowUpdateHandler; @@ -190,8 +189,8 @@ class ConnectionMessageQueueIn extends Object /// to the stream-specific queue. (for debugging purposes) int _count = 0; - ConnectionMessageQueueIn(this._windowUpdateHandler, - this._catchProtocolErrors); + ConnectionMessageQueueIn( + this._windowUpdateHandler, this._catchProtocolErrors); void onTerminated(error) { // NOTE: The higher level will be shutdown first, so all streams @@ -203,7 +202,7 @@ class ConnectionMessageQueueIn extends Object void onCheckForClose() { if (isClosing) { - assert (_stream2messageQueue.isEmpty == _stream2pendingMessages.isEmpty); + assert(_stream2messageQueue.isEmpty == _stream2pendingMessages.isEmpty); if (_stream2messageQueue.isEmpty) { closeWithValue(); } @@ -268,12 +267,11 @@ class ConnectionMessageQueueIn extends Object /// Processes an incoming [PushPromiseFrame] which is addressed to a specific /// stream. - void processPushPromiseFrame(PushPromiseFrame frame, - TransportStream pushedStream) { + void processPushPromiseFrame( + PushPromiseFrame frame, TransportStream pushedStream) { var streamId = frame.header.streamId; - var message = new PushPromiseMessage( - streamId, frame.decodedHeaders, frame.promisedStreamId, pushedStream, - false); + var message = new PushPromiseMessage(streamId, frame.decodedHeaders, + frame.promisedStreamId, pushedStream, false); // NOTE: // * Header frames do not affect flow control - only data frames do. @@ -302,9 +300,8 @@ class ConnectionMessageQueueIn extends Object streamMQ.enqueueMessage(message); } - void _tryDispatch(int streamId, - StreamMessageQueueIn mq, - Queue pendingMessages) { + void _tryDispatch( + int streamId, StreamMessageQueueIn mq, Queue pendingMessages) { int bytesDeliveredToStream = 0; while (!mq.bufferIndicator.wouldBuffer && pendingMessages.length > 0) { _count--; @@ -315,7 +312,7 @@ class ConnectionMessageQueueIn extends Object } mq.enqueueMessage(message); if (message.endStream) { - assert (pendingMessages.isEmpty); + assert(pendingMessages.isEmpty); _stream2messageQueue.remove(streamId); _stream2pendingMessages.remove(streamId); diff --git a/pkgs/http2/lib/src/flowcontrol/queue_messages.dart b/pkgs/http2/lib/src/flowcontrol/queue_messages.dart index d3331a3f05..7638db185e 100644 --- a/pkgs/http2/lib/src/flowcontrol/queue_messages.dart +++ b/pkgs/http2/lib/src/flowcontrol/queue_messages.dart @@ -24,8 +24,8 @@ class HeadersMessage extends Message { HeadersMessage(int streamId, this.headers, bool endStream) : super(streamId, endStream); - String toString() - => 'HeadersMessage(headers: ${headers.length}, endStream: $endStream)'; + String toString() => + 'HeadersMessage(headers: ${headers.length}, endStream: $endStream)'; } class DataMessage extends Message { @@ -34,8 +34,8 @@ class DataMessage extends Message { DataMessage(int streamId, this.bytes, bool endStream) : super(streamId, endStream); - String toString() - => 'DataMessage(bytes: ${bytes.length}, endStream: $endStream)'; + String toString() => + 'DataMessage(bytes: ${bytes.length}, endStream: $endStream)'; } class PushPromiseMessage extends Message { @@ -43,11 +43,10 @@ class PushPromiseMessage extends Message { final int promisedStreamId; final TransportStream pushedStream; - PushPromiseMessage( - int streamId, this.headers, this.promisedStreamId, this.pushedStream, - bool endStream) : super(streamId, endStream); + PushPromiseMessage(int streamId, this.headers, this.promisedStreamId, + this.pushedStream, bool endStream) + : super(streamId, endStream); - String toString() - => 'PushPromiseMessage(bytes: ${headers.length}, ' - 'promisedStreamId: $promisedStreamId, endStream: $endStream)'; + String toString() => 'PushPromiseMessage(bytes: ${headers.length}, ' + 'promisedStreamId: $promisedStreamId, endStream: $endStream)'; } diff --git a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart index 0fb4a73542..14f946aa64 100644 --- a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart @@ -22,7 +22,7 @@ import 'window_handler.dart'; /// It will ensure that we never send more data than the remote flow control /// window allows. class StreamMessageQueueOut extends Object - with TerminatableMixin, ClosableMixin { + with TerminatableMixin, ClosableMixin { /// The id of the stream this message queue belongs to. final int streamId; @@ -47,9 +47,8 @@ class StreamMessageQueueOut extends Object /// message queue. int writtenBytes = 0; - StreamMessageQueueOut(this.streamId, - this.streamWindow, - this.connectionMessageQueue) { + StreamMessageQueueOut( + this.streamId, this.streamWindow, this.connectionMessageQueue) { streamWindow.positiveWindow.bufferEmptyEvents.listen((_) { if (!wasTerminated) { _trySendData(); @@ -94,7 +93,6 @@ class StreamMessageQueueOut extends Object void onCheckForClose() { if (isClosing && _messages.isEmpty) closeWithValue(); - } void _trySendData() { @@ -117,12 +115,11 @@ class StreamMessageQueueOut extends Object // TODO: Do not fragment if the number of bytes we can send is too low if (messageBytes.length > bytesAvailable) { var partA = viewOrSublist(messageBytes, 0, bytesAvailable); - var partB = viewOrSublist( - messageBytes, bytesAvailable, + var partB = viewOrSublist(messageBytes, bytesAvailable, messageBytes.length - bytesAvailable); var messageA = new DataMessage(message.streamId, partA, false); - var messageB = new DataMessage( - message.streamId, partB, message.endStream); + var messageB = + new DataMessage(message.streamId, partB, message.endStream); // Put the second fragment back into the front of the queue. _messages.addFirst(messageB); @@ -155,7 +152,7 @@ class StreamMessageQueueOut extends Object /// It will keep messages up to the stream flow control window size if the /// [messages] listener is paused. class StreamMessageQueueIn extends Object - with TerminatableMixin, ClosableMixin { + with TerminatableMixin, ClosableMixin { /// The stream-level window our peer is using when sending us messages. final IncomingWindowHandler windowHandler; @@ -177,34 +174,32 @@ class StreamMessageQueueIn extends Object // incoming messages will get buffered. bufferIndicator.markBuffered(); - _incomingMessagesC = new StreamController( - onListen: () { - if (!wasClosed && !wasTerminated) { - _tryDispatch(); - _tryUpdateBufferIndicator(); - } - }, onPause: () { + _incomingMessagesC = new StreamController(onListen: () { + if (!wasClosed && !wasTerminated) { + _tryDispatch(); _tryUpdateBufferIndicator(); - // TODO: Would we ever want to decrease the window size in this - // situation? - }, onResume: () { - if (!wasClosed && !wasTerminated) { - _tryDispatch(); - _tryUpdateBufferIndicator(); - } - }, onCancel: () { - _pendingMessages.clear(); - startClosing(); - onCloseCheck(); - }); - - _serverPushStreamsC = new StreamController( - onListen: () { - if (!wasClosed && !wasTerminated) { - _tryDispatch(); - _tryUpdateBufferIndicator(); - } - }); + } + }, onPause: () { + _tryUpdateBufferIndicator(); + // TODO: Would we ever want to decrease the window size in this + // situation? + }, onResume: () { + if (!wasClosed && !wasTerminated) { + _tryDispatch(); + _tryUpdateBufferIndicator(); + } + }, onCancel: () { + _pendingMessages.clear(); + startClosing(); + onCloseCheck(); + }); + + _serverPushStreamsC = new StreamController(onListen: () { + if (!wasClosed && !wasTerminated) { + _tryDispatch(); + _tryUpdateBufferIndicator(); + } + }); } /// Debugging data: the number of pending messages in this queue. @@ -224,9 +219,9 @@ class StreamMessageQueueIn extends Object if (message is PushPromiseMessage) { // NOTE: If server pushes were enabled, the client is responsible for // either rejecting or handling them. - assert (!_serverPushStreamsC.isClosed); - var transportStreamPush = new TransportStreamPush( - message.headers, message.pushedStream); + assert(!_serverPushStreamsC.isClosed); + var transportStreamPush = + new TransportStreamPush(message.headers, message.pushedStream); _serverPushStreamsC.add(transportStreamPush); return; } @@ -264,27 +259,23 @@ class StreamMessageQueueIn extends Object } void _tryDispatch() { - while (!wasTerminated && - _pendingMessages.isNotEmpty) { + while (!wasTerminated && _pendingMessages.isNotEmpty) { bool handled = false; var message = _pendingMessages.first; if (message is HeadersMessage || message is DataMessage) { - assert (!_incomingMessagesC.isClosed); - if (_incomingMessagesC.hasListener && - !_incomingMessagesC.isPaused) { + assert(!_incomingMessagesC.isClosed); + if (_incomingMessagesC.hasListener && !_incomingMessagesC.isPaused) { _pendingMessages.removeFirst(); if (message is HeadersMessage) { // NOTE: Header messages do not affect flow control - only // data messages do. - _incomingMessagesC.add( - new HeadersStreamMessage( - message.headers, endStream: message.endStream)); + _incomingMessagesC.add(new HeadersStreamMessage(message.headers, + endStream: message.endStream)); } else if (message is DataMessage) { if (message.bytes.length > 0) { - _incomingMessagesC.add( - new DataStreamMessage( - message.bytes, endStream: message.endStream)); + _incomingMessagesC.add(new DataStreamMessage(message.bytes, + endStream: message.endStream)); windowHandler.dataProcessed(message.bytes.length); } } else { diff --git a/pkgs/http2/lib/src/flowcontrol/window.dart b/pkgs/http2/lib/src/flowcontrol/window.dart index aa45cb6080..10ba3f703a 100644 --- a/pkgs/http2/lib/src/flowcontrol/window.dart +++ b/pkgs/http2/lib/src/flowcontrol/window.dart @@ -15,8 +15,7 @@ class Window { /// NOTE: This value can potentially become negative. int _size; - Window({int initialSize: (1 << 16) - 1}) - : _size = initialSize; + Window({int initialSize: (1 << 16) - 1}) : _size = initialSize; /// The current size of the flow control window. int get size => _size; diff --git a/pkgs/http2/lib/src/flowcontrol/window_handler.dart b/pkgs/http2/lib/src/flowcontrol/window_handler.dart index 937c2ba210..a549e824d1 100644 --- a/pkgs/http2/lib/src/flowcontrol/window_handler.dart +++ b/pkgs/http2/lib/src/flowcontrol/window_handler.dart @@ -58,13 +58,11 @@ abstract class AbstractOutgoingWindowHandler { } } - /// Handles the connection window for outgoing data frames. class OutgoingConnectionWindowHandler extends AbstractOutgoingWindowHandler { OutgoingConnectionWindowHandler(Window window) : super(window); } - /// Handles the window for outgoing messages to the peer. class OutgoingStreamWindowHandler extends AbstractOutgoingWindowHandler { OutgoingStreamWindowHandler(Window window) : super(window); @@ -91,7 +89,6 @@ class OutgoingStreamWindowHandler extends AbstractOutgoingWindowHandler { } } - /// Mirrors the flow control window the remote end is using. class IncomingWindowHandler { /// The [FrameWriter] used for writing [WindowUpdateFrame]s to the wire. @@ -109,8 +106,8 @@ class IncomingWindowHandler { IncomingWindowHandler.stream( this._frameWriter, this._localWindow, this._streamId); - IncomingWindowHandler.connection( - this._frameWriter, this._localWindow) : _streamId = 0; + IncomingWindowHandler.connection(this._frameWriter, this._localWindow) + : _streamId = 0; /// The current size for the incoming data window. /// diff --git a/pkgs/http2/lib/src/frames/frame_reader.dart b/pkgs/http2/lib/src/frames/frame_reader.dart index 256da5dc80..75e026b697 100644 --- a/pkgs/http2/lib/src/frames/frame_reader.dart +++ b/pkgs/http2/lib/src/frames/frame_reader.dart @@ -12,7 +12,7 @@ class FrameReader { /// complying with. ActiveSettings _localSettings; - StreamSubscription> _subscription; + StreamSubscription> _subscription; StreamController _framesController; FrameReader(this._inputStream, this._localSettings); @@ -62,55 +62,53 @@ class FrameReader { FrameHeader header; void terminateWithError(error, [stack]) { - header = null; - _framesController.addError(error, stack); - _subscription.cancel(); - _framesController.close(); + header = null; + _framesController.addError(error, stack); + _subscription.cancel(); + _framesController.close(); } _subscription = _inputStream.listen((List data) { - bufferedData.add(data); - bufferedLength += data.length; + bufferedData.add(data); + bufferedLength += data.length; - try { - while (true) { - if (header == null) { - header = tryReadHeader(); + try { + while (true) { + if (header == null) { + header = tryReadHeader(); + } + if (header != null) { + if (header.length > _localSettings.maxFrameSize) { + terminateWithError( + new FrameSizeException('Incoming frame is too big.')); + return; } - if (header != null) { - if (header.length > _localSettings.maxFrameSize) { - terminateWithError(new FrameSizeException( - 'Incoming frame is too big.')); - return; - } - - Frame frame = tryReadFrame(header); - - if (frame != null) { - _framesController.add(frame); - header = null; - } else { - break; - } + + Frame frame = tryReadFrame(header); + + if (frame != null) { + _framesController.add(frame); + header = null; } else { break; } + } else { + break; } - } catch (error, stack) { - terminateWithError(error, stack); } - }, - onError: (error, stack) { + } catch (error, stack) { terminateWithError(error, stack); - }, - onDone: () { - if (bufferedLength == 0) { - _framesController.close(); - } else { - terminateWithError(new FrameSizeException( - 'Incoming byte stream ended with incomplete frame')); - } - }); + } + }, onError: (error, stack) { + terminateWithError(error, stack); + }, onDone: () { + if (bufferedLength == 0) { + _framesController.close(); + } else { + terminateWithError(new FrameSizeException( + 'Incoming byte stream ended with incomplete frame')); + } + }); }, onPause: () => _subscription.pause(), onResume: () => _subscription.resume()); @@ -126,12 +124,12 @@ class FrameReader { void _mergeLists(List> bufferedData, int numberOfBytes) { if (bufferedData[0].length < numberOfBytes) { int numLists = 0; - int accumulatedLength = 0; + int accumulatedLength = 0; while (accumulatedLength < numberOfBytes && - numLists <= bufferedData.length) { + numLists <= bufferedData.length) { accumulatedLength += bufferedData[numLists++].length; } - assert (accumulatedLength >= numberOfBytes); + assert(accumulatedLength >= numberOfBytes); var newList = new Uint8List(accumulatedLength); int offset = 0; for (int i = 0; i < numLists; i++) { @@ -162,7 +160,7 @@ class FrameReader { switch (header.type) { case FrameType.DATA: int padLength = 0; - if (_isFlagSet(header.flags, DataFrame.FLAG_PADDED)){ + if (_isFlagSet(header.flags, DataFrame.FLAG_PADDED)) { _checkFrameLengthCondition((frameEnd - offset) >= 1); padLength = bytes[offset++]; } @@ -189,11 +187,9 @@ class FrameReader { } int headerBlockLen = frameEnd - offset - padLength; _checkFrameLengthCondition(headerBlockLen >= 0); - var headerBlockFragment = viewOrSublist( - bytes, offset, headerBlockLen); - return new HeadersFrame( - header, padLength, exclusiveDependency, streamDependency, - weight, headerBlockFragment); + var headerBlockFragment = viewOrSublist(bytes, offset, headerBlockLen); + return new HeadersFrame(header, padLength, exclusiveDependency, + streamDependency, weight, headerBlockFragment); case FrameType.PRIORITY: _checkFrameLengthCondition( @@ -213,8 +209,7 @@ class FrameReader { return new RstStreamFrame(header, errorCode); case FrameType.SETTINGS: - _checkFrameLengthCondition( - (header.length % 6) == 0, + _checkFrameLengthCondition((header.length % 6) == 0, message: 'Settings frame length must be a multiple of 6 bytes.'); int count = header.length ~/ 6; @@ -226,8 +221,7 @@ class FrameReader { } var frame = new SettingsFrame(header, settings); if (frame.hasAckFlag) { - _checkFrameLengthCondition( - header.length == 0, + _checkFrameLengthCondition(header.length == 0, message: 'Settings frame length must 0 for ACKs.'); } return frame; @@ -242,8 +236,7 @@ class FrameReader { offset += 4; int headerBlockLen = frameEnd - offset - padLength; _checkFrameLengthCondition(headerBlockLen >= 0); - var headerBlockFragment = viewOrSublist( - bytes, offset, headerBlockLen); + var headerBlockFragment = viewOrSublist(bytes, offset, headerBlockLen); return new PushPromiseFrame( header, padLength, promisedStreamId, headerBlockFragment); @@ -269,8 +262,8 @@ class FrameReader { return new WindowUpdateFrame(header, windowSizeIncrement); case FrameType.CONTINUATION: - var headerBlockFragment = viewOrSublist( - bytes, offset, frameEnd - offset); + var headerBlockFragment = + viewOrSublist(bytes, offset, frameEnd - offset); return new ContinuationFrame(header, headerBlockFragment); default: @@ -283,7 +276,7 @@ class FrameReader { /// Checks that [condition] is `true` and raises an [FrameSizeException] /// otherwise. void _checkFrameLengthCondition(bool condition, - {String message: 'Frame not long enough.'}) { + {String message: 'Frame not long enough.'}) { if (!condition) { throw new FrameSizeException(message); } diff --git a/pkgs/http2/lib/src/frames/frame_types.dart b/pkgs/http2/lib/src/frames/frame_types.dart index 4022b5e517..205f2a8a7e 100644 --- a/pkgs/http2/lib/src/frames/frame_types.dart +++ b/pkgs/http2/lib/src/frames/frame_types.dart @@ -44,12 +44,8 @@ class FrameHeader { FrameHeader(this.length, this.type, this.flags, this.streamId); - Map toJson() => { - 'length' : length, - 'type' : type, - 'flags' : flags, - 'streamId' : streamId - }; + Map toJson() => + {'length': length, 'type': type, 'flags': flags, 'streamId': streamId}; } class Frame { @@ -59,7 +55,7 @@ class Frame { Frame(this.header); - Map toJson() => { 'header' : header.toJson() }; + Map toJson() => {'header': header.toJson()}; } class DataFrame extends Frame { @@ -71,17 +67,17 @@ class DataFrame extends Frame { final List bytes; - DataFrame(FrameHeader header, this.padLength, this.bytes) - : super(header); + DataFrame(FrameHeader header, this.padLength, this.bytes) : super(header); bool get hasEndStreamFlag => _isFlagSet(header.flags, FLAG_END_STREAM); bool get hasPaddedFlag => _isFlagSet(header.flags, FLAG_PADDED); - Map toJson() => super.toJson()..addAll({ - 'padLength' : padLength, - 'bytes (length)': bytes.length, - 'bytes (up to 4 bytes)' : bytes.length > 4 ? bytes.sublist(0, 4) : bytes, - }); + Map toJson() => super.toJson() + ..addAll({ + 'padLength': padLength, + 'bytes (length)': bytes.length, + 'bytes (up to 4 bytes)': bytes.length > 4 ? bytes.sublist(0, 4) : bytes, + }); } class HeadersFrame extends Frame { @@ -103,7 +99,7 @@ class HeadersFrame extends Frame { final List headerBlockFragment; HeadersFrame(FrameHeader header, this.padLength, this.exclusiveDependency, - this.streamDependency, this.weight, this.headerBlockFragment) + this.streamDependency, this.weight, this.headerBlockFragment) : super(header); /// This will be set from the outside after decoding. @@ -117,31 +113,30 @@ class HeadersFrame extends Frame { HeadersFrame addBlockContinuation(ContinuationFrame frame) { var fragment = frame.headerBlockFragment; var flags = header.flags | frame.header.flags; - var fh = new FrameHeader(header.length + fragment.length, - header.type, flags, header.streamId); + var fh = new FrameHeader( + header.length + fragment.length, header.type, flags, header.streamId); - var mergedHeaderBlockFragment = new Uint8List( - headerBlockFragment.length + fragment.length); + var mergedHeaderBlockFragment = + new Uint8List(headerBlockFragment.length + fragment.length); mergedHeaderBlockFragment.setRange( - 0, headerBlockFragment.length, - headerBlockFragment); + 0, headerBlockFragment.length, headerBlockFragment); mergedHeaderBlockFragment.setRange( - headerBlockFragment.length, mergedHeaderBlockFragment.length, - fragment); + headerBlockFragment.length, mergedHeaderBlockFragment.length, fragment); return new HeadersFrame(fh, padLength, exclusiveDependency, streamDependency, weight, mergedHeaderBlockFragment); } - Map toJson() => super.toJson()..addAll({ - 'padLength' : padLength, - 'exclusiveDependency' : exclusiveDependency, - 'streamDependency' : streamDependency, - 'weight' : weight, - 'headerBlockFragment (length)': headerBlockFragment.length - }); + Map toJson() => super.toJson() + ..addAll({ + 'padLength': padLength, + 'exclusiveDependency': exclusiveDependency, + 'streamDependency': streamDependency, + 'weight': weight, + 'headerBlockFragment (length)': headerBlockFragment.length + }); } class PriorityFrame extends Frame { @@ -152,14 +147,15 @@ class PriorityFrame extends Frame { final int weight; PriorityFrame(FrameHeader header, this.exclusiveDependency, - this.streamDependency, this.weight) + this.streamDependency, this.weight) : super(header); - Map toJson() => super.toJson()..addAll({ - 'exclusiveDependency' : exclusiveDependency, - 'streamDependency' : streamDependency, - 'weight' : weight, - }); + Map toJson() => super.toJson() + ..addAll({ + 'exclusiveDependency': exclusiveDependency, + 'streamDependency': streamDependency, + 'weight': weight, + }); } class RstStreamFrame extends Frame { @@ -167,12 +163,12 @@ class RstStreamFrame extends Frame { final int errorCode; - RstStreamFrame(FrameHeader header, this.errorCode) - : super(header); + RstStreamFrame(FrameHeader header, this.errorCode) : super(header); - Map toJson() => super.toJson()..addAll({ - 'errorCode' : errorCode, - }); + Map toJson() => super.toJson() + ..addAll({ + 'errorCode': errorCode, + }); } class Setting { @@ -188,7 +184,7 @@ class Setting { Setting(this.identifier, this.value); - Map toJson() => {'identifier' : identifier, 'value' : value}; + Map toJson() => {'identifier': identifier, 'value': value}; } class SettingsFrame extends Frame { @@ -199,14 +195,14 @@ class SettingsFrame extends Frame { final List settings; - SettingsFrame(FrameHeader header, this.settings) - : super(header); + SettingsFrame(FrameHeader header, this.settings) : super(header); bool get hasAckFlag => _isFlagSet(header.flags, FLAG_ACK); - Map toJson() => super.toJson()..addAll({ - 'settings' : settings.map((s) => s.toJson()).toList(), - }); + Map toJson() => super.toJson() + ..addAll({ + 'settings': settings.map((s) => s.toJson()).toList(), + }); } class PushPromiseFrame extends Frame { @@ -225,7 +221,7 @@ class PushPromiseFrame extends Frame { List
decodedHeaders; PushPromiseFrame(FrameHeader header, this.padLength, this.promisedStreamId, - this.headerBlockFragment) + this.headerBlockFragment) : super(header); bool get hasEndHeadersFlag => _isFlagSet(header.flags, FLAG_END_HEADERS); @@ -234,29 +230,28 @@ class PushPromiseFrame extends Frame { PushPromiseFrame addBlockContinuation(ContinuationFrame frame) { var fragment = frame.headerBlockFragment; var flags = header.flags | frame.header.flags; - var fh = new FrameHeader(header.length + fragment.length, - header.type, flags, header.streamId); + var fh = new FrameHeader( + header.length + fragment.length, header.type, flags, header.streamId); - var mergedHeaderBlockFragment = new Uint8List( - headerBlockFragment.length + fragment.length); + var mergedHeaderBlockFragment = + new Uint8List(headerBlockFragment.length + fragment.length); mergedHeaderBlockFragment.setRange( - 0, headerBlockFragment.length, - headerBlockFragment); + 0, headerBlockFragment.length, headerBlockFragment); mergedHeaderBlockFragment.setRange( - headerBlockFragment.length, mergedHeaderBlockFragment.length, - fragment); + headerBlockFragment.length, mergedHeaderBlockFragment.length, fragment); return new PushPromiseFrame( fh, padLength, promisedStreamId, mergedHeaderBlockFragment); } - Map toJson() => super.toJson()..addAll({ - 'padLength' : padLength, - 'promisedStreamId' : promisedStreamId, - 'headerBlockFragment (len)' : headerBlockFragment.length, - }); + Map toJson() => super.toJson() + ..addAll({ + 'padLength': padLength, + 'promisedStreamId': promisedStreamId, + 'headerBlockFragment (len)': headerBlockFragment.length, + }); } class PingFrame extends Frame { @@ -266,14 +261,14 @@ class PingFrame extends Frame { final int opaqueData; - PingFrame(FrameHeader header, this.opaqueData) - : super(header); + PingFrame(FrameHeader header, this.opaqueData) : super(header); bool get hasAckFlag => _isFlagSet(header.flags, FLAG_ACK); - Map toJson() => super.toJson()..addAll({ - 'opaqueData' : opaqueData, - }); + Map toJson() => super.toJson() + ..addAll({ + 'opaqueData': opaqueData, + }); } class GoawayFrame extends Frame { @@ -281,15 +276,16 @@ class GoawayFrame extends Frame { final int errorCode; final List debugData; - GoawayFrame(FrameHeader header, this.lastStreamId, this.errorCode, - this.debugData) + GoawayFrame( + FrameHeader header, this.lastStreamId, this.errorCode, this.debugData) : super(header); - Map toJson() => super.toJson()..addAll({ - 'lastStreamId' : lastStreamId, - 'errorCode' : errorCode, - 'debugData (length)' : debugData.length, - }); + Map toJson() => super.toJson() + ..addAll({ + 'lastStreamId': lastStreamId, + 'errorCode': errorCode, + 'debugData (length)': debugData.length, + }); } class WindowUpdateFrame extends Frame { @@ -300,9 +296,10 @@ class WindowUpdateFrame extends Frame { WindowUpdateFrame(FrameHeader header, this.windowSizeIncrement) : super(header); - Map toJson() => super.toJson()..addAll({ - 'windowSizeIncrement' : windowSizeIncrement, - }); + Map toJson() => super.toJson() + ..addAll({ + 'windowSizeIncrement': windowSizeIncrement, + }); } class ContinuationFrame extends Frame { @@ -315,18 +312,19 @@ class ContinuationFrame extends Frame { bool get hasEndHeadersFlag => _isFlagSet(header.flags, FLAG_END_HEADERS); - Map toJson() => super.toJson()..addAll({ - 'headerBlockFragment (length)' : headerBlockFragment.length, - }); + Map toJson() => super.toJson() + ..addAll({ + 'headerBlockFragment (length)': headerBlockFragment.length, + }); } class UnknownFrame extends Frame { final List data; - UnknownFrame(FrameHeader header, this.data) - : super(header); + UnknownFrame(FrameHeader header, this.data) : super(header); - Map toJson() => super.toJson()..addAll({ - 'data (length)' : data.length, - }); + Map toJson() => super.toJson() + ..addAll({ + 'data (length)': data.length, + }); } diff --git a/pkgs/http2/lib/src/frames/frame_writer.dart b/pkgs/http2/lib/src/frames/frame_writer.dart index dc441dd1e4..275aaf65a4 100644 --- a/pkgs/http2/lib/src/frames/frame_writer.dart +++ b/pkgs/http2/lib/src/frames/frame_writer.dart @@ -23,9 +23,8 @@ class FrameWriter { /// Whether this [FrameWriter] is closed. bool _isClosed = false; - FrameWriter(this._hpackEncoder, - StreamSink> outgoing, - this._peerSettings) + FrameWriter( + this._hpackEncoder, StreamSink> outgoing, this._peerSettings) : _outWriter = new BufferedBytesWriter(outgoing); /// A indicator whether writes would be buffered. @@ -39,7 +38,7 @@ class FrameWriter { while (data.length > _peerSettings.maxFrameSize) { var chunk = viewOrSublist(data, 0, _peerSettings.maxFrameSize); data = viewOrSublist(data, _peerSettings.maxFrameSize, - data.length - _peerSettings.maxFrameSize); + data.length - _peerSettings.maxFrameSize); _writeDataFrameNoFragment(streamId, chunk, false); } _writeDataFrameNoFragment(streamId, data, endStream); @@ -61,7 +60,7 @@ class FrameWriter { } void writeHeadersFrame(int streamId, List
headers, - {bool endStream: true}) { + {bool endStream: true}) { var fragment = _hpackEncoder.encode(headers); var maxSize = _peerSettings.maxFrameSize - HeadersFrame.MAX_CONSTANT_PAYLOAD; @@ -81,8 +80,8 @@ class FrameWriter { } } - void _writeHeadersFrameNoFragment(int streamId, List fragment, - bool endHeaders, bool endStream) { + void _writeHeadersFrameNoFragment( + int streamId, List fragment, bool endHeaders, bool endStream) { int type = FrameType.HEADERS; int flags = 0; if (endHeaders) flags |= HeadersFrame.FLAG_END_HEADERS; @@ -99,8 +98,8 @@ class FrameWriter { _writeData(buffer); } - void _writeContinuationFrame(int streamId, List fragment, - bool endHeaders) { + void _writeContinuationFrame( + int streamId, List fragment, bool endHeaders) { int type = FrameType.CONTINUATION; int flags = endHeaders ? ContinuationFrame.FLAG_END_HEADERS : 0; @@ -115,13 +114,13 @@ class FrameWriter { _writeData(buffer); } - void writePriorityFrame(int streamId, int streamDependency, - int weight, {bool exclusive: false}) { + void writePriorityFrame(int streamId, int streamDependency, int weight, + {bool exclusive: false}) { int type = FrameType.PRIORITY; int flags = 0; - var buffer = new Uint8List( - FRAME_HEADER_SIZE + PriorityFrame.FIXED_FRAME_LENGTH); + var buffer = + new Uint8List(FRAME_HEADER_SIZE + PriorityFrame.FIXED_FRAME_LENGTH); int offset = 0; _setFrameHeader(buffer, offset, type, flags, streamId, 5); @@ -141,8 +140,8 @@ class FrameWriter { int type = FrameType.RST_STREAM; int flags = 0; - var buffer = new Uint8List( - FRAME_HEADER_SIZE + RstStreamFrame.FIXED_FRAME_LENGTH); + var buffer = + new Uint8List(FRAME_HEADER_SIZE + RstStreamFrame.FIXED_FRAME_LENGTH); int offset = 0; _setFrameHeader(buffer, offset, type, flags, streamId, 4); @@ -185,8 +184,8 @@ class FrameWriter { _writeData(buffer); } - void writePushPromiseFrame(int streamId, int promisedStreamId, - List
headers) { + void writePushPromiseFrame( + int streamId, int promisedStreamId, List
headers) { var fragment = _hpackEncoder.encode(headers); var maxSize = _peerSettings.maxFrameSize - PushPromiseFrame.MAX_CONSTANT_PAYLOAD; @@ -198,7 +197,7 @@ class FrameWriter { var chunk = fragment.sublist(0, maxSize); fragment = fragment.sublist(maxSize); _writePushPromiseFrameNoFragmentation( - streamId, promisedStreamId, chunk, false); + streamId, promisedStreamId, chunk, false); while (fragment.length > _peerSettings.maxFrameSize) { var chunk = fragment.sublist(0, _peerSettings.maxFrameSize); fragment = fragment.sublist(_peerSettings.maxFrameSize); @@ -229,8 +228,8 @@ class FrameWriter { int type = FrameType.PING; int flags = ack ? PingFrame.FLAG_ACK : 0; - var buffer = new Uint8List( - FRAME_HEADER_SIZE + PingFrame.FIXED_FRAME_LENGTH); + var buffer = + new Uint8List(FRAME_HEADER_SIZE + PingFrame.FIXED_FRAME_LENGTH); int offset = 0; _setFrameHeader(buffer, 0, type, flags, 0, 8); @@ -261,8 +260,8 @@ class FrameWriter { int type = FrameType.WINDOW_UPDATE; int flags = 0; - var buffer = new Uint8List( - FRAME_HEADER_SIZE + WindowUpdateFrame.FIXED_FRAME_LENGTH); + var buffer = + new Uint8List(FRAME_HEADER_SIZE + WindowUpdateFrame.FIXED_FRAME_LENGTH); int offset = 0; _setFrameHeader(buffer, offset, type, flags, streamId, 4); @@ -290,8 +289,8 @@ class FrameWriter { /// The future which will complete once this writer is done. Future get doneFuture => _outWriter.doneFuture; - void _setFrameHeader(List bytes, int offset, - int type, int flags, int streamId, int length) { + void _setFrameHeader(List bytes, int offset, int type, int flags, + int streamId, int length) { setInt24(bytes, offset, length); bytes[3] = type; bytes[4] = flags; diff --git a/pkgs/http2/lib/src/hpack/hpack.dart b/pkgs/http2/lib/src/hpack/hpack.dart index d9e5e2bfe1..d4deda55d2 100644 --- a/pkgs/http2/lib/src/hpack/hpack.dart +++ b/pkgs/http2/lib/src/hpack/hpack.dart @@ -23,7 +23,6 @@ class HPackDecodingException implements Exception { String toString() => 'HPackDecodingException: $_message'; } - /// A HPACK encoding/decoding context. /// /// This is a statefull class, so encoding/decoding changes internal state. @@ -31,16 +30,14 @@ class HPackContext { final HPackEncoder encoder = new HPackEncoder(); final HPackDecoder decoder = new HPackDecoder(); - HPackContext({int maxSendingHeaderTableSize: 4096, - int maxReceivingHeaderTableSize: 4096}) { - encoder.updateMaxSendingHeaderTableSize( - maxSendingHeaderTableSize); - decoder.updateMaxReceivingHeaderTableSize( - maxReceivingHeaderTableSize); + HPackContext( + {int maxSendingHeaderTableSize: 4096, + int maxReceivingHeaderTableSize: 4096}) { + encoder.updateMaxSendingHeaderTableSize(maxSendingHeaderTableSize); + decoder.updateMaxReceivingHeaderTableSize(maxReceivingHeaderTableSize); } } - /// A HTTP/2 header. class Header { final List name; @@ -54,7 +51,6 @@ class Header { } } - /// A stateful HPACK decoder. class HPackDecoder { int _maxHeaderTableSize; @@ -69,7 +65,7 @@ class HPackDecoder { int offset = 0; int readInteger(int prefixBits) { - assert (prefixBits <= 8 && prefixBits > 0); + assert(prefixBits <= 8 && prefixBits > 0); var byte = data[offset++] & ((1 << prefixBits) - 1); @@ -140,8 +136,8 @@ class HPackDecoder { } else if (isWithoutIndexing) { headers.add(readHeaderFieldInternal(readInteger(4))); } else if (isNeverIndexing) { - headers.add( - readHeaderFieldInternal(readInteger(4), neverIndexed: true)); + headers + .add(readHeaderFieldInternal(readInteger(4), neverIndexed: true)); } else if (isDynamicTableSizeUpdate) { int newMaxSize = readInteger(5); if (newMaxSize <= _maxHeaderTableSize) { @@ -165,7 +161,6 @@ class HPackDecoder { } } - /// A stateful HPACK encoder. // TODO: Currently we encode all headers: // - without huffman encoding @@ -186,7 +181,7 @@ class HPackEncoder { int currentByte = 0; void writeInteger(int prefixBits, int value) { - assert (prefixBits <= 8); + assert(prefixBits <= 8); if (value < (1 << prefixBits) - 1) { currentByte |= value; @@ -210,7 +205,7 @@ class HPackEncoder { void writeStringLiteral(List bytes) { // TODO: Support huffman encoding. - currentByte = 0; // 1 would be huffman encoding + currentByte = 0; // 1 would be huffman encoding writeInteger(7, bytes.length); bytesBuilder.add(bytes); } @@ -231,69 +226,68 @@ class HPackEncoder { class IndexTable { static final List
_staticTable = [ - null, - new Header(ASCII.encode(':authority'), const []), - new Header(ASCII.encode(':method'), ASCII.encode('GET')), - new Header(ASCII.encode(':method'), ASCII.encode('POST')), - new Header(ASCII.encode(':path'), ASCII.encode('/')), - new Header(ASCII.encode(':path'), ASCII.encode('/index.html')), - new Header(ASCII.encode(':scheme'), ASCII.encode('http')), - new Header(ASCII.encode(':scheme'), ASCII.encode('https')), - new Header(ASCII.encode(':status'), ASCII.encode('200')), - new Header(ASCII.encode(':status'), ASCII.encode('204')), - new Header(ASCII.encode(':status'), ASCII.encode('206')), - new Header(ASCII.encode(':status'), ASCII.encode('304')), - new Header(ASCII.encode(':status'), ASCII.encode('400')), - new Header(ASCII.encode(':status'), ASCII.encode('404')), - new Header(ASCII.encode(':status'), ASCII.encode('500')), - new Header(ASCII.encode('accept-charset'), const []), - new Header(ASCII.encode('accept-encoding'), - ASCII.encode('gzip, deflate')), - new Header(ASCII.encode('accept-language'), const []), - new Header(ASCII.encode('accept-ranges'), const []), - new Header(ASCII.encode('accept'), const []), - new Header(ASCII.encode('access-control-allow-origin'), const []), - new Header(ASCII.encode('age'), const []), - new Header(ASCII.encode('allow'), const []), - new Header(ASCII.encode('authorization'), const []), - new Header(ASCII.encode('cache-control'), const []), - new Header(ASCII.encode('content-disposition'), const []), - new Header(ASCII.encode('content-encoding'), const []), - new Header(ASCII.encode('content-language'), const []), - new Header(ASCII.encode('content-length'), const []), - new Header(ASCII.encode('content-location'), const []), - new Header(ASCII.encode('content-range'), const []), - new Header(ASCII.encode('content-type'), const []), - new Header(ASCII.encode('cookie'), const []), - new Header(ASCII.encode('date'), const []), - new Header(ASCII.encode('etag'), const []), - new Header(ASCII.encode('expect'), const []), - new Header(ASCII.encode('expires'), const []), - new Header(ASCII.encode('from'), const []), - new Header(ASCII.encode('host'), const []), - new Header(ASCII.encode('if-match'), const []), - new Header(ASCII.encode('if-modified-since'), const []), - new Header(ASCII.encode('if-none-match'), const []), - new Header(ASCII.encode('if-range'), const []), - new Header(ASCII.encode('if-unmodified-since'), const []), - new Header(ASCII.encode('last-modified'), const []), - new Header(ASCII.encode('link'), const []), - new Header(ASCII.encode('location'), const []), - new Header(ASCII.encode('max-forwards'), const []), - new Header(ASCII.encode('proxy-authenticate'), const []), - new Header(ASCII.encode('proxy-authorization'), const []), - new Header(ASCII.encode('range'), const []), - new Header(ASCII.encode('referer'), const []), - new Header(ASCII.encode('refresh'), const []), - new Header(ASCII.encode('retry-after'), const []), - new Header(ASCII.encode('server'), const []), - new Header(ASCII.encode('set-cookie'), const []), - new Header(ASCII.encode('strict-transport-security'), const []), - new Header(ASCII.encode('transfer-encoding'), const []), - new Header(ASCII.encode('user-agent'), const []), - new Header(ASCII.encode('vary'), const []), - new Header(ASCII.encode('via'), const []), - new Header(ASCII.encode('www-authenticate'), const []), + null, + new Header(ASCII.encode(':authority'), const []), + new Header(ASCII.encode(':method'), ASCII.encode('GET')), + new Header(ASCII.encode(':method'), ASCII.encode('POST')), + new Header(ASCII.encode(':path'), ASCII.encode('/')), + new Header(ASCII.encode(':path'), ASCII.encode('/index.html')), + new Header(ASCII.encode(':scheme'), ASCII.encode('http')), + new Header(ASCII.encode(':scheme'), ASCII.encode('https')), + new Header(ASCII.encode(':status'), ASCII.encode('200')), + new Header(ASCII.encode(':status'), ASCII.encode('204')), + new Header(ASCII.encode(':status'), ASCII.encode('206')), + new Header(ASCII.encode(':status'), ASCII.encode('304')), + new Header(ASCII.encode(':status'), ASCII.encode('400')), + new Header(ASCII.encode(':status'), ASCII.encode('404')), + new Header(ASCII.encode(':status'), ASCII.encode('500')), + new Header(ASCII.encode('accept-charset'), const []), + new Header(ASCII.encode('accept-encoding'), ASCII.encode('gzip, deflate')), + new Header(ASCII.encode('accept-language'), const []), + new Header(ASCII.encode('accept-ranges'), const []), + new Header(ASCII.encode('accept'), const []), + new Header(ASCII.encode('access-control-allow-origin'), const []), + new Header(ASCII.encode('age'), const []), + new Header(ASCII.encode('allow'), const []), + new Header(ASCII.encode('authorization'), const []), + new Header(ASCII.encode('cache-control'), const []), + new Header(ASCII.encode('content-disposition'), const []), + new Header(ASCII.encode('content-encoding'), const []), + new Header(ASCII.encode('content-language'), const []), + new Header(ASCII.encode('content-length'), const []), + new Header(ASCII.encode('content-location'), const []), + new Header(ASCII.encode('content-range'), const []), + new Header(ASCII.encode('content-type'), const []), + new Header(ASCII.encode('cookie'), const []), + new Header(ASCII.encode('date'), const []), + new Header(ASCII.encode('etag'), const []), + new Header(ASCII.encode('expect'), const []), + new Header(ASCII.encode('expires'), const []), + new Header(ASCII.encode('from'), const []), + new Header(ASCII.encode('host'), const []), + new Header(ASCII.encode('if-match'), const []), + new Header(ASCII.encode('if-modified-since'), const []), + new Header(ASCII.encode('if-none-match'), const []), + new Header(ASCII.encode('if-range'), const []), + new Header(ASCII.encode('if-unmodified-since'), const []), + new Header(ASCII.encode('last-modified'), const []), + new Header(ASCII.encode('link'), const []), + new Header(ASCII.encode('location'), const []), + new Header(ASCII.encode('max-forwards'), const []), + new Header(ASCII.encode('proxy-authenticate'), const []), + new Header(ASCII.encode('proxy-authorization'), const []), + new Header(ASCII.encode('range'), const []), + new Header(ASCII.encode('referer'), const []), + new Header(ASCII.encode('refresh'), const []), + new Header(ASCII.encode('retry-after'), const []), + new Header(ASCII.encode('server'), const []), + new Header(ASCII.encode('set-cookie'), const []), + new Header(ASCII.encode('strict-transport-security'), const []), + new Header(ASCII.encode('transfer-encoding'), const []), + new Header(ASCII.encode('user-agent'), const []), + new Header(ASCII.encode('vary'), const []), + new Header(ASCII.encode('via'), const []), + new Header(ASCII.encode('www-authenticate'), const []), ]; final List
_dynamicTable = []; diff --git a/pkgs/http2/lib/src/hpack/huffman.dart b/pkgs/http2/lib/src/hpack/huffman.dart index d1a4f6b20c..7dd4d09514 100644 --- a/pkgs/http2/lib/src/hpack/huffman.dart +++ b/pkgs/http2/lib/src/hpack/huffman.dart @@ -16,7 +16,6 @@ class HuffmanDecodingException implements Exception { String toString() => 'HuffmanDecodingException: $_message'; } - /// A codec used for encoding/decoding using a huffman codec. class HuffmanCodec { final HuffmanEncoder _encoder; @@ -29,7 +28,6 @@ class HuffmanCodec { List encode(List bytes) => _encoder.encode(bytes); } - /// A huffman decoder based on a [HuffmanTreeNode]. class HuffmanDecoder { final HuffmanTreeNode _root; @@ -76,8 +74,7 @@ class HuffmanDecoder { while (node.right != null) node = node.right; if (node.value != 256) { - throw new HuffmanDecodingException( - 'Incomplete encoding of a byte.'); + throw new HuffmanDecodingException('Incomplete encoding of a byte.'); } } @@ -85,7 +82,6 @@ class HuffmanDecoder { } } - /// A huffman encoder based on a list of codewords. class HuffmanEncoder { final List _codewords; @@ -122,6 +118,7 @@ class HuffmanEncoder { } } } + for (int i = 0; i < bytes.length; i++) { var byte = bytes[i]; var value = _codewords[byte]; @@ -136,7 +133,6 @@ class HuffmanEncoder { } } - /// Specifies the encoding of a specific value using huffman encoding. class EncodedHuffmanValue { /// An integer representation of the encoded bit-string. @@ -148,7 +144,6 @@ class EncodedHuffmanValue { const EncodedHuffmanValue(this.encodedBytes, this.numBits); } - /// A node in the huffman tree. class HuffmanTreeNode { HuffmanTreeNode left; @@ -156,7 +151,6 @@ class HuffmanTreeNode { int value; } - /// Generates a huffman decoding tree. HuffmanTreeNode generateHuffmanTree(List valueEncodings) { HuffmanTreeNode root = new HuffmanTreeNode(); @@ -187,4 +181,3 @@ HuffmanTreeNode generateHuffmanTree(List valueEncodings) { return root; } - diff --git a/pkgs/http2/lib/src/hpack/huffman_table.dart b/pkgs/http2/lib/src/hpack/huffman_table.dart index 5a794d8283..90471c8b6d 100644 --- a/pkgs/http2/lib/src/hpack/huffman_table.dart +++ b/pkgs/http2/lib/src/hpack/huffman_table.dart @@ -7,8 +7,8 @@ library http2.hpack.huffman_table; import 'huffman.dart'; /// The huffman codec for encoding/decoding HTTP/2 header blocks. -final HuffmanCodec http2HuffmanCodec = - new HuffmanCodec(new HuffmanEncoder(_codeWords), +final HuffmanCodec http2HuffmanCodec = new HuffmanCodec( + new HuffmanEncoder(_codeWords), new HuffmanDecoder(generateHuffmanTree(_codeWords))); /// This is the integer representing the End-of-String symbol @@ -18,261 +18,261 @@ const int EOS_BYTE = 256; /// This list of byte encodings via huffman encoding was generated from the /// HPACK specification. const List _codeWords = const [ - const EncodedHuffmanValue(0x1ff8, 13), - const EncodedHuffmanValue(0x7fffd8, 23), - const EncodedHuffmanValue(0xfffffe2, 28), - const EncodedHuffmanValue(0xfffffe3, 28), - const EncodedHuffmanValue(0xfffffe4, 28), - const EncodedHuffmanValue(0xfffffe5, 28), - const EncodedHuffmanValue(0xfffffe6, 28), - const EncodedHuffmanValue(0xfffffe7, 28), - const EncodedHuffmanValue(0xfffffe8, 28), - const EncodedHuffmanValue(0xffffea, 24), - const EncodedHuffmanValue(0x3ffffffc, 30), - const EncodedHuffmanValue(0xfffffe9, 28), - const EncodedHuffmanValue(0xfffffea, 28), - const EncodedHuffmanValue(0x3ffffffd, 30), - const EncodedHuffmanValue(0xfffffeb, 28), - const EncodedHuffmanValue(0xfffffec, 28), - const EncodedHuffmanValue(0xfffffed, 28), - const EncodedHuffmanValue(0xfffffee, 28), - const EncodedHuffmanValue(0xfffffef, 28), - const EncodedHuffmanValue(0xffffff0, 28), - const EncodedHuffmanValue(0xffffff1, 28), - const EncodedHuffmanValue(0xffffff2, 28), - const EncodedHuffmanValue(0x3ffffffe, 30), - const EncodedHuffmanValue(0xffffff3, 28), - const EncodedHuffmanValue(0xffffff4, 28), - const EncodedHuffmanValue(0xffffff5, 28), - const EncodedHuffmanValue(0xffffff6, 28), - const EncodedHuffmanValue(0xffffff7, 28), - const EncodedHuffmanValue(0xffffff8, 28), - const EncodedHuffmanValue(0xffffff9, 28), - const EncodedHuffmanValue(0xffffffa, 28), - const EncodedHuffmanValue(0xffffffb, 28), - const EncodedHuffmanValue(0x14, 6), - const EncodedHuffmanValue(0x3f8, 10), - const EncodedHuffmanValue(0x3f9, 10), - const EncodedHuffmanValue(0xffa, 12), - const EncodedHuffmanValue(0x1ff9, 13), - const EncodedHuffmanValue(0x15, 6), - const EncodedHuffmanValue(0xf8, 8), - const EncodedHuffmanValue(0x7fa, 11), - const EncodedHuffmanValue(0x3fa, 10), - const EncodedHuffmanValue(0x3fb, 10), - const EncodedHuffmanValue(0xf9, 8), - const EncodedHuffmanValue(0x7fb, 11), - const EncodedHuffmanValue(0xfa, 8), - const EncodedHuffmanValue(0x16, 6), - const EncodedHuffmanValue(0x17, 6), - const EncodedHuffmanValue(0x18, 6), - const EncodedHuffmanValue(0x0, 5), - const EncodedHuffmanValue(0x1, 5), - const EncodedHuffmanValue(0x2, 5), - const EncodedHuffmanValue(0x19, 6), - const EncodedHuffmanValue(0x1a, 6), - const EncodedHuffmanValue(0x1b, 6), - const EncodedHuffmanValue(0x1c, 6), - const EncodedHuffmanValue(0x1d, 6), - const EncodedHuffmanValue(0x1e, 6), - const EncodedHuffmanValue(0x1f, 6), - const EncodedHuffmanValue(0x5c, 7), - const EncodedHuffmanValue(0xfb, 8), - const EncodedHuffmanValue(0x7ffc, 15), - const EncodedHuffmanValue(0x20, 6), - const EncodedHuffmanValue(0xffb, 12), - const EncodedHuffmanValue(0x3fc, 10), - const EncodedHuffmanValue(0x1ffa, 13), - const EncodedHuffmanValue(0x21, 6), - const EncodedHuffmanValue(0x5d, 7), - const EncodedHuffmanValue(0x5e, 7), - const EncodedHuffmanValue(0x5f, 7), - const EncodedHuffmanValue(0x60, 7), - const EncodedHuffmanValue(0x61, 7), - const EncodedHuffmanValue(0x62, 7), - const EncodedHuffmanValue(0x63, 7), - const EncodedHuffmanValue(0x64, 7), - const EncodedHuffmanValue(0x65, 7), - const EncodedHuffmanValue(0x66, 7), - const EncodedHuffmanValue(0x67, 7), - const EncodedHuffmanValue(0x68, 7), - const EncodedHuffmanValue(0x69, 7), - const EncodedHuffmanValue(0x6a, 7), - const EncodedHuffmanValue(0x6b, 7), - const EncodedHuffmanValue(0x6c, 7), - const EncodedHuffmanValue(0x6d, 7), - const EncodedHuffmanValue(0x6e, 7), - const EncodedHuffmanValue(0x6f, 7), - const EncodedHuffmanValue(0x70, 7), - const EncodedHuffmanValue(0x71, 7), - const EncodedHuffmanValue(0x72, 7), - const EncodedHuffmanValue(0xfc, 8), - const EncodedHuffmanValue(0x73, 7), - const EncodedHuffmanValue(0xfd, 8), - const EncodedHuffmanValue(0x1ffb, 13), - const EncodedHuffmanValue(0x7fff0, 19), - const EncodedHuffmanValue(0x1ffc, 13), - const EncodedHuffmanValue(0x3ffc, 14), - const EncodedHuffmanValue(0x22, 6), - const EncodedHuffmanValue(0x7ffd, 15), - const EncodedHuffmanValue(0x3, 5), - const EncodedHuffmanValue(0x23, 6), - const EncodedHuffmanValue(0x4, 5), - const EncodedHuffmanValue(0x24, 6), - const EncodedHuffmanValue(0x5, 5), - const EncodedHuffmanValue(0x25, 6), - const EncodedHuffmanValue(0x26, 6), - const EncodedHuffmanValue(0x27, 6), - const EncodedHuffmanValue(0x6, 5), - const EncodedHuffmanValue(0x74, 7), - const EncodedHuffmanValue(0x75, 7), - const EncodedHuffmanValue(0x28, 6), - const EncodedHuffmanValue(0x29, 6), - const EncodedHuffmanValue(0x2a, 6), - const EncodedHuffmanValue(0x7, 5), - const EncodedHuffmanValue(0x2b, 6), - const EncodedHuffmanValue(0x76, 7), - const EncodedHuffmanValue(0x2c, 6), - const EncodedHuffmanValue(0x8, 5), - const EncodedHuffmanValue(0x9, 5), - const EncodedHuffmanValue(0x2d, 6), - const EncodedHuffmanValue(0x77, 7), - const EncodedHuffmanValue(0x78, 7), - const EncodedHuffmanValue(0x79, 7), - const EncodedHuffmanValue(0x7a, 7), - const EncodedHuffmanValue(0x7b, 7), - const EncodedHuffmanValue(0x7ffe, 15), - const EncodedHuffmanValue(0x7fc, 11), - const EncodedHuffmanValue(0x3ffd, 14), - const EncodedHuffmanValue(0x1ffd, 13), - const EncodedHuffmanValue(0xffffffc, 28), - const EncodedHuffmanValue(0xfffe6, 20), - const EncodedHuffmanValue(0x3fffd2, 22), - const EncodedHuffmanValue(0xfffe7, 20), - const EncodedHuffmanValue(0xfffe8, 20), - const EncodedHuffmanValue(0x3fffd3, 22), - const EncodedHuffmanValue(0x3fffd4, 22), - const EncodedHuffmanValue(0x3fffd5, 22), - const EncodedHuffmanValue(0x7fffd9, 23), - const EncodedHuffmanValue(0x3fffd6, 22), - const EncodedHuffmanValue(0x7fffda, 23), - const EncodedHuffmanValue(0x7fffdb, 23), - const EncodedHuffmanValue(0x7fffdc, 23), - const EncodedHuffmanValue(0x7fffdd, 23), - const EncodedHuffmanValue(0x7fffde, 23), - const EncodedHuffmanValue(0xffffeb, 24), - const EncodedHuffmanValue(0x7fffdf, 23), - const EncodedHuffmanValue(0xffffec, 24), - const EncodedHuffmanValue(0xffffed, 24), - const EncodedHuffmanValue(0x3fffd7, 22), - const EncodedHuffmanValue(0x7fffe0, 23), - const EncodedHuffmanValue(0xffffee, 24), - const EncodedHuffmanValue(0x7fffe1, 23), - const EncodedHuffmanValue(0x7fffe2, 23), - const EncodedHuffmanValue(0x7fffe3, 23), - const EncodedHuffmanValue(0x7fffe4, 23), - const EncodedHuffmanValue(0x1fffdc, 21), - const EncodedHuffmanValue(0x3fffd8, 22), - const EncodedHuffmanValue(0x7fffe5, 23), - const EncodedHuffmanValue(0x3fffd9, 22), - const EncodedHuffmanValue(0x7fffe6, 23), - const EncodedHuffmanValue(0x7fffe7, 23), - const EncodedHuffmanValue(0xffffef, 24), - const EncodedHuffmanValue(0x3fffda, 22), - const EncodedHuffmanValue(0x1fffdd, 21), - const EncodedHuffmanValue(0xfffe9, 20), - const EncodedHuffmanValue(0x3fffdb, 22), - const EncodedHuffmanValue(0x3fffdc, 22), - const EncodedHuffmanValue(0x7fffe8, 23), - const EncodedHuffmanValue(0x7fffe9, 23), - const EncodedHuffmanValue(0x1fffde, 21), - const EncodedHuffmanValue(0x7fffea, 23), - const EncodedHuffmanValue(0x3fffdd, 22), - const EncodedHuffmanValue(0x3fffde, 22), - const EncodedHuffmanValue(0xfffff0, 24), - const EncodedHuffmanValue(0x1fffdf, 21), - const EncodedHuffmanValue(0x3fffdf, 22), - const EncodedHuffmanValue(0x7fffeb, 23), - const EncodedHuffmanValue(0x7fffec, 23), - const EncodedHuffmanValue(0x1fffe0, 21), - const EncodedHuffmanValue(0x1fffe1, 21), - const EncodedHuffmanValue(0x3fffe0, 22), - const EncodedHuffmanValue(0x1fffe2, 21), - const EncodedHuffmanValue(0x7fffed, 23), - const EncodedHuffmanValue(0x3fffe1, 22), - const EncodedHuffmanValue(0x7fffee, 23), - const EncodedHuffmanValue(0x7fffef, 23), - const EncodedHuffmanValue(0xfffea, 20), - const EncodedHuffmanValue(0x3fffe2, 22), - const EncodedHuffmanValue(0x3fffe3, 22), - const EncodedHuffmanValue(0x3fffe4, 22), - const EncodedHuffmanValue(0x7ffff0, 23), - const EncodedHuffmanValue(0x3fffe5, 22), - const EncodedHuffmanValue(0x3fffe6, 22), - const EncodedHuffmanValue(0x7ffff1, 23), - const EncodedHuffmanValue(0x3ffffe0, 26), - const EncodedHuffmanValue(0x3ffffe1, 26), - const EncodedHuffmanValue(0xfffeb, 20), - const EncodedHuffmanValue(0x7fff1, 19), - const EncodedHuffmanValue(0x3fffe7, 22), - const EncodedHuffmanValue(0x7ffff2, 23), - const EncodedHuffmanValue(0x3fffe8, 22), - const EncodedHuffmanValue(0x1ffffec, 25), - const EncodedHuffmanValue(0x3ffffe2, 26), - const EncodedHuffmanValue(0x3ffffe3, 26), - const EncodedHuffmanValue(0x3ffffe4, 26), - const EncodedHuffmanValue(0x7ffffde, 27), - const EncodedHuffmanValue(0x7ffffdf, 27), - const EncodedHuffmanValue(0x3ffffe5, 26), - const EncodedHuffmanValue(0xfffff1, 24), - const EncodedHuffmanValue(0x1ffffed, 25), - const EncodedHuffmanValue(0x7fff2, 19), - const EncodedHuffmanValue(0x1fffe3, 21), - const EncodedHuffmanValue(0x3ffffe6, 26), - const EncodedHuffmanValue(0x7ffffe0, 27), - const EncodedHuffmanValue(0x7ffffe1, 27), - const EncodedHuffmanValue(0x3ffffe7, 26), - const EncodedHuffmanValue(0x7ffffe2, 27), - const EncodedHuffmanValue(0xfffff2, 24), - const EncodedHuffmanValue(0x1fffe4, 21), - const EncodedHuffmanValue(0x1fffe5, 21), - const EncodedHuffmanValue(0x3ffffe8, 26), - const EncodedHuffmanValue(0x3ffffe9, 26), - const EncodedHuffmanValue(0xffffffd, 28), - const EncodedHuffmanValue(0x7ffffe3, 27), - const EncodedHuffmanValue(0x7ffffe4, 27), - const EncodedHuffmanValue(0x7ffffe5, 27), - const EncodedHuffmanValue(0xfffec, 20), - const EncodedHuffmanValue(0xfffff3, 24), - const EncodedHuffmanValue(0xfffed, 20), - const EncodedHuffmanValue(0x1fffe6, 21), - const EncodedHuffmanValue(0x3fffe9, 22), - const EncodedHuffmanValue(0x1fffe7, 21), - const EncodedHuffmanValue(0x1fffe8, 21), - const EncodedHuffmanValue(0x7ffff3, 23), - const EncodedHuffmanValue(0x3fffea, 22), - const EncodedHuffmanValue(0x3fffeb, 22), - const EncodedHuffmanValue(0x1ffffee, 25), - const EncodedHuffmanValue(0x1ffffef, 25), - const EncodedHuffmanValue(0xfffff4, 24), - const EncodedHuffmanValue(0xfffff5, 24), - const EncodedHuffmanValue(0x3ffffea, 26), - const EncodedHuffmanValue(0x7ffff4, 23), - const EncodedHuffmanValue(0x3ffffeb, 26), - const EncodedHuffmanValue(0x7ffffe6, 27), - const EncodedHuffmanValue(0x3ffffec, 26), - const EncodedHuffmanValue(0x3ffffed, 26), - const EncodedHuffmanValue(0x7ffffe7, 27), - const EncodedHuffmanValue(0x7ffffe8, 27), - const EncodedHuffmanValue(0x7ffffe9, 27), - const EncodedHuffmanValue(0x7ffffea, 27), - const EncodedHuffmanValue(0x7ffffeb, 27), - const EncodedHuffmanValue(0xffffffe, 28), - const EncodedHuffmanValue(0x7ffffec, 27), - const EncodedHuffmanValue(0x7ffffed, 27), - const EncodedHuffmanValue(0x7ffffee, 27), - const EncodedHuffmanValue(0x7ffffef, 27), - const EncodedHuffmanValue(0x7fffff0, 27), - const EncodedHuffmanValue(0x3ffffee, 26), - const EncodedHuffmanValue(0x3fffffff, 30), + const EncodedHuffmanValue(0x1ff8, 13), + const EncodedHuffmanValue(0x7fffd8, 23), + const EncodedHuffmanValue(0xfffffe2, 28), + const EncodedHuffmanValue(0xfffffe3, 28), + const EncodedHuffmanValue(0xfffffe4, 28), + const EncodedHuffmanValue(0xfffffe5, 28), + const EncodedHuffmanValue(0xfffffe6, 28), + const EncodedHuffmanValue(0xfffffe7, 28), + const EncodedHuffmanValue(0xfffffe8, 28), + const EncodedHuffmanValue(0xffffea, 24), + const EncodedHuffmanValue(0x3ffffffc, 30), + const EncodedHuffmanValue(0xfffffe9, 28), + const EncodedHuffmanValue(0xfffffea, 28), + const EncodedHuffmanValue(0x3ffffffd, 30), + const EncodedHuffmanValue(0xfffffeb, 28), + const EncodedHuffmanValue(0xfffffec, 28), + const EncodedHuffmanValue(0xfffffed, 28), + const EncodedHuffmanValue(0xfffffee, 28), + const EncodedHuffmanValue(0xfffffef, 28), + const EncodedHuffmanValue(0xffffff0, 28), + const EncodedHuffmanValue(0xffffff1, 28), + const EncodedHuffmanValue(0xffffff2, 28), + const EncodedHuffmanValue(0x3ffffffe, 30), + const EncodedHuffmanValue(0xffffff3, 28), + const EncodedHuffmanValue(0xffffff4, 28), + const EncodedHuffmanValue(0xffffff5, 28), + const EncodedHuffmanValue(0xffffff6, 28), + const EncodedHuffmanValue(0xffffff7, 28), + const EncodedHuffmanValue(0xffffff8, 28), + const EncodedHuffmanValue(0xffffff9, 28), + const EncodedHuffmanValue(0xffffffa, 28), + const EncodedHuffmanValue(0xffffffb, 28), + const EncodedHuffmanValue(0x14, 6), + const EncodedHuffmanValue(0x3f8, 10), + const EncodedHuffmanValue(0x3f9, 10), + const EncodedHuffmanValue(0xffa, 12), + const EncodedHuffmanValue(0x1ff9, 13), + const EncodedHuffmanValue(0x15, 6), + const EncodedHuffmanValue(0xf8, 8), + const EncodedHuffmanValue(0x7fa, 11), + const EncodedHuffmanValue(0x3fa, 10), + const EncodedHuffmanValue(0x3fb, 10), + const EncodedHuffmanValue(0xf9, 8), + const EncodedHuffmanValue(0x7fb, 11), + const EncodedHuffmanValue(0xfa, 8), + const EncodedHuffmanValue(0x16, 6), + const EncodedHuffmanValue(0x17, 6), + const EncodedHuffmanValue(0x18, 6), + const EncodedHuffmanValue(0x0, 5), + const EncodedHuffmanValue(0x1, 5), + const EncodedHuffmanValue(0x2, 5), + const EncodedHuffmanValue(0x19, 6), + const EncodedHuffmanValue(0x1a, 6), + const EncodedHuffmanValue(0x1b, 6), + const EncodedHuffmanValue(0x1c, 6), + const EncodedHuffmanValue(0x1d, 6), + const EncodedHuffmanValue(0x1e, 6), + const EncodedHuffmanValue(0x1f, 6), + const EncodedHuffmanValue(0x5c, 7), + const EncodedHuffmanValue(0xfb, 8), + const EncodedHuffmanValue(0x7ffc, 15), + const EncodedHuffmanValue(0x20, 6), + const EncodedHuffmanValue(0xffb, 12), + const EncodedHuffmanValue(0x3fc, 10), + const EncodedHuffmanValue(0x1ffa, 13), + const EncodedHuffmanValue(0x21, 6), + const EncodedHuffmanValue(0x5d, 7), + const EncodedHuffmanValue(0x5e, 7), + const EncodedHuffmanValue(0x5f, 7), + const EncodedHuffmanValue(0x60, 7), + const EncodedHuffmanValue(0x61, 7), + const EncodedHuffmanValue(0x62, 7), + const EncodedHuffmanValue(0x63, 7), + const EncodedHuffmanValue(0x64, 7), + const EncodedHuffmanValue(0x65, 7), + const EncodedHuffmanValue(0x66, 7), + const EncodedHuffmanValue(0x67, 7), + const EncodedHuffmanValue(0x68, 7), + const EncodedHuffmanValue(0x69, 7), + const EncodedHuffmanValue(0x6a, 7), + const EncodedHuffmanValue(0x6b, 7), + const EncodedHuffmanValue(0x6c, 7), + const EncodedHuffmanValue(0x6d, 7), + const EncodedHuffmanValue(0x6e, 7), + const EncodedHuffmanValue(0x6f, 7), + const EncodedHuffmanValue(0x70, 7), + const EncodedHuffmanValue(0x71, 7), + const EncodedHuffmanValue(0x72, 7), + const EncodedHuffmanValue(0xfc, 8), + const EncodedHuffmanValue(0x73, 7), + const EncodedHuffmanValue(0xfd, 8), + const EncodedHuffmanValue(0x1ffb, 13), + const EncodedHuffmanValue(0x7fff0, 19), + const EncodedHuffmanValue(0x1ffc, 13), + const EncodedHuffmanValue(0x3ffc, 14), + const EncodedHuffmanValue(0x22, 6), + const EncodedHuffmanValue(0x7ffd, 15), + const EncodedHuffmanValue(0x3, 5), + const EncodedHuffmanValue(0x23, 6), + const EncodedHuffmanValue(0x4, 5), + const EncodedHuffmanValue(0x24, 6), + const EncodedHuffmanValue(0x5, 5), + const EncodedHuffmanValue(0x25, 6), + const EncodedHuffmanValue(0x26, 6), + const EncodedHuffmanValue(0x27, 6), + const EncodedHuffmanValue(0x6, 5), + const EncodedHuffmanValue(0x74, 7), + const EncodedHuffmanValue(0x75, 7), + const EncodedHuffmanValue(0x28, 6), + const EncodedHuffmanValue(0x29, 6), + const EncodedHuffmanValue(0x2a, 6), + const EncodedHuffmanValue(0x7, 5), + const EncodedHuffmanValue(0x2b, 6), + const EncodedHuffmanValue(0x76, 7), + const EncodedHuffmanValue(0x2c, 6), + const EncodedHuffmanValue(0x8, 5), + const EncodedHuffmanValue(0x9, 5), + const EncodedHuffmanValue(0x2d, 6), + const EncodedHuffmanValue(0x77, 7), + const EncodedHuffmanValue(0x78, 7), + const EncodedHuffmanValue(0x79, 7), + const EncodedHuffmanValue(0x7a, 7), + const EncodedHuffmanValue(0x7b, 7), + const EncodedHuffmanValue(0x7ffe, 15), + const EncodedHuffmanValue(0x7fc, 11), + const EncodedHuffmanValue(0x3ffd, 14), + const EncodedHuffmanValue(0x1ffd, 13), + const EncodedHuffmanValue(0xffffffc, 28), + const EncodedHuffmanValue(0xfffe6, 20), + const EncodedHuffmanValue(0x3fffd2, 22), + const EncodedHuffmanValue(0xfffe7, 20), + const EncodedHuffmanValue(0xfffe8, 20), + const EncodedHuffmanValue(0x3fffd3, 22), + const EncodedHuffmanValue(0x3fffd4, 22), + const EncodedHuffmanValue(0x3fffd5, 22), + const EncodedHuffmanValue(0x7fffd9, 23), + const EncodedHuffmanValue(0x3fffd6, 22), + const EncodedHuffmanValue(0x7fffda, 23), + const EncodedHuffmanValue(0x7fffdb, 23), + const EncodedHuffmanValue(0x7fffdc, 23), + const EncodedHuffmanValue(0x7fffdd, 23), + const EncodedHuffmanValue(0x7fffde, 23), + const EncodedHuffmanValue(0xffffeb, 24), + const EncodedHuffmanValue(0x7fffdf, 23), + const EncodedHuffmanValue(0xffffec, 24), + const EncodedHuffmanValue(0xffffed, 24), + const EncodedHuffmanValue(0x3fffd7, 22), + const EncodedHuffmanValue(0x7fffe0, 23), + const EncodedHuffmanValue(0xffffee, 24), + const EncodedHuffmanValue(0x7fffe1, 23), + const EncodedHuffmanValue(0x7fffe2, 23), + const EncodedHuffmanValue(0x7fffe3, 23), + const EncodedHuffmanValue(0x7fffe4, 23), + const EncodedHuffmanValue(0x1fffdc, 21), + const EncodedHuffmanValue(0x3fffd8, 22), + const EncodedHuffmanValue(0x7fffe5, 23), + const EncodedHuffmanValue(0x3fffd9, 22), + const EncodedHuffmanValue(0x7fffe6, 23), + const EncodedHuffmanValue(0x7fffe7, 23), + const EncodedHuffmanValue(0xffffef, 24), + const EncodedHuffmanValue(0x3fffda, 22), + const EncodedHuffmanValue(0x1fffdd, 21), + const EncodedHuffmanValue(0xfffe9, 20), + const EncodedHuffmanValue(0x3fffdb, 22), + const EncodedHuffmanValue(0x3fffdc, 22), + const EncodedHuffmanValue(0x7fffe8, 23), + const EncodedHuffmanValue(0x7fffe9, 23), + const EncodedHuffmanValue(0x1fffde, 21), + const EncodedHuffmanValue(0x7fffea, 23), + const EncodedHuffmanValue(0x3fffdd, 22), + const EncodedHuffmanValue(0x3fffde, 22), + const EncodedHuffmanValue(0xfffff0, 24), + const EncodedHuffmanValue(0x1fffdf, 21), + const EncodedHuffmanValue(0x3fffdf, 22), + const EncodedHuffmanValue(0x7fffeb, 23), + const EncodedHuffmanValue(0x7fffec, 23), + const EncodedHuffmanValue(0x1fffe0, 21), + const EncodedHuffmanValue(0x1fffe1, 21), + const EncodedHuffmanValue(0x3fffe0, 22), + const EncodedHuffmanValue(0x1fffe2, 21), + const EncodedHuffmanValue(0x7fffed, 23), + const EncodedHuffmanValue(0x3fffe1, 22), + const EncodedHuffmanValue(0x7fffee, 23), + const EncodedHuffmanValue(0x7fffef, 23), + const EncodedHuffmanValue(0xfffea, 20), + const EncodedHuffmanValue(0x3fffe2, 22), + const EncodedHuffmanValue(0x3fffe3, 22), + const EncodedHuffmanValue(0x3fffe4, 22), + const EncodedHuffmanValue(0x7ffff0, 23), + const EncodedHuffmanValue(0x3fffe5, 22), + const EncodedHuffmanValue(0x3fffe6, 22), + const EncodedHuffmanValue(0x7ffff1, 23), + const EncodedHuffmanValue(0x3ffffe0, 26), + const EncodedHuffmanValue(0x3ffffe1, 26), + const EncodedHuffmanValue(0xfffeb, 20), + const EncodedHuffmanValue(0x7fff1, 19), + const EncodedHuffmanValue(0x3fffe7, 22), + const EncodedHuffmanValue(0x7ffff2, 23), + const EncodedHuffmanValue(0x3fffe8, 22), + const EncodedHuffmanValue(0x1ffffec, 25), + const EncodedHuffmanValue(0x3ffffe2, 26), + const EncodedHuffmanValue(0x3ffffe3, 26), + const EncodedHuffmanValue(0x3ffffe4, 26), + const EncodedHuffmanValue(0x7ffffde, 27), + const EncodedHuffmanValue(0x7ffffdf, 27), + const EncodedHuffmanValue(0x3ffffe5, 26), + const EncodedHuffmanValue(0xfffff1, 24), + const EncodedHuffmanValue(0x1ffffed, 25), + const EncodedHuffmanValue(0x7fff2, 19), + const EncodedHuffmanValue(0x1fffe3, 21), + const EncodedHuffmanValue(0x3ffffe6, 26), + const EncodedHuffmanValue(0x7ffffe0, 27), + const EncodedHuffmanValue(0x7ffffe1, 27), + const EncodedHuffmanValue(0x3ffffe7, 26), + const EncodedHuffmanValue(0x7ffffe2, 27), + const EncodedHuffmanValue(0xfffff2, 24), + const EncodedHuffmanValue(0x1fffe4, 21), + const EncodedHuffmanValue(0x1fffe5, 21), + const EncodedHuffmanValue(0x3ffffe8, 26), + const EncodedHuffmanValue(0x3ffffe9, 26), + const EncodedHuffmanValue(0xffffffd, 28), + const EncodedHuffmanValue(0x7ffffe3, 27), + const EncodedHuffmanValue(0x7ffffe4, 27), + const EncodedHuffmanValue(0x7ffffe5, 27), + const EncodedHuffmanValue(0xfffec, 20), + const EncodedHuffmanValue(0xfffff3, 24), + const EncodedHuffmanValue(0xfffed, 20), + const EncodedHuffmanValue(0x1fffe6, 21), + const EncodedHuffmanValue(0x3fffe9, 22), + const EncodedHuffmanValue(0x1fffe7, 21), + const EncodedHuffmanValue(0x1fffe8, 21), + const EncodedHuffmanValue(0x7ffff3, 23), + const EncodedHuffmanValue(0x3fffea, 22), + const EncodedHuffmanValue(0x3fffeb, 22), + const EncodedHuffmanValue(0x1ffffee, 25), + const EncodedHuffmanValue(0x1ffffef, 25), + const EncodedHuffmanValue(0xfffff4, 24), + const EncodedHuffmanValue(0xfffff5, 24), + const EncodedHuffmanValue(0x3ffffea, 26), + const EncodedHuffmanValue(0x7ffff4, 23), + const EncodedHuffmanValue(0x3ffffeb, 26), + const EncodedHuffmanValue(0x7ffffe6, 27), + const EncodedHuffmanValue(0x3ffffec, 26), + const EncodedHuffmanValue(0x3ffffed, 26), + const EncodedHuffmanValue(0x7ffffe7, 27), + const EncodedHuffmanValue(0x7ffffe8, 27), + const EncodedHuffmanValue(0x7ffffe9, 27), + const EncodedHuffmanValue(0x7ffffea, 27), + const EncodedHuffmanValue(0x7ffffeb, 27), + const EncodedHuffmanValue(0xffffffe, 28), + const EncodedHuffmanValue(0x7ffffec, 27), + const EncodedHuffmanValue(0x7ffffed, 27), + const EncodedHuffmanValue(0x7ffffee, 27), + const EncodedHuffmanValue(0x7ffffef, 27), + const EncodedHuffmanValue(0x7fffff0, 27), + const EncodedHuffmanValue(0x3ffffee, 26), + const EncodedHuffmanValue(0x3fffffff, 30), ]; diff --git a/pkgs/http2/lib/src/settings/settings.dart b/pkgs/http2/lib/src/settings/settings.dart index 398226a010..4cd87bcf79 100644 --- a/pkgs/http2/lib/src/settings/settings.dart +++ b/pkgs/http2/lib/src/settings/settings.dart @@ -73,15 +73,15 @@ class ActiveSettings { /// enforced. The initial value of this setting is unlimited. int maxHeaderListSize; - ActiveSettings({this.headerTableSize: 4096, - this.enablePush: true, - this.maxConcurrentStreams: null, - this.initialWindowSize: (1 << 16) - 1, - this.maxFrameSize: (1 << 14), - this.maxHeaderListSize: null}); + ActiveSettings( + {this.headerTableSize: 4096, + this.enablePush: true, + this.maxConcurrentStreams: null, + this.initialWindowSize: (1 << 16) - 1, + this.maxFrameSize: (1 << 14), + this.maxHeaderListSize: null}); } - /// Handles remote and local connection [Setting]s. /// /// Incoming [SettingFrame]s will be handled here to update the peer settings. @@ -111,13 +111,11 @@ class SettingsHandler extends Object with TerminatableMixin { /// Events are fired when a SettingsFrame changes the initial size /// of stream windows. - Stream get onInitialWindowSizeChange - => _onInitialWindowSizeChangeController.stream; + Stream get onInitialWindowSizeChange => + _onInitialWindowSizeChangeController.stream; - SettingsHandler(this._hpackEncoder, - this._frameWriter, - this._acknowledgedSettings, - this._peerSettings); + SettingsHandler(this._hpackEncoder, this._frameWriter, + this._acknowledgedSettings, this._peerSettings); /// The settings for this endpoint of the connection which the remote peer /// has ACKed and uses. @@ -131,10 +129,10 @@ class SettingsHandler extends Object with TerminatableMixin { /// change. void handleSettingsFrame(SettingsFrame frame) { ensureNotTerminatedSync(() { - assert (frame.header.streamId == 0); + assert(frame.header.streamId == 0); if (frame.hasAckFlag) { - assert (frame.header.length == 0); + assert(frame.header.length == 0); if (_toBeAcknowledgedSettings.isEmpty) { // NOTE: The specification does not say anything about ACKed settings @@ -157,8 +155,8 @@ class SettingsHandler extends Object with TerminatableMixin { void onTerminated(error) { _toBeAcknowledgedSettings.clear(); - _toBeAcknowledgedCompleters.forEach( - (Completer c) => c.completeError(error)); + _toBeAcknowledgedCompleters + .forEach((Completer c) => c.completeError(error)); } Future changeSettings(List changes) { @@ -173,8 +171,8 @@ class SettingsHandler extends Object with TerminatableMixin { }); } - void _modifySettings(ActiveSettings base, List changes, - bool peerSettings) { + void _modifySettings( + ActiveSettings base, List changes, bool peerSettings) { for (var setting in changes) { switch (setting.identifier) { case Setting.SETTINGS_ENABLE_PUSH: diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index ead8d07eb0..a4c73f2741 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -38,8 +38,7 @@ enum StreamState { /// Represents a HTTP/2 stream. class Http2StreamImpl extends TransportStream - implements ClientTransportStream, - ServerTransportStream { + implements ClientTransportStream, ServerTransportStream { /// The id of this stream. /// /// * odd numbered streams are client streams @@ -74,14 +73,15 @@ class Http2StreamImpl extends TransportStream StreamSubscription _outgoingCSubscription; - Http2StreamImpl(this.incomingQueue, - this.outgoingQueue, - this._outgoingC, - this.id, - this.windowHandler, - this._canPushFun, - this._pushStreamFun, - this._terminateStreamFun); + Http2StreamImpl( + this.incomingQueue, + this.outgoingQueue, + this._outgoingC, + this.id, + this.windowHandler, + this._canPushFun, + this._pushStreamFun, + this._terminateStreamFun); /// A stream of data and/or headers from the remote end. Stream get incomingMessages => incomingQueue.messages; @@ -98,8 +98,8 @@ class Http2StreamImpl extends TransportStream /// /// The [requestHeaders] are the headers to which the pushed stream /// responds to. - TransportStream push(List
requestHeaders) - => _pushStreamFun(this, requestHeaders); + TransportStream push(List
requestHeaders) => + _pushStreamFun(this, requestHeaders); void terminate() => _terminateStreamFun(this); @@ -151,10 +151,15 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { final void Function(bool isActive) _onActiveStateChanged; - StreamHandler._(this._frameWriter, this.incomingQueue, this.outgoingQueue, - this._peerSettings, this._localSettings, - this._onActiveStateChanged, this.nextStreamId, - this.lastRemoteStreamId); + StreamHandler._( + this._frameWriter, + this.incomingQueue, + this.outgoingQueue, + this._peerSettings, + this._localSettings, + this._onActiveStateChanged, + this.nextStreamId, + this.lastRemoteStreamId); factory StreamHandler.client( FrameWriter writer, @@ -163,9 +168,8 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { ActiveSettings peerSettings, ActiveSettings localSettings, void Function(bool isActive) onActiveStateChanged) { - return new StreamHandler._( - writer, incomingQueue, outgoingQueue, peerSettings, localSettings, - onActiveStateChanged, 1, 0); + return new StreamHandler._(writer, incomingQueue, outgoingQueue, + peerSettings, localSettings, onActiveStateChanged, 1, 0); } factory StreamHandler.server( @@ -175,15 +179,13 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { ActiveSettings peerSettings, ActiveSettings localSettings, void Function(bool isActive) onActiveStateChanged) { - return new StreamHandler._( - writer, incomingQueue, outgoingQueue, peerSettings, localSettings, - onActiveStateChanged, 2, -1); + return new StreamHandler._(writer, incomingQueue, outgoingQueue, + peerSettings, localSettings, onActiveStateChanged, 2, -1); } void onTerminated(exception) { - _openStreams.values.toList().forEach( - (stream) => _closeStreamAbnormally(stream, exception, - propagateException: true)); + _openStreams.values.toList().forEach((stream) => + _closeStreamAbnormally(stream, exception, propagateException: true)); startClosing(); } @@ -205,7 +207,8 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { .where((id) => id > lastStreamId && !_isPeerInitiatedStream(id)) .toList(); for (int id in streamIds) { - var exception = new StreamException(id, + var exception = new StreamException( + id, 'Remote end was telling us to stop. This stream was not processed ' 'and can therefore be retried (on a new connection).'); _closeStreamIdAbnormally(id, exception, propagateException: true); @@ -246,7 +249,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { TransportStream newRemoteStream(int remoteStreamId) { return ensureNotTerminatedSync(() { - assert (remoteStreamId <= MAX_STREAM_ID); + assert(remoteStreamId <= MAX_STREAM_ID); // NOTE: We cannot enforce that a new stream id is 2 higher than the last // used stream id. Meaning there can be "holes" in the sense that stream // ids are not used: @@ -261,11 +264,11 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { if (remoteStreamId <= lastRemoteStreamId) { throw new ProtocolException('Remote tried to open new stream which is ' - 'not in "idle" state.'); + 'not in "idle" state.'); } bool sameDirection = (nextStreamId + remoteStreamId) % 2 == 0; - assert (!sameDirection); + assert(!sameDirection); lastRemoteStreamId = remoteStreamId; return _newStreamInternal(remoteStreamId); @@ -283,28 +286,34 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { // * the underlying transport [outgoing] // - register incoming stream queue in connection-level queue - var outgoingStreamWindow = new Window( - initialSize: _peerSettings.initialWindowSize); + var outgoingStreamWindow = + new Window(initialSize: _peerSettings.initialWindowSize); - var incomingStreamWindow = new Window( - initialSize: _localSettings.initialWindowSize); + var incomingStreamWindow = + new Window(initialSize: _localSettings.initialWindowSize); - var windowOutHandler = new OutgoingStreamWindowHandler( - outgoingStreamWindow); + var windowOutHandler = + new OutgoingStreamWindowHandler(outgoingStreamWindow); var windowInHandler = new IncomingWindowHandler.stream( _frameWriter, incomingStreamWindow, streamId); var streamQueueIn = new StreamMessageQueueIn(windowInHandler); - var streamQueueOut = new StreamMessageQueueOut( - streamId, windowOutHandler, outgoingQueue); + var streamQueueOut = + new StreamMessageQueueOut(streamId, windowOutHandler, outgoingQueue); incomingQueue.insertNewStreamMessageQueue(streamId, streamQueueIn); var _outgoingC = new StreamController(); var stream = new Http2StreamImpl( - streamQueueIn, streamQueueOut, _outgoingC, streamId, windowOutHandler, - this._canPush, this._push, this._terminateStream); + streamQueueIn, + streamQueueOut, + _outgoingC, + streamId, + windowOutHandler, + this._canPush, + this._push, + this._terminateStream); final wasIdle = _openStreams.isEmpty; _openStreams[stream.id] = stream; @@ -326,9 +335,11 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { bool _canPush(Http2StreamImpl stream) { bool openState = (stream.state == StreamState.Open || - stream.state == StreamState.HalfClosedRemote); + stream.state == StreamState.HalfClosedRemote); bool pushEnabled = this._peerSettings.enablePush; - return openState && pushEnabled && _canCreateNewStream() && + return openState && + pushEnabled && + _canCreateNewStream() && !_ranOutOfStreamIds(); } @@ -336,7 +347,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { if (stream.state != StreamState.Open && stream.state != StreamState.HalfClosedRemote) { throw new StateError('Cannot push based on a stream that is neither open ' - 'nor half-closed-remote.'); + 'nor half-closed-remote.'); } if (!_peerSettings.enablePush) { @@ -349,7 +360,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { if (_ranOutOfStreamIds()) { throw new StateError('There are no more stream ids left. Please use a ' - 'new connection.'); + 'new connection.'); } Http2StreamImpl pushStream = newLocalStream(); @@ -361,8 +372,8 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { // TODO: We should wait for us to send the headers frame before doing this // transition. _changeState(pushStream, StreamState.HalfClosedRemote); - pushStream.incomingQueue.enqueueMessage( - new DataMessage(stream.id, const [], true)); + pushStream.incomingQueue + .enqueueMessage(new DataMessage(stream.id, const [], true)); _frameWriter.writePushPromiseFrame( stream.id, pushStream.id, requestHeaders); @@ -384,20 +395,20 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { void _setupOutgoingMessageHandling(Http2StreamImpl stream) { stream._outgoingCSubscription = stream._outgoingC.stream.listen((StreamMessage msg) { - if (!wasTerminated) { - _handleNewOutgoingMessage(stream, msg); - } - }, onError: (error, stack) { - if (!wasTerminated) { - stream.terminate(); - } - }, onDone: () { - if (!wasTerminated) { - // Stream should already have been closed by the last frame, but we - // allow multiple close calls, just to make sure. - _handleOutgoingClose(stream); - } - }); + if (!wasTerminated) { + _handleNewOutgoingMessage(stream, msg); + } + }, onError: (error, stack) { + if (!wasTerminated) { + stream.terminate(); + } + }, onDone: () { + if (!wasTerminated) { + // Stream should already have been closed by the last frame, but we + // allow multiple close calls, just to make sure. + _handleOutgoingClose(stream); + } + }); stream.outgoingQueue.bufferIndicator.bufferEmptyEvents.listen((_) { if (stream._outgoingCSubscription.isPaused) { stream._outgoingCSubscription.resume(); @@ -455,8 +466,8 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { } } - void _processStreamFrameInternal(ConnectionState connectionState, - Frame frame) { + void _processStreamFrameInternal( + ConnectionState connectionState, Frame frame) { // If we initiated a close of the connection and the received frame belongs // to a stream id which is higher than the last peer-initiated stream we // processed, we'll ignore it. @@ -490,8 +501,9 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { int streamId = frame.header.streamId; bool isServerStreamId = frame.header.streamId.isEven; bool isLocalStream = isServerStreamId == isServer; - bool isIdleStream = isLocalStream ? - streamId >= nextStreamId : streamId > lastRemoteStreamId; + bool isIdleStream = isLocalStream + ? streamId >= nextStreamId + : streamId > lastRemoteStreamId; return isIdleStream; } @@ -557,11 +569,11 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { // TODO: When implementing priorities for HTTP/2 streams, these frames // need to be taken into account. } else if (frame is PushPromiseFrame) { - throw new ProtocolException( - 'Cannot push on a non-existent stream ' + throw new ProtocolException('Cannot push on a non-existent stream ' '(stream ${frame.header.streamId} does not exist)'); } else { - throw new StreamClosedException(frame.header.streamId, + throw new StreamClosedException( + frame.header.streamId, 'No open stream found and was not a headers frame opening a ' 'new stream.'); } @@ -631,8 +643,8 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { void _handleRstFrame(Http2StreamImpl stream, RstStreamFrame frame) { stream._handleTerminated(frame.errorCode); - var exception = new StreamException( - stream.id, 'Stream was terminated by peer.'); + var exception = + new StreamException(stream.id, 'Stream was terminated by peer.'); _closeStreamAbnormally(stream, exception, propagateException: true); } @@ -655,21 +667,20 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { } } - //////////////////////////////////////////////////////////////////////////// //// Process outgoing stream messages //////////////////////////////////////////////////////////////////////////// void _sendHeaders(Http2StreamImpl stream, List
headers, - {bool endStream: false}) { + {bool endStream: false}) { if (stream.state != StreamState.Idle && stream.state != StreamState.Open && stream.state != StreamState.HalfClosedRemote) { throw new StateError('Idle state expected.'); } - stream.outgoingQueue.enqueueMessage( - new HeadersMessage(stream.id, headers, endStream)); + stream.outgoingQueue + .enqueueMessage(new HeadersMessage(stream.id, headers, endStream)); if (stream.state == StreamState.Idle) { _changeState(stream, StreamState.Open); @@ -681,14 +692,14 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { } void _sendData(Http2StreamImpl stream, List data, - {bool endStream: false}) { + {bool endStream: false}) { if (stream.state != StreamState.Open && stream.state != StreamState.HalfClosedRemote) { throw new StateError('Open state expected (was: ${stream.state}).'); } - stream.outgoingQueue.enqueueMessage( - new DataMessage(stream.id, data, endStream)); + stream.outgoingQueue + .enqueueMessage(new DataMessage(stream.id, data, endStream)); if (endStream) { _endStream(stream); @@ -726,16 +737,16 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { } void _closeStreamIdAbnormally(int streamId, Exception exception, - {bool propagateException: false}) { + {bool propagateException: false}) { Http2StreamImpl stream = _openStreams[streamId]; if (stream != null) { - _closeStreamAbnormally( - stream, exception, propagateException: propagateException); + _closeStreamAbnormally(stream, exception, + propagateException: propagateException); } } void _closeStreamAbnormally(Http2StreamImpl stream, Exception exception, - {bool propagateException: false}) { + {bool propagateException: false}) { incomingQueue.removeStreamMessageQueue(stream.id); if (stream.state != StreamState.Terminated) { @@ -783,8 +794,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { StreamState from = stream.state; // In checked mode we'll test that the state transition is allowed. - assert( - (from == StreamState.Idle && to == StreamState.ReservedLocal) || + assert((from == StreamState.Idle && to == StreamState.ReservedLocal) || (from == StreamState.Idle && to == StreamState.ReservedRemote) || (from == StreamState.Idle && to == StreamState.Open) || (from == StreamState.Open && to == StreamState.HalfClosedLocal) || @@ -793,11 +803,11 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { (from == StreamState.HalfClosedLocal && to == StreamState.Closed) || (from == StreamState.HalfClosedRemote && to == StreamState.Closed) || (from == StreamState.ReservedLocal && - to == StreamState.HalfClosedRemote) || + to == StreamState.HalfClosedRemote) || (from == StreamState.ReservedLocal && to == StreamState.Closed) || (from == StreamState.ReservedRemote && to == StreamState.Closed) || (from == StreamState.ReservedRemote && - to == StreamState.HalfClosedLocal) || + to == StreamState.HalfClosedLocal) || (from != StreamState.Terminated && to == StreamState.Terminated)); // If we initiated the stream and it became "open" or "closed" we need to @@ -837,4 +847,3 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { return (isServer && id.isEven) || (!isServer && id.isOdd); } } - diff --git a/pkgs/http2/lib/src/testing/client.dart b/pkgs/http2/lib/src/testing/client.dart index 7fee8ccd8a..6966b829e3 100644 --- a/pkgs/http2/lib/src/testing/client.dart +++ b/pkgs/http2/lib/src/testing/client.dart @@ -39,8 +39,8 @@ class ClientConnection { /// /// If [settings] are omitted, the default [ClientSettings] will be used. ClientConnection(Socket socket, {ClientSettings settings}) - : connection = new ClientTransportConnection.viaSocket( - socket, settings: settings); + : connection = + new ClientTransportConnection.viaSocket(socket, settings: settings); Future makeRequest(Request request) { var path = request.uri.path; @@ -110,8 +110,9 @@ class ClientConnection { Map> _convertHeaders(List
headers) { var headerMap = {}; for (var header in headers) { - headerMap.putIfAbsent(ASCII.decode(header.name), () => []) - .add(ASCII.decode(header.value)); + headerMap + .putIfAbsent(ASCII.decode(header.name), () => []) + .add(ASCII.decode(header.value)); } return headerMap; } @@ -124,20 +125,24 @@ class ClientConnection { /// client. The maximum number of concurrent server pushes can be configured via /// [maxConcurrentPushes] (default is `null` meaning no limit). Future connect(Uri uri, - {bool allowServerPushes: false, - int maxConcurrentPushes: null}) async { - const List Http2AlpnProtocols = - const ['h2-14', 'h2-15', 'h2-16', 'h2-17', 'h2']; + {bool allowServerPushes: false, int maxConcurrentPushes: null}) async { + const List Http2AlpnProtocols = const [ + 'h2-14', + 'h2-15', + 'h2-16', + 'h2-17', + 'h2' + ]; bool useSSL = uri.scheme == 'https'; var settings = new ClientSettings( concurrentStreamLimit: maxConcurrentPushes, allowServerPushes: allowServerPushes); if (useSSL) { - SecureSocket socket = await SecureSocket.connect( - uri.host, uri.port, supportedProtocols: Http2AlpnProtocols); + SecureSocket socket = await SecureSocket.connect(uri.host, uri.port, + supportedProtocols: Http2AlpnProtocols); if (!Http2AlpnProtocols.contains(socket.selectedProtocol)) { - throw new Exception('Server does not support HTTP/2.'); + throw new Exception('Server does not support HTTP/2.'); } return new ClientConnection(socket, settings: settings); } else { diff --git a/pkgs/http2/lib/src/testing/debug.dart b/pkgs/http2/lib/src/testing/debug.dart index 61fc7de4d1..97c1f11e10 100644 --- a/pkgs/http2/lib/src/testing/debug.dart +++ b/pkgs/http2/lib/src/testing/debug.dart @@ -17,10 +17,8 @@ import '../../transport.dart'; final jsonEncoder = new JsonEncoder.withIndent(' '); - TransportConnection debugPrintingConnection(Socket socket, - {bool isServer: true, - bool verbose: true}) { + {bool isServer: true, bool verbose: true}) { var connection; var incoming = decodeVerbose(socket, isServer, verbose: verbose); @@ -33,9 +31,8 @@ TransportConnection debugPrintingConnection(Socket socket, return connection; } -Stream> decodeVerbose(Stream> inc, - bool isServer, - {bool verbose: true}) { +Stream> decodeVerbose(Stream> inc, bool isServer, + {bool verbose: true}) { String name = isServer ? 'server' : 'client'; var sc = new StreamController(); @@ -46,7 +43,7 @@ Stream> decodeVerbose(Stream> inc, if (!isServer) { _decodeFrames(sDebug.stream).listen((frame) { print('[$name/stream:${frame.header.streamId}] ' - 'Incoming ${frame.runtimeType}:'); + 'Incoming ${frame.runtimeType}:'); if (verbose) { print(jsonEncoder.convert(frame.toJson())); print(''); @@ -60,7 +57,7 @@ Stream> decodeVerbose(Stream> inc, var s3 = readConnectionPreface(sDebug.stream); _decodeFrames(s3).listen((frame) { print('[$name/stream:${frame.header.streamId}] ' - 'Incoming ${frame.runtimeType}:'); + 'Incoming ${frame.runtimeType}:'); if (verbose) { print(jsonEncoder.convert(frame.toJson())); print(''); @@ -75,19 +72,18 @@ Stream> decodeVerbose(Stream> inc, return sc.stream; } -StreamSink> decodeOutgoingVerbose(StreamSink> sink, - bool isServer, - {bool verbose: true}) { +StreamSink> decodeOutgoingVerbose( + StreamSink> sink, bool isServer, + {bool verbose: true}) { String name = isServer ? 'server' : 'client'; var proxySink = new StreamController(); var copy = new StreamController(); if (!isServer) { - _decodeFrames(readConnectionPreface(copy.stream)) - .listen((Frame frame) { + _decodeFrames(readConnectionPreface(copy.stream)).listen((Frame frame) { print('[$name/stream:${frame.header.streamId}] ' - 'Outgoing ${frame.runtimeType}:'); + 'Outgoing ${frame.runtimeType}:'); if (verbose) { print(jsonEncoder.convert(frame.toJson())); print(''); @@ -100,7 +96,7 @@ StreamSink> decodeOutgoingVerbose(StreamSink> sink, } else { _decodeFrames(copy.stream).listen((Frame frame) { print('[$name/stream:${frame.header.streamId}] ' - 'Outgoing ${frame.runtimeType}:'); + 'Outgoing ${frame.runtimeType}:'); if (verbose) { print(jsonEncoder.convert(frame.toJson())); print(''); @@ -133,8 +129,10 @@ Future _pipeAndCopy(Stream from, StreamSink to, StreamSink to2) { to.addError(e, s); to2.addError(e, s); }, onDone: () { - Future.wait([to.close(), to2.close()]) - .then(c.complete).catchError(c.completeError); + Future + .wait([to.close(), to2.close()]) + .then(c.complete) + .catchError(c.completeError); }); return c.future; } diff --git a/pkgs/http2/lib/transport.dart b/pkgs/http2/lib/transport.dart index 90ac383f02..f057db0f2f 100644 --- a/pkgs/http2/lib/transport.dart +++ b/pkgs/http2/lib/transport.dart @@ -121,10 +121,10 @@ abstract class Settings { /// Settings for a [TransportConnection] a server can make. class ServerSettings extends Settings { - const ServerSettings({int concurrentStreamLimit, - int streamWindowSize}) - : super(concurrentStreamLimit: concurrentStreamLimit, - streamWindowSize: streamWindowSize); + const ServerSettings({int concurrentStreamLimit, int streamWindowSize}) + : super( + concurrentStreamLimit: concurrentStreamLimit, + streamWindowSize: streamWindowSize); } /// Settings for a [TransportConnection] a client can make. @@ -132,11 +132,13 @@ class ClientSettings extends Settings { /// Whether the client allows pushes from the server (defaults to false). final bool allowServerPushes; - const ClientSettings({int concurrentStreamLimit, - int streamWindowSize, - this.allowServerPushes: false}) - : super(concurrentStreamLimit: concurrentStreamLimit, - streamWindowSize: streamWindowSize); + const ClientSettings( + {int concurrentStreamLimit, + int streamWindowSize, + this.allowServerPushes: false}) + : super( + concurrentStreamLimit: concurrentStreamLimit, + streamWindowSize: streamWindowSize); } /// Represents a HTTP/2 connection. @@ -163,13 +165,12 @@ abstract class TransportConnection { abstract class ClientTransportConnection extends TransportConnection { factory ClientTransportConnection.viaSocket(Socket socket, - {ClientSettings settings}) - => new ClientTransportConnection.viaStreams( - socket, socket, settings: settings); + {ClientSettings settings}) => + new ClientTransportConnection.viaStreams(socket, socket, + settings: settings); factory ClientTransportConnection.viaStreams( - Stream> incoming, - StreamSink> outgoing, + Stream> incoming, StreamSink> outgoing, {ClientSettings settings}) { if (settings == null) settings = const ClientSettings(); return new ClientConnection(incoming, outgoing, settings); @@ -181,21 +182,20 @@ abstract class ClientTransportConnection extends TransportConnection { /// Creates a new outgoing stream. ClientTransportStream makeRequest(List
headers, - {bool endStream: false}); + {bool endStream: false}); } abstract class ServerTransportConnection extends TransportConnection { factory ServerTransportConnection.viaSocket(Socket socket, - {ServerSettings settings}) { - return new ServerTransportConnection.viaStreams( - socket, socket, settings: settings); + {ServerSettings settings}) { + return new ServerTransportConnection.viaStreams(socket, socket, + settings: settings); } factory ServerTransportConnection.viaStreams( - Stream> incoming, - StreamSink> outgoing, - {ServerSettings settings: const ServerSettings( - concurrentStreamLimit: 1000)}) { + Stream> incoming, StreamSink> outgoing, + {ServerSettings settings: + const ServerSettings(concurrentStreamLimit: 1000)}) { if (settings == null) settings = const ServerSettings(); return new ServerConnection(incoming, outgoing, settings); } @@ -235,8 +235,8 @@ abstract class TransportStream { // For convenience only. void sendHeaders(List
headers, {bool endStream: false}) { - outgoingMessages.add( - new HeadersStreamMessage(headers, endStream: endStream)); + outgoingMessages + .add(new HeadersStreamMessage(headers, endStream: endStream)); if (endStream) outgoingMessages.close(); } @@ -273,18 +273,15 @@ abstract class StreamMessage { StreamMessage({bool endStream}) : this.endStream = endStream ?? false; } - /// Represents a data message which can be sent over a HTTP/2 stream. class DataStreamMessage extends StreamMessage { final List bytes; - DataStreamMessage(this.bytes, {bool endStream}) - : super(endStream: endStream); + DataStreamMessage(this.bytes, {bool endStream}) : super(endStream: endStream); String toString() => 'DataStreamMessage(${bytes.length} bytes)'; } - /// Represents a headers message which can be sent over a HTTP/2 stream. class HeadersStreamMessage extends StreamMessage { final List
headers; @@ -295,7 +292,6 @@ class HeadersStreamMessage extends StreamMessage { String toString() => 'HeadersStreamMessage(${headers.length} headers)'; } - /// Represents a remote stream push. class TransportStreamPush { /// The request headers which [stream] is the response to. @@ -330,6 +326,5 @@ class TransportConnectionException extends TransportException { /// An exception thrown when a HTTP/2 stream error occured. class StreamTransportException extends TransportException { - StreamTransportException(String details) - : super('Stream error: $details'); + StreamTransportException(String details) : super('Stream error: $details'); } diff --git a/pkgs/http2/manual_test/out_of_stream_ids_test.dart b/pkgs/http2/manual_test/out_of_stream_ids_test.dart index 24c62a1d21..bddb1462c5 100644 --- a/pkgs/http2/manual_test/out_of_stream_ids_test.dart +++ b/pkgs/http2/manual_test/out_of_stream_ids_test.dart @@ -5,10 +5,10 @@ /// --------------------------------------------------------------------------- /// In order to run this test one needs to change the following line in /// ../lib/src/streams/stream_handler.dart -/// +/// /// - static const int MAX_STREAM_ID = (1 << 31) - 1; /// + static const int MAX_STREAM_ID = (1 << 5) - 1; -/// +/// /// --------------------------------------------------------------------------- import 'dart:async'; @@ -22,7 +22,7 @@ main() { group('transport-test', () { transportTest('client-runs-out-of-stream-ids', (ClientTransportConnection client, - ServerTransportConnection server) async { + ServerTransportConnection server) async { Future serverFun() async { await for (ServerTransportStream stream in server.incomingStreams) { stream.sendHeaders([new Header.ascii('x', 'y')], endStream: true); @@ -57,4 +57,3 @@ main() { }); }); } - diff --git a/pkgs/http2/test/client_test.dart b/pkgs/http2/test/client_test.dart index 4af36bca5d..0ec8a86bc7 100644 --- a/pkgs/http2/test/client_test.dart +++ b/pkgs/http2/test/client_test.dart @@ -24,10 +24,9 @@ main() { group('normal', () { clientTest('gracefull-shutdown-for-unused-connection', (ClientTransportConnection client, - FrameWriter serverWriter, - StreamIterator serverReader, - Future nextFrame()) async { - + FrameWriter serverWriter, + StreamIterator serverReader, + Future nextFrame()) async { var settingsDone = new Completer(); Future serverFun() async { @@ -67,10 +66,9 @@ main() { group('server-errors', () { clientTest('no-settings-frame-at-beginning-immediate-error', (ClientTransportConnection client, - FrameWriter serverWriter, - StreamIterator serverReader, - Future nextFrame()) async { - + FrameWriter serverWriter, + StreamIterator serverReader, + Future nextFrame()) async { var goawayReceived = new Completer(); Future serverFun() async { serverWriter.writePingFrame(42); @@ -92,7 +90,9 @@ main() { var error; try { client.makeRequest([new Header.ascii('a', 'b')]); - } catch (e) { error = '$e'; } + } catch (e) { + error = '$e'; + } expect(error, contains('no longer active')); await client.finish(); @@ -103,10 +103,9 @@ main() { clientTest('no-settings-frame-at-beginning-delayed-error', (ClientTransportConnection client, - FrameWriter serverWriter, - StreamIterator serverReader, - Future nextFrame()) async { - + FrameWriter serverWriter, + StreamIterator serverReader, + Future nextFrame()) async { Future serverFun() async { expect(await nextFrame() is SettingsFrame, true); expect(await nextFrame() is HeadersFrame, true); @@ -134,10 +133,9 @@ main() { clientTest('data-frame-for-invalid-stream', (ClientTransportConnection client, - FrameWriter serverWriter, - StreamIterator serverReader, - Future nextFrame()) async { - + FrameWriter serverWriter, + StreamIterator serverReader, + Future nextFrame()) async { var handshakeCompleter = new Completer(); Future serverFun() async { @@ -149,7 +147,7 @@ main() { handshakeCompleter.complete(); HeadersFrame headers = await nextFrame(); - DataFrame finFrame = await nextFrame(); + DataFrame finFrame = await nextFrame(); expect(finFrame.hasEndStreamFlag, true); // Write a data frame for a non-existent stream. @@ -163,8 +161,8 @@ main() { expect((frame as RstStreamFrame).header.streamId, invalidStreamId); // Close the original stream. - serverWriter.writeDataFrame( - headers.header.streamId, [], endStream: true); + serverWriter.writeDataFrame(headers.header.streamId, [], + endStream: true); // Wait for the client finish. expect(await nextFrame() is GoawayFrame, true); @@ -187,10 +185,9 @@ main() { clientTest('data-frame-after-stream-closed', (ClientTransportConnection client, - FrameWriter serverWriter, - StreamIterator serverReader, - Future nextFrame()) async { - + FrameWriter serverWriter, + StreamIterator serverReader, + Future nextFrame()) async { var handshakeCompleter = new Completer(); Future serverFun() async { @@ -202,7 +199,7 @@ main() { handshakeCompleter.complete(); HeadersFrame headers = await nextFrame(); - DataFrame finFrame = await nextFrame(); + DataFrame finFrame = await nextFrame(); expect(finFrame.hasEndStreamFlag, true); int streamId = headers.header.streamId; @@ -253,10 +250,9 @@ main() { clientTest('client-reports-connection-error-on-push-to-nonexistent', (ClientTransportConnection client, - FrameWriter serverWriter, - StreamIterator serverReader, - Future nextFrame()) async { - + FrameWriter serverWriter, + StreamIterator serverReader, + Future nextFrame()) async { var handshakeCompleter = new Completer(); Future serverFun() async { @@ -268,14 +264,14 @@ main() { handshakeCompleter.complete(); HeadersFrame headers = await nextFrame(); - DataFrame finFrame = await nextFrame(); + DataFrame finFrame = await nextFrame(); expect(finFrame.hasEndStreamFlag, true); int streamId = headers.header.streamId; // Write response. - serverWriter.writeHeadersFrame( - streamId, [new Header.ascii('a', 'b')], endStream: true); + serverWriter.writeHeadersFrame(streamId, [new Header.ascii('a', 'b')], + endStream: true); // Push stream to the (non existing) one. int pushStreamId = 2; @@ -285,7 +281,7 @@ main() { // Make sure we get a connection error. GoawayFrame frame = await nextFrame(); expect(ASCII.decode(frame.debugData), - contains('Cannot push on a non-existent stream')); + contains('Cannot push on a non-existent stream')); expect(await serverReader.moveNext(), false); await serverWriter.close(); } @@ -298,7 +294,7 @@ main() { var messages = await stream.incomingMessages.toList(); expect(messages, hasLength(1)); expect((messages[0] as HeadersStreamMessage).headers.first, - isHeader('a', 'b')); + isHeader('a', 'b')); await client.finish(); } @@ -307,10 +303,9 @@ main() { clientTest('client-reports-connection-error-on-push-to-non-open', (ClientTransportConnection client, - FrameWriter serverWriter, - StreamIterator serverReader, - Future nextFrame()) async { - + FrameWriter serverWriter, + StreamIterator serverReader, + Future nextFrame()) async { var handshakeCompleter = new Completer(); Future serverFun() async { @@ -334,8 +329,10 @@ main() { // Make sure we get a connection error. GoawayFrame frame = await nextFrame(); - expect(ASCII.decode(frame.debugData), - contains('Expected open state (was: StreamState.HalfClosedRemote)')); + expect( + ASCII.decode(frame.debugData), + contains( + 'Expected open state (was: StreamState.HalfClosedRemote)')); expect(await serverReader.moveNext(), false); await serverWriter.close(); } @@ -357,10 +354,9 @@ main() { clientTest('client-reports-flowcontrol-error-on-negative-window', (ClientTransportConnection client, - FrameWriter serverWriter, - StreamIterator serverReader, - Future nextFrame()) async { - + FrameWriter serverWriter, + StreamIterator serverReader, + Future nextFrame()) async { var handshakeCompleter = new Completer(); Future serverFun() async { @@ -386,9 +382,10 @@ main() { // Read the resulting [GoawayFrame] and assert the error message // describes that the flow control window became negative. GoawayFrame frame = await nextFrame(); - expect(ASCII.decode(frame.debugData), - contains('Connection level flow control window became ' - 'negative.')); + expect( + ASCII.decode(frame.debugData), + contains('Connection level flow control window became ' + 'negative.')); expect(await serverReader.moveNext(), false); await serverWriter.close(); } @@ -412,12 +409,10 @@ main() { }); group('client-errors', () { - clientTest('client-resets-stream', - (ClientTransportConnection client, - FrameWriter serverWriter, - StreamIterator serverReader, - Future nextFrame()) async { - + clientTest('client-resets-stream', (ClientTransportConnection client, + FrameWriter serverWriter, + StreamIterator serverReader, + Future nextFrame()) async { var settingsDone = new Completer(); var headersDone = new Completer(); @@ -458,8 +453,8 @@ main() { await settingsDone.future; // Make a new stream and terminate it. - var stream = client.makeRequest( - [new Header.ascii('a', 'b')], endStream: false); + var stream = client + .makeRequest([new Header.ascii('a', 'b')], endStream: false); await headersDone.future; stream.terminate(); @@ -477,10 +472,9 @@ main() { clientTest('goaway-terminates-nonprocessed-streams', (ClientTransportConnection client, - FrameWriter serverWriter, - StreamIterator serverReader, - Future nextFrame()) async { - + FrameWriter serverWriter, + StreamIterator serverReader, + Future nextFrame()) async { var settingsDone = new Completer(); Future serverFun() async { @@ -501,8 +495,7 @@ main() { expect(decodedHeaders[0], isHeader('a', 'b')); // Send the GoawayFrame. - serverWriter.writeGoawayFrame( - 0, ErrorCode.NO_ERROR, []); + serverWriter.writeGoawayFrame(0, ErrorCode.NO_ERROR, []); // Since there are no open streams left, the other end should just // close the connection. @@ -513,13 +506,15 @@ main() { await settingsDone.future; // Make a new stream and terminate it. - var stream = client.makeRequest( - [new Header.ascii('a', 'b')], endStream: false); + var stream = client + .makeRequest([new Header.ascii('a', 'b')], endStream: false); // Make sure we don't get messages/pushes on the terminated stream. stream.incomingMessages.toList().catchError(expectAsync1((e) { - expect('$e', contains('This stream was not processed and can ' - 'therefore be retried')); + expect( + '$e', + contains('This stream was not processed and can ' + 'therefore be retried')); })); expect(await stream.peerPushes.toList(), isEmpty); @@ -533,8 +528,8 @@ main() { }); } -clientTest(String name, - func(clientConnection, frameWriter, frameReader, readNext)) { +clientTest( + String name, func(clientConnection, frameWriter, frameReader, readNext)) { return test(name, () { var streams = new ClientStreams(); var serverReader = streams.serverConnectionFrameReader; @@ -544,10 +539,8 @@ clientTest(String name, return serverReader.current; } - return func(streams.clientConnection, - streams.serverConnectionFrameWriter, - serverReader, - readNext); + return func(streams.clientConnection, streams.serverConnectionFrameWriter, + serverReader, readNext); }); } @@ -562,7 +555,7 @@ class ClientStreams { var streamAfterConnectionPreface = readConnectionPreface(readA); return new StreamIterator( new FrameReader(streamAfterConnectionPreface, localSettings) - .startDecoding()); + .startDecoding()); } FrameWriter get serverConnectionFrameWriter { @@ -571,6 +564,6 @@ class ClientStreams { return new FrameWriter(encoder, writeB, peerSettings); } - ClientTransportConnection get clientConnection - => new ClientTransportConnection.viaStreams(readB, writeA); + ClientTransportConnection get clientConnection => + new ClientTransportConnection.viaStreams(readB, writeA); } diff --git a/pkgs/http2/test/client_websites_test.dart b/pkgs/http2/test/client_websites_test.dart index 1f6adf38bb..62e41b0f10 100644 --- a/pkgs/http2/test/client_websites_test.dart +++ b/pkgs/http2/test/client_websites_test.dart @@ -27,7 +27,7 @@ main() async { expect(body, contains('')); expect(body, contains('twitter.com')); }, onPlatform: { - 'mac-os' : new Skip('ALPN not supported on MacOS'), + 'mac-os': new Skip('ALPN not supported on MacOS'), }); test('nghttp2.org - server push enabled', () async { @@ -55,23 +55,25 @@ main() async { Future> accumulatePushes() async { var futures = []; - return response.serverPushes.listen((ServerPush push) { - futures.add(push.response.then((Response response) { - dumpHeaders(uri, push.requestHeaders, - msg: '**push** Request headers for push request.'); - dumpHeaders(uri, response.headers, - msg: '**push** Response headers for server push ' - 'request.'); - - return readBody(response).then((String body) { - return [push.requestHeaders[':path'].join(''), body]; - }); - })); - }).asFuture().then((_) => Future.wait(futures)); + return response.serverPushes + .listen((ServerPush push) { + futures.add(push.response.then((Response response) { + dumpHeaders(uri, push.requestHeaders, + msg: '**push** Request headers for push request.'); + dumpHeaders(uri, response.headers, + msg: '**push** Response headers for server push ' + 'request.'); + + return readBody(response).then((String body) { + return [push.requestHeaders[':path'].join(''), body]; + }); + })); + }) + .asFuture() + .then((_) => Future.wait(futures)); } - var results = await Future.wait( - [readBody(response), accumulatePushes()]); + var results = await Future.wait([readBody(response), accumulatePushes()]); var body = results[0]; expect(body, contains('')); @@ -83,24 +85,27 @@ main() async { expect(pushes[0][1], contains('audio,video{')); await connection.close(); }, onPlatform: { - 'mac-os' : new Skip('ALPN not supported on MacOS'), + 'mac-os': new Skip('ALPN not supported on MacOS'), }); test('nghttp2.org - server push disabled', () async { var uri = Uri.parse("https://nghttp2.org/"); - ClientConnection connection = await connect( - uri, allowServerPushes: false); + ClientConnection connection = + await connect(uri, allowServerPushes: false); var request = new Request('GET', uri); Response response = await connection.makeRequest(request); dumpHeaders(uri, response.headers); Future> accumulatePushes() async { var futures = []; - return response.serverPushes.listen((ServerPush push) { - futures.add(push.response.then( - (Response response) => response.stream.drain())); - }).asFuture().then((_) => Future.wait(futures)); + return response.serverPushes + .listen((ServerPush push) { + futures.add(push.response + .then((Response response) => response.stream.drain())); + }) + .asFuture() + .then((_) => Future.wait(futures)); } var results = await Future.wait([readBody(response), accumulatePushes()]); @@ -113,13 +118,13 @@ main() async { expect(pushes, hasLength(0)); await connection.close(); }, onPlatform: { - 'mac-os' : new Skip('ALPN not supported on MacOS'), + 'mac-os': new Skip('ALPN not supported on MacOS'), }); }); } dumpHeaders(Uri uri, Map> headers, - {String msg: 'Response headers.'}) { + {String msg: 'Response headers.'}) { print(''); print('[$uri] $msg'); for (var key in headers.keys.toList()..sort()) { diff --git a/pkgs/http2/test/multiprotocol_server_test.dart b/pkgs/http2/test/multiprotocol_server_test.dart index f8f560a0aa..a26e89fbce 100644 --- a/pkgs/http2/test/multiprotocol_server_test.dart +++ b/pkgs/http2/test/multiprotocol_server_test.dart @@ -14,10 +14,9 @@ import 'package:http2/transport.dart'; import 'package:http2/multiprotocol_server.dart'; main() { - SecurityContext context = new SecurityContext() - ..useCertificateChain('test/certificates/server_chain.pem') - ..usePrivateKey('test/certificates/server_key.pem', - password: 'dartdart'); + SecurityContext context = new SecurityContext() + ..useCertificateChain('test/certificates/server_chain.pem') + ..usePrivateKey('test/certificates/server_key.pem', password: 'dartdart'); group('multiprotocol-server', () { test('http/1.1', () async { @@ -32,8 +31,7 @@ main() { await server.close(); } }, count: Count), - expectAsync1((ServerTransportStream stream) { - }, count: 0)); + expectAsync1((ServerTransportStream stream) {}, count: 0)); var client = new HttpClient(); client.badCertificateCallback = (_, __, ___) => true; @@ -41,7 +39,7 @@ main() { await makeHttp11Request(server, client, i); } }, onPlatform: { - 'mac-os' : new Skip('ALPN not supported on MacOS'), + 'mac-os': new Skip('ALPN not supported on MacOS'), }); test('http/2', () async { @@ -50,8 +48,7 @@ main() { var server = await MultiProtocolHttpServer.bind('localhost', 0, context); int requestNr = 0; server.startServing( - expectAsync1((HttpRequest request) { - }, count: 0), + expectAsync1((HttpRequest request) {}, count: 0), expectAsync1((ServerTransportStream stream) async { await handleHttp2Request(stream, requestNr++); if (requestNr == Count) { @@ -59,8 +56,7 @@ main() { } }, count: Count)); - var socket = await SecureSocket.connect( - 'localhost', server.port, + var socket = await SecureSocket.connect('localhost', server.port, onBadCertificate: (_) => true, supportedProtocols: ['http/1.1', 'h2']); var connection = new ClientTransportConnection.viaSocket(socket); @@ -69,16 +65,15 @@ main() { } await connection.finish(); }, onPlatform: { - 'mac-os' : new Skip('ALPN not supported on MacOS'), + 'mac-os': new Skip('ALPN not supported on MacOS'), }); }); } -Future makeHttp11Request(MultiProtocolHttpServer server, - HttpClient client, - int i) async { - var request = await client.getUrl( - Uri.parse('https://localhost:${server.port}/abc$i')); +Future makeHttp11Request( + MultiProtocolHttpServer server, HttpClient client, int i) async { + var request = + await client.getUrl(Uri.parse('https://localhost:${server.port}/abc$i')); var response = await request.close(); var body = await response.transform(UTF8.decoder).join(''); expect(body, 'answer$i'); @@ -92,14 +87,13 @@ Future handleHttp11Request(HttpRequest request, int i) async { } Future makeHttp2Request(MultiProtocolHttpServer server, - ClientTransportConnection connection, - int i) async { + ClientTransportConnection connection, int i) async { expect(connection.isOpen, true); var headers = [ - new Header.ascii(':method', 'GET'), - new Header.ascii(':scheme', 'https'), - new Header.ascii(':authority', 'localhost:${server.port}'), - new Header.ascii(':path', '/abc$i'), + new Header.ascii(':method', 'GET'), + new Header.ascii(':scheme', 'https'), + new Header.ascii(':authority', 'localhost:${server.port}'), + new Header.ascii(':path', '/abc$i'), ]; var stream = connection.makeRequest(headers, endStream: true); @@ -113,7 +107,7 @@ Future makeHttp2Request(MultiProtocolHttpServer server, expect(await si.moveNext(), true); expect(si.current is DataStreamMessage, true); expect(ASCII.decode(si.current.bytes), 'answer$i'); - + expect(await si.moveNext(), false); } @@ -142,4 +136,3 @@ Map getHeaders(HeadersStreamMessage headers) { } return map; } - diff --git a/pkgs/http2/test/server_test.dart b/pkgs/http2/test/server_test.dart index 1d9c5e161b..9c333e4a7c 100644 --- a/pkgs/http2/test/server_test.dart +++ b/pkgs/http2/test/server_test.dart @@ -19,10 +19,9 @@ main() { group('normal', () { serverTest('gracefull-shutdown-for-unused-connection', (ServerTransportConnection server, - FrameWriter clientWriter, - StreamIterator clientReader, - Future nextFrame()) async { - + FrameWriter clientWriter, + StreamIterator clientReader, + Future nextFrame()) async { Future serverFun() async { expect(await server.incomingStreams.toList(), isEmpty); await server.finish(); @@ -48,10 +47,9 @@ main() { group('client-errors', () { serverTest('no-settings-frame-at-beginning', (ServerTransportConnection server, - FrameWriter clientWriter, - StreamIterator clientReader, - Future nextFrame()) async { - + FrameWriter clientWriter, + StreamIterator clientReader, + Future nextFrame()) async { Future serverFun() async { // TODO: Do we want to get an error in this case? expect(await server.incomingStreams.toList(), isEmpty); @@ -78,9 +76,9 @@ main() { serverTest('data-frame-for-invalid-stream', (ServerTransportConnection server, - FrameWriter clientWriter, - StreamIterator clientReader, - Future nextFrame()) async { + FrameWriter clientWriter, + StreamIterator clientReader, + Future nextFrame()) async { Future serverFun() async { await server.incomingStreams.toList(); await server.finish(); @@ -113,10 +111,9 @@ main() { serverTest('data-frame-after-stream-closed', (ServerTransportConnection server, - FrameWriter clientWriter, - StreamIterator clientReader, - Future nextFrame()) async { - + FrameWriter clientWriter, + StreamIterator clientReader, + Future nextFrame()) async { Future serverFun() async { await server.incomingStreams.toList(); await server.finish(); @@ -128,8 +125,8 @@ main() { clientWriter.writeSettingsFrame([]); expect(await nextFrame() is SettingsFrame, true); - clientWriter.writeHeadersFrame( - 3, [new Header.ascii('a', 'b')], endStream: true); + clientWriter.writeHeadersFrame(3, [new Header.ascii('a', 'b')], + endStream: true); // Write data frame to non-existent stream (stream 3 was closed // above). @@ -153,12 +150,10 @@ main() { }); group('server-errors', () { - serverTest('server-resets-stream', - (ServerTransportConnection server, - FrameWriter clientWriter, - StreamIterator clientReader, - Future nextFrame()) async { - + serverTest('server-resets-stream', (ServerTransportConnection server, + FrameWriter clientWriter, + StreamIterator clientReader, + Future nextFrame()) async { Future serverFun() async { var it = new StreamIterator(server.incomingStreams); expect(await it.moveNext(), true); @@ -171,15 +166,14 @@ main() { 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, [new Header.ascii('a', 'b')], endStream: false); + clientWriter.writeHeadersFrame(1, [new Header.ascii('a', 'b')], + endStream: false); // Make sure the client gets a [RstStreamFrame] frame. var frame = await nextFrame(); @@ -200,8 +194,8 @@ main() { }); } -serverTest(String name, - func(serverConnection, frameWriter, frameReader, readNext)) { +serverTest( + String name, func(serverConnection, frameWriter, frameReader, readNext)) { return test(name, () { var streams = new ClientErrorStreams(); var clientReader = streams.clientConnectionFrameReader; @@ -211,10 +205,8 @@ serverTest(String name, return clientReader.current; } - return func(streams.serverConnection, - streams.clientConnectionFrameWriter, - clientReader, - readNext); + return func(streams.serverConnection, streams.clientConnectionFrameWriter, + clientReader, readNext); }); } @@ -237,6 +229,6 @@ class ClientErrorStreams { return new FrameWriter(encoder, writeB, peerSettings); } - ServerTransportConnection get serverConnection - => new ServerTransportConnection.viaStreams(readB, writeA); + ServerTransportConnection get serverConnection => + new ServerTransportConnection.viaStreams(readB, writeA); } diff --git a/pkgs/http2/test/src/error_matchers.dart b/pkgs/http2/test/src/error_matchers.dart index 365d34c5ef..56e87c02f1 100644 --- a/pkgs/http2/test/src/error_matchers.dart +++ b/pkgs/http2/test/src/error_matchers.dart @@ -14,7 +14,6 @@ class _ProtocolException extends TypeMatcher { bool matches(item, Map matchState) => item is ProtocolException; } - const Matcher isFrameSizeException = const _FrameSizeException(); class _FrameSizeException extends TypeMatcher { @@ -22,7 +21,6 @@ class _FrameSizeException extends TypeMatcher { bool matches(item, Map matchState) => item is FrameSizeException; } - const Matcher isTerminatedException = const _TerminatedException(); class _TerminatedException extends TypeMatcher { @@ -30,7 +28,6 @@ class _TerminatedException extends TypeMatcher { bool matches(item, Map matchState) => item is TerminatedException; } - const Matcher isFlowControlException = const _FlowControlException(); class _FlowControlException extends TypeMatcher { diff --git a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart index 2d1b1243f5..2ab88537c5 100644 --- a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart @@ -31,9 +31,8 @@ main() { // Send [HeadersMessage]. var c = new TestCounter(); - fw.mock_writeHeadersFrame = (int streamId, - List
sendingHeaders, - {bool endStream}) { + fw.mock_writeHeadersFrame = + (int streamId, List
sendingHeaders, {bool endStream}) { expect(streamId, 99); expect(sendingHeaders, headers); expect(endStream, false); @@ -52,9 +51,8 @@ main() { expect(difference, bytes.length); c.got(); }; - fw.mock_writeDataFrame = (int streamId, - List
sendingBytes, - {bool endStream}) { + fw.mock_writeDataFrame = + (int streamId, List
sendingBytes, {bool endStream}) { expect(streamId, 99); expect(sendingBytes, bytes); expect(endStream, false); @@ -73,8 +71,8 @@ main() { expect(difference, 1); c.got(); }; - fw.mock_writeDataFrame = (int streamId, List
sendingBytes, - {bool endStream}) { + fw.mock_writeDataFrame = + (int streamId, List
sendingBytes, {bool endStream}) { expect(streamId, 99); expect(sendingBytes, bytes.sublist(0, 1)); expect(endStream, false); @@ -90,8 +88,8 @@ main() { expect(difference, bytes.length - 1); c.got(); }; - fw.mock_writeDataFrame = (int streamId, List
sendingBytes, - {bool endStream}) { + fw.mock_writeDataFrame = + (int streamId, List
sendingBytes, {bool endStream}) { expect(streamId, 99); expect(sendingBytes, bytes.sublist(1)); expect(endStream, true); @@ -104,7 +102,7 @@ main() { queue.done.then(expectAsync1((_) { expect(queue.pendingMessages, 0); expect(() => queue.enqueueMessage(new DataMessage(99, bytes, true)), - throwsA(new isInstanceOf())); + throwsA(new isInstanceOf())); })); }); @@ -174,16 +172,15 @@ class MockFrameWriter extends SmartMock implements FrameWriter { } class MockStreamMessageQueueIn extends SmartMock - implements StreamMessageQueueIn { + implements StreamMessageQueueIn { BufferIndicator bufferIndicator = new BufferIndicator(); } class MockIncomingWindowHandler extends SmartMock - implements IncomingWindowHandler { } + implements IncomingWindowHandler {} class MockOutgoingWindowHandler extends SmartMock - implements OutgoingConnectionWindowHandler, - OutgoingStreamWindowHandler { + implements OutgoingConnectionWindowHandler, OutgoingStreamWindowHandler { BufferIndicator positiveWindow = new BufferIndicator(); int peerWindowSize = new Window().size; } diff --git a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart index b7c66dd7b3..e189c28f6c 100644 --- a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart @@ -126,8 +126,8 @@ main() { var windowMock = new MockIncomingWindowHandler(); var queue = new StreamMessageQueueIn(windowMock); - var sub = queue.messages.listen( - expectAsync1((_) {}, count: 0), onDone: expectAsync0(() {}, count: 0)); + var sub = queue.messages.listen(expectAsync1((_) {}, count: 0), + onDone: expectAsync0(() {}, count: 0)); sub.pause(); // We assert that we got the data, but it wasn't processed. @@ -146,16 +146,14 @@ main() { }); } - class MockConnectionMessageQueueOut extends SmartMock - implements ConnectionMessageQueueOut { } - + implements ConnectionMessageQueueOut {} class MockIncomingWindowHandler extends SmartMock - implements IncomingWindowHandler { } + implements IncomingWindowHandler {} class MockOutgoingStreamWindowHandler extends SmartMock - implements OutgoingStreamWindowHandler { + implements OutgoingStreamWindowHandler { final BufferIndicator positiveWindow = new BufferIndicator(); int peerWindowSize; } diff --git a/pkgs/http2/test/src/flowcontrol/window_handler_test.dart b/pkgs/http2/test/src/flowcontrol/window_handler_test.dart index 5c0b8f88df..771d525b22 100644 --- a/pkgs/http2/test/src/flowcontrol/window_handler_test.dart +++ b/pkgs/http2/test/src/flowcontrol/window_handler_test.dart @@ -14,11 +14,9 @@ import '../mock_utils.dart'; main() { group('flowcontrol', () { void testAbstractOutgoingWindowHandler( - AbstractOutgoingWindowHandler handler, - Window window, - int initialSize) { - var sub = handler.positiveWindow.bufferEmptyEvents.listen( - expectAsync1((_) {}, count: 0)); + AbstractOutgoingWindowHandler handler, Window window, int initialSize) { + var sub = handler.positiveWindow.bufferEmptyEvents + .listen(expectAsync1((_) {}, count: 0)); expect(handler.peerWindowSize, initialSize); expect(window.size, initialSize); @@ -56,7 +54,7 @@ main() { // the maximum size, we throw a [FlowControlException]. var frame = new WindowUpdateFrame(frameHeader, Window.MAX_WINDOW_SIZE); expect(() => handler.processWindowUpdate(frame), - throwsA(isFlowControlException)); + throwsA(isFlowControlException)); } test('outgoing-connection-window-handler', () { @@ -83,8 +81,8 @@ main() { handler = new OutgoingStreamWindowHandler(window); expect(handler.positiveWindow.wouldBuffer, isFalse); - final bufferEmpty = handler.positiveWindow.bufferEmptyEvents.listen( - expectAsync1((_) {}, count: 0)); + final bufferEmpty = handler.positiveWindow.bufferEmptyEvents + .listen(expectAsync1((_) {}, count: 0)); handler.processInitialWindowSizeSettingChange(-window.size); expect(handler.positiveWindow.wouldBuffer, isTrue); expect(handler.peerWindowSize, 0); @@ -95,8 +93,10 @@ main() { expect(handler.peerWindowSize, 1); expect(window.size, 1); - expect(() => handler.processInitialWindowSizeSettingChange( - Window.MAX_WINDOW_SIZE + 1), throwsA(isFlowControlException)); + expect( + () => handler.processInitialWindowSizeSettingChange( + Window.MAX_WINDOW_SIZE + 1), + throwsA(isFlowControlException)); }); test('incoming-window-handler', () { @@ -133,4 +133,4 @@ main() { }); } -class FrameWriterMock extends SmartMock implements FrameWriter { } +class FrameWriterMock extends SmartMock implements FrameWriter {} diff --git a/pkgs/http2/test/src/frames/frame_defragmenter_test.dart b/pkgs/http2/test/src/frames/frame_defragmenter_test.dart index c3eedb46bd..a51349b179 100644 --- a/pkgs/http2/test/src/frames/frame_defragmenter_test.dart +++ b/pkgs/http2/test/src/frames/frame_defragmenter_test.dart @@ -17,16 +17,15 @@ main() { } HeadersFrame headersFrame(List data, - {bool fragmented: false, int streamId: 1}) { + {bool fragmented: false, int streamId: 1}) { int flags = fragmented ? 0 : HeadersFrame.FLAG_END_HEADERS; - var header = new FrameHeader( - data.length, FrameType.HEADERS, flags, streamId); + var header = + new FrameHeader(data.length, FrameType.HEADERS, flags, streamId); return new HeadersFrame(header, 0, false, null, null, data); } PushPromiseFrame pushPromiseFrame(List data, - {bool fragmented: false, - int streamId: 1}) { + {bool fragmented: false, int streamId: 1}) { int flags = fragmented ? 0 : HeadersFrame.FLAG_END_HEADERS; var header = new FrameHeader( data.length, FrameType.PUSH_PROMISE, flags, streamId); @@ -34,8 +33,7 @@ main() { } ContinuationFrame continuationFrame(List data, - {bool fragmented: false, - int streamId: 1}) { + {bool fragmented: false, int streamId: 1}) { int flags = fragmented ? 0 : ContinuationFrame.FLAG_END_HEADERS; var header = new FrameHeader( data.length, FrameType.CONTINUATION, flags, streamId); @@ -87,8 +85,8 @@ main() { var f2 = continuationFrame([4, 5, 6], fragmented: true, streamId: 2); expect(defrag.tryDefragmentFrame(f1), isNull); - expect(() => defrag.tryDefragmentFrame(f2), - throwsA(isProtocolException)); + expect( + () => defrag.tryDefragmentFrame(f2), throwsA(isProtocolException)); }); test('fragmented-push-promise-frame', () { @@ -98,8 +96,8 @@ main() { var f2 = continuationFrame([4, 5, 6], fragmented: true, streamId: 2); expect(defrag.tryDefragmentFrame(f1), isNull); - expect(() => defrag.tryDefragmentFrame(f2), - throwsA(isProtocolException)); + expect( + () => defrag.tryDefragmentFrame(f2), throwsA(isProtocolException)); }); test('fragmented-headers-frame--no-continuation-frame', () { @@ -109,8 +107,8 @@ main() { var f2 = unknownFrame(); expect(defrag.tryDefragmentFrame(f1), isNull); - expect(() => defrag.tryDefragmentFrame(f2), - throwsA(isProtocolException)); + expect( + () => defrag.tryDefragmentFrame(f2), throwsA(isProtocolException)); }); test('fragmented-push-promise-no-continuation-frame', () { @@ -120,8 +118,8 @@ main() { var f2 = unknownFrame(); expect(defrag.tryDefragmentFrame(f1), isNull); - expect(() => defrag.tryDefragmentFrame(f2), - throwsA(isProtocolException)); + expect( + () => defrag.tryDefragmentFrame(f2), throwsA(isProtocolException)); }); test('push-without-headres-or-push-promise-frame', () { diff --git a/pkgs/http2/test/src/frames/frame_reader_test.dart b/pkgs/http2/test/src/frames/frame_reader_test.dart index 1b80241619..7bbd4027d7 100644 --- a/pkgs/http2/test/src/frames/frame_reader_test.dart +++ b/pkgs/http2/test/src/frames/frame_reader_test.dart @@ -25,34 +25,31 @@ main() { // - flags: [0] // - stream id: [0, 0, 0, 1] controller - ..add([0, (body.length >> 8) & 0xff, body.length & 0xff]) - ..add([0]) - ..add([0]) - ..add([0, 0, 0, 1]) - ..add(body) - ..close(); + ..add([0, (body.length >> 8) & 0xff, body.length & 0xff]) + ..add([0]) + ..add([0]) + ..add([0, 0, 0, 1]) + ..add(body) + ..close(); return reader.startDecoding(); } test('data-frame--max-frame-size', () { var body = new List.filled(maxFrameSize, 0x42); - dataFrame(body).listen( - expectAsync1((Frame frame) { - expect(frame is DataFrame, isTrue); - expect(frame.header.length, body.length); - expect(frame.header.flags, 0); - DataFrame dataFrame = frame; - expect(dataFrame.hasEndStreamFlag, isFalse); - expect(dataFrame.hasPaddedFlag, isFalse); - expect(dataFrame.bytes, body); - }), - onError: expectAsync2((error, stack) {}, count: 0)); + dataFrame(body).listen(expectAsync1((Frame frame) { + expect(frame is DataFrame, isTrue); + expect(frame.header.length, body.length); + expect(frame.header.flags, 0); + DataFrame dataFrame = frame; + expect(dataFrame.hasEndStreamFlag, isFalse); + expect(dataFrame.hasPaddedFlag, isFalse); + expect(dataFrame.bytes, body); + }), onError: expectAsync2((error, stack) {}, count: 0)); }); test('data-frame--max-frame-size-plus-1', () { var body = new List.filled(maxFrameSize + 1, 0x42); - dataFrame(body).listen( - expectAsync1((_) {}, count: 0), + dataFrame(body).listen(expectAsync1((_) {}, count: 0), onError: expectAsync2((error, stack) { expect('$error', contains('Incoming frame is too big')); })); @@ -64,10 +61,11 @@ main() { var controller = new StreamController>(); var reader = new FrameReader(controller.stream, settings); - controller..add([1])..close(); + controller + ..add([1]) + ..close(); - reader.startDecoding().listen( - expectAsync1((_) {}, count: 0), + reader.startDecoding().listen(expectAsync1((_) {}, count: 0), onError: expectAsync2((error, stack) { expect('$error', contains('incomplete frame')); })); @@ -84,10 +82,11 @@ main() { // - type: [0] // - flags: [0] // - stream id: [0, 0, 0, 1] - controller..add([0, 0, 255, 0, 0, 0, 0, 0, 1])..close(); + controller + ..add([0, 0, 255, 0, 0, 0, 0, 0, 1]) + ..close(); - reader.startDecoding().listen( - expectAsync1((_) {}, count: 0), + reader.startDecoding().listen(expectAsync1((_) {}, count: 0), onError: expectAsync2((error, stack) { expect('$error', contains('incomplete frame')); })); @@ -99,10 +98,11 @@ main() { var controller = new StreamController>(); var reader = new FrameReader(controller.stream, settings); - controller..addError('hello world')..close(); + controller + ..addError('hello world') + ..close(); - reader.startDecoding().listen( - expectAsync1((_) {}, count: 0), + reader.startDecoding().listen(expectAsync1((_) {}, count: 0), onError: expectAsync2((error, stack) { expect('$error', contains('hello world')); })); diff --git a/pkgs/http2/test/src/frames/frame_writer_reader_test.dart b/pkgs/http2/test/src/frames/frame_writer_reader_test.dart index b65004602f..65d64243b4 100644 --- a/pkgs/http2/test/src/frames/frame_writer_reader_test.dart +++ b/pkgs/http2/test/src/frames/frame_writer_reader_test.dart @@ -15,9 +15,8 @@ import '../hpack/hpack_test.dart' show isHeader; main() { group('frames', () { group('writer-reader', () { - writerReaderTest('data-frame', (FrameWriter writer, - FrameReader reader, - HPackDecoder decoder) async { + writerReaderTest('data-frame', + (FrameWriter writer, FrameReader reader, HPackDecoder decoder) async { writer.writeDataFrame(99, [1, 2, 3], endStream: true); var frames = await finishWriting(writer, reader); @@ -32,11 +31,10 @@ main() { expect(dataFrame.bytes, [1, 2, 3]); }); - writerReaderTest('headers-frame', (FrameWriter writer, - FrameReader reader, - HPackDecoder decoder) async { - writer.writeHeadersFrame( - 99, [new Header.ascii('a', 'b')], endStream: true); + writerReaderTest('headers-frame', + (FrameWriter writer, FrameReader reader, HPackDecoder decoder) async { + writer.writeHeadersFrame(99, [new Header.ascii('a', 'b')], + endStream: true); var frames = await finishWriting(writer, reader); expect(frames.length, 1); @@ -58,9 +56,8 @@ main() { expect(headers[0], isHeader('a', 'b')); }); - writerReaderTest('priority-frame', (FrameWriter writer, - FrameReader reader, - HPackDecoder decoder) async { + writerReaderTest('priority-frame', + (FrameWriter writer, FrameReader reader, HPackDecoder decoder) async { writer.writePriorityFrame(99, 44, 33, exclusive: true); var frames = await finishWriting(writer, reader); @@ -74,9 +71,8 @@ main() { expect(priorityFrame.weight, 33); }); - writerReaderTest('rst-frame', (FrameWriter writer, - FrameReader reader, - HPackDecoder decoder) async { + writerReaderTest('rst-frame', + (FrameWriter writer, FrameReader reader, HPackDecoder decoder) async { writer.writeRstStreamFrame(99, 42); var frames = await finishWriting(writer, reader); @@ -88,11 +84,10 @@ main() { expect(rstFrame.errorCode, 42); }); - writerReaderTest('settings-frame', (FrameWriter writer, - FrameReader reader, - HPackDecoder decoder) async { - writer.writeSettingsFrame( - [new Setting(Setting.SETTINGS_ENABLE_PUSH, 1)]); + writerReaderTest('settings-frame', + (FrameWriter writer, FrameReader reader, HPackDecoder decoder) async { + writer + .writeSettingsFrame([new Setting(Setting.SETTINGS_ENABLE_PUSH, 1)]); var frames = await finishWriting(writer, reader); expect(frames.length, 1); @@ -102,14 +97,13 @@ main() { expect(settingsFrame.hasAckFlag, false); expect(settingsFrame.header.streamId, 0); expect(settingsFrame.settings.length, 1); - expect(settingsFrame.settings[0].identifier, - Setting.SETTINGS_ENABLE_PUSH); + expect( + settingsFrame.settings[0].identifier, Setting.SETTINGS_ENABLE_PUSH); expect(settingsFrame.settings[0].value, 1); }); - writerReaderTest('settings-frame-ack', (FrameWriter writer, - FrameReader reader, - HPackDecoder decoder) async { + writerReaderTest('settings-frame-ack', + (FrameWriter writer, FrameReader reader, HPackDecoder decoder) async { writer.writeSettingsAckFrame(); var frames = await finishWriting(writer, reader); @@ -122,9 +116,8 @@ main() { expect(settingsFrame.settings.length, 0); }); - writerReaderTest('push-promise-frame', (FrameWriter writer, - FrameReader reader, - HPackDecoder decoder) async { + writerReaderTest('push-promise-frame', + (FrameWriter writer, FrameReader reader, HPackDecoder decoder) async { writer.writePushPromiseFrame(99, 44, [new Header.ascii('a', 'b')]); var frames = await finishWriting(writer, reader); @@ -143,9 +136,8 @@ main() { expect(headers[0], isHeader('a', 'b')); }); - writerReaderTest('ping-frame', (FrameWriter writer, - FrameReader reader, - HPackDecoder decoder) async { + writerReaderTest('ping-frame', + (FrameWriter writer, FrameReader reader, HPackDecoder decoder) async { writer.writePingFrame(44, ack: true); var frames = await finishWriting(writer, reader); @@ -158,9 +150,8 @@ main() { expect(pingFrame.hasAckFlag, isTrue); }); - writerReaderTest('goaway-frame', (FrameWriter writer, - FrameReader reader, - HPackDecoder decoder) async { + writerReaderTest('goaway-frame', + (FrameWriter writer, FrameReader reader, HPackDecoder decoder) async { writer.writeGoawayFrame(44, 33, [1, 2, 3]); var frames = await finishWriting(writer, reader); @@ -174,9 +165,8 @@ main() { expect(goawayFrame.debugData, [1, 2, 3]); }); - writerReaderTest('window-update-frame', (FrameWriter writer, - FrameReader reader, - HPackDecoder decoder) async { + writerReaderTest('window-update-frame', + (FrameWriter writer, FrameReader reader, HPackDecoder decoder) async { writer.writeWindowUpdate(55, streamId: 99); var frames = await finishWriting(writer, reader); @@ -188,9 +178,8 @@ main() { expect(windowUpdateFrame.windowSizeIncrement, 55); }); - writerReaderTest('frag-headers-frame', (FrameWriter writer, - FrameReader reader, - HPackDecoder decoder) async { + writerReaderTest('frag-headers-frame', + (FrameWriter writer, FrameReader reader, HPackDecoder decoder) async { var headerName = [1]; var headerValue = new List.filled(1 << 14, 0x42); var header = new Header(headerName, headerValue); @@ -215,8 +204,8 @@ main() { expect(contFrame.hasEndHeadersFlag, isTrue); var headerBlock = [] - ..addAll(headersFrame.headerBlockFragment) - ..addAll(contFrame.headerBlockFragment); + ..addAll(headersFrame.headerBlockFragment) + ..addAll(contFrame.headerBlockFragment); var headers = decoder.decode(headerBlock); expect(headers.length, 1); @@ -239,6 +228,6 @@ writerReaderTest(String name, func(writer, reader, decoder)) { } Future> finishWriting(FrameWriter writer, FrameReader reader) { - return Future.wait([writer.close(), reader.startDecoding().toList()]) - .then((results) => results.last); + return Future.wait([writer.close(), reader.startDecoding().toList()]).then( + (results) => results.last); } diff --git a/pkgs/http2/test/src/hpack/hpack_test.dart b/pkgs/http2/test/src/hpack/hpack_test.dart index 02e8d02a1c..495e519cb5 100644 --- a/pkgs/http2/test/src/hpack/hpack_test.dart +++ b/pkgs/http2/test/src/hpack/hpack_test.dart @@ -14,8 +14,27 @@ main() { // First request headers = context.decoder.decode([ - 0x82, 0x86, 0x84, 0x41, 0x0f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x78, - 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d]); + 0x82, + 0x86, + 0x84, + 0x41, + 0x0f, + 0x77, + 0x77, + 0x77, + 0x2e, + 0x65, + 0x78, + 0x61, + 0x6d, + 0x70, + 0x6c, + 0x65, + 0x2e, + 0x63, + 0x6f, + 0x6d + ]); expect(headers, hasLength(4)); expect(headers[0], isHeader(':method', 'GET')); expect(headers[1], isHeader(':scheme', 'http')); @@ -24,8 +43,21 @@ main() { // Second request headers = context.decoder.decode([ - 0x82, 0x86, 0x84, 0xbe, 0x58, 0x08, 0x6e, 0x6f, 0x2d, 0x63, 0x61, - 0x63, 0x68, 0x65]); + 0x82, + 0x86, + 0x84, + 0xbe, + 0x58, + 0x08, + 0x6e, + 0x6f, + 0x2d, + 0x63, + 0x61, + 0x63, + 0x68, + 0x65 + ]); expect(headers, hasLength(5)); expect(headers[0], isHeader(':method', 'GET')); expect(headers[1], isHeader(':scheme', 'http')); @@ -35,9 +67,36 @@ main() { // Third request headers = context.decoder.decode([ - 0x82, 0x87, 0x85, 0xbf, 0x40, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, - 0x6d, 0x2d, 0x6b, 0x65, 0x79, 0x0c, 0x63, 0x75, 0x73, 0x74, 0x6f, - 0x6d, 0x2d, 0x76, 0x61, 0x6c, 0x75, 0x65]); + 0x82, + 0x87, + 0x85, + 0xbf, + 0x40, + 0x0a, + 0x63, + 0x75, + 0x73, + 0x74, + 0x6f, + 0x6d, + 0x2d, + 0x6b, + 0x65, + 0x79, + 0x0c, + 0x63, + 0x75, + 0x73, + 0x74, + 0x6f, + 0x6d, + 0x2d, + 0x76, + 0x61, + 0x6c, + 0x75, + 0x65 + ]); expect(headers, hasLength(5)); expect(headers[0], isHeader(':method', 'GET')); expect(headers[1], isHeader(':scheme', 'https')); @@ -52,8 +111,24 @@ main() { // First request headers = context.decoder.decode([ - 0x82, 0x86, 0x84, 0x41, 0x8c, 0xf1, 0xe3, 0xc2, 0xe5, 0xf2, 0x3a, - 0x6b, 0xa0, 0xab, 0x90, 0xf4, 0xff]); + 0x82, + 0x86, + 0x84, + 0x41, + 0x8c, + 0xf1, + 0xe3, + 0xc2, + 0xe5, + 0xf2, + 0x3a, + 0x6b, + 0xa0, + 0xab, + 0x90, + 0xf4, + 0xff + ]); expect(headers, hasLength(4)); expect(headers[0], isHeader(':method', 'GET')); expect(headers[1], isHeader(':scheme', 'http')); @@ -62,8 +137,19 @@ main() { // Second request headers = context.decoder.decode([ - 0x82, 0x86, 0x84, 0xbe, 0x58, 0x86, 0xa8, 0xeb, 0x10, 0x64, 0x9c, - 0xbf]); + 0x82, + 0x86, + 0x84, + 0xbe, + 0x58, + 0x86, + 0xa8, + 0xeb, + 0x10, + 0x64, + 0x9c, + 0xbf + ]); expect(headers, hasLength(5)); expect(headers[0], isHeader(':method', 'GET')); expect(headers[1], isHeader(':scheme', 'http')); @@ -73,9 +159,31 @@ main() { // Third request headers = context.decoder.decode([ - 0x82, 0x87, 0x85, 0xbf, 0x40, 0x88, 0x25, 0xa8, 0x49, 0xe9, 0x5b, - 0xa9, 0x7d, 0x7f, 0x89, 0x25, 0xa8, 0x49, 0xe9, 0x5b, 0xb8, 0xe8, - 0xb4, 0xbf]); + 0x82, + 0x87, + 0x85, + 0xbf, + 0x40, + 0x88, + 0x25, + 0xa8, + 0x49, + 0xe9, + 0x5b, + 0xa9, + 0x7d, + 0x7f, + 0x89, + 0x25, + 0xa8, + 0x49, + 0xe9, + 0x5b, + 0xb8, + 0xe8, + 0xb4, + 0xbf + ]); expect(headers, hasLength(5)); expect(headers[0], isHeader(':method', 'GET')); expect(headers[1], isHeader(':scheme', 'https')); @@ -90,13 +198,77 @@ main() { // First response headers = context.decoder.decode([ - 0x48, 0x03, 0x33, 0x30, 0x32, 0x58, 0x07, 0x70, 0x72, 0x69, 0x76, - 0x61, 0x74, 0x65, 0x61, 0x1d, 0x4d, 0x6f, 0x6e, 0x2c, 0x20, 0x32, - 0x31, 0x20, 0x4f, 0x63, 0x74, 0x20, 0x32, 0x30, 0x31, 0x33, 0x20, - 0x32, 0x30, 0x3a, 0x31, 0x33, 0x3a, 0x32, 0x31, 0x20, 0x47, 0x4d, - 0x54, 0x6e, 0x17, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, - 0x77, 0x77, 0x77, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, - 0x2e, 0x63, 0x6f, 0x6d]); + 0x48, + 0x03, + 0x33, + 0x30, + 0x32, + 0x58, + 0x07, + 0x70, + 0x72, + 0x69, + 0x76, + 0x61, + 0x74, + 0x65, + 0x61, + 0x1d, + 0x4d, + 0x6f, + 0x6e, + 0x2c, + 0x20, + 0x32, + 0x31, + 0x20, + 0x4f, + 0x63, + 0x74, + 0x20, + 0x32, + 0x30, + 0x31, + 0x33, + 0x20, + 0x32, + 0x30, + 0x3a, + 0x31, + 0x33, + 0x3a, + 0x32, + 0x31, + 0x20, + 0x47, + 0x4d, + 0x54, + 0x6e, + 0x17, + 0x68, + 0x74, + 0x74, + 0x70, + 0x73, + 0x3a, + 0x2f, + 0x2f, + 0x77, + 0x77, + 0x77, + 0x2e, + 0x65, + 0x78, + 0x61, + 0x6d, + 0x70, + 0x6c, + 0x65, + 0x2e, + 0x63, + 0x6f, + 0x6d + ]); expect(headers, hasLength(4)); expect(headers[0], isHeader(':status', '302')); expect(headers[1], isHeader('cache-control', 'private')); @@ -104,8 +276,8 @@ main() { expect(headers[3], isHeader('location', 'https://www.example.com')); // Second response - headers = context.decoder.decode([ - 0x48, 0x03, 0x33, 0x30, 0x37, 0xc1, 0xc0, 0xbf]); + headers = context.decoder + .decode([0x48, 0x03, 0x33, 0x30, 0x37, 0xc1, 0xc0, 0xbf]); expect(headers, hasLength(4)); expect(headers[0], isHeader(':status', '307')); expect(headers[1], isHeader('cache-control', 'private')); @@ -114,23 +286,115 @@ main() { // Third response headers = context.decoder.decode([ - 0x88, 0xc1, 0x61, 0x1d, 0x4d, 0x6f, 0x6e, 0x2c, 0x20, 0x32, 0x31, - 0x20, 0x4f, 0x63, 0x74, 0x20, 0x32, 0x30, 0x31, 0x33, 0x20, 0x32, - 0x30, 0x3a, 0x31, 0x33, 0x3a, 0x32, 0x32, 0x20, 0x47, 0x4d, 0x54, - 0xc0, 0x5a, 0x04, 0x67, 0x7a, 0x69, 0x70, 0x77, 0x38, 0x66, 0x6f, - 0x6f, 0x3d, 0x41, 0x53, 0x44, 0x4a, 0x4b, 0x48, 0x51, 0x4b, 0x42, - 0x5a, 0x58, 0x4f, 0x51, 0x57, 0x45, 0x4f, 0x50, 0x49, 0x55, 0x41, - 0x58, 0x51, 0x57, 0x45, 0x4f, 0x49, 0x55, 0x3b, 0x20, 0x6d, 0x61, - 0x78, 0x2d, 0x61, 0x67, 0x65, 0x3d, 0x33, 0x36, 0x30, 0x30, 0x3b, - 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31]); + 0x88, + 0xc1, + 0x61, + 0x1d, + 0x4d, + 0x6f, + 0x6e, + 0x2c, + 0x20, + 0x32, + 0x31, + 0x20, + 0x4f, + 0x63, + 0x74, + 0x20, + 0x32, + 0x30, + 0x31, + 0x33, + 0x20, + 0x32, + 0x30, + 0x3a, + 0x31, + 0x33, + 0x3a, + 0x32, + 0x32, + 0x20, + 0x47, + 0x4d, + 0x54, + 0xc0, + 0x5a, + 0x04, + 0x67, + 0x7a, + 0x69, + 0x70, + 0x77, + 0x38, + 0x66, + 0x6f, + 0x6f, + 0x3d, + 0x41, + 0x53, + 0x44, + 0x4a, + 0x4b, + 0x48, + 0x51, + 0x4b, + 0x42, + 0x5a, + 0x58, + 0x4f, + 0x51, + 0x57, + 0x45, + 0x4f, + 0x50, + 0x49, + 0x55, + 0x41, + 0x58, + 0x51, + 0x57, + 0x45, + 0x4f, + 0x49, + 0x55, + 0x3b, + 0x20, + 0x6d, + 0x61, + 0x78, + 0x2d, + 0x61, + 0x67, + 0x65, + 0x3d, + 0x33, + 0x36, + 0x30, + 0x30, + 0x3b, + 0x20, + 0x76, + 0x65, + 0x72, + 0x73, + 0x69, + 0x6f, + 0x6e, + 0x3d, + 0x31 + ]); expect(headers, hasLength(6)); expect(headers[0], isHeader(':status', '200')); expect(headers[1], isHeader('cache-control', 'private')); expect(headers[2], isHeader('date', 'Mon, 21 Oct 2013 20:13:22 GMT')); expect(headers[3], isHeader('location', 'https://www.example.com')); expect(headers[4], isHeader('content-encoding', 'gzip')); - expect(headers[5], isHeader('set-cookie', - 'foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1')); + expect( + headers[5], + isHeader('set-cookie', + 'foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1')); }); test('C.6 response with huffman encoding', () { @@ -139,11 +403,61 @@ main() { // First response headers = context.decoder.decode([ - 0x48, 0x82, 0x64, 0x02, 0x58, 0x85, 0xae, 0xc3, 0x77, 0x1a, 0x4b, - 0x61, 0x96, 0xd0, 0x7a, 0xbe, 0x94, 0x10, 0x54, 0xd4, 0x44, 0xa8, - 0x20, 0x05, 0x95, 0x04, 0x0b, 0x81, 0x66, 0xe0, 0x82, 0xa6, 0x2d, - 0x1b, 0xff, 0x6e, 0x91, 0x9d, 0x29, 0xad, 0x17, 0x18, 0x63, 0xc7, - 0x8f, 0x0b, 0x97, 0xc8, 0xe9, 0xae, 0x82, 0xae, 0x43, 0xd3]); + 0x48, + 0x82, + 0x64, + 0x02, + 0x58, + 0x85, + 0xae, + 0xc3, + 0x77, + 0x1a, + 0x4b, + 0x61, + 0x96, + 0xd0, + 0x7a, + 0xbe, + 0x94, + 0x10, + 0x54, + 0xd4, + 0x44, + 0xa8, + 0x20, + 0x05, + 0x95, + 0x04, + 0x0b, + 0x81, + 0x66, + 0xe0, + 0x82, + 0xa6, + 0x2d, + 0x1b, + 0xff, + 0x6e, + 0x91, + 0x9d, + 0x29, + 0xad, + 0x17, + 0x18, + 0x63, + 0xc7, + 0x8f, + 0x0b, + 0x97, + 0xc8, + 0xe9, + 0xae, + 0x82, + 0xae, + 0x43, + 0xd3 + ]); expect(headers, hasLength(4)); expect(headers[0], isHeader(':status', '302')); expect(headers[1], isHeader('cache-control', 'private')); @@ -151,8 +465,8 @@ main() { expect(headers[3], isHeader('location', 'https://www.example.com')); // Second response - headers = context.decoder.decode([ - 0x48, 0x83, 0x64, 0x0e, 0xff, 0xc1, 0xc0, 0xbf]); + headers = context.decoder + .decode([0x48, 0x83, 0x64, 0x0e, 0xff, 0xc1, 0xc0, 0xbf]); expect(headers, hasLength(4)); expect(headers[0], isHeader(':status', '307')); expect(headers[1], isHeader('cache-control', 'private')); @@ -161,22 +475,96 @@ main() { // Third response headers = context.decoder.decode([ - 0x88, 0xc1, 0x61, 0x96, 0xd0, 0x7a, 0xbe, 0x94, 0x10, 0x54, 0xd4, - 0x44, 0xa8, 0x20, 0x05, 0x95, 0x04, 0x0b, 0x81, 0x66, 0xe0, 0x84, - 0xa6, 0x2d, 0x1b, 0xff, 0xc0, 0x5a, 0x83, 0x9b, 0xd9, 0xab, 0x77, - 0xad, 0x94, 0xe7, 0x82, 0x1d, 0xd7, 0xf2, 0xe6, 0xc7, 0xb3, 0x35, - 0xdf, 0xdf, 0xcd, 0x5b, 0x39, 0x60, 0xd5, 0xaf, 0x27, 0x08, 0x7f, - 0x36, 0x72, 0xc1, 0xab, 0x27, 0x0f, 0xb5, 0x29, 0x1f, 0x95, 0x87, - 0x31, 0x60, 0x65, 0xc0, 0x03, 0xed, 0x4e, 0xe5, 0xb1, 0x06, 0x3d, - 0x50, 0x07]); + 0x88, + 0xc1, + 0x61, + 0x96, + 0xd0, + 0x7a, + 0xbe, + 0x94, + 0x10, + 0x54, + 0xd4, + 0x44, + 0xa8, + 0x20, + 0x05, + 0x95, + 0x04, + 0x0b, + 0x81, + 0x66, + 0xe0, + 0x84, + 0xa6, + 0x2d, + 0x1b, + 0xff, + 0xc0, + 0x5a, + 0x83, + 0x9b, + 0xd9, + 0xab, + 0x77, + 0xad, + 0x94, + 0xe7, + 0x82, + 0x1d, + 0xd7, + 0xf2, + 0xe6, + 0xc7, + 0xb3, + 0x35, + 0xdf, + 0xdf, + 0xcd, + 0x5b, + 0x39, + 0x60, + 0xd5, + 0xaf, + 0x27, + 0x08, + 0x7f, + 0x36, + 0x72, + 0xc1, + 0xab, + 0x27, + 0x0f, + 0xb5, + 0x29, + 0x1f, + 0x95, + 0x87, + 0x31, + 0x60, + 0x65, + 0xc0, + 0x03, + 0xed, + 0x4e, + 0xe5, + 0xb1, + 0x06, + 0x3d, + 0x50, + 0x07 + ]); expect(headers, hasLength(6)); expect(headers[0], isHeader(':status', '200')); expect(headers[1], isHeader('cache-control', 'private')); expect(headers[2], isHeader('date', 'Mon, 21 Oct 2013 20:13:22 GMT')); expect(headers[3], isHeader('location', 'https://www.example.com')); expect(headers[4], isHeader('content-encoding', 'gzip')); - expect(headers[5], isHeader('set-cookie', - 'foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1')); + expect( + headers[5], + isHeader('set-cookie', + 'foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1')); }); }); @@ -184,19 +572,19 @@ main() { test('invalid-integer-encoding', () { var context = new HPackContext(); expect(() => context.decoder.decode([1 << 6, 0xff]), - throwsA(isHPackDecodingException)); + throwsA(isHPackDecodingException)); }); test('index-out-of-table-size', () { var context = new HPackContext(); expect(() => context.decoder.decode([0x7f]), - throwsA(isHPackDecodingException)); + throwsA(isHPackDecodingException)); }); test('invalid-update-dynamic-table-size', () { var context = new HPackContext(); expect(() => context.decoder.decode([0x3f]), - throwsA(isHPackDecodingException)); + throwsA(isHPackDecodingException)); }); test('update-dynamic-table-size-too-high', () { @@ -204,7 +592,7 @@ main() { // Tries to set dynamic table to 4097 (max is 4096 by default) var bytes = TestHelper.newInteger(0x20, 5, 4097); expect(() => context.decoder.decode(bytes), - throwsA(isHPackDecodingException)); + throwsA(isHPackDecodingException)); }); }); @@ -221,8 +609,8 @@ main() { test('update-dynamic-table-size-too-high', () { var context = new HPackContext(); // Sets dynamic table to 4096 - expect(context.decoder.decode(TestHelper.newInteger(0x20, 5, 4096)), - []); + expect( + context.decoder.decode(TestHelper.newInteger(0x20, 5, 4096)), []); }); test('dynamic table entry', () { @@ -261,8 +649,8 @@ main() { // We're reducing now the size by 1 byte, which should evict the last // entry. - headers = context.decoder.decode( - TestHelper.setDynamicTableSize(4096 - 1)); + headers = + context.decoder.decode(TestHelper.setDynamicTableSize(4096 - 1)); expect(headers, hasLength(0)); headers = context.decoder.decode(TestHelper.dynamicTableLookup(0)); @@ -275,7 +663,7 @@ main() { // Since we reduce the size by 1 byte, the last entry must be gone now. expect(() => context.decoder.decode(TestHelper.dynamicTableLookup(2)), - throwsA(isHPackDecodingException)); + throwsA(isHPackDecodingException)); }); }); @@ -284,72 +672,70 @@ main() { var context = new HPackContext(); var headers = [new Header.ascii('key', 'value')]; expect(context.encoder.encode(headers), - [0x00, 0x03, 0x6b, 0x65, 0x79, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65]); + [0x00, 0x03, 0x6b, 0x65, 0x79, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65]); }); test('simple-encoding-long-value', () { var context = new HPackContext(); - var headers = [new Header([0x42], new List.filled(300, 0x84))]; + var headers = [ + new Header([0x42], new List.filled(300, 0x84)) + ]; expect(context.decoder.decode(context.encoder.encode(headers)).first, - equalsHeader(headers.first)); - - expect(context.encoder.encode(headers), - [ - // Literal Header Field with Incremental Indexing - Indexed Name - 0x00, - - // Key: Length - 0x01, - - // Key: Bytes - 0x42, - - // Value: (first 7 bits + rest) - 0x7f, 0xad, 0x01, - - // Value: Bytes - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - - - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - ]); + equalsHeader(headers.first)); + + expect(context.encoder.encode(headers), [ + // Literal Header Field with Incremental Indexing - Indexed Name + 0x00, + + // Key: Length + 0x01, + + // Key: Bytes + 0x42, + + // Value: (first 7 bits + rest) + 0x7f, 0xad, 0x01, + + // Value: Bytes + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + ]); }); }); }); } - class TestHelper { static List setDynamicTableSize(int newSize) { return TestHelper.newInteger(0x20, 5, newSize); @@ -411,7 +797,6 @@ class TestHelper { } } - /// A matcher for HuffmannDecodingExceptions. const Matcher isHPackDecodingException = const _HPackDecodingException(); @@ -428,8 +813,7 @@ class _HeaderMatcher extends Matcher { Description describe(Description description) => description.add('Header'); bool matches(item, Map matchState) { - return - item is Header && + return item is Header && _compareLists(item.name, header.name) && _compareLists(item.value, header.value); } @@ -445,7 +829,7 @@ class _HeaderMatcher extends Matcher { } } -Matcher isHeader(String name, String value) - => new _HeaderMatcher(new Header.ascii(name, value)); +Matcher isHeader(String name, String value) => + new _HeaderMatcher(new Header.ascii(name, value)); Matcher equalsHeader(Header header) => new _HeaderMatcher(header); diff --git a/pkgs/http2/test/src/hpack/huffman_table_test.dart b/pkgs/http2/test/src/hpack/huffman_table_test.dart index a700130c3e..588b9f6708 100644 --- a/pkgs/http2/test/src/hpack/huffman_table_test.dart +++ b/pkgs/http2/test/src/hpack/huffman_table_test.dart @@ -15,12 +15,23 @@ main() { final encode = http2HuffmanCodec.encode; Map> hpackSpecTestCases = { - 'www.example.com' : [0xf1, 0xe3, 0xc2, 0xe5, 0xf2, 0x3a, 0x6b, 0xa0, - 0xab, 0x90, 0xf4, 0xff], - 'no-cache' : [0xa8, 0xeb, 0x10, 0x64, 0x9c, 0xbf], - 'custom-key' : [0x25, 0xa8, 0x49, 0xe9, 0x5b, 0xa9, 0x7d, 0x7f], - 'custom-value' : [0x25, 0xa8, 0x49, 0xe9, 0x5b, 0xb8, 0xe8, 0xb4, - 0xbf], + 'www.example.com': [ + 0xf1, + 0xe3, + 0xc2, + 0xe5, + 0xf2, + 0x3a, + 0x6b, + 0xa0, + 0xab, + 0x90, + 0xf4, + 0xff + ], + 'no-cache': [0xa8, 0xeb, 0x10, 0x64, 0x9c, 0xbf], + 'custom-key': [0x25, 0xa8, 0x49, 0xe9, 0x5b, 0xa9, 0x7d, 0x7f], + 'custom-value': [0x25, 0xa8, 0x49, 0xe9, 0x5b, 0xb8, 0xe8, 0xb4, 0xbf], }; test('hpack-spec-testcases', () { @@ -32,20 +43,20 @@ main() { test('more-than-7-bit-padding', () { var data = [ - // Just more-than-7-bitpadding - [0xff], - [0xff, 0xff], - [0xff, 0xff, 0xff], - [0xff, 0xff, 0xff, 0xff], - - // 0xf8 = '&' + more-than-7-bitpadding - [0xf8, 0xff], - [0xf8, 0xff, 0xff], - [0xf8, 0xff, 0xff, 0xff], - [0xf8, 0xff, 0xff, 0xff, 0xff], - - // ')' + entire EOS - [0xfe, 0xff, 0xff, 0xff, 0xff], + // Just more-than-7-bitpadding + [0xff], + [0xff, 0xff], + [0xff, 0xff, 0xff], + [0xff, 0xff, 0xff, 0xff], + + // 0xf8 = '&' + more-than-7-bitpadding + [0xf8, 0xff], + [0xf8, 0xff, 0xff], + [0xf8, 0xff, 0xff, 0xff], + [0xf8, 0xff, 0xff, 0xff, 0xff], + + // ')' + entire EOS + [0xfe, 0xff, 0xff, 0xff, 0xff], ]; for (var test in data) { @@ -69,70 +80,70 @@ main() { test('fuzzy-test', () { var data = [ - [0xb8, 0xa4, 0x4e, 0xe3, 0xb1, 0x4d, 0x3d, 0x63, 0x16, 0x5b, 0x6a], - [0x71, 0x5f, 0xb3, 0xb1, 0x4b, 0x94, 0xe8, 0x2f, 0x4c, 0x3d, 0x04], - [0x95, 0x6d, 0x89, 0xfb, 0x91, 0x6a, 0x6c, 0x52, 0x64, 0x9a, 0xd1], - [0x64, 0x59, 0x79, 0x38, 0xd2, 0x09, 0xea, 0x94, 0x92, 0xda, 0x24], - [0xb0, 0x35, 0xfe, 0xa9, 0x96, 0xb5, 0xe1, 0xde, 0x0a, 0x82, 0x18], - [0x39, 0xe5, 0xdd, 0xba, 0x50, 0xd4, 0x33, 0xa7, 0xb9, 0x63, 0x21], - [0x26, 0x52, 0x7a, 0xaa, 0x52, 0x4d, 0x27, 0x81, 0xe4, 0xef, 0xcd], - [0x17, 0x9e, 0x09, 0xcc, 0xd0, 0x0f, 0x5e, 0x03, 0x45, 0xc9, 0xba], - [0x84, 0xfc, 0x75, 0xeb, 0xcc, 0x9e, 0xb6, 0x50, 0x3f, 0xf8, 0x00], - [0xb9, 0x24, 0x95, 0x13, 0x6d, 0x89, 0xb2, 0x89, 0x86, 0x02, 0xca], - [0xb7, 0xd5, 0x78, 0xfa, 0xa3, 0xa9, 0x90, 0x1b, 0x35, 0xb4, 0x72], - [0x62, 0x9a, 0x31, 0x0c, 0x32, 0x1c, 0x25, 0x2e, 0x1b, 0x56, 0x55], - [0xa9, 0x5d, 0xa8, 0xa4, 0xed, 0x91, 0xeb, 0xba, 0xa0, 0xf9, 0x82], - [0x59, 0x9c, 0xc3, 0x6f, 0x66, 0xec, 0x65, 0xe0, 0x95, 0x6e, 0x34], - [0x3d, 0xc7, 0x0d, 0x6c, 0x01, 0x7d, 0xf2, 0x03, 0x9b, 0xe3, 0xc1], - [0x1d, 0xc6, 0xa4, 0xd1, 0x59, 0x52, 0xce, 0x42, 0x3d, 0xf6, 0xe5], - [0x2d, 0xbd, 0xb6, 0x5c, 0xfb, 0x52, 0x65, 0x2e, 0x7f, 0x03, 0x61], - [0x22, 0x24, 0x50, 0x48, 0x65, 0x5a, 0xe0, 0x0d, 0xf9, 0x78, 0x8d], - [0x72, 0xeb, 0x1d, 0x31, 0xb7, 0xe3, 0xa8, 0x15, 0x1f, 0xf1, 0x43], - [0x45, 0xa4, 0x40, 0x5a, 0x9c, 0x98, 0xa8, 0x6e, 0xac, 0xba, 0x83], - [0x27, 0x55, 0x33, 0xa7, 0x79, 0x08, 0x29, 0x42, 0x6d, 0x89, 0xfc], - [0x3b, 0x65, 0x21, 0x7a, 0x24, 0x58, 0x58, 0x6a, 0x97, 0x6e, 0x7c], - [0x56, 0x41, 0xff, 0x08, 0xaf, 0x9d, 0x33, 0x12, 0xcd, 0xb5, 0x99], - [0x35, 0x48, 0x38, 0x46, 0x3f, 0xee, 0x15, 0x16, 0x8d, 0xf5, 0x16], - [0xcc, 0xc0, 0x1b, 0x1e, 0xf1, 0xae, 0xf7, 0x40, 0xca, 0xc7, 0x9d], - [0x93, 0xae, 0x93, 0xcf, 0x97, 0xdf, 0xba, 0xd6, 0xb2, 0xac, 0x2f], - [0x45, 0xe4, 0x5b, 0x73, 0x54, 0x4c, 0x6c, 0x95, 0xa9, 0xab, 0x7f], - [0x71, 0xac, 0xbf, 0xdf, 0xa4, 0x29, 0xe3, 0x17, 0x3f, 0x24, 0x2f], - [0x5e, 0xc0, 0xf2, 0xbf, 0x5d, 0xc0, 0x31, 0x2d, 0x97, 0x24, 0x1d], - [0x6d, 0x0b, 0x7c, 0x15, 0x68, 0x7c, 0xe1, 0x15, 0xbf, 0x4f, 0x85], - [0x0a, 0x59, 0xf2, 0x3e, 0x48, 0x1d, 0xac, 0xc8, 0x22, 0xb0, 0x37], - [0x3a, 0xe2, 0x9e, 0xec, 0xf9, 0x1e, 0x88, 0xfa, 0xbe, 0x00, 0xee], - [0xc7, 0x5a, 0x1f, 0xc8, 0x48, 0x23, 0x3b, 0x1a, 0x0f, 0xf3, 0x7c], - [0x43, 0x0d, 0x10, 0x03, 0xb2, 0xc6, 0xbd, 0xed, 0x03, 0x19, 0x49], - [0xc9, 0xc4, 0x0e, 0xf3, 0xc6, 0xf4, 0xc1, 0x71, 0xee, 0x96, 0xeb], - [0x18, 0x51, 0x07, 0x36, 0x1a, 0x13, 0x83, 0x69, 0x2b, 0x1b, 0x09], - [0xac, 0x23, 0xb7, 0x47, 0x2d, 0xeb, 0x39, 0xdc, 0x3e, 0xdb, 0x74], - [0x44, 0x60, 0x06, 0x28, 0x5e, 0x8f, 0xef, 0xfc, 0x70, 0x7b, 0x73], - [0xda, 0x38, 0x25, 0x76, 0xa9, 0x1a, 0x99, 0x9a, 0x52, 0xdf, 0x8c], - [0xd4, 0xc4, 0x99, 0x2b, 0x54, 0x88, 0xc9, 0x34, 0x80, 0x43, 0x15], - [0x11, 0xa1, 0xed, 0xe3, 0xb4, 0x88, 0xd5, 0x1d, 0x4a, 0x1b, 0x9f], - [0xfd, 0x2c, 0xb4, 0x6e, 0x65, 0xfb, 0x27, 0x9b, 0x65, 0x55, 0x19], - [0xb6, 0xa4, 0x67, 0x16, 0x8a, 0x59, 0xf5, 0xfc, 0x0f, 0x7e, 0x24], - [0x40, 0x8e, 0x5d, 0x84, 0x90, 0x76, 0x50, 0xdb, 0x72, 0x2a, 0x3b], - [0x7d, 0x1e, 0x9d, 0x2f, 0xad, 0xce, 0x60, 0x00, 0xf8, 0xbc, 0xfa], - [0xc1, 0x2d, 0x32, 0xbd, 0xa2, 0xe7, 0xed, 0x17, 0x48, 0xca, 0xb0], - [0xe6, 0x91, 0x6c, 0xa7, 0xdc, 0x83, 0x58, 0x19, 0x05, 0xb1, 0xa6], - [0xec, 0xb2, 0x16, 0xa3, 0x89, 0x7a, 0xcd, 0x44, 0xe9, 0x3a, 0x98], - [0xcf, 0xef, 0x78, 0x5b, 0x7a, 0xec, 0xa8, 0xfa, 0x6c, 0x78, 0x23], - [0x8b, 0x53, 0x89, 0x82, 0x21, 0x3e, 0xfc, 0xed, 0xe4, 0x6b, 0xa0], - [0xff, 0x28, 0x10, 0xb2, 0x24, 0xf9, 0xb5, 0x3e, 0x08, 0xb2, 0x50], - [0x5e, 0x57, 0x11, 0xff, 0x06, 0x1b, 0xc7, 0x0b, 0x28, 0x5b, 0x34], - [0x00, 0x4a, 0xcc, 0x4e, 0x8e, 0x07, 0xea, 0x93, 0x10, 0x1c, 0x87], - [0xab, 0xc7, 0x7e, 0x10, 0x64, 0x7f, 0xa4, 0x6c, 0xca, 0x93, 0x73], - [0xcf, 0x57, 0xc5, 0x15, 0xbc, 0x47, 0xed, 0x5b, 0x1e, 0xb5, 0x9b], - [0x8e, 0xa5, 0xf3, 0x07, 0xa0, 0x68, 0x1e, 0x9e, 0xea, 0x57, 0x3f], - [0xfe, 0xa7, 0x7f, 0x91, 0xc7, 0xa4, 0x15, 0x7c, 0xa2, 0x00, 0x4c], - [0xb9, 0x62, 0x28, 0xa5, 0x9b, 0x04, 0x98, 0xf9, 0xdd, 0x37, 0x42], - [0xfa, 0x40, 0x1c, 0xce, 0xa0, 0x75, 0x9d, 0xaf, 0xd2, 0x09, 0xae], - [0xa7, 0x8e, 0xdb, 0x1e, 0x8b, 0x94, 0x24, 0x47, 0xd8, 0x04, 0xd7], - [0x69, 0x95, 0x8a, 0x29, 0xbe, 0x9f, 0xfb, 0x71, 0x91, 0x9a, 0x40], - [0x82, 0xed, 0x1e, 0xf5, 0xac, 0x34, 0x17, 0xfe, 0x5f, 0xfd, 0xd3], - [0x81, 0xe6, 0xaa, 0x7b, 0x12, 0xf0, 0xb2, 0xb9, 0x47, 0x02, 0x3c], - [0x05, 0xc3, 0x6d, 0xd5, 0xf1, 0xa4, 0x93, 0xe2, 0x8b, 0x7c, 0xed], + [0xb8, 0xa4, 0x4e, 0xe3, 0xb1, 0x4d, 0x3d, 0x63, 0x16, 0x5b, 0x6a], + [0x71, 0x5f, 0xb3, 0xb1, 0x4b, 0x94, 0xe8, 0x2f, 0x4c, 0x3d, 0x04], + [0x95, 0x6d, 0x89, 0xfb, 0x91, 0x6a, 0x6c, 0x52, 0x64, 0x9a, 0xd1], + [0x64, 0x59, 0x79, 0x38, 0xd2, 0x09, 0xea, 0x94, 0x92, 0xda, 0x24], + [0xb0, 0x35, 0xfe, 0xa9, 0x96, 0xb5, 0xe1, 0xde, 0x0a, 0x82, 0x18], + [0x39, 0xe5, 0xdd, 0xba, 0x50, 0xd4, 0x33, 0xa7, 0xb9, 0x63, 0x21], + [0x26, 0x52, 0x7a, 0xaa, 0x52, 0x4d, 0x27, 0x81, 0xe4, 0xef, 0xcd], + [0x17, 0x9e, 0x09, 0xcc, 0xd0, 0x0f, 0x5e, 0x03, 0x45, 0xc9, 0xba], + [0x84, 0xfc, 0x75, 0xeb, 0xcc, 0x9e, 0xb6, 0x50, 0x3f, 0xf8, 0x00], + [0xb9, 0x24, 0x95, 0x13, 0x6d, 0x89, 0xb2, 0x89, 0x86, 0x02, 0xca], + [0xb7, 0xd5, 0x78, 0xfa, 0xa3, 0xa9, 0x90, 0x1b, 0x35, 0xb4, 0x72], + [0x62, 0x9a, 0x31, 0x0c, 0x32, 0x1c, 0x25, 0x2e, 0x1b, 0x56, 0x55], + [0xa9, 0x5d, 0xa8, 0xa4, 0xed, 0x91, 0xeb, 0xba, 0xa0, 0xf9, 0x82], + [0x59, 0x9c, 0xc3, 0x6f, 0x66, 0xec, 0x65, 0xe0, 0x95, 0x6e, 0x34], + [0x3d, 0xc7, 0x0d, 0x6c, 0x01, 0x7d, 0xf2, 0x03, 0x9b, 0xe3, 0xc1], + [0x1d, 0xc6, 0xa4, 0xd1, 0x59, 0x52, 0xce, 0x42, 0x3d, 0xf6, 0xe5], + [0x2d, 0xbd, 0xb6, 0x5c, 0xfb, 0x52, 0x65, 0x2e, 0x7f, 0x03, 0x61], + [0x22, 0x24, 0x50, 0x48, 0x65, 0x5a, 0xe0, 0x0d, 0xf9, 0x78, 0x8d], + [0x72, 0xeb, 0x1d, 0x31, 0xb7, 0xe3, 0xa8, 0x15, 0x1f, 0xf1, 0x43], + [0x45, 0xa4, 0x40, 0x5a, 0x9c, 0x98, 0xa8, 0x6e, 0xac, 0xba, 0x83], + [0x27, 0x55, 0x33, 0xa7, 0x79, 0x08, 0x29, 0x42, 0x6d, 0x89, 0xfc], + [0x3b, 0x65, 0x21, 0x7a, 0x24, 0x58, 0x58, 0x6a, 0x97, 0x6e, 0x7c], + [0x56, 0x41, 0xff, 0x08, 0xaf, 0x9d, 0x33, 0x12, 0xcd, 0xb5, 0x99], + [0x35, 0x48, 0x38, 0x46, 0x3f, 0xee, 0x15, 0x16, 0x8d, 0xf5, 0x16], + [0xcc, 0xc0, 0x1b, 0x1e, 0xf1, 0xae, 0xf7, 0x40, 0xca, 0xc7, 0x9d], + [0x93, 0xae, 0x93, 0xcf, 0x97, 0xdf, 0xba, 0xd6, 0xb2, 0xac, 0x2f], + [0x45, 0xe4, 0x5b, 0x73, 0x54, 0x4c, 0x6c, 0x95, 0xa9, 0xab, 0x7f], + [0x71, 0xac, 0xbf, 0xdf, 0xa4, 0x29, 0xe3, 0x17, 0x3f, 0x24, 0x2f], + [0x5e, 0xc0, 0xf2, 0xbf, 0x5d, 0xc0, 0x31, 0x2d, 0x97, 0x24, 0x1d], + [0x6d, 0x0b, 0x7c, 0x15, 0x68, 0x7c, 0xe1, 0x15, 0xbf, 0x4f, 0x85], + [0x0a, 0x59, 0xf2, 0x3e, 0x48, 0x1d, 0xac, 0xc8, 0x22, 0xb0, 0x37], + [0x3a, 0xe2, 0x9e, 0xec, 0xf9, 0x1e, 0x88, 0xfa, 0xbe, 0x00, 0xee], + [0xc7, 0x5a, 0x1f, 0xc8, 0x48, 0x23, 0x3b, 0x1a, 0x0f, 0xf3, 0x7c], + [0x43, 0x0d, 0x10, 0x03, 0xb2, 0xc6, 0xbd, 0xed, 0x03, 0x19, 0x49], + [0xc9, 0xc4, 0x0e, 0xf3, 0xc6, 0xf4, 0xc1, 0x71, 0xee, 0x96, 0xeb], + [0x18, 0x51, 0x07, 0x36, 0x1a, 0x13, 0x83, 0x69, 0x2b, 0x1b, 0x09], + [0xac, 0x23, 0xb7, 0x47, 0x2d, 0xeb, 0x39, 0xdc, 0x3e, 0xdb, 0x74], + [0x44, 0x60, 0x06, 0x28, 0x5e, 0x8f, 0xef, 0xfc, 0x70, 0x7b, 0x73], + [0xda, 0x38, 0x25, 0x76, 0xa9, 0x1a, 0x99, 0x9a, 0x52, 0xdf, 0x8c], + [0xd4, 0xc4, 0x99, 0x2b, 0x54, 0x88, 0xc9, 0x34, 0x80, 0x43, 0x15], + [0x11, 0xa1, 0xed, 0xe3, 0xb4, 0x88, 0xd5, 0x1d, 0x4a, 0x1b, 0x9f], + [0xfd, 0x2c, 0xb4, 0x6e, 0x65, 0xfb, 0x27, 0x9b, 0x65, 0x55, 0x19], + [0xb6, 0xa4, 0x67, 0x16, 0x8a, 0x59, 0xf5, 0xfc, 0x0f, 0x7e, 0x24], + [0x40, 0x8e, 0x5d, 0x84, 0x90, 0x76, 0x50, 0xdb, 0x72, 0x2a, 0x3b], + [0x7d, 0x1e, 0x9d, 0x2f, 0xad, 0xce, 0x60, 0x00, 0xf8, 0xbc, 0xfa], + [0xc1, 0x2d, 0x32, 0xbd, 0xa2, 0xe7, 0xed, 0x17, 0x48, 0xca, 0xb0], + [0xe6, 0x91, 0x6c, 0xa7, 0xdc, 0x83, 0x58, 0x19, 0x05, 0xb1, 0xa6], + [0xec, 0xb2, 0x16, 0xa3, 0x89, 0x7a, 0xcd, 0x44, 0xe9, 0x3a, 0x98], + [0xcf, 0xef, 0x78, 0x5b, 0x7a, 0xec, 0xa8, 0xfa, 0x6c, 0x78, 0x23], + [0x8b, 0x53, 0x89, 0x82, 0x21, 0x3e, 0xfc, 0xed, 0xe4, 0x6b, 0xa0], + [0xff, 0x28, 0x10, 0xb2, 0x24, 0xf9, 0xb5, 0x3e, 0x08, 0xb2, 0x50], + [0x5e, 0x57, 0x11, 0xff, 0x06, 0x1b, 0xc7, 0x0b, 0x28, 0x5b, 0x34], + [0x00, 0x4a, 0xcc, 0x4e, 0x8e, 0x07, 0xea, 0x93, 0x10, 0x1c, 0x87], + [0xab, 0xc7, 0x7e, 0x10, 0x64, 0x7f, 0xa4, 0x6c, 0xca, 0x93, 0x73], + [0xcf, 0x57, 0xc5, 0x15, 0xbc, 0x47, 0xed, 0x5b, 0x1e, 0xb5, 0x9b], + [0x8e, 0xa5, 0xf3, 0x07, 0xa0, 0x68, 0x1e, 0x9e, 0xea, 0x57, 0x3f], + [0xfe, 0xa7, 0x7f, 0x91, 0xc7, 0xa4, 0x15, 0x7c, 0xa2, 0x00, 0x4c], + [0xb9, 0x62, 0x28, 0xa5, 0x9b, 0x04, 0x98, 0xf9, 0xdd, 0x37, 0x42], + [0xfa, 0x40, 0x1c, 0xce, 0xa0, 0x75, 0x9d, 0xaf, 0xd2, 0x09, 0xae], + [0xa7, 0x8e, 0xdb, 0x1e, 0x8b, 0x94, 0x24, 0x47, 0xd8, 0x04, 0xd7], + [0x69, 0x95, 0x8a, 0x29, 0xbe, 0x9f, 0xfb, 0x71, 0x91, 0x9a, 0x40], + [0x82, 0xed, 0x1e, 0xf5, 0xac, 0x34, 0x17, 0xfe, 0x5f, 0xfd, 0xd3], + [0x81, 0xe6, 0xaa, 0x7b, 0x12, 0xf0, 0xb2, 0xb9, 0x47, 0x02, 0x3c], + [0x05, 0xc3, 0x6d, 0xd5, 0xf1, 0xa4, 0x93, 0xe2, 0x8b, 0x7c, 0xed], ]; for (var test in data) { expect(decode(encode(test)), equals(test)); @@ -142,7 +153,6 @@ main() { }); } - /// A matcher for HuffmanDecodingExceptions. const Matcher isHuffmanDecodingException = const _HuffmanDecodingException(); diff --git a/pkgs/http2/test/src/ping/ping_handler_test.dart b/pkgs/http2/test/src/ping/ping_handler_test.dart index f420754faa..09344977f1 100644 --- a/pkgs/http2/test/src/ping/ping_handler_test.dart +++ b/pkgs/http2/test/src/ping/ping_handler_test.dart @@ -72,7 +72,7 @@ main() { var header = new FrameHeader(8, FrameType.PING, PingFrame.FLAG_ACK, 0); expect(() => pingHandler.processPingFrame(new PingFrame(header, 2)), - throwsA(isProtocolException)); + throwsA(isProtocolException)); // Ensure outstanding pings will be completed with an error once we call // `pingHandler.terminate()`. @@ -88,9 +88,8 @@ main() { pingHandler.terminate('hello world'); expect(() => pingHandler.processPingFrame(null), - throwsA(isTerminatedException)); - expect(pingHandler.ping(), - throwsA(isTerminatedException)); + throwsA(isTerminatedException)); + expect(pingHandler.ping(), throwsA(isTerminatedException)); }); test('ping-non-zero-stream-id', () async { @@ -99,9 +98,9 @@ main() { var header = new FrameHeader(8, FrameType.PING, PingFrame.FLAG_ACK, 1); expect(() => pingHandler.processPingFrame(new PingFrame(header, 1)), - throwsA(isProtocolException)); + throwsA(isProtocolException)); }); }); } -class FrameWriterMock extends SmartMock implements FrameWriter { } +class FrameWriterMock extends SmartMock implements FrameWriter {} diff --git a/pkgs/http2/test/src/settings/settings_handler_test.dart b/pkgs/http2/test/src/settings/settings_handler_test.dart index fb2a5c1847..b87181aa8c 100644 --- a/pkgs/http2/test/src/settings/settings_handler_test.dart +++ b/pkgs/http2/test/src/settings/settings_handler_test.dart @@ -21,8 +21,7 @@ main() { test('successful-setting', () async { var writer = new FrameWriterMock(); - var sh = new SettingsHandler( - new HPackEncoder(), writer, + var sh = new SettingsHandler(new HPackEncoder(), writer, new ActiveSettings(), new ActiveSettings()); var tc = new TestCounter(); @@ -39,8 +38,8 @@ main() { expect(sh.acknowledgedSettings.enablePush, true); // Simulate remote end to responsd with an ACK. - var header = new FrameHeader( - 0, FrameType.SETTINGS, SettingsFrame.FLAG_ACK, 0); + var header = + new FrameHeader(0, FrameType.SETTINGS, SettingsFrame.FLAG_ACK, 0); sh.handleSettingsFrame(new SettingsFrame(header, [])); await changed; @@ -51,8 +50,7 @@ main() { test('ack-remote-settings-change', () { var writer = new FrameWriterMock(); - var sh = new SettingsHandler( - new HPackEncoder(), writer, + var sh = new SettingsHandler(new HPackEncoder(), writer, new ActiveSettings(), new ActiveSettings()); var tc = new TestCounter(); @@ -73,23 +71,21 @@ main() { test('invalid-remote-ack', () { var writer = new FrameWriterMock(); - var sh = new SettingsHandler( - new HPackEncoder(), writer, + var sh = new SettingsHandler(new HPackEncoder(), writer, new ActiveSettings(), new ActiveSettings()); // Simulates ACK even though we haven't sent any settings. - var header = new FrameHeader( - 0, FrameType.SETTINGS, SettingsFrame.FLAG_ACK, 0); + var header = + new FrameHeader(0, FrameType.SETTINGS, SettingsFrame.FLAG_ACK, 0); var settingsFrame = new SettingsFrame(header, const []); expect(() => sh.handleSettingsFrame(settingsFrame), - throwsA(isProtocolException)); + throwsA(isProtocolException)); }); test('invalid-remote-settings-change', () { var writer = new FrameWriterMock(); - var sh = new SettingsHandler( - new HPackEncoder(), writer, + var sh = new SettingsHandler(new HPackEncoder(), writer, new ActiveSettings(), new ActiveSettings()); // Check that settings haven't been applied. @@ -99,15 +95,14 @@ main() { var header = new FrameHeader(6, FrameType.SETTINGS, 0, 0); var settingsFrame = new SettingsFrame(header, invalidPushSettings); expect(() => sh.handleSettingsFrame(settingsFrame), - throwsA(isProtocolException)); + throwsA(isProtocolException)); }); test('change-max-header-table-size', () { var writer = new FrameWriterMock(); var mock = new HPackEncoderMock(); var sh = new SettingsHandler( - mock, writer, - new ActiveSettings(), new ActiveSettings()); + mock, writer, new ActiveSettings(), new ActiveSettings()); // Simulate remote end by setting the push setting. var header = new FrameHeader(6, FrameType.SETTINGS, 0, 0); @@ -115,12 +110,12 @@ main() { mock.mock_updateMaxSendingHeaderTableSize = expectAsync1((int newSize) { expect(newSize, 256); }); - writer.mock_writeSettingsAckFrame = expectAsync0(() { }); + writer.mock_writeSettingsAckFrame = expectAsync0(() {}); sh.handleSettingsFrame(settingsFrame); }); }); } -class FrameWriterMock extends SmartMock implements FrameWriter { } +class FrameWriterMock extends SmartMock implements FrameWriter {} -class HPackEncoderMock extends SmartMock implements HPackEncoder { } +class HPackEncoderMock extends SmartMock implements HPackEncoder {} diff --git a/pkgs/http2/test/src/streams/helper.dart b/pkgs/http2/test/src/streams/helper.dart index 7e370d9637..d07a0b3fa7 100644 --- a/pkgs/http2/test/src/streams/helper.dart +++ b/pkgs/http2/test/src/streams/helper.dart @@ -51,10 +51,10 @@ class BidirectionalConnection { Stream> get readA => writeA.stream; Stream> get readB => writeB.stream; - ClientTransportConnection get clientConnection - => new ClientTransportConnection.viaStreams( - readA, writeB, settings: settings); + ClientTransportConnection get clientConnection => + new ClientTransportConnection.viaStreams(readA, writeB, + settings: settings); - ServerTransportConnection get serverConnection - => new ServerTransportConnection.viaStreams(readB, writeA); + ServerTransportConnection get serverConnection => + new ServerTransportConnection.viaStreams(readB, writeA); } diff --git a/pkgs/http2/test/src/streams/simple_flow_test.dart b/pkgs/http2/test/src/streams/simple_flow_test.dart index 347e84eb0f..cd37ce7fbf 100644 --- a/pkgs/http2/test/src/streams/simple_flow_test.dart +++ b/pkgs/http2/test/src/streams/simple_flow_test.dart @@ -24,9 +24,9 @@ main() { return expectAsync1((StreamMessage msg) { expect(msg is HeadersStreamMessage, isTrue); expect((msg as HeadersStreamMessage).headers.first.name, - expectedHeaders.first.name); + expectedHeaders.first.name); expect((msg as HeadersStreamMessage).headers.first.value, - expectedHeaders.first.value); + expectedHeaders.first.value); }); } @@ -40,14 +40,16 @@ main() { expectHeader = false; expect(msg is HeadersStreamMessage, isTrue); expect((msg as HeadersStreamMessage).headers.first.name, - expectedHeaders.first.name); + expectedHeaders.first.name); expect((msg as HeadersStreamMessage).headers.first.value, - expectedHeaders.first.value); + expectedHeaders.first.value); } else { expect(msg is DataStreamMessage, isTrue); var bytes = (msg as DataStreamMessage).bytes; - expect(bytes, allBytes.sublist( - numBytesReceived, numBytesReceived + bytes.length)); + expect( + bytes, + allBytes.sublist( + numBytesReceived, numBytesReceived + bytes.length)); numBytesReceived += bytes.length; if (numBytesReceived == allBytes) { @@ -73,19 +75,19 @@ main() { streamTest('single-header-request--empty-response', (ClientTransportConnection client, - ServerTransportConnection server) async { - server.incomingStreams.listen( - expectAsync1((TransportStream sStream) async { - sStream.incomingMessages.listen( - messageTestFun('server'), onDone: expectAsync0(() { })); + ServerTransportConnection server) async { + server.incomingStreams + .listen(expectAsync1((TransportStream sStream) async { + sStream.incomingMessages + .listen(messageTestFun('server'), onDone: expectAsync0(() {})); sStream.sendHeaders(expectedHeaders, endStream: true); expect(await serverReceivedAllBytes.future, completes); })); TransportStream cStream = client.makeRequest(expectedHeaders); sendData(cStream); - cStream.incomingMessages.listen( - headersTestFun('client'), onDone: expectAsync0(() {})); + cStream.incomingMessages + .listen(headersTestFun('client'), onDone: expectAsync0(() {})); }); }); }); diff --git a/pkgs/http2/test/src/streams/simple_push_test.dart b/pkgs/http2/test/src/streams/simple_push_test.dart index 844dbf6a0f..7f81ecda4a 100644 --- a/pkgs/http2/test/src/streams/simple_push_test.dart +++ b/pkgs/http2/test/src/streams/simple_push_test.dart @@ -50,7 +50,6 @@ main() { return UTF8.decode(all); } - Future sendData(TransportStream stream, String data) { stream.outgoingMessages ..add(new DataStreamMessage(UTF8.encode(data))) @@ -58,11 +57,10 @@ main() { return stream.outgoingMessages.done; } - streamTest('server-push', - (ClientTransportConnection client, - ServerTransportConnection server) async { - server.incomingStreams.listen( - expectAsync1((ServerTransportStream sStream) async { + streamTest('server-push', (ClientTransportConnection client, + ServerTransportConnection server) async { + server.incomingStreams + .listen(expectAsync1((ServerTransportStream sStream) async { var pushStream = sStream.push(expectedHeaders); pushStream.sendHeaders(expectedHeaders); await sendData(pushStream, 'pushing "hello world" :)'); @@ -75,9 +73,10 @@ main() { ClientTransportStream cStream = client.makeRequest(expectedHeaders, endStream: true); - cStream.incomingMessages.listen( - headersTestFun(), onDone: expectAsync0(() { })); - cStream.peerPushes.listen(expectAsync1((TransportStreamPush push) async { + cStream.incomingMessages + .listen(headersTestFun(), onDone: expectAsync0(() {})); + cStream.peerPushes + .listen(expectAsync1((TransportStreamPush push) async { testHeaders(push.requestHeaders); var iterator = new StreamIterator(push.stream.incomingMessages); diff --git a/pkgs/http2/test/src/streams/streams_test.dart b/pkgs/http2/test/src/streams/streams_test.dart index 5f88a8ebea..d63c6e270b 100644 --- a/pkgs/http2/test/src/streams/streams_test.dart +++ b/pkgs/http2/test/src/streams/streams_test.dart @@ -12,8 +12,8 @@ import 'helper.dart'; main() { group('streams', () { streamTest('single-header-request--empty-response', - (ClientTransportConnection client, - ServerTransportConnection server) async { + (ClientTransportConnection client, + ServerTransportConnection server) async { var expectedHeaders = [new Header.ascii('key', 'value')]; server.incomingStreams.listen(expectAsync1((TransportStream sStream) { @@ -32,17 +32,19 @@ main() { }); streamTest('multi-header-request--empty-response', - (ClientTransportConnection client, - ServerTransportConnection server) async { + (ClientTransportConnection client, + ServerTransportConnection server) async { var expectedHeaders = [new Header.ascii('key', 'value')]; server.incomingStreams.listen(expectAsync1((TransportStream sStream) { - sStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { - expect(msg is HeadersStreamMessage, isTrue); + sStream.incomingMessages.listen( + expectAsync1((StreamMessage msg) { + expect(msg is HeadersStreamMessage, isTrue); - HeadersStreamMessage headersMsg = msg; - expectHeadersEqual(headersMsg.headers, expectedHeaders); - }, count: 3), onDone: expectAsync0(() {})); + HeadersStreamMessage headersMsg = msg; + expectHeadersEqual(headersMsg.headers, expectedHeaders); + }, count: 3), + onDone: expectAsync0(() {})); sStream.outgoingMessages.close(); })); @@ -53,29 +55,34 @@ main() { }); streamTest('multi-data-request--empty-response', - (ClientTransportConnection client, - ServerTransportConnection server) async { + (ClientTransportConnection client, + ServerTransportConnection server) async { var expectedHeaders = [new Header.ascii('key', 'value')]; - var chunks = [[1], [2], [3]]; - - server.incomingStreams.listen( - expectAsync1((TransportStream sStream) async { + var chunks = [ + [1], + [2], + [3] + ]; + + server.incomingStreams + .listen(expectAsync1((TransportStream sStream) async { bool isFirst = true; var receivedChunks = []; - sStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { - if (isFirst) { - isFirst = false; - expect(msg is HeadersStreamMessage, isTrue); - - HeadersStreamMessage headersMsg = msg; - expectHeadersEqual(headersMsg.headers, expectedHeaders); - } else { - expect(msg is DataStreamMessage, isTrue); - - DataStreamMessage dataMsg = msg; - receivedChunks.add(dataMsg.bytes); - } - }, count: 1 + chunks.length), onDone: expectAsync0(() { + sStream.incomingMessages.listen( + expectAsync1((StreamMessage msg) { + if (isFirst) { + isFirst = false; + expect(msg is HeadersStreamMessage, isTrue); + + HeadersStreamMessage headersMsg = msg; + expectHeadersEqual(headersMsg.headers, expectedHeaders); + } else { + expect(msg is DataStreamMessage, isTrue); + + DataStreamMessage dataMsg = msg; + receivedChunks.add(dataMsg.bytes); + } + }, count: 1 + chunks.length), onDone: expectAsync0(() { expect(receivedChunks, chunks); })); sStream.outgoingMessages.close(); @@ -88,8 +95,8 @@ main() { }); streamTest('single-header-request--single-headers-response', - (ClientTransportConnection client, - ServerTransportConnection server) async { + (ClientTransportConnection client, + ServerTransportConnection server) async { var expectedHeaders = [new Header.ascii('key', 'value')]; server.incomingStreams.listen(expectAsync1((TransportStream sStream) { @@ -114,8 +121,8 @@ main() { }); streamTest('single-header-request--multi-headers-response', - (ClientTransportConnection client, - ServerTransportConnection server) async { + (ClientTransportConnection client, + ServerTransportConnection server) async { var expectedHeaders = [new Header.ascii('key', 'value')]; server.incomingStreams.listen(expectAsync1((TransportStream sStream) { @@ -143,10 +150,14 @@ main() { }); streamTest('single-header-request--multi-data-response', - (ClientTransportConnection client, - ServerTransportConnection server) async { + (ClientTransportConnection client, + ServerTransportConnection server) async { var expectedHeaders = [new Header.ascii('key', 'value')]; - var chunks = [[1], [2], [3]]; + var chunks = [ + [1], + [2], + [3] + ]; server.incomingStreams.listen(expectAsync1((TransportStream sStream) { sStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { diff --git a/pkgs/http2/test/transport_test.dart b/pkgs/http2/test/transport_test.dart index 670d2c3c47..392ce3b09b 100644 --- a/pkgs/http2/test/transport_test.dart +++ b/pkgs/http2/test/transport_test.dart @@ -13,14 +13,14 @@ import 'src/hpack/hpack_test.dart' show isHeader; main() { group('transport-test', () { - transportTest('ping', (TransportConnection client, - TransportConnection server) async { + transportTest('ping', + (TransportConnection client, TransportConnection server) async { await client.ping(); await server.ping(); }); - transportTest('terminated-client-ping', (TransportConnection client, - TransportConnection server) async { + transportTest('terminated-client-ping', + (TransportConnection client, TransportConnection server) async { var clientError = client.ping().catchError(expectAsync2((e, s) { expect(e is TransportException, isTrue); })); @@ -37,8 +37,8 @@ main() { })); }); - transportTest('terminated-server-ping', (TransportConnection client, - TransportConnection server) async { + transportTest('terminated-server-ping', + (TransportConnection client, TransportConnection server) async { var clientError = client.ping().catchError(expectAsync2((e, s) { expect(e is TransportException, isTrue); })); @@ -56,23 +56,23 @@ main() { }); transportTest('disabled-push', (ClientTransportConnection client, - ServerTransportConnection server) async { - server.incomingStreams.listen( - expectAsync1((ServerTransportStream stream) async { + ServerTransportConnection server) async { + server.incomingStreams + .listen(expectAsync1((ServerTransportStream stream) async { expect(stream.canPush, false); expect(() => stream.push([new Header.ascii('a', 'b')]), throwsA(new isInstanceOf())); stream.sendHeaders([new Header.ascii('x', 'y')], endStream: true); })); - var stream = client.makeRequest( - [new Header.ascii('a', 'b')], endStream: true); + var stream = + client.makeRequest([new Header.ascii('a', 'b')], endStream: true); var messages = await stream.incomingMessages.toList(); expect(messages, hasLength(1)); expect(messages[0] is HeadersStreamMessage, true); - expect((messages[0] as HeadersStreamMessage).headers[0], - isHeader('x', 'y')); + expect( + (messages[0] as HeadersStreamMessage).headers[0], isHeader('x', 'y')); expect(await stream.peerPushes.toList(), isEmpty); }); @@ -80,7 +80,7 @@ main() { // By default, the stream concurrency level is set to this limit. const int kDefaultStreamLimit = 100; transportTest('enabled-push-100', (ClientTransportConnection client, - ServerTransportConnection server) async { + ServerTransportConnection server) async { // To ensure the limit is kept up-to-date with closing/opening streams, we // retry this. const int kRepetitions = 20; @@ -101,8 +101,8 @@ main() { // Finish the pushes for (ServerTransportStream pushedStream in pushes) { - pushedStream.sendHeaders( - [new Header.ascii('e', 'nd')], endStream: true); + pushedStream + .sendHeaders([new Header.ascii('e', 'nd')], endStream: true); await pushedStream.incomingMessages.toList(); } @@ -114,8 +114,8 @@ main() { Future clientFun() async { for (int i = 0; i < kRepetitions; i++) { - var stream = client.makeRequest( - [new Header.ascii('a', 'b')], endStream: true); + var stream = + client.makeRequest([new Header.ascii('a', 'b')], endStream: true); Future expectPeerPushes() async { int numberOfPushes = 0; @@ -125,7 +125,7 @@ main() { await pushedStream.stream.incomingMessages.toList(); expect(messages, hasLength(1)); expect((messages[0] as HeadersStreamMessage).headers[0], - isHeader('e', 'nd')); + isHeader('e', 'nd')); expect(await pushedStream.stream.peerPushes.toList(), isEmpty); } return numberOfPushes; @@ -136,7 +136,7 @@ main() { expect(messages, hasLength(1)); expect(messages[0] is HeadersStreamMessage, true); expect((messages[0] as HeadersStreamMessage).headers[0], - isHeader('x', 'y')); + isHeader('x', 'y')); expect(await expectPeerPushes(), kDefaultStreamLimit); } @@ -147,13 +147,13 @@ main() { await clientFun(); await client.terminate(); await serverFuture; - }, clientSettings: new ClientSettings( - concurrentStreamLimit: kDefaultStreamLimit, - allowServerPushes: true)); + }, + clientSettings: new ClientSettings( + concurrentStreamLimit: kDefaultStreamLimit, + allowServerPushes: true)); - transportTest('early-shutdown', - (ClientTransportConnection client, - ServerTransportConnection server) async { + transportTest('early-shutdown', (ClientTransportConnection client, + ServerTransportConnection server) async { Future serverFun() async { await for (ServerTransportStream stream in server.incomingStreams) { stream.sendHeaders([new Header.ascii('x', 'y')], endStream: true); @@ -174,10 +174,8 @@ main() { await Future.wait([serverFun(), clientFun()]); }); - transportTest('client-terminates-stream', - (ClientTransportConnection client, - ServerTransportConnection server) async { - + transportTest('client-terminates-stream', (ClientTransportConnection client, + ServerTransportConnection server) async { var readyForError = new Completer(); Future serverFun() async { @@ -204,9 +202,8 @@ main() { await Future.wait([serverFun(), clientFun()]); }); - transportTest('server-terminates-stream', - (ClientTransportConnection client, - ServerTransportConnection server) async { + transportTest('server-terminates-stream', (ClientTransportConnection client, + ServerTransportConnection server) async { Future serverFun() async { await for (ServerTransportStream stream in server.incomingStreams) { stream.terminate(); @@ -228,8 +225,7 @@ main() { transportTest('client-terminates-stream-after-half-close', (ClientTransportConnection client, - ServerTransportConnection server) async { - + ServerTransportConnection server) async { var readyForError = new Completer(); Future serverFun() async { @@ -265,41 +261,40 @@ main() { transportTest('server-terminates-stream-after-half-close', (ClientTransportConnection client, - ServerTransportConnection server) async { - var readyForError = new Completer(); - - Future serverFun() async { - await for (ServerTransportStream stream in server.incomingStreams) { - stream.sendHeaders([new Header.ascii('x', 'y')], endStream: false); - stream.incomingMessages.listen( - expectAsync1((msg) async { - expect(msg is HeadersStreamMessage, true); - await readyForError.future; - stream.terminate(); - }), - onError: expectAsync1((_) {}, count: 0), - onDone: expectAsync0(() {}, count: 1), - ); - } - await server.finish(); - } + ServerTransportConnection server) async { + var readyForError = new Completer(); - Future clientFun() async { - var headers = [new Header.ascii('a', 'b')]; - var stream = client.makeRequest(headers, endStream: false); - stream.onTerminated = expectAsync1((errorCode) { - expect(errorCode, 8); - }, count: 1); - readyForError.complete(); - await client.finish(); - } + Future serverFun() async { + await for (ServerTransportStream stream in server.incomingStreams) { + stream.sendHeaders([new Header.ascii('x', 'y')], endStream: false); + stream.incomingMessages.listen( + expectAsync1((msg) async { + expect(msg is HeadersStreamMessage, true); + await readyForError.future; + stream.terminate(); + }), + onError: expectAsync1((_) {}, count: 0), + onDone: expectAsync0(() {}, count: 1), + ); + } + await server.finish(); + } - await Future.wait([serverFun(), clientFun()]); - }); + Future clientFun() async { + var headers = [new Header.ascii('a', 'b')]; + var stream = client.makeRequest(headers, endStream: false); + stream.onTerminated = expectAsync1((errorCode) { + expect(errorCode, 8); + }, count: 1); + readyForError.complete(); + await client.finish(); + } - transportTest('idle-handler', - (ClientTransportConnection client, - ServerTransportConnection server) async { + await Future.wait([serverFun(), clientFun()]); + }); + + transportTest('idle-handler', (ClientTransportConnection client, + ServerTransportConnection server) async { Future serverFun() async { int activeCount = 0; int idleCount = 0; @@ -312,8 +307,9 @@ main() { }, count: 6); await for (final stream in server.incomingStreams) { stream.sendHeaders([]); - stream.incomingMessages.toList().then( - (_) => stream.outgoingMessages.close()); + stream.incomingMessages + .toList() + .then((_) => stream.outgoingMessages.close()); } await server.finish(); expect(activeCount, 3); @@ -364,13 +360,12 @@ main() { const int kNumberOfMessages = 1000; final headers = [new Header.ascii('a', 'b')]; - - Future testWindowSize(ClientTransportConnection client, - ServerTransportConnection server, - int expectedStreamFlowcontrolWindow) async { - + Future testWindowSize( + ClientTransportConnection client, + ServerTransportConnection server, + int expectedStreamFlowcontrolWindow) async { expect(expectedStreamFlowcontrolWindow, - lessThan(kChunkSize * kNumberOfMessages)); + lessThan(kChunkSize * kNumberOfMessages)); int serverSentBytes = 0; Completer flowcontrolWindowFull = new Completer(); @@ -410,7 +405,7 @@ main() { // [kChunkSize - 1] bytes more than allowed, before getting // the pause event). expect((serverSentBytes - kChunkSize + 1), - lessThan(expectedStreamFlowcontrolWindow)); + lessThan(expectedStreamFlowcontrolWindow)); flowcontrolWindowFull.complete(); }), onResume: () { @@ -441,7 +436,7 @@ main() { // We're just testing the first byte, to make the test faster. expect(dataMessage.bytes[0], - ((byteNr ~/ kChunkSize) + (byteNr % kChunkSize)) % 256); + ((byteNr ~/ kChunkSize) + (byteNr % kChunkSize)) % 256); byteNr += dataMessage.bytes.length; } @@ -461,23 +456,21 @@ main() { transportTest('fast-sender-receiver-paused--default-window-size', (ClientTransportConnection client, - ServerTransportConnection server) async { + ServerTransportConnection server) async { await testWindowSize(client, server, new Window().size); }); transportTest('fast-sender-receiver-paused--10kb-window-size', (ClientTransportConnection client, - ServerTransportConnection server) async { + ServerTransportConnection server) async { await testWindowSize(client, server, 8096); }, clientSettings: new ClientSettings(streamWindowSize: 8096)); }); }); } -transportTest(String name, - func(client, server), - {ClientSettings clientSettings, - ServerSettings serverSettings}) { +transportTest(String name, func(client, server), + {ClientSettings clientSettings, ServerSettings serverSettings}) { return test(name, () { var bidirectional = new BidirectionalConnection(); bidirectional.clientSettings = clientSettings; @@ -497,13 +490,11 @@ class BidirectionalConnection { Stream> get readA => writeA.stream; Stream> get readB => writeB.stream; - ClientTransportConnection get clientConnection - => new ClientTransportConnection.viaStreams( - readA, - writeB.sink, settings: clientSettings); + ClientTransportConnection get clientConnection => + new ClientTransportConnection.viaStreams(readA, writeB.sink, + settings: clientSettings); - ServerTransportConnection get serverConnection - => new ServerTransportConnection.viaStreams( - readB, writeA.sink, settings: serverSettings); + ServerTransportConnection get serverConnection => + new ServerTransportConnection.viaStreams(readB, writeA.sink, + settings: serverSettings); } - From cb6b083f29b7b3891d9e1fea0fea088bea16d4c8 Mon Sep 17 00:00:00 2001 From: Jakob Andersen Date: Thu, 5 Oct 2017 14:20:47 +0200 Subject: [PATCH 062/172] Remove use of new Function syntax for onActiveStateChanged. (dart-lang/http2#16) The new syntax isn't fully supported in 1.24, so switching back to good old typedef. Fixes dart-lang/http2#14. --- pkgs/http2/CHANGELOG.md | 5 +++++ pkgs/http2/lib/src/connection.dart | 2 +- pkgs/http2/lib/src/streams/stream_handler.dart | 6 +++--- pkgs/http2/lib/transport.dart | 4 +++- pkgs/http2/pubspec.yaml | 2 +- 5 files changed, 13 insertions(+), 6 deletions(-) diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index e4b49a5baf..a247d52bce 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.1.5 + +* Removed use of new `Function` syntax, since it isn't fully supported in Dart + 1.24. + ## 0.1.4 * Added an `onActiveStateChanged` callback to `Connection`, which is invoked when diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index 0285e53655..127706d348 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -73,7 +73,7 @@ abstract class Connection { final bool isClientConnection; /// Active state handler for this connection. - void Function(bool isActive) onActiveStateChanged; + ActiveStateHandler onActiveStateChanged; /// The HPack context for this connection. final HPackContext _hpackContext = new HPackContext(); diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index a4c73f2741..bba757b271 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -149,7 +149,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { bool get ranOutOfStreamIds => _ranOutOfStreamIds(); - final void Function(bool isActive) _onActiveStateChanged; + final ActiveStateHandler _onActiveStateChanged; StreamHandler._( this._frameWriter, @@ -167,7 +167,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { ConnectionMessageQueueOut outgoingQueue, ActiveSettings peerSettings, ActiveSettings localSettings, - void Function(bool isActive) onActiveStateChanged) { + ActiveStateHandler onActiveStateChanged) { return new StreamHandler._(writer, incomingQueue, outgoingQueue, peerSettings, localSettings, onActiveStateChanged, 1, 0); } @@ -178,7 +178,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { ConnectionMessageQueueOut outgoingQueue, ActiveSettings peerSettings, ActiveSettings localSettings, - void Function(bool isActive) onActiveStateChanged) { + ActiveStateHandler onActiveStateChanged) { return new StreamHandler._(writer, incomingQueue, outgoingQueue, peerSettings, localSettings, onActiveStateChanged, 2, -1); } diff --git a/pkgs/http2/lib/transport.dart b/pkgs/http2/lib/transport.dart index f057db0f2f..6a6005a902 100644 --- a/pkgs/http2/lib/transport.dart +++ b/pkgs/http2/lib/transport.dart @@ -106,6 +106,8 @@ import 'src/hpack/hpack.dart' show Header; export 'src/hpack/hpack.dart' show Header; +typedef void ActiveStateHandler(bool isActive); + /// Settings for a [TransportConnection]. abstract class Settings { /// The maximum number of concurrent streams the remote end can open @@ -152,7 +154,7 @@ abstract class TransportConnection { /// goes from 0 to 1 (the connection goes from idle to active), and with /// [false] when the number of active streams becomes 0 (the connection goes /// from active to idle). - set onActiveStateChanged(void Function(bool isActive) callback); + set onActiveStateChanged(ActiveStateHandler callback); /// Finish this connection. /// diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index 2f22d3b878..4cc8df2a4f 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -1,5 +1,5 @@ name: http2 -version: 0.1.5-dev +version: 0.1.5 description: A HTTP/2 implementation in Dart. author: Dart Team homepage: https://github.com/dart-lang/http2 From f449c7a8fb248285cabcb53560bce0538058def3 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Fri, 20 Oct 2017 08:45:18 -0700 Subject: [PATCH 063/172] Strong mode fixes and other cleanup (dart-lang/http2#17) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * sort directives * make fields final that can be * A bunch of strong-mode cleanup * Fix dartdoc comments * fix test * Remove unneeded field * final cleanup * enable ALPN tests on mac – supported as of Dart 1.24 * tweak imports * Add StackTrace type * Improve cast * Use typed functions if we can... * type fixes * ignore `undefined_setter` errors in tests using mocks --- pkgs/http2/analysis_options.yaml | 37 +++++++++++++++++ pkgs/http2/experimental/server.dart | 8 ++-- pkgs/http2/lib/multiprotocol_server.dart | 7 ++-- .../lib/src/artificial_server_socket.dart | 40 +++++++++---------- .../lib/src/async_utils/async_utils.dart | 9 +++-- pkgs/http2/lib/src/connection.dart | 14 +++---- pkgs/http2/lib/src/connection_preface.dart | 6 +-- pkgs/http2/lib/src/error_handler.dart | 4 +- .../src/flowcontrol/connection_queues.dart | 4 +- .../lib/src/flowcontrol/queue_messages.dart | 2 +- .../lib/src/flowcontrol/stream_queues.dart | 4 +- .../lib/src/flowcontrol/window_handler.dart | 2 +- .../lib/src/frames/frame_defragmenter.dart | 4 +- pkgs/http2/lib/src/frames/frame_reader.dart | 6 +-- pkgs/http2/lib/src/frames/frame_writer.dart | 8 ---- pkgs/http2/lib/src/frames/frames.dart | 2 +- pkgs/http2/lib/src/hpack/hpack.dart | 5 ++- pkgs/http2/lib/src/ping/ping_handler.dart | 2 +- pkgs/http2/lib/src/settings/settings.dart | 12 +++--- .../http2/lib/src/streams/stream_handler.dart | 28 +++++++------ pkgs/http2/lib/src/testing/client.dart | 16 ++++---- pkgs/http2/lib/src/testing/debug.dart | 20 +++++----- pkgs/http2/lib/transport.dart | 4 +- pkgs/http2/pubspec.yaml | 2 +- pkgs/http2/test/client_test.dart | 25 +++++++----- pkgs/http2/test/client_websites_test.dart | 14 ++----- .../http2/test/multiprotocol_server_test.dart | 11 ++--- pkgs/http2/test/server_test.dart | 4 +- .../src/async_utils/async_utils_test.dart | 4 +- .../test/src/connection_preface_test.dart | 10 ++--- .../flowcontrol/connection_queues_test.dart | 1 + .../src/flowcontrol/stream_queues_test.dart | 1 + .../src/flowcontrol/window_handler_test.dart | 1 + .../src/frames/frame_writer_reader_test.dart | 5 ++- pkgs/http2/test/src/hpack/hpack_test.dart | 6 +-- pkgs/http2/test/src/mock_utils.dart | 1 - .../test/src/ping/ping_handler_test.dart | 1 + .../src/settings/settings_handler_test.dart | 1 + pkgs/http2/test/src/streams/helper.dart | 6 ++- .../test/src/streams/simple_flow_test.dart | 2 +- .../test/src/streams/simple_push_test.dart | 7 ++-- pkgs/http2/test/transport_test.dart | 5 ++- 42 files changed, 190 insertions(+), 161 deletions(-) create mode 100644 pkgs/http2/analysis_options.yaml diff --git a/pkgs/http2/analysis_options.yaml b/pkgs/http2/analysis_options.yaml new file mode 100644 index 0000000000..7244d25012 --- /dev/null +++ b/pkgs/http2/analysis_options.yaml @@ -0,0 +1,37 @@ +analyzer: + strong-mode: true + errors: + unused_element: error + unused_import: error + unused_local_variable: error + dead_code: error +linter: + rules: + #- annotate_overrides + - avoid_empty_else + - avoid_init_to_null + - avoid_return_types_on_setters + - await_only_futures + - camel_case_types + - comment_references + - control_flow_in_finally + - directives_ordering + - empty_catches + - empty_constructor_bodies + - empty_statements + - hash_and_equals + - implementation_imports + - library_names + - library_prefixes + - non_constant_identifier_names + - only_throw_errors + - prefer_final_fields + - prefer_is_not_empty + #- prefer_single_quotes + - slash_for_doc_comments + - test_types_in_equals + - test_types_in_equals + - throw_in_finally + - type_init_formals + - unrelated_type_equality_checks + - valid_regexps diff --git a/pkgs/http2/experimental/server.dart b/pkgs/http2/experimental/server.dart index 266a1b7805..4e823e60e9 100644 --- a/pkgs/http2/experimental/server.dart +++ b/pkgs/http2/experimental/server.dart @@ -17,7 +17,7 @@ const String HOSTNAME = 'localhost'; const int PORT = 7777; main() async { - String localFile(path) => Platform.script.resolve(path).toFilePath(); + String localFile(String path) => Platform.script.resolve(path).toFilePath(); var context = new SecurityContext() ..usePrivateKey(localFile('server_key.pem'), password: 'dartdart') @@ -55,7 +55,7 @@ handleClient(SecureSocket socket) { dumpHeaders('${stream.id}', msg.headers); if (path == null) { path = pathFromHeaders(msg.headers); - if (path == null) throw 'no path given'; + if (path == null) throw new Exception('no path given'); if (path == '/') { sendHtml(stream); @@ -97,7 +97,7 @@ void dumpInfo(String prefix, String msg) { print('[$prefix] $msg'); } -Future sendHtml(TransportStream stream) async { +Future sendHtml(ServerTransportStream stream) async { push(stream, '/iframe', sendIFrameHtml); push(stream, '/iframe2', sendIFrameHtml); push(stream, '/favicon.ico', send404); @@ -122,7 +122,7 @@ Future sendHtml(TransportStream stream) async { } Future push(ServerTransportStream stream, String path, - Future sendResponse(stream, path)) async { + Future sendResponse(TransportStream stream, String path)) async { var requestHeaders = [ new Header.ascii(':authority', '$HOSTNAME:$PORT'), new Header.ascii(':method', 'GET'), diff --git a/pkgs/http2/lib/multiprotocol_server.dart b/pkgs/http2/lib/multiprotocol_server.dart index 0f54f885de..6fd168e03e 100644 --- a/pkgs/http2/lib/multiprotocol_server.dart +++ b/pkgs/http2/lib/multiprotocol_server.dart @@ -7,9 +7,8 @@ library http2.multiprotocol_server; import 'dart:async'; import 'dart:io'; -import 'package:http2/transport.dart' as http2; - import 'src/artificial_server_socket.dart'; +import 'transport.dart' as http2; /// Handles protocol negotiation with HTTP/1.1 and HTTP/2 clients. /// @@ -28,7 +27,7 @@ class MultiProtocolHttpServer { StreamController _http2Controller; Stream _http2Server; - Set _http2Connections = new Set(); + final _http2Connections = new Set(); MultiProtocolHttpServer._(this._serverSocket, this._settings) { _http11Controller = @@ -68,7 +67,7 @@ class MultiProtocolHttpServer { /// an exception (i.e. these must take care of error handling themselves). void startServing(void callbackHttp11(HttpRequest request), void callbackHttp2(http2.ServerTransportStream stream), - {void onError(error, stack)}) { + {void onError(error, StackTrace stack)}) { // 1. Start listening on the real [SecureServerSocket]. _serverSocket.listen((SecureSocket socket) { var protocol = socket.selectedProtocol; diff --git a/pkgs/http2/lib/src/artificial_server_socket.dart b/pkgs/http2/lib/src/artificial_server_socket.dart index 0d894cb16f..29314da1af 100644 --- a/pkgs/http2/lib/src/artificial_server_socket.dart +++ b/pkgs/http2/lib/src/artificial_server_socket.dart @@ -50,9 +50,11 @@ abstract class StreamMethodsMixin implements Stream { return _stream.asBroadcastStream(onListen: onListen, onCancel: onCancel); } - Stream asyncExpand(Stream convert(T)) => _stream.asyncExpand(convert); + Stream asyncExpand(Stream convert(T value)) => + _stream.asyncExpand(convert); - Stream asyncMap(convert(T event)) => _stream.asyncExpand(convert); + Stream asyncMap(FutureOr convert(T event)) => + _stream.asyncMap(convert); Future contains(Object needle) => _stream.contains(needle); @@ -60,13 +62,13 @@ abstract class StreamMethodsMixin implements Stream { return _stream.distinct(equals); } - Future drain([futureValue]) => _stream.drain(); + Future drain([T futureValue]) => _stream.drain(); Future elementAt(int index) => _stream.elementAt(index); - Future every(bool test(T)) => _stream.every(test); + Future every(bool test(T item)) => _stream.every(test); - Stream expand(Iterable convert(T)) => _stream.expand(convert); + Stream expand(Iterable convert(T item)) => _stream.expand(convert); Future get first => _stream.first; @@ -74,8 +76,8 @@ abstract class StreamMethodsMixin implements Stream { return _stream.firstWhere(test); } - Future fold(initialValue, combine(previous, T element)) { - return _stream.fold(initialValue, combine); + Future fold(S initialValue, S combine(S previous, T element)) { + return _stream.fold(initialValue, combine); } Future forEach(void action(T element)) => _stream.forEach(action); @@ -104,37 +106,33 @@ abstract class StreamMethodsMixin implements Stream { onError: onError, onDone: onDone, cancelOnError: cancelOnError); } - Stream map(convert(T event)) => _stream.map(convert); + Stream map(S convert(T event)) => _stream.map(convert); Future pipe(StreamConsumer consumer) => _stream.pipe(consumer); - Future reduce(T combine(T previous, T element)) { - return _stream.reduce(combine); - } + Future reduce(T combine(T previous, T element)) => _stream.reduce(combine); Future get single => _stream.single; - Future singleWhere(bool test(T)) => _stream.singleWhere(test); + Future singleWhere(bool test(T item)) => _stream.singleWhere(test); Stream skip(int count) => _stream.skip(count); - Stream skipWhile(bool test(T)) => _stream.skipWhile(test); + Stream skipWhile(bool test(T item)) => _stream.skipWhile(test); Stream take(int count) => _stream.take(count); - Stream takeWhile(bool test(T)) => _stream.takeWhile(test); + Stream takeWhile(bool test(T item)) => _stream.takeWhile(test); - Stream timeout(Duration timeLimit, {void onTimeout(EventSink sink)}) { - return _stream.timeout(timeLimit, onTimeout: onTimeout); - } + Stream timeout(Duration timeLimit, {void onTimeout(EventSink sink)}) => + _stream.timeout(timeLimit, onTimeout: onTimeout); Future> toList() => _stream.toList(); Future> toSet() => _stream.toSet(); - Stream transform(StreamTransformer streamTransformer) { - return _stream.transform(streamTransformer); - } + Stream transform(StreamTransformer streamTransformer) => + _stream.transform(streamTransformer); - Stream where(bool test(T)) => _stream.where(test); + Stream where(bool test(T value)) => _stream.where(test); } diff --git a/pkgs/http2/lib/src/async_utils/async_utils.dart b/pkgs/http2/lib/src/async_utils/async_utils.dart index e6e6a75302..3f6d44a7e1 100644 --- a/pkgs/http2/lib/src/async_utils/async_utils.dart +++ b/pkgs/http2/lib/src/async_utils/async_utils.dart @@ -16,7 +16,8 @@ class BufferIndicator { /// A state variable indicating whether buffereing would occur at the moment. bool _wouldBuffer = true; - /// Indicates whether calling [add] would buffer the data if called. + /// Indicates whether calling [BufferedBytesWriter.add] would buffer the data + /// if called. /// /// This can be used at a higher level as a way to do custom buffering and /// possibly prioritization. @@ -37,8 +38,8 @@ class BufferIndicator { _wouldBuffer = true; } - /// A broadcast stream notifying users that the [add] method would not buffer - /// the data if called. + /// A broadcast stream notifying users that the [BufferedBytesWriter.add] + /// method would not buffer the data if called. Stream get bufferEmptyEvents => _controller.stream; } @@ -53,7 +54,7 @@ class BufferedSink { /// A intermediate [StreamController] used to catch pause signals and to /// propagate the change via [bufferIndicator]. - StreamController _controller; + StreamController> _controller; /// A future which completes once the sink has been closed. Future _doneFuture; diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index 127706d348..958efc9a22 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -8,6 +8,7 @@ import 'dart:async'; import 'dart:convert'; import '../transport.dart'; +import 'connection_preface.dart'; import 'flowcontrol/connection_queues.dart'; import 'flowcontrol/window.dart'; import 'flowcontrol/window_handler.dart'; @@ -19,8 +20,6 @@ import 'settings/settings.dart'; import 'streams/stream_handler.dart'; import 'sync_errors.dart'; -import 'connection_preface.dart'; - class ConnectionState { /// The connection has been established, we're waiting for the settings frame /// of the remote end. @@ -205,7 +204,7 @@ abstract class Connection { } List _decodeSettings(Settings settings) { - var settingsList = []; + var settingsList = []; // By default a endpoitn can make an unlimited number of concurrent streams. if (settings.concurrentStreamLimit != null) { @@ -278,7 +277,7 @@ abstract class Connection { _terminate(ErrorCode.FRAME_SIZE_ERROR, message: '$error'); } on HPackDecodingException catch (error) { _terminate(ErrorCode.PROTOCOL_ERROR, message: '$error'); - } on TerminatedException catch (error) { + } on TerminatedException { // We tried to perform an action even though the connection was already // terminated. // TODO: Can this even happen and if so, how should we propagate this @@ -422,7 +421,8 @@ class ClientConnection extends Connection implements ClientTransportConnection { bool get isOpen => !_state.isFinishing && !_state.isTerminated; - TransportStream makeRequest(List
headers, {bool endStream: false}) { + ClientTransportStream makeRequest(List
headers, + {bool endStream: false}) { if (_state.isFinishing) { throw new StateError( 'The http/2 connection is finishing and can therefore not be used to ' @@ -432,7 +432,7 @@ class ClientConnection extends Connection implements ClientTransportConnection { 'The http/2 connection is no longer active and can therefore not be ' 'used to make new streams.'); } - TransportStream hStream = _streams.newStream(headers, endStream: endStream); + var hStream = _streams.newStream(headers, endStream: endStream); if (_streams.ranOutOfStreamIds) { _finishing(active: true, message: 'Ran out of stream ids'); } @@ -451,5 +451,5 @@ class ServerConnection extends Connection implements ServerTransportConnection { return new ServerConnection._(frameBytes, outgoing, serverSettings); } - Stream get incomingStreams => _streams.incomingStreams; + Stream get incomingStreams => _streams.incomingStreams; } diff --git a/pkgs/http2/lib/src/connection_preface.dart b/pkgs/http2/lib/src/connection_preface.dart index 695832b742..08cf7e86b8 100644 --- a/pkgs/http2/lib/src/connection_preface.dart +++ b/pkgs/http2/lib/src/connection_preface.dart @@ -44,10 +44,10 @@ const List CONNECTION_PREFACE = const [ /// connection preface. If an error occurs while reading the connection /// preface, the returned stream will have only an error. Stream> readConnectionPreface(Stream> incoming) { - StreamController result; + StreamController> result; StreamSubscription subscription; bool connectionPrefaceRead = false; - var prefaceBuffer = []; + var prefaceBuffer = []; bool terminated = false; terminate(error) { @@ -101,7 +101,7 @@ Stream> readConnectionPreface(Stream> incoming) { result = new StreamController( onListen: () { subscription = incoming.listen(onData, - onError: (e, s) => result.addError(e, s), + onError: (e, StackTrace s) => result.addError(e, s), onDone: () { if (prefaceBuffer != null) { terminate('EOS before connection preface could be read.'); diff --git a/pkgs/http2/lib/src/error_handler.dart b/pkgs/http2/lib/src/error_handler.dart index ae2bf96291..40d391d1fc 100644 --- a/pkgs/http2/lib/src/error_handler.dart +++ b/pkgs/http2/lib/src/error_handler.dart @@ -26,7 +26,7 @@ class TerminatableMixin { // Subclasses can override this method if they want. } - dynamic ensureNotTerminatedSync(f()) { + T ensureNotTerminatedSync(T f()) { if (wasTerminated) { throw new TerminatedException(); } @@ -44,7 +44,7 @@ class TerminatableMixin { /// Used by classes which may be closed. class ClosableMixin { bool _closing = false; - Completer _completer = new Completer(); + final Completer _completer = new Completer(); Future get done => _completer.future; diff --git a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart index 076a037a3c..fa11735be1 100644 --- a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart @@ -16,8 +16,8 @@ import '../byte_utils.dart'; import '../error_handler.dart'; import '../frames/frames.dart'; -import 'stream_queues.dart'; import 'queue_messages.dart'; +import 'stream_queues.dart'; import 'window_handler.dart'; /// The last place before messages coming from the application get encoded and @@ -268,7 +268,7 @@ class ConnectionMessageQueueIn extends Object /// Processes an incoming [PushPromiseFrame] which is addressed to a specific /// stream. void processPushPromiseFrame( - PushPromiseFrame frame, TransportStream pushedStream) { + PushPromiseFrame frame, ClientTransportStream pushedStream) { var streamId = frame.header.streamId; var message = new PushPromiseMessage(streamId, frame.decodedHeaders, frame.promisedStreamId, pushedStream, false); diff --git a/pkgs/http2/lib/src/flowcontrol/queue_messages.dart b/pkgs/http2/lib/src/flowcontrol/queue_messages.dart index 7638db185e..bebf8f2342 100644 --- a/pkgs/http2/lib/src/flowcontrol/queue_messages.dart +++ b/pkgs/http2/lib/src/flowcontrol/queue_messages.dart @@ -41,7 +41,7 @@ class DataMessage extends Message { class PushPromiseMessage extends Message { final List
headers; final int promisedStreamId; - final TransportStream pushedStream; + final ClientTransportStream pushedStream; PushPromiseMessage(int streamId, this.headers, this.promisedStreamId, this.pushedStream, bool endStream) diff --git a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart index 14f946aa64..8bbf783dab 100644 --- a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart @@ -146,7 +146,7 @@ class StreamMessageQueueOut extends Object } } -/// Keeps a list of [Messages] which should be delivered to the +/// Keeps a list of [Message] which should be delivered to the /// [TransportStream]. /// /// It will keep messages up to the stream flow control window size if the @@ -208,7 +208,7 @@ class StreamMessageQueueIn extends Object /// The stream of [StreamMessage]s which come from the remote peer. Stream get messages => _incomingMessagesC.stream; - /// The stream of [ServerPush]es which come from the remote peer. + /// The stream of [TransportStreamPush]es which come from the remote peer. Stream get serverPushes => _serverPushStreamsC.stream; /// A lower layer enqueues a new [Message] which should be delivered to the diff --git a/pkgs/http2/lib/src/flowcontrol/window_handler.dart b/pkgs/http2/lib/src/flowcontrol/window_handler.dart index a549e824d1..ab16105691 100644 --- a/pkgs/http2/lib/src/flowcontrol/window_handler.dart +++ b/pkgs/http2/lib/src/flowcontrol/window_handler.dart @@ -5,8 +5,8 @@ library http2.src.window_handler; import '../async_utils/async_utils.dart'; -import '../sync_errors.dart'; import '../frames/frames.dart'; +import '../sync_errors.dart'; import 'window.dart'; diff --git a/pkgs/http2/lib/src/frames/frame_defragmenter.dart b/pkgs/http2/lib/src/frames/frame_defragmenter.dart index 5c42662fce..44fc076417 100644 --- a/pkgs/http2/lib/src/frames/frame_defragmenter.dart +++ b/pkgs/http2/lib/src/frames/frame_defragmenter.dart @@ -4,10 +4,10 @@ library http2.src.frames.frame_defragmenter; -import 'frames.dart'; - import '../sync_errors.dart'; +import 'frames.dart'; + /// Class used for defragmenting [HeadersFrame]s and [PushPromiseFrame]s. // TODO: Somehow emit an error if too many continuation frames have been sent // (since we're buffering all of them). diff --git a/pkgs/http2/lib/src/frames/frame_reader.dart b/pkgs/http2/lib/src/frames/frame_reader.dart index 75e026b697..2812dc6cd4 100644 --- a/pkgs/http2/lib/src/frames/frame_reader.dart +++ b/pkgs/http2/lib/src/frames/frame_reader.dart @@ -61,7 +61,7 @@ class FrameReader { onListen: () { FrameHeader header; - void terminateWithError(error, [stack]) { + void terminateWithError(error, [StackTrace stack]) { header = null; _framesController.addError(error, stack); _subscription.cancel(); @@ -99,7 +99,7 @@ class FrameReader { } catch (error, stack) { terminateWithError(error, stack); } - }, onError: (error, stack) { + }, onError: (error, StackTrace stack) { terminateWithError(error, stack); }, onDone: () { if (bufferedLength == 0) { @@ -213,7 +213,7 @@ class FrameReader { message: 'Settings frame length must be a multiple of 6 bytes.'); int count = header.length ~/ 6; - var settings = new List(count); + var settings = new List(count); for (int i = 0; i < count; i++) { int identifier = readInt16(bytes, offset + 6 * i); int value = readInt32(bytes, offset + 6 * i + 2); diff --git a/pkgs/http2/lib/src/frames/frame_writer.dart b/pkgs/http2/lib/src/frames/frame_writer.dart index 275aaf65a4..6c79581c41 100644 --- a/pkgs/http2/lib/src/frames/frame_writer.dart +++ b/pkgs/http2/lib/src/frames/frame_writer.dart @@ -20,9 +20,6 @@ class FrameWriter { /// sink. int _highestWrittenStreamId = 0; - /// Whether this [FrameWriter] is closed. - bool _isClosed = false; - FrameWriter( this._hpackEncoder, StreamSink> outgoing, this._peerSettings) : _outWriter = new BufferedBytesWriter(outgoing); @@ -273,11 +270,6 @@ class FrameWriter { } void _writeData(List bytes) { - if (_isClosed) { - // We do ignore any frames after this [FrameWriter] has been closed. - return; - } - _outWriter.add(bytes); } diff --git a/pkgs/http2/lib/src/frames/frames.dart b/pkgs/http2/lib/src/frames/frames.dart index 9843165db0..8374eee9a7 100644 --- a/pkgs/http2/lib/src/frames/frames.dart +++ b/pkgs/http2/lib/src/frames/frames.dart @@ -5,8 +5,8 @@ library http2.src.frames; import 'dart:async'; -import 'dart:typed_data'; import 'dart:math' show max; +import 'dart:typed_data'; import '../async_utils/async_utils.dart'; import '../byte_utils.dart'; diff --git a/pkgs/http2/lib/src/hpack/hpack.dart b/pkgs/http2/lib/src/hpack/hpack.dart index d4deda55d2..f4d8b226d6 100644 --- a/pkgs/http2/lib/src/hpack/hpack.dart +++ b/pkgs/http2/lib/src/hpack/hpack.dart @@ -10,9 +10,10 @@ library http2.hpack; import 'dart:convert'; import 'dart:io'; +import '../byte_utils.dart'; + import 'huffman.dart'; import 'huffman_table.dart'; -import '../byte_utils.dart'; /// Exception raised due to encoding/decoding errors. class HPackDecodingException implements Exception { @@ -103,7 +104,7 @@ class HPackDecoder { } Header readHeaderFieldInternal(int index, {bool neverIndexed: false}) { - var name, value; + List name, value; if (index > 0) { name = _table.lookup(index).name; value = readStringLiteral(); diff --git a/pkgs/http2/lib/src/ping/ping_handler.dart b/pkgs/http2/lib/src/ping/ping_handler.dart index 5a35bea7f4..bd8bc17e92 100644 --- a/pkgs/http2/lib/src/ping/ping_handler.dart +++ b/pkgs/http2/lib/src/ping/ping_handler.dart @@ -6,8 +6,8 @@ library http2.src.ping.ping_handler; import 'dart:async'; -import '../frames/frames.dart'; import '../error_handler.dart'; +import '../frames/frames.dart'; import '../sync_errors.dart'; /// Responsible for pinging the other end and for handling pings from the diff --git a/pkgs/http2/lib/src/settings/settings.dart b/pkgs/http2/lib/src/settings/settings.dart index 4cd87bcf79..6ee2284561 100644 --- a/pkgs/http2/lib/src/settings/settings.dart +++ b/pkgs/http2/lib/src/settings/settings.dart @@ -6,10 +6,10 @@ library http2.src.settings; import 'dart:async'; +import '../error_handler.dart'; import '../frames/frames.dart'; import '../hpack/hpack.dart'; import '../sync_errors.dart'; -import '../error_handler.dart'; /// The settings a remote peer can choose to set. class ActiveSettings { @@ -76,15 +76,15 @@ class ActiveSettings { ActiveSettings( {this.headerTableSize: 4096, this.enablePush: true, - this.maxConcurrentStreams: null, + this.maxConcurrentStreams, this.initialWindowSize: (1 << 16) - 1, this.maxFrameSize: (1 << 14), - this.maxHeaderListSize: null}); + this.maxHeaderListSize}); } /// Handles remote and local connection [Setting]s. /// -/// Incoming [SettingFrame]s will be handled here to update the peer settings. +/// Incoming [SettingsFrame]s will be handled here to update the peer settings. /// Changes to [_toBeAcknowledgedSettings] can be made, the peer will then be /// notified of the setting changes it should use. class SettingsHandler extends Object with TerminatableMixin { @@ -106,8 +106,8 @@ class SettingsHandler extends Object with TerminatableMixin { /// The peer settings, which we ACKed and are obeying. final ActiveSettings _peerSettings; - StreamController _onInitialWindowSizeChangeController = - new StreamController.broadcast(sync: true); + final _onInitialWindowSizeChangeController = + new StreamController.broadcast(sync: true); /// Events are fired when a SettingsFrame changes the initial size /// of stream windows. diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index bba757b271..eee117e3f6 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -10,15 +10,15 @@ import 'dart:math'; import '../../transport.dart'; import '../connection.dart'; +import '../error_handler.dart'; import '../flowcontrol/connection_queues.dart'; -import '../flowcontrol/stream_queues.dart'; import '../flowcontrol/queue_messages.dart'; +import '../flowcontrol/stream_queues.dart'; import '../flowcontrol/window.dart'; import '../flowcontrol/window_handler.dart'; import '../frames/frames.dart'; import '../hpack/hpack.dart'; import '../settings/settings.dart'; -import '../error_handler.dart'; import '../sync_errors.dart'; /// Represents the current state of a stream. @@ -67,9 +67,10 @@ class Http2StreamImpl extends TransportStream // Termination handler. Invoked if the stream receives an RST_STREAM frame. void Function(int) _onTerminated; - final Function _canPushFun; - final Function _pushStreamFun; - final Function _terminateStreamFun; + final ZoneUnaryCallback _canPushFun; + final ZoneBinaryCallback> + _pushStreamFun; + final ZoneUnaryCallback _terminateStreamFun; StreamSubscription _outgoingCSubscription; @@ -98,12 +99,12 @@ class Http2StreamImpl extends TransportStream /// /// The [requestHeaders] are the headers to which the pushed stream /// responds to. - TransportStream push(List
requestHeaders) => + ServerTransportStream push(List
requestHeaders) => _pushStreamFun(this, requestHeaders); void terminate() => _terminateStreamFun(this); - set onTerminated(void handler(int)) { + set onTerminated(void handler(int v)) { _onTerminated = handler; if (_terminatedErrorCode != null && _onTerminated != null) { _onTerminated(_terminatedErrorCode); @@ -225,7 +226,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { return !isLocalStream; } - TransportStream newStream(List
headers, {bool endStream: false}) { + Http2StreamImpl newStream(List
headers, {bool endStream: false}) { return ensureNotTerminatedSync(() { var stream = newLocalStream(); _sendHeaders(stream, headers, endStream: endStream); @@ -233,7 +234,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { }); } - TransportStream newLocalStream() { + Http2StreamImpl newLocalStream() { return ensureNotTerminatedSync(() { assert(_canCreateNewStream()); @@ -247,7 +248,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { }); } - TransportStream newRemoteStream(int remoteStreamId) { + Http2StreamImpl newRemoteStream(int remoteStreamId) { return ensureNotTerminatedSync(() { assert(remoteStreamId <= MAX_STREAM_ID); // NOTE: We cannot enforce that a new stream id is 2 higher than the last @@ -304,7 +305,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { incomingQueue.insertNewStreamMessageQueue(streamId, streamQueueIn); - var _outgoingC = new StreamController(); + var _outgoingC = new StreamController(); var stream = new Http2StreamImpl( streamQueueIn, streamQueueOut, @@ -343,7 +344,8 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { !_ranOutOfStreamIds(); } - TransportStream _push(Http2StreamImpl stream, List
requestHeaders) { + ServerTransportStream _push( + Http2StreamImpl stream, List
requestHeaders) { if (stream.state != StreamState.Open && stream.state != StreamState.HalfClosedRemote) { throw new StateError('Cannot push based on a stream that is neither open ' @@ -745,7 +747,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { } } - void _closeStreamAbnormally(Http2StreamImpl stream, Exception exception, + void _closeStreamAbnormally(Http2StreamImpl stream, Object exception, {bool propagateException: false}) { incomingQueue.removeStreamMessageQueue(stream.id); diff --git a/pkgs/http2/lib/src/testing/client.dart b/pkgs/http2/lib/src/testing/client.dart index 6966b829e3..541f702dfd 100644 --- a/pkgs/http2/lib/src/testing/client.dart +++ b/pkgs/http2/lib/src/testing/client.dart @@ -61,10 +61,10 @@ class ClientConnection { } Future _handleStream(ClientTransportStream stream) { - var completer = new Completer(); + var completer = new Completer(); bool isFirst = true; - var controller = new StreamController(); - var serverPushController = new StreamController(sync: true); + var controller = new StreamController>(); + var serverPushController = new StreamController(sync: true); stream.incomingMessages.listen((StreamMessage msg) { if (isFirst) { isFirst = false; @@ -81,16 +81,16 @@ class ClientConnection { Stream _handlePeerPushes( Stream serverPushes) { - var pushesController = new StreamController(); + var pushesController = new StreamController(); serverPushes.listen((TransportStreamPush push) { - var responseCompleter = new Completer(); + var responseCompleter = new Completer(); var serverPush = new ServerPush( _convertHeaders(push.requestHeaders), responseCompleter.future); pushesController.add(serverPush); bool isFirst = true; - var dataController = new StreamController(); + var dataController = new StreamController>(); push.stream.incomingMessages.listen((StreamMessage msg) { if (isFirst) { isFirst = false; @@ -108,7 +108,7 @@ class ClientConnection { } Map> _convertHeaders(List
headers) { - var headerMap = {}; + var headerMap = >{}; for (var header in headers) { headerMap .putIfAbsent(ASCII.decode(header.name), () => []) @@ -125,7 +125,7 @@ class ClientConnection { /// client. The maximum number of concurrent server pushes can be configured via /// [maxConcurrentPushes] (default is `null` meaning no limit). Future connect(Uri uri, - {bool allowServerPushes: false, int maxConcurrentPushes: null}) async { + {bool allowServerPushes: false, int maxConcurrentPushes}) async { const List Http2AlpnProtocols = const [ 'h2-14', 'h2-15', diff --git a/pkgs/http2/lib/src/testing/debug.dart b/pkgs/http2/lib/src/testing/debug.dart index 97c1f11e10..ba56269aac 100644 --- a/pkgs/http2/lib/src/testing/debug.dart +++ b/pkgs/http2/lib/src/testing/debug.dart @@ -5,21 +5,19 @@ library http2.debug; import 'dart:async'; -import 'dart:io'; -import 'dart:io' show stderr; import 'dart:convert'; +import 'dart:io'; +import '../../transport.dart'; import '../connection_preface.dart'; import '../frames/frames.dart'; import '../settings/settings.dart'; -import '../../transport.dart'; - final jsonEncoder = new JsonEncoder.withIndent(' '); TransportConnection debugPrintingConnection(Socket socket, {bool isServer: true, bool verbose: true}) { - var connection; + TransportConnection connection; var incoming = decodeVerbose(socket, isServer, verbose: verbose); var outgoing = decodeOutgoingVerbose(socket, isServer, verbose: verbose); @@ -35,8 +33,8 @@ Stream> decodeVerbose(Stream> inc, bool isServer, {bool verbose: true}) { String name = isServer ? 'server' : 'client'; - var sc = new StreamController(); - var sDebug = new StreamController(); + var sc = new StreamController>(); + var sDebug = new StreamController>(); _pipeAndCopy(inc, sc, sDebug); @@ -77,8 +75,8 @@ StreamSink> decodeOutgoingVerbose( {bool verbose: true}) { String name = isServer ? 'server' : 'client'; - var proxySink = new StreamController(); - var copy = new StreamController(); + var proxySink = new StreamController>(); + var copy = new StreamController>(); if (!isServer) { _decodeFrames(readConnectionPreface(copy.stream)).listen((Frame frame) { @@ -120,12 +118,12 @@ Stream _decodeFrames(Stream> bytes) { return decoder.startDecoding(); } -Future _pipeAndCopy(Stream from, StreamSink to, StreamSink to2) { +Future _pipeAndCopy(Stream> from, StreamSink to, StreamSink to2) { var c = new Completer(); from.listen((List data) { to.add(data); to2.add(data); - }, onError: (e, s) { + }, onError: (e, StackTrace s) { to.addError(e, s); to2.addError(e, s); }, onDone: () { diff --git a/pkgs/http2/lib/transport.dart b/pkgs/http2/lib/transport.dart index 6a6005a902..ce5120ae46 100644 --- a/pkgs/http2/lib/transport.dart +++ b/pkgs/http2/lib/transport.dart @@ -203,7 +203,7 @@ abstract class ServerTransportConnection extends TransportConnection { } /// Incoming HTTP/2 streams. - Stream get incomingStreams; + Stream get incomingStreams; } /// Represents a HTTP/2 stream. @@ -223,7 +223,7 @@ abstract class TransportStream { /// Sets the termination handler on this stream. /// /// The handler will be called if the stream receives an RST_STREAM frame. - set onTerminated(void value(int)); + set onTerminated(void value(int v)); /// Terminates this HTTP/2 stream in an un-normal way. /// diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index 4cc8df2a4f..37252d0c27 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -1,5 +1,5 @@ name: http2 -version: 0.1.5 +version: 0.1.6-dev description: A HTTP/2 implementation in Dart. author: Dart Team homepage: https://github.com/dart-lang/http2 diff --git a/pkgs/http2/test/client_test.dart b/pkgs/http2/test/client_test.dart index 0ec8a86bc7..c7cb6e126a 100644 --- a/pkgs/http2/test/client_test.dart +++ b/pkgs/http2/test/client_test.dart @@ -214,10 +214,10 @@ main() { // happens to be like that ATM. // Await stream/connection window update frame. - var win = await nextFrame(); + var win = await nextFrame() as WindowUpdateFrame; expect(win.header.streamId, 1); expect(win.windowSizeIncrement, 1); - win = await nextFrame(); + win = await nextFrame() as WindowUpdateFrame; expect(win.header.streamId, 0); expect(win.windowSizeIncrement, 1); @@ -427,7 +427,7 @@ main() { settingsDone.complete(); // Make sure we got the new stream. - var frame = await nextFrame(); + var frame = await nextFrame() as HeadersFrame; expect(frame.hasEndStreamFlag, false); var decodedHeaders = decoder.decode(frame.headerBlockFragment); expect(decodedHeaders, hasLength(1)); @@ -436,14 +436,12 @@ main() { headersDone.complete(); // Make sure we got the stream reset. - frame = await nextFrame(); - expect(frame is RstStreamFrame, true); - expect((frame as RstStreamFrame).errorCode, ErrorCode.CANCEL); + var frame2 = await nextFrame() as RstStreamFrame; + expect(frame2.errorCode, ErrorCode.CANCEL); // Make sure we get the graceful shutdown message. - frame = await nextFrame(); - expect(frame is GoawayFrame, true); - expect((frame as GoawayFrame).errorCode, ErrorCode.NO_ERROR); + var frame3 = await nextFrame() as GoawayFrame; + expect(frame3.errorCode, ErrorCode.NO_ERROR); // Make sure the client ended the connection. expect(await serverReader.moveNext(), false); @@ -488,7 +486,7 @@ main() { settingsDone.complete(); // Make sure we got the new stream. - var frame = await nextFrame(); + var frame = await nextFrame() as HeadersFrame; expect(frame.hasEndStreamFlag, false); var decodedHeaders = decoder.decode(frame.headerBlockFragment); expect(decodedHeaders, hasLength(1)); @@ -529,7 +527,12 @@ main() { } clientTest( - String name, func(clientConnection, frameWriter, frameReader, readNext)) { + String name, + Future func( + ClientTransportConnection clientConnection, + FrameWriter frameWriter, + StreamIterator frameReader, + Future readNext())) { return test(name, () { var streams = new ClientStreams(); var serverReader = streams.serverConnectionFrameReader; diff --git a/pkgs/http2/test/client_websites_test.dart b/pkgs/http2/test/client_websites_test.dart index 62e41b0f10..44b00b649a 100644 --- a/pkgs/http2/test/client_websites_test.dart +++ b/pkgs/http2/test/client_websites_test.dart @@ -26,8 +26,6 @@ main() async { body = body.toLowerCase(); expect(body, contains('')); expect(body, contains('twitter.com')); - }, onPlatform: { - 'mac-os': new Skip('ALPN not supported on MacOS'), }); test('nghttp2.org - server push enabled', () async { @@ -54,7 +50,7 @@ main() async { dumpHeaders(uri, response.headers); Future> accumulatePushes() async { - var futures = []; + var futures = >[]; return response.serverPushes .listen((ServerPush push) { futures.add(push.response.then((Response response) { @@ -79,13 +75,11 @@ main() async { expect(body, contains('')); expect(body, contains('nghttp2')); - var pushes = results[1]; + var pushes = results[1] as List; expect(pushes, hasLength(1)); expect(pushes[0][0], '/stylesheets/screen.css'); expect(pushes[0][1], contains('audio,video{')); await connection.close(); - }, onPlatform: { - 'mac-os': new Skip('ALPN not supported on MacOS'), }); test('nghttp2.org - server push disabled', () async { @@ -98,7 +92,7 @@ main() async { dumpHeaders(uri, response.headers); Future> accumulatePushes() async { - var futures = []; + var futures = >[]; return response.serverPushes .listen((ServerPush push) { futures.add(push.response @@ -117,8 +111,6 @@ main() async { var pushes = results[1]; expect(pushes, hasLength(0)); await connection.close(); - }, onPlatform: { - 'mac-os': new Skip('ALPN not supported on MacOS'), }); }); } diff --git a/pkgs/http2/test/multiprotocol_server_test.dart b/pkgs/http2/test/multiprotocol_server_test.dart index a26e89fbce..3afce596f4 100644 --- a/pkgs/http2/test/multiprotocol_server_test.dart +++ b/pkgs/http2/test/multiprotocol_server_test.dart @@ -5,8 +5,8 @@ library http2.test.client_tests; import 'dart:async'; -import 'dart:io'; import 'dart:convert'; +import 'dart:io'; import 'package:test/test.dart'; @@ -38,8 +38,6 @@ main() { for (int i = 0; i < Count; i++) { await makeHttp11Request(server, client, i); } - }, onPlatform: { - 'mac-os': new Skip('ALPN not supported on MacOS'), }); test('http/2', () async { @@ -64,8 +62,6 @@ main() { await makeHttp2Request(server, connection, i); } await connection.finish(); - }, onPlatform: { - 'mac-os': new Skip('ALPN not supported on MacOS'), }); }); } @@ -105,8 +101,7 @@ Future makeHttp2Request(MultiProtocolHttpServer server, expect(responseHeaders[':status'], '200'); expect(await si.moveNext(), true); - expect(si.current is DataStreamMessage, true); - expect(ASCII.decode(si.current.bytes), 'answer$i'); + expect(ASCII.decode((si.current as DataStreamMessage).bytes), 'answer$i'); expect(await si.moveNext(), false); } @@ -130,7 +125,7 @@ Future handleHttp2Request(ServerTransportStream stream, int i) async { } Map getHeaders(HeadersStreamMessage headers) { - var map = {}; + var map = {}; for (var h in headers.headers) { map.putIfAbsent(ASCII.decode(h.name), () => ASCII.decode(h.value)); } diff --git a/pkgs/http2/test/server_test.dart b/pkgs/http2/test/server_test.dart index 9c333e4a7c..49b8ce7096 100644 --- a/pkgs/http2/test/server_test.dart +++ b/pkgs/http2/test/server_test.dart @@ -195,7 +195,9 @@ main() { } serverTest( - String name, func(serverConnection, frameWriter, frameReader, readNext)) { + String name, + func(ServerTransportConnection serverConnection, FrameWriter frameWriter, + StreamIterator frameReader, Future readNext())) { return test(name, () { var streams = new ClientErrorStreams(); var clientReader = streams.clientConnectionFrameReader; diff --git a/pkgs/http2/test/src/async_utils/async_utils_test.dart b/pkgs/http2/test/src/async_utils/async_utils_test.dart index 1af6ceba58..96b7b4bfa2 100644 --- a/pkgs/http2/test/src/async_utils/async_utils_test.dart +++ b/pkgs/http2/test/src/async_utils/async_utils_test.dart @@ -35,7 +35,7 @@ main() { }); test('buffered-sink', () { - var c = new StreamController(); + var c = new StreamController>(); var bs = new BufferedSink(c); expect(bs.bufferIndicator.wouldBuffer, true); @@ -62,7 +62,7 @@ main() { }); test('buffered-bytes-writer', () async { - var c = new StreamController(); + var c = new StreamController>(); var writer = new BufferedBytesWriter(c); expect(writer.bufferIndicator.wouldBuffer, true); diff --git a/pkgs/http2/test/src/connection_preface_test.dart b/pkgs/http2/test/src/connection_preface_test.dart index 83d99948fa..9bdc4fa2c4 100644 --- a/pkgs/http2/test/src/connection_preface_test.dart +++ b/pkgs/http2/test/src/connection_preface_test.dart @@ -14,10 +14,10 @@ main() { group('connection-preface', () { test('successful', () async { final frameBytes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; - final data = new List.from(CONNECTION_PREFACE)..addAll(frameBytes); + final data = new List.from(CONNECTION_PREFACE)..addAll(frameBytes); for (int size = 1; size <= data.length; size++) { - var c = new StreamController(); + var c = new StreamController>(); var resultF = readConnectionPreface(c.stream).fold([], (b, d) => b..addAll(d)); @@ -34,7 +34,7 @@ main() { }); test('only-part-of-connection-sequence', () async { - var c = new StreamController(); + var c = new StreamController>(); var resultF = readConnectionPreface(c.stream).fold([], (b, d) => b..addAll(d)); @@ -49,7 +49,7 @@ main() { }); test('wrong-connection-sequence', () async { - var c = new StreamController(); + var c = new StreamController>(); var resultF = readConnectionPreface(c.stream).fold([], (b, d) => b..addAll(d)); @@ -64,7 +64,7 @@ main() { }); test('incoming-socket-error', () async { - var c = new StreamController(); + var c = new StreamController>(); var resultF = readConnectionPreface(c.stream).fold([], (b, d) => b..addAll(d)); diff --git a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart index 2ab88537c5..ff0f69f90d 100644 --- a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart @@ -2,6 +2,7 @@ // 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. +// ignore_for_file: undefined_setter import 'package:test/test.dart'; import 'package:http2/transport.dart'; diff --git a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart index e189c28f6c..b5b08448de 100644 --- a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart @@ -2,6 +2,7 @@ // 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. +// ignore_for_file: undefined_setter import 'package:test/test.dart'; import 'package:http2/transport.dart'; diff --git a/pkgs/http2/test/src/flowcontrol/window_handler_test.dart b/pkgs/http2/test/src/flowcontrol/window_handler_test.dart index 771d525b22..ecb61fd4fb 100644 --- a/pkgs/http2/test/src/flowcontrol/window_handler_test.dart +++ b/pkgs/http2/test/src/flowcontrol/window_handler_test.dart @@ -2,6 +2,7 @@ // 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. +// ignore_for_file: undefined_setter import 'package:test/test.dart'; import 'package:http2/src/flowcontrol/window.dart'; diff --git a/pkgs/http2/test/src/frames/frame_writer_reader_test.dart b/pkgs/http2/test/src/frames/frame_writer_reader_test.dart index 65d64243b4..2f1d579f5b 100644 --- a/pkgs/http2/test/src/frames/frame_writer_reader_test.dart +++ b/pkgs/http2/test/src/frames/frame_writer_reader_test.dart @@ -203,7 +203,7 @@ main() { expect(contFrame.header.streamId, 99); expect(contFrame.hasEndHeadersFlag, isTrue); - var headerBlock = [] + var headerBlock = [] ..addAll(headersFrame.headerBlockFragment) ..addAll(contFrame.headerBlockFragment); @@ -216,7 +216,8 @@ main() { }); } -writerReaderTest(String name, func(writer, reader, decoder)) { +writerReaderTest(String name, + func(FrameWriter writer, FrameReader reader, HPackDecoder decoder)) { test(name, () { var settings = new ActiveSettings(); var context = new HPackContext(); diff --git a/pkgs/http2/test/src/hpack/hpack_test.dart b/pkgs/http2/test/src/hpack/hpack_test.dart index 495e519cb5..e332917c24 100644 --- a/pkgs/http2/test/src/hpack/hpack_test.dart +++ b/pkgs/http2/test/src/hpack/hpack_test.dart @@ -617,7 +617,7 @@ main() { List
headers; var context = new HPackContext(); - var buffer = []; + var buffer = []; buffer.addAll(TestHelper.insertIntoDynamicTable(2048, char0, charA)); buffer.addAll(TestHelper.insertIntoDynamicTable(2048, char1, charB)); buffer.addAll(TestHelper.dynamicTableLookup(0)); @@ -743,7 +743,7 @@ class TestHelper { static List newInteger(int currentByte, int prefixBits, int value) { assert((currentByte & ((1 << prefixBits) - 1)) == 0); - var buffer = []; + var buffer = []; if (value < ((1 << prefixBits) - 1)) { currentByte |= value; buffer.add(currentByte); @@ -767,7 +767,7 @@ class TestHelper { static List insertIntoDynamicTable(int n, int nameChar, int valueChar) { // NOTE: size(header) = 32 + header.name.length + header.value.length. - var buffer = []; + var buffer = []; // Literal indexed (will be put into dynamic table) buffer.addAll([0x40]); diff --git a/pkgs/http2/test/src/mock_utils.dart b/pkgs/http2/test/src/mock_utils.dart index e62a4f7dc8..992210ce6b 100644 --- a/pkgs/http2/test/src/mock_utils.dart +++ b/pkgs/http2/test/src/mock_utils.dart @@ -32,7 +32,6 @@ import 'package:test/test.dart'; /// /// NOTE: If method signatures change, the test code must be changed and /// the analyzer will not give any warnings if this is not done. -@proxy class SmartMock { final Map _registeredMethods = {}; diff --git a/pkgs/http2/test/src/ping/ping_handler_test.dart b/pkgs/http2/test/src/ping/ping_handler_test.dart index 09344977f1..d1ebedf5e9 100644 --- a/pkgs/http2/test/src/ping/ping_handler_test.dart +++ b/pkgs/http2/test/src/ping/ping_handler_test.dart @@ -2,6 +2,7 @@ // 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. +// ignore_for_file: undefined_setter import 'dart:async'; import 'package:test/test.dart'; diff --git a/pkgs/http2/test/src/settings/settings_handler_test.dart b/pkgs/http2/test/src/settings/settings_handler_test.dart index b87181aa8c..7039745276 100644 --- a/pkgs/http2/test/src/settings/settings_handler_test.dart +++ b/pkgs/http2/test/src/settings/settings_handler_test.dart @@ -2,6 +2,7 @@ // 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. +// ignore_for_file: undefined_setter import 'dart:async'; import 'package:test/test.dart'; diff --git a/pkgs/http2/test/src/streams/helper.dart b/pkgs/http2/test/src/streams/helper.dart index d07a0b3fa7..897d425448 100644 --- a/pkgs/http2/test/src/streams/helper.dart +++ b/pkgs/http2/test/src/streams/helper.dart @@ -24,7 +24,9 @@ expectEmptyStream(Stream s) { s.listen(expectAsync1((_) {}, count: 0), onDone: expectAsync0(() {})); } -streamTest(String name, func(client, server), {ClientSettings settings}) { +streamTest(String name, + func(ClientTransportConnection client, ServerTransportConnection server), + {ClientSettings settings}) { return test(name, () { var bidirect = new BidirectionalConnection(); bidirect.settings = settings; @@ -36,7 +38,7 @@ streamTest(String name, func(client, server), {ClientSettings settings}) { framesTest(String name, func(frameWriter, frameStream)) { return test(name, () { - var c = new StreamController(); + var c = new StreamController>(); var fw = new FrameWriter(null, c, new ActiveSettings()); var frameStream = new FrameReader(c.stream, new ActiveSettings()); diff --git a/pkgs/http2/test/src/streams/simple_flow_test.dart b/pkgs/http2/test/src/streams/simple_flow_test.dart index cd37ce7fbf..acf83e731b 100644 --- a/pkgs/http2/test/src/streams/simple_flow_test.dart +++ b/pkgs/http2/test/src/streams/simple_flow_test.dart @@ -52,7 +52,7 @@ main() { numBytesReceived, numBytesReceived + bytes.length)); numBytesReceived += bytes.length; - if (numBytesReceived == allBytes) { + if (numBytesReceived > allBytes.length) { if (serverReceivedAllBytes.isCompleted) { throw new Exception('Got more messages than expected'); } diff --git a/pkgs/http2/test/src/streams/simple_push_test.dart b/pkgs/http2/test/src/streams/simple_push_test.dart index 7f81ecda4a..4518688823 100644 --- a/pkgs/http2/test/src/streams/simple_push_test.dart +++ b/pkgs/http2/test/src/streams/simple_push_test.dart @@ -38,8 +38,9 @@ main() { Completer serverReceivedAllBytes = new Completer(); - Future readData(StreamIterator iterator) async { - var all = []; + Future readData( + StreamIterator iterator) async { + var all = []; while (await iterator.moveNext()) { var msg = iterator.current; @@ -82,7 +83,7 @@ main() { var iterator = new StreamIterator(push.stream.incomingMessages); bool hasNext = await iterator.moveNext(); expect(hasNext, isTrue); - testHeaders(iterator.current.headers); + testHeaders((iterator.current as HeadersStreamMessage).headers); String msg = await readData(iterator); expect(msg, 'pushing "hello world" :)'); diff --git a/pkgs/http2/test/transport_test.dart b/pkgs/http2/test/transport_test.dart index 392ce3b09b..f301fe8713 100644 --- a/pkgs/http2/test/transport_test.dart +++ b/pkgs/http2/test/transport_test.dart @@ -375,7 +375,7 @@ main() { stream.sendHeaders([new Header.ascii('x', 'y')]); int messageNr = 0; - StreamController controller; + StreamController controller; addData() { if (!controller.isPaused) { if (messageNr < kNumberOfMessages) { @@ -469,7 +469,8 @@ main() { }); } -transportTest(String name, func(client, server), +transportTest(String name, + func(ClientTransportConnection client, ServerTransportConnection server), {ClientSettings clientSettings, ServerSettings serverSettings}) { return test(name, () { var bidirectional = new BidirectionalConnection(); From edb0f11e8abbe147ec579e55fd4f3fbae65301d3 Mon Sep 17 00:00:00 2001 From: Jakob Andersen Date: Fri, 3 Nov 2017 12:12:33 +0100 Subject: [PATCH 064/172] Remove use of dart:mirrors. (dart-lang/http2#18) --- pkgs/http2/CHANGELOG.md | 4 + .../lib/src/flowcontrol/window_handler.dart | 2 +- pkgs/http2/pubspec.yaml | 3 +- .../flowcontrol/connection_queues_test.dart | 125 +++++++----------- .../src/flowcontrol/stream_queues_test.dart | 78 +++++------ .../src/flowcontrol/window_handler_test.dart | 13 +- pkgs/http2/test/src/mock_utils.dart | 90 ------------- .../test/src/ping/ping_handler_test.dart | 45 +++---- .../src/settings/settings_handler_test.dart | 35 ++--- 9 files changed, 126 insertions(+), 269 deletions(-) delete mode 100644 pkgs/http2/test/src/mock_utils.dart diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index a247d52bce..40f5528d9b 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 0.1.6 + +* Strong mode fixes and other cleanup. + ## 0.1.5 * Removed use of new `Function` syntax, since it isn't fully supported in Dart diff --git a/pkgs/http2/lib/src/flowcontrol/window_handler.dart b/pkgs/http2/lib/src/flowcontrol/window_handler.dart index ab16105691..909fa2bb81 100644 --- a/pkgs/http2/lib/src/flowcontrol/window_handler.dart +++ b/pkgs/http2/lib/src/flowcontrol/window_handler.dart @@ -132,7 +132,7 @@ class IncomingWindowHandler { // b) We processed data from the remote end (we can handle now more data) // => This is handled by [dataProcessed]. // - // c) We increase/decrease the initial steram window size after the + // c) We increase/decrease the initial stream window size after the // stream was created (newer streams will start with the changed // initial stream window size). // => This is not an issue, because we don't support changing the diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index 37252d0c27..e208249868 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -1,5 +1,5 @@ name: http2 -version: 0.1.6-dev +version: 0.1.6 description: A HTTP/2 implementation in Dart. author: Dart Team homepage: https://github.com/dart-lang/http2 @@ -8,4 +8,5 @@ environment: sdk: '>=1.24.0 <2.0.0' dev_dependencies: + mockito: ^2.2.0 test: '>=0.12.0 <0.13.0' diff --git a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart index ff0f69f90d..5a0c53c2c0 100644 --- a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart @@ -2,7 +2,7 @@ // 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. -// ignore_for_file: undefined_setter +import 'package:mockito/mockito.dart'; import 'package:test/test.dart'; import 'package:http2/transport.dart'; @@ -14,8 +14,6 @@ import 'package:http2/src/flowcontrol/connection_queues.dart'; import 'package:http2/src/flowcontrol/stream_queues.dart'; import 'package:http2/src/flowcontrol/queue_messages.dart'; -import '../mock_utils.dart'; - main() { group('flowcontrol', () { test('connection-message-queue-out', () { @@ -31,73 +29,56 @@ main() { var bytes = [1, 2, 3]; // Send [HeadersMessage]. - var c = new TestCounter(); - fw.mock_writeHeadersFrame = - (int streamId, List
sendingHeaders, {bool endStream}) { - expect(streamId, 99); - expect(sendingHeaders, headers); - expect(endStream, false); - c.got(); - }; queue.enqueueMessage(new HeadersMessage(99, headers, false)); expect(queue.pendingMessages, 0); + verify(fw.writeHeadersFrame(99, headers, endStream: false)).called(1); + verifyNoMoreInteractions(fw); + verifyZeroInteractions(windowMock); - fw.mock_writeHeadersFrame = null; + clearInteractions(fw); // Send [DataMessage]. - c = new TestCounter(count: 2); windowMock.peerWindowSize = bytes.length; windowMock.positiveWindow.markUnBuffered(); - windowMock.mock_decreaseWindow = (int difference) { - expect(difference, bytes.length); - c.got(); - }; - fw.mock_writeDataFrame = - (int streamId, List
sendingBytes, {bool endStream}) { - expect(streamId, 99); - expect(sendingBytes, bytes); - expect(endStream, false); - c.got(); - }; queue.enqueueMessage(new DataMessage(99, bytes, false)); expect(queue.pendingMessages, 0); + verify(windowMock.decreaseWindow(bytes.length)).called(1); + verify(fw.writeDataFrame(99, bytes, endStream: false)).called(1); + verifyNoMoreInteractions(windowMock); + verifyNoMoreInteractions(fw); - fw.mock_writeDataFrame = null; + clearInteractions(fw); + clearInteractions(windowMock); // Send [DataMessage] if the connection window is too small. // Should trigger fragmentation and should write 1 byte. - c = new TestCounter(count: 2); windowMock.peerWindowSize = 1; - windowMock.mock_decreaseWindow = (int difference) { - expect(difference, 1); - c.got(); - }; - fw.mock_writeDataFrame = - (int streamId, List
sendingBytes, {bool endStream}) { - expect(streamId, 99); - expect(sendingBytes, bytes.sublist(0, 1)); - expect(endStream, false); - c.got(); - }; + // decreaseWindow() marks the window as buffered in this case, so we need + // our mock to do the same (otherwise, the call to markUnBuffered() below + // has no effect). + when(windowMock.decreaseWindow(1)).thenAnswer((_) { + windowMock.positiveWindow.markBuffered(); + }); queue.enqueueMessage(new DataMessage(99, bytes, true)); expect(queue.pendingMessages, 1); + verify(windowMock.decreaseWindow(1)).called(1); + verify(fw.writeDataFrame(99, bytes.sublist(0, 1), endStream: false)) + .called(1); + verifyNoMoreInteractions(windowMock); + verifyNoMoreInteractions(fw); + + clearInteractions(fw); + reset(windowMock); // Now mark it as unbuffered. This should write the rest of the // [bytes.length - 1] bytes. - c = new TestCounter(count: 2); - windowMock.mock_decreaseWindow = (int difference) { - expect(difference, bytes.length - 1); - c.got(); - }; - fw.mock_writeDataFrame = - (int streamId, List
sendingBytes, {bool endStream}) { - expect(streamId, 99); - expect(sendingBytes, bytes.sublist(1)); - expect(endStream, true); - c.got(); - }; windowMock.peerWindowSize = bytes.length - 1; windowMock.positiveWindow.markUnBuffered(); + verify(windowMock.decreaseWindow(bytes.length - 1)).called(1); + verify(fw.writeDataFrame(99, bytes.sublist(1), endStream: true)) + .called(1); + verifyNoMoreInteractions(windowMock); + verifyNoMoreInteractions(fw); queue.startClosing(); queue.done.then(expectAsync1((_) { @@ -121,29 +102,26 @@ main() { // Insert a [DataFrame] and let it be buffered. var header = new FrameHeader(0, 0, 0, STREAM_ID); - var c = new TestCounter(); - windowMock.mock_gotData = (int diff) { - expect(diff, bytes.length); - c.got(); - }; queue.processDataFrame(new DataFrame(header, 0, bytes)); expect(queue.pendingMessages, 1); + verify(windowMock.gotData(bytes.length)).called(1); + verifyNoMoreInteractions(windowMock); + verifyZeroInteractions(streamQueueMock); + + clearInteractions(windowMock); // Indicate that the stream queue has space, and make sure // the data is propagated from the connection to the stream // specific queue. - c = new TestCounter(count: 2); - windowMock.mock_gotData = null; - windowMock.mock_dataProcessed = (int diff) { - expect(diff, bytes.length); - c.got(); - }; - streamQueueMock.mock_enqueueMessage = (DataMessage message) { - expect(message.streamId, STREAM_ID); - expect(message.bytes, bytes); - c.got(); - }; streamQueueMock.bufferIndicator.markUnBuffered(); + verify(windowMock.dataProcessed(bytes.length)).called(1); + DataMessage capturedMessage = + verify(streamQueueMock.enqueueMessage(captureAny)).captured.single; + expect(capturedMessage.streamId, STREAM_ID); + expect(capturedMessage.bytes, bytes); + + verifyNoMoreInteractions(windowMock); + verifyNoMoreInteractions(streamQueueMock); // TODO: Write tests for adding HeadersFrame/PushPromiseFrame. }); @@ -157,30 +135,25 @@ main() { // Insert a [DataFrame] and let it be buffered. var header = new FrameHeader(0, 0, 0, STREAM_ID); - var c = new TestCounter(); - windowMock.mock_gotData = (int diff) { - expect(diff, bytes.length); - c.got(); - }; queue.processIgnoredDataFrame(new DataFrame(header, 0, bytes)); expect(queue.pendingMessages, 0); + verify(windowMock.gotData(bytes.length)).called(1); + verifyNoMoreInteractions(windowMock); }); }); } -class MockFrameWriter extends SmartMock implements FrameWriter { +class MockFrameWriter extends Mock implements FrameWriter { BufferIndicator bufferIndicator = new BufferIndicator(); } -class MockStreamMessageQueueIn extends SmartMock - implements StreamMessageQueueIn { +class MockStreamMessageQueueIn extends Mock implements StreamMessageQueueIn { BufferIndicator bufferIndicator = new BufferIndicator(); } -class MockIncomingWindowHandler extends SmartMock - implements IncomingWindowHandler {} +class MockIncomingWindowHandler extends Mock implements IncomingWindowHandler {} -class MockOutgoingWindowHandler extends SmartMock +class MockOutgoingWindowHandler extends Mock implements OutgoingConnectionWindowHandler, OutgoingStreamWindowHandler { BufferIndicator positiveWindow = new BufferIndicator(); int peerWindowSize = new Window().size; diff --git a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart index b5b08448de..ce0ad8b68e 100644 --- a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart @@ -2,7 +2,7 @@ // 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. -// ignore_for_file: undefined_setter +import 'package:mockito/mockito.dart'; import 'package:test/test.dart'; import 'package:http2/transport.dart'; @@ -12,8 +12,6 @@ import 'package:http2/src/flowcontrol/stream_queues.dart'; import 'package:http2/src/flowcontrol/window_handler.dart'; import 'package:http2/src/flowcontrol/connection_queues.dart'; -import '../mock_utils.dart'; - main() { group('flowcontrol', () { const STREAM_ID = 99; @@ -32,17 +30,16 @@ main() { expect(queue.pendingMessages, 0); windowMock.peerWindowSize = BYTES.length; - windowMock.mock_decreaseWindow = expectAsync1((int difference) { - expect(difference, BYTES.length); - }); - connectionQueueMock.mock_enqueueMessage = - expectAsync1((Message message) { - expect(message is DataMessage, isTrue); - DataMessage dataMessage = message; - expect(dataMessage.bytes, BYTES); - expect(dataMessage.endStream, isTrue); - }); queue.enqueueMessage(new DataMessage(STREAM_ID, BYTES, true)); + verify(windowMock.decreaseWindow(BYTES.length)).called(1); + final capturedMessage = + verify(connectionQueueMock.enqueueMessage(captureAny)) + .captured + .single; + expect(capturedMessage, new isInstanceOf()); + DataMessage capturedDataMessage = capturedMessage; + expect(capturedDataMessage.bytes, BYTES); + expect(capturedDataMessage.endStream, isTrue); }); test('window-smaller-than-necessary', () { @@ -59,21 +56,20 @@ main() { // We set the window size fixed to 1, which means all the data messages // will get fragmented to 1 byte. windowMock.peerWindowSize = 1; - windowMock.mock_decreaseWindow = expectAsync1((int difference) { - expect(difference, 1); - }, count: BYTES.length); - int counter = 0; - connectionQueueMock.mock_enqueueMessage = - expectAsync1((Message message) { - expect(message is DataMessage, isTrue); - DataMessage dataMessage = message; - expect(dataMessage.bytes, BYTES.sublist(counter, counter + 1)); - counter++; - expect(dataMessage.endStream, counter == BYTES.length); - }, count: BYTES.length); queue.enqueueMessage(new DataMessage(STREAM_ID, BYTES, true)); expect(queue.pendingMessages, 0); + verify(windowMock.decreaseWindow(1)).called(BYTES.length); + final messages = + verify(connectionQueueMock.enqueueMessage(captureAny)).captured; + expect(messages.length, BYTES.length); + for (var counter = 0; counter < messages.length; counter++) { + expect(messages[counter], new isInstanceOf()); + DataMessage dataMessage = messages[counter]; + expect(dataMessage.bytes, BYTES.sublist(counter, counter + 1)); + expect(dataMessage.endStream, counter == BYTES.length - 1); + } + verifyNoMoreInteractions(windowMock); }); test('window-empty', () { @@ -88,12 +84,11 @@ main() { expect(queue.pendingMessages, 0); windowMock.peerWindowSize = 0; - windowMock.mock_decreaseWindow = expectAsync1((_) {}, count: 0); - connectionQueueMock.mock_enqueueMessage = - expectAsync1((_) {}, count: 0); queue.enqueueMessage(new DataMessage(STREAM_ID, BYTES, true)); expect(queue.bufferIndicator.wouldBuffer, isTrue); expect(queue.pendingMessages, 1); + verifyZeroInteractions(windowMock); + verifyZeroInteractions(connectionQueueMock); }); }); @@ -109,14 +104,13 @@ main() { DataStreamMessage dataMessage = message; expect(dataMessage.bytes, BYTES); }), onDone: expectAsync0(() {})); - windowMock.mock_gotData = expectAsync1((int difference) { - expect(difference, BYTES.length); - }); - windowMock.mock_dataProcessed = expectAsync1((int difference) { - expect(difference, BYTES.length); - }); queue.enqueueMessage(new DataMessage(STREAM_ID, BYTES, true)); expect(queue.bufferIndicator.wouldBuffer, isFalse); + verifyInOrder([ + windowMock.gotData(BYTES.length), + windowMock.dataProcessed(BYTES.length) + ]); + verifyNoMoreInteractions(windowMock); }); }); @@ -131,29 +125,25 @@ main() { onDone: expectAsync0(() {}, count: 0)); sub.pause(); - // We assert that we got the data, but it wasn't processed. - windowMock.mock_gotData = expectAsync1((int difference) { - expect(difference, bytes.length); - }); - windowMock.mock_dataProcessed = expectAsync1((_) {}, count: 0); - expect(queue.pendingMessages, 0); queue.enqueueMessage(new DataMessage(STREAM_ID, bytes, true)); expect(queue.pendingMessages, 1); expect(queue.bufferIndicator.wouldBuffer, isTrue); + // We assert that we got the data, but it wasn't processed. + verify(windowMock.gotData(bytes.length)).called(1); + verifyNever(windowMock.dataProcessed(any)); }); // TODO: Add tests for Headers/HeadersPush messages. }); } -class MockConnectionMessageQueueOut extends SmartMock +class MockConnectionMessageQueueOut extends Mock implements ConnectionMessageQueueOut {} -class MockIncomingWindowHandler extends SmartMock - implements IncomingWindowHandler {} +class MockIncomingWindowHandler extends Mock implements IncomingWindowHandler {} -class MockOutgoingStreamWindowHandler extends SmartMock +class MockOutgoingStreamWindowHandler extends Mock implements OutgoingStreamWindowHandler { final BufferIndicator positiveWindow = new BufferIndicator(); int peerWindowSize; diff --git a/pkgs/http2/test/src/flowcontrol/window_handler_test.dart b/pkgs/http2/test/src/flowcontrol/window_handler_test.dart index ecb61fd4fb..22d8fe6442 100644 --- a/pkgs/http2/test/src/flowcontrol/window_handler_test.dart +++ b/pkgs/http2/test/src/flowcontrol/window_handler_test.dart @@ -2,7 +2,7 @@ // 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. -// ignore_for_file: undefined_setter +import 'package:mockito/mockito.dart'; import 'package:test/test.dart'; import 'package:http2/src/flowcontrol/window.dart'; @@ -10,7 +10,6 @@ import 'package:http2/src/flowcontrol/window_handler.dart'; import 'package:http2/src/frames/frames.dart'; import '../error_matchers.dart'; -import '../mock_utils.dart'; main() { group('flowcontrol', () { @@ -121,17 +120,13 @@ main() { // 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. - var c = new TestCounter(); - fw.mock_writeWindowUpdate = (int numberOfBytes, {int streamId}) { - expect(numberOfBytes, 100); - expect(streamId, STREAM_ID); - c.got(); - }; 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 SmartMock implements FrameWriter {} +class FrameWriterMock extends Mock implements FrameWriter {} diff --git a/pkgs/http2/test/src/mock_utils.dart b/pkgs/http2/test/src/mock_utils.dart deleted file mode 100644 index 992210ce6b..0000000000 --- a/pkgs/http2/test/src/mock_utils.dart +++ /dev/null @@ -1,90 +0,0 @@ -// 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. - -library http2.test.mock_utils; - -import 'dart:mirrors'; - -import 'package:test/test.dart'; - -/// Used for mocking arbitrary classes. -/// -/// Usage happens in two steps: -/// a) Make a subclass which implements a certain interface, e.g. -/// class FrameWriterMock extends SmartMock implements FrameWriter { -/// dynamic noSuchMethod(_) => super.noSuchMethod(_); -/// } -/// b) Register method mocks with e.g. -/// var writer = new FrameWriterMock(); -/// writer.mock_writeSettingsFrame = (List settings, -/// {bool ack: true}) { -/// // Assert settings/ack & return maybe value. -/// } -/// var settingsHandler = new SettingsHandler(hpackEncoder, writer); -/// -/// // This will trigger a call on [writer] to the mocked method. -/// settingsHandler.changeSettings([]); -/// -/// a) should guarantee that we do not get any checked-mode exceptions. -/// b) allows one to pass the mock into functions/other objects and mock -/// methods. -/// -/// NOTE: If method signatures change, the test code must be changed and -/// the analyzer will not give any warnings if this is not done. -class SmartMock { - final Map _registeredMethods = {}; - - dynamic noSuchMethod(Invocation invocation) { - var name = MirrorSystem.getName(invocation.memberName); - var positional = invocation.positionalArguments; - var named = invocation.namedArguments; - - handleCall() { - var function = _registeredMethods[name]; - if (function == null) { - throw new Exception('No mock registered for setter "$name".'); - } - return Function.apply(function, positional, named); - } - - handleRegistration(String name) { - if (positional[0] == null) { - _registeredMethods.remove(name); - } else { - _registeredMethods[name] = positional[0]; - } - } - - if (invocation.isSetter) { - if (name.startsWith('mock_')) { - name = name.substring('mock_'.length, name.length - 1); - return handleRegistration(name); - } else { - return handleCall(); - } - } else { - return handleCall(); - } - } -} - -/// This is a helper class for working around issues where `expectAsync` -/// cannot be used. -/// -/// For example, expectAsync is not capable for wrapping functions with named -/// arguments -/// (e.g. `expectAsync((List settings, {bool ack: true}) {})`). -class TestCounter { - final Function _complete = expectAsync0(() {}); - - final int count; - int _got = 0; - - TestCounter({this.count: 1}); - - void got() { - _got++; - if (_got == count) _complete(); - } -} diff --git a/pkgs/http2/test/src/ping/ping_handler_test.dart b/pkgs/http2/test/src/ping/ping_handler_test.dart index d1ebedf5e9..605652a3d9 100644 --- a/pkgs/http2/test/src/ping/ping_handler_test.dart +++ b/pkgs/http2/test/src/ping/ping_handler_test.dart @@ -2,35 +2,30 @@ // 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. -// ignore_for_file: undefined_setter import 'dart:async'; +import 'package:mockito/mockito.dart'; import 'package:test/test.dart'; import 'package:http2/src/frames/frames.dart'; import 'package:http2/src/ping/ping_handler.dart'; import '../error_matchers.dart'; -import '../mock_utils.dart'; main() { group('ping-handler', () { test('successful-ping', () async { var writer = new FrameWriterMock(); var pingHandler = new PingHandler(writer); - var tc = new TestCounter(count: 2); - - int pingCount = 1; - writer.mock_writePingFrame = (int opaqueData, {bool ack: false}) { - expect(opaqueData, pingCount); - expect(ack, false); - pingCount++; - tc.got(); - }; Future p1 = pingHandler.ping(); Future p2 = pingHandler.ping(); + verifyInOrder([ + writer.writePingFrame(1), + writer.writePingFrame(2), + ]); + var header = new FrameHeader(8, FrameType.PING, PingFrame.FLAG_ACK, 0); pingHandler.processPingFrame(new PingFrame(header, 1)); var header2 = new FrameHeader(8, FrameType.PING, PingFrame.FLAG_ACK, 0); @@ -38,38 +33,31 @@ main() { await p1; await p2; + verifyNoMoreInteractions(writer); }); test('successful-ack-to-remote-ping', () async { var writer = new FrameWriterMock(); var pingHandler = new PingHandler(writer); - var tc = new TestCounter(count: 2); - - int pingCount = 1; - writer.mock_writePingFrame = (int opaqueData, {bool ack: false}) { - expect(opaqueData, pingCount); - expect(ack, true); - pingCount++; - tc.got(); - }; var header = new FrameHeader(8, FrameType.PING, 0, 0); pingHandler.processPingFrame(new PingFrame(header, 1)); var header2 = new FrameHeader(8, FrameType.PING, 0, 0); pingHandler.processPingFrame(new PingFrame(header2, 2)); + + verifyInOrder([ + writer.writePingFrame(1, ack: true), + writer.writePingFrame(2, ack: true) + ]); + verifyNoMoreInteractions(writer); }); test('ping-unknown-opaque-data', () async { var writer = new FrameWriterMock(); var pingHandler = new PingHandler(writer); - var tc = new TestCounter(); - - writer.mock_writePingFrame = (int opaqueData, {bool ack: false}) { - expect(opaqueData, 1); - tc.got(); - }; Future future = pingHandler.ping(); + verify(writer.writePingFrame(1)).called(1); var header = new FrameHeader(8, FrameType.PING, PingFrame.FLAG_ACK, 0); expect(() => pingHandler.processPingFrame(new PingFrame(header, 2)), @@ -81,6 +69,7 @@ main() { expect(error, 'hello world'); })); pingHandler.terminate('hello world'); + verifyNoMoreInteractions(writer); }); test('terminate-ping-handler', () async { @@ -91,6 +80,7 @@ main() { expect(() => pingHandler.processPingFrame(null), throwsA(isTerminatedException)); expect(pingHandler.ping(), throwsA(isTerminatedException)); + verifyZeroInteractions(writer); }); test('ping-non-zero-stream-id', () async { @@ -100,8 +90,9 @@ main() { var header = new FrameHeader(8, FrameType.PING, PingFrame.FLAG_ACK, 1); expect(() => pingHandler.processPingFrame(new PingFrame(header, 1)), throwsA(isProtocolException)); + verifyZeroInteractions(writer); }); }); } -class FrameWriterMock extends SmartMock implements FrameWriter {} +class FrameWriterMock extends Mock implements FrameWriter {} diff --git a/pkgs/http2/test/src/settings/settings_handler_test.dart b/pkgs/http2/test/src/settings/settings_handler_test.dart index 7039745276..62f5f54427 100644 --- a/pkgs/http2/test/src/settings/settings_handler_test.dart +++ b/pkgs/http2/test/src/settings/settings_handler_test.dart @@ -2,9 +2,9 @@ // 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. -// ignore_for_file: undefined_setter import 'dart:async'; +import 'package:mockito/mockito.dart'; import 'package:test/test.dart'; import 'package:http2/src/frames/frames.dart'; @@ -12,7 +12,6 @@ import 'package:http2/src/hpack/hpack.dart'; import 'package:http2/src/settings/settings.dart'; import '../error_matchers.dart'; -import '../mock_utils.dart'; main() { group('settings-handler', () { @@ -24,21 +23,16 @@ main() { var writer = new FrameWriterMock(); var sh = new SettingsHandler(new HPackEncoder(), writer, new ActiveSettings(), new ActiveSettings()); - var tc = new TestCounter(); - - writer.mock_writeSettingsFrame = (List s, {bool ack: false}) { - expect(s, pushSettings); - expect(ack, false); - tc.got(); - }; // Start changing settings. Future changed = sh.changeSettings(pushSettings); + verify(writer.writeSettingsFrame(pushSettings)).called(1); + verifyNoMoreInteractions(writer); // Check that settings haven't been applied. expect(sh.acknowledgedSettings.enablePush, true); - // Simulate remote end to responsd with an ACK. + // Simulate remote end to respond with an ACK. var header = new FrameHeader(0, FrameType.SETTINGS, SettingsFrame.FLAG_ACK, 0); sh.handleSettingsFrame(new SettingsFrame(header, [])); @@ -53,11 +47,6 @@ main() { var writer = new FrameWriterMock(); var sh = new SettingsHandler(new HPackEncoder(), writer, new ActiveSettings(), new ActiveSettings()); - var tc = new TestCounter(); - - writer.mock_writeSettingsAckFrame = () { - tc.got(); - }; // Check that settings haven't been applied. expect(sh.peerSettings.enablePush, true); @@ -68,6 +57,8 @@ main() { // Check that settings have been applied. expect(sh.peerSettings.enablePush, false); + verify(writer.writeSettingsAckFrame()).called(1); + verifyNoMoreInteractions(writer); }); test('invalid-remote-ack', () { @@ -82,6 +73,7 @@ main() { expect(() => sh.handleSettingsFrame(settingsFrame), throwsA(isProtocolException)); + verifyZeroInteractions(writer); }); test('invalid-remote-settings-change', () { @@ -97,6 +89,7 @@ main() { var settingsFrame = new SettingsFrame(header, invalidPushSettings); expect(() => sh.handleSettingsFrame(settingsFrame), throwsA(isProtocolException)); + verifyZeroInteractions(writer); }); test('change-max-header-table-size', () { @@ -108,15 +101,15 @@ main() { // Simulate remote end by setting the push setting. var header = new FrameHeader(6, FrameType.SETTINGS, 0, 0); var settingsFrame = new SettingsFrame(header, setMaxTable256); - mock.mock_updateMaxSendingHeaderTableSize = expectAsync1((int newSize) { - expect(newSize, 256); - }); - writer.mock_writeSettingsAckFrame = expectAsync0(() {}); sh.handleSettingsFrame(settingsFrame); + verify(mock.updateMaxSendingHeaderTableSize(256)).called(1); + verify(writer.writeSettingsAckFrame()).called(1); + verifyNoMoreInteractions(mock); + verifyNoMoreInteractions(writer); }); }); } -class FrameWriterMock extends SmartMock implements FrameWriter {} +class FrameWriterMock extends Mock implements FrameWriter {} -class HPackEncoderMock extends SmartMock implements HPackEncoder {} +class HPackEncoderMock extends Mock implements HPackEncoder {} From 5e1e377e11aac4861b210dd856c94484fc555c61 Mon Sep 17 00:00:00 2001 From: Jakob Andersen Date: Fri, 3 Nov 2017 13:49:29 +0100 Subject: [PATCH 065/172] Lower mockito dependency to 2.0.0 (dart-lang/http2#19) Mockito v2.2.0 has a SDK constraint that isn't compatible with bleeding edge Dart SDK. v2.0.0 looks fine, and we don't depend on anything new from 2.2.0 anyway. --- pkgs/http2/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index e208249868..8dd7689904 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -8,5 +8,5 @@ environment: sdk: '>=1.24.0 <2.0.0' dev_dependencies: - mockito: ^2.2.0 + mockito: ^2.0.0 test: '>=0.12.0 <0.13.0' From 0531af642bbee3ef98609e8ff34373e0d3c23c40 Mon Sep 17 00:00:00 2001 From: Jakob Andersen Date: Sat, 23 Dec 2017 23:10:51 +0100 Subject: [PATCH 066/172] Use StreamView instead of StreamMethodsMixin. (dart-lang/http2#20) Preparation for upcoming changes to Stream in Dart 2.0. Instead of having our own mixin implementing the Stream interface, let's just use the provided StreamView wrapper. --- .../lib/src/artificial_server_socket.dart | 110 +----------------- pkgs/http2/lib/transport.dart | 4 +- 2 files changed, 5 insertions(+), 109 deletions(-) diff --git a/pkgs/http2/lib/src/artificial_server_socket.dart b/pkgs/http2/lib/src/artificial_server_socket.dart index 29314da1af..4c5cf07b6f 100644 --- a/pkgs/http2/lib/src/artificial_server_socket.dart +++ b/pkgs/http2/lib/src/artificial_server_socket.dart @@ -13,12 +13,10 @@ import 'dart:io'; /// a [InternetAddress] and `port` (an example use case is to filter [Socket]s /// and keep the [ServerSocket] interface for APIs that expect it, /// e.g. `new HttpServer.listenOn()`). -class ArtificialServerSocket extends Object - with StreamMethodsMixin +class ArtificialServerSocket extends StreamView implements ServerSocket { - final Stream _stream; - - ArtificialServerSocket(this.address, this.port, this._stream); + ArtificialServerSocket(this.address, this.port, Stream stream) + : super(stream); // ######################################################################## // These are the methods of [ServerSocket] in addition to [Stream]. @@ -34,105 +32,3 @@ class ArtificialServerSocket extends Object throw new Exception("Did not expect close() to be called."); } } - -/// A mixin used to implement the [Stream] interface. -/// -/// This class can be used as a mixin and will delegate all methods on [Stream] -/// to another [_stream] instance. -abstract class StreamMethodsMixin implements Stream { - Stream get _stream; - - Future any(bool test(T element)) => _stream.any(test); - - Stream asBroadcastStream( - {void onListen(StreamSubscription subscription), - void onCancel(StreamSubscription subscription)}) { - return _stream.asBroadcastStream(onListen: onListen, onCancel: onCancel); - } - - Stream asyncExpand(Stream convert(T value)) => - _stream.asyncExpand(convert); - - Stream asyncMap(FutureOr convert(T event)) => - _stream.asyncMap(convert); - - Future contains(Object needle) => _stream.contains(needle); - - Stream distinct([bool equals(T previous, T next)]) { - return _stream.distinct(equals); - } - - Future drain([T futureValue]) => _stream.drain(); - - Future elementAt(int index) => _stream.elementAt(index); - - Future every(bool test(T item)) => _stream.every(test); - - Stream expand(Iterable convert(T item)) => _stream.expand(convert); - - Future get first => _stream.first; - - Future firstWhere(bool test(T element), {Object defaultValue()}) { - return _stream.firstWhere(test); - } - - Future fold(S initialValue, S combine(S previous, T element)) { - return _stream.fold(initialValue, combine); - } - - Future forEach(void action(T element)) => _stream.forEach(action); - - Stream handleError(Function onError, {bool test(error)}) { - return _stream.handleError(onError, test: test); - } - - bool get isBroadcast => _stream.isBroadcast; - - Future get isEmpty => _stream.isEmpty; - - Future join([String separator = ""]) => _stream.join(separator); - - Future get last => _stream.last; - - Future lastWhere(bool test(T element), {Object defaultValue()}) { - return _stream.lastWhere(test, defaultValue: defaultValue); - } - - Future get length => _stream.length; - - StreamSubscription listen(void onData(T event), - {Function onError, void onDone(), bool cancelOnError}) { - return _stream.listen(onData, - onError: onError, onDone: onDone, cancelOnError: cancelOnError); - } - - Stream map(S convert(T event)) => _stream.map(convert); - - Future pipe(StreamConsumer consumer) => _stream.pipe(consumer); - - Future reduce(T combine(T previous, T element)) => _stream.reduce(combine); - - Future get single => _stream.single; - - Future singleWhere(bool test(T item)) => _stream.singleWhere(test); - - Stream skip(int count) => _stream.skip(count); - - Stream skipWhile(bool test(T item)) => _stream.skipWhile(test); - - Stream take(int count) => _stream.take(count); - - Stream takeWhile(bool test(T item)) => _stream.takeWhile(test); - - Stream timeout(Duration timeLimit, {void onTimeout(EventSink sink)}) => - _stream.timeout(timeLimit, onTimeout: onTimeout); - - Future> toList() => _stream.toList(); - - Future> toSet() => _stream.toSet(); - - Stream transform(StreamTransformer streamTransformer) => - _stream.transform(streamTransformer); - - Stream where(bool test(T value)) => _stream.where(test); -} diff --git a/pkgs/http2/lib/transport.dart b/pkgs/http2/lib/transport.dart index ce5120ae46..e19c90197c 100644 --- a/pkgs/http2/lib/transport.dart +++ b/pkgs/http2/lib/transport.dart @@ -150,9 +150,9 @@ abstract class TransportConnection { /// Sets the active state callback. /// - /// This callback is invoked with [true] when the number of active streams + /// This callback is invoked with `true` when the number of active streams /// goes from 0 to 1 (the connection goes from idle to active), and with - /// [false] when the number of active streams becomes 0 (the connection goes + /// `false` when the number of active streams becomes 0 (the connection goes /// from active to idle). set onActiveStateChanged(ActiveStateHandler callback); From ae5c18431f04c5f23c6d732843dd429b8c89a395 Mon Sep 17 00:00:00 2001 From: Jakob Andersen Date: Thu, 11 Jan 2018 15:27:26 +0100 Subject: [PATCH 067/172] Release 0.1.7 (dart-lang/http2#21) --- pkgs/http2/CHANGELOG.md | 4 ++++ pkgs/http2/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index 40f5528d9b..65c5570dbe 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 0.1.7 + +* Fixes for Dart 2.0. + ## 0.1.6 * Strong mode fixes and other cleanup. diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index 8dd7689904..c41879ae85 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -1,5 +1,5 @@ name: http2 -version: 0.1.6 +version: 0.1.7 description: A HTTP/2 implementation in Dart. author: Dart Team homepage: https://github.com/dart-lang/http2 From 3a3fac5b4c94fc5e2a139c1c23988f4bdfd244cd Mon Sep 17 00:00:00 2001 From: Michael R Fairhurst Date: Tue, 20 Feb 2018 00:48:11 -0800 Subject: [PATCH 068/172] Changes for landing https://github.com/dart-lang/sdk/issues/32161 (dart-lang/http2#22) Add void declarations to methods with implicit dynamic returning void values, which may be illegal in dart 2, but in either case, expresses the current intent better. --- pkgs/http2/test/client_test.dart | 2 +- pkgs/http2/test/server_test.dart | 2 +- pkgs/http2/test/src/streams/helper.dart | 4 ++-- pkgs/http2/test/transport_test.dart | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pkgs/http2/test/client_test.dart b/pkgs/http2/test/client_test.dart index c7cb6e126a..d4bad2f54a 100644 --- a/pkgs/http2/test/client_test.dart +++ b/pkgs/http2/test/client_test.dart @@ -526,7 +526,7 @@ main() { }); } -clientTest( +void clientTest( String name, Future func( ClientTransportConnection clientConnection, diff --git a/pkgs/http2/test/server_test.dart b/pkgs/http2/test/server_test.dart index 49b8ce7096..7cab2c7c17 100644 --- a/pkgs/http2/test/server_test.dart +++ b/pkgs/http2/test/server_test.dart @@ -194,7 +194,7 @@ main() { }); } -serverTest( +void serverTest( String name, func(ServerTransportConnection serverConnection, FrameWriter frameWriter, StreamIterator frameReader, Future readNext())) { diff --git a/pkgs/http2/test/src/streams/helper.dart b/pkgs/http2/test/src/streams/helper.dart index 897d425448..8d7d331a17 100644 --- a/pkgs/http2/test/src/streams/helper.dart +++ b/pkgs/http2/test/src/streams/helper.dart @@ -24,7 +24,7 @@ expectEmptyStream(Stream s) { s.listen(expectAsync1((_) {}, count: 0), onDone: expectAsync0(() {})); } -streamTest(String name, +void streamTest(String name, func(ClientTransportConnection client, ServerTransportConnection server), {ClientSettings settings}) { return test(name, () { @@ -36,7 +36,7 @@ streamTest(String name, }); } -framesTest(String name, func(frameWriter, frameStream)) { +void framesTest(String name, func(frameWriter, frameStream)) { return test(name, () { var c = new StreamController>(); var fw = new FrameWriter(null, c, new ActiveSettings()); diff --git a/pkgs/http2/test/transport_test.dart b/pkgs/http2/test/transport_test.dart index f301fe8713..c9c32d96a2 100644 --- a/pkgs/http2/test/transport_test.dart +++ b/pkgs/http2/test/transport_test.dart @@ -469,7 +469,7 @@ main() { }); } -transportTest(String name, +void transportTest(String name, func(ClientTransportConnection client, ServerTransportConnection server), {ClientSettings clientSettings, ServerSettings serverSettings}) { return test(name, () { From 487dbfa46b94d99f135b2dc0ff509a1dadce26da Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Wed, 21 Mar 2018 16:10:26 +0100 Subject: [PATCH 069/172] Remaining changes to make tests pass under a 2.0 VM (dart-lang/http2#23) --- pkgs/http2/CHANGELOG.md | 5 ++++ pkgs/http2/lib/src/connection.dart | 3 ++- .../manual_test/out_of_stream_ids_test.dart | 2 +- pkgs/http2/pubspec.yaml | 6 ++--- .../flowcontrol/connection_queues_test.dart | 14 ++++++----- .../src/flowcontrol/stream_queues_test.dart | 23 ++++++++++--------- .../src/flowcontrol/window_handler_test.dart | 2 +- .../test/src/ping/ping_handler_test.dart | 6 ++--- .../src/settings/settings_handler_test.dart | 8 +++---- .../test/src/streams/simple_push_test.dart | 5 ++-- 10 files changed, 41 insertions(+), 33 deletions(-) diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index 65c5570dbe..30ad1ad1d7 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.1.8 + +* More changes required for making tests pass under Dart 2.0 runtime. +* Modify sdk constraint to require '>=2.0.0-dev.40.0'. + ## 0.1.7 * Fixes for Dart 2.0. diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index 958efc9a22..e10b649cb9 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -451,5 +451,6 @@ class ServerConnection extends Connection implements ServerTransportConnection { return new ServerConnection._(frameBytes, outgoing, serverSettings); } - Stream get incomingStreams => _streams.incomingStreams; + Stream get incomingStreams => + _streams.incomingStreams.cast(); } diff --git a/pkgs/http2/manual_test/out_of_stream_ids_test.dart b/pkgs/http2/manual_test/out_of_stream_ids_test.dart index bddb1462c5..f289f86adc 100644 --- a/pkgs/http2/manual_test/out_of_stream_ids_test.dart +++ b/pkgs/http2/manual_test/out_of_stream_ids_test.dart @@ -34,7 +34,7 @@ main() { Future clientFun() async { var headers = [new Header.ascii('a', 'b')]; - const kMaxStreamId = (1 << 5) - 1; + const kMaxStreamId = (1 << 31) - 1; for (int i = 1; i <= kMaxStreamId; i += 2) { var stream = client.makeRequest(headers, endStream: true); var messages = await stream.incomingMessages.toList(); diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index c41879ae85..32a7ad1ba9 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -1,12 +1,12 @@ name: http2 -version: 0.1.7 +version: 0.1.8 description: A HTTP/2 implementation in Dart. author: Dart Team homepage: https://github.com/dart-lang/http2 environment: - sdk: '>=1.24.0 <2.0.0' + sdk: '>=2.0.0-dev.40.0' dev_dependencies: - mockito: ^2.0.0 + mockito: ^3.0.0-alpha test: '>=0.12.0 <0.13.0' diff --git a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart index 5a0c53c2c0..807c810a1b 100644 --- a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart @@ -17,8 +17,8 @@ import 'package:http2/src/flowcontrol/queue_messages.dart'; main() { group('flowcontrol', () { test('connection-message-queue-out', () { - var fw = new MockFrameWriter(); - var windowMock = new MockOutgoingWindowHandler(); + dynamic fw = new MockFrameWriter(); + dynamic windowMock = new MockOutgoingWindowHandler(); var queue = new ConnectionMessageQueueOut(windowMock, fw); fw.bufferIndicator.markUnBuffered(); @@ -92,12 +92,12 @@ main() { const STREAM_ID = 99; final bytes = [1, 2, 3]; - var windowMock = new MockIncomingWindowHandler(); + dynamic windowMock = new MockIncomingWindowHandler(); var queue = new ConnectionMessageQueueIn(windowMock, (f) => f()); expect(queue.pendingMessages, 0); - var streamQueueMock = new MockStreamMessageQueueIn(); + dynamic streamQueueMock = new MockStreamMessageQueueIn(); queue.insertNewStreamMessageQueue(STREAM_ID, streamQueueMock); // Insert a [DataFrame] and let it be buffered. @@ -116,7 +116,9 @@ main() { streamQueueMock.bufferIndicator.markUnBuffered(); verify(windowMock.dataProcessed(bytes.length)).called(1); DataMessage capturedMessage = - verify(streamQueueMock.enqueueMessage(captureAny)).captured.single; + verify(streamQueueMock.enqueueMessage(typed(captureAny))) + .captured + .single; expect(capturedMessage.streamId, STREAM_ID); expect(capturedMessage.bytes, bytes); @@ -130,7 +132,7 @@ main() { const STREAM_ID = 99; final bytes = [1, 2, 3]; - var windowMock = new MockIncomingWindowHandler(); + dynamic windowMock = new MockIncomingWindowHandler(); var queue = new ConnectionMessageQueueIn(windowMock, (f) => f()); // Insert a [DataFrame] and let it be buffered. diff --git a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart index ce0ad8b68e..44e21a4ff0 100644 --- a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart @@ -19,8 +19,8 @@ main() { group('stream-message-queue-out', () { test('window-big-enough', () { - var connectionQueueMock = new MockConnectionMessageQueueOut(); - var windowMock = new MockOutgoingStreamWindowHandler(); + dynamic connectionQueueMock = new MockConnectionMessageQueueOut(); + dynamic windowMock = new MockOutgoingStreamWindowHandler(); windowMock.positiveWindow.markUnBuffered(); var queue = new StreamMessageQueueOut( @@ -33,7 +33,7 @@ main() { queue.enqueueMessage(new DataMessage(STREAM_ID, BYTES, true)); verify(windowMock.decreaseWindow(BYTES.length)).called(1); final capturedMessage = - verify(connectionQueueMock.enqueueMessage(captureAny)) + verify(connectionQueueMock.enqueueMessage(typed(captureAny))) .captured .single; expect(capturedMessage, new isInstanceOf()); @@ -43,8 +43,8 @@ main() { }); test('window-smaller-than-necessary', () { - var connectionQueueMock = new MockConnectionMessageQueueOut(); - var windowMock = new MockOutgoingStreamWindowHandler(); + dynamic connectionQueueMock = new MockConnectionMessageQueueOut(); + dynamic windowMock = new MockOutgoingStreamWindowHandler(); windowMock.positiveWindow.markUnBuffered(); var queue = new StreamMessageQueueOut( @@ -61,7 +61,8 @@ main() { expect(queue.pendingMessages, 0); verify(windowMock.decreaseWindow(1)).called(BYTES.length); final messages = - verify(connectionQueueMock.enqueueMessage(captureAny)).captured; + verify(connectionQueueMock.enqueueMessage(typed(captureAny))) + .captured; expect(messages.length, BYTES.length); for (var counter = 0; counter < messages.length; counter++) { expect(messages[counter], new isInstanceOf()); @@ -94,8 +95,8 @@ main() { group('stream-message-queue-in', () { test('data-end-of-stream', () { - var windowMock = new MockIncomingWindowHandler(); - var queue = new StreamMessageQueueIn(windowMock); + dynamic windowMock = new MockIncomingWindowHandler(); + dynamic queue = new StreamMessageQueueIn(windowMock); expect(queue.pendingMessages, 0); queue.messages.listen(expectAsync1((StreamMessage message) { @@ -118,8 +119,8 @@ main() { const STREAM_ID = 99; final bytes = [1, 2, 3]; - var windowMock = new MockIncomingWindowHandler(); - var queue = new StreamMessageQueueIn(windowMock); + dynamic windowMock = new MockIncomingWindowHandler(); + dynamic queue = new StreamMessageQueueIn(windowMock); var sub = queue.messages.listen(expectAsync1((_) {}, count: 0), onDone: expectAsync0(() {}, count: 0)); @@ -131,7 +132,7 @@ main() { expect(queue.bufferIndicator.wouldBuffer, isTrue); // We assert that we got the data, but it wasn't processed. verify(windowMock.gotData(bytes.length)).called(1); - verifyNever(windowMock.dataProcessed(any)); + verifyNever(windowMock.dataProcessed(typed(any))); }); // TODO: Add tests for Headers/HeadersPush messages. diff --git a/pkgs/http2/test/src/flowcontrol/window_handler_test.dart b/pkgs/http2/test/src/flowcontrol/window_handler_test.dart index 22d8fe6442..53e66a14e0 100644 --- a/pkgs/http2/test/src/flowcontrol/window_handler_test.dart +++ b/pkgs/http2/test/src/flowcontrol/window_handler_test.dart @@ -102,7 +102,7 @@ main() { test('incoming-window-handler', () { const STREAM_ID = 99; - var fw = new FrameWriterMock(); + dynamic fw = new FrameWriterMock(); var window = new Window(); int initialSize = window.size; var handler = new IncomingWindowHandler.stream(fw, window, STREAM_ID); diff --git a/pkgs/http2/test/src/ping/ping_handler_test.dart b/pkgs/http2/test/src/ping/ping_handler_test.dart index 605652a3d9..85b0306f7f 100644 --- a/pkgs/http2/test/src/ping/ping_handler_test.dart +++ b/pkgs/http2/test/src/ping/ping_handler_test.dart @@ -15,7 +15,7 @@ import '../error_matchers.dart'; main() { group('ping-handler', () { test('successful-ping', () async { - var writer = new FrameWriterMock(); + dynamic writer = new FrameWriterMock(); var pingHandler = new PingHandler(writer); Future p1 = pingHandler.ping(); @@ -37,7 +37,7 @@ main() { }); test('successful-ack-to-remote-ping', () async { - var writer = new FrameWriterMock(); + dynamic writer = new FrameWriterMock(); var pingHandler = new PingHandler(writer); var header = new FrameHeader(8, FrameType.PING, 0, 0); @@ -53,7 +53,7 @@ main() { }); test('ping-unknown-opaque-data', () async { - var writer = new FrameWriterMock(); + dynamic writer = new FrameWriterMock(); var pingHandler = new PingHandler(writer); Future future = pingHandler.ping(); diff --git a/pkgs/http2/test/src/settings/settings_handler_test.dart b/pkgs/http2/test/src/settings/settings_handler_test.dart index 62f5f54427..774503a1ee 100644 --- a/pkgs/http2/test/src/settings/settings_handler_test.dart +++ b/pkgs/http2/test/src/settings/settings_handler_test.dart @@ -20,7 +20,7 @@ main() { var setMaxTable256 = [new Setting(Setting.SETTINGS_HEADER_TABLE_SIZE, 256)]; test('successful-setting', () async { - var writer = new FrameWriterMock(); + dynamic writer = new FrameWriterMock(); var sh = new SettingsHandler(new HPackEncoder(), writer, new ActiveSettings(), new ActiveSettings()); @@ -44,7 +44,7 @@ main() { }); test('ack-remote-settings-change', () { - var writer = new FrameWriterMock(); + dynamic writer = new FrameWriterMock(); var sh = new SettingsHandler(new HPackEncoder(), writer, new ActiveSettings(), new ActiveSettings()); @@ -93,8 +93,8 @@ main() { }); test('change-max-header-table-size', () { - var writer = new FrameWriterMock(); - var mock = new HPackEncoderMock(); + dynamic writer = new FrameWriterMock(); + dynamic mock = new HPackEncoderMock(); var sh = new SettingsHandler( mock, writer, new ActiveSettings(), new ActiveSettings()); diff --git a/pkgs/http2/test/src/streams/simple_push_test.dart b/pkgs/http2/test/src/streams/simple_push_test.dart index 4518688823..16148faed8 100644 --- a/pkgs/http2/test/src/streams/simple_push_test.dart +++ b/pkgs/http2/test/src/streams/simple_push_test.dart @@ -38,14 +38,13 @@ main() { Completer serverReceivedAllBytes = new Completer(); - Future readData( - StreamIterator iterator) async { + Future readData(StreamIterator iterator) async { var all = []; while (await iterator.moveNext()) { var msg = iterator.current; expect(msg is DataStreamMessage, isTrue); - all.addAll(msg.bytes); + all.addAll((msg as DataStreamMessage).bytes); } return UTF8.decode(all); From 9b6764b1ccd028af57a0b2781c959eb75c0024e6 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Wed, 21 Mar 2018 16:28:56 +0100 Subject: [PATCH 070/172] Add upper-bound on SDK constraint (dart-lang/http2#24) --- pkgs/http2/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index 32a7ad1ba9..311e2dd413 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -5,7 +5,7 @@ author: Dart Team homepage: https://github.com/dart-lang/http2 environment: - sdk: '>=2.0.0-dev.40.0' + sdk: '>=2.0.0-dev.40.0 <2.0.0' dev_dependencies: mockito: ^3.0.0-alpha From 41032c5ae56736307edd43ac33264c330be57c9c Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Fri, 23 Mar 2018 09:00:14 -0700 Subject: [PATCH 071/172] Stop running Travis on Stable --- pkgs/http2/.travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/pkgs/http2/.travis.yml b/pkgs/http2/.travis.yml index f80a1f1986..9d69cddf2b 100644 --- a/pkgs/http2/.travis.yml +++ b/pkgs/http2/.travis.yml @@ -1,7 +1,6 @@ language: dart dart: - dev - - stable dart_task: - test From 2783a9f3db1f32f3177e916ed6940575db20092e Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Thu, 31 May 2018 17:00:34 -0700 Subject: [PATCH 072/172] Switch all uppercase constants to new lowercase (dart-lang/http2#28) Switch all uppercase constants to new lowercase. --- pkgs/http2/CHANGELOG.md | 4 +- pkgs/http2/lib/src/connection.dart | 6 +- pkgs/http2/lib/src/hpack/hpack.dart | 126 +++++++++--------- pkgs/http2/lib/src/testing/client.dart | 6 +- pkgs/http2/pubspec.yaml | 4 +- pkgs/http2/test/client_test.dart | 8 +- pkgs/http2/test/client_websites_test.dart | 8 +- .../http2/test/multiprotocol_server_test.dart | 10 +- .../test/src/hpack/huffman_table_test.dart | 6 +- .../test/src/streams/simple_push_test.dart | 6 +- 10 files changed, 93 insertions(+), 91 deletions(-) diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index 30ad1ad1d7..6cdc71c752 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -1,4 +1,6 @@ -# Changelog +## 0.1.8+1 + +* Switch all uppercase constants from `dart:convert` to lowercase. ## 0.1.8 diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index e10b649cb9..0c097d51de 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -5,7 +5,7 @@ library http2.src.conn; import 'dart:async'; -import 'dart:convert'; +import 'dart:convert' show utf8; import '../transport.dart'; import 'connection_preface.dart'; @@ -359,7 +359,7 @@ abstract class Connection { _state.finishingState |= ConnectionState.FinishingActive; _frameWriter.writeGoawayFrame(_streams.highestPeerInitiatedStream, - ErrorCode.NO_ERROR, message != null ? UTF8.encode(message) : []); + ErrorCode.NO_ERROR, message != null ? utf8.encode(message) : []); } else { _state.state = ConnectionState.Finishing; _state.finishingState |= ConnectionState.FinishingPassive; @@ -380,7 +380,7 @@ abstract class Connection { var cancelFuture = new Future.sync(_frameReaderSubscription.cancel); if (!causedByTransportError) { _frameWriter.writeGoawayFrame(_streams.highestPeerInitiatedStream, - errorCode, message != null ? UTF8.encode(message) : []); + errorCode, message != null ? utf8.encode(message) : []); } var closeFuture = _frameWriter.close().catchError((e, s) { // We ignore any errors after writing to [GoawayFrame] diff --git a/pkgs/http2/lib/src/hpack/hpack.dart b/pkgs/http2/lib/src/hpack/hpack.dart index f4d8b226d6..4821511f73 100644 --- a/pkgs/http2/lib/src/hpack/hpack.dart +++ b/pkgs/http2/lib/src/hpack/hpack.dart @@ -7,7 +7,7 @@ /// https://tools.ietf.org/html/draft-ietf-httpbis-header-compression-10 library http2.hpack; -import 'dart:convert'; +import 'dart:convert' show ascii; import 'dart:io'; import '../byte_utils.dart'; @@ -48,7 +48,7 @@ class Header { Header(this.name, this.value, {this.neverIndexed: false}); factory Header.ascii(String name, String value) { - return new Header(ASCII.encode(name), ASCII.encode(value)); + return new Header(ascii.encode(name), ascii.encode(value)); } } @@ -228,67 +228,67 @@ class HPackEncoder { class IndexTable { static final List
_staticTable = [ null, - new Header(ASCII.encode(':authority'), const []), - new Header(ASCII.encode(':method'), ASCII.encode('GET')), - new Header(ASCII.encode(':method'), ASCII.encode('POST')), - new Header(ASCII.encode(':path'), ASCII.encode('/')), - new Header(ASCII.encode(':path'), ASCII.encode('/index.html')), - new Header(ASCII.encode(':scheme'), ASCII.encode('http')), - new Header(ASCII.encode(':scheme'), ASCII.encode('https')), - new Header(ASCII.encode(':status'), ASCII.encode('200')), - new Header(ASCII.encode(':status'), ASCII.encode('204')), - new Header(ASCII.encode(':status'), ASCII.encode('206')), - new Header(ASCII.encode(':status'), ASCII.encode('304')), - new Header(ASCII.encode(':status'), ASCII.encode('400')), - new Header(ASCII.encode(':status'), ASCII.encode('404')), - new Header(ASCII.encode(':status'), ASCII.encode('500')), - new Header(ASCII.encode('accept-charset'), const []), - new Header(ASCII.encode('accept-encoding'), ASCII.encode('gzip, deflate')), - new Header(ASCII.encode('accept-language'), const []), - new Header(ASCII.encode('accept-ranges'), const []), - new Header(ASCII.encode('accept'), const []), - new Header(ASCII.encode('access-control-allow-origin'), const []), - new Header(ASCII.encode('age'), const []), - new Header(ASCII.encode('allow'), const []), - new Header(ASCII.encode('authorization'), const []), - new Header(ASCII.encode('cache-control'), const []), - new Header(ASCII.encode('content-disposition'), const []), - new Header(ASCII.encode('content-encoding'), const []), - new Header(ASCII.encode('content-language'), const []), - new Header(ASCII.encode('content-length'), const []), - new Header(ASCII.encode('content-location'), const []), - new Header(ASCII.encode('content-range'), const []), - new Header(ASCII.encode('content-type'), const []), - new Header(ASCII.encode('cookie'), const []), - new Header(ASCII.encode('date'), const []), - new Header(ASCII.encode('etag'), const []), - new Header(ASCII.encode('expect'), const []), - new Header(ASCII.encode('expires'), const []), - new Header(ASCII.encode('from'), const []), - new Header(ASCII.encode('host'), const []), - new Header(ASCII.encode('if-match'), const []), - new Header(ASCII.encode('if-modified-since'), const []), - new Header(ASCII.encode('if-none-match'), const []), - new Header(ASCII.encode('if-range'), const []), - new Header(ASCII.encode('if-unmodified-since'), const []), - new Header(ASCII.encode('last-modified'), const []), - new Header(ASCII.encode('link'), const []), - new Header(ASCII.encode('location'), const []), - new Header(ASCII.encode('max-forwards'), const []), - new Header(ASCII.encode('proxy-authenticate'), const []), - new Header(ASCII.encode('proxy-authorization'), const []), - new Header(ASCII.encode('range'), const []), - new Header(ASCII.encode('referer'), const []), - new Header(ASCII.encode('refresh'), const []), - new Header(ASCII.encode('retry-after'), const []), - new Header(ASCII.encode('server'), const []), - new Header(ASCII.encode('set-cookie'), const []), - new Header(ASCII.encode('strict-transport-security'), const []), - new Header(ASCII.encode('transfer-encoding'), const []), - new Header(ASCII.encode('user-agent'), const []), - new Header(ASCII.encode('vary'), const []), - new Header(ASCII.encode('via'), const []), - new Header(ASCII.encode('www-authenticate'), const []), + new Header(ascii.encode(':authority'), const []), + new Header(ascii.encode(':method'), ascii.encode('GET')), + new Header(ascii.encode(':method'), ascii.encode('POST')), + new Header(ascii.encode(':path'), ascii.encode('/')), + new Header(ascii.encode(':path'), ascii.encode('/index.html')), + new Header(ascii.encode(':scheme'), ascii.encode('http')), + new Header(ascii.encode(':scheme'), ascii.encode('https')), + new Header(ascii.encode(':status'), ascii.encode('200')), + new Header(ascii.encode(':status'), ascii.encode('204')), + new Header(ascii.encode(':status'), ascii.encode('206')), + new Header(ascii.encode(':status'), ascii.encode('304')), + new Header(ascii.encode(':status'), ascii.encode('400')), + new Header(ascii.encode(':status'), ascii.encode('404')), + new Header(ascii.encode(':status'), ascii.encode('500')), + new Header(ascii.encode('accept-charset'), const []), + new Header(ascii.encode('accept-encoding'), ascii.encode('gzip, deflate')), + new Header(ascii.encode('accept-language'), const []), + new Header(ascii.encode('accept-ranges'), const []), + new Header(ascii.encode('accept'), const []), + new Header(ascii.encode('access-control-allow-origin'), const []), + new Header(ascii.encode('age'), const []), + new Header(ascii.encode('allow'), const []), + new Header(ascii.encode('authorization'), const []), + new Header(ascii.encode('cache-control'), const []), + new Header(ascii.encode('content-disposition'), const []), + new Header(ascii.encode('content-encoding'), const []), + new Header(ascii.encode('content-language'), const []), + new Header(ascii.encode('content-length'), const []), + new Header(ascii.encode('content-location'), const []), + new Header(ascii.encode('content-range'), const []), + new Header(ascii.encode('content-type'), const []), + new Header(ascii.encode('cookie'), const []), + new Header(ascii.encode('date'), const []), + new Header(ascii.encode('etag'), const []), + new Header(ascii.encode('expect'), const []), + new Header(ascii.encode('expires'), const []), + new Header(ascii.encode('from'), const []), + new Header(ascii.encode('host'), const []), + new Header(ascii.encode('if-match'), const []), + new Header(ascii.encode('if-modified-since'), const []), + new Header(ascii.encode('if-none-match'), const []), + new Header(ascii.encode('if-range'), const []), + new Header(ascii.encode('if-unmodified-since'), const []), + new Header(ascii.encode('last-modified'), const []), + new Header(ascii.encode('link'), const []), + new Header(ascii.encode('location'), const []), + new Header(ascii.encode('max-forwards'), const []), + new Header(ascii.encode('proxy-authenticate'), const []), + new Header(ascii.encode('proxy-authorization'), const []), + new Header(ascii.encode('range'), const []), + new Header(ascii.encode('referer'), const []), + new Header(ascii.encode('refresh'), const []), + new Header(ascii.encode('retry-after'), const []), + new Header(ascii.encode('server'), const []), + new Header(ascii.encode('set-cookie'), const []), + new Header(ascii.encode('strict-transport-security'), const []), + new Header(ascii.encode('transfer-encoding'), const []), + new Header(ascii.encode('user-agent'), const []), + new Header(ascii.encode('vary'), const []), + new Header(ascii.encode('via'), const []), + new Header(ascii.encode('www-authenticate'), const []), ]; final List
_dynamicTable = []; diff --git a/pkgs/http2/lib/src/testing/client.dart b/pkgs/http2/lib/src/testing/client.dart index 541f702dfd..a7d9dc4bee 100644 --- a/pkgs/http2/lib/src/testing/client.dart +++ b/pkgs/http2/lib/src/testing/client.dart @@ -5,7 +5,7 @@ library http2.client; import 'dart:async'; -import 'dart:convert'; +import 'dart:convert' show ascii; import 'dart:io'; import '../../transport.dart'; @@ -111,8 +111,8 @@ class ClientConnection { var headerMap = >{}; for (var header in headers) { headerMap - .putIfAbsent(ASCII.decode(header.name), () => []) - .add(ASCII.decode(header.value)); + .putIfAbsent(ascii.decode(header.name), () => []) + .add(ascii.decode(header.value)); } return headerMap; } diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index 311e2dd413..f9eed554c6 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -1,11 +1,11 @@ name: http2 -version: 0.1.8 +version: 0.1.8+1 description: A HTTP/2 implementation in Dart. author: Dart Team homepage: https://github.com/dart-lang/http2 environment: - sdk: '>=2.0.0-dev.40.0 <2.0.0' + sdk: '>=2.0.0-dev.56.0 <2.0.0' dev_dependencies: mockito: ^3.0.0-alpha diff --git a/pkgs/http2/test/client_test.dart b/pkgs/http2/test/client_test.dart index d4bad2f54a..c9449b1e21 100644 --- a/pkgs/http2/test/client_test.dart +++ b/pkgs/http2/test/client_test.dart @@ -5,7 +5,7 @@ library http2.test.client_tests; import 'dart:async'; -import 'dart:convert'; +import 'dart:convert' show ascii; import 'dart:typed_data'; import 'package:test/test.dart'; @@ -280,7 +280,7 @@ main() { // Make sure we get a connection error. GoawayFrame frame = await nextFrame(); - expect(ASCII.decode(frame.debugData), + expect(ascii.decode(frame.debugData), contains('Cannot push on a non-existent stream')); expect(await serverReader.moveNext(), false); await serverWriter.close(); @@ -330,7 +330,7 @@ main() { // Make sure we get a connection error. GoawayFrame frame = await nextFrame(); expect( - ASCII.decode(frame.debugData), + ascii.decode(frame.debugData), contains( 'Expected open state (was: StreamState.HalfClosedRemote)')); expect(await serverReader.moveNext(), false); @@ -383,7 +383,7 @@ main() { // describes that the flow control window became negative. GoawayFrame frame = await nextFrame(); expect( - ASCII.decode(frame.debugData), + ascii.decode(frame.debugData), contains('Connection level flow control window became ' 'negative.')); expect(await serverReader.moveNext(), false); diff --git a/pkgs/http2/test/client_websites_test.dart b/pkgs/http2/test/client_websites_test.dart index 44b00b649a..8b5b6ec7f0 100644 --- a/pkgs/http2/test/client_websites_test.dart +++ b/pkgs/http2/test/client_websites_test.dart @@ -5,7 +5,7 @@ library http2.test.client_websites_test; import 'dart:async'; -import 'dart:convert'; +import 'dart:convert' show Utf8Decoder, utf8; import 'dart:io'; import 'package:http2/src/testing/client.dart'; @@ -129,9 +129,9 @@ dumpHeaders(Uri uri, Map> headers, Future readBody(Response response) async { var stream = response.stream; if (response.headers['content-encoding']?.join('') == 'gzip') { - stream = stream.transform(GZIP.decoder); + stream = stream.transform(gzip.decoder); } else if (response.headers['content-encoding']?.join('') == 'deflate') { - stream = stream.transform(ZLIB.decoder); + stream = stream.transform(zlib.decoder); } - return await stream.transform(UTF8.decoder).join(''); + return await stream.transform(utf8.decoder).join(''); } diff --git a/pkgs/http2/test/multiprotocol_server_test.dart b/pkgs/http2/test/multiprotocol_server_test.dart index 3afce596f4..dbbcaf8a8b 100644 --- a/pkgs/http2/test/multiprotocol_server_test.dart +++ b/pkgs/http2/test/multiprotocol_server_test.dart @@ -5,7 +5,7 @@ library http2.test.client_tests; import 'dart:async'; -import 'dart:convert'; +import 'dart:convert' show ascii, utf8; import 'dart:io'; import 'package:test/test.dart'; @@ -71,7 +71,7 @@ Future makeHttp11Request( var request = await client.getUrl(Uri.parse('https://localhost:${server.port}/abc$i')); var response = await request.close(); - var body = await response.transform(UTF8.decoder).join(''); + var body = await response.transform(utf8.decoder).join(''); expect(body, 'answer$i'); } @@ -101,7 +101,7 @@ Future makeHttp2Request(MultiProtocolHttpServer server, expect(responseHeaders[':status'], '200'); expect(await si.moveNext(), true); - expect(ASCII.decode((si.current as DataStreamMessage).bytes), 'answer$i'); + expect(ascii.decode((si.current as DataStreamMessage).bytes), 'answer$i'); expect(await si.moveNext(), false); } @@ -120,14 +120,14 @@ Future handleHttp2Request(ServerTransportStream stream, int i) async { new Header.ascii(':status', '200'), ])); - stream.outgoingMessages.add(new DataStreamMessage(ASCII.encode('answer$i'))); + stream.outgoingMessages.add(new DataStreamMessage(ascii.encode('answer$i'))); await stream.outgoingMessages.close(); } Map getHeaders(HeadersStreamMessage headers) { var map = {}; for (var h in headers.headers) { - map.putIfAbsent(ASCII.decode(h.name), () => ASCII.decode(h.value)); + map.putIfAbsent(ascii.decode(h.name), () => ascii.decode(h.value)); } return map; } diff --git a/pkgs/http2/test/src/hpack/huffman_table_test.dart b/pkgs/http2/test/src/hpack/huffman_table_test.dart index 588b9f6708..03ca5982ce 100644 --- a/pkgs/http2/test/src/hpack/huffman_table_test.dart +++ b/pkgs/http2/test/src/hpack/huffman_table_test.dart @@ -2,7 +2,7 @@ // 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:convert'; +import 'dart:convert' show ascii; import 'package:test/test.dart'; import 'package:http2/src/hpack/huffman.dart'; @@ -36,8 +36,8 @@ main() { test('hpack-spec-testcases', () { hpackSpecTestCases.forEach((String value, List encoding) { - expect(decode(encoding), ASCII.encode(value)); - expect(encode(ASCII.encode(value)), encoding); + expect(decode(encoding), ascii.encode(value)); + expect(encode(ascii.encode(value)), encoding); }); }); diff --git a/pkgs/http2/test/src/streams/simple_push_test.dart b/pkgs/http2/test/src/streams/simple_push_test.dart index 16148faed8..39e1f21e43 100644 --- a/pkgs/http2/test/src/streams/simple_push_test.dart +++ b/pkgs/http2/test/src/streams/simple_push_test.dart @@ -5,7 +5,7 @@ library http2.test.streams.simple_push_test; import 'dart:async'; -import 'dart:convert'; +import 'dart:convert' show utf8; import 'package:test/test.dart'; import 'package:http2/transport.dart'; @@ -47,12 +47,12 @@ main() { all.addAll((msg as DataStreamMessage).bytes); } - return UTF8.decode(all); + return utf8.decode(all); } Future sendData(TransportStream stream, String data) { stream.outgoingMessages - ..add(new DataStreamMessage(UTF8.encode(data))) + ..add(new DataStreamMessage(utf8.encode(data))) ..close(); return stream.outgoingMessages.done; } From 1e630d5a313d5ce86b54ed475064b20978a8d05f Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Mon, 16 Jul 2018 17:04:55 +0200 Subject: [PATCH 073/172] Fix `ClientTransportConnection.isOpen` to return `false` if we have exhausted the number of max-concurrent-streams. (dart-lang/http2#31) * Fix `ClientTransportConnection.isOpen` to return `false` if we have exhausted the number of max-concurrent-streams. * Run dartfmt on a few (unrelated) files * addressed comments --- pkgs/http2/CHANGELOG.md | 8 ++++ pkgs/http2/example/display_headers.dart | 4 +- pkgs/http2/lib/src/connection.dart | 31 ++++++++++++++- .../src/flowcontrol/connection_queues.dart | 21 ++++++++++ .../lib/src/flowcontrol/stream_queues.dart | 22 +++++++++++ .../http2/lib/src/streams/stream_handler.dart | 14 ++++++- pkgs/http2/lib/src/testing/debug.dart | 3 +- pkgs/http2/pubspec.yaml | 8 ++-- .../src/frames/frame_writer_reader_test.dart | 4 +- pkgs/http2/test/transport_test.dart | 38 +++++++++++++++++++ 10 files changed, 140 insertions(+), 13 deletions(-) diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index 6cdc71c752..ca5b22fd08 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -1,3 +1,11 @@ +## 0.1.8+2 + +* On connection termination, try to dispatch existing messages, thereby avoiding + terminating existing streams. + +* Fix `ClientTransportConnection.isOpen` to return `false` if we have exhausted + the number of max-concurrent-streams. + ## 0.1.8+1 * Switch all uppercase constants from `dart:convert` to lowercase. diff --git a/pkgs/http2/example/display_headers.dart b/pkgs/http2/example/display_headers.dart index 9e34e48e6d..6cf9be2dbb 100644 --- a/pkgs/http2/example/display_headers.dart +++ b/pkgs/http2/example/display_headers.dart @@ -50,8 +50,8 @@ main(List args) async { Future connect(Uri uri) async { bool useSSL = uri.scheme == 'https'; if (useSSL) { - var secureSocket = await SecureSocket - .connect(uri.host, uri.port, supportedProtocols: ['h2']); + var secureSocket = await SecureSocket.connect(uri.host, uri.port, + supportedProtocols: ['h2']); if (secureSocket.selectedProtocol != 'h2') { throw new Exception("Failed to negogiate http/2 via alpn. Maybe server " "doesn't support http/2."); diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index 0c097d51de..43c1a9cb75 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -58,6 +58,29 @@ class ConnectionState { bool get passiveFinishing => state == Finishing && (finishingState & FinishingPassive) != 0; + + String toString() { + String message = ''; + + void add(bool condition, String flag) { + if (condition) { + if (message.length == 0) { + message = flag; + } else { + message = '$message/$flag'; + } + } + } + + add(isInitialized, 'Initialized'); + add(isOperational, 'IsOperational'); + add(isFinishing, 'IsFinishing'); + add(isTerminated, 'IsTerminated'); + add(activeFinishing, 'ActiveFinishing'); + add(passiveFinishing, 'PassiveFinishing'); + + return message; + } } abstract class Connection { @@ -132,6 +155,11 @@ abstract class Connection { }, onError: (error, stack) { _terminate(ErrorCode.CONNECT_ERROR, causedByTransportError: true); }, onDone: () { + // Ensure existing messages from lower levels are sent to the upper + // levels before we terminate everything. + _incomingQueue.forceDispatchIncomingMessages(); + _streams.forceDispatchIncomingMessages(); + _terminate(ErrorCode.CONNECT_ERROR, causedByTransportError: true); }); @@ -419,7 +447,8 @@ class ClientConnection extends Connection implements ClientTransportConnection { return new ClientConnection._(incoming, outgoing, clientSettings); } - bool get isOpen => !_state.isFinishing && !_state.isTerminated; + bool get isOpen => + !_state.isFinishing && !_state.isTerminated && _streams.canOpenStream; ClientTransportStream makeRequest(List
headers, {bool endStream: false}) { diff --git a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart index fa11735be1..c008b7a063 100644 --- a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart @@ -324,4 +324,25 @@ class ConnectionMessageQueueIn extends Object onCheckForClose(); } + + void forceDispatchIncomingMessages() { + final toBeRemoved = new Set(); + _stream2pendingMessages.forEach((int streamId, Queue messages) { + final mq = _stream2messageQueue[streamId]; + while (messages.isNotEmpty) { + _count--; + final message = messages.removeFirst(); + mq.enqueueMessage(message); + if (message.endStream) { + toBeRemoved.add(streamId); + break; + } + } + }); + + for (final streamId in toBeRemoved) { + _stream2messageQueue.remove(streamId); + _stream2pendingMessages.remove(streamId); + } + } } diff --git a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart index 8bbf783dab..16bcbda45a 100644 --- a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart @@ -258,6 +258,28 @@ class StreamMessageQueueIn extends Object } } + void forceDispatchIncomingMessages() { + while (_pendingMessages.isNotEmpty) { + final message = _pendingMessages.removeFirst(); + assert(!_incomingMessagesC.isClosed); + if (message is HeadersMessage) { + _incomingMessagesC.add(new HeadersStreamMessage(message.headers, + endStream: message.endStream)); + } else if (message is DataMessage) { + if (message.bytes.length > 0) { + _incomingMessagesC.add(new DataStreamMessage(message.bytes, + endStream: message.endStream)); + } + } else { + // This can never happen. + assert(false); + } + if (message.endStream) { + onCloseCheck(); + } + } + } + void _tryDispatch() { while (!wasTerminated && _pendingMessages.isNotEmpty) { bool handled = false; diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index eee117e3f6..4910c50a96 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -150,6 +150,10 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { bool get ranOutOfStreamIds => _ranOutOfStreamIds(); + /// Whether it is possible to open a new stream to the remote end (e.g. based + /// on whether we have reached the limit of maximum concurrent open streams). + bool get canOpenStream => _canCreateNewStream(); + final ActiveStateHandler _onActiveStateChanged; StreamHandler._( @@ -190,6 +194,12 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { startClosing(); } + void forceDispatchIncomingMessages() { + _openStreams.forEach((int streamId, Http2StreamImpl stream) { + stream.incomingQueue.forceDispatchIncomingMessages(); + }); + } + Stream get incomingStreams => _newStreamsC.stream; List get openStreams => _openStreams.values.toList(); @@ -645,8 +655,8 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { void _handleRstFrame(Http2StreamImpl stream, RstStreamFrame frame) { stream._handleTerminated(frame.errorCode); - var exception = - new StreamException(stream.id, 'Stream was terminated by peer.'); + var exception = new StreamTransportException( + 'Stream was terminated by peer (errorCode: ${frame.errorCode}).'); _closeStreamAbnormally(stream, exception, propagateException: true); } diff --git a/pkgs/http2/lib/src/testing/debug.dart b/pkgs/http2/lib/src/testing/debug.dart index ba56269aac..b4e0b2c442 100644 --- a/pkgs/http2/lib/src/testing/debug.dart +++ b/pkgs/http2/lib/src/testing/debug.dart @@ -127,8 +127,7 @@ Future _pipeAndCopy(Stream> from, StreamSink to, StreamSink to2) { to.addError(e, s); to2.addError(e, s); }, onDone: () { - Future - .wait([to.close(), to2.close()]) + Future.wait([to.close(), to2.close()]) .then(c.complete) .catchError(c.completeError); }); diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index f9eed554c6..a5c07dbd8d 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -1,12 +1,12 @@ name: http2 -version: 0.1.8+1 +version: 0.1.8+2 description: A HTTP/2 implementation in Dart. author: Dart Team homepage: https://github.com/dart-lang/http2 environment: - sdk: '>=2.0.0-dev.56.0 <2.0.0' + sdk: '>=2.0.0-dev.56.0 <3.0.0' dev_dependencies: - mockito: ^3.0.0-alpha - test: '>=0.12.0 <0.13.0' + mockito: ^3.0.0-beta+2 + test: ^1.2.0 diff --git a/pkgs/http2/test/src/frames/frame_writer_reader_test.dart b/pkgs/http2/test/src/frames/frame_writer_reader_test.dart index 2f1d579f5b..6fef6f7ab0 100644 --- a/pkgs/http2/test/src/frames/frame_writer_reader_test.dart +++ b/pkgs/http2/test/src/frames/frame_writer_reader_test.dart @@ -229,6 +229,6 @@ writerReaderTest(String name, } Future> finishWriting(FrameWriter writer, FrameReader reader) { - return Future.wait([writer.close(), reader.startDecoding().toList()]).then( - (results) => results.last); + return Future.wait([writer.close(), reader.startDecoding().toList()]) + .then((results) => results.last); } diff --git a/pkgs/http2/test/transport_test.dart b/pkgs/http2/test/transport_test.dart index c9c32d96a2..3841c25c13 100644 --- a/pkgs/http2/test/transport_test.dart +++ b/pkgs/http2/test/transport_test.dart @@ -55,6 +55,44 @@ main() { })); }); + const int concurrentStreamLimit = 5; + transportTest('exhaust-concurrent-stream-limit', + (ClientTransportConnection client, + ServerTransportConnection server) async { + Future clientFun() async { + // We have to wait until the max-concurrent-streams [Setting] was + // transferred from server to client, which is asynchronous. + // The default is unlimited, which is why we have to wait for the server + // setting to arrive on the client. + // At the moment, delaying by 2 microtask cycles is enough. + await new Future.value(); + await new Future.value(); + + final streams = []; + for (int i = 0; i < concurrentStreamLimit; ++i) { + expect(client.isOpen, true); + streams.add(client.makeRequest([new Header.ascii('a', 'b')])); + } + expect(client.isOpen, false); + for (final stream in streams) { + stream.sendData([], endStream: true); + } + await client.finish(); + } + + Future serverFun() async { + await for (final stream in server.incomingStreams) { + await stream.incomingMessages.toList(); + stream.sendHeaders([new Header.ascii('a', 'b')], endStream: true); + } + await server.finish(); + } + + await Future.wait([clientFun(), serverFun()]); + }, + serverSettings: + new ServerSettings(concurrentStreamLimit: concurrentStreamLimit)); + transportTest('disabled-push', (ClientTransportConnection client, ServerTransportConnection server) async { server.incomingStreams From a0f2afdb7df971923400784d0dc8168ae472b486 Mon Sep 17 00:00:00 2001 From: Clement Skau Date: Wed, 8 Aug 2018 16:11:17 +0200 Subject: [PATCH 074/172] Adds a GoawayMessage and makes sure we enqueue these instead of racing other messages waiting to be sent. --- pkgs/http2/lib/src/connection.dart | 13 +++++++++---- .../lib/src/flowcontrol/connection_queues.dart | 4 ++++ pkgs/http2/lib/src/flowcontrol/queue_messages.dart | 12 ++++++++++++ 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index 43c1a9cb75..271b86f653 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -10,6 +10,7 @@ import 'dart:convert' show utf8; import '../transport.dart'; import 'connection_preface.dart'; import 'flowcontrol/connection_queues.dart'; +import 'flowcontrol/queue_messages.dart'; import 'flowcontrol/window.dart'; import 'flowcontrol/window_handler.dart'; import 'frames/frame_defragmenter.dart'; @@ -386,8 +387,10 @@ abstract class Connection { _state.state = ConnectionState.Finishing; _state.finishingState |= ConnectionState.FinishingActive; - _frameWriter.writeGoawayFrame(_streams.highestPeerInitiatedStream, - ErrorCode.NO_ERROR, message != null ? utf8.encode(message) : []); + _outgoingQueue.enqueueMessage(new GoawayMessage( + _streams.highestPeerInitiatedStream, + ErrorCode.NO_ERROR, + message != null ? utf8.encode(message) : [])); } else { _state.state = ConnectionState.Finishing; _state.finishingState |= ConnectionState.FinishingPassive; @@ -407,8 +410,10 @@ abstract class Connection { var cancelFuture = new Future.sync(_frameReaderSubscription.cancel); if (!causedByTransportError) { - _frameWriter.writeGoawayFrame(_streams.highestPeerInitiatedStream, - errorCode, message != null ? utf8.encode(message) : []); + _outgoingQueue.enqueueMessage(new GoawayMessage( + _streams.highestPeerInitiatedStream, + errorCode, + message != null ? utf8.encode(message) : [])); } var closeFuture = _frameWriter.close().catchError((e, s) { // We ignore any errors after writing to [GoawayFrame] diff --git a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart index c008b7a063..4dc1a70256 100644 --- a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart @@ -141,6 +141,10 @@ class ConnectionMessageQueueOut extends Object new DataMessage(message.streamId, tail, message.endStream); _messages.addFirst(tailMessage); } + } else if (message is GoawayMessage) { + _messages.removeFirst(); + _frameWriter.writeGoawayFrame( + message.lastStreamId, message.errorCode, message.debugData); } else { throw new StateError( 'Unexpected message in queue: ${message.runtimeType}'); diff --git a/pkgs/http2/lib/src/flowcontrol/queue_messages.dart b/pkgs/http2/lib/src/flowcontrol/queue_messages.dart index bebf8f2342..34ce58a0f2 100644 --- a/pkgs/http2/lib/src/flowcontrol/queue_messages.dart +++ b/pkgs/http2/lib/src/flowcontrol/queue_messages.dart @@ -50,3 +50,15 @@ class PushPromiseMessage extends Message { String toString() => 'PushPromiseMessage(bytes: ${headers.length}, ' 'promisedStreamId: $promisedStreamId, endStream: $endStream)'; } + +class GoawayMessage extends Message { + final int lastStreamId; + final int errorCode; + final List debugData; + + GoawayMessage(this.lastStreamId, this.errorCode, this.debugData) + : super(0, false); + + String toString() => 'GoawayMessage(lastStreamId: ${lastStreamId}, ' + 'errorCode: ${errorCode}, debugData: ${debugData.length})'; +} From 4ee6e1800014d52460a40b2f75f95b74f7848e43 Mon Sep 17 00:00:00 2001 From: Jonas Finnemann Jensen Date: Thu, 9 Aug 2018 11:08:07 +0200 Subject: [PATCH 075/172] Fix examples to work with dart2 * Use lower case names for constants in dart:convert --- pkgs/http2/example/display_headers.dart | 4 ++-- pkgs/http2/experimental/server.dart | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pkgs/http2/example/display_headers.dart b/pkgs/http2/example/display_headers.dart index 6cf9be2dbb..c1faa4df44 100644 --- a/pkgs/http2/example/display_headers.dart +++ b/pkgs/http2/example/display_headers.dart @@ -36,8 +36,8 @@ main(List args) async { await for (var message in stream.incomingMessages) { if (message is HeadersStreamMessage) { for (var header in message.headers) { - var name = UTF8.decode(header.name); - var value = UTF8.decode(header.value); + var name = utf8.decode(header.name); + var value = utf8.decode(header.value); print('$name: $value'); } } else if (message is DataStreamMessage) { diff --git a/pkgs/http2/experimental/server.dart b/pkgs/http2/experimental/server.dart index 4e823e60e9..6637cc28f4 100644 --- a/pkgs/http2/experimental/server.dart +++ b/pkgs/http2/experimental/server.dart @@ -74,16 +74,16 @@ handleClient(SecureSocket socket) { void dumpHeaders(String prefix, List
headers) { for (int i = 0; i < headers.length; i++) { - String key = ASCII.decode(headers[i].name); - String value = ASCII.decode(headers[i].value); + String key = ascii.decode(headers[i].name); + String value = ascii.decode(headers[i].value); print('[$prefix] $key: $value'); } } String pathFromHeaders(List
headers) { for (int i = 0; i < headers.length; i++) { - if (ASCII.decode(headers[i].name) == ':path') { - return ASCII.decode(headers[i].value); + if (ascii.decode(headers[i].name) == ':path') { + return ascii.decode(headers[i].value); } } throw new Exception('Expected a :path header, but did not find one.'); @@ -106,7 +106,7 @@ Future sendHtml(ServerTransportStream stream) async { new Header.ascii(':status', '200'), new Header.ascii('content-type', 'text/html; charset=utf-8'), ]); - stream.sendData(ASCII.encode(''' + stream.sendData(ascii.encode(''' hello @@ -139,7 +139,7 @@ Future sendIFrameHtml(TransportStream stream, String path) async { new Header.ascii(':status', '200'), new Header.ascii('content-type', 'text/html; charset=utf-8'), ]); - stream.sendData(ASCII.encode(''' + stream.sendData(ascii.encode(''' Content for '$path' inside an IFrame. @@ -155,7 +155,7 @@ Future send404(TransportStream stream, String path) async { new Header.ascii(':status', '404'), new Header.ascii('content-type', 'text/html; charset=utf-8'), ]); - stream.sendData(ASCII.encode(''' + stream.sendData(ascii.encode(''' Path '$path' was not found on this server. From 4508f296b881bf68f6271ed41e91576d70a73f3f Mon Sep 17 00:00:00 2001 From: Jonas Finnemann Jensen Date: Thu, 9 Aug 2018 11:31:45 +0200 Subject: [PATCH 076/172] Fix manual test for dart2 * Migrate `isInstanceOf -> TypeMatcher` * Import `StreamHandler.MAX_STREAM_ID` from `src/` instead of duplicating the constant. --- pkgs/http2/manual_test/out_of_stream_ids_test.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkgs/http2/manual_test/out_of_stream_ids_test.dart b/pkgs/http2/manual_test/out_of_stream_ids_test.dart index f289f86adc..4e22f4b42a 100644 --- a/pkgs/http2/manual_test/out_of_stream_ids_test.dart +++ b/pkgs/http2/manual_test/out_of_stream_ids_test.dart @@ -9,12 +9,14 @@ /// - static const int MAX_STREAM_ID = (1 << 31) - 1; /// + static const int MAX_STREAM_ID = (1 << 5) - 1; /// +/// without this patch this test will run for a _long_ time. /// --------------------------------------------------------------------------- import 'dart:async'; import 'package:test/test.dart'; import 'package:http2/transport.dart'; +import 'package:http2/src/streams/stream_handler.dart'; import '../test/transport_test.dart'; @@ -34,7 +36,7 @@ main() { Future clientFun() async { var headers = [new Header.ascii('a', 'b')]; - const kMaxStreamId = (1 << 31) - 1; + const kMaxStreamId = StreamHandler.MAX_STREAM_ID; for (int i = 1; i <= kMaxStreamId; i += 2) { var stream = client.makeRequest(headers, endStream: true); var messages = await stream.incomingMessages.toList(); @@ -43,7 +45,7 @@ main() { expect(client.isOpen, false); expect(() => client.makeRequest(headers), - throwsA(new isInstanceOf())); + throwsA(const TypeMatcher())); await new Future.delayed(const Duration(seconds: 1)); await client.finish(); From c02eba34a3f367a361445cb37d7d23135b89e48b Mon Sep 17 00:00:00 2001 From: Jonas Finnemann Jensen Date: Thu, 9 Aug 2018 12:31:49 +0200 Subject: [PATCH 077/172] Fix dartanalyzer issues in tests * Migrate `isInstanceOf -> TypeMatcher` * Don't wrap with `typed` from mockito --- pkgs/http2/test/src/error_matchers.dart | 32 +++---------------- .../flowcontrol/connection_queues_test.dart | 6 ++-- .../src/flowcontrol/stream_queues_test.dart | 11 +++---- pkgs/http2/test/src/hpack/hpack_test.dart | 8 ++--- .../test/src/hpack/huffman_table_test.dart | 8 ++--- pkgs/http2/test/src/streams/streams_test.dart | 4 +-- pkgs/http2/test/transport_test.dart | 4 +-- 7 files changed, 20 insertions(+), 53 deletions(-) diff --git a/pkgs/http2/test/src/error_matchers.dart b/pkgs/http2/test/src/error_matchers.dart index 56e87c02f1..c2bb5da5ad 100644 --- a/pkgs/http2/test/src/error_matchers.dart +++ b/pkgs/http2/test/src/error_matchers.dart @@ -7,30 +7,8 @@ library http2.test.error_matchers; import 'package:test/test.dart'; import 'package:http2/src/sync_errors.dart'; -const Matcher isProtocolException = const _ProtocolException(); - -class _ProtocolException extends TypeMatcher { - const _ProtocolException() : super("ProtocolException"); - bool matches(item, Map matchState) => item is ProtocolException; -} - -const Matcher isFrameSizeException = const _FrameSizeException(); - -class _FrameSizeException extends TypeMatcher { - const _FrameSizeException() : super("FrameSizeException"); - bool matches(item, Map matchState) => item is FrameSizeException; -} - -const Matcher isTerminatedException = const _TerminatedException(); - -class _TerminatedException extends TypeMatcher { - const _TerminatedException() : super("TerminatedException"); - bool matches(item, Map matchState) => item is TerminatedException; -} - -const Matcher isFlowControlException = const _FlowControlException(); - -class _FlowControlException extends TypeMatcher { - const _FlowControlException() : super("FlowControlException"); - bool matches(item, Map matchState) => item is FlowControlException; -} +const Matcher isProtocolException = const TypeMatcher(); +const Matcher isFrameSizeException = const TypeMatcher(); +const Matcher isTerminatedException = const TypeMatcher(); +const Matcher isFlowControlException = + const TypeMatcher(); diff --git a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart index 807c810a1b..e4fa69eb00 100644 --- a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart @@ -84,7 +84,7 @@ main() { queue.done.then(expectAsync1((_) { expect(queue.pendingMessages, 0); expect(() => queue.enqueueMessage(new DataMessage(99, bytes, true)), - throwsA(new isInstanceOf())); + throwsA(const TypeMatcher())); })); }); @@ -116,9 +116,7 @@ main() { streamQueueMock.bufferIndicator.markUnBuffered(); verify(windowMock.dataProcessed(bytes.length)).called(1); DataMessage capturedMessage = - verify(streamQueueMock.enqueueMessage(typed(captureAny))) - .captured - .single; + verify(streamQueueMock.enqueueMessage(captureAny)).captured.single; expect(capturedMessage.streamId, STREAM_ID); expect(capturedMessage.bytes, bytes); diff --git a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart index 44e21a4ff0..8b8fceea76 100644 --- a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart @@ -33,10 +33,10 @@ main() { queue.enqueueMessage(new DataMessage(STREAM_ID, BYTES, true)); verify(windowMock.decreaseWindow(BYTES.length)).called(1); final capturedMessage = - verify(connectionQueueMock.enqueueMessage(typed(captureAny))) + verify(connectionQueueMock.enqueueMessage(captureAny)) .captured .single; - expect(capturedMessage, new isInstanceOf()); + expect(capturedMessage, const TypeMatcher()); DataMessage capturedDataMessage = capturedMessage; expect(capturedDataMessage.bytes, BYTES); expect(capturedDataMessage.endStream, isTrue); @@ -61,11 +61,10 @@ main() { expect(queue.pendingMessages, 0); verify(windowMock.decreaseWindow(1)).called(BYTES.length); final messages = - verify(connectionQueueMock.enqueueMessage(typed(captureAny))) - .captured; + verify(connectionQueueMock.enqueueMessage(captureAny)).captured; expect(messages.length, BYTES.length); for (var counter = 0; counter < messages.length; counter++) { - expect(messages[counter], new isInstanceOf()); + expect(messages[counter], const TypeMatcher()); DataMessage dataMessage = messages[counter]; expect(dataMessage.bytes, BYTES.sublist(counter, counter + 1)); expect(dataMessage.endStream, counter == BYTES.length - 1); @@ -132,7 +131,7 @@ main() { expect(queue.bufferIndicator.wouldBuffer, isTrue); // We assert that we got the data, but it wasn't processed. verify(windowMock.gotData(bytes.length)).called(1); - verifyNever(windowMock.dataProcessed(typed(any))); + verifyNever(windowMock.dataProcessed(any)); }); // TODO: Add tests for Headers/HeadersPush messages. diff --git a/pkgs/http2/test/src/hpack/hpack_test.dart b/pkgs/http2/test/src/hpack/hpack_test.dart index e332917c24..b099822e48 100644 --- a/pkgs/http2/test/src/hpack/hpack_test.dart +++ b/pkgs/http2/test/src/hpack/hpack_test.dart @@ -798,12 +798,8 @@ class TestHelper { } /// A matcher for HuffmannDecodingExceptions. -const Matcher isHPackDecodingException = const _HPackDecodingException(); - -class _HPackDecodingException extends TypeMatcher { - const _HPackDecodingException() : super("HPackDecodingException"); - bool matches(item, Map matchState) => item is HPackDecodingException; -} +const Matcher isHPackDecodingException = + const TypeMatcher(); class _HeaderMatcher extends Matcher { final Header header; diff --git a/pkgs/http2/test/src/hpack/huffman_table_test.dart b/pkgs/http2/test/src/hpack/huffman_table_test.dart index 03ca5982ce..49898b33b9 100644 --- a/pkgs/http2/test/src/hpack/huffman_table_test.dart +++ b/pkgs/http2/test/src/hpack/huffman_table_test.dart @@ -154,9 +154,5 @@ main() { } /// A matcher for HuffmanDecodingExceptions. -const Matcher isHuffmanDecodingException = const _HuffmanDecodingException(); - -class _HuffmanDecodingException extends TypeMatcher { - const _HuffmanDecodingException() : super("HuffmanDecodingException"); - bool matches(item, Map matchState) => item is HuffmanDecodingException; -} +const Matcher isHuffmanDecodingException = + const TypeMatcher(); diff --git a/pkgs/http2/test/src/streams/streams_test.dart b/pkgs/http2/test/src/streams/streams_test.dart index d63c6e270b..06b2a335b1 100644 --- a/pkgs/http2/test/src/streams/streams_test.dart +++ b/pkgs/http2/test/src/streams/streams_test.dart @@ -221,12 +221,12 @@ main() { bool isFirst = true; cStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { if (isFirst) { - expect(msg, new isInstanceOf()); + expect(msg, const TypeMatcher()); final data = msg as DataStreamMessage; expect(data.bytes, [2]); isFirst = false; } else { - expect(msg, new isInstanceOf()); + expect(msg, const TypeMatcher()); final trailer = msg as HeadersStreamMessage; expect(trailer.endStream, true); expectHeadersEqual(trailer.headers, expectedHeaders); diff --git a/pkgs/http2/test/transport_test.dart b/pkgs/http2/test/transport_test.dart index 3841c25c13..d97c266f5a 100644 --- a/pkgs/http2/test/transport_test.dart +++ b/pkgs/http2/test/transport_test.dart @@ -99,7 +99,7 @@ main() { .listen(expectAsync1((ServerTransportStream stream) async { expect(stream.canPush, false); expect(() => stream.push([new Header.ascii('a', 'b')]), - throwsA(new isInstanceOf())); + throwsA(const TypeMatcher())); stream.sendHeaders([new Header.ascii('x', 'y')], endStream: true); })); @@ -135,7 +135,7 @@ main() { // create more pushes. expect(stream.canPush, false); expect(() => stream.push([new Header.ascii('a', 'b')]), - throwsA(new isInstanceOf())); + throwsA(const TypeMatcher())); // Finish the pushes for (ServerTransportStream pushedStream in pushes) { From 4891ed0a9e0828611207642893aab1a662f9f1a3 Mon Sep 17 00:00:00 2001 From: Alexandre Ardhuin Date: Thu, 1 Nov 2018 12:12:20 +0100 Subject: [PATCH 078/172] Discard messages incoming after stream cancellation (dart-lang/http2#35) --- pkgs/http2/lib/src/error_handler.dart | 18 +++ .../src/flowcontrol/connection_queues.dart | 3 + .../lib/src/flowcontrol/queue_messages.dart | 8 + .../lib/src/flowcontrol/stream_queues.dart | 72 +++++---- .../http2/lib/src/streams/stream_handler.dart | 13 ++ pkgs/http2/test/client_test.dart | 153 +++++++++++++++++- 6 files changed, 231 insertions(+), 36 deletions(-) diff --git a/pkgs/http2/lib/src/error_handler.dart b/pkgs/http2/lib/src/error_handler.dart index 40d391d1fc..ec1935d1e4 100644 --- a/pkgs/http2/lib/src/error_handler.dart +++ b/pkgs/http2/lib/src/error_handler.dart @@ -41,6 +41,24 @@ class TerminatableMixin { } } +/// Used by classes which may be cancelled. +class CancellableMixin { + bool _cancelled = false; + final _cancelCompleter = new Completer.sync(); + + Future get onCancel => _cancelCompleter.future; + + /// Cancel this stream message queue. Further operations on it will fail. + void cancel() { + if (!wasCancelled) { + _cancelled = true; + _cancelCompleter.complete(); + } + } + + bool get wasCancelled => _cancelled; +} + /// Used by classes which may be closed. class ClosableMixin { bool _closing = false; diff --git a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart index 4dc1a70256..4d28db23df 100644 --- a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart @@ -141,6 +141,9 @@ class ConnectionMessageQueueOut extends Object new DataMessage(message.streamId, tail, message.endStream); _messages.addFirst(tailMessage); } + } else if (message is ResetStreamMessage) { + _messages.removeFirst(); + _frameWriter.writeRstStreamFrame(message.streamId, message.errorCode); } else if (message is GoawayMessage) { _messages.removeFirst(); _frameWriter.writeGoawayFrame( diff --git a/pkgs/http2/lib/src/flowcontrol/queue_messages.dart b/pkgs/http2/lib/src/flowcontrol/queue_messages.dart index 34ce58a0f2..b30ab3f279 100644 --- a/pkgs/http2/lib/src/flowcontrol/queue_messages.dart +++ b/pkgs/http2/lib/src/flowcontrol/queue_messages.dart @@ -51,6 +51,14 @@ class PushPromiseMessage extends Message { 'promisedStreamId: $promisedStreamId, endStream: $endStream)'; } +class ResetStreamMessage extends Message { + final int errorCode; + + ResetStreamMessage(int streamId, this.errorCode) : super(streamId, false); + + String toString() => 'ResetStreamMessage(errorCode: $errorCode)'; +} + class GoawayMessage extends Message { final int lastStreamId; final int errorCode; diff --git a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart index 16bcbda45a..dc6d2f2bc5 100644 --- a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart @@ -68,22 +68,21 @@ class StreamMessageQueueOut extends Object /// Enqueues a new [Message] which is to be delivered to the connection /// message queue. void enqueueMessage(Message message) { - ensureNotClosingSync(() { - if (!wasTerminated) { - if (message.endStream) startClosing(); + if (message is! ResetStreamMessage) ensureNotClosingSync(() {}); + if (!wasTerminated) { + if (message.endStream) startClosing(); - if (message is DataMessage) { - toBeWrittenBytes += message.bytes.length; - } + if (message is DataMessage) { + toBeWrittenBytes += message.bytes.length; + } - _messages.addLast(message); - _trySendData(); + _messages.addLast(message); + _trySendData(); - if (_messages.length > 0) { - bufferIndicator.markBuffered(); - } + if (_messages.length > 0) { + bufferIndicator.markBuffered(); } - }); + } } void onTerminated(error) { @@ -134,6 +133,9 @@ class StreamMessageQueueOut extends Object } else { break; } + } else if (message is ResetStreamMessage) { + _messages.removeFirst(); + connectionMessageQueue.enqueueMessage(message); } else { throw new StateError('Unknown messages type: ${message.runtimeType}'); } @@ -152,7 +154,7 @@ class StreamMessageQueueOut extends Object /// It will keep messages up to the stream flow control window size if the /// [messages] listener is paused. class StreamMessageQueueIn extends Object - with TerminatableMixin, ClosableMixin { + with TerminatableMixin, ClosableMixin, CancellableMixin { /// The stream-level window our peer is using when sending us messages. final IncomingWindowHandler windowHandler; @@ -174,25 +176,25 @@ class StreamMessageQueueIn extends Object // incoming messages will get buffered. bufferIndicator.markBuffered(); - _incomingMessagesC = new StreamController(onListen: () { - if (!wasClosed && !wasTerminated) { - _tryDispatch(); - _tryUpdateBufferIndicator(); - } - }, onPause: () { - _tryUpdateBufferIndicator(); - // TODO: Would we ever want to decrease the window size in this - // situation? - }, onResume: () { - if (!wasClosed && !wasTerminated) { - _tryDispatch(); - _tryUpdateBufferIndicator(); - } - }, onCancel: () { - _pendingMessages.clear(); - startClosing(); - onCloseCheck(); - }); + _incomingMessagesC = new StreamController( + onListen: () { + if (!wasClosed && !wasTerminated) { + _tryDispatch(); + _tryUpdateBufferIndicator(); + } + }, + onPause: () { + _tryUpdateBufferIndicator(); + // TODO: Would we ever want to decrease the window size in this + // situation? + }, + onResume: () { + if (!wasClosed && !wasTerminated) { + _tryDispatch(); + _tryUpdateBufferIndicator(); + } + }, + onCancel: cancel); _serverPushStreamsC = new StreamController(onListen: () { if (!wasClosed && !wasTerminated) { @@ -282,10 +284,12 @@ class StreamMessageQueueIn extends Object void _tryDispatch() { while (!wasTerminated && _pendingMessages.isNotEmpty) { - bool handled = false; + bool handled = wasCancelled; var message = _pendingMessages.first; - if (message is HeadersMessage || message is DataMessage) { + if (wasCancelled) { + _pendingMessages.removeFirst(); + } else if (message is HeadersMessage || message is DataMessage) { assert(!_incomingMessagesC.isClosed); if (_incomingMessagesC.hasListener && !_incomingMessagesC.isPaused) { _pendingMessages.removeFirst(); diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index 4910c50a96..0f79b52e3e 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -330,6 +330,19 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { _setupOutgoingMessageHandling(stream); + // Handle incoming stream cancellation. RST is only sent when streamQueueOut + // has been closed because RST make the stream 'closed'. + streamQueueIn.onCancel.then((_) { + // If our side is done sending data, i.e. we have enqueued the + // end-of-stream in the outgoing message queue, but the remote end is + // still sending us data, despite us not being interested in it, we will + // reset the stream. + if (stream.state == StreamState.HalfClosedLocal) { + stream.outgoingQueue.enqueueMessage( + new ResetStreamMessage(stream.id, ErrorCode.CANCEL)); + } + }); + // NOTE: We are not interested whether the streams were normally finished // or abnormally terminated. Therefore we use 'catchError((_) {})'! var streamDone = [streamQueueIn.done, streamQueueOut.done]; diff --git a/pkgs/http2/test/client_test.dart b/pkgs/http2/test/client_test.dart index c9449b1e21..37ebc30f72 100644 --- a/pkgs/http2/test/client_test.dart +++ b/pkgs/http2/test/client_test.dart @@ -10,12 +10,12 @@ import 'dart:typed_data'; import 'package:test/test.dart'; -import 'package:http2/transport.dart'; -import 'package:http2/src/flowcontrol/window.dart'; import 'package:http2/src/connection_preface.dart'; +import 'package:http2/src/flowcontrol/window.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 'src/hpack/hpack_test.dart' show isHeader; @@ -248,6 +248,155 @@ main() { await Future.wait([serverFun(), clientFun()]); }); + clientTest('data-frame-received-after-stream-cancel', + (ClientTransportConnection client, + FrameWriter serverWriter, + StreamIterator serverReader, + Future nextFrame()) async { + var handshakeCompleter = new Completer(); + var cancelDone = new Completer(); + var endDone = new Completer(); + + Future serverFun() async { + serverWriter.writeSettingsFrame([]); + expect(await nextFrame() is SettingsFrame, true); + serverWriter.writeSettingsAckFrame(); + expect(await nextFrame() is SettingsFrame, true); + + handshakeCompleter.complete(); + + HeadersFrame headers = await nextFrame(); + DataFrame finFrame = await nextFrame(); + expect(finFrame.hasEndStreamFlag, true); + + int streamId = headers.header.streamId; + + // Write a data frame. + serverWriter.writeDataFrame(streamId, [42]); + await cancelDone.future; + serverWriter.writeDataFrame(streamId, [43]); + + // NOTE: The order of the window update frame / rst frame just + // happens to be like that ATM. + + // Await stream/connection window update frame. + var win = await nextFrame() as WindowUpdateFrame; + expect(win.header.streamId, 1); + expect(win.windowSizeIncrement, 1); + win = await nextFrame() as WindowUpdateFrame; + expect(win.header.streamId, 0); + expect(win.windowSizeIncrement, 1); + win = await nextFrame() as WindowUpdateFrame; + expect(win.header.streamId, 0); + expect(win.windowSizeIncrement, 1); + + // Make sure we get a [RstStreamFrame] frame. + var frame = await nextFrame(); + expect(frame is RstStreamFrame, true); + expect((frame as RstStreamFrame).errorCode, ErrorCode.CANCEL); + expect((frame as RstStreamFrame).header.streamId, streamId); + + serverWriter.writeRstStreamFrame(streamId, ErrorCode.STREAM_CLOSED); + + endDone.complete(); + + // Wait for the client finish. + expect(await nextFrame() is GoawayFrame, true); + expect(await serverReader.moveNext(), false); + await serverWriter.close(); + } + + Future clientFun() async { + await handshakeCompleter.future; + + var stream = client.makeRequest([new Header.ascii('a', 'b')]); + await stream.outgoingMessages.close(); + + // first will cancel the stream + var message = await stream.incomingMessages.first; + expect((message as DataStreamMessage).bytes, [42]); + cancelDone.complete(); + + await endDone.future; + await client.finish(); + } + + await Future.wait([serverFun(), clientFun()]); + }); + + clientTest('data-frame-received-after-stream-cancel-and-out-not-closed', + (ClientTransportConnection client, + FrameWriter serverWriter, + StreamIterator serverReader, + Future nextFrame()) async { + var handshakeCompleter = new Completer(); + var cancelDone = new Completer(); + var endDone = new Completer(); + var clientDone = new Completer(); + + Future serverFun() async { + serverWriter.writeSettingsFrame([]); + expect(await nextFrame() is SettingsFrame, true); + serverWriter.writeSettingsAckFrame(); + expect(await nextFrame() is SettingsFrame, true); + + handshakeCompleter.complete(); + + HeadersFrame headers = await nextFrame(); + + int streamId = headers.header.streamId; + + // Write a data frame. + serverWriter.writeDataFrame(streamId, [42]); + await cancelDone.future; + serverWriter.writeDataFrame(streamId, [43]); + serverWriter.writeRstStreamFrame(streamId, ErrorCode.STREAM_CLOSED); + endDone.complete(); + + // NOTE: The order of the window update frame / rst frame just + // happens to be like that ATM. + + // Await stream/connection window update frame. + var win = await nextFrame() as WindowUpdateFrame; + expect(win.header.streamId, 1); + expect(win.windowSizeIncrement, 1); + win = await nextFrame() as WindowUpdateFrame; + expect(win.header.streamId, 0); + expect(win.windowSizeIncrement, 1); + win = await nextFrame() as WindowUpdateFrame; + expect(win.header.streamId, 0); + expect(win.windowSizeIncrement, 1); + + await clientDone.future; + DataFrame finFrame = await nextFrame(); + expect(finFrame.hasEndStreamFlag, true); + + // Wait for the client finish. + expect(await serverReader.moveNext(), false); + await serverWriter.close(); + } + + Future clientFun() async { + await handshakeCompleter.future; + + var stream = client.makeRequest([new Header.ascii('a', 'b')]); + + // first will cancel the stream + var message = await stream.incomingMessages.first; + expect((message as DataStreamMessage).bytes, [42]); + cancelDone.complete(); + + await endDone.future; + + await stream.outgoingMessages.close(); + clientDone.complete(); + + await client.finish(); + } + + await Future.wait([serverFun(), clientFun()]); + }); + clientTest('client-reports-connection-error-on-push-to-nonexistent', (ClientTransportConnection client, FrameWriter serverWriter, From bd9da8602c4ed38730346fde16d327e13deaf900 Mon Sep 17 00:00:00 2001 From: Sigurd Meldgaard Date: Thu, 1 Nov 2018 13:29:17 +0100 Subject: [PATCH 079/172] Version bump --- pkgs/http2/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index a5c07dbd8d..46b869562b 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -1,5 +1,5 @@ name: http2 -version: 0.1.8+2 +version: 0.1.9 description: A HTTP/2 implementation in Dart. author: Dart Team homepage: https://github.com/dart-lang/http2 From ab71647f9c02facca805a8cb2313434ce13efc39 Mon Sep 17 00:00:00 2001 From: Sigurd Meldgaard Date: Thu, 1 Nov 2018 13:31:00 +0100 Subject: [PATCH 080/172] Update CHANGELOG.md --- pkgs/http2/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index ca5b22fd08..9702f9f899 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.1.9 +* Discard messages incoming after stream cancellation. + ## 0.1.8+2 * On connection termination, try to dispatch existing messages, thereby avoiding From bae76f59fffaa26d83e7e5f1a11cfe98ab0009b9 Mon Sep 17 00:00:00 2001 From: Alexandre Ardhuin Date: Mon, 26 Nov 2018 14:38:45 +0100 Subject: [PATCH 081/172] Update AUTHORS (dart-lang/http2#37) --- pkgs/http2/AUTHORS | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkgs/http2/AUTHORS b/pkgs/http2/AUTHORS index e8063a8cd6..93b7228f05 100644 --- a/pkgs/http2/AUTHORS +++ b/pkgs/http2/AUTHORS @@ -3,4 +3,6 @@ # # Name/Organization -Google Inc. +Google Inc. <*@google.com> + +Alexandre Ardhuin From ba85327d7288dd7585604092900470ee0e9632a4 Mon Sep 17 00:00:00 2001 From: Sigurd Meldgaard Date: Thu, 13 Dec 2018 14:41:56 +0100 Subject: [PATCH 082/172] Fill in README.md. (dart-lang/http2#38) * Fill in README.md --- pkgs/http2/README.md | 54 +++++++++++++++++++++++++++++-- pkgs/http2/lib/transport.dart | 60 ++--------------------------------- 2 files changed, 55 insertions(+), 59 deletions(-) diff --git a/pkgs/http2/README.md b/pkgs/http2/README.md index 886090f373..faf9d32c07 100644 --- a/pkgs/http2/README.md +++ b/pkgs/http2/README.md @@ -1,8 +1,57 @@ # HTTP/2 for Dart -Note: This is a work in progress and experimental. +This library provides an http/2 interface on top of a bidirectional stream of bytes. -See the [API docs][api] for more details and sample code. +## Usage: + +Here is a minimal example of connecting to a http/2 capable server, requesting a resource and +iterating over the response. + +```dart +import 'dart:convert'; +import 'dart:io'; + +import 'package:http2/transport.dart'; + +main() async { + var uri = Uri.parse('https://www.google.com/'); + + var transport = new ClientTransportConnection.viaSocket( + await SecureSocket.connect( + uri.host, + uri.port, + supportedProtocols: ['h2'], + ), + ); + + var stream = transport.makeRequest( + [ + new Header.ascii(':method', 'GET'), + new Header.ascii(':path', uri.path), + new Header.ascii(':scheme', uri.scheme), + new Header.ascii(':authority', uri.host), + ], + endStream: true, + ); + + await for (var message in stream.incomingMessages) { + if (message is HeadersStreamMessage) { + for (var header in message.headers) { + var name = utf8.decode(header.name); + var value = utf8.decode(header.value); + print('Header: $name: $value'); + } + } else if (message is DataStreamMessage) { + // Use [message.bytes] (but respect 'content-encoding' header) + } + } + await transport.finish(); +} +``` + +An example with better error handling is available [here][example]. + +See the [API docs][api] for more details. ## Features and bugs @@ -10,3 +59,4 @@ Please file feature requests and bugs at the [issue tracker][tracker]. [tracker]: https://github.com/dart-lang/http2/issues [api]: http://www.dartdocs.org/documentation/http2/latest +[example]: https://github.com/dart-lang/http2/blob/master/example/display_headers.dart. diff --git a/pkgs/http2/lib/transport.dart b/pkgs/http2/lib/transport.dart index e19c90197c..8af154e769 100644 --- a/pkgs/http2/lib/transport.dart +++ b/pkgs/http2/lib/transport.dart @@ -28,7 +28,7 @@ /// of this library. There are 3 common ways to achive this /// /// * connect to a server via SSL and use the ALPN (SSL) protocol extension -/// to negogiate with the server to speak http/2 (the ALPN protocol +/// to negotiate with the server to speak http/2 (the ALPN protocol /// identifier for http/2 is `h2`) /// /// * have prior knowledge about the server - i.e. know ahead of time that @@ -40,62 +40,8 @@ /// `dart:io`s secure socket implementation (by using a `SecurityContext` and /// including 'h2' in the list of protocols used for ALPN). /// -/// Here is a simple example on how to connect to a http/2 capable server and -/// requesting a resource: -/// -/// import 'dart:async'; -/// import 'dart:convert'; -/// import 'dart:io'; -/// -/// import 'package:http2/transport.dart'; -/// -/// main() async { -/// var uri = Uri.parse("https://www.google.com/"); -/// -/// var socket = await connect(uri); -/// -/// // The default client settings will disable server pushes. We -/// // therefore do not need to deal with [stream.peerPushes]. -/// var transport = new ClientTransportConnection.viaSocket(socket); -/// -/// var headers = [ -/// new Header.ascii(':method', 'GET'), -/// new Header.ascii(':path', uri.path), -/// new Header.ascii(':scheme', uri.scheme), -/// new Header.ascii(':authority', uri.host), -/// ]; -/// -/// var stream = transport.makeRequest(headers, endStream: true); -/// await for (var message in stream.incomingMessages) { -/// if (message is HeadersStreamMessage) { -/// for (var header in message.headers) { -/// var name = UTF8.decode(header.name); -/// var value = UTF8.decode(header.value); -/// print('$name: $value'); -/// } -/// } else if (message is DataStreamMessage) { -/// // Use [message.bytes] (but respect 'content-encoding' header) -/// } -/// } -/// await transport.finish(); -/// } -/// -/// Future connect(Uri uri) async { -/// bool useSSL = uri.scheme == 'https'; -/// if (useSSL) { -/// var secureSocket = await SecureSocket.connect( -/// uri.host, uri.port, supportedProtocols: ['h2']); -/// if (secureSocket.selectedProtocol != 'h2') { -/// throw new Exception( -/// "Failed to negogiate http/2 via alpn. Maybe server " -/// "doesn't support http/2."); -/// } -/// return secureSocket; -/// } else { -/// return await Socket.connect(uri.host, uri.port); -/// } -/// } -/// +/// A simple example on how to connect to a http/2 capable server and +/// requesting a resource is available at https://github.com/dart-lang/http2/blob/master/example/display_headers.dart. library http2.transport; import 'dart:async'; From fd23e63a74a6c9d4343451f6e716494adc94b9b3 Mon Sep 17 00:00:00 2001 From: Sigurd Meldgaard Date: Fri, 14 Dec 2018 13:25:02 +0100 Subject: [PATCH 083/172] Add http2.dart and prepare for 1.0 (dart-lang/http2#39) * Add http2.dart, prepare for 1.0 --- pkgs/http2/CHANGELOG.md | 6 +++++ pkgs/http2/README.md | 2 +- pkgs/http2/lib/http2.dart | 48 +++++++++++++++++++++++++++++++++++ pkgs/http2/lib/transport.dart | 42 ------------------------------ pkgs/http2/pubspec.yaml | 4 +-- 5 files changed, 57 insertions(+), 45 deletions(-) create mode 100644 pkgs/http2/lib/http2.dart diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index 9702f9f899..641a73b180 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -1,4 +1,10 @@ +## 1.0 + +* Graduate package to 1.0. +* `package:http2/http2.dart` now reexports `package:http2/transport.dart`. + ## 0.1.9 + * Discard messages incoming after stream cancellation. ## 0.1.8+2 diff --git a/pkgs/http2/README.md b/pkgs/http2/README.md index faf9d32c07..b9102827ec 100644 --- a/pkgs/http2/README.md +++ b/pkgs/http2/README.md @@ -11,7 +11,7 @@ iterating over the response. import 'dart:convert'; import 'dart:io'; -import 'package:http2/transport.dart'; +import 'package:http2/http2.dart'; main() async { var uri = Uri.parse('https://www.google.com/'); diff --git a/pkgs/http2/lib/http2.dart b/pkgs/http2/lib/http2.dart new file mode 100644 index 0000000000..3f1ed78404 --- /dev/null +++ b/pkgs/http2/lib/http2.dart @@ -0,0 +1,48 @@ +// Copyright (c) 2018, 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. + +/// This library provides an http/2 interface on top of a bidirectional stream +/// of bytes. +/// +/// The client and server sides can be created via [ClientTransportStream] and +/// [ServerTransportStream] respectively. Both sides can be configured via +/// settings (see [ClientSettings] and [ServerSettings]). The settings will be +/// communicated to the remote peer (if necessary) and will be valid during the +/// entire lifetime of the connection. +/// +/// A http/2 transport allows a client to open a bidirectional stream (see +/// [ClientTransportConnection.makeRequest]) and a server can open (or push) a +/// unidirectional stream to the client via [ServerTransportStream.push]. +/// +/// In both cases (unidirectional and bidirectional stream), one can send +/// headers and data to the other side (via [HeadersStreamMessage] and +/// [DataStreamMessage]). These messages are ordered and will arrive in the same +/// order as they were sent (data messages may be split up into multiple smaller +/// chunks or might be combined). +/// +/// In the most common case, each direction will send one [HeadersStreamMessage] +/// followed by zero or more [DataStreamMessage]s. +/// +/// Establishing a bidirectional stream of bytes to a server is up to the user +/// of this library. There are 3 common ways to achive this +/// +/// * connect to a server via SSL and use the ALPN (SSL) protocol extension +/// to negotiate with the server to speak http/2 (the ALPN protocol +/// identifier for http/2 is `h2`) +/// +/// * have prior knowledge about the server - i.e. know ahead of time that +/// the server will speak http/2 via an unencrypted tcp connection +/// +/// * use a http/1.1 connection and upgrade it to http/2 +/// +/// The first way is the most common way and can be done in Dart by using +/// `dart:io`s secure socket implementation (by using a `SecurityContext` and +/// including 'h2' in the list of protocols used for ALPN). +/// +/// A simple example on how to connect to a http/2 capable server and +/// requesting a resource is available at https://github.com/dart-lang/http2/blob/master/example/display_headers.dart. +library http2.http2; + +import 'transport.dart'; +export 'transport.dart'; diff --git a/pkgs/http2/lib/transport.dart b/pkgs/http2/lib/transport.dart index 8af154e769..c0ec7bd049 100644 --- a/pkgs/http2/lib/transport.dart +++ b/pkgs/http2/lib/transport.dart @@ -2,48 +2,6 @@ // 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. -/// This library provides an http/2 interface on top of a bidirectional stream -/// of bytes. -/// -/// The client and server sides can be created via [ClientTransportStream] and -/// [ServerTransportStream] respectively. Both sides can be configured via -/// settings (see [ClientSettings] and [ServerSettings]). The settings will be -/// communicated to the remote peer (if necessary) and will be valid during the -/// entire lifetime of the connection. -/// -/// A http/2 transport allows a client to open a bidirectional stream (see -/// [ClientTransportConnection.makeRequest]) and a server can open (or push) a -/// unidirectional stream to the client via [ServerTransportStream.push]. -/// -/// In both cases (unidirectional and bidirectional stream), one can send -/// headers and data to the other side (via [HeadersStreamMessage] and -/// [DataStreamMessage]). These messages are ordered and will arrive in the same -/// order as they were sent (data messages may be split up into multiple smaller -/// chunks or might be combined). -/// -/// In the most common case, each direction will send one [HeadersStreamMessage] -/// followed by zero or more [DataStreamMessage]s. -/// -/// Establishing a bidirectional stream of bytes to a server is up to the user -/// of this library. There are 3 common ways to achive this -/// -/// * connect to a server via SSL and use the ALPN (SSL) protocol extension -/// to negotiate with the server to speak http/2 (the ALPN protocol -/// identifier for http/2 is `h2`) -/// -/// * have prior knowledge about the server - i.e. know ahead of time that -/// the server will speak http/2 via an unencrypted tcp connection -/// -/// * use a http/1.1 connection and upgrade it to http/2 -/// -/// The first way is the most common way and can be done in Dart by using -/// `dart:io`s secure socket implementation (by using a `SecurityContext` and -/// including 'h2' in the list of protocols used for ALPN). -/// -/// A simple example on how to connect to a http/2 capable server and -/// requesting a resource is available at https://github.com/dart-lang/http2/blob/master/example/display_headers.dart. -library http2.transport; - import 'dart:async'; import 'dart:io'; diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index 46b869562b..e3e265171c 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -1,5 +1,5 @@ name: http2 -version: 0.1.9 +version: 1.0.0 description: A HTTP/2 implementation in Dart. author: Dart Team homepage: https://github.com/dart-lang/http2 @@ -8,5 +8,5 @@ environment: sdk: '>=2.0.0-dev.56.0 <3.0.0' dev_dependencies: - mockito: ^3.0.0-beta+2 + mockito: ^4.0.0 test: ^1.2.0 From 07831ac7eb993bebc4c3004dec5f395b2e8dda8e Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Fri, 12 Apr 2019 11:34:30 -0700 Subject: [PATCH 084/172] Update analysis_options.yaml --- pkgs/http2/analysis_options.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/pkgs/http2/analysis_options.yaml b/pkgs/http2/analysis_options.yaml index 7244d25012..56f73a7da2 100644 --- a/pkgs/http2/analysis_options.yaml +++ b/pkgs/http2/analysis_options.yaml @@ -1,5 +1,4 @@ analyzer: - strong-mode: true errors: unused_element: error unused_import: error From 287d4d0b332b6be71cdb5fa58ec83676d48343ea Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 29 May 2019 12:25:27 -0700 Subject: [PATCH 085/172] Remove codereview.settings --- pkgs/http2/codereview.settings | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 pkgs/http2/codereview.settings diff --git a/pkgs/http2/codereview.settings b/pkgs/http2/codereview.settings deleted file mode 100644 index 69e23ea6d8..0000000000 --- a/pkgs/http2/codereview.settings +++ /dev/null @@ -1,3 +0,0 @@ -CODE_REVIEW_SERVER: https://codereview.chromium.org/ -VIEW_VC: https://github.com/dart-lang/http2/commit/ -CC_LIST: reviews@dartlang.org From 1bf896c718daeb07da2ec77849820765a8c502cc Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 29 May 2019 12:26:33 -0700 Subject: [PATCH 086/172] Fix API link --- pkgs/http2/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/http2/README.md b/pkgs/http2/README.md index b9102827ec..b84dff0096 100644 --- a/pkgs/http2/README.md +++ b/pkgs/http2/README.md @@ -58,5 +58,5 @@ See the [API docs][api] for more details. Please file feature requests and bugs at the [issue tracker][tracker]. [tracker]: https://github.com/dart-lang/http2/issues -[api]: http://www.dartdocs.org/documentation/http2/latest +[api]: https://pub.dev/documentation/http2/latest/ [example]: https://github.com/dart-lang/http2/blob/master/example/display_headers.dart. From 6b68d6401cceef8290d803aae57180bd06d48ea1 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 29 May 2019 12:27:56 -0700 Subject: [PATCH 087/172] Travis: test on oldest support Dart SDK --- pkgs/http2/.travis.yml | 11 +++++++++-- pkgs/http2/pubspec.yaml | 4 ++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/pkgs/http2/.travis.yml b/pkgs/http2/.travis.yml index 9d69cddf2b..ea1a3533e6 100644 --- a/pkgs/http2/.travis.yml +++ b/pkgs/http2/.travis.yml @@ -1,11 +1,18 @@ language: dart + dart: + - 2.0.0 - dev dart_task: - test - - dartfmt - - dartanalyzer + - dartanalyzer: --fatal-infos --fatal-warnings . + +matrix: + include: + # Only validate formatting using the dev release + - dart: dev + dart_task: dartfmt # Only building master means that we don't run two builds for each pull request. branches: diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index e3e265171c..422bea5788 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -1,11 +1,11 @@ name: http2 -version: 1.0.0 +version: 1.0.1-dev description: A HTTP/2 implementation in Dart. author: Dart Team homepage: https://github.com/dart-lang/http2 environment: - sdk: '>=2.0.0-dev.56.0 <3.0.0' + sdk: '>=2.0.0 <3.0.0' dev_dependencies: mockito: ^4.0.0 From 12b65a2b51da14c85997826e4e3c52a5891601cc Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 29 May 2019 12:34:13 -0700 Subject: [PATCH 088/172] Update .gitignore --- pkgs/http2/.gitignore | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/pkgs/http2/.gitignore b/pkgs/http2/.gitignore index a19c373b84..ac98e87d12 100644 --- a/pkgs/http2/.gitignore +++ b/pkgs/http2/.gitignore @@ -1,16 +1,4 @@ # Don’t commit the following directories created by pub. -build/ +.dart_tool .packages -.pub/ -packages -.buildlog - -# Or the files created by dart2js. -*.dart.js -*.dart.precompiled.js -*.js_ -*.js.deps -*.js.map - -# Include when developing application packages. pubspec.lock From da54c648f9f8817ef735244c0f0c78b081532fdc Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 29 May 2019 12:42:48 -0700 Subject: [PATCH 089/172] nghttp2.org seems to be down --- pkgs/http2/.travis.yml | 6 ++- pkgs/http2/dart_test.yaml | 2 + pkgs/http2/test/client_websites_test.dart | 56 +++++++++++------------ 3 files changed, 35 insertions(+), 29 deletions(-) create mode 100644 pkgs/http2/dart_test.yaml diff --git a/pkgs/http2/.travis.yml b/pkgs/http2/.travis.yml index ea1a3533e6..7968b29c66 100644 --- a/pkgs/http2/.travis.yml +++ b/pkgs/http2/.travis.yml @@ -5,7 +5,8 @@ dart: - dev dart_task: - - test + - test: --exclude-tags flaky + - test: --tags flaky - dartanalyzer: --fatal-infos --fatal-warnings . matrix: @@ -13,6 +14,9 @@ matrix: # Only validate formatting using the dev release - dart: dev dart_task: dartfmt + allow_failures: + - dart_task: + test: --tags flaky # Only building master means that we don't run two builds for each pull request. branches: diff --git a/pkgs/http2/dart_test.yaml b/pkgs/http2/dart_test.yaml new file mode 100644 index 0000000000..7bcbfc9ae6 --- /dev/null +++ b/pkgs/http2/dart_test.yaml @@ -0,0 +1,2 @@ +tags: + flaky: # Tests that should be run as a separate job on Travis diff --git a/pkgs/http2/test/client_websites_test.dart b/pkgs/http2/test/client_websites_test.dart index 8b5b6ec7f0..4a9616f525 100644 --- a/pkgs/http2/test/client_websites_test.dart +++ b/pkgs/http2/test/client_websites_test.dart @@ -12,36 +12,36 @@ import 'package:http2/src/testing/client.dart'; import 'package:test/test.dart'; main() async { - group('end2end', () { - test('google', () async { - var uri = Uri.parse("https://www.google.com/"); - ClientConnection connection = await connect(uri); - Response response = await connection.makeRequest(new Request('GET', uri)); - dumpHeaders(uri, response.headers); - - final utf8Decoder = new Utf8Decoder(allowMalformed: true); - String body = await response.stream.transform(utf8Decoder).join(''); - connection.close(); - - body = body.toLowerCase(); - expect(body, contains('')); - expect(body, contains('twitter.com')); - }); + expect(body, contains('')); + expect(body, contains('twitter.com')); + }); - test('nghttp2.org - server push enabled', () async { + group('nghttp2.org - ', () { + test('server push enabled', () async { var uri = Uri.parse("https://nghttp2.org/"); ClientConnection connection = await connect(uri, allowServerPushes: true); @@ -82,7 +82,7 @@ main() async { await connection.close(); }); - test('nghttp2.org - server push disabled', () async { + test('server push disabled', () async { var uri = Uri.parse("https://nghttp2.org/"); ClientConnection connection = @@ -112,7 +112,7 @@ main() async { expect(pushes, hasLength(0)); await connection.close(); }); - }); + }, tags: ['flaky']); } dumpHeaders(Uri uri, Map> headers, From 55c069b8e590f93588b894016b694fbf25336ebf Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 29 May 2019 12:45:15 -0700 Subject: [PATCH 090/172] Remove duplicate lint, enable/fix prefer_single_quotes --- pkgs/http2/analysis_options.yaml | 3 +-- pkgs/http2/example/display_headers.dart | 2 +- pkgs/http2/experimental/server.dart | 4 ++-- pkgs/http2/lib/multiprotocol_server.dart | 2 +- pkgs/http2/lib/src/artificial_server_socket.dart | 2 +- pkgs/http2/lib/src/frames/frames.dart | 8 ++++---- pkgs/http2/test/client_websites_test.dart | 8 ++++---- 7 files changed, 14 insertions(+), 15 deletions(-) diff --git a/pkgs/http2/analysis_options.yaml b/pkgs/http2/analysis_options.yaml index 56f73a7da2..f8836b674d 100644 --- a/pkgs/http2/analysis_options.yaml +++ b/pkgs/http2/analysis_options.yaml @@ -26,10 +26,9 @@ linter: - only_throw_errors - prefer_final_fields - prefer_is_not_empty - #- prefer_single_quotes + - prefer_single_quotes - slash_for_doc_comments - test_types_in_equals - - test_types_in_equals - throw_in_finally - type_init_formals - unrelated_type_equality_checks diff --git a/pkgs/http2/example/display_headers.dart b/pkgs/http2/example/display_headers.dart index c1faa4df44..91e2774b4d 100644 --- a/pkgs/http2/example/display_headers.dart +++ b/pkgs/http2/example/display_headers.dart @@ -53,7 +53,7 @@ Future connect(Uri uri) async { var secureSocket = await SecureSocket.connect(uri.host, uri.port, supportedProtocols: ['h2']); if (secureSocket.selectedProtocol != 'h2') { - throw new Exception("Failed to negogiate http/2 via alpn. Maybe server " + throw new Exception('Failed to negogiate http/2 via alpn. Maybe server ' "doesn't support http/2."); } return secureSocket; diff --git a/pkgs/http2/experimental/server.dart b/pkgs/http2/experimental/server.dart index 6637cc28f4..e1f25d63a8 100644 --- a/pkgs/http2/experimental/server.dart +++ b/pkgs/http2/experimental/server.dart @@ -30,8 +30,8 @@ main() async { runZoned(() { server.listen(handleClient); }, onError: (e, s) { - print("Unexpected error: $e"); - print("Unexpected error - stack: $s"); + print('Unexpected error: $e'); + print('Unexpected error - stack: $s'); }); } diff --git a/pkgs/http2/lib/multiprotocol_server.dart b/pkgs/http2/lib/multiprotocol_server.dart index 6fd168e03e..7d1f7eac16 100644 --- a/pkgs/http2/lib/multiprotocol_server.dart +++ b/pkgs/http2/lib/multiprotocol_server.dart @@ -82,7 +82,7 @@ class MultiProtocolHttpServer { onDone: () => _http2Connections.remove(connection)); } else { socket.destroy(); - throw new Exception("Unexpected negotiated ALPN protocol: $protocol."); + throw new Exception('Unexpected negotiated ALPN protocol: $protocol.'); } }, onError: onError); diff --git a/pkgs/http2/lib/src/artificial_server_socket.dart b/pkgs/http2/lib/src/artificial_server_socket.dart index 4c5cf07b6f..9d2589f7e7 100644 --- a/pkgs/http2/lib/src/artificial_server_socket.dart +++ b/pkgs/http2/lib/src/artificial_server_socket.dart @@ -29,6 +29,6 @@ class ArtificialServerSocket extends StreamView /// Closing of an [ArtificialServerSocket] is not possible and an exception /// will be thrown when calling this method. Future close() async { - throw new Exception("Did not expect close() to be called."); + throw new Exception('Did not expect close() to be called.'); } } diff --git a/pkgs/http2/lib/src/frames/frames.dart b/pkgs/http2/lib/src/frames/frames.dart index 8374eee9a7..f6e74d250f 100644 --- a/pkgs/http2/lib/src/frames/frames.dart +++ b/pkgs/http2/lib/src/frames/frames.dart @@ -14,7 +14,7 @@ import '../hpack/hpack.dart'; import '../settings/settings.dart'; import '../sync_errors.dart'; -part "frame_types.dart"; -part "frame_utils.dart"; -part "frame_reader.dart"; -part "frame_writer.dart"; +part 'frame_types.dart'; +part 'frame_utils.dart'; +part 'frame_reader.dart'; +part 'frame_writer.dart'; diff --git a/pkgs/http2/test/client_websites_test.dart b/pkgs/http2/test/client_websites_test.dart index 4a9616f525..679151ad26 100644 --- a/pkgs/http2/test/client_websites_test.dart +++ b/pkgs/http2/test/client_websites_test.dart @@ -13,7 +13,7 @@ import 'package:test/test.dart'; main() async { test('google', () async { - var uri = Uri.parse("https://www.google.com/"); + var uri = Uri.parse('https://www.google.com/'); ClientConnection connection = await connect(uri); Response response = await connection.makeRequest(new Request('GET', uri)); dumpHeaders(uri, response.headers); @@ -28,7 +28,7 @@ main() async { }); test('twitter', () async { - var uri = Uri.parse("https://twitter.com/"); + var uri = Uri.parse('https://twitter.com/'); ClientConnection connection = await connect(uri); Response response = await connection.makeRequest(new Request('GET', uri)); dumpHeaders(uri, response.headers); @@ -42,7 +42,7 @@ main() async { group('nghttp2.org - ', () { test('server push enabled', () async { - var uri = Uri.parse("https://nghttp2.org/"); + var uri = Uri.parse('https://nghttp2.org/'); ClientConnection connection = await connect(uri, allowServerPushes: true); var request = new Request('GET', uri); @@ -83,7 +83,7 @@ main() async { }); test('server push disabled', () async { - var uri = Uri.parse("https://nghttp2.org/"); + var uri = Uri.parse('https://nghttp2.org/'); ClientConnection connection = await connect(uri, allowServerPushes: false); From 5c2a3924a582eebcb9d8127e2a79f719596547c5 Mon Sep 17 00:00:00 2001 From: Todd Volkert Date: Tue, 25 Jun 2019 05:51:58 -0700 Subject: [PATCH 091/172] Prepare for upcoming change to HttpRequest and HttpClientResponse (dart-lang/http2#44) An upcoming change to the Dart SDK will change `HttpRequest` and `HttpClientResponse` from implementing `Stream>` to implementing `Stream`. This forwards-compatible change prepares for that SDK breaking change by casting the Stream to `List` before transforming it. https://github.com/dart-lang/sdk/issues/36900 --- pkgs/http2/test/multiprotocol_server_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/http2/test/multiprotocol_server_test.dart b/pkgs/http2/test/multiprotocol_server_test.dart index dbbcaf8a8b..5b040e1986 100644 --- a/pkgs/http2/test/multiprotocol_server_test.dart +++ b/pkgs/http2/test/multiprotocol_server_test.dart @@ -71,7 +71,7 @@ Future makeHttp11Request( var request = await client.getUrl(Uri.parse('https://localhost:${server.port}/abc$i')); var response = await request.close(); - var body = await response.transform(utf8.decoder).join(''); + var body = await response.cast>().transform(utf8.decoder).join(''); expect(body, 'answer$i'); } From 57db3b23c20020d8e2aed96fda7c8fc0006bc49a Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 10 Jul 2019 10:29:04 -0700 Subject: [PATCH 092/172] Fix lints (dart-lang/http2#45) --- pkgs/http2/lib/src/frames/frame_reader.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/http2/lib/src/frames/frame_reader.dart b/pkgs/http2/lib/src/frames/frame_reader.dart index 2812dc6cd4..884520388e 100644 --- a/pkgs/http2/lib/src/frames/frame_reader.dart +++ b/pkgs/http2/lib/src/frames/frame_reader.dart @@ -10,7 +10,7 @@ class FrameReader { /// Connection settings which this reader needs to ensure the remote end is /// complying with. - ActiveSettings _localSettings; + final ActiveSettings _localSettings; StreamSubscription> _subscription; StreamController _framesController; From ffe665e24d40df16f1d17d905fb9c6ec7747b29c Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 20 Feb 2020 00:02:25 -0800 Subject: [PATCH 093/172] Remove unused private fields (dart-lang/http2#55) Fixes existing hints from the analyzer --- pkgs/http2/lib/src/hpack/hpack.dart | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pkgs/http2/lib/src/hpack/hpack.dart b/pkgs/http2/lib/src/hpack/hpack.dart index 4821511f73..4ce59ebfa6 100644 --- a/pkgs/http2/lib/src/hpack/hpack.dart +++ b/pkgs/http2/lib/src/hpack/hpack.dart @@ -167,14 +167,9 @@ class HPackDecoder { // - without huffman encoding // - without using the dynamic table class HPackEncoder { - int _maxHeaderTableSize = 4096; - - final IndexTable _table = new IndexTable(); - void updateMaxSendingHeaderTableSize(int newMaximumSize) { // TODO: Once we start encoding via dynamic table we need to let the other // side know the maximum table size we're using. - _maxHeaderTableSize = newMaximumSize; } List encode(List
headers) { From 9d5ceecd7a9a7f36601482c09e6059af227baa26 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 20 Feb 2020 00:08:07 -0800 Subject: [PATCH 094/172] Enable and fix lint annotate_overrides (dart-lang/http2#56) --- pkgs/http2/analysis_options.yaml | 2 +- pkgs/http2/lib/src/artificial_server_socket.dart | 3 +++ pkgs/http2/lib/src/connection.dart | 4 ++++ pkgs/http2/lib/src/flowcontrol/connection_queues.dart | 4 ++++ pkgs/http2/lib/src/flowcontrol/queue_messages.dart | 5 +++++ pkgs/http2/lib/src/flowcontrol/stream_queues.dart | 3 +++ pkgs/http2/lib/src/frames/frame_types.dart | 11 +++++++++++ pkgs/http2/lib/src/hpack/hpack.dart | 1 + pkgs/http2/lib/src/hpack/huffman.dart | 1 + pkgs/http2/lib/src/ping/ping_handler.dart | 1 + pkgs/http2/lib/src/settings/settings.dart | 1 + pkgs/http2/lib/src/streams/stream_handler.dart | 11 +++++++++++ pkgs/http2/lib/src/sync_errors.dart | 6 ++++++ pkgs/http2/lib/transport.dart | 4 ++++ .../test/src/flowcontrol/connection_queues_test.dart | 4 ++++ .../test/src/flowcontrol/stream_queues_test.dart | 2 ++ pkgs/http2/test/src/hpack/hpack_test.dart | 2 ++ 17 files changed, 64 insertions(+), 1 deletion(-) diff --git a/pkgs/http2/analysis_options.yaml b/pkgs/http2/analysis_options.yaml index f8836b674d..47de419c6b 100644 --- a/pkgs/http2/analysis_options.yaml +++ b/pkgs/http2/analysis_options.yaml @@ -6,7 +6,7 @@ analyzer: dead_code: error linter: rules: - #- annotate_overrides + - annotate_overrides - avoid_empty_else - avoid_init_to_null - avoid_return_types_on_setters diff --git a/pkgs/http2/lib/src/artificial_server_socket.dart b/pkgs/http2/lib/src/artificial_server_socket.dart index 9d2589f7e7..ca94e839be 100644 --- a/pkgs/http2/lib/src/artificial_server_socket.dart +++ b/pkgs/http2/lib/src/artificial_server_socket.dart @@ -22,12 +22,15 @@ class ArtificialServerSocket extends StreamView // These are the methods of [ServerSocket] in addition to [Stream]. // ######################################################################## + @override final InternetAddress address; + @override final int port; /// Closing of an [ArtificialServerSocket] is not possible and an exception /// will be thrown when calling this method. + @override Future close() async { throw new Exception('Did not expect close() to be called.'); } diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index 271b86f653..07a3bb7d17 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -60,6 +60,7 @@ class ConnectionState { bool get passiveFinishing => state == Finishing && (finishingState & FinishingPassive) != 0; + @override String toString() { String message = ''; @@ -452,9 +453,11 @@ class ClientConnection extends Connection implements ClientTransportConnection { return new ClientConnection._(incoming, outgoing, clientSettings); } + @override bool get isOpen => !_state.isFinishing && !_state.isTerminated && _streams.canOpenStream; + @override ClientTransportStream makeRequest(List
headers, {bool endStream: false}) { if (_state.isFinishing) { @@ -485,6 +488,7 @@ class ServerConnection extends Connection implements ServerTransportConnection { return new ServerConnection._(frameBytes, outgoing, serverSettings); } + @override Stream get incomingStreams => _streams.incomingStreams.cast(); } diff --git a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart index 4d28db23df..bbc98889fb 100644 --- a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart @@ -66,11 +66,13 @@ class ConnectionMessageQueueOut extends Object }); } + @override void onTerminated(error) { _messages.clear(); closeWithError(error); } + @override void onCheckForClose() { if (isClosing && _messages.length == 0) { closeWithValue(); @@ -199,6 +201,7 @@ class ConnectionMessageQueueIn extends Object ConnectionMessageQueueIn( this._windowUpdateHandler, this._catchProtocolErrors); + @override void onTerminated(error) { // NOTE: The higher level will be shutdown first, so all streams // should have been removed at this point. @@ -207,6 +210,7 @@ class ConnectionMessageQueueIn extends Object closeWithError(error); } + @override void onCheckForClose() { if (isClosing) { assert(_stream2messageQueue.isEmpty == _stream2pendingMessages.isEmpty); diff --git a/pkgs/http2/lib/src/flowcontrol/queue_messages.dart b/pkgs/http2/lib/src/flowcontrol/queue_messages.dart index b30ab3f279..33db75b064 100644 --- a/pkgs/http2/lib/src/flowcontrol/queue_messages.dart +++ b/pkgs/http2/lib/src/flowcontrol/queue_messages.dart @@ -24,6 +24,7 @@ class HeadersMessage extends Message { HeadersMessage(int streamId, this.headers, bool endStream) : super(streamId, endStream); + @override String toString() => 'HeadersMessage(headers: ${headers.length}, endStream: $endStream)'; } @@ -34,6 +35,7 @@ class DataMessage extends Message { DataMessage(int streamId, this.bytes, bool endStream) : super(streamId, endStream); + @override String toString() => 'DataMessage(bytes: ${bytes.length}, endStream: $endStream)'; } @@ -47,6 +49,7 @@ class PushPromiseMessage extends Message { this.pushedStream, bool endStream) : super(streamId, endStream); + @override String toString() => 'PushPromiseMessage(bytes: ${headers.length}, ' 'promisedStreamId: $promisedStreamId, endStream: $endStream)'; } @@ -56,6 +59,7 @@ class ResetStreamMessage extends Message { ResetStreamMessage(int streamId, this.errorCode) : super(streamId, false); + @override String toString() => 'ResetStreamMessage(errorCode: $errorCode)'; } @@ -67,6 +71,7 @@ class GoawayMessage extends Message { GoawayMessage(this.lastStreamId, this.errorCode, this.debugData) : super(0, false); + @override String toString() => 'GoawayMessage(lastStreamId: ${lastStreamId}, ' 'errorCode: ${errorCode}, debugData: ${debugData.length})'; } diff --git a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart index dc6d2f2bc5..58d65502dd 100644 --- a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart @@ -85,11 +85,13 @@ class StreamMessageQueueOut extends Object } } + @override void onTerminated(error) { _messages.clear(); closeWithError(error); } + @override void onCheckForClose() { if (isClosing && _messages.isEmpty) closeWithValue(); } @@ -240,6 +242,7 @@ class StreamMessageQueueIn extends Object }); } + @override void onTerminated(exception) { _pendingMessages.clear(); if (!wasClosed) { diff --git a/pkgs/http2/lib/src/frames/frame_types.dart b/pkgs/http2/lib/src/frames/frame_types.dart index 205f2a8a7e..35ff3f9ebf 100644 --- a/pkgs/http2/lib/src/frames/frame_types.dart +++ b/pkgs/http2/lib/src/frames/frame_types.dart @@ -72,6 +72,7 @@ class DataFrame extends Frame { bool get hasEndStreamFlag => _isFlagSet(header.flags, FLAG_END_STREAM); bool get hasPaddedFlag => _isFlagSet(header.flags, FLAG_PADDED); + @override Map toJson() => super.toJson() ..addAll({ 'padLength': padLength, @@ -129,6 +130,7 @@ class HeadersFrame extends Frame { streamDependency, weight, mergedHeaderBlockFragment); } + @override Map toJson() => super.toJson() ..addAll({ 'padLength': padLength, @@ -150,6 +152,7 @@ class PriorityFrame extends Frame { this.streamDependency, this.weight) : super(header); + @override Map toJson() => super.toJson() ..addAll({ 'exclusiveDependency': exclusiveDependency, @@ -165,6 +168,7 @@ class RstStreamFrame extends Frame { RstStreamFrame(FrameHeader header, this.errorCode) : super(header); + @override Map toJson() => super.toJson() ..addAll({ 'errorCode': errorCode, @@ -199,6 +203,7 @@ class SettingsFrame extends Frame { bool get hasAckFlag => _isFlagSet(header.flags, FLAG_ACK); + @override Map toJson() => super.toJson() ..addAll({ 'settings': settings.map((s) => s.toJson()).toList(), @@ -246,6 +251,7 @@ class PushPromiseFrame extends Frame { fh, padLength, promisedStreamId, mergedHeaderBlockFragment); } + @override Map toJson() => super.toJson() ..addAll({ 'padLength': padLength, @@ -265,6 +271,7 @@ class PingFrame extends Frame { bool get hasAckFlag => _isFlagSet(header.flags, FLAG_ACK); + @override Map toJson() => super.toJson() ..addAll({ 'opaqueData': opaqueData, @@ -280,6 +287,7 @@ class GoawayFrame extends Frame { FrameHeader header, this.lastStreamId, this.errorCode, this.debugData) : super(header); + @override Map toJson() => super.toJson() ..addAll({ 'lastStreamId': lastStreamId, @@ -296,6 +304,7 @@ class WindowUpdateFrame extends Frame { WindowUpdateFrame(FrameHeader header, this.windowSizeIncrement) : super(header); + @override Map toJson() => super.toJson() ..addAll({ 'windowSizeIncrement': windowSizeIncrement, @@ -312,6 +321,7 @@ class ContinuationFrame extends Frame { bool get hasEndHeadersFlag => _isFlagSet(header.flags, FLAG_END_HEADERS); + @override Map toJson() => super.toJson() ..addAll({ 'headerBlockFragment (length)': headerBlockFragment.length, @@ -323,6 +333,7 @@ class UnknownFrame extends Frame { UnknownFrame(FrameHeader header, this.data) : super(header); + @override Map toJson() => super.toJson() ..addAll({ 'data (length)': data.length, diff --git a/pkgs/http2/lib/src/hpack/hpack.dart b/pkgs/http2/lib/src/hpack/hpack.dart index 4ce59ebfa6..75316f0621 100644 --- a/pkgs/http2/lib/src/hpack/hpack.dart +++ b/pkgs/http2/lib/src/hpack/hpack.dart @@ -21,6 +21,7 @@ class HPackDecodingException implements Exception { HPackDecodingException(this._message); + @override String toString() => 'HPackDecodingException: $_message'; } diff --git a/pkgs/http2/lib/src/hpack/huffman.dart b/pkgs/http2/lib/src/hpack/huffman.dart index 7dd4d09514..95d16d08c8 100644 --- a/pkgs/http2/lib/src/hpack/huffman.dart +++ b/pkgs/http2/lib/src/hpack/huffman.dart @@ -13,6 +13,7 @@ class HuffmanDecodingException implements Exception { HuffmanDecodingException(this._message); + @override String toString() => 'HuffmanDecodingException: $_message'; } diff --git a/pkgs/http2/lib/src/ping/ping_handler.dart b/pkgs/http2/lib/src/ping/ping_handler.dart index bd8bc17e92..d5bef228f7 100644 --- a/pkgs/http2/lib/src/ping/ping_handler.dart +++ b/pkgs/http2/lib/src/ping/ping_handler.dart @@ -22,6 +22,7 @@ class PingHandler extends Object with TerminatableMixin { PingHandler(this._frameWriter); + @override void onTerminated(error) { var values = _remainingPings.values.toList(); _remainingPings.clear(); diff --git a/pkgs/http2/lib/src/settings/settings.dart b/pkgs/http2/lib/src/settings/settings.dart index 6ee2284561..00fe640cd9 100644 --- a/pkgs/http2/lib/src/settings/settings.dart +++ b/pkgs/http2/lib/src/settings/settings.dart @@ -153,6 +153,7 @@ class SettingsHandler extends Object with TerminatableMixin { }); } + @override void onTerminated(error) { _toBeAcknowledgedSettings.clear(); _toBeAcknowledgedCompleters diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index 0f79b52e3e..a13c5cde99 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -43,6 +43,7 @@ class Http2StreamImpl extends TransportStream /// /// * odd numbered streams are client streams /// * even numbered streams are opened from the server + @override final int id; // The queue for incoming [StreamMessage]s. @@ -85,25 +86,32 @@ class Http2StreamImpl extends TransportStream this._terminateStreamFun); /// A stream of data and/or headers from the remote end. + @override Stream get incomingMessages => incomingQueue.messages; /// A sink for writing data and/or headers to the remote end. + @override StreamSink get outgoingMessages => _outgoingC.sink; /// Streams which the server pushed to this endpoint. + @override Stream get peerPushes => incomingQueue.serverPushes; + @override bool get canPush => _canPushFun(this); /// Pushes a new stream to a client. /// /// The [requestHeaders] are the headers to which the pushed stream /// responds to. + @override ServerTransportStream push(List
requestHeaders) => _pushStreamFun(this, requestHeaders); + @override void terminate() => _terminateStreamFun(this); + @override set onTerminated(void handler(int v)) { _onTerminated = handler; if (_terminatedErrorCode != null && _onTerminated != null) { @@ -188,6 +196,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { peerSettings, localSettings, onActiveStateChanged, 2, -1); } + @override void onTerminated(exception) { _openStreams.values.toList().forEach((stream) => _closeStreamAbnormally(stream, exception, propagateException: true)); @@ -787,10 +796,12 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { onCheckForClose(); } + @override void onClosing() { _newStreamsC.close(); } + @override void onCheckForClose() { if (isClosing && _openStreams.isEmpty) { closeWithValue(); diff --git a/pkgs/http2/lib/src/sync_errors.dart b/pkgs/http2/lib/src/sync_errors.dart index f8afa055b7..226a51999c 100644 --- a/pkgs/http2/lib/src/sync_errors.dart +++ b/pkgs/http2/lib/src/sync_errors.dart @@ -9,6 +9,7 @@ class ProtocolException implements Exception { ProtocolException(this._message); + @override String toString() => 'ProtocolError: $_message'; } @@ -17,6 +18,7 @@ class FlowControlException implements Exception { FlowControlException(this._message); + @override String toString() => 'FlowControlException: $_message'; } @@ -25,10 +27,12 @@ class FrameSizeException implements Exception { FrameSizeException(this._message); + @override String toString() => 'FrameSizeException: $_message'; } class TerminatedException implements Exception { + @override String toString() => 'TerminatedException: The object has been terminated.'; } @@ -38,6 +42,7 @@ class StreamException implements Exception { StreamException(this.streamId, this._message); + @override String toString() => 'StreamException(stream id: $streamId): $_message'; } @@ -45,5 +50,6 @@ class StreamClosedException extends StreamException { StreamClosedException(int streamId, [String message = '']) : super(streamId, message); + @override String toString() => 'StreamClosedException(stream id: $streamId): $_message'; } diff --git a/pkgs/http2/lib/transport.dart b/pkgs/http2/lib/transport.dart index c0ec7bd049..7237c9a318 100644 --- a/pkgs/http2/lib/transport.dart +++ b/pkgs/http2/lib/transport.dart @@ -185,6 +185,7 @@ class DataStreamMessage extends StreamMessage { DataStreamMessage(this.bytes, {bool endStream}) : super(endStream: endStream); + @override String toString() => 'DataStreamMessage(${bytes.length} bytes)'; } @@ -195,6 +196,7 @@ class HeadersStreamMessage extends StreamMessage { HeadersStreamMessage(this.headers, {bool endStream}) : super(endStream: endStream); + @override String toString() => 'HeadersStreamMessage(${headers.length} headers)'; } @@ -208,6 +210,7 @@ class TransportStreamPush { TransportStreamPush(this.requestHeaders, this.stream); + @override String toString() => 'TransportStreamPush(${requestHeaders.length} request headers headers)'; } @@ -218,6 +221,7 @@ class TransportException implements Exception { TransportException(this.message); + @override String toString() => 'HTTP/2 error: $message'; } diff --git a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart index e4fa69eb00..8b68be1beb 100644 --- a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart @@ -144,10 +144,12 @@ main() { } class MockFrameWriter extends Mock implements FrameWriter { + @override BufferIndicator bufferIndicator = new BufferIndicator(); } class MockStreamMessageQueueIn extends Mock implements StreamMessageQueueIn { + @override BufferIndicator bufferIndicator = new BufferIndicator(); } @@ -155,6 +157,8 @@ class MockIncomingWindowHandler extends Mock implements IncomingWindowHandler {} class MockOutgoingWindowHandler extends Mock implements OutgoingConnectionWindowHandler, OutgoingStreamWindowHandler { + @override BufferIndicator positiveWindow = new BufferIndicator(); + @override int peerWindowSize = new Window().size; } diff --git a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart index 8b8fceea76..28381cfafd 100644 --- a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart @@ -145,6 +145,8 @@ class MockIncomingWindowHandler extends Mock implements IncomingWindowHandler {} class MockOutgoingStreamWindowHandler extends Mock implements OutgoingStreamWindowHandler { + @override final BufferIndicator positiveWindow = new BufferIndicator(); + @override int peerWindowSize; } diff --git a/pkgs/http2/test/src/hpack/hpack_test.dart b/pkgs/http2/test/src/hpack/hpack_test.dart index b099822e48..ea37d8bb23 100644 --- a/pkgs/http2/test/src/hpack/hpack_test.dart +++ b/pkgs/http2/test/src/hpack/hpack_test.dart @@ -806,8 +806,10 @@ class _HeaderMatcher extends Matcher { _HeaderMatcher(this.header); + @override Description describe(Description description) => description.add('Header'); + @override bool matches(item, Map matchState) { return item is Header && _compareLists(item.name, header.name) && From 7659f9688f821120f81a1553ffcadfb5fd13b5ec Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 20 Feb 2020 00:21:00 -0800 Subject: [PATCH 095/172] Run dartfmt --fix (dart-lang/http2#54) - Drop optional `new` and redundant `const`. - Use `=` instead of `:` for named argument defaults. - Enforce the relevant lints --- pkgs/http2/CHANGELOG.md | 4 +- pkgs/http2/analysis_options.yaml | 4 + pkgs/http2/example/display_headers.dart | 12 +- pkgs/http2/experimental/server.dart | 28 +- pkgs/http2/lib/multiprotocol_server.dart | 20 +- .../lib/src/artificial_server_socket.dart | 2 +- .../lib/src/async_utils/async_utils.dart | 13 +- pkgs/http2/lib/src/byte_utils.dart | 2 +- pkgs/http2/lib/src/connection.dart | 75 ++- pkgs/http2/lib/src/connection_preface.dart | 4 +- pkgs/http2/lib/src/error_handler.dart | 10 +- .../src/flowcontrol/connection_queues.dart | 22 +- .../lib/src/flowcontrol/stream_queues.dart | 30 +- pkgs/http2/lib/src/flowcontrol/window.dart | 2 +- .../lib/src/flowcontrol/window_handler.dart | 8 +- .../lib/src/frames/frame_defragmenter.dart | 8 +- pkgs/http2/lib/src/frames/frame_reader.dart | 42 +- pkgs/http2/lib/src/frames/frame_types.dart | 14 +- pkgs/http2/lib/src/frames/frame_writer.dart | 35 +- pkgs/http2/lib/src/hpack/hpack.dart | 155 +++--- pkgs/http2/lib/src/hpack/huffman.dart | 16 +- pkgs/http2/lib/src/hpack/huffman_table.dart | 521 +++++++++--------- pkgs/http2/lib/src/ping/ping_handler.dart | 6 +- pkgs/http2/lib/src/settings/settings.dart | 18 +- .../http2/lib/src/streams/stream_handler.dart | 93 ++-- pkgs/http2/lib/src/testing/client.dart | 42 +- pkgs/http2/lib/src/testing/debug.dart | 26 +- pkgs/http2/lib/transport.dart | 26 +- .../manual_test/out_of_stream_ids_test.dart | 6 +- pkgs/http2/test/client_test.dart | 96 ++-- pkgs/http2/test/client_websites_test.dart | 12 +- .../http2/test/multiprotocol_server_test.dart | 24 +- pkgs/http2/test/server_test.dart | 25 +- .../src/async_utils/async_utils_test.dart | 10 +- .../test/src/connection_preface_test.dart | 10 +- pkgs/http2/test/src/error_matchers.dart | 9 +- .../flowcontrol/connection_queues_test.dart | 42 +- .../src/flowcontrol/stream_queues_test.dart | 46 +- .../src/flowcontrol/window_handler_test.dart | 26 +- .../src/frames/frame_defragmenter_test.dart | 40 +- .../test/src/frames/frame_reader_test.dart | 30 +- .../src/frames/frame_writer_reader_test.dart | 22 +- .../test/src/frames/frame_writer_test.dart | 8 +- pkgs/http2/test/src/hpack/hpack_test.dart | 37 +- .../test/src/hpack/huffman_table_test.dart | 2 +- .../test/src/ping/ping_handler_test.dart | 44 +- .../src/settings/settings_handler_test.dart | 58 +- pkgs/http2/test/src/streams/helper.dart | 17 +- .../test/src/streams/simple_flow_test.dart | 10 +- .../test/src/streams/simple_push_test.dart | 14 +- pkgs/http2/test/src/streams/streams_test.dart | 14 +- pkgs/http2/test/transport_test.dart | 86 +-- 52 files changed, 957 insertions(+), 969 deletions(-) diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index 641a73b180..22dad60f39 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -1,4 +1,6 @@ -## 1.0 +## 1.0.1-dev + +## 1.0.0 * Graduate package to 1.0. * `package:http2/http2.dart` now reexports `package:http2/transport.dart`. diff --git a/pkgs/http2/analysis_options.yaml b/pkgs/http2/analysis_options.yaml index 47de419c6b..3dee28995b 100644 --- a/pkgs/http2/analysis_options.yaml +++ b/pkgs/http2/analysis_options.yaml @@ -24,12 +24,16 @@ linter: - library_prefixes - non_constant_identifier_names - only_throw_errors + - prefer_equal_for_default_values - prefer_final_fields + - prefer_generic_function_type_aliases - prefer_is_not_empty - prefer_single_quotes - slash_for_doc_comments - test_types_in_equals - throw_in_finally - type_init_formals + - unnecessary_const + - unnecessary_new - unrelated_type_equality_checks - valid_regexps diff --git a/pkgs/http2/example/display_headers.dart b/pkgs/http2/example/display_headers.dart index 91e2774b4d..528ef3c4f4 100644 --- a/pkgs/http2/example/display_headers.dart +++ b/pkgs/http2/example/display_headers.dart @@ -23,13 +23,13 @@ main(List args) async { // The default client settings will disable server pushes. We // therefore do not need to deal with [stream.peerPushes]. - var transport = new ClientTransportConnection.viaSocket(socket); + var transport = ClientTransportConnection.viaSocket(socket); var headers = [ - new Header.ascii(':method', 'GET'), - new Header.ascii(':path', uri.path), - new Header.ascii(':scheme', uri.scheme), - new Header.ascii(':authority', uri.host), + Header.ascii(':method', 'GET'), + Header.ascii(':path', uri.path), + Header.ascii(':scheme', uri.scheme), + Header.ascii(':authority', uri.host), ]; var stream = transport.makeRequest(headers, endStream: true); @@ -53,7 +53,7 @@ Future connect(Uri uri) async { var secureSocket = await SecureSocket.connect(uri.host, uri.port, supportedProtocols: ['h2']); if (secureSocket.selectedProtocol != 'h2') { - throw new Exception('Failed to negogiate http/2 via alpn. Maybe server ' + throw Exception('Failed to negogiate http/2 via alpn. Maybe server ' "doesn't support http/2."); } return secureSocket; diff --git a/pkgs/http2/experimental/server.dart b/pkgs/http2/experimental/server.dart index e1f25d63a8..5945a48323 100644 --- a/pkgs/http2/experimental/server.dart +++ b/pkgs/http2/experimental/server.dart @@ -19,7 +19,7 @@ const int PORT = 7777; main() async { String localFile(String path) => Platform.script.resolve(path).toFilePath(); - var context = new SecurityContext() + var context = SecurityContext() ..usePrivateKey(localFile('server_key.pem'), password: 'dartdart') ..useCertificateChain(localFile('server_chain.pem')) ..setAlpnProtocols(['h2'], true); @@ -42,7 +42,7 @@ handleClient(SecureSocket socket) { if (DEBUGGING) { connection = debugPrintingConnection(socket); } else { - connection = new ServerTransportConnection.viaSocket(socket); + connection = ServerTransportConnection.viaSocket(socket); } connection.incomingStreams.listen((ServerTransportStream stream) async { @@ -55,7 +55,7 @@ handleClient(SecureSocket socket) { dumpHeaders('${stream.id}', msg.headers); if (path == null) { path = pathFromHeaders(msg.headers); - if (path == null) throw new Exception('no path given'); + if (path == null) throw Exception('no path given'); if (path == '/') { sendHtml(stream); @@ -86,7 +86,7 @@ String pathFromHeaders(List
headers) { return ascii.decode(headers[i].value); } } - throw new Exception('Expected a :path header, but did not find one.'); + throw Exception('Expected a :path header, but did not find one.'); } void dumpData(String prefix, List data) { @@ -103,8 +103,8 @@ Future sendHtml(ServerTransportStream stream) async { push(stream, '/favicon.ico', send404); stream.sendHeaders([ - new Header.ascii(':status', '200'), - new Header.ascii('content-type', 'text/html; charset=utf-8'), + Header.ascii(':status', '200'), + Header.ascii('content-type', 'text/html; charset=utf-8'), ]); stream.sendData(ascii.encode(''' @@ -124,10 +124,10 @@ Future sendHtml(ServerTransportStream stream) async { Future push(ServerTransportStream stream, String path, Future sendResponse(TransportStream stream, String path)) async { var requestHeaders = [ - new Header.ascii(':authority', '$HOSTNAME:$PORT'), - new Header.ascii(':method', 'GET'), - new Header.ascii(':path', path), - new Header.ascii(':scheme', 'https'), + Header.ascii(':authority', '$HOSTNAME:$PORT'), + Header.ascii(':method', 'GET'), + Header.ascii(':path', path), + Header.ascii(':scheme', 'https'), ]; var pushStream = stream.push(requestHeaders); @@ -136,8 +136,8 @@ Future push(ServerTransportStream stream, String path, Future sendIFrameHtml(TransportStream stream, String path) async { stream.sendHeaders([ - new Header.ascii(':status', '200'), - new Header.ascii('content-type', 'text/html; charset=utf-8'), + Header.ascii(':status', '200'), + Header.ascii('content-type', 'text/html; charset=utf-8'), ]); stream.sendData(ascii.encode(''' @@ -152,8 +152,8 @@ Future sendIFrameHtml(TransportStream stream, String path) async { Future send404(TransportStream stream, String path) async { stream.sendHeaders([ - new Header.ascii(':status', '404'), - new Header.ascii('content-type', 'text/html; charset=utf-8'), + Header.ascii(':status', '404'), + Header.ascii('content-type', 'text/html; charset=utf-8'), ]); stream.sendData(ascii.encode(''' diff --git a/pkgs/http2/lib/multiprotocol_server.dart b/pkgs/http2/lib/multiprotocol_server.dart index 7d1f7eac16..ce901cad80 100644 --- a/pkgs/http2/lib/multiprotocol_server.dart +++ b/pkgs/http2/lib/multiprotocol_server.dart @@ -27,14 +27,14 @@ class MultiProtocolHttpServer { StreamController _http2Controller; Stream _http2Server; - final _http2Connections = new Set(); + final _http2Connections = Set(); MultiProtocolHttpServer._(this._serverSocket, this._settings) { _http11Controller = - new _ServerSocketController(_serverSocket.address, _serverSocket.port); - _http11Server = new HttpServer.listenOn(_http11Controller.stream); + _ServerSocketController(_serverSocket.address, _serverSocket.port); + _http11Server = HttpServer.listenOn(_http11Controller.stream); - _http2Controller = new StreamController(); + _http2Controller = StreamController(); _http2Server = _http2Controller.stream; } @@ -51,7 +51,7 @@ class MultiProtocolHttpServer { {http2.ServerSettings settings}) async { context.setAlpnProtocols(['h2', 'h2-14', 'http/1.1'], true); var secureServer = await SecureServerSocket.bind(address, port, context); - return new MultiProtocolHttpServer._(secureServer, settings); + return MultiProtocolHttpServer._(secureServer, settings); } /// The port this multi-protocol HTTP server runs on. @@ -74,7 +74,7 @@ class MultiProtocolHttpServer { if (protocol == null || protocol == 'http/1.1') { _http11Controller.addHttp11Socket(socket); } else if (protocol == 'h2' || protocol == 'h2-14') { - var connection = new http2.ServerTransportConnection.viaSocket(socket, + var connection = http2.ServerTransportConnection.viaSocket(socket, settings: _settings); _http2Connections.add(connection); connection.incomingStreams.listen(_http2Controller.add, @@ -82,7 +82,7 @@ class MultiProtocolHttpServer { onDone: () => _http2Connections.remove(connection)); } else { socket.destroy(); - throw new Exception('Unexpected negotiated ALPN protocol: $protocol.'); + throw Exception('Unexpected negotiated ALPN protocol: $protocol.'); } }, onError: onError); @@ -95,7 +95,7 @@ class MultiProtocolHttpServer { /// Closes this [MultiProtocolHttpServer]. /// /// Completes once everything has been successfully shut down. - Future close({bool force: false}) { + Future close({bool force = false}) { return _serverSocket.close().whenComplete(() { Future done1 = _http11Server.close(force: force); Future done2 = Future.wait( @@ -109,12 +109,12 @@ class MultiProtocolHttpServer { class _ServerSocketController { final InternetAddress address; final int port; - final StreamController _controller = new StreamController(); + final StreamController _controller = StreamController(); _ServerSocketController(this.address, this.port); ArtificialServerSocket get stream { - return new ArtificialServerSocket(address, port, _controller.stream); + return ArtificialServerSocket(address, port, _controller.stream); } void addHttp11Socket(Socket socket) { diff --git a/pkgs/http2/lib/src/artificial_server_socket.dart b/pkgs/http2/lib/src/artificial_server_socket.dart index ca94e839be..ce7450902f 100644 --- a/pkgs/http2/lib/src/artificial_server_socket.dart +++ b/pkgs/http2/lib/src/artificial_server_socket.dart @@ -32,6 +32,6 @@ class ArtificialServerSocket extends StreamView /// will be thrown when calling this method. @override Future close() async { - throw new Exception('Did not expect close() to be called.'); + throw Exception('Did not expect close() to be called.'); } } diff --git a/pkgs/http2/lib/src/async_utils/async_utils.dart b/pkgs/http2/lib/src/async_utils/async_utils.dart index 3f6d44a7e1..234d5e1a3e 100644 --- a/pkgs/http2/lib/src/async_utils/async_utils.dart +++ b/pkgs/http2/lib/src/async_utils/async_utils.dart @@ -10,8 +10,7 @@ import 'dart:io'; /// An interface for `StreamSink`-like classes to indicate whether adding data /// would be buffered and when the buffer is empty again. class BufferIndicator { - final StreamController _controller = - new StreamController.broadcast(sync: true); + final StreamController _controller = StreamController.broadcast(sync: true); /// A state variable indicating whether buffereing would occur at the moment. bool _wouldBuffer = true; @@ -50,7 +49,7 @@ class BufferIndicator { /// whether the underlying stream cannot handle more data and would buffer. class BufferedSink { /// The indicator whether the underlying sink is buffering at the moment. - final BufferIndicator bufferIndicator = new BufferIndicator(); + final BufferIndicator bufferIndicator = BufferIndicator(); /// A intermediate [StreamController] used to catch pause signals and to /// propagate the change via [bufferIndicator]. @@ -62,7 +61,7 @@ class BufferedSink { BufferedSink(StreamSink> dataSink) { bufferIndicator.markBuffered(); - _controller = new StreamController>( + _controller = StreamController>( onListen: () { bufferIndicator.markUnBuffered(); }, @@ -92,13 +91,13 @@ class BufferedSink { /// A small wrapper around [BufferedSink] which writes data in batches. class BufferedBytesWriter { /// A buffer which will be used for batching writes. - final BytesBuilder _builder = new BytesBuilder(copy: false); + final BytesBuilder _builder = BytesBuilder(copy: false); /// The underlying [BufferedSink]. final BufferedSink _bufferedSink; BufferedBytesWriter(StreamSink> outgoing) - : _bufferedSink = new BufferedSink(outgoing); + : _bufferedSink = BufferedSink(outgoing); /// An indicator whether the underlying sink is buffering at the moment. BufferIndicator get bufferIndicator => _bufferedSink.bufferIndicator; @@ -109,7 +108,7 @@ class BufferedBytesWriter { /// has not been flushed with [flushBufferedData] an error will be thrown. void add(List data) { if (_builder.length > 0) { - throw new StateError( + throw StateError( 'Cannot trigger an asynchronous write while there is buffered data.'); } _bufferedSink.sink.add(data); diff --git a/pkgs/http2/lib/src/byte_utils.dart b/pkgs/http2/lib/src/byte_utils.dart index 756e741605..099ceb1968 100644 --- a/pkgs/http2/lib/src/byte_utils.dart +++ b/pkgs/http2/lib/src/byte_utils.dart @@ -8,7 +8,7 @@ import 'dart:typed_data'; List viewOrSublist(List data, int offset, int length) { if (data is Uint8List) { - return new Uint8List.view(data.buffer, data.offsetInBytes + offset, length); + return Uint8List.view(data.buffer, data.offsetInBytes + offset, length); } else { return data.sublist(offset, offset + length); } diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index 07a3bb7d17..33600cecb0 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -88,10 +88,10 @@ class ConnectionState { abstract class Connection { /// The settings the other end has acknowledged to use when communicating with /// us. - final ActiveSettings acknowledgedSettings = new ActiveSettings(); + final ActiveSettings acknowledgedSettings = ActiveSettings(); /// The settings we have to obey communicating with the other side. - final ActiveSettings peerSettings = new ActiveSettings(); + final ActiveSettings peerSettings = ActiveSettings(); /// Whether this connection is a client connection. final bool isClientConnection; @@ -100,16 +100,16 @@ abstract class Connection { ActiveStateHandler onActiveStateChanged; /// The HPack context for this connection. - final HPackContext _hpackContext = new HPackContext(); + final HPackContext _hpackContext = HPackContext(); /// The flow window for this connection of the peer. - final Window _peerWindow = new Window(); + final Window _peerWindow = Window(); /// The flow window for this connection of this end. - final Window _localWindow = new Window(); + final Window _localWindow = Window(); /// Used for defragmenting PushPromise/Header frames. - final FrameDefragmenter _defragmenter = new FrameDefragmenter(); + final FrameDefragmenter _defragmenter = FrameDefragmenter(); /// The outgoing frames of this connection; FrameWriter _frameWriter; @@ -141,7 +141,7 @@ abstract class Connection { Connection(Stream> incoming, StreamSink> outgoing, Settings settings, - {this.isClientConnection: true}) { + {this.isClientConnection = true}) { _setupConnection(incoming, outgoing, settings); } @@ -151,7 +151,7 @@ abstract class Connection { StreamSink> outgoing, Settings settingsObject) { // Setup frame reading. var incomingFrames = - new FrameReader(incoming, acknowledgedSettings).startDecoding(); + FrameReader(incoming, acknowledgedSettings).startDecoding(); _frameReaderSubscription = incomingFrames.listen((Frame frame) { _catchProtocolErrors(() => _handleFrameImpl(frame)); }, onError: (error, stack) { @@ -166,8 +166,7 @@ abstract class Connection { }); // Setup frame writing. - _frameWriter = - new FrameWriter(_hpackContext.encoder, outgoing, peerSettings); + _frameWriter = FrameWriter(_hpackContext.encoder, outgoing, peerSettings); _frameWriter.doneFuture.then((_) { _terminate(ErrorCode.CONNECT_ERROR, causedByTransportError: true); }).catchError((error, stack) { @@ -175,9 +174,9 @@ abstract class Connection { }); // Setup handlers. - _settingsHandler = new SettingsHandler(_hpackContext.encoder, _frameWriter, + _settingsHandler = SettingsHandler(_hpackContext.encoder, _frameWriter, acknowledgedSettings, peerSettings); - _pingHandler = new PingHandler(_frameWriter); + _pingHandler = PingHandler(_frameWriter); var settings = _decodeSettings(settingsObject); @@ -198,19 +197,19 @@ abstract class Connection { // Setup the connection window handler, which keeps track of the // size of the outgoing connection window. - _connectionWindowHandler = new OutgoingConnectionWindowHandler(_peerWindow); + _connectionWindowHandler = OutgoingConnectionWindowHandler(_peerWindow); var connectionWindowUpdater = - new IncomingWindowHandler.connection(_frameWriter, _localWindow); + IncomingWindowHandler.connection(_frameWriter, _localWindow); // Setup queues for outgoing/incoming messages on the connection level. _outgoingQueue = - new ConnectionMessageQueueOut(_connectionWindowHandler, _frameWriter); - _incomingQueue = new ConnectionMessageQueueIn( - connectionWindowUpdater, _catchProtocolErrors); + ConnectionMessageQueueOut(_connectionWindowHandler, _frameWriter); + _incomingQueue = + ConnectionMessageQueueIn(connectionWindowUpdater, _catchProtocolErrors); if (isClientConnection) { - _streams = new StreamHandler.client( + _streams = StreamHandler.client( _frameWriter, _incomingQueue, _outgoingQueue, @@ -218,7 +217,7 @@ abstract class Connection { _settingsHandler.acknowledgedSettings, _activeStateHandler); } else { - _streams = new StreamHandler.server( + _streams = StreamHandler.server( _frameWriter, _incomingQueue, _outgoingQueue, @@ -230,7 +229,7 @@ abstract class Connection { // NOTE: We're not waiting until initial settings have been exchanged // before we start using the connection (i.e. we don't wait for half a // round-trip-time). - _state = new ConnectionState(); + _state = ConnectionState(); } List _decodeSettings(Settings settings) { @@ -238,13 +237,13 @@ abstract class Connection { // By default a endpoitn can make an unlimited number of concurrent streams. if (settings.concurrentStreamLimit != null) { - settingsList.add(new Setting(Setting.SETTINGS_MAX_CONCURRENT_STREAMS, + settingsList.add(Setting(Setting.SETTINGS_MAX_CONCURRENT_STREAMS, settings.concurrentStreamLimit)); } // By default the stream level flow control window is 64 KiB. if (settings.streamWindowSize != null) { - settingsList.add(new Setting( + settingsList.add(Setting( Setting.SETTINGS_INITIAL_WINDOW_SIZE, settings.streamWindowSize)); } @@ -252,7 +251,7 @@ abstract class Connection { // By default the server is allowed to do server pushes. if (settings.allowServerPushes == null || settings.allowServerPushes == false) { - settingsList.add(new Setting(Setting.SETTINGS_ENABLE_PUSH, 0)); + settingsList.add(Setting(Setting.SETTINGS_ENABLE_PUSH, 0)); } } else if (settings is ServerSettings) { // No special server settings at the moment. @@ -266,8 +265,8 @@ abstract class Connection { /// Pings the remote peer (can e.g. be used for measuring latency). Future ping() { return _pingHandler.ping().catchError((e, s) { - return new Future.error( - new TransportException('The connection has been terminated.')); + return Future.error( + TransportException('The connection has been terminated.')); }, test: (e) => e is TerminatedException); } @@ -358,7 +357,7 @@ abstract class Connection { } else if (frame is UnknownFrame) { // We can safely ignore these. } else { - throw new ProtocolException( + throw ProtocolException( 'Cannot handle frame type ${frame.runtimeType} with stream-id 0.'); } } else { @@ -366,7 +365,7 @@ abstract class Connection { } } - void _finishing({bool active: true, String message}) { + void _finishing({bool active = true, String message}) { // If this connection is already dead, we return. if (_state.isTerminated) return; @@ -388,7 +387,7 @@ abstract class Connection { _state.state = ConnectionState.Finishing; _state.finishingState |= ConnectionState.FinishingActive; - _outgoingQueue.enqueueMessage(new GoawayMessage( + _outgoingQueue.enqueueMessage(GoawayMessage( _streams.highestPeerInitiatedStream, ErrorCode.NO_ERROR, message != null ? utf8.encode(message) : [])); @@ -404,14 +403,14 @@ abstract class Connection { /// /// The returned future will never complete with an error. Future _terminate(int errorCode, - {bool causedByTransportError: false, String message}) { + {bool causedByTransportError = false, String message}) { // TODO: When do we complete here? if (_state.state != ConnectionState.Terminated) { _state.state = ConnectionState.Terminated; - var cancelFuture = new Future.sync(_frameReaderSubscription.cancel); + var cancelFuture = Future.sync(_frameReaderSubscription.cancel); if (!causedByTransportError) { - _outgoingQueue.enqueueMessage(new GoawayMessage( + _outgoingQueue.enqueueMessage(GoawayMessage( _streams.highestPeerInitiatedStream, errorCode, message != null ? utf8.encode(message) : [])); @@ -423,7 +422,7 @@ abstract class Connection { // Close all lower level handlers with an error message. // (e.g. if there is a pending connection.ping(), it's returned // Future will complete with this error). - var exception = new TransportConnectionException( + var exception = TransportConnectionException( errorCode, 'Connection is being forcefully terminated.'); // Close all streams & stream queues @@ -438,7 +437,7 @@ abstract class Connection { return Future.wait([cancelFuture, closeFuture]).catchError((_) {}); } - return new Future.value(); + return Future.value(); } } @@ -450,7 +449,7 @@ class ClientConnection extends Connection implements ClientTransportConnection { factory ClientConnection(Stream> incoming, StreamSink> outgoing, ClientSettings clientSettings) { outgoing.add(CONNECTION_PREFACE); - return new ClientConnection._(incoming, outgoing, clientSettings); + return ClientConnection._(incoming, outgoing, clientSettings); } @override @@ -459,13 +458,13 @@ class ClientConnection extends Connection implements ClientTransportConnection { @override ClientTransportStream makeRequest(List
headers, - {bool endStream: false}) { + {bool endStream = false}) { if (_state.isFinishing) { - throw new StateError( + throw StateError( 'The http/2 connection is finishing and can therefore not be used to ' 'make new streams.'); } else if (_state.isTerminated) { - throw new StateError( + throw StateError( 'The http/2 connection is no longer active and can therefore not be ' 'used to make new streams.'); } @@ -485,7 +484,7 @@ class ServerConnection extends Connection implements ServerTransportConnection { factory ServerConnection(Stream> incoming, StreamSink> outgoing, ServerSettings serverSettings) { var frameBytes = readConnectionPreface(incoming); - return new ServerConnection._(frameBytes, outgoing, serverSettings); + return ServerConnection._(frameBytes, outgoing, serverSettings); } @override diff --git a/pkgs/http2/lib/src/connection_preface.dart b/pkgs/http2/lib/src/connection_preface.dart index 08cf7e86b8..e28291de2a 100644 --- a/pkgs/http2/lib/src/connection_preface.dart +++ b/pkgs/http2/lib/src/connection_preface.dart @@ -11,7 +11,7 @@ import 'byte_utils.dart'; /// This is a set of bytes with which a client connection begins in the normal /// case. It can be used on a server to distinguish HTTP/1.1 and HTTP/2 clients. -const List CONNECTION_PREFACE = const [ +const List CONNECTION_PREFACE = [ 0x50, 0x52, 0x49, @@ -98,7 +98,7 @@ Stream> readConnectionPreface(Stream> incoming) { } } - result = new StreamController( + result = StreamController( onListen: () { subscription = incoming.listen(onData, onError: (e, StackTrace s) => result.addError(e, s), diff --git a/pkgs/http2/lib/src/error_handler.dart b/pkgs/http2/lib/src/error_handler.dart index ec1935d1e4..2097257519 100644 --- a/pkgs/http2/lib/src/error_handler.dart +++ b/pkgs/http2/lib/src/error_handler.dart @@ -28,14 +28,14 @@ class TerminatableMixin { T ensureNotTerminatedSync(T f()) { if (wasTerminated) { - throw new TerminatedException(); + throw TerminatedException(); } return f(); } Future ensureNotTerminatedAsync(Future f()) { if (wasTerminated) { - return new Future.error(new TerminatedException()); + return Future.error(TerminatedException()); } return f(); } @@ -44,7 +44,7 @@ class TerminatableMixin { /// Used by classes which may be cancelled. class CancellableMixin { bool _cancelled = false; - final _cancelCompleter = new Completer.sync(); + final _cancelCompleter = Completer.sync(); Future get onCancel => _cancelCompleter.future; @@ -62,7 +62,7 @@ class CancellableMixin { /// Used by classes which may be closed. class ClosableMixin { bool _closing = false; - final Completer _completer = new Completer(); + final Completer _completer = Completer(); Future get done => _completer.future; @@ -88,7 +88,7 @@ class ClosableMixin { dynamic ensureNotClosingSync(f()) { if (isClosing) { - throw new StateError('Was in the process of closing.'); + throw StateError('Was in the process of closing.'); } return f(); } diff --git a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart index bbc98889fb..2eec7e319b 100644 --- a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart @@ -39,7 +39,7 @@ class ConnectionMessageQueueOut extends Object final OutgoingConnectionWindowHandler _connectionWindow; /// The buffered [Message]s which are to be delivered to the remote peer. - final Queue _messages = new Queue(); + final Queue _messages = Queue(); /// The [FrameWriter] used for writing Headers/Data/PushPromise frames. final FrameWriter _frameWriter; @@ -140,7 +140,7 @@ class ConnectionMessageQueueOut extends Object _frameWriter.writeDataFrame(message.streamId, head, endStream: false); var tailMessage = - new DataMessage(message.streamId, tail, message.endStream); + DataMessage(message.streamId, tail, message.endStream); _messages.addFirst(tailMessage); } } else if (message is ResetStreamMessage) { @@ -151,8 +151,7 @@ class ConnectionMessageQueueOut extends Object _frameWriter.writeGoawayFrame( message.lastStreamId, message.errorCode, message.debugData); } else { - throw new StateError( - 'Unexpected message in queue: ${message.runtimeType}'); + throw StateError('Unexpected message in queue: ${message.runtimeType}'); } } } @@ -227,12 +226,12 @@ class ConnectionMessageQueueIn extends Object /// Registers a stream specific [StreamMessageQueueIn] for a new stream id. void insertNewStreamMessageQueue(int streamId, StreamMessageQueueIn mq) { if (_stream2messageQueue.containsKey(streamId)) { - throw new ArgumentError( + throw ArgumentError( 'Cannot register a SteramMessageQueueIn for the same streamId ' 'multiple times'); } - var pendingMessages = new Queue(); + var pendingMessages = Queue(); _stream2pendingMessages[streamId] = pendingMessages; _stream2messageQueue[streamId] = mq; @@ -253,8 +252,7 @@ class ConnectionMessageQueueIn extends Object /// Processes an incoming [DataFrame] which is addressed to a specific stream. void processDataFrame(DataFrame frame) { var streamId = frame.header.streamId; - var message = - new DataMessage(streamId, frame.bytes, frame.hasEndStreamFlag); + var message = DataMessage(streamId, frame.bytes, frame.hasEndStreamFlag); _windowUpdateHandler.gotData(message.bytes.length); _addMessage(streamId, message); @@ -270,8 +268,8 @@ class ConnectionMessageQueueIn extends Object /// stream. void processHeadersFrame(HeadersFrame frame) { var streamId = frame.header.streamId; - var message = new HeadersMessage( - streamId, frame.decodedHeaders, frame.hasEndStreamFlag); + var message = + HeadersMessage(streamId, frame.decodedHeaders, frame.hasEndStreamFlag); // NOTE: Header frames do not affect flow control - only data frames do. _addMessage(streamId, message); } @@ -281,7 +279,7 @@ class ConnectionMessageQueueIn extends Object void processPushPromiseFrame( PushPromiseFrame frame, ClientTransportStream pushedStream) { var streamId = frame.header.streamId; - var message = new PushPromiseMessage(streamId, frame.decodedHeaders, + var message = PushPromiseMessage(streamId, frame.decodedHeaders, frame.promisedStreamId, pushedStream, false); // NOTE: @@ -337,7 +335,7 @@ class ConnectionMessageQueueIn extends Object } void forceDispatchIncomingMessages() { - final toBeRemoved = new Set(); + final toBeRemoved = Set(); _stream2pendingMessages.forEach((int streamId, Queue messages) { final mq = _stream2messageQueue[streamId]; while (messages.isNotEmpty) { diff --git a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart index 58d65502dd..cd1827352e 100644 --- a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart @@ -33,11 +33,11 @@ class StreamMessageQueueOut extends Object final ConnectionMessageQueueOut connectionMessageQueue; /// A indicator for whether this queue is currently buffering. - final BufferIndicator bufferIndicator = new BufferIndicator(); + final BufferIndicator bufferIndicator = BufferIndicator(); /// Buffered [Message]s which will be written to the underlying connection /// if the flow control window allows so. - final Queue _messages = new Queue(); + final Queue _messages = Queue(); /// Debugging data on how much data should be written to the underlying /// connection message queue. @@ -118,9 +118,9 @@ class StreamMessageQueueOut extends Object var partA = viewOrSublist(messageBytes, 0, bytesAvailable); var partB = viewOrSublist(messageBytes, bytesAvailable, messageBytes.length - bytesAvailable); - var messageA = new DataMessage(message.streamId, partA, false); + var messageA = DataMessage(message.streamId, partA, false); var messageB = - new DataMessage(message.streamId, partB, message.endStream); + DataMessage(message.streamId, partB, message.endStream); // Put the second fragment back into the front of the queue. _messages.addFirst(messageB); @@ -139,7 +139,7 @@ class StreamMessageQueueOut extends Object _messages.removeFirst(); connectionMessageQueue.enqueueMessage(message); } else { - throw new StateError('Unknown messages type: ${message.runtimeType}'); + throw StateError('Unknown messages type: ${message.runtimeType}'); } } if (queueLenBefore > 0 && _messages.isEmpty) { @@ -161,11 +161,11 @@ class StreamMessageQueueIn extends Object final IncomingWindowHandler windowHandler; /// A indicator whether this [StreamMessageQueueIn] is currently buffering. - final BufferIndicator bufferIndicator = new BufferIndicator(); + final BufferIndicator bufferIndicator = BufferIndicator(); /// The pending [Message]s which are to be delivered via the [messages] /// stream. - final Queue _pendingMessages = new Queue(); + final Queue _pendingMessages = Queue(); /// The [StreamController] used for producing the [messages] stream. StreamController _incomingMessagesC; @@ -178,7 +178,7 @@ class StreamMessageQueueIn extends Object // incoming messages will get buffered. bufferIndicator.markBuffered(); - _incomingMessagesC = new StreamController( + _incomingMessagesC = StreamController( onListen: () { if (!wasClosed && !wasTerminated) { _tryDispatch(); @@ -198,7 +198,7 @@ class StreamMessageQueueIn extends Object }, onCancel: cancel); - _serverPushStreamsC = new StreamController(onListen: () { + _serverPushStreamsC = StreamController(onListen: () { if (!wasClosed && !wasTerminated) { _tryDispatch(); _tryUpdateBufferIndicator(); @@ -225,7 +225,7 @@ class StreamMessageQueueIn extends Object // either rejecting or handling them. assert(!_serverPushStreamsC.isClosed); var transportStreamPush = - new TransportStreamPush(message.headers, message.pushedStream); + TransportStreamPush(message.headers, message.pushedStream); _serverPushStreamsC.add(transportStreamPush); return; } @@ -268,12 +268,12 @@ class StreamMessageQueueIn extends Object final message = _pendingMessages.removeFirst(); assert(!_incomingMessagesC.isClosed); if (message is HeadersMessage) { - _incomingMessagesC.add(new HeadersStreamMessage(message.headers, + _incomingMessagesC.add(HeadersStreamMessage(message.headers, endStream: message.endStream)); } else if (message is DataMessage) { if (message.bytes.length > 0) { - _incomingMessagesC.add(new DataStreamMessage(message.bytes, - endStream: message.endStream)); + _incomingMessagesC.add( + DataStreamMessage(message.bytes, endStream: message.endStream)); } } else { // This can never happen. @@ -299,11 +299,11 @@ class StreamMessageQueueIn extends Object if (message is HeadersMessage) { // NOTE: Header messages do not affect flow control - only // data messages do. - _incomingMessagesC.add(new HeadersStreamMessage(message.headers, + _incomingMessagesC.add(HeadersStreamMessage(message.headers, endStream: message.endStream)); } else if (message is DataMessage) { if (message.bytes.length > 0) { - _incomingMessagesC.add(new DataStreamMessage(message.bytes, + _incomingMessagesC.add(DataStreamMessage(message.bytes, endStream: message.endStream)); windowHandler.dataProcessed(message.bytes.length); } diff --git a/pkgs/http2/lib/src/flowcontrol/window.dart b/pkgs/http2/lib/src/flowcontrol/window.dart index 10ba3f703a..5f26909bb1 100644 --- a/pkgs/http2/lib/src/flowcontrol/window.dart +++ b/pkgs/http2/lib/src/flowcontrol/window.dart @@ -15,7 +15,7 @@ class Window { /// NOTE: This value can potentially become negative. int _size; - Window({int initialSize: (1 << 16) - 1}) : _size = initialSize; + Window({int initialSize = (1 << 16) - 1}) : _size = initialSize; /// The current size of the flow control window. int get size => _size; diff --git a/pkgs/http2/lib/src/flowcontrol/window_handler.dart b/pkgs/http2/lib/src/flowcontrol/window_handler.dart index 909fa2bb81..4c0daaf80a 100644 --- a/pkgs/http2/lib/src/flowcontrol/window_handler.dart +++ b/pkgs/http2/lib/src/flowcontrol/window_handler.dart @@ -16,7 +16,7 @@ abstract class AbstractOutgoingWindowHandler { /// Indicates when the outgoing connection window turned positive and we can /// send data frames again. - final BufferIndicator positiveWindow = new BufferIndicator(); + final BufferIndicator positiveWindow = BufferIndicator(); AbstractOutgoingWindowHandler(this._peerWindow) { if (_peerWindow.size > 0) { @@ -32,7 +32,7 @@ abstract class AbstractOutgoingWindowHandler { void processWindowUpdate(WindowUpdateFrame frame) { int increment = frame.windowSizeIncrement; if ((_peerWindow.size + increment) > Window.MAX_WINDOW_SIZE) { - throw new FlowControlException( + throw FlowControlException( 'Window update received from remote peer would make flow control ' 'window too large.'); } else { @@ -75,7 +75,7 @@ class OutgoingStreamWindowHandler extends AbstractOutgoingWindowHandler { /// existing streams to update the flow stream-level flow control window. void processInitialWindowSizeSettingChange(int difference) { if ((_peerWindow.size + difference) > Window.MAX_WINDOW_SIZE) { - throw new FlowControlException( + throw FlowControlException( 'Window update received from remote peer would make flow control ' 'window too large.'); } else { @@ -142,7 +142,7 @@ class IncomingWindowHandler { // our initial settings. // if (_localWindow.size < 0) { - throw new FlowControlException( + throw FlowControlException( 'Connection level flow control window became negative.'); } } diff --git a/pkgs/http2/lib/src/frames/frame_defragmenter.dart b/pkgs/http2/lib/src/frames/frame_defragmenter.dart index 44fc076417..900fc7cd5b 100644 --- a/pkgs/http2/lib/src/frames/frame_defragmenter.dart +++ b/pkgs/http2/lib/src/frames/frame_defragmenter.dart @@ -35,7 +35,7 @@ class FrameDefragmenter { if (_headersFrame != null) { if (frame is ContinuationFrame) { if (_headersFrame.header.streamId != frame.header.streamId) { - throw new ProtocolException( + throw ProtocolException( 'Defragmentation: frames have different stream ids.'); } _headersFrame = _headersFrame.addBlockContinuation(frame); @@ -48,14 +48,14 @@ class FrameDefragmenter { return null; } } else { - throw new ProtocolException( + throw ProtocolException( 'Defragmentation: Incomplete frame must be followed by ' 'continuation frame.'); } } else if (_pushPromiseFrame != null) { if (frame is ContinuationFrame) { if (_pushPromiseFrame.header.streamId != frame.header.streamId) { - throw new ProtocolException( + throw ProtocolException( 'Defragmentation: frames have different stream ids.'); } _pushPromiseFrame = _pushPromiseFrame.addBlockContinuation(frame); @@ -68,7 +68,7 @@ class FrameDefragmenter { return null; } } else { - throw new ProtocolException( + throw ProtocolException( 'Defragmentation: Incomplete frame must be followed by ' 'continuation frame.'); } diff --git a/pkgs/http2/lib/src/frames/frame_reader.dart b/pkgs/http2/lib/src/frames/frame_reader.dart index 884520388e..fcb81e16a6 100644 --- a/pkgs/http2/lib/src/frames/frame_reader.dart +++ b/pkgs/http2/lib/src/frames/frame_reader.dart @@ -19,7 +19,7 @@ class FrameReader { /// Starts to listen on the input stream and decodes HTTP/2 transport frames. Stream startDecoding() { - List> bufferedData = new List>(); + List> bufferedData = List>(); int bufferedLength = 0; FrameHeader tryReadHeader() { @@ -57,7 +57,7 @@ class FrameReader { return null; } - _framesController = new StreamController( + _framesController = StreamController( onListen: () { FrameHeader header; @@ -80,7 +80,7 @@ class FrameReader { if (header != null) { if (header.length > _localSettings.maxFrameSize) { terminateWithError( - new FrameSizeException('Incoming frame is too big.')); + FrameSizeException('Incoming frame is too big.')); return; } @@ -105,7 +105,7 @@ class FrameReader { if (bufferedLength == 0) { _framesController.close(); } else { - terminateWithError(new FrameSizeException( + terminateWithError(FrameSizeException( 'Incoming byte stream ended with incomplete frame')); } }); @@ -130,7 +130,7 @@ class FrameReader { accumulatedLength += bufferedData[numLists++].length; } assert(accumulatedLength >= numberOfBytes); - var newList = new Uint8List(accumulatedLength); + var newList = Uint8List(accumulatedLength); int offset = 0; for (int i = 0; i < numLists; i++) { List data = bufferedData[i]; @@ -149,7 +149,7 @@ class FrameReader { int flags = bytes[offset + 4]; int streamId = readInt32(bytes, offset + 5) & 0x7fffffff; - return new FrameHeader(length, type, flags, streamId); + return FrameHeader(length, type, flags, streamId); } /// Reads a [Frame] from [bytes], starting at [frameOffset]. @@ -167,7 +167,7 @@ class FrameReader { int dataLen = frameEnd - offset - padLength; _checkFrameLengthCondition(dataLen >= 0); var dataBytes = viewOrSublist(bytes, offset, dataLen); - return new DataFrame(header, padLength, dataBytes); + return DataFrame(header, padLength, dataBytes); case FrameType.HEADERS: int padLength = 0; @@ -188,7 +188,7 @@ class FrameReader { int headerBlockLen = frameEnd - offset - padLength; _checkFrameLengthCondition(headerBlockLen >= 0); var headerBlockFragment = viewOrSublist(bytes, offset, headerBlockLen); - return new HeadersFrame(header, padLength, exclusiveDependency, + return HeadersFrame(header, padLength, exclusiveDependency, streamDependency, weight, headerBlockFragment); case FrameType.PRIORITY: @@ -198,7 +198,7 @@ class FrameReader { bool exclusiveDependency = (bytes[offset] & 0x80) == 0x80; int streamDependency = readInt32(bytes, offset) & 0x7fffffff; int weight = bytes[offset + 4]; - return new PriorityFrame( + return PriorityFrame( header, exclusiveDependency, streamDependency, weight); case FrameType.RST_STREAM: @@ -206,20 +206,20 @@ class FrameReader { (frameEnd - offset) == RstStreamFrame.FIXED_FRAME_LENGTH, message: 'Rst frames must have a length of 4.'); int errorCode = readInt32(bytes, offset); - return new RstStreamFrame(header, errorCode); + return RstStreamFrame(header, errorCode); case FrameType.SETTINGS: _checkFrameLengthCondition((header.length % 6) == 0, message: 'Settings frame length must be a multiple of 6 bytes.'); int count = header.length ~/ 6; - var settings = new List(count); + var settings = List(count); for (int i = 0; i < count; i++) { int identifier = readInt16(bytes, offset + 6 * i); int value = readInt32(bytes, offset + 6 * i + 2); - settings[i] = new Setting(identifier, value); + settings[i] = Setting(identifier, value); } - var frame = new SettingsFrame(header, settings); + var frame = SettingsFrame(header, settings); if (frame.hasAckFlag) { _checkFrameLengthCondition(header.length == 0, message: 'Settings frame length must 0 for ACKs.'); @@ -237,7 +237,7 @@ class FrameReader { int headerBlockLen = frameEnd - offset - padLength; _checkFrameLengthCondition(headerBlockLen >= 0); var headerBlockFragment = viewOrSublist(bytes, offset, headerBlockLen); - return new PushPromiseFrame( + return PushPromiseFrame( header, padLength, promisedStreamId, headerBlockFragment); case FrameType.PING: @@ -245,30 +245,30 @@ class FrameReader { (frameEnd - offset) == PingFrame.FIXED_FRAME_LENGTH, message: 'Ping frames must have a length of 8.'); var opaqueData = readInt64(bytes, offset); - return new PingFrame(header, opaqueData); + return PingFrame(header, opaqueData); case FrameType.GOAWAY: _checkFrameLengthCondition((frameEnd - offset) >= 8); int lastStreamId = readInt32(bytes, offset); int errorCode = readInt32(bytes, offset + 4); var debugData = viewOrSublist(bytes, offset + 8, header.length - 8); - return new GoawayFrame(header, lastStreamId, errorCode, debugData); + return GoawayFrame(header, lastStreamId, errorCode, debugData); case FrameType.WINDOW_UPDATE: _checkFrameLengthCondition( (frameEnd - offset) == WindowUpdateFrame.FIXED_FRAME_LENGTH, message: 'Window update frames must have a length of 4.'); int windowSizeIncrement = readInt32(bytes, offset) & 0x7fffffff; - return new WindowUpdateFrame(header, windowSizeIncrement); + return WindowUpdateFrame(header, windowSizeIncrement); case FrameType.CONTINUATION: var headerBlockFragment = viewOrSublist(bytes, offset, frameEnd - offset); - return new ContinuationFrame(header, headerBlockFragment); + return ContinuationFrame(header, headerBlockFragment); default: // Unknown frames should be ignored according to spec. - return new UnknownFrame( + return UnknownFrame( header, viewOrSublist(bytes, offset, frameEnd - offset)); } } @@ -276,9 +276,9 @@ class FrameReader { /// Checks that [condition] is `true` and raises an [FrameSizeException] /// otherwise. void _checkFrameLengthCondition(bool condition, - {String message: 'Frame not long enough.'}) { + {String message = 'Frame not long enough.'}) { if (!condition) { - throw new FrameSizeException(message); + throw FrameSizeException(message); } } } diff --git a/pkgs/http2/lib/src/frames/frame_types.dart b/pkgs/http2/lib/src/frames/frame_types.dart index 35ff3f9ebf..2dec30a200 100644 --- a/pkgs/http2/lib/src/frames/frame_types.dart +++ b/pkgs/http2/lib/src/frames/frame_types.dart @@ -114,11 +114,11 @@ class HeadersFrame extends Frame { HeadersFrame addBlockContinuation(ContinuationFrame frame) { var fragment = frame.headerBlockFragment; var flags = header.flags | frame.header.flags; - var fh = new FrameHeader( + var fh = FrameHeader( header.length + fragment.length, header.type, flags, header.streamId); var mergedHeaderBlockFragment = - new Uint8List(headerBlockFragment.length + fragment.length); + Uint8List(headerBlockFragment.length + fragment.length); mergedHeaderBlockFragment.setRange( 0, headerBlockFragment.length, headerBlockFragment); @@ -126,8 +126,8 @@ class HeadersFrame extends Frame { mergedHeaderBlockFragment.setRange( headerBlockFragment.length, mergedHeaderBlockFragment.length, fragment); - return new HeadersFrame(fh, padLength, exclusiveDependency, - streamDependency, weight, mergedHeaderBlockFragment); + return HeadersFrame(fh, padLength, exclusiveDependency, streamDependency, + weight, mergedHeaderBlockFragment); } @override @@ -235,11 +235,11 @@ class PushPromiseFrame extends Frame { PushPromiseFrame addBlockContinuation(ContinuationFrame frame) { var fragment = frame.headerBlockFragment; var flags = header.flags | frame.header.flags; - var fh = new FrameHeader( + var fh = FrameHeader( header.length + fragment.length, header.type, flags, header.streamId); var mergedHeaderBlockFragment = - new Uint8List(headerBlockFragment.length + fragment.length); + Uint8List(headerBlockFragment.length + fragment.length); mergedHeaderBlockFragment.setRange( 0, headerBlockFragment.length, headerBlockFragment); @@ -247,7 +247,7 @@ class PushPromiseFrame extends Frame { mergedHeaderBlockFragment.setRange( headerBlockFragment.length, mergedHeaderBlockFragment.length, fragment); - return new PushPromiseFrame( + return PushPromiseFrame( fh, padLength, promisedStreamId, mergedHeaderBlockFragment); } diff --git a/pkgs/http2/lib/src/frames/frame_writer.dart b/pkgs/http2/lib/src/frames/frame_writer.dart index 6c79581c41..80437926b8 100644 --- a/pkgs/http2/lib/src/frames/frame_writer.dart +++ b/pkgs/http2/lib/src/frames/frame_writer.dart @@ -22,7 +22,7 @@ class FrameWriter { FrameWriter( this._hpackEncoder, StreamSink> outgoing, this._peerSettings) - : _outWriter = new BufferedBytesWriter(outgoing); + : _outWriter = BufferedBytesWriter(outgoing); /// A indicator whether writes would be buffered. BufferIndicator get bufferIndicator => _outWriter.bufferIndicator; @@ -31,7 +31,7 @@ class FrameWriter { /// sink. int get highestWrittenStreamId => _highestWrittenStreamId; - void writeDataFrame(int streamId, List data, {bool endStream: false}) { + void writeDataFrame(int streamId, List data, {bool endStream = false}) { while (data.length > _peerSettings.maxFrameSize) { var chunk = viewOrSublist(data, 0, _peerSettings.maxFrameSize); data = viewOrSublist(data, _peerSettings.maxFrameSize, @@ -45,7 +45,7 @@ class FrameWriter { int type = FrameType.DATA; int flags = endStream ? DataFrame.FLAG_END_STREAM : 0; - var buffer = new Uint8List(FRAME_HEADER_SIZE + data.length); + var buffer = Uint8List(FRAME_HEADER_SIZE + data.length); int offset = 0; _setFrameHeader(buffer, offset, type, flags, streamId, data.length); @@ -57,7 +57,7 @@ class FrameWriter { } void writeHeadersFrame(int streamId, List
headers, - {bool endStream: true}) { + {bool endStream = true}) { var fragment = _hpackEncoder.encode(headers); var maxSize = _peerSettings.maxFrameSize - HeadersFrame.MAX_CONSTANT_PAYLOAD; @@ -84,7 +84,7 @@ class FrameWriter { if (endHeaders) flags |= HeadersFrame.FLAG_END_HEADERS; if (endStream) flags |= HeadersFrame.FLAG_END_STREAM; - var buffer = new Uint8List(FRAME_HEADER_SIZE + fragment.length); + var buffer = Uint8List(FRAME_HEADER_SIZE + fragment.length); int offset = 0; _setFrameHeader(buffer, offset, type, flags, streamId, fragment.length); @@ -100,7 +100,7 @@ class FrameWriter { int type = FrameType.CONTINUATION; int flags = endHeaders ? ContinuationFrame.FLAG_END_HEADERS : 0; - var buffer = new Uint8List(FRAME_HEADER_SIZE + fragment.length); + var buffer = Uint8List(FRAME_HEADER_SIZE + fragment.length); int offset = 0; _setFrameHeader(buffer, offset, type, flags, streamId, fragment.length); @@ -112,12 +112,12 @@ class FrameWriter { } void writePriorityFrame(int streamId, int streamDependency, int weight, - {bool exclusive: false}) { + {bool exclusive = false}) { int type = FrameType.PRIORITY; int flags = 0; var buffer = - new Uint8List(FRAME_HEADER_SIZE + PriorityFrame.FIXED_FRAME_LENGTH); + Uint8List(FRAME_HEADER_SIZE + PriorityFrame.FIXED_FRAME_LENGTH); int offset = 0; _setFrameHeader(buffer, offset, type, flags, streamId, 5); @@ -138,7 +138,7 @@ class FrameWriter { int flags = 0; var buffer = - new Uint8List(FRAME_HEADER_SIZE + RstStreamFrame.FIXED_FRAME_LENGTH); + Uint8List(FRAME_HEADER_SIZE + RstStreamFrame.FIXED_FRAME_LENGTH); int offset = 0; _setFrameHeader(buffer, offset, type, flags, streamId, 4); @@ -153,7 +153,7 @@ class FrameWriter { int type = FrameType.SETTINGS; int flags = 0; - var buffer = new Uint8List(FRAME_HEADER_SIZE + 6 * settings.length); + var buffer = Uint8List(FRAME_HEADER_SIZE + 6 * settings.length); int offset = 0; _setFrameHeader(buffer, offset, type, flags, 0, 6 * settings.length); @@ -172,7 +172,7 @@ class FrameWriter { int type = FrameType.SETTINGS; int flags = SettingsFrame.FLAG_ACK; - var buffer = new Uint8List(FRAME_HEADER_SIZE); + var buffer = Uint8List(FRAME_HEADER_SIZE); int offset = 0; _setFrameHeader(buffer, offset, type, flags, 0, 0); @@ -209,7 +209,7 @@ class FrameWriter { int type = FrameType.PUSH_PROMISE; int flags = endHeaders ? HeadersFrame.FLAG_END_HEADERS : 0; - var buffer = new Uint8List(FRAME_HEADER_SIZE + 4 + fragment.length); + var buffer = Uint8List(FRAME_HEADER_SIZE + 4 + fragment.length); int offset = 0; _setFrameHeader(buffer, offset, type, flags, streamId, 4 + fragment.length); @@ -221,12 +221,11 @@ class FrameWriter { _writeData(buffer); } - void writePingFrame(int opaqueData, {bool ack: false}) { + void writePingFrame(int opaqueData, {bool ack = false}) { int type = FrameType.PING; int flags = ack ? PingFrame.FLAG_ACK : 0; - var buffer = - new Uint8List(FRAME_HEADER_SIZE + PingFrame.FIXED_FRAME_LENGTH); + var buffer = Uint8List(FRAME_HEADER_SIZE + PingFrame.FIXED_FRAME_LENGTH); int offset = 0; _setFrameHeader(buffer, 0, type, flags, 0, 8); @@ -240,7 +239,7 @@ class FrameWriter { int type = FrameType.GOAWAY; int flags = 0; - var buffer = new Uint8List(FRAME_HEADER_SIZE + 8 + debugData.length); + var buffer = Uint8List(FRAME_HEADER_SIZE + 8 + debugData.length); int offset = 0; _setFrameHeader(buffer, offset, type, flags, 0, 8 + debugData.length); @@ -253,12 +252,12 @@ class FrameWriter { _writeData(buffer); } - void writeWindowUpdate(int sizeIncrement, {int streamId: 0}) { + void writeWindowUpdate(int sizeIncrement, {int streamId = 0}) { int type = FrameType.WINDOW_UPDATE; int flags = 0; var buffer = - new Uint8List(FRAME_HEADER_SIZE + WindowUpdateFrame.FIXED_FRAME_LENGTH); + Uint8List(FRAME_HEADER_SIZE + WindowUpdateFrame.FIXED_FRAME_LENGTH); int offset = 0; _setFrameHeader(buffer, offset, type, flags, streamId, 4); diff --git a/pkgs/http2/lib/src/hpack/hpack.dart b/pkgs/http2/lib/src/hpack/hpack.dart index 75316f0621..2477beeca7 100644 --- a/pkgs/http2/lib/src/hpack/hpack.dart +++ b/pkgs/http2/lib/src/hpack/hpack.dart @@ -29,12 +29,12 @@ class HPackDecodingException implements Exception { /// /// This is a statefull class, so encoding/decoding changes internal state. class HPackContext { - final HPackEncoder encoder = new HPackEncoder(); - final HPackDecoder decoder = new HPackDecoder(); + final HPackEncoder encoder = HPackEncoder(); + final HPackDecoder decoder = HPackDecoder(); HPackContext( - {int maxSendingHeaderTableSize: 4096, - int maxReceivingHeaderTableSize: 4096}) { + {int maxSendingHeaderTableSize = 4096, + int maxReceivingHeaderTableSize = 4096}) { encoder.updateMaxSendingHeaderTableSize(maxSendingHeaderTableSize); decoder.updateMaxReceivingHeaderTableSize(maxReceivingHeaderTableSize); } @@ -46,10 +46,10 @@ class Header { final List value; final bool neverIndexed; - Header(this.name, this.value, {this.neverIndexed: false}); + Header(this.name, this.value, {this.neverIndexed = false}); factory Header.ascii(String name, String value) { - return new Header(ascii.encode(name), ascii.encode(value)); + return Header(ascii.encode(name), ascii.encode(value)); } } @@ -57,7 +57,7 @@ class Header { class HPackDecoder { int _maxHeaderTableSize; - final IndexTable _table = new IndexTable(); + final IndexTable _table = IndexTable(); void updateMaxReceivingHeaderTableSize(int newMaximumSize) { _maxHeaderTableSize = newMaximumSize; @@ -104,7 +104,7 @@ class HPackDecoder { } } - Header readHeaderFieldInternal(int index, {bool neverIndexed: false}) { + Header readHeaderFieldInternal(int index, {bool neverIndexed = false}) { List name, value; if (index > 0) { name = _table.lookup(index).name; @@ -113,7 +113,7 @@ class HPackDecoder { name = readStringLiteral(); value = readStringLiteral(); } - return new Header(name, value, neverIndexed: neverIndexed); + return Header(name, value, neverIndexed: neverIndexed); } try { @@ -145,20 +145,19 @@ class HPackDecoder { if (newMaxSize <= _maxHeaderTableSize) { _table.updateMaxSize(newMaxSize); } else { - throw new HPackDecodingException( - 'Dynamic table size update failed: ' + throw HPackDecodingException('Dynamic table size update failed: ' 'A new value of $newMaxSize exceeds the limit of ' '$_maxHeaderTableSize'); } } else { - throw new HPackDecodingException('Invalid encoding of headers.'); + throw HPackDecodingException('Invalid encoding of headers.'); } } return headers; } on RangeError catch (e) { - throw new HPackDecodingException('$e'); + throw HPackDecodingException('$e'); } on HuffmanDecodingException catch (e) { - throw new HPackDecodingException('$e'); + throw HPackDecodingException('$e'); } } } @@ -174,7 +173,7 @@ class HPackEncoder { } List encode(List
headers) { - var bytesBuilder = new BytesBuilder(); + var bytesBuilder = BytesBuilder(); int currentByte = 0; void writeInteger(int prefixBits, int value) { @@ -224,67 +223,67 @@ class HPackEncoder { class IndexTable { static final List
_staticTable = [ null, - new Header(ascii.encode(':authority'), const []), - new Header(ascii.encode(':method'), ascii.encode('GET')), - new Header(ascii.encode(':method'), ascii.encode('POST')), - new Header(ascii.encode(':path'), ascii.encode('/')), - new Header(ascii.encode(':path'), ascii.encode('/index.html')), - new Header(ascii.encode(':scheme'), ascii.encode('http')), - new Header(ascii.encode(':scheme'), ascii.encode('https')), - new Header(ascii.encode(':status'), ascii.encode('200')), - new Header(ascii.encode(':status'), ascii.encode('204')), - new Header(ascii.encode(':status'), ascii.encode('206')), - new Header(ascii.encode(':status'), ascii.encode('304')), - new Header(ascii.encode(':status'), ascii.encode('400')), - new Header(ascii.encode(':status'), ascii.encode('404')), - new Header(ascii.encode(':status'), ascii.encode('500')), - new Header(ascii.encode('accept-charset'), const []), - new Header(ascii.encode('accept-encoding'), ascii.encode('gzip, deflate')), - new Header(ascii.encode('accept-language'), const []), - new Header(ascii.encode('accept-ranges'), const []), - new Header(ascii.encode('accept'), const []), - new Header(ascii.encode('access-control-allow-origin'), const []), - new Header(ascii.encode('age'), const []), - new Header(ascii.encode('allow'), const []), - new Header(ascii.encode('authorization'), const []), - new Header(ascii.encode('cache-control'), const []), - new Header(ascii.encode('content-disposition'), const []), - new Header(ascii.encode('content-encoding'), const []), - new Header(ascii.encode('content-language'), const []), - new Header(ascii.encode('content-length'), const []), - new Header(ascii.encode('content-location'), const []), - new Header(ascii.encode('content-range'), const []), - new Header(ascii.encode('content-type'), const []), - new Header(ascii.encode('cookie'), const []), - new Header(ascii.encode('date'), const []), - new Header(ascii.encode('etag'), const []), - new Header(ascii.encode('expect'), const []), - new Header(ascii.encode('expires'), const []), - new Header(ascii.encode('from'), const []), - new Header(ascii.encode('host'), const []), - new Header(ascii.encode('if-match'), const []), - new Header(ascii.encode('if-modified-since'), const []), - new Header(ascii.encode('if-none-match'), const []), - new Header(ascii.encode('if-range'), const []), - new Header(ascii.encode('if-unmodified-since'), const []), - new Header(ascii.encode('last-modified'), const []), - new Header(ascii.encode('link'), const []), - new Header(ascii.encode('location'), const []), - new Header(ascii.encode('max-forwards'), const []), - new Header(ascii.encode('proxy-authenticate'), const []), - new Header(ascii.encode('proxy-authorization'), const []), - new Header(ascii.encode('range'), const []), - new Header(ascii.encode('referer'), const []), - new Header(ascii.encode('refresh'), const []), - new Header(ascii.encode('retry-after'), const []), - new Header(ascii.encode('server'), const []), - new Header(ascii.encode('set-cookie'), const []), - new Header(ascii.encode('strict-transport-security'), const []), - new Header(ascii.encode('transfer-encoding'), const []), - new Header(ascii.encode('user-agent'), const []), - new Header(ascii.encode('vary'), const []), - new Header(ascii.encode('via'), const []), - new Header(ascii.encode('www-authenticate'), const []), + Header(ascii.encode(':authority'), const []), + Header(ascii.encode(':method'), ascii.encode('GET')), + Header(ascii.encode(':method'), ascii.encode('POST')), + Header(ascii.encode(':path'), ascii.encode('/')), + Header(ascii.encode(':path'), ascii.encode('/index.html')), + Header(ascii.encode(':scheme'), ascii.encode('http')), + Header(ascii.encode(':scheme'), ascii.encode('https')), + Header(ascii.encode(':status'), ascii.encode('200')), + Header(ascii.encode(':status'), ascii.encode('204')), + Header(ascii.encode(':status'), ascii.encode('206')), + Header(ascii.encode(':status'), ascii.encode('304')), + Header(ascii.encode(':status'), ascii.encode('400')), + Header(ascii.encode(':status'), ascii.encode('404')), + Header(ascii.encode(':status'), ascii.encode('500')), + Header(ascii.encode('accept-charset'), const []), + Header(ascii.encode('accept-encoding'), ascii.encode('gzip, deflate')), + Header(ascii.encode('accept-language'), const []), + Header(ascii.encode('accept-ranges'), const []), + Header(ascii.encode('accept'), const []), + Header(ascii.encode('access-control-allow-origin'), const []), + Header(ascii.encode('age'), const []), + Header(ascii.encode('allow'), const []), + Header(ascii.encode('authorization'), const []), + Header(ascii.encode('cache-control'), const []), + Header(ascii.encode('content-disposition'), const []), + Header(ascii.encode('content-encoding'), const []), + Header(ascii.encode('content-language'), const []), + Header(ascii.encode('content-length'), const []), + Header(ascii.encode('content-location'), const []), + Header(ascii.encode('content-range'), const []), + Header(ascii.encode('content-type'), const []), + Header(ascii.encode('cookie'), const []), + Header(ascii.encode('date'), const []), + Header(ascii.encode('etag'), const []), + Header(ascii.encode('expect'), const []), + Header(ascii.encode('expires'), const []), + Header(ascii.encode('from'), const []), + Header(ascii.encode('host'), const []), + Header(ascii.encode('if-match'), const []), + Header(ascii.encode('if-modified-since'), const []), + Header(ascii.encode('if-none-match'), const []), + Header(ascii.encode('if-range'), const []), + Header(ascii.encode('if-unmodified-since'), const []), + Header(ascii.encode('last-modified'), const []), + Header(ascii.encode('link'), const []), + Header(ascii.encode('location'), const []), + Header(ascii.encode('max-forwards'), const []), + Header(ascii.encode('proxy-authenticate'), const []), + Header(ascii.encode('proxy-authorization'), const []), + Header(ascii.encode('range'), const []), + Header(ascii.encode('referer'), const []), + Header(ascii.encode('refresh'), const []), + Header(ascii.encode('retry-after'), const []), + Header(ascii.encode('server'), const []), + Header(ascii.encode('set-cookie'), const []), + Header(ascii.encode('strict-transport-security'), const []), + Header(ascii.encode('transfer-encoding'), const []), + Header(ascii.encode('user-agent'), const []), + Header(ascii.encode('vary'), const []), + Header(ascii.encode('via'), const []), + Header(ascii.encode('www-authenticate'), const []), ]; final List
_dynamicTable = []; @@ -307,7 +306,7 @@ class IndexTable { /// Lookup an item by index. Header lookup(int index) { if (index <= 0) { - throw new HPackDecodingException( + throw HPackDecodingException( 'Invalid index (was: $index) for table lookup.'); } if (index < _staticTable.length) { @@ -317,7 +316,7 @@ class IndexTable { if (index < _dynamicTable.length) { return _dynamicTable[index]; } - throw new HPackDecodingException( + throw HPackDecodingException( 'Invalid index (was: $index) for table lookup.'); } diff --git a/pkgs/http2/lib/src/hpack/huffman.dart b/pkgs/http2/lib/src/hpack/huffman.dart index 95d16d08c8..fc4a91bbe7 100644 --- a/pkgs/http2/lib/src/hpack/huffman.dart +++ b/pkgs/http2/lib/src/hpack/huffman.dart @@ -37,7 +37,7 @@ class HuffmanDecoder { /// Decodes [bytes] using a huffman tree. List decode(List bytes) { - var buffer = new BytesBuilder(); + var buffer = BytesBuilder(); int currentByteOffset = 0; HuffmanTreeNode node = _root; @@ -54,7 +54,7 @@ class HuffmanDecoder { currentDepth++; if (node.value != null) { if (node.value == EOS_BYTE) { - throw new HuffmanDecodingException( + throw HuffmanDecodingException( 'More than 7 bit padding is not allowed. Found entire EOS ' 'encoding'); } @@ -68,14 +68,14 @@ class HuffmanDecoder { if (node != _root) { if (currentDepth > 7) { - throw new HuffmanDecodingException( + throw HuffmanDecodingException( 'Incomplete encoding of a byte or more than 7 bit padding.'); } while (node.right != null) node = node.right; if (node.value != 256) { - throw new HuffmanDecodingException('Incomplete encoding of a byte.'); + throw HuffmanDecodingException('Incomplete encoding of a byte.'); } } @@ -91,7 +91,7 @@ class HuffmanEncoder { /// Encodes [bytes] using a list of codewords. List encode(List bytes) { - var buffer = new BytesBuilder(); + var buffer = BytesBuilder(); int currentByte = 0; int currentBitOffset = 7; @@ -154,7 +154,7 @@ class HuffmanTreeNode { /// Generates a huffman decoding tree. HuffmanTreeNode generateHuffmanTree(List valueEncodings) { - HuffmanTreeNode root = new HuffmanTreeNode(); + HuffmanTreeNode root = HuffmanTreeNode(); for (int byteOffset = 0; byteOffset < valueEncodings.length; byteOffset++) { var entry = valueEncodings[byteOffset]; @@ -166,12 +166,12 @@ HuffmanTreeNode generateHuffmanTree(List valueEncodings) { if (right) { if (current.right == null) { - current.right = new HuffmanTreeNode(); + current.right = HuffmanTreeNode(); } current = current.right; } else { if (current.left == null) { - current.left = new HuffmanTreeNode(); + current.left = HuffmanTreeNode(); } current = current.left; } diff --git a/pkgs/http2/lib/src/hpack/huffman_table.dart b/pkgs/http2/lib/src/hpack/huffman_table.dart index 90471c8b6d..ca3f250922 100644 --- a/pkgs/http2/lib/src/hpack/huffman_table.dart +++ b/pkgs/http2/lib/src/hpack/huffman_table.dart @@ -7,9 +7,8 @@ library http2.hpack.huffman_table; import 'huffman.dart'; /// The huffman codec for encoding/decoding HTTP/2 header blocks. -final HuffmanCodec http2HuffmanCodec = new HuffmanCodec( - new HuffmanEncoder(_codeWords), - new HuffmanDecoder(generateHuffmanTree(_codeWords))); +final HuffmanCodec http2HuffmanCodec = HuffmanCodec(HuffmanEncoder(_codeWords), + HuffmanDecoder(generateHuffmanTree(_codeWords))); /// This is the integer representing the End-of-String symbol /// (it is not representable by a byte). @@ -17,262 +16,262 @@ const int EOS_BYTE = 256; /// This list of byte encodings via huffman encoding was generated from the /// HPACK specification. -const List _codeWords = const [ - const EncodedHuffmanValue(0x1ff8, 13), - const EncodedHuffmanValue(0x7fffd8, 23), - const EncodedHuffmanValue(0xfffffe2, 28), - const EncodedHuffmanValue(0xfffffe3, 28), - const EncodedHuffmanValue(0xfffffe4, 28), - const EncodedHuffmanValue(0xfffffe5, 28), - const EncodedHuffmanValue(0xfffffe6, 28), - const EncodedHuffmanValue(0xfffffe7, 28), - const EncodedHuffmanValue(0xfffffe8, 28), - const EncodedHuffmanValue(0xffffea, 24), - const EncodedHuffmanValue(0x3ffffffc, 30), - const EncodedHuffmanValue(0xfffffe9, 28), - const EncodedHuffmanValue(0xfffffea, 28), - const EncodedHuffmanValue(0x3ffffffd, 30), - const EncodedHuffmanValue(0xfffffeb, 28), - const EncodedHuffmanValue(0xfffffec, 28), - const EncodedHuffmanValue(0xfffffed, 28), - const EncodedHuffmanValue(0xfffffee, 28), - const EncodedHuffmanValue(0xfffffef, 28), - const EncodedHuffmanValue(0xffffff0, 28), - const EncodedHuffmanValue(0xffffff1, 28), - const EncodedHuffmanValue(0xffffff2, 28), - const EncodedHuffmanValue(0x3ffffffe, 30), - const EncodedHuffmanValue(0xffffff3, 28), - const EncodedHuffmanValue(0xffffff4, 28), - const EncodedHuffmanValue(0xffffff5, 28), - const EncodedHuffmanValue(0xffffff6, 28), - const EncodedHuffmanValue(0xffffff7, 28), - const EncodedHuffmanValue(0xffffff8, 28), - const EncodedHuffmanValue(0xffffff9, 28), - const EncodedHuffmanValue(0xffffffa, 28), - const EncodedHuffmanValue(0xffffffb, 28), - const EncodedHuffmanValue(0x14, 6), - const EncodedHuffmanValue(0x3f8, 10), - const EncodedHuffmanValue(0x3f9, 10), - const EncodedHuffmanValue(0xffa, 12), - const EncodedHuffmanValue(0x1ff9, 13), - const EncodedHuffmanValue(0x15, 6), - const EncodedHuffmanValue(0xf8, 8), - const EncodedHuffmanValue(0x7fa, 11), - const EncodedHuffmanValue(0x3fa, 10), - const EncodedHuffmanValue(0x3fb, 10), - const EncodedHuffmanValue(0xf9, 8), - const EncodedHuffmanValue(0x7fb, 11), - const EncodedHuffmanValue(0xfa, 8), - const EncodedHuffmanValue(0x16, 6), - const EncodedHuffmanValue(0x17, 6), - const EncodedHuffmanValue(0x18, 6), - const EncodedHuffmanValue(0x0, 5), - const EncodedHuffmanValue(0x1, 5), - const EncodedHuffmanValue(0x2, 5), - const EncodedHuffmanValue(0x19, 6), - const EncodedHuffmanValue(0x1a, 6), - const EncodedHuffmanValue(0x1b, 6), - const EncodedHuffmanValue(0x1c, 6), - const EncodedHuffmanValue(0x1d, 6), - const EncodedHuffmanValue(0x1e, 6), - const EncodedHuffmanValue(0x1f, 6), - const EncodedHuffmanValue(0x5c, 7), - const EncodedHuffmanValue(0xfb, 8), - const EncodedHuffmanValue(0x7ffc, 15), - const EncodedHuffmanValue(0x20, 6), - const EncodedHuffmanValue(0xffb, 12), - const EncodedHuffmanValue(0x3fc, 10), - const EncodedHuffmanValue(0x1ffa, 13), - const EncodedHuffmanValue(0x21, 6), - const EncodedHuffmanValue(0x5d, 7), - const EncodedHuffmanValue(0x5e, 7), - const EncodedHuffmanValue(0x5f, 7), - const EncodedHuffmanValue(0x60, 7), - const EncodedHuffmanValue(0x61, 7), - const EncodedHuffmanValue(0x62, 7), - const EncodedHuffmanValue(0x63, 7), - const EncodedHuffmanValue(0x64, 7), - const EncodedHuffmanValue(0x65, 7), - const EncodedHuffmanValue(0x66, 7), - const EncodedHuffmanValue(0x67, 7), - const EncodedHuffmanValue(0x68, 7), - const EncodedHuffmanValue(0x69, 7), - const EncodedHuffmanValue(0x6a, 7), - const EncodedHuffmanValue(0x6b, 7), - const EncodedHuffmanValue(0x6c, 7), - const EncodedHuffmanValue(0x6d, 7), - const EncodedHuffmanValue(0x6e, 7), - const EncodedHuffmanValue(0x6f, 7), - const EncodedHuffmanValue(0x70, 7), - const EncodedHuffmanValue(0x71, 7), - const EncodedHuffmanValue(0x72, 7), - const EncodedHuffmanValue(0xfc, 8), - const EncodedHuffmanValue(0x73, 7), - const EncodedHuffmanValue(0xfd, 8), - const EncodedHuffmanValue(0x1ffb, 13), - const EncodedHuffmanValue(0x7fff0, 19), - const EncodedHuffmanValue(0x1ffc, 13), - const EncodedHuffmanValue(0x3ffc, 14), - const EncodedHuffmanValue(0x22, 6), - const EncodedHuffmanValue(0x7ffd, 15), - const EncodedHuffmanValue(0x3, 5), - const EncodedHuffmanValue(0x23, 6), - const EncodedHuffmanValue(0x4, 5), - const EncodedHuffmanValue(0x24, 6), - const EncodedHuffmanValue(0x5, 5), - const EncodedHuffmanValue(0x25, 6), - const EncodedHuffmanValue(0x26, 6), - const EncodedHuffmanValue(0x27, 6), - const EncodedHuffmanValue(0x6, 5), - const EncodedHuffmanValue(0x74, 7), - const EncodedHuffmanValue(0x75, 7), - const EncodedHuffmanValue(0x28, 6), - const EncodedHuffmanValue(0x29, 6), - const EncodedHuffmanValue(0x2a, 6), - const EncodedHuffmanValue(0x7, 5), - const EncodedHuffmanValue(0x2b, 6), - const EncodedHuffmanValue(0x76, 7), - const EncodedHuffmanValue(0x2c, 6), - const EncodedHuffmanValue(0x8, 5), - const EncodedHuffmanValue(0x9, 5), - const EncodedHuffmanValue(0x2d, 6), - const EncodedHuffmanValue(0x77, 7), - const EncodedHuffmanValue(0x78, 7), - const EncodedHuffmanValue(0x79, 7), - const EncodedHuffmanValue(0x7a, 7), - const EncodedHuffmanValue(0x7b, 7), - const EncodedHuffmanValue(0x7ffe, 15), - const EncodedHuffmanValue(0x7fc, 11), - const EncodedHuffmanValue(0x3ffd, 14), - const EncodedHuffmanValue(0x1ffd, 13), - const EncodedHuffmanValue(0xffffffc, 28), - const EncodedHuffmanValue(0xfffe6, 20), - const EncodedHuffmanValue(0x3fffd2, 22), - const EncodedHuffmanValue(0xfffe7, 20), - const EncodedHuffmanValue(0xfffe8, 20), - const EncodedHuffmanValue(0x3fffd3, 22), - const EncodedHuffmanValue(0x3fffd4, 22), - const EncodedHuffmanValue(0x3fffd5, 22), - const EncodedHuffmanValue(0x7fffd9, 23), - const EncodedHuffmanValue(0x3fffd6, 22), - const EncodedHuffmanValue(0x7fffda, 23), - const EncodedHuffmanValue(0x7fffdb, 23), - const EncodedHuffmanValue(0x7fffdc, 23), - const EncodedHuffmanValue(0x7fffdd, 23), - const EncodedHuffmanValue(0x7fffde, 23), - const EncodedHuffmanValue(0xffffeb, 24), - const EncodedHuffmanValue(0x7fffdf, 23), - const EncodedHuffmanValue(0xffffec, 24), - const EncodedHuffmanValue(0xffffed, 24), - const EncodedHuffmanValue(0x3fffd7, 22), - const EncodedHuffmanValue(0x7fffe0, 23), - const EncodedHuffmanValue(0xffffee, 24), - const EncodedHuffmanValue(0x7fffe1, 23), - const EncodedHuffmanValue(0x7fffe2, 23), - const EncodedHuffmanValue(0x7fffe3, 23), - const EncodedHuffmanValue(0x7fffe4, 23), - const EncodedHuffmanValue(0x1fffdc, 21), - const EncodedHuffmanValue(0x3fffd8, 22), - const EncodedHuffmanValue(0x7fffe5, 23), - const EncodedHuffmanValue(0x3fffd9, 22), - const EncodedHuffmanValue(0x7fffe6, 23), - const EncodedHuffmanValue(0x7fffe7, 23), - const EncodedHuffmanValue(0xffffef, 24), - const EncodedHuffmanValue(0x3fffda, 22), - const EncodedHuffmanValue(0x1fffdd, 21), - const EncodedHuffmanValue(0xfffe9, 20), - const EncodedHuffmanValue(0x3fffdb, 22), - const EncodedHuffmanValue(0x3fffdc, 22), - const EncodedHuffmanValue(0x7fffe8, 23), - const EncodedHuffmanValue(0x7fffe9, 23), - const EncodedHuffmanValue(0x1fffde, 21), - const EncodedHuffmanValue(0x7fffea, 23), - const EncodedHuffmanValue(0x3fffdd, 22), - const EncodedHuffmanValue(0x3fffde, 22), - const EncodedHuffmanValue(0xfffff0, 24), - const EncodedHuffmanValue(0x1fffdf, 21), - const EncodedHuffmanValue(0x3fffdf, 22), - const EncodedHuffmanValue(0x7fffeb, 23), - const EncodedHuffmanValue(0x7fffec, 23), - const EncodedHuffmanValue(0x1fffe0, 21), - const EncodedHuffmanValue(0x1fffe1, 21), - const EncodedHuffmanValue(0x3fffe0, 22), - const EncodedHuffmanValue(0x1fffe2, 21), - const EncodedHuffmanValue(0x7fffed, 23), - const EncodedHuffmanValue(0x3fffe1, 22), - const EncodedHuffmanValue(0x7fffee, 23), - const EncodedHuffmanValue(0x7fffef, 23), - const EncodedHuffmanValue(0xfffea, 20), - const EncodedHuffmanValue(0x3fffe2, 22), - const EncodedHuffmanValue(0x3fffe3, 22), - const EncodedHuffmanValue(0x3fffe4, 22), - const EncodedHuffmanValue(0x7ffff0, 23), - const EncodedHuffmanValue(0x3fffe5, 22), - const EncodedHuffmanValue(0x3fffe6, 22), - const EncodedHuffmanValue(0x7ffff1, 23), - const EncodedHuffmanValue(0x3ffffe0, 26), - const EncodedHuffmanValue(0x3ffffe1, 26), - const EncodedHuffmanValue(0xfffeb, 20), - const EncodedHuffmanValue(0x7fff1, 19), - const EncodedHuffmanValue(0x3fffe7, 22), - const EncodedHuffmanValue(0x7ffff2, 23), - const EncodedHuffmanValue(0x3fffe8, 22), - const EncodedHuffmanValue(0x1ffffec, 25), - const EncodedHuffmanValue(0x3ffffe2, 26), - const EncodedHuffmanValue(0x3ffffe3, 26), - const EncodedHuffmanValue(0x3ffffe4, 26), - const EncodedHuffmanValue(0x7ffffde, 27), - const EncodedHuffmanValue(0x7ffffdf, 27), - const EncodedHuffmanValue(0x3ffffe5, 26), - const EncodedHuffmanValue(0xfffff1, 24), - const EncodedHuffmanValue(0x1ffffed, 25), - const EncodedHuffmanValue(0x7fff2, 19), - const EncodedHuffmanValue(0x1fffe3, 21), - const EncodedHuffmanValue(0x3ffffe6, 26), - const EncodedHuffmanValue(0x7ffffe0, 27), - const EncodedHuffmanValue(0x7ffffe1, 27), - const EncodedHuffmanValue(0x3ffffe7, 26), - const EncodedHuffmanValue(0x7ffffe2, 27), - const EncodedHuffmanValue(0xfffff2, 24), - const EncodedHuffmanValue(0x1fffe4, 21), - const EncodedHuffmanValue(0x1fffe5, 21), - const EncodedHuffmanValue(0x3ffffe8, 26), - const EncodedHuffmanValue(0x3ffffe9, 26), - const EncodedHuffmanValue(0xffffffd, 28), - const EncodedHuffmanValue(0x7ffffe3, 27), - const EncodedHuffmanValue(0x7ffffe4, 27), - const EncodedHuffmanValue(0x7ffffe5, 27), - const EncodedHuffmanValue(0xfffec, 20), - const EncodedHuffmanValue(0xfffff3, 24), - const EncodedHuffmanValue(0xfffed, 20), - const EncodedHuffmanValue(0x1fffe6, 21), - const EncodedHuffmanValue(0x3fffe9, 22), - const EncodedHuffmanValue(0x1fffe7, 21), - const EncodedHuffmanValue(0x1fffe8, 21), - const EncodedHuffmanValue(0x7ffff3, 23), - const EncodedHuffmanValue(0x3fffea, 22), - const EncodedHuffmanValue(0x3fffeb, 22), - const EncodedHuffmanValue(0x1ffffee, 25), - const EncodedHuffmanValue(0x1ffffef, 25), - const EncodedHuffmanValue(0xfffff4, 24), - const EncodedHuffmanValue(0xfffff5, 24), - const EncodedHuffmanValue(0x3ffffea, 26), - const EncodedHuffmanValue(0x7ffff4, 23), - const EncodedHuffmanValue(0x3ffffeb, 26), - const EncodedHuffmanValue(0x7ffffe6, 27), - const EncodedHuffmanValue(0x3ffffec, 26), - const EncodedHuffmanValue(0x3ffffed, 26), - const EncodedHuffmanValue(0x7ffffe7, 27), - const EncodedHuffmanValue(0x7ffffe8, 27), - const EncodedHuffmanValue(0x7ffffe9, 27), - const EncodedHuffmanValue(0x7ffffea, 27), - const EncodedHuffmanValue(0x7ffffeb, 27), - const EncodedHuffmanValue(0xffffffe, 28), - const EncodedHuffmanValue(0x7ffffec, 27), - const EncodedHuffmanValue(0x7ffffed, 27), - const EncodedHuffmanValue(0x7ffffee, 27), - const EncodedHuffmanValue(0x7ffffef, 27), - const EncodedHuffmanValue(0x7fffff0, 27), - const EncodedHuffmanValue(0x3ffffee, 26), - const EncodedHuffmanValue(0x3fffffff, 30), +const List _codeWords = [ + EncodedHuffmanValue(0x1ff8, 13), + EncodedHuffmanValue(0x7fffd8, 23), + EncodedHuffmanValue(0xfffffe2, 28), + EncodedHuffmanValue(0xfffffe3, 28), + EncodedHuffmanValue(0xfffffe4, 28), + EncodedHuffmanValue(0xfffffe5, 28), + EncodedHuffmanValue(0xfffffe6, 28), + EncodedHuffmanValue(0xfffffe7, 28), + EncodedHuffmanValue(0xfffffe8, 28), + EncodedHuffmanValue(0xffffea, 24), + EncodedHuffmanValue(0x3ffffffc, 30), + EncodedHuffmanValue(0xfffffe9, 28), + EncodedHuffmanValue(0xfffffea, 28), + EncodedHuffmanValue(0x3ffffffd, 30), + EncodedHuffmanValue(0xfffffeb, 28), + EncodedHuffmanValue(0xfffffec, 28), + EncodedHuffmanValue(0xfffffed, 28), + EncodedHuffmanValue(0xfffffee, 28), + EncodedHuffmanValue(0xfffffef, 28), + EncodedHuffmanValue(0xffffff0, 28), + EncodedHuffmanValue(0xffffff1, 28), + EncodedHuffmanValue(0xffffff2, 28), + EncodedHuffmanValue(0x3ffffffe, 30), + EncodedHuffmanValue(0xffffff3, 28), + EncodedHuffmanValue(0xffffff4, 28), + EncodedHuffmanValue(0xffffff5, 28), + EncodedHuffmanValue(0xffffff6, 28), + EncodedHuffmanValue(0xffffff7, 28), + EncodedHuffmanValue(0xffffff8, 28), + EncodedHuffmanValue(0xffffff9, 28), + EncodedHuffmanValue(0xffffffa, 28), + EncodedHuffmanValue(0xffffffb, 28), + EncodedHuffmanValue(0x14, 6), + EncodedHuffmanValue(0x3f8, 10), + EncodedHuffmanValue(0x3f9, 10), + EncodedHuffmanValue(0xffa, 12), + EncodedHuffmanValue(0x1ff9, 13), + EncodedHuffmanValue(0x15, 6), + EncodedHuffmanValue(0xf8, 8), + EncodedHuffmanValue(0x7fa, 11), + EncodedHuffmanValue(0x3fa, 10), + EncodedHuffmanValue(0x3fb, 10), + EncodedHuffmanValue(0xf9, 8), + EncodedHuffmanValue(0x7fb, 11), + EncodedHuffmanValue(0xfa, 8), + EncodedHuffmanValue(0x16, 6), + EncodedHuffmanValue(0x17, 6), + EncodedHuffmanValue(0x18, 6), + EncodedHuffmanValue(0x0, 5), + EncodedHuffmanValue(0x1, 5), + EncodedHuffmanValue(0x2, 5), + EncodedHuffmanValue(0x19, 6), + EncodedHuffmanValue(0x1a, 6), + EncodedHuffmanValue(0x1b, 6), + EncodedHuffmanValue(0x1c, 6), + EncodedHuffmanValue(0x1d, 6), + EncodedHuffmanValue(0x1e, 6), + EncodedHuffmanValue(0x1f, 6), + EncodedHuffmanValue(0x5c, 7), + EncodedHuffmanValue(0xfb, 8), + EncodedHuffmanValue(0x7ffc, 15), + EncodedHuffmanValue(0x20, 6), + EncodedHuffmanValue(0xffb, 12), + EncodedHuffmanValue(0x3fc, 10), + EncodedHuffmanValue(0x1ffa, 13), + EncodedHuffmanValue(0x21, 6), + EncodedHuffmanValue(0x5d, 7), + EncodedHuffmanValue(0x5e, 7), + EncodedHuffmanValue(0x5f, 7), + EncodedHuffmanValue(0x60, 7), + EncodedHuffmanValue(0x61, 7), + EncodedHuffmanValue(0x62, 7), + EncodedHuffmanValue(0x63, 7), + EncodedHuffmanValue(0x64, 7), + EncodedHuffmanValue(0x65, 7), + EncodedHuffmanValue(0x66, 7), + EncodedHuffmanValue(0x67, 7), + EncodedHuffmanValue(0x68, 7), + EncodedHuffmanValue(0x69, 7), + EncodedHuffmanValue(0x6a, 7), + EncodedHuffmanValue(0x6b, 7), + EncodedHuffmanValue(0x6c, 7), + EncodedHuffmanValue(0x6d, 7), + EncodedHuffmanValue(0x6e, 7), + EncodedHuffmanValue(0x6f, 7), + EncodedHuffmanValue(0x70, 7), + EncodedHuffmanValue(0x71, 7), + EncodedHuffmanValue(0x72, 7), + EncodedHuffmanValue(0xfc, 8), + EncodedHuffmanValue(0x73, 7), + EncodedHuffmanValue(0xfd, 8), + EncodedHuffmanValue(0x1ffb, 13), + EncodedHuffmanValue(0x7fff0, 19), + EncodedHuffmanValue(0x1ffc, 13), + EncodedHuffmanValue(0x3ffc, 14), + EncodedHuffmanValue(0x22, 6), + EncodedHuffmanValue(0x7ffd, 15), + EncodedHuffmanValue(0x3, 5), + EncodedHuffmanValue(0x23, 6), + EncodedHuffmanValue(0x4, 5), + EncodedHuffmanValue(0x24, 6), + EncodedHuffmanValue(0x5, 5), + EncodedHuffmanValue(0x25, 6), + EncodedHuffmanValue(0x26, 6), + EncodedHuffmanValue(0x27, 6), + EncodedHuffmanValue(0x6, 5), + EncodedHuffmanValue(0x74, 7), + EncodedHuffmanValue(0x75, 7), + EncodedHuffmanValue(0x28, 6), + EncodedHuffmanValue(0x29, 6), + EncodedHuffmanValue(0x2a, 6), + EncodedHuffmanValue(0x7, 5), + EncodedHuffmanValue(0x2b, 6), + EncodedHuffmanValue(0x76, 7), + EncodedHuffmanValue(0x2c, 6), + EncodedHuffmanValue(0x8, 5), + EncodedHuffmanValue(0x9, 5), + EncodedHuffmanValue(0x2d, 6), + EncodedHuffmanValue(0x77, 7), + EncodedHuffmanValue(0x78, 7), + EncodedHuffmanValue(0x79, 7), + EncodedHuffmanValue(0x7a, 7), + EncodedHuffmanValue(0x7b, 7), + EncodedHuffmanValue(0x7ffe, 15), + EncodedHuffmanValue(0x7fc, 11), + EncodedHuffmanValue(0x3ffd, 14), + EncodedHuffmanValue(0x1ffd, 13), + EncodedHuffmanValue(0xffffffc, 28), + EncodedHuffmanValue(0xfffe6, 20), + EncodedHuffmanValue(0x3fffd2, 22), + EncodedHuffmanValue(0xfffe7, 20), + EncodedHuffmanValue(0xfffe8, 20), + EncodedHuffmanValue(0x3fffd3, 22), + EncodedHuffmanValue(0x3fffd4, 22), + EncodedHuffmanValue(0x3fffd5, 22), + EncodedHuffmanValue(0x7fffd9, 23), + EncodedHuffmanValue(0x3fffd6, 22), + EncodedHuffmanValue(0x7fffda, 23), + EncodedHuffmanValue(0x7fffdb, 23), + EncodedHuffmanValue(0x7fffdc, 23), + EncodedHuffmanValue(0x7fffdd, 23), + EncodedHuffmanValue(0x7fffde, 23), + EncodedHuffmanValue(0xffffeb, 24), + EncodedHuffmanValue(0x7fffdf, 23), + EncodedHuffmanValue(0xffffec, 24), + EncodedHuffmanValue(0xffffed, 24), + EncodedHuffmanValue(0x3fffd7, 22), + EncodedHuffmanValue(0x7fffe0, 23), + EncodedHuffmanValue(0xffffee, 24), + EncodedHuffmanValue(0x7fffe1, 23), + EncodedHuffmanValue(0x7fffe2, 23), + EncodedHuffmanValue(0x7fffe3, 23), + EncodedHuffmanValue(0x7fffe4, 23), + EncodedHuffmanValue(0x1fffdc, 21), + EncodedHuffmanValue(0x3fffd8, 22), + EncodedHuffmanValue(0x7fffe5, 23), + EncodedHuffmanValue(0x3fffd9, 22), + EncodedHuffmanValue(0x7fffe6, 23), + EncodedHuffmanValue(0x7fffe7, 23), + EncodedHuffmanValue(0xffffef, 24), + EncodedHuffmanValue(0x3fffda, 22), + EncodedHuffmanValue(0x1fffdd, 21), + EncodedHuffmanValue(0xfffe9, 20), + EncodedHuffmanValue(0x3fffdb, 22), + EncodedHuffmanValue(0x3fffdc, 22), + EncodedHuffmanValue(0x7fffe8, 23), + EncodedHuffmanValue(0x7fffe9, 23), + EncodedHuffmanValue(0x1fffde, 21), + EncodedHuffmanValue(0x7fffea, 23), + EncodedHuffmanValue(0x3fffdd, 22), + EncodedHuffmanValue(0x3fffde, 22), + EncodedHuffmanValue(0xfffff0, 24), + EncodedHuffmanValue(0x1fffdf, 21), + EncodedHuffmanValue(0x3fffdf, 22), + EncodedHuffmanValue(0x7fffeb, 23), + EncodedHuffmanValue(0x7fffec, 23), + EncodedHuffmanValue(0x1fffe0, 21), + EncodedHuffmanValue(0x1fffe1, 21), + EncodedHuffmanValue(0x3fffe0, 22), + EncodedHuffmanValue(0x1fffe2, 21), + EncodedHuffmanValue(0x7fffed, 23), + EncodedHuffmanValue(0x3fffe1, 22), + EncodedHuffmanValue(0x7fffee, 23), + EncodedHuffmanValue(0x7fffef, 23), + EncodedHuffmanValue(0xfffea, 20), + EncodedHuffmanValue(0x3fffe2, 22), + EncodedHuffmanValue(0x3fffe3, 22), + EncodedHuffmanValue(0x3fffe4, 22), + EncodedHuffmanValue(0x7ffff0, 23), + EncodedHuffmanValue(0x3fffe5, 22), + EncodedHuffmanValue(0x3fffe6, 22), + EncodedHuffmanValue(0x7ffff1, 23), + EncodedHuffmanValue(0x3ffffe0, 26), + EncodedHuffmanValue(0x3ffffe1, 26), + EncodedHuffmanValue(0xfffeb, 20), + EncodedHuffmanValue(0x7fff1, 19), + EncodedHuffmanValue(0x3fffe7, 22), + EncodedHuffmanValue(0x7ffff2, 23), + EncodedHuffmanValue(0x3fffe8, 22), + EncodedHuffmanValue(0x1ffffec, 25), + EncodedHuffmanValue(0x3ffffe2, 26), + EncodedHuffmanValue(0x3ffffe3, 26), + EncodedHuffmanValue(0x3ffffe4, 26), + EncodedHuffmanValue(0x7ffffde, 27), + EncodedHuffmanValue(0x7ffffdf, 27), + EncodedHuffmanValue(0x3ffffe5, 26), + EncodedHuffmanValue(0xfffff1, 24), + EncodedHuffmanValue(0x1ffffed, 25), + EncodedHuffmanValue(0x7fff2, 19), + EncodedHuffmanValue(0x1fffe3, 21), + EncodedHuffmanValue(0x3ffffe6, 26), + EncodedHuffmanValue(0x7ffffe0, 27), + EncodedHuffmanValue(0x7ffffe1, 27), + EncodedHuffmanValue(0x3ffffe7, 26), + EncodedHuffmanValue(0x7ffffe2, 27), + EncodedHuffmanValue(0xfffff2, 24), + EncodedHuffmanValue(0x1fffe4, 21), + EncodedHuffmanValue(0x1fffe5, 21), + EncodedHuffmanValue(0x3ffffe8, 26), + EncodedHuffmanValue(0x3ffffe9, 26), + EncodedHuffmanValue(0xffffffd, 28), + EncodedHuffmanValue(0x7ffffe3, 27), + EncodedHuffmanValue(0x7ffffe4, 27), + EncodedHuffmanValue(0x7ffffe5, 27), + EncodedHuffmanValue(0xfffec, 20), + EncodedHuffmanValue(0xfffff3, 24), + EncodedHuffmanValue(0xfffed, 20), + EncodedHuffmanValue(0x1fffe6, 21), + EncodedHuffmanValue(0x3fffe9, 22), + EncodedHuffmanValue(0x1fffe7, 21), + EncodedHuffmanValue(0x1fffe8, 21), + EncodedHuffmanValue(0x7ffff3, 23), + EncodedHuffmanValue(0x3fffea, 22), + EncodedHuffmanValue(0x3fffeb, 22), + EncodedHuffmanValue(0x1ffffee, 25), + EncodedHuffmanValue(0x1ffffef, 25), + EncodedHuffmanValue(0xfffff4, 24), + EncodedHuffmanValue(0xfffff5, 24), + EncodedHuffmanValue(0x3ffffea, 26), + EncodedHuffmanValue(0x7ffff4, 23), + EncodedHuffmanValue(0x3ffffeb, 26), + EncodedHuffmanValue(0x7ffffe6, 27), + EncodedHuffmanValue(0x3ffffec, 26), + EncodedHuffmanValue(0x3ffffed, 26), + EncodedHuffmanValue(0x7ffffe7, 27), + EncodedHuffmanValue(0x7ffffe8, 27), + EncodedHuffmanValue(0x7ffffe9, 27), + EncodedHuffmanValue(0x7ffffea, 27), + EncodedHuffmanValue(0x7ffffeb, 27), + EncodedHuffmanValue(0xffffffe, 28), + EncodedHuffmanValue(0x7ffffec, 27), + EncodedHuffmanValue(0x7ffffed, 27), + EncodedHuffmanValue(0x7ffffee, 27), + EncodedHuffmanValue(0x7ffffef, 27), + EncodedHuffmanValue(0x7fffff0, 27), + EncodedHuffmanValue(0x3ffffee, 26), + EncodedHuffmanValue(0x3fffffff, 30), ]; diff --git a/pkgs/http2/lib/src/ping/ping_handler.dart b/pkgs/http2/lib/src/ping/ping_handler.dart index d5bef228f7..928c2d1a9b 100644 --- a/pkgs/http2/lib/src/ping/ping_handler.dart +++ b/pkgs/http2/lib/src/ping/ping_handler.dart @@ -32,7 +32,7 @@ class PingHandler extends Object with TerminatableMixin { void processPingFrame(PingFrame frame) { ensureNotTerminatedSync(() { if (frame.header.streamId != 0) { - throw new ProtocolException('Ping frames must have a stream id of 0.'); + throw ProtocolException('Ping frames must have a stream id of 0.'); } if (!frame.hasAckFlag) { @@ -44,7 +44,7 @@ class PingHandler extends Object with TerminatableMixin { } else { // NOTE: It is not specified what happens when one gets an ACK for a // ping we never sent. We be very strict and fail in this case. - throw new ProtocolException( + throw ProtocolException( 'Received ping ack with unknown opaque data.'); } } @@ -53,7 +53,7 @@ class PingHandler extends Object with TerminatableMixin { Future ping() { return ensureNotTerminatedAsync(() { - Completer c = new Completer(); + Completer c = Completer(); var id = _nextId++; _remainingPings[id] = c; _frameWriter.writePingFrame(id); diff --git a/pkgs/http2/lib/src/settings/settings.dart b/pkgs/http2/lib/src/settings/settings.dart index 00fe640cd9..957814a234 100644 --- a/pkgs/http2/lib/src/settings/settings.dart +++ b/pkgs/http2/lib/src/settings/settings.dart @@ -74,11 +74,11 @@ class ActiveSettings { int maxHeaderListSize; ActiveSettings( - {this.headerTableSize: 4096, - this.enablePush: true, + {this.headerTableSize = 4096, + this.enablePush = true, this.maxConcurrentStreams, - this.initialWindowSize: (1 << 16) - 1, - this.maxFrameSize: (1 << 14), + this.initialWindowSize = (1 << 16) - 1, + this.maxFrameSize = (1 << 14), this.maxHeaderListSize}); } @@ -107,7 +107,7 @@ class SettingsHandler extends Object with TerminatableMixin { final ActiveSettings _peerSettings; final _onInitialWindowSizeChangeController = - new StreamController.broadcast(sync: true); + StreamController.broadcast(sync: true); /// Events are fired when a SettingsFrame changes the initial size /// of stream windows. @@ -138,7 +138,7 @@ class SettingsHandler extends Object with TerminatableMixin { // NOTE: The specification does not say anything about ACKed settings // which were never sent to the other side. We consider this definitly // an error. - throw new ProtocolException( + throw ProtocolException( 'Received an acknowledged settings frame which did not have a ' 'outstanding settings request.'); } @@ -164,7 +164,7 @@ class SettingsHandler extends Object with TerminatableMixin { return ensureNotTerminatedAsync(() { // TODO: Have a timeout: When ACK doesn't get back in a reasonable time // frame we should quit with ErrorCode.SETTINGS_TIMEOUT. - var completer = new Completer(); + var completer = Completer(); _toBeAcknowledgedSettings.add(changes); _toBeAcknowledgedCompleters.add(completer); _frameWriter.writeSettingsFrame(changes); @@ -182,7 +182,7 @@ class SettingsHandler extends Object with TerminatableMixin { } else if (setting.value == 1) { base.enablePush = true; } else { - throw new ProtocolException( + throw ProtocolException( 'The push setting can be only set to 0 or 1.'); } break; @@ -213,7 +213,7 @@ class SettingsHandler extends Object with TerminatableMixin { _onInitialWindowSizeChangeController.add(difference); base.initialWindowSize = setting.value; } else { - throw new FlowControlException('Invalid initial window size.'); + throw FlowControlException('Invalid initial window size.'); } break; diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index a13c5cde99..167463a95c 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -139,7 +139,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { final ConnectionMessageQueueIn incomingQueue; final ConnectionMessageQueueOut outgoingQueue; - final StreamController _newStreamsC = new StreamController(); + final StreamController _newStreamsC = StreamController(); final ActiveSettings _peerSettings; final ActiveSettings _localSettings; @@ -181,8 +181,8 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { ActiveSettings peerSettings, ActiveSettings localSettings, ActiveStateHandler onActiveStateChanged) { - return new StreamHandler._(writer, incomingQueue, outgoingQueue, - peerSettings, localSettings, onActiveStateChanged, 1, 0); + return StreamHandler._(writer, incomingQueue, outgoingQueue, peerSettings, + localSettings, onActiveStateChanged, 1, 0); } factory StreamHandler.server( @@ -192,8 +192,8 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { ActiveSettings peerSettings, ActiveSettings localSettings, ActiveStateHandler onActiveStateChanged) { - return new StreamHandler._(writer, incomingQueue, outgoingQueue, - peerSettings, localSettings, onActiveStateChanged, 2, -1); + return StreamHandler._(writer, incomingQueue, outgoingQueue, peerSettings, + localSettings, onActiveStateChanged, 2, -1); } @override @@ -227,7 +227,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { .where((id) => id > lastStreamId && !_isPeerInitiatedStream(id)) .toList(); for (int id in streamIds) { - var exception = new StreamException( + var exception = StreamException( id, 'Remote end was telling us to stop. This stream was not processed ' 'and can therefore be retried (on a new connection).'); @@ -245,7 +245,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { return !isLocalStream; } - Http2StreamImpl newStream(List
headers, {bool endStream: false}) { + Http2StreamImpl newStream(List
headers, {bool endStream = false}) { return ensureNotTerminatedSync(() { var stream = newLocalStream(); _sendHeaders(stream, headers, endStream: endStream); @@ -258,7 +258,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { assert(_canCreateNewStream()); if (MAX_STREAM_ID < nextStreamId) { - throw new StateError( + throw StateError( 'Cannot create new streams, since a wrap around would happen.'); } int streamId = nextStreamId; @@ -283,7 +283,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { // first frame for stream 7 is sent or received. if (remoteStreamId <= lastRemoteStreamId) { - throw new ProtocolException('Remote tried to open new stream which is ' + throw ProtocolException('Remote tried to open new stream which is ' 'not in "idle" state.'); } @@ -307,25 +307,24 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { // - register incoming stream queue in connection-level queue var outgoingStreamWindow = - new Window(initialSize: _peerSettings.initialWindowSize); + Window(initialSize: _peerSettings.initialWindowSize); var incomingStreamWindow = - new Window(initialSize: _localSettings.initialWindowSize); + Window(initialSize: _localSettings.initialWindowSize); - var windowOutHandler = - new OutgoingStreamWindowHandler(outgoingStreamWindow); + var windowOutHandler = OutgoingStreamWindowHandler(outgoingStreamWindow); - var windowInHandler = new IncomingWindowHandler.stream( + var windowInHandler = IncomingWindowHandler.stream( _frameWriter, incomingStreamWindow, streamId); - var streamQueueIn = new StreamMessageQueueIn(windowInHandler); + var streamQueueIn = StreamMessageQueueIn(windowInHandler); var streamQueueOut = - new StreamMessageQueueOut(streamId, windowOutHandler, outgoingQueue); + StreamMessageQueueOut(streamId, windowOutHandler, outgoingQueue); incomingQueue.insertNewStreamMessageQueue(streamId, streamQueueIn); - var _outgoingC = new StreamController(); - var stream = new Http2StreamImpl( + var _outgoingC = StreamController(); + var stream = Http2StreamImpl( streamQueueIn, streamQueueOut, _outgoingC, @@ -347,8 +346,8 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { // still sending us data, despite us not being interested in it, we will // reset the stream. if (stream.state == StreamState.HalfClosedLocal) { - stream.outgoingQueue.enqueueMessage( - new ResetStreamMessage(stream.id, ErrorCode.CANCEL)); + stream.outgoingQueue + .enqueueMessage(ResetStreamMessage(stream.id, ErrorCode.CANCEL)); } }); @@ -380,20 +379,20 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { Http2StreamImpl stream, List
requestHeaders) { if (stream.state != StreamState.Open && stream.state != StreamState.HalfClosedRemote) { - throw new StateError('Cannot push based on a stream that is neither open ' + throw StateError('Cannot push based on a stream that is neither open ' 'nor half-closed-remote.'); } if (!_peerSettings.enablePush) { - throw new StateError('Client did disable server pushes.'); + throw StateError('Client did disable server pushes.'); } if (!_canCreateNewStream()) { - throw new StateError('Maximum number of streams reached.'); + throw StateError('Maximum number of streams reached.'); } if (_ranOutOfStreamIds()) { - throw new StateError('There are no more stream ids left. Please use a ' + throw StateError('There are no more stream ids left. Please use a ' 'new connection.'); } @@ -407,7 +406,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { // transition. _changeState(pushStream, StreamState.HalfClosedRemote); pushStream.incomingQueue - .enqueueMessage(new DataMessage(stream.id, const [], true)); + .enqueueMessage(DataMessage(stream.id, const [], true)); _frameWriter.writePushPromiseFrame( stream.id, pushStream.id, requestHeaders); @@ -453,7 +452,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { void _handleNewOutgoingMessage(Http2StreamImpl stream, StreamMessage msg) { if (stream.state == StreamState.Idle) { if (msg is! HeadersStreamMessage) { - var exception = new TransportException( + var exception = TransportException( 'The first message on a stream needs to be a headers frame.'); _closeStreamAbnormally(stream, exception); return; @@ -559,7 +558,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { } else { // A server cannot open new streams to the client. The only way // for a server to start a new stream is via a PUSH_PROMISE_FRAME. - throw new ProtocolException( + throw ProtocolException( 'HTTP/2 clients cannot receive HEADER_FRAMEs as a connection' 'attempt.'); } @@ -567,7 +566,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { if (frameBelongsToIdleStream()) { // We treat this as a protocol error even though not enforced // or specified by the HTTP/2 spec. - throw new ProtocolException( + throw ProtocolException( 'Got a WINDOW_UPDATE_FRAME for an "idle" stream id.'); } else { // We must be able to receive window update frames for streams that @@ -579,7 +578,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { if (frameBelongsToIdleStream()) { // [RstFrame]s for streams which haven't been established (known as // idle streams) must be treated as a connection error. - throw new ProtocolException( + throw ProtocolException( 'Got a RST_STREAM_FRAME for an "idle" stream id.'); } else { // [RstFrame]s for already dead (known as "closed") streams should @@ -603,10 +602,10 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { // TODO: When implementing priorities for HTTP/2 streams, these frames // need to be taken into account. } else if (frame is PushPromiseFrame) { - throw new ProtocolException('Cannot push on a non-existent stream ' + throw ProtocolException('Cannot push on a non-existent stream ' '(stream ${frame.header.streamId} does not exist)'); } else { - throw new StreamClosedException( + throw StreamClosedException( frame.header.streamId, 'No open stream found and was not a headers frame opening a ' 'new stream.'); @@ -623,7 +622,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { } else if (frame is RstStreamFrame) { _handleRstFrame(stream, frame); } else { - throw new ProtocolException( + throw ProtocolException( 'Unsupported frame type ${frame.runtimeType}.'); } } @@ -637,7 +636,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { if (stream.state != StreamState.Open && stream.state != StreamState.HalfClosedLocal) { - throw new StreamClosedException( + throw StreamClosedException( stream.id, 'Expected open state (was: ${stream.state}).'); } @@ -649,7 +648,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { void _handleDataFrame(Http2StreamImpl stream, DataFrame frame) { if (stream.state != StreamState.Open && stream.state != StreamState.HalfClosedLocal) { - throw new StreamClosedException( + throw StreamClosedException( stream.id, 'Expected open state (was: ${stream.state}).'); } @@ -661,8 +660,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { void _handlePushPromiseFrame(Http2StreamImpl stream, PushPromiseFrame frame) { if (stream.state != StreamState.Open && stream.state != StreamState.HalfClosedLocal) { - throw new ProtocolException( - 'Expected open state (was: ${stream.state}).'); + throw ProtocolException('Expected open state (was: ${stream.state}).'); } var pushedStream = newRemoteStream(frame.promisedStreamId); @@ -677,7 +675,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { void _handleRstFrame(Http2StreamImpl stream, RstStreamFrame frame) { stream._handleTerminated(frame.errorCode); - var exception = new StreamTransportException( + var exception = StreamTransportException( 'Stream was terminated by peer (errorCode: ${frame.errorCode}).'); _closeStreamAbnormally(stream, exception, propagateException: true); } @@ -694,7 +692,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { // (e.g. MessageQueues which are not empty yet). _openStreams.remove(stream.id); } else { - throw new StateError( + throw StateError( 'Got an end-of-stream from the remote end, but this stream is ' 'neither in the Open nor in the HalfClosedLocal state. ' 'This should never happen.'); @@ -706,15 +704,15 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { //////////////////////////////////////////////////////////////////////////// void _sendHeaders(Http2StreamImpl stream, List
headers, - {bool endStream: false}) { + {bool endStream = false}) { if (stream.state != StreamState.Idle && stream.state != StreamState.Open && stream.state != StreamState.HalfClosedRemote) { - throw new StateError('Idle state expected.'); + throw StateError('Idle state expected.'); } stream.outgoingQueue - .enqueueMessage(new HeadersMessage(stream.id, headers, endStream)); + .enqueueMessage(HeadersMessage(stream.id, headers, endStream)); if (stream.state == StreamState.Idle) { _changeState(stream, StreamState.Open); @@ -726,14 +724,14 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { } void _sendData(Http2StreamImpl stream, List data, - {bool endStream: false}) { + {bool endStream = false}) { if (stream.state != StreamState.Open && stream.state != StreamState.HalfClosedRemote) { - throw new StateError('Open state expected (was: ${stream.state}).'); + throw StateError('Open state expected (was: ${stream.state}).'); } stream.outgoingQueue - .enqueueMessage(new DataMessage(stream.id, data, endStream)); + .enqueueMessage(DataMessage(stream.id, data, endStream)); if (endStream) { _endStream(stream); @@ -746,8 +744,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { } else if (stream.state == StreamState.HalfClosedRemote) { _changeState(stream, StreamState.Closed); } else { - throw new StateError( - 'Invalid state transition. This should never happen.'); + throw StateError('Invalid state transition. This should never happen.'); } } @@ -771,7 +768,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { } void _closeStreamIdAbnormally(int streamId, Exception exception, - {bool propagateException: false}) { + {bool propagateException = false}) { Http2StreamImpl stream = _openStreams[streamId]; if (stream != null) { _closeStreamAbnormally(stream, exception, @@ -780,7 +777,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { } void _closeStreamAbnormally(Http2StreamImpl stream, Object exception, - {bool propagateException: false}) { + {bool propagateException = false}) { incomingQueue.removeStreamMessageQueue(stream.id); if (stream.state != StreamState.Terminated) { diff --git a/pkgs/http2/lib/src/testing/client.dart b/pkgs/http2/lib/src/testing/client.dart index a7d9dc4bee..001478d97f 100644 --- a/pkgs/http2/lib/src/testing/client.dart +++ b/pkgs/http2/lib/src/testing/client.dart @@ -40,17 +40,17 @@ class ClientConnection { /// If [settings] are omitted, the default [ClientSettings] will be used. ClientConnection(Socket socket, {ClientSettings settings}) : connection = - new ClientTransportConnection.viaSocket(socket, settings: settings); + ClientTransportConnection.viaSocket(socket, settings: settings); Future makeRequest(Request request) { var path = request.uri.path; if (path.isEmpty) path = '/'; var headers = [ - new Header.ascii(':method', request.method), - new Header.ascii(':path', path), - new Header.ascii(':scheme', request.uri.scheme), - new Header.ascii(':authority', '${request.uri.host}'), + Header.ascii(':method', request.method), + Header.ascii(':path', path), + Header.ascii(':scheme', request.uri.scheme), + Header.ascii(':authority', '${request.uri.host}'), ]; return _handleStream(connection.makeRequest(headers, endStream: true)); @@ -61,15 +61,15 @@ class ClientConnection { } Future _handleStream(ClientTransportStream stream) { - var completer = new Completer(); + var completer = Completer(); bool isFirst = true; - var controller = new StreamController>(); - var serverPushController = new StreamController(sync: true); + var controller = StreamController>(); + var serverPushController = StreamController(sync: true); stream.incomingMessages.listen((StreamMessage msg) { if (isFirst) { isFirst = false; var headerMap = _convertHeaders((msg as HeadersStreamMessage).headers); - completer.complete(new Response( + completer.complete(Response( headerMap, controller.stream, serverPushController.stream)); } else { controller.add((msg as DataStreamMessage).bytes); @@ -81,23 +81,23 @@ class ClientConnection { Stream _handlePeerPushes( Stream serverPushes) { - var pushesController = new StreamController(); + var pushesController = StreamController(); serverPushes.listen((TransportStreamPush push) { - var responseCompleter = new Completer(); - var serverPush = new ServerPush( + var responseCompleter = Completer(); + var serverPush = ServerPush( _convertHeaders(push.requestHeaders), responseCompleter.future); pushesController.add(serverPush); bool isFirst = true; - var dataController = new StreamController>(); + var dataController = StreamController>(); push.stream.incomingMessages.listen((StreamMessage msg) { if (isFirst) { isFirst = false; var headerMap = _convertHeaders((msg as HeadersStreamMessage).headers); - var response = new Response( - headerMap, dataController.stream, new Stream.fromIterable([])); + var response = Response( + headerMap, dataController.stream, Stream.fromIterable([])); responseCompleter.complete(response); } else { dataController.add((msg as DataStreamMessage).bytes); @@ -125,8 +125,8 @@ class ClientConnection { /// client. The maximum number of concurrent server pushes can be configured via /// [maxConcurrentPushes] (default is `null` meaning no limit). Future connect(Uri uri, - {bool allowServerPushes: false, int maxConcurrentPushes}) async { - const List Http2AlpnProtocols = const [ + {bool allowServerPushes = false, int maxConcurrentPushes}) async { + const List Http2AlpnProtocols = [ 'h2-14', 'h2-15', 'h2-16', @@ -135,18 +135,18 @@ Future connect(Uri uri, ]; bool useSSL = uri.scheme == 'https'; - var settings = new ClientSettings( + var settings = ClientSettings( concurrentStreamLimit: maxConcurrentPushes, allowServerPushes: allowServerPushes); if (useSSL) { SecureSocket socket = await SecureSocket.connect(uri.host, uri.port, supportedProtocols: Http2AlpnProtocols); if (!Http2AlpnProtocols.contains(socket.selectedProtocol)) { - throw new Exception('Server does not support HTTP/2.'); + throw Exception('Server does not support HTTP/2.'); } - return new ClientConnection(socket, settings: settings); + return ClientConnection(socket, settings: settings); } else { Socket socket = await Socket.connect(uri.host, uri.port); - return new ClientConnection(socket, settings: settings); + return ClientConnection(socket, settings: settings); } } diff --git a/pkgs/http2/lib/src/testing/debug.dart b/pkgs/http2/lib/src/testing/debug.dart index b4e0b2c442..bef37d59be 100644 --- a/pkgs/http2/lib/src/testing/debug.dart +++ b/pkgs/http2/lib/src/testing/debug.dart @@ -13,28 +13,28 @@ import '../connection_preface.dart'; import '../frames/frames.dart'; import '../settings/settings.dart'; -final jsonEncoder = new JsonEncoder.withIndent(' '); +final jsonEncoder = JsonEncoder.withIndent(' '); TransportConnection debugPrintingConnection(Socket socket, - {bool isServer: true, bool verbose: true}) { + {bool isServer = true, bool verbose = true}) { TransportConnection connection; var incoming = decodeVerbose(socket, isServer, verbose: verbose); var outgoing = decodeOutgoingVerbose(socket, isServer, verbose: verbose); if (isServer) { - connection = new ServerTransportConnection.viaStreams(incoming, outgoing); + connection = ServerTransportConnection.viaStreams(incoming, outgoing); } else { - connection = new ClientTransportConnection.viaStreams(incoming, outgoing); + connection = ClientTransportConnection.viaStreams(incoming, outgoing); } return connection; } Stream> decodeVerbose(Stream> inc, bool isServer, - {bool verbose: true}) { + {bool verbose = true}) { String name = isServer ? 'server' : 'client'; - var sc = new StreamController>(); - var sDebug = new StreamController>(); + var sc = StreamController>(); + var sDebug = StreamController>(); _pipeAndCopy(inc, sc, sDebug); @@ -72,11 +72,11 @@ Stream> decodeVerbose(Stream> inc, bool isServer, StreamSink> decodeOutgoingVerbose( StreamSink> sink, bool isServer, - {bool verbose: true}) { + {bool verbose = true}) { String name = isServer ? 'server' : 'client'; - var proxySink = new StreamController>(); - var copy = new StreamController>(); + var proxySink = StreamController>(); + var copy = StreamController>(); if (!isServer) { _decodeFrames(readConnectionPreface(copy.stream)).listen((Frame frame) { @@ -113,13 +113,13 @@ StreamSink> decodeOutgoingVerbose( } Stream _decodeFrames(Stream> bytes) { - var settings = new ActiveSettings(); - var decoder = new FrameReader(bytes, settings); + var settings = ActiveSettings(); + var decoder = FrameReader(bytes, settings); return decoder.startDecoding(); } Future _pipeAndCopy(Stream> from, StreamSink to, StreamSink to2) { - var c = new Completer(); + var c = Completer(); from.listen((List data) { to.add(data); to2.add(data); diff --git a/pkgs/http2/lib/transport.dart b/pkgs/http2/lib/transport.dart index 7237c9a318..444b3159a3 100644 --- a/pkgs/http2/lib/transport.dart +++ b/pkgs/http2/lib/transport.dart @@ -10,7 +10,7 @@ import 'src/hpack/hpack.dart' show Header; export 'src/hpack/hpack.dart' show Header; -typedef void ActiveStateHandler(bool isActive); +typedef ActiveStateHandler = void Function(bool isActive); /// Settings for a [TransportConnection]. abstract class Settings { @@ -41,7 +41,7 @@ class ClientSettings extends Settings { const ClientSettings( {int concurrentStreamLimit, int streamWindowSize, - this.allowServerPushes: false}) + this.allowServerPushes = false}) : super( concurrentStreamLimit: concurrentStreamLimit, streamWindowSize: streamWindowSize); @@ -72,14 +72,13 @@ abstract class TransportConnection { abstract class ClientTransportConnection extends TransportConnection { factory ClientTransportConnection.viaSocket(Socket socket, {ClientSettings settings}) => - new ClientTransportConnection.viaStreams(socket, socket, - settings: settings); + ClientTransportConnection.viaStreams(socket, socket, settings: settings); factory ClientTransportConnection.viaStreams( Stream> incoming, StreamSink> outgoing, {ClientSettings settings}) { if (settings == null) settings = const ClientSettings(); - return new ClientConnection(incoming, outgoing, settings); + return ClientConnection(incoming, outgoing, settings); } /// Whether this connection is open and can be used to make new requests @@ -88,22 +87,22 @@ abstract class ClientTransportConnection extends TransportConnection { /// Creates a new outgoing stream. ClientTransportStream makeRequest(List
headers, - {bool endStream: false}); + {bool endStream = false}); } abstract class ServerTransportConnection extends TransportConnection { factory ServerTransportConnection.viaSocket(Socket socket, {ServerSettings settings}) { - return new ServerTransportConnection.viaStreams(socket, socket, + return ServerTransportConnection.viaStreams(socket, socket, settings: settings); } factory ServerTransportConnection.viaStreams( Stream> incoming, StreamSink> outgoing, - {ServerSettings settings: + {ServerSettings settings = const ServerSettings(concurrentStreamLimit: 1000)}) { if (settings == null) settings = const ServerSettings(); - return new ServerConnection(incoming, outgoing, settings); + return ServerConnection(incoming, outgoing, settings); } /// Incoming HTTP/2 streams. @@ -140,14 +139,13 @@ abstract class TransportStream { void terminate(); // For convenience only. - void sendHeaders(List
headers, {bool endStream: false}) { - outgoingMessages - .add(new HeadersStreamMessage(headers, endStream: endStream)); + void sendHeaders(List
headers, {bool endStream = false}) { + outgoingMessages.add(HeadersStreamMessage(headers, endStream: endStream)); if (endStream) outgoingMessages.close(); } - void sendData(List bytes, {bool endStream: false}) { - outgoingMessages.add(new DataStreamMessage(bytes, endStream: endStream)); + void sendData(List bytes, {bool endStream = false}) { + outgoingMessages.add(DataStreamMessage(bytes, endStream: endStream)); if (endStream) outgoingMessages.close(); } } diff --git a/pkgs/http2/manual_test/out_of_stream_ids_test.dart b/pkgs/http2/manual_test/out_of_stream_ids_test.dart index 4e22f4b42a..e281c13b5d 100644 --- a/pkgs/http2/manual_test/out_of_stream_ids_test.dart +++ b/pkgs/http2/manual_test/out_of_stream_ids_test.dart @@ -27,14 +27,14 @@ main() { ServerTransportConnection server) async { Future serverFun() async { await for (ServerTransportStream stream in server.incomingStreams) { - stream.sendHeaders([new Header.ascii('x', 'y')], endStream: true); + stream.sendHeaders([Header.ascii('x', 'y')], endStream: true); expect(await stream.incomingMessages.toList(), hasLength(1)); } await server.finish(); } Future clientFun() async { - var headers = [new Header.ascii('a', 'b')]; + var headers = [Header.ascii('a', 'b')]; const kMaxStreamId = StreamHandler.MAX_STREAM_ID; for (int i = 1; i <= kMaxStreamId; i += 2) { @@ -47,7 +47,7 @@ main() { expect(() => client.makeRequest(headers), throwsA(const TypeMatcher())); - await new Future.delayed(const Duration(seconds: 1)); + await Future.delayed(const Duration(seconds: 1)); await client.finish(); } diff --git a/pkgs/http2/test/client_test.dart b/pkgs/http2/test/client_test.dart index 37ebc30f72..ac560752f5 100644 --- a/pkgs/http2/test/client_test.dart +++ b/pkgs/http2/test/client_test.dart @@ -27,7 +27,7 @@ main() { FrameWriter serverWriter, StreamIterator serverReader, Future nextFrame()) async { - var settingsDone = new Completer(); + var settingsDone = Completer(); Future serverFun() async { serverWriter.writeSettingsFrame([]); @@ -69,7 +69,7 @@ main() { FrameWriter serverWriter, StreamIterator serverReader, Future nextFrame()) async { - var goawayReceived = new Completer(); + var goawayReceived = Completer(); Future serverFun() async { serverWriter.writePingFrame(42); expect(await nextFrame() is SettingsFrame, true); @@ -89,7 +89,7 @@ main() { var error; try { - client.makeRequest([new Header.ascii('a', 'b')]); + client.makeRequest([Header.ascii('a', 'b')]); } catch (e) { error = '$e'; } @@ -116,7 +116,7 @@ main() { Future clientFun() async { expect(client.isOpen, true); - var stream = client.makeRequest([new Header.ascii('a', 'b')]); + var stream = client.makeRequest([Header.ascii('a', 'b')]); String error; try { @@ -136,7 +136,7 @@ main() { FrameWriter serverWriter, StreamIterator serverReader, Future nextFrame()) async { - var handshakeCompleter = new Completer(); + var handshakeCompleter = Completer(); Future serverFun() async { serverWriter.writeSettingsFrame([]); @@ -173,7 +173,7 @@ main() { Future clientFun() async { await handshakeCompleter.future; - var stream = client.makeRequest([new Header.ascii('a', 'b')]); + var stream = client.makeRequest([Header.ascii('a', 'b')]); await stream.outgoingMessages.close(); expect(await stream.incomingMessages.toList(), isEmpty); @@ -188,7 +188,7 @@ main() { FrameWriter serverWriter, StreamIterator serverReader, Future nextFrame()) async { - var handshakeCompleter = new Completer(); + var handshakeCompleter = Completer(); Future serverFun() async { serverWriter.writeSettingsFrame([]); @@ -236,7 +236,7 @@ main() { Future clientFun() async { await handshakeCompleter.future; - var stream = client.makeRequest([new Header.ascii('a', 'b')]); + var stream = client.makeRequest([Header.ascii('a', 'b')]); await stream.outgoingMessages.close(); var messages = await stream.incomingMessages.toList(); expect(messages, hasLength(1)); @@ -253,9 +253,9 @@ main() { FrameWriter serverWriter, StreamIterator serverReader, Future nextFrame()) async { - var handshakeCompleter = new Completer(); - var cancelDone = new Completer(); - var endDone = new Completer(); + var handshakeCompleter = Completer(); + var cancelDone = Completer(); + var endDone = Completer(); Future serverFun() async { serverWriter.writeSettingsFrame([]); @@ -309,7 +309,7 @@ main() { Future clientFun() async { await handshakeCompleter.future; - var stream = client.makeRequest([new Header.ascii('a', 'b')]); + var stream = client.makeRequest([Header.ascii('a', 'b')]); await stream.outgoingMessages.close(); // first will cancel the stream @@ -329,10 +329,10 @@ main() { FrameWriter serverWriter, StreamIterator serverReader, Future nextFrame()) async { - var handshakeCompleter = new Completer(); - var cancelDone = new Completer(); - var endDone = new Completer(); - var clientDone = new Completer(); + var handshakeCompleter = Completer(); + var cancelDone = Completer(); + var endDone = Completer(); + var clientDone = Completer(); Future serverFun() async { serverWriter.writeSettingsFrame([]); @@ -379,7 +379,7 @@ main() { Future clientFun() async { await handshakeCompleter.future; - var stream = client.makeRequest([new Header.ascii('a', 'b')]); + var stream = client.makeRequest([Header.ascii('a', 'b')]); // first will cancel the stream var message = await stream.incomingMessages.first; @@ -402,7 +402,7 @@ main() { FrameWriter serverWriter, StreamIterator serverReader, Future nextFrame()) async { - var handshakeCompleter = new Completer(); + var handshakeCompleter = Completer(); Future serverFun() async { serverWriter.writeSettingsFrame([]); @@ -419,13 +419,13 @@ main() { int streamId = headers.header.streamId; // Write response. - serverWriter.writeHeadersFrame(streamId, [new Header.ascii('a', 'b')], + serverWriter.writeHeadersFrame(streamId, [Header.ascii('a', 'b')], endStream: true); // Push stream to the (non existing) one. int pushStreamId = 2; serverWriter.writePushPromiseFrame( - streamId, pushStreamId, [new Header.ascii('a', 'b')]); + streamId, pushStreamId, [Header.ascii('a', 'b')]); // Make sure we get a connection error. GoawayFrame frame = await nextFrame(); @@ -438,7 +438,7 @@ main() { Future clientFun() async { await handshakeCompleter.future; - var stream = client.makeRequest([new Header.ascii('a', 'b')]); + var stream = client.makeRequest([Header.ascii('a', 'b')]); await stream.outgoingMessages.close(); var messages = await stream.incomingMessages.toList(); expect(messages, hasLength(1)); @@ -455,7 +455,7 @@ main() { FrameWriter serverWriter, StreamIterator serverReader, Future nextFrame()) async { - var handshakeCompleter = new Completer(); + var handshakeCompleter = Completer(); Future serverFun() async { serverWriter.writeSettingsFrame([]); @@ -474,7 +474,7 @@ main() { // Push stream onto the existing (but half-closed) one. int pushStreamId = 2; serverWriter.writePushPromiseFrame( - streamId, pushStreamId, [new Header.ascii('a', 'b')]); + streamId, pushStreamId, [Header.ascii('a', 'b')]); // Make sure we get a connection error. GoawayFrame frame = await nextFrame(); @@ -489,7 +489,7 @@ main() { Future clientFun() async { await handshakeCompleter.future; - var stream = client.makeRequest([new Header.ascii('a', 'b')]); + var stream = client.makeRequest([Header.ascii('a', 'b')]); // NOTE: We are not closing the outgoing part on purpose. expect(await stream.incomingMessages.toList(), isEmpty); @@ -506,7 +506,7 @@ main() { FrameWriter serverWriter, StreamIterator serverReader, Future nextFrame()) async { - var handshakeCompleter = new Completer(); + var handshakeCompleter = Completer(); Future serverFun() async { serverWriter.writeSettingsFrame([]); @@ -520,9 +520,9 @@ main() { int streamId = headers.header.streamId; // Write more than [kFlowControlWindowSize] bytes. - final int kFlowControlWindowSize = new Window().size; + final int kFlowControlWindowSize = Window().size; int sentBytes = 0; - final bytes = new Uint8List(1024); + final bytes = Uint8List(1024); while (sentBytes <= kFlowControlWindowSize) { serverWriter.writeDataFrame(streamId, bytes); sentBytes += bytes.length; @@ -542,12 +542,12 @@ main() { Future clientFun() async { await handshakeCompleter.future; - var stream = client.makeRequest([new Header.ascii('a', 'b')]); + var stream = client.makeRequest([Header.ascii('a', 'b')]); var sub = stream.incomingMessages.listen( expectAsync1((StreamMessage msg) {}, count: 0), onError: expectAsync1((error) {})); sub.pause(); - await new Future.delayed(const Duration(milliseconds: 40)); + await Future.delayed(const Duration(milliseconds: 40)); sub.resume(); await client.finish(); @@ -562,11 +562,11 @@ main() { FrameWriter serverWriter, StreamIterator serverReader, Future nextFrame()) async { - var settingsDone = new Completer(); - var headersDone = new Completer(); + var settingsDone = Completer(); + var headersDone = Completer(); Future serverFun() async { - var decoder = new HPackDecoder(); + var decoder = HPackDecoder(); serverWriter.writeSettingsFrame([]); expect(await nextFrame() is SettingsFrame, true); @@ -600,8 +600,8 @@ main() { await settingsDone.future; // Make a new stream and terminate it. - var stream = client - .makeRequest([new Header.ascii('a', 'b')], endStream: false); + var stream = + client.makeRequest([Header.ascii('a', 'b')], endStream: false); await headersDone.future; stream.terminate(); @@ -622,10 +622,10 @@ main() { FrameWriter serverWriter, StreamIterator serverReader, Future nextFrame()) async { - var settingsDone = new Completer(); + var settingsDone = Completer(); Future serverFun() async { - var decoder = new HPackDecoder(); + var decoder = HPackDecoder(); serverWriter.writeSettingsFrame([]); expect(await nextFrame() is SettingsFrame, true); @@ -653,8 +653,8 @@ main() { await settingsDone.future; // Make a new stream and terminate it. - var stream = client - .makeRequest([new Header.ascii('a', 'b')], endStream: false); + var stream = + client.makeRequest([Header.ascii('a', 'b')], endStream: false); // Make sure we don't get messages/pushes on the terminated stream. stream.incomingMessages.toList().catchError(expectAsync1((e) { @@ -683,7 +683,7 @@ void clientTest( StreamIterator frameReader, Future readNext())) { return test(name, () { - var streams = new ClientStreams(); + var streams = ClientStreams(); var serverReader = streams.serverConnectionFrameReader; Future readNext() async { @@ -697,25 +697,25 @@ void clientTest( } class ClientStreams { - final StreamController> writeA = new StreamController(); - final StreamController> writeB = new StreamController(); + final StreamController> writeA = StreamController(); + final StreamController> writeB = StreamController(); Stream> get readA => writeA.stream; Stream> get readB => writeB.stream; StreamIterator get serverConnectionFrameReader { - ActiveSettings localSettings = new ActiveSettings(); + ActiveSettings localSettings = ActiveSettings(); var streamAfterConnectionPreface = readConnectionPreface(readA); - return new StreamIterator( - new FrameReader(streamAfterConnectionPreface, localSettings) + return StreamIterator( + FrameReader(streamAfterConnectionPreface, localSettings) .startDecoding()); } FrameWriter get serverConnectionFrameWriter { - var encoder = new HPackEncoder(); - ActiveSettings peerSettings = new ActiveSettings(); - return new FrameWriter(encoder, writeB, peerSettings); + var encoder = HPackEncoder(); + ActiveSettings peerSettings = ActiveSettings(); + return FrameWriter(encoder, writeB, peerSettings); } ClientTransportConnection get clientConnection => - new ClientTransportConnection.viaStreams(readB, writeA); + ClientTransportConnection.viaStreams(readB, writeA); } diff --git a/pkgs/http2/test/client_websites_test.dart b/pkgs/http2/test/client_websites_test.dart index 679151ad26..26961a6a7a 100644 --- a/pkgs/http2/test/client_websites_test.dart +++ b/pkgs/http2/test/client_websites_test.dart @@ -15,10 +15,10 @@ main() async { test('google', () async { var uri = Uri.parse('https://www.google.com/'); ClientConnection connection = await connect(uri); - Response response = await connection.makeRequest(new Request('GET', uri)); + Response response = await connection.makeRequest(Request('GET', uri)); dumpHeaders(uri, response.headers); - final utf8Decoder = new Utf8Decoder(allowMalformed: true); + final utf8Decoder = Utf8Decoder(allowMalformed: true); String body = await response.stream.transform(utf8Decoder).join(''); connection.close(); @@ -30,7 +30,7 @@ main() async { test('twitter', () async { var uri = Uri.parse('https://twitter.com/'); ClientConnection connection = await connect(uri); - Response response = await connection.makeRequest(new Request('GET', uri)); + Response response = await connection.makeRequest(Request('GET', uri)); dumpHeaders(uri, response.headers); String body = await readBody(response); @@ -45,7 +45,7 @@ main() async { var uri = Uri.parse('https://nghttp2.org/'); ClientConnection connection = await connect(uri, allowServerPushes: true); - var request = new Request('GET', uri); + var request = Request('GET', uri); Response response = await connection.makeRequest(request); dumpHeaders(uri, response.headers); @@ -87,7 +87,7 @@ main() async { ClientConnection connection = await connect(uri, allowServerPushes: false); - var request = new Request('GET', uri); + var request = Request('GET', uri); Response response = await connection.makeRequest(request); dumpHeaders(uri, response.headers); @@ -116,7 +116,7 @@ main() async { } dumpHeaders(Uri uri, Map> headers, - {String msg: 'Response headers.'}) { + {String msg = 'Response headers.'}) { print(''); print('[$uri] $msg'); for (var key in headers.keys.toList()..sort()) { diff --git a/pkgs/http2/test/multiprotocol_server_test.dart b/pkgs/http2/test/multiprotocol_server_test.dart index 5b040e1986..bc2d05b346 100644 --- a/pkgs/http2/test/multiprotocol_server_test.dart +++ b/pkgs/http2/test/multiprotocol_server_test.dart @@ -14,7 +14,7 @@ import 'package:http2/transport.dart'; import 'package:http2/multiprotocol_server.dart'; main() { - SecurityContext context = new SecurityContext() + SecurityContext context = SecurityContext() ..useCertificateChain('test/certificates/server_chain.pem') ..usePrivateKey('test/certificates/server_key.pem', password: 'dartdart'); @@ -33,7 +33,7 @@ main() { }, count: Count), expectAsync1((ServerTransportStream stream) {}, count: 0)); - var client = new HttpClient(); + var client = HttpClient(); client.badCertificateCallback = (_, __, ___) => true; for (int i = 0; i < Count; i++) { await makeHttp11Request(server, client, i); @@ -57,7 +57,7 @@ main() { var socket = await SecureSocket.connect('localhost', server.port, onBadCertificate: (_) => true, supportedProtocols: ['http/1.1', 'h2']); - var connection = new ClientTransportConnection.viaSocket(socket); + var connection = ClientTransportConnection.viaSocket(socket); for (int i = 0; i < Count; i++) { await makeHttp2Request(server, connection, i); } @@ -86,14 +86,14 @@ Future makeHttp2Request(MultiProtocolHttpServer server, ClientTransportConnection connection, int i) async { expect(connection.isOpen, true); var headers = [ - new Header.ascii(':method', 'GET'), - new Header.ascii(':scheme', 'https'), - new Header.ascii(':authority', 'localhost:${server.port}'), - new Header.ascii(':path', '/abc$i'), + Header.ascii(':method', 'GET'), + Header.ascii(':scheme', 'https'), + Header.ascii(':authority', 'localhost:${server.port}'), + Header.ascii(':path', '/abc$i'), ]; var stream = connection.makeRequest(headers, endStream: true); - var si = new StreamIterator(stream.incomingMessages); + var si = StreamIterator(stream.incomingMessages); expect(await si.moveNext(), true); expect(si.current is HeadersStreamMessage, true); @@ -107,7 +107,7 @@ Future makeHttp2Request(MultiProtocolHttpServer server, } Future handleHttp2Request(ServerTransportStream stream, int i) async { - var si = new StreamIterator(stream.incomingMessages); + var si = StreamIterator(stream.incomingMessages); expect(await si.moveNext(), true); expect(si.current is HeadersStreamMessage, true); @@ -116,11 +116,11 @@ Future handleHttp2Request(ServerTransportStream stream, int i) async { expect(headers[':path'], '/abc$i'); expect(await si.moveNext(), false); - stream.outgoingMessages.add(new HeadersStreamMessage([ - new Header.ascii(':status', '200'), + stream.outgoingMessages.add(HeadersStreamMessage([ + Header.ascii(':status', '200'), ])); - stream.outgoingMessages.add(new DataStreamMessage(ascii.encode('answer$i'))); + stream.outgoingMessages.add(DataStreamMessage(ascii.encode('answer$i'))); await stream.outgoingMessages.close(); } diff --git a/pkgs/http2/test/server_test.dart b/pkgs/http2/test/server_test.dart index 7cab2c7c17..2b7a70d5ab 100644 --- a/pkgs/http2/test/server_test.dart +++ b/pkgs/http2/test/server_test.dart @@ -125,7 +125,7 @@ main() { clientWriter.writeSettingsFrame([]); expect(await nextFrame() is SettingsFrame, true); - clientWriter.writeHeadersFrame(3, [new Header.ascii('a', 'b')], + clientWriter.writeHeadersFrame(3, [Header.ascii('a', 'b')], endStream: true); // Write data frame to non-existent stream (stream 3 was closed @@ -155,7 +155,7 @@ main() { StreamIterator clientReader, Future nextFrame()) async { Future serverFun() async { - var it = new StreamIterator(server.incomingStreams); + var it = StreamIterator(server.incomingStreams); expect(await it.moveNext(), true); TransportStream stream = it.current; @@ -172,7 +172,7 @@ main() { clientWriter.writeSettingsFrame([]); expect(await nextFrame() is SettingsFrame, true); - clientWriter.writeHeadersFrame(1, [new Header.ascii('a', 'b')], + clientWriter.writeHeadersFrame(1, [Header.ascii('a', 'b')], endStream: false); // Make sure the client gets a [RstStreamFrame] frame. @@ -199,7 +199,7 @@ void serverTest( func(ServerTransportConnection serverConnection, FrameWriter frameWriter, StreamIterator frameReader, Future readNext())) { return test(name, () { - var streams = new ClientErrorStreams(); + var streams = ClientErrorStreams(); var clientReader = streams.clientConnectionFrameReader; Future readNext() async { @@ -213,24 +213,23 @@ void serverTest( } class ClientErrorStreams { - final StreamController> writeA = new StreamController(); - final StreamController> writeB = new StreamController(); + final StreamController> writeA = StreamController(); + final StreamController> writeB = StreamController(); Stream> get readA => writeA.stream; Stream> get readB => writeB.stream; StreamIterator get clientConnectionFrameReader { - ActiveSettings localSettings = new ActiveSettings(); - return new StreamIterator( - new FrameReader(readA, localSettings).startDecoding()); + ActiveSettings localSettings = ActiveSettings(); + return StreamIterator(FrameReader(readA, localSettings).startDecoding()); } FrameWriter get clientConnectionFrameWriter { - var encoder = new HPackEncoder(); - ActiveSettings peerSettings = new ActiveSettings(); + var encoder = HPackEncoder(); + ActiveSettings peerSettings = ActiveSettings(); writeB.add(CONNECTION_PREFACE); - return new FrameWriter(encoder, writeB, peerSettings); + return FrameWriter(encoder, writeB, peerSettings); } ServerTransportConnection get serverConnection => - new ServerTransportConnection.viaStreams(readB, writeA); + ServerTransportConnection.viaStreams(readB, writeA); } diff --git a/pkgs/http2/test/src/async_utils/async_utils_test.dart b/pkgs/http2/test/src/async_utils/async_utils_test.dart index 96b7b4bfa2..82f2c75f06 100644 --- a/pkgs/http2/test/src/async_utils/async_utils_test.dart +++ b/pkgs/http2/test/src/async_utils/async_utils_test.dart @@ -10,7 +10,7 @@ import 'package:http2/src/async_utils/async_utils.dart'; main() { group('async_utils', () { test('buffer-indicator', () { - var bi = new BufferIndicator(); + var bi = BufferIndicator(); bi.bufferEmptyEvents.listen(expectAsync1((_) {}, count: 2)); expect(bi.wouldBuffer, true); @@ -35,8 +35,8 @@ main() { }); test('buffered-sink', () { - var c = new StreamController>(); - var bs = new BufferedSink(c); + var c = StreamController>(); + var bs = BufferedSink(c); expect(bs.bufferIndicator.wouldBuffer, true); var sub = c.stream.listen(expectAsync1((_) {}, count: 2)); @@ -62,8 +62,8 @@ main() { }); test('buffered-bytes-writer', () async { - var c = new StreamController>(); - var writer = new BufferedBytesWriter(c); + var c = StreamController>(); + var writer = BufferedBytesWriter(c); expect(writer.bufferIndicator.wouldBuffer, true); diff --git a/pkgs/http2/test/src/connection_preface_test.dart b/pkgs/http2/test/src/connection_preface_test.dart index 9bdc4fa2c4..b1c50bc634 100644 --- a/pkgs/http2/test/src/connection_preface_test.dart +++ b/pkgs/http2/test/src/connection_preface_test.dart @@ -14,10 +14,10 @@ main() { group('connection-preface', () { test('successful', () async { final frameBytes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; - final data = new List.from(CONNECTION_PREFACE)..addAll(frameBytes); + final data = List.from(CONNECTION_PREFACE)..addAll(frameBytes); for (int size = 1; size <= data.length; size++) { - var c = new StreamController>(); + var c = StreamController>(); var resultF = readConnectionPreface(c.stream).fold([], (b, d) => b..addAll(d)); @@ -34,7 +34,7 @@ main() { }); test('only-part-of-connection-sequence', () async { - var c = new StreamController>(); + var c = StreamController>(); var resultF = readConnectionPreface(c.stream).fold([], (b, d) => b..addAll(d)); @@ -49,7 +49,7 @@ main() { }); test('wrong-connection-sequence', () async { - var c = new StreamController>(); + var c = StreamController>(); var resultF = readConnectionPreface(c.stream).fold([], (b, d) => b..addAll(d)); @@ -64,7 +64,7 @@ main() { }); test('incoming-socket-error', () async { - var c = new StreamController>(); + var c = StreamController>(); var resultF = readConnectionPreface(c.stream).fold([], (b, d) => b..addAll(d)); diff --git a/pkgs/http2/test/src/error_matchers.dart b/pkgs/http2/test/src/error_matchers.dart index c2bb5da5ad..ee10fb85e7 100644 --- a/pkgs/http2/test/src/error_matchers.dart +++ b/pkgs/http2/test/src/error_matchers.dart @@ -7,8 +7,7 @@ library http2.test.error_matchers; import 'package:test/test.dart'; import 'package:http2/src/sync_errors.dart'; -const Matcher isProtocolException = const TypeMatcher(); -const Matcher isFrameSizeException = const TypeMatcher(); -const Matcher isTerminatedException = const TypeMatcher(); -const Matcher isFlowControlException = - const TypeMatcher(); +const Matcher isProtocolException = TypeMatcher(); +const Matcher isFrameSizeException = TypeMatcher(); +const Matcher isTerminatedException = TypeMatcher(); +const Matcher isFlowControlException = TypeMatcher(); diff --git a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart index 8b68be1beb..53c34296f8 100644 --- a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart @@ -17,19 +17,19 @@ import 'package:http2/src/flowcontrol/queue_messages.dart'; main() { group('flowcontrol', () { test('connection-message-queue-out', () { - dynamic fw = new MockFrameWriter(); - dynamic windowMock = new MockOutgoingWindowHandler(); - var queue = new ConnectionMessageQueueOut(windowMock, fw); + dynamic fw = MockFrameWriter(); + dynamic windowMock = MockOutgoingWindowHandler(); + var queue = ConnectionMessageQueueOut(windowMock, fw); fw.bufferIndicator.markUnBuffered(); expect(queue.pendingMessages, 0); - var headers = [new Header.ascii('a', 'b')]; + var headers = [Header.ascii('a', 'b')]; var bytes = [1, 2, 3]; // Send [HeadersMessage]. - queue.enqueueMessage(new HeadersMessage(99, headers, false)); + queue.enqueueMessage(HeadersMessage(99, headers, false)); expect(queue.pendingMessages, 0); verify(fw.writeHeadersFrame(99, headers, endStream: false)).called(1); verifyNoMoreInteractions(fw); @@ -40,7 +40,7 @@ main() { // Send [DataMessage]. windowMock.peerWindowSize = bytes.length; windowMock.positiveWindow.markUnBuffered(); - queue.enqueueMessage(new DataMessage(99, bytes, false)); + queue.enqueueMessage(DataMessage(99, bytes, false)); expect(queue.pendingMessages, 0); verify(windowMock.decreaseWindow(bytes.length)).called(1); verify(fw.writeDataFrame(99, bytes, endStream: false)).called(1); @@ -59,7 +59,7 @@ main() { when(windowMock.decreaseWindow(1)).thenAnswer((_) { windowMock.positiveWindow.markBuffered(); }); - queue.enqueueMessage(new DataMessage(99, bytes, true)); + queue.enqueueMessage(DataMessage(99, bytes, true)); expect(queue.pendingMessages, 1); verify(windowMock.decreaseWindow(1)).called(1); verify(fw.writeDataFrame(99, bytes.sublist(0, 1), endStream: false)) @@ -83,7 +83,7 @@ main() { queue.startClosing(); queue.done.then(expectAsync1((_) { expect(queue.pendingMessages, 0); - expect(() => queue.enqueueMessage(new DataMessage(99, bytes, true)), + expect(() => queue.enqueueMessage(DataMessage(99, bytes, true)), throwsA(const TypeMatcher())); })); }); @@ -92,17 +92,17 @@ main() { const STREAM_ID = 99; final bytes = [1, 2, 3]; - dynamic windowMock = new MockIncomingWindowHandler(); + dynamic windowMock = MockIncomingWindowHandler(); - var queue = new ConnectionMessageQueueIn(windowMock, (f) => f()); + var queue = ConnectionMessageQueueIn(windowMock, (f) => f()); expect(queue.pendingMessages, 0); - dynamic streamQueueMock = new MockStreamMessageQueueIn(); + dynamic streamQueueMock = MockStreamMessageQueueIn(); queue.insertNewStreamMessageQueue(STREAM_ID, streamQueueMock); // Insert a [DataFrame] and let it be buffered. - var header = new FrameHeader(0, 0, 0, STREAM_ID); - queue.processDataFrame(new DataFrame(header, 0, bytes)); + var header = FrameHeader(0, 0, 0, STREAM_ID); + queue.processDataFrame(DataFrame(header, 0, bytes)); expect(queue.pendingMessages, 1); verify(windowMock.gotData(bytes.length)).called(1); verifyNoMoreInteractions(windowMock); @@ -130,12 +130,12 @@ main() { const STREAM_ID = 99; final bytes = [1, 2, 3]; - dynamic windowMock = new MockIncomingWindowHandler(); - var queue = new ConnectionMessageQueueIn(windowMock, (f) => f()); + dynamic windowMock = MockIncomingWindowHandler(); + var queue = ConnectionMessageQueueIn(windowMock, (f) => f()); // Insert a [DataFrame] and let it be buffered. - var header = new FrameHeader(0, 0, 0, STREAM_ID); - queue.processIgnoredDataFrame(new DataFrame(header, 0, bytes)); + var header = FrameHeader(0, 0, 0, STREAM_ID); + queue.processIgnoredDataFrame(DataFrame(header, 0, bytes)); expect(queue.pendingMessages, 0); verify(windowMock.gotData(bytes.length)).called(1); verifyNoMoreInteractions(windowMock); @@ -145,12 +145,12 @@ main() { class MockFrameWriter extends Mock implements FrameWriter { @override - BufferIndicator bufferIndicator = new BufferIndicator(); + BufferIndicator bufferIndicator = BufferIndicator(); } class MockStreamMessageQueueIn extends Mock implements StreamMessageQueueIn { @override - BufferIndicator bufferIndicator = new BufferIndicator(); + BufferIndicator bufferIndicator = BufferIndicator(); } class MockIncomingWindowHandler extends Mock implements IncomingWindowHandler {} @@ -158,7 +158,7 @@ class MockIncomingWindowHandler extends Mock implements IncomingWindowHandler {} class MockOutgoingWindowHandler extends Mock implements OutgoingConnectionWindowHandler, OutgoingStreamWindowHandler { @override - BufferIndicator positiveWindow = new BufferIndicator(); + BufferIndicator positiveWindow = BufferIndicator(); @override - int peerWindowSize = new Window().size; + int peerWindowSize = Window().size; } diff --git a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart index 28381cfafd..1ff3c82562 100644 --- a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart @@ -15,22 +15,22 @@ import 'package:http2/src/flowcontrol/connection_queues.dart'; main() { group('flowcontrol', () { const STREAM_ID = 99; - const BYTES = const [1, 2, 3]; + const BYTES = [1, 2, 3]; group('stream-message-queue-out', () { test('window-big-enough', () { - dynamic connectionQueueMock = new MockConnectionMessageQueueOut(); - dynamic windowMock = new MockOutgoingStreamWindowHandler(); + dynamic connectionQueueMock = MockConnectionMessageQueueOut(); + dynamic windowMock = MockOutgoingStreamWindowHandler(); windowMock.positiveWindow.markUnBuffered(); - var queue = new StreamMessageQueueOut( - STREAM_ID, windowMock, connectionQueueMock); + var queue = + StreamMessageQueueOut(STREAM_ID, windowMock, connectionQueueMock); expect(queue.bufferIndicator.wouldBuffer, isFalse); expect(queue.pendingMessages, 0); windowMock.peerWindowSize = BYTES.length; - queue.enqueueMessage(new DataMessage(STREAM_ID, BYTES, true)); + queue.enqueueMessage(DataMessage(STREAM_ID, BYTES, true)); verify(windowMock.decreaseWindow(BYTES.length)).called(1); final capturedMessage = verify(connectionQueueMock.enqueueMessage(captureAny)) @@ -43,12 +43,12 @@ main() { }); test('window-smaller-than-necessary', () { - dynamic connectionQueueMock = new MockConnectionMessageQueueOut(); - dynamic windowMock = new MockOutgoingStreamWindowHandler(); + dynamic connectionQueueMock = MockConnectionMessageQueueOut(); + dynamic windowMock = MockOutgoingStreamWindowHandler(); windowMock.positiveWindow.markUnBuffered(); - var queue = new StreamMessageQueueOut( - STREAM_ID, windowMock, connectionQueueMock); + var queue = + StreamMessageQueueOut(STREAM_ID, windowMock, connectionQueueMock); expect(queue.bufferIndicator.wouldBuffer, isFalse); expect(queue.pendingMessages, 0); @@ -56,7 +56,7 @@ main() { // We set the window size fixed to 1, which means all the data messages // will get fragmented to 1 byte. windowMock.peerWindowSize = 1; - queue.enqueueMessage(new DataMessage(STREAM_ID, BYTES, true)); + queue.enqueueMessage(DataMessage(STREAM_ID, BYTES, true)); expect(queue.pendingMessages, 0); verify(windowMock.decreaseWindow(1)).called(BYTES.length); @@ -73,18 +73,18 @@ main() { }); test('window-empty', () { - var connectionQueueMock = new MockConnectionMessageQueueOut(); - var windowMock = new MockOutgoingStreamWindowHandler(); + var connectionQueueMock = MockConnectionMessageQueueOut(); + var windowMock = MockOutgoingStreamWindowHandler(); windowMock.positiveWindow.markUnBuffered(); - var queue = new StreamMessageQueueOut( - STREAM_ID, windowMock, connectionQueueMock); + var queue = + StreamMessageQueueOut(STREAM_ID, windowMock, connectionQueueMock); expect(queue.bufferIndicator.wouldBuffer, isFalse); expect(queue.pendingMessages, 0); windowMock.peerWindowSize = 0; - queue.enqueueMessage(new DataMessage(STREAM_ID, BYTES, true)); + queue.enqueueMessage(DataMessage(STREAM_ID, BYTES, true)); expect(queue.bufferIndicator.wouldBuffer, isTrue); expect(queue.pendingMessages, 1); verifyZeroInteractions(windowMock); @@ -94,8 +94,8 @@ main() { group('stream-message-queue-in', () { test('data-end-of-stream', () { - dynamic windowMock = new MockIncomingWindowHandler(); - dynamic queue = new StreamMessageQueueIn(windowMock); + dynamic windowMock = MockIncomingWindowHandler(); + dynamic queue = StreamMessageQueueIn(windowMock); expect(queue.pendingMessages, 0); queue.messages.listen(expectAsync1((StreamMessage message) { @@ -104,7 +104,7 @@ main() { DataStreamMessage dataMessage = message; expect(dataMessage.bytes, BYTES); }), onDone: expectAsync0(() {})); - queue.enqueueMessage(new DataMessage(STREAM_ID, BYTES, true)); + queue.enqueueMessage(DataMessage(STREAM_ID, BYTES, true)); expect(queue.bufferIndicator.wouldBuffer, isFalse); verifyInOrder([ windowMock.gotData(BYTES.length), @@ -118,15 +118,15 @@ main() { const STREAM_ID = 99; final bytes = [1, 2, 3]; - dynamic windowMock = new MockIncomingWindowHandler(); - dynamic queue = new StreamMessageQueueIn(windowMock); + dynamic windowMock = MockIncomingWindowHandler(); + dynamic queue = StreamMessageQueueIn(windowMock); var sub = queue.messages.listen(expectAsync1((_) {}, count: 0), onDone: expectAsync0(() {}, count: 0)); sub.pause(); expect(queue.pendingMessages, 0); - queue.enqueueMessage(new DataMessage(STREAM_ID, bytes, true)); + queue.enqueueMessage(DataMessage(STREAM_ID, bytes, true)); expect(queue.pendingMessages, 1); expect(queue.bufferIndicator.wouldBuffer, isTrue); // We assert that we got the data, but it wasn't processed. @@ -146,7 +146,7 @@ class MockIncomingWindowHandler extends Mock implements IncomingWindowHandler {} class MockOutgoingStreamWindowHandler extends Mock implements OutgoingStreamWindowHandler { @override - final BufferIndicator positiveWindow = new BufferIndicator(); + final BufferIndicator positiveWindow = BufferIndicator(); @override int peerWindowSize; } diff --git a/pkgs/http2/test/src/flowcontrol/window_handler_test.dart b/pkgs/http2/test/src/flowcontrol/window_handler_test.dart index 53e66a14e0..ebc8551f22 100644 --- a/pkgs/http2/test/src/flowcontrol/window_handler_test.dart +++ b/pkgs/http2/test/src/flowcontrol/window_handler_test.dart @@ -29,8 +29,8 @@ main() { // If we received a window update frame, the window should be increased // again. - var frameHeader = new FrameHeader(4, FrameType.WINDOW_UPDATE, 0, 0); - handler.processWindowUpdate(new WindowUpdateFrame(frameHeader, 100)); + var frameHeader = FrameHeader(4, FrameType.WINDOW_UPDATE, 0, 0); + handler.processWindowUpdate(WindowUpdateFrame(frameHeader, 100)); expect(handler.peerWindowSize, initialSize); expect(window.size, initialSize); @@ -47,28 +47,28 @@ main() { })); // Now we trigger the 1 byte window increase - handler.processWindowUpdate(new WindowUpdateFrame(frameHeader, 1)); + 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 = new WindowUpdateFrame(frameHeader, Window.MAX_WINDOW_SIZE); + var frame = WindowUpdateFrame(frameHeader, Window.MAX_WINDOW_SIZE); expect(() => handler.processWindowUpdate(frame), throwsA(isFlowControlException)); } test('outgoing-connection-window-handler', () { - var window = new Window(); + var window = Window(); int initialSize = window.size; - var handler = new OutgoingConnectionWindowHandler(window); + var handler = OutgoingConnectionWindowHandler(window); testAbstractOutgoingWindowHandler(handler, window, initialSize); }); test('outgoing-stream-window-handler', () { - var window = new Window(); + var window = Window(); int initialSize = window.size; - var handler = new OutgoingStreamWindowHandler(window); + var handler = OutgoingStreamWindowHandler(window); testAbstractOutgoingWindowHandler(handler, window, initialSize); @@ -76,9 +76,9 @@ main() { // gets increased/decreased via a [SettingsFrame], all stream // windows need to get updated as well. - window = new Window(); + window = Window(); initialSize = window.size; - handler = new OutgoingStreamWindowHandler(window); + handler = OutgoingStreamWindowHandler(window); expect(handler.positiveWindow.wouldBuffer, isFalse); final bufferEmpty = handler.positiveWindow.bufferEmptyEvents @@ -102,10 +102,10 @@ main() { test('incoming-window-handler', () { const STREAM_ID = 99; - dynamic fw = new FrameWriterMock(); - var window = new Window(); + dynamic fw = FrameWriterMock(); + var window = Window(); int initialSize = window.size; - var handler = new IncomingWindowHandler.stream(fw, window, STREAM_ID); + var handler = IncomingWindowHandler.stream(fw, window, STREAM_ID); expect(handler.localWindowSize, initialSize); expect(window.size, initialSize); diff --git a/pkgs/http2/test/src/frames/frame_defragmenter_test.dart b/pkgs/http2/test/src/frames/frame_defragmenter_test.dart index a51349b179..23a7761018 100644 --- a/pkgs/http2/test/src/frames/frame_defragmenter_test.dart +++ b/pkgs/http2/test/src/frames/frame_defragmenter_test.dart @@ -13,40 +13,40 @@ main() { group('frames', () { group('frame-defragmenter', () { UnknownFrame unknownFrame() { - return new UnknownFrame(new FrameHeader(0, 0, 0, 1), []); + return UnknownFrame(FrameHeader(0, 0, 0, 1), []); } HeadersFrame headersFrame(List data, - {bool fragmented: false, int streamId: 1}) { + {bool fragmented = false, int streamId = 1}) { int flags = fragmented ? 0 : HeadersFrame.FLAG_END_HEADERS; var header = - new FrameHeader(data.length, FrameType.HEADERS, flags, streamId); - return new HeadersFrame(header, 0, false, null, null, data); + FrameHeader(data.length, FrameType.HEADERS, flags, streamId); + return HeadersFrame(header, 0, false, null, null, data); } PushPromiseFrame pushPromiseFrame(List data, - {bool fragmented: false, int streamId: 1}) { + {bool fragmented = false, int streamId = 1}) { int flags = fragmented ? 0 : HeadersFrame.FLAG_END_HEADERS; - var header = new FrameHeader( - data.length, FrameType.PUSH_PROMISE, flags, streamId); - return new PushPromiseFrame(header, 0, 44, data); + var header = + FrameHeader(data.length, FrameType.PUSH_PROMISE, flags, streamId); + return PushPromiseFrame(header, 0, 44, data); } ContinuationFrame continuationFrame(List data, - {bool fragmented: false, int streamId: 1}) { + {bool fragmented = false, int streamId = 1}) { int flags = fragmented ? 0 : ContinuationFrame.FLAG_END_HEADERS; - var header = new FrameHeader( - data.length, FrameType.CONTINUATION, flags, streamId); - return new ContinuationFrame(header, data); + var header = + FrameHeader(data.length, FrameType.CONTINUATION, flags, streamId); + return ContinuationFrame(header, data); } test('unknown-frame', () { - var defrag = new FrameDefragmenter(); + var defrag = FrameDefragmenter(); expect(defrag.tryDefragmentFrame(unknownFrame()) is UnknownFrame, true); }); test('fragmented-headers-frame', () { - var defrag = new FrameDefragmenter(); + var defrag = FrameDefragmenter(); var f1 = headersFrame([1, 2, 3], fragmented: true); var f2 = continuationFrame([4, 5, 6], fragmented: true); @@ -63,7 +63,7 @@ main() { }); test('fragmented-push-promise-frame', () { - var defrag = new FrameDefragmenter(); + var defrag = FrameDefragmenter(); var f1 = pushPromiseFrame([1, 2, 3], fragmented: true); var f2 = continuationFrame([4, 5, 6], fragmented: true); @@ -79,7 +79,7 @@ main() { }); test('fragmented-headers-frame--wrong-id', () { - var defrag = new FrameDefragmenter(); + var defrag = FrameDefragmenter(); var f1 = headersFrame([1, 2, 3], fragmented: true, streamId: 1); var f2 = continuationFrame([4, 5, 6], fragmented: true, streamId: 2); @@ -90,7 +90,7 @@ main() { }); test('fragmented-push-promise-frame', () { - var defrag = new FrameDefragmenter(); + var defrag = FrameDefragmenter(); var f1 = pushPromiseFrame([1, 2, 3], fragmented: true, streamId: 1); var f2 = continuationFrame([4, 5, 6], fragmented: true, streamId: 2); @@ -101,7 +101,7 @@ main() { }); test('fragmented-headers-frame--no-continuation-frame', () { - var defrag = new FrameDefragmenter(); + var defrag = FrameDefragmenter(); var f1 = headersFrame([1, 2, 3], fragmented: true); var f2 = unknownFrame(); @@ -112,7 +112,7 @@ main() { }); test('fragmented-push-promise-no-continuation-frame', () { - var defrag = new FrameDefragmenter(); + var defrag = FrameDefragmenter(); var f1 = pushPromiseFrame([1, 2, 3], fragmented: true); var f2 = unknownFrame(); @@ -123,7 +123,7 @@ main() { }); test('push-without-headres-or-push-promise-frame', () { - var defrag = new FrameDefragmenter(); + var defrag = FrameDefragmenter(); var f1 = continuationFrame([4, 5, 6], fragmented: true, streamId: 1); expect(defrag.tryDefragmentFrame(f1), equals(f1)); diff --git a/pkgs/http2/test/src/frames/frame_reader_test.dart b/pkgs/http2/test/src/frames/frame_reader_test.dart index 7bbd4027d7..90eeb957e6 100644 --- a/pkgs/http2/test/src/frames/frame_reader_test.dart +++ b/pkgs/http2/test/src/frames/frame_reader_test.dart @@ -12,12 +12,12 @@ import 'package:http2/src/settings/settings.dart'; main() { group('frames', () { group('frame-reader', () { - final int maxFrameSize = new ActiveSettings().maxFrameSize; + final int maxFrameSize = ActiveSettings().maxFrameSize; Stream dataFrame(List body) { - var settings = new ActiveSettings(); - var controller = new StreamController>(); - var reader = new FrameReader(controller.stream, settings); + var settings = ActiveSettings(); + var controller = StreamController>(); + var reader = FrameReader(controller.stream, settings); // This is a DataFrame: // - length: n @@ -35,7 +35,7 @@ main() { } test('data-frame--max-frame-size', () { - var body = new List.filled(maxFrameSize, 0x42); + var body = List.filled(maxFrameSize, 0x42); dataFrame(body).listen(expectAsync1((Frame frame) { expect(frame is DataFrame, isTrue); expect(frame.header.length, body.length); @@ -48,7 +48,7 @@ main() { }); test('data-frame--max-frame-size-plus-1', () { - var body = new List.filled(maxFrameSize + 1, 0x42); + var body = List.filled(maxFrameSize + 1, 0x42); dataFrame(body).listen(expectAsync1((_) {}, count: 0), onError: expectAsync2((error, stack) { expect('$error', contains('Incoming frame is too big')); @@ -56,10 +56,10 @@ main() { }); test('incomplete-header', () { - var settings = new ActiveSettings(); + var settings = ActiveSettings(); - var controller = new StreamController>(); - var reader = new FrameReader(controller.stream, settings); + var controller = StreamController>(); + var reader = FrameReader(controller.stream, settings); controller ..add([1]) @@ -72,10 +72,10 @@ main() { }); test('incomplete-frame', () { - var settings = new ActiveSettings(); + var settings = ActiveSettings(); - var controller = new StreamController>(); - var reader = new FrameReader(controller.stream, settings); + var controller = StreamController>(); + var reader = FrameReader(controller.stream, settings); // This is a DataFrame: // - length: [0, 0, 255] @@ -93,10 +93,10 @@ main() { }); test('connection-error', () { - var settings = new ActiveSettings(); + var settings = ActiveSettings(); - var controller = new StreamController>(); - var reader = new FrameReader(controller.stream, settings); + var controller = StreamController>(); + var reader = FrameReader(controller.stream, settings); controller ..addError('hello world') diff --git a/pkgs/http2/test/src/frames/frame_writer_reader_test.dart b/pkgs/http2/test/src/frames/frame_writer_reader_test.dart index 6fef6f7ab0..4ea96c964e 100644 --- a/pkgs/http2/test/src/frames/frame_writer_reader_test.dart +++ b/pkgs/http2/test/src/frames/frame_writer_reader_test.dart @@ -33,8 +33,7 @@ main() { writerReaderTest('headers-frame', (FrameWriter writer, FrameReader reader, HPackDecoder decoder) async { - writer.writeHeadersFrame(99, [new Header.ascii('a', 'b')], - endStream: true); + writer.writeHeadersFrame(99, [Header.ascii('a', 'b')], endStream: true); var frames = await finishWriting(writer, reader); expect(frames.length, 1); @@ -86,8 +85,7 @@ main() { writerReaderTest('settings-frame', (FrameWriter writer, FrameReader reader, HPackDecoder decoder) async { - writer - .writeSettingsFrame([new Setting(Setting.SETTINGS_ENABLE_PUSH, 1)]); + writer.writeSettingsFrame([Setting(Setting.SETTINGS_ENABLE_PUSH, 1)]); var frames = await finishWriting(writer, reader); expect(frames.length, 1); @@ -118,7 +116,7 @@ main() { writerReaderTest('push-promise-frame', (FrameWriter writer, FrameReader reader, HPackDecoder decoder) async { - writer.writePushPromiseFrame(99, 44, [new Header.ascii('a', 'b')]); + writer.writePushPromiseFrame(99, 44, [Header.ascii('a', 'b')]); var frames = await finishWriting(writer, reader); expect(frames.length, 1); @@ -181,8 +179,8 @@ main() { writerReaderTest('frag-headers-frame', (FrameWriter writer, FrameReader reader, HPackDecoder decoder) async { var headerName = [1]; - var headerValue = new List.filled(1 << 14, 0x42); - var header = new Header(headerName, headerValue); + var headerValue = List.filled(1 << 14, 0x42); + var header = Header(headerName, headerValue); writer.writeHeadersFrame(99, [header], endStream: true); @@ -219,11 +217,11 @@ main() { writerReaderTest(String name, func(FrameWriter writer, FrameReader reader, HPackDecoder decoder)) { test(name, () { - var settings = new ActiveSettings(); - var context = new HPackContext(); - var controller = new StreamController>(); - var writer = new FrameWriter(context.encoder, controller, settings); - var reader = new FrameReader(controller.stream, settings); + var settings = ActiveSettings(); + var context = HPackContext(); + var controller = StreamController>(); + var writer = FrameWriter(context.encoder, controller, settings); + var reader = FrameReader(controller.stream, settings); return func(writer, reader, context.decoder); }); } diff --git a/pkgs/http2/test/src/frames/frame_writer_test.dart b/pkgs/http2/test/src/frames/frame_writer_test.dart index e7ff6ae91a..78f337b349 100644 --- a/pkgs/http2/test/src/frames/frame_writer_test.dart +++ b/pkgs/http2/test/src/frames/frame_writer_test.dart @@ -14,10 +14,10 @@ main() { group('frames', () { group('frame-writer', () { test('connection-error', () { - var settings = new ActiveSettings(); - var context = new HPackContext(); - var controller = new StreamController>(); - var writer = new FrameWriter(context.encoder, controller, settings); + var settings = ActiveSettings(); + var context = HPackContext(); + var controller = StreamController>(); + var writer = FrameWriter(context.encoder, controller, settings); writer.doneFuture.then(expectAsync1((_) { // We expect that the writer is done at this point. diff --git a/pkgs/http2/test/src/hpack/hpack_test.dart b/pkgs/http2/test/src/hpack/hpack_test.dart index ea37d8bb23..58e3530c71 100644 --- a/pkgs/http2/test/src/hpack/hpack_test.dart +++ b/pkgs/http2/test/src/hpack/hpack_test.dart @@ -9,7 +9,7 @@ main() { group('hpack', () { group('hpack-spec-decoder', () { test('C.3 request without huffman encoding', () { - var context = new HPackContext(); + var context = HPackContext(); List
headers; // First request @@ -106,7 +106,7 @@ main() { }); test('C.4 request with huffman encoding', () { - var context = new HPackContext(); + var context = HPackContext(); List
headers; // First request @@ -193,7 +193,7 @@ main() { }); test('C.5 response without huffman encoding', () { - var context = new HPackContext(); + var context = HPackContext(); List
headers; // First response @@ -398,7 +398,7 @@ main() { }); test('C.6 response with huffman encoding', () { - var context = new HPackContext(); + var context = HPackContext(); List
headers; // First response @@ -570,25 +570,25 @@ main() { group('negative-decoder-tests', () { test('invalid-integer-encoding', () { - var context = new HPackContext(); + var context = HPackContext(); expect(() => context.decoder.decode([1 << 6, 0xff]), throwsA(isHPackDecodingException)); }); test('index-out-of-table-size', () { - var context = new HPackContext(); + var context = HPackContext(); expect(() => context.decoder.decode([0x7f]), throwsA(isHPackDecodingException)); }); test('invalid-update-dynamic-table-size', () { - var context = new HPackContext(); + var context = HPackContext(); expect(() => context.decoder.decode([0x3f]), throwsA(isHPackDecodingException)); }); test('update-dynamic-table-size-too-high', () { - var context = new HPackContext(); + var context = HPackContext(); // Tries to set dynamic table to 4097 (max is 4096 by default) var bytes = TestHelper.newInteger(0x20, 5, 4097); expect(() => context.decoder.decode(bytes), @@ -607,7 +607,7 @@ main() { const charD = 0x64; test('update-dynamic-table-size-too-high', () { - var context = new HPackContext(); + var context = HPackContext(); // Sets dynamic table to 4096 expect( context.decoder.decode(TestHelper.newInteger(0x20, 5, 4096)), []); @@ -615,7 +615,7 @@ main() { test('dynamic table entry', () { List
headers; - var context = new HPackContext(); + var context = HPackContext(); var buffer = []; buffer.addAll(TestHelper.insertIntoDynamicTable(2048, char0, charA)); @@ -669,16 +669,16 @@ main() { group('encoder-tests', () { test('simple-encoding', () { - var context = new HPackContext(); - var headers = [new Header.ascii('key', 'value')]; + var context = HPackContext(); + var headers = [Header.ascii('key', 'value')]; expect(context.encoder.encode(headers), [0x00, 0x03, 0x6b, 0x65, 0x79, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65]); }); test('simple-encoding-long-value', () { - var context = new HPackContext(); + var context = HPackContext(); var headers = [ - new Header([0x42], new List.filled(300, 0x84)) + Header([0x42], List.filled(300, 0x84)) ]; expect(context.decoder.decode(context.encoder.encode(headers)).first, @@ -776,7 +776,7 @@ class TestHelper { buffer.addAll(newInteger(0, 7, name.length)); buffer.addAll(name); - var value = new List.filled(n - 32 - name.length, valueChar); + var value = List.filled(n - 32 - name.length, valueChar); buffer.addAll(newInteger(0, 7, value.length)); buffer.addAll(value); @@ -798,8 +798,7 @@ class TestHelper { } /// A matcher for HuffmannDecodingExceptions. -const Matcher isHPackDecodingException = - const TypeMatcher(); +const Matcher isHPackDecodingException = TypeMatcher(); class _HeaderMatcher extends Matcher { final Header header; @@ -828,6 +827,6 @@ class _HeaderMatcher extends Matcher { } Matcher isHeader(String name, String value) => - new _HeaderMatcher(new Header.ascii(name, value)); + _HeaderMatcher(Header.ascii(name, value)); -Matcher equalsHeader(Header header) => new _HeaderMatcher(header); +Matcher equalsHeader(Header header) => _HeaderMatcher(header); diff --git a/pkgs/http2/test/src/hpack/huffman_table_test.dart b/pkgs/http2/test/src/hpack/huffman_table_test.dart index 49898b33b9..cf0e0025b4 100644 --- a/pkgs/http2/test/src/hpack/huffman_table_test.dart +++ b/pkgs/http2/test/src/hpack/huffman_table_test.dart @@ -155,4 +155,4 @@ main() { /// A matcher for HuffmanDecodingExceptions. const Matcher isHuffmanDecodingException = - const TypeMatcher(); + TypeMatcher(); diff --git a/pkgs/http2/test/src/ping/ping_handler_test.dart b/pkgs/http2/test/src/ping/ping_handler_test.dart index 85b0306f7f..8f770507ac 100644 --- a/pkgs/http2/test/src/ping/ping_handler_test.dart +++ b/pkgs/http2/test/src/ping/ping_handler_test.dart @@ -15,8 +15,8 @@ import '../error_matchers.dart'; main() { group('ping-handler', () { test('successful-ping', () async { - dynamic writer = new FrameWriterMock(); - var pingHandler = new PingHandler(writer); + dynamic writer = FrameWriterMock(); + var pingHandler = PingHandler(writer); Future p1 = pingHandler.ping(); Future p2 = pingHandler.ping(); @@ -26,10 +26,10 @@ main() { writer.writePingFrame(2), ]); - var header = new FrameHeader(8, FrameType.PING, PingFrame.FLAG_ACK, 0); - pingHandler.processPingFrame(new PingFrame(header, 1)); - var header2 = new FrameHeader(8, FrameType.PING, PingFrame.FLAG_ACK, 0); - pingHandler.processPingFrame(new PingFrame(header2, 2)); + var header = FrameHeader(8, FrameType.PING, PingFrame.FLAG_ACK, 0); + pingHandler.processPingFrame(PingFrame(header, 1)); + var header2 = FrameHeader(8, FrameType.PING, PingFrame.FLAG_ACK, 0); + pingHandler.processPingFrame(PingFrame(header2, 2)); await p1; await p2; @@ -37,13 +37,13 @@ main() { }); test('successful-ack-to-remote-ping', () async { - dynamic writer = new FrameWriterMock(); - var pingHandler = new PingHandler(writer); + dynamic writer = FrameWriterMock(); + var pingHandler = PingHandler(writer); - var header = new FrameHeader(8, FrameType.PING, 0, 0); - pingHandler.processPingFrame(new PingFrame(header, 1)); - var header2 = new FrameHeader(8, FrameType.PING, 0, 0); - pingHandler.processPingFrame(new PingFrame(header2, 2)); + var header = FrameHeader(8, FrameType.PING, 0, 0); + pingHandler.processPingFrame(PingFrame(header, 1)); + var header2 = FrameHeader(8, FrameType.PING, 0, 0); + pingHandler.processPingFrame(PingFrame(header2, 2)); verifyInOrder([ writer.writePingFrame(1, ack: true), @@ -53,14 +53,14 @@ main() { }); test('ping-unknown-opaque-data', () async { - dynamic writer = new FrameWriterMock(); - var pingHandler = new PingHandler(writer); + dynamic writer = FrameWriterMock(); + var pingHandler = PingHandler(writer); Future future = pingHandler.ping(); verify(writer.writePingFrame(1)).called(1); - var header = new FrameHeader(8, FrameType.PING, PingFrame.FLAG_ACK, 0); - expect(() => pingHandler.processPingFrame(new PingFrame(header, 2)), + var header = FrameHeader(8, FrameType.PING, PingFrame.FLAG_ACK, 0); + expect(() => pingHandler.processPingFrame(PingFrame(header, 2)), throwsA(isProtocolException)); // Ensure outstanding pings will be completed with an error once we call @@ -73,8 +73,8 @@ main() { }); test('terminate-ping-handler', () async { - var writer = new FrameWriterMock(); - var pingHandler = new PingHandler(writer); + var writer = FrameWriterMock(); + var pingHandler = PingHandler(writer); pingHandler.terminate('hello world'); expect(() => pingHandler.processPingFrame(null), @@ -84,11 +84,11 @@ main() { }); test('ping-non-zero-stream-id', () async { - var writer = new FrameWriterMock(); - var pingHandler = new PingHandler(writer); + var writer = FrameWriterMock(); + var pingHandler = PingHandler(writer); - var header = new FrameHeader(8, FrameType.PING, PingFrame.FLAG_ACK, 1); - expect(() => pingHandler.processPingFrame(new PingFrame(header, 1)), + var header = FrameHeader(8, FrameType.PING, PingFrame.FLAG_ACK, 1); + expect(() => pingHandler.processPingFrame(PingFrame(header, 1)), throwsA(isProtocolException)); verifyZeroInteractions(writer); }); diff --git a/pkgs/http2/test/src/settings/settings_handler_test.dart b/pkgs/http2/test/src/settings/settings_handler_test.dart index 774503a1ee..16e5a4f56b 100644 --- a/pkgs/http2/test/src/settings/settings_handler_test.dart +++ b/pkgs/http2/test/src/settings/settings_handler_test.dart @@ -15,14 +15,14 @@ import '../error_matchers.dart'; main() { group('settings-handler', () { - var pushSettings = [new Setting(Setting.SETTINGS_ENABLE_PUSH, 0)]; - var invalidPushSettings = [new Setting(Setting.SETTINGS_ENABLE_PUSH, 2)]; - var setMaxTable256 = [new Setting(Setting.SETTINGS_HEADER_TABLE_SIZE, 256)]; + var pushSettings = [Setting(Setting.SETTINGS_ENABLE_PUSH, 0)]; + var invalidPushSettings = [Setting(Setting.SETTINGS_ENABLE_PUSH, 2)]; + var setMaxTable256 = [Setting(Setting.SETTINGS_HEADER_TABLE_SIZE, 256)]; test('successful-setting', () async { - dynamic writer = new FrameWriterMock(); - var sh = new SettingsHandler(new HPackEncoder(), writer, - new ActiveSettings(), new ActiveSettings()); + dynamic writer = FrameWriterMock(); + var sh = SettingsHandler( + HPackEncoder(), writer, ActiveSettings(), ActiveSettings()); // Start changing settings. Future changed = sh.changeSettings(pushSettings); @@ -34,8 +34,8 @@ main() { // Simulate remote end to respond with an ACK. var header = - new FrameHeader(0, FrameType.SETTINGS, SettingsFrame.FLAG_ACK, 0); - sh.handleSettingsFrame(new SettingsFrame(header, [])); + FrameHeader(0, FrameType.SETTINGS, SettingsFrame.FLAG_ACK, 0); + sh.handleSettingsFrame(SettingsFrame(header, [])); await changed; @@ -44,16 +44,16 @@ main() { }); test('ack-remote-settings-change', () { - dynamic writer = new FrameWriterMock(); - var sh = new SettingsHandler(new HPackEncoder(), writer, - new ActiveSettings(), new ActiveSettings()); + dynamic writer = FrameWriterMock(); + var sh = SettingsHandler( + HPackEncoder(), writer, ActiveSettings(), ActiveSettings()); // Check that settings haven't been applied. expect(sh.peerSettings.enablePush, true); // Simulate remote end by setting the push setting. - var header = new FrameHeader(6, FrameType.SETTINGS, 0, 0); - sh.handleSettingsFrame(new SettingsFrame(header, pushSettings)); + var header = FrameHeader(6, FrameType.SETTINGS, 0, 0); + sh.handleSettingsFrame(SettingsFrame(header, pushSettings)); // Check that settings have been applied. expect(sh.peerSettings.enablePush, false); @@ -62,14 +62,14 @@ main() { }); test('invalid-remote-ack', () { - var writer = new FrameWriterMock(); - var sh = new SettingsHandler(new HPackEncoder(), writer, - new ActiveSettings(), new ActiveSettings()); + var writer = FrameWriterMock(); + var sh = SettingsHandler( + HPackEncoder(), writer, ActiveSettings(), ActiveSettings()); // Simulates ACK even though we haven't sent any settings. var header = - new FrameHeader(0, FrameType.SETTINGS, SettingsFrame.FLAG_ACK, 0); - var settingsFrame = new SettingsFrame(header, const []); + FrameHeader(0, FrameType.SETTINGS, SettingsFrame.FLAG_ACK, 0); + var settingsFrame = SettingsFrame(header, const []); expect(() => sh.handleSettingsFrame(settingsFrame), throwsA(isProtocolException)); @@ -77,30 +77,30 @@ main() { }); test('invalid-remote-settings-change', () { - var writer = new FrameWriterMock(); - var sh = new SettingsHandler(new HPackEncoder(), writer, - new ActiveSettings(), new ActiveSettings()); + var writer = FrameWriterMock(); + var sh = SettingsHandler( + HPackEncoder(), writer, ActiveSettings(), ActiveSettings()); // Check that settings haven't been applied. expect(sh.peerSettings.enablePush, true); // Simulate remote end by setting the push setting. - var header = new FrameHeader(6, FrameType.SETTINGS, 0, 0); - var settingsFrame = new SettingsFrame(header, invalidPushSettings); + var header = FrameHeader(6, FrameType.SETTINGS, 0, 0); + var settingsFrame = SettingsFrame(header, invalidPushSettings); expect(() => sh.handleSettingsFrame(settingsFrame), throwsA(isProtocolException)); verifyZeroInteractions(writer); }); test('change-max-header-table-size', () { - dynamic writer = new FrameWriterMock(); - dynamic mock = new HPackEncoderMock(); - var sh = new SettingsHandler( - mock, writer, new ActiveSettings(), new ActiveSettings()); + dynamic writer = FrameWriterMock(); + dynamic mock = HPackEncoderMock(); + var sh = + SettingsHandler(mock, writer, ActiveSettings(), ActiveSettings()); // Simulate remote end by setting the push setting. - var header = new FrameHeader(6, FrameType.SETTINGS, 0, 0); - var settingsFrame = new SettingsFrame(header, setMaxTable256); + var header = FrameHeader(6, FrameType.SETTINGS, 0, 0); + var settingsFrame = SettingsFrame(header, setMaxTable256); sh.handleSettingsFrame(settingsFrame); verify(mock.updateMaxSendingHeaderTableSize(256)).called(1); verify(writer.writeSettingsAckFrame()).called(1); diff --git a/pkgs/http2/test/src/streams/helper.dart b/pkgs/http2/test/src/streams/helper.dart index 8d7d331a17..42144f188f 100644 --- a/pkgs/http2/test/src/streams/helper.dart +++ b/pkgs/http2/test/src/streams/helper.dart @@ -28,7 +28,7 @@ void streamTest(String name, func(ClientTransportConnection client, ServerTransportConnection server), {ClientSettings settings}) { return test(name, () { - var bidirect = new BidirectionalConnection(); + var bidirect = BidirectionalConnection(); bidirect.settings = settings; var client = bidirect.clientConnection; var server = bidirect.serverConnection; @@ -38,9 +38,9 @@ void streamTest(String name, void framesTest(String name, func(frameWriter, frameStream)) { return test(name, () { - var c = new StreamController>(); - var fw = new FrameWriter(null, c, new ActiveSettings()); - var frameStream = new FrameReader(c.stream, new ActiveSettings()); + var c = StreamController>(); + var fw = FrameWriter(null, c, ActiveSettings()); + var frameStream = FrameReader(c.stream, ActiveSettings()); return func(fw, frameStream); }); @@ -48,15 +48,14 @@ void framesTest(String name, func(frameWriter, frameStream)) { class BidirectionalConnection { ClientSettings settings; - final StreamController> writeA = new StreamController(); - final StreamController> writeB = new StreamController(); + final StreamController> writeA = StreamController(); + final StreamController> writeB = StreamController(); Stream> get readA => writeA.stream; Stream> get readB => writeB.stream; ClientTransportConnection get clientConnection => - new ClientTransportConnection.viaStreams(readA, writeB, - settings: settings); + ClientTransportConnection.viaStreams(readA, writeB, settings: settings); ServerTransportConnection get serverConnection => - new ServerTransportConnection.viaStreams(readB, writeA); + ServerTransportConnection.viaStreams(readB, writeA); } diff --git a/pkgs/http2/test/src/streams/simple_flow_test.dart b/pkgs/http2/test/src/streams/simple_flow_test.dart index acf83e731b..c19c3e4a49 100644 --- a/pkgs/http2/test/src/streams/simple_flow_test.dart +++ b/pkgs/http2/test/src/streams/simple_flow_test.dart @@ -16,9 +16,9 @@ main() { group('flowcontrol', () { const int numOfOneKB = 1000; - var expectedHeaders = [new Header.ascii('key', 'value')]; - var allBytes = new List.generate(numOfOneKB * 1024, (i) => i % 256); - allBytes.addAll(new List.generate(42, (i) => 42)); + var expectedHeaders = [Header.ascii('key', 'value')]; + var allBytes = List.generate(numOfOneKB * 1024, (i) => i % 256); + allBytes.addAll(List.generate(42, (i) => 42)); headersTestFun(String type) { return expectAsync1((StreamMessage msg) { @@ -30,7 +30,7 @@ main() { }); } - Completer serverReceivedAllBytes = new Completer(); + Completer serverReceivedAllBytes = Completer(); messageTestFun(String type) { bool expectHeader = true; @@ -54,7 +54,7 @@ main() { if (numBytesReceived > allBytes.length) { if (serverReceivedAllBytes.isCompleted) { - throw new Exception('Got more messages than expected'); + throw Exception('Got more messages than expected'); } serverReceivedAllBytes.complete(); } diff --git a/pkgs/http2/test/src/streams/simple_push_test.dart b/pkgs/http2/test/src/streams/simple_push_test.dart index 39e1f21e43..1633553441 100644 --- a/pkgs/http2/test/src/streams/simple_push_test.dart +++ b/pkgs/http2/test/src/streams/simple_push_test.dart @@ -17,9 +17,9 @@ main() { group('server-push', () { const int numOfOneKB = 1000; - var expectedHeaders = [new Header.ascii('key', 'value')]; - var allBytes = new List.generate(numOfOneKB * 1024, (i) => i % 256); - allBytes.addAll(new List.generate(42, (i) => 42)); + var expectedHeaders = [Header.ascii('key', 'value')]; + var allBytes = List.generate(numOfOneKB * 1024, (i) => i % 256); + allBytes.addAll(List.generate(42, (i) => 42)); testHeaders(List
headers) { expect(headers.length, expectedHeaders.length); @@ -36,7 +36,7 @@ main() { }); } - Completer serverReceivedAllBytes = new Completer(); + Completer serverReceivedAllBytes = Completer(); Future readData(StreamIterator iterator) async { var all = []; @@ -52,7 +52,7 @@ main() { Future sendData(TransportStream stream, String data) { stream.outgoingMessages - ..add(new DataStreamMessage(utf8.encode(data))) + ..add(DataStreamMessage(utf8.encode(data))) ..close(); return stream.outgoingMessages.done; } @@ -79,7 +79,7 @@ main() { .listen(expectAsync1((TransportStreamPush push) async { testHeaders(push.requestHeaders); - var iterator = new StreamIterator(push.stream.incomingMessages); + var iterator = StreamIterator(push.stream.incomingMessages); bool hasNext = await iterator.moveNext(); expect(hasNext, isTrue); testHeaders((iterator.current as HeadersStreamMessage).headers); @@ -87,7 +87,7 @@ main() { String msg = await readData(iterator); expect(msg, 'pushing "hello world" :)'); })); - }, settings: new ClientSettings(allowServerPushes: true)); + }, settings: ClientSettings(allowServerPushes: true)); }); }); } diff --git a/pkgs/http2/test/src/streams/streams_test.dart b/pkgs/http2/test/src/streams/streams_test.dart index 06b2a335b1..e6ad6e921e 100644 --- a/pkgs/http2/test/src/streams/streams_test.dart +++ b/pkgs/http2/test/src/streams/streams_test.dart @@ -14,7 +14,7 @@ main() { streamTest('single-header-request--empty-response', (ClientTransportConnection client, ServerTransportConnection server) async { - var expectedHeaders = [new Header.ascii('key', 'value')]; + var expectedHeaders = [Header.ascii('key', 'value')]; server.incomingStreams.listen(expectAsync1((TransportStream sStream) { sStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { @@ -34,7 +34,7 @@ main() { streamTest('multi-header-request--empty-response', (ClientTransportConnection client, ServerTransportConnection server) async { - var expectedHeaders = [new Header.ascii('key', 'value')]; + var expectedHeaders = [Header.ascii('key', 'value')]; server.incomingStreams.listen(expectAsync1((TransportStream sStream) { sStream.incomingMessages.listen( @@ -57,7 +57,7 @@ main() { streamTest('multi-data-request--empty-response', (ClientTransportConnection client, ServerTransportConnection server) async { - var expectedHeaders = [new Header.ascii('key', 'value')]; + var expectedHeaders = [Header.ascii('key', 'value')]; var chunks = [ [1], [2], @@ -97,7 +97,7 @@ main() { streamTest('single-header-request--single-headers-response', (ClientTransportConnection client, ServerTransportConnection server) async { - var expectedHeaders = [new Header.ascii('key', 'value')]; + var expectedHeaders = [Header.ascii('key', 'value')]; server.incomingStreams.listen(expectAsync1((TransportStream sStream) { sStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { @@ -123,7 +123,7 @@ main() { streamTest('single-header-request--multi-headers-response', (ClientTransportConnection client, ServerTransportConnection server) async { - var expectedHeaders = [new Header.ascii('key', 'value')]; + var expectedHeaders = [Header.ascii('key', 'value')]; server.incomingStreams.listen(expectAsync1((TransportStream sStream) { sStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { @@ -152,7 +152,7 @@ main() { streamTest('single-header-request--multi-data-response', (ClientTransportConnection client, ServerTransportConnection server) async { - var expectedHeaders = [new Header.ascii('key', 'value')]; + var expectedHeaders = [Header.ascii('key', 'value')]; var chunks = [ [1], [2], @@ -185,7 +185,7 @@ main() { streamTest('single-data-request--data-trailer-response', (ClientTransportConnection client, ServerTransportConnection server) async { - var expectedHeaders = [new Header.ascii('key', 'value')]; + var expectedHeaders = [Header.ascii('key', 'value')]; var chunk = [1]; server.incomingStreams.listen(expectAsync1((TransportStream sStream) async { diff --git a/pkgs/http2/test/transport_test.dart b/pkgs/http2/test/transport_test.dart index d97c266f5a..59f60f2f98 100644 --- a/pkgs/http2/test/transport_test.dart +++ b/pkgs/http2/test/transport_test.dart @@ -65,13 +65,13 @@ main() { // The default is unlimited, which is why we have to wait for the server // setting to arrive on the client. // At the moment, delaying by 2 microtask cycles is enough. - await new Future.value(); - await new Future.value(); + await Future.value(); + await Future.value(); final streams = []; for (int i = 0; i < concurrentStreamLimit; ++i) { expect(client.isOpen, true); - streams.add(client.makeRequest([new Header.ascii('a', 'b')])); + streams.add(client.makeRequest([Header.ascii('a', 'b')])); } expect(client.isOpen, false); for (final stream in streams) { @@ -83,7 +83,7 @@ main() { Future serverFun() async { await for (final stream in server.incomingStreams) { await stream.incomingMessages.toList(); - stream.sendHeaders([new Header.ascii('a', 'b')], endStream: true); + stream.sendHeaders([Header.ascii('a', 'b')], endStream: true); } await server.finish(); } @@ -91,20 +91,20 @@ main() { await Future.wait([clientFun(), serverFun()]); }, serverSettings: - new ServerSettings(concurrentStreamLimit: concurrentStreamLimit)); + ServerSettings(concurrentStreamLimit: concurrentStreamLimit)); transportTest('disabled-push', (ClientTransportConnection client, ServerTransportConnection server) async { server.incomingStreams .listen(expectAsync1((ServerTransportStream stream) async { expect(stream.canPush, false); - expect(() => stream.push([new Header.ascii('a', 'b')]), + expect(() => stream.push([Header.ascii('a', 'b')]), throwsA(const TypeMatcher())); - stream.sendHeaders([new Header.ascii('x', 'y')], endStream: true); + stream.sendHeaders([Header.ascii('x', 'y')], endStream: true); })); var stream = - client.makeRequest([new Header.ascii('a', 'b')], endStream: true); + client.makeRequest([Header.ascii('a', 'b')], endStream: true); var messages = await stream.incomingMessages.toList(); expect(messages, hasLength(1)); @@ -128,24 +128,24 @@ main() { var pushes = []; for (int i = 0; i < kDefaultStreamLimit; i++) { expect(stream.canPush, true); - pushes.add(stream.push([new Header.ascii('a', 'b')])); + pushes.add(stream.push([Header.ascii('a', 'b')])); } // Now we should have reached the limit and we should not be able to // create more pushes. expect(stream.canPush, false); - expect(() => stream.push([new Header.ascii('a', 'b')]), + expect(() => stream.push([Header.ascii('a', 'b')]), throwsA(const TypeMatcher())); // Finish the pushes for (ServerTransportStream pushedStream in pushes) { pushedStream - .sendHeaders([new Header.ascii('e', 'nd')], endStream: true); + .sendHeaders([Header.ascii('e', 'nd')], endStream: true); await pushedStream.incomingMessages.toList(); } // Finish the stream. - stream.sendHeaders([new Header.ascii('x', 'y')], endStream: true); + stream.sendHeaders([Header.ascii('x', 'y')], endStream: true); expect(await stream.incomingMessages.toList(), hasLength(1)); } } @@ -153,7 +153,7 @@ main() { Future clientFun() async { for (int i = 0; i < kRepetitions; i++) { var stream = - client.makeRequest([new Header.ascii('a', 'b')], endStream: true); + client.makeRequest([Header.ascii('a', 'b')], endStream: true); Future expectPeerPushes() async { int numberOfPushes = 0; @@ -186,7 +186,7 @@ main() { await client.terminate(); await serverFuture; }, - clientSettings: new ClientSettings( + clientSettings: ClientSettings( concurrentStreamLimit: kDefaultStreamLimit, allowServerPushes: true)); @@ -194,14 +194,14 @@ main() { ServerTransportConnection server) async { Future serverFun() async { await for (ServerTransportStream stream in server.incomingStreams) { - stream.sendHeaders([new Header.ascii('x', 'y')], endStream: true); + stream.sendHeaders([Header.ascii('x', 'y')], endStream: true); expect(await stream.incomingMessages.toList(), hasLength(1)); } await server.finish(); } Future clientFun() async { - var headers = [new Header.ascii('a', 'b')]; + var headers = [Header.ascii('a', 'b')]; var stream = client.makeRequest(headers, endStream: true); var finishFuture = client.finish(); var messages = await stream.incomingMessages.toList(); @@ -214,11 +214,11 @@ main() { transportTest('client-terminates-stream', (ClientTransportConnection client, ServerTransportConnection server) async { - var readyForError = new Completer(); + var readyForError = Completer(); Future serverFun() async { await for (ServerTransportStream stream in server.incomingStreams) { - stream.sendHeaders([new Header.ascii('x', 'y')], endStream: true); + stream.sendHeaders([Header.ascii('x', 'y')], endStream: true); stream.incomingMessages.listen(expectAsync1((msg) { expect(msg is HeadersStreamMessage, true); readyForError.complete(); @@ -230,7 +230,7 @@ main() { } Future clientFun() async { - var headers = [new Header.ascii('a', 'b')]; + var headers = [Header.ascii('a', 'b')]; var stream = client.makeRequest(headers, endStream: false); await readyForError.future; stream.terminate(); @@ -250,7 +250,7 @@ main() { } Future clientFun() async { - var headers = [new Header.ascii('a', 'b')]; + var headers = [Header.ascii('a', 'b')]; var stream = client.makeRequest(headers, endStream: true); await stream.incomingMessages.toList().catchError(expectAsync1((error) { expect('$error', contains('Stream was terminated by peer')); @@ -264,14 +264,14 @@ main() { transportTest('client-terminates-stream-after-half-close', (ClientTransportConnection client, ServerTransportConnection server) async { - var readyForError = new Completer(); + var readyForError = Completer(); Future serverFun() async { await for (ServerTransportStream stream in server.incomingStreams) { stream.onTerminated = expectAsync1((errorCode) { expect(errorCode, 8); }, count: 1); - stream.sendHeaders([new Header.ascii('x', 'y')], endStream: false); + stream.sendHeaders([Header.ascii('x', 'y')], endStream: false); stream.incomingMessages.listen( expectAsync1((msg) { expect(msg is HeadersStreamMessage, true); @@ -286,7 +286,7 @@ main() { } Future clientFun() async { - var headers = [new Header.ascii('a', 'b')]; + var headers = [Header.ascii('a', 'b')]; var stream = client.makeRequest(headers, endStream: true); await stream.outgoingMessages.close(); await readyForError.future; @@ -300,11 +300,11 @@ main() { transportTest('server-terminates-stream-after-half-close', (ClientTransportConnection client, ServerTransportConnection server) async { - var readyForError = new Completer(); + var readyForError = Completer(); Future serverFun() async { await for (ServerTransportStream stream in server.incomingStreams) { - stream.sendHeaders([new Header.ascii('x', 'y')], endStream: false); + stream.sendHeaders([Header.ascii('x', 'y')], endStream: false); stream.incomingMessages.listen( expectAsync1((msg) async { expect(msg is HeadersStreamMessage, true); @@ -319,7 +319,7 @@ main() { } Future clientFun() async { - var headers = [new Header.ascii('a', 'b')]; + var headers = [Header.ascii('a', 'b')]; var stream = client.makeRequest(headers, endStream: false); stream.onTerminated = expectAsync1((errorCode) { expect(errorCode, 8); @@ -364,26 +364,26 @@ main() { idleCount++; } }, count: 6); - final streams = new List.generate( + final streams = List.generate( 5, (_) => client.makeRequest([])); await Future.wait(streams.map((s) => s.outgoingMessages.close())); await Future.wait(streams.map((s) => s.incomingMessages.toList())); // This extra await is needed to allow the idle handler to run before // verifying the idleCount, because the stream cleanup runs // asynchronously after the stream is closed. - await new Future.value(); + await Future.value(); expect(activeCount, 1); expect(idleCount, 1); var stream = client.makeRequest([]); await stream.outgoingMessages.close(); await stream.incomingMessages.toList(); - await new Future.value(); + await Future.value(); stream = client.makeRequest([]); await stream.outgoingMessages.close(); await stream.incomingMessages.toList(); - await new Future.value(); + await Future.value(); await client.finish(); expect(activeCount, 3); @@ -396,7 +396,7 @@ main() { group('flow-control', () { const int kChunkSize = 1024; const int kNumberOfMessages = 1000; - final headers = [new Header.ascii('a', 'b')]; + final headers = [Header.ascii('a', 'b')]; Future testWindowSize( ClientTransportConnection client, @@ -406,22 +406,22 @@ main() { lessThan(kChunkSize * kNumberOfMessages)); int serverSentBytes = 0; - Completer flowcontrolWindowFull = new Completer(); + Completer flowcontrolWindowFull = Completer(); Future serverFun() async { await for (ServerTransportStream stream in server.incomingStreams) { - stream.sendHeaders([new Header.ascii('x', 'y')]); + stream.sendHeaders([Header.ascii('x', 'y')]); int messageNr = 0; StreamController controller; addData() { if (!controller.isPaused) { if (messageNr < kNumberOfMessages) { - var messageBytes = new Uint8List(kChunkSize); + var messageBytes = Uint8List(kChunkSize); for (int j = 0; j < messageBytes.length; j++) { messageBytes[j] = (messageNr + j) % 256; } - controller.add(new DataStreamMessage(messageBytes)); + controller.add(DataStreamMessage(messageBytes)); messageNr++; serverSentBytes += messageBytes.length; @@ -433,7 +433,7 @@ main() { } } - controller = new StreamController( + controller = StreamController( onListen: () { addData(); }, @@ -495,14 +495,14 @@ main() { transportTest('fast-sender-receiver-paused--default-window-size', (ClientTransportConnection client, ServerTransportConnection server) async { - await testWindowSize(client, server, new Window().size); + await testWindowSize(client, server, Window().size); }); transportTest('fast-sender-receiver-paused--10kb-window-size', (ClientTransportConnection client, ServerTransportConnection server) async { await testWindowSize(client, server, 8096); - }, clientSettings: new ClientSettings(streamWindowSize: 8096)); + }, clientSettings: ClientSettings(streamWindowSize: 8096)); }); }); } @@ -511,7 +511,7 @@ void transportTest(String name, func(ClientTransportConnection client, ServerTransportConnection server), {ClientSettings clientSettings, ServerSettings serverSettings}) { return test(name, () { - var bidirectional = new BidirectionalConnection(); + var bidirectional = BidirectionalConnection(); bidirectional.clientSettings = clientSettings; bidirectional.serverSettings = serverSettings; var client = bidirectional.clientConnection; @@ -524,16 +524,16 @@ class BidirectionalConnection { ClientSettings clientSettings; ServerSettings serverSettings; - final StreamController> writeA = new StreamController(); - final StreamController> writeB = new StreamController(); + final StreamController> writeA = StreamController(); + final StreamController> writeB = StreamController(); Stream> get readA => writeA.stream; Stream> get readB => writeB.stream; ClientTransportConnection get clientConnection => - new ClientTransportConnection.viaStreams(readA, writeB.sink, + ClientTransportConnection.viaStreams(readA, writeB.sink, settings: clientSettings); ServerTransportConnection get serverConnection => - new ServerTransportConnection.viaStreams(readB, writeA.sink, + ServerTransportConnection.viaStreams(readB, writeA.sink, settings: serverSettings); } From 99df062bdcfc3d0a78c44fc8cd578fcebf5012b5 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 20 Feb 2020 10:44:52 -0800 Subject: [PATCH 096/172] Enable and fix lint always_declare_return_types (dart-lang/http2#57) --- pkgs/http2/analysis_options.yaml | 1 + pkgs/http2/example/display_headers.dart | 2 +- pkgs/http2/experimental/server.dart | 4 ++-- pkgs/http2/lib/src/connection_preface.dart | 2 +- pkgs/http2/lib/src/hpack/huffman.dart | 2 +- pkgs/http2/manual_test/out_of_stream_ids_test.dart | 2 +- pkgs/http2/test/client_test.dart | 2 +- pkgs/http2/test/client_websites_test.dart | 4 ++-- pkgs/http2/test/multiprotocol_server_test.dart | 2 +- pkgs/http2/test/server_test.dart | 2 +- pkgs/http2/test/src/async_utils/async_utils_test.dart | 2 +- pkgs/http2/test/src/connection_preface_test.dart | 2 +- .../test/src/flowcontrol/connection_queues_test.dart | 2 +- pkgs/http2/test/src/flowcontrol/stream_queues_test.dart | 2 +- pkgs/http2/test/src/flowcontrol/window_handler_test.dart | 2 +- pkgs/http2/test/src/frames/frame_defragmenter_test.dart | 2 +- pkgs/http2/test/src/frames/frame_reader_test.dart | 2 +- pkgs/http2/test/src/frames/frame_writer_reader_test.dart | 4 ++-- pkgs/http2/test/src/frames/frame_writer_test.dart | 2 +- pkgs/http2/test/src/hpack/hpack_test.dart | 2 +- pkgs/http2/test/src/hpack/huffman_table_test.dart | 2 +- pkgs/http2/test/src/ping/ping_handler_test.dart | 2 +- pkgs/http2/test/src/settings/settings_handler_test.dart | 2 +- pkgs/http2/test/src/streams/helper.dart | 4 ++-- pkgs/http2/test/src/streams/simple_flow_test.dart | 8 ++++---- pkgs/http2/test/src/streams/simple_push_test.dart | 6 +++--- pkgs/http2/test/src/streams/streams_test.dart | 2 +- pkgs/http2/test/transport_test.dart | 4 ++-- 28 files changed, 38 insertions(+), 37 deletions(-) diff --git a/pkgs/http2/analysis_options.yaml b/pkgs/http2/analysis_options.yaml index 3dee28995b..702102efba 100644 --- a/pkgs/http2/analysis_options.yaml +++ b/pkgs/http2/analysis_options.yaml @@ -6,6 +6,7 @@ analyzer: dead_code: error linter: rules: + - always_declare_return_types - annotate_overrides - avoid_empty_else - avoid_init_to_null diff --git a/pkgs/http2/example/display_headers.dart b/pkgs/http2/example/display_headers.dart index 528ef3c4f4..5e57049646 100644 --- a/pkgs/http2/example/display_headers.dart +++ b/pkgs/http2/example/display_headers.dart @@ -4,7 +4,7 @@ import 'dart:io'; import 'package:http2/transport.dart'; -main(List args) async { +void main(List args) async { if (args == null || args.length != 1) { print('Usage: dart display_headers.dart '); exit(1); diff --git a/pkgs/http2/experimental/server.dart b/pkgs/http2/experimental/server.dart index 5945a48323..e0f99339e7 100644 --- a/pkgs/http2/experimental/server.dart +++ b/pkgs/http2/experimental/server.dart @@ -16,7 +16,7 @@ const bool DEBUGGING = false; const String HOSTNAME = 'localhost'; const int PORT = 7777; -main() async { +void main() async { String localFile(String path) => Platform.script.resolve(path).toFilePath(); var context = SecurityContext() @@ -35,7 +35,7 @@ main() async { }); } -handleClient(SecureSocket socket) { +void handleClient(SecureSocket socket) { dumpInfo('main', 'Got new https client'); var connection; diff --git a/pkgs/http2/lib/src/connection_preface.dart b/pkgs/http2/lib/src/connection_preface.dart index e28291de2a..750d7b4573 100644 --- a/pkgs/http2/lib/src/connection_preface.dart +++ b/pkgs/http2/lib/src/connection_preface.dart @@ -50,7 +50,7 @@ Stream> readConnectionPreface(Stream> incoming) { var prefaceBuffer = []; bool terminated = false; - terminate(error) { + void terminate(error) { if (!terminated) { result.addError(error); result.close(); diff --git a/pkgs/http2/lib/src/hpack/huffman.dart b/pkgs/http2/lib/src/hpack/huffman.dart index fc4a91bbe7..d66033e9ee 100644 --- a/pkgs/http2/lib/src/hpack/huffman.dart +++ b/pkgs/http2/lib/src/hpack/huffman.dart @@ -96,7 +96,7 @@ class HuffmanEncoder { int currentByte = 0; int currentBitOffset = 7; - writeValue(int value, int numBits) { + void writeValue(int value, int numBits) { int i = numBits - 1; while (i >= 0) { if (currentBitOffset == 7 && i >= 7) { diff --git a/pkgs/http2/manual_test/out_of_stream_ids_test.dart b/pkgs/http2/manual_test/out_of_stream_ids_test.dart index e281c13b5d..2c44d3cdb4 100644 --- a/pkgs/http2/manual_test/out_of_stream_ids_test.dart +++ b/pkgs/http2/manual_test/out_of_stream_ids_test.dart @@ -20,7 +20,7 @@ import 'package:http2/src/streams/stream_handler.dart'; import '../test/transport_test.dart'; -main() { +void main() { group('transport-test', () { transportTest('client-runs-out-of-stream-ids', (ClientTransportConnection client, diff --git a/pkgs/http2/test/client_test.dart b/pkgs/http2/test/client_test.dart index ac560752f5..ecb04c749b 100644 --- a/pkgs/http2/test/client_test.dart +++ b/pkgs/http2/test/client_test.dart @@ -19,7 +19,7 @@ import 'package:http2/transport.dart'; import 'src/hpack/hpack_test.dart' show isHeader; -main() { +void main() { group('client-tests', () { group('normal', () { clientTest('gracefull-shutdown-for-unused-connection', diff --git a/pkgs/http2/test/client_websites_test.dart b/pkgs/http2/test/client_websites_test.dart index 26961a6a7a..26be986085 100644 --- a/pkgs/http2/test/client_websites_test.dart +++ b/pkgs/http2/test/client_websites_test.dart @@ -11,7 +11,7 @@ import 'dart:io'; import 'package:http2/src/testing/client.dart'; import 'package:test/test.dart'; -main() async { +void main() async { test('google', () async { var uri = Uri.parse('https://www.google.com/'); ClientConnection connection = await connect(uri); @@ -115,7 +115,7 @@ main() async { }, tags: ['flaky']); } -dumpHeaders(Uri uri, Map> headers, +void dumpHeaders(Uri uri, Map> headers, {String msg = 'Response headers.'}) { print(''); print('[$uri] $msg'); diff --git a/pkgs/http2/test/multiprotocol_server_test.dart b/pkgs/http2/test/multiprotocol_server_test.dart index bc2d05b346..22fb8de4b7 100644 --- a/pkgs/http2/test/multiprotocol_server_test.dart +++ b/pkgs/http2/test/multiprotocol_server_test.dart @@ -13,7 +13,7 @@ import 'package:test/test.dart'; import 'package:http2/transport.dart'; import 'package:http2/multiprotocol_server.dart'; -main() { +void main() { SecurityContext context = SecurityContext() ..useCertificateChain('test/certificates/server_chain.pem') ..usePrivateKey('test/certificates/server_key.pem', password: 'dartdart'); diff --git a/pkgs/http2/test/server_test.dart b/pkgs/http2/test/server_test.dart index 2b7a70d5ab..b0de9333c6 100644 --- a/pkgs/http2/test/server_test.dart +++ b/pkgs/http2/test/server_test.dart @@ -14,7 +14,7 @@ import 'package:http2/src/frames/frames.dart'; import 'package:http2/src/hpack/hpack.dart'; import 'package:http2/src/settings/settings.dart'; -main() { +void main() { group('server-tests', () { group('normal', () { serverTest('gracefull-shutdown-for-unused-connection', diff --git a/pkgs/http2/test/src/async_utils/async_utils_test.dart b/pkgs/http2/test/src/async_utils/async_utils_test.dart index 82f2c75f06..3854ac03e0 100644 --- a/pkgs/http2/test/src/async_utils/async_utils_test.dart +++ b/pkgs/http2/test/src/async_utils/async_utils_test.dart @@ -7,7 +7,7 @@ import 'dart:async'; import 'package:test/test.dart'; import 'package:http2/src/async_utils/async_utils.dart'; -main() { +void main() { group('async_utils', () { test('buffer-indicator', () { var bi = BufferIndicator(); diff --git a/pkgs/http2/test/src/connection_preface_test.dart b/pkgs/http2/test/src/connection_preface_test.dart index b1c50bc634..d98c510a5c 100644 --- a/pkgs/http2/test/src/connection_preface_test.dart +++ b/pkgs/http2/test/src/connection_preface_test.dart @@ -10,7 +10,7 @@ import 'dart:math' show min; import 'package:test/test.dart'; import 'package:http2/src/connection_preface.dart'; -main() { +void main() { group('connection-preface', () { test('successful', () async { final frameBytes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; diff --git a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart index 53c34296f8..0bc7432e98 100644 --- a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart @@ -14,7 +14,7 @@ import 'package:http2/src/flowcontrol/connection_queues.dart'; import 'package:http2/src/flowcontrol/stream_queues.dart'; import 'package:http2/src/flowcontrol/queue_messages.dart'; -main() { +void main() { group('flowcontrol', () { test('connection-message-queue-out', () { dynamic fw = MockFrameWriter(); diff --git a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart index 1ff3c82562..a78d9c9d87 100644 --- a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart @@ -12,7 +12,7 @@ import 'package:http2/src/flowcontrol/stream_queues.dart'; import 'package:http2/src/flowcontrol/window_handler.dart'; import 'package:http2/src/flowcontrol/connection_queues.dart'; -main() { +void main() { group('flowcontrol', () { const STREAM_ID = 99; const BYTES = [1, 2, 3]; diff --git a/pkgs/http2/test/src/flowcontrol/window_handler_test.dart b/pkgs/http2/test/src/flowcontrol/window_handler_test.dart index ebc8551f22..565c7a266d 100644 --- a/pkgs/http2/test/src/flowcontrol/window_handler_test.dart +++ b/pkgs/http2/test/src/flowcontrol/window_handler_test.dart @@ -11,7 +11,7 @@ import 'package:http2/src/frames/frames.dart'; import '../error_matchers.dart'; -main() { +void main() { group('flowcontrol', () { void testAbstractOutgoingWindowHandler( AbstractOutgoingWindowHandler handler, Window window, int initialSize) { diff --git a/pkgs/http2/test/src/frames/frame_defragmenter_test.dart b/pkgs/http2/test/src/frames/frame_defragmenter_test.dart index 23a7761018..672d9ae343 100644 --- a/pkgs/http2/test/src/frames/frame_defragmenter_test.dart +++ b/pkgs/http2/test/src/frames/frame_defragmenter_test.dart @@ -9,7 +9,7 @@ import 'package:http2/src/frames/frame_defragmenter.dart'; import '../error_matchers.dart'; -main() { +void main() { group('frames', () { group('frame-defragmenter', () { UnknownFrame unknownFrame() { diff --git a/pkgs/http2/test/src/frames/frame_reader_test.dart b/pkgs/http2/test/src/frames/frame_reader_test.dart index 90eeb957e6..8a4fa460b5 100644 --- a/pkgs/http2/test/src/frames/frame_reader_test.dart +++ b/pkgs/http2/test/src/frames/frame_reader_test.dart @@ -9,7 +9,7 @@ import 'package:test/test.dart'; import 'package:http2/src/frames/frames.dart'; import 'package:http2/src/settings/settings.dart'; -main() { +void main() { group('frames', () { group('frame-reader', () { final int maxFrameSize = ActiveSettings().maxFrameSize; diff --git a/pkgs/http2/test/src/frames/frame_writer_reader_test.dart b/pkgs/http2/test/src/frames/frame_writer_reader_test.dart index 4ea96c964e..7cc3917b65 100644 --- a/pkgs/http2/test/src/frames/frame_writer_reader_test.dart +++ b/pkgs/http2/test/src/frames/frame_writer_reader_test.dart @@ -12,7 +12,7 @@ import 'package:http2/src/settings/settings.dart'; import '../hpack/hpack_test.dart' show isHeader; -main() { +void main() { group('frames', () { group('writer-reader', () { writerReaderTest('data-frame', @@ -214,7 +214,7 @@ main() { }); } -writerReaderTest(String name, +void writerReaderTest(String name, func(FrameWriter writer, FrameReader reader, HPackDecoder decoder)) { test(name, () { var settings = ActiveSettings(); diff --git a/pkgs/http2/test/src/frames/frame_writer_test.dart b/pkgs/http2/test/src/frames/frame_writer_test.dart index 78f337b349..e9a07bddde 100644 --- a/pkgs/http2/test/src/frames/frame_writer_test.dart +++ b/pkgs/http2/test/src/frames/frame_writer_test.dart @@ -10,7 +10,7 @@ import 'package:http2/src/hpack/hpack.dart'; import 'package:http2/src/frames/frames.dart'; import 'package:http2/src/settings/settings.dart'; -main() { +void main() { group('frames', () { group('frame-writer', () { test('connection-error', () { diff --git a/pkgs/http2/test/src/hpack/hpack_test.dart b/pkgs/http2/test/src/hpack/hpack_test.dart index 58e3530c71..a986cac976 100644 --- a/pkgs/http2/test/src/hpack/hpack_test.dart +++ b/pkgs/http2/test/src/hpack/hpack_test.dart @@ -5,7 +5,7 @@ import 'package:test/test.dart'; import 'package:http2/src/hpack/hpack.dart'; -main() { +void main() { group('hpack', () { group('hpack-spec-decoder', () { test('C.3 request without huffman encoding', () { diff --git a/pkgs/http2/test/src/hpack/huffman_table_test.dart b/pkgs/http2/test/src/hpack/huffman_table_test.dart index cf0e0025b4..a28c451862 100644 --- a/pkgs/http2/test/src/hpack/huffman_table_test.dart +++ b/pkgs/http2/test/src/hpack/huffman_table_test.dart @@ -8,7 +8,7 @@ import 'package:test/test.dart'; import 'package:http2/src/hpack/huffman.dart'; import 'package:http2/src/hpack/huffman_table.dart'; -main() { +void main() { group('hpack', () { group('huffman', () { final decode = http2HuffmanCodec.decode; diff --git a/pkgs/http2/test/src/ping/ping_handler_test.dart b/pkgs/http2/test/src/ping/ping_handler_test.dart index 8f770507ac..8727b39f5b 100644 --- a/pkgs/http2/test/src/ping/ping_handler_test.dart +++ b/pkgs/http2/test/src/ping/ping_handler_test.dart @@ -12,7 +12,7 @@ import 'package:http2/src/ping/ping_handler.dart'; import '../error_matchers.dart'; -main() { +void main() { group('ping-handler', () { test('successful-ping', () async { dynamic writer = FrameWriterMock(); diff --git a/pkgs/http2/test/src/settings/settings_handler_test.dart b/pkgs/http2/test/src/settings/settings_handler_test.dart index 16e5a4f56b..27273b42cc 100644 --- a/pkgs/http2/test/src/settings/settings_handler_test.dart +++ b/pkgs/http2/test/src/settings/settings_handler_test.dart @@ -13,7 +13,7 @@ import 'package:http2/src/settings/settings.dart'; import '../error_matchers.dart'; -main() { +void main() { group('settings-handler', () { var pushSettings = [Setting(Setting.SETTINGS_ENABLE_PUSH, 0)]; var invalidPushSettings = [Setting(Setting.SETTINGS_ENABLE_PUSH, 2)]; diff --git a/pkgs/http2/test/src/streams/helper.dart b/pkgs/http2/test/src/streams/helper.dart index 42144f188f..ca620bed71 100644 --- a/pkgs/http2/test/src/streams/helper.dart +++ b/pkgs/http2/test/src/streams/helper.dart @@ -12,7 +12,7 @@ import 'package:http2/transport.dart'; import 'package:http2/src/frames/frames.dart'; import 'package:http2/src/settings/settings.dart'; -expectHeadersEqual(List
headers, List
expectedHeaders) { +void expectHeadersEqual(List
headers, List
expectedHeaders) { expect(headers, hasLength(expectedHeaders.length)); for (int i = 0; i < expectedHeaders.length; i++) { expect(headers[i].name, expectedHeaders[i].name); @@ -20,7 +20,7 @@ expectHeadersEqual(List
headers, List
expectedHeaders) { } } -expectEmptyStream(Stream s) { +void expectEmptyStream(Stream s) { s.listen(expectAsync1((_) {}, count: 0), onDone: expectAsync0(() {})); } diff --git a/pkgs/http2/test/src/streams/simple_flow_test.dart b/pkgs/http2/test/src/streams/simple_flow_test.dart index c19c3e4a49..fba375576a 100644 --- a/pkgs/http2/test/src/streams/simple_flow_test.dart +++ b/pkgs/http2/test/src/streams/simple_flow_test.dart @@ -11,7 +11,7 @@ import 'package:http2/transport.dart'; import 'helper.dart'; -main() { +void main() { group('streams', () { group('flowcontrol', () { const int numOfOneKB = 1000; @@ -20,7 +20,7 @@ main() { var allBytes = List.generate(numOfOneKB * 1024, (i) => i % 256); allBytes.addAll(List.generate(42, (i) => 42)); - headersTestFun(String type) { + void Function(StreamMessage) headersTestFun(String type) { return expectAsync1((StreamMessage msg) { expect(msg is HeadersStreamMessage, isTrue); expect((msg as HeadersStreamMessage).headers.first.name, @@ -32,7 +32,7 @@ main() { Completer serverReceivedAllBytes = Completer(); - messageTestFun(String type) { + void Function(StreamMessage) messageTestFun(String type) { bool expectHeader = true; int numBytesReceived = 0; return (StreamMessage msg) { @@ -62,7 +62,7 @@ main() { }; } - sendData(TransportStream cStream) { + void sendData(TransportStream cStream) { for (int i = 0; i < (allBytes.length + 1023) ~/ 1024; i++) { int end = 1024 * (i + 1); bool isLast = end > allBytes.length; diff --git a/pkgs/http2/test/src/streams/simple_push_test.dart b/pkgs/http2/test/src/streams/simple_push_test.dart index 1633553441..7b01c6eccc 100644 --- a/pkgs/http2/test/src/streams/simple_push_test.dart +++ b/pkgs/http2/test/src/streams/simple_push_test.dart @@ -12,7 +12,7 @@ import 'package:http2/transport.dart'; import 'helper.dart'; -main() { +void main() { group('streams', () { group('server-push', () { const int numOfOneKB = 1000; @@ -21,7 +21,7 @@ main() { var allBytes = List.generate(numOfOneKB * 1024, (i) => i % 256); allBytes.addAll(List.generate(42, (i) => 42)); - testHeaders(List
headers) { + void testHeaders(List
headers) { expect(headers.length, expectedHeaders.length); for (int i = 0; i < headers.length; i++) { expect(headers[i].name, expectedHeaders[i].name); @@ -29,7 +29,7 @@ main() { } } - headersTestFun() { + void Function(StreamMessage) headersTestFun() { return expectAsync1((StreamMessage msg) { expect(msg is HeadersStreamMessage, isTrue); testHeaders((msg as HeadersStreamMessage).headers); diff --git a/pkgs/http2/test/src/streams/streams_test.dart b/pkgs/http2/test/src/streams/streams_test.dart index e6ad6e921e..1576af2bc8 100644 --- a/pkgs/http2/test/src/streams/streams_test.dart +++ b/pkgs/http2/test/src/streams/streams_test.dart @@ -9,7 +9,7 @@ import 'package:http2/transport.dart'; import 'helper.dart'; -main() { +void main() { group('streams', () { streamTest('single-header-request--empty-response', (ClientTransportConnection client, diff --git a/pkgs/http2/test/transport_test.dart b/pkgs/http2/test/transport_test.dart index 59f60f2f98..375d859e54 100644 --- a/pkgs/http2/test/transport_test.dart +++ b/pkgs/http2/test/transport_test.dart @@ -11,7 +11,7 @@ import 'package:http2/src/flowcontrol/window.dart'; import 'src/hpack/hpack_test.dart' show isHeader; -main() { +void main() { group('transport-test', () { transportTest('ping', (TransportConnection client, TransportConnection server) async { @@ -414,7 +414,7 @@ main() { int messageNr = 0; StreamController controller; - addData() { + void addData() { if (!controller.isPaused) { if (messageNr < kNumberOfMessages) { var messageBytes = Uint8List(kChunkSize); From 211f0d951134585925eaec33ff120ecf36538a4c Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 20 Feb 2020 11:06:15 -0800 Subject: [PATCH 097/172] Enable lint unawaited_futures (dart-lang/http2#58) This lint typically highlights async bugs. In this case the violations were all in tests so it is relatively safe to ignore them all (with `unawaited`) for now as the least disruptive change. - Add a dev dependency on `pedantic`. - Use `unawaited` to mask all lints in tests. --- pkgs/http2/analysis_options.yaml | 1 + pkgs/http2/experimental/server.dart | 13 +++++----- pkgs/http2/pubspec.yaml | 1 + pkgs/http2/test/client_test.dart | 6 +++-- pkgs/http2/test/client_websites_test.dart | 5 ++-- .../test/src/connection_preface_test.dart | 23 +++++++++-------- .../test/src/ping/ping_handler_test.dart | 5 ++-- .../test/src/streams/simple_push_test.dart | 5 ++-- pkgs/http2/test/src/streams/streams_test.dart | 9 ++++--- pkgs/http2/test/transport_test.dart | 25 ++++++++++--------- 10 files changed, 52 insertions(+), 41 deletions(-) diff --git a/pkgs/http2/analysis_options.yaml b/pkgs/http2/analysis_options.yaml index 702102efba..430e680278 100644 --- a/pkgs/http2/analysis_options.yaml +++ b/pkgs/http2/analysis_options.yaml @@ -34,6 +34,7 @@ linter: - test_types_in_equals - throw_in_finally - type_init_formals + - unawaited_futures - unnecessary_const - unnecessary_new - unrelated_type_equality_checks diff --git a/pkgs/http2/experimental/server.dart b/pkgs/http2/experimental/server.dart index e0f99339e7..2251947f5b 100644 --- a/pkgs/http2/experimental/server.dart +++ b/pkgs/http2/experimental/server.dart @@ -10,6 +10,7 @@ import 'dart:io'; import 'package:http2/src/testing/debug.dart' hide print; import 'package:http2/transport.dart'; +import 'package:pedantic/pedantic.dart'; const bool DEBUGGING = false; @@ -58,11 +59,11 @@ void handleClient(SecureSocket socket) { if (path == null) throw Exception('no path given'); if (path == '/') { - sendHtml(stream); + unawaited(sendHtml(stream)); } else if (['/iframe', '/iframe2'].contains(path)) { - sendIFrameHtml(stream, path); + unawaited(sendIFrameHtml(stream, path)); } else { - send404(stream, path); + unawaited(send404(stream, path)); } } } else if (msg is DataStreamMessage) { @@ -98,9 +99,9 @@ void dumpInfo(String prefix, String msg) { } Future sendHtml(ServerTransportStream stream) async { - push(stream, '/iframe', sendIFrameHtml); - push(stream, '/iframe2', sendIFrameHtml); - push(stream, '/favicon.ico', send404); + unawaited(push(stream, '/iframe', sendIFrameHtml)); + unawaited(push(stream, '/iframe2', sendIFrameHtml)); + unawaited(push(stream, '/favicon.ico', send404)); stream.sendHeaders([ Header.ascii(':status', '200'), diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index 422bea5788..b82b3245f6 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -10,3 +10,4 @@ environment: dev_dependencies: mockito: ^4.0.0 test: ^1.2.0 + pedantic: ^1.0.0 diff --git a/pkgs/http2/test/client_test.dart b/pkgs/http2/test/client_test.dart index ecb04c749b..75aa929d05 100644 --- a/pkgs/http2/test/client_test.dart +++ b/pkgs/http2/test/client_test.dart @@ -8,6 +8,7 @@ import 'dart:async'; import 'dart:convert' show ascii; import 'dart:typed_data'; +import 'package:pedantic/pedantic.dart'; import 'package:test/test.dart'; import 'package:http2/src/connection_preface.dart'; @@ -657,12 +658,13 @@ void main() { client.makeRequest([Header.ascii('a', 'b')], endStream: false); // Make sure we don't get messages/pushes on the terminated stream. - stream.incomingMessages.toList().catchError(expectAsync1((e) { + unawaited( + stream.incomingMessages.toList().catchError(expectAsync1((e) { expect( '$e', contains('This stream was not processed and can ' 'therefore be retried')); - })); + }))); expect(await stream.peerPushes.toList(), isEmpty); // Try to gracefully finish the connection. diff --git a/pkgs/http2/test/client_websites_test.dart b/pkgs/http2/test/client_websites_test.dart index 26be986085..ac404555e4 100644 --- a/pkgs/http2/test/client_websites_test.dart +++ b/pkgs/http2/test/client_websites_test.dart @@ -9,6 +9,7 @@ import 'dart:convert' show Utf8Decoder, utf8; import 'dart:io'; import 'package:http2/src/testing/client.dart'; +import 'package:pedantic/pedantic.dart'; import 'package:test/test.dart'; void main() async { @@ -20,7 +21,7 @@ void main() async { final utf8Decoder = Utf8Decoder(allowMalformed: true); String body = await response.stream.transform(utf8Decoder).join(''); - connection.close(); + unawaited(connection.close()); body = body.toLowerCase(); expect(body, contains('')); expect(body, contains('twitter.com')); diff --git a/pkgs/http2/test/src/connection_preface_test.dart b/pkgs/http2/test/src/connection_preface_test.dart index d98c510a5c..18e4418b1b 100644 --- a/pkgs/http2/test/src/connection_preface_test.dart +++ b/pkgs/http2/test/src/connection_preface_test.dart @@ -7,8 +7,9 @@ library http2.test.connection_preface_test; import 'dart:async'; import 'dart:math' show min; -import 'package:test/test.dart'; import 'package:http2/src/connection_preface.dart'; +import 'package:pedantic/pedantic.dart'; +import 'package:test/test.dart'; void main() { group('connection-preface', () { @@ -27,7 +28,7 @@ void main() { c.add(data.sublist(from, to)); } - c.close(); + unawaited(c.close()); expect(await resultF, frameBytes); } @@ -41,11 +42,11 @@ void main() { for (int i = 0; i < CONNECTION_PREFACE.length - 1; i++) { c.add([CONNECTION_PREFACE[i]]); } - c.close(); + unawaited(c.close()); - resultF.catchError(expectAsync2((error, _) { + unawaited(resultF.catchError(expectAsync2((error, _) { expect(error, contains('EOS before connection preface could be read')); - })); + }))); }); test('wrong-connection-sequence', () async { @@ -56,11 +57,11 @@ void main() { for (int i = 0; i < CONNECTION_PREFACE.length; i++) { c.add([0xff]); } - c.close(); + unawaited(c.close()); - resultF.catchError(expectAsync2((error, _) { + unawaited(resultF.catchError(expectAsync2((error, _) { expect(error, contains('Connection preface does not match.')); - })); + }))); }); test('incoming-socket-error', () async { @@ -69,11 +70,11 @@ void main() { readConnectionPreface(c.stream).fold([], (b, d) => b..addAll(d)); c.addError('hello world'); - c.close(); + unawaited(c.close()); - resultF.catchError(expectAsync2((error, _) { + unawaited(resultF.catchError(expectAsync2((error, _) { expect(error, contains('hello world')); - })); + }))); }); }); } diff --git a/pkgs/http2/test/src/ping/ping_handler_test.dart b/pkgs/http2/test/src/ping/ping_handler_test.dart index 8727b39f5b..658681e022 100644 --- a/pkgs/http2/test/src/ping/ping_handler_test.dart +++ b/pkgs/http2/test/src/ping/ping_handler_test.dart @@ -9,6 +9,7 @@ import 'package:test/test.dart'; import 'package:http2/src/frames/frames.dart'; import 'package:http2/src/ping/ping_handler.dart'; +import 'package:pedantic/pedantic.dart'; import '../error_matchers.dart'; @@ -65,9 +66,9 @@ void main() { // Ensure outstanding pings will be completed with an error once we call // `pingHandler.terminate()`. - future.catchError(expectAsync2((error, _) { + unawaited(future.catchError(expectAsync2((error, _) { expect(error, 'hello world'); - })); + }))); pingHandler.terminate('hello world'); verifyNoMoreInteractions(writer); }); diff --git a/pkgs/http2/test/src/streams/simple_push_test.dart b/pkgs/http2/test/src/streams/simple_push_test.dart index 7b01c6eccc..3b34fe5abf 100644 --- a/pkgs/http2/test/src/streams/simple_push_test.dart +++ b/pkgs/http2/test/src/streams/simple_push_test.dart @@ -7,8 +7,9 @@ library http2.test.streams.simple_push_test; import 'dart:async'; import 'dart:convert' show utf8; -import 'package:test/test.dart'; import 'package:http2/transport.dart'; +import 'package:pedantic/pedantic.dart'; +import 'package:test/test.dart'; import 'helper.dart'; @@ -65,7 +66,7 @@ void main() { pushStream.sendHeaders(expectedHeaders); await sendData(pushStream, 'pushing "hello world" :)'); - sStream.incomingMessages.drain(); + unawaited(sStream.incomingMessages.drain()); sStream.sendHeaders(expectedHeaders, endStream: true); expect(await serverReceivedAllBytes.future, completes); diff --git a/pkgs/http2/test/src/streams/streams_test.dart b/pkgs/http2/test/src/streams/streams_test.dart index 1576af2bc8..560e20445f 100644 --- a/pkgs/http2/test/src/streams/streams_test.dart +++ b/pkgs/http2/test/src/streams/streams_test.dart @@ -4,8 +4,9 @@ library http2.test.end2end_test; -import 'package:test/test.dart'; import 'package:http2/transport.dart'; +import 'package:pedantic/pedantic.dart'; +import 'package:test/test.dart'; import 'helper.dart'; @@ -85,12 +86,12 @@ void main() { }, count: 1 + chunks.length), onDone: expectAsync0(() { expect(receivedChunks, chunks); })); - sStream.outgoingMessages.close(); + unawaited(sStream.outgoingMessages.close()); })); TransportStream cStream = client.makeRequest(expectedHeaders); chunks.forEach(cStream.sendData); - cStream.outgoingMessages.close(); + unawaited(cStream.outgoingMessages.close()); expectEmptyStream(cStream.incomingMessages); }); @@ -172,7 +173,7 @@ void main() { })); TransportStream cStream = client.makeRequest(expectedHeaders); - cStream.outgoingMessages.close(); + unawaited(cStream.outgoingMessages.close()); int i = 0; cStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { diff --git a/pkgs/http2/test/transport_test.dart b/pkgs/http2/test/transport_test.dart index 375d859e54..00f743df5d 100644 --- a/pkgs/http2/test/transport_test.dart +++ b/pkgs/http2/test/transport_test.dart @@ -5,9 +5,10 @@ import 'dart:async'; import 'dart:typed_data'; -import 'package:test/test.dart'; -import 'package:http2/transport.dart'; import 'package:http2/src/flowcontrol/window.dart'; +import 'package:http2/transport.dart'; +import 'package:pedantic/pedantic.dart'; +import 'package:test/test.dart'; import 'src/hpack/hpack_test.dart' show isHeader; @@ -29,12 +30,12 @@ void main() { // NOTE: Now the connection is dead and client/server should complete // with [TransportException]s when doing work (e.g. ping). - client.ping().catchError(expectAsync2((e, s) { + unawaited(client.ping().catchError(expectAsync2((e, s) { expect(e is TransportException, isTrue); - })); - server.ping().catchError(expectAsync2((e, s) { + }))); + unawaited(server.ping().catchError(expectAsync2((e, s) { expect(e is TransportException, isTrue); - })); + }))); }); transportTest('terminated-server-ping', @@ -47,12 +48,12 @@ void main() { // NOTE: Now the connection is dead and the client/server should complete // with [TransportException]s when doing work (e.g. ping). - client.ping().catchError(expectAsync2((e, s) { + unawaited(client.ping().catchError(expectAsync2((e, s) { expect(e is TransportException, isTrue); - })); - server.ping().catchError(expectAsync2((e, s) { + }))); + unawaited(server.ping().catchError(expectAsync2((e, s) { expect(e is TransportException, isTrue); - })); + }))); }); const int concurrentStreamLimit = 5; @@ -345,9 +346,9 @@ void main() { }, count: 6); await for (final stream in server.incomingStreams) { stream.sendHeaders([]); - stream.incomingMessages + unawaited(stream.incomingMessages .toList() - .then((_) => stream.outgoingMessages.close()); + .then((_) => stream.outgoingMessages.close())); } await server.finish(); expect(activeCount, 3); From a78e218fd76e912a7243a99fa6a7311373bf9537 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 20 Feb 2020 12:12:06 -0800 Subject: [PATCH 098/172] Enable and fix lint omit_local_variable_types (dart-lang/http2#61) --- pkgs/http2/analysis_options.yaml | 1 + pkgs/http2/example/display_headers.dart | 2 +- pkgs/http2/experimental/server.dart | 8 +-- pkgs/http2/lib/multiprotocol_server.dart | 2 +- pkgs/http2/lib/src/byte_utils.dart | 4 +- pkgs/http2/lib/src/connection.dart | 2 +- pkgs/http2/lib/src/connection_preface.dart | 10 +-- .../src/flowcontrol/connection_queues.dart | 6 +- .../lib/src/flowcontrol/stream_queues.dart | 12 ++-- .../lib/src/flowcontrol/window_handler.dart | 2 +- pkgs/http2/lib/src/frames/frame_reader.dart | 72 +++++++++---------- pkgs/http2/lib/src/frames/frame_writer.dart | 68 +++++++++--------- pkgs/http2/lib/src/hpack/hpack.dart | 34 ++++----- pkgs/http2/lib/src/hpack/huffman.dart | 28 ++++---- pkgs/http2/lib/src/ping/ping_handler.dart | 4 +- pkgs/http2/lib/src/settings/settings.dart | 6 +- .../http2/lib/src/streams/stream_handler.dart | 34 ++++----- pkgs/http2/lib/src/testing/client.dart | 20 ++---- pkgs/http2/lib/src/testing/debug.dart | 4 +- .../manual_test/out_of_stream_ids_test.dart | 2 +- pkgs/http2/test/client_test.dart | 26 +++---- pkgs/http2/test/client_websites_test.dart | 21 +++--- .../http2/test/multiprotocol_server_test.dart | 10 +-- pkgs/http2/test/server_test.dart | 4 +- .../test/src/connection_preface_test.dart | 12 ++-- .../src/flowcontrol/window_handler_test.dart | 6 +- .../src/frames/frame_defragmenter_test.dart | 6 +- .../test/src/frames/frame_reader_test.dart | 2 +- pkgs/http2/test/src/hpack/hpack_test.dart | 8 +-- .../test/src/hpack/huffman_table_test.dart | 2 +- .../test/src/ping/ping_handler_test.dart | 8 +-- .../src/settings/settings_handler_test.dart | 4 +- pkgs/http2/test/src/streams/helper.dart | 2 +- .../test/src/streams/simple_flow_test.dart | 14 ++-- .../test/src/streams/simple_push_test.dart | 13 ++-- pkgs/http2/test/src/streams/streams_test.dart | 8 +-- pkgs/http2/test/transport_test.dart | 38 +++++----- 37 files changed, 247 insertions(+), 258 deletions(-) diff --git a/pkgs/http2/analysis_options.yaml b/pkgs/http2/analysis_options.yaml index 430e680278..0518e4a013 100644 --- a/pkgs/http2/analysis_options.yaml +++ b/pkgs/http2/analysis_options.yaml @@ -24,6 +24,7 @@ linter: - library_names - library_prefixes - non_constant_identifier_names + - omit_local_variable_types - only_throw_errors - prefer_equal_for_default_values - prefer_final_fields diff --git a/pkgs/http2/example/display_headers.dart b/pkgs/http2/example/display_headers.dart index 5e57049646..57b6f01405 100644 --- a/pkgs/http2/example/display_headers.dart +++ b/pkgs/http2/example/display_headers.dart @@ -48,7 +48,7 @@ void main(List args) async { } Future connect(Uri uri) async { - bool useSSL = uri.scheme == 'https'; + var useSSL = uri.scheme == 'https'; if (useSSL) { var secureSocket = await SecureSocket.connect(uri.host, uri.port, supportedProtocols: ['h2']); diff --git a/pkgs/http2/experimental/server.dart b/pkgs/http2/experimental/server.dart index 2251947f5b..a5ca8c5767 100644 --- a/pkgs/http2/experimental/server.dart +++ b/pkgs/http2/experimental/server.dart @@ -74,15 +74,15 @@ void handleClient(SecureSocket socket) { } void dumpHeaders(String prefix, List
headers) { - for (int i = 0; i < headers.length; i++) { - String key = ascii.decode(headers[i].name); - String value = ascii.decode(headers[i].value); + for (var i = 0; i < headers.length; i++) { + var key = ascii.decode(headers[i].name); + var value = ascii.decode(headers[i].value); print('[$prefix] $key: $value'); } } String pathFromHeaders(List
headers) { - for (int i = 0; i < headers.length; i++) { + for (var i = 0; i < headers.length; i++) { if (ascii.decode(headers[i].name) == ':path') { return ascii.decode(headers[i].value); } diff --git a/pkgs/http2/lib/multiprotocol_server.dart b/pkgs/http2/lib/multiprotocol_server.dart index ce901cad80..ba097e83b9 100644 --- a/pkgs/http2/lib/multiprotocol_server.dart +++ b/pkgs/http2/lib/multiprotocol_server.dart @@ -97,7 +97,7 @@ class MultiProtocolHttpServer { /// Completes once everything has been successfully shut down. Future close({bool force = false}) { return _serverSocket.close().whenComplete(() { - Future done1 = _http11Server.close(force: force); + var done1 = _http11Server.close(force: force); Future done2 = Future.wait( _http2Connections.map((c) => force ? c.terminate() : c.finish())); return Future.wait([done1, done2]); diff --git a/pkgs/http2/lib/src/byte_utils.dart b/pkgs/http2/lib/src/byte_utils.dart index 099ceb1968..411e78e22b 100644 --- a/pkgs/http2/lib/src/byte_utils.dart +++ b/pkgs/http2/lib/src/byte_utils.dart @@ -15,8 +15,8 @@ List viewOrSublist(List data, int offset, int length) { } int readInt64(List bytes, int offset) { - int high = readInt32(bytes, offset); - int low = readInt32(bytes, offset + 4); + var high = readInt32(bytes, offset); + var low = readInt32(bytes, offset + 4); return high << 32 | low; } diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index 33600cecb0..81ad2a770f 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -62,7 +62,7 @@ class ConnectionState { @override String toString() { - String message = ''; + var message = ''; void add(bool condition, String flag) { if (condition) { diff --git a/pkgs/http2/lib/src/connection_preface.dart b/pkgs/http2/lib/src/connection_preface.dart index 750d7b4573..8625ad39ff 100644 --- a/pkgs/http2/lib/src/connection_preface.dart +++ b/pkgs/http2/lib/src/connection_preface.dart @@ -46,9 +46,9 @@ const List CONNECTION_PREFACE = [ Stream> readConnectionPreface(Stream> incoming) { StreamController> result; StreamSubscription subscription; - bool connectionPrefaceRead = false; + var connectionPrefaceRead = false; var prefaceBuffer = []; - bool terminated = false; + var terminated = false; void terminate(error) { if (!terminated) { @@ -60,7 +60,7 @@ Stream> readConnectionPreface(Stream> incoming) { } bool compareConnectionPreface(List data) { - for (int i = 0; i < CONNECTION_PREFACE.length; i++) { + for (var i = 0; i < CONNECTION_PREFACE.length; i++) { if (data[i] != CONNECTION_PREFACE[i]) { terminate('Connection preface does not match.'); return false; @@ -80,9 +80,9 @@ Stream> readConnectionPreface(Stream> incoming) { if (!compareConnectionPreface(data)) return; data = data.sublist(CONNECTION_PREFACE.length); } else if (prefaceBuffer.length < CONNECTION_PREFACE.length) { - int remaining = CONNECTION_PREFACE.length - prefaceBuffer.length; + var remaining = CONNECTION_PREFACE.length - prefaceBuffer.length; - int end = min(data.length, remaining); + var end = min(data.length, remaining); var part1 = viewOrSublist(data, 0, end); var part2 = viewOrSublist(data, end, data.length - end); prefaceBuffer.addAll(part1); diff --git a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart index 2eec7e319b..12a43ded26 100644 --- a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart @@ -112,7 +112,7 @@ class ConnectionMessageQueueOut extends Object } void _trySendMessage() { - Message message = _messages.first; + var message = _messages.first; if (message is HeadersMessage) { _messages.removeFirst(); _frameWriter.writeHeadersFrame(message.streamId, message.headers, @@ -131,7 +131,7 @@ class ConnectionMessageQueueOut extends Object } else { // NOTE: We need to fragment the DataMessage. // TODO: Do not fragment if the number of bytes we can send is too low - int len = _connectionWindow.peerWindowSize; + var len = _connectionWindow.peerWindowSize; var head = viewOrSublist(message.bytes, 0, len); var tail = viewOrSublist(message.bytes, len, message.bytes.length - len); @@ -311,7 +311,7 @@ class ConnectionMessageQueueIn extends Object void _tryDispatch( int streamId, StreamMessageQueueIn mq, Queue pendingMessages) { - int bytesDeliveredToStream = 0; + var bytesDeliveredToStream = 0; while (!mq.bufferIndicator.wouldBuffer && pendingMessages.length > 0) { _count--; diff --git a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart index cd1827352e..dec04f9398 100644 --- a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart @@ -97,22 +97,22 @@ class StreamMessageQueueOut extends Object } void _trySendData() { - int queueLenBefore = _messages.length; + var queueLenBefore = _messages.length; while (_messages.length > 0) { - Message message = _messages.first; + var message = _messages.first; if (message is HeadersMessage) { _messages.removeFirst(); connectionMessageQueue.enqueueMessage(message); } else if (message is DataMessage) { - int bytesAvailable = streamWindow.peerWindowSize; + var bytesAvailable = streamWindow.peerWindowSize; if (bytesAvailable > 0 || message.bytes.length == 0) { _messages.removeFirst(); // Do we need to fragment? - DataMessage messageToSend = message; - List messageBytes = message.bytes; + var messageToSend = message; + var messageBytes = message.bytes; // TODO: Do not fragment if the number of bytes we can send is too low if (messageBytes.length > bytesAvailable) { var partA = viewOrSublist(messageBytes, 0, bytesAvailable); @@ -287,7 +287,7 @@ class StreamMessageQueueIn extends Object void _tryDispatch() { while (!wasTerminated && _pendingMessages.isNotEmpty) { - bool handled = wasCancelled; + var handled = wasCancelled; var message = _pendingMessages.first; if (wasCancelled) { diff --git a/pkgs/http2/lib/src/flowcontrol/window_handler.dart b/pkgs/http2/lib/src/flowcontrol/window_handler.dart index 4c0daaf80a..eb165ad913 100644 --- a/pkgs/http2/lib/src/flowcontrol/window_handler.dart +++ b/pkgs/http2/lib/src/flowcontrol/window_handler.dart @@ -30,7 +30,7 @@ abstract class AbstractOutgoingWindowHandler { /// Process a window update frame received from the remote end. void processWindowUpdate(WindowUpdateFrame frame) { - int increment = frame.windowSizeIncrement; + var increment = frame.windowSizeIncrement; if ((_peerWindow.size + increment) > Window.MAX_WINDOW_SIZE) { throw FlowControlException( 'Window update received from remote peer would make flow control ' diff --git a/pkgs/http2/lib/src/frames/frame_reader.dart b/pkgs/http2/lib/src/frames/frame_reader.dart index fcb81e16a6..2c62216516 100644 --- a/pkgs/http2/lib/src/frames/frame_reader.dart +++ b/pkgs/http2/lib/src/frames/frame_reader.dart @@ -19,8 +19,8 @@ class FrameReader { /// Starts to listen on the input stream and decodes HTTP/2 transport frames. Stream startDecoding() { - List> bufferedData = List>(); - int bufferedLength = 0; + var bufferedData = List>(); + var bufferedLength = 0; FrameHeader tryReadHeader() { if (bufferedLength >= FRAME_HEADER_SIZE) { @@ -34,16 +34,16 @@ class FrameReader { } Frame tryReadFrame(FrameHeader header) { - int totalFrameLen = FRAME_HEADER_SIZE + header.length; + var totalFrameLen = FRAME_HEADER_SIZE + header.length; if (bufferedLength >= totalFrameLen) { // Get the whole frame in the first byte array. _mergeLists(bufferedData, totalFrameLen); // Read the frame. - Frame frame = _readFrame(header, bufferedData[0], FRAME_HEADER_SIZE); + var frame = _readFrame(header, bufferedData[0], FRAME_HEADER_SIZE); // Update bufferedData/bufferedLength - int firstChunkLen = bufferedData[0].length; + var firstChunkLen = bufferedData[0].length; if (firstChunkLen == totalFrameLen) { bufferedData.removeAt(0); } else { @@ -84,7 +84,7 @@ class FrameReader { return; } - Frame frame = tryReadFrame(header); + var frame = tryReadFrame(header); if (frame != null) { _framesController.add(frame); @@ -123,17 +123,17 @@ class FrameReader { /// `numberOfBytes` bytes. void _mergeLists(List> bufferedData, int numberOfBytes) { if (bufferedData[0].length < numberOfBytes) { - int numLists = 0; - int accumulatedLength = 0; + var numLists = 0; + var accumulatedLength = 0; while (accumulatedLength < numberOfBytes && numLists <= bufferedData.length) { accumulatedLength += bufferedData[numLists++].length; } assert(accumulatedLength >= numberOfBytes); var newList = Uint8List(accumulatedLength); - int offset = 0; - for (int i = 0; i < numLists; i++) { - List data = bufferedData[i]; + var offset = 0; + for (var i = 0; i < numLists; i++) { + var data = bufferedData[i]; newList.setRange(offset, offset + data.length, data); offset += data.length; } @@ -144,39 +144,39 @@ class FrameReader { /// Reads a FrameHeader] from [bytes], starting at [offset]. FrameHeader _readFrameHeader(List bytes, int offset) { - int length = readInt24(bytes, offset); - int type = bytes[offset + 3]; - int flags = bytes[offset + 4]; - int streamId = readInt32(bytes, offset + 5) & 0x7fffffff; + var length = readInt24(bytes, offset); + var type = bytes[offset + 3]; + var flags = bytes[offset + 4]; + var streamId = readInt32(bytes, offset + 5) & 0x7fffffff; return FrameHeader(length, type, flags, streamId); } /// Reads a [Frame] from [bytes], starting at [frameOffset]. Frame _readFrame(FrameHeader header, List bytes, int frameOffset) { - int frameEnd = frameOffset + header.length; + var frameEnd = frameOffset + header.length; - int offset = frameOffset; + var offset = frameOffset; switch (header.type) { case FrameType.DATA: - int padLength = 0; + var padLength = 0; if (_isFlagSet(header.flags, DataFrame.FLAG_PADDED)) { _checkFrameLengthCondition((frameEnd - offset) >= 1); padLength = bytes[offset++]; } - int dataLen = frameEnd - offset - padLength; + var dataLen = frameEnd - offset - padLength; _checkFrameLengthCondition(dataLen >= 0); var dataBytes = viewOrSublist(bytes, offset, dataLen); return DataFrame(header, padLength, dataBytes); case FrameType.HEADERS: - int padLength = 0; + var padLength = 0; if (_isFlagSet(header.flags, HeadersFrame.FLAG_PADDED)) { _checkFrameLengthCondition((frameEnd - offset) >= 1); padLength = bytes[offset++]; } int streamDependency; - bool exclusiveDependency = false; + var exclusiveDependency = false; int weight; if (_isFlagSet(header.flags, HeadersFrame.FLAG_PRIORITY)) { _checkFrameLengthCondition((frameEnd - offset) >= 5); @@ -185,7 +185,7 @@ class FrameReader { offset += 4; weight = bytes[offset++]; } - int headerBlockLen = frameEnd - offset - padLength; + var headerBlockLen = frameEnd - offset - padLength; _checkFrameLengthCondition(headerBlockLen >= 0); var headerBlockFragment = viewOrSublist(bytes, offset, headerBlockLen); return HeadersFrame(header, padLength, exclusiveDependency, @@ -195,9 +195,9 @@ class FrameReader { _checkFrameLengthCondition( (frameEnd - offset) == PriorityFrame.FIXED_FRAME_LENGTH, message: 'Priority frame length must be exactly 5 bytes.'); - bool exclusiveDependency = (bytes[offset] & 0x80) == 0x80; - int streamDependency = readInt32(bytes, offset) & 0x7fffffff; - int weight = bytes[offset + 4]; + var exclusiveDependency = (bytes[offset] & 0x80) == 0x80; + var streamDependency = readInt32(bytes, offset) & 0x7fffffff; + var weight = bytes[offset + 4]; return PriorityFrame( header, exclusiveDependency, streamDependency, weight); @@ -205,18 +205,18 @@ class FrameReader { _checkFrameLengthCondition( (frameEnd - offset) == RstStreamFrame.FIXED_FRAME_LENGTH, message: 'Rst frames must have a length of 4.'); - int errorCode = readInt32(bytes, offset); + var errorCode = readInt32(bytes, offset); return RstStreamFrame(header, errorCode); case FrameType.SETTINGS: _checkFrameLengthCondition((header.length % 6) == 0, message: 'Settings frame length must be a multiple of 6 bytes.'); - int count = header.length ~/ 6; + var count = header.length ~/ 6; var settings = List(count); - for (int i = 0; i < count; i++) { - int identifier = readInt16(bytes, offset + 6 * i); - int value = readInt32(bytes, offset + 6 * i + 2); + for (var i = 0; i < count; i++) { + var identifier = readInt16(bytes, offset + 6 * i); + var value = readInt32(bytes, offset + 6 * i + 2); settings[i] = Setting(identifier, value); } var frame = SettingsFrame(header, settings); @@ -227,14 +227,14 @@ class FrameReader { return frame; case FrameType.PUSH_PROMISE: - int padLength = 0; + var padLength = 0; if (_isFlagSet(header.flags, PushPromiseFrame.FLAG_PADDED)) { _checkFrameLengthCondition((frameEnd - offset) >= 1); padLength = bytes[offset++]; } - int promisedStreamId = readInt32(bytes, offset) & 0x7fffffff; + var promisedStreamId = readInt32(bytes, offset) & 0x7fffffff; offset += 4; - int headerBlockLen = frameEnd - offset - padLength; + var headerBlockLen = frameEnd - offset - padLength; _checkFrameLengthCondition(headerBlockLen >= 0); var headerBlockFragment = viewOrSublist(bytes, offset, headerBlockLen); return PushPromiseFrame( @@ -249,8 +249,8 @@ class FrameReader { case FrameType.GOAWAY: _checkFrameLengthCondition((frameEnd - offset) >= 8); - int lastStreamId = readInt32(bytes, offset); - int errorCode = readInt32(bytes, offset + 4); + var lastStreamId = readInt32(bytes, offset); + var errorCode = readInt32(bytes, offset + 4); var debugData = viewOrSublist(bytes, offset + 8, header.length - 8); return GoawayFrame(header, lastStreamId, errorCode, debugData); @@ -258,7 +258,7 @@ class FrameReader { _checkFrameLengthCondition( (frameEnd - offset) == WindowUpdateFrame.FIXED_FRAME_LENGTH, message: 'Window update frames must have a length of 4.'); - int windowSizeIncrement = readInt32(bytes, offset) & 0x7fffffff; + var windowSizeIncrement = readInt32(bytes, offset) & 0x7fffffff; return WindowUpdateFrame(header, windowSizeIncrement); case FrameType.CONTINUATION: diff --git a/pkgs/http2/lib/src/frames/frame_writer.dart b/pkgs/http2/lib/src/frames/frame_writer.dart index 80437926b8..211aa54429 100644 --- a/pkgs/http2/lib/src/frames/frame_writer.dart +++ b/pkgs/http2/lib/src/frames/frame_writer.dart @@ -42,11 +42,11 @@ class FrameWriter { } void _writeDataFrameNoFragment(int streamId, List data, bool endStream) { - int type = FrameType.DATA; - int flags = endStream ? DataFrame.FLAG_END_STREAM : 0; + var type = FrameType.DATA; + var flags = endStream ? DataFrame.FLAG_END_STREAM : 0; var buffer = Uint8List(FRAME_HEADER_SIZE + data.length); - int offset = 0; + var offset = 0; _setFrameHeader(buffer, offset, type, flags, streamId, data.length); offset += FRAME_HEADER_SIZE; @@ -79,13 +79,13 @@ class FrameWriter { void _writeHeadersFrameNoFragment( int streamId, List fragment, bool endHeaders, bool endStream) { - int type = FrameType.HEADERS; - int flags = 0; + var type = FrameType.HEADERS; + var flags = 0; if (endHeaders) flags |= HeadersFrame.FLAG_END_HEADERS; if (endStream) flags |= HeadersFrame.FLAG_END_STREAM; var buffer = Uint8List(FRAME_HEADER_SIZE + fragment.length); - int offset = 0; + var offset = 0; _setFrameHeader(buffer, offset, type, flags, streamId, fragment.length); offset += FRAME_HEADER_SIZE; @@ -97,11 +97,11 @@ class FrameWriter { void _writeContinuationFrame( int streamId, List fragment, bool endHeaders) { - int type = FrameType.CONTINUATION; - int flags = endHeaders ? ContinuationFrame.FLAG_END_HEADERS : 0; + var type = FrameType.CONTINUATION; + var flags = endHeaders ? ContinuationFrame.FLAG_END_HEADERS : 0; var buffer = Uint8List(FRAME_HEADER_SIZE + fragment.length); - int offset = 0; + var offset = 0; _setFrameHeader(buffer, offset, type, flags, streamId, fragment.length); offset += FRAME_HEADER_SIZE; @@ -113,12 +113,12 @@ class FrameWriter { void writePriorityFrame(int streamId, int streamDependency, int weight, {bool exclusive = false}) { - int type = FrameType.PRIORITY; - int flags = 0; + var type = FrameType.PRIORITY; + var flags = 0; var buffer = Uint8List(FRAME_HEADER_SIZE + PriorityFrame.FIXED_FRAME_LENGTH); - int offset = 0; + var offset = 0; _setFrameHeader(buffer, offset, type, flags, streamId, 5); offset += FRAME_HEADER_SIZE; @@ -134,12 +134,12 @@ class FrameWriter { } void writeRstStreamFrame(int streamId, int errorCode) { - int type = FrameType.RST_STREAM; - int flags = 0; + var type = FrameType.RST_STREAM; + var flags = 0; var buffer = Uint8List(FRAME_HEADER_SIZE + RstStreamFrame.FIXED_FRAME_LENGTH); - int offset = 0; + var offset = 0; _setFrameHeader(buffer, offset, type, flags, streamId, 4); offset += FRAME_HEADER_SIZE; @@ -150,16 +150,16 @@ class FrameWriter { } void writeSettingsFrame(List settings) { - int type = FrameType.SETTINGS; - int flags = 0; + var type = FrameType.SETTINGS; + var flags = 0; var buffer = Uint8List(FRAME_HEADER_SIZE + 6 * settings.length); - int offset = 0; + var offset = 0; _setFrameHeader(buffer, offset, type, flags, 0, 6 * settings.length); offset += FRAME_HEADER_SIZE; - for (int i = 0; i < settings.length; i++) { + for (var i = 0; i < settings.length; i++) { var setting = settings[i]; setInt16(buffer, offset + 6 * i, setting.identifier); setInt32(buffer, offset + 6 * i + 2, setting.value); @@ -169,11 +169,11 @@ class FrameWriter { } void writeSettingsAckFrame() { - int type = FrameType.SETTINGS; - int flags = SettingsFrame.FLAG_ACK; + var type = FrameType.SETTINGS; + var flags = SettingsFrame.FLAG_ACK; var buffer = Uint8List(FRAME_HEADER_SIZE); - int offset = 0; + var offset = 0; _setFrameHeader(buffer, offset, type, flags, 0, 0); offset += FRAME_HEADER_SIZE; @@ -206,11 +206,11 @@ class FrameWriter { void _writePushPromiseFrameNoFragmentation( int streamId, int promisedStreamId, List fragment, bool endHeaders) { - int type = FrameType.PUSH_PROMISE; - int flags = endHeaders ? HeadersFrame.FLAG_END_HEADERS : 0; + var type = FrameType.PUSH_PROMISE; + var flags = endHeaders ? HeadersFrame.FLAG_END_HEADERS : 0; var buffer = Uint8List(FRAME_HEADER_SIZE + 4 + fragment.length); - int offset = 0; + var offset = 0; _setFrameHeader(buffer, offset, type, flags, streamId, 4 + fragment.length); offset += FRAME_HEADER_SIZE; @@ -222,11 +222,11 @@ class FrameWriter { } void writePingFrame(int opaqueData, {bool ack = false}) { - int type = FrameType.PING; - int flags = ack ? PingFrame.FLAG_ACK : 0; + var type = FrameType.PING; + var flags = ack ? PingFrame.FLAG_ACK : 0; var buffer = Uint8List(FRAME_HEADER_SIZE + PingFrame.FIXED_FRAME_LENGTH); - int offset = 0; + var offset = 0; _setFrameHeader(buffer, 0, type, flags, 0, 8); offset += FRAME_HEADER_SIZE; @@ -236,11 +236,11 @@ class FrameWriter { } void writeGoawayFrame(int lastStreamId, int errorCode, List debugData) { - int type = FrameType.GOAWAY; - int flags = 0; + var type = FrameType.GOAWAY; + var flags = 0; var buffer = Uint8List(FRAME_HEADER_SIZE + 8 + debugData.length); - int offset = 0; + var offset = 0; _setFrameHeader(buffer, offset, type, flags, 0, 8 + debugData.length); offset += FRAME_HEADER_SIZE; @@ -253,12 +253,12 @@ class FrameWriter { } void writeWindowUpdate(int sizeIncrement, {int streamId = 0}) { - int type = FrameType.WINDOW_UPDATE; - int flags = 0; + var type = FrameType.WINDOW_UPDATE; + var flags = 0; var buffer = Uint8List(FRAME_HEADER_SIZE + WindowUpdateFrame.FIXED_FRAME_LENGTH); - int offset = 0; + var offset = 0; _setFrameHeader(buffer, offset, type, flags, streamId, 4); offset += FRAME_HEADER_SIZE; diff --git a/pkgs/http2/lib/src/hpack/hpack.dart b/pkgs/http2/lib/src/hpack/hpack.dart index 2477beeca7..5c120f0a88 100644 --- a/pkgs/http2/lib/src/hpack/hpack.dart +++ b/pkgs/http2/lib/src/hpack/hpack.dart @@ -64,7 +64,7 @@ class HPackDecoder { } List
decode(List data) { - int offset = 0; + var offset = 0; int readInteger(int prefixBits) { assert(prefixBits <= 8 && prefixBits > 0); @@ -75,9 +75,9 @@ class HPackDecoder { if (byte == ((1 << prefixBits) - 1)) { // Length encodeded. integer = 0; - int shift = 0; + var shift = 0; while (true) { - bool done = (data[offset] & 0x80) != 0x80; + var done = (data[offset] & 0x80) != 0x80; integer += (data[offset++] & 0x7f) << shift; shift += 7; if (done) break; @@ -92,8 +92,8 @@ class HPackDecoder { } List readStringLiteral() { - bool isHuffmanEncoding = (data[offset] & 0x80) != 0; - int length = readInteger(7); + var isHuffmanEncoding = (data[offset] & 0x80) != 0; + var length = readInteger(7); var sublist = viewOrSublist(data, offset, length); offset += length; @@ -117,18 +117,18 @@ class HPackDecoder { } try { - List
headers = []; + var headers =
[]; while (offset < data.length) { - int byte = data[offset]; - bool isIndexedField = (byte & 0x80) != 0; - bool isIncrementalIndexing = (byte & 0xc0) == 0x40; + var byte = data[offset]; + var isIndexedField = (byte & 0x80) != 0; + var isIncrementalIndexing = (byte & 0xc0) == 0x40; - bool isWithoutIndexing = (byte & 0xf0) == 0; - bool isNeverIndexing = (byte & 0xf0) == 0x10; - bool isDynamicTableSizeUpdate = (byte & 0xe0) == 0x20; + var isWithoutIndexing = (byte & 0xf0) == 0; + var isNeverIndexing = (byte & 0xf0) == 0x10; + var isDynamicTableSizeUpdate = (byte & 0xe0) == 0x20; if (isIndexedField) { - int index = readInteger(7); + var index = readInteger(7); var field = _table.lookup(index); headers.add(field); } else if (isIncrementalIndexing) { @@ -141,7 +141,7 @@ class HPackDecoder { headers .add(readHeaderFieldInternal(readInteger(4), neverIndexed: true)); } else if (isDynamicTableSizeUpdate) { - int newMaxSize = readInteger(5); + var newMaxSize = readInteger(5); if (newMaxSize <= _maxHeaderTableSize) { _table.updateMaxSize(newMaxSize); } else { @@ -174,7 +174,7 @@ class HPackEncoder { List encode(List
headers) { var bytesBuilder = BytesBuilder(); - int currentByte = 0; + var currentByte = 0; void writeInteger(int prefixBits, int value) { assert(prefixBits <= 8); @@ -187,7 +187,7 @@ class HPackEncoder { currentByte |= (1 << prefixBits) - 1; value -= (1 << prefixBits) - 1; bytesBuilder.addByte(currentByte); - bool done = false; + var done = false; while (!done) { currentByte = value & 0x7f; value = value >> 7; @@ -332,7 +332,7 @@ class IndexTable { /// [_maximumSize]. void _reduce() { while (_currentSize > _maximumSize) { - Header h = _dynamicTable.removeLast(); + var h = _dynamicTable.removeLast(); _currentSize -= _sizeOf(h); } } diff --git a/pkgs/http2/lib/src/hpack/huffman.dart b/pkgs/http2/lib/src/hpack/huffman.dart index d66033e9ee..5abe7273a8 100644 --- a/pkgs/http2/lib/src/hpack/huffman.dart +++ b/pkgs/http2/lib/src/hpack/huffman.dart @@ -39,13 +39,13 @@ class HuffmanDecoder { List decode(List bytes) { var buffer = BytesBuilder(); - int currentByteOffset = 0; - HuffmanTreeNode node = _root; - int currentDepth = 0; + var currentByteOffset = 0; + var node = _root; + var currentDepth = 0; while (currentByteOffset < bytes.length) { var byte = bytes[currentByteOffset]; - for (int currentBit = 7; currentBit >= 0; currentBit--) { - bool right = (byte >> currentBit) & 1 == 1; + for (var currentBit = 7; currentBit >= 0; currentBit--) { + var right = (byte >> currentBit) & 1 == 1; if (right) { node = node.right; } else { @@ -93,11 +93,11 @@ class HuffmanEncoder { List encode(List bytes) { var buffer = BytesBuilder(); - int currentByte = 0; - int currentBitOffset = 7; + var currentByte = 0; + var currentBitOffset = 7; void writeValue(int value, int numBits) { - int i = numBits - 1; + var i = numBits - 1; while (i >= 0) { if (currentBitOffset == 7 && i >= 7) { assert(currentByte == 0); @@ -120,7 +120,7 @@ class HuffmanEncoder { } } - for (int i = 0; i < bytes.length; i++) { + for (var i = 0; i < bytes.length; i++) { var byte = bytes[i]; var value = _codewords[byte]; writeValue(value.encodedBytes, value.numBits); @@ -154,14 +154,14 @@ class HuffmanTreeNode { /// Generates a huffman decoding tree. HuffmanTreeNode generateHuffmanTree(List valueEncodings) { - HuffmanTreeNode root = HuffmanTreeNode(); + var root = HuffmanTreeNode(); - for (int byteOffset = 0; byteOffset < valueEncodings.length; byteOffset++) { + for (var byteOffset = 0; byteOffset < valueEncodings.length; byteOffset++) { var entry = valueEncodings[byteOffset]; - HuffmanTreeNode current = root; - for (int bitNr = 0; bitNr < entry.numBits; bitNr++) { - bool right = + var current = root; + for (var bitNr = 0; bitNr < entry.numBits; bitNr++) { + var right = ((entry.encodedBytes >> (entry.numBits - bitNr - 1)) & 1) == 1; if (right) { diff --git a/pkgs/http2/lib/src/ping/ping_handler.dart b/pkgs/http2/lib/src/ping/ping_handler.dart index 928c2d1a9b..9be7dc154a 100644 --- a/pkgs/http2/lib/src/ping/ping_handler.dart +++ b/pkgs/http2/lib/src/ping/ping_handler.dart @@ -38,7 +38,7 @@ class PingHandler extends Object with TerminatableMixin { if (!frame.hasAckFlag) { _frameWriter.writePingFrame(frame.opaqueData, ack: true); } else { - Completer c = _remainingPings.remove(frame.opaqueData); + var c = _remainingPings.remove(frame.opaqueData); if (c != null) { c.complete(); } else { @@ -53,7 +53,7 @@ class PingHandler extends Object with TerminatableMixin { Future ping() { return ensureNotTerminatedAsync(() { - Completer c = Completer(); + var c = Completer(); var id = _nextId++; _remainingPings[id] = c; _frameWriter.writePingFrame(id); diff --git a/pkgs/http2/lib/src/settings/settings.dart b/pkgs/http2/lib/src/settings/settings.dart index 957814a234..78f6980830 100644 --- a/pkgs/http2/lib/src/settings/settings.dart +++ b/pkgs/http2/lib/src/settings/settings.dart @@ -142,8 +142,8 @@ class SettingsHandler extends Object with TerminatableMixin { 'Received an acknowledged settings frame which did not have a ' 'outstanding settings request.'); } - List settingChanges = _toBeAcknowledgedSettings.removeAt(0); - Completer completer = _toBeAcknowledgedCompleters.removeAt(0); + var settingChanges = _toBeAcknowledgedSettings.removeAt(0); + var completer = _toBeAcknowledgedCompleters.removeAt(0); _modifySettings(_acknowledgedSettings, settingChanges, false); completer.complete(); } else { @@ -209,7 +209,7 @@ class SettingsHandler extends Object with TerminatableMixin { case Setting.SETTINGS_INITIAL_WINDOW_SIZE: if (setting.value < (1 << 31)) { - int difference = setting.value - base.initialWindowSize; + var difference = setting.value - base.initialWindowSize; _onInitialWindowSizeChangeController.add(difference); base.initialWindowSize = setting.value; } else { diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index 167463a95c..47267ab786 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -226,7 +226,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { var streamIds = _openStreams.keys .where((id) => id > lastStreamId && !_isPeerInitiatedStream(id)) .toList(); - for (int id in streamIds) { + for (var id in streamIds) { var exception = StreamException( id, 'Remote end was telling us to stop. This stream was not processed ' @@ -240,8 +240,8 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { //////////////////////////////////////////////////////////////////////////// bool _isPeerInitiatedStream(int streamId) { - bool isServerStreamId = streamId.isEven; - bool isLocalStream = isServerStreamId == isServer; + var isServerStreamId = streamId.isEven; + var isLocalStream = isServerStreamId == isServer; return !isLocalStream; } @@ -261,7 +261,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { throw StateError( 'Cannot create new streams, since a wrap around would happen.'); } - int streamId = nextStreamId; + var streamId = nextStreamId; nextStreamId += 2; return _newStreamInternal(streamId); }); @@ -287,7 +287,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { 'not in "idle" state.'); } - bool sameDirection = (nextStreamId + remoteStreamId) % 2 == 0; + var sameDirection = (nextStreamId + remoteStreamId) % 2 == 0; assert(!sameDirection); lastRemoteStreamId = remoteStreamId; @@ -366,9 +366,9 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { } bool _canPush(Http2StreamImpl stream) { - bool openState = (stream.state == StreamState.Open || + var openState = (stream.state == StreamState.Open || stream.state == StreamState.HalfClosedRemote); - bool pushEnabled = this._peerSettings.enablePush; + var pushEnabled = this._peerSettings.enablePush; return openState && pushEnabled && _canCreateNewStream() && @@ -396,7 +396,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { 'new connection.'); } - Http2StreamImpl pushStream = newLocalStream(); + var pushStream = newLocalStream(); // NOTE: Since there was no real request from the client, we simulate it // by adding a synthetic `endStream = true` Data message into the incoming @@ -531,10 +531,10 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { var stream = _openStreams[frame.header.streamId]; if (stream == null) { bool frameBelongsToIdleStream() { - int streamId = frame.header.streamId; - bool isServerStreamId = frame.header.streamId.isEven; - bool isLocalStream = isServerStreamId == isServer; - bool isIdleStream = isLocalStream + var streamId = frame.header.streamId; + var isServerStreamId = frame.header.streamId.isEven; + var isLocalStream = isServerStreamId == isServer; + var isIdleStream = isLocalStream ? streamId >= nextStreamId : streamId > lastRemoteStreamId; return isIdleStream; @@ -550,7 +550,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { if (frame is HeadersFrame) { if (isServer) { - Http2StreamImpl newStream = newRemoteStream(frame.header.streamId); + var newStream = newRemoteStream(frame.header.streamId); _changeState(newStream, StreamState.Open); _handleHeadersFrame(newStream, frame); @@ -769,7 +769,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { void _closeStreamIdAbnormally(int streamId, Exception exception, {bool propagateException = false}) { - Http2StreamImpl stream = _openStreams[streamId]; + var stream = _openStreams[streamId]; if (stream != null) { _closeStreamAbnormally(stream, exception, propagateException: propagateException); @@ -815,7 +815,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { int _numberOfActiveStreams = 0; bool _canCreateNewStream() { - int limit = _peerSettings.maxConcurrentStreams; + var limit = _peerSettings.maxConcurrentStreams; return limit == null || _numberOfActiveStreams < limit; } @@ -824,7 +824,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { } void _changeState(Http2StreamImpl stream, StreamState to) { - StreamState from = stream.state; + var from = stream.state; // In checked mode we'll test that the state transition is allowed. assert((from == StreamState.Idle && to == StreamState.ReservedLocal) || @@ -876,7 +876,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { } bool _didInitiateStream(Http2StreamImpl stream) { - int id = stream.id; + var id = stream.id; return (isServer && id.isEven) || (!isServer && id.isOdd); } } diff --git a/pkgs/http2/lib/src/testing/client.dart b/pkgs/http2/lib/src/testing/client.dart index 001478d97f..6cd6e4d512 100644 --- a/pkgs/http2/lib/src/testing/client.dart +++ b/pkgs/http2/lib/src/testing/client.dart @@ -62,7 +62,7 @@ class ClientConnection { Future _handleStream(ClientTransportStream stream) { var completer = Completer(); - bool isFirst = true; + var isFirst = true; var controller = StreamController>(); var serverPushController = StreamController(sync: true); stream.incomingMessages.listen((StreamMessage msg) { @@ -89,7 +89,7 @@ class ClientConnection { pushesController.add(serverPush); - bool isFirst = true; + var isFirst = true; var dataController = StreamController>(); push.stream.incomingMessages.listen((StreamMessage msg) { if (isFirst) { @@ -126,27 +126,21 @@ class ClientConnection { /// [maxConcurrentPushes] (default is `null` meaning no limit). Future connect(Uri uri, {bool allowServerPushes = false, int maxConcurrentPushes}) async { - const List Http2AlpnProtocols = [ - 'h2-14', - 'h2-15', - 'h2-16', - 'h2-17', - 'h2' - ]; - - bool useSSL = uri.scheme == 'https'; + const Http2AlpnProtocols = ['h2-14', 'h2-15', 'h2-16', 'h2-17', 'h2']; + + var useSSL = uri.scheme == 'https'; var settings = ClientSettings( concurrentStreamLimit: maxConcurrentPushes, allowServerPushes: allowServerPushes); if (useSSL) { - SecureSocket socket = await SecureSocket.connect(uri.host, uri.port, + var socket = await SecureSocket.connect(uri.host, uri.port, supportedProtocols: Http2AlpnProtocols); if (!Http2AlpnProtocols.contains(socket.selectedProtocol)) { throw Exception('Server does not support HTTP/2.'); } return ClientConnection(socket, settings: settings); } else { - Socket socket = await Socket.connect(uri.host, uri.port); + var socket = await Socket.connect(uri.host, uri.port); return ClientConnection(socket, settings: settings); } } diff --git a/pkgs/http2/lib/src/testing/debug.dart b/pkgs/http2/lib/src/testing/debug.dart index bef37d59be..f00ae20807 100644 --- a/pkgs/http2/lib/src/testing/debug.dart +++ b/pkgs/http2/lib/src/testing/debug.dart @@ -31,7 +31,7 @@ TransportConnection debugPrintingConnection(Socket socket, Stream> decodeVerbose(Stream> inc, bool isServer, {bool verbose = true}) { - String name = isServer ? 'server' : 'client'; + var name = isServer ? 'server' : 'client'; var sc = StreamController>(); var sDebug = StreamController>(); @@ -73,7 +73,7 @@ Stream> decodeVerbose(Stream> inc, bool isServer, StreamSink> decodeOutgoingVerbose( StreamSink> sink, bool isServer, {bool verbose = true}) { - String name = isServer ? 'server' : 'client'; + var name = isServer ? 'server' : 'client'; var proxySink = StreamController>(); var copy = StreamController>(); diff --git a/pkgs/http2/manual_test/out_of_stream_ids_test.dart b/pkgs/http2/manual_test/out_of_stream_ids_test.dart index 2c44d3cdb4..a3d9e6be90 100644 --- a/pkgs/http2/manual_test/out_of_stream_ids_test.dart +++ b/pkgs/http2/manual_test/out_of_stream_ids_test.dart @@ -37,7 +37,7 @@ void main() { var headers = [Header.ascii('a', 'b')]; const kMaxStreamId = StreamHandler.MAX_STREAM_ID; - for (int i = 1; i <= kMaxStreamId; i += 2) { + for (var i = 1; i <= kMaxStreamId; i += 2) { var stream = client.makeRequest(headers, endStream: true); var messages = await stream.incomingMessages.toList(); expect(messages, hasLength(1)); diff --git a/pkgs/http2/test/client_test.dart b/pkgs/http2/test/client_test.dart index 75aa929d05..574ac4e309 100644 --- a/pkgs/http2/test/client_test.dart +++ b/pkgs/http2/test/client_test.dart @@ -152,7 +152,7 @@ void main() { expect(finFrame.hasEndStreamFlag, true); // Write a data frame for a non-existent stream. - int invalidStreamId = headers.header.streamId + 2; + var invalidStreamId = headers.header.streamId + 2; serverWriter.writeDataFrame(invalidStreamId, [42]); // Make sure the client sends a [RstStreamFrame] frame. @@ -203,7 +203,7 @@ void main() { DataFrame finFrame = await nextFrame(); expect(finFrame.hasEndStreamFlag, true); - int streamId = headers.header.streamId; + var streamId = headers.header.streamId; // Write a data frame for a non-existent stream. serverWriter.writeDataFrame(streamId, [42], endStream: true); @@ -270,7 +270,7 @@ void main() { DataFrame finFrame = await nextFrame(); expect(finFrame.hasEndStreamFlag, true); - int streamId = headers.header.streamId; + var streamId = headers.header.streamId; // Write a data frame. serverWriter.writeDataFrame(streamId, [42]); @@ -345,7 +345,7 @@ void main() { HeadersFrame headers = await nextFrame(); - int streamId = headers.header.streamId; + var streamId = headers.header.streamId; // Write a data frame. serverWriter.writeDataFrame(streamId, [42]); @@ -417,14 +417,14 @@ void main() { DataFrame finFrame = await nextFrame(); expect(finFrame.hasEndStreamFlag, true); - int streamId = headers.header.streamId; + var streamId = headers.header.streamId; // Write response. serverWriter.writeHeadersFrame(streamId, [Header.ascii('a', 'b')], endStream: true); // Push stream to the (non existing) one. - int pushStreamId = 2; + var pushStreamId = 2; serverWriter.writePushPromiseFrame( streamId, pushStreamId, [Header.ascii('a', 'b')]); @@ -467,13 +467,13 @@ void main() { handshakeCompleter.complete(); HeadersFrame headers = await nextFrame(); - int streamId = headers.header.streamId; + var streamId = headers.header.streamId; // Write response. serverWriter.writeDataFrame(streamId, [], endStream: true); // Push stream onto the existing (but half-closed) one. - int pushStreamId = 2; + var pushStreamId = 2; serverWriter.writePushPromiseFrame( streamId, pushStreamId, [Header.ascii('a', 'b')]); @@ -518,11 +518,11 @@ void main() { handshakeCompleter.complete(); HeadersFrame headers = await nextFrame(); - int streamId = headers.header.streamId; + var streamId = headers.header.streamId; // Write more than [kFlowControlWindowSize] bytes. - final int kFlowControlWindowSize = Window().size; - int sentBytes = 0; + final kFlowControlWindowSize = Window().size; + var sentBytes = 0; final bytes = Uint8List(1024); while (sentBytes <= kFlowControlWindowSize) { serverWriter.writeDataFrame(streamId, bytes); @@ -705,7 +705,7 @@ class ClientStreams { Stream> get readB => writeB.stream; StreamIterator get serverConnectionFrameReader { - ActiveSettings localSettings = ActiveSettings(); + var localSettings = ActiveSettings(); var streamAfterConnectionPreface = readConnectionPreface(readA); return StreamIterator( FrameReader(streamAfterConnectionPreface, localSettings) @@ -714,7 +714,7 @@ class ClientStreams { FrameWriter get serverConnectionFrameWriter { var encoder = HPackEncoder(); - ActiveSettings peerSettings = ActiveSettings(); + var peerSettings = ActiveSettings(); return FrameWriter(encoder, writeB, peerSettings); } diff --git a/pkgs/http2/test/client_websites_test.dart b/pkgs/http2/test/client_websites_test.dart index ac404555e4..6ea6e450a2 100644 --- a/pkgs/http2/test/client_websites_test.dart +++ b/pkgs/http2/test/client_websites_test.dart @@ -15,12 +15,12 @@ import 'package:test/test.dart'; void main() async { test('google', () async { var uri = Uri.parse('https://www.google.com/'); - ClientConnection connection = await connect(uri); - Response response = await connection.makeRequest(Request('GET', uri)); + var connection = await connect(uri); + var response = await connection.makeRequest(Request('GET', uri)); dumpHeaders(uri, response.headers); final utf8Decoder = Utf8Decoder(allowMalformed: true); - String body = await response.stream.transform(utf8Decoder).join(''); + var body = await response.stream.transform(utf8Decoder).join(''); unawaited(connection.close()); body = body.toLowerCase(); @@ -30,11 +30,11 @@ void main() async { test('twitter', () async { var uri = Uri.parse('https://twitter.com/'); - ClientConnection connection = await connect(uri); - Response response = await connection.makeRequest(Request('GET', uri)); + var connection = await connect(uri); + var response = await connection.makeRequest(Request('GET', uri)); dumpHeaders(uri, response.headers); - String body = await readBody(response); + var body = await readBody(response); unawaited(connection.close()); expect(body, contains('')); @@ -45,9 +45,9 @@ void main() async { test('server push enabled', () async { var uri = Uri.parse('https://nghttp2.org/'); - ClientConnection connection = await connect(uri, allowServerPushes: true); + var connection = await connect(uri, allowServerPushes: true); var request = Request('GET', uri); - Response response = await connection.makeRequest(request); + var response = await connection.makeRequest(request); dumpHeaders(uri, response.headers); Future> accumulatePushes() async { @@ -86,10 +86,9 @@ void main() async { test('server push disabled', () async { var uri = Uri.parse('https://nghttp2.org/'); - ClientConnection connection = - await connect(uri, allowServerPushes: false); + var connection = await connect(uri, allowServerPushes: false); var request = Request('GET', uri); - Response response = await connection.makeRequest(request); + var response = await connection.makeRequest(request); dumpHeaders(uri, response.headers); Future> accumulatePushes() async { diff --git a/pkgs/http2/test/multiprotocol_server_test.dart b/pkgs/http2/test/multiprotocol_server_test.dart index 22fb8de4b7..4d57eec6c6 100644 --- a/pkgs/http2/test/multiprotocol_server_test.dart +++ b/pkgs/http2/test/multiprotocol_server_test.dart @@ -14,7 +14,7 @@ import 'package:http2/transport.dart'; import 'package:http2/multiprotocol_server.dart'; void main() { - SecurityContext context = SecurityContext() + var context = SecurityContext() ..useCertificateChain('test/certificates/server_chain.pem') ..usePrivateKey('test/certificates/server_key.pem', password: 'dartdart'); @@ -23,7 +23,7 @@ void main() { const Count = 2; var server = await MultiProtocolHttpServer.bind('localhost', 0, context); - int requestNr = 0; + var requestNr = 0; server.startServing( expectAsync1((HttpRequest request) async { await handleHttp11Request(request, requestNr++); @@ -35,7 +35,7 @@ void main() { var client = HttpClient(); client.badCertificateCallback = (_, __, ___) => true; - for (int i = 0; i < Count; i++) { + for (var i = 0; i < Count; i++) { await makeHttp11Request(server, client, i); } }); @@ -44,7 +44,7 @@ void main() { const Count = 2; var server = await MultiProtocolHttpServer.bind('localhost', 0, context); - int requestNr = 0; + var requestNr = 0; server.startServing( expectAsync1((HttpRequest request) {}, count: 0), expectAsync1((ServerTransportStream stream) async { @@ -58,7 +58,7 @@ void main() { onBadCertificate: (_) => true, supportedProtocols: ['http/1.1', 'h2']); var connection = ClientTransportConnection.viaSocket(socket); - for (int i = 0; i < Count; i++) { + for (var i = 0; i < Count; i++) { await makeHttp2Request(server, connection, i); } await connection.finish(); diff --git a/pkgs/http2/test/server_test.dart b/pkgs/http2/test/server_test.dart index b0de9333c6..566bb0149a 100644 --- a/pkgs/http2/test/server_test.dart +++ b/pkgs/http2/test/server_test.dart @@ -219,13 +219,13 @@ class ClientErrorStreams { Stream> get readB => writeB.stream; StreamIterator get clientConnectionFrameReader { - ActiveSettings localSettings = ActiveSettings(); + var localSettings = ActiveSettings(); return StreamIterator(FrameReader(readA, localSettings).startDecoding()); } FrameWriter get clientConnectionFrameWriter { var encoder = HPackEncoder(); - ActiveSettings peerSettings = ActiveSettings(); + var peerSettings = ActiveSettings(); writeB.add(CONNECTION_PREFACE); return FrameWriter(encoder, writeB, peerSettings); } diff --git a/pkgs/http2/test/src/connection_preface_test.dart b/pkgs/http2/test/src/connection_preface_test.dart index 18e4418b1b..662f75b61b 100644 --- a/pkgs/http2/test/src/connection_preface_test.dart +++ b/pkgs/http2/test/src/connection_preface_test.dart @@ -17,14 +17,14 @@ void main() { final frameBytes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; final data = List.from(CONNECTION_PREFACE)..addAll(frameBytes); - for (int size = 1; size <= data.length; size++) { + for (var size = 1; size <= data.length; size++) { var c = StreamController>(); var resultF = readConnectionPreface(c.stream).fold([], (b, d) => b..addAll(d)); - for (int i = 0; i < (size - 1 + data.length) ~/ size; i++) { - int from = size * i; - int to = min(size * (i + 1), data.length); + for (var i = 0; i < (size - 1 + data.length) ~/ size; i++) { + var from = size * i; + var to = min(size * (i + 1), data.length); c.add(data.sublist(from, to)); } @@ -39,7 +39,7 @@ void main() { var resultF = readConnectionPreface(c.stream).fold([], (b, d) => b..addAll(d)); - for (int i = 0; i < CONNECTION_PREFACE.length - 1; i++) { + for (var i = 0; i < CONNECTION_PREFACE.length - 1; i++) { c.add([CONNECTION_PREFACE[i]]); } unawaited(c.close()); @@ -54,7 +54,7 @@ void main() { var resultF = readConnectionPreface(c.stream).fold([], (b, d) => b..addAll(d)); - for (int i = 0; i < CONNECTION_PREFACE.length; i++) { + for (var i = 0; i < CONNECTION_PREFACE.length; i++) { c.add([0xff]); } unawaited(c.close()); diff --git a/pkgs/http2/test/src/flowcontrol/window_handler_test.dart b/pkgs/http2/test/src/flowcontrol/window_handler_test.dart index 565c7a266d..705d32028e 100644 --- a/pkgs/http2/test/src/flowcontrol/window_handler_test.dart +++ b/pkgs/http2/test/src/flowcontrol/window_handler_test.dart @@ -59,7 +59,7 @@ void main() { test('outgoing-connection-window-handler', () { var window = Window(); - int initialSize = window.size; + var initialSize = window.size; var handler = OutgoingConnectionWindowHandler(window); testAbstractOutgoingWindowHandler(handler, window, initialSize); @@ -67,7 +67,7 @@ void main() { test('outgoing-stream-window-handler', () { var window = Window(); - int initialSize = window.size; + var initialSize = window.size; var handler = OutgoingStreamWindowHandler(window); testAbstractOutgoingWindowHandler(handler, window, initialSize); @@ -104,7 +104,7 @@ void main() { dynamic fw = FrameWriterMock(); var window = Window(); - int initialSize = window.size; + var initialSize = window.size; var handler = IncomingWindowHandler.stream(fw, window, STREAM_ID); expect(handler.localWindowSize, initialSize); diff --git a/pkgs/http2/test/src/frames/frame_defragmenter_test.dart b/pkgs/http2/test/src/frames/frame_defragmenter_test.dart index 672d9ae343..899154a28d 100644 --- a/pkgs/http2/test/src/frames/frame_defragmenter_test.dart +++ b/pkgs/http2/test/src/frames/frame_defragmenter_test.dart @@ -18,7 +18,7 @@ void main() { HeadersFrame headersFrame(List data, {bool fragmented = false, int streamId = 1}) { - int flags = fragmented ? 0 : HeadersFrame.FLAG_END_HEADERS; + var flags = fragmented ? 0 : HeadersFrame.FLAG_END_HEADERS; var header = FrameHeader(data.length, FrameType.HEADERS, flags, streamId); return HeadersFrame(header, 0, false, null, null, data); @@ -26,7 +26,7 @@ void main() { PushPromiseFrame pushPromiseFrame(List data, {bool fragmented = false, int streamId = 1}) { - int flags = fragmented ? 0 : HeadersFrame.FLAG_END_HEADERS; + var flags = fragmented ? 0 : HeadersFrame.FLAG_END_HEADERS; var header = FrameHeader(data.length, FrameType.PUSH_PROMISE, flags, streamId); return PushPromiseFrame(header, 0, 44, data); @@ -34,7 +34,7 @@ void main() { ContinuationFrame continuationFrame(List data, {bool fragmented = false, int streamId = 1}) { - int flags = fragmented ? 0 : ContinuationFrame.FLAG_END_HEADERS; + var flags = fragmented ? 0 : ContinuationFrame.FLAG_END_HEADERS; var header = FrameHeader(data.length, FrameType.CONTINUATION, flags, streamId); return ContinuationFrame(header, data); diff --git a/pkgs/http2/test/src/frames/frame_reader_test.dart b/pkgs/http2/test/src/frames/frame_reader_test.dart index 8a4fa460b5..993180f0a8 100644 --- a/pkgs/http2/test/src/frames/frame_reader_test.dart +++ b/pkgs/http2/test/src/frames/frame_reader_test.dart @@ -12,7 +12,7 @@ import 'package:http2/src/settings/settings.dart'; void main() { group('frames', () { group('frame-reader', () { - final int maxFrameSize = ActiveSettings().maxFrameSize; + final maxFrameSize = ActiveSettings().maxFrameSize; Stream dataFrame(List body) { var settings = ActiveSettings(); diff --git a/pkgs/http2/test/src/hpack/hpack_test.dart b/pkgs/http2/test/src/hpack/hpack_test.dart index a986cac976..e9e606581d 100644 --- a/pkgs/http2/test/src/hpack/hpack_test.dart +++ b/pkgs/http2/test/src/hpack/hpack_test.dart @@ -752,7 +752,7 @@ class TestHelper { currentByte |= (1 << prefixBits) - 1; value -= (1 << prefixBits) - 1; buffer.add(currentByte); - bool done = false; + var done = false; while (!done) { currentByte = value & 0x7f; value = value >> 7; @@ -789,9 +789,9 @@ class TestHelper { } static void expectHeader(Header h, int len, int nameChar, int valueChar) { - List data = h.value; + var data = h.value; expect(data.length, len - 32 - 1); - for (int i = 0; i < data.length; i++) { + for (var i = 0; i < data.length; i++) { expect(data[i], valueChar); } } @@ -819,7 +819,7 @@ class _HeaderMatcher extends Matcher { if (a == null && b == null) return true; if (a == null && b != null) return false; if (a.length != b.length) return false; - for (int i = 0; i < a.length; i++) { + for (var i = 0; i < a.length; i++) { if (a[i] != b[i]) return false; } return true; diff --git a/pkgs/http2/test/src/hpack/huffman_table_test.dart b/pkgs/http2/test/src/hpack/huffman_table_test.dart index a28c451862..a2e9bd3037 100644 --- a/pkgs/http2/test/src/hpack/huffman_table_test.dart +++ b/pkgs/http2/test/src/hpack/huffman_table_test.dart @@ -14,7 +14,7 @@ void main() { final decode = http2HuffmanCodec.decode; final encode = http2HuffmanCodec.encode; - Map> hpackSpecTestCases = { + var hpackSpecTestCases = { 'www.example.com': [ 0xf1, 0xe3, diff --git a/pkgs/http2/test/src/ping/ping_handler_test.dart b/pkgs/http2/test/src/ping/ping_handler_test.dart index 658681e022..e5e0de4823 100644 --- a/pkgs/http2/test/src/ping/ping_handler_test.dart +++ b/pkgs/http2/test/src/ping/ping_handler_test.dart @@ -2,8 +2,6 @@ // 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:mockito/mockito.dart'; import 'package:test/test.dart'; @@ -19,8 +17,8 @@ void main() { dynamic writer = FrameWriterMock(); var pingHandler = PingHandler(writer); - Future p1 = pingHandler.ping(); - Future p2 = pingHandler.ping(); + var p1 = pingHandler.ping(); + var p2 = pingHandler.ping(); verifyInOrder([ writer.writePingFrame(1), @@ -57,7 +55,7 @@ void main() { dynamic writer = FrameWriterMock(); var pingHandler = PingHandler(writer); - Future future = pingHandler.ping(); + var future = pingHandler.ping(); verify(writer.writePingFrame(1)).called(1); var header = FrameHeader(8, FrameType.PING, PingFrame.FLAG_ACK, 0); diff --git a/pkgs/http2/test/src/settings/settings_handler_test.dart b/pkgs/http2/test/src/settings/settings_handler_test.dart index 27273b42cc..d24ba2ed4f 100644 --- a/pkgs/http2/test/src/settings/settings_handler_test.dart +++ b/pkgs/http2/test/src/settings/settings_handler_test.dart @@ -2,8 +2,6 @@ // 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:mockito/mockito.dart'; import 'package:test/test.dart'; @@ -25,7 +23,7 @@ void main() { HPackEncoder(), writer, ActiveSettings(), ActiveSettings()); // Start changing settings. - Future changed = sh.changeSettings(pushSettings); + var changed = sh.changeSettings(pushSettings); verify(writer.writeSettingsFrame(pushSettings)).called(1); verifyNoMoreInteractions(writer); diff --git a/pkgs/http2/test/src/streams/helper.dart b/pkgs/http2/test/src/streams/helper.dart index ca620bed71..12f31bccac 100644 --- a/pkgs/http2/test/src/streams/helper.dart +++ b/pkgs/http2/test/src/streams/helper.dart @@ -14,7 +14,7 @@ import 'package:http2/src/settings/settings.dart'; void expectHeadersEqual(List
headers, List
expectedHeaders) { expect(headers, hasLength(expectedHeaders.length)); - for (int i = 0; i < expectedHeaders.length; i++) { + for (var i = 0; i < expectedHeaders.length; i++) { expect(headers[i].name, expectedHeaders[i].name); expect(headers[i].value, expectedHeaders[i].value); } diff --git a/pkgs/http2/test/src/streams/simple_flow_test.dart b/pkgs/http2/test/src/streams/simple_flow_test.dart index fba375576a..be4f2fe02e 100644 --- a/pkgs/http2/test/src/streams/simple_flow_test.dart +++ b/pkgs/http2/test/src/streams/simple_flow_test.dart @@ -14,7 +14,7 @@ import 'helper.dart'; void main() { group('streams', () { group('flowcontrol', () { - const int numOfOneKB = 1000; + const numOfOneKB = 1000; var expectedHeaders = [Header.ascii('key', 'value')]; var allBytes = List.generate(numOfOneKB * 1024, (i) => i % 256); @@ -30,11 +30,11 @@ void main() { }); } - Completer serverReceivedAllBytes = Completer(); + var serverReceivedAllBytes = Completer(); void Function(StreamMessage) messageTestFun(String type) { - bool expectHeader = true; - int numBytesReceived = 0; + var expectHeader = true; + var numBytesReceived = 0; return (StreamMessage msg) { if (expectHeader) { expectHeader = false; @@ -63,9 +63,9 @@ void main() { } void sendData(TransportStream cStream) { - for (int i = 0; i < (allBytes.length + 1023) ~/ 1024; i++) { - int end = 1024 * (i + 1); - bool isLast = end > allBytes.length; + for (var i = 0; i < (allBytes.length + 1023) ~/ 1024; i++) { + var end = 1024 * (i + 1); + var isLast = end > allBytes.length; if (isLast) { end = allBytes.length; } diff --git a/pkgs/http2/test/src/streams/simple_push_test.dart b/pkgs/http2/test/src/streams/simple_push_test.dart index 3b34fe5abf..122ee9179f 100644 --- a/pkgs/http2/test/src/streams/simple_push_test.dart +++ b/pkgs/http2/test/src/streams/simple_push_test.dart @@ -16,7 +16,7 @@ import 'helper.dart'; void main() { group('streams', () { group('server-push', () { - const int numOfOneKB = 1000; + const numOfOneKB = 1000; var expectedHeaders = [Header.ascii('key', 'value')]; var allBytes = List.generate(numOfOneKB * 1024, (i) => i % 256); @@ -24,7 +24,7 @@ void main() { void testHeaders(List
headers) { expect(headers.length, expectedHeaders.length); - for (int i = 0; i < headers.length; i++) { + for (var i = 0; i < headers.length; i++) { expect(headers[i].name, expectedHeaders[i].name); expect(headers[i].value, expectedHeaders[i].value); } @@ -37,7 +37,7 @@ void main() { }); } - Completer serverReceivedAllBytes = Completer(); + var serverReceivedAllBytes = Completer(); Future readData(StreamIterator iterator) async { var all = []; @@ -72,8 +72,7 @@ void main() { expect(await serverReceivedAllBytes.future, completes); })); - ClientTransportStream cStream = - client.makeRequest(expectedHeaders, endStream: true); + var cStream = client.makeRequest(expectedHeaders, endStream: true); cStream.incomingMessages .listen(headersTestFun(), onDone: expectAsync0(() {})); cStream.peerPushes @@ -81,11 +80,11 @@ void main() { testHeaders(push.requestHeaders); var iterator = StreamIterator(push.stream.incomingMessages); - bool hasNext = await iterator.moveNext(); + var hasNext = await iterator.moveNext(); expect(hasNext, isTrue); testHeaders((iterator.current as HeadersStreamMessage).headers); - String msg = await readData(iterator); + var msg = await readData(iterator); expect(msg, 'pushing "hello world" :)'); })); }, settings: ClientSettings(allowServerPushes: true)); diff --git a/pkgs/http2/test/src/streams/streams_test.dart b/pkgs/http2/test/src/streams/streams_test.dart index 560e20445f..a5132b1c5d 100644 --- a/pkgs/http2/test/src/streams/streams_test.dart +++ b/pkgs/http2/test/src/streams/streams_test.dart @@ -67,7 +67,7 @@ void main() { server.incomingStreams .listen(expectAsync1((TransportStream sStream) async { - bool isFirst = true; + var isFirst = true; var receivedChunks = []; sStream.incomingMessages.listen( expectAsync1((StreamMessage msg) { @@ -175,7 +175,7 @@ void main() { TransportStream cStream = client.makeRequest(expectedHeaders); unawaited(cStream.outgoingMessages.close()); - int i = 0; + var i = 0; cStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { expect(msg is DataStreamMessage, isTrue); expect((msg as DataStreamMessage).bytes, chunks[i++]); @@ -190,7 +190,7 @@ void main() { var chunk = [1]; server.incomingStreams.listen(expectAsync1((TransportStream sStream) async { - bool isFirst = true; + var isFirst = true; var receivedChunk; sStream.incomingMessages.listen( expectAsync1((StreamMessage msg) { @@ -219,7 +219,7 @@ void main() { TransportStream cStream = client.makeRequest(expectedHeaders); cStream.sendData(chunk, endStream: true); - bool isFirst = true; + var isFirst = true; cStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { if (isFirst) { expect(msg, const TypeMatcher()); diff --git a/pkgs/http2/test/transport_test.dart b/pkgs/http2/test/transport_test.dart index 00f743df5d..12ff55fa00 100644 --- a/pkgs/http2/test/transport_test.dart +++ b/pkgs/http2/test/transport_test.dart @@ -56,7 +56,7 @@ void main() { }))); }); - const int concurrentStreamLimit = 5; + const concurrentStreamLimit = 5; transportTest('exhaust-concurrent-stream-limit', (ClientTransportConnection client, ServerTransportConnection server) async { @@ -70,7 +70,7 @@ void main() { await Future.value(); final streams = []; - for (int i = 0; i < concurrentStreamLimit; ++i) { + for (var i = 0; i < concurrentStreamLimit; ++i) { expect(client.isOpen, true); streams.add(client.makeRequest([Header.ascii('a', 'b')])); } @@ -117,17 +117,17 @@ void main() { }); // By default, the stream concurrency level is set to this limit. - const int kDefaultStreamLimit = 100; + const kDefaultStreamLimit = 100; transportTest('enabled-push-100', (ClientTransportConnection client, ServerTransportConnection server) async { // To ensure the limit is kept up-to-date with closing/opening streams, we // retry this. - const int kRepetitions = 20; + const kRepetitions = 20; Future serverFun() async { await for (ServerTransportStream stream in server.incomingStreams) { var pushes = []; - for (int i = 0; i < kDefaultStreamLimit; i++) { + for (var i = 0; i < kDefaultStreamLimit; i++) { expect(stream.canPush, true); pushes.add(stream.push([Header.ascii('a', 'b')])); } @@ -152,12 +152,12 @@ void main() { } Future clientFun() async { - for (int i = 0; i < kRepetitions; i++) { + for (var i = 0; i < kRepetitions; i++) { var stream = client.makeRequest([Header.ascii('a', 'b')], endStream: true); Future expectPeerPushes() async { - int numberOfPushes = 0; + var numberOfPushes = 0; await for (TransportStreamPush pushedStream in stream.peerPushes) { numberOfPushes++; var messages = @@ -335,8 +335,8 @@ void main() { transportTest('idle-handler', (ClientTransportConnection client, ServerTransportConnection server) async { Future serverFun() async { - int activeCount = 0; - int idleCount = 0; + var activeCount = 0; + var idleCount = 0; server.onActiveStateChanged = expectAsync1((active) { if (active) { activeCount++; @@ -356,8 +356,8 @@ void main() { } Future clientFun() async { - int activeCount = 0; - int idleCount = 0; + var activeCount = 0; + var idleCount = 0; client.onActiveStateChanged = expectAsync1((active) { if (active) { activeCount++; @@ -395,8 +395,8 @@ void main() { }); group('flow-control', () { - const int kChunkSize = 1024; - const int kNumberOfMessages = 1000; + const kChunkSize = 1024; + const kNumberOfMessages = 1000; final headers = [Header.ascii('a', 'b')]; Future testWindowSize( @@ -406,20 +406,20 @@ void main() { expect(expectedStreamFlowcontrolWindow, lessThan(kChunkSize * kNumberOfMessages)); - int serverSentBytes = 0; - Completer flowcontrolWindowFull = Completer(); + var serverSentBytes = 0; + var flowcontrolWindowFull = Completer(); Future serverFun() async { await for (ServerTransportStream stream in server.incomingStreams) { stream.sendHeaders([Header.ascii('x', 'y')]); - int messageNr = 0; + var messageNr = 0; StreamController controller; void addData() { if (!controller.isPaused) { if (messageNr < kNumberOfMessages) { var messageBytes = Uint8List(kChunkSize); - for (int j = 0; j < messageBytes.length; j++) { + for (var j = 0; j < messageBytes.length; j++) { messageBytes[j] = (messageNr + j) % 256; } controller.add(DataStreamMessage(messageBytes)); @@ -462,8 +462,8 @@ void main() { Future clientFun() async { var stream = client.makeRequest(headers, endStream: true); - bool gotHeadersFrame = false; - int byteNr = 0; + var gotHeadersFrame = false; + var byteNr = 0; var sub = stream.incomingMessages.listen((message) { if (!gotHeadersFrame) { From cb048bb1542664169e2f283709e1e5d46eaad135 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 20 Feb 2020 12:25:18 -0800 Subject: [PATCH 099/172] Enable and fix lint prefer_is_empty (dart-lang/http2#60) --- pkgs/http2/analysis_options.yaml | 1 + pkgs/http2/lib/src/connection.dart | 2 +- pkgs/http2/lib/src/connection_preface.dart | 2 +- .../http2/lib/src/flowcontrol/connection_queues.dart | 8 ++++---- pkgs/http2/lib/src/flowcontrol/stream_queues.dart | 12 ++++++------ 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/pkgs/http2/analysis_options.yaml b/pkgs/http2/analysis_options.yaml index 0518e4a013..df9176db66 100644 --- a/pkgs/http2/analysis_options.yaml +++ b/pkgs/http2/analysis_options.yaml @@ -29,6 +29,7 @@ linter: - prefer_equal_for_default_values - prefer_final_fields - prefer_generic_function_type_aliases + - prefer_is_empty - prefer_is_not_empty - prefer_single_quotes - slash_for_doc_comments diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index 81ad2a770f..8ce911e488 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -66,7 +66,7 @@ class ConnectionState { void add(bool condition, String flag) { if (condition) { - if (message.length == 0) { + if (message.isEmpty) { message = flag; } else { message = '$message/$flag'; diff --git a/pkgs/http2/lib/src/connection_preface.dart b/pkgs/http2/lib/src/connection_preface.dart index 8625ad39ff..b3f82f9563 100644 --- a/pkgs/http2/lib/src/connection_preface.dart +++ b/pkgs/http2/lib/src/connection_preface.dart @@ -92,7 +92,7 @@ Stream> readConnectionPreface(Stream> incoming) { } data = part2; } - if (data.length > 0) { + if (data.isNotEmpty) { result.add(data); } } diff --git a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart index 12a43ded26..8cf7a8cea0 100644 --- a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart @@ -74,7 +74,7 @@ class ConnectionMessageQueueOut extends Object @override void onCheckForClose() { - if (isClosing && _messages.length == 0) { + if (isClosing && _messages.isEmpty) { closeWithValue(); } } @@ -88,7 +88,7 @@ class ConnectionMessageQueueOut extends Object // * the next message is a non-flow control message (e.g. headers) // * the connection window is positive - if (_messages.length > 0 && + if (_messages.isNotEmpty && !_frameWriter.bufferIndicator.wouldBuffer && (!_connectionWindow.positiveWindow.wouldBuffer || _messages.first is! DataMessage)) { @@ -96,7 +96,7 @@ class ConnectionMessageQueueOut extends Object // If we have more messages and we can send them, we'll run them // using `Timer.run()` to let other things get in-between. - if (_messages.length > 0 && + if (_messages.isNotEmpty && !_frameWriter.bufferIndicator.wouldBuffer && (!_connectionWindow.positiveWindow.wouldBuffer || _messages.first is! DataMessage)) { @@ -312,7 +312,7 @@ class ConnectionMessageQueueIn extends Object void _tryDispatch( int streamId, StreamMessageQueueIn mq, Queue pendingMessages) { var bytesDeliveredToStream = 0; - while (!mq.bufferIndicator.wouldBuffer && pendingMessages.length > 0) { + while (!mq.bufferIndicator.wouldBuffer && pendingMessages.isNotEmpty) { _count--; var message = pendingMessages.removeFirst(); diff --git a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart index dec04f9398..5ee5923528 100644 --- a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart @@ -79,7 +79,7 @@ class StreamMessageQueueOut extends Object _messages.addLast(message); _trySendData(); - if (_messages.length > 0) { + if (_messages.isNotEmpty) { bufferIndicator.markBuffered(); } } @@ -99,7 +99,7 @@ class StreamMessageQueueOut extends Object void _trySendData() { var queueLenBefore = _messages.length; - while (_messages.length > 0) { + while (_messages.isNotEmpty) { var message = _messages.first; if (message is HeadersMessage) { @@ -107,7 +107,7 @@ class StreamMessageQueueOut extends Object connectionMessageQueue.enqueueMessage(message); } else if (message is DataMessage) { var bytesAvailable = streamWindow.peerWindowSize; - if (bytesAvailable > 0 || message.bytes.length == 0) { + if (bytesAvailable > 0 || message.bytes.isEmpty) { _messages.removeFirst(); // Do we need to fragment? @@ -271,7 +271,7 @@ class StreamMessageQueueIn extends Object _incomingMessagesC.add(HeadersStreamMessage(message.headers, endStream: message.endStream)); } else if (message is DataMessage) { - if (message.bytes.length > 0) { + if (message.bytes.isNotEmpty) { _incomingMessagesC.add( DataStreamMessage(message.bytes, endStream: message.endStream)); } @@ -302,7 +302,7 @@ class StreamMessageQueueIn extends Object _incomingMessagesC.add(HeadersStreamMessage(message.headers, endStream: message.endStream)); } else if (message is DataMessage) { - if (message.bytes.length > 0) { + if (message.bytes.isNotEmpty) { _incomingMessagesC.add(DataStreamMessage(message.bytes, endStream: message.endStream)); windowHandler.dataProcessed(message.bytes.length); @@ -325,7 +325,7 @@ class StreamMessageQueueIn extends Object } void _tryUpdateBufferIndicator() { - if (_incomingMessagesC.isPaused || _pendingMessages.length > 0) { + if (_incomingMessagesC.isPaused || _pendingMessages.isNotEmpty) { bufferIndicator.markBuffered(); } else if (bufferIndicator.wouldBuffer && !_incomingMessagesC.isPaused) { bufferIndicator.markUnBuffered(); From a4fcc1b5dc40805aaf2356ebe09cedec05eb0164 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 20 Feb 2020 12:25:34 -0800 Subject: [PATCH 100/172] Remove unnecessary library statements (dart-lang/http2#59) These statements are no longer useful unless there is a library level doc comment or part files referencing the library name. Modern Dart style does not use them. --- pkgs/http2/experimental/server.dart | 2 -- pkgs/http2/lib/multiprotocol_server.dart | 2 -- pkgs/http2/lib/src/artificial_server_socket.dart | 2 -- pkgs/http2/lib/src/async_utils/async_utils.dart | 2 -- pkgs/http2/lib/src/byte_utils.dart | 2 -- pkgs/http2/lib/src/connection.dart | 2 -- pkgs/http2/lib/src/connection_preface.dart | 2 -- pkgs/http2/lib/src/error_handler.dart | 2 -- pkgs/http2/lib/src/flowcontrol/connection_queues.dart | 1 - pkgs/http2/lib/src/flowcontrol/queue_messages.dart | 2 -- pkgs/http2/lib/src/flowcontrol/stream_queues.dart | 2 -- pkgs/http2/lib/src/flowcontrol/window.dart | 2 -- pkgs/http2/lib/src/flowcontrol/window_handler.dart | 2 -- pkgs/http2/lib/src/frames/frame_defragmenter.dart | 2 -- pkgs/http2/lib/src/hpack/huffman.dart | 2 -- pkgs/http2/lib/src/hpack/huffman_table.dart | 2 -- pkgs/http2/lib/src/ping/ping_handler.dart | 2 -- pkgs/http2/lib/src/settings/settings.dart | 2 -- pkgs/http2/lib/src/streams/stream_handler.dart | 2 -- pkgs/http2/lib/src/sync_errors.dart | 2 -- pkgs/http2/lib/src/testing/client.dart | 2 -- pkgs/http2/lib/src/testing/debug.dart | 2 -- pkgs/http2/test/client_test.dart | 2 -- pkgs/http2/test/client_websites_test.dart | 2 -- pkgs/http2/test/multiprotocol_server_test.dart | 2 -- pkgs/http2/test/server_test.dart | 2 -- pkgs/http2/test/src/connection_preface_test.dart | 2 -- pkgs/http2/test/src/error_matchers.dart | 2 -- pkgs/http2/test/src/streams/helper.dart | 2 -- pkgs/http2/test/src/streams/simple_flow_test.dart | 2 -- pkgs/http2/test/src/streams/simple_push_test.dart | 2 -- pkgs/http2/test/src/streams/streams_test.dart | 2 -- 32 files changed, 63 deletions(-) diff --git a/pkgs/http2/experimental/server.dart b/pkgs/http2/experimental/server.dart index a5ca8c5767..fff74894cf 100644 --- a/pkgs/http2/experimental/server.dart +++ b/pkgs/http2/experimental/server.dart @@ -2,8 +2,6 @@ // 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. -library http2.experimental.server; - import 'dart:async'; import 'dart:convert'; import 'dart:io'; diff --git a/pkgs/http2/lib/multiprotocol_server.dart b/pkgs/http2/lib/multiprotocol_server.dart index ba097e83b9..a521ddca00 100644 --- a/pkgs/http2/lib/multiprotocol_server.dart +++ b/pkgs/http2/lib/multiprotocol_server.dart @@ -2,8 +2,6 @@ // 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. -library http2.multiprotocol_server; - import 'dart:async'; import 'dart:io'; diff --git a/pkgs/http2/lib/src/artificial_server_socket.dart b/pkgs/http2/lib/src/artificial_server_socket.dart index ce7450902f..1a486fe062 100644 --- a/pkgs/http2/lib/src/artificial_server_socket.dart +++ b/pkgs/http2/lib/src/artificial_server_socket.dart @@ -2,8 +2,6 @@ // 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. -library http2.artificial_server_socket; - import 'dart:async'; import 'dart:io'; diff --git a/pkgs/http2/lib/src/async_utils/async_utils.dart b/pkgs/http2/lib/src/async_utils/async_utils.dart index 234d5e1a3e..d37cdc1083 100644 --- a/pkgs/http2/lib/src/async_utils/async_utils.dart +++ b/pkgs/http2/lib/src/async_utils/async_utils.dart @@ -2,8 +2,6 @@ // 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. -library http2.src.async_utils.async_utils; - import 'dart:async'; import 'dart:io'; diff --git a/pkgs/http2/lib/src/byte_utils.dart b/pkgs/http2/lib/src/byte_utils.dart index 411e78e22b..671e339ea5 100644 --- a/pkgs/http2/lib/src/byte_utils.dart +++ b/pkgs/http2/lib/src/byte_utils.dart @@ -2,8 +2,6 @@ // 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. -library http2.src.byte_utils; - import 'dart:typed_data'; List viewOrSublist(List data, int offset, int length) { diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index 8ce911e488..43dd741f5c 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -2,8 +2,6 @@ // 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. -library http2.src.conn; - import 'dart:async'; import 'dart:convert' show utf8; diff --git a/pkgs/http2/lib/src/connection_preface.dart b/pkgs/http2/lib/src/connection_preface.dart index b3f82f9563..5995ba8e9e 100644 --- a/pkgs/http2/lib/src/connection_preface.dart +++ b/pkgs/http2/lib/src/connection_preface.dart @@ -2,8 +2,6 @@ // 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. -library http2.src.connection_preface; - import 'dart:async'; import 'dart:math'; diff --git a/pkgs/http2/lib/src/error_handler.dart b/pkgs/http2/lib/src/error_handler.dart index 2097257519..37ecc26f49 100644 --- a/pkgs/http2/lib/src/error_handler.dart +++ b/pkgs/http2/lib/src/error_handler.dart @@ -2,8 +2,6 @@ // 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. -library http2.src.error_handler; - import 'dart:async'; import 'sync_errors.dart'; diff --git a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart index 8cf7a8cea0..87ee116239 100644 --- a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart @@ -5,7 +5,6 @@ // TODO: Take priorities into account. // TODO: Properly fragment large data frames, so they are not taking up too much // bandwidth. -library http2.src.flowcontrol.connection_flow_controller; import 'dart:async'; import 'dart:collection'; diff --git a/pkgs/http2/lib/src/flowcontrol/queue_messages.dart b/pkgs/http2/lib/src/flowcontrol/queue_messages.dart index 33db75b064..ea22b63689 100644 --- a/pkgs/http2/lib/src/flowcontrol/queue_messages.dart +++ b/pkgs/http2/lib/src/flowcontrol/queue_messages.dart @@ -2,8 +2,6 @@ // 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. -library http2.src.flowcontrol.queue_messages; - import '../../transport.dart'; /// The subclasses of [Message] are objects that are coming from the diff --git a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart index 5ee5923528..9cd14992dd 100644 --- a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart @@ -2,8 +2,6 @@ // 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. -library http2.src.flowcontrol.stream_queues; - import 'dart:async'; import 'dart:collection'; diff --git a/pkgs/http2/lib/src/flowcontrol/window.dart b/pkgs/http2/lib/src/flowcontrol/window.dart index 5f26909bb1..51b90161c0 100644 --- a/pkgs/http2/lib/src/flowcontrol/window.dart +++ b/pkgs/http2/lib/src/flowcontrol/window.dart @@ -2,8 +2,6 @@ // 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. -library http2.src.window; - class Window { static const int MAX_WINDOW_SIZE = (1 << 31) - 1; diff --git a/pkgs/http2/lib/src/flowcontrol/window_handler.dart b/pkgs/http2/lib/src/flowcontrol/window_handler.dart index eb165ad913..6f9a5a6c3f 100644 --- a/pkgs/http2/lib/src/flowcontrol/window_handler.dart +++ b/pkgs/http2/lib/src/flowcontrol/window_handler.dart @@ -2,8 +2,6 @@ // 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. -library http2.src.window_handler; - import '../async_utils/async_utils.dart'; import '../frames/frames.dart'; import '../sync_errors.dart'; diff --git a/pkgs/http2/lib/src/frames/frame_defragmenter.dart b/pkgs/http2/lib/src/frames/frame_defragmenter.dart index 900fc7cd5b..0527480eec 100644 --- a/pkgs/http2/lib/src/frames/frame_defragmenter.dart +++ b/pkgs/http2/lib/src/frames/frame_defragmenter.dart @@ -2,8 +2,6 @@ // 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. -library http2.src.frames.frame_defragmenter; - import '../sync_errors.dart'; import 'frames.dart'; diff --git a/pkgs/http2/lib/src/hpack/huffman.dart b/pkgs/http2/lib/src/hpack/huffman.dart index 5abe7273a8..64e2be7ace 100644 --- a/pkgs/http2/lib/src/hpack/huffman.dart +++ b/pkgs/http2/lib/src/hpack/huffman.dart @@ -2,8 +2,6 @@ // 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. -library http2.src.hpack.huffman; - import 'dart:io'; import 'huffman_table.dart'; diff --git a/pkgs/http2/lib/src/hpack/huffman_table.dart b/pkgs/http2/lib/src/hpack/huffman_table.dart index ca3f250922..488e1246d9 100644 --- a/pkgs/http2/lib/src/hpack/huffman_table.dart +++ b/pkgs/http2/lib/src/hpack/huffman_table.dart @@ -2,8 +2,6 @@ // 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. -library http2.hpack.huffman_table; - import 'huffman.dart'; /// The huffman codec for encoding/decoding HTTP/2 header blocks. diff --git a/pkgs/http2/lib/src/ping/ping_handler.dart b/pkgs/http2/lib/src/ping/ping_handler.dart index 9be7dc154a..7bf8013958 100644 --- a/pkgs/http2/lib/src/ping/ping_handler.dart +++ b/pkgs/http2/lib/src/ping/ping_handler.dart @@ -2,8 +2,6 @@ // 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. -library http2.src.ping.ping_handler; - import 'dart:async'; import '../error_handler.dart'; diff --git a/pkgs/http2/lib/src/settings/settings.dart b/pkgs/http2/lib/src/settings/settings.dart index 78f6980830..ab38ebb2a4 100644 --- a/pkgs/http2/lib/src/settings/settings.dart +++ b/pkgs/http2/lib/src/settings/settings.dart @@ -2,8 +2,6 @@ // 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. -library http2.src.settings; - import 'dart:async'; import '../error_handler.dart'; diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index 47267ab786..3ceaa195ec 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -2,8 +2,6 @@ // 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. -library http2.src.stream_handler; - import 'dart:async'; import 'dart:math'; diff --git a/pkgs/http2/lib/src/sync_errors.dart b/pkgs/http2/lib/src/sync_errors.dart index 226a51999c..2423e6a4a9 100644 --- a/pkgs/http2/lib/src/sync_errors.dart +++ b/pkgs/http2/lib/src/sync_errors.dart @@ -2,8 +2,6 @@ // 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. -library http2.src.sync_errors; - class ProtocolException implements Exception { final String _message; diff --git a/pkgs/http2/lib/src/testing/client.dart b/pkgs/http2/lib/src/testing/client.dart index 6cd6e4d512..6896a86970 100644 --- a/pkgs/http2/lib/src/testing/client.dart +++ b/pkgs/http2/lib/src/testing/client.dart @@ -2,8 +2,6 @@ // 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. -library http2.client; - import 'dart:async'; import 'dart:convert' show ascii; import 'dart:io'; diff --git a/pkgs/http2/lib/src/testing/debug.dart b/pkgs/http2/lib/src/testing/debug.dart index f00ae20807..b48198791e 100644 --- a/pkgs/http2/lib/src/testing/debug.dart +++ b/pkgs/http2/lib/src/testing/debug.dart @@ -2,8 +2,6 @@ // 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. -library http2.debug; - import 'dart:async'; import 'dart:convert'; import 'dart:io'; diff --git a/pkgs/http2/test/client_test.dart b/pkgs/http2/test/client_test.dart index 574ac4e309..57b13316b4 100644 --- a/pkgs/http2/test/client_test.dart +++ b/pkgs/http2/test/client_test.dart @@ -2,8 +2,6 @@ // 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. -library http2.test.client_tests; - import 'dart:async'; import 'dart:convert' show ascii; import 'dart:typed_data'; diff --git a/pkgs/http2/test/client_websites_test.dart b/pkgs/http2/test/client_websites_test.dart index 6ea6e450a2..5eac0eb55c 100644 --- a/pkgs/http2/test/client_websites_test.dart +++ b/pkgs/http2/test/client_websites_test.dart @@ -2,8 +2,6 @@ // 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. -library http2.test.client_websites_test; - import 'dart:async'; import 'dart:convert' show Utf8Decoder, utf8; import 'dart:io'; diff --git a/pkgs/http2/test/multiprotocol_server_test.dart b/pkgs/http2/test/multiprotocol_server_test.dart index 4d57eec6c6..9b55fdd4c2 100644 --- a/pkgs/http2/test/multiprotocol_server_test.dart +++ b/pkgs/http2/test/multiprotocol_server_test.dart @@ -2,8 +2,6 @@ // 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. -library http2.test.client_tests; - import 'dart:async'; import 'dart:convert' show ascii, utf8; import 'dart:io'; diff --git a/pkgs/http2/test/server_test.dart b/pkgs/http2/test/server_test.dart index 566bb0149a..61432b4bd1 100644 --- a/pkgs/http2/test/server_test.dart +++ b/pkgs/http2/test/server_test.dart @@ -2,8 +2,6 @@ // 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. -library http2.test.server_tests; - import 'dart:async'; import 'package:test/test.dart'; diff --git a/pkgs/http2/test/src/connection_preface_test.dart b/pkgs/http2/test/src/connection_preface_test.dart index 662f75b61b..9c42829cda 100644 --- a/pkgs/http2/test/src/connection_preface_test.dart +++ b/pkgs/http2/test/src/connection_preface_test.dart @@ -2,8 +2,6 @@ // 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. -library http2.test.connection_preface_test; - import 'dart:async'; import 'dart:math' show min; diff --git a/pkgs/http2/test/src/error_matchers.dart b/pkgs/http2/test/src/error_matchers.dart index ee10fb85e7..f0eabb0aa6 100644 --- a/pkgs/http2/test/src/error_matchers.dart +++ b/pkgs/http2/test/src/error_matchers.dart @@ -2,8 +2,6 @@ // 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. -library http2.test.error_matchers; - import 'package:test/test.dart'; import 'package:http2/src/sync_errors.dart'; diff --git a/pkgs/http2/test/src/streams/helper.dart b/pkgs/http2/test/src/streams/helper.dart index 12f31bccac..ab5261ee1f 100644 --- a/pkgs/http2/test/src/streams/helper.dart +++ b/pkgs/http2/test/src/streams/helper.dart @@ -2,8 +2,6 @@ // 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. -library http2.test.end2end_test; - import 'dart:async'; import 'package:test/test.dart'; diff --git a/pkgs/http2/test/src/streams/simple_flow_test.dart b/pkgs/http2/test/src/streams/simple_flow_test.dart index be4f2fe02e..197bccef84 100644 --- a/pkgs/http2/test/src/streams/simple_flow_test.dart +++ b/pkgs/http2/test/src/streams/simple_flow_test.dart @@ -2,8 +2,6 @@ // 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. -library http2.test.streams.simple_flow_test; - import 'dart:async'; import 'package:test/test.dart'; diff --git a/pkgs/http2/test/src/streams/simple_push_test.dart b/pkgs/http2/test/src/streams/simple_push_test.dart index 122ee9179f..cd6df4924a 100644 --- a/pkgs/http2/test/src/streams/simple_push_test.dart +++ b/pkgs/http2/test/src/streams/simple_push_test.dart @@ -2,8 +2,6 @@ // 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. -library http2.test.streams.simple_push_test; - import 'dart:async'; import 'dart:convert' show utf8; diff --git a/pkgs/http2/test/src/streams/streams_test.dart b/pkgs/http2/test/src/streams/streams_test.dart index a5132b1c5d..aedc422598 100644 --- a/pkgs/http2/test/src/streams/streams_test.dart +++ b/pkgs/http2/test/src/streams/streams_test.dart @@ -2,8 +2,6 @@ // 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. -library http2.test.end2end_test; - import 'package:http2/transport.dart'; import 'package:pedantic/pedantic.dart'; import 'package:test/test.dart'; From a05c8c1b595612c9240432b590f505040b16d42f Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 20 Feb 2020 12:42:07 -0800 Subject: [PATCH 101/172] Enable and fix use_function_type_syntax_for_parameters (dart-lang/http2#62) --- pkgs/http2/analysis_options.yaml | 1 + pkgs/http2/experimental/server.dart | 2 +- pkgs/http2/lib/multiprotocol_server.dart | 6 ++-- pkgs/http2/lib/src/connection.dart | 2 +- pkgs/http2/lib/src/error_handler.dart | 6 ++-- .../http2/lib/src/streams/stream_handler.dart | 2 +- pkgs/http2/lib/transport.dart | 2 +- pkgs/http2/test/client_test.dart | 35 ++++++++++--------- pkgs/http2/test/server_test.dart | 18 ++++++---- .../src/frames/frame_writer_reader_test.dart | 2 +- pkgs/http2/test/src/streams/helper.dart | 9 +++-- pkgs/http2/test/transport_test.dart | 9 +++-- 12 files changed, 53 insertions(+), 41 deletions(-) diff --git a/pkgs/http2/analysis_options.yaml b/pkgs/http2/analysis_options.yaml index df9176db66..e80614fd1f 100644 --- a/pkgs/http2/analysis_options.yaml +++ b/pkgs/http2/analysis_options.yaml @@ -40,4 +40,5 @@ linter: - unnecessary_const - unnecessary_new - unrelated_type_equality_checks + - use_function_type_syntax_for_parameters - valid_regexps diff --git a/pkgs/http2/experimental/server.dart b/pkgs/http2/experimental/server.dart index fff74894cf..688942522a 100644 --- a/pkgs/http2/experimental/server.dart +++ b/pkgs/http2/experimental/server.dart @@ -121,7 +121,7 @@ Future sendHtml(ServerTransportStream stream) async { } Future push(ServerTransportStream stream, String path, - Future sendResponse(TransportStream stream, String path)) async { + Future Function(TransportStream, String path) sendResponse) async { var requestHeaders = [ Header.ascii(':authority', '$HOSTNAME:$PORT'), Header.ascii(':method', 'GET'), diff --git a/pkgs/http2/lib/multiprotocol_server.dart b/pkgs/http2/lib/multiprotocol_server.dart index a521ddca00..ff3b47431c 100644 --- a/pkgs/http2/lib/multiprotocol_server.dart +++ b/pkgs/http2/lib/multiprotocol_server.dart @@ -63,9 +63,9 @@ class MultiProtocolHttpServer { /// /// It is expected that [callbackHttp11] and [callbackHttp2] will never throw /// an exception (i.e. these must take care of error handling themselves). - void startServing(void callbackHttp11(HttpRequest request), - void callbackHttp2(http2.ServerTransportStream stream), - {void onError(error, StackTrace stack)}) { + void startServing(void Function(HttpRequest) callbackHttp11, + void Function(http2.ServerTransportStream) callbackHttp2, + {void Function(dynamic error, StackTrace) onError}) { // 1. Start listening on the real [SecureServerSocket]. _serverSocket.listen((SecureSocket socket) { var protocol = socket.selectedProtocol; diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index 43dd741f5c..111d422d22 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -293,7 +293,7 @@ abstract class Connection { } /// Invokes the passed in closure and catches any exceptions. - void _catchProtocolErrors(void fn()) { + void _catchProtocolErrors(void Function() fn) { try { fn(); } on ProtocolException catch (error) { diff --git a/pkgs/http2/lib/src/error_handler.dart b/pkgs/http2/lib/src/error_handler.dart index 37ecc26f49..c04785e323 100644 --- a/pkgs/http2/lib/src/error_handler.dart +++ b/pkgs/http2/lib/src/error_handler.dart @@ -24,14 +24,14 @@ class TerminatableMixin { // Subclasses can override this method if they want. } - T ensureNotTerminatedSync(T f()) { + T ensureNotTerminatedSync(T Function() f) { if (wasTerminated) { throw TerminatedException(); } return f(); } - Future ensureNotTerminatedAsync(Future f()) { + Future ensureNotTerminatedAsync(Future Function() f) { if (wasTerminated) { return Future.error(TerminatedException()); } @@ -84,7 +84,7 @@ class ClosableMixin { // Subclasses can override this method if they want. } - dynamic ensureNotClosingSync(f()) { + dynamic ensureNotClosingSync(dynamic Function() f) { if (isClosing) { throw StateError('Was in the process of closing.'); } diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index 3ceaa195ec..87c6f172b4 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -110,7 +110,7 @@ class Http2StreamImpl extends TransportStream void terminate() => _terminateStreamFun(this); @override - set onTerminated(void handler(int v)) { + set onTerminated(void Function(int) handler) { _onTerminated = handler; if (_terminatedErrorCode != null && _onTerminated != null) { _onTerminated(_terminatedErrorCode); diff --git a/pkgs/http2/lib/transport.dart b/pkgs/http2/lib/transport.dart index 444b3159a3..d82da531f3 100644 --- a/pkgs/http2/lib/transport.dart +++ b/pkgs/http2/lib/transport.dart @@ -126,7 +126,7 @@ abstract class TransportStream { /// Sets the termination handler on this stream. /// /// The handler will be called if the stream receives an RST_STREAM frame. - set onTerminated(void value(int v)); + set onTerminated(void Function(int) value); /// Terminates this HTTP/2 stream in an un-normal way. /// diff --git a/pkgs/http2/test/client_test.dart b/pkgs/http2/test/client_test.dart index 57b13316b4..551c9d3c2d 100644 --- a/pkgs/http2/test/client_test.dart +++ b/pkgs/http2/test/client_test.dart @@ -25,7 +25,7 @@ void main() { (ClientTransportConnection client, FrameWriter serverWriter, StreamIterator serverReader, - Future nextFrame()) async { + Future Function() nextFrame) async { var settingsDone = Completer(); Future serverFun() async { @@ -67,7 +67,7 @@ void main() { (ClientTransportConnection client, FrameWriter serverWriter, StreamIterator serverReader, - Future nextFrame()) async { + Future Function() nextFrame) async { var goawayReceived = Completer(); Future serverFun() async { serverWriter.writePingFrame(42); @@ -104,7 +104,7 @@ void main() { (ClientTransportConnection client, FrameWriter serverWriter, StreamIterator serverReader, - Future nextFrame()) async { + Future Function() nextFrame) async { Future serverFun() async { expect(await nextFrame() is SettingsFrame, true); expect(await nextFrame() is HeadersFrame, true); @@ -134,7 +134,7 @@ void main() { (ClientTransportConnection client, FrameWriter serverWriter, StreamIterator serverReader, - Future nextFrame()) async { + Future Function() nextFrame) async { var handshakeCompleter = Completer(); Future serverFun() async { @@ -186,7 +186,7 @@ void main() { (ClientTransportConnection client, FrameWriter serverWriter, StreamIterator serverReader, - Future nextFrame()) async { + Future Function() nextFrame) async { var handshakeCompleter = Completer(); Future serverFun() async { @@ -251,7 +251,7 @@ void main() { (ClientTransportConnection client, FrameWriter serverWriter, StreamIterator serverReader, - Future nextFrame()) async { + Future Function() nextFrame) async { var handshakeCompleter = Completer(); var cancelDone = Completer(); var endDone = Completer(); @@ -327,7 +327,7 @@ void main() { (ClientTransportConnection client, FrameWriter serverWriter, StreamIterator serverReader, - Future nextFrame()) async { + Future Function() nextFrame) async { var handshakeCompleter = Completer(); var cancelDone = Completer(); var endDone = Completer(); @@ -400,7 +400,7 @@ void main() { (ClientTransportConnection client, FrameWriter serverWriter, StreamIterator serverReader, - Future nextFrame()) async { + Future Function() nextFrame) async { var handshakeCompleter = Completer(); Future serverFun() async { @@ -453,7 +453,7 @@ void main() { (ClientTransportConnection client, FrameWriter serverWriter, StreamIterator serverReader, - Future nextFrame()) async { + Future Function() nextFrame) async { var handshakeCompleter = Completer(); Future serverFun() async { @@ -504,7 +504,7 @@ void main() { (ClientTransportConnection client, FrameWriter serverWriter, StreamIterator serverReader, - Future nextFrame()) async { + Future Function() nextFrame) async { var handshakeCompleter = Completer(); Future serverFun() async { @@ -560,7 +560,7 @@ void main() { clientTest('client-resets-stream', (ClientTransportConnection client, FrameWriter serverWriter, StreamIterator serverReader, - Future nextFrame()) async { + Future Function() nextFrame) async { var settingsDone = Completer(); var headersDone = Completer(); @@ -620,7 +620,7 @@ void main() { (ClientTransportConnection client, FrameWriter serverWriter, StreamIterator serverReader, - Future nextFrame()) async { + Future Function() nextFrame) async { var settingsDone = Completer(); Future serverFun() async { @@ -677,11 +677,12 @@ void main() { void clientTest( String name, - Future func( - ClientTransportConnection clientConnection, - FrameWriter frameWriter, - StreamIterator frameReader, - Future readNext())) { + Future Function( + ClientTransportConnection, + FrameWriter, + StreamIterator frameReader, + Future Function() readNext) + func) { return test(name, () { var streams = ClientStreams(); var serverReader = streams.serverConnectionFrameReader; diff --git a/pkgs/http2/test/server_test.dart b/pkgs/http2/test/server_test.dart index 61432b4bd1..c9b8cc2811 100644 --- a/pkgs/http2/test/server_test.dart +++ b/pkgs/http2/test/server_test.dart @@ -19,7 +19,7 @@ void main() { (ServerTransportConnection server, FrameWriter clientWriter, StreamIterator clientReader, - Future nextFrame()) async { + Future Function() nextFrame) async { Future serverFun() async { expect(await server.incomingStreams.toList(), isEmpty); await server.finish(); @@ -47,7 +47,7 @@ void main() { (ServerTransportConnection server, FrameWriter clientWriter, StreamIterator clientReader, - Future nextFrame()) async { + Future Function() nextFrame) async { Future serverFun() async { // TODO: Do we want to get an error in this case? expect(await server.incomingStreams.toList(), isEmpty); @@ -76,7 +76,7 @@ void main() { (ServerTransportConnection server, FrameWriter clientWriter, StreamIterator clientReader, - Future nextFrame()) async { + Future Function() nextFrame) async { Future serverFun() async { await server.incomingStreams.toList(); await server.finish(); @@ -111,7 +111,7 @@ void main() { (ServerTransportConnection server, FrameWriter clientWriter, StreamIterator clientReader, - Future nextFrame()) async { + Future Function() nextFrame) async { Future serverFun() async { await server.incomingStreams.toList(); await server.finish(); @@ -151,7 +151,7 @@ void main() { serverTest('server-resets-stream', (ServerTransportConnection server, FrameWriter clientWriter, StreamIterator clientReader, - Future nextFrame()) async { + Future Function() nextFrame) async { Future serverFun() async { var it = StreamIterator(server.incomingStreams); expect(await it.moveNext(), true); @@ -194,8 +194,12 @@ void main() { void serverTest( String name, - func(ServerTransportConnection serverConnection, FrameWriter frameWriter, - StreamIterator frameReader, Future readNext())) { + void Function( + ServerTransportConnection, + FrameWriter, + StreamIterator frameReader, + Future Function() readNext) + func) { return test(name, () { var streams = ClientErrorStreams(); var clientReader = streams.clientConnectionFrameReader; diff --git a/pkgs/http2/test/src/frames/frame_writer_reader_test.dart b/pkgs/http2/test/src/frames/frame_writer_reader_test.dart index 7cc3917b65..24031819f3 100644 --- a/pkgs/http2/test/src/frames/frame_writer_reader_test.dart +++ b/pkgs/http2/test/src/frames/frame_writer_reader_test.dart @@ -215,7 +215,7 @@ void main() { } void writerReaderTest(String name, - func(FrameWriter writer, FrameReader reader, HPackDecoder decoder)) { + Future Function(FrameWriter, FrameReader, HPackDecoder) func) { test(name, () { var settings = ActiveSettings(); var context = HPackContext(); diff --git a/pkgs/http2/test/src/streams/helper.dart b/pkgs/http2/test/src/streams/helper.dart index ab5261ee1f..9aba787dff 100644 --- a/pkgs/http2/test/src/streams/helper.dart +++ b/pkgs/http2/test/src/streams/helper.dart @@ -22,8 +22,10 @@ void expectEmptyStream(Stream s) { s.listen(expectAsync1((_) {}, count: 0), onDone: expectAsync0(() {})); } -void streamTest(String name, - func(ClientTransportConnection client, ServerTransportConnection server), +void streamTest( + String name, + Future Function(ClientTransportConnection, ServerTransportConnection) + func, {ClientSettings settings}) { return test(name, () { var bidirect = BidirectionalConnection(); @@ -34,7 +36,8 @@ void streamTest(String name, }); } -void framesTest(String name, func(frameWriter, frameStream)) { +void framesTest( + String name, Future Function(FrameWriter, FrameReader) func) { return test(name, () { var c = StreamController>(); var fw = FrameWriter(null, c, ActiveSettings()); diff --git a/pkgs/http2/test/transport_test.dart b/pkgs/http2/test/transport_test.dart index 12ff55fa00..b6cdf42d04 100644 --- a/pkgs/http2/test/transport_test.dart +++ b/pkgs/http2/test/transport_test.dart @@ -508,9 +508,12 @@ void main() { }); } -void transportTest(String name, - func(ClientTransportConnection client, ServerTransportConnection server), - {ClientSettings clientSettings, ServerSettings serverSettings}) { +void transportTest( + String name, + Future Function(ClientTransportConnection, ServerTransportConnection) + func, + {ClientSettings clientSettings, + ServerSettings serverSettings}) { return test(name, () { var bidirectional = BidirectionalConnection(); bidirectional.clientSettings = clientSettings; From c5da8a296cc9052ebcd57a9122532b656443c677 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 20 Feb 2020 13:05:30 -0800 Subject: [PATCH 102/172] Enable package:pedantic lints (dart-lang/http2#63) Fix the remaining new lints: - curly_braces_in_flow_control_structures - prefer_collection_literals - prefer_conditional_assignment - prefer_spread_collections - unnecessary_this Remove the manually specified lints that overlap with pedantic. Bump to a more recent SDK to support enhanced collection literals. --- pkgs/http2/.travis.yml | 2 +- pkgs/http2/analysis_options.yaml | 25 +------------------ pkgs/http2/lib/multiprotocol_server.dart | 2 +- .../src/flowcontrol/connection_queues.dart | 2 +- pkgs/http2/lib/src/frames/frame_reader.dart | 6 ++--- pkgs/http2/lib/src/hpack/huffman.dart | 12 ++++----- .../http2/lib/src/streams/stream_handler.dart | 13 +++------- pkgs/http2/lib/transport.dart | 6 ++--- pkgs/http2/pubspec.yaml | 2 +- .../src/frames/frame_writer_reader_test.dart | 7 +++--- 10 files changed, 22 insertions(+), 55 deletions(-) diff --git a/pkgs/http2/.travis.yml b/pkgs/http2/.travis.yml index 7968b29c66..4654b113c7 100644 --- a/pkgs/http2/.travis.yml +++ b/pkgs/http2/.travis.yml @@ -1,7 +1,7 @@ language: dart dart: - - 2.0.0 + - 2.5.0 - dev dart_task: diff --git a/pkgs/http2/analysis_options.yaml b/pkgs/http2/analysis_options.yaml index e80614fd1f..3f3b516af0 100644 --- a/pkgs/http2/analysis_options.yaml +++ b/pkgs/http2/analysis_options.yaml @@ -1,3 +1,4 @@ +include: package:pedantic/analysis_options.yaml analyzer: errors: unused_element: error @@ -6,39 +7,15 @@ analyzer: dead_code: error linter: rules: - - always_declare_return_types - - annotate_overrides - - avoid_empty_else - - avoid_init_to_null - - avoid_return_types_on_setters - await_only_futures - camel_case_types - comment_references - control_flow_in_finally - directives_ordering - - empty_catches - - empty_constructor_bodies - empty_statements - hash_and_equals - implementation_imports - - library_names - - library_prefixes - non_constant_identifier_names - - omit_local_variable_types - only_throw_errors - - prefer_equal_for_default_values - - prefer_final_fields - - prefer_generic_function_type_aliases - - prefer_is_empty - - prefer_is_not_empty - - prefer_single_quotes - - slash_for_doc_comments - test_types_in_equals - throw_in_finally - - type_init_formals - - unawaited_futures - - unnecessary_const - - unnecessary_new - - unrelated_type_equality_checks - - use_function_type_syntax_for_parameters - - valid_regexps diff --git a/pkgs/http2/lib/multiprotocol_server.dart b/pkgs/http2/lib/multiprotocol_server.dart index ff3b47431c..d0a7b5dba1 100644 --- a/pkgs/http2/lib/multiprotocol_server.dart +++ b/pkgs/http2/lib/multiprotocol_server.dart @@ -25,7 +25,7 @@ class MultiProtocolHttpServer { StreamController _http2Controller; Stream _http2Server; - final _http2Connections = Set(); + final _http2Connections = {}; MultiProtocolHttpServer._(this._serverSocket, this._settings) { _http11Controller = diff --git a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart index 87ee116239..778c1ef788 100644 --- a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart @@ -334,7 +334,7 @@ class ConnectionMessageQueueIn extends Object } void forceDispatchIncomingMessages() { - final toBeRemoved = Set(); + final toBeRemoved = {}; _stream2pendingMessages.forEach((int streamId, Queue messages) { final mq = _stream2messageQueue[streamId]; while (messages.isNotEmpty) { diff --git a/pkgs/http2/lib/src/frames/frame_reader.dart b/pkgs/http2/lib/src/frames/frame_reader.dart index 2c62216516..10ac3092cd 100644 --- a/pkgs/http2/lib/src/frames/frame_reader.dart +++ b/pkgs/http2/lib/src/frames/frame_reader.dart @@ -19,7 +19,7 @@ class FrameReader { /// Starts to listen on the input stream and decodes HTTP/2 transport frames. Stream startDecoding() { - var bufferedData = List>(); + var bufferedData = >[]; var bufferedLength = 0; FrameHeader tryReadHeader() { @@ -74,9 +74,7 @@ class FrameReader { try { while (true) { - if (header == null) { - header = tryReadHeader(); - } + header ??= tryReadHeader(); if (header != null) { if (header.length > _localSettings.maxFrameSize) { terminateWithError( diff --git a/pkgs/http2/lib/src/hpack/huffman.dart b/pkgs/http2/lib/src/hpack/huffman.dart index 64e2be7ace..6d0322b02d 100644 --- a/pkgs/http2/lib/src/hpack/huffman.dart +++ b/pkgs/http2/lib/src/hpack/huffman.dart @@ -70,7 +70,9 @@ class HuffmanDecoder { 'Incomplete encoding of a byte or more than 7 bit padding.'); } - while (node.right != null) node = node.right; + while (node.right != null) { + node = node.right; + } if (node.value != 256) { throw HuffmanDecodingException('Incomplete encoding of a byte.'); @@ -163,14 +165,10 @@ HuffmanTreeNode generateHuffmanTree(List valueEncodings) { ((entry.encodedBytes >> (entry.numBits - bitNr - 1)) & 1) == 1; if (right) { - if (current.right == null) { - current.right = HuffmanTreeNode(); - } + current.right ??= HuffmanTreeNode(); current = current.right; } else { - if (current.left == null) { - current.left = HuffmanTreeNode(); - } + current.left ??= HuffmanTreeNode(); current = current.left; } } diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index 87c6f172b4..f1d1c50b93 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -322,15 +322,8 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { incomingQueue.insertNewStreamMessageQueue(streamId, streamQueueIn); var _outgoingC = StreamController(); - var stream = Http2StreamImpl( - streamQueueIn, - streamQueueOut, - _outgoingC, - streamId, - windowOutHandler, - this._canPush, - this._push, - this._terminateStream); + var stream = Http2StreamImpl(streamQueueIn, streamQueueOut, _outgoingC, + streamId, windowOutHandler, _canPush, _push, _terminateStream); final wasIdle = _openStreams.isEmpty; _openStreams[stream.id] = stream; @@ -366,7 +359,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { bool _canPush(Http2StreamImpl stream) { var openState = (stream.state == StreamState.Open || stream.state == StreamState.HalfClosedRemote); - var pushEnabled = this._peerSettings.enablePush; + var pushEnabled = _peerSettings.enablePush; return openState && pushEnabled && _canCreateNewStream() && diff --git a/pkgs/http2/lib/transport.dart b/pkgs/http2/lib/transport.dart index d82da531f3..d48dd41896 100644 --- a/pkgs/http2/lib/transport.dart +++ b/pkgs/http2/lib/transport.dart @@ -77,7 +77,7 @@ abstract class ClientTransportConnection extends TransportConnection { factory ClientTransportConnection.viaStreams( Stream> incoming, StreamSink> outgoing, {ClientSettings settings}) { - if (settings == null) settings = const ClientSettings(); + settings ??= const ClientSettings(); return ClientConnection(incoming, outgoing, settings); } @@ -101,7 +101,7 @@ abstract class ServerTransportConnection extends TransportConnection { Stream> incoming, StreamSink> outgoing, {ServerSettings settings = const ServerSettings(concurrentStreamLimit: 1000)}) { - if (settings == null) settings = const ServerSettings(); + settings ??= const ServerSettings(); return ServerConnection(incoming, outgoing, settings); } @@ -174,7 +174,7 @@ abstract class ServerTransportStream extends TransportStream { abstract class StreamMessage { final bool endStream; - StreamMessage({bool endStream}) : this.endStream = endStream ?? false; + StreamMessage({bool endStream}) : endStream = endStream ?? false; } /// Represents a data message which can be sent over a HTTP/2 stream. diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index b82b3245f6..94f47e4a39 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -5,7 +5,7 @@ author: Dart Team homepage: https://github.com/dart-lang/http2 environment: - sdk: '>=2.0.0 <3.0.0' + sdk: '>=2.5.0 <3.0.0' dev_dependencies: mockito: ^4.0.0 diff --git a/pkgs/http2/test/src/frames/frame_writer_reader_test.dart b/pkgs/http2/test/src/frames/frame_writer_reader_test.dart index 24031819f3..edce268d0a 100644 --- a/pkgs/http2/test/src/frames/frame_writer_reader_test.dart +++ b/pkgs/http2/test/src/frames/frame_writer_reader_test.dart @@ -201,9 +201,10 @@ void main() { expect(contFrame.header.streamId, 99); expect(contFrame.hasEndHeadersFlag, isTrue); - var headerBlock = [] - ..addAll(headersFrame.headerBlockFragment) - ..addAll(contFrame.headerBlockFragment); + var headerBlock = [ + ...headersFrame.headerBlockFragment, + ...contFrame.headerBlockFragment + ]; var headers = decoder.decode(headerBlock); expect(headers.length, 1); From 0bbda864aa60464f02fb907f549220fc10377c91 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 20 Feb 2020 13:37:14 -0800 Subject: [PATCH 103/172] Use hasLength in tests (dart-lang/http2#64) The failure message for specific matchers is nicer than comparing ints. Find places using `expression.length` as the actual and a number as the expected, and rewrite with the `hasLength` matcher. --- .../src/flowcontrol/stream_queues_test.dart | 2 +- .../test/src/frames/frame_reader_test.dart | 2 +- .../src/frames/frame_writer_reader_test.dart | 32 +++++++++---------- pkgs/http2/test/src/hpack/hpack_test.dart | 2 +- .../test/src/streams/simple_push_test.dart | 2 +- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart index a78d9c9d87..4a2aa65a75 100644 --- a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart @@ -62,7 +62,7 @@ void main() { verify(windowMock.decreaseWindow(1)).called(BYTES.length); final messages = verify(connectionQueueMock.enqueueMessage(captureAny)).captured; - expect(messages.length, BYTES.length); + expect(messages, hasLength(BYTES.length)); for (var counter = 0; counter < messages.length; counter++) { expect(messages[counter], const TypeMatcher()); DataMessage dataMessage = messages[counter]; diff --git a/pkgs/http2/test/src/frames/frame_reader_test.dart b/pkgs/http2/test/src/frames/frame_reader_test.dart index 993180f0a8..99c8fe5535 100644 --- a/pkgs/http2/test/src/frames/frame_reader_test.dart +++ b/pkgs/http2/test/src/frames/frame_reader_test.dart @@ -38,7 +38,7 @@ void main() { var body = List.filled(maxFrameSize, 0x42); dataFrame(body).listen(expectAsync1((Frame frame) { expect(frame is DataFrame, isTrue); - expect(frame.header.length, body.length); + expect(frame.header, hasLength(body.length)); expect(frame.header.flags, 0); DataFrame dataFrame = frame; expect(dataFrame.hasEndStreamFlag, isFalse); diff --git a/pkgs/http2/test/src/frames/frame_writer_reader_test.dart b/pkgs/http2/test/src/frames/frame_writer_reader_test.dart index edce268d0a..f15d7882d1 100644 --- a/pkgs/http2/test/src/frames/frame_writer_reader_test.dart +++ b/pkgs/http2/test/src/frames/frame_writer_reader_test.dart @@ -20,7 +20,7 @@ void main() { writer.writeDataFrame(99, [1, 2, 3], endStream: true); var frames = await finishWriting(writer, reader); - expect(frames.length, 1); + expect(frames, hasLength(1)); expect(frames[0] is DataFrame, isTrue); DataFrame dataFrame = frames[0]; @@ -36,7 +36,7 @@ void main() { writer.writeHeadersFrame(99, [Header.ascii('a', 'b')], endStream: true); var frames = await finishWriting(writer, reader); - expect(frames.length, 1); + expect(frames, hasLength(1)); expect(frames[0] is HeadersFrame, isTrue); HeadersFrame headersFrame = frames[0]; @@ -51,7 +51,7 @@ void main() { expect(headersFrame.weight, isNull); var headers = decoder.decode(headersFrame.headerBlockFragment); - expect(headers.length, 1); + expect(headers, hasLength(1)); expect(headers[0], isHeader('a', 'b')); }); @@ -60,7 +60,7 @@ void main() { writer.writePriorityFrame(99, 44, 33, exclusive: true); var frames = await finishWriting(writer, reader); - expect(frames.length, 1); + expect(frames, hasLength(1)); expect(frames[0] is PriorityFrame, isTrue); PriorityFrame priorityFrame = frames[0]; @@ -75,7 +75,7 @@ void main() { writer.writeRstStreamFrame(99, 42); var frames = await finishWriting(writer, reader); - expect(frames.length, 1); + expect(frames, hasLength(1)); expect(frames[0] is RstStreamFrame, isTrue); RstStreamFrame rstFrame = frames[0]; @@ -88,13 +88,13 @@ void main() { writer.writeSettingsFrame([Setting(Setting.SETTINGS_ENABLE_PUSH, 1)]); var frames = await finishWriting(writer, reader); - expect(frames.length, 1); + expect(frames, hasLength(1)); expect(frames[0] is SettingsFrame, isTrue); SettingsFrame settingsFrame = frames[0]; expect(settingsFrame.hasAckFlag, false); expect(settingsFrame.header.streamId, 0); - expect(settingsFrame.settings.length, 1); + expect(settingsFrame.settings, hasLength(1)); expect( settingsFrame.settings[0].identifier, Setting.SETTINGS_ENABLE_PUSH); expect(settingsFrame.settings[0].value, 1); @@ -105,13 +105,13 @@ void main() { writer.writeSettingsAckFrame(); var frames = await finishWriting(writer, reader); - expect(frames.length, 1); + expect(frames, hasLength(1)); expect(frames[0] is SettingsFrame, isTrue); SettingsFrame settingsFrame = frames[0]; expect(settingsFrame.hasAckFlag, true); expect(settingsFrame.header.streamId, 0); - expect(settingsFrame.settings.length, 0); + expect(settingsFrame.settings, hasLength(0)); }); writerReaderTest('push-promise-frame', @@ -119,7 +119,7 @@ void main() { writer.writePushPromiseFrame(99, 44, [Header.ascii('a', 'b')]); var frames = await finishWriting(writer, reader); - expect(frames.length, 1); + expect(frames, hasLength(1)); expect(frames[0] is PushPromiseFrame, isTrue); PushPromiseFrame pushPromiseFrame = frames[0]; @@ -130,7 +130,7 @@ void main() { expect(pushPromiseFrame.promisedStreamId, 44); var headers = decoder.decode(pushPromiseFrame.headerBlockFragment); - expect(headers.length, 1); + expect(headers, hasLength(1)); expect(headers[0], isHeader('a', 'b')); }); @@ -139,7 +139,7 @@ void main() { writer.writePingFrame(44, ack: true); var frames = await finishWriting(writer, reader); - expect(frames.length, 1); + expect(frames, hasLength(1)); expect(frames[0] is PingFrame, isTrue); PingFrame pingFrame = frames[0]; @@ -153,7 +153,7 @@ void main() { writer.writeGoawayFrame(44, 33, [1, 2, 3]); var frames = await finishWriting(writer, reader); - expect(frames.length, 1); + expect(frames, hasLength(1)); expect(frames[0] is GoawayFrame, isTrue); GoawayFrame goawayFrame = frames[0]; @@ -168,7 +168,7 @@ void main() { writer.writeWindowUpdate(55, streamId: 99); var frames = await finishWriting(writer, reader); - expect(frames.length, 1); + expect(frames, hasLength(1)); expect(frames[0] is WindowUpdateFrame, isTrue); WindowUpdateFrame windowUpdateFrame = frames[0]; @@ -185,7 +185,7 @@ void main() { writer.writeHeadersFrame(99, [header], endStream: true); var frames = await finishWriting(writer, reader); - expect(frames.length, 2); + expect(frames, hasLength(2)); expect(frames[0] is HeadersFrame, isTrue); expect(frames[1] is ContinuationFrame, isTrue); @@ -207,7 +207,7 @@ void main() { ]; var headers = decoder.decode(headerBlock); - expect(headers.length, 1); + expect(headers, hasLength(1)); expect(headers[0].name, headerName); expect(headers[0].value, headerValue); }); diff --git a/pkgs/http2/test/src/hpack/hpack_test.dart b/pkgs/http2/test/src/hpack/hpack_test.dart index e9e606581d..206e251e28 100644 --- a/pkgs/http2/test/src/hpack/hpack_test.dart +++ b/pkgs/http2/test/src/hpack/hpack_test.dart @@ -790,7 +790,7 @@ class TestHelper { static void expectHeader(Header h, int len, int nameChar, int valueChar) { var data = h.value; - expect(data.length, len - 32 - 1); + expect(data, hasLength(len - 32 - 1)); for (var i = 0; i < data.length; i++) { expect(data[i], valueChar); } diff --git a/pkgs/http2/test/src/streams/simple_push_test.dart b/pkgs/http2/test/src/streams/simple_push_test.dart index cd6df4924a..d3946c0755 100644 --- a/pkgs/http2/test/src/streams/simple_push_test.dart +++ b/pkgs/http2/test/src/streams/simple_push_test.dart @@ -21,7 +21,7 @@ void main() { allBytes.addAll(List.generate(42, (i) => 42)); void testHeaders(List
headers) { - expect(headers.length, expectedHeaders.length); + expect(headers, hasLength(expectedHeaders.length)); for (var i = 0; i < headers.length; i++) { expect(headers[i].name, expectedHeaders[i].name); expect(headers[i].value, expectedHeaders[i].value); From 930bb5d303a7df20a2a9c052c529b9fdfa6c6217 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 20 Feb 2020 14:55:15 -0800 Subject: [PATCH 104/172] Disable implicit casts (dart-lang/http2#65) Makes the places where a cast error could happen more explicit. In many places remove the explicit `dynamic` from variable declarations to avoid throwing away type information that is valid. --- pkgs/http2/analysis_options.yaml | 2 ++ pkgs/http2/test/client_test.dart | 30 +++++++++---------- .../http2/test/multiprotocol_server_test.dart | 4 +-- .../flowcontrol/connection_queues_test.dart | 15 +++++----- .../src/flowcontrol/stream_queues_test.dart | 22 +++++++------- .../src/flowcontrol/window_handler_test.dart | 2 +- .../src/frames/frame_defragmenter_test.dart | 4 +-- .../test/src/frames/frame_reader_test.dart | 2 +- .../src/frames/frame_writer_reader_test.dart | 26 ++++++++-------- .../test/src/ping/ping_handler_test.dart | 6 ++-- .../src/settings/settings_handler_test.dart | 8 ++--- pkgs/http2/test/src/streams/streams_test.dart | 22 +++++++------- pkgs/http2/test/transport_test.dart | 6 ++-- 13 files changed, 76 insertions(+), 73 deletions(-) diff --git a/pkgs/http2/analysis_options.yaml b/pkgs/http2/analysis_options.yaml index 3f3b516af0..56a5db7f62 100644 --- a/pkgs/http2/analysis_options.yaml +++ b/pkgs/http2/analysis_options.yaml @@ -1,5 +1,7 @@ include: package:pedantic/analysis_options.yaml analyzer: + strong-mode: + implicit-casts: false errors: unused_element: error unused_import: error diff --git a/pkgs/http2/test/client_test.dart b/pkgs/http2/test/client_test.dart index 551c9d3c2d..9adbce22f4 100644 --- a/pkgs/http2/test/client_test.dart +++ b/pkgs/http2/test/client_test.dart @@ -145,8 +145,8 @@ void main() { handshakeCompleter.complete(); - HeadersFrame headers = await nextFrame(); - DataFrame finFrame = await nextFrame(); + var headers = await nextFrame() as HeadersFrame; + var finFrame = await nextFrame() as DataFrame; expect(finFrame.hasEndStreamFlag, true); // Write a data frame for a non-existent stream. @@ -197,8 +197,8 @@ void main() { handshakeCompleter.complete(); - HeadersFrame headers = await nextFrame(); - DataFrame finFrame = await nextFrame(); + var headers = await nextFrame() as HeadersFrame; + var finFrame = await nextFrame() as DataFrame; expect(finFrame.hasEndStreamFlag, true); var streamId = headers.header.streamId; @@ -264,8 +264,8 @@ void main() { handshakeCompleter.complete(); - HeadersFrame headers = await nextFrame(); - DataFrame finFrame = await nextFrame(); + var headers = await nextFrame() as HeadersFrame; + var finFrame = await nextFrame() as DataFrame; expect(finFrame.hasEndStreamFlag, true); var streamId = headers.header.streamId; @@ -341,7 +341,7 @@ void main() { handshakeCompleter.complete(); - HeadersFrame headers = await nextFrame(); + var headers = await nextFrame() as HeadersFrame; var streamId = headers.header.streamId; @@ -367,7 +367,7 @@ void main() { expect(win.windowSizeIncrement, 1); await clientDone.future; - DataFrame finFrame = await nextFrame(); + var finFrame = await nextFrame() as DataFrame; expect(finFrame.hasEndStreamFlag, true); // Wait for the client finish. @@ -411,8 +411,8 @@ void main() { handshakeCompleter.complete(); - HeadersFrame headers = await nextFrame(); - DataFrame finFrame = await nextFrame(); + var headers = await nextFrame() as HeadersFrame; + var finFrame = await nextFrame() as DataFrame; expect(finFrame.hasEndStreamFlag, true); var streamId = headers.header.streamId; @@ -427,7 +427,7 @@ void main() { streamId, pushStreamId, [Header.ascii('a', 'b')]); // Make sure we get a connection error. - GoawayFrame frame = await nextFrame(); + var frame = await nextFrame() as GoawayFrame; expect(ascii.decode(frame.debugData), contains('Cannot push on a non-existent stream')); expect(await serverReader.moveNext(), false); @@ -464,7 +464,7 @@ void main() { handshakeCompleter.complete(); - HeadersFrame headers = await nextFrame(); + var headers = await nextFrame() as HeadersFrame; var streamId = headers.header.streamId; // Write response. @@ -476,7 +476,7 @@ void main() { streamId, pushStreamId, [Header.ascii('a', 'b')]); // Make sure we get a connection error. - GoawayFrame frame = await nextFrame(); + var frame = await nextFrame() as GoawayFrame; expect( ascii.decode(frame.debugData), contains( @@ -515,7 +515,7 @@ void main() { handshakeCompleter.complete(); - HeadersFrame headers = await nextFrame(); + var headers = await nextFrame() as HeadersFrame; var streamId = headers.header.streamId; // Write more than [kFlowControlWindowSize] bytes. @@ -529,7 +529,7 @@ void main() { // Read the resulting [GoawayFrame] and assert the error message // describes that the flow control window became negative. - GoawayFrame frame = await nextFrame(); + var frame = await nextFrame() as GoawayFrame; expect( ascii.decode(frame.debugData), contains('Connection level flow control window became ' diff --git a/pkgs/http2/test/multiprotocol_server_test.dart b/pkgs/http2/test/multiprotocol_server_test.dart index 9b55fdd4c2..1eb2663861 100644 --- a/pkgs/http2/test/multiprotocol_server_test.dart +++ b/pkgs/http2/test/multiprotocol_server_test.dart @@ -95,7 +95,7 @@ Future makeHttp2Request(MultiProtocolHttpServer server, expect(await si.moveNext(), true); expect(si.current is HeadersStreamMessage, true); - var responseHeaders = getHeaders(si.current); + var responseHeaders = getHeaders(si.current as HeadersStreamMessage); expect(responseHeaders[':status'], '200'); expect(await si.moveNext(), true); @@ -109,7 +109,7 @@ Future handleHttp2Request(ServerTransportStream stream, int i) async { expect(await si.moveNext(), true); expect(si.current is HeadersStreamMessage, true); - var headers = getHeaders(si.current); + var headers = getHeaders(si.current as HeadersStreamMessage); expect(headers[':path'], '/abc$i'); expect(await si.moveNext(), false); diff --git a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart index 0bc7432e98..7989c1a860 100644 --- a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart @@ -17,8 +17,8 @@ import 'package:http2/src/flowcontrol/queue_messages.dart'; void main() { group('flowcontrol', () { test('connection-message-queue-out', () { - dynamic fw = MockFrameWriter(); - dynamic windowMock = MockOutgoingWindowHandler(); + var fw = MockFrameWriter(); + var windowMock = MockOutgoingWindowHandler(); var queue = ConnectionMessageQueueOut(windowMock, fw); fw.bufferIndicator.markUnBuffered(); @@ -92,12 +92,12 @@ void main() { const STREAM_ID = 99; final bytes = [1, 2, 3]; - dynamic windowMock = MockIncomingWindowHandler(); + var windowMock = MockIncomingWindowHandler(); var queue = ConnectionMessageQueueIn(windowMock, (f) => f()); expect(queue.pendingMessages, 0); - dynamic streamQueueMock = MockStreamMessageQueueIn(); + var streamQueueMock = MockStreamMessageQueueIn(); queue.insertNewStreamMessageQueue(STREAM_ID, streamQueueMock); // Insert a [DataFrame] and let it be buffered. @@ -115,8 +115,9 @@ void main() { // specific queue. streamQueueMock.bufferIndicator.markUnBuffered(); verify(windowMock.dataProcessed(bytes.length)).called(1); - DataMessage capturedMessage = - verify(streamQueueMock.enqueueMessage(captureAny)).captured.single; + var capturedMessage = verify(streamQueueMock.enqueueMessage(captureAny)) + .captured + .single as DataMessage; expect(capturedMessage.streamId, STREAM_ID); expect(capturedMessage.bytes, bytes); @@ -130,7 +131,7 @@ void main() { const STREAM_ID = 99; final bytes = [1, 2, 3]; - dynamic windowMock = MockIncomingWindowHandler(); + var windowMock = MockIncomingWindowHandler(); var queue = ConnectionMessageQueueIn(windowMock, (f) => f()); // Insert a [DataFrame] and let it be buffered. diff --git a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart index 4a2aa65a75..5e6b94bfd1 100644 --- a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart @@ -19,8 +19,8 @@ void main() { group('stream-message-queue-out', () { test('window-big-enough', () { - dynamic connectionQueueMock = MockConnectionMessageQueueOut(); - dynamic windowMock = MockOutgoingStreamWindowHandler(); + var connectionQueueMock = MockConnectionMessageQueueOut(); + var windowMock = MockOutgoingStreamWindowHandler(); windowMock.positiveWindow.markUnBuffered(); var queue = @@ -37,14 +37,14 @@ void main() { .captured .single; expect(capturedMessage, const TypeMatcher()); - DataMessage capturedDataMessage = capturedMessage; + var capturedDataMessage = capturedMessage as DataMessage; expect(capturedDataMessage.bytes, BYTES); expect(capturedDataMessage.endStream, isTrue); }); test('window-smaller-than-necessary', () { - dynamic connectionQueueMock = MockConnectionMessageQueueOut(); - dynamic windowMock = MockOutgoingStreamWindowHandler(); + var connectionQueueMock = MockConnectionMessageQueueOut(); + var windowMock = MockOutgoingStreamWindowHandler(); windowMock.positiveWindow.markUnBuffered(); var queue = @@ -65,7 +65,7 @@ void main() { expect(messages, hasLength(BYTES.length)); for (var counter = 0; counter < messages.length; counter++) { expect(messages[counter], const TypeMatcher()); - DataMessage dataMessage = messages[counter]; + var dataMessage = messages[counter] as DataMessage; expect(dataMessage.bytes, BYTES.sublist(counter, counter + 1)); expect(dataMessage.endStream, counter == BYTES.length - 1); } @@ -94,14 +94,14 @@ void main() { group('stream-message-queue-in', () { test('data-end-of-stream', () { - dynamic windowMock = MockIncomingWindowHandler(); - dynamic queue = StreamMessageQueueIn(windowMock); + var windowMock = MockIncomingWindowHandler(); + var queue = StreamMessageQueueIn(windowMock); expect(queue.pendingMessages, 0); queue.messages.listen(expectAsync1((StreamMessage message) { expect(message is DataStreamMessage, isTrue); - DataStreamMessage dataMessage = message; + var dataMessage = message as DataStreamMessage; expect(dataMessage.bytes, BYTES); }), onDone: expectAsync0(() {})); queue.enqueueMessage(DataMessage(STREAM_ID, BYTES, true)); @@ -118,8 +118,8 @@ void main() { const STREAM_ID = 99; final bytes = [1, 2, 3]; - dynamic windowMock = MockIncomingWindowHandler(); - dynamic queue = StreamMessageQueueIn(windowMock); + var windowMock = MockIncomingWindowHandler(); + var queue = StreamMessageQueueIn(windowMock); var sub = queue.messages.listen(expectAsync1((_) {}, count: 0), onDone: expectAsync0(() {}, count: 0)); diff --git a/pkgs/http2/test/src/flowcontrol/window_handler_test.dart b/pkgs/http2/test/src/flowcontrol/window_handler_test.dart index 705d32028e..5f1dc5c989 100644 --- a/pkgs/http2/test/src/flowcontrol/window_handler_test.dart +++ b/pkgs/http2/test/src/flowcontrol/window_handler_test.dart @@ -102,7 +102,7 @@ void main() { test('incoming-window-handler', () { const STREAM_ID = 99; - dynamic fw = FrameWriterMock(); + var fw = FrameWriterMock(); var window = Window(); var initialSize = window.size; var handler = IncomingWindowHandler.stream(fw, window, STREAM_ID); diff --git a/pkgs/http2/test/src/frames/frame_defragmenter_test.dart b/pkgs/http2/test/src/frames/frame_defragmenter_test.dart index 899154a28d..87067e8827 100644 --- a/pkgs/http2/test/src/frames/frame_defragmenter_test.dart +++ b/pkgs/http2/test/src/frames/frame_defragmenter_test.dart @@ -54,7 +54,7 @@ void main() { expect(defrag.tryDefragmentFrame(f1), isNull); expect(defrag.tryDefragmentFrame(f2), isNull); - HeadersFrame h = defrag.tryDefragmentFrame(f3); + var h = defrag.tryDefragmentFrame(f3) as HeadersFrame; expect(h.hasEndHeadersFlag, isTrue); expect(h.hasEndStreamFlag, isFalse); expect(h.hasPaddedFlag, isFalse); @@ -71,7 +71,7 @@ void main() { expect(defrag.tryDefragmentFrame(f1), isNull); expect(defrag.tryDefragmentFrame(f2), isNull); - PushPromiseFrame h = defrag.tryDefragmentFrame(f3); + var h = defrag.tryDefragmentFrame(f3) as PushPromiseFrame; expect(h.hasEndHeadersFlag, isTrue); expect(h.hasPaddedFlag, isFalse); expect(h.padLength, 0); diff --git a/pkgs/http2/test/src/frames/frame_reader_test.dart b/pkgs/http2/test/src/frames/frame_reader_test.dart index 99c8fe5535..5da16628d5 100644 --- a/pkgs/http2/test/src/frames/frame_reader_test.dart +++ b/pkgs/http2/test/src/frames/frame_reader_test.dart @@ -40,7 +40,7 @@ void main() { expect(frame is DataFrame, isTrue); expect(frame.header, hasLength(body.length)); expect(frame.header.flags, 0); - DataFrame dataFrame = frame; + var dataFrame = frame as DataFrame; expect(dataFrame.hasEndStreamFlag, isFalse); expect(dataFrame.hasPaddedFlag, isFalse); expect(dataFrame.bytes, body); diff --git a/pkgs/http2/test/src/frames/frame_writer_reader_test.dart b/pkgs/http2/test/src/frames/frame_writer_reader_test.dart index f15d7882d1..c0f502fe09 100644 --- a/pkgs/http2/test/src/frames/frame_writer_reader_test.dart +++ b/pkgs/http2/test/src/frames/frame_writer_reader_test.dart @@ -23,7 +23,7 @@ void main() { expect(frames, hasLength(1)); expect(frames[0] is DataFrame, isTrue); - DataFrame dataFrame = frames[0]; + var dataFrame = frames[0] as DataFrame; expect(dataFrame.header.streamId, 99); expect(dataFrame.hasPaddedFlag, isFalse); expect(dataFrame.padLength, 0); @@ -39,7 +39,7 @@ void main() { expect(frames, hasLength(1)); expect(frames[0] is HeadersFrame, isTrue); - HeadersFrame headersFrame = frames[0]; + var headersFrame = frames[0] as HeadersFrame; expect(headersFrame.header.streamId, 99); expect(headersFrame.hasPaddedFlag, isFalse); expect(headersFrame.padLength, 0); @@ -63,7 +63,7 @@ void main() { expect(frames, hasLength(1)); expect(frames[0] is PriorityFrame, isTrue); - PriorityFrame priorityFrame = frames[0]; + var priorityFrame = frames[0] as PriorityFrame; expect(priorityFrame.header.streamId, 99); expect(priorityFrame.exclusiveDependency, isTrue); expect(priorityFrame.streamDependency, 44); @@ -78,7 +78,7 @@ void main() { expect(frames, hasLength(1)); expect(frames[0] is RstStreamFrame, isTrue); - RstStreamFrame rstFrame = frames[0]; + var rstFrame = frames[0] as RstStreamFrame; expect(rstFrame.header.streamId, 99); expect(rstFrame.errorCode, 42); }); @@ -91,7 +91,7 @@ void main() { expect(frames, hasLength(1)); expect(frames[0] is SettingsFrame, isTrue); - SettingsFrame settingsFrame = frames[0]; + var settingsFrame = frames[0] as SettingsFrame; expect(settingsFrame.hasAckFlag, false); expect(settingsFrame.header.streamId, 0); expect(settingsFrame.settings, hasLength(1)); @@ -108,7 +108,7 @@ void main() { expect(frames, hasLength(1)); expect(frames[0] is SettingsFrame, isTrue); - SettingsFrame settingsFrame = frames[0]; + var settingsFrame = frames[0] as SettingsFrame; expect(settingsFrame.hasAckFlag, true); expect(settingsFrame.header.streamId, 0); expect(settingsFrame.settings, hasLength(0)); @@ -122,7 +122,7 @@ void main() { expect(frames, hasLength(1)); expect(frames[0] is PushPromiseFrame, isTrue); - PushPromiseFrame pushPromiseFrame = frames[0]; + var pushPromiseFrame = frames[0] as PushPromiseFrame; expect(pushPromiseFrame.header.streamId, 99); expect(pushPromiseFrame.hasEndHeadersFlag, isTrue); expect(pushPromiseFrame.hasPaddedFlag, isFalse); @@ -142,7 +142,7 @@ void main() { expect(frames, hasLength(1)); expect(frames[0] is PingFrame, isTrue); - PingFrame pingFrame = frames[0]; + var pingFrame = frames[0] as PingFrame; expect(pingFrame.header.streamId, 0); expect(pingFrame.opaqueData, 44); expect(pingFrame.hasAckFlag, isTrue); @@ -156,7 +156,7 @@ void main() { expect(frames, hasLength(1)); expect(frames[0] is GoawayFrame, isTrue); - GoawayFrame goawayFrame = frames[0]; + var goawayFrame = frames[0] as GoawayFrame; expect(goawayFrame.header.streamId, 0); expect(goawayFrame.lastStreamId, 44); expect(goawayFrame.errorCode, 33); @@ -171,7 +171,7 @@ void main() { expect(frames, hasLength(1)); expect(frames[0] is WindowUpdateFrame, isTrue); - WindowUpdateFrame windowUpdateFrame = frames[0]; + var windowUpdateFrame = frames[0] as WindowUpdateFrame; expect(windowUpdateFrame.header.streamId, 99); expect(windowUpdateFrame.windowSizeIncrement, 55); }); @@ -189,7 +189,7 @@ void main() { expect(frames[0] is HeadersFrame, isTrue); expect(frames[1] is ContinuationFrame, isTrue); - HeadersFrame headersFrame = frames[0]; + var headersFrame = frames[0] as HeadersFrame; expect(headersFrame.header.streamId, 99); expect(headersFrame.hasPaddedFlag, isFalse); expect(headersFrame.padLength, 0); @@ -197,7 +197,7 @@ void main() { expect(headersFrame.hasEndStreamFlag, isTrue); expect(headersFrame.hasPriorityFlag, isFalse); - ContinuationFrame contFrame = frames[1]; + var contFrame = frames[1] as ContinuationFrame; expect(contFrame.header.streamId, 99); expect(contFrame.hasEndHeadersFlag, isTrue); @@ -229,5 +229,5 @@ void writerReaderTest(String name, Future> finishWriting(FrameWriter writer, FrameReader reader) { return Future.wait([writer.close(), reader.startDecoding().toList()]) - .then((results) => results.last); + .then((results) => results.last as List); } diff --git a/pkgs/http2/test/src/ping/ping_handler_test.dart b/pkgs/http2/test/src/ping/ping_handler_test.dart index e5e0de4823..c2bdeab80a 100644 --- a/pkgs/http2/test/src/ping/ping_handler_test.dart +++ b/pkgs/http2/test/src/ping/ping_handler_test.dart @@ -14,7 +14,7 @@ import '../error_matchers.dart'; void main() { group('ping-handler', () { test('successful-ping', () async { - dynamic writer = FrameWriterMock(); + var writer = FrameWriterMock(); var pingHandler = PingHandler(writer); var p1 = pingHandler.ping(); @@ -36,7 +36,7 @@ void main() { }); test('successful-ack-to-remote-ping', () async { - dynamic writer = FrameWriterMock(); + var writer = FrameWriterMock(); var pingHandler = PingHandler(writer); var header = FrameHeader(8, FrameType.PING, 0, 0); @@ -52,7 +52,7 @@ void main() { }); test('ping-unknown-opaque-data', () async { - dynamic writer = FrameWriterMock(); + var writer = FrameWriterMock(); var pingHandler = PingHandler(writer); var future = pingHandler.ping(); diff --git a/pkgs/http2/test/src/settings/settings_handler_test.dart b/pkgs/http2/test/src/settings/settings_handler_test.dart index d24ba2ed4f..202914ee14 100644 --- a/pkgs/http2/test/src/settings/settings_handler_test.dart +++ b/pkgs/http2/test/src/settings/settings_handler_test.dart @@ -18,7 +18,7 @@ void main() { var setMaxTable256 = [Setting(Setting.SETTINGS_HEADER_TABLE_SIZE, 256)]; test('successful-setting', () async { - dynamic writer = FrameWriterMock(); + var writer = FrameWriterMock(); var sh = SettingsHandler( HPackEncoder(), writer, ActiveSettings(), ActiveSettings()); @@ -42,7 +42,7 @@ void main() { }); test('ack-remote-settings-change', () { - dynamic writer = FrameWriterMock(); + var writer = FrameWriterMock(); var sh = SettingsHandler( HPackEncoder(), writer, ActiveSettings(), ActiveSettings()); @@ -91,8 +91,8 @@ void main() { }); test('change-max-header-table-size', () { - dynamic writer = FrameWriterMock(); - dynamic mock = HPackEncoderMock(); + var writer = FrameWriterMock(); + var mock = HPackEncoderMock(); var sh = SettingsHandler(mock, writer, ActiveSettings(), ActiveSettings()); diff --git a/pkgs/http2/test/src/streams/streams_test.dart b/pkgs/http2/test/src/streams/streams_test.dart index aedc422598..5f71c930fd 100644 --- a/pkgs/http2/test/src/streams/streams_test.dart +++ b/pkgs/http2/test/src/streams/streams_test.dart @@ -19,7 +19,7 @@ void main() { sStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { expect(msg is HeadersStreamMessage, isTrue); - HeadersStreamMessage headersMsg = msg; + var headersMsg = msg as HeadersStreamMessage; expectHeadersEqual(headersMsg.headers, expectedHeaders); }), onDone: expectAsync0(() {})); sStream.outgoingMessages.close(); @@ -40,7 +40,7 @@ void main() { expectAsync1((StreamMessage msg) { expect(msg is HeadersStreamMessage, isTrue); - HeadersStreamMessage headersMsg = msg; + var headersMsg = msg as HeadersStreamMessage; expectHeadersEqual(headersMsg.headers, expectedHeaders); }, count: 3), onDone: expectAsync0(() {})); @@ -73,12 +73,12 @@ void main() { isFirst = false; expect(msg is HeadersStreamMessage, isTrue); - HeadersStreamMessage headersMsg = msg; + var headersMsg = msg as HeadersStreamMessage; expectHeadersEqual(headersMsg.headers, expectedHeaders); } else { expect(msg is DataStreamMessage, isTrue); - DataStreamMessage dataMsg = msg; + var dataMsg = msg as DataStreamMessage; receivedChunks.add(dataMsg.bytes); } }, count: 1 + chunks.length), onDone: expectAsync0(() { @@ -102,7 +102,7 @@ void main() { sStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { expect(msg is HeadersStreamMessage, isTrue); - HeadersStreamMessage headersMsg = msg; + var headersMsg = msg as HeadersStreamMessage; expectHeadersEqual(headersMsg.headers, expectedHeaders); }), onDone: expectAsync0(() {})); sStream.sendHeaders(expectedHeaders, endStream: true); @@ -114,7 +114,7 @@ void main() { cStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { expect(msg is HeadersStreamMessage, isTrue); - HeadersStreamMessage headersMsg = msg; + var headersMsg = msg as HeadersStreamMessage; expectHeadersEqual(headersMsg.headers, expectedHeaders); }), onDone: expectAsync0(() {})); }); @@ -128,7 +128,7 @@ void main() { sStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { expect(msg is HeadersStreamMessage, isTrue); - HeadersStreamMessage headersMsg = msg; + var headersMsg = msg as HeadersStreamMessage; expectHeadersEqual(headersMsg.headers, expectedHeaders); }), onDone: expectAsync0(() {})); @@ -143,7 +143,7 @@ void main() { cStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { expect(msg is HeadersStreamMessage, isTrue); - HeadersStreamMessage headersMsg = msg; + var headersMsg = msg as HeadersStreamMessage; expectHeadersEqual(headersMsg.headers, expectedHeaders); }, count: 3)); }); @@ -162,7 +162,7 @@ void main() { sStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { expect(msg is HeadersStreamMessage, isTrue); - HeadersStreamMessage headersMsg = msg; + var headersMsg = msg as HeadersStreamMessage; expectHeadersEqual(headersMsg.headers, expectedHeaders); }), onDone: expectAsync0(() {})); @@ -197,14 +197,14 @@ void main() { expect(msg is HeadersStreamMessage, isTrue); expect(msg.endStream, false); - HeadersStreamMessage headersMsg = msg; + var headersMsg = msg as HeadersStreamMessage; expectHeadersEqual(headersMsg.headers, expectedHeaders); } else { expect(msg is DataStreamMessage, isTrue); expect(msg.endStream, true); expect(receivedChunk, null); - DataStreamMessage dataMsg = msg; + var dataMsg = msg as DataStreamMessage; receivedChunk = dataMsg.bytes; } }, count: 2), onDone: expectAsync0(() { diff --git a/pkgs/http2/test/transport_test.dart b/pkgs/http2/test/transport_test.dart index b6cdf42d04..e3745a81aa 100644 --- a/pkgs/http2/test/transport_test.dart +++ b/pkgs/http2/test/transport_test.dart @@ -126,7 +126,7 @@ void main() { Future serverFun() async { await for (ServerTransportStream stream in server.incomingStreams) { - var pushes = []; + var pushes = []; for (var i = 0; i < kDefaultStreamLimit; i++) { expect(stream.canPush, true); pushes.add(stream.push([Header.ascii('a', 'b')])); @@ -139,7 +139,7 @@ void main() { throwsA(const TypeMatcher())); // Finish the pushes - for (ServerTransportStream pushedStream in pushes) { + for (var pushedStream in pushes) { pushedStream .sendHeaders([Header.ascii('e', 'nd')], endStream: true); await pushedStream.incomingMessages.toList(); @@ -471,7 +471,7 @@ void main() { gotHeadersFrame = true; } else { expect(message is DataStreamMessage, true); - DataStreamMessage dataMessage = message; + var dataMessage = message as DataStreamMessage; // We're just testing the first byte, to make the test faster. expect(dataMessage.bytes[0], From 89f95665e690a3e7d91c85919ec3ab2bbb60cd22 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 20 Feb 2020 14:56:48 -0800 Subject: [PATCH 105/172] Use isA type matcher in tests (dart-lang/http2#66) The failure output is nicer when using a TypeMatcher over using an `is` check and comparing against the boolean `true`. --- pkgs/http2/test/client_test.dart | 39 ++++++++++++------- .../http2/test/multiprotocol_server_test.dart | 4 +- pkgs/http2/test/server_test.dart | 36 ++++++++++------- .../src/flowcontrol/stream_queues_test.dart | 2 +- .../test/src/frames/frame_reader_test.dart | 2 +- .../test/src/streams/simple_flow_test.dart | 26 +++++++------ .../test/src/streams/simple_push_test.dart | 4 +- pkgs/http2/test/src/streams/streams_test.dart | 28 ++++++------- pkgs/http2/test/transport_test.dart | 22 +++++------ 9 files changed, 92 insertions(+), 71 deletions(-) diff --git a/pkgs/http2/test/client_test.dart b/pkgs/http2/test/client_test.dart index 9adbce22f4..76d25a008a 100644 --- a/pkgs/http2/test/client_test.dart +++ b/pkgs/http2/test/client_test.dart @@ -37,9 +37,10 @@ void main() { settingsDone.complete(); // Make sure we get the graceful shutdown message. - var frame = await nextFrame(); - expect(frame is GoawayFrame, true); - expect((frame as GoawayFrame).errorCode, ErrorCode.NO_ERROR); + expect( + await nextFrame(), + isA() + .having((f) => f.errorCode, 'errorCode', ErrorCode.NO_ERROR)); // Make sure the client ended the connection. expect(await serverReader.moveNext(), false); @@ -154,10 +155,13 @@ void main() { serverWriter.writeDataFrame(invalidStreamId, [42]); // Make sure the client sends a [RstStreamFrame] frame. - var frame = await nextFrame(); - expect(frame is RstStreamFrame, true); - expect((frame as RstStreamFrame).errorCode, ErrorCode.STREAM_CLOSED); - expect((frame as RstStreamFrame).header.streamId, invalidStreamId); + expect( + await nextFrame(), + isA() + .having( + (f) => f.errorCode, 'errorCode', ErrorCode.STREAM_CLOSED) + .having((f) => f.header.streamId, 'header.streamId', + invalidStreamId)); // Close the original stream. serverWriter.writeDataFrame(headers.header.streamId, [], @@ -221,10 +225,13 @@ void main() { expect(win.windowSizeIncrement, 1); // Make sure we get a [RstStreamFrame] frame. - var frame = await nextFrame(); - expect(frame is RstStreamFrame, true); - expect((frame as RstStreamFrame).errorCode, ErrorCode.STREAM_CLOSED); - expect((frame as RstStreamFrame).header.streamId, streamId); + expect( + await nextFrame(), + isA() + .having( + (f) => f.errorCode, 'errorCode', ErrorCode.STREAM_CLOSED) + .having( + (f) => f.header.streamId, 'header.streamId', streamId)); // Wait for the client finish. expect(await nextFrame() is GoawayFrame, true); @@ -290,10 +297,12 @@ void main() { expect(win.windowSizeIncrement, 1); // Make sure we get a [RstStreamFrame] frame. - var frame = await nextFrame(); - expect(frame is RstStreamFrame, true); - expect((frame as RstStreamFrame).errorCode, ErrorCode.CANCEL); - expect((frame as RstStreamFrame).header.streamId, streamId); + expect( + await nextFrame(), + isA() + .having((f) => f.errorCode, 'errorCode', ErrorCode.CANCEL) + .having( + (f) => f.header.streamId, 'header.streamId', streamId)); serverWriter.writeRstStreamFrame(streamId, ErrorCode.STREAM_CLOSED); diff --git a/pkgs/http2/test/multiprotocol_server_test.dart b/pkgs/http2/test/multiprotocol_server_test.dart index 1eb2663861..42c11ba187 100644 --- a/pkgs/http2/test/multiprotocol_server_test.dart +++ b/pkgs/http2/test/multiprotocol_server_test.dart @@ -94,7 +94,7 @@ Future makeHttp2Request(MultiProtocolHttpServer server, var si = StreamIterator(stream.incomingMessages); expect(await si.moveNext(), true); - expect(si.current is HeadersStreamMessage, true); + expect(si.current, isA()); var responseHeaders = getHeaders(si.current as HeadersStreamMessage); expect(responseHeaders[':status'], '200'); @@ -108,7 +108,7 @@ Future handleHttp2Request(ServerTransportStream stream, int i) async { var si = StreamIterator(stream.incomingMessages); expect(await si.moveNext(), true); - expect(si.current is HeadersStreamMessage, true); + expect(si.current, isA()); var headers = getHeaders(si.current as HeadersStreamMessage); expect(headers[':path'], '/abc$i'); diff --git a/pkgs/http2/test/server_test.dart b/pkgs/http2/test/server_test.dart index c9b8cc2811..28b8d46acf 100644 --- a/pkgs/http2/test/server_test.dart +++ b/pkgs/http2/test/server_test.dart @@ -61,9 +61,10 @@ void main() { clientWriter.writeHeadersFrame(1, [], endStream: true); // Make sure the client gets a [GoawayFrame] frame. - var frame = await nextFrame(); - expect(frame is GoawayFrame, true); - expect((frame as GoawayFrame).errorCode, ErrorCode.PROTOCOL_ERROR); + expect( + await nextFrame(), + isA().having( + (f) => f.errorCode, 'errorCode', ErrorCode.PROTOCOL_ERROR)); // Make sure the server ended the connection. expect(await clientReader.moveNext(), false); @@ -92,10 +93,12 @@ void main() { clientWriter.writeDataFrame(3, [1, 2, 3]); // Make sure the client gets a [RstStreamFrame] frame. - var frame = await nextFrame(); - expect(frame is RstStreamFrame, true); - expect((frame as RstStreamFrame).errorCode, ErrorCode.STREAM_CLOSED); - expect((frame as RstStreamFrame).header.streamId, 3); + expect( + await nextFrame(), + isA() + .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, []); @@ -131,10 +134,12 @@ void main() { clientWriter.writeDataFrame(3, [1, 2, 3]); // Make sure the client gets a [RstStreamFrame] frame. - var frame = await nextFrame(); - expect(frame is RstStreamFrame, true); - expect((frame as RstStreamFrame).errorCode, ErrorCode.STREAM_CLOSED); - expect((frame as RstStreamFrame).header.streamId, 3); + expect( + await nextFrame(), + isA() + .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, []); @@ -174,10 +179,11 @@ void main() { endStream: false); // Make sure the client gets a [RstStreamFrame] frame. - var frame = await nextFrame(); - expect(frame is RstStreamFrame, true); - expect((frame as RstStreamFrame).errorCode, ErrorCode.CANCEL); - expect((frame as RstStreamFrame).header.streamId, 1); + expect( + await nextFrame(), + isA() + .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, []); diff --git a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart index 5e6b94bfd1..ed7db2ea9d 100644 --- a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart @@ -99,7 +99,7 @@ void main() { expect(queue.pendingMessages, 0); queue.messages.listen(expectAsync1((StreamMessage message) { - expect(message is DataStreamMessage, isTrue); + expect(message, isA()); var dataMessage = message as DataStreamMessage; expect(dataMessage.bytes, BYTES); diff --git a/pkgs/http2/test/src/frames/frame_reader_test.dart b/pkgs/http2/test/src/frames/frame_reader_test.dart index 5da16628d5..d35fec6fb3 100644 --- a/pkgs/http2/test/src/frames/frame_reader_test.dart +++ b/pkgs/http2/test/src/frames/frame_reader_test.dart @@ -37,7 +37,7 @@ void main() { test('data-frame--max-frame-size', () { var body = List.filled(maxFrameSize, 0x42); dataFrame(body).listen(expectAsync1((Frame frame) { - expect(frame is DataFrame, isTrue); + expect(frame, isA()); expect(frame.header, hasLength(body.length)); expect(frame.header.flags, 0); var dataFrame = frame as DataFrame; diff --git a/pkgs/http2/test/src/streams/simple_flow_test.dart b/pkgs/http2/test/src/streams/simple_flow_test.dart index 197bccef84..2f15deac81 100644 --- a/pkgs/http2/test/src/streams/simple_flow_test.dart +++ b/pkgs/http2/test/src/streams/simple_flow_test.dart @@ -20,11 +20,13 @@ void main() { void Function(StreamMessage) headersTestFun(String type) { return expectAsync1((StreamMessage msg) { - expect(msg is HeadersStreamMessage, isTrue); - expect((msg as HeadersStreamMessage).headers.first.name, - expectedHeaders.first.name); - expect((msg as HeadersStreamMessage).headers.first.value, - expectedHeaders.first.value); + expect( + msg, + isA() + .having((m) => m.headers.first.name, 'headers.first.name', + expectedHeaders.first.name) + .having((m) => m.headers.first.value, 'headers.first.value', + expectedHeaders.first.value)); }); } @@ -36,13 +38,15 @@ void main() { return (StreamMessage msg) { if (expectHeader) { expectHeader = false; - expect(msg is HeadersStreamMessage, isTrue); - expect((msg as HeadersStreamMessage).headers.first.name, - expectedHeaders.first.name); - expect((msg as HeadersStreamMessage).headers.first.value, - expectedHeaders.first.value); + expect( + msg, + isA() + .having((m) => m.headers.first.name, 'headers.first.name', + expectedHeaders.first.name) + .having((m) => m.headers.first.value, 'headers.first.value', + expectedHeaders.first.value)); } else { - expect(msg is DataStreamMessage, isTrue); + expect(msg, isA()); var bytes = (msg as DataStreamMessage).bytes; expect( bytes, diff --git a/pkgs/http2/test/src/streams/simple_push_test.dart b/pkgs/http2/test/src/streams/simple_push_test.dart index d3946c0755..f8a96f28fc 100644 --- a/pkgs/http2/test/src/streams/simple_push_test.dart +++ b/pkgs/http2/test/src/streams/simple_push_test.dart @@ -30,7 +30,7 @@ void main() { void Function(StreamMessage) headersTestFun() { return expectAsync1((StreamMessage msg) { - expect(msg is HeadersStreamMessage, isTrue); + expect(msg, isA()); testHeaders((msg as HeadersStreamMessage).headers); }); } @@ -42,7 +42,7 @@ void main() { while (await iterator.moveNext()) { var msg = iterator.current; - expect(msg is DataStreamMessage, isTrue); + expect(msg, isA()); all.addAll((msg as DataStreamMessage).bytes); } diff --git a/pkgs/http2/test/src/streams/streams_test.dart b/pkgs/http2/test/src/streams/streams_test.dart index 5f71c930fd..ebfdea71c7 100644 --- a/pkgs/http2/test/src/streams/streams_test.dart +++ b/pkgs/http2/test/src/streams/streams_test.dart @@ -17,7 +17,7 @@ void main() { server.incomingStreams.listen(expectAsync1((TransportStream sStream) { sStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { - expect(msg is HeadersStreamMessage, isTrue); + expect(msg, isA()); var headersMsg = msg as HeadersStreamMessage; expectHeadersEqual(headersMsg.headers, expectedHeaders); @@ -38,7 +38,7 @@ void main() { server.incomingStreams.listen(expectAsync1((TransportStream sStream) { sStream.incomingMessages.listen( expectAsync1((StreamMessage msg) { - expect(msg is HeadersStreamMessage, isTrue); + expect(msg, isA()); var headersMsg = msg as HeadersStreamMessage; expectHeadersEqual(headersMsg.headers, expectedHeaders); @@ -71,12 +71,12 @@ void main() { expectAsync1((StreamMessage msg) { if (isFirst) { isFirst = false; - expect(msg is HeadersStreamMessage, isTrue); + expect(msg, isA()); var headersMsg = msg as HeadersStreamMessage; expectHeadersEqual(headersMsg.headers, expectedHeaders); } else { - expect(msg is DataStreamMessage, isTrue); + expect(msg, isA()); var dataMsg = msg as DataStreamMessage; receivedChunks.add(dataMsg.bytes); @@ -100,7 +100,7 @@ void main() { server.incomingStreams.listen(expectAsync1((TransportStream sStream) { sStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { - expect(msg is HeadersStreamMessage, isTrue); + expect(msg, isA()); var headersMsg = msg as HeadersStreamMessage; expectHeadersEqual(headersMsg.headers, expectedHeaders); @@ -112,7 +112,7 @@ void main() { client.makeRequest(expectedHeaders, endStream: true); cStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { - expect(msg is HeadersStreamMessage, isTrue); + expect(msg, isA()); var headersMsg = msg as HeadersStreamMessage; expectHeadersEqual(headersMsg.headers, expectedHeaders); @@ -126,7 +126,7 @@ void main() { server.incomingStreams.listen(expectAsync1((TransportStream sStream) { sStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { - expect(msg is HeadersStreamMessage, isTrue); + expect(msg, isA()); var headersMsg = msg as HeadersStreamMessage; expectHeadersEqual(headersMsg.headers, expectedHeaders); @@ -141,7 +141,7 @@ void main() { client.makeRequest(expectedHeaders, endStream: true); cStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { - expect(msg is HeadersStreamMessage, isTrue); + expect(msg, isA()); var headersMsg = msg as HeadersStreamMessage; expectHeadersEqual(headersMsg.headers, expectedHeaders); @@ -160,7 +160,7 @@ void main() { server.incomingStreams.listen(expectAsync1((TransportStream sStream) { sStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { - expect(msg is HeadersStreamMessage, isTrue); + expect(msg, isA()); var headersMsg = msg as HeadersStreamMessage; expectHeadersEqual(headersMsg.headers, expectedHeaders); @@ -175,8 +175,10 @@ void main() { var i = 0; cStream.incomingMessages.listen(expectAsync1((StreamMessage msg) { - expect(msg is DataStreamMessage, isTrue); - expect((msg as DataStreamMessage).bytes, chunks[i++]); + expect( + msg, + isA() + .having((m) => m.bytes, 'bytes', chunks[i++])); }, count: chunks.length)); }); }); @@ -194,13 +196,13 @@ void main() { expectAsync1((StreamMessage msg) { if (isFirst) { isFirst = false; - expect(msg is HeadersStreamMessage, isTrue); + expect(msg, isA()); expect(msg.endStream, false); var headersMsg = msg as HeadersStreamMessage; expectHeadersEqual(headersMsg.headers, expectedHeaders); } else { - expect(msg is DataStreamMessage, isTrue); + expect(msg, isA()); expect(msg.endStream, true); expect(receivedChunk, null); diff --git a/pkgs/http2/test/transport_test.dart b/pkgs/http2/test/transport_test.dart index e3745a81aa..6f5a9b9573 100644 --- a/pkgs/http2/test/transport_test.dart +++ b/pkgs/http2/test/transport_test.dart @@ -23,7 +23,7 @@ void main() { transportTest('terminated-client-ping', (TransportConnection client, TransportConnection server) async { var clientError = client.ping().catchError(expectAsync2((e, s) { - expect(e is TransportException, isTrue); + expect(e, isA()); })); await client.terminate(); await clientError; @@ -31,17 +31,17 @@ void main() { // NOTE: Now the connection is dead and client/server should complete // with [TransportException]s when doing work (e.g. ping). unawaited(client.ping().catchError(expectAsync2((e, s) { - expect(e is TransportException, isTrue); + expect(e, isA()); }))); unawaited(server.ping().catchError(expectAsync2((e, s) { - expect(e is TransportException, isTrue); + expect(e, isA()); }))); }); transportTest('terminated-server-ping', (TransportConnection client, TransportConnection server) async { var clientError = client.ping().catchError(expectAsync2((e, s) { - expect(e is TransportException, isTrue); + expect(e, isA()); })); await server.terminate(); await clientError; @@ -49,10 +49,10 @@ void main() { // NOTE: Now the connection is dead and the client/server should complete // with [TransportException]s when doing work (e.g. ping). unawaited(client.ping().catchError(expectAsync2((e, s) { - expect(e is TransportException, isTrue); + expect(e, isA()); }))); unawaited(server.ping().catchError(expectAsync2((e, s) { - expect(e is TransportException, isTrue); + expect(e, isA()); }))); }); @@ -221,7 +221,7 @@ void main() { await for (ServerTransportStream stream in server.incomingStreams) { stream.sendHeaders([Header.ascii('x', 'y')], endStream: true); stream.incomingMessages.listen(expectAsync1((msg) { - expect(msg is HeadersStreamMessage, true); + expect(msg, isA()); readyForError.complete(); }), onError: expectAsync1((error) { expect('$error', contains('Stream was terminated by peer')); @@ -275,7 +275,7 @@ void main() { stream.sendHeaders([Header.ascii('x', 'y')], endStream: false); stream.incomingMessages.listen( expectAsync1((msg) { - expect(msg is HeadersStreamMessage, true); + expect(msg, isA()); }), onError: expectAsync1((_) {}, count: 0), onDone: expectAsync0(() { @@ -308,7 +308,7 @@ void main() { stream.sendHeaders([Header.ascii('x', 'y')], endStream: false); stream.incomingMessages.listen( expectAsync1((msg) async { - expect(msg is HeadersStreamMessage, true); + expect(msg, isA()); await readyForError.future; stream.terminate(); }), @@ -467,10 +467,10 @@ void main() { var sub = stream.incomingMessages.listen((message) { if (!gotHeadersFrame) { - expect(message is HeadersStreamMessage, true); + expect(message, isA()); gotHeadersFrame = true; } else { - expect(message is DataStreamMessage, true); + expect(message, isA()); var dataMessage = message as DataStreamMessage; // We're just testing the first byte, to make the test faster. From 123dfd66acbcb1ed3e9f84f46e16d596793d81aa Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Wed, 26 Feb 2020 11:57:31 -0800 Subject: [PATCH 106/172] Use whenComplete over then with catchError (dart-lang/http2#67) Adding the same behavior on either a success or error completion can be simplified. --- pkgs/http2/lib/src/connection.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index 111d422d22..1b7ceadc05 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -165,9 +165,7 @@ abstract class Connection { // Setup frame writing. _frameWriter = FrameWriter(_hpackContext.encoder, outgoing, peerSettings); - _frameWriter.doneFuture.then((_) { - _terminate(ErrorCode.CONNECT_ERROR, causedByTransportError: true); - }).catchError((error, stack) { + _frameWriter.doneFuture.whenComplete(() { _terminate(ErrorCode.CONNECT_ERROR, causedByTransportError: true); }); From f1b3a6fd7e5c23fd42eff360492efa2a9e0e18b5 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Sun, 15 Mar 2020 12:48:41 -0700 Subject: [PATCH 107/172] Remove author from pubspec --- pkgs/http2/pubspec.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index 94f47e4a39..8952f561f5 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -1,7 +1,6 @@ name: http2 version: 1.0.1-dev description: A HTTP/2 implementation in Dart. -author: Dart Team homepage: https://github.com/dart-lang/http2 environment: From 04eabddb1fb1b54a508e7d56495634f95b7eb35a Mon Sep 17 00:00:00 2001 From: Gennadii Kurbatov Date: Mon, 22 Jun 2020 20:39:43 +0300 Subject: [PATCH 108/172] Update README.md Fix example link --- pkgs/http2/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/http2/README.md b/pkgs/http2/README.md index b84dff0096..4bea5dbd49 100644 --- a/pkgs/http2/README.md +++ b/pkgs/http2/README.md @@ -59,4 +59,4 @@ Please file feature requests and bugs at the [issue tracker][tracker]. [tracker]: https://github.com/dart-lang/http2/issues [api]: https://pub.dev/documentation/http2/latest/ -[example]: https://github.com/dart-lang/http2/blob/master/example/display_headers.dart. +[example]: https://github.com/dart-lang/http2/blob/master/example/display_headers.dart From 11c21c9208342ef1b685422e9479fbeb126353d4 Mon Sep 17 00:00:00 2001 From: Vyacheslav Egorov Date: Wed, 23 Sep 2020 15:06:28 +0200 Subject: [PATCH 109/172] Add TransportConnection.onInitialPeerSettingsReceived (dart-lang/http2#70) This allows to detect when settings frame was received from the server thus avoiding any sort of hacks with artificial delays that clients were previously resorting to. This does of course resolve a general problem of potential races between server updating settings and client starting to honor them. --- pkgs/http2/CHANGELOG.md | 3 ++ pkgs/http2/lib/src/connection.dart | 8 +++ pkgs/http2/lib/transport.dart | 4 ++ pkgs/http2/test/client_test.dart | 86 ++++++++++++++++++++++++++++++ 4 files changed, 101 insertions(+) diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index 22dad60f39..6a3971122e 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -1,5 +1,8 @@ ## 1.0.1-dev +* Add `TransportConnection.onInitialPeerSettingsReceived` which fires when + intial SETTINGS frame is received from the peer. + ## 1.0.0 * Graduate package to 1.0. diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index 1b7ceadc05..bc8a9b5e50 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -97,6 +97,13 @@ abstract class Connection { /// Active state handler for this connection. ActiveStateHandler onActiveStateChanged; + final Completer _onInitialPeerSettingsReceived = Completer(); + + /// Future which completes when the first SETTINGS frame is received from + /// the peer. + Future get onInitialPeerSettingsReceived => + _onInitialPeerSettingsReceived.future; + /// The HPack context for this connection. final HPackContext _hpackContext = HPackContext(); @@ -322,6 +329,7 @@ abstract class Connection { return; } _state.state = ConnectionState.Operational; + _onInitialPeerSettingsReceived.complete(); } // Try to defragment [frame] if it is a Headers/PushPromise frame. diff --git a/pkgs/http2/lib/transport.dart b/pkgs/http2/lib/transport.dart index d48dd41896..7bd95a6f79 100644 --- a/pkgs/http2/lib/transport.dart +++ b/pkgs/http2/lib/transport.dart @@ -60,6 +60,10 @@ abstract class TransportConnection { /// from active to idle). set onActiveStateChanged(ActiveStateHandler callback); + /// Future which completes when the first SETTINGS frame is received from + /// the peer. + Future get onInitialPeerSettingsReceived; + /// Finish this connection. /// /// No new streams will be accepted or can be created. diff --git a/pkgs/http2/test/client_test.dart b/pkgs/http2/test/client_test.dart index 76d25a008a..061168f7dc 100644 --- a/pkgs/http2/test/client_test.dart +++ b/pkgs/http2/test/client_test.dart @@ -63,6 +63,92 @@ void main() { }); }); + group('connection-operational', () { + clientTest('on-connection-operational-fires', + (ClientTransportConnection client, + FrameWriter serverWriter, + StreamIterator serverReader, + Future Function() nextFrame) async { + final settingsDone = Completer(); + + Future serverFun() async { + serverWriter.writeSettingsFrame([]); + settingsDone.complete(); + expect(await nextFrame() is SettingsFrame, true); + serverWriter.writeSettingsAckFrame(); + expect(await nextFrame() is SettingsFrame, true); + + // Make sure we get the graceful shutdown message. + expect( + await nextFrame(), + isA() + .having((f) => f.errorCode, 'errorCode', ErrorCode.NO_ERROR)); + + // Make sure the client ended the connection. + expect(await serverReader.moveNext(), false); + } + + Future clientFun() async { + await settingsDone.future; + await client.onInitialPeerSettingsReceived + .timeout(Duration(milliseconds: 20)); // Should complete + + expect(client.isOpen, true); + + // Try to gracefully finish the connection. + var future = client.finish(); + + expect(client.isOpen, false); + + await future; + } + + await Future.wait([serverFun(), clientFun()]); + }); + + clientTest('on-connection-operational-does-not-fire', + (ClientTransportConnection client, + FrameWriter serverWriter, + StreamIterator serverReader, + Future Function() nextFrame) async { + final goawayReceived = Completer(); + Future serverFun() async { + serverWriter.writePingFrame(42); + expect(await nextFrame() is SettingsFrame, true); + expect(await nextFrame() is GoawayFrame, true); + goawayReceived.complete(); + expect(await serverReader.moveNext(), false); + } + + Future clientFun() async { + expect(client.isOpen, true); + + expect( + client.onInitialPeerSettingsReceived + .timeout(Duration(seconds: 1)), + throwsA(isA())); + + // We wait until the server received the error (it's actually later + // than necessary, but we can't make a deterministic test otherwise). + await goawayReceived.future; + + expect(client.isOpen, false); + + var error; + try { + client.makeRequest([Header.ascii('a', 'b')]); + } catch (e) { + error = '$e'; + } + expect(error, contains('no longer active')); + + await client.finish(); + } + + await Future.wait([serverFun(), clientFun()]); + }); + }); + group('server-errors', () { clientTest('no-settings-frame-at-beginning-immediate-error', (ClientTransportConnection client, From 093edd95d914ca70a76c96a5b34d7f8d0070b4c0 Mon Sep 17 00:00:00 2001 From: Vyacheslav Egorov Date: Wed, 23 Sep 2020 16:08:53 +0200 Subject: [PATCH 110/172] Start working on 1.0.2 (dart-lang/http2#71) 1.0.1 is released --- pkgs/http2/CHANGELOG.md | 6 ++++-- pkgs/http2/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index 6a3971122e..4358a8a28f 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -1,7 +1,9 @@ -## 1.0.1-dev +## 1.0.2-dev + +## 1.0.1 * Add `TransportConnection.onInitialPeerSettingsReceived` which fires when - intial SETTINGS frame is received from the peer. + initial SETTINGS frame is received from the peer. ## 1.0.0 diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index 8952f561f5..33f47b66f5 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -1,5 +1,5 @@ name: http2 -version: 1.0.1-dev +version: 1.0.2-dev description: A HTTP/2 implementation in Dart. homepage: https://github.com/dart-lang/http2 From 8f01b401b78eba496702f33613871b6f153c68b6 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 24 Nov 2020 11:53:11 -0800 Subject: [PATCH 111/172] Fix deprecated API usage, bump minimum SDK (dart-lang/http2#72) --- pkgs/http2/.travis.yml | 2 +- pkgs/http2/CHANGELOG.md | 2 ++ pkgs/http2/experimental/server.dart | 4 ++-- pkgs/http2/pubspec.yaml | 4 ++-- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/pkgs/http2/.travis.yml b/pkgs/http2/.travis.yml index 4654b113c7..d3bdcc2e5e 100644 --- a/pkgs/http2/.travis.yml +++ b/pkgs/http2/.travis.yml @@ -1,7 +1,7 @@ language: dart dart: - - 2.5.0 + - 2.8.4 - dev dart_task: diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index 4358a8a28f..11a84441ff 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -1,5 +1,7 @@ ## 1.0.2-dev +* Update minimum Dart SDK to `2.8.4`. + ## 1.0.1 * Add `TransportConnection.onInitialPeerSettingsReceived` which fires when diff --git a/pkgs/http2/experimental/server.dart b/pkgs/http2/experimental/server.dart index 688942522a..51f34dbf5e 100644 --- a/pkgs/http2/experimental/server.dart +++ b/pkgs/http2/experimental/server.dart @@ -26,9 +26,9 @@ void main() async { var server = await SecureServerSocket.bind(HOSTNAME, PORT, context); print('HTTP/2 server listening on https://$HOSTNAME:$PORT'); - runZoned(() { + runZonedGuarded(() { server.listen(handleClient); - }, onError: (e, s) { + }, (e, s) { print('Unexpected error: $e'); print('Unexpected error - stack: $s'); }); diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index 33f47b66f5..1227ce93e1 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -1,10 +1,10 @@ name: http2 version: 1.0.2-dev description: A HTTP/2 implementation in Dart. -homepage: https://github.com/dart-lang/http2 +repository: https://github.com/dart-lang/http2 environment: - sdk: '>=2.5.0 <3.0.0' + sdk: '>=2.8.4 <3.0.0' dev_dependencies: mockito: ^4.0.0 From c7863df51c4582d39c2ef071851d371f475ab58e Mon Sep 17 00:00:00 2001 From: Ivan Inozemtsev Date: Wed, 20 Jan 2021 22:57:29 +0100 Subject: [PATCH 112/172] Migrate http2 to null safety (dart-lang/http2#78) --- pkgs/http2/.travis.yml | 28 +-- pkgs/http2/CHANGELOG.md | 4 + pkgs/http2/example/display_headers.dart | 2 +- pkgs/http2/experimental/server.dart | 15 +- pkgs/http2/lib/multiprotocol_server.dart | 20 +-- .../lib/src/async_utils/async_utils.dart | 37 ++-- pkgs/http2/lib/src/connection.dart | 60 +++---- pkgs/http2/lib/src/connection_preface.dart | 37 ++-- pkgs/http2/lib/src/error_handler.dart | 2 +- .../src/flowcontrol/connection_queues.dart | 8 +- .../lib/src/flowcontrol/stream_queues.dart | 44 ++--- .../lib/src/frames/frame_defragmenter.dart | 14 +- pkgs/http2/lib/src/frames/frame_reader.dart | 114 ++++++------ pkgs/http2/lib/src/frames/frame_types.dart | 8 +- pkgs/http2/lib/src/hpack/hpack.dart | 6 +- pkgs/http2/lib/src/hpack/huffman.dart | 18 +- pkgs/http2/lib/src/ping/ping_handler.dart | 5 +- pkgs/http2/lib/src/settings/settings.dart | 8 +- .../http2/lib/src/streams/stream_handler.dart | 12 +- pkgs/http2/lib/src/testing/client.dart | 4 +- pkgs/http2/lib/src/testing/debug.dart | 2 +- pkgs/http2/lib/transport.dart | 27 +-- pkgs/http2/pubspec.yaml | 11 +- pkgs/http2/test/client_test.dart | 5 +- pkgs/http2/test/client_websites_test.dart | 4 +- .../src/async_utils/async_utils_test.dart | 2 +- .../test/src/connection_preface_test.dart | 25 +-- .../flowcontrol/connection_queues_test.dart | 35 ++-- .../connection_queues_test.mocks.dart | 169 ++++++++++++++++++ .../src/flowcontrol/stream_queues_test.dart | 49 ++--- .../flowcontrol/stream_queues_test.mocks.dart | 103 +++++++++++ .../test/src/frames/frame_reader_test.dart | 12 +- pkgs/http2/test/src/hpack/hpack_test.dart | 2 - .../test/src/ping/ping_handler_test.dart | 6 +- pkgs/http2/test/src/streams/helper.dart | 17 +- pkgs/http2/test/transport_test.dart | 72 ++++---- 36 files changed, 638 insertions(+), 349 deletions(-) create mode 100644 pkgs/http2/test/src/flowcontrol/connection_queues_test.mocks.dart create mode 100644 pkgs/http2/test/src/flowcontrol/stream_queues_test.mocks.dart diff --git a/pkgs/http2/.travis.yml b/pkgs/http2/.travis.yml index d3bdcc2e5e..b1bdcd366d 100644 --- a/pkgs/http2/.travis.yml +++ b/pkgs/http2/.travis.yml @@ -1,22 +1,26 @@ language: dart dart: - - 2.8.4 - dev -dart_task: - - test: --exclude-tags flaky - - test: --tags flaky - - dartanalyzer: --fatal-infos --fatal-warnings . - -matrix: +jobs: include: - # Only validate formatting using the dev release - - dart: dev - dart_task: dartfmt + - name: "Analyzer" + os: linux + script: dart analyze --fatal-warnings --fatal-infos . + - name: "Format" + os: linux + script: dartfmt -n --set-exit-if-changed . + - name: "Tests" + os: linux + script: dart test --exclude-tags flaky + - name: "Flaky Tests" + os: linux + script: dart test --tags flaky allow_failures: - - dart_task: - test: --tags flaky + - name: "Flaky Tests" + os: linux + script: dart test --tags flaky # Only building master means that we don't run two builds for each pull request. branches: diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index 11a84441ff..86c5698e07 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.0-nullsafety.0 + +* Migrate to null safety. + ## 1.0.2-dev * Update minimum Dart SDK to `2.8.4`. diff --git a/pkgs/http2/example/display_headers.dart b/pkgs/http2/example/display_headers.dart index 57b6f01405..9cefdab383 100644 --- a/pkgs/http2/example/display_headers.dart +++ b/pkgs/http2/example/display_headers.dart @@ -5,7 +5,7 @@ import 'dart:io'; import 'package:http2/transport.dart'; void main(List args) async { - if (args == null || args.length != 1) { + if (args.length != 1) { print('Usage: dart display_headers.dart '); exit(1); } diff --git a/pkgs/http2/experimental/server.dart b/pkgs/http2/experimental/server.dart index 51f34dbf5e..e85d372ce0 100644 --- a/pkgs/http2/experimental/server.dart +++ b/pkgs/http2/experimental/server.dart @@ -47,18 +47,17 @@ void handleClient(SecureSocket socket) { connection.incomingStreams.listen((ServerTransportStream stream) async { dumpInfo('main', 'Got new HTTP/2 stream with id: ${stream.id}'); - String path; - stream.incomingMessages.listen((StreamMessage msg) async { + var pathSeen = false; + unawaited(stream.incomingMessages.forEach((StreamMessage msg) async { dumpInfo('${stream.id}', 'Got new incoming message'); if (msg is HeadersStreamMessage) { dumpHeaders('${stream.id}', msg.headers); - if (path == null) { - path = pathFromHeaders(msg.headers); - if (path == null) throw Exception('no path given'); - + if (!pathSeen) { + var path = pathFromHeaders(msg.headers); + pathSeen = true; if (path == '/') { unawaited(sendHtml(stream)); - } else if (['/iframe', '/iframe2'].contains(path)) { + } else if (path == '/iframe' || path == '/iframe2') { unawaited(sendIFrameHtml(stream, path)); } else { unawaited(send404(stream, path)); @@ -67,7 +66,7 @@ void handleClient(SecureSocket socket) { } else if (msg is DataStreamMessage) { dumpData('${stream.id}', msg.bytes); } - }); + })); }); } diff --git a/pkgs/http2/lib/multiprotocol_server.dart b/pkgs/http2/lib/multiprotocol_server.dart index d0a7b5dba1..320f96d479 100644 --- a/pkgs/http2/lib/multiprotocol_server.dart +++ b/pkgs/http2/lib/multiprotocol_server.dart @@ -18,22 +18,22 @@ import 'transport.dart' as http2; /// * one handles HTTP/2 clients (called with a [http2.ServerTransportStream]) class MultiProtocolHttpServer { final SecureServerSocket _serverSocket; - final http2.ServerSettings _settings; + final http2.ServerSettings? _settings; - _ServerSocketController _http11Controller; - HttpServer _http11Server; + late _ServerSocketController _http11Controller; + late HttpServer _http11Server; + + final StreamController _http2Controller = + StreamController(); + Stream get _http2Server => + _http2Controller.stream; - StreamController _http2Controller; - Stream _http2Server; final _http2Connections = {}; MultiProtocolHttpServer._(this._serverSocket, this._settings) { _http11Controller = _ServerSocketController(_serverSocket.address, _serverSocket.port); _http11Server = HttpServer.listenOn(_http11Controller.stream); - - _http2Controller = StreamController(); - _http2Server = _http2Controller.stream; } /// Binds a new [SecureServerSocket] with a security [context] at [port] and @@ -46,7 +46,7 @@ class MultiProtocolHttpServer { /// See also [startServing]. static Future bind( address, int port, SecurityContext context, - {http2.ServerSettings settings}) async { + {http2.ServerSettings? settings}) async { context.setAlpnProtocols(['h2', 'h2-14', 'http/1.1'], true); var secureServer = await SecureServerSocket.bind(address, port, context); return MultiProtocolHttpServer._(secureServer, settings); @@ -65,7 +65,7 @@ class MultiProtocolHttpServer { /// an exception (i.e. these must take care of error handling themselves). void startServing(void Function(HttpRequest) callbackHttp11, void Function(http2.ServerTransportStream) callbackHttp2, - {void Function(dynamic error, StackTrace) onError}) { + {void Function(dynamic error, StackTrace)? onError}) { // 1. Start listening on the real [SecureServerSocket]. _serverSocket.listen((SecureSocket socket) { var protocol = socket.selectedProtocol; diff --git a/pkgs/http2/lib/src/async_utils/async_utils.dart b/pkgs/http2/lib/src/async_utils/async_utils.dart index d37cdc1083..744fd90140 100644 --- a/pkgs/http2/lib/src/async_utils/async_utils.dart +++ b/pkgs/http2/lib/src/async_utils/async_utils.dart @@ -47,34 +47,33 @@ class BufferIndicator { /// whether the underlying stream cannot handle more data and would buffer. class BufferedSink { /// The indicator whether the underlying sink is buffering at the moment. - final BufferIndicator bufferIndicator = BufferIndicator(); + final bufferIndicator = BufferIndicator(); /// A intermediate [StreamController] used to catch pause signals and to /// propagate the change via [bufferIndicator]. - StreamController> _controller; + final _controller = StreamController>(sync: true); /// A future which completes once the sink has been closed. - Future _doneFuture; + late final Future _doneFuture; BufferedSink(StreamSink> dataSink) { bufferIndicator.markBuffered(); - _controller = StreamController>( - onListen: () { - bufferIndicator.markUnBuffered(); - }, - onPause: () { - bufferIndicator.markBuffered(); - }, - onResume: () { - bufferIndicator.markUnBuffered(); - }, - onCancel: () { - // TODO: We may want to propagate cancel events as errors. - // Currently `_doneFuture` will just complete normally if the sink - // cancelled. - }, - sync: true); + _controller + ..onListen = () { + bufferIndicator.markUnBuffered(); + } + ..onPause = () { + bufferIndicator.markBuffered(); + } + ..onResume = () { + bufferIndicator.markUnBuffered(); + } + ..onCancel = () { + // TODO: We may want to propagate cancel events as errors. + // Currently `_doneFuture` will just complete normally if the sink + // cancelled. + }; _doneFuture = Future.wait([_controller.stream.pipe(dataSink), dataSink.done]); } diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index bc8a9b5e50..32393d9be3 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -95,7 +95,7 @@ abstract class Connection { final bool isClientConnection; /// Active state handler for this connection. - ActiveStateHandler onActiveStateChanged; + ActiveStateHandler? onActiveStateChanged; final Completer _onInitialPeerSettingsReceived = Completer(); @@ -117,32 +117,32 @@ abstract class Connection { final FrameDefragmenter _defragmenter = FrameDefragmenter(); /// The outgoing frames of this connection; - FrameWriter _frameWriter; + late FrameWriter _frameWriter; /// A subscription of incoming [Frame]s. - StreamSubscription _frameReaderSubscription; + late StreamSubscription _frameReaderSubscription; /// The incoming connection-level message queue. - ConnectionMessageQueueIn _incomingQueue; + late ConnectionMessageQueueIn _incomingQueue; /// The outgoing connection-level message queue. - ConnectionMessageQueueOut _outgoingQueue; + late ConnectionMessageQueueOut _outgoingQueue; /// The ping handler used for making pings & handling remote pings. - PingHandler _pingHandler; + late PingHandler _pingHandler; /// The settings handler used for changing settings & for handling remote /// setting changes. - SettingsHandler _settingsHandler; + late SettingsHandler _settingsHandler; /// The set of active streams this connection has. - StreamHandler _streams; + late StreamHandler _streams; /// The connection-level flow control window handler for outgoing messages. - OutgoingConnectionWindowHandler _connectionWindowHandler; + late OutgoingConnectionWindowHandler _connectionWindowHandler; /// The state of this connection. - ConnectionState _state; + late ConnectionState _state; Connection(Stream> incoming, StreamSink> outgoing, Settings settings, @@ -238,22 +238,23 @@ abstract class Connection { List _decodeSettings(Settings settings) { var settingsList = []; - // By default a endpoitn can make an unlimited number of concurrent streams. - if (settings.concurrentStreamLimit != null) { - settingsList.add(Setting(Setting.SETTINGS_MAX_CONCURRENT_STREAMS, - settings.concurrentStreamLimit)); + // By default a endpoint can make an unlimited number of concurrent streams. + var concurrentStreamLimit = settings.concurrentStreamLimit; + if (concurrentStreamLimit != null) { + settingsList.add(Setting( + Setting.SETTINGS_MAX_CONCURRENT_STREAMS, concurrentStreamLimit)); } // By default the stream level flow control window is 64 KiB. - if (settings.streamWindowSize != null) { - settingsList.add(Setting( - Setting.SETTINGS_INITIAL_WINDOW_SIZE, settings.streamWindowSize)); + var streamWindowSize = settings.streamWindowSize; + if (streamWindowSize != null) { + settingsList + .add(Setting(Setting.SETTINGS_INITIAL_WINDOW_SIZE, streamWindowSize)); } if (settings is ClientSettings) { // By default the server is allowed to do server pushes. - if (settings.allowServerPushes == null || - settings.allowServerPushes == false) { + if (!settings.allowServerPushes) { settingsList.add(Setting(Setting.SETTINGS_ENABLE_PUSH, 0)); } } else if (settings is ServerSettings) { @@ -278,12 +279,8 @@ abstract class Connection { _finishing(active: true); // TODO: There is probably more we need to wait for. - return _streams.done.whenComplete(() { - var futures = [_frameWriter.close()]; - var f = _frameReaderSubscription.cancel(); - if (f != null) futures.add(f); - return Future.wait(futures); - }); + return _streams.done.whenComplete(() => + Future.wait([_frameWriter.close(), _frameReaderSubscription.cancel()])); } /// Terminates this connection forcefully. @@ -291,11 +288,8 @@ abstract class Connection { return _terminate(ErrorCode.NO_ERROR); } - void _activeStateHandler(bool isActive) { - if (onActiveStateChanged != null) { - onActiveStateChanged(isActive); - } - } + void _activeStateHandler(bool isActive) => + onActiveStateChanged?.call(isActive); /// Invokes the passed in closure and catches any exceptions. void _catchProtocolErrors(void Function() fn) { @@ -319,7 +313,7 @@ abstract class Connection { } } - void _handleFrameImpl(Frame frame) { + void _handleFrameImpl(Frame? frame) { // The first frame from the other side must be a [SettingsFrame], otherwise // we terminate the connection. if (_state.isInitialized) { @@ -369,7 +363,7 @@ abstract class Connection { } } - void _finishing({bool active = true, String message}) { + void _finishing({bool active = true, String? message}) { // If this connection is already dead, we return. if (_state.isTerminated) return; @@ -407,7 +401,7 @@ abstract class Connection { /// /// The returned future will never complete with an error. Future _terminate(int errorCode, - {bool causedByTransportError = false, String message}) { + {bool causedByTransportError = false, String? message}) { // TODO: When do we complete here? if (_state.state != ConnectionState.Terminated) { _state.state = ConnectionState.Terminated; diff --git a/pkgs/http2/lib/src/connection_preface.dart b/pkgs/http2/lib/src/connection_preface.dart index 5995ba8e9e..c2bf652c2e 100644 --- a/pkgs/http2/lib/src/connection_preface.dart +++ b/pkgs/http2/lib/src/connection_preface.dart @@ -42,13 +42,13 @@ const List CONNECTION_PREFACE = [ /// connection preface. If an error occurs while reading the connection /// preface, the returned stream will have only an error. Stream> readConnectionPreface(Stream> incoming) { - StreamController> result; - StreamSubscription subscription; + final result = StreamController>(); + late StreamSubscription subscription; var connectionPrefaceRead = false; var prefaceBuffer = []; var terminated = false; - void terminate(error) { + void terminate(Object error) { if (!terminated) { result.addError(error); result.close(); @@ -64,7 +64,6 @@ Stream> readConnectionPreface(Stream> incoming) { return false; } } - prefaceBuffer = null; connectionPrefaceRead = true; return true; } @@ -96,21 +95,21 @@ Stream> readConnectionPreface(Stream> incoming) { } } - result = StreamController( - onListen: () { - subscription = incoming.listen(onData, - onError: (e, StackTrace s) => result.addError(e, s), - onDone: () { - if (prefaceBuffer != null) { - terminate('EOS before connection preface could be read.'); - } else { - result.close(); - } - }); - }, - onPause: () => subscription.pause(), - onResume: () => subscription.resume(), - onCancel: () => subscription.cancel()); + result.onListen = () { + subscription = incoming.listen(onData, + onError: (Object e, StackTrace s) => result.addError(e, s), + onDone: () { + if (!connectionPrefaceRead) { + terminate('EOS before connection preface could be read.'); + } else { + result.close(); + } + }); + result + ..onPause = subscription.pause + ..onResume = subscription.resume + ..onCancel = subscription.cancel; + }; return result.stream; } diff --git a/pkgs/http2/lib/src/error_handler.dart b/pkgs/http2/lib/src/error_handler.dart index c04785e323..d599dc208e 100644 --- a/pkgs/http2/lib/src/error_handler.dart +++ b/pkgs/http2/lib/src/error_handler.dart @@ -20,7 +20,7 @@ class TerminatableMixin { bool get wasTerminated => _terminated; - void onTerminated(error) { + void onTerminated(Object? error) { // Subclasses can override this method if they want. } diff --git a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart index 778c1ef788..b6247873ad 100644 --- a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart @@ -293,8 +293,8 @@ class ConnectionMessageQueueIn extends Object // TODO: Do we need to do a runtime check here and // raise a protocol error if we cannot find the registered stream? - var streamMQ = _stream2messageQueue[streamId]; - var pendingMessages = _stream2pendingMessages[streamId]; + var streamMQ = _stream2messageQueue[streamId]!; + var pendingMessages = _stream2pendingMessages[streamId]!; pendingMessages.addLast(message); _tryDispatch(streamId, streamMQ, pendingMessages); } @@ -304,7 +304,7 @@ class ConnectionMessageQueueIn extends Object // TODO: Do we need to do a runtime check here and // raise a protocol error if we cannot find the registered stream? - var streamMQ = _stream2messageQueue[streamId]; + var streamMQ = _stream2messageQueue[streamId]!; streamMQ.enqueueMessage(message); } @@ -336,7 +336,7 @@ class ConnectionMessageQueueIn extends Object void forceDispatchIncomingMessages() { final toBeRemoved = {}; _stream2pendingMessages.forEach((int streamId, Queue messages) { - final mq = _stream2messageQueue[streamId]; + final mq = _stream2messageQueue[streamId]!; while (messages.isNotEmpty) { _count--; final message = messages.removeFirst(); diff --git a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart index 9cd14992dd..e2e82e5319 100644 --- a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart @@ -166,42 +166,42 @@ class StreamMessageQueueIn extends Object final Queue _pendingMessages = Queue(); /// The [StreamController] used for producing the [messages] stream. - StreamController _incomingMessagesC; + final _incomingMessagesC = StreamController(); /// The [StreamController] used for producing the [serverPushes] stream. - StreamController _serverPushStreamsC; + final _serverPushStreamsC = StreamController(); StreamMessageQueueIn(this.windowHandler) { // We start by marking it as buffered, since no one is listening yet and // incoming messages will get buffered. bufferIndicator.markBuffered(); - _incomingMessagesC = StreamController( - onListen: () { - if (!wasClosed && !wasTerminated) { - _tryDispatch(); - _tryUpdateBufferIndicator(); - } - }, - onPause: () { + _incomingMessagesC + ..onListen = () { + if (!wasClosed && !wasTerminated) { + _tryDispatch(); _tryUpdateBufferIndicator(); - // TODO: Would we ever want to decrease the window size in this - // situation? - }, - onResume: () { - if (!wasClosed && !wasTerminated) { - _tryDispatch(); - _tryUpdateBufferIndicator(); - } - }, - onCancel: cancel); + } + } + ..onPause = () { + _tryUpdateBufferIndicator(); + // TODO: Would we ever want to decrease the window size in this + // situation? + } + ..onResume = () { + if (!wasClosed && !wasTerminated) { + _tryDispatch(); + _tryUpdateBufferIndicator(); + } + } + ..onCancel = cancel; - _serverPushStreamsC = StreamController(onListen: () { + _serverPushStreamsC.onListen = () { if (!wasClosed && !wasTerminated) { _tryDispatch(); _tryUpdateBufferIndicator(); } - }); + }; } /// Debugging data: the number of pending messages in this queue. diff --git a/pkgs/http2/lib/src/frames/frame_defragmenter.dart b/pkgs/http2/lib/src/frames/frame_defragmenter.dart index 0527480eec..5ab2c72426 100644 --- a/pkgs/http2/lib/src/frames/frame_defragmenter.dart +++ b/pkgs/http2/lib/src/frames/frame_defragmenter.dart @@ -11,10 +11,10 @@ import 'frames.dart'; // (since we're buffering all of them). class FrameDefragmenter { /// The current incomplete [HeadersFrame] fragment. - HeadersFrame _headersFrame; + HeadersFrame? _headersFrame; /// The current incomplete [PushPromiseFrame] fragment. - PushPromiseFrame _pushPromiseFrame; + PushPromiseFrame? _pushPromiseFrame; /// Tries to defragment [frame]. /// @@ -29,14 +29,14 @@ class FrameDefragmenter { /// All other [Frame] types will be returned. // TODO: Consider handling continuation frames without preceding // headers/push-promise frame here instead of the call site? - Frame tryDefragmentFrame(Frame frame) { + Frame? tryDefragmentFrame(Frame? frame) { if (_headersFrame != null) { if (frame is ContinuationFrame) { - if (_headersFrame.header.streamId != frame.header.streamId) { + if (_headersFrame!.header.streamId != frame.header.streamId) { throw ProtocolException( 'Defragmentation: frames have different stream ids.'); } - _headersFrame = _headersFrame.addBlockContinuation(frame); + _headersFrame = _headersFrame!.addBlockContinuation(frame); if (frame.hasEndHeadersFlag) { var frame = _headersFrame; @@ -52,11 +52,11 @@ class FrameDefragmenter { } } else if (_pushPromiseFrame != null) { if (frame is ContinuationFrame) { - if (_pushPromiseFrame.header.streamId != frame.header.streamId) { + if (_pushPromiseFrame!.header.streamId != frame.header.streamId) { throw ProtocolException( 'Defragmentation: frames have different stream ids.'); } - _pushPromiseFrame = _pushPromiseFrame.addBlockContinuation(frame); + _pushPromiseFrame = _pushPromiseFrame!.addBlockContinuation(frame); if (frame.hasEndHeadersFlag) { var frame = _pushPromiseFrame; diff --git a/pkgs/http2/lib/src/frames/frame_reader.dart b/pkgs/http2/lib/src/frames/frame_reader.dart index 10ac3092cd..2d93f01a47 100644 --- a/pkgs/http2/lib/src/frames/frame_reader.dart +++ b/pkgs/http2/lib/src/frames/frame_reader.dart @@ -12,8 +12,7 @@ class FrameReader { /// complying with. final ActiveSettings _localSettings; - StreamSubscription> _subscription; - StreamController _framesController; + final _framesController = StreamController(); FrameReader(this._inputStream, this._localSettings); @@ -22,7 +21,7 @@ class FrameReader { var bufferedData = >[]; var bufferedLength = 0; - FrameHeader tryReadHeader() { + FrameHeader? tryReadHeader() { if (bufferedLength >= FRAME_HEADER_SIZE) { // Get at least FRAME_HEADER_SIZE bytes in the first byte array. _mergeLists(bufferedData, FRAME_HEADER_SIZE); @@ -33,7 +32,7 @@ class FrameReader { return null; } - Frame tryReadFrame(FrameHeader header) { + Frame? tryReadFrame(FrameHeader header) { var totalFrameLen = FRAME_HEADER_SIZE + header.length; if (bufferedLength >= totalFrameLen) { // Get the whole frame in the first byte array. @@ -57,59 +56,62 @@ class FrameReader { return null; } - _framesController = StreamController( - onListen: () { - FrameHeader header; + _framesController.onListen = () { + FrameHeader? header; - void terminateWithError(error, [StackTrace stack]) { - header = null; - _framesController.addError(error, stack); - _subscription.cancel(); - _framesController.close(); - } + late StreamSubscription> subscription; + + void terminateWithError(Object error, [StackTrace? stack]) { + header = null; + _framesController.addError(error, stack); + subscription.cancel(); + _framesController.close(); + } - _subscription = _inputStream.listen((List data) { - bufferedData.add(data); - bufferedLength += data.length; - - try { - while (true) { - header ??= tryReadHeader(); - if (header != null) { - if (header.length > _localSettings.maxFrameSize) { - terminateWithError( - FrameSizeException('Incoming frame is too big.')); - return; - } - - var frame = tryReadFrame(header); - - if (frame != null) { - _framesController.add(frame); - header = null; - } else { - break; - } - } else { - break; - } + subscription = _inputStream.listen((List data) { + bufferedData.add(data); + bufferedLength += data.length; + + try { + while (true) { + header ??= tryReadHeader(); + if (header != null) { + if (header!.length > _localSettings.maxFrameSize) { + terminateWithError( + FrameSizeException('Incoming frame is too big.')); + return; + } + + var frame = tryReadFrame(header!); + + if (frame != null) { + _framesController.add(frame); + header = null; + } else { + break; } - } catch (error, stack) { - terminateWithError(error, stack); - } - }, onError: (error, StackTrace stack) { - terminateWithError(error, stack); - }, onDone: () { - if (bufferedLength == 0) { - _framesController.close(); } else { - terminateWithError(FrameSizeException( - 'Incoming byte stream ended with incomplete frame')); + break; } - }); - }, - onPause: () => _subscription.pause(), - onResume: () => _subscription.resume()); + } + } catch (error, stack) { + terminateWithError(error, stack); + } + }, onError: (Object error, StackTrace stack) { + terminateWithError(error, stack); + }, onDone: () { + if (bufferedLength == 0) { + _framesController.close(); + } else { + terminateWithError(FrameSizeException( + 'Incoming byte stream ended with incomplete frame')); + } + }); + + _framesController + ..onPause = subscription.pause + ..onResume = subscription.resume; + }; return _framesController.stream; } @@ -173,9 +175,9 @@ class FrameReader { _checkFrameLengthCondition((frameEnd - offset) >= 1); padLength = bytes[offset++]; } - int streamDependency; + int? streamDependency; var exclusiveDependency = false; - int weight; + int? weight; if (_isFlagSet(header.flags, HeadersFrame.FLAG_PRIORITY)) { _checkFrameLengthCondition((frameEnd - offset) >= 5); exclusiveDependency = (bytes[offset] & 0x80) == 0x80; @@ -211,11 +213,11 @@ class FrameReader { message: 'Settings frame length must be a multiple of 6 bytes.'); var count = header.length ~/ 6; - var settings = List(count); + var settings = []; for (var i = 0; i < count; i++) { var identifier = readInt16(bytes, offset + 6 * i); var value = readInt32(bytes, offset + 6 * i + 2); - settings[i] = Setting(identifier, value); + settings.add(Setting(identifier, value)); } var frame = SettingsFrame(header, settings); if (frame.hasAckFlag) { diff --git a/pkgs/http2/lib/src/frames/frame_types.dart b/pkgs/http2/lib/src/frames/frame_types.dart index 2dec30a200..abab588c10 100644 --- a/pkgs/http2/lib/src/frames/frame_types.dart +++ b/pkgs/http2/lib/src/frames/frame_types.dart @@ -95,8 +95,8 @@ class HeadersFrame extends Frame { final int padLength; final bool exclusiveDependency; - final int streamDependency; - final int weight; + final int? streamDependency; + final int? weight; final List headerBlockFragment; HeadersFrame(FrameHeader header, this.padLength, this.exclusiveDependency, @@ -104,7 +104,7 @@ class HeadersFrame extends Frame { : super(header); /// This will be set from the outside after decoding. - List
decodedHeaders; + late List
decodedHeaders; bool get hasEndStreamFlag => _isFlagSet(header.flags, FLAG_END_STREAM); bool get hasEndHeadersFlag => _isFlagSet(header.flags, FLAG_END_HEADERS); @@ -223,7 +223,7 @@ class PushPromiseFrame extends Frame { final List headerBlockFragment; /// This will be set from the outside after decoding. - List
decodedHeaders; + late List
decodedHeaders; PushPromiseFrame(FrameHeader header, this.padLength, this.promisedStreamId, this.headerBlockFragment) diff --git a/pkgs/http2/lib/src/hpack/hpack.dart b/pkgs/http2/lib/src/hpack/hpack.dart index 5c120f0a88..62338a1c34 100644 --- a/pkgs/http2/lib/src/hpack/hpack.dart +++ b/pkgs/http2/lib/src/hpack/hpack.dart @@ -55,7 +55,7 @@ class Header { /// A stateful HPACK decoder. class HPackDecoder { - int _maxHeaderTableSize; + late int _maxHeaderTableSize; final IndexTable _table = IndexTable(); @@ -221,7 +221,7 @@ class HPackEncoder { } class IndexTable { - static final List
_staticTable = [ + static final List _staticTable = [ null, Header(ascii.encode(':authority'), const []), Header(ascii.encode(':method'), ascii.encode('GET')), @@ -310,7 +310,7 @@ class IndexTable { 'Invalid index (was: $index) for table lookup.'); } if (index < _staticTable.length) { - return _staticTable[index]; + return _staticTable[index]!; } index -= _staticTable.length; if (index < _dynamicTable.length) { diff --git a/pkgs/http2/lib/src/hpack/huffman.dart b/pkgs/http2/lib/src/hpack/huffman.dart index 6d0322b02d..11b5a0ac79 100644 --- a/pkgs/http2/lib/src/hpack/huffman.dart +++ b/pkgs/http2/lib/src/hpack/huffman.dart @@ -45,9 +45,9 @@ class HuffmanDecoder { for (var currentBit = 7; currentBit >= 0; currentBit--) { var right = (byte >> currentBit) & 1 == 1; if (right) { - node = node.right; + node = node.right!; } else { - node = node.left; + node = node.left!; } currentDepth++; if (node.value != null) { @@ -56,7 +56,7 @@ class HuffmanDecoder { 'More than 7 bit padding is not allowed. Found entire EOS ' 'encoding'); } - buffer.addByte(node.value); + buffer.addByte(node.value!); node = _root; currentDepth = 0; } @@ -71,7 +71,7 @@ class HuffmanDecoder { } while (node.right != null) { - node = node.right; + node = node.right!; } if (node.value != 256) { @@ -147,9 +147,9 @@ class EncodedHuffmanValue { /// A node in the huffman tree. class HuffmanTreeNode { - HuffmanTreeNode left; - HuffmanTreeNode right; - int value; + HuffmanTreeNode? left; + HuffmanTreeNode? right; + int? value; } /// Generates a huffman decoding tree. @@ -166,10 +166,10 @@ HuffmanTreeNode generateHuffmanTree(List valueEncodings) { if (right) { current.right ??= HuffmanTreeNode(); - current = current.right; + current = current.right!; } else { current.left ??= HuffmanTreeNode(); - current = current.left; + current = current.left!; } } diff --git a/pkgs/http2/lib/src/ping/ping_handler.dart b/pkgs/http2/lib/src/ping/ping_handler.dart index 7bf8013958..7682e7efcd 100644 --- a/pkgs/http2/lib/src/ping/ping_handler.dart +++ b/pkgs/http2/lib/src/ping/ping_handler.dart @@ -21,10 +21,11 @@ class PingHandler extends Object with TerminatableMixin { PingHandler(this._frameWriter); @override - void onTerminated(error) { + void onTerminated(Object? error) { var values = _remainingPings.values.toList(); _remainingPings.clear(); - values.forEach((Completer c) => c.completeError(error)); + values.forEach( + (Completer c) => c.completeError(error ?? 'Unspecified error')); } void processPingFrame(PingFrame frame) { diff --git a/pkgs/http2/lib/src/settings/settings.dart b/pkgs/http2/lib/src/settings/settings.dart index ab38ebb2a4..fc9c1b726d 100644 --- a/pkgs/http2/lib/src/settings/settings.dart +++ b/pkgs/http2/lib/src/settings/settings.dart @@ -41,7 +41,7 @@ class ActiveSettings { /// active streams. Servers SHOULD only set a zero value for short durations; /// if a server does not wish to accept requests, closing the connection is /// more appropriate. - int maxConcurrentStreams; + int? maxConcurrentStreams; /// Indicates the sender's initial window size (in octets) for stream level /// flow control. The initial value is 2^16-1 (65,535) octets. @@ -69,7 +69,7 @@ class ActiveSettings { /// /// For any given request, a lower limit than what is advertised MAY be /// enforced. The initial value of this setting is unlimited. - int maxHeaderListSize; + int? maxHeaderListSize; ActiveSettings( {this.headerTableSize = 4096, @@ -152,10 +152,10 @@ class SettingsHandler extends Object with TerminatableMixin { } @override - void onTerminated(error) { + void onTerminated(Object? error) { _toBeAcknowledgedSettings.clear(); _toBeAcknowledgedCompleters - .forEach((Completer c) => c.completeError(error)); + .forEach((Completer c) => c.completeError(error!)); } Future changeSettings(List changes) { diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index f1d1c50b93..eed12404bf 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -61,17 +61,17 @@ class Http2StreamImpl extends TransportStream // Error code from RST_STREAM frame, if the stream has been terminated // remotely. - int _terminatedErrorCode; + int? _terminatedErrorCode; // Termination handler. Invoked if the stream receives an RST_STREAM frame. - void Function(int) _onTerminated; + void Function(int)? _onTerminated; final ZoneUnaryCallback _canPushFun; final ZoneBinaryCallback> _pushStreamFun; final ZoneUnaryCallback _terminateStreamFun; - StreamSubscription _outgoingCSubscription; + late StreamSubscription _outgoingCSubscription; Http2StreamImpl( this.incomingQueue, @@ -113,14 +113,14 @@ class Http2StreamImpl extends TransportStream set onTerminated(void Function(int) handler) { _onTerminated = handler; if (_terminatedErrorCode != null && _onTerminated != null) { - _onTerminated(_terminatedErrorCode); + _onTerminated!(_terminatedErrorCode!); } } void _handleTerminated(int errorCode) { _terminatedErrorCode = errorCode; if (_onTerminated != null) { - _onTerminated(_terminatedErrorCode); + _onTerminated!(_terminatedErrorCode!); } } } @@ -767,7 +767,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { } } - void _closeStreamAbnormally(Http2StreamImpl stream, Object exception, + void _closeStreamAbnormally(Http2StreamImpl stream, Object? exception, {bool propagateException = false}) { incomingQueue.removeStreamMessageQueue(stream.id); diff --git a/pkgs/http2/lib/src/testing/client.dart b/pkgs/http2/lib/src/testing/client.dart index 6896a86970..ecf4e9954f 100644 --- a/pkgs/http2/lib/src/testing/client.dart +++ b/pkgs/http2/lib/src/testing/client.dart @@ -36,7 +36,7 @@ class ClientConnection { /// Assumes the protocol on [socket] was negogiated to be http/2. /// /// If [settings] are omitted, the default [ClientSettings] will be used. - ClientConnection(Socket socket, {ClientSettings settings}) + ClientConnection(Socket socket, {ClientSettings? settings}) : connection = ClientTransportConnection.viaSocket(socket, settings: settings); @@ -123,7 +123,7 @@ class ClientConnection { /// client. The maximum number of concurrent server pushes can be configured via /// [maxConcurrentPushes] (default is `null` meaning no limit). Future connect(Uri uri, - {bool allowServerPushes = false, int maxConcurrentPushes}) async { + {bool allowServerPushes = false, int? maxConcurrentPushes}) async { const Http2AlpnProtocols = ['h2-14', 'h2-15', 'h2-16', 'h2-17', 'h2']; var useSSL = uri.scheme == 'https'; diff --git a/pkgs/http2/lib/src/testing/debug.dart b/pkgs/http2/lib/src/testing/debug.dart index b48198791e..243e60f0f4 100644 --- a/pkgs/http2/lib/src/testing/debug.dart +++ b/pkgs/http2/lib/src/testing/debug.dart @@ -121,7 +121,7 @@ Future _pipeAndCopy(Stream> from, StreamSink to, StreamSink to2) { from.listen((List data) { to.add(data); to2.add(data); - }, onError: (e, StackTrace s) { + }, onError: (Object e, StackTrace s) { to.addError(e, s); to2.addError(e, s); }, onDone: () { diff --git a/pkgs/http2/lib/transport.dart b/pkgs/http2/lib/transport.dart index 7bd95a6f79..f43353dc69 100644 --- a/pkgs/http2/lib/transport.dart +++ b/pkgs/http2/lib/transport.dart @@ -16,18 +16,18 @@ typedef ActiveStateHandler = void Function(bool isActive); abstract class Settings { /// The maximum number of concurrent streams the remote end can open /// (defaults to being unlimited). - final int concurrentStreamLimit; + final int? concurrentStreamLimit; /// The default stream window size the remote peer can use when creating new /// streams (defaults to 65535 bytes). - final int streamWindowSize; + final int? streamWindowSize; const Settings({this.concurrentStreamLimit, this.streamWindowSize}); } /// Settings for a [TransportConnection] a server can make. class ServerSettings extends Settings { - const ServerSettings({int concurrentStreamLimit, int streamWindowSize}) + const ServerSettings({int? concurrentStreamLimit, int? streamWindowSize}) : super( concurrentStreamLimit: concurrentStreamLimit, streamWindowSize: streamWindowSize); @@ -39,8 +39,8 @@ class ClientSettings extends Settings { final bool allowServerPushes; const ClientSettings( - {int concurrentStreamLimit, - int streamWindowSize, + {int? concurrentStreamLimit, + int? streamWindowSize, this.allowServerPushes = false}) : super( concurrentStreamLimit: concurrentStreamLimit, @@ -75,12 +75,12 @@ abstract class TransportConnection { abstract class ClientTransportConnection extends TransportConnection { factory ClientTransportConnection.viaSocket(Socket socket, - {ClientSettings settings}) => + {ClientSettings? settings}) => ClientTransportConnection.viaStreams(socket, socket, settings: settings); factory ClientTransportConnection.viaStreams( Stream> incoming, StreamSink> outgoing, - {ClientSettings settings}) { + {ClientSettings? settings}) { settings ??= const ClientSettings(); return ClientConnection(incoming, outgoing, settings); } @@ -96,14 +96,14 @@ abstract class ClientTransportConnection extends TransportConnection { abstract class ServerTransportConnection extends TransportConnection { factory ServerTransportConnection.viaSocket(Socket socket, - {ServerSettings settings}) { + {ServerSettings? settings}) { return ServerTransportConnection.viaStreams(socket, socket, settings: settings); } factory ServerTransportConnection.viaStreams( Stream> incoming, StreamSink> outgoing, - {ServerSettings settings = + {ServerSettings? settings = const ServerSettings(concurrentStreamLimit: 1000)}) { settings ??= const ServerSettings(); return ServerConnection(incoming, outgoing, settings); @@ -130,7 +130,7 @@ abstract class TransportStream { /// Sets the termination handler on this stream. /// /// The handler will be called if the stream receives an RST_STREAM frame. - set onTerminated(void Function(int) value); + set onTerminated(void Function(int?) value); /// Terminates this HTTP/2 stream in an un-normal way. /// @@ -178,14 +178,15 @@ abstract class ServerTransportStream extends TransportStream { abstract class StreamMessage { final bool endStream; - StreamMessage({bool endStream}) : endStream = endStream ?? false; + StreamMessage({bool? endStream}) : endStream = endStream ?? false; } /// Represents a data message which can be sent over a HTTP/2 stream. class DataStreamMessage extends StreamMessage { final List bytes; - DataStreamMessage(this.bytes, {bool endStream}) : super(endStream: endStream); + DataStreamMessage(this.bytes, {bool? endStream}) + : super(endStream: endStream); @override String toString() => 'DataStreamMessage(${bytes.length} bytes)'; @@ -195,7 +196,7 @@ class DataStreamMessage extends StreamMessage { class HeadersStreamMessage extends StreamMessage { final List
headers; - HeadersStreamMessage(this.headers, {bool endStream}) + HeadersStreamMessage(this.headers, {bool? endStream}) : super(endStream: endStream); @override diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index 1227ce93e1..5bd542301f 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -1,12 +1,13 @@ name: http2 -version: 1.0.2-dev +version: 2.0.0-nullsafety.0 description: A HTTP/2 implementation in Dart. repository: https://github.com/dart-lang/http2 environment: - sdk: '>=2.8.4 <3.0.0' + sdk: '>=2.12.0-0 <3.0.0' dev_dependencies: - mockito: ^4.0.0 - test: ^1.2.0 - pedantic: ^1.0.0 + build_runner: ^1.10.0 + mockito: ^5.0.0-nullsafety + test: ^1.16.0-nullsafety + pedantic: ^1.10.0-nullsafety diff --git a/pkgs/http2/test/client_test.dart b/pkgs/http2/test/client_test.dart index 061168f7dc..19b33cd084 100644 --- a/pkgs/http2/test/client_test.dart +++ b/pkgs/http2/test/client_test.dart @@ -204,7 +204,7 @@ void main() { expect(client.isOpen, true); var stream = client.makeRequest([Header.ascii('a', 'b')]); - String error; + String? error; try { await stream.incomingMessages.toList(); } catch (e) { @@ -639,7 +639,7 @@ void main() { var stream = client.makeRequest([Header.ascii('a', 'b')]); var sub = stream.incomingMessages.listen( expectAsync1((StreamMessage msg) {}, count: 0), - onError: expectAsync1((error) {})); + onError: expectAsync1((Object error) {})); sub.pause(); await Future.delayed(const Duration(milliseconds: 40)); sub.resume(); @@ -757,6 +757,7 @@ void main() { '$e', contains('This stream was not processed and can ' 'therefore be retried')); + return []; }))); expect(await stream.peerPushes.toList(), isEmpty); diff --git a/pkgs/http2/test/client_websites_test.dart b/pkgs/http2/test/client_websites_test.dart index 5eac0eb55c..f19ca22473 100644 --- a/pkgs/http2/test/client_websites_test.dart +++ b/pkgs/http2/test/client_websites_test.dart @@ -60,7 +60,7 @@ void main() async { 'request.'); return readBody(response).then((String body) { - return [push.requestHeaders[':path'].join(''), body]; + return [push.requestHeaders[':path']!.join(''), body]; }); })); }) @@ -119,7 +119,7 @@ void dumpHeaders(Uri uri, Map> headers, print('[$uri] $msg'); for (var key in headers.keys.toList()..sort()) { var spaces = ' ' * (20 - key.length); - print('$key $spaces ${headers[key].join(', ')}'); + print('$key $spaces ${headers[key]!.join(', ')}'); } print(''); } diff --git a/pkgs/http2/test/src/async_utils/async_utils_test.dart b/pkgs/http2/test/src/async_utils/async_utils_test.dart index 3854ac03e0..7a49ed771d 100644 --- a/pkgs/http2/test/src/async_utils/async_utils_test.dart +++ b/pkgs/http2/test/src/async_utils/async_utils_test.dart @@ -67,7 +67,7 @@ void main() { expect(writer.bufferIndicator.wouldBuffer, true); - var bytesFuture = c.stream.fold([], (b, d) => b..addAll(d)); + var bytesFuture = c.stream.fold>([], (b, d) => b..addAll(d)); expect(writer.bufferIndicator.wouldBuffer, false); diff --git a/pkgs/http2/test/src/connection_preface_test.dart b/pkgs/http2/test/src/connection_preface_test.dart index 9c42829cda..7a28a0c2fd 100644 --- a/pkgs/http2/test/src/connection_preface_test.dart +++ b/pkgs/http2/test/src/connection_preface_test.dart @@ -17,8 +17,8 @@ void main() { for (var size = 1; size <= data.length; size++) { var c = StreamController>(); - var resultF = - readConnectionPreface(c.stream).fold([], (b, d) => b..addAll(d)); + var resultF = readConnectionPreface(c.stream) + .fold>([], (b, d) => b..addAll(d)); for (var i = 0; i < (size - 1 + data.length) ~/ size; i++) { var from = size * i; @@ -34,44 +34,47 @@ void main() { test('only-part-of-connection-sequence', () async { var c = StreamController>(); - var resultF = - readConnectionPreface(c.stream).fold([], (b, d) => b..addAll(d)); + var resultF = readConnectionPreface(c.stream) + .fold>([], (b, d) => b..addAll(d)); for (var i = 0; i < CONNECTION_PREFACE.length - 1; i++) { c.add([CONNECTION_PREFACE[i]]); } unawaited(c.close()); - unawaited(resultF.catchError(expectAsync2((error, _) { + unawaited(resultF.catchError(expectAsync2((Object error, Object _) { expect(error, contains('EOS before connection preface could be read')); + return []; }))); }); test('wrong-connection-sequence', () async { var c = StreamController>(); - var resultF = - readConnectionPreface(c.stream).fold([], (b, d) => b..addAll(d)); + var resultF = readConnectionPreface(c.stream) + .fold>([], (b, d) => b..addAll(d)); for (var i = 0; i < CONNECTION_PREFACE.length; i++) { c.add([0xff]); } unawaited(c.close()); - unawaited(resultF.catchError(expectAsync2((error, _) { + unawaited(resultF.catchError(expectAsync2((Object error, Object _) { expect(error, contains('Connection preface does not match.')); + return []; }))); }); test('incoming-socket-error', () async { var c = StreamController>(); - var resultF = - readConnectionPreface(c.stream).fold([], (b, d) => b..addAll(d)); + var resultF = readConnectionPreface(c.stream) + .fold>([], (b, d) => b..addAll(d)); c.addError('hello world'); unawaited(c.close()); - unawaited(resultF.catchError(expectAsync2((error, _) { + unawaited(resultF.catchError(expectAsync2((Object error, Object _) { expect(error, contains('hello world')); + return []; }))); }); }); diff --git a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart index 7989c1a860..fd80ad8eef 100644 --- a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart @@ -2,6 +2,7 @@ // 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:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:test/test.dart'; @@ -14,10 +15,18 @@ import 'package:http2/src/flowcontrol/connection_queues.dart'; import 'package:http2/src/flowcontrol/stream_queues.dart'; import 'package:http2/src/flowcontrol/queue_messages.dart'; +import 'connection_queues_test.mocks.dart'; + +@GenerateMocks([FrameWriter, IncomingWindowHandler, StreamMessageQueueIn]) void main() { group('flowcontrol', () { test('connection-message-queue-out', () { var fw = MockFrameWriter(); + when(fw.bufferIndicator).thenReturn(BufferIndicator()); + when(fw.writeHeadersFrame(any, any, endStream: anyNamed('endStream'))) + .thenReturn(null); + when(fw.writeDataFrame(any, any, endStream: anyNamed('endStream'))) + .thenReturn(null); var windowMock = MockOutgoingWindowHandler(); var queue = ConnectionMessageQueueOut(windowMock, fw); @@ -32,6 +41,7 @@ void main() { queue.enqueueMessage(HeadersMessage(99, headers, false)); expect(queue.pendingMessages, 0); verify(fw.writeHeadersFrame(99, headers, endStream: false)).called(1); + verify(fw.bufferIndicator).called(greaterThan(0)); verifyNoMoreInteractions(fw); verifyZeroInteractions(windowMock); @@ -45,6 +55,7 @@ void main() { verify(windowMock.decreaseWindow(bytes.length)).called(1); verify(fw.writeDataFrame(99, bytes, endStream: false)).called(1); verifyNoMoreInteractions(windowMock); + verify(fw.bufferIndicator).called(greaterThan(0)); verifyNoMoreInteractions(fw); clearInteractions(fw); @@ -62,6 +73,7 @@ void main() { queue.enqueueMessage(DataMessage(99, bytes, true)); expect(queue.pendingMessages, 1); verify(windowMock.decreaseWindow(1)).called(1); + verify(fw.bufferIndicator).called(greaterThan(0)); verify(fw.writeDataFrame(99, bytes.sublist(0, 1), endStream: false)) .called(1); verifyNoMoreInteractions(windowMock); @@ -78,6 +90,7 @@ void main() { verify(fw.writeDataFrame(99, bytes.sublist(1), endStream: true)) .called(1); verifyNoMoreInteractions(windowMock); + verify(fw.bufferIndicator).called(greaterThan(0)); verifyNoMoreInteractions(fw); queue.startClosing(); @@ -93,11 +106,16 @@ void main() { final bytes = [1, 2, 3]; var windowMock = MockIncomingWindowHandler(); + when(windowMock.gotData(any)).thenReturn(null); + when(windowMock.dataProcessed(any)).thenReturn(null); var queue = ConnectionMessageQueueIn(windowMock, (f) => f()); expect(queue.pendingMessages, 0); var streamQueueMock = MockStreamMessageQueueIn(); + when(streamQueueMock.bufferIndicator).thenReturn(BufferIndicator()); + when(streamQueueMock.enqueueMessage(any)).thenReturn(null); + queue.insertNewStreamMessageQueue(STREAM_ID, streamQueueMock); // Insert a [DataFrame] and let it be buffered. @@ -106,7 +124,8 @@ void main() { expect(queue.pendingMessages, 1); verify(windowMock.gotData(bytes.length)).called(1); verifyNoMoreInteractions(windowMock); - verifyZeroInteractions(streamQueueMock); + verify(streamQueueMock.bufferIndicator).called(greaterThan(0)); + verifyNoMoreInteractions(streamQueueMock); clearInteractions(windowMock); @@ -122,6 +141,7 @@ void main() { expect(capturedMessage.bytes, bytes); verifyNoMoreInteractions(windowMock); + verify(streamQueueMock.bufferIndicator).called(greaterThan(0)); verifyNoMoreInteractions(streamQueueMock); // TODO: Write tests for adding HeadersFrame/PushPromiseFrame. @@ -132,6 +152,7 @@ void main() { final bytes = [1, 2, 3]; var windowMock = MockIncomingWindowHandler(); + when(windowMock.gotData(any)).thenReturn(null); var queue = ConnectionMessageQueueIn(windowMock, (f) => f()); // Insert a [DataFrame] and let it be buffered. @@ -144,18 +165,6 @@ void main() { }); } -class MockFrameWriter extends Mock implements FrameWriter { - @override - BufferIndicator bufferIndicator = BufferIndicator(); -} - -class MockStreamMessageQueueIn extends Mock implements StreamMessageQueueIn { - @override - BufferIndicator bufferIndicator = BufferIndicator(); -} - -class MockIncomingWindowHandler extends Mock implements IncomingWindowHandler {} - class MockOutgoingWindowHandler extends Mock implements OutgoingConnectionWindowHandler, OutgoingStreamWindowHandler { @override diff --git a/pkgs/http2/test/src/flowcontrol/connection_queues_test.mocks.dart b/pkgs/http2/test/src/flowcontrol/connection_queues_test.mocks.dart new file mode 100644 index 0000000000..5424460b1f --- /dev/null +++ b/pkgs/http2/test/src/flowcontrol/connection_queues_test.mocks.dart @@ -0,0 +1,169 @@ +import 'dart:async' as _i5; + +import 'package:http2/src/async_utils/async_utils.dart' as _i2; +import 'package:http2/src/flowcontrol/queue_messages.dart' as _i9; +import 'package:http2/src/flowcontrol/stream_queues.dart' as _i7; +import 'package:http2/src/flowcontrol/window_handler.dart' as _i3; +import 'package:http2/src/frames/frames.dart' as _i4; +import 'package:http2/src/hpack/hpack.dart' as _i6; +import 'package:http2/transport.dart' as _i8; +import 'package:mockito/mockito.dart' as _i1; + +// ignore_for_file: comment_references + +// ignore_for_file: unnecessary_parenthesis + +class _FakeBufferIndicator extends _i1.Fake implements _i2.BufferIndicator {} + +class _FakeIncomingWindowHandler extends _i1.Fake + implements _i3.IncomingWindowHandler {} + +/// A class which mocks [FrameWriter]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockFrameWriter extends _i1.Mock implements _i4.FrameWriter { + MockFrameWriter() { + _i1.throwOnMissingStub(this); + } + + @override + _i2.BufferIndicator get bufferIndicator => (super.noSuchMethod( + Invocation.getter(#bufferIndicator), _FakeBufferIndicator()) + as _i2.BufferIndicator); + @override + int get highestWrittenStreamId => + (super.noSuchMethod(Invocation.getter(#highestWrittenStreamId), 0) + as int); + @override + _i5.Future get doneFuture => + (super.noSuchMethod(Invocation.getter(#doneFuture), Future.value(null)) + as _i5.Future); + @override + void writeDataFrame(int? streamId, List? data, + {bool? endStream = false}) => + super.noSuchMethod(Invocation.method( + #writeDataFrame, [streamId, data], {#endStream: endStream})); + @override + void writeHeadersFrame(int? streamId, List<_i6.Header>? headers, + {bool? endStream = true}) => + super.noSuchMethod(Invocation.method( + #writeHeadersFrame, [streamId, headers], {#endStream: endStream})); + @override + void writePriorityFrame(int? streamId, int? streamDependency, int? weight, + {bool? exclusive = false}) => + super.noSuchMethod(Invocation.method(#writePriorityFrame, + [streamId, streamDependency, weight], {#exclusive: exclusive})); + @override + void writeRstStreamFrame(int? streamId, int? errorCode) => super.noSuchMethod( + Invocation.method(#writeRstStreamFrame, [streamId, errorCode])); + @override + void writeSettingsFrame(List<_i4.Setting>? settings) => + super.noSuchMethod(Invocation.method(#writeSettingsFrame, [settings])); + @override + void writePushPromiseFrame( + int? streamId, int? promisedStreamId, List<_i6.Header>? headers) => + super.noSuchMethod(Invocation.method( + #writePushPromiseFrame, [streamId, promisedStreamId, headers])); + @override + void writePingFrame(int? opaqueData, {bool? ack = false}) => + super.noSuchMethod( + Invocation.method(#writePingFrame, [opaqueData], {#ack: ack})); + @override + void writeGoawayFrame( + int? lastStreamId, int? errorCode, List? debugData) => + super.noSuchMethod(Invocation.method( + #writeGoawayFrame, [lastStreamId, errorCode, debugData])); + @override + void writeWindowUpdate(int? sizeIncrement, {int? streamId = 0}) => + super.noSuchMethod(Invocation.method( + #writeWindowUpdate, [sizeIncrement], {#streamId: streamId})); + @override + _i5.Future close() => + (super.noSuchMethod(Invocation.method(#close, []), Future.value(null)) + as _i5.Future); +} + +/// A class which mocks [IncomingWindowHandler]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockIncomingWindowHandler extends _i1.Mock + implements _i3.IncomingWindowHandler { + MockIncomingWindowHandler() { + _i1.throwOnMissingStub(this); + } + + @override + int get localWindowSize => + (super.noSuchMethod(Invocation.getter(#localWindowSize), 0) as int); + @override + void gotData(int? numberOfBytes) => + super.noSuchMethod(Invocation.method(#gotData, [numberOfBytes])); + @override + void dataProcessed(int? numberOfBytes) => + super.noSuchMethod(Invocation.method(#dataProcessed, [numberOfBytes])); +} + +/// A class which mocks [StreamMessageQueueIn]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockStreamMessageQueueIn extends _i1.Mock + implements _i7.StreamMessageQueueIn { + MockStreamMessageQueueIn() { + _i1.throwOnMissingStub(this); + } + + @override + _i3.IncomingWindowHandler get windowHandler => (super.noSuchMethod( + Invocation.getter(#windowHandler), _FakeIncomingWindowHandler()) + as _i3.IncomingWindowHandler); + @override + _i2.BufferIndicator get bufferIndicator => (super.noSuchMethod( + Invocation.getter(#bufferIndicator), _FakeBufferIndicator()) + as _i2.BufferIndicator); + @override + int get pendingMessages => + (super.noSuchMethod(Invocation.getter(#pendingMessages), 0) as int); + @override + _i5.Stream<_i8.StreamMessage> get messages => (super.noSuchMethod( + Invocation.getter(#messages), Stream<_i8.StreamMessage>.empty()) + as _i5.Stream<_i8.StreamMessage>); + @override + _i5.Stream<_i8.TransportStreamPush> get serverPushes => (super.noSuchMethod( + Invocation.getter(#serverPushes), + Stream<_i8.TransportStreamPush>.empty()) + as _i5.Stream<_i8.TransportStreamPush>); + @override + bool get wasTerminated => + (super.noSuchMethod(Invocation.getter(#wasTerminated), false) as bool); + @override + _i5.Future get done => + (super.noSuchMethod(Invocation.getter(#done), Future.value(null)) + as _i5.Future); + @override + bool get isClosing => + (super.noSuchMethod(Invocation.getter(#isClosing), false) as bool); + @override + bool get wasClosed => + (super.noSuchMethod(Invocation.getter(#wasClosed), false) as bool); + @override + _i5.Future get onCancel => + (super.noSuchMethod(Invocation.getter(#onCancel), Future.value(null)) + as _i5.Future); + @override + bool get wasCancelled => + (super.noSuchMethod(Invocation.getter(#wasCancelled), false) as bool); + @override + void enqueueMessage(_i9.Message? message) => + super.noSuchMethod(Invocation.method(#enqueueMessage, [message])); + @override + T ensureNotTerminatedSync(T Function()? f) => (super.noSuchMethod( + Invocation.method(#ensureNotTerminatedSync, [f]), null) as T); + @override + _i5.Future ensureNotTerminatedAsync( + _i5.Future Function()? f) => + (super.noSuchMethod(Invocation.method(#ensureNotTerminatedAsync, [f]), + Future.value(null)) as _i5.Future); + @override + dynamic ensureNotClosingSync(dynamic Function()? f) => + super.noSuchMethod(Invocation.method(#ensureNotClosingSync, [f])); +} diff --git a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart index ed7db2ea9d..8f471a25c8 100644 --- a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart @@ -2,16 +2,24 @@ // 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/async_utils/async_utils.dart'; +import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:test/test.dart'; import 'package:http2/transport.dart'; -import 'package:http2/src/async_utils/async_utils.dart'; import 'package:http2/src/flowcontrol/queue_messages.dart'; import 'package:http2/src/flowcontrol/stream_queues.dart'; import 'package:http2/src/flowcontrol/window_handler.dart'; import 'package:http2/src/flowcontrol/connection_queues.dart'; +import 'stream_queues_test.mocks.dart'; + +@GenerateMocks([ + ConnectionMessageQueueOut, + IncomingWindowHandler, + OutgoingStreamWindowHandler +]) void main() { group('flowcontrol', () { const STREAM_ID = 99; @@ -20,7 +28,10 @@ void main() { group('stream-message-queue-out', () { test('window-big-enough', () { var connectionQueueMock = MockConnectionMessageQueueOut(); + when(connectionQueueMock.enqueueMessage(any)).thenReturn(null); var windowMock = MockOutgoingStreamWindowHandler(); + when(windowMock.positiveWindow).thenReturn(BufferIndicator()); + when(windowMock.decreaseWindow(any)).thenReturn(null); windowMock.positiveWindow.markUnBuffered(); var queue = @@ -28,8 +39,8 @@ void main() { expect(queue.bufferIndicator.wouldBuffer, isFalse); expect(queue.pendingMessages, 0); + when(windowMock.peerWindowSize).thenReturn(BYTES.length); - windowMock.peerWindowSize = BYTES.length; queue.enqueueMessage(DataMessage(STREAM_ID, BYTES, true)); verify(windowMock.decreaseWindow(BYTES.length)).called(1); final capturedMessage = @@ -44,8 +55,10 @@ void main() { test('window-smaller-than-necessary', () { var connectionQueueMock = MockConnectionMessageQueueOut(); + when(connectionQueueMock.enqueueMessage(any)).thenReturn(null); var windowMock = MockOutgoingStreamWindowHandler(); - + when(windowMock.positiveWindow).thenReturn(BufferIndicator()); + when(windowMock.decreaseWindow(any)).thenReturn(null); windowMock.positiveWindow.markUnBuffered(); var queue = StreamMessageQueueOut(STREAM_ID, windowMock, connectionQueueMock); @@ -55,7 +68,7 @@ void main() { // We set the window size fixed to 1, which means all the data messages // will get fragmented to 1 byte. - windowMock.peerWindowSize = 1; + when(windowMock.peerWindowSize).thenReturn(1); queue.enqueueMessage(DataMessage(STREAM_ID, BYTES, true)); expect(queue.pendingMessages, 0); @@ -69,13 +82,15 @@ void main() { expect(dataMessage.bytes, BYTES.sublist(counter, counter + 1)); expect(dataMessage.endStream, counter == BYTES.length - 1); } + verify(windowMock.positiveWindow).called(greaterThan(0)); + verify(windowMock.peerWindowSize).called(greaterThan(0)); verifyNoMoreInteractions(windowMock); }); test('window-empty', () { var connectionQueueMock = MockConnectionMessageQueueOut(); var windowMock = MockOutgoingStreamWindowHandler(); - + when(windowMock.positiveWindow).thenReturn(BufferIndicator()); windowMock.positiveWindow.markUnBuffered(); var queue = StreamMessageQueueOut(STREAM_ID, windowMock, connectionQueueMock); @@ -83,11 +98,13 @@ void main() { expect(queue.bufferIndicator.wouldBuffer, isFalse); expect(queue.pendingMessages, 0); - windowMock.peerWindowSize = 0; + when(windowMock.peerWindowSize).thenReturn(0); queue.enqueueMessage(DataMessage(STREAM_ID, BYTES, true)); expect(queue.bufferIndicator.wouldBuffer, isTrue); expect(queue.pendingMessages, 1); - verifyZeroInteractions(windowMock); + verify(windowMock.positiveWindow).called(greaterThan(0)); + verify(windowMock.peerWindowSize).called(greaterThan(0)); + verifyNoMoreInteractions(windowMock); verifyZeroInteractions(connectionQueueMock); }); }); @@ -95,6 +112,8 @@ void main() { group('stream-message-queue-in', () { test('data-end-of-stream', () { var windowMock = MockIncomingWindowHandler(); + when(windowMock.gotData(any)).thenReturn(null); + when(windowMock.dataProcessed(any)).thenReturn(null); var queue = StreamMessageQueueIn(windowMock); expect(queue.pendingMessages, 0); @@ -119,6 +138,7 @@ void main() { final bytes = [1, 2, 3]; var windowMock = MockIncomingWindowHandler(); + when(windowMock.gotData(any)).thenReturn(null); var queue = StreamMessageQueueIn(windowMock); var sub = queue.messages.listen(expectAsync1((_) {}, count: 0), @@ -131,22 +151,9 @@ void main() { expect(queue.bufferIndicator.wouldBuffer, isTrue); // We assert that we got the data, but it wasn't processed. verify(windowMock.gotData(bytes.length)).called(1); - verifyNever(windowMock.dataProcessed(any)); + // verifyNever(windowMock.dataProcessed(any)); }); // TODO: Add tests for Headers/HeadersPush messages. }); } - -class MockConnectionMessageQueueOut extends Mock - implements ConnectionMessageQueueOut {} - -class MockIncomingWindowHandler extends Mock implements IncomingWindowHandler {} - -class MockOutgoingStreamWindowHandler extends Mock - implements OutgoingStreamWindowHandler { - @override - final BufferIndicator positiveWindow = BufferIndicator(); - @override - int peerWindowSize; -} diff --git a/pkgs/http2/test/src/flowcontrol/stream_queues_test.mocks.dart b/pkgs/http2/test/src/flowcontrol/stream_queues_test.mocks.dart new file mode 100644 index 0000000000..c52b1bd04c --- /dev/null +++ b/pkgs/http2/test/src/flowcontrol/stream_queues_test.mocks.dart @@ -0,0 +1,103 @@ +import 'dart:async' as _i4; + +import 'package:http2/src/async_utils/async_utils.dart' as _i2; +import 'package:http2/src/flowcontrol/connection_queues.dart' as _i3; +import 'package:http2/src/flowcontrol/queue_messages.dart' as _i5; +import 'package:http2/src/flowcontrol/window_handler.dart' as _i6; +import 'package:http2/src/frames/frames.dart' as _i7; +import 'package:mockito/mockito.dart' as _i1; + +// ignore_for_file: comment_references + +// ignore_for_file: unnecessary_parenthesis + +class _FakeBufferIndicator extends _i1.Fake implements _i2.BufferIndicator {} + +/// A class which mocks [ConnectionMessageQueueOut]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockConnectionMessageQueueOut extends _i1.Mock + implements _i3.ConnectionMessageQueueOut { + MockConnectionMessageQueueOut() { + _i1.throwOnMissingStub(this); + } + + @override + int get pendingMessages => + (super.noSuchMethod(Invocation.getter(#pendingMessages), 0) as int); + @override + bool get wasTerminated => + (super.noSuchMethod(Invocation.getter(#wasTerminated), false) as bool); + @override + _i4.Future get done => + (super.noSuchMethod(Invocation.getter(#done), Future.value(null)) + as _i4.Future); + @override + bool get isClosing => + (super.noSuchMethod(Invocation.getter(#isClosing), false) as bool); + @override + bool get wasClosed => + (super.noSuchMethod(Invocation.getter(#wasClosed), false) as bool); + @override + void enqueueMessage(_i5.Message? message) => + super.noSuchMethod(Invocation.method(#enqueueMessage, [message])); + @override + T ensureNotTerminatedSync(T Function()? f) => (super.noSuchMethod( + Invocation.method(#ensureNotTerminatedSync, [f]), null) as T); + @override + _i4.Future ensureNotTerminatedAsync( + _i4.Future Function()? f) => + (super.noSuchMethod(Invocation.method(#ensureNotTerminatedAsync, [f]), + Future.value(null)) as _i4.Future); + @override + dynamic ensureNotClosingSync(dynamic Function()? f) => + super.noSuchMethod(Invocation.method(#ensureNotClosingSync, [f])); +} + +/// A class which mocks [IncomingWindowHandler]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockIncomingWindowHandler extends _i1.Mock + implements _i6.IncomingWindowHandler { + MockIncomingWindowHandler() { + _i1.throwOnMissingStub(this); + } + + @override + int get localWindowSize => + (super.noSuchMethod(Invocation.getter(#localWindowSize), 0) as int); + @override + void gotData(int? numberOfBytes) => + super.noSuchMethod(Invocation.method(#gotData, [numberOfBytes])); + @override + void dataProcessed(int? numberOfBytes) => + super.noSuchMethod(Invocation.method(#dataProcessed, [numberOfBytes])); +} + +/// A class which mocks [OutgoingStreamWindowHandler]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockOutgoingStreamWindowHandler extends _i1.Mock + implements _i6.OutgoingStreamWindowHandler { + MockOutgoingStreamWindowHandler() { + _i1.throwOnMissingStub(this); + } + + @override + _i2.BufferIndicator get positiveWindow => (super.noSuchMethod( + Invocation.getter(#positiveWindow), _FakeBufferIndicator()) + as _i2.BufferIndicator); + @override + int get peerWindowSize => + (super.noSuchMethod(Invocation.getter(#peerWindowSize), 0) as int); + @override + void processInitialWindowSizeSettingChange(int? difference) => + super.noSuchMethod(Invocation.method( + #processInitialWindowSizeSettingChange, [difference])); + @override + void processWindowUpdate(_i7.WindowUpdateFrame? frame) => + super.noSuchMethod(Invocation.method(#processWindowUpdate, [frame])); + @override + void decreaseWindow(int? numberOfBytes) => + super.noSuchMethod(Invocation.method(#decreaseWindow, [numberOfBytes])); +} diff --git a/pkgs/http2/test/src/frames/frame_reader_test.dart b/pkgs/http2/test/src/frames/frame_reader_test.dart index d35fec6fb3..614c8abe82 100644 --- a/pkgs/http2/test/src/frames/frame_reader_test.dart +++ b/pkgs/http2/test/src/frames/frame_reader_test.dart @@ -44,13 +44,15 @@ void main() { expect(dataFrame.hasEndStreamFlag, isFalse); expect(dataFrame.hasPaddedFlag, isFalse); expect(dataFrame.bytes, body); - }), onError: expectAsync2((error, stack) {}, count: 0)); + }), + onError: + expectAsync2((Object error, StackTrace stack) {}, count: 0)); }); test('data-frame--max-frame-size-plus-1', () { var body = List.filled(maxFrameSize + 1, 0x42); dataFrame(body).listen(expectAsync1((_) {}, count: 0), - onError: expectAsync2((error, stack) { + onError: expectAsync2((Object error, StackTrace stack) { expect('$error', contains('Incoming frame is too big')); })); }); @@ -66,7 +68,7 @@ void main() { ..close(); reader.startDecoding().listen(expectAsync1((_) {}, count: 0), - onError: expectAsync2((error, stack) { + onError: expectAsync2((Object error, StackTrace stack) { expect('$error', contains('incomplete frame')); })); }); @@ -87,7 +89,7 @@ void main() { ..close(); reader.startDecoding().listen(expectAsync1((_) {}, count: 0), - onError: expectAsync2((error, stack) { + onError: expectAsync2((Object error, StackTrace stack) { expect('$error', contains('incomplete frame')); })); }); @@ -103,7 +105,7 @@ void main() { ..close(); reader.startDecoding().listen(expectAsync1((_) {}, count: 0), - onError: expectAsync2((error, stack) { + onError: expectAsync2((Object error, StackTrace stack) { expect('$error', contains('hello world')); })); }); diff --git a/pkgs/http2/test/src/hpack/hpack_test.dart b/pkgs/http2/test/src/hpack/hpack_test.dart index 206e251e28..8e11e25d08 100644 --- a/pkgs/http2/test/src/hpack/hpack_test.dart +++ b/pkgs/http2/test/src/hpack/hpack_test.dart @@ -816,8 +816,6 @@ class _HeaderMatcher extends Matcher { } bool _compareLists(List a, List b) { - if (a == null && b == null) return true; - if (a == null && b != null) return false; if (a.length != b.length) return false; for (var i = 0; i < a.length; i++) { if (a[i] != b[i]) return false; diff --git a/pkgs/http2/test/src/ping/ping_handler_test.dart b/pkgs/http2/test/src/ping/ping_handler_test.dart index c2bdeab80a..22dff7b104 100644 --- a/pkgs/http2/test/src/ping/ping_handler_test.dart +++ b/pkgs/http2/test/src/ping/ping_handler_test.dart @@ -64,7 +64,7 @@ void main() { // Ensure outstanding pings will be completed with an error once we call // `pingHandler.terminate()`. - unawaited(future.catchError(expectAsync2((error, _) { + unawaited(future.catchError(expectAsync2((Object error, Object _) { expect(error, 'hello world'); }))); pingHandler.terminate('hello world'); @@ -76,7 +76,9 @@ void main() { var pingHandler = PingHandler(writer); pingHandler.terminate('hello world'); - expect(() => pingHandler.processPingFrame(null), + expect( + () => pingHandler.processPingFrame(PingFrame( + FrameHeader(8, FrameType.PING, PingFrame.FLAG_ACK, 1), 1)), throwsA(isTerminatedException)); expect(pingHandler.ping(), throwsA(isTerminatedException)); verifyZeroInteractions(writer); diff --git a/pkgs/http2/test/src/streams/helper.dart b/pkgs/http2/test/src/streams/helper.dart index 9aba787dff..1ccba6049d 100644 --- a/pkgs/http2/test/src/streams/helper.dart +++ b/pkgs/http2/test/src/streams/helper.dart @@ -7,8 +7,6 @@ import 'dart:async'; import 'package:test/test.dart'; import 'package:http2/transport.dart'; -import 'package:http2/src/frames/frames.dart'; -import 'package:http2/src/settings/settings.dart'; void expectHeadersEqual(List
headers, List
expectedHeaders) { expect(headers, hasLength(expectedHeaders.length)); @@ -26,7 +24,7 @@ void streamTest( String name, Future Function(ClientTransportConnection, ServerTransportConnection) func, - {ClientSettings settings}) { + {ClientSettings? settings}) { return test(name, () { var bidirect = BidirectionalConnection(); bidirect.settings = settings; @@ -36,19 +34,8 @@ void streamTest( }); } -void framesTest( - String name, Future Function(FrameWriter, FrameReader) func) { - return test(name, () { - var c = StreamController>(); - var fw = FrameWriter(null, c, ActiveSettings()); - var frameStream = FrameReader(c.stream, ActiveSettings()); - - return func(fw, frameStream); - }); -} - class BidirectionalConnection { - ClientSettings settings; + ClientSettings? settings; final StreamController> writeA = StreamController(); final StreamController> writeB = StreamController(); Stream> get readA => writeA.stream; diff --git a/pkgs/http2/test/transport_test.dart b/pkgs/http2/test/transport_test.dart index 6f5a9b9573..0503dcfc43 100644 --- a/pkgs/http2/test/transport_test.dart +++ b/pkgs/http2/test/transport_test.dart @@ -22,7 +22,8 @@ void main() { transportTest('terminated-client-ping', (TransportConnection client, TransportConnection server) async { - var clientError = client.ping().catchError(expectAsync2((e, s) { + var clientError = + client.ping().catchError(expectAsync2((Object e, StackTrace s) { expect(e, isA()); })); await client.terminate(); @@ -30,17 +31,18 @@ void main() { // NOTE: Now the connection is dead and client/server should complete // with [TransportException]s when doing work (e.g. ping). - unawaited(client.ping().catchError(expectAsync2((e, s) { + unawaited(client.ping().catchError(expectAsync2((Object e, StackTrace s) { expect(e, isA()); }))); - unawaited(server.ping().catchError(expectAsync2((e, s) { + unawaited(server.ping().catchError(expectAsync2((Object e, StackTrace s) { expect(e, isA()); }))); }); transportTest('terminated-server-ping', (TransportConnection client, TransportConnection server) async { - var clientError = client.ping().catchError(expectAsync2((e, s) { + var clientError = + client.ping().catchError(expectAsync2((Object e, StackTrace s) { expect(e, isA()); })); await server.terminate(); @@ -48,10 +50,10 @@ void main() { // NOTE: Now the connection is dead and the client/server should complete // with [TransportException]s when doing work (e.g. ping). - unawaited(client.ping().catchError(expectAsync2((e, s) { + unawaited(client.ping().catchError(expectAsync2((Object e, StackTrace s) { expect(e, isA()); }))); - unawaited(server.ping().catchError(expectAsync2((e, s) { + unawaited(server.ping().catchError(expectAsync2((Object e, StackTrace s) { expect(e, isA()); }))); }); @@ -223,7 +225,7 @@ void main() { stream.incomingMessages.listen(expectAsync1((msg) { expect(msg, isA()); readyForError.complete(); - }), onError: expectAsync1((error) { + }), onError: expectAsync1((Object error) { expect('$error', contains('Stream was terminated by peer')); })); } @@ -253,8 +255,10 @@ void main() { Future clientFun() async { var headers = [Header.ascii('a', 'b')]; var stream = client.makeRequest(headers, endStream: true); - await stream.incomingMessages.toList().catchError(expectAsync1((error) { + var messageList = stream.incomingMessages.toList(); + await messageList.catchError(expectAsync1((Object error) { expect('$error', contains('Stream was terminated by peer')); + return []; })); await client.finish(); } @@ -269,7 +273,7 @@ void main() { Future serverFun() async { await for (ServerTransportStream stream in server.incomingStreams) { - stream.onTerminated = expectAsync1((errorCode) { + stream.onTerminated = expectAsync1((Object? errorCode) { expect(errorCode, 8); }, count: 1); stream.sendHeaders([Header.ascii('x', 'y')], endStream: false); @@ -277,7 +281,7 @@ void main() { expectAsync1((msg) { expect(msg, isA()); }), - onError: expectAsync1((_) {}, count: 0), + onError: expectAsync1((Object _) {}, count: 0), onDone: expectAsync0(() { readyForError.complete(); }, count: 1), @@ -312,7 +316,7 @@ void main() { await readyForError.future; stream.terminate(); }), - onError: expectAsync1((_) {}, count: 0), + onError: expectAsync1((Object _) {}, count: 0), onDone: expectAsync0(() {}, count: 1), ); } @@ -322,7 +326,7 @@ void main() { Future clientFun() async { var headers = [Header.ascii('a', 'b')]; var stream = client.makeRequest(headers, endStream: false); - stream.onTerminated = expectAsync1((errorCode) { + stream.onTerminated = expectAsync1((Object? errorCode) { expect(errorCode, 8); }, count: 1); readyForError.complete(); @@ -414,7 +418,7 @@ void main() { stream.sendHeaders([Header.ascii('x', 'y')]); var messageNr = 0; - StreamController controller; + var controller = StreamController(); void addData() { if (!controller.isPaused) { if (messageNr < kNumberOfMessages) { @@ -434,23 +438,23 @@ void main() { } } - controller = StreamController( - onListen: () { - addData(); - }, - onPause: expectAsync0(() { - // Assert that we're now at the place (since the granularity - // of adding is [kChunkSize], it could be that we added - // [kChunkSize - 1] bytes more than allowed, before getting - // the pause event). - expect((serverSentBytes - kChunkSize + 1), - lessThan(expectedStreamFlowcontrolWindow)); - flowcontrolWindowFull.complete(); - }), - onResume: () { - addData(); - }, - onCancel: () {}); + controller + ..onListen = () { + addData(); + } + ..onPause = expectAsync0(() { + // Assert that we're now at the place (since the granularity + // of adding is [kChunkSize], it could be that we added + // [kChunkSize - 1] bytes more than allowed, before getting + // the pause event). + expect((serverSentBytes - kChunkSize + 1), + lessThan(expectedStreamFlowcontrolWindow)); + flowcontrolWindowFull.complete(); + }) + ..onResume = () { + addData(); + } + ..onCancel = () {}; await stream.outgoingMessages.addStream(controller.stream); await stream.outgoingMessages.close(); @@ -512,8 +516,8 @@ void transportTest( String name, Future Function(ClientTransportConnection, ServerTransportConnection) func, - {ClientSettings clientSettings, - ServerSettings serverSettings}) { + {ClientSettings? clientSettings, + ServerSettings? serverSettings}) { return test(name, () { var bidirectional = BidirectionalConnection(); bidirectional.clientSettings = clientSettings; @@ -525,8 +529,8 @@ void transportTest( } class BidirectionalConnection { - ClientSettings clientSettings; - ServerSettings serverSettings; + ClientSettings? clientSettings; + ServerSettings? serverSettings; final StreamController> writeA = StreamController(); final StreamController> writeB = StreamController(); From 903662ecd9d0dbaca6e8bc154e242aa1dd0dc2f6 Mon Sep 17 00:00:00 2001 From: Alexander Thomas Date: Wed, 27 Jan 2021 17:56:48 +0100 Subject: [PATCH 113/172] Migrate to GitHub Actions (dart-lang/http2#81) --- pkgs/http2/.github/workflows/test-package.yml | 61 +++++++++++++++++++ pkgs/http2/.travis.yml | 31 ---------- 2 files changed, 61 insertions(+), 31 deletions(-) create mode 100644 pkgs/http2/.github/workflows/test-package.yml delete mode 100644 pkgs/http2/.travis.yml diff --git a/pkgs/http2/.github/workflows/test-package.yml b/pkgs/http2/.github/workflows/test-package.yml new file mode 100644 index 0000000000..21a3c50b68 --- /dev/null +++ b/pkgs/http2/.github/workflows/test-package.yml @@ -0,0 +1,61 @@ +name: Dart CI + +on: + # Run on PRs and pushes to the default branch. + push: + branches: [ master ] + pull_request: + branches: [ master ] + schedule: + - cron: "0 0 * * 0" + +env: + PUB_ENVIRONMENT: bot.github + +jobs: + # Check code formatting and static analysis on a single OS (linux) + # against Dart dev. + analyze: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + sdk: [dev] + steps: + - uses: actions/checkout@v2 + - uses: dart-lang/setup-dart@v0.3 + with: + sdk: ${{ matrix.sdk }} + - id: install + name: Install dependencies + run: dart pub get + - name: Check formatting + run: dart format --output=none --set-exit-if-changed . + if: always() && steps.install.outcome == 'success' + - name: Analyze code + run: dart analyze --fatal-infos + if: always() && steps.install.outcome == 'success' + + # Run tests on a matrix consisting of two dimensions: + # 1. OS: ubuntu-latest, (macos-latest, windows-latest) + # 2. release channel: dev + test: + needs: analyze + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + # Add macos-latest and/or windows-latest if relevant for this package. + os: [ubuntu-latest] + sdk: [dev] + steps: + - uses: actions/checkout@v2 + - uses: dart-lang/setup-dart@v0.3 + with: + sdk: ${{ matrix.sdk }} + - id: install + name: Install dependencies + run: dart pub get + - name: Run VM tests + run: dart test --platform vm + if: always() && steps.install.outcome == 'success' diff --git a/pkgs/http2/.travis.yml b/pkgs/http2/.travis.yml deleted file mode 100644 index b1bdcd366d..0000000000 --- a/pkgs/http2/.travis.yml +++ /dev/null @@ -1,31 +0,0 @@ -language: dart - -dart: - - dev - -jobs: - include: - - name: "Analyzer" - os: linux - script: dart analyze --fatal-warnings --fatal-infos . - - name: "Format" - os: linux - script: dartfmt -n --set-exit-if-changed . - - name: "Tests" - os: linux - script: dart test --exclude-tags flaky - - name: "Flaky Tests" - os: linux - script: dart test --tags flaky - allow_failures: - - name: "Flaky Tests" - os: linux - script: dart test --tags flaky - -# Only building master means that we don't run two builds for each pull request. -branches: - only: [master] - -cache: - directories: - - $HOME/.pub-cache From 2a595e960bf74ee2c130727945e3b82fb4feeae4 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 8 Feb 2021 06:49:56 -0800 Subject: [PATCH 114/172] Fix tests and some lints (dart-lang/http2#83) --- pkgs/http2/analysis_options.yaml | 11 ++ .../lib/src/flowcontrol/queue_messages.dart | 4 +- pkgs/http2/lib/src/settings/settings.dart | 2 +- .../http2/lib/src/streams/stream_handler.dart | 4 +- .../connection_queues_test.mocks.dart | 105 ++++++++++++------ .../flowcontrol/stream_queues_test.mocks.dart | 50 ++++++--- pkgs/http2/test/transport_test.dart | 4 +- 7 files changed, 124 insertions(+), 56 deletions(-) diff --git a/pkgs/http2/analysis_options.yaml b/pkgs/http2/analysis_options.yaml index 56a5db7f62..2ad0cbccc4 100644 --- a/pkgs/http2/analysis_options.yaml +++ b/pkgs/http2/analysis_options.yaml @@ -1,4 +1,5 @@ include: package:pedantic/analysis_options.yaml + analyzer: strong-mode: implicit-casts: false @@ -7,8 +8,10 @@ analyzer: unused_import: error unused_local_variable: error dead_code: error + linter: rules: + - avoid_unused_constructor_parameters - await_only_futures - camel_case_types - comment_references @@ -17,7 +20,15 @@ linter: - empty_statements - hash_and_equals - implementation_imports + - iterable_contains_unrelated_type + - list_remove_unrelated_type - non_constant_identifier_names - only_throw_errors + - overridden_fields + - package_api_docs + - package_names + - package_prefixed_library_names - test_types_in_equals - throw_in_finally + - unnecessary_parenthesis + - unnecessary_brace_in_string_interps diff --git a/pkgs/http2/lib/src/flowcontrol/queue_messages.dart b/pkgs/http2/lib/src/flowcontrol/queue_messages.dart index ea22b63689..5c71228d45 100644 --- a/pkgs/http2/lib/src/flowcontrol/queue_messages.dart +++ b/pkgs/http2/lib/src/flowcontrol/queue_messages.dart @@ -70,6 +70,6 @@ class GoawayMessage extends Message { : super(0, false); @override - String toString() => 'GoawayMessage(lastStreamId: ${lastStreamId}, ' - 'errorCode: ${errorCode}, debugData: ${debugData.length})'; + String toString() => 'GoawayMessage(lastStreamId: $lastStreamId, ' + 'errorCode: $errorCode, debugData: ${debugData.length})'; } diff --git a/pkgs/http2/lib/src/settings/settings.dart b/pkgs/http2/lib/src/settings/settings.dart index fc9c1b726d..19d48a0490 100644 --- a/pkgs/http2/lib/src/settings/settings.dart +++ b/pkgs/http2/lib/src/settings/settings.dart @@ -76,7 +76,7 @@ class ActiveSettings { this.enablePush = true, this.maxConcurrentStreams, this.initialWindowSize = (1 << 16) - 1, - this.maxFrameSize = (1 << 14), + this.maxFrameSize = 1 << 14, this.maxHeaderListSize}); } diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index eed12404bf..e11a61abf4 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -357,8 +357,8 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { } bool _canPush(Http2StreamImpl stream) { - var openState = (stream.state == StreamState.Open || - stream.state == StreamState.HalfClosedRemote); + var openState = stream.state == StreamState.Open || + stream.state == StreamState.HalfClosedRemote; var pushEnabled = _peerSettings.enablePush; return openState && pushEnabled && diff --git a/pkgs/http2/test/src/flowcontrol/connection_queues_test.mocks.dart b/pkgs/http2/test/src/flowcontrol/connection_queues_test.mocks.dart index 5424460b1f..c72f2364a4 100644 --- a/pkgs/http2/test/src/flowcontrol/connection_queues_test.mocks.dart +++ b/pkgs/http2/test/src/flowcontrol/connection_queues_test.mocks.dart @@ -27,60 +27,72 @@ class MockFrameWriter extends _i1.Mock implements _i4.FrameWriter { } @override - _i2.BufferIndicator get bufferIndicator => (super.noSuchMethod( - Invocation.getter(#bufferIndicator), _FakeBufferIndicator()) - as _i2.BufferIndicator); + _i2.BufferIndicator get bufferIndicator => + (super.noSuchMethod(Invocation.getter(#bufferIndicator), + returnValue: _FakeBufferIndicator()) as _i2.BufferIndicator); + @override int get highestWrittenStreamId => - (super.noSuchMethod(Invocation.getter(#highestWrittenStreamId), 0) - as int); + (super.noSuchMethod(Invocation.getter(#highestWrittenStreamId), + returnValue: 0) as int); + @override _i5.Future get doneFuture => - (super.noSuchMethod(Invocation.getter(#doneFuture), Future.value(null)) - as _i5.Future); + (super.noSuchMethod(Invocation.getter(#doneFuture), + returnValue: Future.value(null)) as _i5.Future); + @override void writeDataFrame(int? streamId, List? data, {bool? endStream = false}) => super.noSuchMethod(Invocation.method( #writeDataFrame, [streamId, data], {#endStream: endStream})); + @override void writeHeadersFrame(int? streamId, List<_i6.Header>? headers, {bool? endStream = true}) => super.noSuchMethod(Invocation.method( #writeHeadersFrame, [streamId, headers], {#endStream: endStream})); + @override void writePriorityFrame(int? streamId, int? streamDependency, int? weight, {bool? exclusive = false}) => super.noSuchMethod(Invocation.method(#writePriorityFrame, [streamId, streamDependency, weight], {#exclusive: exclusive})); + @override void writeRstStreamFrame(int? streamId, int? errorCode) => super.noSuchMethod( Invocation.method(#writeRstStreamFrame, [streamId, errorCode])); + @override void writeSettingsFrame(List<_i4.Setting>? settings) => super.noSuchMethod(Invocation.method(#writeSettingsFrame, [settings])); + @override void writePushPromiseFrame( int? streamId, int? promisedStreamId, List<_i6.Header>? headers) => super.noSuchMethod(Invocation.method( #writePushPromiseFrame, [streamId, promisedStreamId, headers])); + @override void writePingFrame(int? opaqueData, {bool? ack = false}) => super.noSuchMethod( Invocation.method(#writePingFrame, [opaqueData], {#ack: ack})); + @override void writeGoawayFrame( int? lastStreamId, int? errorCode, List? debugData) => super.noSuchMethod(Invocation.method( #writeGoawayFrame, [lastStreamId, errorCode, debugData])); + @override void writeWindowUpdate(int? sizeIncrement, {int? streamId = 0}) => super.noSuchMethod(Invocation.method( #writeWindowUpdate, [sizeIncrement], {#streamId: streamId})); + @override _i5.Future close() => - (super.noSuchMethod(Invocation.method(#close, []), Future.value(null)) - as _i5.Future); + (super.noSuchMethod(Invocation.method(#close, []), + returnValue: Future.value(null)) as _i5.Future); } /// A class which mocks [IncomingWindowHandler]. @@ -94,10 +106,13 @@ class MockIncomingWindowHandler extends _i1.Mock @override int get localWindowSize => - (super.noSuchMethod(Invocation.getter(#localWindowSize), 0) as int); + (super.noSuchMethod(Invocation.getter(#localWindowSize), returnValue: 0) + as int); + @override void gotData(int? numberOfBytes) => super.noSuchMethod(Invocation.method(#gotData, [numberOfBytes])); + @override void dataProcessed(int? numberOfBytes) => super.noSuchMethod(Invocation.method(#dataProcessed, [numberOfBytes])); @@ -114,55 +129,75 @@ class MockStreamMessageQueueIn extends _i1.Mock @override _i3.IncomingWindowHandler get windowHandler => (super.noSuchMethod( - Invocation.getter(#windowHandler), _FakeIncomingWindowHandler()) - as _i3.IncomingWindowHandler); + Invocation.getter(#windowHandler), + returnValue: _FakeIncomingWindowHandler()) as _i3.IncomingWindowHandler); + @override - _i2.BufferIndicator get bufferIndicator => (super.noSuchMethod( - Invocation.getter(#bufferIndicator), _FakeBufferIndicator()) - as _i2.BufferIndicator); + _i2.BufferIndicator get bufferIndicator => + (super.noSuchMethod(Invocation.getter(#bufferIndicator), + returnValue: _FakeBufferIndicator()) as _i2.BufferIndicator); + @override int get pendingMessages => - (super.noSuchMethod(Invocation.getter(#pendingMessages), 0) as int); + (super.noSuchMethod(Invocation.getter(#pendingMessages), returnValue: 0) + as int); + @override - _i5.Stream<_i8.StreamMessage> get messages => (super.noSuchMethod( - Invocation.getter(#messages), Stream<_i8.StreamMessage>.empty()) - as _i5.Stream<_i8.StreamMessage>); + _i5.Stream<_i8.StreamMessage> get messages => + (super.noSuchMethod(Invocation.getter(#messages), + returnValue: Stream<_i8.StreamMessage>.empty()) + as _i5.Stream<_i8.StreamMessage>); + @override - _i5.Stream<_i8.TransportStreamPush> get serverPushes => (super.noSuchMethod( - Invocation.getter(#serverPushes), - Stream<_i8.TransportStreamPush>.empty()) - as _i5.Stream<_i8.TransportStreamPush>); + _i5.Stream<_i8.TransportStreamPush> get serverPushes => + (super.noSuchMethod(Invocation.getter(#serverPushes), + returnValue: Stream<_i8.TransportStreamPush>.empty()) + as _i5.Stream<_i8.TransportStreamPush>); + @override bool get wasTerminated => - (super.noSuchMethod(Invocation.getter(#wasTerminated), false) as bool); + (super.noSuchMethod(Invocation.getter(#wasTerminated), returnValue: false) + as bool); + @override - _i5.Future get done => - (super.noSuchMethod(Invocation.getter(#done), Future.value(null)) - as _i5.Future); + _i5.Future get done => (super.noSuchMethod(Invocation.getter(#done), + returnValue: Future.value(null)) as _i5.Future); + @override bool get isClosing => - (super.noSuchMethod(Invocation.getter(#isClosing), false) as bool); + (super.noSuchMethod(Invocation.getter(#isClosing), returnValue: false) + as bool); + @override bool get wasClosed => - (super.noSuchMethod(Invocation.getter(#wasClosed), false) as bool); + (super.noSuchMethod(Invocation.getter(#wasClosed), returnValue: false) + as bool); + @override _i5.Future get onCancel => - (super.noSuchMethod(Invocation.getter(#onCancel), Future.value(null)) - as _i5.Future); + (super.noSuchMethod(Invocation.getter(#onCancel), + returnValue: Future.value(null)) as _i5.Future); + @override bool get wasCancelled => - (super.noSuchMethod(Invocation.getter(#wasCancelled), false) as bool); + (super.noSuchMethod(Invocation.getter(#wasCancelled), returnValue: false) + as bool); + @override void enqueueMessage(_i9.Message? message) => super.noSuchMethod(Invocation.method(#enqueueMessage, [message])); + @override - T ensureNotTerminatedSync(T Function()? f) => (super.noSuchMethod( - Invocation.method(#ensureNotTerminatedSync, [f]), null) as T); + T ensureNotTerminatedSync(T Function()? f) => + (super.noSuchMethod(Invocation.method(#ensureNotTerminatedSync, [f]), + returnValue: null) as T); + @override _i5.Future ensureNotTerminatedAsync( _i5.Future Function()? f) => (super.noSuchMethod(Invocation.method(#ensureNotTerminatedAsync, [f]), - Future.value(null)) as _i5.Future); + returnValue: Future.value(null)) as _i5.Future); + @override dynamic ensureNotClosingSync(dynamic Function()? f) => super.noSuchMethod(Invocation.method(#ensureNotClosingSync, [f])); diff --git a/pkgs/http2/test/src/flowcontrol/stream_queues_test.mocks.dart b/pkgs/http2/test/src/flowcontrol/stream_queues_test.mocks.dart index c52b1bd04c..a1cd3db000 100644 --- a/pkgs/http2/test/src/flowcontrol/stream_queues_test.mocks.dart +++ b/pkgs/http2/test/src/flowcontrol/stream_queues_test.mocks.dart @@ -24,31 +24,43 @@ class MockConnectionMessageQueueOut extends _i1.Mock @override int get pendingMessages => - (super.noSuchMethod(Invocation.getter(#pendingMessages), 0) as int); + (super.noSuchMethod(Invocation.getter(#pendingMessages), returnValue: 0) + as int); + @override bool get wasTerminated => - (super.noSuchMethod(Invocation.getter(#wasTerminated), false) as bool); + (super.noSuchMethod(Invocation.getter(#wasTerminated), returnValue: false) + as bool); + @override - _i4.Future get done => - (super.noSuchMethod(Invocation.getter(#done), Future.value(null)) - as _i4.Future); + _i4.Future get done => (super.noSuchMethod(Invocation.getter(#done), + returnValue: Future.value(null)) as _i4.Future); + @override bool get isClosing => - (super.noSuchMethod(Invocation.getter(#isClosing), false) as bool); + (super.noSuchMethod(Invocation.getter(#isClosing), returnValue: false) + as bool); + @override bool get wasClosed => - (super.noSuchMethod(Invocation.getter(#wasClosed), false) as bool); + (super.noSuchMethod(Invocation.getter(#wasClosed), returnValue: false) + as bool); + @override void enqueueMessage(_i5.Message? message) => super.noSuchMethod(Invocation.method(#enqueueMessage, [message])); + @override - T ensureNotTerminatedSync(T Function()? f) => (super.noSuchMethod( - Invocation.method(#ensureNotTerminatedSync, [f]), null) as T); + T ensureNotTerminatedSync(T Function()? f) => + (super.noSuchMethod(Invocation.method(#ensureNotTerminatedSync, [f]), + returnValue: null) as T); + @override _i4.Future ensureNotTerminatedAsync( _i4.Future Function()? f) => (super.noSuchMethod(Invocation.method(#ensureNotTerminatedAsync, [f]), - Future.value(null)) as _i4.Future); + returnValue: Future.value(null)) as _i4.Future); + @override dynamic ensureNotClosingSync(dynamic Function()? f) => super.noSuchMethod(Invocation.method(#ensureNotClosingSync, [f])); @@ -65,10 +77,13 @@ class MockIncomingWindowHandler extends _i1.Mock @override int get localWindowSize => - (super.noSuchMethod(Invocation.getter(#localWindowSize), 0) as int); + (super.noSuchMethod(Invocation.getter(#localWindowSize), returnValue: 0) + as int); + @override void gotData(int? numberOfBytes) => super.noSuchMethod(Invocation.method(#gotData, [numberOfBytes])); + @override void dataProcessed(int? numberOfBytes) => super.noSuchMethod(Invocation.method(#dataProcessed, [numberOfBytes])); @@ -84,19 +99,24 @@ class MockOutgoingStreamWindowHandler extends _i1.Mock } @override - _i2.BufferIndicator get positiveWindow => (super.noSuchMethod( - Invocation.getter(#positiveWindow), _FakeBufferIndicator()) - as _i2.BufferIndicator); + _i2.BufferIndicator get positiveWindow => + (super.noSuchMethod(Invocation.getter(#positiveWindow), + returnValue: _FakeBufferIndicator()) as _i2.BufferIndicator); + @override int get peerWindowSize => - (super.noSuchMethod(Invocation.getter(#peerWindowSize), 0) as int); + (super.noSuchMethod(Invocation.getter(#peerWindowSize), returnValue: 0) + as int); + @override void processInitialWindowSizeSettingChange(int? difference) => super.noSuchMethod(Invocation.method( #processInitialWindowSizeSettingChange, [difference])); + @override void processWindowUpdate(_i7.WindowUpdateFrame? frame) => super.noSuchMethod(Invocation.method(#processWindowUpdate, [frame])); + @override void decreaseWindow(int? numberOfBytes) => super.noSuchMethod(Invocation.method(#decreaseWindow, [numberOfBytes])); diff --git a/pkgs/http2/test/transport_test.dart b/pkgs/http2/test/transport_test.dart index 0503dcfc43..2d09e3107c 100644 --- a/pkgs/http2/test/transport_test.dart +++ b/pkgs/http2/test/transport_test.dart @@ -447,7 +447,7 @@ void main() { // of adding is [kChunkSize], it could be that we added // [kChunkSize - 1] bytes more than allowed, before getting // the pause event). - expect((serverSentBytes - kChunkSize + 1), + expect(serverSentBytes - kChunkSize + 1, lessThan(expectedStreamFlowcontrolWindow)); flowcontrolWindowFull.complete(); }) @@ -534,7 +534,9 @@ class BidirectionalConnection { final StreamController> writeA = StreamController(); final StreamController> writeB = StreamController(); + Stream> get readA => writeA.stream; + Stream> get readB => writeB.stream; ClientTransportConnection get clientConnection => From 2a4578ace35cad103fbc99fc1160e8502d1a84c0 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 10 Feb 2021 16:55:09 -0800 Subject: [PATCH 115/172] Prepare for v2, null-safe stable (dart-lang/http2#84) --- pkgs/http2/CHANGELOG.md | 6 +----- pkgs/http2/pubspec.yaml | 6 +++--- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index 86c5698e07..7897c34502 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -1,11 +1,7 @@ -## 2.0.0-nullsafety.0 +## 2.0.0 * Migrate to null safety. -## 1.0.2-dev - -* Update minimum Dart SDK to `2.8.4`. - ## 1.0.1 * Add `TransportConnection.onInitialPeerSettingsReceived` which fires when diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index 5bd542301f..26e60b0c75 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -1,5 +1,5 @@ name: http2 -version: 2.0.0-nullsafety.0 +version: 2.0.0 description: A HTTP/2 implementation in Dart. repository: https://github.com/dart-lang/http2 @@ -9,5 +9,5 @@ environment: dev_dependencies: build_runner: ^1.10.0 mockito: ^5.0.0-nullsafety - test: ^1.16.0-nullsafety - pedantic: ^1.10.0-nullsafety + test: ^1.16.0 + pedantic: ^1.10.0 From 82adfc5e634a49cd446c97a737190bb5f83e13f2 Mon Sep 17 00:00:00 2001 From: Franklin Yow <58489007+franklinyow@users.noreply.github.com> Date: Fri, 2 Apr 2021 16:55:08 -0700 Subject: [PATCH 116/172] Update LICENSE Changes to comply with internal review --- pkgs/http2/LICENSE | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkgs/http2/LICENSE b/pkgs/http2/LICENSE index de31e1a0a4..dbd2843a08 100644 --- a/pkgs/http2/LICENSE +++ b/pkgs/http2/LICENSE @@ -1,4 +1,5 @@ -Copyright 2015, the Dart project authors. All rights reserved. +Copyright 2015, the Dart project authors. + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -9,7 +10,7 @@ met: copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Google Inc. nor the names of its + * Neither the name of Google LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. From 2cc6e44e804f11ae7cff6ccf0a4cc5ef80d8d7e0 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 3 May 2021 10:09:07 -0700 Subject: [PATCH 117/172] fix directive sorting (dart-lang/http2#87) --- .../manual_test/out_of_stream_ids_test.dart | 5 ++--- pkgs/http2/test/client_test.dart | 5 ++--- pkgs/http2/test/multiprotocol_server_test.dart | 5 ++--- pkgs/http2/test/server_test.dart | 5 ++--- .../test/src/async_utils/async_utils_test.dart | 2 +- pkgs/http2/test/src/error_matchers.dart | 2 +- .../src/flowcontrol/connection_queues_test.dart | 17 ++++++++--------- .../src/flowcontrol/stream_queues_test.dart | 11 +++++------ .../src/flowcontrol/window_handler_test.dart | 5 ++--- .../src/frames/frame_defragmenter_test.dart | 5 ++--- .../test/src/frames/frame_reader_test.dart | 3 +-- .../src/frames/frame_writer_reader_test.dart | 5 ++--- .../test/src/frames/frame_writer_test.dart | 5 ++--- pkgs/http2/test/src/hpack/hpack_test.dart | 2 +- .../test/src/hpack/huffman_table_test.dart | 2 +- pkgs/http2/test/src/ping/ping_handler_test.dart | 5 ++--- .../src/settings/settings_handler_test.dart | 5 ++--- pkgs/http2/test/src/streams/helper.dart | 5 +++-- .../test/src/streams/simple_flow_test.dart | 2 +- 19 files changed, 42 insertions(+), 54 deletions(-) diff --git a/pkgs/http2/manual_test/out_of_stream_ids_test.dart b/pkgs/http2/manual_test/out_of_stream_ids_test.dart index a3d9e6be90..d653793e28 100644 --- a/pkgs/http2/manual_test/out_of_stream_ids_test.dart +++ b/pkgs/http2/manual_test/out_of_stream_ids_test.dart @@ -11,12 +11,11 @@ /// /// without this patch this test will run for a _long_ time. /// --------------------------------------------------------------------------- - import 'dart:async'; -import 'package:test/test.dart'; -import 'package:http2/transport.dart'; import 'package:http2/src/streams/stream_handler.dart'; +import 'package:http2/transport.dart'; +import 'package:test/test.dart'; import '../test/transport_test.dart'; diff --git a/pkgs/http2/test/client_test.dart b/pkgs/http2/test/client_test.dart index 19b33cd084..8bd5a0b9dd 100644 --- a/pkgs/http2/test/client_test.dart +++ b/pkgs/http2/test/client_test.dart @@ -6,15 +6,14 @@ import 'dart:async'; import 'dart:convert' show ascii; import 'dart:typed_data'; -import 'package:pedantic/pedantic.dart'; -import 'package:test/test.dart'; - import 'package:http2/src/connection_preface.dart'; import 'package:http2/src/flowcontrol/window.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:pedantic/pedantic.dart'; +import 'package:test/test.dart'; import 'src/hpack/hpack_test.dart' show isHeader; diff --git a/pkgs/http2/test/multiprotocol_server_test.dart b/pkgs/http2/test/multiprotocol_server_test.dart index 42c11ba187..997f0dadfb 100644 --- a/pkgs/http2/test/multiprotocol_server_test.dart +++ b/pkgs/http2/test/multiprotocol_server_test.dart @@ -6,10 +6,9 @@ import 'dart:async'; import 'dart:convert' show ascii, utf8; import 'dart:io'; -import 'package:test/test.dart'; - -import 'package:http2/transport.dart'; import 'package:http2/multiprotocol_server.dart'; +import 'package:http2/transport.dart'; +import 'package:test/test.dart'; void main() { var context = SecurityContext() diff --git a/pkgs/http2/test/server_test.dart b/pkgs/http2/test/server_test.dart index 28b8d46acf..dffc84529b 100644 --- a/pkgs/http2/test/server_test.dart +++ b/pkgs/http2/test/server_test.dart @@ -4,13 +4,12 @@ import 'dart:async'; -import 'package:test/test.dart'; - -import 'package:http2/transport.dart'; 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', () { diff --git a/pkgs/http2/test/src/async_utils/async_utils_test.dart b/pkgs/http2/test/src/async_utils/async_utils_test.dart index 7a49ed771d..6d1ebaadd5 100644 --- a/pkgs/http2/test/src/async_utils/async_utils_test.dart +++ b/pkgs/http2/test/src/async_utils/async_utils_test.dart @@ -4,8 +4,8 @@ import 'dart:async'; -import 'package:test/test.dart'; import 'package:http2/src/async_utils/async_utils.dart'; +import 'package:test/test.dart'; void main() { group('async_utils', () { diff --git a/pkgs/http2/test/src/error_matchers.dart b/pkgs/http2/test/src/error_matchers.dart index f0eabb0aa6..7886462525 100644 --- a/pkgs/http2/test/src/error_matchers.dart +++ b/pkgs/http2/test/src/error_matchers.dart @@ -2,8 +2,8 @@ // 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:test/test.dart'; import 'package:http2/src/sync_errors.dart'; +import 'package:test/test.dart'; const Matcher isProtocolException = TypeMatcher(); const Matcher isFrameSizeException = TypeMatcher(); diff --git a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart index fd80ad8eef..ed105177e3 100644 --- a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart @@ -2,18 +2,17 @@ // 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:mockito/annotations.dart'; -import 'package:mockito/mockito.dart'; -import 'package:test/test.dart'; - -import 'package:http2/transport.dart'; import 'package:http2/src/async_utils/async_utils.dart'; -import 'package:http2/src/frames/frames.dart'; -import 'package:http2/src/flowcontrol/window.dart'; -import 'package:http2/src/flowcontrol/window_handler.dart'; import 'package:http2/src/flowcontrol/connection_queues.dart'; -import 'package:http2/src/flowcontrol/stream_queues.dart'; import 'package:http2/src/flowcontrol/queue_messages.dart'; +import 'package:http2/src/flowcontrol/stream_queues.dart'; +import 'package:http2/src/flowcontrol/window.dart'; +import 'package:http2/src/flowcontrol/window_handler.dart'; +import 'package:http2/src/frames/frames.dart'; +import 'package:http2/transport.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:test/test.dart'; import 'connection_queues_test.mocks.dart'; diff --git a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart index 8f471a25c8..099a42e11b 100644 --- a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart @@ -3,15 +3,14 @@ // BSD-style license that can be found in the LICENSE file. import 'package:http2/src/async_utils/async_utils.dart'; -import 'package:mockito/annotations.dart'; -import 'package:mockito/mockito.dart'; -import 'package:test/test.dart'; - -import 'package:http2/transport.dart'; +import 'package:http2/src/flowcontrol/connection_queues.dart'; import 'package:http2/src/flowcontrol/queue_messages.dart'; import 'package:http2/src/flowcontrol/stream_queues.dart'; import 'package:http2/src/flowcontrol/window_handler.dart'; -import 'package:http2/src/flowcontrol/connection_queues.dart'; +import 'package:http2/transport.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:test/test.dart'; import 'stream_queues_test.mocks.dart'; diff --git a/pkgs/http2/test/src/flowcontrol/window_handler_test.dart b/pkgs/http2/test/src/flowcontrol/window_handler_test.dart index 5f1dc5c989..017dadd4ef 100644 --- a/pkgs/http2/test/src/flowcontrol/window_handler_test.dart +++ b/pkgs/http2/test/src/flowcontrol/window_handler_test.dart @@ -2,12 +2,11 @@ // 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:mockito/mockito.dart'; -import 'package:test/test.dart'; - 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'; diff --git a/pkgs/http2/test/src/frames/frame_defragmenter_test.dart b/pkgs/http2/test/src/frames/frame_defragmenter_test.dart index 87067e8827..04abec1195 100644 --- a/pkgs/http2/test/src/frames/frame_defragmenter_test.dart +++ b/pkgs/http2/test/src/frames/frame_defragmenter_test.dart @@ -2,10 +2,9 @@ // 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:test/test.dart'; - -import 'package:http2/src/frames/frames.dart'; import 'package:http2/src/frames/frame_defragmenter.dart'; +import 'package:http2/src/frames/frames.dart'; +import 'package:test/test.dart'; import '../error_matchers.dart'; diff --git a/pkgs/http2/test/src/frames/frame_reader_test.dart b/pkgs/http2/test/src/frames/frame_reader_test.dart index 614c8abe82..9cdf068c40 100644 --- a/pkgs/http2/test/src/frames/frame_reader_test.dart +++ b/pkgs/http2/test/src/frames/frame_reader_test.dart @@ -4,10 +4,9 @@ import 'dart:async'; -import 'package:test/test.dart'; - import 'package:http2/src/frames/frames.dart'; import 'package:http2/src/settings/settings.dart'; +import 'package:test/test.dart'; void main() { group('frames', () { diff --git a/pkgs/http2/test/src/frames/frame_writer_reader_test.dart b/pkgs/http2/test/src/frames/frame_writer_reader_test.dart index c0f502fe09..28bc59cddd 100644 --- a/pkgs/http2/test/src/frames/frame_writer_reader_test.dart +++ b/pkgs/http2/test/src/frames/frame_writer_reader_test.dart @@ -4,11 +4,10 @@ import 'dart:async'; -import 'package:test/test.dart'; - -import 'package:http2/src/hpack/hpack.dart'; import 'package:http2/src/frames/frames.dart'; +import 'package:http2/src/hpack/hpack.dart'; import 'package:http2/src/settings/settings.dart'; +import 'package:test/test.dart'; import '../hpack/hpack_test.dart' show isHeader; diff --git a/pkgs/http2/test/src/frames/frame_writer_test.dart b/pkgs/http2/test/src/frames/frame_writer_test.dart index e9a07bddde..24b822fef0 100644 --- a/pkgs/http2/test/src/frames/frame_writer_test.dart +++ b/pkgs/http2/test/src/frames/frame_writer_test.dart @@ -4,11 +4,10 @@ import 'dart:async'; -import 'package:test/test.dart'; - -import 'package:http2/src/hpack/hpack.dart'; import 'package:http2/src/frames/frames.dart'; +import 'package:http2/src/hpack/hpack.dart'; import 'package:http2/src/settings/settings.dart'; +import 'package:test/test.dart'; void main() { group('frames', () { diff --git a/pkgs/http2/test/src/hpack/hpack_test.dart b/pkgs/http2/test/src/hpack/hpack_test.dart index 8e11e25d08..4d3bf53f60 100644 --- a/pkgs/http2/test/src/hpack/hpack_test.dart +++ b/pkgs/http2/test/src/hpack/hpack_test.dart @@ -2,8 +2,8 @@ // 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:test/test.dart'; import 'package:http2/src/hpack/hpack.dart'; +import 'package:test/test.dart'; void main() { group('hpack', () { diff --git a/pkgs/http2/test/src/hpack/huffman_table_test.dart b/pkgs/http2/test/src/hpack/huffman_table_test.dart index a2e9bd3037..fa1c75be71 100644 --- a/pkgs/http2/test/src/hpack/huffman_table_test.dart +++ b/pkgs/http2/test/src/hpack/huffman_table_test.dart @@ -4,9 +4,9 @@ import 'dart:convert' show ascii; -import 'package:test/test.dart'; import 'package:http2/src/hpack/huffman.dart'; import 'package:http2/src/hpack/huffman_table.dart'; +import 'package:test/test.dart'; void main() { group('hpack', () { diff --git a/pkgs/http2/test/src/ping/ping_handler_test.dart b/pkgs/http2/test/src/ping/ping_handler_test.dart index 22dff7b104..c4b3f86e1d 100644 --- a/pkgs/http2/test/src/ping/ping_handler_test.dart +++ b/pkgs/http2/test/src/ping/ping_handler_test.dart @@ -2,12 +2,11 @@ // 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:mockito/mockito.dart'; -import 'package:test/test.dart'; - import 'package:http2/src/frames/frames.dart'; import 'package:http2/src/ping/ping_handler.dart'; +import 'package:mockito/mockito.dart'; import 'package:pedantic/pedantic.dart'; +import 'package:test/test.dart'; import '../error_matchers.dart'; diff --git a/pkgs/http2/test/src/settings/settings_handler_test.dart b/pkgs/http2/test/src/settings/settings_handler_test.dart index 202914ee14..3d16a73ae8 100644 --- a/pkgs/http2/test/src/settings/settings_handler_test.dart +++ b/pkgs/http2/test/src/settings/settings_handler_test.dart @@ -2,12 +2,11 @@ // 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:mockito/mockito.dart'; -import 'package:test/test.dart'; - import 'package:http2/src/frames/frames.dart'; import 'package:http2/src/hpack/hpack.dart'; import 'package:http2/src/settings/settings.dart'; +import 'package:mockito/mockito.dart'; +import 'package:test/test.dart'; import '../error_matchers.dart'; diff --git a/pkgs/http2/test/src/streams/helper.dart b/pkgs/http2/test/src/streams/helper.dart index 1ccba6049d..726a7d6449 100644 --- a/pkgs/http2/test/src/streams/helper.dart +++ b/pkgs/http2/test/src/streams/helper.dart @@ -4,9 +4,8 @@ import 'dart:async'; -import 'package:test/test.dart'; - import 'package:http2/transport.dart'; +import 'package:test/test.dart'; void expectHeadersEqual(List
headers, List
expectedHeaders) { expect(headers, hasLength(expectedHeaders.length)); @@ -38,7 +37,9 @@ class BidirectionalConnection { ClientSettings? settings; final StreamController> writeA = StreamController(); final StreamController> writeB = StreamController(); + Stream> get readA => writeA.stream; + Stream> get readB => writeB.stream; ClientTransportConnection get clientConnection => diff --git a/pkgs/http2/test/src/streams/simple_flow_test.dart b/pkgs/http2/test/src/streams/simple_flow_test.dart index 2f15deac81..7a294687c6 100644 --- a/pkgs/http2/test/src/streams/simple_flow_test.dart +++ b/pkgs/http2/test/src/streams/simple_flow_test.dart @@ -4,8 +4,8 @@ import 'dart:async'; -import 'package:test/test.dart'; import 'package:http2/transport.dart'; +import 'package:test/test.dart'; import 'helper.dart'; From e7609860744563a1611c7fbc4b3cebdbf85e4e4e Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Sat, 5 Jun 2021 13:26:06 -0700 Subject: [PATCH 118/172] Add dependabot --- pkgs/http2/.github/dependabot.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 pkgs/http2/.github/dependabot.yml diff --git a/pkgs/http2/.github/dependabot.yml b/pkgs/http2/.github/dependabot.yml new file mode 100644 index 0000000000..430a85e7d0 --- /dev/null +++ b/pkgs/http2/.github/dependabot.yml @@ -0,0 +1,11 @@ +# Set update schedule for GitHub Actions +# See https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/keeping-your-actions-up-to-date-with-dependabot + +version: 2 +updates: + +- package-ecosystem: "github-actions" + directory: "/" + schedule: + # Check for updates to GitHub Actions every weekday + interval: "daily" From 4c0149b4edabaefd2612210ed612f0348d072a59 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 5 Jun 2021 13:39:20 -0700 Subject: [PATCH 119/172] Bump dart-lang/setup-dart from 0.3 to 1 (dart-lang/http2#88) Bumps [dart-lang/setup-dart](https://github.com/dart-lang/setup-dart) from 0.3 to 1. - [Release notes](https://github.com/dart-lang/setup-dart/releases) - [Changelog](https://github.com/dart-lang/setup-dart/blob/main/CHANGELOG.md) - [Commits](https://github.com/dart-lang/setup-dart/compare/v0.3...v1) --- updated-dependencies: - dependency-name: dart-lang/setup-dart dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Kevin Moore --- pkgs/http2/.github/workflows/test-package.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkgs/http2/.github/workflows/test-package.yml b/pkgs/http2/.github/workflows/test-package.yml index 21a3c50b68..cecfdde6f9 100644 --- a/pkgs/http2/.github/workflows/test-package.yml +++ b/pkgs/http2/.github/workflows/test-package.yml @@ -23,7 +23,7 @@ jobs: sdk: [dev] steps: - uses: actions/checkout@v2 - - uses: dart-lang/setup-dart@v0.3 + - uses: dart-lang/setup-dart@v1 with: sdk: ${{ matrix.sdk }} - id: install @@ -47,10 +47,10 @@ jobs: matrix: # Add macos-latest and/or windows-latest if relevant for this package. os: [ubuntu-latest] - sdk: [dev] + sdk: [2.12.0, dev] steps: - uses: actions/checkout@v2 - - uses: dart-lang/setup-dart@v0.3 + - uses: dart-lang/setup-dart@v1 with: sdk: ${{ matrix.sdk }} - id: install From 59abd34676d586c87090d4e951bab7dd92db0c74 Mon Sep 17 00:00:00 2001 From: "Mahdi K. Fard" Date: Wed, 23 Jun 2021 05:10:24 +0430 Subject: [PATCH 120/172] Ditch dart:io import. (dart-lang/http2#89) --- pkgs/http2/lib/src/hpack/huffman.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/http2/lib/src/hpack/huffman.dart b/pkgs/http2/lib/src/hpack/huffman.dart index 11b5a0ac79..aca4cde481 100644 --- a/pkgs/http2/lib/src/hpack/huffman.dart +++ b/pkgs/http2/lib/src/hpack/huffman.dart @@ -2,7 +2,7 @@ // 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:io'; +import 'dart:typed_data'; import 'huffman_table.dart'; From 95c4c639c23eff09ff8bd73f1d3e54111d195ebb Mon Sep 17 00:00:00 2001 From: "Mahdi K. Fard" Date: Wed, 23 Jun 2021 07:07:42 +0430 Subject: [PATCH 121/172] Migrate to null-safety stable. (dart-lang/http2#92) --- pkgs/http2/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index 26e60b0c75..1024188cb2 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -4,7 +4,7 @@ description: A HTTP/2 implementation in Dart. repository: https://github.com/dart-lang/http2 environment: - sdk: '>=2.12.0-0 <3.0.0' + sdk: '>=2.12.0 <3.0.0' dev_dependencies: build_runner: ^1.10.0 From 4e05de68aa611e66eaf7e3ea3fa450a2f2d830bf Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Wed, 23 Jun 2021 09:28:57 -0700 Subject: [PATCH 122/172] Drop unused code (dart-lang/http2#94) The testing/client.dart library was committed as a "proof of concept" and has not seen usage since. The testing/debug.dart library was added to support an experimental server which has no usage that I am aware of. --- pkgs/http2/experimental/server.dart | 165 ---------------------- pkgs/http2/experimental/server_chain.pem | 57 -------- pkgs/http2/experimental/server_key.pem | 29 ---- pkgs/http2/lib/src/testing/client.dart | 144 ------------------- pkgs/http2/lib/src/testing/debug.dart | 137 ------------------ pkgs/http2/test/client_websites_test.dart | 135 ------------------ 6 files changed, 667 deletions(-) delete mode 100644 pkgs/http2/experimental/server.dart delete mode 100644 pkgs/http2/experimental/server_chain.pem delete mode 100644 pkgs/http2/experimental/server_key.pem delete mode 100644 pkgs/http2/lib/src/testing/client.dart delete mode 100644 pkgs/http2/lib/src/testing/debug.dart delete mode 100644 pkgs/http2/test/client_websites_test.dart diff --git a/pkgs/http2/experimental/server.dart b/pkgs/http2/experimental/server.dart deleted file mode 100644 index e85d372ce0..0000000000 --- a/pkgs/http2/experimental/server.dart +++ /dev/null @@ -1,165 +0,0 @@ -// 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 'dart:convert'; -import 'dart:io'; - -import 'package:http2/src/testing/debug.dart' hide print; -import 'package:http2/transport.dart'; -import 'package:pedantic/pedantic.dart'; - -const bool DEBUGGING = false; - -const String HOSTNAME = 'localhost'; -const int PORT = 7777; - -void main() async { - String localFile(String path) => Platform.script.resolve(path).toFilePath(); - - var context = SecurityContext() - ..usePrivateKey(localFile('server_key.pem'), password: 'dartdart') - ..useCertificateChain(localFile('server_chain.pem')) - ..setAlpnProtocols(['h2'], true); - - var server = await SecureServerSocket.bind(HOSTNAME, PORT, context); - print('HTTP/2 server listening on https://$HOSTNAME:$PORT'); - - runZonedGuarded(() { - server.listen(handleClient); - }, (e, s) { - print('Unexpected error: $e'); - print('Unexpected error - stack: $s'); - }); -} - -void handleClient(SecureSocket socket) { - dumpInfo('main', 'Got new https client'); - - var connection; - if (DEBUGGING) { - connection = debugPrintingConnection(socket); - } else { - connection = ServerTransportConnection.viaSocket(socket); - } - - connection.incomingStreams.listen((ServerTransportStream stream) async { - dumpInfo('main', 'Got new HTTP/2 stream with id: ${stream.id}'); - - var pathSeen = false; - unawaited(stream.incomingMessages.forEach((StreamMessage msg) async { - dumpInfo('${stream.id}', 'Got new incoming message'); - if (msg is HeadersStreamMessage) { - dumpHeaders('${stream.id}', msg.headers); - if (!pathSeen) { - var path = pathFromHeaders(msg.headers); - pathSeen = true; - if (path == '/') { - unawaited(sendHtml(stream)); - } else if (path == '/iframe' || path == '/iframe2') { - unawaited(sendIFrameHtml(stream, path)); - } else { - unawaited(send404(stream, path)); - } - } - } else if (msg is DataStreamMessage) { - dumpData('${stream.id}', msg.bytes); - } - })); - }); -} - -void dumpHeaders(String prefix, List
headers) { - for (var i = 0; i < headers.length; i++) { - var key = ascii.decode(headers[i].name); - var value = ascii.decode(headers[i].value); - print('[$prefix] $key: $value'); - } -} - -String pathFromHeaders(List
headers) { - for (var i = 0; i < headers.length; i++) { - if (ascii.decode(headers[i].name) == ':path') { - return ascii.decode(headers[i].value); - } - } - throw Exception('Expected a :path header, but did not find one.'); -} - -void dumpData(String prefix, List data) { - print('[$prefix] Got ${data.length} bytes.'); -} - -void dumpInfo(String prefix, String msg) { - print('[$prefix] $msg'); -} - -Future sendHtml(ServerTransportStream stream) async { - unawaited(push(stream, '/iframe', sendIFrameHtml)); - unawaited(push(stream, '/iframe2', sendIFrameHtml)); - unawaited(push(stream, '/favicon.ico', send404)); - - stream.sendHeaders([ - Header.ascii(':status', '200'), - Header.ascii('content-type', 'text/html; charset=utf-8'), - ]); - stream.sendData(ascii.encode(''' - - hello - -

head

- first
-
- second
-
- - -''')); - return stream.outgoingMessages.close(); -} - -Future push(ServerTransportStream stream, String path, - Future Function(TransportStream, String path) sendResponse) async { - var requestHeaders = [ - Header.ascii(':authority', '$HOSTNAME:$PORT'), - Header.ascii(':method', 'GET'), - Header.ascii(':path', path), - Header.ascii(':scheme', 'https'), - ]; - - var pushStream = stream.push(requestHeaders); - await sendResponse(pushStream, path); -} - -Future sendIFrameHtml(TransportStream stream, String path) async { - stream.sendHeaders([ - Header.ascii(':status', '200'), - Header.ascii('content-type', 'text/html; charset=utf-8'), - ]); - stream.sendData(ascii.encode(''' - - Content for '$path' inside an IFrame. - -

Content for '$path' inside an IFrame.

- - -''')); - await stream.outgoingMessages.close(); -} - -Future send404(TransportStream stream, String path) async { - stream.sendHeaders([ - Header.ascii(':status', '404'), - Header.ascii('content-type', 'text/html; charset=utf-8'), - ]); - stream.sendData(ascii.encode(''' - - Path '$path' was not found on this server. - -

Path '$path' was not found on this server.

- - -''')); - return stream.outgoingMessages.close(); -} diff --git a/pkgs/http2/experimental/server_chain.pem b/pkgs/http2/experimental/server_chain.pem deleted file mode 100644 index 4163fe7ddd..0000000000 --- a/pkgs/http2/experimental/server_chain.pem +++ /dev/null @@ -1,57 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDKTCCAhGgAwIBAgIJAOWmjTS+OnTEMA0GCSqGSIb3DQEBCwUAMBcxFTATBgNV -BAMMDGludGVybWVkaWF0ZTAeFw0xNTA1MTgwOTAwNDBaFw0yMzA4MDQwOTAwNDBa -MBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC -AQoCggEBALlcwQJuzd+xH8QFgfJSn5tRlvhkldSX98cE7NiA602NBbnAVyUrkRXq -Ni75lgt0kwjYfA9z674m8WSVbgpLPintPCla9CYky1TH0keIs8Rz6cGWHryWEHiu -EDuljQynu2b3sAFuHu9nfWurbJwZnFakBKpdQ9m4EyOZCHC/jHYY7HacKSXg1Cki -we2ca0BWDrcqy8kLy0dZ5oC6IZG8O8drAK8f3f44CRYw59D3sOKBrKXaabpvyEcb -N7Wk2HDBVwHpUJo1reVwtbM8dhqQayYSD8oXnGpP3RQNu/e2rzlXRyq/BfcDY1JI -7TbC4t/7/N4EcPSpGsTcSOC9A7FpzvECAwEAAaN7MHkwCQYDVR0TBAIwADAsBglg -hkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0O -BBYEFCnwiEMMFZh7NhCr+qA8K0w4Q+AOMB8GA1UdIwQYMBaAFB0h1Evsaw2vfrmS -YuoCTmC4EE6ZMA0GCSqGSIb3DQEBCwUAA4IBAQAcFmHMaXRxyoNaeOowQ6iQWoZd -AUbvG7SHr7I6Pi2aqdqofsKWts7Ytm5WsS0M2nN+sW504houu0iCPeJJX8RQw2q4 -CCcNOs9IXk+2uMzlpocHpv+yYoUiD5DxgWh7eghQMLyMpf8FX3Gy4VazeuXznHOM -4gE4L417xkDzYOzqVTp0FTyAPUv6G2euhNCD6TMru9REcRhYul+K9kocjA5tt2KG -MH6y28LXbLyq4YJUxSUU9gY/xlnbbZS48KDqEcdYC9zjW9nQ0qS+XQuQuFIcwjJ5 -V4kAUYxDu6FoTpyQjgsrmBbZlKNxH7Nj4NDlcdJhp/zeSKHqWa5hSWjjKIxp ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDAjCCAeqgAwIBAgIJAOWmjTS+OnTDMA0GCSqGSIb3DQEBCwUAMBgxFjAUBgNV -BAMMDXJvb3RhdXRob3JpdHkwHhcNMTUwNTE4MDkwMDQwWhcNMjMwODA0MDkwMDQw -WjAXMRUwEwYDVQQDDAxpbnRlcm1lZGlhdGUwggEiMA0GCSqGSIb3DQEBAQUAA4IB -DwAwggEKAoIBAQDSrAO1CoPvUllgLOzDm5nG0skDF7vh1DUgAIDVGz0ecD0JFbQx -EF79pju/6MbtpTW2FYvRp11t/G7rGtX923ybOHY/1MNFQrdIvPlO1VV7IGKjoMwP -DNeb0fIGjHoE9QxaDxR8NX8xQbItpsw+TUtRfc9SLkR+jaYJfVRoM21BOncZbSHE -YKiZlEbpecB/+EtwVpgvl+8mPD5U07Fi4fp/lza3WXInXQPyiTVllIEJCt4PKmlu -MocNaJOW38bysL7i0PzDpVZtOxLHOTaW68yF3FckIHNCaA7k1ABEEEegjFMmIao7 -B9w7A0jvr4jZVvNmui5Djjn+oJxwEVVgyf8LAgMBAAGjUDBOMB0GA1UdDgQWBBQd -IdRL7GsNr365kmLqAk5guBBOmTAfBgNVHSMEGDAWgBRk81s9d0ZbiZhh44KckwPb -oTc0XzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBZQTK0plfdB5PC -cC5icut4EmrByJa1RbU7ayuEE70e7hla6KVmVjVdCBGltI4jBYwfhKbRItHiAJ/8 -x+XZKBG8DLPFuDb7lAa1ObhAYF7YThUFPQYaBhfzKcWrdmWDBFpvNv6E0Mm364dZ -e7Yxmbe5S4agkYPoxEzgEYmcUk9jbjdR6eTbs8laG169ljrECXfEU9RiAcqz5iSX -NLSewqB47hn3B9qgKcQn+PsgO2j7M+rfklhNgeGJeWmy7j6clSOuCsIjWHU0RLQ4 -0W3SB/rpEAJ7fgQbYUPTIUNALSOWi/o1tDX2mXPRjBoxqAv7I+vYk1lZPmSzkyRh -FKvRDxsW ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDAzCCAeugAwIBAgIJAJ0MomS4Ck+8MA0GCSqGSIb3DQEBCwUAMBgxFjAUBgNV -BAMMDXJvb3RhdXRob3JpdHkwHhcNMTUwNTE4MDkwMDQwWhcNMjMwODA0MDkwMDQw -WjAYMRYwFAYDVQQDDA1yb290YXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEAts1ijtBV92S2cOvpUMOSTp9c6A34nIGr0T5Nhz6XiqRVT+gv -dQgmkdKJQjbvR60y6jzltYFsI2MpGVXY8h/oAL81D/k7PDB2aREgyBfTPAhBHyGw -siR+2xYt5b/Zs99q5RdRqQNzNpLPJriIKvUsRyQWy1UiG2s7pRXQeA8qB0XtJdCj -kFIi+G2bDsaffspGeDOCqt7t+yqvRXfSES0c/l7DIHaiMbbp4//ZNML3RNgAjPz2 -hCezZ+wOYajOIyoSPK8IgICrhYFYxvgWxwbLDBEfC5B3jOQsySe10GoRAKZz1gBV -DmgReu81tYJmdgkc9zknnQtIFdA0ex+GvZlfWQIDAQABo1AwTjAdBgNVHQ4EFgQU -ZPNbPXdGW4mYYeOCnJMD26E3NF8wHwYDVR0jBBgwFoAUZPNbPXdGW4mYYeOCnJMD -26E3NF8wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEATzkZ97K777uZ -lQcduNX3ey4IbCiEzFA2zO5Blj+ilfIwNbZXNOgm/lqNvVGDYs6J1apJJe30vL3X -J+t2zsZWzzQzb9uIU37zYemt6m0fHrSrx/iy5lGNqt3HMfqEcOqSCOIK3PCTMz2/ -uyGe1iw33PVeWsm1JUybQ9IrU/huJjbgOHU4wab+8SJCM49ipArp68Fr6j4lcEaE -4rfRg1ZsvxiOyUB3qPn6wyL/JB8kOJ+QCBe498376eaem8AEFk0kQRh6hDaWtq/k -t6IIXQLjx+EBDVP/veK0UnVhKRP8YTOoV8ZiG1NcdlJmX/Uk7iAfevP7CkBfSN8W -r6AL284qtw== ------END CERTIFICATE----- diff --git a/pkgs/http2/experimental/server_key.pem b/pkgs/http2/experimental/server_key.pem deleted file mode 100644 index 1fd2324045..0000000000 --- a/pkgs/http2/experimental/server_key.pem +++ /dev/null @@ -1,29 +0,0 @@ ------BEGIN ENCRYPTED PRIVATE KEY----- -MIIE5DAcBgoqhkiG9w0BDAEBMA4ECL7L6rj6uEHGAgIIAASCBMLbucyfqAkgCbhP -xNSHYllPMAv/dsIjtnsBwepCXPGkCBCuOAw/2FaCHjN9hBqL5V7fkrKeaemhm2YE -ycPtlHJYPDf3kEkyMjdZ9rIY6kePGfQizs2uJPcXj4YPyQ4HsfVXpOicKfQrouf5 -Mze9bGzeMN065q3iP4dYUMwHAyZYteXCsanQNHlqvsWli0W+H8St8fdsXefZhnv1 -qVatKWdNdWQ9t5MuljgNU2Vv56sHKEYXI0yLxk2QUMk8KlJfnmt8foYUsnPUXHmc -gIjLKwwVkpdololnEHSNu0cEOUPowjgJru+uMpn7vdNl7TPEQ9jbEgdNg4JwoYzU -0nao8WzjaSp7kzvZz0VFwKnk5AjstGvvuAWckADdq23QElbn/mF7AG1m/TBpYxzF -gTt37UdndS/AcvVznWVVrRP5iTSIawdIwvqI4s7rqsoE0GCcak+RhchgAz2gWKkS -oODUo0JL6pPVbJ3l4ebbaO6c99nDVc8dViPtc1EkStJEJ2O4kI4xgLSCr4Y9ahKn -oAaoSkX7Xxq3aQm+BzqSpLjdGL8atsqR/YVOIHYIl3gThvP0NfZGx1xHyvO5mCdZ -kHxSA7tKWxauZ3eQ2clbnzeRsl4El0WMHy/5K1ovene4v7sunmoXVtghBC8hK6eh -zMO9orex2PNQ/VQC7HCvtytunOVx1lkSBoNo7hR70igg6rW9H7UyoAoBOwMpT1xa -J6V62nqruTKOqFNfur7aHJGpHGtDb5/ickHeYCyPTvmGp67u4wChzKReeg02oECe -d1E5FKAcIa8s9TVOB6Z+HvTRNQZu2PsI6TJnjQRowvY9DAHiWTlJZBBY/pko3hxX -TsIeybpvRdEHpDWv86/iqtw1hv9CUxS/8ZTWUgBo+osShHW79FeDASr9FC4/Zn76 -ZDERTgV4YWlW/klVWcG2lFo7jix+OPXAB+ZQavLhlN1xdWBcIz1AUWjAM4hdPylW -HCX4PB9CQIPl2E7F+Y2p6nMcMWSJVBi5UIH7E9LfaBguXSzMmTk2Fw5p1aOQ6wfN -goVAMVwi8ppAVs741PfHdZ295xMmK/1LCxz5DeAdD/tsA/SYfT753GotioDuC7im -EyJ5JyvTr5I6RFFBuqt3NlUb3Hp16wP3B2x9DZiB6jxr0l341/NHgsyeBXkuIy9j -ON2mvpBPCJhS8kgWo3G0UyyKnx64tcgpGuSvZhGwPz843B6AbYyE6pMRfSWRMkMS -YZYa+VNKhR4ixdj07ocFZEWLVjCH7kxkE8JZXKt8jKYmkWd0lS1QVjgaKlO6lRa3 -q6SPJkhW6pvqobvcqVNXwi1XuzpZeEbuh0B7OTekFTTxx5g9XeDl56M8SVQ1KEhT -Q1t7H2Nba18WCB7cf+6PN0F0K0Jz1Kq7ZWaqEI/grX1m4RQuvNF5807sB/QKMO/Z -Gz3NXvHg5xTJRd/567lxPGkor0cE7qD1EZfmJ2HrBYXQ91bhgA7LToBuMZo6ZRXH -QfsanjbP4FPLMiGdQigLjj3A35L/f4sQOOVac/sRaFnm7pzcxsMvyVU/YtvGcjYE -xaOOVnamg661Wo0wksXoDjeSz/JIyyKO3Gwp1FSm2wGLjjy/Ehmqcqy8rvHuf07w -AUukhVtTNn4= ------END ENCRYPTED PRIVATE KEY----- diff --git a/pkgs/http2/lib/src/testing/client.dart b/pkgs/http2/lib/src/testing/client.dart deleted file mode 100644 index ecf4e9954f..0000000000 --- a/pkgs/http2/lib/src/testing/client.dart +++ /dev/null @@ -1,144 +0,0 @@ -// 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 'dart:convert' show ascii; -import 'dart:io'; - -import '../../transport.dart'; - -class Request { - final String method; - final Uri uri; - - Request(this.method, this.uri); -} - -class Response { - final Map> headers; - final Stream> stream; - final Stream serverPushes; - - Response(this.headers, this.stream, this.serverPushes); -} - -class ServerPush { - final Map> requestHeaders; - final Future response; - - ServerPush(this.requestHeaders, this.response); -} - -class ClientConnection { - final ClientTransportConnection connection; - - /// Assumes the protocol on [socket] was negogiated to be http/2. - /// - /// If [settings] are omitted, the default [ClientSettings] will be used. - ClientConnection(Socket socket, {ClientSettings? settings}) - : connection = - ClientTransportConnection.viaSocket(socket, settings: settings); - - Future makeRequest(Request request) { - var path = request.uri.path; - if (path.isEmpty) path = '/'; - - var headers = [ - Header.ascii(':method', request.method), - Header.ascii(':path', path), - Header.ascii(':scheme', request.uri.scheme), - Header.ascii(':authority', '${request.uri.host}'), - ]; - - return _handleStream(connection.makeRequest(headers, endStream: true)); - } - - Future close() { - return connection.finish(); - } - - Future _handleStream(ClientTransportStream stream) { - var completer = Completer(); - var isFirst = true; - var controller = StreamController>(); - var serverPushController = StreamController(sync: true); - stream.incomingMessages.listen((StreamMessage msg) { - if (isFirst) { - isFirst = false; - var headerMap = _convertHeaders((msg as HeadersStreamMessage).headers); - completer.complete(Response( - headerMap, controller.stream, serverPushController.stream)); - } else { - controller.add((msg as DataStreamMessage).bytes); - } - }, onDone: controller.close); - _handlePeerPushes(stream.peerPushes).pipe(serverPushController); - return completer.future; - } - - Stream _handlePeerPushes( - Stream serverPushes) { - var pushesController = StreamController(); - serverPushes.listen((TransportStreamPush push) { - var responseCompleter = Completer(); - var serverPush = ServerPush( - _convertHeaders(push.requestHeaders), responseCompleter.future); - - pushesController.add(serverPush); - - var isFirst = true; - var dataController = StreamController>(); - push.stream.incomingMessages.listen((StreamMessage msg) { - if (isFirst) { - isFirst = false; - var headerMap = - _convertHeaders((msg as HeadersStreamMessage).headers); - var response = Response( - headerMap, dataController.stream, Stream.fromIterable([])); - responseCompleter.complete(response); - } else { - dataController.add((msg as DataStreamMessage).bytes); - } - }, onDone: dataController.close); - }, onDone: pushesController.close); - return pushesController.stream; - } - - Map> _convertHeaders(List
headers) { - var headerMap = >{}; - for (var header in headers) { - headerMap - .putIfAbsent(ascii.decode(header.name), () => []) - .add(ascii.decode(header.value)); - } - return headerMap; - } -} - -/// Tries to connect to [uri] via a secure socket connection and establishes a -/// http/2 connection. -/// -/// If [allowServerPushes] is `true`, server pushes need to be handled by the -/// client. The maximum number of concurrent server pushes can be configured via -/// [maxConcurrentPushes] (default is `null` meaning no limit). -Future connect(Uri uri, - {bool allowServerPushes = false, int? maxConcurrentPushes}) async { - const Http2AlpnProtocols = ['h2-14', 'h2-15', 'h2-16', 'h2-17', 'h2']; - - var useSSL = uri.scheme == 'https'; - var settings = ClientSettings( - concurrentStreamLimit: maxConcurrentPushes, - allowServerPushes: allowServerPushes); - if (useSSL) { - var socket = await SecureSocket.connect(uri.host, uri.port, - supportedProtocols: Http2AlpnProtocols); - if (!Http2AlpnProtocols.contains(socket.selectedProtocol)) { - throw Exception('Server does not support HTTP/2.'); - } - return ClientConnection(socket, settings: settings); - } else { - var socket = await Socket.connect(uri.host, uri.port); - return ClientConnection(socket, settings: settings); - } -} diff --git a/pkgs/http2/lib/src/testing/debug.dart b/pkgs/http2/lib/src/testing/debug.dart deleted file mode 100644 index 243e60f0f4..0000000000 --- a/pkgs/http2/lib/src/testing/debug.dart +++ /dev/null @@ -1,137 +0,0 @@ -// 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 'dart:convert'; -import 'dart:io'; - -import '../../transport.dart'; -import '../connection_preface.dart'; -import '../frames/frames.dart'; -import '../settings/settings.dart'; - -final jsonEncoder = JsonEncoder.withIndent(' '); - -TransportConnection debugPrintingConnection(Socket socket, - {bool isServer = true, bool verbose = true}) { - TransportConnection connection; - - var incoming = decodeVerbose(socket, isServer, verbose: verbose); - var outgoing = decodeOutgoingVerbose(socket, isServer, verbose: verbose); - if (isServer) { - connection = ServerTransportConnection.viaStreams(incoming, outgoing); - } else { - connection = ClientTransportConnection.viaStreams(incoming, outgoing); - } - return connection; -} - -Stream> decodeVerbose(Stream> inc, bool isServer, - {bool verbose = true}) { - var name = isServer ? 'server' : 'client'; - - var sc = StreamController>(); - var sDebug = StreamController>(); - - _pipeAndCopy(inc, sc, sDebug); - - if (!isServer) { - _decodeFrames(sDebug.stream).listen((frame) { - print('[$name/stream:${frame.header.streamId}] ' - 'Incoming ${frame.runtimeType}:'); - if (verbose) { - print(jsonEncoder.convert(frame.toJson())); - print(''); - } - }, onError: (e, s) { - print('[$name] Stream error: $e.'); - }, onDone: () { - print('[$name] Closed.'); - }); - } else { - var s3 = readConnectionPreface(sDebug.stream); - _decodeFrames(s3).listen((frame) { - print('[$name/stream:${frame.header.streamId}] ' - 'Incoming ${frame.runtimeType}:'); - if (verbose) { - print(jsonEncoder.convert(frame.toJson())); - print(''); - } - }, onError: (e, s) { - print('[$name] Stream error: $e.'); - }, onDone: () { - print('[$name] Closed.'); - }); - } - - return sc.stream; -} - -StreamSink> decodeOutgoingVerbose( - StreamSink> sink, bool isServer, - {bool verbose = true}) { - var name = isServer ? 'server' : 'client'; - - var proxySink = StreamController>(); - var copy = StreamController>(); - - if (!isServer) { - _decodeFrames(readConnectionPreface(copy.stream)).listen((Frame frame) { - print('[$name/stream:${frame.header.streamId}] ' - 'Outgoing ${frame.runtimeType}:'); - if (verbose) { - print(jsonEncoder.convert(frame.toJson())); - print(''); - } - }, onError: (e, s) { - print('[$name] Outgoing stream error: $e'); - }, onDone: () { - print('[$name] Closing.'); - }); - } else { - _decodeFrames(copy.stream).listen((Frame frame) { - print('[$name/stream:${frame.header.streamId}] ' - 'Outgoing ${frame.runtimeType}:'); - if (verbose) { - print(jsonEncoder.convert(frame.toJson())); - print(''); - } - }, onError: (e, s) { - print('[$name] Outgoing stream error: $e'); - }, onDone: () { - print('[$name] Closing.'); - proxySink.close(); - }); - } - - _pipeAndCopy(proxySink.stream, sink, copy); - - return proxySink; -} - -Stream _decodeFrames(Stream> bytes) { - var settings = ActiveSettings(); - var decoder = FrameReader(bytes, settings); - return decoder.startDecoding(); -} - -Future _pipeAndCopy(Stream> from, StreamSink to, StreamSink to2) { - var c = Completer(); - from.listen((List data) { - to.add(data); - to2.add(data); - }, onError: (Object e, StackTrace s) { - to.addError(e, s); - to2.addError(e, s); - }, onDone: () { - Future.wait([to.close(), to2.close()]) - .then(c.complete) - .catchError(c.completeError); - }); - return c.future; -} - -void print(String s) { - stderr.writeln(s); -} diff --git a/pkgs/http2/test/client_websites_test.dart b/pkgs/http2/test/client_websites_test.dart deleted file mode 100644 index f19ca22473..0000000000 --- a/pkgs/http2/test/client_websites_test.dart +++ /dev/null @@ -1,135 +0,0 @@ -// 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 'dart:convert' show Utf8Decoder, utf8; -import 'dart:io'; - -import 'package:http2/src/testing/client.dart'; -import 'package:pedantic/pedantic.dart'; -import 'package:test/test.dart'; - -void main() async { - test('google', () async { - var uri = Uri.parse('https://www.google.com/'); - var connection = await connect(uri); - var response = await connection.makeRequest(Request('GET', uri)); - dumpHeaders(uri, response.headers); - - final utf8Decoder = Utf8Decoder(allowMalformed: true); - var body = await response.stream.transform(utf8Decoder).join(''); - unawaited(connection.close()); - - body = body.toLowerCase(); - expect(body, contains('')); - expect(body, contains('twitter.com')); - }); - - group('nghttp2.org - ', () { - test('server push enabled', () async { - var uri = Uri.parse('https://nghttp2.org/'); - - var connection = await connect(uri, allowServerPushes: true); - var request = Request('GET', uri); - var response = await connection.makeRequest(request); - dumpHeaders(uri, response.headers); - - Future> accumulatePushes() async { - var futures = >[]; - return response.serverPushes - .listen((ServerPush push) { - futures.add(push.response.then((Response response) { - dumpHeaders(uri, push.requestHeaders, - msg: '**push** Request headers for push request.'); - dumpHeaders(uri, response.headers, - msg: '**push** Response headers for server push ' - 'request.'); - - return readBody(response).then((String body) { - return [push.requestHeaders[':path']!.join(''), body]; - }); - })); - }) - .asFuture() - .then((_) => Future.wait(futures)); - } - - var results = await Future.wait([readBody(response), accumulatePushes()]); - - var body = results[0]; - expect(body, contains('')); - expect(body, contains('nghttp2')); - - var pushes = results[1] as List; - expect(pushes, hasLength(1)); - expect(pushes[0][0], '/stylesheets/screen.css'); - expect(pushes[0][1], contains('audio,video{')); - await connection.close(); - }); - - test('server push disabled', () async { - var uri = Uri.parse('https://nghttp2.org/'); - - var connection = await connect(uri, allowServerPushes: false); - var request = Request('GET', uri); - var response = await connection.makeRequest(request); - dumpHeaders(uri, response.headers); - - Future> accumulatePushes() async { - var futures = >[]; - return response.serverPushes - .listen((ServerPush push) { - futures.add(push.response - .then((Response response) => response.stream.drain())); - }) - .asFuture() - .then((_) => Future.wait(futures)); - } - - var results = await Future.wait([readBody(response), accumulatePushes()]); - - var body = results[0]; - expect(body, contains('')); - expect(body, contains('nghttp2')); - - var pushes = results[1]; - expect(pushes, hasLength(0)); - await connection.close(); - }); - }, tags: ['flaky']); -} - -void dumpHeaders(Uri uri, Map> headers, - {String msg = 'Response headers.'}) { - print(''); - print('[$uri] $msg'); - for (var key in headers.keys.toList()..sort()) { - var spaces = ' ' * (20 - key.length); - print('$key $spaces ${headers[key]!.join(', ')}'); - } - print(''); -} - -Future readBody(Response response) async { - var stream = response.stream; - if (response.headers['content-encoding']?.join('') == 'gzip') { - stream = stream.transform(gzip.decoder); - } else if (response.headers['content-encoding']?.join('') == 'deflate') { - stream = stream.transform(zlib.decoder); - } - return await stream.transform(utf8.decoder).join(''); -} From 4448b2312378941f1fabdb01abedb442a6c63e4c Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Tue, 14 Sep 2021 17:36:34 -0700 Subject: [PATCH 123/172] Fix MockGenerator for TerminatableMixin.ensureNotTerminatedSync (dart-lang/http2#100) --- pkgs/http2/pubspec.yaml | 4 +- .../flowcontrol/connection_queues_test.dart | 5 +- .../connection_queues_test.mocks.dart | 204 ---------- pkgs/http2/test/src/flowcontrol/mocks.dart | 25 ++ .../test/src/flowcontrol/mocks.mocks.dart | 365 ++++++++++++++++++ .../src/flowcontrol/stream_queues_test.dart | 10 +- .../flowcontrol/stream_queues_test.mocks.dart | 123 ------ 7 files changed, 394 insertions(+), 342 deletions(-) delete mode 100644 pkgs/http2/test/src/flowcontrol/connection_queues_test.mocks.dart create mode 100644 pkgs/http2/test/src/flowcontrol/mocks.dart create mode 100644 pkgs/http2/test/src/flowcontrol/mocks.mocks.dart delete mode 100644 pkgs/http2/test/src/flowcontrol/stream_queues_test.mocks.dart diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index 1024188cb2..5fa63c5ceb 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -7,7 +7,7 @@ environment: sdk: '>=2.12.0 <3.0.0' dev_dependencies: - build_runner: ^1.10.0 - mockito: ^5.0.0-nullsafety + build_runner: '>=1.10.0 <3.0.0' + mockito: ^5.0.16 test: ^1.16.0 pedantic: ^1.10.0 diff --git a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart index ed105177e3..a1d2755baa 100644 --- a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart @@ -5,18 +5,15 @@ import 'package:http2/src/async_utils/async_utils.dart'; import 'package:http2/src/flowcontrol/connection_queues.dart'; import 'package:http2/src/flowcontrol/queue_messages.dart'; -import 'package:http2/src/flowcontrol/stream_queues.dart'; import 'package:http2/src/flowcontrol/window.dart'; import 'package:http2/src/flowcontrol/window_handler.dart'; import 'package:http2/src/frames/frames.dart'; import 'package:http2/transport.dart'; -import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:test/test.dart'; -import 'connection_queues_test.mocks.dart'; +import 'mocks.mocks.dart'; -@GenerateMocks([FrameWriter, IncomingWindowHandler, StreamMessageQueueIn]) void main() { group('flowcontrol', () { test('connection-message-queue-out', () { diff --git a/pkgs/http2/test/src/flowcontrol/connection_queues_test.mocks.dart b/pkgs/http2/test/src/flowcontrol/connection_queues_test.mocks.dart deleted file mode 100644 index c72f2364a4..0000000000 --- a/pkgs/http2/test/src/flowcontrol/connection_queues_test.mocks.dart +++ /dev/null @@ -1,204 +0,0 @@ -import 'dart:async' as _i5; - -import 'package:http2/src/async_utils/async_utils.dart' as _i2; -import 'package:http2/src/flowcontrol/queue_messages.dart' as _i9; -import 'package:http2/src/flowcontrol/stream_queues.dart' as _i7; -import 'package:http2/src/flowcontrol/window_handler.dart' as _i3; -import 'package:http2/src/frames/frames.dart' as _i4; -import 'package:http2/src/hpack/hpack.dart' as _i6; -import 'package:http2/transport.dart' as _i8; -import 'package:mockito/mockito.dart' as _i1; - -// ignore_for_file: comment_references - -// ignore_for_file: unnecessary_parenthesis - -class _FakeBufferIndicator extends _i1.Fake implements _i2.BufferIndicator {} - -class _FakeIncomingWindowHandler extends _i1.Fake - implements _i3.IncomingWindowHandler {} - -/// A class which mocks [FrameWriter]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockFrameWriter extends _i1.Mock implements _i4.FrameWriter { - MockFrameWriter() { - _i1.throwOnMissingStub(this); - } - - @override - _i2.BufferIndicator get bufferIndicator => - (super.noSuchMethod(Invocation.getter(#bufferIndicator), - returnValue: _FakeBufferIndicator()) as _i2.BufferIndicator); - - @override - int get highestWrittenStreamId => - (super.noSuchMethod(Invocation.getter(#highestWrittenStreamId), - returnValue: 0) as int); - - @override - _i5.Future get doneFuture => - (super.noSuchMethod(Invocation.getter(#doneFuture), - returnValue: Future.value(null)) as _i5.Future); - - @override - void writeDataFrame(int? streamId, List? data, - {bool? endStream = false}) => - super.noSuchMethod(Invocation.method( - #writeDataFrame, [streamId, data], {#endStream: endStream})); - - @override - void writeHeadersFrame(int? streamId, List<_i6.Header>? headers, - {bool? endStream = true}) => - super.noSuchMethod(Invocation.method( - #writeHeadersFrame, [streamId, headers], {#endStream: endStream})); - - @override - void writePriorityFrame(int? streamId, int? streamDependency, int? weight, - {bool? exclusive = false}) => - super.noSuchMethod(Invocation.method(#writePriorityFrame, - [streamId, streamDependency, weight], {#exclusive: exclusive})); - - @override - void writeRstStreamFrame(int? streamId, int? errorCode) => super.noSuchMethod( - Invocation.method(#writeRstStreamFrame, [streamId, errorCode])); - - @override - void writeSettingsFrame(List<_i4.Setting>? settings) => - super.noSuchMethod(Invocation.method(#writeSettingsFrame, [settings])); - - @override - void writePushPromiseFrame( - int? streamId, int? promisedStreamId, List<_i6.Header>? headers) => - super.noSuchMethod(Invocation.method( - #writePushPromiseFrame, [streamId, promisedStreamId, headers])); - - @override - void writePingFrame(int? opaqueData, {bool? ack = false}) => - super.noSuchMethod( - Invocation.method(#writePingFrame, [opaqueData], {#ack: ack})); - - @override - void writeGoawayFrame( - int? lastStreamId, int? errorCode, List? debugData) => - super.noSuchMethod(Invocation.method( - #writeGoawayFrame, [lastStreamId, errorCode, debugData])); - - @override - void writeWindowUpdate(int? sizeIncrement, {int? streamId = 0}) => - super.noSuchMethod(Invocation.method( - #writeWindowUpdate, [sizeIncrement], {#streamId: streamId})); - - @override - _i5.Future close() => - (super.noSuchMethod(Invocation.method(#close, []), - returnValue: Future.value(null)) as _i5.Future); -} - -/// A class which mocks [IncomingWindowHandler]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockIncomingWindowHandler extends _i1.Mock - implements _i3.IncomingWindowHandler { - MockIncomingWindowHandler() { - _i1.throwOnMissingStub(this); - } - - @override - int get localWindowSize => - (super.noSuchMethod(Invocation.getter(#localWindowSize), returnValue: 0) - as int); - - @override - void gotData(int? numberOfBytes) => - super.noSuchMethod(Invocation.method(#gotData, [numberOfBytes])); - - @override - void dataProcessed(int? numberOfBytes) => - super.noSuchMethod(Invocation.method(#dataProcessed, [numberOfBytes])); -} - -/// A class which mocks [StreamMessageQueueIn]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockStreamMessageQueueIn extends _i1.Mock - implements _i7.StreamMessageQueueIn { - MockStreamMessageQueueIn() { - _i1.throwOnMissingStub(this); - } - - @override - _i3.IncomingWindowHandler get windowHandler => (super.noSuchMethod( - Invocation.getter(#windowHandler), - returnValue: _FakeIncomingWindowHandler()) as _i3.IncomingWindowHandler); - - @override - _i2.BufferIndicator get bufferIndicator => - (super.noSuchMethod(Invocation.getter(#bufferIndicator), - returnValue: _FakeBufferIndicator()) as _i2.BufferIndicator); - - @override - int get pendingMessages => - (super.noSuchMethod(Invocation.getter(#pendingMessages), returnValue: 0) - as int); - - @override - _i5.Stream<_i8.StreamMessage> get messages => - (super.noSuchMethod(Invocation.getter(#messages), - returnValue: Stream<_i8.StreamMessage>.empty()) - as _i5.Stream<_i8.StreamMessage>); - - @override - _i5.Stream<_i8.TransportStreamPush> get serverPushes => - (super.noSuchMethod(Invocation.getter(#serverPushes), - returnValue: Stream<_i8.TransportStreamPush>.empty()) - as _i5.Stream<_i8.TransportStreamPush>); - - @override - bool get wasTerminated => - (super.noSuchMethod(Invocation.getter(#wasTerminated), returnValue: false) - as bool); - - @override - _i5.Future get done => (super.noSuchMethod(Invocation.getter(#done), - returnValue: Future.value(null)) as _i5.Future); - - @override - bool get isClosing => - (super.noSuchMethod(Invocation.getter(#isClosing), returnValue: false) - as bool); - - @override - bool get wasClosed => - (super.noSuchMethod(Invocation.getter(#wasClosed), returnValue: false) - as bool); - - @override - _i5.Future get onCancel => - (super.noSuchMethod(Invocation.getter(#onCancel), - returnValue: Future.value(null)) as _i5.Future); - - @override - bool get wasCancelled => - (super.noSuchMethod(Invocation.getter(#wasCancelled), returnValue: false) - as bool); - - @override - void enqueueMessage(_i9.Message? message) => - super.noSuchMethod(Invocation.method(#enqueueMessage, [message])); - - @override - T ensureNotTerminatedSync(T Function()? f) => - (super.noSuchMethod(Invocation.method(#ensureNotTerminatedSync, [f]), - returnValue: null) as T); - - @override - _i5.Future ensureNotTerminatedAsync( - _i5.Future Function()? f) => - (super.noSuchMethod(Invocation.method(#ensureNotTerminatedAsync, [f]), - returnValue: Future.value(null)) as _i5.Future); - - @override - dynamic ensureNotClosingSync(dynamic Function()? f) => - super.noSuchMethod(Invocation.method(#ensureNotClosingSync, [f])); -} diff --git a/pkgs/http2/test/src/flowcontrol/mocks.dart b/pkgs/http2/test/src/flowcontrol/mocks.dart new file mode 100644 index 0000000000..c30048936c --- /dev/null +++ b/pkgs/http2/test/src/flowcontrol/mocks.dart @@ -0,0 +1,25 @@ +// Copyright (c) 2021, 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/connection_queues.dart'; +import 'package:http2/src/flowcontrol/stream_queues.dart'; +import 'package:http2/src/flowcontrol/window_handler.dart'; +import 'package:http2/src/frames/frames.dart'; +import 'package:mockito/annotations.dart'; + +@GenerateMocks([ + FrameWriter, + IncomingWindowHandler, + OutgoingStreamWindowHandler, +], customMocks: [ + MockSpec(fallbackGenerators: { + #ensureNotTerminatedSync: ensureNotTerminatedSyncFallback, + }), + MockSpec(fallbackGenerators: { + #ensureNotTerminatedSync: ensureNotTerminatedSyncFallback, + }) +]) +T ensureNotTerminatedSyncFallback(T Function()? f) => + throw UnimplementedError( + 'Method cannot be stubbed; requires fallback values for return'); diff --git a/pkgs/http2/test/src/flowcontrol/mocks.mocks.dart b/pkgs/http2/test/src/flowcontrol/mocks.mocks.dart new file mode 100644 index 0000000000..e2c40907da --- /dev/null +++ b/pkgs/http2/test/src/flowcontrol/mocks.mocks.dart @@ -0,0 +1,365 @@ +// Mocks generated by Mockito 5.0.16 from annotations +// in http2/test/src/flowcontrol/mocks.dart. +// Do not manually edit this file. + +import 'dart:async' as _i5; + +import 'package:http2/src/async_utils/async_utils.dart' as _i2; +import 'package:http2/src/flowcontrol/connection_queues.dart' as _i7; +import 'package:http2/src/flowcontrol/queue_messages.dart' as _i8; +import 'package:http2/src/flowcontrol/stream_queues.dart' as _i10; +import 'package:http2/src/flowcontrol/window_handler.dart' as _i3; +import 'package:http2/src/frames/frames.dart' as _i4; +import 'package:http2/src/hpack/hpack.dart' as _i6; +import 'package:http2/transport.dart' as _i11; +import 'package:mockito/mockito.dart' as _i1; + +import 'mocks.dart' as _i9; + +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types + +class _FakeBufferIndicator_0 extends _i1.Fake implements _i2.BufferIndicator {} + +class _FakeIncomingWindowHandler_1 extends _i1.Fake + implements _i3.IncomingWindowHandler {} + +/// A class which mocks [FrameWriter]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockFrameWriter extends _i1.Mock implements _i4.FrameWriter { + MockFrameWriter() { + _i1.throwOnMissingStub(this); + } + + @override + _i2.BufferIndicator get bufferIndicator => + (super.noSuchMethod(Invocation.getter(#bufferIndicator), + returnValue: _FakeBufferIndicator_0()) as _i2.BufferIndicator); + @override + int get highestWrittenStreamId => + (super.noSuchMethod(Invocation.getter(#highestWrittenStreamId), + returnValue: 0) as int); + @override + _i5.Future get doneFuture => + (super.noSuchMethod(Invocation.getter(#doneFuture), + returnValue: Future.value()) as _i5.Future); + @override + void writeDataFrame(int? streamId, List? data, + {bool? endStream = false}) => + super.noSuchMethod( + Invocation.method( + #writeDataFrame, [streamId, data], {#endStream: endStream}), + returnValueForMissingStub: null); + @override + void writeHeadersFrame(int? streamId, List<_i6.Header>? headers, + {bool? endStream = true}) => + super.noSuchMethod( + Invocation.method( + #writeHeadersFrame, [streamId, headers], {#endStream: endStream}), + returnValueForMissingStub: null); + @override + void writePriorityFrame(int? streamId, int? streamDependency, int? weight, + {bool? exclusive = false}) => + super.noSuchMethod( + Invocation.method(#writePriorityFrame, + [streamId, streamDependency, weight], {#exclusive: exclusive}), + returnValueForMissingStub: null); + @override + void writeRstStreamFrame(int? streamId, int? errorCode) => super.noSuchMethod( + Invocation.method(#writeRstStreamFrame, [streamId, errorCode]), + returnValueForMissingStub: null); + @override + void writeSettingsFrame(List<_i4.Setting>? settings) => + super.noSuchMethod(Invocation.method(#writeSettingsFrame, [settings]), + returnValueForMissingStub: null); + @override + void writeSettingsAckFrame() => + super.noSuchMethod(Invocation.method(#writeSettingsAckFrame, []), + returnValueForMissingStub: null); + @override + void writePushPromiseFrame( + int? streamId, int? promisedStreamId, List<_i6.Header>? headers) => + super.noSuchMethod( + Invocation.method( + #writePushPromiseFrame, [streamId, promisedStreamId, headers]), + returnValueForMissingStub: null); + @override + void writePingFrame(int? opaqueData, {bool? ack = false}) => + super.noSuchMethod( + Invocation.method(#writePingFrame, [opaqueData], {#ack: ack}), + returnValueForMissingStub: null); + @override + void writeGoawayFrame( + int? lastStreamId, int? errorCode, List? debugData) => + super.noSuchMethod( + Invocation.method( + #writeGoawayFrame, [lastStreamId, errorCode, debugData]), + returnValueForMissingStub: null); + @override + void writeWindowUpdate(int? sizeIncrement, {int? streamId = 0}) => + super.noSuchMethod( + Invocation.method( + #writeWindowUpdate, [sizeIncrement], {#streamId: streamId}), + returnValueForMissingStub: null); + @override + _i5.Future close() => + (super.noSuchMethod(Invocation.method(#close, []), + returnValue: Future.value()) as _i5.Future); + @override + String toString() => super.toString(); +} + +/// A class which mocks [IncomingWindowHandler]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockIncomingWindowHandler extends _i1.Mock + implements _i3.IncomingWindowHandler { + MockIncomingWindowHandler() { + _i1.throwOnMissingStub(this); + } + + @override + int get localWindowSize => + (super.noSuchMethod(Invocation.getter(#localWindowSize), returnValue: 0) + as int); + @override + void gotData(int? numberOfBytes) => + super.noSuchMethod(Invocation.method(#gotData, [numberOfBytes]), + returnValueForMissingStub: null); + @override + void dataProcessed(int? numberOfBytes) => + super.noSuchMethod(Invocation.method(#dataProcessed, [numberOfBytes]), + returnValueForMissingStub: null); + @override + String toString() => super.toString(); +} + +/// A class which mocks [OutgoingStreamWindowHandler]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockOutgoingStreamWindowHandler extends _i1.Mock + implements _i3.OutgoingStreamWindowHandler { + MockOutgoingStreamWindowHandler() { + _i1.throwOnMissingStub(this); + } + + @override + _i2.BufferIndicator get positiveWindow => + (super.noSuchMethod(Invocation.getter(#positiveWindow), + returnValue: _FakeBufferIndicator_0()) as _i2.BufferIndicator); + @override + int get peerWindowSize => + (super.noSuchMethod(Invocation.getter(#peerWindowSize), returnValue: 0) + as int); + @override + void processInitialWindowSizeSettingChange(int? difference) => + super.noSuchMethod( + Invocation.method( + #processInitialWindowSizeSettingChange, [difference]), + returnValueForMissingStub: null); + @override + void processWindowUpdate(_i4.WindowUpdateFrame? frame) => + super.noSuchMethod(Invocation.method(#processWindowUpdate, [frame]), + returnValueForMissingStub: null); + @override + void decreaseWindow(int? numberOfBytes) => + super.noSuchMethod(Invocation.method(#decreaseWindow, [numberOfBytes]), + returnValueForMissingStub: null); + @override + String toString() => super.toString(); +} + +/// A class which mocks [ConnectionMessageQueueOut]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockConnectionMessageQueueOut extends _i1.Mock + implements _i7.ConnectionMessageQueueOut { + MockConnectionMessageQueueOut() { + _i1.throwOnMissingStub(this); + } + + @override + int get pendingMessages => + (super.noSuchMethod(Invocation.getter(#pendingMessages), returnValue: 0) + as int); + @override + bool get wasTerminated => + (super.noSuchMethod(Invocation.getter(#wasTerminated), returnValue: false) + as bool); + @override + _i5.Future get done => (super.noSuchMethod(Invocation.getter(#done), + returnValue: Future.value()) as _i5.Future); + @override + bool get isClosing => + (super.noSuchMethod(Invocation.getter(#isClosing), returnValue: false) + as bool); + @override + bool get wasClosed => + (super.noSuchMethod(Invocation.getter(#wasClosed), returnValue: false) + as bool); + @override + void enqueueMessage(_i8.Message? message) => + super.noSuchMethod(Invocation.method(#enqueueMessage, [message]), + returnValueForMissingStub: null); + @override + void onTerminated(Object? error) => + super.noSuchMethod(Invocation.method(#onTerminated, [error]), + returnValueForMissingStub: null); + @override + void onCheckForClose() => + super.noSuchMethod(Invocation.method(#onCheckForClose, []), + returnValueForMissingStub: null); + @override + String toString() => super.toString(); + @override + void terminate([dynamic error]) => + super.noSuchMethod(Invocation.method(#terminate, [error]), + returnValueForMissingStub: null); + @override + T ensureNotTerminatedSync(T Function()? f) => + (super.noSuchMethod(Invocation.method(#ensureNotTerminatedSync, [f]), + returnValue: _i9.ensureNotTerminatedSyncFallback(f)) as T); + @override + _i5.Future ensureNotTerminatedAsync( + _i5.Future Function()? f) => + (super.noSuchMethod(Invocation.method(#ensureNotTerminatedAsync, [f]), + returnValue: Future.value()) as _i5.Future); + @override + void startClosing() => + super.noSuchMethod(Invocation.method(#startClosing, []), + returnValueForMissingStub: null); + @override + void onClosing() => super.noSuchMethod(Invocation.method(#onClosing, []), + returnValueForMissingStub: null); + @override + dynamic ensureNotClosingSync(dynamic Function()? f) => + super.noSuchMethod(Invocation.method(#ensureNotClosingSync, [f])); + @override + void closeWithValue([dynamic value]) => + super.noSuchMethod(Invocation.method(#closeWithValue, [value]), + returnValueForMissingStub: null); + @override + void closeWithError(dynamic error) => + super.noSuchMethod(Invocation.method(#closeWithError, [error]), + returnValueForMissingStub: null); +} + +/// A class which mocks [StreamMessageQueueIn]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockStreamMessageQueueIn extends _i1.Mock + implements _i10.StreamMessageQueueIn { + MockStreamMessageQueueIn() { + _i1.throwOnMissingStub(this); + } + + @override + _i3.IncomingWindowHandler get windowHandler => + (super.noSuchMethod(Invocation.getter(#windowHandler), + returnValue: _FakeIncomingWindowHandler_1()) + as _i3.IncomingWindowHandler); + @override + _i2.BufferIndicator get bufferIndicator => + (super.noSuchMethod(Invocation.getter(#bufferIndicator), + returnValue: _FakeBufferIndicator_0()) as _i2.BufferIndicator); + @override + int get pendingMessages => + (super.noSuchMethod(Invocation.getter(#pendingMessages), returnValue: 0) + as int); + @override + _i5.Stream<_i11.StreamMessage> get messages => + (super.noSuchMethod(Invocation.getter(#messages), + returnValue: Stream<_i11.StreamMessage>.empty()) + as _i5.Stream<_i11.StreamMessage>); + @override + _i5.Stream<_i11.TransportStreamPush> get serverPushes => + (super.noSuchMethod(Invocation.getter(#serverPushes), + returnValue: Stream<_i11.TransportStreamPush>.empty()) + as _i5.Stream<_i11.TransportStreamPush>); + @override + bool get wasTerminated => + (super.noSuchMethod(Invocation.getter(#wasTerminated), returnValue: false) + as bool); + @override + _i5.Future get done => (super.noSuchMethod(Invocation.getter(#done), + returnValue: Future.value()) as _i5.Future); + @override + bool get isClosing => + (super.noSuchMethod(Invocation.getter(#isClosing), returnValue: false) + as bool); + @override + bool get wasClosed => + (super.noSuchMethod(Invocation.getter(#wasClosed), returnValue: false) + as bool); + @override + _i5.Future get onCancel => + (super.noSuchMethod(Invocation.getter(#onCancel), + returnValue: Future.value()) as _i5.Future); + @override + bool get wasCancelled => + (super.noSuchMethod(Invocation.getter(#wasCancelled), returnValue: false) + as bool); + @override + void enqueueMessage(_i8.Message? message) => + super.noSuchMethod(Invocation.method(#enqueueMessage, [message]), + returnValueForMissingStub: null); + @override + void onTerminated(Object? exception) => + super.noSuchMethod(Invocation.method(#onTerminated, [exception]), + returnValueForMissingStub: null); + @override + void onCloseCheck() => + super.noSuchMethod(Invocation.method(#onCloseCheck, []), + returnValueForMissingStub: null); + @override + void forceDispatchIncomingMessages() => + super.noSuchMethod(Invocation.method(#forceDispatchIncomingMessages, []), + returnValueForMissingStub: null); + @override + String toString() => super.toString(); + @override + void terminate([dynamic error]) => + super.noSuchMethod(Invocation.method(#terminate, [error]), + returnValueForMissingStub: null); + @override + T ensureNotTerminatedSync(T Function()? f) => + (super.noSuchMethod(Invocation.method(#ensureNotTerminatedSync, [f]), + returnValue: _i9.ensureNotTerminatedSyncFallback(f)) as T); + @override + _i5.Future ensureNotTerminatedAsync( + _i5.Future Function()? f) => + (super.noSuchMethod(Invocation.method(#ensureNotTerminatedAsync, [f]), + returnValue: Future.value()) as _i5.Future); + @override + void startClosing() => + super.noSuchMethod(Invocation.method(#startClosing, []), + returnValueForMissingStub: null); + @override + void onCheckForClose() => + super.noSuchMethod(Invocation.method(#onCheckForClose, []), + returnValueForMissingStub: null); + @override + void onClosing() => super.noSuchMethod(Invocation.method(#onClosing, []), + returnValueForMissingStub: null); + @override + dynamic ensureNotClosingSync(dynamic Function()? f) => + super.noSuchMethod(Invocation.method(#ensureNotClosingSync, [f])); + @override + void closeWithValue([dynamic value]) => + super.noSuchMethod(Invocation.method(#closeWithValue, [value]), + returnValueForMissingStub: null); + @override + void closeWithError(dynamic error) => + super.noSuchMethod(Invocation.method(#closeWithError, [error]), + returnValueForMissingStub: null); + @override + void cancel() => super.noSuchMethod(Invocation.method(#cancel, []), + returnValueForMissingStub: null); +} diff --git a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart index 099a42e11b..0b5ae7a7a1 100644 --- a/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/stream_queues_test.dart @@ -3,22 +3,14 @@ // BSD-style license that can be found in the LICENSE file. import 'package:http2/src/async_utils/async_utils.dart'; -import 'package:http2/src/flowcontrol/connection_queues.dart'; import 'package:http2/src/flowcontrol/queue_messages.dart'; import 'package:http2/src/flowcontrol/stream_queues.dart'; -import 'package:http2/src/flowcontrol/window_handler.dart'; import 'package:http2/transport.dart'; -import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:test/test.dart'; -import 'stream_queues_test.mocks.dart'; +import 'mocks.mocks.dart'; -@GenerateMocks([ - ConnectionMessageQueueOut, - IncomingWindowHandler, - OutgoingStreamWindowHandler -]) void main() { group('flowcontrol', () { const STREAM_ID = 99; diff --git a/pkgs/http2/test/src/flowcontrol/stream_queues_test.mocks.dart b/pkgs/http2/test/src/flowcontrol/stream_queues_test.mocks.dart deleted file mode 100644 index a1cd3db000..0000000000 --- a/pkgs/http2/test/src/flowcontrol/stream_queues_test.mocks.dart +++ /dev/null @@ -1,123 +0,0 @@ -import 'dart:async' as _i4; - -import 'package:http2/src/async_utils/async_utils.dart' as _i2; -import 'package:http2/src/flowcontrol/connection_queues.dart' as _i3; -import 'package:http2/src/flowcontrol/queue_messages.dart' as _i5; -import 'package:http2/src/flowcontrol/window_handler.dart' as _i6; -import 'package:http2/src/frames/frames.dart' as _i7; -import 'package:mockito/mockito.dart' as _i1; - -// ignore_for_file: comment_references - -// ignore_for_file: unnecessary_parenthesis - -class _FakeBufferIndicator extends _i1.Fake implements _i2.BufferIndicator {} - -/// A class which mocks [ConnectionMessageQueueOut]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockConnectionMessageQueueOut extends _i1.Mock - implements _i3.ConnectionMessageQueueOut { - MockConnectionMessageQueueOut() { - _i1.throwOnMissingStub(this); - } - - @override - int get pendingMessages => - (super.noSuchMethod(Invocation.getter(#pendingMessages), returnValue: 0) - as int); - - @override - bool get wasTerminated => - (super.noSuchMethod(Invocation.getter(#wasTerminated), returnValue: false) - as bool); - - @override - _i4.Future get done => (super.noSuchMethod(Invocation.getter(#done), - returnValue: Future.value(null)) as _i4.Future); - - @override - bool get isClosing => - (super.noSuchMethod(Invocation.getter(#isClosing), returnValue: false) - as bool); - - @override - bool get wasClosed => - (super.noSuchMethod(Invocation.getter(#wasClosed), returnValue: false) - as bool); - - @override - void enqueueMessage(_i5.Message? message) => - super.noSuchMethod(Invocation.method(#enqueueMessage, [message])); - - @override - T ensureNotTerminatedSync(T Function()? f) => - (super.noSuchMethod(Invocation.method(#ensureNotTerminatedSync, [f]), - returnValue: null) as T); - - @override - _i4.Future ensureNotTerminatedAsync( - _i4.Future Function()? f) => - (super.noSuchMethod(Invocation.method(#ensureNotTerminatedAsync, [f]), - returnValue: Future.value(null)) as _i4.Future); - - @override - dynamic ensureNotClosingSync(dynamic Function()? f) => - super.noSuchMethod(Invocation.method(#ensureNotClosingSync, [f])); -} - -/// A class which mocks [IncomingWindowHandler]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockIncomingWindowHandler extends _i1.Mock - implements _i6.IncomingWindowHandler { - MockIncomingWindowHandler() { - _i1.throwOnMissingStub(this); - } - - @override - int get localWindowSize => - (super.noSuchMethod(Invocation.getter(#localWindowSize), returnValue: 0) - as int); - - @override - void gotData(int? numberOfBytes) => - super.noSuchMethod(Invocation.method(#gotData, [numberOfBytes])); - - @override - void dataProcessed(int? numberOfBytes) => - super.noSuchMethod(Invocation.method(#dataProcessed, [numberOfBytes])); -} - -/// A class which mocks [OutgoingStreamWindowHandler]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockOutgoingStreamWindowHandler extends _i1.Mock - implements _i6.OutgoingStreamWindowHandler { - MockOutgoingStreamWindowHandler() { - _i1.throwOnMissingStub(this); - } - - @override - _i2.BufferIndicator get positiveWindow => - (super.noSuchMethod(Invocation.getter(#positiveWindow), - returnValue: _FakeBufferIndicator()) as _i2.BufferIndicator); - - @override - int get peerWindowSize => - (super.noSuchMethod(Invocation.getter(#peerWindowSize), returnValue: 0) - as int); - - @override - void processInitialWindowSizeSettingChange(int? difference) => - super.noSuchMethod(Invocation.method( - #processInitialWindowSizeSettingChange, [difference])); - - @override - void processWindowUpdate(_i7.WindowUpdateFrame? frame) => - super.noSuchMethod(Invocation.method(#processWindowUpdate, [frame])); - - @override - void decreaseWindow(int? numberOfBytes) => - super.noSuchMethod(Invocation.method(#decreaseWindow, [numberOfBytes])); -} From ca7e20f58ba1851e88abc2d2a49e81844ff366ae Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Sun, 26 Sep 2021 13:02:44 -0700 Subject: [PATCH 124/172] Drop unneeded import (dart-lang/http2#101) --- pkgs/http2/lib/src/streams/stream_handler.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index e11a61abf4..d78374b1de 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -6,7 +6,6 @@ import 'dart:async'; import 'dart:math'; import '../../transport.dart'; - import '../connection.dart'; import '../error_handler.dart'; import '../flowcontrol/connection_queues.dart'; @@ -15,7 +14,6 @@ import '../flowcontrol/stream_queues.dart'; import '../flowcontrol/window.dart'; import '../flowcontrol/window_handler.dart'; import '../frames/frames.dart'; -import '../hpack/hpack.dart'; import '../settings/settings.dart'; import '../sync_errors.dart'; From dd16c9d87032933979df0da0e91b1de2af9446c5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Mar 2022 11:16:17 -0800 Subject: [PATCH 125/172] Bump actions/checkout from 2 to 3 (dart-lang/http2#103) Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pkgs/http2/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http2/.github/workflows/test-package.yml b/pkgs/http2/.github/workflows/test-package.yml index cecfdde6f9..6c7108d860 100644 --- a/pkgs/http2/.github/workflows/test-package.yml +++ b/pkgs/http2/.github/workflows/test-package.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: dart-lang/setup-dart@v1 with: sdk: ${{ matrix.sdk }} @@ -49,7 +49,7 @@ jobs: os: [ubuntu-latest] sdk: [2.12.0, dev] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: dart-lang/setup-dart@v1 with: sdk: ${{ matrix.sdk }} From 578336ac22d59106307cf50078c5dec5dcf8894f Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Thu, 12 May 2022 03:30:56 +0000 Subject: [PATCH 126/172] switch to package:lints --- pkgs/http2/CHANGELOG.md | 3 +++ pkgs/http2/analysis_options.yaml | 26 +------------------ pkgs/http2/lib/multiprotocol_server.dart | 4 +-- pkgs/http2/pubspec.yaml | 4 +-- pkgs/http2/test/client_test.dart | 5 ++-- .../test/src/connection_preface_test.dart | 1 - .../test/src/flowcontrol/mocks.mocks.dart | 2 ++ .../test/src/ping/ping_handler_test.dart | 3 ++- .../test/src/streams/simple_push_test.dart | 1 - pkgs/http2/test/src/streams/streams_test.dart | 5 ++-- pkgs/http2/test/transport_test.dart | 1 - 11 files changed, 17 insertions(+), 38 deletions(-) diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index 7897c34502..d3ea414885 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -1,3 +1,6 @@ +## 2.0.1-dev + + ## 2.0.0 * Migrate to null safety. diff --git a/pkgs/http2/analysis_options.yaml b/pkgs/http2/analysis_options.yaml index 2ad0cbccc4..570f6b74b0 100644 --- a/pkgs/http2/analysis_options.yaml +++ b/pkgs/http2/analysis_options.yaml @@ -1,4 +1,4 @@ -include: package:pedantic/analysis_options.yaml +include: package:lints/core.yaml analyzer: strong-mode: @@ -8,27 +8,3 @@ analyzer: unused_import: error unused_local_variable: error dead_code: error - -linter: - rules: - - avoid_unused_constructor_parameters - - await_only_futures - - camel_case_types - - comment_references - - control_flow_in_finally - - directives_ordering - - empty_statements - - hash_and_equals - - implementation_imports - - iterable_contains_unrelated_type - - list_remove_unrelated_type - - non_constant_identifier_names - - only_throw_errors - - overridden_fields - - package_api_docs - - package_names - - package_prefixed_library_names - - test_types_in_equals - - throw_in_finally - - unnecessary_parenthesis - - unnecessary_brace_in_string_interps diff --git a/pkgs/http2/lib/multiprotocol_server.dart b/pkgs/http2/lib/multiprotocol_server.dart index 320f96d479..8dcd78c8ea 100644 --- a/pkgs/http2/lib/multiprotocol_server.dart +++ b/pkgs/http2/lib/multiprotocol_server.dart @@ -94,11 +94,11 @@ class MultiProtocolHttpServer { /// /// Completes once everything has been successfully shut down. Future close({bool force = false}) { - return _serverSocket.close().whenComplete(() { + return _serverSocket.close().whenComplete(() async { var done1 = _http11Server.close(force: force); Future done2 = Future.wait( _http2Connections.map((c) => force ? c.terminate() : c.finish())); - return Future.wait([done1, done2]); + await Future.wait([done1, done2]); }); } } diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index 5fa63c5ceb..f2e699d850 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -1,5 +1,5 @@ name: http2 -version: 2.0.0 +version: 2.0.1-dev description: A HTTP/2 implementation in Dart. repository: https://github.com/dart-lang/http2 @@ -8,6 +8,6 @@ environment: dev_dependencies: build_runner: '>=1.10.0 <3.0.0' + lints: ^1.0.0 mockito: ^5.0.16 test: ^1.16.0 - pedantic: ^1.10.0 diff --git a/pkgs/http2/test/client_test.dart b/pkgs/http2/test/client_test.dart index 8bd5a0b9dd..8bac9d32b2 100644 --- a/pkgs/http2/test/client_test.dart +++ b/pkgs/http2/test/client_test.dart @@ -12,7 +12,6 @@ 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:pedantic/pedantic.dart'; import 'package:test/test.dart'; import 'src/hpack/hpack_test.dart' show isHeader; @@ -133,7 +132,7 @@ void main() { expect(client.isOpen, false); - var error; + String? error; try { client.makeRequest([Header.ascii('a', 'b')]); } catch (e) { @@ -172,7 +171,7 @@ void main() { expect(client.isOpen, false); - var error; + String? error; try { client.makeRequest([Header.ascii('a', 'b')]); } catch (e) { diff --git a/pkgs/http2/test/src/connection_preface_test.dart b/pkgs/http2/test/src/connection_preface_test.dart index 7a28a0c2fd..d2e6eb3f61 100644 --- a/pkgs/http2/test/src/connection_preface_test.dart +++ b/pkgs/http2/test/src/connection_preface_test.dart @@ -6,7 +6,6 @@ import 'dart:async'; import 'dart:math' show min; import 'package:http2/src/connection_preface.dart'; -import 'package:pedantic/pedantic.dart'; import 'package:test/test.dart'; void main() { diff --git a/pkgs/http2/test/src/flowcontrol/mocks.mocks.dart b/pkgs/http2/test/src/flowcontrol/mocks.mocks.dart index e2c40907da..58facbb367 100644 --- a/pkgs/http2/test/src/flowcontrol/mocks.mocks.dart +++ b/pkgs/http2/test/src/flowcontrol/mocks.mocks.dart @@ -2,6 +2,8 @@ // in http2/test/src/flowcontrol/mocks.dart. // Do not manually edit this file. +// ignore_for_file: unnecessary_overrides + import 'dart:async' as _i5; import 'package:http2/src/async_utils/async_utils.dart' as _i2; diff --git a/pkgs/http2/test/src/ping/ping_handler_test.dart b/pkgs/http2/test/src/ping/ping_handler_test.dart index c4b3f86e1d..f466f4545b 100644 --- a/pkgs/http2/test/src/ping/ping_handler_test.dart +++ b/pkgs/http2/test/src/ping/ping_handler_test.dart @@ -2,10 +2,11 @@ // 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/frames/frames.dart'; import 'package:http2/src/ping/ping_handler.dart'; import 'package:mockito/mockito.dart'; -import 'package:pedantic/pedantic.dart'; import 'package:test/test.dart'; import '../error_matchers.dart'; diff --git a/pkgs/http2/test/src/streams/simple_push_test.dart b/pkgs/http2/test/src/streams/simple_push_test.dart index f8a96f28fc..e91f95ba27 100644 --- a/pkgs/http2/test/src/streams/simple_push_test.dart +++ b/pkgs/http2/test/src/streams/simple_push_test.dart @@ -6,7 +6,6 @@ import 'dart:async'; import 'dart:convert' show utf8; import 'package:http2/transport.dart'; -import 'package:pedantic/pedantic.dart'; import 'package:test/test.dart'; import 'helper.dart'; diff --git a/pkgs/http2/test/src/streams/streams_test.dart b/pkgs/http2/test/src/streams/streams_test.dart index ebfdea71c7..18109fc38b 100644 --- a/pkgs/http2/test/src/streams/streams_test.dart +++ b/pkgs/http2/test/src/streams/streams_test.dart @@ -2,8 +2,9 @@ // 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/transport.dart'; -import 'package:pedantic/pedantic.dart'; import 'package:test/test.dart'; import 'helper.dart'; @@ -191,7 +192,7 @@ void main() { server.incomingStreams.listen(expectAsync1((TransportStream sStream) async { var isFirst = true; - var receivedChunk; + List? receivedChunk; sStream.incomingMessages.listen( expectAsync1((StreamMessage msg) { if (isFirst) { diff --git a/pkgs/http2/test/transport_test.dart b/pkgs/http2/test/transport_test.dart index 2d09e3107c..e227ed2394 100644 --- a/pkgs/http2/test/transport_test.dart +++ b/pkgs/http2/test/transport_test.dart @@ -7,7 +7,6 @@ import 'dart:typed_data'; import 'package:http2/src/flowcontrol/window.dart'; import 'package:http2/transport.dart'; -import 'package:pedantic/pedantic.dart'; import 'package:test/test.dart'; import 'src/hpack/hpack_test.dart' show isHeader; From aae9f9bc1eea33f13400ccb05a2ccaa5ace478d0 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Thu, 12 May 2022 03:36:02 +0000 Subject: [PATCH 127/172] test against 2.15 --- pkgs/http2/.github/workflows/test-package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/http2/.github/workflows/test-package.yml b/pkgs/http2/.github/workflows/test-package.yml index 6c7108d860..096a5cfb02 100644 --- a/pkgs/http2/.github/workflows/test-package.yml +++ b/pkgs/http2/.github/workflows/test-package.yml @@ -47,7 +47,7 @@ jobs: matrix: # Add macos-latest and/or windows-latest if relevant for this package. os: [ubuntu-latest] - sdk: [2.12.0, dev] + sdk: [2.15.0, dev] steps: - uses: actions/checkout@v3 - uses: dart-lang/setup-dart@v1 From d7b7ff849439cf01cbc576e5adf305c09fdb4d49 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Fri, 13 May 2022 09:15:34 -0700 Subject: [PATCH 128/172] Simplify waiting during close (dart-lang/http2#105) Use a for loop element in a list literal to collect all the futures rather than a nested `Future.wait` to concatenate the single server close with many connection closes. Refactor from `return` to `=>` at both levels since this reads more nicely if it looks declarative. --- pkgs/http2/lib/multiprotocol_server.dart | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/pkgs/http2/lib/multiprotocol_server.dart b/pkgs/http2/lib/multiprotocol_server.dart index 8dcd78c8ea..0b1aeeb0ec 100644 --- a/pkgs/http2/lib/multiprotocol_server.dart +++ b/pkgs/http2/lib/multiprotocol_server.dart @@ -93,14 +93,11 @@ class MultiProtocolHttpServer { /// Closes this [MultiProtocolHttpServer]. /// /// Completes once everything has been successfully shut down. - Future close({bool force = false}) { - return _serverSocket.close().whenComplete(() async { - var done1 = _http11Server.close(force: force); - Future done2 = Future.wait( - _http2Connections.map((c) => force ? c.terminate() : c.finish())); - await Future.wait([done1, done2]); - }); - } + Future close({bool force = false}) => + _serverSocket.close().whenComplete(() => Future.wait([ + _http11Server.close(force: force), + for (var c in _http2Connections) force ? c.terminate() : c.finish() + ])); } /// An internal helper class. From 79527a77708d01133dd5b084400c2725623226b2 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 19 Sep 2022 15:18:31 -0700 Subject: [PATCH 129/172] Fix hint related to importing BytesBuilder from dart:io (dart-lang/http2#107) Switch to dart:typed_data --- pkgs/http2/lib/src/async_utils/async_utils.dart | 2 +- pkgs/http2/lib/src/hpack/hpack.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http2/lib/src/async_utils/async_utils.dart b/pkgs/http2/lib/src/async_utils/async_utils.dart index 744fd90140..61483282ea 100644 --- a/pkgs/http2/lib/src/async_utils/async_utils.dart +++ b/pkgs/http2/lib/src/async_utils/async_utils.dart @@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import 'dart:async'; -import 'dart:io'; +import 'dart:typed_data'; /// An interface for `StreamSink`-like classes to indicate whether adding data /// would be buffered and when the buffer is empty again. diff --git a/pkgs/http2/lib/src/hpack/hpack.dart b/pkgs/http2/lib/src/hpack/hpack.dart index 62338a1c34..e59cd37df0 100644 --- a/pkgs/http2/lib/src/hpack/hpack.dart +++ b/pkgs/http2/lib/src/hpack/hpack.dart @@ -8,7 +8,7 @@ library http2.hpack; import 'dart:convert' show ascii; -import 'dart:io'; +import 'dart:typed_data'; import '../byte_utils.dart'; From b65c98e8a5a860049ce5aea4cf392e91642ead5c Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Wed, 5 Oct 2022 09:56:36 -0700 Subject: [PATCH 130/172] update CI config; use lints/recommended (dart-lang/http2#108) * update CI config; use lints/recommended * Update CHANGELOG.md --- pkgs/http2/.github/dependabot.yaml | 8 ++++++++ pkgs/http2/.github/dependabot.yml | 11 ----------- pkgs/http2/.github/workflows/test-package.yml | 8 ++++---- pkgs/http2/CHANGELOG.md | 4 +++- pkgs/http2/README.md | 10 ++++++---- pkgs/http2/analysis_options.yaml | 7 ++++++- pkgs/http2/lib/src/flowcontrol/stream_queues.dart | 8 ++++---- pkgs/http2/lib/src/ping/ping_handler.dart | 5 +++-- pkgs/http2/lib/src/settings/settings.dart | 5 +++-- pkgs/http2/lib/src/streams/stream_handler.dart | 10 +++++----- pkgs/http2/lib/transport.dart | 5 ++--- pkgs/http2/pubspec.yaml | 4 ++-- pkgs/http2/test/client_test.dart | 12 +++++------- 13 files changed, 51 insertions(+), 46 deletions(-) create mode 100644 pkgs/http2/.github/dependabot.yaml delete mode 100644 pkgs/http2/.github/dependabot.yml diff --git a/pkgs/http2/.github/dependabot.yaml b/pkgs/http2/.github/dependabot.yaml new file mode 100644 index 0000000000..9859fdc7f2 --- /dev/null +++ b/pkgs/http2/.github/dependabot.yaml @@ -0,0 +1,8 @@ +# Dependabot configuration file. +version: 2 + +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/pkgs/http2/.github/dependabot.yml b/pkgs/http2/.github/dependabot.yml deleted file mode 100644 index 430a85e7d0..0000000000 --- a/pkgs/http2/.github/dependabot.yml +++ /dev/null @@ -1,11 +0,0 @@ -# Set update schedule for GitHub Actions -# See https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/keeping-your-actions-up-to-date-with-dependabot - -version: 2 -updates: - -- package-ecosystem: "github-actions" - directory: "/" - schedule: - # Check for updates to GitHub Actions every weekday - interval: "daily" diff --git a/pkgs/http2/.github/workflows/test-package.yml b/pkgs/http2/.github/workflows/test-package.yml index 096a5cfb02..2e5ff01f83 100644 --- a/pkgs/http2/.github/workflows/test-package.yml +++ b/pkgs/http2/.github/workflows/test-package.yml @@ -22,8 +22,8 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@v3 - - uses: dart-lang/setup-dart@v1 + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d with: sdk: ${{ matrix.sdk }} - id: install @@ -49,8 +49,8 @@ jobs: os: [ubuntu-latest] sdk: [2.15.0, dev] steps: - - uses: actions/checkout@v3 - - uses: dart-lang/setup-dart@v1 + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d with: sdk: ${{ matrix.sdk }} - id: install diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index d3ea414885..9f58722d8b 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -1,5 +1,7 @@ -## 2.0.1-dev +## 2.0.1 +- Simplify the implementation of `MultiProtocolHttpServer.close`. +- Require Dart `2.15.0`. ## 2.0.0 diff --git a/pkgs/http2/README.md b/pkgs/http2/README.md index 4bea5dbd49..e776da2a89 100644 --- a/pkgs/http2/README.md +++ b/pkgs/http2/README.md @@ -1,11 +1,13 @@ -# HTTP/2 for Dart +[![Dart CI](https://github.com/dart-lang/http2/actions/workflows/test-package.yml/badge.svg)](https://github.com/dart-lang/http2/actions/workflows/test-package.yml) +[![pub package](https://img.shields.io/pub/v/http2.svg)](https://pub.dev/packages/http2) +[![package publisher](https://img.shields.io/pub/publisher/http2.svg)](https://pub.dev/packages/http2/publisher) This library provides an http/2 interface on top of a bidirectional stream of bytes. -## Usage: +## Usage -Here is a minimal example of connecting to a http/2 capable server, requesting a resource and -iterating over the response. +Here is a minimal example of connecting to a http/2 capable server, requesting +a resource and iterating over the response. ```dart import 'dart:convert'; diff --git a/pkgs/http2/analysis_options.yaml b/pkgs/http2/analysis_options.yaml index 570f6b74b0..3ec37e4d51 100644 --- a/pkgs/http2/analysis_options.yaml +++ b/pkgs/http2/analysis_options.yaml @@ -1,4 +1,4 @@ -include: package:lints/core.yaml +include: package:lints/recommended.yaml analyzer: strong-mode: @@ -8,3 +8,8 @@ analyzer: unused_import: error unused_local_variable: error dead_code: error + +linter: + rules: + # Disabled as there are several dozen violations. + constant_identifier_names: false diff --git a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart index e2e82e5319..ff9711c09e 100644 --- a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart @@ -241,15 +241,15 @@ class StreamMessageQueueIn extends Object } @override - void onTerminated(exception) { + void onTerminated(Object? error) { _pendingMessages.clear(); if (!wasClosed) { - if (exception != null) { - _incomingMessagesC.addError(exception); + if (error != null) { + _incomingMessagesC.addError(error); } _incomingMessagesC.close(); _serverPushStreamsC.close(); - closeWithError(exception); + closeWithError(error); } } diff --git a/pkgs/http2/lib/src/ping/ping_handler.dart b/pkgs/http2/lib/src/ping/ping_handler.dart index 7682e7efcd..20ce4871cb 100644 --- a/pkgs/http2/lib/src/ping/ping_handler.dart +++ b/pkgs/http2/lib/src/ping/ping_handler.dart @@ -24,8 +24,9 @@ class PingHandler extends Object with TerminatableMixin { void onTerminated(Object? error) { var values = _remainingPings.values.toList(); _remainingPings.clear(); - values.forEach( - (Completer c) => c.completeError(error ?? 'Unspecified error')); + for (var value in values) { + value.completeError(error ?? 'Unspecified error'); + } } void processPingFrame(PingFrame frame) { diff --git a/pkgs/http2/lib/src/settings/settings.dart b/pkgs/http2/lib/src/settings/settings.dart index 19d48a0490..948658d5be 100644 --- a/pkgs/http2/lib/src/settings/settings.dart +++ b/pkgs/http2/lib/src/settings/settings.dart @@ -154,8 +154,9 @@ class SettingsHandler extends Object with TerminatableMixin { @override void onTerminated(Object? error) { _toBeAcknowledgedSettings.clear(); - _toBeAcknowledgedCompleters - .forEach((Completer c) => c.completeError(error!)); + for (var completer in _toBeAcknowledgedCompleters) { + completer.completeError(error!); + } } Future changeSettings(List changes) { diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index d78374b1de..86d6c7b2bb 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -193,9 +193,9 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { } @override - void onTerminated(exception) { + void onTerminated(Object? error) { _openStreams.values.toList().forEach((stream) => - _closeStreamAbnormally(stream, exception, propagateException: true)); + _closeStreamAbnormally(stream, error, propagateException: true)); startClosing(); } @@ -212,9 +212,9 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { void processInitialWindowSizeSettingChange(int difference) { // If the initialFlowWindow size was changed via a SettingsFrame, all // existing streams must be updated to reflect this change. - _openStreams.values.forEach((Http2StreamImpl stream) { + for (var stream in _openStreams.values) { stream.windowHandler.processInitialWindowSizeSettingChange(difference); - }); + } } void processGoawayFrame(GoawayFrame frame) { @@ -512,7 +512,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { if (frame is DataFrame) { incomingQueue.processIgnoredDataFrame(frame); } - return null; + return; } // TODO: Consider splitting this method into client/server handling. diff --git a/pkgs/http2/lib/transport.dart b/pkgs/http2/lib/transport.dart index f43353dc69..6423f9371a 100644 --- a/pkgs/http2/lib/transport.dart +++ b/pkgs/http2/lib/transport.dart @@ -232,9 +232,8 @@ class TransportException implements Exception { class TransportConnectionException extends TransportException { final int errorCode; - TransportConnectionException(int errorCode, String details) - : errorCode = errorCode, - super('Connection error: $details (errorCode: $errorCode)'); + TransportConnectionException(this.errorCode, String details) + : super('Connection error: $details (errorCode: $errorCode)'); } /// An exception thrown when a HTTP/2 stream error occured. diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index f2e699d850..1138662952 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -1,10 +1,10 @@ name: http2 -version: 2.0.1-dev +version: 2.0.1 description: A HTTP/2 implementation in Dart. repository: https://github.com/dart-lang/http2 environment: - sdk: '>=2.12.0 <3.0.0' + sdk: '>=2.15.0 <3.0.0' dev_dependencies: build_runner: '>=1.10.0 <3.0.0' diff --git a/pkgs/http2/test/client_test.dart b/pkgs/http2/test/client_test.dart index 8bac9d32b2..d6a4df3e17 100644 --- a/pkgs/http2/test/client_test.dart +++ b/pkgs/http2/test/client_test.dart @@ -770,13 +770,11 @@ void main() { } void clientTest( - String name, - Future Function( - ClientTransportConnection, - FrameWriter, - StreamIterator frameReader, - Future Function() readNext) - func) { + String name, + Future Function(ClientTransportConnection, FrameWriter, + StreamIterator frameReader, Future Function() readNext) + func, +) { return test(name, () { var streams = ClientStreams(); var serverReader = streams.serverConnectionFrameReader; From df93757aa8e872d6d9e304401311b8811115819f Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 14 Nov 2022 07:29:12 -0800 Subject: [PATCH 131/172] Fix lint updates (dart-lang/http2#110) * Fix lint updates * oops --- pkgs/http2/.github/workflows/test-package.yml | 2 +- pkgs/http2/CHANGELOG.md | 4 + pkgs/http2/lib/src/connection.dart | 3 +- .../http2/lib/src/streams/stream_handler.dart | 6 +- pkgs/http2/pubspec.yaml | 12 +- .../test/src/flowcontrol/mocks.mocks.dart | 765 ++++++++++++------ 6 files changed, 525 insertions(+), 267 deletions(-) diff --git a/pkgs/http2/.github/workflows/test-package.yml b/pkgs/http2/.github/workflows/test-package.yml index 2e5ff01f83..4e90396cbc 100644 --- a/pkgs/http2/.github/workflows/test-package.yml +++ b/pkgs/http2/.github/workflows/test-package.yml @@ -47,7 +47,7 @@ jobs: matrix: # Add macos-latest and/or windows-latest if relevant for this package. os: [ubuntu-latest] - sdk: [2.15.0, dev] + sdk: [2.17.0, dev] steps: - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index 9f58722d8b..d0a764647a 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.2-dev + +- Require Dart `2.17.0`. + ## 2.0.1 - Simplify the implementation of `MultiProtocolHttpServer.close`. diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index 32393d9be3..e27b90e676 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -433,7 +433,8 @@ abstract class Connection { _pingHandler.terminate(exception); _settingsHandler.terminate(exception); - return Future.wait([cancelFuture, closeFuture]).catchError((_) {}); + return Future.wait([cancelFuture, closeFuture]) + .catchError((_) => const []); } return Future.value(); } diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index 86d6c7b2bb..f32a1e5546 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -319,8 +319,8 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { incomingQueue.insertNewStreamMessageQueue(streamId, streamQueueIn); - var _outgoingC = StreamController(); - var stream = Http2StreamImpl(streamQueueIn, streamQueueOut, _outgoingC, + var outgoingC = StreamController(); + var stream = Http2StreamImpl(streamQueueIn, streamQueueOut, outgoingC, streamId, windowOutHandler, _canPush, _push, _terminateStream); final wasIdle = _openStreams.isEmpty; _openStreams[stream.id] = stream; @@ -343,7 +343,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { // NOTE: We are not interested whether the streams were normally finished // or abnormally terminated. Therefore we use 'catchError((_) {})'! var streamDone = [streamQueueIn.done, streamQueueOut.done]; - Future.wait(streamDone).catchError((_) {}).whenComplete(() { + Future.wait(streamDone).catchError((_) => const []).whenComplete(() { _cleanupClosedStream(stream); }); diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index 1138662952..eb9187aead 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -1,13 +1,13 @@ name: http2 -version: 2.0.1 +version: 2.0.2-dev description: A HTTP/2 implementation in Dart. repository: https://github.com/dart-lang/http2 environment: - sdk: '>=2.15.0 <3.0.0' + sdk: '>=2.17.0 <3.0.0' dev_dependencies: - build_runner: '>=1.10.0 <3.0.0' - lints: ^1.0.0 - mockito: ^5.0.16 - test: ^1.16.0 + build_runner: ^2.3.0 + lints: ^2.0.0 + mockito: ^5.3.2 + test: ^1.21.4 diff --git a/pkgs/http2/test/src/flowcontrol/mocks.mocks.dart b/pkgs/http2/test/src/flowcontrol/mocks.mocks.dart index 58facbb367..b15faae1ae 100644 --- a/pkgs/http2/test/src/flowcontrol/mocks.mocks.dart +++ b/pkgs/http2/test/src/flowcontrol/mocks.mocks.dart @@ -1,9 +1,8 @@ -// Mocks generated by Mockito 5.0.16 from annotations +// Mocks generated by Mockito 5.3.2 from annotations // in http2/test/src/flowcontrol/mocks.dart. // Do not manually edit this file. -// ignore_for_file: unnecessary_overrides - +// ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i5; import 'package:http2/src/async_utils/async_utils.dart' as _i2; @@ -18,6 +17,7 @@ import 'package:mockito/mockito.dart' as _i1; import 'mocks.dart' as _i9; +// ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references @@ -26,11 +26,29 @@ import 'mocks.dart' as _i9; // ignore_for_file: prefer_const_constructors // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class -class _FakeBufferIndicator_0 extends _i1.Fake implements _i2.BufferIndicator {} +class _FakeBufferIndicator_0 extends _i1.SmartFake + implements _i2.BufferIndicator { + _FakeBufferIndicator_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeIncomingWindowHandler_1 extends _i1.Fake - implements _i3.IncomingWindowHandler {} +class _FakeIncomingWindowHandler_1 extends _i1.SmartFake + implements _i3.IncomingWindowHandler { + _FakeIncomingWindowHandler_1( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} /// A class which mocks [FrameWriter]. /// @@ -41,81 +59,175 @@ class MockFrameWriter extends _i1.Mock implements _i4.FrameWriter { } @override - _i2.BufferIndicator get bufferIndicator => - (super.noSuchMethod(Invocation.getter(#bufferIndicator), - returnValue: _FakeBufferIndicator_0()) as _i2.BufferIndicator); - @override - int get highestWrittenStreamId => - (super.noSuchMethod(Invocation.getter(#highestWrittenStreamId), - returnValue: 0) as int); - @override - _i5.Future get doneFuture => - (super.noSuchMethod(Invocation.getter(#doneFuture), - returnValue: Future.value()) as _i5.Future); - @override - void writeDataFrame(int? streamId, List? data, - {bool? endStream = false}) => + _i2.BufferIndicator get bufferIndicator => (super.noSuchMethod( + Invocation.getter(#bufferIndicator), + returnValue: _FakeBufferIndicator_0( + this, + Invocation.getter(#bufferIndicator), + ), + ) as _i2.BufferIndicator); + @override + int get highestWrittenStreamId => (super.noSuchMethod( + Invocation.getter(#highestWrittenStreamId), + returnValue: 0, + ) as int); + @override + _i5.Future get doneFuture => (super.noSuchMethod( + Invocation.getter(#doneFuture), + returnValue: _i5.Future.value(), + ) as _i5.Future); + @override + void writeDataFrame( + int? streamId, + List? data, { + bool? endStream = false, + }) => super.noSuchMethod( - Invocation.method( - #writeDataFrame, [streamId, data], {#endStream: endStream}), - returnValueForMissingStub: null); - @override - void writeHeadersFrame(int? streamId, List<_i6.Header>? headers, - {bool? endStream = true}) => + Invocation.method( + #writeDataFrame, + [ + streamId, + data, + ], + {#endStream: endStream}, + ), + returnValueForMissingStub: null, + ); + @override + void writeHeadersFrame( + int? streamId, + List<_i6.Header>? headers, { + bool? endStream = true, + }) => super.noSuchMethod( - Invocation.method( - #writeHeadersFrame, [streamId, headers], {#endStream: endStream}), - returnValueForMissingStub: null); - @override - void writePriorityFrame(int? streamId, int? streamDependency, int? weight, - {bool? exclusive = false}) => + Invocation.method( + #writeHeadersFrame, + [ + streamId, + headers, + ], + {#endStream: endStream}, + ), + returnValueForMissingStub: null, + ); + @override + void writePriorityFrame( + int? streamId, + int? streamDependency, + int? weight, { + bool? exclusive = false, + }) => super.noSuchMethod( - Invocation.method(#writePriorityFrame, - [streamId, streamDependency, weight], {#exclusive: exclusive}), - returnValueForMissingStub: null); - @override - void writeRstStreamFrame(int? streamId, int? errorCode) => super.noSuchMethod( - Invocation.method(#writeRstStreamFrame, [streamId, errorCode]), - returnValueForMissingStub: null); - @override - void writeSettingsFrame(List<_i4.Setting>? settings) => - super.noSuchMethod(Invocation.method(#writeSettingsFrame, [settings]), - returnValueForMissingStub: null); - @override - void writeSettingsAckFrame() => - super.noSuchMethod(Invocation.method(#writeSettingsAckFrame, []), - returnValueForMissingStub: null); + Invocation.method( + #writePriorityFrame, + [ + streamId, + streamDependency, + weight, + ], + {#exclusive: exclusive}, + ), + returnValueForMissingStub: null, + ); + @override + void writeRstStreamFrame( + int? streamId, + int? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #writeRstStreamFrame, + [ + streamId, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + @override + void writeSettingsFrame(List<_i4.Setting>? settings) => super.noSuchMethod( + Invocation.method( + #writeSettingsFrame, + [settings], + ), + returnValueForMissingStub: null, + ); + @override + void writeSettingsAckFrame() => super.noSuchMethod( + Invocation.method( + #writeSettingsAckFrame, + [], + ), + returnValueForMissingStub: null, + ); @override void writePushPromiseFrame( - int? streamId, int? promisedStreamId, List<_i6.Header>? headers) => + int? streamId, + int? promisedStreamId, + List<_i6.Header>? headers, + ) => super.noSuchMethod( - Invocation.method( - #writePushPromiseFrame, [streamId, promisedStreamId, headers]), - returnValueForMissingStub: null); - @override - void writePingFrame(int? opaqueData, {bool? ack = false}) => + Invocation.method( + #writePushPromiseFrame, + [ + streamId, + promisedStreamId, + headers, + ], + ), + returnValueForMissingStub: null, + ); + @override + void writePingFrame( + int? opaqueData, { + bool? ack = false, + }) => super.noSuchMethod( - Invocation.method(#writePingFrame, [opaqueData], {#ack: ack}), - returnValueForMissingStub: null); + Invocation.method( + #writePingFrame, + [opaqueData], + {#ack: ack}, + ), + returnValueForMissingStub: null, + ); @override void writeGoawayFrame( - int? lastStreamId, int? errorCode, List? debugData) => + int? lastStreamId, + int? errorCode, + List? debugData, + ) => super.noSuchMethod( - Invocation.method( - #writeGoawayFrame, [lastStreamId, errorCode, debugData]), - returnValueForMissingStub: null); - @override - void writeWindowUpdate(int? sizeIncrement, {int? streamId = 0}) => + Invocation.method( + #writeGoawayFrame, + [ + lastStreamId, + errorCode, + debugData, + ], + ), + returnValueForMissingStub: null, + ); + @override + void writeWindowUpdate( + int? sizeIncrement, { + int? streamId = 0, + }) => super.noSuchMethod( - Invocation.method( - #writeWindowUpdate, [sizeIncrement], {#streamId: streamId}), - returnValueForMissingStub: null); - @override - _i5.Future close() => - (super.noSuchMethod(Invocation.method(#close, []), - returnValue: Future.value()) as _i5.Future); - @override - String toString() => super.toString(); + Invocation.method( + #writeWindowUpdate, + [sizeIncrement], + {#streamId: streamId}, + ), + returnValueForMissingStub: null, + ); + @override + _i5.Future close() => (super.noSuchMethod( + Invocation.method( + #close, + [], + ), + returnValue: _i5.Future.value(), + ) as _i5.Future); } /// A class which mocks [IncomingWindowHandler]. @@ -128,19 +240,26 @@ class MockIncomingWindowHandler extends _i1.Mock } @override - int get localWindowSize => - (super.noSuchMethod(Invocation.getter(#localWindowSize), returnValue: 0) - as int); - @override - void gotData(int? numberOfBytes) => - super.noSuchMethod(Invocation.method(#gotData, [numberOfBytes]), - returnValueForMissingStub: null); - @override - void dataProcessed(int? numberOfBytes) => - super.noSuchMethod(Invocation.method(#dataProcessed, [numberOfBytes]), - returnValueForMissingStub: null); - @override - String toString() => super.toString(); + int get localWindowSize => (super.noSuchMethod( + Invocation.getter(#localWindowSize), + returnValue: 0, + ) as int); + @override + void gotData(int? numberOfBytes) => super.noSuchMethod( + Invocation.method( + #gotData, + [numberOfBytes], + ), + returnValueForMissingStub: null, + ); + @override + void dataProcessed(int? numberOfBytes) => super.noSuchMethod( + Invocation.method( + #dataProcessed, + [numberOfBytes], + ), + returnValueForMissingStub: null, + ); } /// A class which mocks [OutgoingStreamWindowHandler]. @@ -153,29 +272,43 @@ class MockOutgoingStreamWindowHandler extends _i1.Mock } @override - _i2.BufferIndicator get positiveWindow => - (super.noSuchMethod(Invocation.getter(#positiveWindow), - returnValue: _FakeBufferIndicator_0()) as _i2.BufferIndicator); + _i2.BufferIndicator get positiveWindow => (super.noSuchMethod( + Invocation.getter(#positiveWindow), + returnValue: _FakeBufferIndicator_0( + this, + Invocation.getter(#positiveWindow), + ), + ) as _i2.BufferIndicator); @override - int get peerWindowSize => - (super.noSuchMethod(Invocation.getter(#peerWindowSize), returnValue: 0) - as int); + int get peerWindowSize => (super.noSuchMethod( + Invocation.getter(#peerWindowSize), + returnValue: 0, + ) as int); @override void processInitialWindowSizeSettingChange(int? difference) => super.noSuchMethod( - Invocation.method( - #processInitialWindowSizeSettingChange, [difference]), - returnValueForMissingStub: null); - @override - void processWindowUpdate(_i4.WindowUpdateFrame? frame) => - super.noSuchMethod(Invocation.method(#processWindowUpdate, [frame]), - returnValueForMissingStub: null); - @override - void decreaseWindow(int? numberOfBytes) => - super.noSuchMethod(Invocation.method(#decreaseWindow, [numberOfBytes]), - returnValueForMissingStub: null); - @override - String toString() => super.toString(); + Invocation.method( + #processInitialWindowSizeSettingChange, + [difference], + ), + returnValueForMissingStub: null, + ); + @override + void processWindowUpdate(_i4.WindowUpdateFrame? frame) => super.noSuchMethod( + Invocation.method( + #processWindowUpdate, + [frame], + ), + returnValueForMissingStub: null, + ); + @override + void decreaseWindow(int? numberOfBytes) => super.noSuchMethod( + Invocation.method( + #decreaseWindow, + [numberOfBytes], + ), + returnValueForMissingStub: null, + ); } /// A class which mocks [ConnectionMessageQueueOut]. @@ -188,69 +321,118 @@ class MockConnectionMessageQueueOut extends _i1.Mock } @override - int get pendingMessages => - (super.noSuchMethod(Invocation.getter(#pendingMessages), returnValue: 0) - as int); - @override - bool get wasTerminated => - (super.noSuchMethod(Invocation.getter(#wasTerminated), returnValue: false) - as bool); - @override - _i5.Future get done => (super.noSuchMethod(Invocation.getter(#done), - returnValue: Future.value()) as _i5.Future); - @override - bool get isClosing => - (super.noSuchMethod(Invocation.getter(#isClosing), returnValue: false) - as bool); - @override - bool get wasClosed => - (super.noSuchMethod(Invocation.getter(#wasClosed), returnValue: false) - as bool); - @override - void enqueueMessage(_i8.Message? message) => - super.noSuchMethod(Invocation.method(#enqueueMessage, [message]), - returnValueForMissingStub: null); - @override - void onTerminated(Object? error) => - super.noSuchMethod(Invocation.method(#onTerminated, [error]), - returnValueForMissingStub: null); - @override - void onCheckForClose() => - super.noSuchMethod(Invocation.method(#onCheckForClose, []), - returnValueForMissingStub: null); - @override - String toString() => super.toString(); - @override - void terminate([dynamic error]) => - super.noSuchMethod(Invocation.method(#terminate, [error]), - returnValueForMissingStub: null); - @override - T ensureNotTerminatedSync(T Function()? f) => - (super.noSuchMethod(Invocation.method(#ensureNotTerminatedSync, [f]), - returnValue: _i9.ensureNotTerminatedSyncFallback(f)) as T); + int get pendingMessages => (super.noSuchMethod( + Invocation.getter(#pendingMessages), + returnValue: 0, + ) as int); + @override + bool get wasTerminated => (super.noSuchMethod( + Invocation.getter(#wasTerminated), + returnValue: false, + ) as bool); + @override + _i5.Future get done => (super.noSuchMethod( + Invocation.getter(#done), + returnValue: _i5.Future.value(), + ) as _i5.Future); + @override + bool get isClosing => (super.noSuchMethod( + Invocation.getter(#isClosing), + returnValue: false, + ) as bool); + @override + bool get wasClosed => (super.noSuchMethod( + Invocation.getter(#wasClosed), + returnValue: false, + ) as bool); + @override + void enqueueMessage(_i8.Message? message) => super.noSuchMethod( + Invocation.method( + #enqueueMessage, + [message], + ), + returnValueForMissingStub: null, + ); + @override + void onTerminated(Object? error) => super.noSuchMethod( + Invocation.method( + #onTerminated, + [error], + ), + returnValueForMissingStub: null, + ); + @override + void onCheckForClose() => super.noSuchMethod( + Invocation.method( + #onCheckForClose, + [], + ), + returnValueForMissingStub: null, + ); + @override + void terminate([dynamic error]) => super.noSuchMethod( + Invocation.method( + #terminate, + [error], + ), + returnValueForMissingStub: null, + ); + @override + T ensureNotTerminatedSync(T Function()? f) => (super.noSuchMethod( + Invocation.method( + #ensureNotTerminatedSync, + [f], + ), + returnValue: _i9.ensureNotTerminatedSyncFallback(f), + ) as T); @override _i5.Future ensureNotTerminatedAsync( _i5.Future Function()? f) => - (super.noSuchMethod(Invocation.method(#ensureNotTerminatedAsync, [f]), - returnValue: Future.value()) as _i5.Future); - @override - void startClosing() => - super.noSuchMethod(Invocation.method(#startClosing, []), - returnValueForMissingStub: null); - @override - void onClosing() => super.noSuchMethod(Invocation.method(#onClosing, []), - returnValueForMissingStub: null); + (super.noSuchMethod( + Invocation.method( + #ensureNotTerminatedAsync, + [f], + ), + returnValue: _i5.Future.value(), + ) as _i5.Future); + @override + void startClosing() => super.noSuchMethod( + Invocation.method( + #startClosing, + [], + ), + returnValueForMissingStub: null, + ); + @override + void onClosing() => super.noSuchMethod( + Invocation.method( + #onClosing, + [], + ), + returnValueForMissingStub: null, + ); @override dynamic ensureNotClosingSync(dynamic Function()? f) => - super.noSuchMethod(Invocation.method(#ensureNotClosingSync, [f])); - @override - void closeWithValue([dynamic value]) => - super.noSuchMethod(Invocation.method(#closeWithValue, [value]), - returnValueForMissingStub: null); - @override - void closeWithError(dynamic error) => - super.noSuchMethod(Invocation.method(#closeWithError, [error]), - returnValueForMissingStub: null); + super.noSuchMethod(Invocation.method( + #ensureNotClosingSync, + [f], + )); + @override + void closeWithValue([dynamic value]) => super.noSuchMethod( + Invocation.method( + #closeWithValue, + [value], + ), + returnValueForMissingStub: null, + ); + @override + void closeWithError(dynamic error) => super.noSuchMethod( + Invocation.method( + #closeWithError, + [error], + ), + returnValueForMissingStub: null, + ); } /// A class which mocks [StreamMessageQueueIn]. @@ -263,105 +445,176 @@ class MockStreamMessageQueueIn extends _i1.Mock } @override - _i3.IncomingWindowHandler get windowHandler => - (super.noSuchMethod(Invocation.getter(#windowHandler), - returnValue: _FakeIncomingWindowHandler_1()) - as _i3.IncomingWindowHandler); - @override - _i2.BufferIndicator get bufferIndicator => - (super.noSuchMethod(Invocation.getter(#bufferIndicator), - returnValue: _FakeBufferIndicator_0()) as _i2.BufferIndicator); - @override - int get pendingMessages => - (super.noSuchMethod(Invocation.getter(#pendingMessages), returnValue: 0) - as int); - @override - _i5.Stream<_i11.StreamMessage> get messages => - (super.noSuchMethod(Invocation.getter(#messages), - returnValue: Stream<_i11.StreamMessage>.empty()) - as _i5.Stream<_i11.StreamMessage>); - @override - _i5.Stream<_i11.TransportStreamPush> get serverPushes => - (super.noSuchMethod(Invocation.getter(#serverPushes), - returnValue: Stream<_i11.TransportStreamPush>.empty()) - as _i5.Stream<_i11.TransportStreamPush>); - @override - bool get wasTerminated => - (super.noSuchMethod(Invocation.getter(#wasTerminated), returnValue: false) - as bool); - @override - _i5.Future get done => (super.noSuchMethod(Invocation.getter(#done), - returnValue: Future.value()) as _i5.Future); - @override - bool get isClosing => - (super.noSuchMethod(Invocation.getter(#isClosing), returnValue: false) - as bool); - @override - bool get wasClosed => - (super.noSuchMethod(Invocation.getter(#wasClosed), returnValue: false) - as bool); - @override - _i5.Future get onCancel => - (super.noSuchMethod(Invocation.getter(#onCancel), - returnValue: Future.value()) as _i5.Future); - @override - bool get wasCancelled => - (super.noSuchMethod(Invocation.getter(#wasCancelled), returnValue: false) - as bool); - @override - void enqueueMessage(_i8.Message? message) => - super.noSuchMethod(Invocation.method(#enqueueMessage, [message]), - returnValueForMissingStub: null); - @override - void onTerminated(Object? exception) => - super.noSuchMethod(Invocation.method(#onTerminated, [exception]), - returnValueForMissingStub: null); - @override - void onCloseCheck() => - super.noSuchMethod(Invocation.method(#onCloseCheck, []), - returnValueForMissingStub: null); - @override - void forceDispatchIncomingMessages() => - super.noSuchMethod(Invocation.method(#forceDispatchIncomingMessages, []), - returnValueForMissingStub: null); - @override - String toString() => super.toString(); - @override - void terminate([dynamic error]) => - super.noSuchMethod(Invocation.method(#terminate, [error]), - returnValueForMissingStub: null); - @override - T ensureNotTerminatedSync(T Function()? f) => - (super.noSuchMethod(Invocation.method(#ensureNotTerminatedSync, [f]), - returnValue: _i9.ensureNotTerminatedSyncFallback(f)) as T); + _i3.IncomingWindowHandler get windowHandler => (super.noSuchMethod( + Invocation.getter(#windowHandler), + returnValue: _FakeIncomingWindowHandler_1( + this, + Invocation.getter(#windowHandler), + ), + ) as _i3.IncomingWindowHandler); + @override + _i2.BufferIndicator get bufferIndicator => (super.noSuchMethod( + Invocation.getter(#bufferIndicator), + returnValue: _FakeBufferIndicator_0( + this, + Invocation.getter(#bufferIndicator), + ), + ) as _i2.BufferIndicator); + @override + int get pendingMessages => (super.noSuchMethod( + Invocation.getter(#pendingMessages), + returnValue: 0, + ) as int); + @override + _i5.Stream<_i11.StreamMessage> get messages => (super.noSuchMethod( + Invocation.getter(#messages), + returnValue: _i5.Stream<_i11.StreamMessage>.empty(), + ) as _i5.Stream<_i11.StreamMessage>); + @override + _i5.Stream<_i11.TransportStreamPush> get serverPushes => (super.noSuchMethod( + Invocation.getter(#serverPushes), + returnValue: _i5.Stream<_i11.TransportStreamPush>.empty(), + ) as _i5.Stream<_i11.TransportStreamPush>); + @override + bool get wasTerminated => (super.noSuchMethod( + Invocation.getter(#wasTerminated), + returnValue: false, + ) as bool); + @override + _i5.Future get done => (super.noSuchMethod( + Invocation.getter(#done), + returnValue: _i5.Future.value(), + ) as _i5.Future); + @override + bool get isClosing => (super.noSuchMethod( + Invocation.getter(#isClosing), + returnValue: false, + ) as bool); + @override + bool get wasClosed => (super.noSuchMethod( + Invocation.getter(#wasClosed), + returnValue: false, + ) as bool); + @override + _i5.Future get onCancel => (super.noSuchMethod( + Invocation.getter(#onCancel), + returnValue: _i5.Future.value(), + ) as _i5.Future); + @override + bool get wasCancelled => (super.noSuchMethod( + Invocation.getter(#wasCancelled), + returnValue: false, + ) as bool); + @override + void enqueueMessage(_i8.Message? message) => super.noSuchMethod( + Invocation.method( + #enqueueMessage, + [message], + ), + returnValueForMissingStub: null, + ); + @override + void onTerminated(Object? error) => super.noSuchMethod( + Invocation.method( + #onTerminated, + [error], + ), + returnValueForMissingStub: null, + ); + @override + void onCloseCheck() => super.noSuchMethod( + Invocation.method( + #onCloseCheck, + [], + ), + returnValueForMissingStub: null, + ); + @override + void forceDispatchIncomingMessages() => super.noSuchMethod( + Invocation.method( + #forceDispatchIncomingMessages, + [], + ), + returnValueForMissingStub: null, + ); + @override + void terminate([dynamic error]) => super.noSuchMethod( + Invocation.method( + #terminate, + [error], + ), + returnValueForMissingStub: null, + ); + @override + T ensureNotTerminatedSync(T Function()? f) => (super.noSuchMethod( + Invocation.method( + #ensureNotTerminatedSync, + [f], + ), + returnValue: _i9.ensureNotTerminatedSyncFallback(f), + ) as T); @override _i5.Future ensureNotTerminatedAsync( _i5.Future Function()? f) => - (super.noSuchMethod(Invocation.method(#ensureNotTerminatedAsync, [f]), - returnValue: Future.value()) as _i5.Future); - @override - void startClosing() => - super.noSuchMethod(Invocation.method(#startClosing, []), - returnValueForMissingStub: null); - @override - void onCheckForClose() => - super.noSuchMethod(Invocation.method(#onCheckForClose, []), - returnValueForMissingStub: null); - @override - void onClosing() => super.noSuchMethod(Invocation.method(#onClosing, []), - returnValueForMissingStub: null); + (super.noSuchMethod( + Invocation.method( + #ensureNotTerminatedAsync, + [f], + ), + returnValue: _i5.Future.value(), + ) as _i5.Future); + @override + void startClosing() => super.noSuchMethod( + Invocation.method( + #startClosing, + [], + ), + returnValueForMissingStub: null, + ); + @override + void onCheckForClose() => super.noSuchMethod( + Invocation.method( + #onCheckForClose, + [], + ), + returnValueForMissingStub: null, + ); + @override + void onClosing() => super.noSuchMethod( + Invocation.method( + #onClosing, + [], + ), + returnValueForMissingStub: null, + ); @override dynamic ensureNotClosingSync(dynamic Function()? f) => - super.noSuchMethod(Invocation.method(#ensureNotClosingSync, [f])); - @override - void closeWithValue([dynamic value]) => - super.noSuchMethod(Invocation.method(#closeWithValue, [value]), - returnValueForMissingStub: null); - @override - void closeWithError(dynamic error) => - super.noSuchMethod(Invocation.method(#closeWithError, [error]), - returnValueForMissingStub: null); - @override - void cancel() => super.noSuchMethod(Invocation.method(#cancel, []), - returnValueForMissingStub: null); + super.noSuchMethod(Invocation.method( + #ensureNotClosingSync, + [f], + )); + @override + void closeWithValue([dynamic value]) => super.noSuchMethod( + Invocation.method( + #closeWithValue, + [value], + ), + returnValueForMissingStub: null, + ); + @override + void closeWithError(dynamic error) => super.noSuchMethod( + Invocation.method( + #closeWithError, + [error], + ), + returnValueForMissingStub: null, + ); + @override + void cancel() => super.noSuchMethod( + Invocation.method( + #cancel, + [], + ), + returnValueForMissingStub: null, + ); } From 5074ad3ca2fc518fa405bd904dac01479d58bc33 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Dec 2022 18:04:05 -0800 Subject: [PATCH 132/172] Bump actions/checkout from 3.1.0 to 3.2.0 (dart-lang/http2#111) Bumps [actions/checkout](https://github.com/actions/checkout) from 3.1.0 to 3.2.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8...755da8c3cf115ac066823e79a1e1788f8940201b) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pkgs/http2/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http2/.github/workflows/test-package.yml b/pkgs/http2/.github/workflows/test-package.yml index 4e90396cbc..1e27276884 100644 --- a/pkgs/http2/.github/workflows/test-package.yml +++ b/pkgs/http2/.github/workflows/test-package.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + - uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d with: sdk: ${{ matrix.sdk }} @@ -49,7 +49,7 @@ jobs: os: [ubuntu-latest] sdk: [2.17.0, dev] steps: - - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + - uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d with: sdk: ${{ matrix.sdk }} From 35da177a5e6e19a3fe95cf505a937f74b8c81003 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Jan 2023 14:34:36 -0800 Subject: [PATCH 133/172] Bump actions/checkout from 3.2.0 to 3.3.0 (dart-lang/http2#112) Bumps [actions/checkout](https://github.com/actions/checkout) from 3.2.0 to 3.3.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/755da8c3cf115ac066823e79a1e1788f8940201b...ac593985615ec2ede58e132d2e21d2b1cbd6127c) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pkgs/http2/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http2/.github/workflows/test-package.yml b/pkgs/http2/.github/workflows/test-package.yml index 1e27276884..6ee47fbb36 100644 --- a/pkgs/http2/.github/workflows/test-package.yml +++ b/pkgs/http2/.github/workflows/test-package.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d with: sdk: ${{ matrix.sdk }} @@ -49,7 +49,7 @@ jobs: os: [ubuntu-latest] sdk: [2.17.0, dev] steps: - - uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d with: sdk: ${{ matrix.sdk }} From 14d2c8b55dff157260eff39c8e209808341eca5b Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Mon, 9 Jan 2023 14:58:25 -0800 Subject: [PATCH 134/172] Migrate from no-implicit-casts to strict-casts (dart-lang/http2#113) --- pkgs/http2/analysis_options.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http2/analysis_options.yaml b/pkgs/http2/analysis_options.yaml index 3ec37e4d51..8b5373d0dc 100644 --- a/pkgs/http2/analysis_options.yaml +++ b/pkgs/http2/analysis_options.yaml @@ -1,8 +1,8 @@ include: package:lints/recommended.yaml analyzer: - strong-mode: - implicit-casts: false + language: + strict-casts: true errors: unused_element: error unused_import: error From 38079c557bee5b118e8168d0de021f6615e2b4de Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 17 Jan 2023 07:17:53 -0800 Subject: [PATCH 135/172] dependabot: monthly is plenty (dart-lang/http2#115) --- pkgs/http2/.github/dependabot.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/http2/.github/dependabot.yaml b/pkgs/http2/.github/dependabot.yaml index 9859fdc7f2..214481934b 100644 --- a/pkgs/http2/.github/dependabot.yaml +++ b/pkgs/http2/.github/dependabot.yaml @@ -5,4 +5,4 @@ updates: - package-ecosystem: "github-actions" directory: "/" schedule: - interval: "weekly" + interval: "monthly" From 68da2dc9cfb666ef745f252f8cb16a8f63afb691 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Jan 2023 08:41:41 -0800 Subject: [PATCH 136/172] Bump dart-lang/setup-dart from 1.3 to 1.4 (dart-lang/http2#114) Bumps [dart-lang/setup-dart](https://github.com/dart-lang/setup-dart) from 1.3 to 1.4. - [Release notes](https://github.com/dart-lang/setup-dart/releases) - [Changelog](https://github.com/dart-lang/setup-dart/blob/main/CHANGELOG.md) - [Commits](https://github.com/dart-lang/setup-dart/compare/6a218f2413a3e78e9087f638a238f6b40893203d...a57a6c04cf7d4840e88432aad6281d1e125f0d46) --- updated-dependencies: - dependency-name: dart-lang/setup-dart dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pkgs/http2/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http2/.github/workflows/test-package.yml b/pkgs/http2/.github/workflows/test-package.yml index 6ee47fbb36..58201d8209 100644 --- a/pkgs/http2/.github/workflows/test-package.yml +++ b/pkgs/http2/.github/workflows/test-package.yml @@ -23,7 +23,7 @@ jobs: sdk: [dev] steps: - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c - - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d + - uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46 with: sdk: ${{ matrix.sdk }} - id: install @@ -50,7 +50,7 @@ jobs: sdk: [2.17.0, dev] steps: - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c - - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d + - uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46 with: sdk: ${{ matrix.sdk }} - id: install From bb786147d4f25c8dff164e334ae7762c6d22943d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Apr 2023 14:35:28 -0700 Subject: [PATCH 137/172] Bump actions/checkout from 3.3.0 to 3.5.0 (dart-lang/http2#118) Bumps [actions/checkout](https://github.com/actions/checkout) from 3.3.0 to 3.5.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/ac593985615ec2ede58e132d2e21d2b1cbd6127c...8f4b7f84864484a7bf31766abe9204da3cbe65b3) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pkgs/http2/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http2/.github/workflows/test-package.yml b/pkgs/http2/.github/workflows/test-package.yml index 58201d8209..8c72a4358c 100644 --- a/pkgs/http2/.github/workflows/test-package.yml +++ b/pkgs/http2/.github/workflows/test-package.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c + - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 - uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46 with: sdk: ${{ matrix.sdk }} @@ -49,7 +49,7 @@ jobs: os: [ubuntu-latest] sdk: [2.17.0, dev] steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c + - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 - uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46 with: sdk: ${{ matrix.sdk }} From 16ee983b8284cade953c4616974edd280b2028e8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Apr 2023 14:39:04 -0700 Subject: [PATCH 138/172] Bump dart-lang/setup-dart from 1.4.0 to 1.5.0 (dart-lang/http2#117) Bumps [dart-lang/setup-dart](https://github.com/dart-lang/setup-dart) from 1.4.0 to 1.5.0. - [Release notes](https://github.com/dart-lang/setup-dart/releases) - [Changelog](https://github.com/dart-lang/setup-dart/blob/main/CHANGELOG.md) - [Commits](https://github.com/dart-lang/setup-dart/compare/a57a6c04cf7d4840e88432aad6281d1e125f0d46...d6a63dab3335f427404425de0fbfed4686d93c4f) --- updated-dependencies: - dependency-name: dart-lang/setup-dart dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pkgs/http2/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http2/.github/workflows/test-package.yml b/pkgs/http2/.github/workflows/test-package.yml index 8c72a4358c..7bcd23232a 100644 --- a/pkgs/http2/.github/workflows/test-package.yml +++ b/pkgs/http2/.github/workflows/test-package.yml @@ -23,7 +23,7 @@ jobs: sdk: [dev] steps: - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 - - uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46 + - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} - id: install @@ -50,7 +50,7 @@ jobs: sdk: [2.17.0, dev] steps: - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 - - uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46 + - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} - id: install From 0b75978a8470d2cdb3cadecc8492cc31df16b5ce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 May 2023 22:48:02 +0000 Subject: [PATCH 139/172] Bump actions/checkout from 3.5.0 to 3.5.2 (dart-lang/http2#119) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [actions/checkout](https://github.com/actions/checkout) from 3.5.0 to 3.5.2.
Release notes

Sourced from actions/checkout's releases.

v3.5.2

What's Changed

Full Changelog: https://github.com/actions/checkout/compare/v3.5.1...v3.5.2

v3.5.1

What's Changed

New Contributors

Full Changelog: https://github.com/actions/checkout/compare/v3.5.0...v3.5.1

Changelog

Sourced from actions/checkout's changelog.

Changelog

v3.5.2

v3.5.1

v3.5.0

v3.4.0

v3.3.0

v3.2.0

v3.1.0

v3.0.2

v3.0.1

v3.0.0

v2.3.1

v2.3.0

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=3.5.0&new-version=3.5.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- pkgs/http2/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http2/.github/workflows/test-package.yml b/pkgs/http2/.github/workflows/test-package.yml index 7bcd23232a..c21c3dde51 100644 --- a/pkgs/http2/.github/workflows/test-package.yml +++ b/pkgs/http2/.github/workflows/test-package.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} @@ -49,7 +49,7 @@ jobs: os: [ubuntu-latest] sdk: [2.17.0, dev] steps: - - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} From 2f44da144a31c36b4de9776e6617add5b5183794 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Wed, 17 May 2023 10:40:45 -0700 Subject: [PATCH 140/172] blast_repo fixes (dart-lang/http2#120) dependabot --- pkgs/http2/.github/dependabot.yaml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pkgs/http2/.github/dependabot.yaml b/pkgs/http2/.github/dependabot.yaml index 214481934b..439e796b48 100644 --- a/pkgs/http2/.github/dependabot.yaml +++ b/pkgs/http2/.github/dependabot.yaml @@ -2,7 +2,9 @@ version: 2 updates: - - package-ecosystem: "github-actions" - directory: "/" + - package-ecosystem: github-actions + directory: / schedule: - interval: "monthly" + interval: monthly + labels: + - autosubmit From b3c49b4ddcbcf9337f429667e1410331f4069554 Mon Sep 17 00:00:00 2001 From: Moritz Date: Tue, 6 Jun 2023 14:58:34 +0200 Subject: [PATCH 141/172] Send WINDOW_UPDATE frames for data on closed streams. (dart-lang/http2#122) * Send WINDOW_UPDATE frames for data on closed streams. * Changes as per review * Format tests --- pkgs/http2/CHANGELOG.md | 1 + .../src/flowcontrol/connection_queues.dart | 2 +- .../http2/lib/src/streams/stream_handler.dart | 30 ++- pkgs/http2/test/client_test.dart | 225 ++++++++++++------ pkgs/http2/test/server_test.dart | 2 + .../flowcontrol/connection_queues_test.dart | 2 +- 6 files changed, 177 insertions(+), 85 deletions(-) diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index d0a764647a..4af516b7a1 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -1,6 +1,7 @@ ## 2.0.2-dev - Require Dart `2.17.0`. +- Send `WINDOW_UPDATE` frames for the connection to account for data being sent on closed streams until the `RST_STREAM` has been processed. ## 2.0.1 diff --git a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart index b6247873ad..7382884b77 100644 --- a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart @@ -260,7 +260,7 @@ class ConnectionMessageQueueIn extends Object /// If a [DataFrame] will be ignored, this method will take the minimal /// action necessary. void processIgnoredDataFrame(DataFrame frame) { - _windowUpdateHandler.gotData(frame.bytes.length); + _windowUpdateHandler.dataProcessed(frame.bytes.length); } /// Processes an incoming [HeadersFrame] which is addressed to a specific diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index f32a1e5546..4018d3a70b 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -593,11 +593,26 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { } else if (frame is PushPromiseFrame) { throw ProtocolException('Cannot push on a non-existent stream ' '(stream ${frame.header.streamId} does not exist)'); + } else if (frame is DataFrame) { + // http/2 spec: + // However, after sending the RST_STREAM, the sending endpoint + // MUST be prepared to receive and process additional frames sent + // on the stream that might have been sent by the peer prior to + // the arrival of the RST_STREAM. + // and: + // A receiver that receives a flow-controlled frame MUST always + // account for its contribution against the connection + // flow-control window, unless the receiver treats this as a + // connection error (Section 5.4.1). This is necessary even if the + // frame is in error. The sender counts the frame toward the + // flow-control window, but if the receiver does not, the + // flow-control window at the sender and receiver can become + // different. + incomingQueue.processIgnoredDataFrame(frame); + // Still respond with an error, as the stream is closed. + throw _throwStreamClosedException(frame.header.streamId); } else { - throw StreamClosedException( - frame.header.streamId, - 'No open stream found and was not a headers frame opening a ' - 'new stream.'); + throw _throwStreamClosedException(frame.header.streamId); } } else { if (frame is HeadersFrame) { @@ -868,4 +883,11 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { var id = stream.id; return (isServer && id.isEven) || (!isServer && id.isOdd); } + + static Exception _throwStreamClosedException(int streamId) => + StreamClosedException( + streamId, + 'No open stream found and was not a headers frame opening a ' + 'new stream.', + ); } diff --git a/pkgs/http2/test/client_test.dart b/pkgs/http2/test/client_test.dart index d6a4df3e17..cf66a9ef97 100644 --- a/pkgs/http2/test/client_test.dart +++ b/pkgs/http2/test/client_test.dart @@ -28,9 +28,9 @@ void main() { Future serverFun() async { serverWriter.writeSettingsFrame([]); - expect(await nextFrame() is SettingsFrame, true); + expect(await nextFrame(), isA()); serverWriter.writeSettingsAckFrame(); - expect(await nextFrame() is SettingsFrame, true); + expect(await nextFrame(), isA()); settingsDone.complete(); @@ -72,9 +72,9 @@ void main() { Future serverFun() async { serverWriter.writeSettingsFrame([]); settingsDone.complete(); - expect(await nextFrame() is SettingsFrame, true); + expect(await nextFrame(), isA()); serverWriter.writeSettingsAckFrame(); - expect(await nextFrame() is SettingsFrame, true); + expect(await nextFrame(), isA()); // Make sure we get the graceful shutdown message. expect( @@ -112,8 +112,8 @@ void main() { final goawayReceived = Completer(); Future serverFun() async { serverWriter.writePingFrame(42); - expect(await nextFrame() is SettingsFrame, true); - expect(await nextFrame() is GoawayFrame, true); + expect(await nextFrame(), isA()); + expect(await nextFrame(), isA()); goawayReceived.complete(); expect(await serverReader.moveNext(), false); } @@ -156,8 +156,8 @@ void main() { var goawayReceived = Completer(); Future serverFun() async { serverWriter.writePingFrame(42); - expect(await nextFrame() is SettingsFrame, true); - expect(await nextFrame() is GoawayFrame, true); + expect(await nextFrame(), isA()); + expect(await nextFrame(), isA()); goawayReceived.complete(); expect(await serverReader.moveNext(), false); } @@ -191,10 +191,10 @@ void main() { StreamIterator serverReader, Future Function() nextFrame) async { Future serverFun() async { - expect(await nextFrame() is SettingsFrame, true); - expect(await nextFrame() is HeadersFrame, true); + expect(await nextFrame(), isA()); + expect(await nextFrame(), isA()); serverWriter.writePingFrame(42); - expect(await nextFrame() is GoawayFrame, true); + expect(await nextFrame(), isA()); expect(await serverReader.moveNext(), false); } @@ -224,21 +224,27 @@ void main() { Future serverFun() async { serverWriter.writeSettingsFrame([]); - expect(await nextFrame() is SettingsFrame, true); + expect(await nextFrame(), isA()); serverWriter.writeSettingsAckFrame(); - expect(await nextFrame() is SettingsFrame, true); + expect(await nextFrame(), isA()); handshakeCompleter.complete(); var headers = await nextFrame() as HeadersFrame; - var finFrame = await nextFrame() as DataFrame; - expect(finFrame.hasEndStreamFlag, true); + expect( + await nextFrame(), + isA().having( + (p0) => p0.hasEndStreamFlag, 'Last data frame', true)); // Write a data frame for a non-existent stream. var invalidStreamId = headers.header.streamId + 2; serverWriter.writeDataFrame(invalidStreamId, [42]); // Make sure the client sends a [RstStreamFrame] frame. + expect( + await nextFrame(), + isA() + .having((p0) => p0.header.streamId, 'Connection update', 0)); expect( await nextFrame(), isA() @@ -252,7 +258,7 @@ void main() { endStream: true); // Wait for the client finish. - expect(await nextFrame() is GoawayFrame, true); + expect(await nextFrame(), isA()); expect(await serverReader.moveNext(), false); await serverWriter.close(); } @@ -279,34 +285,54 @@ void main() { Future serverFun() async { serverWriter.writeSettingsFrame([]); - expect(await nextFrame() is SettingsFrame, true); + expect(await nextFrame(), isA()); serverWriter.writeSettingsAckFrame(); - expect(await nextFrame() is SettingsFrame, true); + expect(await nextFrame(), isA()); handshakeCompleter.complete(); var headers = await nextFrame() as HeadersFrame; - var finFrame = await nextFrame() as DataFrame; - expect(finFrame.hasEndStreamFlag, true); + expect( + await nextFrame(), + isA().having( + (p0) => p0.hasEndStreamFlag, 'Last data frame', true)); var streamId = headers.header.streamId; // Write a data frame for a non-existent stream. - serverWriter.writeDataFrame(streamId, [42], endStream: true); + var data1 = [42, 42]; + serverWriter.writeDataFrame(streamId, data1, endStream: true); // Write more data on the closed stream. - serverWriter.writeDataFrame(streamId, [42]); + var data2 = [42]; + serverWriter.writeDataFrame(streamId, data2); // NOTE: The order of the window update frame / rst frame just // happens to be like that ATM. - // Await stream/connection window update frame. - var win = await nextFrame() as WindowUpdateFrame; - expect(win.header.streamId, 1); - expect(win.windowSizeIncrement, 1); - win = await nextFrame() as WindowUpdateFrame; - expect(win.header.streamId, 0); - expect(win.windowSizeIncrement, 1); + // The two WindowUpdateFrames for the data1 DataFrame. + expect( + await nextFrame(), + isA() + .having((p0) => p0.header.streamId, 'Stream update', 1) + .having((p0) => p0.windowSizeIncrement, 'Windowsize', + data1.length)); + + expect( + await nextFrame(), + isA() + .having((p0) => p0.header.streamId, 'Connection update', 0) + .having((p0) => p0.windowSizeIncrement, 'Windowsize', + data1.length)); + + // The [WindowUpdateFrame] for the frame on the closed stream, which + // should still update the connection. + expect( + await nextFrame(), + isA() + .having((p0) => p0.header.streamId, 'Connection update', 0) + .having((p0) => p0.windowSizeIncrement, 'Windowsize', + data2.length)); // Make sure we get a [RstStreamFrame] frame. expect( @@ -318,7 +344,7 @@ void main() { (f) => f.header.streamId, 'header.streamId', streamId)); // Wait for the client finish. - expect(await nextFrame() is GoawayFrame, true); + expect(await nextFrame(), isA()); expect(await serverReader.moveNext(), false); await serverWriter.close(); } @@ -330,7 +356,11 @@ void main() { await stream.outgoingMessages.close(); var messages = await stream.incomingMessages.toList(); expect(messages, hasLength(1)); - expect((messages[0] as DataStreamMessage).bytes, [42]); + expect( + messages[0], + isA() + .having((p0) => p0.bytes, 'Same as `data1` above', [42, 42]), + ); await client.finish(); } @@ -349,16 +379,17 @@ void main() { Future serverFun() async { serverWriter.writeSettingsFrame([]); - expect(await nextFrame() is SettingsFrame, true); + expect(await nextFrame(), isA()); serverWriter.writeSettingsAckFrame(); - expect(await nextFrame() is SettingsFrame, true); + expect(await nextFrame(), isA()); handshakeCompleter.complete(); var headers = await nextFrame() as HeadersFrame; - var finFrame = await nextFrame() as DataFrame; - expect(finFrame.hasEndStreamFlag, true); - + expect( + await nextFrame(), + isA().having( + (p0) => p0.hasEndStreamFlag, 'Last data frame', true)); var streamId = headers.header.streamId; // Write a data frame. @@ -370,15 +401,21 @@ void main() { // happens to be like that ATM. // Await stream/connection window update frame. - var win = await nextFrame() as WindowUpdateFrame; - expect(win.header.streamId, 1); - expect(win.windowSizeIncrement, 1); - win = await nextFrame() as WindowUpdateFrame; - expect(win.header.streamId, 0); - expect(win.windowSizeIncrement, 1); - win = await nextFrame() as WindowUpdateFrame; - expect(win.header.streamId, 0); - expect(win.windowSizeIncrement, 1); + expect( + await nextFrame(), + isA() + .having((p0) => p0.header.streamId, 'Stream update', 1) + .having((p0) => p0.windowSizeIncrement, 'Windowsize', 1)); + expect( + await nextFrame(), + isA() + .having((p0) => p0.header.streamId, 'Connection update', 0) + .having((p0) => p0.windowSizeIncrement, 'Windowsize', 1)); + expect( + await nextFrame(), + isA() + .having((p0) => p0.header.streamId, 'Connection update', 0) + .having((p0) => p0.windowSizeIncrement, 'Windowsize', 1)); // Make sure we get a [RstStreamFrame] frame. expect( @@ -393,7 +430,7 @@ void main() { endDone.complete(); // Wait for the client finish. - expect(await nextFrame() is GoawayFrame, true); + expect(await nextFrame(), isA()); expect(await serverReader.moveNext(), false); await serverWriter.close(); } @@ -406,7 +443,12 @@ void main() { // first will cancel the stream var message = await stream.incomingMessages.first; - expect((message as DataStreamMessage).bytes, [42]); + expect( + message, + isA() + .having((p0) => p0.bytes, 'Same sent data above', [42]), + ); + cancelDone.complete(); await endDone.future; @@ -428,9 +470,9 @@ void main() { Future serverFun() async { serverWriter.writeSettingsFrame([]); - expect(await nextFrame() is SettingsFrame, true); + expect(await nextFrame(), isA()); serverWriter.writeSettingsAckFrame(); - expect(await nextFrame() is SettingsFrame, true); + expect(await nextFrame(), isA()); handshakeCompleter.complete(); @@ -449,19 +491,28 @@ void main() { // happens to be like that ATM. // Await stream/connection window update frame. - var win = await nextFrame() as WindowUpdateFrame; - expect(win.header.streamId, 1); - expect(win.windowSizeIncrement, 1); - win = await nextFrame() as WindowUpdateFrame; - expect(win.header.streamId, 0); - expect(win.windowSizeIncrement, 1); - win = await nextFrame() as WindowUpdateFrame; - expect(win.header.streamId, 0); - expect(win.windowSizeIncrement, 1); + + expect( + await nextFrame(), + isA() + .having((p0) => p0.header.streamId, 'Stream update', 1) + .having((p0) => p0.windowSizeIncrement, 'Windowsize', 1)); + expect( + await nextFrame(), + isA() + .having((p0) => p0.header.streamId, 'Connection update', 0) + .having((p0) => p0.windowSizeIncrement, 'Windowsize', 1)); + expect( + await nextFrame(), + isA() + .having((p0) => p0.header.streamId, 'Connection update', 0) + .having((p0) => p0.windowSizeIncrement, 'Windowsize', 1)); await clientDone.future; - var finFrame = await nextFrame() as DataFrame; - expect(finFrame.hasEndStreamFlag, true); + expect( + await nextFrame(), + isA().having( + (p0) => p0.hasEndStreamFlag, 'Last data frame', true)); // Wait for the client finish. expect(await serverReader.moveNext(), false); @@ -475,7 +526,12 @@ void main() { // first will cancel the stream var message = await stream.incomingMessages.first; - expect((message as DataStreamMessage).bytes, [42]); + expect( + message, + isA() + .having((p0) => p0.bytes, 'Same sent data above', [42]), + ); + cancelDone.complete(); await endDone.future; @@ -498,15 +554,17 @@ void main() { Future serverFun() async { serverWriter.writeSettingsFrame([]); - expect(await nextFrame() is SettingsFrame, true); + expect(await nextFrame(), isA()); serverWriter.writeSettingsAckFrame(); - expect(await nextFrame() is SettingsFrame, true); + expect(await nextFrame(), isA()); handshakeCompleter.complete(); var headers = await nextFrame() as HeadersFrame; - var finFrame = await nextFrame() as DataFrame; - expect(finFrame.hasEndStreamFlag, true); + expect( + await nextFrame(), + isA().having( + (p0) => p0.hasEndStreamFlag, 'Last data frame', true)); var streamId = headers.header.streamId; @@ -534,8 +592,13 @@ void main() { await stream.outgoingMessages.close(); var messages = await stream.incomingMessages.toList(); expect(messages, hasLength(1)); - expect((messages[0] as HeadersStreamMessage).headers.first, - isHeader('a', 'b')); + + expect( + messages[0], + isA().having((p0) => p0.headers.first, + 'Same sent headers above', isHeader('a', 'b')), + ); + await client.finish(); } @@ -551,9 +614,9 @@ void main() { Future serverFun() async { serverWriter.writeSettingsFrame([]); - expect(await nextFrame() is SettingsFrame, true); + expect(await nextFrame(), isA()); serverWriter.writeSettingsAckFrame(); - expect(await nextFrame() is SettingsFrame, true); + expect(await nextFrame(), isA()); handshakeCompleter.complete(); @@ -602,9 +665,9 @@ void main() { Future serverFun() async { serverWriter.writeSettingsFrame([]); - expect(await nextFrame() is SettingsFrame, true); + expect(await nextFrame(), isA()); serverWriter.writeSettingsAckFrame(); - expect(await nextFrame() is SettingsFrame, true); + expect(await nextFrame(), isA()); handshakeCompleter.complete(); @@ -661,9 +724,9 @@ void main() { var decoder = HPackDecoder(); serverWriter.writeSettingsFrame([]); - expect(await nextFrame() is SettingsFrame, true); + expect(await nextFrame(), isA()); serverWriter.writeSettingsAckFrame(); - expect(await nextFrame() is SettingsFrame, true); + expect(await nextFrame(), isA()); settingsDone.complete(); @@ -677,12 +740,16 @@ void main() { headersDone.complete(); // Make sure we got the stream reset. - var frame2 = await nextFrame() as RstStreamFrame; - expect(frame2.errorCode, ErrorCode.CANCEL); + expect( + await nextFrame(), + isA().having( + (p0) => p0.errorCode, 'Stream reset', ErrorCode.CANCEL)); // Make sure we get the graceful shutdown message. - var frame3 = await nextFrame() as GoawayFrame; - expect(frame3.errorCode, ErrorCode.NO_ERROR); + expect( + await nextFrame(), + isA().having( + (p0) => p0.errorCode, 'Stream reset', ErrorCode.NO_ERROR)); // Make sure the client ended the connection. expect(await serverReader.moveNext(), false); @@ -720,9 +787,9 @@ void main() { var decoder = HPackDecoder(); serverWriter.writeSettingsFrame([]); - expect(await nextFrame() is SettingsFrame, true); + expect(await nextFrame(), isA()); serverWriter.writeSettingsAckFrame(); - expect(await nextFrame() is SettingsFrame, true); + expect(await nextFrame(), isA()); settingsDone.complete(); diff --git a/pkgs/http2/test/server_test.dart b/pkgs/http2/test/server_test.dart index dffc84529b..1af51c929a 100644 --- a/pkgs/http2/test/server_test.dart +++ b/pkgs/http2/test/server_test.dart @@ -92,6 +92,8 @@ void main() { 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() diff --git a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart index a1d2755baa..1cde81be6a 100644 --- a/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart +++ b/pkgs/http2/test/src/flowcontrol/connection_queues_test.dart @@ -155,7 +155,7 @@ void main() { var header = FrameHeader(0, 0, 0, STREAM_ID); queue.processIgnoredDataFrame(DataFrame(header, 0, bytes)); expect(queue.pendingMessages, 0); - verify(windowMock.gotData(bytes.length)).called(1); + verify(windowMock.dataProcessed(bytes.length)).called(1); verifyNoMoreInteractions(windowMock); }); }); From fa77acfe64c4d506e3ce8678047bfa49c7b619a3 Mon Sep 17 00:00:00 2001 From: Moritz Date: Wed, 7 Jun 2023 09:13:00 +0200 Subject: [PATCH 142/172] Switch to mixins for Dart 3 (dart-lang/http2#123) * Switch to mixins for Dart 3 * Switch to 3.0.0 for testing * Ready to publish --- pkgs/http2/.github/workflows/test-package.yml | 2 +- pkgs/http2/CHANGELOG.md | 3 ++- pkgs/http2/lib/src/error_handler.dart | 6 +++--- pkgs/http2/pubspec.yaml | 4 ++-- pkgs/http2/test/src/flowcontrol/mocks.mocks.dart | 4 +++- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/pkgs/http2/.github/workflows/test-package.yml b/pkgs/http2/.github/workflows/test-package.yml index c21c3dde51..1f82430891 100644 --- a/pkgs/http2/.github/workflows/test-package.yml +++ b/pkgs/http2/.github/workflows/test-package.yml @@ -47,7 +47,7 @@ jobs: matrix: # Add macos-latest and/or windows-latest if relevant for this package. os: [ubuntu-latest] - sdk: [2.17.0, dev] + sdk: [3.0.0, dev] steps: - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index 4af516b7a1..618f83d0fe 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -1,5 +1,6 @@ -## 2.0.2-dev +## 2.1.0 +- Require Dart `3.0.0` - Require Dart `2.17.0`. - Send `WINDOW_UPDATE` frames for the connection to account for data being sent on closed streams until the `RST_STREAM` has been processed. diff --git a/pkgs/http2/lib/src/error_handler.dart b/pkgs/http2/lib/src/error_handler.dart index d599dc208e..f7eed21b6a 100644 --- a/pkgs/http2/lib/src/error_handler.dart +++ b/pkgs/http2/lib/src/error_handler.dart @@ -7,7 +7,7 @@ import 'dart:async'; import 'sync_errors.dart'; /// Used by classes which may be terminated from the outside. -class TerminatableMixin { +mixin TerminatableMixin { bool _terminated = false; /// Terminates this stream message queue. Further operations on it will fail. @@ -40,7 +40,7 @@ class TerminatableMixin { } /// Used by classes which may be cancelled. -class CancellableMixin { +mixin CancellableMixin { bool _cancelled = false; final _cancelCompleter = Completer.sync(); @@ -58,7 +58,7 @@ class CancellableMixin { } /// Used by classes which may be closed. -class ClosableMixin { +mixin ClosableMixin { bool _closing = false; final Completer _completer = Completer(); diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index eb9187aead..ac436eda34 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -1,10 +1,10 @@ name: http2 -version: 2.0.2-dev +version: 2.1.0 description: A HTTP/2 implementation in Dart. repository: https://github.com/dart-lang/http2 environment: - sdk: '>=2.17.0 <3.0.0' + sdk: '>=3.0.0 <4.0.0' dev_dependencies: build_runner: ^2.3.0 diff --git a/pkgs/http2/test/src/flowcontrol/mocks.mocks.dart b/pkgs/http2/test/src/flowcontrol/mocks.mocks.dart index b15faae1ae..0f09888cb6 100644 --- a/pkgs/http2/test/src/flowcontrol/mocks.mocks.dart +++ b/pkgs/http2/test/src/flowcontrol/mocks.mocks.dart @@ -1,7 +1,9 @@ -// Mocks generated by Mockito 5.3.2 from annotations +// Mocks generated by Mockito 5.4.1 from annotations // in http2/test/src/flowcontrol/mocks.dart. // Do not manually edit this file. +// @dart=2.19 + // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i5; From 2c81ef6bfcd310c25f59b7db8e2a30dfca108452 Mon Sep 17 00:00:00 2001 From: Moritz Date: Wed, 7 Jun 2023 10:44:12 +0200 Subject: [PATCH 143/172] Transform header names to lowercase (dart-lang/http2#125) --- pkgs/http2/CHANGELOG.md | 4 ++++ pkgs/http2/lib/src/hpack/hpack.dart | 5 ++++- pkgs/http2/pubspec.yaml | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index 618f83d0fe..70a9352bf5 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.0-dev + +- Transform headers to lowercase. + ## 2.1.0 - Require Dart `3.0.0` diff --git a/pkgs/http2/lib/src/hpack/hpack.dart b/pkgs/http2/lib/src/hpack/hpack.dart index e59cd37df0..f34e0431c3 100644 --- a/pkgs/http2/lib/src/hpack/hpack.dart +++ b/pkgs/http2/lib/src/hpack/hpack.dart @@ -49,7 +49,10 @@ class Header { Header(this.name, this.value, {this.neverIndexed = false}); factory Header.ascii(String name, String value) { - return Header(ascii.encode(name), ascii.encode(value)); + // Specs: `However, header field names MUST be converted to lowercase prior + // to their encoding in HTTP/2. A request or response containing uppercase + // header field names MUST be treated as malformed (Section 8.1.2.6).` + return Header(ascii.encode(name.toLowerCase()), ascii.encode(value)); } } diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index ac436eda34..05f88e220a 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -1,5 +1,5 @@ name: http2 -version: 2.1.0 +version: 2.1.0-dev description: A HTTP/2 implementation in Dart. repository: https://github.com/dart-lang/http2 From 2b8cfccfb7c5cc7a4e735fe2ad94db2ce9c0fce4 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Wed, 7 Jun 2023 09:00:03 -0700 Subject: [PATCH 144/172] Bump version (dart-lang/http2#126) Avoid multiple changelog entries with the same version. Bump the patch version for a bug fix. Switch from `-dev` to `-wip` to match updated patterns for the Dart team. --- pkgs/http2/CHANGELOG.md | 2 +- pkgs/http2/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index 70a9352bf5..b5376717bf 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -1,4 +1,4 @@ -## 2.1.0-dev +## 2.1.1-wip - Transform headers to lowercase. diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index 05f88e220a..a7c7e01143 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -1,5 +1,5 @@ name: http2 -version: 2.1.0-dev +version: 2.1.1-wip description: A HTTP/2 implementation in Dart. repository: https://github.com/dart-lang/http2 From f4482d9c689a600ce272312e229843dcc96c10a8 Mon Sep 17 00:00:00 2001 From: Moritz Date: Mon, 19 Jun 2023 14:56:18 -0400 Subject: [PATCH 145/172] Expose pings to connection (dart-lang/http2#127) * Expose pings to connection * Update docs * Add changelog entry * Expose trigger for frame received by connection * Expose error codes to terminate * Add ping id to stream * Fix test --- pkgs/http2/CHANGELOG.md | 1 + pkgs/http2/lib/src/connection.dart | 23 ++++++++++++++++--- pkgs/http2/lib/src/ping/ping_handler.dart | 4 +++- pkgs/http2/lib/transport.dart | 11 ++++++++- .../test/src/ping/ping_handler_test.dart | 12 ++++++++++ 5 files changed, 46 insertions(+), 5 deletions(-) diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index b5376717bf..830b80e48f 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -1,6 +1,7 @@ ## 2.1.1-wip - Transform headers to lowercase. +- Expose pings to connection to enable the KEEPALIVE feature for gRPC. ## 2.1.0 diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index e27b90e676..29830eaa89 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -99,6 +99,10 @@ abstract class Connection { final Completer _onInitialPeerSettingsReceived = Completer(); + final StreamController _pingReceived = StreamController(); + + final StreamController _receivedFrame = StreamController(); + /// Future which completes when the first SETTINGS frame is received from /// the peer. Future get onInitialPeerSettingsReceived => @@ -179,7 +183,7 @@ abstract class Connection { // Setup handlers. _settingsHandler = SettingsHandler(_hpackContext.encoder, _frameWriter, acknowledgedSettings, peerSettings); - _pingHandler = PingHandler(_frameWriter); + _pingHandler = PingHandler(_frameWriter, _pingReceived.sink); var settings = _decodeSettings(settingsObject); @@ -284,8 +288,8 @@ abstract class Connection { } /// Terminates this connection forcefully. - Future terminate() { - return _terminate(ErrorCode.NO_ERROR); + Future terminate([int? errorCode]) { + return _terminate(errorCode ?? ErrorCode.NO_ERROR); } void _activeStateHandler(bool isActive) => @@ -340,6 +344,7 @@ abstract class Connection { frame.decodedHeaders = _hpackContext.decoder.decode(frame.headerBlockFragment); } + _receivedFrame.add(null); // Handle the frame as either a connection or a stream frame. if (frame.header.streamId == 0) { @@ -473,6 +478,12 @@ class ClientConnection extends Connection implements ClientTransportConnection { } return hStream; } + + @override + Stream get onPingReceived => _pingReceived.stream; + + @override + Stream get onFrameReceived => _receivedFrame.stream; } class ServerConnection extends Connection implements ServerTransportConnection { @@ -489,4 +500,10 @@ class ServerConnection extends Connection implements ServerTransportConnection { @override Stream get incomingStreams => _streams.incomingStreams.cast(); + + @override + Stream get onPingReceived => _pingReceived.stream; + + @override + Stream get onFrameReceived => _receivedFrame.stream; } diff --git a/pkgs/http2/lib/src/ping/ping_handler.dart b/pkgs/http2/lib/src/ping/ping_handler.dart index 20ce4871cb..5ab3ffbd1e 100644 --- a/pkgs/http2/lib/src/ping/ping_handler.dart +++ b/pkgs/http2/lib/src/ping/ping_handler.dart @@ -16,9 +16,10 @@ import '../sync_errors.dart'; class PingHandler extends Object with TerminatableMixin { final FrameWriter _frameWriter; final Map _remainingPings = {}; + final Sink? pingReceived; int _nextId = 1; - PingHandler(this._frameWriter); + PingHandler(this._frameWriter, [this.pingReceived]); @override void onTerminated(Object? error) { @@ -36,6 +37,7 @@ class PingHandler extends Object with TerminatableMixin { } if (!frame.hasAckFlag) { + pingReceived?.add(frame.opaqueData); _frameWriter.writePingFrame(frame.opaqueData, ack: true); } else { var c = _remainingPings.remove(frame.opaqueData); diff --git a/pkgs/http2/lib/transport.dart b/pkgs/http2/lib/transport.dart index 6423f9371a..af37cf614e 100644 --- a/pkgs/http2/lib/transport.dart +++ b/pkgs/http2/lib/transport.dart @@ -9,6 +9,7 @@ import 'src/connection.dart'; import 'src/hpack/hpack.dart' show Header; export 'src/hpack/hpack.dart' show Header; +export 'src/frames/frames.dart' show ErrorCode; typedef ActiveStateHandler = void Function(bool isActive); @@ -64,13 +65,21 @@ abstract class TransportConnection { /// the peer. Future get onInitialPeerSettingsReceived; + /// Stream which emits an event with the ping id every time a ping is received + /// on this connection. + Stream get onPingReceived; + + /// Stream which emits an event every time a ping is received on this + /// connection. + Stream get onFrameReceived; + /// Finish this connection. /// /// No new streams will be accepted or can be created. Future finish(); /// Terminates this connection forcefully. - Future terminate(); + Future terminate([int? errorCode]); } abstract class ClientTransportConnection extends TransportConnection { diff --git a/pkgs/http2/test/src/ping/ping_handler_test.dart b/pkgs/http2/test/src/ping/ping_handler_test.dart index f466f4545b..563f9cefeb 100644 --- a/pkgs/http2/test/src/ping/ping_handler_test.dart +++ b/pkgs/http2/test/src/ping/ping_handler_test.dart @@ -93,6 +93,18 @@ void main() { throwsA(isProtocolException)); verifyZeroInteractions(writer); }); + + test('receiving-ping-calls-stream', () async { + var writer = FrameWriterMock(); + var streamController = StreamController(); + var pingHandler = PingHandler(writer, streamController.sink); + + var header = FrameHeader(8, FrameType.PING, 0, 0); + pingHandler.processPingFrame(PingFrame(header, 1)); + var header2 = FrameHeader(8, FrameType.PING, 0, 0); + pingHandler.processPingFrame(PingFrame(header2, 2)); + await expectLater(streamController.stream, emitsInOrder([1, 2])); + }); }); } From 10b3eedadbfaab639638123b4dc1d83e038c645a Mon Sep 17 00:00:00 2001 From: Moritz Date: Wed, 21 Jun 2023 11:00:26 -0400 Subject: [PATCH 146/172] Prepare release to 2.2.0 (dart-lang/http2#128) * Prepare release to 2.1.1 * Actually increase minor --- pkgs/http2/CHANGELOG.md | 2 +- pkgs/http2/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index 830b80e48f..baa561d874 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -1,4 +1,4 @@ -## 2.1.1-wip +## 2.2.0 - Transform headers to lowercase. - Expose pings to connection to enable the KEEPALIVE feature for gRPC. diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index a7c7e01143..5eb21d1dfe 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -1,5 +1,5 @@ name: http2 -version: 2.1.1-wip +version: 2.2.0 description: A HTTP/2 implementation in Dart. repository: https://github.com/dart-lang/http2 From 996a3d80fc307439b7d99dfb684bb9817b913623 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jul 2023 21:32:20 +0000 Subject: [PATCH 147/172] Bump actions/checkout from 3.5.2 to 3.5.3 (dart-lang/http2#129) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [actions/checkout](https://github.com/actions/checkout) from 3.5.2 to 3.5.3.
Release notes

Sourced from actions/checkout's releases.

v3.5.3

What's Changed

New Contributors

Full Changelog: https://github.com/actions/checkout/compare/v3...v3.5.3

Changelog

Sourced from actions/checkout's changelog.

Changelog

v3.5.3

v3.5.2

v3.5.1

v3.5.0

v3.4.0

v3.3.0

v3.2.0

v3.1.0

v3.0.2

v3.0.1

v3.0.0

v2.3.1

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=3.5.2&new-version=3.5.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- pkgs/http2/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http2/.github/workflows/test-package.yml b/pkgs/http2/.github/workflows/test-package.yml index 1f82430891..325fac8f39 100644 --- a/pkgs/http2/.github/workflows/test-package.yml +++ b/pkgs/http2/.github/workflows/test-package.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} @@ -49,7 +49,7 @@ jobs: os: [ubuntu-latest] sdk: [3.0.0, dev] steps: - - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} From 182b198c97893eac44cb13250f626d721ff12d65 Mon Sep 17 00:00:00 2001 From: Moritz Date: Fri, 4 Aug 2023 08:16:10 +0200 Subject: [PATCH 148/172] Update README.md (dart-lang/http2#131) Apply Dart style to code in README.md --- pkgs/http2/README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pkgs/http2/README.md b/pkgs/http2/README.md index e776da2a89..36a8aaa042 100644 --- a/pkgs/http2/README.md +++ b/pkgs/http2/README.md @@ -15,10 +15,10 @@ import 'dart:io'; import 'package:http2/http2.dart'; -main() async { - var uri = Uri.parse('https://www.google.com/'); +Future main() async { + final uri = Uri.parse('https://www.google.com/'); - var transport = new ClientTransportConnection.viaSocket( + final transport = ClientTransportConnection.viaSocket( await SecureSocket.connect( uri.host, uri.port, @@ -26,12 +26,12 @@ main() async { ), ); - var stream = transport.makeRequest( + final stream = transport.makeRequest( [ - new Header.ascii(':method', 'GET'), - new Header.ascii(':path', uri.path), - new Header.ascii(':scheme', uri.scheme), - new Header.ascii(':authority', uri.host), + Header.ascii(':method', 'GET'), + Header.ascii(':path', uri.path), + Header.ascii(':scheme', uri.scheme), + Header.ascii(':authority', uri.host), ], endStream: true, ); @@ -39,8 +39,8 @@ main() async { await for (var message in stream.incomingMessages) { if (message is HeadersStreamMessage) { for (var header in message.headers) { - var name = utf8.decode(header.name); - var value = utf8.decode(header.value); + final name = utf8.decode(header.name); + final value = utf8.decode(header.value); print('Header: $name: $value'); } } else if (message is DataStreamMessage) { From 7810c64fdb308629dba6319c92a580af6b233253 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Sep 2023 21:25:01 +0000 Subject: [PATCH 149/172] Bump actions/checkout from 3.5.3 to 3.6.0 (dart-lang/http2#132) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [actions/checkout](https://github.com/actions/checkout) from 3.5.3 to 3.6.0.
Release notes

Sourced from actions/checkout's releases.

v3.6.0

What's Changed

New Contributors

Full Changelog: https://github.com/actions/checkout/compare/v3.5.3...v3.6.0

Changelog

Sourced from actions/checkout's changelog.

Changelog

v3.6.0

v3.5.3

v3.5.2

v3.5.1

v3.5.0

v3.4.0

v3.3.0

v3.2.0

v3.1.0

v3.0.2

v3.0.1

v3.0.0

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=3.5.3&new-version=3.6.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- pkgs/http2/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http2/.github/workflows/test-package.yml b/pkgs/http2/.github/workflows/test-package.yml index 325fac8f39..6e4f6ec135 100644 --- a/pkgs/http2/.github/workflows/test-package.yml +++ b/pkgs/http2/.github/workflows/test-package.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 + - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} @@ -49,7 +49,7 @@ jobs: os: [ubuntu-latest] sdk: [3.0.0, dev] steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 + - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} From 0b91b76f50c25b075ba65b44c07df7f8cfb8386f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Oct 2023 21:24:21 +0000 Subject: [PATCH 150/172] Bump dart-lang/setup-dart from 1.5.0 to 1.5.1 (dart-lang/http2#133) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [dart-lang/setup-dart](https://github.com/dart-lang/setup-dart) from 1.5.0 to 1.5.1.
Release notes

Sourced from dart-lang/setup-dart's releases.

v1.5.1

  • No longer test the setup-dart action on pre-2.12 SDKs.
  • Upgrade JS interop code to use extension types (the new name for inline classes).
  • The upcoming rename of the be channel to main is now supported with forward compatibility that switches when the rename happens.
Changelog

Sourced from dart-lang/setup-dart's changelog.

v1.6.0

  • Enable provisioning of the latest Dart SDK patch release by specifying just the major and minor version (e.g. 3.2).

v1.5.1

  • No longer test the setup-dart action on pre-2.12 SDKs.
  • Upgrade JS interop code to use extension types (the new name for inline classes).
  • The upcoming rename of the be channel to main is now supported with forward compatibility that switches when the rename happens.

v1.5.0

  • Re-wrote the implementation of the action into Dart.
  • Auto-detect the platform architecture (x64, ia32, arm, arm64).
  • Improved the caching and download resilience of the sdk.
  • Added a new action output: dart-version - the installed version of the sdk.

v1.4.0

  • Automatically create OIDC token for pub.dev.
  • Add a reusable workflow for publishing.

v1.3.0

  • The install location of the Dart SDK is now available in an environment variable, DART_HOME (dart-lang/http2#43).
  • Fixed an issue where cached downloads could lead to unzip issues on self-hosted runners (dart-lang/http2#35).

v1.2.0

  • Fixed a path issue impacting git dependencies on Windows.

v1.1.0

  • Added a flavor option setup.sh to allow downloading unpublished builds.

v1.0.0

  • Promoted to 1.0 stable.

v0.5

  • Fixed a Windows pub global activate path issue.

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=dart-lang/setup-dart&package-manager=github_actions&previous-version=1.5.0&new-version=1.5.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- pkgs/http2/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http2/.github/workflows/test-package.yml b/pkgs/http2/.github/workflows/test-package.yml index 6e4f6ec135..f476e35867 100644 --- a/pkgs/http2/.github/workflows/test-package.yml +++ b/pkgs/http2/.github/workflows/test-package.yml @@ -23,7 +23,7 @@ jobs: sdk: [dev] steps: - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 - - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f + - uses: dart-lang/setup-dart@8a4b97ea2017cc079571daec46542f76189836b1 with: sdk: ${{ matrix.sdk }} - id: install @@ -50,7 +50,7 @@ jobs: sdk: [3.0.0, dev] steps: - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 - - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f + - uses: dart-lang/setup-dart@8a4b97ea2017cc079571daec46542f76189836b1 with: sdk: ${{ matrix.sdk }} - id: install From 1dd3c12b88d18fe24ebe78e5af2c15f5f7557f18 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Oct 2023 18:25:16 +0000 Subject: [PATCH 151/172] Bump actions/checkout from 3.6.0 to 4.1.0 (dart-lang/http2#134) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [actions/checkout](https://github.com/actions/checkout) from 3.6.0 to 4.1.0.
Release notes

Sourced from actions/checkout's releases.

v4.1.0

What's Changed

New Contributors

Full Changelog: https://github.com/actions/checkout/compare/v4.0.0...v4.1.0

v4.0.0

What's Changed

New Contributors

Full Changelog: https://github.com/actions/checkout/compare/v3...v4.0.0

Changelog

Sourced from actions/checkout's changelog.

Changelog

v4.1.0

v4.0.0

v3.6.0

v3.5.3

v3.5.2

v3.5.1

v3.5.0

v3.4.0

v3.3.0

v3.2.0

v3.1.0

v3.0.2

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=3.6.0&new-version=4.1.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- pkgs/http2/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http2/.github/workflows/test-package.yml b/pkgs/http2/.github/workflows/test-package.yml index f476e35867..8f5c3b2903 100644 --- a/pkgs/http2/.github/workflows/test-package.yml +++ b/pkgs/http2/.github/workflows/test-package.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 - uses: dart-lang/setup-dart@8a4b97ea2017cc079571daec46542f76189836b1 with: sdk: ${{ matrix.sdk }} @@ -49,7 +49,7 @@ jobs: os: [ubuntu-latest] sdk: [3.0.0, dev] steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 - uses: dart-lang/setup-dart@8a4b97ea2017cc079571daec46542f76189836b1 with: sdk: ${{ matrix.sdk }} From ededaf7b6800d8ff7e07aa8f41c8a97ac34d658f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Nov 2023 21:37:39 +0000 Subject: [PATCH 152/172] Bump actions/checkout from 4.1.0 to 4.1.1 (dart-lang/http2#136) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.0 to 4.1.1.
Release notes

Sourced from actions/checkout's releases.

v4.1.1

What's Changed

New Contributors

Full Changelog: https://github.com/actions/checkout/compare/v4.1.0...v4.1.1

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=4.1.0&new-version=4.1.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- pkgs/http2/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http2/.github/workflows/test-package.yml b/pkgs/http2/.github/workflows/test-package.yml index 8f5c3b2903..9ba65f0e69 100644 --- a/pkgs/http2/.github/workflows/test-package.yml +++ b/pkgs/http2/.github/workflows/test-package.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - uses: dart-lang/setup-dart@8a4b97ea2017cc079571daec46542f76189836b1 with: sdk: ${{ matrix.sdk }} @@ -49,7 +49,7 @@ jobs: os: [ubuntu-latest] sdk: [3.0.0, dev] steps: - - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - uses: dart-lang/setup-dart@8a4b97ea2017cc079571daec46542f76189836b1 with: sdk: ${{ matrix.sdk }} From b9f1d5d158074ef55c3a4cf279c7d8f3a34038ed Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Nov 2023 21:53:05 +0000 Subject: [PATCH 153/172] Bump dart-lang/setup-dart from 1.5.1 to 1.6.0 (dart-lang/http2#135) Bumps [dart-lang/setup-dart](https://github.com/dart-lang/setup-dart) from 1.5.1 to 1.6.0.
Release notes

Sourced from dart-lang/setup-dart's releases.

v1.6.0

  • Enable provisioning of the latest Dart SDK patch release by specifying just the major and minor version (e.g. 3.2).
Changelog

Sourced from dart-lang/setup-dart's changelog.

v1.6.0

  • Enable provisioning of the latest Dart SDK patch release by specifying just the major and minor version (e.g. 3.2).

v1.5.1

  • No longer test the setup-dart action on pre-2.12 SDKs.
  • Upgrade JS interop code to use extension types (the new name for inline classes).
  • The upcoming rename of the be channel to main is now supported with forward compatibility that switches when the rename happens.

v1.5.0

  • Re-wrote the implementation of the action into Dart.
  • Auto-detect the platform architecture (x64, ia32, arm, arm64).
  • Improved the caching and download resilience of the sdk.
  • Added a new action output: dart-version - the installed version of the sdk.

v1.4.0

  • Automatically create OIDC token for pub.dev.
  • Add a reusable workflow for publishing.

v1.3.0

  • The install location of the Dart SDK is now available in an environment variable, DART_HOME (dart-lang/http2#43).
  • Fixed an issue where cached downloads could lead to unzip issues on self-hosted runners (dart-lang/http2#35).

v1.2.0

  • Fixed a path issue impacting git dependencies on Windows.

v1.1.0

  • Added a flavor option setup.sh to allow downloading unpublished builds.

v1.0.0

  • Promoted to 1.0 stable.

v0.5

  • Fixed a Windows pub global activate path issue.

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=dart-lang/setup-dart&package-manager=github_actions&previous-version=1.5.1&new-version=1.6.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- pkgs/http2/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http2/.github/workflows/test-package.yml b/pkgs/http2/.github/workflows/test-package.yml index 9ba65f0e69..38ced1a4fd 100644 --- a/pkgs/http2/.github/workflows/test-package.yml +++ b/pkgs/http2/.github/workflows/test-package.yml @@ -23,7 +23,7 @@ jobs: sdk: [dev] steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - - uses: dart-lang/setup-dart@8a4b97ea2017cc079571daec46542f76189836b1 + - uses: dart-lang/setup-dart@b64355ae6ca0b5d484f0106a033dd1388965d06d with: sdk: ${{ matrix.sdk }} - id: install @@ -50,7 +50,7 @@ jobs: sdk: [3.0.0, dev] steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - - uses: dart-lang/setup-dart@8a4b97ea2017cc079571daec46542f76189836b1 + - uses: dart-lang/setup-dart@b64355ae6ca0b5d484f0106a033dd1388965d06d with: sdk: ${{ matrix.sdk }} - id: install From 1f2c62b392f85058e8430b598a536d703c79027a Mon Sep 17 00:00:00 2001 From: Moritz Date: Mon, 8 Jan 2024 14:00:35 +0100 Subject: [PATCH 154/172] Add auto publish workflow (dart-lang/http2#138) * Add auto publish workflow * Use SHA * Trigger on branches * Fix * Use main branch * Fix typo --- pkgs/http2/.github/workflows/publish.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 pkgs/http2/.github/workflows/publish.yml diff --git a/pkgs/http2/.github/workflows/publish.yml b/pkgs/http2/.github/workflows/publish.yml new file mode 100644 index 0000000000..fbeb3d76b7 --- /dev/null +++ b/pkgs/http2/.github/workflows/publish.yml @@ -0,0 +1,10 @@ +name: Publish +on: + pull_request: + branches: [ master ] + types: [opened, synchronize, reopened, labeled, unlabeled] + push: + tags: [ 'v[0-9]+.[0-9]+.[0-9]+*' ] +jobs: + publish: + uses: dart-lang/ecosystem/.github/workflows/publish.yaml@main From dc24e032bf351f617ae92562102be10ef8da6556 Mon Sep 17 00:00:00 2001 From: Moritz Date: Mon, 8 Jan 2024 14:13:09 +0100 Subject: [PATCH 155/172] Switch to callbacks from streams for ping handling (dart-lang/http2#137) * Switch to callbacks from streams for ping handling * Debugging * Revert "Debugging" This reverts commit 910c1e1b0527f508df68cb596cf88c014028952d. * Revert "Switch to callbacks from streams for ping handling" This reverts commit c4f4730940961d08b31bd6e60a1ba2d620d2c6d3. * Fix by using `hasListener` * Rev version * Rerun checks --- pkgs/http2/CHANGELOG.md | 4 ++ pkgs/http2/lib/src/connection.dart | 12 +++--- pkgs/http2/lib/src/ping/ping_handler.dart | 16 +++++--- pkgs/http2/pubspec.yaml | 2 +- .../test/src/ping/ping_handler_test.dart | 41 +++++++++++++------ 5 files changed, 51 insertions(+), 24 deletions(-) diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index baa561d874..0c25cce5cb 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.3.0 + +- Only send updates on frames and pings being received when there are listeners, as to not fill up memory. + ## 2.2.0 - Transform headers to lowercase. diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index 29830eaa89..35a851b5da 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -101,7 +101,7 @@ abstract class Connection { final StreamController _pingReceived = StreamController(); - final StreamController _receivedFrame = StreamController(); + final StreamController _frameReceived = StreamController(); /// Future which completes when the first SETTINGS frame is received from /// the peer. @@ -183,7 +183,7 @@ abstract class Connection { // Setup handlers. _settingsHandler = SettingsHandler(_hpackContext.encoder, _frameWriter, acknowledgedSettings, peerSettings); - _pingHandler = PingHandler(_frameWriter, _pingReceived.sink); + _pingHandler = PingHandler(_frameWriter, _pingReceived); var settings = _decodeSettings(settingsObject); @@ -344,7 +344,9 @@ abstract class Connection { frame.decodedHeaders = _hpackContext.decoder.decode(frame.headerBlockFragment); } - _receivedFrame.add(null); + if (_frameReceived.hasListener) { + _frameReceived.add(null); + } // Handle the frame as either a connection or a stream frame. if (frame.header.streamId == 0) { @@ -483,7 +485,7 @@ class ClientConnection extends Connection implements ClientTransportConnection { Stream get onPingReceived => _pingReceived.stream; @override - Stream get onFrameReceived => _receivedFrame.stream; + Stream get onFrameReceived => _frameReceived.stream; } class ServerConnection extends Connection implements ServerTransportConnection { @@ -505,5 +507,5 @@ class ServerConnection extends Connection implements ServerTransportConnection { Stream get onPingReceived => _pingReceived.stream; @override - Stream get onFrameReceived => _receivedFrame.stream; + Stream get onFrameReceived => _frameReceived.stream; } diff --git a/pkgs/http2/lib/src/ping/ping_handler.dart b/pkgs/http2/lib/src/ping/ping_handler.dart index 5ab3ffbd1e..49bd6cc04a 100644 --- a/pkgs/http2/lib/src/ping/ping_handler.dart +++ b/pkgs/http2/lib/src/ping/ping_handler.dart @@ -17,16 +17,20 @@ class PingHandler extends Object with TerminatableMixin { final FrameWriter _frameWriter; final Map _remainingPings = {}; final Sink? pingReceived; + final bool Function() isListeningToPings; int _nextId = 1; - PingHandler(this._frameWriter, [this.pingReceived]); + PingHandler(this._frameWriter, StreamController pingStream) + : pingReceived = pingStream.sink, + isListeningToPings = (() => pingStream.hasListener); @override void onTerminated(Object? error) { - var values = _remainingPings.values.toList(); + final remainingPings = _remainingPings.values.toList(); _remainingPings.clear(); - for (var value in values) { - value.completeError(error ?? 'Unspecified error'); + for (final ping in remainingPings) { + ping.completeError( + error ?? 'Remaining ping completed with unspecified error'); } } @@ -37,7 +41,9 @@ class PingHandler extends Object with TerminatableMixin { } if (!frame.hasAckFlag) { - pingReceived?.add(frame.opaqueData); + if (isListeningToPings()) { + pingReceived?.add(frame.opaqueData); + } _frameWriter.writePingFrame(frame.opaqueData, ack: true); } else { var c = _remainingPings.remove(frame.opaqueData); diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index 5eb21d1dfe..d8d21c852d 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -1,5 +1,5 @@ name: http2 -version: 2.2.0 +version: 2.3.0 description: A HTTP/2 implementation in Dart. repository: https://github.com/dart-lang/http2 diff --git a/pkgs/http2/test/src/ping/ping_handler_test.dart b/pkgs/http2/test/src/ping/ping_handler_test.dart index 563f9cefeb..ef5250d825 100644 --- a/pkgs/http2/test/src/ping/ping_handler_test.dart +++ b/pkgs/http2/test/src/ping/ping_handler_test.dart @@ -15,7 +15,7 @@ void main() { group('ping-handler', () { test('successful-ping', () async { var writer = FrameWriterMock(); - var pingHandler = PingHandler(writer); + var pingHandler = instantiateHandler(writer); var p1 = pingHandler.ping(); var p2 = pingHandler.ping(); @@ -37,7 +37,7 @@ void main() { test('successful-ack-to-remote-ping', () async { var writer = FrameWriterMock(); - var pingHandler = PingHandler(writer); + var pingHandler = instantiateHandler(writer); var header = FrameHeader(8, FrameType.PING, 0, 0); pingHandler.processPingFrame(PingFrame(header, 1)); @@ -53,7 +53,7 @@ void main() { test('ping-unknown-opaque-data', () async { var writer = FrameWriterMock(); - var pingHandler = PingHandler(writer); + var pingHandler = instantiateHandler(writer); var future = pingHandler.ping(); verify(writer.writePingFrame(1)).called(1); @@ -73,7 +73,7 @@ void main() { test('terminate-ping-handler', () async { var writer = FrameWriterMock(); - var pingHandler = PingHandler(writer); + var pingHandler = instantiateHandler(writer); pingHandler.terminate('hello world'); expect( @@ -86,7 +86,7 @@ void main() { test('ping-non-zero-stream-id', () async { var writer = FrameWriterMock(); - var pingHandler = PingHandler(writer); + var pingHandler = instantiateHandler(writer); var header = FrameHeader(8, FrameType.PING, PingFrame.FLAG_ACK, 1); expect(() => pingHandler.processPingFrame(PingFrame(header, 1)), @@ -95,17 +95,32 @@ void main() { }); test('receiving-ping-calls-stream', () async { - var writer = FrameWriterMock(); - var streamController = StreamController(); - var pingHandler = PingHandler(writer, streamController.sink); + final pings = []; - var header = FrameHeader(8, FrameType.PING, 0, 0); - pingHandler.processPingFrame(PingFrame(header, 1)); - var header2 = FrameHeader(8, FrameType.PING, 0, 0); - pingHandler.processPingFrame(PingFrame(header2, 2)); - await expectLater(streamController.stream, emitsInOrder([1, 2])); + final writer = FrameWriterMock(); + final pingStream = StreamController() + ..stream.listen((event) => pings.add(event)); + + PingHandler(writer, pingStream) + ..processPingFrame(PingFrame( + FrameHeader(8, FrameType.PING, 0, 0), + 1, + )) + ..processPingFrame(PingFrame( + FrameHeader(8, FrameType.PING, 0, 0), + 2, + )); + + await pingStream.close(); + + expect(pings, [1, 2]); }); }); } +PingHandler instantiateHandler(FrameWriterMock writer) { + StreamController controller = StreamController(); + return PingHandler(writer, controller); +} + class FrameWriterMock extends Mock implements FrameWriter {} From 816f6c59a473d23d0e9522b72fdb35ff28591c8c Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Thu, 25 Jan 2024 10:16:04 -0800 Subject: [PATCH 156/172] blast repo changes: auto-publish, github-actions, no-response (dart-lang/http2#139) --- pkgs/http2/.github/workflows/no-response.yml | 37 +++++++++++++++++++ pkgs/http2/.github/workflows/publish.yaml | 17 +++++++++ pkgs/http2/.github/workflows/publish.yml | 10 ----- pkgs/http2/.github/workflows/test-package.yml | 4 +- 4 files changed, 56 insertions(+), 12 deletions(-) create mode 100644 pkgs/http2/.github/workflows/no-response.yml create mode 100644 pkgs/http2/.github/workflows/publish.yaml delete mode 100644 pkgs/http2/.github/workflows/publish.yml diff --git a/pkgs/http2/.github/workflows/no-response.yml b/pkgs/http2/.github/workflows/no-response.yml new file mode 100644 index 0000000000..ab1ac49842 --- /dev/null +++ b/pkgs/http2/.github/workflows/no-response.yml @@ -0,0 +1,37 @@ +# A workflow to close issues where the author hasn't responded to a request for +# more information; see https://github.com/actions/stale. + +name: No Response + +# Run as a daily cron. +on: + schedule: + # Every day at 8am + - cron: '0 8 * * *' + +# All permissions not specified are set to 'none'. +permissions: + issues: write + pull-requests: write + +jobs: + no-response: + runs-on: ubuntu-latest + if: ${{ github.repository_owner == 'dart-lang' }} + steps: + - uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e + with: + # Don't automatically mark inactive issues+PRs as stale. + days-before-stale: -1 + # Close needs-info issues and PRs after 14 days of inactivity. + days-before-close: 14 + stale-issue-label: "needs-info" + close-issue-message: > + Without additional information we're not able to resolve this issue. + Feel free to add more info or respond to any questions above and we + can reopen the case. Thanks for your contribution! + stale-pr-label: "needs-info" + close-pr-message: > + Without additional information we're not able to resolve this PR. + Feel free to add more info or respond to any questions above. + Thanks for your contribution! diff --git a/pkgs/http2/.github/workflows/publish.yaml b/pkgs/http2/.github/workflows/publish.yaml new file mode 100644 index 0000000000..27157a046a --- /dev/null +++ b/pkgs/http2/.github/workflows/publish.yaml @@ -0,0 +1,17 @@ +# A CI configuration to auto-publish pub packages. + +name: Publish + +on: + pull_request: + branches: [ master ] + push: + tags: [ 'v[0-9]+.[0-9]+.[0-9]+' ] + +jobs: + publish: + if: ${{ github.repository_owner == 'dart-lang' }} + uses: dart-lang/ecosystem/.github/workflows/publish.yaml@main + permissions: + id-token: write # Required for authentication using OIDC + pull-requests: write # Required for writing the pull request note diff --git a/pkgs/http2/.github/workflows/publish.yml b/pkgs/http2/.github/workflows/publish.yml deleted file mode 100644 index fbeb3d76b7..0000000000 --- a/pkgs/http2/.github/workflows/publish.yml +++ /dev/null @@ -1,10 +0,0 @@ -name: Publish -on: - pull_request: - branches: [ master ] - types: [opened, synchronize, reopened, labeled, unlabeled] - push: - tags: [ 'v[0-9]+.[0-9]+.[0-9]+*' ] -jobs: - publish: - uses: dart-lang/ecosystem/.github/workflows/publish.yaml@main diff --git a/pkgs/http2/.github/workflows/test-package.yml b/pkgs/http2/.github/workflows/test-package.yml index 38ced1a4fd..1ded117594 100644 --- a/pkgs/http2/.github/workflows/test-package.yml +++ b/pkgs/http2/.github/workflows/test-package.yml @@ -23,7 +23,7 @@ jobs: sdk: [dev] steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - - uses: dart-lang/setup-dart@b64355ae6ca0b5d484f0106a033dd1388965d06d + - uses: dart-lang/setup-dart@ca7e6fee45ffbd82b555a7ebfc236d2c86439f5b with: sdk: ${{ matrix.sdk }} - id: install @@ -50,7 +50,7 @@ jobs: sdk: [3.0.0, dev] steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - - uses: dart-lang/setup-dart@b64355ae6ca0b5d484f0106a033dd1388965d06d + - uses: dart-lang/setup-dart@ca7e6fee45ffbd82b555a7ebfc236d2c86439f5b with: sdk: ${{ matrix.sdk }} - id: install From d5bde2405ebd325869762cb25919ef15612448b4 Mon Sep 17 00:00:00 2001 From: Moritz Date: Fri, 26 Jan 2024 18:25:02 +0100 Subject: [PATCH 157/172] Add PR health workflow (dart-lang/http2#141) * Add PR health workflow * Fix branch name * Add missing license --- pkgs/http2/.github/workflows/health.yaml | 15 +++++++++++++++ .../http2/.github/workflows/post_summaries.yaml | 17 +++++++++++++++++ pkgs/http2/example/display_headers.dart | 4 ++++ 3 files changed, 36 insertions(+) create mode 100644 pkgs/http2/.github/workflows/health.yaml create mode 100644 pkgs/http2/.github/workflows/post_summaries.yaml diff --git a/pkgs/http2/.github/workflows/health.yaml b/pkgs/http2/.github/workflows/health.yaml new file mode 100644 index 0000000000..9f47a032f7 --- /dev/null +++ b/pkgs/http2/.github/workflows/health.yaml @@ -0,0 +1,15 @@ +name: Health + +on: + pull_request: + branches: [ master ] + types: [opened, synchronize, reopened, labeled, unlabeled] + +jobs: + health: + uses: dart-lang/ecosystem/.github/workflows/health.yaml@main + with: + coverage_web: false + sdk: dev + permissions: + pull-requests: write diff --git a/pkgs/http2/.github/workflows/post_summaries.yaml b/pkgs/http2/.github/workflows/post_summaries.yaml new file mode 100644 index 0000000000..e082efe95a --- /dev/null +++ b/pkgs/http2/.github/workflows/post_summaries.yaml @@ -0,0 +1,17 @@ +name: Comment on the pull request + +on: + # Trigger this workflow after the Health workflow completes. This workflow will have permissions to + # do things like create comments on the PR, even if the original workflow couldn't. + workflow_run: + workflows: + - Health + - Publish + types: + - completed + +jobs: + upload: + uses: dart-lang/ecosystem/.github/workflows/post_summaries.yaml@main + permissions: + pull-requests: write diff --git a/pkgs/http2/example/display_headers.dart b/pkgs/http2/example/display_headers.dart index 9cefdab383..42dbf22b79 100644 --- a/pkgs/http2/example/display_headers.dart +++ b/pkgs/http2/example/display_headers.dart @@ -1,3 +1,7 @@ +// Copyright (c) 2024, 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 'dart:convert'; import 'dart:io'; From 95338a16064aa73adf33be2c420bebb01772081a Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 31 Jan 2024 00:49:31 -0800 Subject: [PATCH 158/172] Require Dart 3.2, update lints (dart-lang/http2#140) --- pkgs/http2/.github/workflows/test-package.yml | 2 +- pkgs/http2/CHANGELOG.md | 4 ++ pkgs/http2/analysis_options.yaml | 12 ++--- pkgs/http2/lib/multiprotocol_server.dart | 2 +- .../lib/src/async_utils/async_utils.dart | 12 ++--- pkgs/http2/lib/src/connection.dart | 24 ++++----- pkgs/http2/lib/src/connection_preface.dart | 17 +++---- pkgs/http2/lib/src/error_handler.dart | 8 +-- .../src/flowcontrol/connection_queues.dart | 8 ++- .../lib/src/flowcontrol/stream_queues.dart | 8 +-- .../lib/src/flowcontrol/window_handler.dart | 4 +- pkgs/http2/lib/src/frames/frame_reader.dart | 2 +- pkgs/http2/lib/src/frames/frame_types.dart | 51 +++++++++++-------- pkgs/http2/lib/src/frames/frame_utils.dart | 2 +- pkgs/http2/lib/src/frames/frame_writer.dart | 2 +- pkgs/http2/lib/src/hpack/hpack.dart | 2 +- pkgs/http2/lib/src/ping/ping_handler.dart | 2 +- pkgs/http2/lib/src/settings/settings.dart | 2 +- .../http2/lib/src/streams/stream_handler.dart | 4 +- pkgs/http2/lib/src/sync_errors.dart | 3 +- pkgs/http2/lib/transport.dart | 22 +++----- .../manual_test/out_of_stream_ids_test.dart | 4 +- pkgs/http2/pubspec.yaml | 6 +-- pkgs/http2/test/client_test.dart | 44 ++++++++-------- .../http2/test/multiprotocol_server_test.dart | 2 +- pkgs/http2/test/src/hpack/hpack_test.dart | 6 +-- .../test/src/ping/ping_handler_test.dart | 5 +- .../test/src/streams/simple_flow_test.dart | 4 +- .../test/src/streams/simple_push_test.dart | 6 +-- pkgs/http2/test/src/streams/streams_test.dart | 2 +- pkgs/http2/test/transport_test.dart | 32 +++++------- 31 files changed, 141 insertions(+), 163 deletions(-) diff --git a/pkgs/http2/.github/workflows/test-package.yml b/pkgs/http2/.github/workflows/test-package.yml index 1ded117594..89c7e63566 100644 --- a/pkgs/http2/.github/workflows/test-package.yml +++ b/pkgs/http2/.github/workflows/test-package.yml @@ -47,7 +47,7 @@ jobs: matrix: # Add macos-latest and/or windows-latest if relevant for this package. os: [ubuntu-latest] - sdk: [3.0.0, dev] + sdk: [3.2, dev] steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - uses: dart-lang/setup-dart@ca7e6fee45ffbd82b555a7ebfc236d2c86439f5b diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index 0c25cce5cb..9674672017 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.3.1-wip + +- Require Dart 3.2 + ## 2.3.0 - Only send updates on frames and pings being received when there are listeners, as to not fill up memory. diff --git a/pkgs/http2/analysis_options.yaml b/pkgs/http2/analysis_options.yaml index 8b5373d0dc..9f9fe93611 100644 --- a/pkgs/http2/analysis_options.yaml +++ b/pkgs/http2/analysis_options.yaml @@ -1,15 +1,9 @@ -include: package:lints/recommended.yaml +# https://dart.dev/tools/analysis#the-analysis-options-file +include: package:dart_flutter_team_lints/analysis_options.yaml analyzer: language: strict-casts: true errors: - unused_element: error - unused_import: error - unused_local_variable: error - dead_code: error - -linter: - rules: # Disabled as there are several dozen violations. - constant_identifier_names: false + constant_identifier_names: ignore diff --git a/pkgs/http2/lib/multiprotocol_server.dart b/pkgs/http2/lib/multiprotocol_server.dart index 0b1aeeb0ec..54fe6c0a60 100644 --- a/pkgs/http2/lib/multiprotocol_server.dart +++ b/pkgs/http2/lib/multiprotocol_server.dart @@ -45,7 +45,7 @@ class MultiProtocolHttpServer { /// /// See also [startServing]. static Future bind( - address, int port, SecurityContext context, + Object? address, int port, SecurityContext context, {http2.ServerSettings? settings}) async { context.setAlpnProtocols(['h2', 'h2-14', 'http/1.1'], true); var secureServer = await SecureServerSocket.bind(address, port, context); diff --git a/pkgs/http2/lib/src/async_utils/async_utils.dart b/pkgs/http2/lib/src/async_utils/async_utils.dart index 61483282ea..22a73e99ef 100644 --- a/pkgs/http2/lib/src/async_utils/async_utils.dart +++ b/pkgs/http2/lib/src/async_utils/async_utils.dart @@ -60,15 +60,9 @@ class BufferedSink { bufferIndicator.markBuffered(); _controller - ..onListen = () { - bufferIndicator.markUnBuffered(); - } - ..onPause = () { - bufferIndicator.markBuffered(); - } - ..onResume = () { - bufferIndicator.markUnBuffered(); - } + ..onListen = bufferIndicator.markUnBuffered + ..onPause = bufferIndicator.markBuffered + ..onResume = bufferIndicator.markUnBuffered ..onCancel = () { // TODO: We may want to propagate cancel events as errors. // Currently `_doneFuture` will just complete normally if the sink diff --git a/pkgs/http2/lib/src/connection.dart b/pkgs/http2/lib/src/connection.dart index 35a851b5da..4e52e57f66 100644 --- a/pkgs/http2/lib/src/connection.dart +++ b/pkgs/http2/lib/src/connection.dart @@ -188,7 +188,7 @@ abstract class Connection { var settings = _decodeSettings(settingsObject); // Do the initial settings handshake (possibly with pushes disabled). - _settingsHandler.changeSettings(settings).catchError((error) { + _settingsHandler.changeSettings(settings).catchError((Object error) { // TODO: The [error] can contain sensitive information we now expose via // a [Goaway] frame. We should somehow ensure we're only sending useful // but non-sensitive information. @@ -271,15 +271,15 @@ abstract class Connection { } /// Pings the remote peer (can e.g. be used for measuring latency). - Future ping() { + Future ping() { return _pingHandler.ping().catchError((e, s) { - return Future.error( + return Future.error( TransportException('The connection has been terminated.')); }, test: (e) => e is TerminatedException); } /// Finishes this connection. - Future finish() { + Future finish() { _finishing(active: true); // TODO: There is probably more we need to wait for. @@ -288,7 +288,7 @@ abstract class Connection { } /// Terminates this connection forcefully. - Future terminate([int? errorCode]) { + Future terminate([int? errorCode]) { return _terminate(errorCode ?? ErrorCode.NO_ERROR); } @@ -441,16 +441,15 @@ abstract class Connection { _settingsHandler.terminate(exception); return Future.wait([cancelFuture, closeFuture]) - .catchError((_) => const []); + .catchError((_) => const []); } - return Future.value(); + return Future.value(); } } class ClientConnection extends Connection implements ClientTransportConnection { - ClientConnection._(Stream> incoming, StreamSink> outgoing, - Settings settings) - : super(incoming, outgoing, settings, isClientConnection: true); + ClientConnection._(super.incoming, super.outgoing, super.settings) + : super(isClientConnection: true); factory ClientConnection(Stream> incoming, StreamSink> outgoing, ClientSettings clientSettings) { @@ -489,9 +488,8 @@ class ClientConnection extends Connection implements ClientTransportConnection { } class ServerConnection extends Connection implements ServerTransportConnection { - ServerConnection._(Stream> incoming, StreamSink> outgoing, - Settings settings) - : super(incoming, outgoing, settings, isClientConnection: false); + ServerConnection._(super.incoming, super.outgoing, super.settings) + : super(isClientConnection: false); factory ServerConnection(Stream> incoming, StreamSink> outgoing, ServerSettings serverSettings) { diff --git a/pkgs/http2/lib/src/connection_preface.dart b/pkgs/http2/lib/src/connection_preface.dart index c2bf652c2e..2f0b98c402 100644 --- a/pkgs/http2/lib/src/connection_preface.dart +++ b/pkgs/http2/lib/src/connection_preface.dart @@ -96,15 +96,14 @@ Stream> readConnectionPreface(Stream> incoming) { } result.onListen = () { - subscription = incoming.listen(onData, - onError: (Object e, StackTrace s) => result.addError(e, s), - onDone: () { - if (!connectionPrefaceRead) { - terminate('EOS before connection preface could be read.'); - } else { - result.close(); - } - }); + subscription = + incoming.listen(onData, onError: result.addError, onDone: () { + if (!connectionPrefaceRead) { + terminate('EOS before connection preface could be read.'); + } else { + result.close(); + } + }); result ..onPause = subscription.pause ..onResume = subscription.resume diff --git a/pkgs/http2/lib/src/error_handler.dart b/pkgs/http2/lib/src/error_handler.dart index f7eed21b6a..a8e19204ef 100644 --- a/pkgs/http2/lib/src/error_handler.dart +++ b/pkgs/http2/lib/src/error_handler.dart @@ -11,7 +11,7 @@ mixin TerminatableMixin { bool _terminated = false; /// Terminates this stream message queue. Further operations on it will fail. - void terminate([error]) { + void terminate([Object? error]) { if (!wasTerminated) { _terminated = true; onTerminated(error); @@ -60,7 +60,7 @@ mixin CancellableMixin { /// Used by classes which may be closed. mixin ClosableMixin { bool _closing = false; - final Completer _completer = Completer(); + final Completer _completer = Completer(); Future get done => _completer.future; @@ -91,13 +91,13 @@ mixin ClosableMixin { return f(); } - void closeWithValue([value]) { + void closeWithValue([Object? value]) { if (!wasClosed) { _completer.complete(value); } } - void closeWithError(error) { + void closeWithError(Object? error) { if (!wasClosed) { _completer.complete(error); } diff --git a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart index 7382884b77..f1a499a903 100644 --- a/pkgs/http2/lib/src/flowcontrol/connection_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/connection_queues.dart @@ -10,11 +10,9 @@ import 'dart:async'; import 'dart:collection'; import '../../transport.dart'; - import '../byte_utils.dart'; import '../error_handler.dart'; import '../frames/frames.dart'; - import 'queue_messages.dart'; import 'stream_queues.dart'; import 'window_handler.dart'; @@ -66,7 +64,7 @@ class ConnectionMessageQueueOut extends Object } @override - void onTerminated(error) { + void onTerminated(Object? error) { _messages.clear(); closeWithError(error); } @@ -182,7 +180,7 @@ class ConnectionMessageQueueIn extends Object final IncomingWindowHandler _windowUpdateHandler; /// Catches any protocol errors and acts upon them. - final Function _catchProtocolErrors; + final void Function(void Function()) _catchProtocolErrors; /// A mapping from stream-id to the corresponding stream-specific /// [StreamMessageQueueIn]. @@ -200,7 +198,7 @@ class ConnectionMessageQueueIn extends Object this._windowUpdateHandler, this._catchProtocolErrors); @override - void onTerminated(error) { + void onTerminated(Object? error) { // NOTE: The higher level will be shutdown first, so all streams // should have been removed at this point. assert(_stream2messageQueue.isEmpty); diff --git a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart index ff9711c09e..de7950a66e 100644 --- a/pkgs/http2/lib/src/flowcontrol/stream_queues.dart +++ b/pkgs/http2/lib/src/flowcontrol/stream_queues.dart @@ -84,7 +84,7 @@ class StreamMessageQueueOut extends Object } @override - void onTerminated(error) { + void onTerminated(Object? error) { _messages.clear(); closeWithError(error); } @@ -183,11 +183,7 @@ class StreamMessageQueueIn extends Object _tryUpdateBufferIndicator(); } } - ..onPause = () { - _tryUpdateBufferIndicator(); - // TODO: Would we ever want to decrease the window size in this - // situation? - } + ..onPause = _tryUpdateBufferIndicator ..onResume = () { if (!wasClosed && !wasTerminated) { _tryDispatch(); diff --git a/pkgs/http2/lib/src/flowcontrol/window_handler.dart b/pkgs/http2/lib/src/flowcontrol/window_handler.dart index 6f9a5a6c3f..27d0321694 100644 --- a/pkgs/http2/lib/src/flowcontrol/window_handler.dart +++ b/pkgs/http2/lib/src/flowcontrol/window_handler.dart @@ -58,12 +58,12 @@ abstract class AbstractOutgoingWindowHandler { /// Handles the connection window for outgoing data frames. class OutgoingConnectionWindowHandler extends AbstractOutgoingWindowHandler { - OutgoingConnectionWindowHandler(Window window) : super(window); + OutgoingConnectionWindowHandler(super.window); } /// Handles the window for outgoing messages to the peer. class OutgoingStreamWindowHandler extends AbstractOutgoingWindowHandler { - OutgoingStreamWindowHandler(Window window) : super(window); + OutgoingStreamWindowHandler(super.window); /// Update the peer window by adding [difference] to it. /// diff --git a/pkgs/http2/lib/src/frames/frame_reader.dart b/pkgs/http2/lib/src/frames/frame_reader.dart index 2d93f01a47..7b692614aa 100644 --- a/pkgs/http2/lib/src/frames/frame_reader.dart +++ b/pkgs/http2/lib/src/frames/frame_reader.dart @@ -2,7 +2,7 @@ // 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. -part of http2.src.frames; +part of 'frames.dart'; /// Used for converting a `Stream>` to a `Stream`. class FrameReader { diff --git a/pkgs/http2/lib/src/frames/frame_types.dart b/pkgs/http2/lib/src/frames/frame_types.dart index abab588c10..33c9e4aff1 100644 --- a/pkgs/http2/lib/src/frames/frame_types.dart +++ b/pkgs/http2/lib/src/frames/frame_types.dart @@ -2,7 +2,7 @@ // 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. -part of http2.src.frames; +part of 'frames.dart'; const int FRAME_HEADER_SIZE = 9; @@ -67,7 +67,7 @@ class DataFrame extends Frame { final List bytes; - DataFrame(FrameHeader header, this.padLength, this.bytes) : super(header); + DataFrame(super.header, this.padLength, this.bytes); bool get hasEndStreamFlag => _isFlagSet(header.flags, FLAG_END_STREAM); bool get hasPaddedFlag => _isFlagSet(header.flags, FLAG_PADDED); @@ -99,9 +99,14 @@ class HeadersFrame extends Frame { final int? weight; final List headerBlockFragment; - HeadersFrame(FrameHeader header, this.padLength, this.exclusiveDependency, - this.streamDependency, this.weight, this.headerBlockFragment) - : super(header); + HeadersFrame( + super.header, + this.padLength, + this.exclusiveDependency, + this.streamDependency, + this.weight, + this.headerBlockFragment, + ); /// This will be set from the outside after decoding. late List
decodedHeaders; @@ -148,9 +153,12 @@ class PriorityFrame extends Frame { final int streamDependency; final int weight; - PriorityFrame(FrameHeader header, this.exclusiveDependency, - this.streamDependency, this.weight) - : super(header); + PriorityFrame( + super.header, + this.exclusiveDependency, + this.streamDependency, + this.weight, + ); @override Map toJson() => super.toJson() @@ -166,7 +174,7 @@ class RstStreamFrame extends Frame { final int errorCode; - RstStreamFrame(FrameHeader header, this.errorCode) : super(header); + RstStreamFrame(super.header, this.errorCode); @override Map toJson() => super.toJson() @@ -199,7 +207,7 @@ class SettingsFrame extends Frame { final List settings; - SettingsFrame(FrameHeader header, this.settings) : super(header); + SettingsFrame(super.header, this.settings); bool get hasAckFlag => _isFlagSet(header.flags, FLAG_ACK); @@ -225,9 +233,12 @@ class PushPromiseFrame extends Frame { /// This will be set from the outside after decoding. late List
decodedHeaders; - PushPromiseFrame(FrameHeader header, this.padLength, this.promisedStreamId, - this.headerBlockFragment) - : super(header); + PushPromiseFrame( + super.header, + this.padLength, + this.promisedStreamId, + this.headerBlockFragment, + ); bool get hasEndHeadersFlag => _isFlagSet(header.flags, FLAG_END_HEADERS); bool get hasPaddedFlag => _isFlagSet(header.flags, FLAG_PADDED); @@ -267,7 +278,7 @@ class PingFrame extends Frame { final int opaqueData; - PingFrame(FrameHeader header, this.opaqueData) : super(header); + PingFrame(super.header, this.opaqueData); bool get hasAckFlag => _isFlagSet(header.flags, FLAG_ACK); @@ -283,9 +294,7 @@ class GoawayFrame extends Frame { final int errorCode; final List debugData; - GoawayFrame( - FrameHeader header, this.lastStreamId, this.errorCode, this.debugData) - : super(header); + GoawayFrame(super.header, this.lastStreamId, this.errorCode, this.debugData); @override Map toJson() => super.toJson() @@ -301,8 +310,7 @@ class WindowUpdateFrame extends Frame { final int windowSizeIncrement; - WindowUpdateFrame(FrameHeader header, this.windowSizeIncrement) - : super(header); + WindowUpdateFrame(super.header, this.windowSizeIncrement); @override Map toJson() => super.toJson() @@ -316,8 +324,7 @@ class ContinuationFrame extends Frame { final List headerBlockFragment; - ContinuationFrame(FrameHeader header, this.headerBlockFragment) - : super(header); + ContinuationFrame(super.header, this.headerBlockFragment); bool get hasEndHeadersFlag => _isFlagSet(header.flags, FLAG_END_HEADERS); @@ -331,7 +338,7 @@ class ContinuationFrame extends Frame { class UnknownFrame extends Frame { final List data; - UnknownFrame(FrameHeader header, this.data) : super(header); + UnknownFrame(super.header, this.data); @override Map toJson() => super.toJson() diff --git a/pkgs/http2/lib/src/frames/frame_utils.dart b/pkgs/http2/lib/src/frames/frame_utils.dart index 73a3173423..11c30a5f96 100644 --- a/pkgs/http2/lib/src/frames/frame_utils.dart +++ b/pkgs/http2/lib/src/frames/frame_utils.dart @@ -2,6 +2,6 @@ // 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. -part of http2.src.frames; +part of 'frames.dart'; bool _isFlagSet(int value, int flag) => value & flag == flag; diff --git a/pkgs/http2/lib/src/frames/frame_writer.dart b/pkgs/http2/lib/src/frames/frame_writer.dart index 211aa54429..50caad75c9 100644 --- a/pkgs/http2/lib/src/frames/frame_writer.dart +++ b/pkgs/http2/lib/src/frames/frame_writer.dart @@ -2,7 +2,7 @@ // 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. -part of http2.src.frames; +part of 'frames.dart'; // TODO: No support for writing padded information. // TODO: No support for stream priorities. diff --git a/pkgs/http2/lib/src/hpack/hpack.dart b/pkgs/http2/lib/src/hpack/hpack.dart index f34e0431c3..ab5d9c8fa8 100644 --- a/pkgs/http2/lib/src/hpack/hpack.dart +++ b/pkgs/http2/lib/src/hpack/hpack.dart @@ -11,7 +11,6 @@ import 'dart:convert' show ascii; import 'dart:typed_data'; import '../byte_utils.dart'; - import 'huffman.dart'; import 'huffman_table.dart'; @@ -157,6 +156,7 @@ class HPackDecoder { } } return headers; + // ignore: avoid_catching_errors } on RangeError catch (e) { throw HPackDecodingException('$e'); } on HuffmanDecodingException catch (e) { diff --git a/pkgs/http2/lib/src/ping/ping_handler.dart b/pkgs/http2/lib/src/ping/ping_handler.dart index 49bd6cc04a..f9be1f9b44 100644 --- a/pkgs/http2/lib/src/ping/ping_handler.dart +++ b/pkgs/http2/lib/src/ping/ping_handler.dart @@ -61,7 +61,7 @@ class PingHandler extends Object with TerminatableMixin { Future ping() { return ensureNotTerminatedAsync(() { - var c = Completer(); + var c = Completer(); var id = _nextId++; _remainingPings[id] = c; _frameWriter.writePingFrame(id); diff --git a/pkgs/http2/lib/src/settings/settings.dart b/pkgs/http2/lib/src/settings/settings.dart index 948658d5be..291c66856c 100644 --- a/pkgs/http2/lib/src/settings/settings.dart +++ b/pkgs/http2/lib/src/settings/settings.dart @@ -163,7 +163,7 @@ class SettingsHandler extends Object with TerminatableMixin { return ensureNotTerminatedAsync(() { // TODO: Have a timeout: When ACK doesn't get back in a reasonable time // frame we should quit with ErrorCode.SETTINGS_TIMEOUT. - var completer = Completer(); + var completer = Completer(); _toBeAcknowledgedSettings.add(changes); _toBeAcknowledgedCompleters.add(completer); _frameWriter.writeSettingsFrame(changes); diff --git a/pkgs/http2/lib/src/streams/stream_handler.dart b/pkgs/http2/lib/src/streams/stream_handler.dart index 4018d3a70b..92a228df5e 100644 --- a/pkgs/http2/lib/src/streams/stream_handler.dart +++ b/pkgs/http2/lib/src/streams/stream_handler.dart @@ -283,7 +283,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { 'not in "idle" state.'); } - var sameDirection = (nextStreamId + remoteStreamId) % 2 == 0; + var sameDirection = (nextStreamId + remoteStreamId).isEven; assert(!sameDirection); lastRemoteStreamId = remoteStreamId; @@ -343,7 +343,7 @@ class StreamHandler extends Object with TerminatableMixin, ClosableMixin { // NOTE: We are not interested whether the streams were normally finished // or abnormally terminated. Therefore we use 'catchError((_) {})'! var streamDone = [streamQueueIn.done, streamQueueOut.done]; - Future.wait(streamDone).catchError((_) => const []).whenComplete(() { + Future.wait(streamDone).catchError((_) => const []).whenComplete(() { _cleanupClosedStream(stream); }); diff --git a/pkgs/http2/lib/src/sync_errors.dart b/pkgs/http2/lib/src/sync_errors.dart index 2423e6a4a9..3d11616ad1 100644 --- a/pkgs/http2/lib/src/sync_errors.dart +++ b/pkgs/http2/lib/src/sync_errors.dart @@ -45,8 +45,7 @@ class StreamException implements Exception { } class StreamClosedException extends StreamException { - StreamClosedException(int streamId, [String message = '']) - : super(streamId, message); + StreamClosedException(super.streamId, [super.message = '']); @override String toString() => 'StreamClosedException(stream id: $streamId): $_message'; diff --git a/pkgs/http2/lib/transport.dart b/pkgs/http2/lib/transport.dart index af37cf614e..87a10f6799 100644 --- a/pkgs/http2/lib/transport.dart +++ b/pkgs/http2/lib/transport.dart @@ -8,8 +8,8 @@ import 'dart:io'; import 'src/connection.dart'; import 'src/hpack/hpack.dart' show Header; -export 'src/hpack/hpack.dart' show Header; export 'src/frames/frames.dart' show ErrorCode; +export 'src/hpack/hpack.dart' show Header; typedef ActiveStateHandler = void Function(bool isActive); @@ -28,10 +28,7 @@ abstract class Settings { /// Settings for a [TransportConnection] a server can make. class ServerSettings extends Settings { - const ServerSettings({int? concurrentStreamLimit, int? streamWindowSize}) - : super( - concurrentStreamLimit: concurrentStreamLimit, - streamWindowSize: streamWindowSize); + const ServerSettings({super.concurrentStreamLimit, super.streamWindowSize}); } /// Settings for a [TransportConnection] a client can make. @@ -40,12 +37,9 @@ class ClientSettings extends Settings { final bool allowServerPushes; const ClientSettings( - {int? concurrentStreamLimit, - int? streamWindowSize, - this.allowServerPushes = false}) - : super( - concurrentStreamLimit: concurrentStreamLimit, - streamWindowSize: streamWindowSize); + {super.concurrentStreamLimit, + super.streamWindowSize, + this.allowServerPushes = false}); } /// Represents a HTTP/2 connection. @@ -194,8 +188,7 @@ abstract class StreamMessage { class DataStreamMessage extends StreamMessage { final List bytes; - DataStreamMessage(this.bytes, {bool? endStream}) - : super(endStream: endStream); + DataStreamMessage(this.bytes, {super.endStream}); @override String toString() => 'DataStreamMessage(${bytes.length} bytes)'; @@ -205,8 +198,7 @@ class DataStreamMessage extends StreamMessage { class HeadersStreamMessage extends StreamMessage { final List
headers; - HeadersStreamMessage(this.headers, {bool? endStream}) - : super(endStream: endStream); + HeadersStreamMessage(this.headers, {super.endStream}); @override String toString() => 'HeadersStreamMessage(${headers.length} headers)'; diff --git a/pkgs/http2/manual_test/out_of_stream_ids_test.dart b/pkgs/http2/manual_test/out_of_stream_ids_test.dart index d653793e28..acfbbd9cee 100644 --- a/pkgs/http2/manual_test/out_of_stream_ids_test.dart +++ b/pkgs/http2/manual_test/out_of_stream_ids_test.dart @@ -11,6 +11,8 @@ /// /// without this patch this test will run for a _long_ time. /// --------------------------------------------------------------------------- +library; + import 'dart:async'; import 'package:http2/src/streams/stream_handler.dart'; @@ -46,7 +48,7 @@ void main() { expect(() => client.makeRequest(headers), throwsA(const TypeMatcher())); - await Future.delayed(const Duration(seconds: 1)); + await Future.delayed(const Duration(seconds: 1)); await client.finish(); } diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index d8d21c852d..72528e2926 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -1,13 +1,13 @@ name: http2 -version: 2.3.0 +version: 2.3.1-wip description: A HTTP/2 implementation in Dart. repository: https://github.com/dart-lang/http2 environment: - sdk: '>=3.0.0 <4.0.0' + sdk: ^3.2.0 dev_dependencies: build_runner: ^2.3.0 - lints: ^2.0.0 + dart_flutter_team_lints: ^2.0.0 mockito: ^5.3.2 test: ^1.21.4 diff --git a/pkgs/http2/test/client_test.dart b/pkgs/http2/test/client_test.dart index cf66a9ef97..726707f357 100644 --- a/pkgs/http2/test/client_test.dart +++ b/pkgs/http2/test/client_test.dart @@ -24,7 +24,7 @@ void main() { FrameWriter serverWriter, StreamIterator serverReader, Future Function() nextFrame) async { - var settingsDone = Completer(); + var settingsDone = Completer(); Future serverFun() async { serverWriter.writeSettingsFrame([]); @@ -67,7 +67,7 @@ void main() { FrameWriter serverWriter, StreamIterator serverReader, Future Function() nextFrame) async { - final settingsDone = Completer(); + final settingsDone = Completer(); Future serverFun() async { serverWriter.writeSettingsFrame([]); @@ -89,7 +89,7 @@ void main() { Future clientFun() async { await settingsDone.future; await client.onInitialPeerSettingsReceived - .timeout(Duration(milliseconds: 20)); // Should complete + .timeout(const Duration(milliseconds: 20)); // Should complete expect(client.isOpen, true); @@ -109,7 +109,7 @@ void main() { FrameWriter serverWriter, StreamIterator serverReader, Future Function() nextFrame) async { - final goawayReceived = Completer(); + final goawayReceived = Completer(); Future serverFun() async { serverWriter.writePingFrame(42); expect(await nextFrame(), isA()); @@ -123,7 +123,7 @@ void main() { expect( client.onInitialPeerSettingsReceived - .timeout(Duration(seconds: 1)), + .timeout(const Duration(seconds: 1)), throwsA(isA())); // We wait until the server received the error (it's actually later @@ -153,7 +153,7 @@ void main() { FrameWriter serverWriter, StreamIterator serverReader, Future Function() nextFrame) async { - var goawayReceived = Completer(); + var goawayReceived = Completer(); Future serverFun() async { serverWriter.writePingFrame(42); expect(await nextFrame(), isA()); @@ -220,7 +220,7 @@ void main() { FrameWriter serverWriter, StreamIterator serverReader, Future Function() nextFrame) async { - var handshakeCompleter = Completer(); + var handshakeCompleter = Completer(); Future serverFun() async { serverWriter.writeSettingsFrame([]); @@ -281,7 +281,7 @@ void main() { FrameWriter serverWriter, StreamIterator serverReader, Future Function() nextFrame) async { - var handshakeCompleter = Completer(); + var handshakeCompleter = Completer(); Future serverFun() async { serverWriter.writeSettingsFrame([]); @@ -373,9 +373,9 @@ void main() { FrameWriter serverWriter, StreamIterator serverReader, Future Function() nextFrame) async { - var handshakeCompleter = Completer(); - var cancelDone = Completer(); - var endDone = Completer(); + var handshakeCompleter = Completer(); + var cancelDone = Completer(); + var endDone = Completer(); Future serverFun() async { serverWriter.writeSettingsFrame([]); @@ -463,10 +463,10 @@ void main() { FrameWriter serverWriter, StreamIterator serverReader, Future Function() nextFrame) async { - var handshakeCompleter = Completer(); - var cancelDone = Completer(); - var endDone = Completer(); - var clientDone = Completer(); + var handshakeCompleter = Completer(); + var cancelDone = Completer(); + var endDone = Completer(); + var clientDone = Completer(); Future serverFun() async { serverWriter.writeSettingsFrame([]); @@ -550,7 +550,7 @@ void main() { FrameWriter serverWriter, StreamIterator serverReader, Future Function() nextFrame) async { - var handshakeCompleter = Completer(); + var handshakeCompleter = Completer(); Future serverFun() async { serverWriter.writeSettingsFrame([]); @@ -610,7 +610,7 @@ void main() { FrameWriter serverWriter, StreamIterator serverReader, Future Function() nextFrame) async { - var handshakeCompleter = Completer(); + var handshakeCompleter = Completer(); Future serverFun() async { serverWriter.writeSettingsFrame([]); @@ -661,7 +661,7 @@ void main() { FrameWriter serverWriter, StreamIterator serverReader, Future Function() nextFrame) async { - var handshakeCompleter = Completer(); + var handshakeCompleter = Completer(); Future serverFun() async { serverWriter.writeSettingsFrame([]); @@ -702,7 +702,7 @@ void main() { expectAsync1((StreamMessage msg) {}, count: 0), onError: expectAsync1((Object error) {})); sub.pause(); - await Future.delayed(const Duration(milliseconds: 40)); + await Future.delayed(const Duration(milliseconds: 40)); sub.resume(); await client.finish(); @@ -717,8 +717,8 @@ void main() { FrameWriter serverWriter, StreamIterator serverReader, Future Function() nextFrame) async { - var settingsDone = Completer(); - var headersDone = Completer(); + var settingsDone = Completer(); + var headersDone = Completer(); Future serverFun() async { var decoder = HPackDecoder(); @@ -781,7 +781,7 @@ void main() { FrameWriter serverWriter, StreamIterator serverReader, Future Function() nextFrame) async { - var settingsDone = Completer(); + var settingsDone = Completer(); Future serverFun() async { var decoder = HPackDecoder(); diff --git a/pkgs/http2/test/multiprotocol_server_test.dart b/pkgs/http2/test/multiprotocol_server_test.dart index 997f0dadfb..736e7da6b3 100644 --- a/pkgs/http2/test/multiprotocol_server_test.dart +++ b/pkgs/http2/test/multiprotocol_server_test.dart @@ -74,7 +74,7 @@ Future makeHttp11Request( Future handleHttp11Request(HttpRequest request, int i) async { expect(request.uri.path, '/abc$i'); - await request.drain(); + await request.drain(); request.response.write('answer$i'); await request.response.close(); } diff --git a/pkgs/http2/test/src/hpack/hpack_test.dart b/pkgs/http2/test/src/hpack/hpack_test.dart index 4d3bf53f60..aadf6cb17e 100644 --- a/pkgs/http2/test/src/hpack/hpack_test.dart +++ b/pkgs/http2/test/src/hpack/hpack_test.dart @@ -609,8 +609,8 @@ void main() { test('update-dynamic-table-size-too-high', () { var context = HPackContext(); // Sets dynamic table to 4096 - expect( - context.decoder.decode(TestHelper.newInteger(0x20, 5, 4096)), []); + expect(context.decoder.decode(TestHelper.newInteger(0x20, 5, 4096)), + []); }); test('dynamic table entry', () { @@ -809,7 +809,7 @@ class _HeaderMatcher extends Matcher { Description describe(Description description) => description.add('Header'); @override - bool matches(item, Map matchState) { + bool matches(Object? item, Map matchState) { return item is Header && _compareLists(item.name, header.name) && _compareLists(item.value, header.value); diff --git a/pkgs/http2/test/src/ping/ping_handler_test.dart b/pkgs/http2/test/src/ping/ping_handler_test.dart index ef5250d825..df02420870 100644 --- a/pkgs/http2/test/src/ping/ping_handler_test.dart +++ b/pkgs/http2/test/src/ping/ping_handler_test.dart @@ -98,8 +98,7 @@ void main() { final pings = []; final writer = FrameWriterMock(); - final pingStream = StreamController() - ..stream.listen((event) => pings.add(event)); + final pingStream = StreamController()..stream.listen(pings.add); PingHandler(writer, pingStream) ..processPingFrame(PingFrame( @@ -119,7 +118,7 @@ void main() { } PingHandler instantiateHandler(FrameWriterMock writer) { - StreamController controller = StreamController(); + var controller = StreamController(); return PingHandler(writer, controller); } diff --git a/pkgs/http2/test/src/streams/simple_flow_test.dart b/pkgs/http2/test/src/streams/simple_flow_test.dart index 7a294687c6..25226d1a60 100644 --- a/pkgs/http2/test/src/streams/simple_flow_test.dart +++ b/pkgs/http2/test/src/streams/simple_flow_test.dart @@ -30,7 +30,7 @@ void main() { }); } - var serverReceivedAllBytes = Completer(); + var serverReceivedAllBytes = Completer(); void Function(StreamMessage) messageTestFun(String type) { var expectHeader = true; @@ -83,7 +83,7 @@ void main() { sStream.incomingMessages .listen(messageTestFun('server'), onDone: expectAsync0(() {})); sStream.sendHeaders(expectedHeaders, endStream: true); - expect(await serverReceivedAllBytes.future, completes); + await serverReceivedAllBytes.future; })); TransportStream cStream = client.makeRequest(expectedHeaders); diff --git a/pkgs/http2/test/src/streams/simple_push_test.dart b/pkgs/http2/test/src/streams/simple_push_test.dart index e91f95ba27..d12dbb5d5d 100644 --- a/pkgs/http2/test/src/streams/simple_push_test.dart +++ b/pkgs/http2/test/src/streams/simple_push_test.dart @@ -34,7 +34,7 @@ void main() { }); } - var serverReceivedAllBytes = Completer(); + var serverReceivedAllBytes = Completer(); Future readData(StreamIterator iterator) async { var all = []; @@ -66,7 +66,7 @@ void main() { unawaited(sStream.incomingMessages.drain()); sStream.sendHeaders(expectedHeaders, endStream: true); - expect(await serverReceivedAllBytes.future, completes); + await serverReceivedAllBytes.future; })); var cStream = client.makeRequest(expectedHeaders, endStream: true); @@ -84,7 +84,7 @@ void main() { var msg = await readData(iterator); expect(msg, 'pushing "hello world" :)'); })); - }, settings: ClientSettings(allowServerPushes: true)); + }, settings: const ClientSettings(allowServerPushes: true)); }); }); } diff --git a/pkgs/http2/test/src/streams/streams_test.dart b/pkgs/http2/test/src/streams/streams_test.dart index 18109fc38b..6bfa49c495 100644 --- a/pkgs/http2/test/src/streams/streams_test.dart +++ b/pkgs/http2/test/src/streams/streams_test.dart @@ -67,7 +67,7 @@ void main() { server.incomingStreams .listen(expectAsync1((TransportStream sStream) async { var isFirst = true; - var receivedChunks = []; + var receivedChunks = >[]; sStream.incomingMessages.listen( expectAsync1((StreamMessage msg) { if (isFirst) { diff --git a/pkgs/http2/test/transport_test.dart b/pkgs/http2/test/transport_test.dart index e227ed2394..96c2c03e0c 100644 --- a/pkgs/http2/test/transport_test.dart +++ b/pkgs/http2/test/transport_test.dart @@ -67,8 +67,8 @@ void main() { // The default is unlimited, which is why we have to wait for the server // setting to arrive on the client. // At the moment, delaying by 2 microtask cycles is enough. - await Future.value(); - await Future.value(); + await Future.value(); + await Future.value(); final streams = []; for (var i = 0; i < concurrentStreamLimit; ++i) { @@ -93,7 +93,7 @@ void main() { await Future.wait([clientFun(), serverFun()]); }, serverSettings: - ServerSettings(concurrentStreamLimit: concurrentStreamLimit)); + const ServerSettings(concurrentStreamLimit: concurrentStreamLimit)); transportTest('disabled-push', (ClientTransportConnection client, ServerTransportConnection server) async { @@ -188,7 +188,7 @@ void main() { await client.terminate(); await serverFuture; }, - clientSettings: ClientSettings( + clientSettings: const ClientSettings( concurrentStreamLimit: kDefaultStreamLimit, allowServerPushes: true)); @@ -216,7 +216,7 @@ void main() { transportTest('client-terminates-stream', (ClientTransportConnection client, ServerTransportConnection server) async { - var readyForError = Completer(); + var readyForError = Completer(); Future serverFun() async { await for (ServerTransportStream stream in server.incomingStreams) { @@ -268,7 +268,7 @@ void main() { transportTest('client-terminates-stream-after-half-close', (ClientTransportConnection client, ServerTransportConnection server) async { - var readyForError = Completer(); + var readyForError = Completer(); Future serverFun() async { await for (ServerTransportStream stream in server.incomingStreams) { @@ -304,7 +304,7 @@ void main() { transportTest('server-terminates-stream-after-half-close', (ClientTransportConnection client, ServerTransportConnection server) async { - var readyForError = Completer(); + var readyForError = Completer(); Future serverFun() async { await for (ServerTransportStream stream in server.incomingStreams) { @@ -375,19 +375,19 @@ void main() { // This extra await is needed to allow the idle handler to run before // verifying the idleCount, because the stream cleanup runs // asynchronously after the stream is closed. - await Future.value(); + await Future.value(); expect(activeCount, 1); expect(idleCount, 1); var stream = client.makeRequest([]); await stream.outgoingMessages.close(); await stream.incomingMessages.toList(); - await Future.value(); + await Future.value(); stream = client.makeRequest([]); await stream.outgoingMessages.close(); await stream.incomingMessages.toList(); - await Future.value(); + await Future.value(); await client.finish(); expect(activeCount, 3); @@ -410,7 +410,7 @@ void main() { lessThan(kChunkSize * kNumberOfMessages)); var serverSentBytes = 0; - var flowcontrolWindowFull = Completer(); + var flowcontrolWindowFull = Completer(); Future serverFun() async { await for (ServerTransportStream stream in server.incomingStreams) { @@ -438,9 +438,7 @@ void main() { } controller - ..onListen = () { - addData(); - } + ..onListen = addData ..onPause = expectAsync0(() { // Assert that we're now at the place (since the granularity // of adding is [kChunkSize], it could be that we added @@ -450,9 +448,7 @@ void main() { lessThan(expectedStreamFlowcontrolWindow)); flowcontrolWindowFull.complete(); }) - ..onResume = () { - addData(); - } + ..onResume = addData ..onCancel = () {}; await stream.outgoingMessages.addStream(controller.stream); @@ -506,7 +502,7 @@ void main() { (ClientTransportConnection client, ServerTransportConnection server) async { await testWindowSize(client, server, 8096); - }, clientSettings: ClientSettings(streamWindowSize: 8096)); + }, clientSettings: const ClientSettings(streamWindowSize: 8096)); }); }); } From 478d2fea79fbad6153d44e7e5cc8ce3c6663c9ab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Feb 2024 21:30:18 +0000 Subject: [PATCH 159/172] Bump dart-lang/setup-dart from 1.6.1 to 1.6.2 (dart-lang/http2#142) Bumps [dart-lang/setup-dart](https://github.com/dart-lang/setup-dart) from 1.6.1 to 1.6.2.
Release notes

Sourced from dart-lang/setup-dart's releases.

v1.6.2

Changelog

Sourced from dart-lang/setup-dart's changelog.

v1.6.2

v1.6.1

  • Updated the google storage url for main channel releases.

v1.6.0

  • Enable provisioning of the latest Dart SDK patch release by specifying just the major and minor version (e.g. 3.2).

v1.5.1

  • No longer test the setup-dart action on pre-2.12 SDKs.
  • Upgrade JS interop code to use extension types (the new name for inline classes).
  • The upcoming rename of the be channel to main is now supported with forward compatibility that switches when the rename happens.

v1.5.0

  • Re-wrote the implementation of the action into Dart.
  • Auto-detect the platform architecture (x64, ia32, arm, arm64).
  • Improved the caching and download resilience of the sdk.
  • Added a new action output: dart-version - the installed version of the sdk.

v1.4.0

  • Automatically create OIDC token for pub.dev.
  • Add a reusable workflow for publishing.

v1.3.0

  • The install location of the Dart SDK is now available in an environment variable, DART_HOME (dart-lang/http2#43).
  • Fixed an issue where cached downloads could lead to unzip issues on self-hosted runners (dart-lang/http2#35).

v1.2.0

  • Fixed a path issue impacting git dependencies on Windows.

v1.1.0

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=dart-lang/setup-dart&package-manager=github_actions&previous-version=1.6.1&new-version=1.6.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- pkgs/http2/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http2/.github/workflows/test-package.yml b/pkgs/http2/.github/workflows/test-package.yml index 89c7e63566..cf89e3c0fe 100644 --- a/pkgs/http2/.github/workflows/test-package.yml +++ b/pkgs/http2/.github/workflows/test-package.yml @@ -23,7 +23,7 @@ jobs: sdk: [dev] steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - - uses: dart-lang/setup-dart@ca7e6fee45ffbd82b555a7ebfc236d2c86439f5b + - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 with: sdk: ${{ matrix.sdk }} - id: install @@ -50,7 +50,7 @@ jobs: sdk: [3.2, dev] steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - - uses: dart-lang/setup-dart@ca7e6fee45ffbd82b555a7ebfc236d2c86439f5b + - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 with: sdk: ${{ matrix.sdk }} - id: install From 1535d43d33cc9998ff4ff6e136dd753fe97dc168 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Apr 2024 21:57:53 +0000 Subject: [PATCH 160/172] Bump actions/checkout from 4.1.1 to 4.1.2 (dart-lang/http2#143) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.1 to 4.1.2.
Release notes

Sourced from actions/checkout's releases.

v4.1.2

We are investigating the following issue with this release and have rolled-back the v4 tag to point to v4.1.1

What's Changed

New Contributors

Full Changelog: https://github.com/actions/checkout/compare/v4.1.1...v4.1.2

Changelog

Sourced from actions/checkout's changelog.

Changelog

v4.1.2

v4.1.1

v4.1.0

v4.0.0

v3.6.0

v3.5.3

v3.5.2

v3.5.1

v3.5.0

v3.4.0

v3.3.0

v3.2.0

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=4.1.1&new-version=4.1.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- pkgs/http2/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http2/.github/workflows/test-package.yml b/pkgs/http2/.github/workflows/test-package.yml index cf89e3c0fe..7b2526891d 100644 --- a/pkgs/http2/.github/workflows/test-package.yml +++ b/pkgs/http2/.github/workflows/test-package.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 + - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 with: sdk: ${{ matrix.sdk }} @@ -49,7 +49,7 @@ jobs: os: [ubuntu-latest] sdk: [3.2, dev] steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 + - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 with: sdk: ${{ matrix.sdk }} From 2e8d0ad90e050baddd011747fd8026b7bc266ac2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 21:55:06 +0000 Subject: [PATCH 161/172] Bump actions/checkout from 4.1.2 to 4.1.4 (dart-lang/http2#146) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.2 to 4.1.4.
Release notes

Sourced from actions/checkout's releases.

v4.1.4

What's Changed

Full Changelog: https://github.com/actions/checkout/compare/v4.1.3...v4.1.4

v4.1.3

What's Changed

Full Changelog: https://github.com/actions/checkout/compare/v4.1.2...v4.1.3

Changelog

Sourced from actions/checkout's changelog.

Changelog

v4.1.4

v4.1.3

v4.1.2

v4.1.1

v4.1.0

v4.0.0

v3.6.0

v3.5.3

v3.5.2

v3.5.1

v3.5.0

v3.4.0

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=4.1.2&new-version=4.1.4)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- pkgs/http2/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http2/.github/workflows/test-package.yml b/pkgs/http2/.github/workflows/test-package.yml index 7b2526891d..b82aa5071b 100644 --- a/pkgs/http2/.github/workflows/test-package.yml +++ b/pkgs/http2/.github/workflows/test-package.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 with: sdk: ${{ matrix.sdk }} @@ -49,7 +49,7 @@ jobs: os: [ubuntu-latest] sdk: [3.2, dev] steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 with: sdk: ${{ matrix.sdk }} From 2a96bfc5573e93660d95d6b4c325cd1bd8957bd6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 21:57:35 +0000 Subject: [PATCH 162/172] Bump dart-lang/setup-dart from 1.6.2 to 1.6.4 (dart-lang/http2#145) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [dart-lang/setup-dart](https://github.com/dart-lang/setup-dart) from 1.6.2 to 1.6.4.
Release notes

Sourced from dart-lang/setup-dart's releases.

v1.6.4

  • Rebuild JS code to include changes from v1.6.3

v1.6.3

Changelog

Sourced from dart-lang/setup-dart's changelog.

v1.6.4

  • Rebuild JS code.

v1.6.3

v1.6.2

v1.6.1

  • Updated the google storage url for main channel releases.

v1.6.0

  • Enable provisioning of the latest Dart SDK patch release by specifying just the major and minor version (e.g. 3.2).

v1.5.1

  • No longer test the setup-dart action on pre-2.12 SDKs.
  • Upgrade JS interop code to use extension types (the new name for inline classes).
  • The upcoming rename of the be channel to main is now supported with forward compatibility that switches when the rename happens.

v1.5.0

  • Re-wrote the implementation of the action into Dart.
  • Auto-detect the platform architecture (x64, ia32, arm, arm64).
  • Improved the caching and download resilience of the sdk.
  • Added a new action output: dart-version - the installed version of the sdk.

v1.4.0

  • Automatically create OIDC token for pub.dev.
  • Add a reusable workflow for publishing.

v1.3.0

  • The install location of the Dart SDK is now available

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=dart-lang/setup-dart&package-manager=github_actions&previous-version=1.6.2&new-version=1.6.4)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- pkgs/http2/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http2/.github/workflows/test-package.yml b/pkgs/http2/.github/workflows/test-package.yml index b82aa5071b..920630a953 100644 --- a/pkgs/http2/.github/workflows/test-package.yml +++ b/pkgs/http2/.github/workflows/test-package.yml @@ -23,7 +23,7 @@ jobs: sdk: [dev] steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b - - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 + - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 with: sdk: ${{ matrix.sdk }} - id: install @@ -50,7 +50,7 @@ jobs: sdk: [3.2, dev] steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b - - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 + - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 with: sdk: ${{ matrix.sdk }} - id: install From 672a7d4e9e0962c2d58051c9623b5d37058ae16d Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Tue, 7 May 2024 08:07:59 -0700 Subject: [PATCH 163/172] blast_repo fixes (dart-lang/http2#147) dependabot --- pkgs/http2/.github/dependabot.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkgs/http2/.github/dependabot.yaml b/pkgs/http2/.github/dependabot.yaml index 439e796b48..bf6b38a4d8 100644 --- a/pkgs/http2/.github/dependabot.yaml +++ b/pkgs/http2/.github/dependabot.yaml @@ -8,3 +8,7 @@ updates: interval: monthly labels: - autosubmit + groups: + github-actions: + patterns: + - "*" From 49a77f3dbadc4faf613ed37ad2bcbcfccb1b6751 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 May 2024 15:10:57 +0000 Subject: [PATCH 164/172] Bump actions/checkout from 4.1.4 to 4.1.5 in the github-actions group (dart-lang/http2#148) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the github-actions group with 1 update: [actions/checkout](https://github.com/actions/checkout). Updates `actions/checkout` from 4.1.4 to 4.1.5
Release notes

Sourced from actions/checkout's releases.

v4.1.5

What's Changed

Full Changelog: https://github.com/actions/checkout/compare/v4.1.4...v4.1.5

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=4.1.4&new-version=4.1.5)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
--- pkgs/http2/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http2/.github/workflows/test-package.yml b/pkgs/http2/.github/workflows/test-package.yml index 920630a953..a4dbf1bda2 100644 --- a/pkgs/http2/.github/workflows/test-package.yml +++ b/pkgs/http2/.github/workflows/test-package.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 with: sdk: ${{ matrix.sdk }} @@ -49,7 +49,7 @@ jobs: os: [ubuntu-latest] sdk: [3.2, dev] steps: - - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 with: sdk: ${{ matrix.sdk }} From 3463b2f324901b6a3c136c64139253c3a4e678a4 Mon Sep 17 00:00:00 2001 From: Sarah Zakarias Date: Thu, 30 May 2024 10:49:24 +0200 Subject: [PATCH 165/172] Add `topics` to package "http2" `pubspec.yaml` (dart-lang/http2#150) --- pkgs/http2/CHANGELOG.md | 1 + pkgs/http2/pubspec.yaml | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index 9674672017..8248505f2b 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -1,6 +1,7 @@ ## 2.3.1-wip - Require Dart 3.2 +- Add topics to `pubspec.yaml` ## 2.3.0 diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index 72528e2926..ac0e139d3c 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -3,6 +3,11 @@ version: 2.3.1-wip description: A HTTP/2 implementation in Dart. repository: https://github.com/dart-lang/http2 +topics: + - http + - network + - protocols + environment: sdk: ^3.2.0 From d932a2914596622a346caf1cff9669d89136c42a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jun 2024 21:42:23 +0000 Subject: [PATCH 166/172] Bump actions/checkout from 4.1.5 to 4.1.6 in the github-actions group (dart-lang/http2#151) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the github-actions group with 1 update: [actions/checkout](https://github.com/actions/checkout). Updates `actions/checkout` from 4.1.5 to 4.1.6
Release notes

Sourced from actions/checkout's releases.

v4.1.6

What's Changed

Full Changelog: https://github.com/actions/checkout/compare/v4.1.5...v4.1.6

Changelog

Sourced from actions/checkout's changelog.

Changelog

v4.1.6

v4.1.5

v4.1.4

v4.1.3

v4.1.2

v4.1.1

v4.1.0

v4.0.0

v3.6.0

v3.5.3

v3.5.2

v3.5.1

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=4.1.5&new-version=4.1.6)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
--- pkgs/http2/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http2/.github/workflows/test-package.yml b/pkgs/http2/.github/workflows/test-package.yml index a4dbf1bda2..b82cea50a1 100644 --- a/pkgs/http2/.github/workflows/test-package.yml +++ b/pkgs/http2/.github/workflows/test-package.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 with: sdk: ${{ matrix.sdk }} @@ -49,7 +49,7 @@ jobs: os: [ubuntu-latest] sdk: [3.2, dev] steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 with: sdk: ${{ matrix.sdk }} From db395cb534dbad0d7d7616e5a0c15d55c0119231 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 21:10:38 +0000 Subject: [PATCH 167/172] Bump the github-actions group with 2 updates (dart-lang/http2#154) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the github-actions group with 2 updates: [actions/checkout](https://github.com/actions/checkout) and [dart-lang/setup-dart](https://github.com/dart-lang/setup-dart). Updates `actions/checkout` from 4.1.6 to 4.1.7
Release notes

Sourced from actions/checkout's releases.

v4.1.7

What's Changed

New Contributors

Full Changelog: https://github.com/actions/checkout/compare/v4.1.6...v4.1.7

Changelog

Sourced from actions/checkout's changelog.

Changelog

v4.1.7

v4.1.6

v4.1.5

v4.1.4

v4.1.3

v4.1.2

v4.1.1

v4.1.0

v4.0.0

v3.6.0

v3.5.3

... (truncated)

Commits

Updates `dart-lang/setup-dart` from 1.6.4 to 1.6.5
Release notes

Sourced from dart-lang/setup-dart's releases.

v1.6.5

dart-lang/http2#118: dart-lang/setup-dartdart-lang/http2#118

Changelog

Sourced from dart-lang/setup-dart's changelog.

v1.6.5

dart-lang/http2#118: dart-lang/setup-dartdart-lang/http2#118

v1.6.4

  • Rebuild JS code.

v1.6.3

v1.6.2

v1.6.1

  • Updated the google storage url for main channel releases.

v1.6.0

  • Enable provisioning of the latest Dart SDK patch release by specifying just the major and minor version (e.g. 3.2).

v1.5.1

  • No longer test the setup-dart action on pre-2.12 SDKs.
  • Upgrade JS interop code to use extension types (the new name for inline classes).
  • The upcoming rename of the be channel to main is now supported with forward compatibility that switches when the rename happens.

v1.5.0

  • Re-wrote the implementation of the action into Dart.
  • Auto-detect the platform architecture (x64, ia32, arm, arm64).
  • Improved the caching and download resilience of the sdk.
  • Added a new action output: dart-version - the installed version of the sdk.

v1.4.0

... (truncated)

Commits

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
--- pkgs/http2/.github/workflows/test-package.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkgs/http2/.github/workflows/test-package.yml b/pkgs/http2/.github/workflows/test-package.yml index b82cea50a1..90a5c8e6ce 100644 --- a/pkgs/http2/.github/workflows/test-package.yml +++ b/pkgs/http2/.github/workflows/test-package.yml @@ -22,8 +22,8 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 - - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + - uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672 with: sdk: ${{ matrix.sdk }} - id: install @@ -49,8 +49,8 @@ jobs: os: [ubuntu-latest] sdk: [3.2, dev] steps: - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 - - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + - uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672 with: sdk: ${{ matrix.sdk }} - id: install From 4d1b69ad14eaeb7630fb8bdf7822a4840c746eea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 21:32:54 +0000 Subject: [PATCH 168/172] Bump actions/checkout from 4.1.7 to 4.2.0 in the github-actions group (dart-lang/http2#156) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the github-actions group with 1 update: [actions/checkout](https://github.com/actions/checkout). Updates `actions/checkout` from 4.1.7 to 4.2.0
Release notes

Sourced from actions/checkout's releases.

v4.2.0

What's Changed

New Contributors

Full Changelog: https://github.com/actions/checkout/compare/v4.1.7...v4.2.0

Changelog

Sourced from actions/checkout's changelog.

Changelog

v4.2.0

v4.1.7

v4.1.6

v4.1.5

v4.1.4

v4.1.3

v4.1.2

v4.1.1

v4.1.0

v4.0.0

v3.6.0

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=4.1.7&new-version=4.2.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
--- pkgs/http2/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http2/.github/workflows/test-package.yml b/pkgs/http2/.github/workflows/test-package.yml index 90a5c8e6ce..9fd5c8a050 100644 --- a/pkgs/http2/.github/workflows/test-package.yml +++ b/pkgs/http2/.github/workflows/test-package.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672 with: sdk: ${{ matrix.sdk }} @@ -49,7 +49,7 @@ jobs: os: [ubuntu-latest] sdk: [3.2, dev] steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672 with: sdk: ${{ matrix.sdk }} From ca023aa260ae46b4e12b0b17b5f03313a3b31f23 Mon Sep 17 00:00:00 2001 From: Moritz Date: Thu, 17 Oct 2024 15:45:28 +0200 Subject: [PATCH 169/172] Add issue template and other fixes --- .github/ISSUE_TEMPLATE/http2.md | 5 +++++ pkgs/http2/CONTRIBUTING.md | 33 --------------------------------- pkgs/http2/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 34 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/http2.md delete mode 100644 pkgs/http2/CONTRIBUTING.md diff --git a/.github/ISSUE_TEMPLATE/http2.md b/.github/ISSUE_TEMPLATE/http2.md new file mode 100644 index 0000000000..9c982e75a8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/http2.md @@ -0,0 +1,5 @@ +--- +name: "package:http2" +about: "Create a bug or file a feature request against package:http2." +labels: "package:http2" +--- \ No newline at end of file diff --git a/pkgs/http2/CONTRIBUTING.md b/pkgs/http2/CONTRIBUTING.md deleted file mode 100644 index 6f5e0ea67d..0000000000 --- a/pkgs/http2/CONTRIBUTING.md +++ /dev/null @@ -1,33 +0,0 @@ -Want to contribute? Great! First, read this page (including the small print at -the end). - -### Before you contribute -Before we can use your code, you must sign the -[Google Individual Contributor License Agreement](https://cla.developers.google.com/about/google-individual) -(CLA), which you can do online. The CLA is necessary mainly because you own the -copyright to your changes, even after your contribution becomes part of our -codebase, so we need your permission to use and distribute your code. We also -need to be sure of various other things—for instance that you'll tell us if you -know that your code infringes on other people's patents. You don't have to sign -the CLA until after you've submitted your code for review and a member has -approved it, but you must do it before we can put your code into our codebase. - -Before you start working on a larger contribution, you should get in touch with -us first through the issue tracker with your idea so that we can help out and -possibly guide you. Coordinating up front makes it much easier to avoid -frustration later on. - -### Code reviews -All submissions, including submissions by project members, require review. - -### File headers -All files in the project must start with the following header. - - // 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. - -### The small print -Contributions made by corporations are covered by a different agreement than the -one above, the -[Software Grant and Corporate Contributor License Agreement](https://developers.google.com/open-source/cla/corporate). diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index ac0e139d3c..a28d1b1934 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -1,7 +1,7 @@ name: http2 version: 2.3.1-wip description: A HTTP/2 implementation in Dart. -repository: https://github.com/dart-lang/http2 +repository: https://github.com/dart-lang/http/tree/master/pkgs/http2 topics: - http From ced6bc8e4520291ffd15f69da24e889638f94e87 Mon Sep 17 00:00:00 2001 From: Moritz Date: Thu, 17 Oct 2024 15:51:38 +0200 Subject: [PATCH 170/172] Moving fixes --- .github/labeler.yml | 6 ++- .../workflows/http2.yaml | 12 ++++-- README.md | 1 + pkgs/http2/.github/dependabot.yaml | 14 ------- pkgs/http2/.github/workflows/health.yaml | 15 -------- pkgs/http2/.github/workflows/no-response.yml | 37 ------------------- .../.github/workflows/post_summaries.yaml | 17 --------- pkgs/http2/.github/workflows/publish.yaml | 17 --------- pkgs/http2/CHANGELOG.md | 3 +- pkgs/http2/README.md | 9 ----- pkgs/http2/pubspec.yaml | 2 +- 11 files changed, 18 insertions(+), 115 deletions(-) rename pkgs/http2/.github/workflows/test-package.yml => .github/workflows/http2.yaml (89%) delete mode 100644 pkgs/http2/.github/dependabot.yaml delete mode 100644 pkgs/http2/.github/workflows/health.yaml delete mode 100644 pkgs/http2/.github/workflows/no-response.yml delete mode 100644 pkgs/http2/.github/workflows/post_summaries.yaml delete mode 100644 pkgs/http2/.github/workflows/publish.yaml diff --git a/.github/labeler.yml b/.github/labeler.yml index 6d477b3173..1fc30162b4 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -16,6 +16,10 @@ - changed-files: - any-glob-to-any-file: 'pkgs/http/**' +'package:http2': + - changed-files: + - any-glob-to-any-file: 'pkgs/http2/**' + 'package:http_client_conformance_tests': - changed-files: - - any-glob-to-any-file: 'pkgs/http_client_conformance_tests/**' \ No newline at end of file + - any-glob-to-any-file: 'pkgs/http_client_conformance_tests/**' diff --git a/pkgs/http2/.github/workflows/test-package.yml b/.github/workflows/http2.yaml similarity index 89% rename from pkgs/http2/.github/workflows/test-package.yml rename to .github/workflows/http2.yaml index 9fd5c8a050..0d698dba2f 100644 --- a/pkgs/http2/.github/workflows/test-package.yml +++ b/.github/workflows/http2.yaml @@ -1,11 +1,17 @@ name: Dart CI on: - # Run on PRs and pushes to the default branch. push: - branches: [ master ] + branches: + - main + - master + paths: + - '.github/workflows/http2.yaml' + - 'pkgs/http2/**' pull_request: - branches: [ master ] + paths: + - '.github/workflows/http2.yaml' + - 'pkgs/http2/**' schedule: - cron: "0 0 * * 0" diff --git a/README.md b/README.md index 14e6a7d6b0..0c39b58811 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ and the browser. | [cupertino_http](pkgs/cupertino_http/) | A macOS/iOS Flutter plugin that provides access to the [Foundation URL Loading System](https://developer.apple.com/documentation/foundation/url_loading_system). | [![pub package](https://img.shields.io/pub/v/cupertino_http.svg)](https://pub.dev/packages/cupertino_http) | | [flutter_http_example](pkgs/flutter_http_example/) | An Flutter app that demonstrates how to configure and use `package:http`. | — | | [http](pkgs/http/) | A composable, multi-platform, Future-based API for HTTP requests. | [![pub package](https://img.shields.io/pub/v/http.svg)](https://pub.dev/packages/http) | +| [http2](pkgs/http2/) | A HTTP/2 implementation in Dart. | [![pub package](https://img.shields.io/pub/v/http2.svg)](https://pub.dev/packages/http2) | | [http_client_conformance_tests](pkgs/http_client_conformance_tests/) | A library that tests whether implementations of package:http's `Client` class behave as expected. | | | [http_profile](pkgs/http_profile/) | A library used by HTTP client authors to integrate with the DevTools Network View. | [![pub package](https://img.shields.io/pub/v/http_profile.svg)](https://pub.dev/packages/http_profile) | | [ok_http](pkgs/ok_http/) | An Android Flutter plugin that provides access to the [OkHttp](https://square.github.io/okhttp/) HTTP client and the OkHttp [WebSocket](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-web-socket/index.html) API. | [![pub package](https://img.shields.io/pub/v/ok_http.svg)](https://pub.dev/packages/ok_http) | diff --git a/pkgs/http2/.github/dependabot.yaml b/pkgs/http2/.github/dependabot.yaml deleted file mode 100644 index bf6b38a4d8..0000000000 --- a/pkgs/http2/.github/dependabot.yaml +++ /dev/null @@ -1,14 +0,0 @@ -# Dependabot configuration file. -version: 2 - -updates: - - package-ecosystem: github-actions - directory: / - schedule: - interval: monthly - labels: - - autosubmit - groups: - github-actions: - patterns: - - "*" diff --git a/pkgs/http2/.github/workflows/health.yaml b/pkgs/http2/.github/workflows/health.yaml deleted file mode 100644 index 9f47a032f7..0000000000 --- a/pkgs/http2/.github/workflows/health.yaml +++ /dev/null @@ -1,15 +0,0 @@ -name: Health - -on: - pull_request: - branches: [ master ] - types: [opened, synchronize, reopened, labeled, unlabeled] - -jobs: - health: - uses: dart-lang/ecosystem/.github/workflows/health.yaml@main - with: - coverage_web: false - sdk: dev - permissions: - pull-requests: write diff --git a/pkgs/http2/.github/workflows/no-response.yml b/pkgs/http2/.github/workflows/no-response.yml deleted file mode 100644 index ab1ac49842..0000000000 --- a/pkgs/http2/.github/workflows/no-response.yml +++ /dev/null @@ -1,37 +0,0 @@ -# A workflow to close issues where the author hasn't responded to a request for -# more information; see https://github.com/actions/stale. - -name: No Response - -# Run as a daily cron. -on: - schedule: - # Every day at 8am - - cron: '0 8 * * *' - -# All permissions not specified are set to 'none'. -permissions: - issues: write - pull-requests: write - -jobs: - no-response: - runs-on: ubuntu-latest - if: ${{ github.repository_owner == 'dart-lang' }} - steps: - - uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e - with: - # Don't automatically mark inactive issues+PRs as stale. - days-before-stale: -1 - # Close needs-info issues and PRs after 14 days of inactivity. - days-before-close: 14 - stale-issue-label: "needs-info" - close-issue-message: > - Without additional information we're not able to resolve this issue. - Feel free to add more info or respond to any questions above and we - can reopen the case. Thanks for your contribution! - stale-pr-label: "needs-info" - close-pr-message: > - Without additional information we're not able to resolve this PR. - Feel free to add more info or respond to any questions above. - Thanks for your contribution! diff --git a/pkgs/http2/.github/workflows/post_summaries.yaml b/pkgs/http2/.github/workflows/post_summaries.yaml deleted file mode 100644 index e082efe95a..0000000000 --- a/pkgs/http2/.github/workflows/post_summaries.yaml +++ /dev/null @@ -1,17 +0,0 @@ -name: Comment on the pull request - -on: - # Trigger this workflow after the Health workflow completes. This workflow will have permissions to - # do things like create comments on the PR, even if the original workflow couldn't. - workflow_run: - workflows: - - Health - - Publish - types: - - completed - -jobs: - upload: - uses: dart-lang/ecosystem/.github/workflows/post_summaries.yaml@main - permissions: - pull-requests: write diff --git a/pkgs/http2/.github/workflows/publish.yaml b/pkgs/http2/.github/workflows/publish.yaml deleted file mode 100644 index 27157a046a..0000000000 --- a/pkgs/http2/.github/workflows/publish.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# A CI configuration to auto-publish pub packages. - -name: Publish - -on: - pull_request: - branches: [ master ] - push: - tags: [ 'v[0-9]+.[0-9]+.[0-9]+' ] - -jobs: - publish: - if: ${{ github.repository_owner == 'dart-lang' }} - uses: dart-lang/ecosystem/.github/workflows/publish.yaml@main - permissions: - id-token: write # Required for authentication using OIDC - pull-requests: write # Required for writing the pull request note diff --git a/pkgs/http2/CHANGELOG.md b/pkgs/http2/CHANGELOG.md index 8248505f2b..6e482a92b3 100644 --- a/pkgs/http2/CHANGELOG.md +++ b/pkgs/http2/CHANGELOG.md @@ -1,7 +1,8 @@ -## 2.3.1-wip +## 2.3.1 - Require Dart 3.2 - Add topics to `pubspec.yaml` +- Move to `dart-lang/http` monorepo. ## 2.3.0 diff --git a/pkgs/http2/README.md b/pkgs/http2/README.md index 36a8aaa042..edb75c99d6 100644 --- a/pkgs/http2/README.md +++ b/pkgs/http2/README.md @@ -1,4 +1,3 @@ -[![Dart CI](https://github.com/dart-lang/http2/actions/workflows/test-package.yml/badge.svg)](https://github.com/dart-lang/http2/actions/workflows/test-package.yml) [![pub package](https://img.shields.io/pub/v/http2.svg)](https://pub.dev/packages/http2) [![package publisher](https://img.shields.io/pub/publisher/http2.svg)](https://pub.dev/packages/http2/publisher) @@ -54,11 +53,3 @@ Future main() async { An example with better error handling is available [here][example]. See the [API docs][api] for more details. - -## Features and bugs - -Please file feature requests and bugs at the [issue tracker][tracker]. - -[tracker]: https://github.com/dart-lang/http2/issues -[api]: https://pub.dev/documentation/http2/latest/ -[example]: https://github.com/dart-lang/http2/blob/master/example/display_headers.dart diff --git a/pkgs/http2/pubspec.yaml b/pkgs/http2/pubspec.yaml index a28d1b1934..7d9e6b851b 100644 --- a/pkgs/http2/pubspec.yaml +++ b/pkgs/http2/pubspec.yaml @@ -1,5 +1,5 @@ name: http2 -version: 2.3.1-wip +version: 2.3.1 description: A HTTP/2 implementation in Dart. repository: https://github.com/dart-lang/http/tree/master/pkgs/http2 From a6eba74b6c7ce462571aa77257d9c14e97445863 Mon Sep 17 00:00:00 2001 From: Moritz Date: Thu, 17 Oct 2024 15:51:59 +0200 Subject: [PATCH 171/172] Rename wf --- .github/workflows/http2.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/http2.yaml b/.github/workflows/http2.yaml index 0d698dba2f..70e3346b01 100644 --- a/.github/workflows/http2.yaml +++ b/.github/workflows/http2.yaml @@ -1,4 +1,4 @@ -name: Dart CI +name: package:http2 on: push: From e7db29d6f805bc10dc712b75cb941c768f2884a7 Mon Sep 17 00:00:00 2001 From: Moritz Date: Fri, 18 Oct 2024 10:57:56 +0200 Subject: [PATCH 172/172] Fixes as per review --- .github/workflows/http2.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/http2.yaml b/.github/workflows/http2.yaml index 70e3346b01..eb3dc28010 100644 --- a/.github/workflows/http2.yaml +++ b/.github/workflows/http2.yaml @@ -3,7 +3,6 @@ name: package:http2 on: push: branches: - - main - master paths: - '.github/workflows/http2.yaml' @@ -15,6 +14,10 @@ on: schedule: - cron: "0 0 * * 0" +defaults: + run: + working-directory: pkgs/http2/ + env: PUB_ENVIRONMENT: bot.github