From fc2ccf44be2f107ecef9ffc0fe83439002765a1e Mon Sep 17 00:00:00 2001 From: "nweiz@google.com" Date: Mon, 2 Jun 2014 21:26:39 +0000 Subject: [PATCH 001/126] Fix a library name conflict in pkg/http_parser. R=rnystrom@google.com BUG=19117 Review URL: https://codereview.chromium.org//311663002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/http_parser@36902 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/http_parser/CHANGELOG.md | 16 + pkgs/http_parser/LICENSE | 26 + pkgs/http_parser/README.md | 3 + pkgs/http_parser/lib/http_parser.dart | 9 + pkgs/http_parser/lib/src/bytes_builder.dart | 214 +++++ pkgs/http_parser/lib/src/http_date.dart | 152 ++++ pkgs/http_parser/lib/src/media_type.dart | 161 ++++ pkgs/http_parser/lib/src/web_socket.dart | 936 ++++++++++++++++++++ pkgs/http_parser/pubspec.yaml | 14 + pkgs/http_parser/test/http_date_test.dart | 347 ++++++++ pkgs/http_parser/test/media_type_test.dart | 163 ++++ pkgs/http_parser/test/web_socket_test.dart | 95 ++ 12 files changed, 2136 insertions(+) create mode 100644 pkgs/http_parser/CHANGELOG.md create mode 100644 pkgs/http_parser/LICENSE create mode 100644 pkgs/http_parser/README.md create mode 100644 pkgs/http_parser/lib/http_parser.dart create mode 100644 pkgs/http_parser/lib/src/bytes_builder.dart create mode 100644 pkgs/http_parser/lib/src/http_date.dart create mode 100644 pkgs/http_parser/lib/src/media_type.dart create mode 100644 pkgs/http_parser/lib/src/web_socket.dart create mode 100644 pkgs/http_parser/pubspec.yaml create mode 100644 pkgs/http_parser/test/http_date_test.dart create mode 100644 pkgs/http_parser/test/media_type_test.dart create mode 100644 pkgs/http_parser/test/web_socket_test.dart diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md new file mode 100644 index 0000000000..328e77911a --- /dev/null +++ b/pkgs/http_parser/CHANGELOG.md @@ -0,0 +1,16 @@ +## 0.0.2+3 + +* Fix a library name conflict. + +## 0.0.2+2 + +* Fixes for HTTP date formatting. + +## 0.0.2+1 + +* Minor code refactoring. + +## 0.0.2 + +* Added `CompatibleWebSocket`, for platform- and API-independent support for the + WebSocket API. diff --git a/pkgs/http_parser/LICENSE b/pkgs/http_parser/LICENSE new file mode 100644 index 0000000000..5c60afea39 --- /dev/null +++ b/pkgs/http_parser/LICENSE @@ -0,0 +1,26 @@ +Copyright 2014, 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/http_parser/README.md b/pkgs/http_parser/README.md new file mode 100644 index 0000000000..edfa71ae0d --- /dev/null +++ b/pkgs/http_parser/README.md @@ -0,0 +1,3 @@ +`http_parser` is a platform-independent package for parsing and serializing +various HTTP-related formats. It's designed to be usable on both the browser and +the server, and thus avoids referencing any types from `dart:io` or `dart:html`. diff --git a/pkgs/http_parser/lib/http_parser.dart b/pkgs/http_parser/lib/http_parser.dart new file mode 100644 index 0000000000..0aa9cea6b7 --- /dev/null +++ b/pkgs/http_parser/lib/http_parser.dart @@ -0,0 +1,9 @@ +// Copyright (c) 2014, 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 http_parser; + +export 'src/http_date.dart'; +export 'src/media_type.dart'; +export 'src/web_socket.dart'; diff --git a/pkgs/http_parser/lib/src/bytes_builder.dart b/pkgs/http_parser/lib/src/bytes_builder.dart new file mode 100644 index 0000000000..446e1751ba --- /dev/null +++ b/pkgs/http_parser/lib/src/bytes_builder.dart @@ -0,0 +1,214 @@ +// Copyright (c) 2013, 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 is a copy of "dart:io"'s BytesBuilder implementation, from +// sdk/lib/io/bytes_builder.dart. It's copied here to make it available to +// non-"dart:io" applications (issue 18348). +// +// Because it's copied directly, there are no modifications from the original. +library http_parser.bytes_builder; + +import 'dart:math'; +import 'dart:typed_data'; + +/** + * Builds a list of bytes, allowing bytes and lists of bytes to be added at the + * end. + * + * Used to efficiently collect bytes and lists of bytes. + */ +abstract class BytesBuilder { + /** + * Construct a new empty [BytesBuilder]. + * + * If [copy] is true, the data is always copied when added to the list. If + * it [copy] is false, the data is only copied if needed. That means that if + * the lists are changed after added to the [BytesBuilder], it may effect the + * output. Default is `true`. + */ + factory BytesBuilder({bool copy: true}) { + if (copy) { + return new _CopyingBytesBuilder(); + } else { + return new _BytesBuilder(); + } + } + + /** + * Appends [bytes] to the current contents of the builder. + * + * Each value of [bytes] will be bit-representation truncated to the range + * 0 .. 255. + */ + void add(List bytes); + + /** + * Append [byte] to the current contents of the builder. + * + * The [byte] will be bit-representation truncated to the range 0 .. 255. + */ + void addByte(int byte); + + /** + * Returns the contents of `this` and clears `this`. + * + * The list returned is a view of the the internal buffer, limited to the + * [length]. + */ + List takeBytes(); + + /** + * Returns a copy of the current contents of the builder. + * + * Leaves the contents of the builder intact. + */ + List toBytes(); + + /** + * The number of bytes in the builder. + */ + int get length; + + /** + * Returns `true` if the buffer is empty. + */ + bool get isEmpty; + + /** + * Returns `true` if the buffer is not empty. + */ + bool get isNotEmpty; + + /** + * Clear the contents of the builder. + */ + void clear(); +} + + +class _CopyingBytesBuilder implements BytesBuilder { + // Start with 1024 bytes. + static const int _INIT_SIZE = 1024; + + int _length = 0; + Uint8List _buffer; + + void add(List bytes) { + int bytesLength = bytes.length; + if (bytesLength == 0) return; + int required = _length + bytesLength; + if (_buffer == null) { + int size = _pow2roundup(required); + size = max(size, _INIT_SIZE); + _buffer = new Uint8List(size); + } else if (_buffer.length < required) { + // We will create a list in the range of 2-4 times larger than + // required. + int size = _pow2roundup(required) * 2; + var newBuffer = new Uint8List(size); + newBuffer.setRange(0, _buffer.length, _buffer); + _buffer = newBuffer; + } + assert(_buffer.length >= required); + if (bytes is Uint8List) { + _buffer.setRange(_length, required, bytes); + } else { + for (int i = 0; i < bytesLength; i++) { + _buffer[_length + i] = bytes[i]; + } + } + _length = required; + } + + void addByte(int byte) => add([byte]); + + List takeBytes() { + if (_buffer == null) return new Uint8List(0); + var buffer = new Uint8List.view(_buffer.buffer, 0, _length); + clear(); + return buffer; + } + + List toBytes() { + if (_buffer == null) return new Uint8List(0); + return new Uint8List.fromList( + new Uint8List.view(_buffer.buffer, 0, _length)); + } + + int get length => _length; + + bool get isEmpty => _length == 0; + + bool get isNotEmpty => _length != 0; + + void clear() { + _length = 0; + _buffer = null; + } + + int _pow2roundup(int x) { + --x; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + return x + 1; + } +} + + +class _BytesBuilder implements BytesBuilder { + int _length = 0; + final List _chunks = []; + + void add(List bytes) { + if (bytes is! Uint8List) { + bytes = new Uint8List.fromList(bytes); + } + _chunks.add(bytes); + _length += bytes.length; + } + + void addByte(int byte) => add([byte]); + + List takeBytes() { + if (_chunks.length == 0) return new Uint8List(0); + if (_chunks.length == 1) { + var buffer = _chunks.single; + clear(); + return buffer; + } + var buffer = new Uint8List(_length); + int offset = 0; + for (var chunk in _chunks) { + buffer.setRange(offset, offset + chunk.length, chunk); + offset += chunk.length; + } + clear(); + return buffer; + } + + List toBytes() { + if (_chunks.length == 0) return new Uint8List(0); + var buffer = new Uint8List(_length); + int offset = 0; + for (var chunk in _chunks) { + buffer.setRange(offset, offset + chunk.length, chunk); + offset += chunk.length; + } + return buffer; + } + + int get length => _length; + + bool get isEmpty => _length == 0; + + bool get isNotEmpty => _length != 0; + + void clear() { + _length = 0; + _chunks.clear(); + } +} diff --git a/pkgs/http_parser/lib/src/http_date.dart b/pkgs/http_parser/lib/src/http_date.dart new file mode 100644 index 0000000000..0626c72e21 --- /dev/null +++ b/pkgs/http_parser/lib/src/http_date.dart @@ -0,0 +1,152 @@ +// Copyright (c) 2014, 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 http_parser.http_date; + +import 'package:string_scanner/string_scanner.dart'; + +const _WEEKDAYS = const ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]; +const _MONTHS = const ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", + "Sep", "Oct", "Nov", "Dec"]; + +final _shortWeekdayRegExp = new RegExp(r"Mon|Tue|Wed|Thu|Fri|Sat|Sun"); +final _longWeekdayRegExp = + new RegExp(r"Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday"); +final _monthRegExp = + new RegExp(r"Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec"); +final _digitRegExp = new RegExp(r"\d+"); + +/// Return a HTTP-formatted string representation of [date]. +/// +/// This follows [RFC 822](http://tools.ietf.org/html/rfc822) as updated by [RFC +/// 1123](http://tools.ietf.org/html/rfc1123). +String formatHttpDate(DateTime date) { + date = date.toUtc(); + var buffer = new StringBuffer() + ..write(_WEEKDAYS[date.weekday - 1]) + ..write(", ") + ..write(date.day <= 9 ? "0" : "") + ..write(date.day.toString()) + ..write(" ") + ..write(_MONTHS[date.month - 1]) + ..write(" ") + ..write(date.year.toString()) + ..write(date.hour <= 9 ? " 0" : " ") + ..write(date.hour.toString()) + ..write(date.minute <= 9 ? ":0" : ":") + ..write(date.minute.toString()) + ..write(date.second <= 9 ? ":0" : ":") + ..write(date.second.toString()) + ..write(" GMT"); + return buffer.toString(); +} + +/// Parses an HTTP-formatted date into a UTC [DateTime]. +/// +/// This follows [RFC +/// 2616](http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3). It will +/// throw a [FormatException] if [date] is invalid. +DateTime parseHttpDate(String date) { + try { + var scanner = new StringScanner(date); + + if (scanner.scan(_longWeekdayRegExp)) { + // RFC 850 starts with a long weekday. + scanner.expect(", "); + var day = _parseInt(scanner, 2); + scanner.expect("-"); + var month = _parseMonth(scanner); + scanner.expect("-"); + var year = 1900 + _parseInt(scanner, 2); + scanner.expect(" "); + var time = _parseTime(scanner); + scanner.expect(" GMT"); + scanner.expectDone(); + + return _makeDateTime(year, month, day, time); + } + + // RFC 1123 and asctime both start with a short weekday. + scanner.expect(_shortWeekdayRegExp); + if (scanner.scan(", ")) { + // RFC 1123 follows the weekday with a comma. + var day = _parseInt(scanner, 2); + scanner.expect(" "); + var month = _parseMonth(scanner); + scanner.expect(" "); + var year = _parseInt(scanner, 4); + scanner.expect(" "); + var time = _parseTime(scanner); + scanner.expect(" GMT"); + scanner.expectDone(); + + return _makeDateTime(year, month, day, time); + } + + // asctime follows the weekday with a space. + scanner.expect(" "); + var month = _parseMonth(scanner); + scanner.expect(" "); + var day = scanner.scan(" ") ? + _parseInt(scanner, 1) : + _parseInt(scanner, 2); + scanner.expect(" "); + var time = _parseTime(scanner); + scanner.expect(" "); + var year = _parseInt(scanner, 4); + scanner.expectDone(); + + return _makeDateTime(year, month, day, time); + } on FormatException catch (error) { + throw new FormatException('Invalid HTTP date "$date": ${error.message}'); + } +} + +/// Parses a short-form month name to a form accepted by [DateTime]. +int _parseMonth(StringScanner scanner) { + scanner.expect(_monthRegExp); + // DateTime uses 1-indexed months. + return _MONTHS.indexOf(scanner.lastMatch[0]) + 1; +} + +/// Parses an int an enforces that it has exactly [digits] digits. +int _parseInt(StringScanner scanner, int digits) { + scanner.expect(_digitRegExp); + if (scanner.lastMatch[0].length != digits) { + scanner.error("expected a $digits-digit number."); + } + + return int.parse(scanner.lastMatch[0]); +} + +/// Parses an timestamp of the form "HH:MM:SS" on a 24-hour clock. +DateTime _parseTime(StringScanner scanner) { + var hours = _parseInt(scanner, 2); + if (hours >= 24) scanner.error("hours may not be greater than 24."); + scanner.expect(':'); + + var minutes = _parseInt(scanner, 2); + if (minutes >= 60) scanner.error("minutes may not be greater than 60."); + scanner.expect(':'); + + var seconds = _parseInt(scanner, 2); + if (seconds >= 60) scanner.error("seconds may not be greater than 60."); + + return new DateTime(1, 1, 1, hours, minutes, seconds); +} + +/// Returns a UTC [DateTime] from the given components. +/// +/// Validates that [day] is a valid day for [month]. If it's not, throws a +/// [FormatException]. +DateTime _makeDateTime(int year, int month, int day, DateTime time) { + var dateTime = new DateTime.utc( + year, month, day, time.hour, time.minute, time.second); + + // If [day] was too large, it will cause [month] to overflow. + if (dateTime.month != month) { + throw new FormatException("invalid day '$day' for month '$month'."); + } + return dateTime; +} diff --git a/pkgs/http_parser/lib/src/media_type.dart b/pkgs/http_parser/lib/src/media_type.dart new file mode 100644 index 0000000000..7a1ff23ba7 --- /dev/null +++ b/pkgs/http_parser/lib/src/media_type.dart @@ -0,0 +1,161 @@ +// Copyright (c) 2014, 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 http_parser.media_type; + +import 'package:collection/collection.dart'; +import 'package:string_scanner/string_scanner.dart'; + +// All of the following regular expressions come from section 2.2 of the HTTP +// spec: http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html +final _lws = new RegExp(r"(?:\r\n)?[ \t]+"); +final _token = new RegExp(r'[^()<>@,;:"\\/[\]?={} \t\x00-\x1F\x7F]+'); +final _quotedString = new RegExp(r'"(?:[^"\x00-\x1F\x7F]|\\.)*"'); +final _quotedPair = new RegExp(r'\\(.)'); + +/// A regular expression matching any number of [_lws] productions in a row. +final _whitespace = new RegExp("(?:${_lws.pattern})*"); + +/// A regular expression matching a character that is not a valid HTTP token. +final _nonToken = new RegExp(r'[()<>@,;:"\\/\[\]?={} \t\x00-\x1F\x7F]'); + +/// A regular expression matching a character that needs to be backslash-escaped +/// in a quoted string. +final _escapedChar = new RegExp(r'["\x00-\x1F\x7F]'); + +/// A class representing an HTTP media type, as used in Accept and Content-Type +/// headers. +/// +/// This is immutable; new instances can be created based on an old instance by +/// calling [change]. +class MediaType { + /// The primary identifier of the MIME type. + final String type; + + /// The secondary identifier of the MIME type. + final String subtype; + + /// The parameters to the media type. + /// + /// This map is immutable. + final Map parameters; + + /// The media type's MIME type. + String get mimeType => "$type/$subtype"; + + /// Parses a media type. + /// + /// This will throw a FormatError if the media type is invalid. + factory MediaType.parse(String mediaType) { + // This parsing is based on sections 3.6 and 3.7 of the HTTP spec: + // http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html. + try { + var scanner = new StringScanner(mediaType); + scanner.scan(_whitespace); + scanner.expect(_token); + var type = scanner.lastMatch[0]; + scanner.expect('/'); + scanner.expect(_token); + var subtype = scanner.lastMatch[0]; + scanner.scan(_whitespace); + + var parameters = {}; + while (scanner.scan(';')) { + scanner.scan(_whitespace); + scanner.expect(_token); + var attribute = scanner.lastMatch[0]; + scanner.expect('='); + + var value; + if (scanner.scan(_token)) { + value = scanner.lastMatch[0]; + } else { + scanner.expect(_quotedString); + var quotedString = scanner.lastMatch[0]; + value = quotedString.substring(1, quotedString.length - 1). + replaceAllMapped(_quotedPair, (match) => match[1]); + } + + scanner.scan(_whitespace); + parameters[attribute] = value; + } + + scanner.expectDone(); + return new MediaType(type, subtype, parameters); + } on FormatException catch (error) { + throw new FormatException( + 'Invalid media type "$mediaType": ${error.message}'); + } + } + + MediaType(this.type, this.subtype, [Map parameters]) + : this.parameters = new UnmodifiableMapView( + parameters == null ? {} : new Map.from(parameters)); + + /// Returns a copy of this [MediaType] with some fields altered. + /// + /// [type] and [subtype] alter the corresponding fields. [mimeType] is parsed + /// and alters both the [type] and [subtype] fields; it cannot be passed along + /// with [type] or [subtype]. + /// + /// [parameters] overwrites and adds to the corresponding field. If + /// [clearParameters] is passed, it replaces the corresponding field entirely + /// instead. + MediaType change({String type, String subtype, String mimeType, + Map parameters, bool clearParameters: false}) { + if (mimeType != null) { + if (type != null) { + throw new ArgumentError("You may not pass both [type] and [mimeType]."); + } else if (subtype != null) { + throw new ArgumentError("You may not pass both [subtype] and " + "[mimeType]."); + } + + var segments = mimeType.split('/'); + if (segments.length != 2) { + throw new FormatException('Invalid mime type "$mimeType".'); + } + + type = segments[0]; + subtype = segments[1]; + } + + if (type == null) type = this.type; + if (subtype == null) subtype = this.subtype; + if (parameters == null) parameters = {}; + + if (!clearParameters) { + var newParameters = parameters; + parameters = new Map.from(this.parameters); + parameters.addAll(newParameters); + } + + return new MediaType(type, subtype, parameters); + } + + /// Converts the media type to a string. + /// + /// This will produce a valid HTTP media type. + String toString() { + var buffer = new StringBuffer() + ..write(type) + ..write("/") + ..write(subtype); + + parameters.forEach((attribute, value) { + buffer.write("; $attribute="); + if (_nonToken.hasMatch(value)) { + buffer + ..write('"') + ..write(value.replaceAllMapped( + _escapedChar, (match) => "\\" + match[0])) + ..write('"'); + } else { + buffer.write(value); + } + }); + + return buffer.toString(); + } +} diff --git a/pkgs/http_parser/lib/src/web_socket.dart b/pkgs/http_parser/lib/src/web_socket.dart new file mode 100644 index 0000000000..77c9d2b873 --- /dev/null +++ b/pkgs/http_parser/lib/src/web_socket.dart @@ -0,0 +1,936 @@ +// Copyright (c) 2014, 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 http_parser.web_socket; + +import 'dart:async'; +import 'dart:convert'; +import 'dart:math'; +import 'dart:typed_data'; + +import 'package:crypto/crypto.dart'; + +import 'bytes_builder.dart'; + +/// An implementation of the WebSocket protocol that's not specific to "dart:io" +/// or to any particular HTTP API. +/// +/// Because this is HTTP-API-agnostic, it doesn't handle the initial [WebSocket +/// handshake][]. This needs to be handled manually by the user of the code. +/// Once that's been done, [new CompatibleWebSocket] can be called with the +/// underlying socket and it will handle the remainder of the protocol. +/// +/// [WebSocket handshake]: https://tools.ietf.org/html/rfc6455#section-4 +abstract class CompatibleWebSocket implements Stream, StreamSink { + /// The interval for sending ping signals. + /// + /// If a ping message is not answered by a pong message from the peer, the + /// `WebSocket` is assumed disconnected and the connection is closed with a + /// [WebSocketStatus.GOING_AWAY] close code. When a ping signal is sent, the + /// pong message must be received within [pingInterval]. + /// + /// There are never two outstanding pings at any given time, and the next ping + /// timer starts when the pong is received. + /// + /// By default, the [pingInterval] is `null`, indicating that ping messages + /// are disabled. + Duration pingInterval; + + /// The [close code][] set when the WebSocket connection is closed. + /// + /// [close code]: https://tools.ietf.org/html/rfc6455#section-7.1.5 + /// + /// Before the connection has been closed, this will be `null`. + int get closeCode; + + /// The [close reason][] set when the WebSocket connection is closed. + /// + /// [close reason]: https://tools.ietf.org/html/rfc6455#section-7.1.6 + /// + /// Before the connection has been closed, this will be `null`. + String get closeReason; + + /// Signs a `Sec-WebSocket-Key` header sent by a WebSocket client as part of + /// the [initial handshake]. + /// + /// The return value should be sent back to the client in a + /// `Sec-WebSocket-Accept` header. + /// + /// [initial handshake]: https://tools.ietf.org/html/rfc6455#section-4.2.2 + static String signKey(String key) { + var hash = new SHA1(); + // We use [codeUnits] here rather than UTF-8-decoding the string because + // [key] is expected to be base64 encoded, and so will be pure ASCII. + hash.add((key + _webSocketGUID).codeUnits); + return CryptoUtils.bytesToBase64(hash.close()); + } + + /// Creates a new WebSocket handling messaging across an existing socket. + /// + /// Because this is HTTP-API-agnostic, the initial [WebSocket handshake][] + /// must have already been completed on the socket before this is called. + /// + /// If [stream] is also a [StreamSink] (for example, if it's a "dart:io" + /// `Socket`), it will be used for both sending and receiving data. Otherwise, + /// it will be used for receiving data and [sink] will be used for sending it. + /// + /// If this is a WebSocket server, [serverSide] should be `true` (the + /// default); if it's a client, [serverSide] should be `false`. + /// + /// [WebSocket handshake]: https://tools.ietf.org/html/rfc6455#section-4 + factory CompatibleWebSocket(Stream> stream, + {StreamSink> sink, bool serverSide: true}) { + if (sink == null) { + if (stream is! StreamSink) { + throw new ArgumentError("If stream isn't also a StreamSink, sink must " + "be passed explicitly."); + } + sink = stream as StreamSink; + } + + return new _WebSocketImpl._fromSocket(stream, sink, serverSide); + } + + /// Closes the web socket connection. + /// + /// [closeCode] and [closeReason] are the [close code][] and [reason][] sent + /// to the remote peer, respectively. If they are omitted, the peer will see + /// a "no status received" code with no reason. + /// + /// [close code]: https://tools.ietf.org/html/rfc6455#section-7.1.5 + /// [reason]: https://tools.ietf.org/html/rfc6455#section-7.1.6 + Future close([int closeCode, String closeReason]); +} + +/// An exception thrown by [CompatibleWebSocket]. +class CompatibleWebSocketException implements Exception { + final String message; + + CompatibleWebSocketException([this.message]); + + String toString() => message == null + ? "CompatibleWebSocketException" : + "CompatibleWebSocketException: $message"; +} + +// The following code is copied from sdk/lib/io/websocket_impl.dart. The +// "dart:io" implementation isn't used directly both to support non-"dart:io" +// applications, and because it's incompatible with non-"dart:io" HTTP requests +// (issue 18172). +// +// Because it's copied directly, only modifications necessary to support the +// desired public API and to remove "dart:io" dependencies have been made. + +/** + * Web socket status codes used when closing a web socket connection. + */ +abstract class _WebSocketStatus { + static const int NORMAL_CLOSURE = 1000; + static const int GOING_AWAY = 1001; + static const int PROTOCOL_ERROR = 1002; + static const int UNSUPPORTED_DATA = 1003; + static const int RESERVED_1004 = 1004; + static const int NO_STATUS_RECEIVED = 1005; + static const int ABNORMAL_CLOSURE = 1006; + static const int INVALID_FRAME_PAYLOAD_DATA = 1007; + static const int POLICY_VIOLATION = 1008; + static const int MESSAGE_TOO_BIG = 1009; + static const int MISSING_MANDATORY_EXTENSION = 1010; + static const int INTERNAL_SERVER_ERROR = 1011; + static const int RESERVED_1015 = 1015; +} + +abstract class _WebSocketState { + static const int CONNECTING = 0; + static const int OPEN = 1; + static const int CLOSING = 2; + static const int CLOSED = 3; +} + +const String _webSocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + +final _random = new Random(); + +// Matches _WebSocketOpcode. +class _WebSocketMessageType { + static const int NONE = 0; + static const int TEXT = 1; + static const int BINARY = 2; +} + + +class _WebSocketOpcode { + static const int CONTINUATION = 0; + static const int TEXT = 1; + static const int BINARY = 2; + static const int RESERVED_3 = 3; + static const int RESERVED_4 = 4; + static const int RESERVED_5 = 5; + static const int RESERVED_6 = 6; + static const int RESERVED_7 = 7; + static const int CLOSE = 8; + static const int PING = 9; + static const int PONG = 10; + static const int RESERVED_B = 11; + static const int RESERVED_C = 12; + static const int RESERVED_D = 13; + static const int RESERVED_E = 14; + static const int RESERVED_F = 15; +} + +/** + * The web socket protocol transformer handles the protocol byte stream + * which is supplied through the [:handleData:]. As the protocol is processed, + * it'll output frame data as either a List or String. + * + * Important infomation about usage: Be sure you use cancelOnError, so the + * socket will be closed when the processer encounter an error. Not using it + * will lead to undefined behaviour. + */ +// TODO(ajohnsen): make this transformer reusable? +class _WebSocketProtocolTransformer implements StreamTransformer, EventSink { + static const int START = 0; + static const int LEN_FIRST = 1; + static const int LEN_REST = 2; + static const int MASK = 3; + static const int PAYLOAD = 4; + static const int CLOSED = 5; + static const int FAILURE = 6; + + int _state = START; + bool _fin = false; + int _opcode = -1; + int _len = -1; + bool _masked = false; + int _remainingLenBytes = -1; + int _remainingMaskingKeyBytes = 4; + int _remainingPayloadBytes = -1; + int _unmaskingIndex = 0; + int _currentMessageType = _WebSocketMessageType.NONE; + int closeCode = _WebSocketStatus.NO_STATUS_RECEIVED; + String closeReason = ""; + + EventSink _eventSink; + + final bool _serverSide; + final List _maskingBytes = new List(4); + final BytesBuilder _payload = new BytesBuilder(copy: false); + + _WebSocketProtocolTransformer([this._serverSide = false]); + + Stream bind(Stream stream) { + return new Stream.eventTransformed( + stream, + (EventSink eventSink) { + if (_eventSink != null) { + throw new StateError("WebSocket transformer already used."); + } + _eventSink = eventSink; + return this; + }); + } + + void addError(Object error, [StackTrace stackTrace]) => + _eventSink.addError(error, stackTrace); + + void close() => _eventSink.close(); + + /** + * Process data received from the underlying communication channel. + */ + void add(Uint8List buffer) { + int count = buffer.length; + int index = 0; + int lastIndex = count; + if (_state == CLOSED) { + throw new CompatibleWebSocketException("Data on closed connection"); + } + if (_state == FAILURE) { + throw new CompatibleWebSocketException("Data on failed connection"); + } + while ((index < lastIndex) && _state != CLOSED && _state != FAILURE) { + int byte = buffer[index]; + if (_state <= LEN_REST) { + if (_state == START) { + _fin = (byte & 0x80) != 0; + if ((byte & 0x70) != 0) { + // The RSV1, RSV2 bits RSV3 must be all zero. + throw new CompatibleWebSocketException("Protocol error"); + } + _opcode = (byte & 0xF); + if (_opcode <= _WebSocketOpcode.BINARY) { + if (_opcode == _WebSocketOpcode.CONTINUATION) { + if (_currentMessageType == _WebSocketMessageType.NONE) { + throw new CompatibleWebSocketException("Protocol error"); + } + } else { + assert(_opcode == _WebSocketOpcode.TEXT || + _opcode == _WebSocketOpcode.BINARY); + if (_currentMessageType != _WebSocketMessageType.NONE) { + throw new CompatibleWebSocketException("Protocol error"); + } + _currentMessageType = _opcode; + } + } else if (_opcode >= _WebSocketOpcode.CLOSE && + _opcode <= _WebSocketOpcode.PONG) { + // Control frames cannot be fragmented. + if (!_fin) throw new CompatibleWebSocketException("Protocol error"); + } else { + throw new CompatibleWebSocketException("Protocol error"); + } + _state = LEN_FIRST; + } else if (_state == LEN_FIRST) { + _masked = (byte & 0x80) != 0; + _len = byte & 0x7F; + if (_isControlFrame() && _len > 125) { + throw new CompatibleWebSocketException("Protocol error"); + } + if (_len == 126) { + _len = 0; + _remainingLenBytes = 2; + _state = LEN_REST; + } else if (_len == 127) { + _len = 0; + _remainingLenBytes = 8; + _state = LEN_REST; + } else { + assert(_len < 126); + _lengthDone(); + } + } else { + assert(_state == LEN_REST); + _len = _len << 8 | byte; + _remainingLenBytes--; + if (_remainingLenBytes == 0) { + _lengthDone(); + } + } + } else { + if (_state == MASK) { + _maskingBytes[4 - _remainingMaskingKeyBytes--] = byte; + if (_remainingMaskingKeyBytes == 0) { + _maskDone(); + } + } else { + assert(_state == PAYLOAD); + // The payload is not handled one byte at a time but in blocks. + int payloadLength = min(lastIndex - index, _remainingPayloadBytes); + _remainingPayloadBytes -= payloadLength; + // Unmask payload if masked. + if (_masked) { + _unmask(index, payloadLength, buffer); + } + // Control frame and data frame share _payloads. + _payload.add( + new Uint8List.view(buffer.buffer, index, payloadLength)); + index += payloadLength; + if (_isControlFrame()) { + if (_remainingPayloadBytes == 0) _controlFrameEnd(); + } else { + if (_currentMessageType != _WebSocketMessageType.TEXT && + _currentMessageType != _WebSocketMessageType.BINARY) { + throw new CompatibleWebSocketException("Protocol error"); + } + if (_remainingPayloadBytes == 0) _messageFrameEnd(); + } + + // Hack - as we always do index++ below. + index--; + } + } + + // Move to the next byte. + index++; + } + } + + void _unmask(int index, int length, Uint8List buffer) { + const int BLOCK_SIZE = 16; + // Skip Int32x4-version if message is small. + if (length >= BLOCK_SIZE) { + // Start by aligning to 16 bytes. + final int startOffset = BLOCK_SIZE - (index & 15); + final int end = index + startOffset; + for (int i = index; i < end; i++) { + buffer[i] ^= _maskingBytes[_unmaskingIndex++ & 3]; + } + index += startOffset; + length -= startOffset; + final int blockCount = length ~/ BLOCK_SIZE; + if (blockCount > 0) { + // Create mask block. + int mask = 0; + for (int i = 3; i >= 0; i--) { + mask = (mask << 8) | _maskingBytes[(_unmaskingIndex + i) & 3]; + } + Int32x4 blockMask = new Int32x4(mask, mask, mask, mask); + Int32x4List blockBuffer = new Int32x4List.view( + buffer.buffer, index, blockCount); + for (int i = 0; i < blockBuffer.length; i++) { + blockBuffer[i] ^= blockMask; + } + final int bytes = blockCount * BLOCK_SIZE; + index += bytes; + length -= bytes; + } + } + // Handle end. + final int end = index + length; + for (int i = index; i < end; i++) { + buffer[i] ^= _maskingBytes[_unmaskingIndex++ & 3]; + } + } + + void _lengthDone() { + if (_masked) { + if (!_serverSide) { + throw new CompatibleWebSocketException( + "Received masked frame from server"); + } + _state = MASK; + } else { + if (_serverSide) { + throw new CompatibleWebSocketException( + "Received unmasked frame from client"); + } + _remainingPayloadBytes = _len; + _startPayload(); + } + } + + void _maskDone() { + _remainingPayloadBytes = _len; + _startPayload(); + } + + void _startPayload() { + // If there is no actual payload perform perform callbacks without + // going through the PAYLOAD state. + if (_remainingPayloadBytes == 0) { + if (_isControlFrame()) { + switch (_opcode) { + case _WebSocketOpcode.CLOSE: + _state = CLOSED; + _eventSink.close(); + break; + case _WebSocketOpcode.PING: + _eventSink.add(new _WebSocketPing()); + break; + case _WebSocketOpcode.PONG: + _eventSink.add(new _WebSocketPong()); + break; + } + _prepareForNextFrame(); + } else { + _messageFrameEnd(); + } + } else { + _state = PAYLOAD; + } + } + + void _messageFrameEnd() { + if (_fin) { + switch (_currentMessageType) { + case _WebSocketMessageType.TEXT: + _eventSink.add(UTF8.decode(_payload.takeBytes())); + break; + case _WebSocketMessageType.BINARY: + _eventSink.add(_payload.takeBytes()); + break; + } + _currentMessageType = _WebSocketMessageType.NONE; + } + _prepareForNextFrame(); + } + + void _controlFrameEnd() { + switch (_opcode) { + case _WebSocketOpcode.CLOSE: + closeCode = _WebSocketStatus.NO_STATUS_RECEIVED; + var payload = _payload.takeBytes(); + if (payload.length > 0) { + if (payload.length == 1) { + throw new CompatibleWebSocketException("Protocol error"); + } + closeCode = payload[0] << 8 | payload[1]; + if (closeCode == _WebSocketStatus.NO_STATUS_RECEIVED) { + throw new CompatibleWebSocketException("Protocol error"); + } + if (payload.length > 2) { + closeReason = UTF8.decode(payload.sublist(2)); + } + } + _state = CLOSED; + _eventSink.close(); + break; + + case _WebSocketOpcode.PING: + _eventSink.add(new _WebSocketPing(_payload.takeBytes())); + break; + + case _WebSocketOpcode.PONG: + _eventSink.add(new _WebSocketPong(_payload.takeBytes())); + break; + } + _prepareForNextFrame(); + } + + bool _isControlFrame() { + return _opcode == _WebSocketOpcode.CLOSE || + _opcode == _WebSocketOpcode.PING || + _opcode == _WebSocketOpcode.PONG; + } + + void _prepareForNextFrame() { + if (_state != CLOSED && _state != FAILURE) _state = START; + _fin = false; + _opcode = -1; + _len = -1; + _remainingLenBytes = -1; + _remainingMaskingKeyBytes = 4; + _remainingPayloadBytes = -1; + _unmaskingIndex = 0; + } +} + + +class _WebSocketPing { + final List payload; + _WebSocketPing([this.payload = null]); +} + + +class _WebSocketPong { + final List payload; + _WebSocketPong([this.payload = null]); +} + +// TODO(ajohnsen): Make this transformer reusable. +class _WebSocketOutgoingTransformer implements StreamTransformer, EventSink { + final _WebSocketImpl webSocket; + EventSink _eventSink; + + _WebSocketOutgoingTransformer(this.webSocket); + + Stream bind(Stream stream) { + return new Stream.eventTransformed( + stream, + (EventSink eventSink) { + if (_eventSink != null) { + throw new StateError("WebSocket transformer already used"); + } + _eventSink = eventSink; + return this; + }); + } + + void add(message) { + if (message is _WebSocketPong) { + addFrame(_WebSocketOpcode.PONG, message.payload); + return; + } + if (message is _WebSocketPing) { + addFrame(_WebSocketOpcode.PING, message.payload); + return; + } + List data; + int opcode; + if (message != null) { + if (message is String) { + opcode = _WebSocketOpcode.TEXT; + data = UTF8.encode(message); + } else { + if (message is !List) { + throw new ArgumentError(message); + } + opcode = _WebSocketOpcode.BINARY; + data = message; + } + } else { + opcode = _WebSocketOpcode.TEXT; + } + addFrame(opcode, data); + } + + void addError(Object error, [StackTrace stackTrace]) => + _eventSink.addError(error, stackTrace); + + void close() { + int code = webSocket._outCloseCode; + String reason = webSocket._outCloseReason; + List data; + if (code != null) { + data = new List(); + data.add((code >> 8) & 0xFF); + data.add(code & 0xFF); + if (reason != null) { + data.addAll(UTF8.encode(reason)); + } + } + addFrame(_WebSocketOpcode.CLOSE, data); + _eventSink.close(); + } + + void addFrame(int opcode, List data) => + createFrame(opcode, data, webSocket._serverSide).forEach(_eventSink.add); + + static Iterable createFrame(int opcode, List data, bool serverSide) { + bool mask = !serverSide; // Masking not implemented for server. + int dataLength = data == null ? 0 : data.length; + // Determine the header size. + int headerSize = (mask) ? 6 : 2; + if (dataLength > 65535) { + headerSize += 8; + } else if (dataLength > 125) { + headerSize += 2; + } + Uint8List header = new Uint8List(headerSize); + int index = 0; + // Set FIN and opcode. + header[index++] = 0x80 | opcode; + // Determine size and position of length field. + int lengthBytes = 1; + int firstLengthByte = 1; + if (dataLength > 65535) { + header[index++] = 127; + lengthBytes = 8; + } else if (dataLength > 125) { + header[index++] = 126; + lengthBytes = 2; + } + // Write the length in network byte order into the header. + for (int i = 0; i < lengthBytes; i++) { + header[index++] = dataLength >> (((lengthBytes - 1) - i) * 8) & 0xFF; + } + if (mask) { + header[1] |= 1 << 7; + var maskBytes = [_random.nextInt(256), _random.nextInt(256), + _random.nextInt(256), _random.nextInt(256)]; + header.setRange(index, index + 4, maskBytes); + index += 4; + if (data != null) { + Uint8List list; + // If this is a text message just do the masking inside the + // encoded data. + if (opcode == _WebSocketOpcode.TEXT && data is Uint8List) { + list = data; + } else { + if (data is Uint8List) { + list = new Uint8List.fromList(data); + } else { + list = new Uint8List(data.length); + for (int i = 0; i < data.length; i++) { + if (data[i] < 0 || 255 < data[i]) { + throw new ArgumentError( + "List element is not a byte value " + "(value ${data[i]} at index $i)"); + } + list[i] = data[i]; + } + } + } + const int BLOCK_SIZE = 16; + int blockCount = list.length ~/ BLOCK_SIZE; + if (blockCount > 0) { + // Create mask block. + int mask = 0; + for (int i = 3; i >= 0; i--) { + mask = (mask << 8) | maskBytes[i]; + } + Int32x4 blockMask = new Int32x4(mask, mask, mask, mask); + Int32x4List blockBuffer = new Int32x4List.view( + list.buffer, 0, blockCount); + for (int i = 0; i < blockBuffer.length; i++) { + blockBuffer[i] ^= blockMask; + } + } + // Handle end. + for (int i = blockCount * BLOCK_SIZE; i < list.length; i++) { + list[i] ^= maskBytes[i & 3]; + } + data = list; + } + } + assert(index == headerSize); + if (data == null) { + return [header]; + } else { + return [header, data]; + } + } +} + + +class _WebSocketConsumer implements StreamConsumer { + final _WebSocketImpl webSocket; + final StreamSink> sink; + StreamController _controller; + StreamSubscription _subscription; + bool _issuedPause = false; + bool _closed = false; + Completer _closeCompleter = new Completer(); + Completer _completer; + + _WebSocketConsumer(this.webSocket, this.sink); + + void _onListen() { + if (_subscription != null) { + _subscription.cancel(); + } + } + + void _onPause() { + if (_subscription != null) { + _subscription.pause(); + } else { + _issuedPause = true; + } + } + + void _onResume() { + if (_subscription != null) { + _subscription.resume(); + } else { + _issuedPause = false; + } + } + + void _cancel() { + if (_subscription != null) { + var subscription = _subscription; + _subscription = null; + subscription.cancel(); + } + } + + _ensureController() { + if (_controller != null) return; + _controller = new StreamController(sync: true, + onPause: _onPause, + onResume: _onResume, + onCancel: _onListen); + var stream = _controller.stream.transform( + new _WebSocketOutgoingTransformer(webSocket)); + sink.addStream(stream) + .then((_) { + _done(); + _closeCompleter.complete(webSocket); + }, onError: (error, StackTrace stackTrace) { + _closed = true; + _cancel(); + if (error is ArgumentError) { + if (!_done(error, stackTrace)) { + _closeCompleter.completeError(error, stackTrace); + } + } else { + _done(); + _closeCompleter.complete(webSocket); + } + }); + } + + bool _done([error, StackTrace stackTrace]) { + if (_completer == null) return false; + if (error != null) { + _completer.completeError(error, stackTrace); + } else { + _completer.complete(webSocket); + } + _completer = null; + return true; + } + + Future addStream(var stream) { + if (_closed) { + stream.listen(null).cancel(); + return new Future.value(webSocket); + } + _ensureController(); + _completer = new Completer(); + _subscription = stream.listen( + (data) { + _controller.add(data); + }, + onDone: _done, + onError: _done, + cancelOnError: true); + if (_issuedPause) { + _subscription.pause(); + _issuedPause = false; + } + return _completer.future; + } + + Future close() { + _ensureController(); + Future closeSocket() { + return sink.close().catchError((_) {}).then((_) => webSocket); + } + _controller.close(); + return _closeCompleter.future.then((_) => closeSocket()); + } + + void add(data) { + if (_closed) return; + _ensureController(); + _controller.add(data); + } + + void closeSocket() { + _closed = true; + _cancel(); + close(); + } +} + + +class _WebSocketImpl extends Stream implements CompatibleWebSocket { + StreamController _controller; + StreamSubscription _subscription; + StreamController _sink; + + final bool _serverSide; + int _readyState = _WebSocketState.CONNECTING; + bool _writeClosed = false; + int _closeCode; + String _closeReason; + Duration _pingInterval; + Timer _pingTimer; + _WebSocketConsumer _consumer; + + int _outCloseCode; + String _outCloseReason; + Timer _closeTimer; + + _WebSocketImpl._fromSocket(Stream> stream, + StreamSink> sink, [this._serverSide = false]) { + _consumer = new _WebSocketConsumer(this, sink); + _sink = new StreamController(); + _sink.stream.pipe(_consumer); + _readyState = _WebSocketState.OPEN; + + var transformer = new _WebSocketProtocolTransformer(_serverSide); + _subscription = stream.transform(transformer).listen( + (data) { + if (data is _WebSocketPing) { + if (!_writeClosed) _consumer.add(new _WebSocketPong(data.payload)); + } else if (data is _WebSocketPong) { + // Simply set pingInterval, as it'll cancel any timers. + pingInterval = _pingInterval; + } else { + _controller.add(data); + } + }, + onError: (error) { + if (_closeTimer != null) _closeTimer.cancel(); + if (error is FormatException) { + _close(_WebSocketStatus.INVALID_FRAME_PAYLOAD_DATA); + } else { + _close(_WebSocketStatus.PROTOCOL_ERROR); + } + _controller.close(); + }, + onDone: () { + if (_closeTimer != null) _closeTimer.cancel(); + if (_readyState == _WebSocketState.OPEN) { + _readyState = _WebSocketState.CLOSING; + if (!_isReservedStatusCode(transformer.closeCode)) { + _close(transformer.closeCode); + } else { + _close(); + } + _readyState = _WebSocketState.CLOSED; + } + _closeCode = transformer.closeCode; + _closeReason = transformer.closeReason; + _controller.close(); + }, + cancelOnError: true); + _subscription.pause(); + _controller = new StreamController(sync: true, + onListen: _subscription.resume, + onPause: _subscription.pause, + onResume: _subscription.resume); + } + + StreamSubscription listen(void onData(message), + {Function onError, + void onDone(), + bool cancelOnError}) { + return _controller.stream.listen(onData, + onError: onError, + onDone: onDone, + cancelOnError: cancelOnError); + } + + Duration get pingInterval => _pingInterval; + + void set pingInterval(Duration interval) { + if (_writeClosed) return; + if (_pingTimer != null) _pingTimer.cancel(); + _pingInterval = interval; + + if (_pingInterval == null) return; + + _pingTimer = new Timer(_pingInterval, () { + if (_writeClosed) return; + _consumer.add(new _WebSocketPing()); + _pingTimer = new Timer(_pingInterval, () { + // No pong received. + _close(_WebSocketStatus.GOING_AWAY); + }); + }); + } + + int get closeCode => _closeCode; + String get closeReason => _closeReason; + + void add(data) => _sink.add(data); + void addError(error, [StackTrace stackTrace]) => + _sink.addError(error, stackTrace); + Future addStream(Stream stream) => _sink.addStream(stream); + Future get done => _sink.done; + + Future close([int code, String reason]) { + if (_isReservedStatusCode(code)) { + throw new CompatibleWebSocketException("Reserved status code $code"); + } + if (_outCloseCode == null) { + _outCloseCode = code; + _outCloseReason = reason; + } + if (_closeTimer == null && !_controller.isClosed) { + // When closing the web-socket, we no longer accept data. + _closeTimer = new Timer(const Duration(seconds: 5), () { + _subscription.cancel(); + _controller.close(); + }); + } + return _sink.close(); + } + + void _close([int code, String reason]) { + if (_writeClosed) return; + if (_outCloseCode == null) { + _outCloseCode = code; + _outCloseReason = reason; + } + _writeClosed = true; + _consumer.closeSocket(); + } + + static bool _isReservedStatusCode(int code) { + return code != null && + (code < _WebSocketStatus.NORMAL_CLOSURE || + code == _WebSocketStatus.RESERVED_1004 || + code == _WebSocketStatus.NO_STATUS_RECEIVED || + code == _WebSocketStatus.ABNORMAL_CLOSURE || + (code > _WebSocketStatus.INTERNAL_SERVER_ERROR && + code < _WebSocketStatus.RESERVED_1015) || + (code >= _WebSocketStatus.RESERVED_1015 && + code < 3000)); + } +} + diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml new file mode 100644 index 0000000000..af1a192aa4 --- /dev/null +++ b/pkgs/http_parser/pubspec.yaml @@ -0,0 +1,14 @@ +name: http_parser +version: 0.0.2+3 +author: "Dart Team " +homepage: http://www.dartlang.org +description: > + A platform-independent package for parsing and serializing HTTP formats. +dependencies: + crypto: ">=0.9.0 <0.10.0" + collection: ">=0.9.1 <0.10.0" + string_scanner: ">=0.0.0 <0.1.0" +dev_dependencies: + unittest: ">=0.10.0 <0.11.0" +environment: + sdk: ">=1.2.0 <2.0.0" diff --git a/pkgs/http_parser/test/http_date_test.dart b/pkgs/http_parser/test/http_date_test.dart new file mode 100644 index 0000000000..303d6d76d0 --- /dev/null +++ b/pkgs/http_parser/test/http_date_test.dart @@ -0,0 +1,347 @@ +// Copyright (c) 2014, 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 http_parser.http_date_test; + +import 'package:http_parser/http_parser.dart'; +import 'package:unittest/unittest.dart'; + +void main() { + group('format', () { + test('many values with 9', () { + var date = new DateTime.utc(2014, 9, 9, 9, 9, 9); + var formatted = formatHttpDate(date); + + expect(formatted, 'Tue, 09 Sep 2014 09:09:09 GMT'); + var parsed = parseHttpDate(formatted); + + expect(parsed, date); + }); + + test('end of year', () { + var date = new DateTime.utc(1999, 12, 31, 23, 59, 59); + var formatted = formatHttpDate(date); + + expect(formatted, 'Fri, 31 Dec 1999 23:59:59 GMT'); + var parsed = parseHttpDate(formatted); + + expect(parsed, date); + }); + + test('start of year', () { + var date = new DateTime.utc(2000, 1, 1, 0, 0, 0); + var formatted = formatHttpDate(date); + + expect(formatted, 'Sat, 01 Jan 2000 00:00:00 GMT'); + var parsed = parseHttpDate(formatted); + + expect(parsed, date); + }); + }); + + group("parse", () { + group("RFC 1123", () { + test("parses the example date", () { + var date = parseHttpDate("Sun, 06 Nov 1994 08:49:37 GMT"); + expect(date.day, equals(6)); + expect(date.month, equals(DateTime.NOVEMBER)); + expect(date.year, equals(1994)); + expect(date.hour, equals(8)); + expect(date.minute, equals(49)); + expect(date.second, equals(37)); + expect(date.timeZoneName, equals("UTC")); + }); + + test("whitespace is required", () { + expect(() => parseHttpDate("Sun,06 Nov 1994 08:49:37 GMT"), + throwsFormatException); + + expect(() => parseHttpDate("Sun, 06Nov 1994 08:49:37 GMT"), + throwsFormatException); + + expect(() => parseHttpDate("Sun, 06 Nov1994 08:49:37 GMT"), + throwsFormatException); + + expect(() => parseHttpDate("Sun, 06 Nov 199408:49:37 GMT"), + throwsFormatException); + + expect(() => parseHttpDate("Sun, 06 Nov 1994 08:49:37GMT"), + throwsFormatException); + }); + + test("exactly one space is required", () { + expect(() => parseHttpDate("Sun, 06 Nov 1994 08:49:37 GMT"), + throwsFormatException); + + expect(() => parseHttpDate("Sun, 06 Nov 1994 08:49:37 GMT"), + throwsFormatException); + + expect(() => parseHttpDate("Sun, 06 Nov 1994 08:49:37 GMT"), + throwsFormatException); + + expect(() => parseHttpDate("Sun, 06 Nov 1994 08:49:37 GMT"), + throwsFormatException); + + expect(() => parseHttpDate("Sun, 06 Nov 1994 08:49:37 GMT"), + throwsFormatException); + }); + + test("requires precise number lengths", () { + expect(() => parseHttpDate("Sun, 6 Nov 1994 08:49:37 GMT"), + throwsFormatException); + + expect(() => parseHttpDate("Sun, 06 Nov 94 08:49:37 GMT"), + throwsFormatException); + + expect(() => parseHttpDate("Sun, 06 Nov 1994 8:49:37 GMT"), + throwsFormatException); + + expect(() => parseHttpDate("Sun, 06 Nov 1994 08:9:37 GMT"), + throwsFormatException); + + expect(() => parseHttpDate("Sun, 06 Nov 1994 08:49:7 GMT"), + throwsFormatException); + }); + + test("requires reasonable numbers", () { + expect(() => parseHttpDate("Sun, 00 Nov 1994 08:49:37 GMT"), + throwsFormatException); + + expect(() => parseHttpDate("Sun, 31 Nov 1994 08:49:37 GMT"), + throwsFormatException); + + expect(() => parseHttpDate("Sun, 32 Aug 1994 08:49:37 GMT"), + throwsFormatException); + + expect(() => parseHttpDate("Sun, 06 Nov 1994 24:49:37 GMT"), + throwsFormatException); + + expect(() => parseHttpDate("Sun, 06 Nov 1994 08:60:37 GMT"), + throwsFormatException); + + expect(() => parseHttpDate("Sun, 06 Nov 1994 08:49:60 GMT"), + throwsFormatException); + }); + + test("only allows short weekday names", () { + expect(() => parseHttpDate("Sunday, 6 Nov 1994 08:49:37 GMT"), + throwsFormatException); + }); + + test("only allows short month names", () { + expect(() => parseHttpDate("Sun, 6 November 1994 08:49:37 GMT"), + throwsFormatException); + }); + + test("only allows GMT", () { + expect(() => parseHttpDate("Sun, 6 Nov 1994 08:49:37 PST"), + throwsFormatException); + }); + + test("disallows trailing whitespace", () { + expect(() => parseHttpDate("Sun, 6 Nov 1994 08:49:37 GMT "), + throwsFormatException); + }); + }); + + group("RFC 850", () { + test("parses the example date", () { + var date = parseHttpDate("Sunday, 06-Nov-94 08:49:37 GMT"); + expect(date.day, equals(6)); + expect(date.month, equals(DateTime.NOVEMBER)); + expect(date.year, equals(1994)); + expect(date.hour, equals(8)); + expect(date.minute, equals(49)); + expect(date.second, equals(37)); + expect(date.timeZoneName, equals("UTC")); + }); + + test("whitespace is required", () { + expect(() => parseHttpDate("Sunday,06-Nov-94 08:49:37 GMT"), + throwsFormatException); + + expect(() => parseHttpDate("Sunday, 06-Nov-9408:49:37 GMT"), + throwsFormatException); + + expect(() => parseHttpDate("Sunday, 06-Nov-94 08:49:37GMT"), + throwsFormatException); + }); + + test("exactly one space is required", () { + expect(() => parseHttpDate("Sunday, 06-Nov-94 08:49:37 GMT"), + throwsFormatException); + + expect(() => parseHttpDate("Sunday, 06-Nov-94 08:49:37 GMT"), + throwsFormatException); + + expect(() => parseHttpDate("Sunday, 06-Nov-94 08:49:37 GMT"), + throwsFormatException); + }); + + test("requires precise number lengths", () { + expect(() => parseHttpDate("Sunday, 6-Nov-94 08:49:37 GMT"), + throwsFormatException); + + expect(() => parseHttpDate("Sunday, 06-Nov-1994 08:49:37 GMT"), + throwsFormatException); + + expect(() => parseHttpDate("Sunday, 06-Nov-94 8:49:37 GMT"), + throwsFormatException); + + expect(() => parseHttpDate("Sunday, 06-Nov-94 08:9:37 GMT"), + throwsFormatException); + + expect(() => parseHttpDate("Sunday, 06-Nov-94 08:49:7 GMT"), + throwsFormatException); + }); + + test("requires reasonable numbers", () { + expect(() => parseHttpDate("Sunday, 00-Nov-94 08:49:37 GMT"), + throwsFormatException); + + expect(() => parseHttpDate("Sunday, 31-Nov-94 08:49:37 GMT"), + throwsFormatException); + + expect(() => parseHttpDate("Sunday, 32-Aug-94 08:49:37 GMT"), + throwsFormatException); + + expect(() => parseHttpDate("Sunday, 06-Nov-94 24:49:37 GMT"), + throwsFormatException); + + expect(() => parseHttpDate("Sunday, 06-Nov-94 08:60:37 GMT"), + throwsFormatException); + + expect(() => parseHttpDate("Sunday, 06-Nov-94 08:49:60 GMT"), + throwsFormatException); + }); + + test("only allows long weekday names", () { + expect(() => parseHttpDate("Sun, 6-Nov-94 08:49:37 GMT"), + throwsFormatException); + }); + + test("only allows short month names", () { + expect(() => parseHttpDate("Sunday, 6-November-94 08:49:37 GMT"), + throwsFormatException); + }); + + test("only allows GMT", () { + expect(() => parseHttpDate("Sunday, 6-Nov-94 08:49:37 PST"), + throwsFormatException); + }); + + test("disallows trailing whitespace", () { + expect(() => parseHttpDate("Sunday, 6-Nov-94 08:49:37 GMT "), + throwsFormatException); + }); + }); + + group("asctime()", () { + test("parses the example date", () { + var date = parseHttpDate("Sun Nov 6 08:49:37 1994"); + expect(date.day, equals(6)); + expect(date.month, equals(DateTime.NOVEMBER)); + expect(date.year, equals(1994)); + expect(date.hour, equals(8)); + expect(date.minute, equals(49)); + expect(date.second, equals(37)); + expect(date.timeZoneName, equals("UTC")); + }); + + test("parses a date with a two-digit day", () { + var date = parseHttpDate("Sun Nov 16 08:49:37 1994"); + expect(date.day, equals(16)); + expect(date.month, equals(DateTime.NOVEMBER)); + expect(date.year, equals(1994)); + expect(date.hour, equals(8)); + expect(date.minute, equals(49)); + expect(date.second, equals(37)); + expect(date.timeZoneName, equals("UTC")); + }); + + test("whitespace is required", () { + expect(() => parseHttpDate("SunNov 6 08:49:37 1994"), + throwsFormatException); + + expect(() => parseHttpDate("Sun Nov6 08:49:37 1994"), + throwsFormatException); + + expect(() => parseHttpDate("Sun Nov 608:49:37 1994"), + throwsFormatException); + + expect(() => parseHttpDate("Sun Nov 6 08:49:371994"), + throwsFormatException); + }); + + test("the right amount of whitespace is required", () { + expect(() => parseHttpDate("Sun Nov 6 08:49:37 1994"), + throwsFormatException); + + expect(() => parseHttpDate("Sun Nov 6 08:49:37 1994"), + throwsFormatException); + + expect(() => parseHttpDate("Sun Nov 6 08:49:37 1994"), + throwsFormatException); + + expect(() => parseHttpDate("Sun Nov 6 08:49:37 1994"), + throwsFormatException); + + expect(() => parseHttpDate("Sun Nov 6 08:49:37 1994"), + throwsFormatException); + }); + + test("requires precise number lengths", () { + expect(() => parseHttpDate("Sun Nov 016 08:49:37 1994"), + throwsFormatException); + + expect(() => parseHttpDate("Sun Nov 6 8:49:37 1994"), + throwsFormatException); + + expect(() => parseHttpDate("Sun Nov 6 08:9:37 1994"), + throwsFormatException); + + expect(() => parseHttpDate("Sun Nov 6 08:49:7 1994"), + throwsFormatException); + + expect(() => parseHttpDate("Sun Nov 6 08:49:37 94"), + throwsFormatException); + }); + + test("requires reasonable numbers", () { + expect(() => parseHttpDate("Sun Nov 0 08:49:37 1994"), + throwsFormatException); + + expect(() => parseHttpDate("Sun Nov 31 08:49:37 1994"), + throwsFormatException); + + expect(() => parseHttpDate("Sun Aug 32 08:49:37 1994"), + throwsFormatException); + + expect(() => parseHttpDate("Sun Nov 6 24:49:37 1994"), + throwsFormatException); + + expect(() => parseHttpDate("Sun Nov 6 08:60:37 1994"), + throwsFormatException); + + expect(() => parseHttpDate("Sun Nov 6 08:49:60 1994"), + throwsFormatException); + }); + + test("only allows short weekday names", () { + expect(() => parseHttpDate("Sunday Nov 0 08:49:37 1994"), + throwsFormatException); + }); + + test("only allows short month names", () { + expect(() => parseHttpDate("Sun November 0 08:49:37 1994"), + throwsFormatException); + }); + + test("disallows trailing whitespace", () { + expect(() => parseHttpDate("Sun November 0 08:49:37 1994 "), + throwsFormatException); + }); + }); + }); +} diff --git a/pkgs/http_parser/test/media_type_test.dart b/pkgs/http_parser/test/media_type_test.dart new file mode 100644 index 0000000000..14affbb3cc --- /dev/null +++ b/pkgs/http_parser/test/media_type_test.dart @@ -0,0 +1,163 @@ +// Copyright (c) 2014, 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 http_parser.media_type_test; + +import 'package:http_parser/http_parser.dart'; +import 'package:unittest/unittest.dart'; + +void main() { + group("parse", () { + test("parses a simple MIME type", () { + var type = new MediaType.parse("text/plain"); + expect(type.type, equals("text")); + expect(type.subtype, equals("plain")); + }); + + test("allows leading whitespace", () { + expect(new MediaType.parse(" text/plain").mimeType, equals("text/plain")); + expect(new MediaType.parse("\ttext/plain").mimeType, + equals("text/plain")); + }); + + test("allows trailing whitespace", () { + expect(new MediaType.parse("text/plain ").mimeType, equals("text/plain")); + expect(new MediaType.parse("text/plain\t").mimeType, + equals("text/plain")); + }); + + test("disallows separators in the MIME type", () { + expect(() => new MediaType.parse("te(xt/plain"), throwsFormatException); + expect(() => new MediaType.parse("text/pla=in"), throwsFormatException); + }); + + test("disallows whitespace around the slash", () { + expect(() => new MediaType.parse("text /plain"), throwsFormatException); + expect(() => new MediaType.parse("text/ plain"), throwsFormatException); + }); + + test("parses parameters", () { + var type = new MediaType.parse("text/plain;foo=bar;baz=bang"); + expect(type.mimeType, equals("text/plain")); + expect(type.parameters, equals({"foo": "bar", "baz": "bang"})); + }); + + test("allows whitespace around the semicolon", () { + var type = new MediaType.parse("text/plain ; foo=bar ; baz=bang"); + expect(type.mimeType, equals("text/plain")); + expect(type.parameters, equals({"foo": "bar", "baz": "bang"})); + }); + + test("disallows whitespace around the equals", () { + expect(() => new MediaType.parse("text/plain; foo =bar"), + throwsFormatException); + expect(() => new MediaType.parse("text/plain; foo= bar"), + throwsFormatException); + }); + + test("disallows separators in the parameters", () { + expect(() => new MediaType.parse("text/plain; fo:o=bar"), + throwsFormatException); + expect(() => new MediaType.parse("text/plain; foo=b@ar"), + throwsFormatException); + }); + + test("parses quoted parameters", () { + var type = new MediaType.parse( + 'text/plain; foo="bar space"; baz="bang\\\\escape"'); + expect(type.mimeType, equals("text/plain")); + expect(type.parameters, equals({ + "foo": "bar space", + "baz": "bang\\escape" + })); + }); + }); + + group("change", () { + var type; + setUp(() { + type = new MediaType.parse("text/plain; foo=bar; baz=bang"); + }); + + test("uses the existing fields by default", () { + var newType = type.change(); + expect(newType.type, equals("text")); + expect(newType.subtype, equals("plain")); + expect(newType.parameters, equals({"foo": "bar", "baz": "bang"})); + }); + + test("[type] overrides the existing type", () { + expect(type.change(type: "new").type, equals("new")); + }); + + test("[subtype] overrides the existing subtype", () { + expect(type.change(subtype: "new").subtype, equals("new")); + }); + + test("[mimeType] overrides the existing type and subtype", () { + var newType = type.change(mimeType: "image/png"); + expect(newType.type, equals("image")); + expect(newType.subtype, equals("png")); + }); + + test("[parameters] overrides and adds to existing parameters", () { + expect(type.change(parameters: { + "foo": "zap", + "qux": "fblthp" + }).parameters, equals({ + "foo": "zap", + "baz": "bang", + "qux": "fblthp" + })); + }); + + test("[clearParameters] removes existing parameters", () { + expect(type.change(clearParameters: true).parameters, isEmpty); + }); + + test("[clearParameters] with [parameters] removes before adding", () { + var newType = type.change( + parameters: {"foo": "zap"}, + clearParameters: true); + expect(newType.parameters, equals({"foo": "zap"})); + }); + + test("[type] with [mimeType] is illegal", () { + expect(() => type.change(type: "new", mimeType: "image/png"), + throwsArgumentError); + }); + + test("[subtype] with [mimeType] is illegal", () { + expect(() => type.change(subtype: "new", mimeType: "image/png"), + throwsArgumentError); + }); + }); + + group("toString", () { + test("serializes a simple MIME type", () { + expect(new MediaType("text", "plain").toString(), equals("text/plain")); + }); + + test("serializes a token parameter as a token", () { + expect(new MediaType("text", "plain", {"foo": "bar"}).toString(), + equals("text/plain; foo=bar")); + }); + + test("serializes a non-token parameter as a quoted string", () { + expect(new MediaType("text", "plain", {"foo": "bar baz"}).toString(), + equals('text/plain; foo="bar baz"')); + }); + + test("escapes a quoted string as necessary", () { + expect(new MediaType("text", "plain", {"foo": 'bar"\x7Fbaz'}).toString(), + equals('text/plain; foo="bar\\"\\\x7Fbaz"')); + }); + + test("serializes multiple parameters", () { + expect(new MediaType("text", "plain", { + "foo": "bar", "baz": "bang" + }).toString(), equals("text/plain; foo=bar; baz=bang")); + }); + }); +} diff --git a/pkgs/http_parser/test/web_socket_test.dart b/pkgs/http_parser/test/web_socket_test.dart new file mode 100644 index 0000000000..45b84d9ec7 --- /dev/null +++ b/pkgs/http_parser/test/web_socket_test.dart @@ -0,0 +1,95 @@ +// Copyright (c) 2014, 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 http_parser.web_socket_test; + +import 'dart:io'; + +import 'package:http_parser/http_parser.dart'; +import 'package:unittest/unittest.dart'; + +void main() { + test("a client can communicate with a WebSocket server", () { + return HttpServer.bind("localhost", 0).then((server) { + server.transform(new WebSocketTransformer()).listen((webSocket) { + webSocket.add("hello!"); + webSocket.first.then((request) { + expect(request, equals("ping")); + webSocket.add("pong"); + webSocket.close(); + }); + }); + + var client = new HttpClient(); + return client.openUrl("GET", Uri.parse("http://localhost:${server.port}")) + .then((request) { + request.headers + ..set("Connection", "Upgrade") + ..set("Upgrade", "websocket") + ..set("Sec-WebSocket-Key", "x3JJHMbDL1EzLkh9GBhXDw==") + ..set("Sec-WebSocket-Version", "13"); + return request.close(); + }).then((response) => response.detachSocket()).then((socket) { + var webSocket = new CompatibleWebSocket(socket, serverSide: false); + + var n = 0; + return webSocket.listen((message) { + if (n == 0) { + expect(message, equals("hello!")); + webSocket.add("ping"); + } else if (n == 1) { + expect(message, equals("pong")); + webSocket.close(); + server.close(); + } else { + fail("Only expected two messages."); + } + n++; + }).asFuture(); + }); + }); + }); + + test("a server can communicate with a WebSocket client", () { + return HttpServer.bind("localhost", 0).then((server) { + server.listen((request) { + var response = request.response; + response.statusCode = 101; + response.headers + ..set("Connection", "Upgrade") + ..set("Upgrade", "websocket") + ..set("Sec-WebSocket-Accept", CompatibleWebSocket.signKey( + request.headers.value('Sec-WebSocket-Key'))); + response.contentLength = 0; + response.detachSocket().then((socket) { + var webSocket = new CompatibleWebSocket(socket); + webSocket.add("hello!"); + webSocket.first.then((request) { + expect(request, equals("ping")); + webSocket.add("pong"); + webSocket.close(); + }); + }); + }); + + return WebSocket.connect('ws://localhost:${server.port}') + .then((webSocket) { + var n = 0; + return webSocket.listen((message) { + if (n == 0) { + expect(message, equals("hello!")); + webSocket.add("ping"); + } else if (n == 1) { + expect(message, equals("pong")); + webSocket.close(); + server.close(); + } else { + fail("Only expected two messages."); + } + n++; + }).asFuture(); + }); + }); + }); +} From adacbe15e1007c9fb61bf39ff063e02f8fe2f5f7 Mon Sep 17 00:00:00 2001 From: "nweiz@google.com" Date: Wed, 23 Jul 2014 23:44:09 +0000 Subject: [PATCH 002/126] Move a number of packages and some of pub over to using source_span. R=efortuna@google.com, rnystrom@google.com, sigmund@google.com BUG=19930 Review URL: https://codereview.chromium.org//401753002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/http_parser@38526 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/http_parser/CHANGELOG.md | 4 ++++ pkgs/http_parser/pubspec.yaml | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index 328e77911a..72f0218db2 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.2+4 + +* Widen the `string_scanner` version constraint. + ## 0.0.2+3 * Fix a library name conflict. diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index af1a192aa4..fd279ce163 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,5 +1,5 @@ name: http_parser -version: 0.0.2+3 +version: 0.0.2+4 author: "Dart Team " homepage: http://www.dartlang.org description: > @@ -7,7 +7,7 @@ description: > dependencies: crypto: ">=0.9.0 <0.10.0" collection: ">=0.9.1 <0.10.0" - string_scanner: ">=0.0.0 <0.1.0" + string_scanner: ">=0.0.0 <0.2.0" dev_dependencies: unittest: ">=0.10.0 <0.11.0" environment: From aee2082009623b71eedf82e6b79bffc44e649efa Mon Sep 17 00:00:00 2001 From: "lrn@google.com" Date: Fri, 22 Aug 2014 08:52:09 +0000 Subject: [PATCH 003/126] Update dart:collection to version 1.0.0 R=sgjesse@google.com Review URL: https://codereview.chromium.org//463333004 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/http_parser@39484 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/http_parser/pubspec.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index fd279ce163..340c08df8a 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,12 +1,12 @@ name: http_parser -version: 0.0.2+4 +version: 0.0.2+5 author: "Dart Team " homepage: http://www.dartlang.org description: > A platform-independent package for parsing and serializing HTTP formats. dependencies: crypto: ">=0.9.0 <0.10.0" - collection: ">=0.9.1 <0.10.0" + collection: ">=0.9.1 <2.0.0" string_scanner: ">=0.0.0 <0.2.0" dev_dependencies: unittest: ">=0.10.0 <0.11.0" From 9be549c22ea8ca35b4d66eaff395d8468eeccc1b Mon Sep 17 00:00:00 2001 From: "nweiz@google.com" Date: Tue, 26 Aug 2014 19:49:44 +0000 Subject: [PATCH 004/126] Clean up after r39484 and r39486. In particular, this adds CHANGELOG entries for packages that have been modified and fixes pub's barback dependency so it doesn't declare compatibility on an uncut release. This releases args, barback, http_parser, and shelf. R=rnystrom@google.com Review URL: https://codereview.chromium.org//506973003 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/http_parser@39563 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/http_parser/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index 72f0218db2..c1e7926efc 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.2+5 + +* Widen the version constraint on the `collection` package. + ## 0.0.2+4 * Widen the `string_scanner` version constraint. From 50af99e78d0e05ceab9c506f9262970ad8a5242d Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 17 Dec 2014 16:05:12 -0800 Subject: [PATCH 005/126] Add gitignore, status, and codereview files. --- pkgs/http_parser/.gitignore | 14 ++++++++++++++ pkgs/http_parser/.status | 6 ++++++ pkgs/http_parser/codereview.settings | 3 +++ 3 files changed, 23 insertions(+) create mode 100644 pkgs/http_parser/.gitignore create mode 100644 pkgs/http_parser/.status create mode 100644 pkgs/http_parser/codereview.settings diff --git a/pkgs/http_parser/.gitignore b/pkgs/http_parser/.gitignore new file mode 100644 index 0000000000..388eff0ba3 --- /dev/null +++ b/pkgs/http_parser/.gitignore @@ -0,0 +1,14 @@ +# Don’t commit the following directories created by pub. +.buildlog +.pub/ +build/ +packages + +# Or the files created by dart2js. +*.dart.js +*.js_ +*.js.deps +*.js.map + +# Include when developing application packages. +pubspec.lock \ No newline at end of file diff --git a/pkgs/http_parser/.status b/pkgs/http_parser/.status new file mode 100644 index 0000000000..8ba0bdc8ec --- /dev/null +++ b/pkgs/http_parser/.status @@ -0,0 +1,6 @@ +# Copyright (c) 2014, 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. + +[ $browser ] +http_parser/test/web_socket_test: Fail, OK # Uses dart:io diff --git a/pkgs/http_parser/codereview.settings b/pkgs/http_parser/codereview.settings new file mode 100644 index 0000000000..99e5711625 --- /dev/null +++ b/pkgs/http_parser/codereview.settings @@ -0,0 +1,3 @@ +CODE_REVIEW_SERVER: http://codereview.chromium.org/ +VIEW_VC: https://github.com/dart-lang/http_parser/commit/ +CC_LIST: reviews@dartlang.org \ No newline at end of file From e64ccb6b8c73cf81610b23b90fe6542f62e0994d Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 17 Dec 2014 16:05:27 -0800 Subject: [PATCH 006/126] Update the pubspec's homepage link. --- pkgs/http_parser/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index 340c08df8a..b681e1cd57 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,7 +1,7 @@ name: http_parser version: 0.0.2+5 author: "Dart Team " -homepage: http://www.dartlang.org +homepage: http://github.com/dart-lang/http_parser description: > A platform-independent package for parsing and serializing HTTP formats. dependencies: From 11a3f4a7f168c5b844dcfaabbe9b91f4a83431c5 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 17 Dec 2014 16:50:11 -0800 Subject: [PATCH 007/126] Fix the status file to match the package bots' expectations. --- pkgs/http_parser/.status | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pkgs/http_parser/.status b/pkgs/http_parser/.status index 8ba0bdc8ec..996a798fef 100644 --- a/pkgs/http_parser/.status +++ b/pkgs/http_parser/.status @@ -2,5 +2,12 @@ # 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. +# Skip non-test files ending with "_test". +*/packages/*: Skip + +# Only run tests from the build directory, since we don't care about the +# difference between transformed an untransformed code. +test/*: Skip + [ $browser ] -http_parser/test/web_socket_test: Fail, OK # Uses dart:io +*/web_socket_test: Fail, OK # Uses dart:io From ea960492f545092cae70783400c582bbf34109a9 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 18 Dec 2014 17:41:23 -0800 Subject: [PATCH 008/126] Properly skip tests in packages directories. --- pkgs/http_parser/.status | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkgs/http_parser/.status b/pkgs/http_parser/.status index 996a798fef..bfc03eded0 100644 --- a/pkgs/http_parser/.status +++ b/pkgs/http_parser/.status @@ -3,7 +3,12 @@ # BSD-style license that can be found in the LICENSE file. # Skip non-test files ending with "_test". +packages/*: Skip */packages/*: Skip +*/*/packages/*: Skip +*/*/*/packages/*: Skip +*/*/*/*packages/*: Skip +*/*/*/*/*packages/*: Skip # Only run tests from the build directory, since we don't care about the # difference between transformed an untransformed code. From 59ce107f02e292493fd84e14923eada07cec2d2b Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Fri, 19 Dec 2014 12:48:21 -0800 Subject: [PATCH 009/126] Remove initial */s in .status. --- pkgs/http_parser/.status | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/http_parser/.status b/pkgs/http_parser/.status index bfc03eded0..e191550368 100644 --- a/pkgs/http_parser/.status +++ b/pkgs/http_parser/.status @@ -15,4 +15,4 @@ packages/*: Skip test/*: Skip [ $browser ] -*/web_socket_test: Fail, OK # Uses dart:io +build/test/web_socket_test: Fail, OK # Uses dart:io From a732fdeadef761e8156874e6ba6856b94585c907 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 20 Jan 2015 16:21:17 -0800 Subject: [PATCH 010/126] http_parser: update unittest dependency, urls in pubspec and codereview remove unused variable R=nweiz@google.com Review URL: https://codereview.chromium.org//853353004 --- pkgs/http_parser/codereview.settings | 2 +- pkgs/http_parser/lib/src/web_socket.dart | 1 - pkgs/http_parser/pubspec.yaml | 6 +++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/pkgs/http_parser/codereview.settings b/pkgs/http_parser/codereview.settings index 99e5711625..d8547c6570 100644 --- a/pkgs/http_parser/codereview.settings +++ b/pkgs/http_parser/codereview.settings @@ -1,3 +1,3 @@ -CODE_REVIEW_SERVER: http://codereview.chromium.org/ +CODE_REVIEW_SERVER: https://codereview.chromium.org/ VIEW_VC: https://github.com/dart-lang/http_parser/commit/ CC_LIST: reviews@dartlang.org \ No newline at end of file diff --git a/pkgs/http_parser/lib/src/web_socket.dart b/pkgs/http_parser/lib/src/web_socket.dart index 77c9d2b873..968c70b0e1 100644 --- a/pkgs/http_parser/lib/src/web_socket.dart +++ b/pkgs/http_parser/lib/src/web_socket.dart @@ -592,7 +592,6 @@ class _WebSocketOutgoingTransformer implements StreamTransformer, EventSink { header[index++] = 0x80 | opcode; // Determine size and position of length field. int lengthBytes = 1; - int firstLengthByte = 1; if (dataLength > 65535) { header[index++] = 127; lengthBytes = 8; diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index b681e1cd57..649af89a76 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,7 +1,7 @@ name: http_parser -version: 0.0.2+5 +version: 0.0.3-dev author: "Dart Team " -homepage: http://github.com/dart-lang/http_parser +homepage: https://github.com/dart-lang/http_parser description: > A platform-independent package for parsing and serializing HTTP formats. dependencies: @@ -9,6 +9,6 @@ dependencies: collection: ">=0.9.1 <2.0.0" string_scanner: ">=0.0.0 <0.2.0" dev_dependencies: - unittest: ">=0.10.0 <0.11.0" + unittest: ">=0.10.0 <0.12.0" environment: sdk: ">=1.2.0 <2.0.0" From e9a1bf2ec166c7b03fdaaf5e5350477b0a6d52f7 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 30 Mar 2015 15:58:28 -0700 Subject: [PATCH 011/126] format code, prepare for +6 release R=nweiz@google.com Review URL: https://codereview.chromium.org//1046573002 --- pkgs/http_parser/CHANGELOG.md | 4 +++ pkgs/http_parser/lib/src/bytes_builder.dart | 2 -- pkgs/http_parser/lib/src/http_date.dart | 38 ++++++++++----------- pkgs/http_parser/lib/src/media_type.dart | 15 ++++---- pkgs/http_parser/pubspec.yaml | 2 +- pkgs/http_parser/test/media_type_test.dart | 35 +++++++------------ pkgs/http_parser/test/web_socket_test.dart | 22 ++++++------ 7 files changed, 56 insertions(+), 62 deletions(-) diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index c1e7926efc..43f346e7e9 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.2+6 + +* Updated homepage URL. + ## 0.0.2+5 * Widen the version constraint on the `collection` package. diff --git a/pkgs/http_parser/lib/src/bytes_builder.dart b/pkgs/http_parser/lib/src/bytes_builder.dart index 446e1751ba..68af4170bd 100644 --- a/pkgs/http_parser/lib/src/bytes_builder.dart +++ b/pkgs/http_parser/lib/src/bytes_builder.dart @@ -86,7 +86,6 @@ abstract class BytesBuilder { void clear(); } - class _CopyingBytesBuilder implements BytesBuilder { // Start with 1024 bytes. static const int _INIT_SIZE = 1024; @@ -158,7 +157,6 @@ class _CopyingBytesBuilder implements BytesBuilder { } } - class _BytesBuilder implements BytesBuilder { int _length = 0; final List _chunks = []; diff --git a/pkgs/http_parser/lib/src/http_date.dart b/pkgs/http_parser/lib/src/http_date.dart index 0626c72e21..7d46d553ed 100644 --- a/pkgs/http_parser/lib/src/http_date.dart +++ b/pkgs/http_parser/lib/src/http_date.dart @@ -24,21 +24,21 @@ final _digitRegExp = new RegExp(r"\d+"); String formatHttpDate(DateTime date) { date = date.toUtc(); var buffer = new StringBuffer() - ..write(_WEEKDAYS[date.weekday - 1]) - ..write(", ") - ..write(date.day <= 9 ? "0" : "") - ..write(date.day.toString()) - ..write(" ") - ..write(_MONTHS[date.month - 1]) - ..write(" ") - ..write(date.year.toString()) - ..write(date.hour <= 9 ? " 0" : " ") - ..write(date.hour.toString()) - ..write(date.minute <= 9 ? ":0" : ":") - ..write(date.minute.toString()) - ..write(date.second <= 9 ? ":0" : ":") - ..write(date.second.toString()) - ..write(" GMT"); + ..write(_WEEKDAYS[date.weekday - 1]) + ..write(", ") + ..write(date.day <= 9 ? "0" : "") + ..write(date.day.toString()) + ..write(" ") + ..write(_MONTHS[date.month - 1]) + ..write(" ") + ..write(date.year.toString()) + ..write(date.hour <= 9 ? " 0" : " ") + ..write(date.hour.toString()) + ..write(date.minute <= 9 ? ":0" : ":") + ..write(date.minute.toString()) + ..write(date.second <= 9 ? ":0" : ":") + ..write(date.second.toString()) + ..write(" GMT"); return buffer.toString(); } @@ -88,9 +88,7 @@ DateTime parseHttpDate(String date) { scanner.expect(" "); var month = _parseMonth(scanner); scanner.expect(" "); - var day = scanner.scan(" ") ? - _parseInt(scanner, 1) : - _parseInt(scanner, 2); + var day = scanner.scan(" ") ? _parseInt(scanner, 1) : _parseInt(scanner, 2); scanner.expect(" "); var time = _parseTime(scanner); scanner.expect(" "); @@ -141,8 +139,8 @@ DateTime _parseTime(StringScanner scanner) { /// Validates that [day] is a valid day for [month]. If it's not, throws a /// [FormatException]. DateTime _makeDateTime(int year, int month, int day, DateTime time) { - var dateTime = new DateTime.utc( - year, month, day, time.hour, time.minute, time.second); + var dateTime = + new DateTime.utc(year, month, day, time.hour, time.minute, time.second); // If [day] was too large, it will cause [month] to overflow. if (dateTime.month != month) { diff --git a/pkgs/http_parser/lib/src/media_type.dart b/pkgs/http_parser/lib/src/media_type.dart index 7a1ff23ba7..fa9350e32e 100644 --- a/pkgs/http_parser/lib/src/media_type.dart +++ b/pkgs/http_parser/lib/src/media_type.dart @@ -73,8 +73,9 @@ class MediaType { } else { scanner.expect(_quotedString); var quotedString = scanner.lastMatch[0]; - value = quotedString.substring(1, quotedString.length - 1). - replaceAllMapped(_quotedPair, (match) => match[1]); + value = quotedString + .substring(1, quotedString.length - 1) + .replaceAllMapped(_quotedPair, (match) => match[1]); } scanner.scan(_whitespace); @@ -139,17 +140,17 @@ class MediaType { /// This will produce a valid HTTP media type. String toString() { var buffer = new StringBuffer() - ..write(type) - ..write("/") - ..write(subtype); + ..write(type) + ..write("/") + ..write(subtype); parameters.forEach((attribute, value) { buffer.write("; $attribute="); if (_nonToken.hasMatch(value)) { buffer ..write('"') - ..write(value.replaceAllMapped( - _escapedChar, (match) => "\\" + match[0])) + ..write( + value.replaceAllMapped(_escapedChar, (match) => "\\" + match[0])) ..write('"'); } else { buffer.write(value); diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index 649af89a76..71544b1270 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,5 +1,5 @@ name: http_parser -version: 0.0.3-dev +version: 0.0.2+6 author: "Dart Team " homepage: https://github.com/dart-lang/http_parser description: > diff --git a/pkgs/http_parser/test/media_type_test.dart b/pkgs/http_parser/test/media_type_test.dart index 14affbb3cc..fe9005193b 100644 --- a/pkgs/http_parser/test/media_type_test.dart +++ b/pkgs/http_parser/test/media_type_test.dart @@ -17,14 +17,14 @@ void main() { test("allows leading whitespace", () { expect(new MediaType.parse(" text/plain").mimeType, equals("text/plain")); - expect(new MediaType.parse("\ttext/plain").mimeType, - equals("text/plain")); + expect( + new MediaType.parse("\ttext/plain").mimeType, equals("text/plain")); }); test("allows trailing whitespace", () { expect(new MediaType.parse("text/plain ").mimeType, equals("text/plain")); - expect(new MediaType.parse("text/plain\t").mimeType, - equals("text/plain")); + expect( + new MediaType.parse("text/plain\t").mimeType, equals("text/plain")); }); test("disallows separators in the MIME type", () { @@ -67,10 +67,8 @@ void main() { var type = new MediaType.parse( 'text/plain; foo="bar space"; baz="bang\\\\escape"'); expect(type.mimeType, equals("text/plain")); - expect(type.parameters, equals({ - "foo": "bar space", - "baz": "bang\\escape" - })); + expect( + type.parameters, equals({"foo": "bar space", "baz": "bang\\escape"})); }); }); @@ -102,14 +100,9 @@ void main() { }); test("[parameters] overrides and adds to existing parameters", () { - expect(type.change(parameters: { - "foo": "zap", - "qux": "fblthp" - }).parameters, equals({ - "foo": "zap", - "baz": "bang", - "qux": "fblthp" - })); + expect( + type.change(parameters: {"foo": "zap", "qux": "fblthp"}).parameters, + equals({"foo": "zap", "baz": "bang", "qux": "fblthp"})); }); test("[clearParameters] removes existing parameters", () { @@ -117,9 +110,8 @@ void main() { }); test("[clearParameters] with [parameters] removes before adding", () { - var newType = type.change( - parameters: {"foo": "zap"}, - clearParameters: true); + var newType = + type.change(parameters: {"foo": "zap"}, clearParameters: true); expect(newType.parameters, equals({"foo": "zap"})); }); @@ -155,9 +147,8 @@ void main() { }); test("serializes multiple parameters", () { - expect(new MediaType("text", "plain", { - "foo": "bar", "baz": "bang" - }).toString(), equals("text/plain; foo=bar; baz=bang")); + expect(new MediaType("text", "plain", {"foo": "bar", "baz": "bang"}) + .toString(), equals("text/plain; foo=bar; baz=bang")); }); }); } diff --git a/pkgs/http_parser/test/web_socket_test.dart b/pkgs/http_parser/test/web_socket_test.dart index 45b84d9ec7..6a0849d944 100644 --- a/pkgs/http_parser/test/web_socket_test.dart +++ b/pkgs/http_parser/test/web_socket_test.dart @@ -22,13 +22,14 @@ void main() { }); var client = new HttpClient(); - return client.openUrl("GET", Uri.parse("http://localhost:${server.port}")) + return client + .openUrl("GET", Uri.parse("http://localhost:${server.port}")) .then((request) { request.headers - ..set("Connection", "Upgrade") - ..set("Upgrade", "websocket") - ..set("Sec-WebSocket-Key", "x3JJHMbDL1EzLkh9GBhXDw==") - ..set("Sec-WebSocket-Version", "13"); + ..set("Connection", "Upgrade") + ..set("Upgrade", "websocket") + ..set("Sec-WebSocket-Key", "x3JJHMbDL1EzLkh9GBhXDw==") + ..set("Sec-WebSocket-Version", "13"); return request.close(); }).then((response) => response.detachSocket()).then((socket) { var webSocket = new CompatibleWebSocket(socket, serverSide: false); @@ -57,10 +58,10 @@ void main() { var response = request.response; response.statusCode = 101; response.headers - ..set("Connection", "Upgrade") - ..set("Upgrade", "websocket") - ..set("Sec-WebSocket-Accept", CompatibleWebSocket.signKey( - request.headers.value('Sec-WebSocket-Key'))); + ..set("Connection", "Upgrade") + ..set("Upgrade", "websocket") + ..set("Sec-WebSocket-Accept", CompatibleWebSocket + .signKey(request.headers.value('Sec-WebSocket-Key'))); response.contentLength = 0; response.detachSocket().then((socket) { var webSocket = new CompatibleWebSocket(socket); @@ -73,7 +74,8 @@ void main() { }); }); - return WebSocket.connect('ws://localhost:${server.port}') + return WebSocket + .connect('ws://localhost:${server.port}') .then((webSocket) { var n = 0; return webSocket.listen((message) { From 4687b5eaf5f15e850cceefd041ad01adcf89804a Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 8 Jun 2015 13:05:58 -0700 Subject: [PATCH 012/126] Add more detail to the README. Closes dart-lang/http_parser#2 R=kevmoo@google.com Review URL: https://codereview.chromium.org//1144383009 --- pkgs/http_parser/CHANGELOG.md | 4 ++++ pkgs/http_parser/README.md | 15 +++++++++++++++ pkgs/http_parser/pubspec.yaml | 2 +- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index 43f346e7e9..571c8a6f5c 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.2+7 + +* Add more detail to the readme. + ## 0.0.2+6 * Updated homepage URL. diff --git a/pkgs/http_parser/README.md b/pkgs/http_parser/README.md index edfa71ae0d..1407c16d71 100644 --- a/pkgs/http_parser/README.md +++ b/pkgs/http_parser/README.md @@ -1,3 +1,18 @@ `http_parser` is a platform-independent package for parsing and serializing various HTTP-related formats. It's designed to be usable on both the browser and the server, and thus avoids referencing any types from `dart:io` or `dart:html`. +It includes: + +* Support for parsing and formatting dates according to [HTTP/1.1][2616], the + HTTP/1.1 standard. + +* A `MediaType` class that represents an HTTP media type, as used in `Accept` + and `Content-Type` headers. This class supports both parsing and formatting + media types according to [HTTP/1.1][2616]. + +* A `CompatibleWebSocket` class that supports both the client and server sides + of the [WebSocket protocol][6455] independently of any specific server + implementation. + +[2616]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html +[6455]: https://tools.ietf.org/html/rfc6455 diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index 71544b1270..c627953159 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,5 +1,5 @@ name: http_parser -version: 0.0.2+6 +version: 0.0.2+7 author: "Dart Team " homepage: https://github.com/dart-lang/http_parser description: > From c0a5aba02bd70dad95f99fbb1c5b93958a5d2d23 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 8 Jun 2015 13:34:36 -0700 Subject: [PATCH 013/126] Upgrade to the new test package. R=kevmoo@google.com Review URL: https://codereview.chromium.org//1169883002 --- pkgs/http_parser/pubspec.yaml | 6 +++--- pkgs/http_parser/test/http_date_test.dart | 2 +- pkgs/http_parser/test/media_type_test.dart | 2 +- pkgs/http_parser/test/web_socket_test.dart | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index c627953159..c06a60ea2a 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -5,10 +5,10 @@ homepage: https://github.com/dart-lang/http_parser description: > A platform-independent package for parsing and serializing HTTP formats. dependencies: - crypto: ">=0.9.0 <0.10.0" + crypto: "^0.9.0" collection: ">=0.9.1 <2.0.0" string_scanner: ">=0.0.0 <0.2.0" dev_dependencies: - unittest: ">=0.10.0 <0.12.0" + test: "^0.12.0" environment: - sdk: ">=1.2.0 <2.0.0" + sdk: ">=1.8.0 <2.0.0" diff --git a/pkgs/http_parser/test/http_date_test.dart b/pkgs/http_parser/test/http_date_test.dart index 303d6d76d0..c0a2537db6 100644 --- a/pkgs/http_parser/test/http_date_test.dart +++ b/pkgs/http_parser/test/http_date_test.dart @@ -5,7 +5,7 @@ library http_parser.http_date_test; import 'package:http_parser/http_parser.dart'; -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; void main() { group('format', () { diff --git a/pkgs/http_parser/test/media_type_test.dart b/pkgs/http_parser/test/media_type_test.dart index fe9005193b..0a9f904004 100644 --- a/pkgs/http_parser/test/media_type_test.dart +++ b/pkgs/http_parser/test/media_type_test.dart @@ -5,7 +5,7 @@ library http_parser.media_type_test; import 'package:http_parser/http_parser.dart'; -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; void main() { group("parse", () { diff --git a/pkgs/http_parser/test/web_socket_test.dart b/pkgs/http_parser/test/web_socket_test.dart index 6a0849d944..60934c5dd0 100644 --- a/pkgs/http_parser/test/web_socket_test.dart +++ b/pkgs/http_parser/test/web_socket_test.dart @@ -7,7 +7,7 @@ library http_parser.web_socket_test; import 'dart:io'; import 'package:http_parser/http_parser.dart'; -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; void main() { test("a client can communicate with a WebSocket server", () { From 0cd7425910b4a81924a3a2e381992db69c89027c Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 9 Jun 2015 12:08:47 -0700 Subject: [PATCH 014/126] Don't double-close a WebSocket in web_socket_test. R=kevmoo@google.com Review URL: https://codereview.chromium.org//1168803003. --- pkgs/http_parser/test/web_socket_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/http_parser/test/web_socket_test.dart b/pkgs/http_parser/test/web_socket_test.dart index 60934c5dd0..4da985ac43 100644 --- a/pkgs/http_parser/test/web_socket_test.dart +++ b/pkgs/http_parser/test/web_socket_test.dart @@ -14,7 +14,7 @@ void main() { return HttpServer.bind("localhost", 0).then((server) { server.transform(new WebSocketTransformer()).listen((webSocket) { webSocket.add("hello!"); - webSocket.first.then((request) { + webSocket.listen((request) { expect(request, equals("ping")); webSocket.add("pong"); webSocket.close(); From a02634b06ff6782e06efbcc601a42a93baf71f9c Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 15 Jul 2015 16:02:44 -0700 Subject: [PATCH 015/126] Bring in latest dart:io WebSocket code. This also moves various copies of dart:io code into their own subdirectory. R=kevmoo@google.com Review URL: https://codereview.chromium.org//1225403008 . --- pkgs/http_parser/CHANGELOG.md | 4 + .../lib/src/{ => copy}/bytes_builder.dart | 7 +- pkgs/http_parser/lib/src/copy/io_sink.dart | 147 +++ pkgs/http_parser/lib/src/copy/web_socket.dart | 42 + .../lib/src/copy/web_socket_impl.dart | 863 ++++++++++++++++++ pkgs/http_parser/lib/src/web_socket.dart | 833 +---------------- pkgs/http_parser/pubspec.yaml | 2 +- 7 files changed, 1069 insertions(+), 829 deletions(-) rename pkgs/http_parser/lib/src/{ => copy}/bytes_builder.dart (97%) create mode 100644 pkgs/http_parser/lib/src/copy/io_sink.dart create mode 100644 pkgs/http_parser/lib/src/copy/web_socket.dart create mode 100644 pkgs/http_parser/lib/src/copy/web_socket_impl.dart diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index 571c8a6f5c..7f50217f47 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.2+8 + +* Bring in the latest `dart:io` WebSocket code. + ## 0.0.2+7 * Add more detail to the readme. diff --git a/pkgs/http_parser/lib/src/bytes_builder.dart b/pkgs/http_parser/lib/src/copy/bytes_builder.dart similarity index 97% rename from pkgs/http_parser/lib/src/bytes_builder.dart rename to pkgs/http_parser/lib/src/copy/bytes_builder.dart index 68af4170bd..4ab83b3695 100644 --- a/pkgs/http_parser/lib/src/bytes_builder.dart +++ b/pkgs/http_parser/lib/src/copy/bytes_builder.dart @@ -7,7 +7,10 @@ // non-"dart:io" applications (issue 18348). // // Because it's copied directly, there are no modifications from the original. -library http_parser.bytes_builder; +// +// This is up-to-date as of sdk revision +// 86227840d75d974feb238f8b3c59c038b99c05cf. +library http_parser.copy.bytes_builder; import 'dart:math'; import 'dart:typed_data'; @@ -86,6 +89,7 @@ abstract class BytesBuilder { void clear(); } + class _CopyingBytesBuilder implements BytesBuilder { // Start with 1024 bytes. static const int _INIT_SIZE = 1024; @@ -157,6 +161,7 @@ class _CopyingBytesBuilder implements BytesBuilder { } } + class _BytesBuilder implements BytesBuilder { int _length = 0; final List _chunks = []; diff --git a/pkgs/http_parser/lib/src/copy/io_sink.dart b/pkgs/http_parser/lib/src/copy/io_sink.dart new file mode 100644 index 0000000000..a23b44b38a --- /dev/null +++ b/pkgs/http_parser/lib/src/copy/io_sink.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. + +// The following code is copied from sdk/lib/io/io_sink.dart. The "dart:io" +// implementation isn't used directly to support non-"dart:io" applications. +// +// Because it's copied directly, only modifications necessary to support the +// desired public API and to remove "dart:io" dependencies have been made. +// +// This is up-to-date as of sdk revision +// 86227840d75d974feb238f8b3c59c038b99c05cf. +library http_parser.copy.io_sink; + +import 'dart:async'; + +class StreamSinkImpl implements StreamSink { + final StreamConsumer _target; + Completer _doneCompleter = new Completer(); + Future _doneFuture; + StreamController _controllerInstance; + Completer _controllerCompleter; + bool _isClosed = false; + bool _isBound = false; + bool _hasError = false; + + StreamSinkImpl(this._target) { + _doneFuture = _doneCompleter.future; + } + + void add(T data) { + if (_isClosed) return; + _controller.add(data); + } + + void addError(error, [StackTrace stackTrace]) { + _controller.addError(error, stackTrace); + } + + Future addStream(Stream stream) { + if (_isBound) { + throw new StateError("StreamSink is already bound to a stream"); + } + _isBound = true; + if (_hasError) return done; + // Wait for any sync operations to complete. + Future targetAddStream() { + return _target.addStream(stream) + .whenComplete(() { + _isBound = false; + }); + } + if (_controllerInstance == null) return targetAddStream(); + var future = _controllerCompleter.future; + _controllerInstance.close(); + return future.then((_) => targetAddStream()); + } + + Future flush() { + if (_isBound) { + throw new StateError("StreamSink is bound to a stream"); + } + if (_controllerInstance == null) return new Future.value(this); + // Adding an empty stream-controller will return a future that will complete + // when all data is done. + _isBound = true; + var future = _controllerCompleter.future; + _controllerInstance.close(); + return future.whenComplete(() { + _isBound = false; + }); + } + + Future close() { + if (_isBound) { + throw new StateError("StreamSink is bound to a stream"); + } + if (!_isClosed) { + _isClosed = true; + if (_controllerInstance != null) { + _controllerInstance.close(); + } else { + _closeTarget(); + } + } + return done; + } + + void _closeTarget() { + _target.close().then(_completeDoneValue, onError: _completeDoneError); + } + + Future get done => _doneFuture; + + void _completeDoneValue(value) { + if (_doneCompleter == null) return; + _doneCompleter.complete(value); + _doneCompleter = null; + } + + void _completeDoneError(error, StackTrace stackTrace) { + if (_doneCompleter == null) return; + _hasError = true; + _doneCompleter.completeError(error, stackTrace); + _doneCompleter = null; + } + + StreamController get _controller { + if (_isBound) { + throw new StateError("StreamSink is bound to a stream"); + } + if (_isClosed) { + throw new StateError("StreamSink is closed"); + } + if (_controllerInstance == null) { + _controllerInstance = new StreamController(sync: true); + _controllerCompleter = new Completer(); + _target.addStream(_controller.stream) + .then( + (_) { + if (_isBound) { + // A new stream takes over - forward values to that stream. + _controllerCompleter.complete(this); + _controllerCompleter = null; + _controllerInstance = null; + } else { + // No new stream, .close was called. Close _target. + _closeTarget(); + } + }, + onError: (error, stackTrace) { + if (_isBound) { + // A new stream takes over - forward errors to that stream. + _controllerCompleter.completeError(error, stackTrace); + _controllerCompleter = null; + _controllerInstance = null; + } else { + // No new stream. No need to close target, as it have already + // failed. + _completeDoneError(error, stackTrace); + } + }); + } + return _controllerInstance; + } +} + diff --git a/pkgs/http_parser/lib/src/copy/web_socket.dart b/pkgs/http_parser/lib/src/copy/web_socket.dart new file mode 100644 index 0000000000..8c34beada2 --- /dev/null +++ b/pkgs/http_parser/lib/src/copy/web_socket.dart @@ -0,0 +1,42 @@ +// 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 following code is copied from sdk/lib/io/websocket.dart. The "dart:io" +// implementation isn't used directly to support non-"dart:io" applications. +// +// Because it's copied directly, only modifications necessary to support the +// desired public API and to remove "dart:io" dependencies have been made. +// +// This is up-to-date as of sdk revision +// 86227840d75d974feb238f8b3c59c038b99c05cf. +library http_parser.copy.web_socket; + +/** + * Web socket status codes used when closing a web socket connection. + */ +abstract class WebSocketStatus { + static const int NORMAL_CLOSURE = 1000; + static const int GOING_AWAY = 1001; + static const int PROTOCOL_ERROR = 1002; + static const int UNSUPPORTED_DATA = 1003; + static const int RESERVED_1004 = 1004; + static const int NO_STATUS_RECEIVED = 1005; + static const int ABNORMAL_CLOSURE = 1006; + static const int INVALID_FRAME_PAYLOAD_DATA = 1007; + static const int POLICY_VIOLATION = 1008; + static const int MESSAGE_TOO_BIG = 1009; + static const int MISSING_MANDATORY_EXTENSION = 1010; + static const int INTERNAL_SERVER_ERROR = 1011; + static const int RESERVED_1015 = 1015; +} + +abstract class WebSocket { + /** + * Possible states of the connection. + */ + static const int CONNECTING = 0; + static const int OPEN = 1; + static const int CLOSING = 2; + static const int CLOSED = 3; +} diff --git a/pkgs/http_parser/lib/src/copy/web_socket_impl.dart b/pkgs/http_parser/lib/src/copy/web_socket_impl.dart new file mode 100644 index 0000000000..a53a2891d9 --- /dev/null +++ b/pkgs/http_parser/lib/src/copy/web_socket_impl.dart @@ -0,0 +1,863 @@ +// 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 following code is copied from sdk/lib/io/websocket_impl.dart. The +// "dart:io" implementation isn't used directly to support non-"dart:io" +// applications. +// +// Because it's copied directly, only modifications necessary to support the +// desired public API and to remove "dart:io" dependencies have been made. +// +// This is up-to-date as of sdk revision +// 86227840d75d974feb238f8b3c59c038b99c05cf. +library http_parser.copy.web_socket_impl; + +import 'dart:async'; +import 'dart:convert'; +import 'dart:math'; +import 'dart:typed_data'; + +import '../web_socket.dart'; +import 'bytes_builder.dart'; +import 'io_sink.dart'; +import 'web_socket.dart'; + +const String webSocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + +final _random = new Random(); + +// Matches _WebSocketOpcode. +class _WebSocketMessageType { + static const int NONE = 0; + static const int TEXT = 1; + static const int BINARY = 2; +} + + +class _WebSocketOpcode { + static const int CONTINUATION = 0; + static const int TEXT = 1; + static const int BINARY = 2; + static const int RESERVED_3 = 3; + static const int RESERVED_4 = 4; + static const int RESERVED_5 = 5; + static const int RESERVED_6 = 6; + static const int RESERVED_7 = 7; + static const int CLOSE = 8; + static const int PING = 9; + static const int PONG = 10; + static const int RESERVED_B = 11; + static const int RESERVED_C = 12; + static const int RESERVED_D = 13; + static const int RESERVED_E = 14; + static const int RESERVED_F = 15; +} + +/** + * The web socket protocol transformer handles the protocol byte stream + * which is supplied through the [:handleData:]. As the protocol is processed, + * it'll output frame data as either a List or String. + * + * Important infomation about usage: Be sure you use cancelOnError, so the + * socket will be closed when the processer encounter an error. Not using it + * will lead to undefined behaviour. + */ +// TODO(ajohnsen): make this transformer reusable? +class _WebSocketProtocolTransformer implements StreamTransformer, EventSink { + static const int START = 0; + static const int LEN_FIRST = 1; + static const int LEN_REST = 2; + static const int MASK = 3; + static const int PAYLOAD = 4; + static const int CLOSED = 5; + static const int FAILURE = 6; + + int _state = START; + bool _fin = false; + int _opcode = -1; + int _len = -1; + bool _masked = false; + int _remainingLenBytes = -1; + int _remainingMaskingKeyBytes = 4; + int _remainingPayloadBytes = -1; + int _unmaskingIndex = 0; + int _currentMessageType = _WebSocketMessageType.NONE; + int closeCode = WebSocketStatus.NO_STATUS_RECEIVED; + String closeReason = ""; + + EventSink _eventSink; + + final bool _serverSide; + final List _maskingBytes = new List(4); + final BytesBuilder _payload = new BytesBuilder(copy: false); + + _WebSocketProtocolTransformer([this._serverSide = false]); + + Stream bind(Stream stream) { + return new Stream.eventTransformed( + stream, + (EventSink eventSink) { + if (_eventSink != null) { + throw new StateError("WebSocket transformer already used."); + } + _eventSink = eventSink; + return this; + }); + } + + void addError(Object error, [StackTrace stackTrace]) => + _eventSink.addError(error, stackTrace); + + void close() => _eventSink.close(); + + /** + * Process data received from the underlying communication channel. + */ + void add(Uint8List buffer) { + int count = buffer.length; + int index = 0; + int lastIndex = count; + if (_state == CLOSED) { + throw new CompatibleWebSocketException("Data on closed connection"); + } + if (_state == FAILURE) { + throw new CompatibleWebSocketException("Data on failed connection"); + } + while ((index < lastIndex) && _state != CLOSED && _state != FAILURE) { + int byte = buffer[index]; + if (_state <= LEN_REST) { + if (_state == START) { + _fin = (byte & 0x80) != 0; + if ((byte & 0x70) != 0) { + // The RSV1, RSV2 bits RSV3 must be all zero. + throw new CompatibleWebSocketException("Protocol error"); + } + _opcode = (byte & 0xF); + if (_opcode <= _WebSocketOpcode.BINARY) { + if (_opcode == _WebSocketOpcode.CONTINUATION) { + if (_currentMessageType == _WebSocketMessageType.NONE) { + throw new CompatibleWebSocketException("Protocol error"); + } + } else { + assert(_opcode == _WebSocketOpcode.TEXT || + _opcode == _WebSocketOpcode.BINARY); + if (_currentMessageType != _WebSocketMessageType.NONE) { + throw new CompatibleWebSocketException("Protocol error"); + } + _currentMessageType = _opcode; + } + } else if (_opcode >= _WebSocketOpcode.CLOSE && + _opcode <= _WebSocketOpcode.PONG) { + // Control frames cannot be fragmented. + if (!_fin) throw new CompatibleWebSocketException("Protocol error"); + } else { + throw new CompatibleWebSocketException("Protocol error"); + } + _state = LEN_FIRST; + } else if (_state == LEN_FIRST) { + _masked = (byte & 0x80) != 0; + _len = byte & 0x7F; + if (_isControlFrame() && _len > 125) { + throw new CompatibleWebSocketException("Protocol error"); + } + if (_len == 126) { + _len = 0; + _remainingLenBytes = 2; + _state = LEN_REST; + } else if (_len == 127) { + _len = 0; + _remainingLenBytes = 8; + _state = LEN_REST; + } else { + assert(_len < 126); + _lengthDone(); + } + } else { + assert(_state == LEN_REST); + _len = _len << 8 | byte; + _remainingLenBytes--; + if (_remainingLenBytes == 0) { + _lengthDone(); + } + } + } else { + if (_state == MASK) { + _maskingBytes[4 - _remainingMaskingKeyBytes--] = byte; + if (_remainingMaskingKeyBytes == 0) { + _maskDone(); + } + } else { + assert(_state == PAYLOAD); + // The payload is not handled one byte at a time but in blocks. + int payloadLength = min(lastIndex - index, _remainingPayloadBytes); + _remainingPayloadBytes -= payloadLength; + // Unmask payload if masked. + if (_masked) { + _unmask(index, payloadLength, buffer); + } + // Control frame and data frame share _payloads. + _payload.add( + new Uint8List.view(buffer.buffer, index, payloadLength)); + index += payloadLength; + if (_isControlFrame()) { + if (_remainingPayloadBytes == 0) _controlFrameEnd(); + } else { + if (_currentMessageType != _WebSocketMessageType.TEXT && + _currentMessageType != _WebSocketMessageType.BINARY) { + throw new CompatibleWebSocketException("Protocol error"); + } + if (_remainingPayloadBytes == 0) _messageFrameEnd(); + } + + // Hack - as we always do index++ below. + index--; + } + } + + // Move to the next byte. + index++; + } + } + + void _unmask(int index, int length, Uint8List buffer) { + const int BLOCK_SIZE = 16; + // Skip Int32x4-version if message is small. + if (length >= BLOCK_SIZE) { + // Start by aligning to 16 bytes. + final int startOffset = BLOCK_SIZE - (index & 15); + final int end = index + startOffset; + for (int i = index; i < end; i++) { + buffer[i] ^= _maskingBytes[_unmaskingIndex++ & 3]; + } + index += startOffset; + length -= startOffset; + final int blockCount = length ~/ BLOCK_SIZE; + if (blockCount > 0) { + // Create mask block. + int mask = 0; + for (int i = 3; i >= 0; i--) { + mask = (mask << 8) | _maskingBytes[(_unmaskingIndex + i) & 3]; + } + Int32x4 blockMask = new Int32x4(mask, mask, mask, mask); + Int32x4List blockBuffer = new Int32x4List.view( + buffer.buffer, index, blockCount); + for (int i = 0; i < blockBuffer.length; i++) { + blockBuffer[i] ^= blockMask; + } + final int bytes = blockCount * BLOCK_SIZE; + index += bytes; + length -= bytes; + } + } + // Handle end. + final int end = index + length; + for (int i = index; i < end; i++) { + buffer[i] ^= _maskingBytes[_unmaskingIndex++ & 3]; + } + } + + void _lengthDone() { + if (_masked) { + if (!_serverSide) { + throw new CompatibleWebSocketException( + "Received masked frame from server"); + } + _state = MASK; + } else { + if (_serverSide) { + throw new CompatibleWebSocketException( + "Received unmasked frame from client"); + } + _remainingPayloadBytes = _len; + _startPayload(); + } + } + + void _maskDone() { + _remainingPayloadBytes = _len; + _startPayload(); + } + + void _startPayload() { + // If there is no actual payload perform perform callbacks without + // going through the PAYLOAD state. + if (_remainingPayloadBytes == 0) { + if (_isControlFrame()) { + switch (_opcode) { + case _WebSocketOpcode.CLOSE: + _state = CLOSED; + _eventSink.close(); + break; + case _WebSocketOpcode.PING: + _eventSink.add(new _WebSocketPing()); + break; + case _WebSocketOpcode.PONG: + _eventSink.add(new _WebSocketPong()); + break; + } + _prepareForNextFrame(); + } else { + _messageFrameEnd(); + } + } else { + _state = PAYLOAD; + } + } + + void _messageFrameEnd() { + if (_fin) { + switch (_currentMessageType) { + case _WebSocketMessageType.TEXT: + _eventSink.add(UTF8.decode(_payload.takeBytes())); + break; + case _WebSocketMessageType.BINARY: + _eventSink.add(_payload.takeBytes()); + break; + } + _currentMessageType = _WebSocketMessageType.NONE; + } + _prepareForNextFrame(); + } + + void _controlFrameEnd() { + switch (_opcode) { + case _WebSocketOpcode.CLOSE: + closeCode = WebSocketStatus.NO_STATUS_RECEIVED; + var payload = _payload.takeBytes(); + if (payload.length > 0) { + if (payload.length == 1) { + throw new CompatibleWebSocketException("Protocol error"); + } + closeCode = payload[0] << 8 | payload[1]; + if (closeCode == WebSocketStatus.NO_STATUS_RECEIVED) { + throw new CompatibleWebSocketException("Protocol error"); + } + if (payload.length > 2) { + closeReason = UTF8.decode(payload.sublist(2)); + } + } + _state = CLOSED; + _eventSink.close(); + break; + + case _WebSocketOpcode.PING: + _eventSink.add(new _WebSocketPing(_payload.takeBytes())); + break; + + case _WebSocketOpcode.PONG: + _eventSink.add(new _WebSocketPong(_payload.takeBytes())); + break; + } + _prepareForNextFrame(); + } + + bool _isControlFrame() { + return _opcode == _WebSocketOpcode.CLOSE || + _opcode == _WebSocketOpcode.PING || + _opcode == _WebSocketOpcode.PONG; + } + + void _prepareForNextFrame() { + if (_state != CLOSED && _state != FAILURE) _state = START; + _fin = false; + _opcode = -1; + _len = -1; + _remainingLenBytes = -1; + _remainingMaskingKeyBytes = 4; + _remainingPayloadBytes = -1; + _unmaskingIndex = 0; + } +} + + +class _WebSocketPing { + final List payload; + _WebSocketPing([this.payload = null]); +} + + +class _WebSocketPong { + final List payload; + _WebSocketPong([this.payload = null]); +} + +// TODO(ajohnsen): Make this transformer reusable. +class _WebSocketOutgoingTransformer implements StreamTransformer, EventSink { + final WebSocketImpl webSocket; + EventSink _eventSink; + + _WebSocketOutgoingTransformer(this.webSocket); + + Stream bind(Stream stream) { + return new Stream.eventTransformed( + stream, + (EventSink eventSink) { + if (_eventSink != null) { + throw new StateError("WebSocket transformer already used"); + } + _eventSink = eventSink; + return this; + }); + } + + void add(message) { + if (message is _WebSocketPong) { + addFrame(_WebSocketOpcode.PONG, message.payload); + return; + } + if (message is _WebSocketPing) { + addFrame(_WebSocketOpcode.PING, message.payload); + return; + } + List data; + int opcode; + if (message != null) { + if (message is String) { + opcode = _WebSocketOpcode.TEXT; + data = UTF8.encode(message); + } else { + if (message is !List) { + throw new ArgumentError(message); + } + opcode = _WebSocketOpcode.BINARY; + data = message; + } + } else { + opcode = _WebSocketOpcode.TEXT; + } + addFrame(opcode, data); + } + + void addError(Object error, [StackTrace stackTrace]) => + _eventSink.addError(error, stackTrace); + + void close() { + int code = webSocket._outCloseCode; + String reason = webSocket._outCloseReason; + List data; + if (code != null) { + data = new List(); + data.add((code >> 8) & 0xFF); + data.add(code & 0xFF); + if (reason != null) { + data.addAll(UTF8.encode(reason)); + } + } + addFrame(_WebSocketOpcode.CLOSE, data); + _eventSink.close(); + } + + void addFrame(int opcode, List data) => + createFrame(opcode, data, webSocket._serverSide).forEach(_eventSink.add); + + static Iterable createFrame(int opcode, List data, bool serverSide) { + bool mask = !serverSide; // Masking not implemented for server. + int dataLength = data == null ? 0 : data.length; + // Determine the header size. + int headerSize = (mask) ? 6 : 2; + if (dataLength > 65535) { + headerSize += 8; + } else if (dataLength > 125) { + headerSize += 2; + } + Uint8List header = new Uint8List(headerSize); + int index = 0; + // Set FIN and opcode. + header[index++] = 0x80 | opcode; + // Determine size and position of length field. + int lengthBytes = 1; + if (dataLength > 65535) { + header[index++] = 127; + lengthBytes = 8; + } else if (dataLength > 125) { + header[index++] = 126; + lengthBytes = 2; + } + // Write the length in network byte order into the header. + for (int i = 0; i < lengthBytes; i++) { + header[index++] = dataLength >> (((lengthBytes - 1) - i) * 8) & 0xFF; + } + if (mask) { + header[1] |= 1 << 7; + var maskBytes = [_random.nextInt(256), _random.nextInt(256), + _random.nextInt(256), _random.nextInt(256)]; + header.setRange(index, index + 4, maskBytes); + index += 4; + if (data != null) { + Uint8List list; + // If this is a text message just do the masking inside the + // encoded data. + if (opcode == _WebSocketOpcode.TEXT && data is Uint8List) { + list = data; + } else { + if (data is Uint8List) { + list = new Uint8List.fromList(data); + } else { + list = new Uint8List(data.length); + for (int i = 0; i < data.length; i++) { + if (data[i] < 0 || 255 < data[i]) { + throw new ArgumentError( + "List element is not a byte value " + "(value ${data[i]} at index $i)"); + } + list[i] = data[i]; + } + } + } + const int BLOCK_SIZE = 16; + int blockCount = list.length ~/ BLOCK_SIZE; + if (blockCount > 0) { + // Create mask block. + int mask = 0; + for (int i = 3; i >= 0; i--) { + mask = (mask << 8) | maskBytes[i]; + } + Int32x4 blockMask = new Int32x4(mask, mask, mask, mask); + Int32x4List blockBuffer = new Int32x4List.view( + list.buffer, 0, blockCount); + for (int i = 0; i < blockBuffer.length; i++) { + blockBuffer[i] ^= blockMask; + } + } + // Handle end. + for (int i = blockCount * BLOCK_SIZE; i < list.length; i++) { + list[i] ^= maskBytes[i & 3]; + } + data = list; + } + } + assert(index == headerSize); + if (data == null) { + return [header]; + } else { + return [header, data]; + } + } +} + + +class _WebSocketConsumer implements StreamConsumer { + final WebSocketImpl webSocket; + final StreamSink> sink; + StreamController _controller; + StreamSubscription _subscription; + bool _issuedPause = false; + bool _closed = false; + Completer _closeCompleter = new Completer(); + Completer _completer; + + _WebSocketConsumer(this.webSocket, this.sink); + + void _onListen() { + if (_subscription != null) { + _subscription.cancel(); + } + } + + void _onPause() { + if (_subscription != null) { + _subscription.pause(); + } else { + _issuedPause = true; + } + } + + void _onResume() { + if (_subscription != null) { + _subscription.resume(); + } else { + _issuedPause = false; + } + } + + void _cancel() { + if (_subscription != null) { + var subscription = _subscription; + _subscription = null; + subscription.cancel(); + } + } + + _ensureController() { + if (_controller != null) return; + _controller = new StreamController(sync: true, + onPause: _onPause, + onResume: _onResume, + onCancel: _onListen); + var stream = _controller.stream.transform( + new _WebSocketOutgoingTransformer(webSocket)); + sink.addStream(stream) + .then((_) { + _done(); + _closeCompleter.complete(webSocket); + }, onError: (error, StackTrace stackTrace) { + _closed = true; + _cancel(); + if (error is ArgumentError) { + if (!_done(error, stackTrace)) { + _closeCompleter.completeError(error, stackTrace); + } + } else { + _done(); + _closeCompleter.complete(webSocket); + } + }); + } + + bool _done([error, StackTrace stackTrace]) { + if (_completer == null) return false; + if (error != null) { + _completer.completeError(error, stackTrace); + } else { + _completer.complete(webSocket); + } + _completer = null; + return true; + } + + Future addStream(var stream) { + if (_closed) { + stream.listen(null).cancel(); + return new Future.value(webSocket); + } + _ensureController(); + _completer = new Completer(); + _subscription = stream.listen( + (data) { + _controller.add(data); + }, + onDone: _done, + onError: _done, + cancelOnError: true); + if (_issuedPause) { + _subscription.pause(); + _issuedPause = false; + } + return _completer.future; + } + + Future close() { + _ensureController(); + Future closeSocket() { + return sink.close().catchError((_) {}).then((_) => webSocket); + } + _controller.close(); + return _closeCompleter.future.then((_) => closeSocket()); + } + + void add(data) { + if (_closed) return; + _ensureController(); + _controller.add(data); + } + + void closeSocket() { + _closed = true; + _cancel(); + close(); + } +} + + +class WebSocketImpl extends Stream with _ServiceObject + implements CompatibleWebSocket { + // Use default Map so we keep order. + static Map _webSockets = new Map(); + + final String protocol; + + StreamController _controller; + StreamSubscription _subscription; + StreamSink _sink; + + final bool _serverSide; + int _readyState = WebSocket.CONNECTING; + bool _writeClosed = false; + int _closeCode; + String _closeReason; + Duration _pingInterval; + Timer _pingTimer; + _WebSocketConsumer _consumer; + + int _outCloseCode; + String _outCloseReason; + Timer _closeTimer; + + WebSocketImpl.fromSocket(Stream> stream, + StreamSink> sink, this.protocol, [this._serverSide = false]) { + _consumer = new _WebSocketConsumer(this, sink); + _sink = new StreamSinkImpl(_consumer); + _readyState = WebSocket.OPEN; + + var transformer = new _WebSocketProtocolTransformer(_serverSide); + _subscription = stream.transform(transformer).listen( + (data) { + if (data is _WebSocketPing) { + if (!_writeClosed) _consumer.add(new _WebSocketPong(data.payload)); + } else if (data is _WebSocketPong) { + // Simply set pingInterval, as it'll cancel any timers. + pingInterval = _pingInterval; + } else { + _controller.add(data); + } + }, + onError: (error) { + if (_closeTimer != null) _closeTimer.cancel(); + if (error is FormatException) { + _close(WebSocketStatus.INVALID_FRAME_PAYLOAD_DATA); + } else { + _close(WebSocketStatus.PROTOCOL_ERROR); + } + // An error happened, set the close code set above. + _closeCode = _outCloseCode; + _closeReason = _outCloseReason; + _controller.close(); + }, + onDone: () { + if (_closeTimer != null) _closeTimer.cancel(); + if (_readyState == WebSocket.OPEN) { + _readyState = WebSocket.CLOSING; + if (!_isReservedStatusCode(transformer.closeCode)) { + _close(transformer.closeCode); + } else { + _close(); + } + _readyState = WebSocket.CLOSED; + } + // Protocol close, use close code from transformer. + _closeCode = transformer.closeCode; + _closeReason = transformer.closeReason; + _controller.close(); + }, + cancelOnError: true); + _subscription.pause(); + _controller = new StreamController(sync: true, + onListen: () => _subscription.resume(), + onCancel: () { + _subscription.cancel(); + _subscription = null; + }, + onPause: _subscription.pause, + onResume: _subscription.resume); + + _webSockets[_serviceId] = this; + } + + StreamSubscription listen(void onData(message), + {Function onError, + void onDone(), + bool cancelOnError}) { + return _controller.stream.listen(onData, + onError: onError, + onDone: onDone, + cancelOnError: cancelOnError); + } + + Duration get pingInterval => _pingInterval; + + void set pingInterval(Duration interval) { + if (_writeClosed) return; + if (_pingTimer != null) _pingTimer.cancel(); + _pingInterval = interval; + + if (_pingInterval == null) return; + + _pingTimer = new Timer(_pingInterval, () { + if (_writeClosed) return; + _consumer.add(new _WebSocketPing()); + _pingTimer = new Timer(_pingInterval, () { + // No pong received. + _close(WebSocketStatus.GOING_AWAY); + }); + }); + } + + int get readyState => _readyState; + + String get extensions => null; + int get closeCode => _closeCode; + String get closeReason => _closeReason; + + void add(data) => _sink.add(data); + void addError(error, [StackTrace stackTrace]) => + _sink.addError(error, stackTrace); + Future addStream(Stream stream) => _sink.addStream(stream); + Future get done => _sink.done; + + Future close([int code, String reason]) { + if (_isReservedStatusCode(code)) { + throw new CompatibleWebSocketException("Reserved status code $code"); + } + if (_outCloseCode == null) { + _outCloseCode = code; + _outCloseReason = reason; + } + if (!_controller.isClosed) { + // If a close has not yet been received from the other end then + // 1) make sure to listen on the stream so the close frame will be + // processed if received. + // 2) set a timer terminate the connection if a close frame is + // not received. + if (!_controller.hasListener && _subscription != null) { + _controller.stream.drain().catchError((_) => {}); + } + if (_closeTimer == null) { + // When closing the web-socket, we no longer accept data. + _closeTimer = new Timer(const Duration(seconds: 5), () { + // Reuse code and reason from the local close. + _closeCode = _outCloseCode; + _closeReason = _outCloseReason; + if (_subscription != null) _subscription.cancel(); + _controller.close(); + _webSockets.remove(_serviceId); + }); + } + } + return _sink.close(); + } + + void _close([int code, String reason]) { + if (_writeClosed) return; + if (_outCloseCode == null) { + _outCloseCode = code; + _outCloseReason = reason; + } + _writeClosed = true; + _consumer.closeSocket(); + _webSockets.remove(_serviceId); + } + + // The _toJSON, _serviceTypePath, and _serviceTypeName methods + // have been deleted for http_parser. The methods were unused in WebSocket + // code and produced warnings. + + static bool _isReservedStatusCode(int code) { + return code != null && + (code < WebSocketStatus.NORMAL_CLOSURE || + code == WebSocketStatus.RESERVED_1004 || + code == WebSocketStatus.NO_STATUS_RECEIVED || + code == WebSocketStatus.ABNORMAL_CLOSURE || + (code > WebSocketStatus.INTERNAL_SERVER_ERROR && + code < WebSocketStatus.RESERVED_1015) || + (code >= WebSocketStatus.RESERVED_1015 && + code < 3000)); + } +} + +// The following code is from sdk/lib/io/service_object.dart. + +int _nextServiceId = 1; + +// TODO(ajohnsen): Use other way of getting a uniq id. +abstract class _ServiceObject { + int __serviceId = 0; + int get _serviceId { + if (__serviceId == 0) __serviceId = _nextServiceId++; + return __serviceId; + } + + // The _toJSON, _servicePath, _serviceTypePath, _serviceTypeName, and + // _serviceType methods have been deleted for http_parser. The methods were + // unused in WebSocket code and produced warnings. +} diff --git a/pkgs/http_parser/lib/src/web_socket.dart b/pkgs/http_parser/lib/src/web_socket.dart index 968c70b0e1..e52c5f5290 100644 --- a/pkgs/http_parser/lib/src/web_socket.dart +++ b/pkgs/http_parser/lib/src/web_socket.dart @@ -5,13 +5,10 @@ library http_parser.web_socket; import 'dart:async'; -import 'dart:convert'; -import 'dart:math'; -import 'dart:typed_data'; import 'package:crypto/crypto.dart'; -import 'bytes_builder.dart'; +import 'copy/web_socket_impl.dart'; /// An implementation of the WebSocket protocol that's not specific to "dart:io" /// or to any particular HTTP API. @@ -62,7 +59,7 @@ abstract class CompatibleWebSocket implements Stream, StreamSink { var hash = new SHA1(); // We use [codeUnits] here rather than UTF-8-decoding the string because // [key] is expected to be base64 encoded, and so will be pure ASCII. - hash.add((key + _webSocketGUID).codeUnits); + hash.add((key + webSocketGUID).codeUnits); return CryptoUtils.bytesToBase64(hash.close()); } @@ -75,12 +72,14 @@ abstract class CompatibleWebSocket implements Stream, StreamSink { /// `Socket`), it will be used for both sending and receiving data. Otherwise, /// it will be used for receiving data and [sink] will be used for sending it. /// + /// [protocol] should be the protocol negotiated by this handshake, if any. + /// /// If this is a WebSocket server, [serverSide] should be `true` (the /// default); if it's a client, [serverSide] should be `false`. /// /// [WebSocket handshake]: https://tools.ietf.org/html/rfc6455#section-4 factory CompatibleWebSocket(Stream> stream, - {StreamSink> sink, bool serverSide: true}) { + {StreamSink> sink, String protocol, bool serverSide: true}) { if (sink == null) { if (stream is! StreamSink) { throw new ArgumentError("If stream isn't also a StreamSink, sink must " @@ -89,7 +88,7 @@ abstract class CompatibleWebSocket implements Stream, StreamSink { sink = stream as StreamSink; } - return new _WebSocketImpl._fromSocket(stream, sink, serverSide); + return new WebSocketImpl.fromSocket(stream, sink, protocol, serverSide); } /// Closes the web socket connection. @@ -113,823 +112,3 @@ class CompatibleWebSocketException implements Exception { ? "CompatibleWebSocketException" : "CompatibleWebSocketException: $message"; } - -// The following code is copied from sdk/lib/io/websocket_impl.dart. The -// "dart:io" implementation isn't used directly both to support non-"dart:io" -// applications, and because it's incompatible with non-"dart:io" HTTP requests -// (issue 18172). -// -// Because it's copied directly, only modifications necessary to support the -// desired public API and to remove "dart:io" dependencies have been made. - -/** - * Web socket status codes used when closing a web socket connection. - */ -abstract class _WebSocketStatus { - static const int NORMAL_CLOSURE = 1000; - static const int GOING_AWAY = 1001; - static const int PROTOCOL_ERROR = 1002; - static const int UNSUPPORTED_DATA = 1003; - static const int RESERVED_1004 = 1004; - static const int NO_STATUS_RECEIVED = 1005; - static const int ABNORMAL_CLOSURE = 1006; - static const int INVALID_FRAME_PAYLOAD_DATA = 1007; - static const int POLICY_VIOLATION = 1008; - static const int MESSAGE_TOO_BIG = 1009; - static const int MISSING_MANDATORY_EXTENSION = 1010; - static const int INTERNAL_SERVER_ERROR = 1011; - static const int RESERVED_1015 = 1015; -} - -abstract class _WebSocketState { - static const int CONNECTING = 0; - static const int OPEN = 1; - static const int CLOSING = 2; - static const int CLOSED = 3; -} - -const String _webSocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - -final _random = new Random(); - -// Matches _WebSocketOpcode. -class _WebSocketMessageType { - static const int NONE = 0; - static const int TEXT = 1; - static const int BINARY = 2; -} - - -class _WebSocketOpcode { - static const int CONTINUATION = 0; - static const int TEXT = 1; - static const int BINARY = 2; - static const int RESERVED_3 = 3; - static const int RESERVED_4 = 4; - static const int RESERVED_5 = 5; - static const int RESERVED_6 = 6; - static const int RESERVED_7 = 7; - static const int CLOSE = 8; - static const int PING = 9; - static const int PONG = 10; - static const int RESERVED_B = 11; - static const int RESERVED_C = 12; - static const int RESERVED_D = 13; - static const int RESERVED_E = 14; - static const int RESERVED_F = 15; -} - -/** - * The web socket protocol transformer handles the protocol byte stream - * which is supplied through the [:handleData:]. As the protocol is processed, - * it'll output frame data as either a List or String. - * - * Important infomation about usage: Be sure you use cancelOnError, so the - * socket will be closed when the processer encounter an error. Not using it - * will lead to undefined behaviour. - */ -// TODO(ajohnsen): make this transformer reusable? -class _WebSocketProtocolTransformer implements StreamTransformer, EventSink { - static const int START = 0; - static const int LEN_FIRST = 1; - static const int LEN_REST = 2; - static const int MASK = 3; - static const int PAYLOAD = 4; - static const int CLOSED = 5; - static const int FAILURE = 6; - - int _state = START; - bool _fin = false; - int _opcode = -1; - int _len = -1; - bool _masked = false; - int _remainingLenBytes = -1; - int _remainingMaskingKeyBytes = 4; - int _remainingPayloadBytes = -1; - int _unmaskingIndex = 0; - int _currentMessageType = _WebSocketMessageType.NONE; - int closeCode = _WebSocketStatus.NO_STATUS_RECEIVED; - String closeReason = ""; - - EventSink _eventSink; - - final bool _serverSide; - final List _maskingBytes = new List(4); - final BytesBuilder _payload = new BytesBuilder(copy: false); - - _WebSocketProtocolTransformer([this._serverSide = false]); - - Stream bind(Stream stream) { - return new Stream.eventTransformed( - stream, - (EventSink eventSink) { - if (_eventSink != null) { - throw new StateError("WebSocket transformer already used."); - } - _eventSink = eventSink; - return this; - }); - } - - void addError(Object error, [StackTrace stackTrace]) => - _eventSink.addError(error, stackTrace); - - void close() => _eventSink.close(); - - /** - * Process data received from the underlying communication channel. - */ - void add(Uint8List buffer) { - int count = buffer.length; - int index = 0; - int lastIndex = count; - if (_state == CLOSED) { - throw new CompatibleWebSocketException("Data on closed connection"); - } - if (_state == FAILURE) { - throw new CompatibleWebSocketException("Data on failed connection"); - } - while ((index < lastIndex) && _state != CLOSED && _state != FAILURE) { - int byte = buffer[index]; - if (_state <= LEN_REST) { - if (_state == START) { - _fin = (byte & 0x80) != 0; - if ((byte & 0x70) != 0) { - // The RSV1, RSV2 bits RSV3 must be all zero. - throw new CompatibleWebSocketException("Protocol error"); - } - _opcode = (byte & 0xF); - if (_opcode <= _WebSocketOpcode.BINARY) { - if (_opcode == _WebSocketOpcode.CONTINUATION) { - if (_currentMessageType == _WebSocketMessageType.NONE) { - throw new CompatibleWebSocketException("Protocol error"); - } - } else { - assert(_opcode == _WebSocketOpcode.TEXT || - _opcode == _WebSocketOpcode.BINARY); - if (_currentMessageType != _WebSocketMessageType.NONE) { - throw new CompatibleWebSocketException("Protocol error"); - } - _currentMessageType = _opcode; - } - } else if (_opcode >= _WebSocketOpcode.CLOSE && - _opcode <= _WebSocketOpcode.PONG) { - // Control frames cannot be fragmented. - if (!_fin) throw new CompatibleWebSocketException("Protocol error"); - } else { - throw new CompatibleWebSocketException("Protocol error"); - } - _state = LEN_FIRST; - } else if (_state == LEN_FIRST) { - _masked = (byte & 0x80) != 0; - _len = byte & 0x7F; - if (_isControlFrame() && _len > 125) { - throw new CompatibleWebSocketException("Protocol error"); - } - if (_len == 126) { - _len = 0; - _remainingLenBytes = 2; - _state = LEN_REST; - } else if (_len == 127) { - _len = 0; - _remainingLenBytes = 8; - _state = LEN_REST; - } else { - assert(_len < 126); - _lengthDone(); - } - } else { - assert(_state == LEN_REST); - _len = _len << 8 | byte; - _remainingLenBytes--; - if (_remainingLenBytes == 0) { - _lengthDone(); - } - } - } else { - if (_state == MASK) { - _maskingBytes[4 - _remainingMaskingKeyBytes--] = byte; - if (_remainingMaskingKeyBytes == 0) { - _maskDone(); - } - } else { - assert(_state == PAYLOAD); - // The payload is not handled one byte at a time but in blocks. - int payloadLength = min(lastIndex - index, _remainingPayloadBytes); - _remainingPayloadBytes -= payloadLength; - // Unmask payload if masked. - if (_masked) { - _unmask(index, payloadLength, buffer); - } - // Control frame and data frame share _payloads. - _payload.add( - new Uint8List.view(buffer.buffer, index, payloadLength)); - index += payloadLength; - if (_isControlFrame()) { - if (_remainingPayloadBytes == 0) _controlFrameEnd(); - } else { - if (_currentMessageType != _WebSocketMessageType.TEXT && - _currentMessageType != _WebSocketMessageType.BINARY) { - throw new CompatibleWebSocketException("Protocol error"); - } - if (_remainingPayloadBytes == 0) _messageFrameEnd(); - } - - // Hack - as we always do index++ below. - index--; - } - } - - // Move to the next byte. - index++; - } - } - - void _unmask(int index, int length, Uint8List buffer) { - const int BLOCK_SIZE = 16; - // Skip Int32x4-version if message is small. - if (length >= BLOCK_SIZE) { - // Start by aligning to 16 bytes. - final int startOffset = BLOCK_SIZE - (index & 15); - final int end = index + startOffset; - for (int i = index; i < end; i++) { - buffer[i] ^= _maskingBytes[_unmaskingIndex++ & 3]; - } - index += startOffset; - length -= startOffset; - final int blockCount = length ~/ BLOCK_SIZE; - if (blockCount > 0) { - // Create mask block. - int mask = 0; - for (int i = 3; i >= 0; i--) { - mask = (mask << 8) | _maskingBytes[(_unmaskingIndex + i) & 3]; - } - Int32x4 blockMask = new Int32x4(mask, mask, mask, mask); - Int32x4List blockBuffer = new Int32x4List.view( - buffer.buffer, index, blockCount); - for (int i = 0; i < blockBuffer.length; i++) { - blockBuffer[i] ^= blockMask; - } - final int bytes = blockCount * BLOCK_SIZE; - index += bytes; - length -= bytes; - } - } - // Handle end. - final int end = index + length; - for (int i = index; i < end; i++) { - buffer[i] ^= _maskingBytes[_unmaskingIndex++ & 3]; - } - } - - void _lengthDone() { - if (_masked) { - if (!_serverSide) { - throw new CompatibleWebSocketException( - "Received masked frame from server"); - } - _state = MASK; - } else { - if (_serverSide) { - throw new CompatibleWebSocketException( - "Received unmasked frame from client"); - } - _remainingPayloadBytes = _len; - _startPayload(); - } - } - - void _maskDone() { - _remainingPayloadBytes = _len; - _startPayload(); - } - - void _startPayload() { - // If there is no actual payload perform perform callbacks without - // going through the PAYLOAD state. - if (_remainingPayloadBytes == 0) { - if (_isControlFrame()) { - switch (_opcode) { - case _WebSocketOpcode.CLOSE: - _state = CLOSED; - _eventSink.close(); - break; - case _WebSocketOpcode.PING: - _eventSink.add(new _WebSocketPing()); - break; - case _WebSocketOpcode.PONG: - _eventSink.add(new _WebSocketPong()); - break; - } - _prepareForNextFrame(); - } else { - _messageFrameEnd(); - } - } else { - _state = PAYLOAD; - } - } - - void _messageFrameEnd() { - if (_fin) { - switch (_currentMessageType) { - case _WebSocketMessageType.TEXT: - _eventSink.add(UTF8.decode(_payload.takeBytes())); - break; - case _WebSocketMessageType.BINARY: - _eventSink.add(_payload.takeBytes()); - break; - } - _currentMessageType = _WebSocketMessageType.NONE; - } - _prepareForNextFrame(); - } - - void _controlFrameEnd() { - switch (_opcode) { - case _WebSocketOpcode.CLOSE: - closeCode = _WebSocketStatus.NO_STATUS_RECEIVED; - var payload = _payload.takeBytes(); - if (payload.length > 0) { - if (payload.length == 1) { - throw new CompatibleWebSocketException("Protocol error"); - } - closeCode = payload[0] << 8 | payload[1]; - if (closeCode == _WebSocketStatus.NO_STATUS_RECEIVED) { - throw new CompatibleWebSocketException("Protocol error"); - } - if (payload.length > 2) { - closeReason = UTF8.decode(payload.sublist(2)); - } - } - _state = CLOSED; - _eventSink.close(); - break; - - case _WebSocketOpcode.PING: - _eventSink.add(new _WebSocketPing(_payload.takeBytes())); - break; - - case _WebSocketOpcode.PONG: - _eventSink.add(new _WebSocketPong(_payload.takeBytes())); - break; - } - _prepareForNextFrame(); - } - - bool _isControlFrame() { - return _opcode == _WebSocketOpcode.CLOSE || - _opcode == _WebSocketOpcode.PING || - _opcode == _WebSocketOpcode.PONG; - } - - void _prepareForNextFrame() { - if (_state != CLOSED && _state != FAILURE) _state = START; - _fin = false; - _opcode = -1; - _len = -1; - _remainingLenBytes = -1; - _remainingMaskingKeyBytes = 4; - _remainingPayloadBytes = -1; - _unmaskingIndex = 0; - } -} - - -class _WebSocketPing { - final List payload; - _WebSocketPing([this.payload = null]); -} - - -class _WebSocketPong { - final List payload; - _WebSocketPong([this.payload = null]); -} - -// TODO(ajohnsen): Make this transformer reusable. -class _WebSocketOutgoingTransformer implements StreamTransformer, EventSink { - final _WebSocketImpl webSocket; - EventSink _eventSink; - - _WebSocketOutgoingTransformer(this.webSocket); - - Stream bind(Stream stream) { - return new Stream.eventTransformed( - stream, - (EventSink eventSink) { - if (_eventSink != null) { - throw new StateError("WebSocket transformer already used"); - } - _eventSink = eventSink; - return this; - }); - } - - void add(message) { - if (message is _WebSocketPong) { - addFrame(_WebSocketOpcode.PONG, message.payload); - return; - } - if (message is _WebSocketPing) { - addFrame(_WebSocketOpcode.PING, message.payload); - return; - } - List data; - int opcode; - if (message != null) { - if (message is String) { - opcode = _WebSocketOpcode.TEXT; - data = UTF8.encode(message); - } else { - if (message is !List) { - throw new ArgumentError(message); - } - opcode = _WebSocketOpcode.BINARY; - data = message; - } - } else { - opcode = _WebSocketOpcode.TEXT; - } - addFrame(opcode, data); - } - - void addError(Object error, [StackTrace stackTrace]) => - _eventSink.addError(error, stackTrace); - - void close() { - int code = webSocket._outCloseCode; - String reason = webSocket._outCloseReason; - List data; - if (code != null) { - data = new List(); - data.add((code >> 8) & 0xFF); - data.add(code & 0xFF); - if (reason != null) { - data.addAll(UTF8.encode(reason)); - } - } - addFrame(_WebSocketOpcode.CLOSE, data); - _eventSink.close(); - } - - void addFrame(int opcode, List data) => - createFrame(opcode, data, webSocket._serverSide).forEach(_eventSink.add); - - static Iterable createFrame(int opcode, List data, bool serverSide) { - bool mask = !serverSide; // Masking not implemented for server. - int dataLength = data == null ? 0 : data.length; - // Determine the header size. - int headerSize = (mask) ? 6 : 2; - if (dataLength > 65535) { - headerSize += 8; - } else if (dataLength > 125) { - headerSize += 2; - } - Uint8List header = new Uint8List(headerSize); - int index = 0; - // Set FIN and opcode. - header[index++] = 0x80 | opcode; - // Determine size and position of length field. - int lengthBytes = 1; - if (dataLength > 65535) { - header[index++] = 127; - lengthBytes = 8; - } else if (dataLength > 125) { - header[index++] = 126; - lengthBytes = 2; - } - // Write the length in network byte order into the header. - for (int i = 0; i < lengthBytes; i++) { - header[index++] = dataLength >> (((lengthBytes - 1) - i) * 8) & 0xFF; - } - if (mask) { - header[1] |= 1 << 7; - var maskBytes = [_random.nextInt(256), _random.nextInt(256), - _random.nextInt(256), _random.nextInt(256)]; - header.setRange(index, index + 4, maskBytes); - index += 4; - if (data != null) { - Uint8List list; - // If this is a text message just do the masking inside the - // encoded data. - if (opcode == _WebSocketOpcode.TEXT && data is Uint8List) { - list = data; - } else { - if (data is Uint8List) { - list = new Uint8List.fromList(data); - } else { - list = new Uint8List(data.length); - for (int i = 0; i < data.length; i++) { - if (data[i] < 0 || 255 < data[i]) { - throw new ArgumentError( - "List element is not a byte value " - "(value ${data[i]} at index $i)"); - } - list[i] = data[i]; - } - } - } - const int BLOCK_SIZE = 16; - int blockCount = list.length ~/ BLOCK_SIZE; - if (blockCount > 0) { - // Create mask block. - int mask = 0; - for (int i = 3; i >= 0; i--) { - mask = (mask << 8) | maskBytes[i]; - } - Int32x4 blockMask = new Int32x4(mask, mask, mask, mask); - Int32x4List blockBuffer = new Int32x4List.view( - list.buffer, 0, blockCount); - for (int i = 0; i < blockBuffer.length; i++) { - blockBuffer[i] ^= blockMask; - } - } - // Handle end. - for (int i = blockCount * BLOCK_SIZE; i < list.length; i++) { - list[i] ^= maskBytes[i & 3]; - } - data = list; - } - } - assert(index == headerSize); - if (data == null) { - return [header]; - } else { - return [header, data]; - } - } -} - - -class _WebSocketConsumer implements StreamConsumer { - final _WebSocketImpl webSocket; - final StreamSink> sink; - StreamController _controller; - StreamSubscription _subscription; - bool _issuedPause = false; - bool _closed = false; - Completer _closeCompleter = new Completer(); - Completer _completer; - - _WebSocketConsumer(this.webSocket, this.sink); - - void _onListen() { - if (_subscription != null) { - _subscription.cancel(); - } - } - - void _onPause() { - if (_subscription != null) { - _subscription.pause(); - } else { - _issuedPause = true; - } - } - - void _onResume() { - if (_subscription != null) { - _subscription.resume(); - } else { - _issuedPause = false; - } - } - - void _cancel() { - if (_subscription != null) { - var subscription = _subscription; - _subscription = null; - subscription.cancel(); - } - } - - _ensureController() { - if (_controller != null) return; - _controller = new StreamController(sync: true, - onPause: _onPause, - onResume: _onResume, - onCancel: _onListen); - var stream = _controller.stream.transform( - new _WebSocketOutgoingTransformer(webSocket)); - sink.addStream(stream) - .then((_) { - _done(); - _closeCompleter.complete(webSocket); - }, onError: (error, StackTrace stackTrace) { - _closed = true; - _cancel(); - if (error is ArgumentError) { - if (!_done(error, stackTrace)) { - _closeCompleter.completeError(error, stackTrace); - } - } else { - _done(); - _closeCompleter.complete(webSocket); - } - }); - } - - bool _done([error, StackTrace stackTrace]) { - if (_completer == null) return false; - if (error != null) { - _completer.completeError(error, stackTrace); - } else { - _completer.complete(webSocket); - } - _completer = null; - return true; - } - - Future addStream(var stream) { - if (_closed) { - stream.listen(null).cancel(); - return new Future.value(webSocket); - } - _ensureController(); - _completer = new Completer(); - _subscription = stream.listen( - (data) { - _controller.add(data); - }, - onDone: _done, - onError: _done, - cancelOnError: true); - if (_issuedPause) { - _subscription.pause(); - _issuedPause = false; - } - return _completer.future; - } - - Future close() { - _ensureController(); - Future closeSocket() { - return sink.close().catchError((_) {}).then((_) => webSocket); - } - _controller.close(); - return _closeCompleter.future.then((_) => closeSocket()); - } - - void add(data) { - if (_closed) return; - _ensureController(); - _controller.add(data); - } - - void closeSocket() { - _closed = true; - _cancel(); - close(); - } -} - - -class _WebSocketImpl extends Stream implements CompatibleWebSocket { - StreamController _controller; - StreamSubscription _subscription; - StreamController _sink; - - final bool _serverSide; - int _readyState = _WebSocketState.CONNECTING; - bool _writeClosed = false; - int _closeCode; - String _closeReason; - Duration _pingInterval; - Timer _pingTimer; - _WebSocketConsumer _consumer; - - int _outCloseCode; - String _outCloseReason; - Timer _closeTimer; - - _WebSocketImpl._fromSocket(Stream> stream, - StreamSink> sink, [this._serverSide = false]) { - _consumer = new _WebSocketConsumer(this, sink); - _sink = new StreamController(); - _sink.stream.pipe(_consumer); - _readyState = _WebSocketState.OPEN; - - var transformer = new _WebSocketProtocolTransformer(_serverSide); - _subscription = stream.transform(transformer).listen( - (data) { - if (data is _WebSocketPing) { - if (!_writeClosed) _consumer.add(new _WebSocketPong(data.payload)); - } else if (data is _WebSocketPong) { - // Simply set pingInterval, as it'll cancel any timers. - pingInterval = _pingInterval; - } else { - _controller.add(data); - } - }, - onError: (error) { - if (_closeTimer != null) _closeTimer.cancel(); - if (error is FormatException) { - _close(_WebSocketStatus.INVALID_FRAME_PAYLOAD_DATA); - } else { - _close(_WebSocketStatus.PROTOCOL_ERROR); - } - _controller.close(); - }, - onDone: () { - if (_closeTimer != null) _closeTimer.cancel(); - if (_readyState == _WebSocketState.OPEN) { - _readyState = _WebSocketState.CLOSING; - if (!_isReservedStatusCode(transformer.closeCode)) { - _close(transformer.closeCode); - } else { - _close(); - } - _readyState = _WebSocketState.CLOSED; - } - _closeCode = transformer.closeCode; - _closeReason = transformer.closeReason; - _controller.close(); - }, - cancelOnError: true); - _subscription.pause(); - _controller = new StreamController(sync: true, - onListen: _subscription.resume, - onPause: _subscription.pause, - onResume: _subscription.resume); - } - - StreamSubscription listen(void onData(message), - {Function onError, - void onDone(), - bool cancelOnError}) { - return _controller.stream.listen(onData, - onError: onError, - onDone: onDone, - cancelOnError: cancelOnError); - } - - Duration get pingInterval => _pingInterval; - - void set pingInterval(Duration interval) { - if (_writeClosed) return; - if (_pingTimer != null) _pingTimer.cancel(); - _pingInterval = interval; - - if (_pingInterval == null) return; - - _pingTimer = new Timer(_pingInterval, () { - if (_writeClosed) return; - _consumer.add(new _WebSocketPing()); - _pingTimer = new Timer(_pingInterval, () { - // No pong received. - _close(_WebSocketStatus.GOING_AWAY); - }); - }); - } - - int get closeCode => _closeCode; - String get closeReason => _closeReason; - - void add(data) => _sink.add(data); - void addError(error, [StackTrace stackTrace]) => - _sink.addError(error, stackTrace); - Future addStream(Stream stream) => _sink.addStream(stream); - Future get done => _sink.done; - - Future close([int code, String reason]) { - if (_isReservedStatusCode(code)) { - throw new CompatibleWebSocketException("Reserved status code $code"); - } - if (_outCloseCode == null) { - _outCloseCode = code; - _outCloseReason = reason; - } - if (_closeTimer == null && !_controller.isClosed) { - // When closing the web-socket, we no longer accept data. - _closeTimer = new Timer(const Duration(seconds: 5), () { - _subscription.cancel(); - _controller.close(); - }); - } - return _sink.close(); - } - - void _close([int code, String reason]) { - if (_writeClosed) return; - if (_outCloseCode == null) { - _outCloseCode = code; - _outCloseReason = reason; - } - _writeClosed = true; - _consumer.closeSocket(); - } - - static bool _isReservedStatusCode(int code) { - return code != null && - (code < _WebSocketStatus.NORMAL_CLOSURE || - code == _WebSocketStatus.RESERVED_1004 || - code == _WebSocketStatus.NO_STATUS_RECEIVED || - code == _WebSocketStatus.ABNORMAL_CLOSURE || - (code > _WebSocketStatus.INTERNAL_SERVER_ERROR && - code < _WebSocketStatus.RESERVED_1015) || - (code >= _WebSocketStatus.RESERVED_1015 && - code < 3000)); - } -} - diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index c06a60ea2a..0d813cc8c3 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,5 +1,5 @@ name: http_parser -version: 0.0.2+7 +version: 0.0.2+8 author: "Dart Team " homepage: https://github.com/dart-lang/http_parser description: > From c24d3d2c13f09608964fbba8fc08a48581bc48d4 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 16 Jul 2015 13:02:40 -0700 Subject: [PATCH 016/126] Use the new test runner on the bots. R=kevmoo@google.com Review URL: https://codereview.chromium.org//1235263004 . --- pkgs/http_parser/.gitignore | 1 + pkgs/http_parser/.status | 18 ------------------ pkgs/http_parser/.test_config | 3 +++ pkgs/http_parser/pubspec.yaml | 2 +- pkgs/http_parser/test/web_socket_test.dart | 2 ++ 5 files changed, 7 insertions(+), 19 deletions(-) delete mode 100644 pkgs/http_parser/.status create mode 100644 pkgs/http_parser/.test_config diff --git a/pkgs/http_parser/.gitignore b/pkgs/http_parser/.gitignore index 388eff0ba3..7dbf0350d6 100644 --- a/pkgs/http_parser/.gitignore +++ b/pkgs/http_parser/.gitignore @@ -3,6 +3,7 @@ .pub/ build/ packages +.packages # Or the files created by dart2js. *.dart.js diff --git a/pkgs/http_parser/.status b/pkgs/http_parser/.status deleted file mode 100644 index e191550368..0000000000 --- a/pkgs/http_parser/.status +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) 2014, 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. - -# Skip non-test files ending with "_test". -packages/*: Skip -*/packages/*: Skip -*/*/packages/*: Skip -*/*/*/packages/*: Skip -*/*/*/*packages/*: Skip -*/*/*/*/*packages/*: Skip - -# Only run tests from the build directory, since we don't care about the -# difference between transformed an untransformed code. -test/*: Skip - -[ $browser ] -build/test/web_socket_test: Fail, OK # Uses dart:io diff --git a/pkgs/http_parser/.test_config b/pkgs/http_parser/.test_config new file mode 100644 index 0000000000..412fc5c5cf --- /dev/null +++ b/pkgs/http_parser/.test_config @@ -0,0 +1,3 @@ +{ + "test_package": true +} \ No newline at end of file diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index 0d813cc8c3..b4e002c221 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,5 +1,5 @@ name: http_parser -version: 0.0.2+8 +version: 0.0.3-dev author: "Dart Team " homepage: https://github.com/dart-lang/http_parser description: > diff --git a/pkgs/http_parser/test/web_socket_test.dart b/pkgs/http_parser/test/web_socket_test.dart index 4da985ac43..61bf0cc821 100644 --- a/pkgs/http_parser/test/web_socket_test.dart +++ b/pkgs/http_parser/test/web_socket_test.dart @@ -2,6 +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. +@TestOn('vm') + library http_parser.web_socket_test; import 'dart:io'; From 413f24c9fa0261b9cf9c712f91454e7a93520121 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 24 Aug 2015 16:55:48 -0700 Subject: [PATCH 017/126] Add an AuthenticationChallenge class. A simpler version of this existed already in dart-lang/oauth2; this makes it widely available and more robust. --- pkgs/http_parser/CHANGELOG.md | 7 + pkgs/http_parser/lib/http_parser.dart | 1 + .../lib/src/authentication_challenge.dart | 145 ++++++++++++++++++ pkgs/http_parser/lib/src/http_date.dart | 8 +- pkgs/http_parser/lib/src/media_type.dart | 45 ++---- pkgs/http_parser/lib/src/scan.dart | 71 +++++++++ pkgs/http_parser/lib/src/utils.dart | 23 +++ pkgs/http_parser/pubspec.yaml | 3 +- .../test/authentication_challenge_test.dart | 144 +++++++++++++++++ 9 files changed, 411 insertions(+), 36 deletions(-) create mode 100644 pkgs/http_parser/lib/src/authentication_challenge.dart create mode 100644 pkgs/http_parser/lib/src/scan.dart create mode 100644 pkgs/http_parser/lib/src/utils.dart create mode 100644 pkgs/http_parser/test/authentication_challenge_test.dart diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index 7f50217f47..3ac45efdd6 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,3 +1,10 @@ +## 1.0.0 + +This is 1.0.0 because the API is stable—there are no breaking changes. + +* Added an `AuthenticationChallenge` class for parsing and representing the + value of `WWW-Authenticate` and related headers. + ## 0.0.2+8 * Bring in the latest `dart:io` WebSocket code. diff --git a/pkgs/http_parser/lib/http_parser.dart b/pkgs/http_parser/lib/http_parser.dart index 0aa9cea6b7..f5921ee06c 100644 --- a/pkgs/http_parser/lib/http_parser.dart +++ b/pkgs/http_parser/lib/http_parser.dart @@ -4,6 +4,7 @@ library http_parser; +export 'src/authentication_challenge.dart'; export 'src/http_date.dart'; export 'src/media_type.dart'; export 'src/web_socket.dart'; diff --git a/pkgs/http_parser/lib/src/authentication_challenge.dart b/pkgs/http_parser/lib/src/authentication_challenge.dart new file mode 100644 index 0000000000..377398d80a --- /dev/null +++ b/pkgs/http_parser/lib/src/authentication_challenge.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 http_paser.authentication_challenge; + +import 'dart:collection'; + +import 'package:string_scanner/string_scanner.dart'; + +import 'scan.dart'; +import 'utils.dart'; + +/// A single challenge in a WWW-Authenticate header, parsed as per [RFC 2617][]. +/// +/// [RFC 2617]: http://tools.ietf.org/html/rfc2617 +/// +/// Each WWW-Authenticate header contains one or more challenges, representing +/// valid ways to authenticate with the server. +class AuthenticationChallenge { + /// The scheme describing the type of authentication that's required, for + /// example "basic" or "digest". + /// + /// This is normalized to always be lower-case. + final String scheme; + + /// The parameters describing how to authenticate. + /// + /// The semantics of these parameters are scheme-specific. + final Map parameters; + + /// Parses a WWW-Authenticate header, which should contain one or more + /// challenges. + /// + /// Throws a [FormatException] if the header is invalid. + static List parseHeader(String header) { + return wrapFormatException("authentication header", header, () { + var scanner = new StringScanner(header); + scanner.scan(whitespace); + var challenges = parseList(scanner, () { + scanner.expect(token, name: "a token"); + var scheme = scanner.lastMatch[0].toLowerCase(); + + scanner.scan(whitespace); + + // The spec specifically requires a space between the scheme and its + // params. + if (scanner.lastMatch == null || !scanner.lastMatch[0].contains(" ")) { + scanner.expect(" ", name: '" " or "="'); + } + + // Manually parse the inner list. We need to do some lookahead to + // disambiguate between an auth param and another challenge. + var params = {}; + _scanAuthParam(scanner, params); + + var beforeComma = scanner.position; + while (scanner.scan(",")) { + scanner.scan(whitespace); + + // Empty elements are allowed, but excluded from the results. + if (scanner.matches(",")) continue; + + scanner.expect(token, name: "a token"); + var name = scanner.lastMatch[0].toLowerCase(); + scanner.scan(whitespace); + + // If there's no "=", then this is another challenge rather than a + // parameter for the current challenge. + if (!scanner.scan('=')) { + scanner.position = beforeComma; + break; + } + + scanner.scan(whitespace); + + if (scanner.scan(token)) { + params[name] = scanner.lastMatch[0]; + } else { + params[name] = expectQuotedString( + scanner, name: "a token or a quoted string"); + } + + scanner.scan(whitespace); + beforeComma = scanner.position; + } + + return new AuthenticationChallenge(scheme, params); + }); + + scanner.expectDone(); + return challenges; + }); + } + + /// Parses a single WWW-Authenticate challenge value. + /// + /// Throws a [FormatException] if the challenge is invalid. + factory AuthenticationChallenge.parse(String challenge) { + return wrapFormatException("authentication challenge", challenge, () { + var scanner = new StringScanner(challenge); + scanner.scan(whitespace); + scanner.expect(token, name: "a token"); + var scheme = scanner.lastMatch[0].toLowerCase(); + + scanner.scan(whitespace); + + // The spec specifically requires a space between the scheme and its + // params. + if (scanner.lastMatch == null || !scanner.lastMatch[0].contains(" ")) { + scanner.expect(" "); + } + + var params = {}; + parseList(scanner, () => _scanAuthParam(scanner, params)); + + scanner.expectDone(); + return new AuthenticationChallenge(scheme, params); + }); + } + + /// Scans a single authentication parameter and stores its result in [params]. + static void _scanAuthParam(StringScanner scanner, Map params) { + scanner.expect(token, name: "a token"); + var name = scanner.lastMatch[0].toLowerCase(); + scanner.scan(whitespace); + scanner.expect('='); + scanner.scan(whitespace); + + if (scanner.scan(token)) { + params[name] = scanner.lastMatch[0]; + } else { + params[name] = expectQuotedString( + scanner, name: "a token or a quoted string"); + } + + scanner.scan(whitespace); + } + + /// Creates a new challenge value with [scheme] and, optionally, [parameters]. + /// + /// If [parameters] isn't passed, it defaults to an empty map. + AuthenticationChallenge(this.scheme, Map parameters) + : parameters = new UnmodifiableMapView(parameters); +} diff --git a/pkgs/http_parser/lib/src/http_date.dart b/pkgs/http_parser/lib/src/http_date.dart index 7d46d553ed..959053649d 100644 --- a/pkgs/http_parser/lib/src/http_date.dart +++ b/pkgs/http_parser/lib/src/http_date.dart @@ -6,6 +6,8 @@ library http_parser.http_date; import 'package:string_scanner/string_scanner.dart'; +import 'utils.dart'; + const _WEEKDAYS = const ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]; const _MONTHS = const ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; @@ -48,7 +50,7 @@ String formatHttpDate(DateTime date) { /// 2616](http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3). It will /// throw a [FormatException] if [date] is invalid. DateTime parseHttpDate(String date) { - try { + return wrapFormatException("HTTP date", date, () { var scanner = new StringScanner(date); if (scanner.scan(_longWeekdayRegExp)) { @@ -96,9 +98,7 @@ DateTime parseHttpDate(String date) { scanner.expectDone(); return _makeDateTime(year, month, day, time); - } on FormatException catch (error) { - throw new FormatException('Invalid HTTP date "$date": ${error.message}'); - } + }); } /// Parses a short-form month name to a form accepted by [DateTime]. diff --git a/pkgs/http_parser/lib/src/media_type.dart b/pkgs/http_parser/lib/src/media_type.dart index fa9350e32e..5bae0ec2ad 100644 --- a/pkgs/http_parser/lib/src/media_type.dart +++ b/pkgs/http_parser/lib/src/media_type.dart @@ -7,18 +7,8 @@ library http_parser.media_type; import 'package:collection/collection.dart'; import 'package:string_scanner/string_scanner.dart'; -// All of the following regular expressions come from section 2.2 of the HTTP -// spec: http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html -final _lws = new RegExp(r"(?:\r\n)?[ \t]+"); -final _token = new RegExp(r'[^()<>@,;:"\\/[\]?={} \t\x00-\x1F\x7F]+'); -final _quotedString = new RegExp(r'"(?:[^"\x00-\x1F\x7F]|\\.)*"'); -final _quotedPair = new RegExp(r'\\(.)'); - -/// A regular expression matching any number of [_lws] productions in a row. -final _whitespace = new RegExp("(?:${_lws.pattern})*"); - -/// A regular expression matching a character that is not a valid HTTP token. -final _nonToken = new RegExp(r'[()<>@,;:"\\/\[\]?={} \t\x00-\x1F\x7F]'); +import 'scan.dart'; +import 'utils.dart'; /// A regular expression matching a character that needs to be backslash-escaped /// in a quoted string. @@ -50,44 +40,37 @@ class MediaType { factory MediaType.parse(String mediaType) { // This parsing is based on sections 3.6 and 3.7 of the HTTP spec: // http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html. - try { + return wrapFormatException("media type", mediaType, () { var scanner = new StringScanner(mediaType); - scanner.scan(_whitespace); - scanner.expect(_token); + scanner.scan(whitespace); + scanner.expect(token); var type = scanner.lastMatch[0]; scanner.expect('/'); - scanner.expect(_token); + scanner.expect(token); var subtype = scanner.lastMatch[0]; - scanner.scan(_whitespace); + scanner.scan(whitespace); var parameters = {}; while (scanner.scan(';')) { - scanner.scan(_whitespace); - scanner.expect(_token); + scanner.scan(whitespace); + scanner.expect(token); var attribute = scanner.lastMatch[0]; scanner.expect('='); var value; - if (scanner.scan(_token)) { + if (scanner.scan(token)) { value = scanner.lastMatch[0]; } else { - scanner.expect(_quotedString); - var quotedString = scanner.lastMatch[0]; - value = quotedString - .substring(1, quotedString.length - 1) - .replaceAllMapped(_quotedPair, (match) => match[1]); + value = expectQuotedString(scanner); } - scanner.scan(_whitespace); + scanner.scan(whitespace); parameters[attribute] = value; } scanner.expectDone(); return new MediaType(type, subtype, parameters); - } on FormatException catch (error) { - throw new FormatException( - 'Invalid media type "$mediaType": ${error.message}'); - } + }); } MediaType(this.type, this.subtype, [Map parameters]) @@ -146,7 +129,7 @@ class MediaType { parameters.forEach((attribute, value) { buffer.write("; $attribute="); - if (_nonToken.hasMatch(value)) { + if (nonToken.hasMatch(value)) { buffer ..write('"') ..write( diff --git a/pkgs/http_parser/lib/src/scan.dart b/pkgs/http_parser/lib/src/scan.dart new file mode 100644 index 0000000000..1036383290 --- /dev/null +++ b/pkgs/http_parser/lib/src/scan.dart @@ -0,0 +1,71 @@ +// 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. + +/// A library for broadly-useful functions and regular expressions for scanning +/// HTTP entities. +/// +/// Many of the regular expressions come from [section 2.2 of the HTTP +/// spec][spec]. +/// +/// [spec]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html +library http_parser.scan; + +import 'package:string_scanner/string_scanner.dart'; + +/// An HTTP token. +final token = new RegExp(r'[^()<>@,;:"\\/[\]?={} \t\x00-\x1F\x7F]+'); + +/// Linear whitespace. +final _lws = new RegExp(r"(?:\r\n)?[ \t]+"); + +/// A quoted string. +final _quotedString = new RegExp(r'"(?:[^"\x00-\x1F\x7F]|\\.)*"'); + +/// A quoted pair. +final _quotedPair = new RegExp(r'\\(.)'); + +/// A character that is *not* a valid HTTP token. +final nonToken = new RegExp(r'[()<>@,;:"\\/\[\]?={} \t\x00-\x1F\x7F]'); + +/// A regular expression matching any number of [_lws] productions in a row. +final whitespace = new RegExp("(?:${_lws.pattern})*"); + +/// Parses a list of elements, as in `1#element` in the HTTP spec. +/// +/// [scanner] is used to parse the elements, and [parseElement] is used to parse +/// each one individually. The values returned by [parseElement] are collected +/// in a list and returned. +/// +/// Once this is finished, [scanner] will be at the next non-LWS character in +/// the string, or the end of the string. +List parseList(StringScanner scanner, parseElement()) { + var result = []; + result.add(parseElement()); + scanner.scan(whitespace); + + while (scanner.scan(",")) { + scanner.scan(whitespace); + + // Empty elements are allowed, but excluded from the results. + if (scanner.matches(",")) continue; + + result.add(parseElement()); + scanner.scan(whitespace); + } + + return result; +} + +/// Parses a single quoted string, and returns its contents. +/// +/// If [name] is passed, it's used to describe the expected value if it's not +/// found. +String expectQuotedString(StringScanner scanner, {String name}) { + if (name == null) name = "quoted string"; + scanner.expect(_quotedString, name: name); + var string = scanner.lastMatch[0]; + return string + .substring(1, string.length - 1) + .replaceAllMapped(_quotedPair, (match) => match[1]); +} diff --git a/pkgs/http_parser/lib/src/utils.dart b/pkgs/http_parser/lib/src/utils.dart new file mode 100644 index 0000000000..4dcee19880 --- /dev/null +++ b/pkgs/http_parser/lib/src/utils.dart @@ -0,0 +1,23 @@ +// 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:source_span/source_span.dart'; + +/// Runs [body] and wraps any format exceptions it produces. +/// +/// [name] should describe the type of thing being parsed, and [value] should be +/// its actual value. +wrapFormatException(String name, String value, body()) { + try { + return body(); + } on SourceSpanFormatException catch (error) { + throw new SourceSpanFormatException( + 'Invalid $name: ${error.message}', error.span, error.source); + } on FormatException catch (error) { + throw new FormatException( + 'Invalid $name "$value": ${error.message}', + error.source, + error.offset); + } +} diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index b4e002c221..9af4b458b4 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,5 +1,5 @@ name: http_parser -version: 0.0.3-dev +version: 1.0.0-dev author: "Dart Team " homepage: https://github.com/dart-lang/http_parser description: > @@ -7,6 +7,7 @@ description: > dependencies: crypto: "^0.9.0" collection: ">=0.9.1 <2.0.0" + source_span: "^1.0.0" string_scanner: ">=0.0.0 <0.2.0" dev_dependencies: test: "^0.12.0" diff --git a/pkgs/http_parser/test/authentication_challenge_test.dart b/pkgs/http_parser/test/authentication_challenge_test.dart new file mode 100644 index 0000000000..ccb8d3230b --- /dev/null +++ b/pkgs/http_parser/test/authentication_challenge_test.dart @@ -0,0 +1,144 @@ +// 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:http_parser/http_parser.dart'; +import 'package:test/test.dart'; + +void main() { + group("parse", () { + _singleChallengeTests( + (challenge) => new AuthenticationChallenge.parse(challenge)); + }); + + group("parseHeader", () { + group("with a single challenge", () { + _singleChallengeTests((challenge) { + var challenges = AuthenticationChallenge.parseHeader(challenge); + expect(challenges, hasLength(1)); + return challenges.single; + }); + }); + + test("parses multiple challenges", () { + var challenges = AuthenticationChallenge.parseHeader( + "scheme1 realm=fblthp, scheme2 realm=asdfg"); + expect(challenges, hasLength(2)); + expect(challenges.first.scheme, equals("scheme1")); + expect(challenges.first.parameters, equals({"realm": "fblthp"})); + expect(challenges.last.scheme, equals("scheme2")); + expect(challenges.last.parameters, equals({"realm": "asdfg"})); + }); + + test("parses multiple challenges with multiple parameters", () { + var challenges = AuthenticationChallenge.parseHeader( + "scheme1 realm=fblthp, foo=bar, scheme2 realm=asdfg, baz=bang"); + expect(challenges, hasLength(2)); + + expect(challenges.first.scheme, equals("scheme1")); + expect(challenges.first.parameters, equals({ + "realm": "fblthp", + "foo": "bar" + })); + + expect(challenges.last.scheme, equals("scheme2")); + expect(challenges.last.parameters, equals({ + "realm": "asdfg", + "baz": "bang" + })); + }); + }); +} + +/// Tests to run for parsing a single challenge. +/// +/// These are run on both [AuthenticationChallenge.parse] and +/// [AuthenticationChallenge.parseHeader], since they use almost entirely +/// separate code paths. +void _singleChallengeTests( + AuthenticationChallenge parseChallenge(String challenge)) { + test("parses a simple challenge", () { + var challenge = parseChallenge("scheme realm=fblthp"); + expect(challenge.scheme, equals("scheme")); + expect(challenge.parameters, equals({"realm": "fblthp"})); + }); + + test("parses multiple parameters", () { + var challenge = parseChallenge("scheme realm=fblthp, foo=bar, baz=qux"); + expect(challenge.scheme, equals("scheme")); + expect(challenge.parameters, equals({ + "realm": "fblthp", + "foo": "bar", + "baz": "qux" + })); + }); + + test("parses quoted string parameters", () { + var challenge = parseChallenge('scheme realm="fblthp, foo=bar", baz="qux"'); + expect(challenge.scheme, equals("scheme")); + expect(challenge.parameters, equals({ + "realm": "fblthp, foo=bar", + "baz": "qux" + })); + }); + + test("normalizes the case of the scheme", () { + var challenge = parseChallenge("ScHeMe realm=fblthp"); + expect(challenge.scheme, equals("scheme")); + expect(challenge.parameters, equals({"realm": "fblthp"})); + }); + + test("normalizes the case of the parameter name", () { + var challenge = parseChallenge("scheme ReAlM=fblthp"); + expect(challenge.scheme, equals("scheme")); + expect(challenge.parameters, containsPair("realm", "fblthp")); + }); + + test("allows extra whitespace", () { + var challenge = parseChallenge( + " scheme\t \trealm\t = \tfblthp\t, \tfoo\t\r\n =\tbar\t"); + expect(challenge.scheme, equals("scheme")); + expect(challenge.parameters, equals({ + "realm": "fblthp", + "foo": "bar" + })); + }); + + test("allows an empty parameter", () { + var challenge = parseChallenge( + "scheme realm=fblthp, , foo=bar"); + expect(challenge.scheme, equals("scheme")); + expect(challenge.parameters, equals({ + "realm": "fblthp", + "foo": "bar" + })); + }); + + test("disallows only a scheme", () { + expect(() => parseChallenge("scheme"), + throwsFormatException); + }); + + test("disallows a valueless parameter", () { + expect(() => parseChallenge("scheme realm"), + throwsFormatException); + expect(() => parseChallenge("scheme realm="), + throwsFormatException); + expect(() => parseChallenge("scheme realm, foo=bar"), + throwsFormatException); + }); + + test("requires a space after the scheme", () { + expect(() => parseChallenge("scheme\trealm"), + throwsFormatException); + expect(() => parseChallenge("scheme\r\n\trealm="), + throwsFormatException); + }); + + test("disallows junk after the parameters", () { + expect(() => parseChallenge("scheme realm=fblthp foo"), + throwsFormatException); + expect(() => parseChallenge("scheme realm=fblthp, foo=bar baz"), + throwsFormatException); + }); +} From b9a069a6dde7b1bd90a20110488e3281e0727ae4 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 24 Aug 2015 18:14:14 -0700 Subject: [PATCH 018/126] Add a CaseInsensitiveMap class. --- pkgs/http_parser/CHANGELOG.md | 3 ++ pkgs/http_parser/lib/http_parser.dart | 1 + .../lib/src/authentication_challenge.dart | 11 ++++--- .../lib/src/case_insensitive_map.dart | 19 ++++++++++++ pkgs/http_parser/pubspec.yaml | 2 +- .../test/case_insensitive_map_test.dart | 29 +++++++++++++++++++ 6 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 pkgs/http_parser/lib/src/case_insensitive_map.dart create mode 100644 pkgs/http_parser/test/case_insensitive_map_test.dart diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index 3ac45efdd6..cdf85d40ea 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -5,6 +5,9 @@ This is 1.0.0 because the API is stable—there are no breaking changes. * Added an `AuthenticationChallenge` class for parsing and representing the value of `WWW-Authenticate` and related headers. +* Added a `CaseInsensitiveMap` class for representing case-insensitive HTTP + values. + ## 0.0.2+8 * Bring in the latest `dart:io` WebSocket code. diff --git a/pkgs/http_parser/lib/http_parser.dart b/pkgs/http_parser/lib/http_parser.dart index f5921ee06c..77c75c0aa9 100644 --- a/pkgs/http_parser/lib/http_parser.dart +++ b/pkgs/http_parser/lib/http_parser.dart @@ -5,6 +5,7 @@ library http_parser; export 'src/authentication_challenge.dart'; +export 'src/case_insensitive_map.dart'; export 'src/http_date.dart'; export 'src/media_type.dart'; export 'src/web_socket.dart'; diff --git a/pkgs/http_parser/lib/src/authentication_challenge.dart b/pkgs/http_parser/lib/src/authentication_challenge.dart index 377398d80a..22dc2a69b8 100644 --- a/pkgs/http_parser/lib/src/authentication_challenge.dart +++ b/pkgs/http_parser/lib/src/authentication_challenge.dart @@ -8,6 +8,7 @@ import 'dart:collection'; import 'package:string_scanner/string_scanner.dart'; +import 'case_insensitive_map.dart'; import 'scan.dart'; import 'utils.dart'; @@ -26,7 +27,8 @@ class AuthenticationChallenge { /// The parameters describing how to authenticate. /// - /// The semantics of these parameters are scheme-specific. + /// The semantics of these parameters are scheme-specific. The keys of this + /// map are case-insensitive. final Map parameters; /// Parses a WWW-Authenticate header, which should contain one or more @@ -62,7 +64,7 @@ class AuthenticationChallenge { if (scanner.matches(",")) continue; scanner.expect(token, name: "a token"); - var name = scanner.lastMatch[0].toLowerCase(); + var name = scanner.lastMatch[0]; scanner.scan(whitespace); // If there's no "=", then this is another challenge rather than a @@ -122,7 +124,7 @@ class AuthenticationChallenge { /// Scans a single authentication parameter and stores its result in [params]. static void _scanAuthParam(StringScanner scanner, Map params) { scanner.expect(token, name: "a token"); - var name = scanner.lastMatch[0].toLowerCase(); + var name = scanner.lastMatch[0]; scanner.scan(whitespace); scanner.expect('='); scanner.scan(whitespace); @@ -141,5 +143,6 @@ class AuthenticationChallenge { /// /// If [parameters] isn't passed, it defaults to an empty map. AuthenticationChallenge(this.scheme, Map parameters) - : parameters = new UnmodifiableMapView(parameters); + : parameters = new UnmodifiableMapView( + new CaseInsensitiveMap.from(parameters)); } diff --git a/pkgs/http_parser/lib/src/case_insensitive_map.dart b/pkgs/http_parser/lib/src/case_insensitive_map.dart new file mode 100644 index 0000000000..34e85b24af --- /dev/null +++ b/pkgs/http_parser/lib/src/case_insensitive_map.dart @@ -0,0 +1,19 @@ +// 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 http_parser.case_insensitive_map; + +import 'package:collection/collection.dart'; + +/// A map from case-insensitive strings to values. +/// +/// Much of HTTP is case-insensitive, so this is useful to have pre-defined. +class CaseInsensitiveMap extends CanonicalizedMap { + CaseInsensitiveMap() + : super((key) => key.toLowerCase(), isValidKey: (key) => key != null); + + CaseInsensitiveMap.from(Map other) + : super.from(other, (key) => key.toLowerCase(), + isValidKey: (key) => key != null); +} diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index 9af4b458b4..06c3529ee5 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,5 +1,5 @@ name: http_parser -version: 1.0.0-dev +version: 1.0.0 author: "Dart Team " homepage: https://github.com/dart-lang/http_parser description: > diff --git a/pkgs/http_parser/test/case_insensitive_map_test.dart b/pkgs/http_parser/test/case_insensitive_map_test.dart new file mode 100644 index 0000000000..9bfa00a173 --- /dev/null +++ b/pkgs/http_parser/test/case_insensitive_map_test.dart @@ -0,0 +1,29 @@ +// 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:http_parser/http_parser.dart'; +import 'package:test/test.dart'; + +void main() { + test("provides case-insensitive access to the map", () { + var map = new CaseInsensitiveMap(); + map["fOo"] = "bAr"; + expect(map, containsPair("FoO", "bAr")); + + map["foo"] = "baz"; + expect(map, containsPair("FOO", "baz")); + }); + + test("stores the original key cases", () { + var map = new CaseInsensitiveMap(); + map["fOo"] = "bAr"; + expect(map, equals({"fOo": "bAr"})); + }); + + test(".from() converts an existing map", () { + var map = new CaseInsensitiveMap.from({"fOo": "bAr"}); + expect(map, containsPair("FoO", "bAr")); + expect(map, equals({"fOo": "bAr"})); + }); +} From 16149edd860a69424f1b48eff2022104ae761e7a Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 25 Aug 2015 15:02:01 -0700 Subject: [PATCH 019/126] cr --- .../lib/src/authentication_challenge.dart | 53 ++++++++++--------- pkgs/http_parser/lib/src/scan.dart | 8 ++- .../test/authentication_challenge_test.dart | 27 ++++++++++ 3 files changed, 63 insertions(+), 25 deletions(-) diff --git a/pkgs/http_parser/lib/src/authentication_challenge.dart b/pkgs/http_parser/lib/src/authentication_challenge.dart index 22dc2a69b8..773ee3b81b 100644 --- a/pkgs/http_parser/lib/src/authentication_challenge.dart +++ b/pkgs/http_parser/lib/src/authentication_challenge.dart @@ -40,20 +40,17 @@ class AuthenticationChallenge { var scanner = new StringScanner(header); scanner.scan(whitespace); var challenges = parseList(scanner, () { - scanner.expect(token, name: "a token"); - var scheme = scanner.lastMatch[0].toLowerCase(); - - scanner.scan(whitespace); - - // The spec specifically requires a space between the scheme and its - // params. - if (scanner.lastMatch == null || !scanner.lastMatch[0].contains(" ")) { - scanner.expect(" ", name: '" " or "="'); - } + var scheme = _scanScheme(scanner, whitespaceName: '" " or "="'); // Manually parse the inner list. We need to do some lookahead to // disambiguate between an auth param and another challenge. var params = {}; + + // Consume initial empty values. + while (scanner.scan(",")) { + scanner.scan(whitespace); + } + _scanAuthParam(scanner, params); var beforeComma = scanner.position; @@ -61,7 +58,7 @@ class AuthenticationChallenge { scanner.scan(whitespace); // Empty elements are allowed, but excluded from the results. - if (scanner.matches(",")) continue; + if (scanner.matches(",") || scanner.isDone) continue; scanner.expect(token, name: "a token"); var name = scanner.lastMatch[0]; @@ -102,16 +99,7 @@ class AuthenticationChallenge { return wrapFormatException("authentication challenge", challenge, () { var scanner = new StringScanner(challenge); scanner.scan(whitespace); - scanner.expect(token, name: "a token"); - var scheme = scanner.lastMatch[0].toLowerCase(); - - scanner.scan(whitespace); - - // The spec specifically requires a space between the scheme and its - // params. - if (scanner.lastMatch == null || !scanner.lastMatch[0].contains(" ")) { - scanner.expect(" "); - } + var scheme = _scanScheme(scanner); var params = {}; parseList(scanner, () => _scanAuthParam(scanner, params)); @@ -121,6 +109,25 @@ class AuthenticationChallenge { }); } + /// Scans a single scheme name and asserts that it's followed by a space. + /// + /// If [whitespaceName] is passed, it's used as the name for exceptions thrown + /// due to invalid trailing whitespace. + static String _scanScheme(StringScanner scanner, {String whitespaceName}) { + scanner.expect(token, name: "a token"); + var scheme = scanner.lastMatch[0].toLowerCase(); + + scanner.scan(whitespace); + + // The spec specifically requires a space between the scheme and its + // params. + if (scanner.lastMatch == null || !scanner.lastMatch[0].contains(" ")) { + scanner.expect(" ", name: whitespaceName); + } + + return scheme; + } + /// Scans a single authentication parameter and stores its result in [params]. static void _scanAuthParam(StringScanner scanner, Map params) { scanner.expect(token, name: "a token"); @@ -139,9 +146,7 @@ class AuthenticationChallenge { scanner.scan(whitespace); } - /// Creates a new challenge value with [scheme] and, optionally, [parameters]. - /// - /// If [parameters] isn't passed, it defaults to an empty map. + /// Creates a new challenge value with [scheme] and [parameters]. AuthenticationChallenge(this.scheme, Map parameters) : parameters = new UnmodifiableMapView( new CaseInsensitiveMap.from(parameters)); diff --git a/pkgs/http_parser/lib/src/scan.dart b/pkgs/http_parser/lib/src/scan.dart index 1036383290..d675b8b41c 100644 --- a/pkgs/http_parser/lib/src/scan.dart +++ b/pkgs/http_parser/lib/src/scan.dart @@ -41,6 +41,12 @@ final whitespace = new RegExp("(?:${_lws.pattern})*"); /// the string, or the end of the string. List parseList(StringScanner scanner, parseElement()) { var result = []; + + // Consume initial empty values. + while (scanner.scan(",")) { + scanner.scan(whitespace); + } + result.add(parseElement()); scanner.scan(whitespace); @@ -48,7 +54,7 @@ List parseList(StringScanner scanner, parseElement()) { scanner.scan(whitespace); // Empty elements are allowed, but excluded from the results. - if (scanner.matches(",")) continue; + if (scanner.matches(",") || scanner.isDone) continue; result.add(parseElement()); scanner.scan(whitespace); diff --git a/pkgs/http_parser/test/authentication_challenge_test.dart b/pkgs/http_parser/test/authentication_challenge_test.dart index ccb8d3230b..4be4444aba 100644 --- a/pkgs/http_parser/test/authentication_challenge_test.dart +++ b/pkgs/http_parser/test/authentication_challenge_test.dart @@ -94,6 +94,13 @@ void _singleChallengeTests( expect(challenge.parameters, containsPair("realm", "fblthp")); }); + test("doesn't normalize the case of the parameter value", () { + var challenge = parseChallenge("scheme realm=FbLtHp"); + expect(challenge.scheme, equals("scheme")); + expect(challenge.parameters, containsPair("realm", "FbLtHp")); + expect(challenge.parameters, isNot(containsPair("realm", "fblthp"))); + }); + test("allows extra whitespace", () { var challenge = parseChallenge( " scheme\t \trealm\t = \tfblthp\t, \tfoo\t\r\n =\tbar\t"); @@ -114,6 +121,26 @@ void _singleChallengeTests( })); }); + test("allows a leading comma", () { + var challenge = parseChallenge( + "scheme , realm=fblthp, foo=bar,"); + expect(challenge.scheme, equals("scheme")); + expect(challenge.parameters, equals({ + "realm": "fblthp", + "foo": "bar" + })); + }); + + test("allows a trailing comma", () { + var challenge = parseChallenge( + "scheme realm=fblthp, foo=bar, ,"); + expect(challenge.scheme, equals("scheme")); + expect(challenge.parameters, equals({ + "realm": "fblthp", + "foo": "bar" + })); + }); + test("disallows only a scheme", () { expect(() => parseChallenge("scheme"), throwsFormatException); From 56e14682ab83160050907be19b13d6e388a40c3b Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 25 Aug 2015 15:02:01 -0700 Subject: [PATCH 020/126] cr --- .../lib/src/authentication_challenge.dart | 53 ++++++++++--------- pkgs/http_parser/lib/src/scan.dart | 8 ++- .../test/authentication_challenge_test.dart | 27 ++++++++++ 3 files changed, 63 insertions(+), 25 deletions(-) diff --git a/pkgs/http_parser/lib/src/authentication_challenge.dart b/pkgs/http_parser/lib/src/authentication_challenge.dart index 377398d80a..3b70fed46e 100644 --- a/pkgs/http_parser/lib/src/authentication_challenge.dart +++ b/pkgs/http_parser/lib/src/authentication_challenge.dart @@ -38,20 +38,17 @@ class AuthenticationChallenge { var scanner = new StringScanner(header); scanner.scan(whitespace); var challenges = parseList(scanner, () { - scanner.expect(token, name: "a token"); - var scheme = scanner.lastMatch[0].toLowerCase(); - - scanner.scan(whitespace); - - // The spec specifically requires a space between the scheme and its - // params. - if (scanner.lastMatch == null || !scanner.lastMatch[0].contains(" ")) { - scanner.expect(" ", name: '" " or "="'); - } + var scheme = _scanScheme(scanner, whitespaceName: '" " or "="'); // Manually parse the inner list. We need to do some lookahead to // disambiguate between an auth param and another challenge. var params = {}; + + // Consume initial empty values. + while (scanner.scan(",")) { + scanner.scan(whitespace); + } + _scanAuthParam(scanner, params); var beforeComma = scanner.position; @@ -59,7 +56,7 @@ class AuthenticationChallenge { scanner.scan(whitespace); // Empty elements are allowed, but excluded from the results. - if (scanner.matches(",")) continue; + if (scanner.matches(",") || scanner.isDone) continue; scanner.expect(token, name: "a token"); var name = scanner.lastMatch[0].toLowerCase(); @@ -100,16 +97,7 @@ class AuthenticationChallenge { return wrapFormatException("authentication challenge", challenge, () { var scanner = new StringScanner(challenge); scanner.scan(whitespace); - scanner.expect(token, name: "a token"); - var scheme = scanner.lastMatch[0].toLowerCase(); - - scanner.scan(whitespace); - - // The spec specifically requires a space between the scheme and its - // params. - if (scanner.lastMatch == null || !scanner.lastMatch[0].contains(" ")) { - scanner.expect(" "); - } + var scheme = _scanScheme(scanner); var params = {}; parseList(scanner, () => _scanAuthParam(scanner, params)); @@ -119,6 +107,25 @@ class AuthenticationChallenge { }); } + /// Scans a single scheme name and asserts that it's followed by a space. + /// + /// If [whitespaceName] is passed, it's used as the name for exceptions thrown + /// due to invalid trailing whitespace. + static String _scanScheme(StringScanner scanner, {String whitespaceName}) { + scanner.expect(token, name: "a token"); + var scheme = scanner.lastMatch[0].toLowerCase(); + + scanner.scan(whitespace); + + // The spec specifically requires a space between the scheme and its + // params. + if (scanner.lastMatch == null || !scanner.lastMatch[0].contains(" ")) { + scanner.expect(" ", name: whitespaceName); + } + + return scheme; + } + /// Scans a single authentication parameter and stores its result in [params]. static void _scanAuthParam(StringScanner scanner, Map params) { scanner.expect(token, name: "a token"); @@ -137,9 +144,7 @@ class AuthenticationChallenge { scanner.scan(whitespace); } - /// Creates a new challenge value with [scheme] and, optionally, [parameters]. - /// - /// If [parameters] isn't passed, it defaults to an empty map. + /// Creates a new challenge value with [scheme] and [parameters]. AuthenticationChallenge(this.scheme, Map parameters) : parameters = new UnmodifiableMapView(parameters); } diff --git a/pkgs/http_parser/lib/src/scan.dart b/pkgs/http_parser/lib/src/scan.dart index 1036383290..d675b8b41c 100644 --- a/pkgs/http_parser/lib/src/scan.dart +++ b/pkgs/http_parser/lib/src/scan.dart @@ -41,6 +41,12 @@ final whitespace = new RegExp("(?:${_lws.pattern})*"); /// the string, or the end of the string. List parseList(StringScanner scanner, parseElement()) { var result = []; + + // Consume initial empty values. + while (scanner.scan(",")) { + scanner.scan(whitespace); + } + result.add(parseElement()); scanner.scan(whitespace); @@ -48,7 +54,7 @@ List parseList(StringScanner scanner, parseElement()) { scanner.scan(whitespace); // Empty elements are allowed, but excluded from the results. - if (scanner.matches(",")) continue; + if (scanner.matches(",") || scanner.isDone) continue; result.add(parseElement()); scanner.scan(whitespace); diff --git a/pkgs/http_parser/test/authentication_challenge_test.dart b/pkgs/http_parser/test/authentication_challenge_test.dart index ccb8d3230b..4be4444aba 100644 --- a/pkgs/http_parser/test/authentication_challenge_test.dart +++ b/pkgs/http_parser/test/authentication_challenge_test.dart @@ -94,6 +94,13 @@ void _singleChallengeTests( expect(challenge.parameters, containsPair("realm", "fblthp")); }); + test("doesn't normalize the case of the parameter value", () { + var challenge = parseChallenge("scheme realm=FbLtHp"); + expect(challenge.scheme, equals("scheme")); + expect(challenge.parameters, containsPair("realm", "FbLtHp")); + expect(challenge.parameters, isNot(containsPair("realm", "fblthp"))); + }); + test("allows extra whitespace", () { var challenge = parseChallenge( " scheme\t \trealm\t = \tfblthp\t, \tfoo\t\r\n =\tbar\t"); @@ -114,6 +121,26 @@ void _singleChallengeTests( })); }); + test("allows a leading comma", () { + var challenge = parseChallenge( + "scheme , realm=fblthp, foo=bar,"); + expect(challenge.scheme, equals("scheme")); + expect(challenge.parameters, equals({ + "realm": "fblthp", + "foo": "bar" + })); + }); + + test("allows a trailing comma", () { + var challenge = parseChallenge( + "scheme realm=fblthp, foo=bar, ,"); + expect(challenge.scheme, equals("scheme")); + expect(challenge.parameters, equals({ + "realm": "fblthp", + "foo": "bar" + })); + }); + test("disallows only a scheme", () { expect(() => parseChallenge("scheme"), throwsFormatException); From 9f7224ab23e123f289cac55429098c78f98dbf98 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 25 Aug 2015 16:26:50 -0700 Subject: [PATCH 021/126] Add an AuthenticationChallenge class. A simpler version of this existed already in dart-lang/oauth2; this makes it widely available and more robust. R=rnystrom@google.com Review URL: https://codereview.chromium.org//1307353003 . --- pkgs/http_parser/CHANGELOG.md | 7 + pkgs/http_parser/lib/http_parser.dart | 1 + .../lib/src/authentication_challenge.dart | 150 +++++++++++++++ pkgs/http_parser/lib/src/http_date.dart | 8 +- pkgs/http_parser/lib/src/media_type.dart | 45 ++--- pkgs/http_parser/lib/src/scan.dart | 77 ++++++++ pkgs/http_parser/lib/src/utils.dart | 23 +++ pkgs/http_parser/pubspec.yaml | 3 +- .../test/authentication_challenge_test.dart | 171 ++++++++++++++++++ 9 files changed, 449 insertions(+), 36 deletions(-) create mode 100644 pkgs/http_parser/lib/src/authentication_challenge.dart create mode 100644 pkgs/http_parser/lib/src/scan.dart create mode 100644 pkgs/http_parser/lib/src/utils.dart create mode 100644 pkgs/http_parser/test/authentication_challenge_test.dart diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index 7f50217f47..3ac45efdd6 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,3 +1,10 @@ +## 1.0.0 + +This is 1.0.0 because the API is stable—there are no breaking changes. + +* Added an `AuthenticationChallenge` class for parsing and representing the + value of `WWW-Authenticate` and related headers. + ## 0.0.2+8 * Bring in the latest `dart:io` WebSocket code. diff --git a/pkgs/http_parser/lib/http_parser.dart b/pkgs/http_parser/lib/http_parser.dart index 0aa9cea6b7..f5921ee06c 100644 --- a/pkgs/http_parser/lib/http_parser.dart +++ b/pkgs/http_parser/lib/http_parser.dart @@ -4,6 +4,7 @@ library http_parser; +export 'src/authentication_challenge.dart'; export 'src/http_date.dart'; export 'src/media_type.dart'; export 'src/web_socket.dart'; diff --git a/pkgs/http_parser/lib/src/authentication_challenge.dart b/pkgs/http_parser/lib/src/authentication_challenge.dart new file mode 100644 index 0000000000..3b70fed46e --- /dev/null +++ b/pkgs/http_parser/lib/src/authentication_challenge.dart @@ -0,0 +1,150 @@ +// 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 http_paser.authentication_challenge; + +import 'dart:collection'; + +import 'package:string_scanner/string_scanner.dart'; + +import 'scan.dart'; +import 'utils.dart'; + +/// A single challenge in a WWW-Authenticate header, parsed as per [RFC 2617][]. +/// +/// [RFC 2617]: http://tools.ietf.org/html/rfc2617 +/// +/// Each WWW-Authenticate header contains one or more challenges, representing +/// valid ways to authenticate with the server. +class AuthenticationChallenge { + /// The scheme describing the type of authentication that's required, for + /// example "basic" or "digest". + /// + /// This is normalized to always be lower-case. + final String scheme; + + /// The parameters describing how to authenticate. + /// + /// The semantics of these parameters are scheme-specific. + final Map parameters; + + /// Parses a WWW-Authenticate header, which should contain one or more + /// challenges. + /// + /// Throws a [FormatException] if the header is invalid. + static List parseHeader(String header) { + return wrapFormatException("authentication header", header, () { + var scanner = new StringScanner(header); + scanner.scan(whitespace); + var challenges = parseList(scanner, () { + var scheme = _scanScheme(scanner, whitespaceName: '" " or "="'); + + // Manually parse the inner list. We need to do some lookahead to + // disambiguate between an auth param and another challenge. + var params = {}; + + // Consume initial empty values. + while (scanner.scan(",")) { + scanner.scan(whitespace); + } + + _scanAuthParam(scanner, params); + + var beforeComma = scanner.position; + while (scanner.scan(",")) { + scanner.scan(whitespace); + + // Empty elements are allowed, but excluded from the results. + if (scanner.matches(",") || scanner.isDone) continue; + + scanner.expect(token, name: "a token"); + var name = scanner.lastMatch[0].toLowerCase(); + scanner.scan(whitespace); + + // If there's no "=", then this is another challenge rather than a + // parameter for the current challenge. + if (!scanner.scan('=')) { + scanner.position = beforeComma; + break; + } + + scanner.scan(whitespace); + + if (scanner.scan(token)) { + params[name] = scanner.lastMatch[0]; + } else { + params[name] = expectQuotedString( + scanner, name: "a token or a quoted string"); + } + + scanner.scan(whitespace); + beforeComma = scanner.position; + } + + return new AuthenticationChallenge(scheme, params); + }); + + scanner.expectDone(); + return challenges; + }); + } + + /// Parses a single WWW-Authenticate challenge value. + /// + /// Throws a [FormatException] if the challenge is invalid. + factory AuthenticationChallenge.parse(String challenge) { + return wrapFormatException("authentication challenge", challenge, () { + var scanner = new StringScanner(challenge); + scanner.scan(whitespace); + var scheme = _scanScheme(scanner); + + var params = {}; + parseList(scanner, () => _scanAuthParam(scanner, params)); + + scanner.expectDone(); + return new AuthenticationChallenge(scheme, params); + }); + } + + /// Scans a single scheme name and asserts that it's followed by a space. + /// + /// If [whitespaceName] is passed, it's used as the name for exceptions thrown + /// due to invalid trailing whitespace. + static String _scanScheme(StringScanner scanner, {String whitespaceName}) { + scanner.expect(token, name: "a token"); + var scheme = scanner.lastMatch[0].toLowerCase(); + + scanner.scan(whitespace); + + // The spec specifically requires a space between the scheme and its + // params. + if (scanner.lastMatch == null || !scanner.lastMatch[0].contains(" ")) { + scanner.expect(" ", name: whitespaceName); + } + + return scheme; + } + + /// Scans a single authentication parameter and stores its result in [params]. + static void _scanAuthParam(StringScanner scanner, Map params) { + scanner.expect(token, name: "a token"); + var name = scanner.lastMatch[0].toLowerCase(); + scanner.scan(whitespace); + scanner.expect('='); + scanner.scan(whitespace); + + if (scanner.scan(token)) { + params[name] = scanner.lastMatch[0]; + } else { + params[name] = expectQuotedString( + scanner, name: "a token or a quoted string"); + } + + scanner.scan(whitespace); + } + + /// Creates a new challenge value with [scheme] and [parameters]. + AuthenticationChallenge(this.scheme, Map parameters) + : parameters = new UnmodifiableMapView(parameters); +} diff --git a/pkgs/http_parser/lib/src/http_date.dart b/pkgs/http_parser/lib/src/http_date.dart index 7d46d553ed..959053649d 100644 --- a/pkgs/http_parser/lib/src/http_date.dart +++ b/pkgs/http_parser/lib/src/http_date.dart @@ -6,6 +6,8 @@ library http_parser.http_date; import 'package:string_scanner/string_scanner.dart'; +import 'utils.dart'; + const _WEEKDAYS = const ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]; const _MONTHS = const ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; @@ -48,7 +50,7 @@ String formatHttpDate(DateTime date) { /// 2616](http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3). It will /// throw a [FormatException] if [date] is invalid. DateTime parseHttpDate(String date) { - try { + return wrapFormatException("HTTP date", date, () { var scanner = new StringScanner(date); if (scanner.scan(_longWeekdayRegExp)) { @@ -96,9 +98,7 @@ DateTime parseHttpDate(String date) { scanner.expectDone(); return _makeDateTime(year, month, day, time); - } on FormatException catch (error) { - throw new FormatException('Invalid HTTP date "$date": ${error.message}'); - } + }); } /// Parses a short-form month name to a form accepted by [DateTime]. diff --git a/pkgs/http_parser/lib/src/media_type.dart b/pkgs/http_parser/lib/src/media_type.dart index fa9350e32e..5bae0ec2ad 100644 --- a/pkgs/http_parser/lib/src/media_type.dart +++ b/pkgs/http_parser/lib/src/media_type.dart @@ -7,18 +7,8 @@ library http_parser.media_type; import 'package:collection/collection.dart'; import 'package:string_scanner/string_scanner.dart'; -// All of the following regular expressions come from section 2.2 of the HTTP -// spec: http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html -final _lws = new RegExp(r"(?:\r\n)?[ \t]+"); -final _token = new RegExp(r'[^()<>@,;:"\\/[\]?={} \t\x00-\x1F\x7F]+'); -final _quotedString = new RegExp(r'"(?:[^"\x00-\x1F\x7F]|\\.)*"'); -final _quotedPair = new RegExp(r'\\(.)'); - -/// A regular expression matching any number of [_lws] productions in a row. -final _whitespace = new RegExp("(?:${_lws.pattern})*"); - -/// A regular expression matching a character that is not a valid HTTP token. -final _nonToken = new RegExp(r'[()<>@,;:"\\/\[\]?={} \t\x00-\x1F\x7F]'); +import 'scan.dart'; +import 'utils.dart'; /// A regular expression matching a character that needs to be backslash-escaped /// in a quoted string. @@ -50,44 +40,37 @@ class MediaType { factory MediaType.parse(String mediaType) { // This parsing is based on sections 3.6 and 3.7 of the HTTP spec: // http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html. - try { + return wrapFormatException("media type", mediaType, () { var scanner = new StringScanner(mediaType); - scanner.scan(_whitespace); - scanner.expect(_token); + scanner.scan(whitespace); + scanner.expect(token); var type = scanner.lastMatch[0]; scanner.expect('/'); - scanner.expect(_token); + scanner.expect(token); var subtype = scanner.lastMatch[0]; - scanner.scan(_whitespace); + scanner.scan(whitespace); var parameters = {}; while (scanner.scan(';')) { - scanner.scan(_whitespace); - scanner.expect(_token); + scanner.scan(whitespace); + scanner.expect(token); var attribute = scanner.lastMatch[0]; scanner.expect('='); var value; - if (scanner.scan(_token)) { + if (scanner.scan(token)) { value = scanner.lastMatch[0]; } else { - scanner.expect(_quotedString); - var quotedString = scanner.lastMatch[0]; - value = quotedString - .substring(1, quotedString.length - 1) - .replaceAllMapped(_quotedPair, (match) => match[1]); + value = expectQuotedString(scanner); } - scanner.scan(_whitespace); + scanner.scan(whitespace); parameters[attribute] = value; } scanner.expectDone(); return new MediaType(type, subtype, parameters); - } on FormatException catch (error) { - throw new FormatException( - 'Invalid media type "$mediaType": ${error.message}'); - } + }); } MediaType(this.type, this.subtype, [Map parameters]) @@ -146,7 +129,7 @@ class MediaType { parameters.forEach((attribute, value) { buffer.write("; $attribute="); - if (_nonToken.hasMatch(value)) { + if (nonToken.hasMatch(value)) { buffer ..write('"') ..write( diff --git a/pkgs/http_parser/lib/src/scan.dart b/pkgs/http_parser/lib/src/scan.dart new file mode 100644 index 0000000000..d675b8b41c --- /dev/null +++ b/pkgs/http_parser/lib/src/scan.dart @@ -0,0 +1,77 @@ +// 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. + +/// A library for broadly-useful functions and regular expressions for scanning +/// HTTP entities. +/// +/// Many of the regular expressions come from [section 2.2 of the HTTP +/// spec][spec]. +/// +/// [spec]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html +library http_parser.scan; + +import 'package:string_scanner/string_scanner.dart'; + +/// An HTTP token. +final token = new RegExp(r'[^()<>@,;:"\\/[\]?={} \t\x00-\x1F\x7F]+'); + +/// Linear whitespace. +final _lws = new RegExp(r"(?:\r\n)?[ \t]+"); + +/// A quoted string. +final _quotedString = new RegExp(r'"(?:[^"\x00-\x1F\x7F]|\\.)*"'); + +/// A quoted pair. +final _quotedPair = new RegExp(r'\\(.)'); + +/// A character that is *not* a valid HTTP token. +final nonToken = new RegExp(r'[()<>@,;:"\\/\[\]?={} \t\x00-\x1F\x7F]'); + +/// A regular expression matching any number of [_lws] productions in a row. +final whitespace = new RegExp("(?:${_lws.pattern})*"); + +/// Parses a list of elements, as in `1#element` in the HTTP spec. +/// +/// [scanner] is used to parse the elements, and [parseElement] is used to parse +/// each one individually. The values returned by [parseElement] are collected +/// in a list and returned. +/// +/// Once this is finished, [scanner] will be at the next non-LWS character in +/// the string, or the end of the string. +List parseList(StringScanner scanner, parseElement()) { + var result = []; + + // Consume initial empty values. + while (scanner.scan(",")) { + scanner.scan(whitespace); + } + + result.add(parseElement()); + scanner.scan(whitespace); + + while (scanner.scan(",")) { + scanner.scan(whitespace); + + // Empty elements are allowed, but excluded from the results. + if (scanner.matches(",") || scanner.isDone) continue; + + result.add(parseElement()); + scanner.scan(whitespace); + } + + return result; +} + +/// Parses a single quoted string, and returns its contents. +/// +/// If [name] is passed, it's used to describe the expected value if it's not +/// found. +String expectQuotedString(StringScanner scanner, {String name}) { + if (name == null) name = "quoted string"; + scanner.expect(_quotedString, name: name); + var string = scanner.lastMatch[0]; + return string + .substring(1, string.length - 1) + .replaceAllMapped(_quotedPair, (match) => match[1]); +} diff --git a/pkgs/http_parser/lib/src/utils.dart b/pkgs/http_parser/lib/src/utils.dart new file mode 100644 index 0000000000..4dcee19880 --- /dev/null +++ b/pkgs/http_parser/lib/src/utils.dart @@ -0,0 +1,23 @@ +// 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:source_span/source_span.dart'; + +/// Runs [body] and wraps any format exceptions it produces. +/// +/// [name] should describe the type of thing being parsed, and [value] should be +/// its actual value. +wrapFormatException(String name, String value, body()) { + try { + return body(); + } on SourceSpanFormatException catch (error) { + throw new SourceSpanFormatException( + 'Invalid $name: ${error.message}', error.span, error.source); + } on FormatException catch (error) { + throw new FormatException( + 'Invalid $name "$value": ${error.message}', + error.source, + error.offset); + } +} diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index b4e002c221..9af4b458b4 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,5 +1,5 @@ name: http_parser -version: 0.0.3-dev +version: 1.0.0-dev author: "Dart Team " homepage: https://github.com/dart-lang/http_parser description: > @@ -7,6 +7,7 @@ description: > dependencies: crypto: "^0.9.0" collection: ">=0.9.1 <2.0.0" + source_span: "^1.0.0" string_scanner: ">=0.0.0 <0.2.0" dev_dependencies: test: "^0.12.0" diff --git a/pkgs/http_parser/test/authentication_challenge_test.dart b/pkgs/http_parser/test/authentication_challenge_test.dart new file mode 100644 index 0000000000..4be4444aba --- /dev/null +++ b/pkgs/http_parser/test/authentication_challenge_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 'package:http_parser/http_parser.dart'; +import 'package:test/test.dart'; + +void main() { + group("parse", () { + _singleChallengeTests( + (challenge) => new AuthenticationChallenge.parse(challenge)); + }); + + group("parseHeader", () { + group("with a single challenge", () { + _singleChallengeTests((challenge) { + var challenges = AuthenticationChallenge.parseHeader(challenge); + expect(challenges, hasLength(1)); + return challenges.single; + }); + }); + + test("parses multiple challenges", () { + var challenges = AuthenticationChallenge.parseHeader( + "scheme1 realm=fblthp, scheme2 realm=asdfg"); + expect(challenges, hasLength(2)); + expect(challenges.first.scheme, equals("scheme1")); + expect(challenges.first.parameters, equals({"realm": "fblthp"})); + expect(challenges.last.scheme, equals("scheme2")); + expect(challenges.last.parameters, equals({"realm": "asdfg"})); + }); + + test("parses multiple challenges with multiple parameters", () { + var challenges = AuthenticationChallenge.parseHeader( + "scheme1 realm=fblthp, foo=bar, scheme2 realm=asdfg, baz=bang"); + expect(challenges, hasLength(2)); + + expect(challenges.first.scheme, equals("scheme1")); + expect(challenges.first.parameters, equals({ + "realm": "fblthp", + "foo": "bar" + })); + + expect(challenges.last.scheme, equals("scheme2")); + expect(challenges.last.parameters, equals({ + "realm": "asdfg", + "baz": "bang" + })); + }); + }); +} + +/// Tests to run for parsing a single challenge. +/// +/// These are run on both [AuthenticationChallenge.parse] and +/// [AuthenticationChallenge.parseHeader], since they use almost entirely +/// separate code paths. +void _singleChallengeTests( + AuthenticationChallenge parseChallenge(String challenge)) { + test("parses a simple challenge", () { + var challenge = parseChallenge("scheme realm=fblthp"); + expect(challenge.scheme, equals("scheme")); + expect(challenge.parameters, equals({"realm": "fblthp"})); + }); + + test("parses multiple parameters", () { + var challenge = parseChallenge("scheme realm=fblthp, foo=bar, baz=qux"); + expect(challenge.scheme, equals("scheme")); + expect(challenge.parameters, equals({ + "realm": "fblthp", + "foo": "bar", + "baz": "qux" + })); + }); + + test("parses quoted string parameters", () { + var challenge = parseChallenge('scheme realm="fblthp, foo=bar", baz="qux"'); + expect(challenge.scheme, equals("scheme")); + expect(challenge.parameters, equals({ + "realm": "fblthp, foo=bar", + "baz": "qux" + })); + }); + + test("normalizes the case of the scheme", () { + var challenge = parseChallenge("ScHeMe realm=fblthp"); + expect(challenge.scheme, equals("scheme")); + expect(challenge.parameters, equals({"realm": "fblthp"})); + }); + + test("normalizes the case of the parameter name", () { + var challenge = parseChallenge("scheme ReAlM=fblthp"); + expect(challenge.scheme, equals("scheme")); + expect(challenge.parameters, containsPair("realm", "fblthp")); + }); + + test("doesn't normalize the case of the parameter value", () { + var challenge = parseChallenge("scheme realm=FbLtHp"); + expect(challenge.scheme, equals("scheme")); + expect(challenge.parameters, containsPair("realm", "FbLtHp")); + expect(challenge.parameters, isNot(containsPair("realm", "fblthp"))); + }); + + test("allows extra whitespace", () { + var challenge = parseChallenge( + " scheme\t \trealm\t = \tfblthp\t, \tfoo\t\r\n =\tbar\t"); + expect(challenge.scheme, equals("scheme")); + expect(challenge.parameters, equals({ + "realm": "fblthp", + "foo": "bar" + })); + }); + + test("allows an empty parameter", () { + var challenge = parseChallenge( + "scheme realm=fblthp, , foo=bar"); + expect(challenge.scheme, equals("scheme")); + expect(challenge.parameters, equals({ + "realm": "fblthp", + "foo": "bar" + })); + }); + + test("allows a leading comma", () { + var challenge = parseChallenge( + "scheme , realm=fblthp, foo=bar,"); + expect(challenge.scheme, equals("scheme")); + expect(challenge.parameters, equals({ + "realm": "fblthp", + "foo": "bar" + })); + }); + + test("allows a trailing comma", () { + var challenge = parseChallenge( + "scheme realm=fblthp, foo=bar, ,"); + expect(challenge.scheme, equals("scheme")); + expect(challenge.parameters, equals({ + "realm": "fblthp", + "foo": "bar" + })); + }); + + test("disallows only a scheme", () { + expect(() => parseChallenge("scheme"), + throwsFormatException); + }); + + test("disallows a valueless parameter", () { + expect(() => parseChallenge("scheme realm"), + throwsFormatException); + expect(() => parseChallenge("scheme realm="), + throwsFormatException); + expect(() => parseChallenge("scheme realm, foo=bar"), + throwsFormatException); + }); + + test("requires a space after the scheme", () { + expect(() => parseChallenge("scheme\trealm"), + throwsFormatException); + expect(() => parseChallenge("scheme\r\n\trealm="), + throwsFormatException); + }); + + test("disallows junk after the parameters", () { + expect(() => parseChallenge("scheme realm=fblthp foo"), + throwsFormatException); + expect(() => parseChallenge("scheme realm=fblthp, foo=bar baz"), + throwsFormatException); + }); +} From 71604559046c43d3d5b65cb75c1e065edd21fec2 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 25 Aug 2015 16:44:51 -0700 Subject: [PATCH 022/126] Add a CaseInsensitiveMap class. R=rnystrom@google.com Review URL: https://codereview.chromium.org//1320533002 . --- pkgs/http_parser/CHANGELOG.md | 3 ++ pkgs/http_parser/lib/http_parser.dart | 1 + .../lib/src/authentication_challenge.dart | 11 ++++--- .../lib/src/case_insensitive_map.dart | 19 ++++++++++++ pkgs/http_parser/pubspec.yaml | 2 +- .../test/case_insensitive_map_test.dart | 29 +++++++++++++++++++ 6 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 pkgs/http_parser/lib/src/case_insensitive_map.dart create mode 100644 pkgs/http_parser/test/case_insensitive_map_test.dart diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index 3ac45efdd6..cdf85d40ea 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -5,6 +5,9 @@ This is 1.0.0 because the API is stable—there are no breaking changes. * Added an `AuthenticationChallenge` class for parsing and representing the value of `WWW-Authenticate` and related headers. +* Added a `CaseInsensitiveMap` class for representing case-insensitive HTTP + values. + ## 0.0.2+8 * Bring in the latest `dart:io` WebSocket code. diff --git a/pkgs/http_parser/lib/http_parser.dart b/pkgs/http_parser/lib/http_parser.dart index f5921ee06c..77c75c0aa9 100644 --- a/pkgs/http_parser/lib/http_parser.dart +++ b/pkgs/http_parser/lib/http_parser.dart @@ -5,6 +5,7 @@ library http_parser; export 'src/authentication_challenge.dart'; +export 'src/case_insensitive_map.dart'; export 'src/http_date.dart'; export 'src/media_type.dart'; export 'src/web_socket.dart'; diff --git a/pkgs/http_parser/lib/src/authentication_challenge.dart b/pkgs/http_parser/lib/src/authentication_challenge.dart index 3b70fed46e..773ee3b81b 100644 --- a/pkgs/http_parser/lib/src/authentication_challenge.dart +++ b/pkgs/http_parser/lib/src/authentication_challenge.dart @@ -8,6 +8,7 @@ import 'dart:collection'; import 'package:string_scanner/string_scanner.dart'; +import 'case_insensitive_map.dart'; import 'scan.dart'; import 'utils.dart'; @@ -26,7 +27,8 @@ class AuthenticationChallenge { /// The parameters describing how to authenticate. /// - /// The semantics of these parameters are scheme-specific. + /// The semantics of these parameters are scheme-specific. The keys of this + /// map are case-insensitive. final Map parameters; /// Parses a WWW-Authenticate header, which should contain one or more @@ -59,7 +61,7 @@ class AuthenticationChallenge { if (scanner.matches(",") || scanner.isDone) continue; scanner.expect(token, name: "a token"); - var name = scanner.lastMatch[0].toLowerCase(); + var name = scanner.lastMatch[0]; scanner.scan(whitespace); // If there's no "=", then this is another challenge rather than a @@ -129,7 +131,7 @@ class AuthenticationChallenge { /// Scans a single authentication parameter and stores its result in [params]. static void _scanAuthParam(StringScanner scanner, Map params) { scanner.expect(token, name: "a token"); - var name = scanner.lastMatch[0].toLowerCase(); + var name = scanner.lastMatch[0]; scanner.scan(whitespace); scanner.expect('='); scanner.scan(whitespace); @@ -146,5 +148,6 @@ class AuthenticationChallenge { /// Creates a new challenge value with [scheme] and [parameters]. AuthenticationChallenge(this.scheme, Map parameters) - : parameters = new UnmodifiableMapView(parameters); + : parameters = new UnmodifiableMapView( + new CaseInsensitiveMap.from(parameters)); } diff --git a/pkgs/http_parser/lib/src/case_insensitive_map.dart b/pkgs/http_parser/lib/src/case_insensitive_map.dart new file mode 100644 index 0000000000..34e85b24af --- /dev/null +++ b/pkgs/http_parser/lib/src/case_insensitive_map.dart @@ -0,0 +1,19 @@ +// 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 http_parser.case_insensitive_map; + +import 'package:collection/collection.dart'; + +/// A map from case-insensitive strings to values. +/// +/// Much of HTTP is case-insensitive, so this is useful to have pre-defined. +class CaseInsensitiveMap extends CanonicalizedMap { + CaseInsensitiveMap() + : super((key) => key.toLowerCase(), isValidKey: (key) => key != null); + + CaseInsensitiveMap.from(Map other) + : super.from(other, (key) => key.toLowerCase(), + isValidKey: (key) => key != null); +} diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index 9af4b458b4..06c3529ee5 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,5 +1,5 @@ name: http_parser -version: 1.0.0-dev +version: 1.0.0 author: "Dart Team " homepage: https://github.com/dart-lang/http_parser description: > diff --git a/pkgs/http_parser/test/case_insensitive_map_test.dart b/pkgs/http_parser/test/case_insensitive_map_test.dart new file mode 100644 index 0000000000..9bfa00a173 --- /dev/null +++ b/pkgs/http_parser/test/case_insensitive_map_test.dart @@ -0,0 +1,29 @@ +// 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:http_parser/http_parser.dart'; +import 'package:test/test.dart'; + +void main() { + test("provides case-insensitive access to the map", () { + var map = new CaseInsensitiveMap(); + map["fOo"] = "bAr"; + expect(map, containsPair("FoO", "bAr")); + + map["foo"] = "baz"; + expect(map, containsPair("FOO", "baz")); + }); + + test("stores the original key cases", () { + var map = new CaseInsensitiveMap(); + map["fOo"] = "bAr"; + expect(map, equals({"fOo": "bAr"})); + }); + + test(".from() converts an existing map", () { + var map = new CaseInsensitiveMap.from({"fOo": "bAr"}); + expect(map, containsPair("FoO", "bAr")); + expect(map, equals({"fOo": "bAr"})); + }); +} From 306ef098926634b77530fad9ab28a763d38c5316 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 19 Oct 2015 14:42:54 -0700 Subject: [PATCH 023/126] Make MediaType case-insensitive, following the spec. R=kevmoo@google.com Review URL: https://codereview.chromium.org//1419493002 . --- pkgs/http_parser/CHANGELOG.md | 6 ++++++ pkgs/http_parser/lib/src/media_type.dart | 15 +++++++++++---- pkgs/http_parser/pubspec.yaml | 2 +- pkgs/http_parser/test/media_type_test.dart | 17 +++++++++++++++++ 4 files changed, 35 insertions(+), 5 deletions(-) diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index cdf85d40ea..08992e90e8 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,3 +1,9 @@ +## 1.1.0 + +* The MIME spec says that media types and their parameter names are + case-insensitive. Accordingly, `MediaType` now uses a case-insensitive map for + its parameters and its `type` and `subtype` fields are now always lowercase. + ## 1.0.0 This is 1.0.0 because the API is stable—there are no breaking changes. diff --git a/pkgs/http_parser/lib/src/media_type.dart b/pkgs/http_parser/lib/src/media_type.dart index 5bae0ec2ad..e15bccf2f5 100644 --- a/pkgs/http_parser/lib/src/media_type.dart +++ b/pkgs/http_parser/lib/src/media_type.dart @@ -7,6 +7,7 @@ library http_parser.media_type; import 'package:collection/collection.dart'; import 'package:string_scanner/string_scanner.dart'; +import 'case_insensitive_map.dart'; import 'scan.dart'; import 'utils.dart'; @@ -21,14 +22,18 @@ final _escapedChar = new RegExp(r'["\x00-\x1F\x7F]'); /// calling [change]. class MediaType { /// The primary identifier of the MIME type. + /// + /// This is always lowercase. final String type; /// The secondary identifier of the MIME type. + /// + /// This is always lowercase. final String subtype; /// The parameters to the media type. /// - /// This map is immutable. + /// This map is immutable and the keys are case-insensitive. final Map parameters; /// The media type's MIME type. @@ -73,9 +78,11 @@ class MediaType { }); } - MediaType(this.type, this.subtype, [Map parameters]) - : this.parameters = new UnmodifiableMapView( - parameters == null ? {} : new Map.from(parameters)); + MediaType(String type, String subtype, [Map parameters]) + : type = type.toLowerCase(), + subtype = subtype.toLowerCase(), + parameters = new UnmodifiableMapView( + parameters == null ? {} : new CaseInsensitiveMap.from(parameters)); /// Returns a copy of this [MediaType] with some fields altered. /// diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index 06c3529ee5..0c1d578c87 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,5 +1,5 @@ name: http_parser -version: 1.0.0 +version: 1.1.0-dev author: "Dart Team " homepage: https://github.com/dart-lang/http_parser description: > diff --git a/pkgs/http_parser/test/media_type_test.dart b/pkgs/http_parser/test/media_type_test.dart index 0a9f904004..98f482becd 100644 --- a/pkgs/http_parser/test/media_type_test.dart +++ b/pkgs/http_parser/test/media_type_test.dart @@ -70,6 +70,23 @@ void main() { expect( type.parameters, equals({"foo": "bar space", "baz": "bang\\escape"})); }); + + test("lower-cases type and subtype", () { + var type = new MediaType.parse('TeXt/pLaIn'); + expect(type.type, equals("text")); + expect(type.subtype, equals("plain")); + expect(type.mimeType, equals("text/plain")); + }); + + test("records parameters as case-insensitive", () { + var type = new MediaType.parse('test/plain;FoO=bar;bAz=bang'); + expect(type.parameters, equals({ + "FoO": "bar", + "bAz": "bang" + })); + expect(type.parameters, containsPair("foo", "bar")); + expect(type.parameters, containsPair("baz", "bang")); + }); }); group("change", () { From b023517f72f9b0e0dc044d719075938585fb1a3d Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 20 Oct 2015 14:29:30 -0700 Subject: [PATCH 024/126] Add a DataUri class. This supports encoding and decoding data URIs, using both bytes and strings. R=kevmoo@google.com Review URL: https://codereview.chromium.org//1390353008 . --- pkgs/http_parser/CHANGELOG.md | 2 + pkgs/http_parser/lib/http_parser.dart | 1 + pkgs/http_parser/lib/src/data_uri.dart | 328 +++++++++++++++++++++++ pkgs/http_parser/pubspec.yaml | 8 +- pkgs/http_parser/test/data_uri_test.dart | 241 +++++++++++++++++ 5 files changed, 577 insertions(+), 3 deletions(-) create mode 100644 pkgs/http_parser/lib/src/data_uri.dart create mode 100644 pkgs/http_parser/test/data_uri_test.dart diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index 08992e90e8..51e21cb6b9 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,5 +1,7 @@ ## 1.1.0 +* Added a `DataUri` class for encoding and decoding data URIs. + * The MIME spec says that media types and their parameter names are case-insensitive. Accordingly, `MediaType` now uses a case-insensitive map for its parameters and its `type` and `subtype` fields are now always lowercase. diff --git a/pkgs/http_parser/lib/http_parser.dart b/pkgs/http_parser/lib/http_parser.dart index 77c75c0aa9..a03c84b8b7 100644 --- a/pkgs/http_parser/lib/http_parser.dart +++ b/pkgs/http_parser/lib/http_parser.dart @@ -6,6 +6,7 @@ library http_parser; export 'src/authentication_challenge.dart'; export 'src/case_insensitive_map.dart'; +export 'src/data_uri.dart'; export 'src/http_date.dart'; export 'src/media_type.dart'; export 'src/web_socket.dart'; diff --git a/pkgs/http_parser/lib/src/data_uri.dart b/pkgs/http_parser/lib/src/data_uri.dart new file mode 100644 index 0000000000..b8cdee95f8 --- /dev/null +++ b/pkgs/http_parser/lib/src/data_uri.dart @@ -0,0 +1,328 @@ +// 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:convert/convert.dart'; +import 'package:crypto/crypto.dart'; +import 'package:string_scanner/string_scanner.dart'; + +import 'media_type.dart'; +import 'scan.dart'; +import 'utils.dart'; + +/// Like [whitespace] from scan.dart, except that it matches URI-encoded +/// whitespace rather than literal characters. +final _whitespace = new RegExp(r'(?:(?:%0D%0A)?(?:%20|%09)+)*'); + +/// A converter for percent encoding strings using UTF-8. +final _utf8Percent = UTF8.fuse(percent); + +/// A class representing a `data:` URI that provides access to its [mediaType] +/// and the [data] it contains. +/// +/// Data can be encoded as a `data:` URI using [encode] or [encodeString], and +/// decoded using [decode]. +/// +/// This implementation is based on [RFC 2397][rfc], but as that RFC is +/// [notoriously ambiguous][ambiguities], some judgment calls have been made. +/// This class tries to match browsers' data URI logic, to ensure that it can +/// losslessly parse its own output, and to accept as much input as it can make +/// sense of. A balance has been struck between these goals so that while none +/// of them have been accomplished perfectly, all of them are close enough for +/// practical use. +/// +/// [rfc]: http://tools.ietf.org/html/rfc2397 +/// [ambiguities]: https://simonsapin.github.io/data-urls/ +/// +/// Some particular notes on the behavior: +/// +/// * When encoding, all characters that are not [reserved][] in the type, +/// subtype, parameter names, and parameter values of media types are +/// percent-encoded using UTF-8. +/// +/// * When decoding, the type, subtype, parameter names, and parameter values of +/// media types are percent-decoded using UTF-8. Parameter values are allowed +/// to contain non-token characters once decoded, but the other tokens are +/// not. +/// +/// * As per the spec, quoted-string parameters are not supported when decoding. +/// +/// * Query components are included in the decoding algorithm, but fragments are +/// not. +/// +/// * Invalid media types and parameters will raise exceptions when decoding. +/// This is standard for Dart parsers but contrary to browser behavior. +/// +/// * The URL and filename-safe base64 alphabet is accepted when decoding but +/// never emitted when encoding, since browsers don't support it. +/// +/// [lws]: https://tools.ietf.org/html/rfc2616#section-2.2 +/// [reserved]: https://tools.ietf.org/html/rfc3986#section-2.2 +class DataUri implements Uri { + /// The inner URI to which all [Uri] methods are forwarded. + final Uri _inner; + + /// The byte data contained in the data URI. + final List data; + + /// The media type declared for the data URI. + /// + /// This defaults to `text/plain;charset=US-ASCII`. + final MediaType mediaType; + + /// The encoding declared by the `charset` parameter in [mediaType]. + /// + /// If [mediaType] has no `charset` parameter, this defaults to [ASCII]. If + /// the `charset` parameter declares an encoding that can't be found using + /// [Encoding.getByName], this returns `null`. + Encoding get declaredEncoding { + var charset = mediaType.parameters["charset"]; + return charset == null ? ASCII : Encoding.getByName(charset); + } + + /// Creates a new data URI with the given [mediaType] and [data]. + /// + /// If [base64] is `true` (the default), the data is base64-encoded; + /// otherwise, it's percent-encoded. + /// + /// If [encoding] is passed or [mediaType] declares a `charset` parameter, + /// [data] is encoded using that encoding. Otherwise, it's encoded using + /// [UTF8] or [ASCII] depending on whether it contains any non-ASCII + /// characters. + /// + /// Throws [ArgumentError] if [mediaType] and [encoding] disagree on the + /// encoding, and an [UnsupportedError] if [mediaType] defines an encoding + /// that's not supported by [Encoding.getByName]. + factory DataUri.encodeString(String data, {bool base64: true, + MediaType mediaType, Encoding encoding}) { + if (mediaType == null) mediaType = new MediaType("text", "plain"); + + var charset = mediaType.parameters["charset"]; + var bytes; + if (encoding != null) { + if (charset == null) { + mediaType = mediaType.change(parameters: {"charset": encoding.name}); + } else if (Encoding.getByName(charset) != encoding) { + throw new ArgumentError("Media type charset '$charset' disagrees with " + "encoding '${encoding.name}'."); + } + bytes = encoding.encode(data); + } else if (charset != null) { + encoding = Encoding.getByName(charset); + if (encoding == null) { + throw new UnsupportedError( + 'Unsupported media type charset "$charset".'); + } + bytes = encoding.encode(data); + } else if (data.codeUnits.every((codeUnit) => codeUnit < 0x80)) { + // If the data is pure ASCII, don't bother explicitly defining a charset. + bytes = data.codeUnits; + } else { + // If the data isn't pure ASCII, default to UTF-8. + bytes = UTF8.encode(data); + mediaType = mediaType.change(parameters: {"charset": "utf-8"}); + } + + return new DataUri.encode(bytes, base64: base64, mediaType: mediaType); + } + + /// Creates a new data URI with the given [mediaType] and [data]. + /// + /// If [base64] is `true` (the default), the data is base64-encoded; + /// otherwise, it's percent-encoded. + factory DataUri.encode(List data, {bool base64: true, + MediaType mediaType}) { + mediaType ??= new MediaType('text', 'plain'); + + var buffer = new StringBuffer(); + + // Manually stringify the media type because [section 3][rfc] requires that + // parameter values should have non-token characters URL-escaped rather than + // emitting them as quoted-strings. This also allows us to omit text/plain + // if possible. + // + // [rfc]: http://tools.ietf.org/html/rfc2397#section-3 + if (mediaType.type != 'text' || mediaType.subtype != 'plain') { + buffer.write(_utf8Percent.encode(mediaType.type)); + buffer.write("/"); + buffer.write(_utf8Percent.encode(mediaType.subtype)); + } + + mediaType.parameters.forEach((attribute, value) { + buffer.write(";${_utf8Percent.encode(attribute)}="); + buffer.write(_utf8Percent.encode(value)); + }); + + if (base64) { + buffer.write(";base64,"); + // *Don't* use the URL-safe encoding scheme, since browsers don't actually + // support it. + buffer.write(CryptoUtils.bytesToBase64(data)); + } else { + buffer.write(","); + buffer.write(percent.encode(data)); + } + + return new DataUri._(data, mediaType, + new Uri(scheme: 'data', path: buffer.toString())); + } + + /// Decodes [uri] to make its [data] and [mediaType] available. + /// + /// [uri] may be a [Uri] or a [String]. + /// + /// Throws an [ArgumentError] if [uri] is an invalid type or has a scheme + /// other than `data:`. Throws a [FormatException] if parsing fails. + factory DataUri.decode(uri) { + if (uri is String) { + uri = Uri.parse(uri); + } else if (uri is! Uri) { + throw new ArgumentError.value(uri, "uri", "Must be a String or a Uri."); + } + + if (uri.scheme != 'data') { + throw new ArgumentError.value(uri, "uri", "Can only decode a data: URI."); + } + + return wrapFormatException("data URI", uri.toString(), () { + // Remove the fragment, as per https://simonsapin.github.io/data-urls/. + // TODO(nweiz): Use Uri.removeFragment once sdk#24593 is fixed. + var string = uri.toString(); + var fragment = string.indexOf('#'); + if (fragment != -1) string = string.substring(0, fragment); + var scanner = new StringScanner(string); + scanner.expect('data:'); + + // Manually scan the media type for three reasons: + // + // * Media type parameter values that aren't valid tokens are URL-encoded + // rather than quoted. + // + // * The media type may be omitted without omitting the parameters. + // + // * We need to be able to stop once we reach `;base64,`, even though at + // first it looks like a parameter. + var type; + var subtype; + var implicitType = false; + if (scanner.scan(token)) { + type = _verifyToken(scanner); + scanner.expect('/'); + subtype = _expectToken(scanner); + } else { + type = 'text'; + subtype = 'plain'; + implicitType = true; + } + + // Scan the parameters, up through ";base64" or a comma. + var parameters = {}; + var base64 = false; + while (scanner.scan(';')) { + var attribute = _expectToken(scanner); + + if (attribute != 'base64') { + scanner.expect('='); + } else if (!scanner.scan('=')) { + base64 = true; + break; + } + + // Don't use [_expectToken] because the value uses percent-encoding to + // escape non-token characters. + scanner.expect(token); + parameters[attribute] = _utf8Percent.decode(scanner.lastMatch[0]); + } + scanner.expect(','); + + if (implicitType && parameters.isEmpty) { + parameters = {"charset": "US-ASCII"}; + } + + var mediaType = new MediaType(type, subtype, parameters); + + var data = base64 + ? CryptoUtils.base64StringToBytes(scanner.rest) + : percent.decode(scanner.rest); + + return new DataUri._(data, mediaType, uri); + }); + } + + /// Returns the percent-decoded value of the last MIME token scanned by + /// [scanner]. + /// + /// Throws a [FormatException] if it's not a valid token after + /// percent-decoding. + static String _verifyToken(StringScanner scanner) { + var value = _utf8Percent.decode(scanner.lastMatch[0]); + if (!value.contains(nonToken)) return value; + scanner.error("Invalid token."); + return null; + } + + /// Scans [scanner] through a MIME token and returns its percent-decoded + /// value. + /// + /// Throws a [FormatException] if it's not a valid token after + /// percent-decoding. + static String _expectToken(StringScanner scanner) { + scanner.expect(token, name: "a token"); + return _verifyToken(scanner); + } + + DataUri._(this.data, this.mediaType, this._inner); + + /// Returns the decoded [data] decoded using [encoding]. + /// + /// [encoding] defaults to [declaredEncoding]. If the declared encoding isn't + /// supported by [Encoding.getByName] and [encoding] isn't passed, this throws + /// an [UnsupportedError]. + String dataAsString({Encoding encoding}) { + encoding ??= declaredEncoding; + if (encoding == null) { + throw new UnsupportedError( + 'Unsupported media type charset ' + '"${mediaType.parameters["charset"]}".'); + } + + return encoding.decode(data); + } + + String get scheme => _inner.scheme; + String get authority => _inner.authority; + String get userInfo => _inner.userInfo; + String get host => _inner.host; + int get port => _inner.port; + String get path => _inner.path; + String get query => _inner.query; + String get fragment => _inner.fragment; + Uri replace({String scheme, String userInfo, String host, int port, + String path, Iterable pathSegments, String query, + Map queryParameters, String fragment}) => + _inner.replace( + scheme: scheme, userInfo: userInfo, host: host, port: port, + path: path, pathSegments: pathSegments, query: query, + queryParameters: queryParameters, fragment: fragment); + Uri removeFragment() => _inner.removeFragment(); + List get pathSegments => _inner.pathSegments; + Map get queryParameters => _inner.queryParameters; + Uri normalizePath() => _inner.normalizePath(); + bool get isAbsolute => _inner.isAbsolute; + Uri resolve(String reference) => _inner.resolve(reference); + Uri resolveUri(Uri reference) => _inner.resolveUri(reference); + bool get hasScheme => _inner.hasScheme; + bool get hasAuthority => _inner.hasAuthority; + bool get hasPort => _inner.hasPort; + bool get hasQuery => _inner.hasQuery; + bool get hasFragment => _inner.hasFragment; + bool get hasEmptyPath => _inner.hasEmptyPath; + bool get hasAbsolutePath => _inner.hasAbsolutePath; + String get origin => _inner.origin; + String toFilePath({bool windows}) => _inner.toFilePath(windows: windows); + String toString() => _inner.toString(); + bool operator==(other) => _inner == other; + int get hashCode => _inner.hashCode; +} diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index 0c1d578c87..a78597f8e0 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,15 +1,17 @@ name: http_parser -version: 1.1.0-dev +version: 1.1.0 author: "Dart Team " homepage: https://github.com/dart-lang/http_parser description: > A platform-independent package for parsing and serializing HTTP formats. dependencies: - crypto: "^0.9.0" + convert: "^1.0.0" collection: ">=0.9.1 <2.0.0" + crypto: "^0.9.0" source_span: "^1.0.0" string_scanner: ">=0.0.0 <0.2.0" dev_dependencies: + charcode: "^1.1.0" test: "^0.12.0" environment: - sdk: ">=1.8.0 <2.0.0" + sdk: ">=1.12.0 <2.0.0" diff --git a/pkgs/http_parser/test/data_uri_test.dart b/pkgs/http_parser/test/data_uri_test.dart new file mode 100644 index 0000000000..da97cfe51f --- /dev/null +++ b/pkgs/http_parser/test/data_uri_test.dart @@ -0,0 +1,241 @@ +// 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:charcode/charcode.dart'; +import 'package:http_parser/http_parser.dart'; +import 'package:test/test.dart'; + +void main() { + group("encode", () { + test("base64-encodes data by default", () { + var uri = new DataUri.encode([1, 2, 3, 4]); + expect(uri.toString(), equals("data:;base64,AQIDBA==")); + }); + + test("doesn't use URL-safe base64 encoding", () { + var uri = new DataUri.encode([0xFB, 0xFF]); + expect(uri.toString(), equals("data:;base64,+/8=")); + }); + + test("percent-encodes data if base64 is disabled", () { + var uri = new DataUri.encode([$a, $B, $plus, $slash, 0xFF], + base64: false); + expect(uri.toString(), equals("data:,aB%2B%2F%FF")); + }); + + test("includes a media type and its parameters", () { + var mediaType = new MediaType('text', 'html', { + 'foo': 'bar', + 'baz': 'bang' + }); + var uri = new DataUri.encode([], mediaType: mediaType); + expect(uri.toString(), equals('data:text/html;foo=bar;baz=bang;base64,')); + }); + + test("percent-encodes the media type and its parameters", () { + var mediaType = new MediaType('te=xt', 'ht%ml', {'f;oo': 'ba,r'}); + var uri = new DataUri.encode([], mediaType: mediaType); + expect(uri.toString(), + equals('data:te%3Dxt/ht%25ml;f%3Boo=ba%2Cr;base64,')); + }); + + test("UTF-8 encodes non-ASCII characters", () { + var mediaType = new MediaType('tëxt', 'ћtml', {'føo': 'bår'}); + var uri = new DataUri.encode([], mediaType: mediaType); + expect(uri.toString(), + equals('data:t%C3%ABxt/%D1%9Btml;f%C3%B8o=b%C3%A5r;base64,')); + }); + + test("doesn't include a text/plain media type", () { + var mediaType = new MediaType('text', 'plain', {'foo': 'bar'}); + var uri = new DataUri.encode([], mediaType: mediaType); + expect(uri.toString(), equals('data:;foo=bar;base64,')); + }); + + group("with a string", () { + test("defaults to ASCII if it's sufficient", () { + var uri = new DataUri.encodeString('foo'); + expect(uri.toString(), equals("data:;base64,Zm9v")); + }); + + test("defaults to UTF-8 encoding if it's needed", () { + var uri = new DataUri.encodeString('føo'); + expect(uri.toString(), equals("data:;charset=utf-8;base64,ZsO4bw==")); + }); + + test("obeys a passed encoding", () { + var uri = new DataUri.encodeString('føo', encoding: LATIN1); + expect(uri.toString(), equals("data:;charset=iso-8859-1;base64,Zvhv")); + }); + + test("obeys a media type encoding", () { + var mediaType = new MediaType('text', 'plain', + {'charset': 'iso-8859-1'}); + var uri = new DataUri.encodeString('føo', mediaType: mediaType); + expect(uri.toString(), equals("data:;charset=iso-8859-1;base64,Zvhv")); + }); + + test("obeys a passed encoding that matches a media type encoding", () { + var mediaType = new MediaType('text', 'plain', + {'charset': 'iso-8859-1'}); + var uri = new DataUri.encodeString('føo', + encoding: LATIN1, mediaType: mediaType); + expect(uri.toString(), equals("data:;charset=iso-8859-1;base64,Zvhv")); + }); + + test("throws if a media type encoding is unsupported", () { + var mediaType = new MediaType('text', 'plain', {'charset': 'fblthp'}); + expect(() => new DataUri.encodeString('føo', mediaType: mediaType), + throwsUnsupportedError); + }); + + test("throws if a passed encoding disagrees with a media type encoding", + () { + var mediaType = new MediaType('text', 'plain', {'charset': 'utf-8'}); + expect(() { + new DataUri.encodeString('føo', + encoding: LATIN1, mediaType: mediaType); + }, throwsArgumentError); + }); + }); + }); + + group("decode", () { + test("decodes a base64 URI", () { + var uri = new DataUri.decode("data:;base64,AQIDBA=="); + expect(uri.data, equals([1, 2, 3, 4])); + }); + + test("decodes a percent-encoded URI", () { + var uri = new DataUri.decode("data:,aB%2B%2F%FF"); + expect(uri.data, equals([$a, $B, $plus, $slash, 0xFF])); + }); + + test("decodes a media type and its parameters", () { + var uri = new DataUri.decode("data:text/html;foo=bar;baz=bang;base64,"); + expect(uri.data, isEmpty); + expect(uri.mediaType.type, equals('text')); + expect(uri.mediaType.subtype, equals('html')); + expect(uri.mediaType.parameters, equals({ + 'foo': 'bar', + 'baz': 'bang' + })); + }); + + test("defaults to a text/plain media type", () { + var uri = new DataUri.decode("data:;base64,"); + expect(uri.mediaType.type, equals('text')); + expect(uri.mediaType.subtype, equals('plain')); + expect(uri.mediaType.parameters, equals({'charset': 'US-ASCII'})); + }); + + test("defaults to a text/plain media type with parameters", () { + var uri = new DataUri.decode("data:;foo=bar;base64,"); + expect(uri.mediaType.type, equals('text')); + expect(uri.mediaType.subtype, equals('plain')); + expect(uri.mediaType.parameters, equals({'foo': 'bar'})); + }); + + test("percent-decodes the media type and its parameters", () { + var uri = new DataUri.decode( + 'data:te%78t/ht%6Dl;f%6Fo=ba%2Cr;base64,'); + expect(uri.mediaType.type, equals('text')); + expect(uri.mediaType.subtype, equals('html')); + expect(uri.mediaType.parameters, equals({'foo': 'ba,r'})); + }); + + test("assumes the URI is UTF-8", () { + var uri = new DataUri.decode( + 'data:t%C3%ABxt/%D1%9Btml;f%C3%B8o=b%C3%A5r;base64,'); + expect(uri.mediaType.type, equals('tëxt')); + expect(uri.mediaType.subtype, equals('ћtml')); + expect(uri.mediaType.parameters, equals({'føo': 'bår'})); + }); + + test("allows a parameter named base64", () { + var uri = new DataUri.decode("data:;base64=no,foo"); + expect(uri.mediaType.parameters, equals({'base64': 'no'})); + expect(uri.data, equals([$f, $o, $o])); + }); + + test("includes the query", () { + var uri = new DataUri.decode("data:,a?b=c"); + expect(uri.data, equals([$a, $question, $b, $equal, $c])); + }); + + test("doesn't include the fragment", () { + var uri = new DataUri.decode("data:,a#b=c"); + expect(uri.data, equals([$a])); + }); + + test("supports the URL-safe base64 alphabet", () { + var uri = new DataUri.decode("data:;base64,-_8%3D"); + expect(uri.data, equals([0xFB, 0xFF])); + }); + + group("forbids", () { + test("a parameter with the wrong type", () { + expect(() => new DataUri.decode(12), throwsArgumentError); + }); + + test("a parameter with the wrong scheme", () { + expect(() => new DataUri.decode("http:;base64,"), throwsArgumentError); + }); + + test("non-token characters in invalid positions", () { + expect(() => new DataUri.decode("data:text//plain;base64,"), + throwsFormatException); + expect(() => new DataUri.decode("data:text/plain;;base64,"), + throwsFormatException); + expect(() => new DataUri.decode("data:text/plain;/base64,"), + throwsFormatException); + expect(() => new DataUri.decode("data:text/plain;,"), + throwsFormatException); + expect(() => new DataUri.decode("data:text/plain;base64;"), + throwsFormatException); + expect(() => new DataUri.decode("data:text/plain;foo=bar=baz;base64,"), + throwsFormatException); + }); + + test("encoded non-token characters in invalid positions", () { + expect(() => new DataUri.decode("data:te%2Cxt/plain;base64,"), + throwsFormatException); + expect(() => new DataUri.decode("data:text/pl%2Cain;base64,"), + throwsFormatException); + expect(() => new DataUri.decode("data:text/plain;f%2Coo=bar;base64,"), + throwsFormatException); + }); + }); + }); + + group("dataAsString", () { + test("decodes the data as ASCII by default", () { + var uri = new DataUri.decode("data:;base64,Zm9v"); + expect(uri.dataAsString(), equals("foo")); + + uri = new DataUri.decode("data:;base64,ZsO4bw=="); + expect(() => uri.dataAsString(), throwsFormatException); + }); + + test("decodes the data using the declared charset", () { + var uri = new DataUri.decode("data:;charset=iso-8859-1;base64,ZsO4bw=="); + expect(uri.dataAsString(), equals("føo")); + }); + + test("throws if the charset isn't supported", () { + var uri = new DataUri.decode("data:;charset=fblthp;base64,ZsO4bw=="); + expect(() => uri.dataAsString(), throwsUnsupportedError); + }); + + test("uses the given encoding in preference to the declared charset", () { + var uri = new DataUri.decode("data:;charset=fblthp;base64,ZsO4bw=="); + expect(uri.dataAsString(encoding: UTF8), equals("føo")); + + uri = new DataUri.decode("data:;charset=utf-8;base64,ZsO4bw=="); + expect(uri.dataAsString(encoding: LATIN1), equals("føo")); + }); + }); +} From a53a9410571f5a23be527b8f8a37d4ef508bc090 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 14 Dec 2015 14:06:08 -0800 Subject: [PATCH 025/126] Remove DataUri. Closes dart-lang/http_parser#6 R=kevmoo@google.com Review URL: https://codereview.chromium.org//1516133002 . --- pkgs/http_parser/CHANGELOG.md | 8 +- pkgs/http_parser/lib/http_parser.dart | 1 - pkgs/http_parser/lib/src/data_uri.dart | 328 ----------------------- pkgs/http_parser/pubspec.yaml | 6 +- pkgs/http_parser/test/data_uri_test.dart | 241 ----------------- 5 files changed, 8 insertions(+), 576 deletions(-) delete mode 100644 pkgs/http_parser/lib/src/data_uri.dart delete mode 100644 pkgs/http_parser/test/data_uri_test.dart diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index 51e21cb6b9..9b54230677 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,6 +1,10 @@ -## 1.1.0 +## 2.0.0 + +* Removed the `DataUri` class. It's redundant with the `Uri.data` getter that's + coming in Dart 1.14, and the `DataUri.data` field in particular was an invalid + override of that field. -* Added a `DataUri` class for encoding and decoding data URIs. +## 1.1.0 * The MIME spec says that media types and their parameter names are case-insensitive. Accordingly, `MediaType` now uses a case-insensitive map for diff --git a/pkgs/http_parser/lib/http_parser.dart b/pkgs/http_parser/lib/http_parser.dart index a03c84b8b7..77c75c0aa9 100644 --- a/pkgs/http_parser/lib/http_parser.dart +++ b/pkgs/http_parser/lib/http_parser.dart @@ -6,7 +6,6 @@ library http_parser; export 'src/authentication_challenge.dart'; export 'src/case_insensitive_map.dart'; -export 'src/data_uri.dart'; export 'src/http_date.dart'; export 'src/media_type.dart'; export 'src/web_socket.dart'; diff --git a/pkgs/http_parser/lib/src/data_uri.dart b/pkgs/http_parser/lib/src/data_uri.dart deleted file mode 100644 index b8cdee95f8..0000000000 --- a/pkgs/http_parser/lib/src/data_uri.dart +++ /dev/null @@ -1,328 +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:convert'; - -import 'package:convert/convert.dart'; -import 'package:crypto/crypto.dart'; -import 'package:string_scanner/string_scanner.dart'; - -import 'media_type.dart'; -import 'scan.dart'; -import 'utils.dart'; - -/// Like [whitespace] from scan.dart, except that it matches URI-encoded -/// whitespace rather than literal characters. -final _whitespace = new RegExp(r'(?:(?:%0D%0A)?(?:%20|%09)+)*'); - -/// A converter for percent encoding strings using UTF-8. -final _utf8Percent = UTF8.fuse(percent); - -/// A class representing a `data:` URI that provides access to its [mediaType] -/// and the [data] it contains. -/// -/// Data can be encoded as a `data:` URI using [encode] or [encodeString], and -/// decoded using [decode]. -/// -/// This implementation is based on [RFC 2397][rfc], but as that RFC is -/// [notoriously ambiguous][ambiguities], some judgment calls have been made. -/// This class tries to match browsers' data URI logic, to ensure that it can -/// losslessly parse its own output, and to accept as much input as it can make -/// sense of. A balance has been struck between these goals so that while none -/// of them have been accomplished perfectly, all of them are close enough for -/// practical use. -/// -/// [rfc]: http://tools.ietf.org/html/rfc2397 -/// [ambiguities]: https://simonsapin.github.io/data-urls/ -/// -/// Some particular notes on the behavior: -/// -/// * When encoding, all characters that are not [reserved][] in the type, -/// subtype, parameter names, and parameter values of media types are -/// percent-encoded using UTF-8. -/// -/// * When decoding, the type, subtype, parameter names, and parameter values of -/// media types are percent-decoded using UTF-8. Parameter values are allowed -/// to contain non-token characters once decoded, but the other tokens are -/// not. -/// -/// * As per the spec, quoted-string parameters are not supported when decoding. -/// -/// * Query components are included in the decoding algorithm, but fragments are -/// not. -/// -/// * Invalid media types and parameters will raise exceptions when decoding. -/// This is standard for Dart parsers but contrary to browser behavior. -/// -/// * The URL and filename-safe base64 alphabet is accepted when decoding but -/// never emitted when encoding, since browsers don't support it. -/// -/// [lws]: https://tools.ietf.org/html/rfc2616#section-2.2 -/// [reserved]: https://tools.ietf.org/html/rfc3986#section-2.2 -class DataUri implements Uri { - /// The inner URI to which all [Uri] methods are forwarded. - final Uri _inner; - - /// The byte data contained in the data URI. - final List data; - - /// The media type declared for the data URI. - /// - /// This defaults to `text/plain;charset=US-ASCII`. - final MediaType mediaType; - - /// The encoding declared by the `charset` parameter in [mediaType]. - /// - /// If [mediaType] has no `charset` parameter, this defaults to [ASCII]. If - /// the `charset` parameter declares an encoding that can't be found using - /// [Encoding.getByName], this returns `null`. - Encoding get declaredEncoding { - var charset = mediaType.parameters["charset"]; - return charset == null ? ASCII : Encoding.getByName(charset); - } - - /// Creates a new data URI with the given [mediaType] and [data]. - /// - /// If [base64] is `true` (the default), the data is base64-encoded; - /// otherwise, it's percent-encoded. - /// - /// If [encoding] is passed or [mediaType] declares a `charset` parameter, - /// [data] is encoded using that encoding. Otherwise, it's encoded using - /// [UTF8] or [ASCII] depending on whether it contains any non-ASCII - /// characters. - /// - /// Throws [ArgumentError] if [mediaType] and [encoding] disagree on the - /// encoding, and an [UnsupportedError] if [mediaType] defines an encoding - /// that's not supported by [Encoding.getByName]. - factory DataUri.encodeString(String data, {bool base64: true, - MediaType mediaType, Encoding encoding}) { - if (mediaType == null) mediaType = new MediaType("text", "plain"); - - var charset = mediaType.parameters["charset"]; - var bytes; - if (encoding != null) { - if (charset == null) { - mediaType = mediaType.change(parameters: {"charset": encoding.name}); - } else if (Encoding.getByName(charset) != encoding) { - throw new ArgumentError("Media type charset '$charset' disagrees with " - "encoding '${encoding.name}'."); - } - bytes = encoding.encode(data); - } else if (charset != null) { - encoding = Encoding.getByName(charset); - if (encoding == null) { - throw new UnsupportedError( - 'Unsupported media type charset "$charset".'); - } - bytes = encoding.encode(data); - } else if (data.codeUnits.every((codeUnit) => codeUnit < 0x80)) { - // If the data is pure ASCII, don't bother explicitly defining a charset. - bytes = data.codeUnits; - } else { - // If the data isn't pure ASCII, default to UTF-8. - bytes = UTF8.encode(data); - mediaType = mediaType.change(parameters: {"charset": "utf-8"}); - } - - return new DataUri.encode(bytes, base64: base64, mediaType: mediaType); - } - - /// Creates a new data URI with the given [mediaType] and [data]. - /// - /// If [base64] is `true` (the default), the data is base64-encoded; - /// otherwise, it's percent-encoded. - factory DataUri.encode(List data, {bool base64: true, - MediaType mediaType}) { - mediaType ??= new MediaType('text', 'plain'); - - var buffer = new StringBuffer(); - - // Manually stringify the media type because [section 3][rfc] requires that - // parameter values should have non-token characters URL-escaped rather than - // emitting them as quoted-strings. This also allows us to omit text/plain - // if possible. - // - // [rfc]: http://tools.ietf.org/html/rfc2397#section-3 - if (mediaType.type != 'text' || mediaType.subtype != 'plain') { - buffer.write(_utf8Percent.encode(mediaType.type)); - buffer.write("/"); - buffer.write(_utf8Percent.encode(mediaType.subtype)); - } - - mediaType.parameters.forEach((attribute, value) { - buffer.write(";${_utf8Percent.encode(attribute)}="); - buffer.write(_utf8Percent.encode(value)); - }); - - if (base64) { - buffer.write(";base64,"); - // *Don't* use the URL-safe encoding scheme, since browsers don't actually - // support it. - buffer.write(CryptoUtils.bytesToBase64(data)); - } else { - buffer.write(","); - buffer.write(percent.encode(data)); - } - - return new DataUri._(data, mediaType, - new Uri(scheme: 'data', path: buffer.toString())); - } - - /// Decodes [uri] to make its [data] and [mediaType] available. - /// - /// [uri] may be a [Uri] or a [String]. - /// - /// Throws an [ArgumentError] if [uri] is an invalid type or has a scheme - /// other than `data:`. Throws a [FormatException] if parsing fails. - factory DataUri.decode(uri) { - if (uri is String) { - uri = Uri.parse(uri); - } else if (uri is! Uri) { - throw new ArgumentError.value(uri, "uri", "Must be a String or a Uri."); - } - - if (uri.scheme != 'data') { - throw new ArgumentError.value(uri, "uri", "Can only decode a data: URI."); - } - - return wrapFormatException("data URI", uri.toString(), () { - // Remove the fragment, as per https://simonsapin.github.io/data-urls/. - // TODO(nweiz): Use Uri.removeFragment once sdk#24593 is fixed. - var string = uri.toString(); - var fragment = string.indexOf('#'); - if (fragment != -1) string = string.substring(0, fragment); - var scanner = new StringScanner(string); - scanner.expect('data:'); - - // Manually scan the media type for three reasons: - // - // * Media type parameter values that aren't valid tokens are URL-encoded - // rather than quoted. - // - // * The media type may be omitted without omitting the parameters. - // - // * We need to be able to stop once we reach `;base64,`, even though at - // first it looks like a parameter. - var type; - var subtype; - var implicitType = false; - if (scanner.scan(token)) { - type = _verifyToken(scanner); - scanner.expect('/'); - subtype = _expectToken(scanner); - } else { - type = 'text'; - subtype = 'plain'; - implicitType = true; - } - - // Scan the parameters, up through ";base64" or a comma. - var parameters = {}; - var base64 = false; - while (scanner.scan(';')) { - var attribute = _expectToken(scanner); - - if (attribute != 'base64') { - scanner.expect('='); - } else if (!scanner.scan('=')) { - base64 = true; - break; - } - - // Don't use [_expectToken] because the value uses percent-encoding to - // escape non-token characters. - scanner.expect(token); - parameters[attribute] = _utf8Percent.decode(scanner.lastMatch[0]); - } - scanner.expect(','); - - if (implicitType && parameters.isEmpty) { - parameters = {"charset": "US-ASCII"}; - } - - var mediaType = new MediaType(type, subtype, parameters); - - var data = base64 - ? CryptoUtils.base64StringToBytes(scanner.rest) - : percent.decode(scanner.rest); - - return new DataUri._(data, mediaType, uri); - }); - } - - /// Returns the percent-decoded value of the last MIME token scanned by - /// [scanner]. - /// - /// Throws a [FormatException] if it's not a valid token after - /// percent-decoding. - static String _verifyToken(StringScanner scanner) { - var value = _utf8Percent.decode(scanner.lastMatch[0]); - if (!value.contains(nonToken)) return value; - scanner.error("Invalid token."); - return null; - } - - /// Scans [scanner] through a MIME token and returns its percent-decoded - /// value. - /// - /// Throws a [FormatException] if it's not a valid token after - /// percent-decoding. - static String _expectToken(StringScanner scanner) { - scanner.expect(token, name: "a token"); - return _verifyToken(scanner); - } - - DataUri._(this.data, this.mediaType, this._inner); - - /// Returns the decoded [data] decoded using [encoding]. - /// - /// [encoding] defaults to [declaredEncoding]. If the declared encoding isn't - /// supported by [Encoding.getByName] and [encoding] isn't passed, this throws - /// an [UnsupportedError]. - String dataAsString({Encoding encoding}) { - encoding ??= declaredEncoding; - if (encoding == null) { - throw new UnsupportedError( - 'Unsupported media type charset ' - '"${mediaType.parameters["charset"]}".'); - } - - return encoding.decode(data); - } - - String get scheme => _inner.scheme; - String get authority => _inner.authority; - String get userInfo => _inner.userInfo; - String get host => _inner.host; - int get port => _inner.port; - String get path => _inner.path; - String get query => _inner.query; - String get fragment => _inner.fragment; - Uri replace({String scheme, String userInfo, String host, int port, - String path, Iterable pathSegments, String query, - Map queryParameters, String fragment}) => - _inner.replace( - scheme: scheme, userInfo: userInfo, host: host, port: port, - path: path, pathSegments: pathSegments, query: query, - queryParameters: queryParameters, fragment: fragment); - Uri removeFragment() => _inner.removeFragment(); - List get pathSegments => _inner.pathSegments; - Map get queryParameters => _inner.queryParameters; - Uri normalizePath() => _inner.normalizePath(); - bool get isAbsolute => _inner.isAbsolute; - Uri resolve(String reference) => _inner.resolve(reference); - Uri resolveUri(Uri reference) => _inner.resolveUri(reference); - bool get hasScheme => _inner.hasScheme; - bool get hasAuthority => _inner.hasAuthority; - bool get hasPort => _inner.hasPort; - bool get hasQuery => _inner.hasQuery; - bool get hasFragment => _inner.hasFragment; - bool get hasEmptyPath => _inner.hasEmptyPath; - bool get hasAbsolutePath => _inner.hasAbsolutePath; - String get origin => _inner.origin; - String toFilePath({bool windows}) => _inner.toFilePath(windows: windows); - String toString() => _inner.toString(); - bool operator==(other) => _inner == other; - int get hashCode => _inner.hashCode; -} diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index a78597f8e0..930db8f2db 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,17 +1,15 @@ name: http_parser -version: 1.1.0 +version: 2.0.0 author: "Dart Team " homepage: https://github.com/dart-lang/http_parser description: > A platform-independent package for parsing and serializing HTTP formats. dependencies: - convert: "^1.0.0" collection: ">=0.9.1 <2.0.0" crypto: "^0.9.0" source_span: "^1.0.0" string_scanner: ">=0.0.0 <0.2.0" dev_dependencies: - charcode: "^1.1.0" test: "^0.12.0" environment: - sdk: ">=1.12.0 <2.0.0" + sdk: ">=1.8.0 <2.0.0" diff --git a/pkgs/http_parser/test/data_uri_test.dart b/pkgs/http_parser/test/data_uri_test.dart deleted file mode 100644 index da97cfe51f..0000000000 --- a/pkgs/http_parser/test/data_uri_test.dart +++ /dev/null @@ -1,241 +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:convert'; - -import 'package:charcode/charcode.dart'; -import 'package:http_parser/http_parser.dart'; -import 'package:test/test.dart'; - -void main() { - group("encode", () { - test("base64-encodes data by default", () { - var uri = new DataUri.encode([1, 2, 3, 4]); - expect(uri.toString(), equals("data:;base64,AQIDBA==")); - }); - - test("doesn't use URL-safe base64 encoding", () { - var uri = new DataUri.encode([0xFB, 0xFF]); - expect(uri.toString(), equals("data:;base64,+/8=")); - }); - - test("percent-encodes data if base64 is disabled", () { - var uri = new DataUri.encode([$a, $B, $plus, $slash, 0xFF], - base64: false); - expect(uri.toString(), equals("data:,aB%2B%2F%FF")); - }); - - test("includes a media type and its parameters", () { - var mediaType = new MediaType('text', 'html', { - 'foo': 'bar', - 'baz': 'bang' - }); - var uri = new DataUri.encode([], mediaType: mediaType); - expect(uri.toString(), equals('data:text/html;foo=bar;baz=bang;base64,')); - }); - - test("percent-encodes the media type and its parameters", () { - var mediaType = new MediaType('te=xt', 'ht%ml', {'f;oo': 'ba,r'}); - var uri = new DataUri.encode([], mediaType: mediaType); - expect(uri.toString(), - equals('data:te%3Dxt/ht%25ml;f%3Boo=ba%2Cr;base64,')); - }); - - test("UTF-8 encodes non-ASCII characters", () { - var mediaType = new MediaType('tëxt', 'ћtml', {'føo': 'bår'}); - var uri = new DataUri.encode([], mediaType: mediaType); - expect(uri.toString(), - equals('data:t%C3%ABxt/%D1%9Btml;f%C3%B8o=b%C3%A5r;base64,')); - }); - - test("doesn't include a text/plain media type", () { - var mediaType = new MediaType('text', 'plain', {'foo': 'bar'}); - var uri = new DataUri.encode([], mediaType: mediaType); - expect(uri.toString(), equals('data:;foo=bar;base64,')); - }); - - group("with a string", () { - test("defaults to ASCII if it's sufficient", () { - var uri = new DataUri.encodeString('foo'); - expect(uri.toString(), equals("data:;base64,Zm9v")); - }); - - test("defaults to UTF-8 encoding if it's needed", () { - var uri = new DataUri.encodeString('føo'); - expect(uri.toString(), equals("data:;charset=utf-8;base64,ZsO4bw==")); - }); - - test("obeys a passed encoding", () { - var uri = new DataUri.encodeString('føo', encoding: LATIN1); - expect(uri.toString(), equals("data:;charset=iso-8859-1;base64,Zvhv")); - }); - - test("obeys a media type encoding", () { - var mediaType = new MediaType('text', 'plain', - {'charset': 'iso-8859-1'}); - var uri = new DataUri.encodeString('føo', mediaType: mediaType); - expect(uri.toString(), equals("data:;charset=iso-8859-1;base64,Zvhv")); - }); - - test("obeys a passed encoding that matches a media type encoding", () { - var mediaType = new MediaType('text', 'plain', - {'charset': 'iso-8859-1'}); - var uri = new DataUri.encodeString('føo', - encoding: LATIN1, mediaType: mediaType); - expect(uri.toString(), equals("data:;charset=iso-8859-1;base64,Zvhv")); - }); - - test("throws if a media type encoding is unsupported", () { - var mediaType = new MediaType('text', 'plain', {'charset': 'fblthp'}); - expect(() => new DataUri.encodeString('føo', mediaType: mediaType), - throwsUnsupportedError); - }); - - test("throws if a passed encoding disagrees with a media type encoding", - () { - var mediaType = new MediaType('text', 'plain', {'charset': 'utf-8'}); - expect(() { - new DataUri.encodeString('føo', - encoding: LATIN1, mediaType: mediaType); - }, throwsArgumentError); - }); - }); - }); - - group("decode", () { - test("decodes a base64 URI", () { - var uri = new DataUri.decode("data:;base64,AQIDBA=="); - expect(uri.data, equals([1, 2, 3, 4])); - }); - - test("decodes a percent-encoded URI", () { - var uri = new DataUri.decode("data:,aB%2B%2F%FF"); - expect(uri.data, equals([$a, $B, $plus, $slash, 0xFF])); - }); - - test("decodes a media type and its parameters", () { - var uri = new DataUri.decode("data:text/html;foo=bar;baz=bang;base64,"); - expect(uri.data, isEmpty); - expect(uri.mediaType.type, equals('text')); - expect(uri.mediaType.subtype, equals('html')); - expect(uri.mediaType.parameters, equals({ - 'foo': 'bar', - 'baz': 'bang' - })); - }); - - test("defaults to a text/plain media type", () { - var uri = new DataUri.decode("data:;base64,"); - expect(uri.mediaType.type, equals('text')); - expect(uri.mediaType.subtype, equals('plain')); - expect(uri.mediaType.parameters, equals({'charset': 'US-ASCII'})); - }); - - test("defaults to a text/plain media type with parameters", () { - var uri = new DataUri.decode("data:;foo=bar;base64,"); - expect(uri.mediaType.type, equals('text')); - expect(uri.mediaType.subtype, equals('plain')); - expect(uri.mediaType.parameters, equals({'foo': 'bar'})); - }); - - test("percent-decodes the media type and its parameters", () { - var uri = new DataUri.decode( - 'data:te%78t/ht%6Dl;f%6Fo=ba%2Cr;base64,'); - expect(uri.mediaType.type, equals('text')); - expect(uri.mediaType.subtype, equals('html')); - expect(uri.mediaType.parameters, equals({'foo': 'ba,r'})); - }); - - test("assumes the URI is UTF-8", () { - var uri = new DataUri.decode( - 'data:t%C3%ABxt/%D1%9Btml;f%C3%B8o=b%C3%A5r;base64,'); - expect(uri.mediaType.type, equals('tëxt')); - expect(uri.mediaType.subtype, equals('ћtml')); - expect(uri.mediaType.parameters, equals({'føo': 'bår'})); - }); - - test("allows a parameter named base64", () { - var uri = new DataUri.decode("data:;base64=no,foo"); - expect(uri.mediaType.parameters, equals({'base64': 'no'})); - expect(uri.data, equals([$f, $o, $o])); - }); - - test("includes the query", () { - var uri = new DataUri.decode("data:,a?b=c"); - expect(uri.data, equals([$a, $question, $b, $equal, $c])); - }); - - test("doesn't include the fragment", () { - var uri = new DataUri.decode("data:,a#b=c"); - expect(uri.data, equals([$a])); - }); - - test("supports the URL-safe base64 alphabet", () { - var uri = new DataUri.decode("data:;base64,-_8%3D"); - expect(uri.data, equals([0xFB, 0xFF])); - }); - - group("forbids", () { - test("a parameter with the wrong type", () { - expect(() => new DataUri.decode(12), throwsArgumentError); - }); - - test("a parameter with the wrong scheme", () { - expect(() => new DataUri.decode("http:;base64,"), throwsArgumentError); - }); - - test("non-token characters in invalid positions", () { - expect(() => new DataUri.decode("data:text//plain;base64,"), - throwsFormatException); - expect(() => new DataUri.decode("data:text/plain;;base64,"), - throwsFormatException); - expect(() => new DataUri.decode("data:text/plain;/base64,"), - throwsFormatException); - expect(() => new DataUri.decode("data:text/plain;,"), - throwsFormatException); - expect(() => new DataUri.decode("data:text/plain;base64;"), - throwsFormatException); - expect(() => new DataUri.decode("data:text/plain;foo=bar=baz;base64,"), - throwsFormatException); - }); - - test("encoded non-token characters in invalid positions", () { - expect(() => new DataUri.decode("data:te%2Cxt/plain;base64,"), - throwsFormatException); - expect(() => new DataUri.decode("data:text/pl%2Cain;base64,"), - throwsFormatException); - expect(() => new DataUri.decode("data:text/plain;f%2Coo=bar;base64,"), - throwsFormatException); - }); - }); - }); - - group("dataAsString", () { - test("decodes the data as ASCII by default", () { - var uri = new DataUri.decode("data:;base64,Zm9v"); - expect(uri.dataAsString(), equals("foo")); - - uri = new DataUri.decode("data:;base64,ZsO4bw=="); - expect(() => uri.dataAsString(), throwsFormatException); - }); - - test("decodes the data using the declared charset", () { - var uri = new DataUri.decode("data:;charset=iso-8859-1;base64,ZsO4bw=="); - expect(uri.dataAsString(), equals("føo")); - }); - - test("throws if the charset isn't supported", () { - var uri = new DataUri.decode("data:;charset=fblthp;base64,ZsO4bw=="); - expect(() => uri.dataAsString(), throwsUnsupportedError); - }); - - test("uses the given encoding in preference to the declared charset", () { - var uri = new DataUri.decode("data:;charset=fblthp;base64,ZsO4bw=="); - expect(uri.dataAsString(encoding: UTF8), equals("føo")); - - uri = new DataUri.decode("data:;charset=utf-8;base64,ZsO4bw=="); - expect(uri.dataAsString(encoding: LATIN1), equals("føo")); - }); - }); -} From b3a8581b5c3fd13af4c84f74977ad316d4d47a33 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 12 Jan 2016 17:21:10 -0800 Subject: [PATCH 026/126] Get rid of all the library tags. R=rnystrom@google.com Review URL: https://codereview.chromium.org//1580233004 . --- pkgs/http_parser/lib/http_parser.dart | 2 -- pkgs/http_parser/lib/src/authentication_challenge.dart | 2 -- pkgs/http_parser/lib/src/case_insensitive_map.dart | 2 -- pkgs/http_parser/lib/src/copy/bytes_builder.dart | 2 -- pkgs/http_parser/lib/src/copy/io_sink.dart | 2 -- pkgs/http_parser/lib/src/copy/web_socket.dart | 2 -- pkgs/http_parser/lib/src/copy/web_socket_impl.dart | 2 -- pkgs/http_parser/lib/src/http_date.dart | 2 -- pkgs/http_parser/lib/src/media_type.dart | 2 -- pkgs/http_parser/lib/src/scan.dart | 2 -- pkgs/http_parser/lib/src/web_socket.dart | 2 -- pkgs/http_parser/test/http_date_test.dart | 2 -- pkgs/http_parser/test/media_type_test.dart | 2 -- pkgs/http_parser/test/web_socket_test.dart | 2 -- 14 files changed, 28 deletions(-) diff --git a/pkgs/http_parser/lib/http_parser.dart b/pkgs/http_parser/lib/http_parser.dart index 77c75c0aa9..2af40522bf 100644 --- a/pkgs/http_parser/lib/http_parser.dart +++ b/pkgs/http_parser/lib/http_parser.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 http_parser; - export 'src/authentication_challenge.dart'; export 'src/case_insensitive_map.dart'; export 'src/http_date.dart'; diff --git a/pkgs/http_parser/lib/src/authentication_challenge.dart b/pkgs/http_parser/lib/src/authentication_challenge.dart index 773ee3b81b..c0b63ca809 100644 --- a/pkgs/http_parser/lib/src/authentication_challenge.dart +++ b/pkgs/http_parser/lib/src/authentication_challenge.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 http_paser.authentication_challenge; - import 'dart:collection'; import 'package:string_scanner/string_scanner.dart'; diff --git a/pkgs/http_parser/lib/src/case_insensitive_map.dart b/pkgs/http_parser/lib/src/case_insensitive_map.dart index 34e85b24af..870f84c042 100644 --- a/pkgs/http_parser/lib/src/case_insensitive_map.dart +++ b/pkgs/http_parser/lib/src/case_insensitive_map.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 http_parser.case_insensitive_map; - import 'package:collection/collection.dart'; /// A map from case-insensitive strings to values. diff --git a/pkgs/http_parser/lib/src/copy/bytes_builder.dart b/pkgs/http_parser/lib/src/copy/bytes_builder.dart index 4ab83b3695..39d44fe654 100644 --- a/pkgs/http_parser/lib/src/copy/bytes_builder.dart +++ b/pkgs/http_parser/lib/src/copy/bytes_builder.dart @@ -10,8 +10,6 @@ // // This is up-to-date as of sdk revision // 86227840d75d974feb238f8b3c59c038b99c05cf. -library http_parser.copy.bytes_builder; - import 'dart:math'; import 'dart:typed_data'; diff --git a/pkgs/http_parser/lib/src/copy/io_sink.dart b/pkgs/http_parser/lib/src/copy/io_sink.dart index a23b44b38a..0578bdb03b 100644 --- a/pkgs/http_parser/lib/src/copy/io_sink.dart +++ b/pkgs/http_parser/lib/src/copy/io_sink.dart @@ -10,8 +10,6 @@ // // This is up-to-date as of sdk revision // 86227840d75d974feb238f8b3c59c038b99c05cf. -library http_parser.copy.io_sink; - import 'dart:async'; class StreamSinkImpl implements StreamSink { diff --git a/pkgs/http_parser/lib/src/copy/web_socket.dart b/pkgs/http_parser/lib/src/copy/web_socket.dart index 8c34beada2..53460baa3e 100644 --- a/pkgs/http_parser/lib/src/copy/web_socket.dart +++ b/pkgs/http_parser/lib/src/copy/web_socket.dart @@ -10,8 +10,6 @@ // // This is up-to-date as of sdk revision // 86227840d75d974feb238f8b3c59c038b99c05cf. -library http_parser.copy.web_socket; - /** * Web socket status codes used when closing a web socket connection. */ diff --git a/pkgs/http_parser/lib/src/copy/web_socket_impl.dart b/pkgs/http_parser/lib/src/copy/web_socket_impl.dart index a53a2891d9..31b3efad63 100644 --- a/pkgs/http_parser/lib/src/copy/web_socket_impl.dart +++ b/pkgs/http_parser/lib/src/copy/web_socket_impl.dart @@ -11,8 +11,6 @@ // // This is up-to-date as of sdk revision // 86227840d75d974feb238f8b3c59c038b99c05cf. -library http_parser.copy.web_socket_impl; - import 'dart:async'; import 'dart:convert'; import 'dart:math'; diff --git a/pkgs/http_parser/lib/src/http_date.dart b/pkgs/http_parser/lib/src/http_date.dart index 959053649d..8751b78c8f 100644 --- a/pkgs/http_parser/lib/src/http_date.dart +++ b/pkgs/http_parser/lib/src/http_date.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 http_parser.http_date; - import 'package:string_scanner/string_scanner.dart'; import 'utils.dart'; diff --git a/pkgs/http_parser/lib/src/media_type.dart b/pkgs/http_parser/lib/src/media_type.dart index e15bccf2f5..6a040e0544 100644 --- a/pkgs/http_parser/lib/src/media_type.dart +++ b/pkgs/http_parser/lib/src/media_type.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 http_parser.media_type; - import 'package:collection/collection.dart'; import 'package:string_scanner/string_scanner.dart'; diff --git a/pkgs/http_parser/lib/src/scan.dart b/pkgs/http_parser/lib/src/scan.dart index d675b8b41c..3541038438 100644 --- a/pkgs/http_parser/lib/src/scan.dart +++ b/pkgs/http_parser/lib/src/scan.dart @@ -9,8 +9,6 @@ /// spec][spec]. /// /// [spec]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html -library http_parser.scan; - import 'package:string_scanner/string_scanner.dart'; /// An HTTP token. diff --git a/pkgs/http_parser/lib/src/web_socket.dart b/pkgs/http_parser/lib/src/web_socket.dart index e52c5f5290..a11840c09f 100644 --- a/pkgs/http_parser/lib/src/web_socket.dart +++ b/pkgs/http_parser/lib/src/web_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 http_parser.web_socket; - import 'dart:async'; import 'package:crypto/crypto.dart'; diff --git a/pkgs/http_parser/test/http_date_test.dart b/pkgs/http_parser/test/http_date_test.dart index c0a2537db6..02b7c6f8d1 100644 --- a/pkgs/http_parser/test/http_date_test.dart +++ b/pkgs/http_parser/test/http_date_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 http_parser.http_date_test; - import 'package:http_parser/http_parser.dart'; import 'package:test/test.dart'; diff --git a/pkgs/http_parser/test/media_type_test.dart b/pkgs/http_parser/test/media_type_test.dart index 98f482becd..01c58d4d9e 100644 --- a/pkgs/http_parser/test/media_type_test.dart +++ b/pkgs/http_parser/test/media_type_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 http_parser.media_type_test; - import 'package:http_parser/http_parser.dart'; import 'package:test/test.dart'; diff --git a/pkgs/http_parser/test/web_socket_test.dart b/pkgs/http_parser/test/web_socket_test.dart index 61bf0cc821..a8278b606c 100644 --- a/pkgs/http_parser/test/web_socket_test.dart +++ b/pkgs/http_parser/test/web_socket_test.dart @@ -4,8 +4,6 @@ @TestOn('vm') -library http_parser.web_socket_test; - import 'dart:io'; import 'package:http_parser/http_parser.dart'; From 2c4d4cc6959e7660f89e1e431ae1104b488351ab Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 28 Jan 2016 12:07:50 -0800 Subject: [PATCH 027/126] Add a WebSocketChannel class. This deprecates CompatibleWebSocket in favor of the new class. R=rnystrom@google.com Review URL: https://codereview.chromium.org//1646583003 . --- pkgs/http_parser/CHANGELOG.md | 7 + pkgs/http_parser/README.md | 6 +- pkgs/http_parser/lib/src/web_socket.dart | 111 +--------- .../lib/src/web_socket/channel.dart | 123 +++++++++++ .../lib/src/web_socket/deprecated.dart | 95 +++++++++ .../lib/src/web_socket/exception.dart | 16 ++ pkgs/http_parser/pubspec.yaml | 8 +- pkgs/http_parser/test/web_socket_test.dart | 192 +++++++++++++----- 8 files changed, 393 insertions(+), 165 deletions(-) create mode 100644 pkgs/http_parser/lib/src/web_socket/channel.dart create mode 100644 pkgs/http_parser/lib/src/web_socket/deprecated.dart create mode 100644 pkgs/http_parser/lib/src/web_socket/exception.dart diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index 9b54230677..cda67d2141 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,3 +1,10 @@ +## 2.1.0 + +* Added `WebSocketChannel`, an implementation of `StreamChannel` that's backed + by a `WebSocket`. + +* Deprecated `CompatibleWebSocket` in favor of `WebSocketChannel`. + ## 2.0.0 * Removed the `DataUri` class. It's redundant with the `Uri.data` getter that's diff --git a/pkgs/http_parser/README.md b/pkgs/http_parser/README.md index 1407c16d71..1bd5a90a0c 100644 --- a/pkgs/http_parser/README.md +++ b/pkgs/http_parser/README.md @@ -10,9 +10,9 @@ It includes: and `Content-Type` headers. This class supports both parsing and formatting media types according to [HTTP/1.1][2616]. -* A `CompatibleWebSocket` class that supports both the client and server sides - of the [WebSocket protocol][6455] independently of any specific server - implementation. +* A `WebSocketChannel` class that provides a `StreamChannel` interface for both + the client and server sides of the [WebSocket protocol][6455] independently of + any specific server implementation. [2616]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html [6455]: https://tools.ietf.org/html/rfc6455 diff --git a/pkgs/http_parser/lib/src/web_socket.dart b/pkgs/http_parser/lib/src/web_socket.dart index a11840c09f..edc0a185be 100644 --- a/pkgs/http_parser/lib/src/web_socket.dart +++ b/pkgs/http_parser/lib/src/web_socket.dart @@ -2,111 +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:crypto/crypto.dart'; - -import 'copy/web_socket_impl.dart'; - -/// An implementation of the WebSocket protocol that's not specific to "dart:io" -/// or to any particular HTTP API. -/// -/// Because this is HTTP-API-agnostic, it doesn't handle the initial [WebSocket -/// handshake][]. This needs to be handled manually by the user of the code. -/// Once that's been done, [new CompatibleWebSocket] can be called with the -/// underlying socket and it will handle the remainder of the protocol. -/// -/// [WebSocket handshake]: https://tools.ietf.org/html/rfc6455#section-4 -abstract class CompatibleWebSocket implements Stream, StreamSink { - /// The interval for sending ping signals. - /// - /// If a ping message is not answered by a pong message from the peer, the - /// `WebSocket` is assumed disconnected and the connection is closed with a - /// [WebSocketStatus.GOING_AWAY] close code. When a ping signal is sent, the - /// pong message must be received within [pingInterval]. - /// - /// There are never two outstanding pings at any given time, and the next ping - /// timer starts when the pong is received. - /// - /// By default, the [pingInterval] is `null`, indicating that ping messages - /// are disabled. - Duration pingInterval; - - /// The [close code][] set when the WebSocket connection is closed. - /// - /// [close code]: https://tools.ietf.org/html/rfc6455#section-7.1.5 - /// - /// Before the connection has been closed, this will be `null`. - int get closeCode; - - /// The [close reason][] set when the WebSocket connection is closed. - /// - /// [close reason]: https://tools.ietf.org/html/rfc6455#section-7.1.6 - /// - /// Before the connection has been closed, this will be `null`. - String get closeReason; - - /// Signs a `Sec-WebSocket-Key` header sent by a WebSocket client as part of - /// the [initial handshake]. - /// - /// The return value should be sent back to the client in a - /// `Sec-WebSocket-Accept` header. - /// - /// [initial handshake]: https://tools.ietf.org/html/rfc6455#section-4.2.2 - static String signKey(String key) { - var hash = new SHA1(); - // We use [codeUnits] here rather than UTF-8-decoding the string because - // [key] is expected to be base64 encoded, and so will be pure ASCII. - hash.add((key + webSocketGUID).codeUnits); - return CryptoUtils.bytesToBase64(hash.close()); - } - - /// Creates a new WebSocket handling messaging across an existing socket. - /// - /// Because this is HTTP-API-agnostic, the initial [WebSocket handshake][] - /// must have already been completed on the socket before this is called. - /// - /// If [stream] is also a [StreamSink] (for example, if it's a "dart:io" - /// `Socket`), it will be used for both sending and receiving data. Otherwise, - /// it will be used for receiving data and [sink] will be used for sending it. - /// - /// [protocol] should be the protocol negotiated by this handshake, if any. - /// - /// If this is a WebSocket server, [serverSide] should be `true` (the - /// default); if it's a client, [serverSide] should be `false`. - /// - /// [WebSocket handshake]: https://tools.ietf.org/html/rfc6455#section-4 - factory CompatibleWebSocket(Stream> stream, - {StreamSink> sink, String protocol, bool serverSide: true}) { - if (sink == null) { - if (stream is! StreamSink) { - throw new ArgumentError("If stream isn't also a StreamSink, sink must " - "be passed explicitly."); - } - sink = stream as StreamSink; - } - - return new WebSocketImpl.fromSocket(stream, sink, protocol, serverSide); - } - - /// Closes the web socket connection. - /// - /// [closeCode] and [closeReason] are the [close code][] and [reason][] sent - /// to the remote peer, respectively. If they are omitted, the peer will see - /// a "no status received" code with no reason. - /// - /// [close code]: https://tools.ietf.org/html/rfc6455#section-7.1.5 - /// [reason]: https://tools.ietf.org/html/rfc6455#section-7.1.6 - Future close([int closeCode, String closeReason]); -} - -/// An exception thrown by [CompatibleWebSocket]. -class CompatibleWebSocketException implements Exception { - final String message; - - CompatibleWebSocketException([this.message]); - - String toString() => message == null - ? "CompatibleWebSocketException" : - "CompatibleWebSocketException: $message"; -} +export 'web_socket/channel.dart'; +export 'web_socket/deprecated.dart'; +export 'web_socket/exception.dart'; diff --git a/pkgs/http_parser/lib/src/web_socket/channel.dart b/pkgs/http_parser/lib/src/web_socket/channel.dart new file mode 100644 index 0000000000..339f06513e --- /dev/null +++ b/pkgs/http_parser/lib/src/web_socket/channel.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. + +import 'dart:async'; + +import 'package:async/async.dart'; +import 'package:crypto/crypto.dart'; +import 'package:stream_channel/stream_channel.dart'; + +import '../copy/web_socket_impl.dart'; + +/// A [StreamChannel] implementation of the WebSocket protocol. +/// +/// This is not specific to `dart:io` or to any particular HTTP API. Because of +/// that, it doesn't handle the initial [WebSocket handshake][]. This needs to +/// be handled manually by the user of the code. Once that's been done, [new +/// WebSocketChannel] can be called with the underlying socket and it will +/// handle the remainder of the protocol. +/// +/// [WebSocket handshake]: https://tools.ietf.org/html/rfc6455#section-4 +class WebSocketChannel extends StreamChannelMixin { + /// The underlying web socket. + /// + /// This is essentially a copy of `dart:io`'s WebSocket implementation, with + /// the IO-specific pieces factored out. + final WebSocketImpl _webSocket; + + /// The interval for sending ping signals. + /// + /// If a ping message is not answered by a pong message from the peer, the + /// `WebSocket` is assumed disconnected and the connection is closed with a + /// [WebSocketStatus.GOING_AWAY] close code. When a ping signal is sent, the + /// pong message must be received within [pingInterval]. + /// + /// There are never two outstanding pings at any given time, and the next ping + /// timer starts when the pong is received. + /// + /// By default, the [pingInterval] is `null`, indicating that ping messages + /// are disabled. + Duration get pingInterval => _webSocket.pingInterval; + set pingInterval(Duration value) => _webSocket.pingInterval = value; + + /// The [close code][] set when the WebSocket connection is closed. + /// + /// [close code]: https://tools.ietf.org/html/rfc6455#section-7.1.5 + /// + /// Before the connection has been closed, this will be `null`. + int get closeCode => _webSocket.closeCode; + + /// The [close reason][] set when the WebSocket connection is closed. + /// + /// [close reason]: https://tools.ietf.org/html/rfc6455#section-7.1.6 + /// + /// Before the connection has been closed, this will be `null`. + String get closeReason => _webSocket.closeReason; + + Stream get stream => new StreamView(_webSocket); + + /// The sink for sending values to the other endpoint. + /// + /// This has additional arguments to [WebSocketSink.close] arguments that + /// provide the remote endpoint reasons for closing the connection. + WebSocketSink get sink => new WebSocketSink._(_webSocket); + + /// Signs a `Sec-WebSocket-Key` header sent by a WebSocket client as part of + /// the [initial handshake]. + /// + /// The return value should be sent back to the client in a + /// `Sec-WebSocket-Accept` header. + /// + /// [initial handshake]: https://tools.ietf.org/html/rfc6455#section-4.2.2 + static String signKey(String key) { + var hash = new SHA1(); + // We use [codeUnits] here rather than UTF-8-decoding the string because + // [key] is expected to be base64 encoded, and so will be pure ASCII. + hash.add((key + webSocketGUID).codeUnits); + return CryptoUtils.bytesToBase64(hash.close()); + } + + /// Creates a new WebSocket handling messaging across an existing socket. + /// + /// Because this is HTTP-API-agnostic, the initial [WebSocket handshake][] + /// must have already been completed on the socket before this is called. + /// + /// If [stream] is also a [StreamSink] (for example, if it's a "dart:io" + /// `Socket`), it will be used for both sending and receiving data. Otherwise, + /// it will be used for receiving data and [sink] will be used for sending it. + /// + /// [protocol] should be the protocol negotiated by this handshake, if any. + /// + /// If this is a WebSocket server, [serverSide] should be `true` (the + /// default); if it's a client, [serverSide] should be `false`. + /// + /// [WebSocket handshake]: https://tools.ietf.org/html/rfc6455#section-4 + WebSocketChannel(StreamChannel> channel, + {String protocol, bool serverSide: true}) + : _webSocket = new WebSocketImpl.fromSocket( + channel.stream, channel.sink, protocol, serverSide); +} + +/// The sink exposed by a [CompatibleWebSocket]. +/// +/// This is like a normal [StreamSink], except that it supports extra arguments +/// to [close]. +class WebSocketSink extends DelegatingStreamSink { + final WebSocketImpl _webSocket; + + WebSocketSink._(WebSocketImpl webSocket) + : super(webSocket), + _webSocket = webSocket; + + /// Closes the web socket connection. + /// + /// [closeCode] and [closeReason] are the [close code][] and [reason][] sent + /// to the remote peer, respectively. If they are omitted, the peer will see + /// a "no status received" code with no reason. + /// + /// [close code]: https://tools.ietf.org/html/rfc6455#section-7.1.5 + /// [reason]: https://tools.ietf.org/html/rfc6455#section-7.1.6 + Future close([int closeCode, String closeReason]) => + _webSocket.close(closeCode, closeReason); +} diff --git a/pkgs/http_parser/lib/src/web_socket/deprecated.dart b/pkgs/http_parser/lib/src/web_socket/deprecated.dart new file mode 100644 index 0000000000..c2162d7be9 --- /dev/null +++ b/pkgs/http_parser/lib/src/web_socket/deprecated.dart @@ -0,0 +1,95 @@ +// Copyright (c) 2014, 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:crypto/crypto.dart'; + +import '../copy/web_socket_impl.dart'; +import 'channel.dart'; + +/// Use [WebSocketChannel] instead. +@Deprecated("Will be removed in 3.0.0.") +abstract class CompatibleWebSocket implements Stream, StreamSink { + /// The interval for sending ping signals. + /// + /// If a ping message is not answered by a pong message from the peer, the + /// `WebSocket` is assumed disconnected and the connection is closed with a + /// [WebSocketStatus.GOING_AWAY] close code. When a ping signal is sent, the + /// pong message must be received within [pingInterval]. + /// + /// There are never two outstanding pings at any given time, and the next ping + /// timer starts when the pong is received. + /// + /// By default, the [pingInterval] is `null`, indicating that ping messages + /// are disabled. + Duration pingInterval; + + /// The [close code][] set when the WebSocket connection is closed. + /// + /// [close code]: https://tools.ietf.org/html/rfc6455#section-7.1.5 + /// + /// Before the connection has been closed, this will be `null`. + int get closeCode; + + /// The [close reason][] set when the WebSocket connection is closed. + /// + /// [close reason]: https://tools.ietf.org/html/rfc6455#section-7.1.6 + /// + /// Before the connection has been closed, this will be `null`. + String get closeReason; + + /// Signs a `Sec-WebSocket-Key` header sent by a WebSocket client as part of + /// the [initial handshake]. + /// + /// The return value should be sent back to the client in a + /// `Sec-WebSocket-Accept` header. + /// + /// [initial handshake]: https://tools.ietf.org/html/rfc6455#section-4.2.2 + static String signKey(String key) { + var hash = new SHA1(); + // We use [codeUnits] here rather than UTF-8-decoding the string because + // [key] is expected to be base64 encoded, and so will be pure ASCII. + hash.add((key + webSocketGUID).codeUnits); + return CryptoUtils.bytesToBase64(hash.close()); + } + + /// Creates a new WebSocket handling messaging across an existing socket. + /// + /// Because this is HTTP-API-agnostic, the initial [WebSocket handshake][] + /// must have already been completed on the socket before this is called. + /// + /// If [stream] is also a [StreamSink] (for example, if it's a "dart:io" + /// `Socket`), it will be used for both sending and receiving data. Otherwise, + /// it will be used for receiving data and [sink] will be used for sending it. + /// + /// [protocol] should be the protocol negotiated by this handshake, if any. + /// + /// If this is a WebSocket server, [serverSide] should be `true` (the + /// default); if it's a client, [serverSide] should be `false`. + /// + /// [WebSocket handshake]: https://tools.ietf.org/html/rfc6455#section-4 + factory CompatibleWebSocket(Stream> stream, + {StreamSink> sink, String protocol, bool serverSide: true}) { + if (sink == null) { + if (stream is! StreamSink) { + throw new ArgumentError("If stream isn't also a StreamSink, sink must " + "be passed explicitly."); + } + sink = stream as StreamSink; + } + + return new WebSocketImpl.fromSocket(stream, sink, protocol, serverSide); + } + + /// Closes the web socket connection. + /// + /// [closeCode] and [closeReason] are the [close code][] and [reason][] sent + /// to the remote peer, respectively. If they are omitted, the peer will see + /// a "no status received" code with no reason. + /// + /// [close code]: https://tools.ietf.org/html/rfc6455#section-7.1.5 + /// [reason]: https://tools.ietf.org/html/rfc6455#section-7.1.6 + Future close([int closeCode, String closeReason]); +} diff --git a/pkgs/http_parser/lib/src/web_socket/exception.dart b/pkgs/http_parser/lib/src/web_socket/exception.dart new file mode 100644 index 0000000000..87f61d8911 --- /dev/null +++ b/pkgs/http_parser/lib/src/web_socket/exception.dart @@ -0,0 +1,16 @@ +// 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. + +import 'channel.dart'; + +/// An exception thrown by [WebSocketChannel]. +class CompatibleWebSocketException implements Exception { + final String message; + + CompatibleWebSocketException([this.message]); + + String toString() => message == null + ? "CompatibleWebSocketException" : + "CompatibleWebSocketException: $message"; +} diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index 930db8f2db..15ad358b50 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,15 +1,21 @@ name: http_parser -version: 2.0.0 +version: 2.1.0-dev author: "Dart Team " homepage: https://github.com/dart-lang/http_parser description: > A platform-independent package for parsing and serializing HTTP formats. dependencies: + async: "^1.3.0" collection: ">=0.9.1 <2.0.0" crypto: "^0.9.0" source_span: "^1.0.0" + stream_channel: "^1.0.0" string_scanner: ">=0.0.0 <0.2.0" dev_dependencies: test: "^0.12.0" environment: sdk: ">=1.8.0 <2.0.0" + +dependency_overrides: + stream_channel: + git: git://github.com/dart-lang/stream_channel.git diff --git a/pkgs/http_parser/test/web_socket_test.dart b/pkgs/http_parser/test/web_socket_test.dart index a8278b606c..53aa5be415 100644 --- a/pkgs/http_parser/test/web_socket_test.dart +++ b/pkgs/http_parser/test/web_socket_test.dart @@ -7,11 +7,13 @@ import 'dart:io'; import 'package:http_parser/http_parser.dart'; +import 'package:stream_channel/stream_channel.dart'; import 'package:test/test.dart'; void main() { - test("a client can communicate with a WebSocket server", () { - return HttpServer.bind("localhost", 0).then((server) { + group("using WebSocketChannel", () { + test("a client can communicate with a WebSocket server", () async { + var server = await HttpServer.bind("localhost", 0); server.transform(new WebSocketTransformer()).listen((webSocket) { webSocket.add("hello!"); webSocket.listen((request) { @@ -22,39 +24,38 @@ void main() { }); var client = new HttpClient(); - return client - .openUrl("GET", Uri.parse("http://localhost:${server.port}")) - .then((request) { - request.headers - ..set("Connection", "Upgrade") - ..set("Upgrade", "websocket") - ..set("Sec-WebSocket-Key", "x3JJHMbDL1EzLkh9GBhXDw==") - ..set("Sec-WebSocket-Version", "13"); - return request.close(); - }).then((response) => response.detachSocket()).then((socket) { - var webSocket = new CompatibleWebSocket(socket, serverSide: false); - - var n = 0; - return webSocket.listen((message) { - if (n == 0) { - expect(message, equals("hello!")); - webSocket.add("ping"); - } else if (n == 1) { - expect(message, equals("pong")); - webSocket.close(); - server.close(); - } else { - fail("Only expected two messages."); - } - n++; - }).asFuture(); - }); + var request = await client.openUrl( + "GET", Uri.parse("http://localhost:${server.port}")); + request.headers + ..set("Connection", "Upgrade") + ..set("Upgrade", "websocket") + ..set("Sec-WebSocket-Key", "x3JJHMbDL1EzLkh9GBhXDw==") + ..set("Sec-WebSocket-Version", "13"); + + var response = await request.close(); + var socket = await response.detachSocket(); + var innerChannel = new StreamChannel(socket, socket); + var webSocket = new WebSocketChannel(innerChannel, serverSide: false); + + var n = 0; + await webSocket.stream.listen((message) { + if (n == 0) { + expect(message, equals("hello!")); + webSocket.sink.add("ping"); + } else if (n == 1) { + expect(message, equals("pong")); + webSocket.sink.close(); + server.close(); + } else { + fail("Only expected two messages."); + } + n++; + }).asFuture(); }); - }); - test("a server can communicate with a WebSocket client", () { - return HttpServer.bind("localhost", 0).then((server) { - server.listen((request) { + test("a server can communicate with a WebSocket client", () async { + var server = await HttpServer.bind("localhost", 0); + server.listen((request) async { var response = request.response; response.statusCode = 101; response.headers @@ -63,34 +64,119 @@ void main() { ..set("Sec-WebSocket-Accept", CompatibleWebSocket .signKey(request.headers.value('Sec-WebSocket-Key'))); response.contentLength = 0; - response.detachSocket().then((socket) { - var webSocket = new CompatibleWebSocket(socket); + + var socket = await response.detachSocket(); + var innerChannel = new StreamChannel(socket, socket); + var webSocket = new WebSocketChannel(innerChannel); + webSocket.sink.add("hello!"); + + var message = await webSocket.stream.first; + expect(message, equals("ping")); + webSocket.sink.add("pong"); + webSocket.sink.close(); + }); + + var webSocket = await WebSocket.connect('ws://localhost:${server.port}'); + var n = 0; + await webSocket.listen((message) { + if (n == 0) { + expect(message, equals("hello!")); + webSocket.add("ping"); + } else if (n == 1) { + expect(message, equals("pong")); + webSocket.close(); + server.close(); + } else { + fail("Only expected two messages."); + } + n++; + }).asFuture(); + }); + }); + + group("using CompatibleWebSocket", () { + test("a client can communicate with a WebSocket server", () { + return HttpServer.bind("localhost", 0).then((server) { + server.transform(new WebSocketTransformer()).listen((webSocket) { webSocket.add("hello!"); - webSocket.first.then((request) { + webSocket.listen((request) { expect(request, equals("ping")); webSocket.add("pong"); webSocket.close(); }); }); + + var client = new HttpClient(); + return client + .openUrl("GET", Uri.parse("http://localhost:${server.port}")) + .then((request) { + request.headers + ..set("Connection", "Upgrade") + ..set("Upgrade", "websocket") + ..set("Sec-WebSocket-Key", "x3JJHMbDL1EzLkh9GBhXDw==") + ..set("Sec-WebSocket-Version", "13"); + return request.close(); + }).then((response) => response.detachSocket()).then((socket) { + var webSocket = new CompatibleWebSocket(socket, serverSide: false); + + var n = 0; + return webSocket.listen((message) { + if (n == 0) { + expect(message, equals("hello!")); + webSocket.add("ping"); + } else if (n == 1) { + expect(message, equals("pong")); + webSocket.close(); + server.close(); + } else { + fail("Only expected two messages."); + } + n++; + }).asFuture(); + }); }); + }); - return WebSocket - .connect('ws://localhost:${server.port}') - .then((webSocket) { - var n = 0; - return webSocket.listen((message) { - if (n == 0) { - expect(message, equals("hello!")); - webSocket.add("ping"); - } else if (n == 1) { - expect(message, equals("pong")); - webSocket.close(); - server.close(); - } else { - fail("Only expected two messages."); - } - n++; - }).asFuture(); + test("a server can communicate with a WebSocket client", () { + return HttpServer.bind("localhost", 0).then((server) { + server.listen((request) { + var response = request.response; + response.statusCode = 101; + response.headers + ..set("Connection", "Upgrade") + ..set("Upgrade", "websocket") + ..set("Sec-WebSocket-Accept", CompatibleWebSocket + .signKey(request.headers.value('Sec-WebSocket-Key'))); + response.contentLength = 0; + response.detachSocket().then((socket) { + var webSocket = new CompatibleWebSocket(socket); + webSocket.add("hello!"); + webSocket.first.then((request) { + expect(request, equals("ping")); + webSocket.add("pong"); + webSocket.close(); + }); + }); + }); + + return WebSocket + .connect('ws://localhost:${server.port}') + .then((webSocket) { + var n = 0; + return webSocket.listen((message) { + if (n == 0) { + expect(message, equals("hello!")); + webSocket.add("ping"); + } else if (n == 1) { + expect(message, equals("pong")); + webSocket.close(); + server.close(); + } else { + fail("Only expected two messages."); + } + n++; + }).asFuture(); + }); }); }); }); From 0af1e7b084ce047162c42682be86ed9e0f0cf181 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 28 Jan 2016 13:39:59 -0800 Subject: [PATCH 028/126] Release 2.1.0. R=rnystrom@google.com Review URL: https://codereview.chromium.org//1641323003 . --- pkgs/http_parser/pubspec.yaml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index 15ad358b50..40da5583ff 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,5 +1,5 @@ name: http_parser -version: 2.1.0-dev +version: 2.1.0 author: "Dart Team " homepage: https://github.com/dart-lang/http_parser description: > @@ -15,7 +15,3 @@ dev_dependencies: test: "^0.12.0" environment: sdk: ">=1.8.0 <2.0.0" - -dependency_overrides: - stream_channel: - git: git://github.com/dart-lang/stream_channel.git From e2d654fb46af8e321f71ac06fedfce4063a398bb Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 3 Mar 2016 12:06:43 -0800 Subject: [PATCH 029/126] Deprecate WebSocketChannel. R=kevmoo@google.com Review URL: https://codereview.chromium.org//1763583003 . --- pkgs/http_parser/CHANGELOG.md | 8 ++++++++ pkgs/http_parser/lib/src/web_socket.dart | 2 +- .../lib/src/web_socket/channel.dart | 19 +++++++++---------- .../{deprecated.dart => compatible.dart} | 7 +++++-- .../lib/src/web_socket/exception.dart | 9 ++++++--- pkgs/http_parser/pubspec.yaml | 2 +- 6 files changed, 30 insertions(+), 17 deletions(-) rename pkgs/http_parser/lib/src/web_socket/{deprecated.dart => compatible.dart} (95%) diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index cda67d2141..341ba0bbf5 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,3 +1,11 @@ +## 2.2.0 + +* `WebSocketChannel` has been moved to + [the `web_socket_channel` package][web_socket_channel]. The implementation + here is now deprecated. + +[web_socket_channel]: https://pub.dartlang.org/packages/web_socket_channel + ## 2.1.0 * Added `WebSocketChannel`, an implementation of `StreamChannel` that's backed diff --git a/pkgs/http_parser/lib/src/web_socket.dart b/pkgs/http_parser/lib/src/web_socket.dart index edc0a185be..dee3edfe6e 100644 --- a/pkgs/http_parser/lib/src/web_socket.dart +++ b/pkgs/http_parser/lib/src/web_socket.dart @@ -3,5 +3,5 @@ // BSD-style license that can be found in the LICENSE file. export 'web_socket/channel.dart'; -export 'web_socket/deprecated.dart'; +export 'web_socket/compatible.dart'; export 'web_socket/exception.dart'; diff --git a/pkgs/http_parser/lib/src/web_socket/channel.dart b/pkgs/http_parser/lib/src/web_socket/channel.dart index 339f06513e..fd1e3c5aae 100644 --- a/pkgs/http_parser/lib/src/web_socket/channel.dart +++ b/pkgs/http_parser/lib/src/web_socket/channel.dart @@ -10,15 +10,12 @@ import 'package:stream_channel/stream_channel.dart'; import '../copy/web_socket_impl.dart'; -/// A [StreamChannel] implementation of the WebSocket protocol. +/// This class is deprecated. /// -/// This is not specific to `dart:io` or to any particular HTTP API. Because of -/// that, it doesn't handle the initial [WebSocket handshake][]. This needs to -/// be handled manually by the user of the code. Once that's been done, [new -/// WebSocketChannel] can be called with the underlying socket and it will -/// handle the remainder of the protocol. +/// Use the [`web_socket_channel`][web_socket_channel] package instead. /// -/// [WebSocket handshake]: https://tools.ietf.org/html/rfc6455#section-4 +/// [web_socket_channel]: https://pub.dartlang.org/packages/web_socket_channel +@Deprecated("Will be removed in 3.0.0.") class WebSocketChannel extends StreamChannelMixin { /// The underlying web socket. /// @@ -99,10 +96,12 @@ class WebSocketChannel extends StreamChannelMixin { channel.stream, channel.sink, protocol, serverSide); } -/// The sink exposed by a [CompatibleWebSocket]. +/// This class is deprecated. /// -/// This is like a normal [StreamSink], except that it supports extra arguments -/// to [close]. +/// Use the [`web_socket_channel`][web_socket_channel] package instead. +/// +/// [web_socket_channel]: https://pub.dartlang.org/packages/web_socket_channel +@Deprecated("Will be removed in 3.0.0.") class WebSocketSink extends DelegatingStreamSink { final WebSocketImpl _webSocket; diff --git a/pkgs/http_parser/lib/src/web_socket/deprecated.dart b/pkgs/http_parser/lib/src/web_socket/compatible.dart similarity index 95% rename from pkgs/http_parser/lib/src/web_socket/deprecated.dart rename to pkgs/http_parser/lib/src/web_socket/compatible.dart index c2162d7be9..35e7d0e364 100644 --- a/pkgs/http_parser/lib/src/web_socket/deprecated.dart +++ b/pkgs/http_parser/lib/src/web_socket/compatible.dart @@ -7,9 +7,12 @@ import 'dart:async'; import 'package:crypto/crypto.dart'; import '../copy/web_socket_impl.dart'; -import 'channel.dart'; -/// Use [WebSocketChannel] instead. +/// This class is deprecated. +/// +/// Use the [`web_socket_channel`][web_socket_channel] package instead. +/// +/// [web_socket_channel]: https://pub.dartlang.org/packages/web_socket_channel @Deprecated("Will be removed in 3.0.0.") abstract class CompatibleWebSocket implements Stream, StreamSink { /// The interval for sending ping signals. diff --git a/pkgs/http_parser/lib/src/web_socket/exception.dart b/pkgs/http_parser/lib/src/web_socket/exception.dart index 87f61d8911..c1a6473925 100644 --- a/pkgs/http_parser/lib/src/web_socket/exception.dart +++ b/pkgs/http_parser/lib/src/web_socket/exception.dart @@ -2,9 +2,12 @@ // 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 'channel.dart'; - -/// An exception thrown by [WebSocketChannel]. +/// This class is deprecated. +/// +/// Use the [`web_socket_channel`][web_socket_channel] package instead. +/// +/// [web_socket_channel]: https://pub.dartlang.org/packages/web_socket_channel +@Deprecated("Will be removed in 3.0.0.") class CompatibleWebSocketException implements Exception { final String message; diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index 40da5583ff..a572172813 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,5 +1,5 @@ name: http_parser -version: 2.1.0 +version: 2.2.0 author: "Dart Team " homepage: https://github.com/dart-lang/http_parser description: > From 75d86257f5e32e42ad726d9824f0034963ab2aa4 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 23 Mar 2016 13:53:41 -0700 Subject: [PATCH 030/126] Add support for crypto 1.0.0. R=kevmoo@google.com Review URL: https://codereview.chromium.org//1823303002 . --- pkgs/http_parser/CHANGELOG.md | 4 ++++ pkgs/http_parser/lib/src/web_socket/channel.dart | 5 ++--- pkgs/http_parser/lib/src/web_socket/compatible.dart | 5 ++--- pkgs/http_parser/pubspec.yaml | 4 ++-- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index 341ba0bbf5..bb1b79ec5d 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.2.1 + +* Add support for `crypto` 1.0.0. + ## 2.2.0 * `WebSocketChannel` has been moved to diff --git a/pkgs/http_parser/lib/src/web_socket/channel.dart b/pkgs/http_parser/lib/src/web_socket/channel.dart index fd1e3c5aae..d2a6674e1d 100644 --- a/pkgs/http_parser/lib/src/web_socket/channel.dart +++ b/pkgs/http_parser/lib/src/web_socket/channel.dart @@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import 'dart:async'; +import 'dart:convert'; import 'package:async/async.dart'; import 'package:crypto/crypto.dart'; @@ -68,11 +69,9 @@ class WebSocketChannel extends StreamChannelMixin { /// /// [initial handshake]: https://tools.ietf.org/html/rfc6455#section-4.2.2 static String signKey(String key) { - var hash = new SHA1(); // We use [codeUnits] here rather than UTF-8-decoding the string because // [key] is expected to be base64 encoded, and so will be pure ASCII. - hash.add((key + webSocketGUID).codeUnits); - return CryptoUtils.bytesToBase64(hash.close()); + return BASE64.encode(sha1.convert((key + webSocketGUID).codeUnits).bytes); } /// Creates a new WebSocket handling messaging across an existing socket. diff --git a/pkgs/http_parser/lib/src/web_socket/compatible.dart b/pkgs/http_parser/lib/src/web_socket/compatible.dart index 35e7d0e364..862f6d6e53 100644 --- a/pkgs/http_parser/lib/src/web_socket/compatible.dart +++ b/pkgs/http_parser/lib/src/web_socket/compatible.dart @@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import 'dart:async'; +import 'dart:convert'; import 'package:crypto/crypto.dart'; @@ -51,11 +52,9 @@ abstract class CompatibleWebSocket implements Stream, StreamSink { /// /// [initial handshake]: https://tools.ietf.org/html/rfc6455#section-4.2.2 static String signKey(String key) { - var hash = new SHA1(); // We use [codeUnits] here rather than UTF-8-decoding the string because // [key] is expected to be base64 encoded, and so will be pure ASCII. - hash.add((key + webSocketGUID).codeUnits); - return CryptoUtils.bytesToBase64(hash.close()); + return BASE64.encode(sha1.convert((key + webSocketGUID).codeUnits).bytes); } /// Creates a new WebSocket handling messaging across an existing socket. diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index a572172813..804d951588 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,5 +1,5 @@ name: http_parser -version: 2.2.0 +version: 2.2.1 author: "Dart Team " homepage: https://github.com/dart-lang/http_parser description: > @@ -7,7 +7,7 @@ description: > dependencies: async: "^1.3.0" collection: ">=0.9.1 <2.0.0" - crypto: "^0.9.0" + crypto: ">=0.9.2 <2.0.0" source_span: "^1.0.0" stream_channel: "^1.0.0" string_scanner: ">=0.0.0 <0.2.0" From 076e71910f646068286d219fd6c0cebe18cf4adc Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 4 May 2016 17:45:54 -0700 Subject: [PATCH 031/126] Remove deprecated APIs. This also makes the package strong-mode clean. R=kevmoo@google.com Review URL: https://codereview.chromium.org//1950183003 . --- pkgs/http_parser/.analysis_options | 2 + pkgs/http_parser/CHANGELOG.md | 8 + pkgs/http_parser/lib/http_parser.dart | 1 - .../lib/src/authentication_challenge.dart | 4 +- pkgs/http_parser/lib/src/media_type.dart | 2 +- pkgs/http_parser/lib/src/scan.dart | 4 +- pkgs/http_parser/lib/src/utils.dart | 2 +- pkgs/http_parser/lib/src/web_socket.dart | 7 - .../lib/src/web_socket/channel.dart | 121 ------------ .../lib/src/web_socket/compatible.dart | 97 ---------- .../lib/src/web_socket/exception.dart | 19 -- pkgs/http_parser/pubspec.yaml | 2 +- pkgs/http_parser/test/web_socket_test.dart | 183 ------------------ 13 files changed, 17 insertions(+), 435 deletions(-) create mode 100644 pkgs/http_parser/.analysis_options delete mode 100644 pkgs/http_parser/lib/src/web_socket.dart delete mode 100644 pkgs/http_parser/lib/src/web_socket/channel.dart delete mode 100644 pkgs/http_parser/lib/src/web_socket/compatible.dart delete mode 100644 pkgs/http_parser/lib/src/web_socket/exception.dart delete mode 100644 pkgs/http_parser/test/web_socket_test.dart diff --git a/pkgs/http_parser/.analysis_options b/pkgs/http_parser/.analysis_options new file mode 100644 index 0000000000..a10d4c5a05 --- /dev/null +++ b/pkgs/http_parser/.analysis_options @@ -0,0 +1,2 @@ +analyzer: + strong-mode: true diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index bb1b79ec5d..756c5bcf89 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,3 +1,11 @@ +## 3.0.0 + +* All deprecated APIs have been removed. No new APIs have been added. Packages + that would use 3.0.0 as a lower bound should use 2.2.0 instead—for example, + `http_parser: ">=2.2.0 <4.0.0"`. + +* Fix all strong-mode warnings. + ## 2.2.1 * Add support for `crypto` 1.0.0. diff --git a/pkgs/http_parser/lib/http_parser.dart b/pkgs/http_parser/lib/http_parser.dart index 2af40522bf..684c0b5fd3 100644 --- a/pkgs/http_parser/lib/http_parser.dart +++ b/pkgs/http_parser/lib/http_parser.dart @@ -6,4 +6,3 @@ export 'src/authentication_challenge.dart'; export 'src/case_insensitive_map.dart'; export 'src/http_date.dart'; export 'src/media_type.dart'; -export 'src/web_socket.dart'; diff --git a/pkgs/http_parser/lib/src/authentication_challenge.dart b/pkgs/http_parser/lib/src/authentication_challenge.dart index c0b63ca809..66841b4c8c 100644 --- a/pkgs/http_parser/lib/src/authentication_challenge.dart +++ b/pkgs/http_parser/lib/src/authentication_challenge.dart @@ -42,7 +42,7 @@ class AuthenticationChallenge { // Manually parse the inner list. We need to do some lookahead to // disambiguate between an auth param and another challenge. - var params = {}; + var params = {}; // Consume initial empty values. while (scanner.scan(",")) { @@ -99,7 +99,7 @@ class AuthenticationChallenge { scanner.scan(whitespace); var scheme = _scanScheme(scanner); - var params = {}; + var params = {}; parseList(scanner, () => _scanAuthParam(scanner, params)); scanner.expectDone(); diff --git a/pkgs/http_parser/lib/src/media_type.dart b/pkgs/http_parser/lib/src/media_type.dart index 6a040e0544..37dd6be1fb 100644 --- a/pkgs/http_parser/lib/src/media_type.dart +++ b/pkgs/http_parser/lib/src/media_type.dart @@ -53,7 +53,7 @@ class MediaType { var subtype = scanner.lastMatch[0]; scanner.scan(whitespace); - var parameters = {}; + var parameters = {}; while (scanner.scan(';')) { scanner.scan(whitespace); scanner.expect(token); diff --git a/pkgs/http_parser/lib/src/scan.dart b/pkgs/http_parser/lib/src/scan.dart index 3541038438..9af8428a21 100644 --- a/pkgs/http_parser/lib/src/scan.dart +++ b/pkgs/http_parser/lib/src/scan.dart @@ -37,8 +37,8 @@ final whitespace = new RegExp("(?:${_lws.pattern})*"); /// /// Once this is finished, [scanner] will be at the next non-LWS character in /// the string, or the end of the string. -List parseList(StringScanner scanner, parseElement()) { - var result = []; +List/**/ parseList/**/(StringScanner scanner, /*=T*/ parseElement()) { + var result = /**/[]; // Consume initial empty values. while (scanner.scan(",")) { diff --git a/pkgs/http_parser/lib/src/utils.dart b/pkgs/http_parser/lib/src/utils.dart index 4dcee19880..79ce448682 100644 --- a/pkgs/http_parser/lib/src/utils.dart +++ b/pkgs/http_parser/lib/src/utils.dart @@ -8,7 +8,7 @@ import 'package:source_span/source_span.dart'; /// /// [name] should describe the type of thing being parsed, and [value] should be /// its actual value. -wrapFormatException(String name, String value, body()) { +/*=T*/ wrapFormatException/**/(String name, String value, /*=T*/ body()) { try { return body(); } on SourceSpanFormatException catch (error) { diff --git a/pkgs/http_parser/lib/src/web_socket.dart b/pkgs/http_parser/lib/src/web_socket.dart deleted file mode 100644 index dee3edfe6e..0000000000 --- a/pkgs/http_parser/lib/src/web_socket.dart +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) 2014, 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. - -export 'web_socket/channel.dart'; -export 'web_socket/compatible.dart'; -export 'web_socket/exception.dart'; diff --git a/pkgs/http_parser/lib/src/web_socket/channel.dart b/pkgs/http_parser/lib/src/web_socket/channel.dart deleted file mode 100644 index d2a6674e1d..0000000000 --- a/pkgs/http_parser/lib/src/web_socket/channel.dart +++ /dev/null @@ -1,121 +0,0 @@ -// 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. - -import 'dart:async'; -import 'dart:convert'; - -import 'package:async/async.dart'; -import 'package:crypto/crypto.dart'; -import 'package:stream_channel/stream_channel.dart'; - -import '../copy/web_socket_impl.dart'; - -/// This class is deprecated. -/// -/// Use the [`web_socket_channel`][web_socket_channel] package instead. -/// -/// [web_socket_channel]: https://pub.dartlang.org/packages/web_socket_channel -@Deprecated("Will be removed in 3.0.0.") -class WebSocketChannel extends StreamChannelMixin { - /// The underlying web socket. - /// - /// This is essentially a copy of `dart:io`'s WebSocket implementation, with - /// the IO-specific pieces factored out. - final WebSocketImpl _webSocket; - - /// The interval for sending ping signals. - /// - /// If a ping message is not answered by a pong message from the peer, the - /// `WebSocket` is assumed disconnected and the connection is closed with a - /// [WebSocketStatus.GOING_AWAY] close code. When a ping signal is sent, the - /// pong message must be received within [pingInterval]. - /// - /// There are never two outstanding pings at any given time, and the next ping - /// timer starts when the pong is received. - /// - /// By default, the [pingInterval] is `null`, indicating that ping messages - /// are disabled. - Duration get pingInterval => _webSocket.pingInterval; - set pingInterval(Duration value) => _webSocket.pingInterval = value; - - /// The [close code][] set when the WebSocket connection is closed. - /// - /// [close code]: https://tools.ietf.org/html/rfc6455#section-7.1.5 - /// - /// Before the connection has been closed, this will be `null`. - int get closeCode => _webSocket.closeCode; - - /// The [close reason][] set when the WebSocket connection is closed. - /// - /// [close reason]: https://tools.ietf.org/html/rfc6455#section-7.1.6 - /// - /// Before the connection has been closed, this will be `null`. - String get closeReason => _webSocket.closeReason; - - Stream get stream => new StreamView(_webSocket); - - /// The sink for sending values to the other endpoint. - /// - /// This has additional arguments to [WebSocketSink.close] arguments that - /// provide the remote endpoint reasons for closing the connection. - WebSocketSink get sink => new WebSocketSink._(_webSocket); - - /// Signs a `Sec-WebSocket-Key` header sent by a WebSocket client as part of - /// the [initial handshake]. - /// - /// The return value should be sent back to the client in a - /// `Sec-WebSocket-Accept` header. - /// - /// [initial handshake]: https://tools.ietf.org/html/rfc6455#section-4.2.2 - static String signKey(String key) { - // We use [codeUnits] here rather than UTF-8-decoding the string because - // [key] is expected to be base64 encoded, and so will be pure ASCII. - return BASE64.encode(sha1.convert((key + webSocketGUID).codeUnits).bytes); - } - - /// Creates a new WebSocket handling messaging across an existing socket. - /// - /// Because this is HTTP-API-agnostic, the initial [WebSocket handshake][] - /// must have already been completed on the socket before this is called. - /// - /// If [stream] is also a [StreamSink] (for example, if it's a "dart:io" - /// `Socket`), it will be used for both sending and receiving data. Otherwise, - /// it will be used for receiving data and [sink] will be used for sending it. - /// - /// [protocol] should be the protocol negotiated by this handshake, if any. - /// - /// If this is a WebSocket server, [serverSide] should be `true` (the - /// default); if it's a client, [serverSide] should be `false`. - /// - /// [WebSocket handshake]: https://tools.ietf.org/html/rfc6455#section-4 - WebSocketChannel(StreamChannel> channel, - {String protocol, bool serverSide: true}) - : _webSocket = new WebSocketImpl.fromSocket( - channel.stream, channel.sink, protocol, serverSide); -} - -/// This class is deprecated. -/// -/// Use the [`web_socket_channel`][web_socket_channel] package instead. -/// -/// [web_socket_channel]: https://pub.dartlang.org/packages/web_socket_channel -@Deprecated("Will be removed in 3.0.0.") -class WebSocketSink extends DelegatingStreamSink { - final WebSocketImpl _webSocket; - - WebSocketSink._(WebSocketImpl webSocket) - : super(webSocket), - _webSocket = webSocket; - - /// Closes the web socket connection. - /// - /// [closeCode] and [closeReason] are the [close code][] and [reason][] sent - /// to the remote peer, respectively. If they are omitted, the peer will see - /// a "no status received" code with no reason. - /// - /// [close code]: https://tools.ietf.org/html/rfc6455#section-7.1.5 - /// [reason]: https://tools.ietf.org/html/rfc6455#section-7.1.6 - Future close([int closeCode, String closeReason]) => - _webSocket.close(closeCode, closeReason); -} diff --git a/pkgs/http_parser/lib/src/web_socket/compatible.dart b/pkgs/http_parser/lib/src/web_socket/compatible.dart deleted file mode 100644 index 862f6d6e53..0000000000 --- a/pkgs/http_parser/lib/src/web_socket/compatible.dart +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) 2014, 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 'package:crypto/crypto.dart'; - -import '../copy/web_socket_impl.dart'; - -/// This class is deprecated. -/// -/// Use the [`web_socket_channel`][web_socket_channel] package instead. -/// -/// [web_socket_channel]: https://pub.dartlang.org/packages/web_socket_channel -@Deprecated("Will be removed in 3.0.0.") -abstract class CompatibleWebSocket implements Stream, StreamSink { - /// The interval for sending ping signals. - /// - /// If a ping message is not answered by a pong message from the peer, the - /// `WebSocket` is assumed disconnected and the connection is closed with a - /// [WebSocketStatus.GOING_AWAY] close code. When a ping signal is sent, the - /// pong message must be received within [pingInterval]. - /// - /// There are never two outstanding pings at any given time, and the next ping - /// timer starts when the pong is received. - /// - /// By default, the [pingInterval] is `null`, indicating that ping messages - /// are disabled. - Duration pingInterval; - - /// The [close code][] set when the WebSocket connection is closed. - /// - /// [close code]: https://tools.ietf.org/html/rfc6455#section-7.1.5 - /// - /// Before the connection has been closed, this will be `null`. - int get closeCode; - - /// The [close reason][] set when the WebSocket connection is closed. - /// - /// [close reason]: https://tools.ietf.org/html/rfc6455#section-7.1.6 - /// - /// Before the connection has been closed, this will be `null`. - String get closeReason; - - /// Signs a `Sec-WebSocket-Key` header sent by a WebSocket client as part of - /// the [initial handshake]. - /// - /// The return value should be sent back to the client in a - /// `Sec-WebSocket-Accept` header. - /// - /// [initial handshake]: https://tools.ietf.org/html/rfc6455#section-4.2.2 - static String signKey(String key) { - // We use [codeUnits] here rather than UTF-8-decoding the string because - // [key] is expected to be base64 encoded, and so will be pure ASCII. - return BASE64.encode(sha1.convert((key + webSocketGUID).codeUnits).bytes); - } - - /// Creates a new WebSocket handling messaging across an existing socket. - /// - /// Because this is HTTP-API-agnostic, the initial [WebSocket handshake][] - /// must have already been completed on the socket before this is called. - /// - /// If [stream] is also a [StreamSink] (for example, if it's a "dart:io" - /// `Socket`), it will be used for both sending and receiving data. Otherwise, - /// it will be used for receiving data and [sink] will be used for sending it. - /// - /// [protocol] should be the protocol negotiated by this handshake, if any. - /// - /// If this is a WebSocket server, [serverSide] should be `true` (the - /// default); if it's a client, [serverSide] should be `false`. - /// - /// [WebSocket handshake]: https://tools.ietf.org/html/rfc6455#section-4 - factory CompatibleWebSocket(Stream> stream, - {StreamSink> sink, String protocol, bool serverSide: true}) { - if (sink == null) { - if (stream is! StreamSink) { - throw new ArgumentError("If stream isn't also a StreamSink, sink must " - "be passed explicitly."); - } - sink = stream as StreamSink; - } - - return new WebSocketImpl.fromSocket(stream, sink, protocol, serverSide); - } - - /// Closes the web socket connection. - /// - /// [closeCode] and [closeReason] are the [close code][] and [reason][] sent - /// to the remote peer, respectively. If they are omitted, the peer will see - /// a "no status received" code with no reason. - /// - /// [close code]: https://tools.ietf.org/html/rfc6455#section-7.1.5 - /// [reason]: https://tools.ietf.org/html/rfc6455#section-7.1.6 - Future close([int closeCode, String closeReason]); -} diff --git a/pkgs/http_parser/lib/src/web_socket/exception.dart b/pkgs/http_parser/lib/src/web_socket/exception.dart deleted file mode 100644 index c1a6473925..0000000000 --- a/pkgs/http_parser/lib/src/web_socket/exception.dart +++ /dev/null @@ -1,19 +0,0 @@ -// 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. - -/// This class is deprecated. -/// -/// Use the [`web_socket_channel`][web_socket_channel] package instead. -/// -/// [web_socket_channel]: https://pub.dartlang.org/packages/web_socket_channel -@Deprecated("Will be removed in 3.0.0.") -class CompatibleWebSocketException implements Exception { - final String message; - - CompatibleWebSocketException([this.message]); - - String toString() => message == null - ? "CompatibleWebSocketException" : - "CompatibleWebSocketException: $message"; -} diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index 804d951588..21a397734e 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,5 +1,5 @@ name: http_parser -version: 2.2.1 +version: 3.0.0 author: "Dart Team " homepage: https://github.com/dart-lang/http_parser description: > diff --git a/pkgs/http_parser/test/web_socket_test.dart b/pkgs/http_parser/test/web_socket_test.dart deleted file mode 100644 index 53aa5be415..0000000000 --- a/pkgs/http_parser/test/web_socket_test.dart +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright (c) 2014, 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. - -@TestOn('vm') - -import 'dart:io'; - -import 'package:http_parser/http_parser.dart'; -import 'package:stream_channel/stream_channel.dart'; -import 'package:test/test.dart'; - -void main() { - group("using WebSocketChannel", () { - test("a client can communicate with a WebSocket server", () async { - var server = await HttpServer.bind("localhost", 0); - server.transform(new WebSocketTransformer()).listen((webSocket) { - webSocket.add("hello!"); - webSocket.listen((request) { - expect(request, equals("ping")); - webSocket.add("pong"); - webSocket.close(); - }); - }); - - var client = new HttpClient(); - var request = await client.openUrl( - "GET", Uri.parse("http://localhost:${server.port}")); - request.headers - ..set("Connection", "Upgrade") - ..set("Upgrade", "websocket") - ..set("Sec-WebSocket-Key", "x3JJHMbDL1EzLkh9GBhXDw==") - ..set("Sec-WebSocket-Version", "13"); - - var response = await request.close(); - var socket = await response.detachSocket(); - var innerChannel = new StreamChannel(socket, socket); - var webSocket = new WebSocketChannel(innerChannel, serverSide: false); - - var n = 0; - await webSocket.stream.listen((message) { - if (n == 0) { - expect(message, equals("hello!")); - webSocket.sink.add("ping"); - } else if (n == 1) { - expect(message, equals("pong")); - webSocket.sink.close(); - server.close(); - } else { - fail("Only expected two messages."); - } - n++; - }).asFuture(); - }); - - test("a server can communicate with a WebSocket client", () async { - var server = await HttpServer.bind("localhost", 0); - server.listen((request) async { - var response = request.response; - response.statusCode = 101; - response.headers - ..set("Connection", "Upgrade") - ..set("Upgrade", "websocket") - ..set("Sec-WebSocket-Accept", CompatibleWebSocket - .signKey(request.headers.value('Sec-WebSocket-Key'))); - response.contentLength = 0; - - var socket = await response.detachSocket(); - var innerChannel = new StreamChannel(socket, socket); - var webSocket = new WebSocketChannel(innerChannel); - webSocket.sink.add("hello!"); - - var message = await webSocket.stream.first; - expect(message, equals("ping")); - webSocket.sink.add("pong"); - webSocket.sink.close(); - }); - - var webSocket = await WebSocket.connect('ws://localhost:${server.port}'); - var n = 0; - await webSocket.listen((message) { - if (n == 0) { - expect(message, equals("hello!")); - webSocket.add("ping"); - } else if (n == 1) { - expect(message, equals("pong")); - webSocket.close(); - server.close(); - } else { - fail("Only expected two messages."); - } - n++; - }).asFuture(); - }); - }); - - group("using CompatibleWebSocket", () { - test("a client can communicate with a WebSocket server", () { - return HttpServer.bind("localhost", 0).then((server) { - server.transform(new WebSocketTransformer()).listen((webSocket) { - webSocket.add("hello!"); - webSocket.listen((request) { - expect(request, equals("ping")); - webSocket.add("pong"); - webSocket.close(); - }); - }); - - var client = new HttpClient(); - return client - .openUrl("GET", Uri.parse("http://localhost:${server.port}")) - .then((request) { - request.headers - ..set("Connection", "Upgrade") - ..set("Upgrade", "websocket") - ..set("Sec-WebSocket-Key", "x3JJHMbDL1EzLkh9GBhXDw==") - ..set("Sec-WebSocket-Version", "13"); - return request.close(); - }).then((response) => response.detachSocket()).then((socket) { - var webSocket = new CompatibleWebSocket(socket, serverSide: false); - - var n = 0; - return webSocket.listen((message) { - if (n == 0) { - expect(message, equals("hello!")); - webSocket.add("ping"); - } else if (n == 1) { - expect(message, equals("pong")); - webSocket.close(); - server.close(); - } else { - fail("Only expected two messages."); - } - n++; - }).asFuture(); - }); - }); - }); - - test("a server can communicate with a WebSocket client", () { - return HttpServer.bind("localhost", 0).then((server) { - server.listen((request) { - var response = request.response; - response.statusCode = 101; - response.headers - ..set("Connection", "Upgrade") - ..set("Upgrade", "websocket") - ..set("Sec-WebSocket-Accept", CompatibleWebSocket - .signKey(request.headers.value('Sec-WebSocket-Key'))); - response.contentLength = 0; - response.detachSocket().then((socket) { - var webSocket = new CompatibleWebSocket(socket); - webSocket.add("hello!"); - webSocket.first.then((request) { - expect(request, equals("ping")); - webSocket.add("pong"); - webSocket.close(); - }); - }); - }); - - return WebSocket - .connect('ws://localhost:${server.port}') - .then((webSocket) { - var n = 0; - return webSocket.listen((message) { - if (n == 0) { - expect(message, equals("hello!")); - webSocket.add("ping"); - } else if (n == 1) { - expect(message, equals("pong")); - webSocket.close(); - server.close(); - } else { - fail("Only expected two messages."); - } - n++; - }).asFuture(); - }); - }); - }); - }); -} From 4a3d12f393e693f33dfa338e399627669cce0525 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 18 May 2016 16:39:40 -0700 Subject: [PATCH 032/126] Remove unused libraries. I guess I forgot to remove these when we stopped using them. R=rnystrom@google.com Review URL: https://codereview.chromium.org//1993933003 . --- .../lib/src/copy/bytes_builder.dart | 215 ----- pkgs/http_parser/lib/src/copy/io_sink.dart | 145 --- pkgs/http_parser/lib/src/copy/web_socket.dart | 40 - .../lib/src/copy/web_socket_impl.dart | 861 ------------------ pkgs/http_parser/pubspec.yaml | 2 +- 5 files changed, 1 insertion(+), 1262 deletions(-) delete mode 100644 pkgs/http_parser/lib/src/copy/bytes_builder.dart delete mode 100644 pkgs/http_parser/lib/src/copy/io_sink.dart delete mode 100644 pkgs/http_parser/lib/src/copy/web_socket.dart delete mode 100644 pkgs/http_parser/lib/src/copy/web_socket_impl.dart diff --git a/pkgs/http_parser/lib/src/copy/bytes_builder.dart b/pkgs/http_parser/lib/src/copy/bytes_builder.dart deleted file mode 100644 index 39d44fe654..0000000000 --- a/pkgs/http_parser/lib/src/copy/bytes_builder.dart +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright (c) 2013, 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 is a copy of "dart:io"'s BytesBuilder implementation, from -// sdk/lib/io/bytes_builder.dart. It's copied here to make it available to -// non-"dart:io" applications (issue 18348). -// -// Because it's copied directly, there are no modifications from the original. -// -// This is up-to-date as of sdk revision -// 86227840d75d974feb238f8b3c59c038b99c05cf. -import 'dart:math'; -import 'dart:typed_data'; - -/** - * Builds a list of bytes, allowing bytes and lists of bytes to be added at the - * end. - * - * Used to efficiently collect bytes and lists of bytes. - */ -abstract class BytesBuilder { - /** - * Construct a new empty [BytesBuilder]. - * - * If [copy] is true, the data is always copied when added to the list. If - * it [copy] is false, the data is only copied if needed. That means that if - * the lists are changed after added to the [BytesBuilder], it may effect the - * output. Default is `true`. - */ - factory BytesBuilder({bool copy: true}) { - if (copy) { - return new _CopyingBytesBuilder(); - } else { - return new _BytesBuilder(); - } - } - - /** - * Appends [bytes] to the current contents of the builder. - * - * Each value of [bytes] will be bit-representation truncated to the range - * 0 .. 255. - */ - void add(List bytes); - - /** - * Append [byte] to the current contents of the builder. - * - * The [byte] will be bit-representation truncated to the range 0 .. 255. - */ - void addByte(int byte); - - /** - * Returns the contents of `this` and clears `this`. - * - * The list returned is a view of the the internal buffer, limited to the - * [length]. - */ - List takeBytes(); - - /** - * Returns a copy of the current contents of the builder. - * - * Leaves the contents of the builder intact. - */ - List toBytes(); - - /** - * The number of bytes in the builder. - */ - int get length; - - /** - * Returns `true` if the buffer is empty. - */ - bool get isEmpty; - - /** - * Returns `true` if the buffer is not empty. - */ - bool get isNotEmpty; - - /** - * Clear the contents of the builder. - */ - void clear(); -} - - -class _CopyingBytesBuilder implements BytesBuilder { - // Start with 1024 bytes. - static const int _INIT_SIZE = 1024; - - int _length = 0; - Uint8List _buffer; - - void add(List bytes) { - int bytesLength = bytes.length; - if (bytesLength == 0) return; - int required = _length + bytesLength; - if (_buffer == null) { - int size = _pow2roundup(required); - size = max(size, _INIT_SIZE); - _buffer = new Uint8List(size); - } else if (_buffer.length < required) { - // We will create a list in the range of 2-4 times larger than - // required. - int size = _pow2roundup(required) * 2; - var newBuffer = new Uint8List(size); - newBuffer.setRange(0, _buffer.length, _buffer); - _buffer = newBuffer; - } - assert(_buffer.length >= required); - if (bytes is Uint8List) { - _buffer.setRange(_length, required, bytes); - } else { - for (int i = 0; i < bytesLength; i++) { - _buffer[_length + i] = bytes[i]; - } - } - _length = required; - } - - void addByte(int byte) => add([byte]); - - List takeBytes() { - if (_buffer == null) return new Uint8List(0); - var buffer = new Uint8List.view(_buffer.buffer, 0, _length); - clear(); - return buffer; - } - - List toBytes() { - if (_buffer == null) return new Uint8List(0); - return new Uint8List.fromList( - new Uint8List.view(_buffer.buffer, 0, _length)); - } - - int get length => _length; - - bool get isEmpty => _length == 0; - - bool get isNotEmpty => _length != 0; - - void clear() { - _length = 0; - _buffer = null; - } - - int _pow2roundup(int x) { - --x; - x |= x >> 1; - x |= x >> 2; - x |= x >> 4; - x |= x >> 8; - x |= x >> 16; - return x + 1; - } -} - - -class _BytesBuilder implements BytesBuilder { - int _length = 0; - final List _chunks = []; - - void add(List bytes) { - if (bytes is! Uint8List) { - bytes = new Uint8List.fromList(bytes); - } - _chunks.add(bytes); - _length += bytes.length; - } - - void addByte(int byte) => add([byte]); - - List takeBytes() { - if (_chunks.length == 0) return new Uint8List(0); - if (_chunks.length == 1) { - var buffer = _chunks.single; - clear(); - return buffer; - } - var buffer = new Uint8List(_length); - int offset = 0; - for (var chunk in _chunks) { - buffer.setRange(offset, offset + chunk.length, chunk); - offset += chunk.length; - } - clear(); - return buffer; - } - - List toBytes() { - if (_chunks.length == 0) return new Uint8List(0); - var buffer = new Uint8List(_length); - int offset = 0; - for (var chunk in _chunks) { - buffer.setRange(offset, offset + chunk.length, chunk); - offset += chunk.length; - } - return buffer; - } - - int get length => _length; - - bool get isEmpty => _length == 0; - - bool get isNotEmpty => _length != 0; - - void clear() { - _length = 0; - _chunks.clear(); - } -} diff --git a/pkgs/http_parser/lib/src/copy/io_sink.dart b/pkgs/http_parser/lib/src/copy/io_sink.dart deleted file mode 100644 index 0578bdb03b..0000000000 --- a/pkgs/http_parser/lib/src/copy/io_sink.dart +++ /dev/null @@ -1,145 +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. - -// The following code is copied from sdk/lib/io/io_sink.dart. The "dart:io" -// implementation isn't used directly to support non-"dart:io" applications. -// -// Because it's copied directly, only modifications necessary to support the -// desired public API and to remove "dart:io" dependencies have been made. -// -// This is up-to-date as of sdk revision -// 86227840d75d974feb238f8b3c59c038b99c05cf. -import 'dart:async'; - -class StreamSinkImpl implements StreamSink { - final StreamConsumer _target; - Completer _doneCompleter = new Completer(); - Future _doneFuture; - StreamController _controllerInstance; - Completer _controllerCompleter; - bool _isClosed = false; - bool _isBound = false; - bool _hasError = false; - - StreamSinkImpl(this._target) { - _doneFuture = _doneCompleter.future; - } - - void add(T data) { - if (_isClosed) return; - _controller.add(data); - } - - void addError(error, [StackTrace stackTrace]) { - _controller.addError(error, stackTrace); - } - - Future addStream(Stream stream) { - if (_isBound) { - throw new StateError("StreamSink is already bound to a stream"); - } - _isBound = true; - if (_hasError) return done; - // Wait for any sync operations to complete. - Future targetAddStream() { - return _target.addStream(stream) - .whenComplete(() { - _isBound = false; - }); - } - if (_controllerInstance == null) return targetAddStream(); - var future = _controllerCompleter.future; - _controllerInstance.close(); - return future.then((_) => targetAddStream()); - } - - Future flush() { - if (_isBound) { - throw new StateError("StreamSink is bound to a stream"); - } - if (_controllerInstance == null) return new Future.value(this); - // Adding an empty stream-controller will return a future that will complete - // when all data is done. - _isBound = true; - var future = _controllerCompleter.future; - _controllerInstance.close(); - return future.whenComplete(() { - _isBound = false; - }); - } - - Future close() { - if (_isBound) { - throw new StateError("StreamSink is bound to a stream"); - } - if (!_isClosed) { - _isClosed = true; - if (_controllerInstance != null) { - _controllerInstance.close(); - } else { - _closeTarget(); - } - } - return done; - } - - void _closeTarget() { - _target.close().then(_completeDoneValue, onError: _completeDoneError); - } - - Future get done => _doneFuture; - - void _completeDoneValue(value) { - if (_doneCompleter == null) return; - _doneCompleter.complete(value); - _doneCompleter = null; - } - - void _completeDoneError(error, StackTrace stackTrace) { - if (_doneCompleter == null) return; - _hasError = true; - _doneCompleter.completeError(error, stackTrace); - _doneCompleter = null; - } - - StreamController get _controller { - if (_isBound) { - throw new StateError("StreamSink is bound to a stream"); - } - if (_isClosed) { - throw new StateError("StreamSink is closed"); - } - if (_controllerInstance == null) { - _controllerInstance = new StreamController(sync: true); - _controllerCompleter = new Completer(); - _target.addStream(_controller.stream) - .then( - (_) { - if (_isBound) { - // A new stream takes over - forward values to that stream. - _controllerCompleter.complete(this); - _controllerCompleter = null; - _controllerInstance = null; - } else { - // No new stream, .close was called. Close _target. - _closeTarget(); - } - }, - onError: (error, stackTrace) { - if (_isBound) { - // A new stream takes over - forward errors to that stream. - _controllerCompleter.completeError(error, stackTrace); - _controllerCompleter = null; - _controllerInstance = null; - } else { - // No new stream. No need to close target, as it have already - // failed. - _completeDoneError(error, stackTrace); - } - }); - } - return _controllerInstance; - } -} - diff --git a/pkgs/http_parser/lib/src/copy/web_socket.dart b/pkgs/http_parser/lib/src/copy/web_socket.dart deleted file mode 100644 index 53460baa3e..0000000000 --- a/pkgs/http_parser/lib/src/copy/web_socket.dart +++ /dev/null @@ -1,40 +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. - -// The following code is copied from sdk/lib/io/websocket.dart. The "dart:io" -// implementation isn't used directly to support non-"dart:io" applications. -// -// Because it's copied directly, only modifications necessary to support the -// desired public API and to remove "dart:io" dependencies have been made. -// -// This is up-to-date as of sdk revision -// 86227840d75d974feb238f8b3c59c038b99c05cf. -/** - * Web socket status codes used when closing a web socket connection. - */ -abstract class WebSocketStatus { - static const int NORMAL_CLOSURE = 1000; - static const int GOING_AWAY = 1001; - static const int PROTOCOL_ERROR = 1002; - static const int UNSUPPORTED_DATA = 1003; - static const int RESERVED_1004 = 1004; - static const int NO_STATUS_RECEIVED = 1005; - static const int ABNORMAL_CLOSURE = 1006; - static const int INVALID_FRAME_PAYLOAD_DATA = 1007; - static const int POLICY_VIOLATION = 1008; - static const int MESSAGE_TOO_BIG = 1009; - static const int MISSING_MANDATORY_EXTENSION = 1010; - static const int INTERNAL_SERVER_ERROR = 1011; - static const int RESERVED_1015 = 1015; -} - -abstract class WebSocket { - /** - * Possible states of the connection. - */ - static const int CONNECTING = 0; - static const int OPEN = 1; - static const int CLOSING = 2; - static const int CLOSED = 3; -} diff --git a/pkgs/http_parser/lib/src/copy/web_socket_impl.dart b/pkgs/http_parser/lib/src/copy/web_socket_impl.dart deleted file mode 100644 index 31b3efad63..0000000000 --- a/pkgs/http_parser/lib/src/copy/web_socket_impl.dart +++ /dev/null @@ -1,861 +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. - -// The following code is copied from sdk/lib/io/websocket_impl.dart. The -// "dart:io" implementation isn't used directly to support non-"dart:io" -// applications. -// -// Because it's copied directly, only modifications necessary to support the -// desired public API and to remove "dart:io" dependencies have been made. -// -// This is up-to-date as of sdk revision -// 86227840d75d974feb238f8b3c59c038b99c05cf. -import 'dart:async'; -import 'dart:convert'; -import 'dart:math'; -import 'dart:typed_data'; - -import '../web_socket.dart'; -import 'bytes_builder.dart'; -import 'io_sink.dart'; -import 'web_socket.dart'; - -const String webSocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - -final _random = new Random(); - -// Matches _WebSocketOpcode. -class _WebSocketMessageType { - static const int NONE = 0; - static const int TEXT = 1; - static const int BINARY = 2; -} - - -class _WebSocketOpcode { - static const int CONTINUATION = 0; - static const int TEXT = 1; - static const int BINARY = 2; - static const int RESERVED_3 = 3; - static const int RESERVED_4 = 4; - static const int RESERVED_5 = 5; - static const int RESERVED_6 = 6; - static const int RESERVED_7 = 7; - static const int CLOSE = 8; - static const int PING = 9; - static const int PONG = 10; - static const int RESERVED_B = 11; - static const int RESERVED_C = 12; - static const int RESERVED_D = 13; - static const int RESERVED_E = 14; - static const int RESERVED_F = 15; -} - -/** - * The web socket protocol transformer handles the protocol byte stream - * which is supplied through the [:handleData:]. As the protocol is processed, - * it'll output frame data as either a List or String. - * - * Important infomation about usage: Be sure you use cancelOnError, so the - * socket will be closed when the processer encounter an error. Not using it - * will lead to undefined behaviour. - */ -// TODO(ajohnsen): make this transformer reusable? -class _WebSocketProtocolTransformer implements StreamTransformer, EventSink { - static const int START = 0; - static const int LEN_FIRST = 1; - static const int LEN_REST = 2; - static const int MASK = 3; - static const int PAYLOAD = 4; - static const int CLOSED = 5; - static const int FAILURE = 6; - - int _state = START; - bool _fin = false; - int _opcode = -1; - int _len = -1; - bool _masked = false; - int _remainingLenBytes = -1; - int _remainingMaskingKeyBytes = 4; - int _remainingPayloadBytes = -1; - int _unmaskingIndex = 0; - int _currentMessageType = _WebSocketMessageType.NONE; - int closeCode = WebSocketStatus.NO_STATUS_RECEIVED; - String closeReason = ""; - - EventSink _eventSink; - - final bool _serverSide; - final List _maskingBytes = new List(4); - final BytesBuilder _payload = new BytesBuilder(copy: false); - - _WebSocketProtocolTransformer([this._serverSide = false]); - - Stream bind(Stream stream) { - return new Stream.eventTransformed( - stream, - (EventSink eventSink) { - if (_eventSink != null) { - throw new StateError("WebSocket transformer already used."); - } - _eventSink = eventSink; - return this; - }); - } - - void addError(Object error, [StackTrace stackTrace]) => - _eventSink.addError(error, stackTrace); - - void close() => _eventSink.close(); - - /** - * Process data received from the underlying communication channel. - */ - void add(Uint8List buffer) { - int count = buffer.length; - int index = 0; - int lastIndex = count; - if (_state == CLOSED) { - throw new CompatibleWebSocketException("Data on closed connection"); - } - if (_state == FAILURE) { - throw new CompatibleWebSocketException("Data on failed connection"); - } - while ((index < lastIndex) && _state != CLOSED && _state != FAILURE) { - int byte = buffer[index]; - if (_state <= LEN_REST) { - if (_state == START) { - _fin = (byte & 0x80) != 0; - if ((byte & 0x70) != 0) { - // The RSV1, RSV2 bits RSV3 must be all zero. - throw new CompatibleWebSocketException("Protocol error"); - } - _opcode = (byte & 0xF); - if (_opcode <= _WebSocketOpcode.BINARY) { - if (_opcode == _WebSocketOpcode.CONTINUATION) { - if (_currentMessageType == _WebSocketMessageType.NONE) { - throw new CompatibleWebSocketException("Protocol error"); - } - } else { - assert(_opcode == _WebSocketOpcode.TEXT || - _opcode == _WebSocketOpcode.BINARY); - if (_currentMessageType != _WebSocketMessageType.NONE) { - throw new CompatibleWebSocketException("Protocol error"); - } - _currentMessageType = _opcode; - } - } else if (_opcode >= _WebSocketOpcode.CLOSE && - _opcode <= _WebSocketOpcode.PONG) { - // Control frames cannot be fragmented. - if (!_fin) throw new CompatibleWebSocketException("Protocol error"); - } else { - throw new CompatibleWebSocketException("Protocol error"); - } - _state = LEN_FIRST; - } else if (_state == LEN_FIRST) { - _masked = (byte & 0x80) != 0; - _len = byte & 0x7F; - if (_isControlFrame() && _len > 125) { - throw new CompatibleWebSocketException("Protocol error"); - } - if (_len == 126) { - _len = 0; - _remainingLenBytes = 2; - _state = LEN_REST; - } else if (_len == 127) { - _len = 0; - _remainingLenBytes = 8; - _state = LEN_REST; - } else { - assert(_len < 126); - _lengthDone(); - } - } else { - assert(_state == LEN_REST); - _len = _len << 8 | byte; - _remainingLenBytes--; - if (_remainingLenBytes == 0) { - _lengthDone(); - } - } - } else { - if (_state == MASK) { - _maskingBytes[4 - _remainingMaskingKeyBytes--] = byte; - if (_remainingMaskingKeyBytes == 0) { - _maskDone(); - } - } else { - assert(_state == PAYLOAD); - // The payload is not handled one byte at a time but in blocks. - int payloadLength = min(lastIndex - index, _remainingPayloadBytes); - _remainingPayloadBytes -= payloadLength; - // Unmask payload if masked. - if (_masked) { - _unmask(index, payloadLength, buffer); - } - // Control frame and data frame share _payloads. - _payload.add( - new Uint8List.view(buffer.buffer, index, payloadLength)); - index += payloadLength; - if (_isControlFrame()) { - if (_remainingPayloadBytes == 0) _controlFrameEnd(); - } else { - if (_currentMessageType != _WebSocketMessageType.TEXT && - _currentMessageType != _WebSocketMessageType.BINARY) { - throw new CompatibleWebSocketException("Protocol error"); - } - if (_remainingPayloadBytes == 0) _messageFrameEnd(); - } - - // Hack - as we always do index++ below. - index--; - } - } - - // Move to the next byte. - index++; - } - } - - void _unmask(int index, int length, Uint8List buffer) { - const int BLOCK_SIZE = 16; - // Skip Int32x4-version if message is small. - if (length >= BLOCK_SIZE) { - // Start by aligning to 16 bytes. - final int startOffset = BLOCK_SIZE - (index & 15); - final int end = index + startOffset; - for (int i = index; i < end; i++) { - buffer[i] ^= _maskingBytes[_unmaskingIndex++ & 3]; - } - index += startOffset; - length -= startOffset; - final int blockCount = length ~/ BLOCK_SIZE; - if (blockCount > 0) { - // Create mask block. - int mask = 0; - for (int i = 3; i >= 0; i--) { - mask = (mask << 8) | _maskingBytes[(_unmaskingIndex + i) & 3]; - } - Int32x4 blockMask = new Int32x4(mask, mask, mask, mask); - Int32x4List blockBuffer = new Int32x4List.view( - buffer.buffer, index, blockCount); - for (int i = 0; i < blockBuffer.length; i++) { - blockBuffer[i] ^= blockMask; - } - final int bytes = blockCount * BLOCK_SIZE; - index += bytes; - length -= bytes; - } - } - // Handle end. - final int end = index + length; - for (int i = index; i < end; i++) { - buffer[i] ^= _maskingBytes[_unmaskingIndex++ & 3]; - } - } - - void _lengthDone() { - if (_masked) { - if (!_serverSide) { - throw new CompatibleWebSocketException( - "Received masked frame from server"); - } - _state = MASK; - } else { - if (_serverSide) { - throw new CompatibleWebSocketException( - "Received unmasked frame from client"); - } - _remainingPayloadBytes = _len; - _startPayload(); - } - } - - void _maskDone() { - _remainingPayloadBytes = _len; - _startPayload(); - } - - void _startPayload() { - // If there is no actual payload perform perform callbacks without - // going through the PAYLOAD state. - if (_remainingPayloadBytes == 0) { - if (_isControlFrame()) { - switch (_opcode) { - case _WebSocketOpcode.CLOSE: - _state = CLOSED; - _eventSink.close(); - break; - case _WebSocketOpcode.PING: - _eventSink.add(new _WebSocketPing()); - break; - case _WebSocketOpcode.PONG: - _eventSink.add(new _WebSocketPong()); - break; - } - _prepareForNextFrame(); - } else { - _messageFrameEnd(); - } - } else { - _state = PAYLOAD; - } - } - - void _messageFrameEnd() { - if (_fin) { - switch (_currentMessageType) { - case _WebSocketMessageType.TEXT: - _eventSink.add(UTF8.decode(_payload.takeBytes())); - break; - case _WebSocketMessageType.BINARY: - _eventSink.add(_payload.takeBytes()); - break; - } - _currentMessageType = _WebSocketMessageType.NONE; - } - _prepareForNextFrame(); - } - - void _controlFrameEnd() { - switch (_opcode) { - case _WebSocketOpcode.CLOSE: - closeCode = WebSocketStatus.NO_STATUS_RECEIVED; - var payload = _payload.takeBytes(); - if (payload.length > 0) { - if (payload.length == 1) { - throw new CompatibleWebSocketException("Protocol error"); - } - closeCode = payload[0] << 8 | payload[1]; - if (closeCode == WebSocketStatus.NO_STATUS_RECEIVED) { - throw new CompatibleWebSocketException("Protocol error"); - } - if (payload.length > 2) { - closeReason = UTF8.decode(payload.sublist(2)); - } - } - _state = CLOSED; - _eventSink.close(); - break; - - case _WebSocketOpcode.PING: - _eventSink.add(new _WebSocketPing(_payload.takeBytes())); - break; - - case _WebSocketOpcode.PONG: - _eventSink.add(new _WebSocketPong(_payload.takeBytes())); - break; - } - _prepareForNextFrame(); - } - - bool _isControlFrame() { - return _opcode == _WebSocketOpcode.CLOSE || - _opcode == _WebSocketOpcode.PING || - _opcode == _WebSocketOpcode.PONG; - } - - void _prepareForNextFrame() { - if (_state != CLOSED && _state != FAILURE) _state = START; - _fin = false; - _opcode = -1; - _len = -1; - _remainingLenBytes = -1; - _remainingMaskingKeyBytes = 4; - _remainingPayloadBytes = -1; - _unmaskingIndex = 0; - } -} - - -class _WebSocketPing { - final List payload; - _WebSocketPing([this.payload = null]); -} - - -class _WebSocketPong { - final List payload; - _WebSocketPong([this.payload = null]); -} - -// TODO(ajohnsen): Make this transformer reusable. -class _WebSocketOutgoingTransformer implements StreamTransformer, EventSink { - final WebSocketImpl webSocket; - EventSink _eventSink; - - _WebSocketOutgoingTransformer(this.webSocket); - - Stream bind(Stream stream) { - return new Stream.eventTransformed( - stream, - (EventSink eventSink) { - if (_eventSink != null) { - throw new StateError("WebSocket transformer already used"); - } - _eventSink = eventSink; - return this; - }); - } - - void add(message) { - if (message is _WebSocketPong) { - addFrame(_WebSocketOpcode.PONG, message.payload); - return; - } - if (message is _WebSocketPing) { - addFrame(_WebSocketOpcode.PING, message.payload); - return; - } - List data; - int opcode; - if (message != null) { - if (message is String) { - opcode = _WebSocketOpcode.TEXT; - data = UTF8.encode(message); - } else { - if (message is !List) { - throw new ArgumentError(message); - } - opcode = _WebSocketOpcode.BINARY; - data = message; - } - } else { - opcode = _WebSocketOpcode.TEXT; - } - addFrame(opcode, data); - } - - void addError(Object error, [StackTrace stackTrace]) => - _eventSink.addError(error, stackTrace); - - void close() { - int code = webSocket._outCloseCode; - String reason = webSocket._outCloseReason; - List data; - if (code != null) { - data = new List(); - data.add((code >> 8) & 0xFF); - data.add(code & 0xFF); - if (reason != null) { - data.addAll(UTF8.encode(reason)); - } - } - addFrame(_WebSocketOpcode.CLOSE, data); - _eventSink.close(); - } - - void addFrame(int opcode, List data) => - createFrame(opcode, data, webSocket._serverSide).forEach(_eventSink.add); - - static Iterable createFrame(int opcode, List data, bool serverSide) { - bool mask = !serverSide; // Masking not implemented for server. - int dataLength = data == null ? 0 : data.length; - // Determine the header size. - int headerSize = (mask) ? 6 : 2; - if (dataLength > 65535) { - headerSize += 8; - } else if (dataLength > 125) { - headerSize += 2; - } - Uint8List header = new Uint8List(headerSize); - int index = 0; - // Set FIN and opcode. - header[index++] = 0x80 | opcode; - // Determine size and position of length field. - int lengthBytes = 1; - if (dataLength > 65535) { - header[index++] = 127; - lengthBytes = 8; - } else if (dataLength > 125) { - header[index++] = 126; - lengthBytes = 2; - } - // Write the length in network byte order into the header. - for (int i = 0; i < lengthBytes; i++) { - header[index++] = dataLength >> (((lengthBytes - 1) - i) * 8) & 0xFF; - } - if (mask) { - header[1] |= 1 << 7; - var maskBytes = [_random.nextInt(256), _random.nextInt(256), - _random.nextInt(256), _random.nextInt(256)]; - header.setRange(index, index + 4, maskBytes); - index += 4; - if (data != null) { - Uint8List list; - // If this is a text message just do the masking inside the - // encoded data. - if (opcode == _WebSocketOpcode.TEXT && data is Uint8List) { - list = data; - } else { - if (data is Uint8List) { - list = new Uint8List.fromList(data); - } else { - list = new Uint8List(data.length); - for (int i = 0; i < data.length; i++) { - if (data[i] < 0 || 255 < data[i]) { - throw new ArgumentError( - "List element is not a byte value " - "(value ${data[i]} at index $i)"); - } - list[i] = data[i]; - } - } - } - const int BLOCK_SIZE = 16; - int blockCount = list.length ~/ BLOCK_SIZE; - if (blockCount > 0) { - // Create mask block. - int mask = 0; - for (int i = 3; i >= 0; i--) { - mask = (mask << 8) | maskBytes[i]; - } - Int32x4 blockMask = new Int32x4(mask, mask, mask, mask); - Int32x4List blockBuffer = new Int32x4List.view( - list.buffer, 0, blockCount); - for (int i = 0; i < blockBuffer.length; i++) { - blockBuffer[i] ^= blockMask; - } - } - // Handle end. - for (int i = blockCount * BLOCK_SIZE; i < list.length; i++) { - list[i] ^= maskBytes[i & 3]; - } - data = list; - } - } - assert(index == headerSize); - if (data == null) { - return [header]; - } else { - return [header, data]; - } - } -} - - -class _WebSocketConsumer implements StreamConsumer { - final WebSocketImpl webSocket; - final StreamSink> sink; - StreamController _controller; - StreamSubscription _subscription; - bool _issuedPause = false; - bool _closed = false; - Completer _closeCompleter = new Completer(); - Completer _completer; - - _WebSocketConsumer(this.webSocket, this.sink); - - void _onListen() { - if (_subscription != null) { - _subscription.cancel(); - } - } - - void _onPause() { - if (_subscription != null) { - _subscription.pause(); - } else { - _issuedPause = true; - } - } - - void _onResume() { - if (_subscription != null) { - _subscription.resume(); - } else { - _issuedPause = false; - } - } - - void _cancel() { - if (_subscription != null) { - var subscription = _subscription; - _subscription = null; - subscription.cancel(); - } - } - - _ensureController() { - if (_controller != null) return; - _controller = new StreamController(sync: true, - onPause: _onPause, - onResume: _onResume, - onCancel: _onListen); - var stream = _controller.stream.transform( - new _WebSocketOutgoingTransformer(webSocket)); - sink.addStream(stream) - .then((_) { - _done(); - _closeCompleter.complete(webSocket); - }, onError: (error, StackTrace stackTrace) { - _closed = true; - _cancel(); - if (error is ArgumentError) { - if (!_done(error, stackTrace)) { - _closeCompleter.completeError(error, stackTrace); - } - } else { - _done(); - _closeCompleter.complete(webSocket); - } - }); - } - - bool _done([error, StackTrace stackTrace]) { - if (_completer == null) return false; - if (error != null) { - _completer.completeError(error, stackTrace); - } else { - _completer.complete(webSocket); - } - _completer = null; - return true; - } - - Future addStream(var stream) { - if (_closed) { - stream.listen(null).cancel(); - return new Future.value(webSocket); - } - _ensureController(); - _completer = new Completer(); - _subscription = stream.listen( - (data) { - _controller.add(data); - }, - onDone: _done, - onError: _done, - cancelOnError: true); - if (_issuedPause) { - _subscription.pause(); - _issuedPause = false; - } - return _completer.future; - } - - Future close() { - _ensureController(); - Future closeSocket() { - return sink.close().catchError((_) {}).then((_) => webSocket); - } - _controller.close(); - return _closeCompleter.future.then((_) => closeSocket()); - } - - void add(data) { - if (_closed) return; - _ensureController(); - _controller.add(data); - } - - void closeSocket() { - _closed = true; - _cancel(); - close(); - } -} - - -class WebSocketImpl extends Stream with _ServiceObject - implements CompatibleWebSocket { - // Use default Map so we keep order. - static Map _webSockets = new Map(); - - final String protocol; - - StreamController _controller; - StreamSubscription _subscription; - StreamSink _sink; - - final bool _serverSide; - int _readyState = WebSocket.CONNECTING; - bool _writeClosed = false; - int _closeCode; - String _closeReason; - Duration _pingInterval; - Timer _pingTimer; - _WebSocketConsumer _consumer; - - int _outCloseCode; - String _outCloseReason; - Timer _closeTimer; - - WebSocketImpl.fromSocket(Stream> stream, - StreamSink> sink, this.protocol, [this._serverSide = false]) { - _consumer = new _WebSocketConsumer(this, sink); - _sink = new StreamSinkImpl(_consumer); - _readyState = WebSocket.OPEN; - - var transformer = new _WebSocketProtocolTransformer(_serverSide); - _subscription = stream.transform(transformer).listen( - (data) { - if (data is _WebSocketPing) { - if (!_writeClosed) _consumer.add(new _WebSocketPong(data.payload)); - } else if (data is _WebSocketPong) { - // Simply set pingInterval, as it'll cancel any timers. - pingInterval = _pingInterval; - } else { - _controller.add(data); - } - }, - onError: (error) { - if (_closeTimer != null) _closeTimer.cancel(); - if (error is FormatException) { - _close(WebSocketStatus.INVALID_FRAME_PAYLOAD_DATA); - } else { - _close(WebSocketStatus.PROTOCOL_ERROR); - } - // An error happened, set the close code set above. - _closeCode = _outCloseCode; - _closeReason = _outCloseReason; - _controller.close(); - }, - onDone: () { - if (_closeTimer != null) _closeTimer.cancel(); - if (_readyState == WebSocket.OPEN) { - _readyState = WebSocket.CLOSING; - if (!_isReservedStatusCode(transformer.closeCode)) { - _close(transformer.closeCode); - } else { - _close(); - } - _readyState = WebSocket.CLOSED; - } - // Protocol close, use close code from transformer. - _closeCode = transformer.closeCode; - _closeReason = transformer.closeReason; - _controller.close(); - }, - cancelOnError: true); - _subscription.pause(); - _controller = new StreamController(sync: true, - onListen: () => _subscription.resume(), - onCancel: () { - _subscription.cancel(); - _subscription = null; - }, - onPause: _subscription.pause, - onResume: _subscription.resume); - - _webSockets[_serviceId] = this; - } - - StreamSubscription listen(void onData(message), - {Function onError, - void onDone(), - bool cancelOnError}) { - return _controller.stream.listen(onData, - onError: onError, - onDone: onDone, - cancelOnError: cancelOnError); - } - - Duration get pingInterval => _pingInterval; - - void set pingInterval(Duration interval) { - if (_writeClosed) return; - if (_pingTimer != null) _pingTimer.cancel(); - _pingInterval = interval; - - if (_pingInterval == null) return; - - _pingTimer = new Timer(_pingInterval, () { - if (_writeClosed) return; - _consumer.add(new _WebSocketPing()); - _pingTimer = new Timer(_pingInterval, () { - // No pong received. - _close(WebSocketStatus.GOING_AWAY); - }); - }); - } - - int get readyState => _readyState; - - String get extensions => null; - int get closeCode => _closeCode; - String get closeReason => _closeReason; - - void add(data) => _sink.add(data); - void addError(error, [StackTrace stackTrace]) => - _sink.addError(error, stackTrace); - Future addStream(Stream stream) => _sink.addStream(stream); - Future get done => _sink.done; - - Future close([int code, String reason]) { - if (_isReservedStatusCode(code)) { - throw new CompatibleWebSocketException("Reserved status code $code"); - } - if (_outCloseCode == null) { - _outCloseCode = code; - _outCloseReason = reason; - } - if (!_controller.isClosed) { - // If a close has not yet been received from the other end then - // 1) make sure to listen on the stream so the close frame will be - // processed if received. - // 2) set a timer terminate the connection if a close frame is - // not received. - if (!_controller.hasListener && _subscription != null) { - _controller.stream.drain().catchError((_) => {}); - } - if (_closeTimer == null) { - // When closing the web-socket, we no longer accept data. - _closeTimer = new Timer(const Duration(seconds: 5), () { - // Reuse code and reason from the local close. - _closeCode = _outCloseCode; - _closeReason = _outCloseReason; - if (_subscription != null) _subscription.cancel(); - _controller.close(); - _webSockets.remove(_serviceId); - }); - } - } - return _sink.close(); - } - - void _close([int code, String reason]) { - if (_writeClosed) return; - if (_outCloseCode == null) { - _outCloseCode = code; - _outCloseReason = reason; - } - _writeClosed = true; - _consumer.closeSocket(); - _webSockets.remove(_serviceId); - } - - // The _toJSON, _serviceTypePath, and _serviceTypeName methods - // have been deleted for http_parser. The methods were unused in WebSocket - // code and produced warnings. - - static bool _isReservedStatusCode(int code) { - return code != null && - (code < WebSocketStatus.NORMAL_CLOSURE || - code == WebSocketStatus.RESERVED_1004 || - code == WebSocketStatus.NO_STATUS_RECEIVED || - code == WebSocketStatus.ABNORMAL_CLOSURE || - (code > WebSocketStatus.INTERNAL_SERVER_ERROR && - code < WebSocketStatus.RESERVED_1015) || - (code >= WebSocketStatus.RESERVED_1015 && - code < 3000)); - } -} - -// The following code is from sdk/lib/io/service_object.dart. - -int _nextServiceId = 1; - -// TODO(ajohnsen): Use other way of getting a uniq id. -abstract class _ServiceObject { - int __serviceId = 0; - int get _serviceId { - if (__serviceId == 0) __serviceId = _nextServiceId++; - return __serviceId; - } - - // The _toJSON, _servicePath, _serviceTypePath, _serviceTypeName, and - // _serviceType methods have been deleted for http_parser. The methods were - // unused in WebSocket code and produced warnings. -} diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index 21a397734e..c03e59da2a 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,5 +1,5 @@ name: http_parser -version: 3.0.0 +version: 3.0.1-dev author: "Dart Team " homepage: https://github.com/dart-lang/http_parser description: > From 6cf995c6adf5d5e489e728c863bcb891514c64b4 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 19 May 2016 12:15:11 -0700 Subject: [PATCH 033/126] Remove unnecessary dependencies. R=jmesserly@google.com Review URL: https://codereview.chromium.org//1996553003 . --- pkgs/http_parser/CHANGELOG.md | 4 ++++ pkgs/http_parser/pubspec.yaml | 5 +---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index 756c5bcf89..25fe8f8f51 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.0.1 + +* Remove unnecessary dependencies. + ## 3.0.0 * All deprecated APIs have been removed. No new APIs have been added. Packages diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index c03e59da2a..0cf276a862 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,15 +1,12 @@ name: http_parser -version: 3.0.1-dev +version: 3.0.1 author: "Dart Team " homepage: https://github.com/dart-lang/http_parser description: > A platform-independent package for parsing and serializing HTTP formats. dependencies: - async: "^1.3.0" collection: ">=0.9.1 <2.0.0" - crypto: ">=0.9.2 <2.0.0" source_span: "^1.0.0" - stream_channel: "^1.0.0" string_scanner: ">=0.0.0 <0.2.0" dev_dependencies: test: "^0.12.0" From c9bc7607edd9da44a8b2d6f4ee42479d45d6efe3 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 20 Jun 2016 16:25:35 -0700 Subject: [PATCH 034/126] Support string_scanner 1.0.0. R=alanknight@google.com Review URL: https://codereview.chromium.org//2089523002 . --- pkgs/http_parser/CHANGELOG.md | 4 ++++ pkgs/http_parser/pubspec.yaml | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index 25fe8f8f51..9802eb1574 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.0.2 + +* Support `string_scanner` 1.0.0. + ## 3.0.1 * Remove unnecessary dependencies. diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index 0cf276a862..c96bc0b695 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,5 +1,5 @@ name: http_parser -version: 3.0.1 +version: 3.0.2 author: "Dart Team " homepage: https://github.com/dart-lang/http_parser description: > @@ -8,6 +8,7 @@ dependencies: collection: ">=0.9.1 <2.0.0" source_span: "^1.0.0" string_scanner: ">=0.0.0 <0.2.0" + string_scanner: ">=0.0.0 <2.0.0" dev_dependencies: test: "^0.12.0" environment: From 8182d4e4003706f9904fb8c9587bed4f9ce6c88f Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 12 Oct 2016 09:36:26 -0700 Subject: [PATCH 035/126] Fix duplicate dependency on string_scanner --- pkgs/http_parser/pubspec.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index c96bc0b695..69ffe2cec8 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,5 +1,5 @@ name: http_parser -version: 3.0.2 +version: 3.0.3 author: "Dart Team " homepage: https://github.com/dart-lang/http_parser description: > @@ -7,7 +7,6 @@ description: > dependencies: collection: ">=0.9.1 <2.0.0" source_span: "^1.0.0" - string_scanner: ">=0.0.0 <0.2.0" string_scanner: ">=0.0.0 <2.0.0" dev_dependencies: test: "^0.12.0" From 964da2b09c3048569ec3c26c5027a15ff2984ce5 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 6 Dec 2016 13:44:02 -0800 Subject: [PATCH 036/126] Add a Codec for the chunked transfer coding. (dart-lang/http_parser#8) --- pkgs/http_parser/CHANGELOG.md | 7 + pkgs/http_parser/lib/http_parser.dart | 1 + pkgs/http_parser/lib/src/chunked_coding.dart | 39 ++ .../lib/src/chunked_coding/decoder.dart | 212 ++++++++++ .../lib/src/chunked_coding/encoder.dart | 72 ++++ pkgs/http_parser/pubspec.yaml | 4 +- .../http_parser/test/chunked_coding_test.dart | 364 ++++++++++++++++++ 7 files changed, 698 insertions(+), 1 deletion(-) create mode 100644 pkgs/http_parser/lib/src/chunked_coding.dart create mode 100644 pkgs/http_parser/lib/src/chunked_coding/decoder.dart create mode 100644 pkgs/http_parser/lib/src/chunked_coding/encoder.dart create mode 100644 pkgs/http_parser/test/chunked_coding_test.dart diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index 9802eb1574..c12d2cb6fe 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,3 +1,10 @@ +## 3.1.0 + +* Add `chunkedCoding`, a `Codec` that supports encoding and decoding the + [chunked transfer coding][]. + +[chunked transfer coding]: https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1 + ## 3.0.2 * Support `string_scanner` 1.0.0. diff --git a/pkgs/http_parser/lib/http_parser.dart b/pkgs/http_parser/lib/http_parser.dart index 684c0b5fd3..77b20c7a05 100644 --- a/pkgs/http_parser/lib/http_parser.dart +++ b/pkgs/http_parser/lib/http_parser.dart @@ -4,5 +4,6 @@ export 'src/authentication_challenge.dart'; export 'src/case_insensitive_map.dart'; +export 'src/chunked_coding.dart'; export 'src/http_date.dart'; export 'src/media_type.dart'; diff --git a/pkgs/http_parser/lib/src/chunked_coding.dart b/pkgs/http_parser/lib/src/chunked_coding.dart new file mode 100644 index 0000000000..8d2326cbfc --- /dev/null +++ b/pkgs/http_parser/lib/src/chunked_coding.dart @@ -0,0 +1,39 @@ +// 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. + +import 'dart:convert'; + +import 'chunked_coding/encoder.dart'; +import 'chunked_coding/decoder.dart'; + +export 'chunked_coding/encoder.dart' hide chunkedCodingEncoder; +export 'chunked_coding/decoder.dart' hide chunkedCodingDecoder; + +/// The canonical instance of [ChunkedCodec]. +const chunkedCoding = const ChunkedCodingCodec._(); + +/// A codec that encodes and decodes the [chunked transfer coding][]. +/// +/// [chunked transfer coding]: https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1 +/// +/// The [encoder] creates a *single* chunked message for each call to +/// [ChunkedEncoder.convert] or [ChunkedEncoder.startChunkedConversion]. This +/// means that it will always add an end-of-message footer once conversion has +/// finished. It doesn't support generating chunk extensions or trailing +/// headers. +/// +/// Similarly, the [decoder] decodes a *single* chunked message into a stream of +/// byte arrays that must be concatenated to get the full list (like most Dart +/// byte streams). It doesn't support decoding a stream that contains multiple +/// chunked messages, nor does it support a stream that contains chunked data +/// mixed with other types of data. +/// +/// Currently, [decoder] will fail to parse chunk extensions and trailing +/// headers. It may be updated to silently ignore them in the future. +class ChunkedCodingCodec extends Codec, List> { + ChunkedCodingEncoder get encoder => chunkedCodingEncoder; + ChunkedCodingDecoder get decoder => chunkedCodingDecoder; + + const ChunkedCodingCodec._(); +} diff --git a/pkgs/http_parser/lib/src/chunked_coding/decoder.dart b/pkgs/http_parser/lib/src/chunked_coding/decoder.dart new file mode 100644 index 0000000000..e2a27fcf7d --- /dev/null +++ b/pkgs/http_parser/lib/src/chunked_coding/decoder.dart @@ -0,0 +1,212 @@ +// 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. + +import 'dart:convert'; +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'package:charcode/ascii.dart'; +import 'package:typed_data/typed_data.dart'; + +/// The canonical instance of [ChunkedCodingDecoder]. +const chunkedCodingDecoder = const ChunkedCodingDecoder._(); + +/// A converter that decodes byte arrays into chunks with size tags. +class ChunkedCodingDecoder extends Converter, List> { + const ChunkedCodingDecoder._(); + + List convert(List bytes) { + var sink = new _Sink(null); + var output = sink._decode(bytes, 0, bytes.length); + if (sink._state == _State.end) return output; + + throw new FormatException( + "Input ended unexpectedly.", bytes, bytes.length); + } + + ByteConversionSink startChunkedConversion(Sink> sink) => + new _Sink(sink); +} + +/// A conversion sink for the chunked transfer encoding. +class _Sink extends ByteConversionSinkBase { + /// The underlying sink to which decoded byte arrays will be passed. + final Sink> _sink; + + /// The current state of the sink's parsing. + var _state = _State.boundary; + + /// The size of the chunk being parsed, or `null` if the size hasn't been + /// parsed yet. + int _size; + + _Sink(this._sink); + + void add(List chunk) => addSlice(chunk, 0, chunk.length, false); + + void addSlice(List chunk, int start, int end, bool isLast) { + RangeError.checkValidRange(start, end, chunk.length); + var output = _decode(chunk, start, end); + if (output.isNotEmpty) _sink.add(output); + if (isLast) _close(chunk, end); + } + + void close() => _close(); + + /// Like [close], but includes [chunk] and [index] in the [FormatException] if + /// one is thrown. + void _close([List chunk, int index]) { + if (_state != _State.end) { + throw new FormatException("Input ended unexpectedly.", chunk, index); + } + + _sink.close(); + } + + /// Decodes the data in [bytes] from [start] to [end]. + Uint8List _decode(List bytes, int start, int end) { + /// Throws a [FormatException] if `bytes[start] != $char`. Uses [name] to + /// describe the character in the exception text. + assertCurrentChar(int char, String name) { + if (bytes[start] != char) { + throw new FormatException("Expected LF.", bytes, start); + } + } + + var buffer = new Uint8Buffer(); + while (start != end) { + switch (_state) { + case _State.boundary: + _size = _digitForByte(bytes, start); + _state = _State.size; + start++; + break; + + case _State.size: + if (bytes[start] == $cr) { + _state = _State.beforeLF; + } else { + // Shift four bits left since a single hex digit contains four bits + // of information. + _size = (_size << 4) + _digitForByte(bytes, start); + } + start++; + break; + + case _State.beforeLF: + assertCurrentChar($lf, "LF"); + _state = _size == 0 ? _State.endBeforeCR : _State.body; + start++; + break; + + case _State.body: + var chunkEnd = math.min(end, start + _size); + buffer.addAll(bytes, start, chunkEnd); + _size -= chunkEnd - start; + start = chunkEnd; + if (_size == 0) _state = _State.boundary; + break; + + case _State.endBeforeCR: + assertCurrentChar($cr, "CR"); + _state = _State.endBeforeLF; + start++; + break; + + case _State.endBeforeLF: + assertCurrentChar($lf, "CR"); + _state = _State.end; + start++; + break; + + case _State.end: + throw new FormatException("Expected no more data.", bytes, start); + } + } + return buffer.buffer.asUint8List(0, buffer.length); + } + + /// Returns the hex digit (0 through 15) corresponding to the byte at index + /// [i] in [bytes]. + /// + /// If the given byte isn't a hexadecimal ASCII character, throws a + /// [FormatException]. + int _digitForByte(List bytes, int index) { + // If the byte is a numeral, get its value. XOR works because 0 in ASCII is + // `0b110000` and the other numerals come after it in ascending order and + // take up at most four bits. + // + // We check for digits first because it ensures there's only a single branch + // for 10 out of 16 of the expected cases. We don't count the `digit >= 0` + // check because branch prediction will always work on it for valid data. + var byte = bytes[index]; + var digit = $0 ^ byte; + if (digit <= 9) { + if (digit >= 0) return digit; + } else { + // If the byte is an uppercase letter, convert it to lowercase. This works + // because uppercase letters in ASCII are exactly `0b100000 = 0x20` less + // than lowercase letters, so if we ensure that that bit is 1 we ensure that + // the letter is lowercase. + var letter = 0x20 | byte; + if ($a <= letter && letter <= $f) return letter - $a + 10; + } + + throw new FormatException( + "Invalid hexadecimal byte 0x${byte.toRadixString(16).toUpperCase()}.", + bytes, index); + } +} + +/// An enumeration of states that [_Sink] can exist in when decoded a chunked +/// message. +/// +/// [_SizeState], [_CRState], and [_ChunkState] have additional data attached. +class _State { + /// The parser has fully parsed one chunk and is expecting the header for the + /// next chunk. + /// + /// Transitions to [size]. + static const boundary = const _State._("boundary"); + + /// The parser has parsed at least one digit of the chunk size header, but has + /// not yet parsed the `CR LF` sequence that indicates the end of that header. + /// + /// Transitions to [beforeLF]. + static const size = const _State._("size"); + + /// The parser has parsed the chunk size header and the CR character after it, + /// but not the LF. + /// + /// Transitions to [body] or [endBeforeCR]. + static const beforeLF = const _State._("before LF"); + + /// The parser has parsed a chunk header and possibly some of the body, but + /// still needs to consume more bytes. + /// + /// Transitions to [boundary]. + static const body = const _State._("CR"); + + /// The parser has parsed the final empty chunk but not the CR LF sequence + /// that follows it. + /// + /// Transitions to [endBeforeLF]. + static const endBeforeCR = const _State._("end before CR"); + + /// The parser has parsed the final empty chunk and the CR that follows it, + /// but not the LF after that. + /// + /// Transitions to [end]. + static const endBeforeLF = const _State._("end before LF"); + + /// The parser has parsed the final empty chunk as well as the CR LF that + /// follows, and expects no more data. + static const end = const _State._("end"); + + final String _name; + + const _State._(this._name); + + String toString() => _name; +} diff --git a/pkgs/http_parser/lib/src/chunked_coding/encoder.dart b/pkgs/http_parser/lib/src/chunked_coding/encoder.dart new file mode 100644 index 0000000000..c724700253 --- /dev/null +++ b/pkgs/http_parser/lib/src/chunked_coding/encoder.dart @@ -0,0 +1,72 @@ +// 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. + +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:charcode/ascii.dart'; + +/// The canonical instance of [ChunkedCodingEncoder]. +const chunkedCodingEncoder = const ChunkedCodingEncoder._(); + +/// The chunk indicating that the chunked message has finished. +final _doneChunk = new Uint8List.fromList([$0, $cr, $lf, $cr, $lf]); + +/// A converter that encodes byte arrays into chunks with size tags. +class ChunkedCodingEncoder extends Converter, List> { + const ChunkedCodingEncoder._(); + + List convert(List bytes) => + _convert(bytes, 0, bytes.length, isLast: true); + + ByteConversionSink startChunkedConversion(Sink> sink) => + new _Sink(sink); +} + +/// A conversion sink for the chunked transfer encoding. +class _Sink extends ByteConversionSinkBase { + /// The underlying sink to which encoded byte arrays will be passed. + final Sink> _sink; + + _Sink(this._sink); + + void add(List chunk) { + _sink.add(_convert(chunk, 0, chunk.length)); + } + + void addSlice(List chunk, int start, int end, bool isLast) { + RangeError.checkValidRange(start, end, chunk.length); + _sink.add(_convert(chunk, start, end, isLast: isLast)); + if (isLast) _sink.close(); + } + + void close() { + _sink.add(_doneChunk); + _sink.close(); + } +} + +/// Returns a new list a chunked transfer encoding header followed by the slice +/// of [bytes] from [start] to [end]. +/// +/// If [isLast] is `true`, this adds the footer that indicates that the chunked +/// message is complete. +List _convert(List bytes, int start, int end, {bool isLast: false}) { + if (end == start) return isLast ? _doneChunk : const []; + + var size = end - start; + var sizeInHex = size.toRadixString(16); + var footerSize = isLast ? _doneChunk.length : 0; + + // Add 2 for the CRLF sequence that follows the size header. + var list = new Uint8List(sizeInHex.length + 2 + size + footerSize); + list.setRange(0, sizeInHex.length, sizeInHex.codeUnits); + list[sizeInHex.length] = $cr; + list[sizeInHex.length + 1] = $lf; + list.setRange(sizeInHex.length + 2, list.length - footerSize, bytes, start); + if (isLast) { + list.setRange(list.length - footerSize, list.length, _doneChunk); + } + return list; +} diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index 69ffe2cec8..d88dec9a17 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,13 +1,15 @@ name: http_parser -version: 3.0.3 +version: 3.1.0 author: "Dart Team " homepage: https://github.com/dart-lang/http_parser description: > A platform-independent package for parsing and serializing HTTP formats. dependencies: + charcode: "^1.1.0" collection: ">=0.9.1 <2.0.0" source_span: "^1.0.0" string_scanner: ">=0.0.0 <2.0.0" + typed_data: "^1.1.0" dev_dependencies: test: "^0.12.0" environment: diff --git a/pkgs/http_parser/test/chunked_coding_test.dart b/pkgs/http_parser/test/chunked_coding_test.dart new file mode 100644 index 0000000000..29fce256c5 --- /dev/null +++ b/pkgs/http_parser/test/chunked_coding_test.dart @@ -0,0 +1,364 @@ +// 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. + +import 'dart:async'; +import 'dart:convert'; + +import 'package:http_parser/http_parser.dart'; + +import 'package:charcode/charcode.dart'; +import 'package:test/test.dart'; + +void main() { + group("encoder", () { + test("adds a header to the chunk of bytes", () { + expect(chunkedCoding.encode([1, 2, 3]), + equals([$3, $cr, $lf, 1, 2, 3, $0, $cr, $lf, $cr, $lf])); + }); + + test("uses hex for chunk size", () { + var data = new Iterable.generate(0xA7).toList(); + expect(chunkedCoding.encode(data), + equals([$a, $7, $cr, $lf] + ..addAll(data) + ..addAll([$0, $cr, $lf, $cr, $lf]))); + }); + + test("just generates a footer for an empty input", () { + expect(chunkedCoding.encode([]), equals([$0, $cr, $lf, $cr, $lf])); + }); + + group("with chunked conversion", () { + List> results; + ByteConversionSink> sink; + setUp(() { + results = []; + var controller = new StreamController>(sync: true); + controller.stream.listen(results.add); + sink = chunkedCoding.encoder.startChunkedConversion(controller.sink); + }); + + test("adds headers to each chunk of bytes", () { + sink.add([1, 2, 3, 4]); + expect(results, equals([[$4, $cr, $lf, 1, 2, 3, 4]])); + + sink.add([5, 6, 7]); + expect(results, equals([ + [$4, $cr, $lf, 1, 2, 3, 4], + [$3, $cr, $lf, 5, 6, 7], + ])); + + sink.close(); + expect(results, equals([ + [$4, $cr, $lf, 1, 2, 3, 4], + [$3, $cr, $lf, 5, 6, 7], + [$0, $cr, $lf, $cr, $lf], + ])); + }); + + test("handles empty chunks", () { + sink.add([]); + expect(results, equals([[]])); + + sink.add([1, 2, 3]); + expect(results, equals([[], [$3, $cr, $lf, 1, 2, 3]])); + + sink.add([]); + expect(results, equals([[], [$3, $cr, $lf, 1, 2, 3], []])); + + sink.close(); + expect(results, equals([ + [], + [$3, $cr, $lf, 1, 2, 3], + [], + [$0, $cr, $lf, $cr, $lf], + ])); + }); + + group("addSlice()", () { + test("adds bytes from the specified slice", () { + sink.addSlice([1, 2, 3, 4, 5], 1, 4, false); + expect(results, equals([[$3, $cr, $lf, 2, 3, 4]])); + }); + + test("doesn't add a header if the slice is empty", () { + sink.addSlice([1, 2, 3, 4, 5], 1, 1, false); + expect(results, equals([[]])); + }); + + test("adds a footer if isLast is true", () { + sink.addSlice([1, 2, 3, 4, 5], 1, 4, true); + expect(results, + equals([[$3, $cr, $lf, 2, 3, 4, $0, $cr, $lf, $cr, $lf]])); + + // Setting isLast shuld close the sink. + expect(() => sink.add([]), throwsStateError); + }); + + group("disallows", () { + test("start < 0", () { + expect(() => sink.addSlice([1, 2, 3, 4, 5], -1, 4, false), + throwsRangeError); + }); + + test("start > end", () { + expect(() => sink.addSlice([1, 2, 3, 4, 5], 3, 2, false), + throwsRangeError); + }); + + test("end > length", () { + expect(() => sink.addSlice([1, 2, 3, 4, 5], 1, 10, false), + throwsRangeError); + }); + }); + }); + }); + }); + + group("decoder", () { + test("parses chunked data", () { + expect(chunkedCoding.decode([ + $3, $cr, $lf, 1, 2, 3, + $4, $cr, $lf, 4, 5, 6, 7, + $0, $cr, $lf, $cr, $lf, + ]), equals([1, 2, 3, 4, 5, 6, 7])); + }); + + test("parses hex size", () { + var data = new Iterable.generate(0xA7).toList(); + expect( + chunkedCoding.decode([$a, $7, $cr, $lf] + ..addAll(data) + ..addAll([$0, $cr, $lf, $cr, $lf])), + equals(data)); + }); + + test("parses capital hex size", () { + var data = new Iterable.generate(0xA7).toList(); + expect( + chunkedCoding.decode([$A, $7, $cr, $lf] + ..addAll(data) + ..addAll([$0, $cr, $lf, $cr, $lf])), + equals(data)); + }); + + test("parses an empty message", () { + expect(chunkedCoding.decode([$0, $cr, $lf, $cr, $lf]), isEmpty); + }); + + group("disallows a message", () { + test("that ends without any input", () { + expect(() => chunkedCoding.decode([]), throwsFormatException); + }); + + test("that ends after the size", () { + expect(() => chunkedCoding.decode([$a]), throwsFormatException); + }); + + test("that ends after CR", () { + expect(() => chunkedCoding.decode([$a, $cr]), throwsFormatException); + }); + + test("that ends after LF", () { + expect(() => chunkedCoding.decode([$a, $cr, $lf]), + throwsFormatException); + }); + + test("that ends after insufficient bytes", () { + expect(() => chunkedCoding.decode([$a, $cr, $lf, 1, 2, 3]), + throwsFormatException); + }); + + test("that ends at a chunk boundary", () { + expect(() => chunkedCoding.decode([$1, $cr, $lf, 1]), + throwsFormatException); + }); + + test("that ends after the empty chunk", () { + expect(() => chunkedCoding.decode([$0, $cr, $lf]), + throwsFormatException); + }); + + test("that ends after the closing CR", () { + expect(() => chunkedCoding.decode([$0, $cr, $lf, $cr]), + throwsFormatException); + }); + + test("with a chunk without a size", () { + expect(() => chunkedCoding.decode([$cr, $lf, $0, $cr, $lf, $cr, $lf]), + throwsFormatException); + }); + + test("with a chunk with a non-hex size", () { + expect( + () => chunkedCoding.decode([$q, $cr, $lf, $0, $cr, $lf, $cr, $lf]), + throwsFormatException); + }); + }); + + group("with chunked conversion", () { + List> results; + ByteConversionSink> sink; + setUp(() { + results = []; + var controller = new StreamController>(sync: true); + controller.stream.listen(results.add); + sink = chunkedCoding.decoder.startChunkedConversion(controller.sink); + }); + + test("decodes each chunk of bytes", () { + sink.add([$4, $cr, $lf, 1, 2, 3, 4]); + expect(results, equals([[1, 2, 3, 4]])); + + sink.add([$3, $cr, $lf, 5, 6, 7]); + expect(results, equals([[1, 2, 3, 4], [5, 6, 7]])); + + sink.add([$0, $cr, $lf, $cr, $lf]); + sink.close(); + expect(results, equals([[1, 2, 3, 4], [5, 6, 7]])); + }); + + test("handles empty chunks", () { + sink.add([]); + expect(results, isEmpty); + + sink.add([$3, $cr, $lf, 1, 2, 3]); + expect(results, equals([[1, 2, 3]])); + + sink.add([]); + expect(results, equals([[1, 2, 3]])); + + sink.add([$0, $cr, $lf, $cr, $lf]); + sink.close(); + expect(results, equals([[1, 2, 3]])); + }); + + test("throws if the sink is closed before the message is done", () { + sink.add([$3, $cr, $lf, 1, 2, 3]); + expect(() => sink.close(), throwsFormatException); + }); + + group("preserves state when a byte array ends", () { + test("within chunk size", () { + sink.add([$a]); + expect(results, isEmpty); + + var data = new Iterable.generate(0xA7).toList(); + sink.add([$7, $cr, $lf]..addAll(data)); + expect(results, equals([data])); + }); + + test("after chunk size", () { + sink.add([$3]); + expect(results, isEmpty); + + sink.add([$cr, $lf, 1, 2, 3]); + expect(results, equals([[1, 2, 3]])); + }); + + test("after CR", () { + sink.add([$3, $cr]); + expect(results, isEmpty); + + sink.add([$lf, 1, 2, 3]); + expect(results, equals([[1, 2, 3]])); + }); + + test("after LF", () { + sink.add([$3, $cr, $lf]); + expect(results, isEmpty); + + sink.add([1, 2, 3]); + expect(results, equals([[1, 2, 3]])); + }); + + test("after some bytes", () { + sink.add([$3, $cr, $lf, 1, 2]); + expect(results, equals([[1, 2]])); + + sink.add([3]); + expect(results, equals([[1, 2], [3]])); + }); + + test("after empty chunk size", () { + sink.add([$0]); + expect(results, isEmpty); + + sink.add([$cr, $lf, $cr, $lf]); + expect(results, isEmpty); + + sink.close(); + expect(results, isEmpty); + }); + + test("after first empty chunk CR", () { + sink.add([$0, $cr]); + expect(results, isEmpty); + + sink.add([$lf, $cr, $lf]); + expect(results, isEmpty); + + sink.close(); + expect(results, isEmpty); + }); + + test("after first empty chunk LF", () { + sink.add([$0, $cr, $lf]); + expect(results, isEmpty); + + sink.add([$cr, $lf]); + expect(results, isEmpty); + + sink.close(); + expect(results, isEmpty); + }); + + test("after second empty chunk CR", () { + sink.add([$0, $cr, $lf, $cr]); + expect(results, isEmpty); + + sink.add([$lf]); + expect(results, isEmpty); + + sink.close(); + expect(results, isEmpty); + }); + }); + + group("addSlice()", () { + test("adds bytes from the specified slice", () { + sink.addSlice([1, $3, $cr, $lf, 2, 3, 4, 5], 1, 7, false); + expect(results, equals([[2, 3, 4]])); + }); + + test("doesn't decode if the slice is empty", () { + sink.addSlice([1, 2, 3, 4, 5], 1, 1, false); + expect(results, isEmpty); + }); + + test("closes the sink if isLast is true", () { + sink.addSlice([1, $0, $cr, $lf, $cr, $lf, 7], 1, 6, true); + expect(results, isEmpty); + }); + + group("disallows", () { + test("start < 0", () { + expect(() => sink.addSlice([1, 2, 3, 4, 5], -1, 4, false), + throwsRangeError); + }); + + test("start > end", () { + expect(() => sink.addSlice([1, 2, 3, 4, 5], 3, 2, false), + throwsRangeError); + }); + + test("end > length", () { + expect(() => sink.addSlice([1, 2, 3, 4, 5], 1, 10, false), + throwsRangeError); + }); + }); + }); + }); + }); +} From 9275100e6b8ce959df580f51ea4176025c8b94b3 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 14 Dec 2016 17:15:34 -0800 Subject: [PATCH 037/126] Fix chunkedCoding. (dart-lang/http_parser#9) I misread the spec; it turns out there's a CR LF sequence after the body bytes as well as after the size header. --- pkgs/http_parser/CHANGELOG.md | 5 ++ .../lib/src/chunked_coding/decoder.dart | 46 ++++++++--- .../lib/src/chunked_coding/encoder.dart | 16 ++-- .../http_parser/test/chunked_coding_test.dart | 76 ++++++++++++++----- 4 files changed, 105 insertions(+), 38 deletions(-) diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index c12d2cb6fe..2c50995f58 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,3 +1,8 @@ +## 3.1.1 + +* Fix a logic bug in the `chunkedCoding` codec. It had been producing invalid + output and rejecting valid input. + ## 3.1.0 * Add `chunkedCoding`, a `Codec` that supports encoding and decoding the diff --git a/pkgs/http_parser/lib/src/chunked_coding/decoder.dart b/pkgs/http_parser/lib/src/chunked_coding/decoder.dart index e2a27fcf7d..18530bc019 100644 --- a/pkgs/http_parser/lib/src/chunked_coding/decoder.dart +++ b/pkgs/http_parser/lib/src/chunked_coding/decoder.dart @@ -70,7 +70,7 @@ class _Sink extends ByteConversionSinkBase { /// describe the character in the exception text. assertCurrentChar(int char, String name) { if (bytes[start] != char) { - throw new FormatException("Expected LF.", bytes, start); + throw new FormatException("Expected $name.", bytes, start); } } @@ -85,7 +85,7 @@ class _Sink extends ByteConversionSinkBase { case _State.size: if (bytes[start] == $cr) { - _state = _State.beforeLF; + _state = _State.sizeBeforeLF; } else { // Shift four bits left since a single hex digit contains four bits // of information. @@ -94,7 +94,7 @@ class _Sink extends ByteConversionSinkBase { start++; break; - case _State.beforeLF: + case _State.sizeBeforeLF: assertCurrentChar($lf, "LF"); _state = _size == 0 ? _State.endBeforeCR : _State.body; start++; @@ -105,7 +105,19 @@ class _Sink extends ByteConversionSinkBase { buffer.addAll(bytes, start, chunkEnd); _size -= chunkEnd - start; start = chunkEnd; - if (_size == 0) _state = _State.boundary; + if (_size == 0) _state = _State.bodyBeforeCR; + break; + + case _State.bodyBeforeCR: + assertCurrentChar($cr, "CR"); + _state = _State.bodyBeforeLF; + start++; + break; + + case _State.bodyBeforeLF: + assertCurrentChar($lf, "LF"); + _state = _State.boundary; + start++; break; case _State.endBeforeCR: @@ -115,7 +127,7 @@ class _Sink extends ByteConversionSinkBase { break; case _State.endBeforeLF: - assertCurrentChar($lf, "CR"); + assertCurrentChar($lf, "LF"); _state = _State.end; start++; break; @@ -161,8 +173,6 @@ class _Sink extends ByteConversionSinkBase { /// An enumeration of states that [_Sink] can exist in when decoded a chunked /// message. -/// -/// [_SizeState], [_CRState], and [_ChunkState] have additional data attached. class _State { /// The parser has fully parsed one chunk and is expecting the header for the /// next chunk. @@ -173,20 +183,32 @@ class _State { /// The parser has parsed at least one digit of the chunk size header, but has /// not yet parsed the `CR LF` sequence that indicates the end of that header. /// - /// Transitions to [beforeLF]. + /// Transitions to [sizeBeforeLF]. static const size = const _State._("size"); /// The parser has parsed the chunk size header and the CR character after it, /// but not the LF. /// - /// Transitions to [body] or [endBeforeCR]. - static const beforeLF = const _State._("before LF"); + /// Transitions to [body] or [bodyBeforeCR]. + static const sizeBeforeLF = const _State._("size before LF"); /// The parser has parsed a chunk header and possibly some of the body, but /// still needs to consume more bytes. /// - /// Transitions to [boundary]. - static const body = const _State._("CR"); + /// Transitions to [bodyBeforeCR]. + static const body = const _State._("body"); + + // The parser has parsed all the bytes in a chunk body but not the CR LF + // sequence that follows it. + // + // Transitions to [bodyBeforeLF]. + static const bodyBeforeCR = const _State._("body before CR"); + + // The parser has parsed all the bytes in a chunk body and the CR that follows + // it, but not the LF after that. + // + // Transitions to [bounday]. + static const bodyBeforeLF = const _State._("body before LF"); /// The parser has parsed the final empty chunk but not the CR LF sequence /// that follows it. diff --git a/pkgs/http_parser/lib/src/chunked_coding/encoder.dart b/pkgs/http_parser/lib/src/chunked_coding/encoder.dart index c724700253..183ecfcd2f 100644 --- a/pkgs/http_parser/lib/src/chunked_coding/encoder.dart +++ b/pkgs/http_parser/lib/src/chunked_coding/encoder.dart @@ -59,12 +59,18 @@ List _convert(List bytes, int start, int end, {bool isLast: false}) { var sizeInHex = size.toRadixString(16); var footerSize = isLast ? _doneChunk.length : 0; - // Add 2 for the CRLF sequence that follows the size header. - var list = new Uint8List(sizeInHex.length + 2 + size + footerSize); + // Add 4 for the CRLF sequences that follow the size header and the bytes. + var list = new Uint8List(sizeInHex.length + 4 + size + footerSize); list.setRange(0, sizeInHex.length, sizeInHex.codeUnits); - list[sizeInHex.length] = $cr; - list[sizeInHex.length + 1] = $lf; - list.setRange(sizeInHex.length + 2, list.length - footerSize, bytes, start); + + var cursor = sizeInHex.length; + list[cursor++] = $cr; + list[cursor++] = $lf; + list.setRange(cursor, cursor + end - start, bytes, start); + cursor += end - start; + list[cursor++] = $cr; + list[cursor++] = $lf; + if (isLast) { list.setRange(list.length - footerSize, list.length, _doneChunk); } diff --git a/pkgs/http_parser/test/chunked_coding_test.dart b/pkgs/http_parser/test/chunked_coding_test.dart index 29fce256c5..241cfc9a29 100644 --- a/pkgs/http_parser/test/chunked_coding_test.dart +++ b/pkgs/http_parser/test/chunked_coding_test.dart @@ -14,7 +14,7 @@ void main() { group("encoder", () { test("adds a header to the chunk of bytes", () { expect(chunkedCoding.encode([1, 2, 3]), - equals([$3, $cr, $lf, 1, 2, 3, $0, $cr, $lf, $cr, $lf])); + equals([$3, $cr, $lf, 1, 2, 3, $cr, $lf, $0, $cr, $lf, $cr, $lf])); }); test("uses hex for chunk size", () { @@ -22,7 +22,7 @@ void main() { expect(chunkedCoding.encode(data), equals([$a, $7, $cr, $lf] ..addAll(data) - ..addAll([$0, $cr, $lf, $cr, $lf]))); + ..addAll([$cr, $lf, $0, $cr, $lf, $cr, $lf]))); }); test("just generates a footer for an empty input", () { @@ -41,18 +41,18 @@ void main() { test("adds headers to each chunk of bytes", () { sink.add([1, 2, 3, 4]); - expect(results, equals([[$4, $cr, $lf, 1, 2, 3, 4]])); + expect(results, equals([[$4, $cr, $lf, 1, 2, 3, 4, $cr, $lf]])); sink.add([5, 6, 7]); expect(results, equals([ - [$4, $cr, $lf, 1, 2, 3, 4], - [$3, $cr, $lf, 5, 6, 7], + [$4, $cr, $lf, 1, 2, 3, 4, $cr, $lf], + [$3, $cr, $lf, 5, 6, 7, $cr, $lf], ])); sink.close(); expect(results, equals([ - [$4, $cr, $lf, 1, 2, 3, 4], - [$3, $cr, $lf, 5, 6, 7], + [$4, $cr, $lf, 1, 2, 3, 4, $cr, $lf], + [$3, $cr, $lf, 5, 6, 7, $cr, $lf], [$0, $cr, $lf, $cr, $lf], ])); }); @@ -62,15 +62,15 @@ void main() { expect(results, equals([[]])); sink.add([1, 2, 3]); - expect(results, equals([[], [$3, $cr, $lf, 1, 2, 3]])); + expect(results, equals([[], [$3, $cr, $lf, 1, 2, 3, $cr, $lf]])); sink.add([]); - expect(results, equals([[], [$3, $cr, $lf, 1, 2, 3], []])); + expect(results, equals([[], [$3, $cr, $lf, 1, 2, 3, $cr, $lf], []])); sink.close(); expect(results, equals([ [], - [$3, $cr, $lf, 1, 2, 3], + [$3, $cr, $lf, 1, 2, 3, $cr, $lf], [], [$0, $cr, $lf, $cr, $lf], ])); @@ -79,7 +79,7 @@ void main() { group("addSlice()", () { test("adds bytes from the specified slice", () { sink.addSlice([1, 2, 3, 4, 5], 1, 4, false); - expect(results, equals([[$3, $cr, $lf, 2, 3, 4]])); + expect(results, equals([[$3, $cr, $lf, 2, 3, 4, $cr, $lf]])); }); test("doesn't add a header if the slice is empty", () { @@ -89,8 +89,8 @@ void main() { test("adds a footer if isLast is true", () { sink.addSlice([1, 2, 3, 4, 5], 1, 4, true); - expect(results, - equals([[$3, $cr, $lf, 2, 3, 4, $0, $cr, $lf, $cr, $lf]])); + expect(results, equals( + [[$3, $cr, $lf, 2, 3, 4, $cr, $lf, $0, $cr, $lf, $cr, $lf]])); // Setting isLast shuld close the sink. expect(() => sink.add([]), throwsStateError); @@ -119,8 +119,8 @@ void main() { group("decoder", () { test("parses chunked data", () { expect(chunkedCoding.decode([ - $3, $cr, $lf, 1, 2, 3, - $4, $cr, $lf, 4, 5, 6, 7, + $3, $cr, $lf, 1, 2, 3, $cr, $lf, + $4, $cr, $lf, 4, 5, 6, 7, $cr, $lf, $0, $cr, $lf, $cr, $lf, ]), equals([1, 2, 3, 4, 5, 6, 7])); }); @@ -130,7 +130,7 @@ void main() { expect( chunkedCoding.decode([$a, $7, $cr, $lf] ..addAll(data) - ..addAll([$0, $cr, $lf, $cr, $lf])), + ..addAll([$cr, $lf, $0, $cr, $lf, $cr, $lf])), equals(data)); }); @@ -139,7 +139,7 @@ void main() { expect( chunkedCoding.decode([$A, $7, $cr, $lf] ..addAll(data) - ..addAll([$0, $cr, $lf, $cr, $lf])), + ..addAll([$cr, $lf, $0, $cr, $lf, $cr, $lf])), equals(data)); }); @@ -170,11 +170,21 @@ void main() { throwsFormatException); }); - test("that ends at a chunk boundary", () { + test("that ends after a chunk's bytes", () { expect(() => chunkedCoding.decode([$1, $cr, $lf, 1]), throwsFormatException); }); + test("that ends after a chunk's CR", () { + expect(() => chunkedCoding.decode([$1, $cr, $lf, 1, $cr]), + throwsFormatException); + }); + + test("that ends atfter a chunk's LF", () { + expect(() => chunkedCoding.decode([$1, $cr, $lf, 1, $cr, $lf]), + throwsFormatException); + }); + test("that ends after the empty chunk", () { expect(() => chunkedCoding.decode([$0, $cr, $lf]), throwsFormatException); @@ -208,10 +218,10 @@ void main() { }); test("decodes each chunk of bytes", () { - sink.add([$4, $cr, $lf, 1, 2, 3, 4]); + sink.add([$4, $cr, $lf, 1, 2, 3, 4, $cr, $lf]); expect(results, equals([[1, 2, 3, 4]])); - sink.add([$3, $cr, $lf, 5, 6, 7]); + sink.add([$3, $cr, $lf, 5, 6, 7, $cr, $lf]); expect(results, equals([[1, 2, 3, 4], [5, 6, 7]])); sink.add([$0, $cr, $lf, $cr, $lf]); @@ -223,7 +233,7 @@ void main() { sink.add([]); expect(results, isEmpty); - sink.add([$3, $cr, $lf, 1, 2, 3]); + sink.add([$3, $cr, $lf, 1, 2, 3, $cr, $lf]); expect(results, equals([[1, 2, 3]])); sink.add([]); @@ -281,6 +291,30 @@ void main() { expect(results, equals([[1, 2], [3]])); }); + test("after all bytes", () { + sink.add([$3, $cr, $lf, 1, 2, 3]); + expect(results, equals([[1, 2, 3]])); + + sink.add([$cr, $lf, $3, $cr, $lf, 2, 3, 4, $cr, $lf]); + expect(results, equals([[1, 2, 3], [2, 3, 4]])); + }); + + test("after a post-chunk CR", () { + sink.add([$3, $cr, $lf, 1, 2, 3, $cr]); + expect(results, equals([[1, 2, 3]])); + + sink.add([$lf, $3, $cr, $lf, 2, 3, 4, $cr, $lf]); + expect(results, equals([[1, 2, 3], [2, 3, 4]])); + }); + + test("after a post-chunk LF", () { + sink.add([$3, $cr, $lf, 1, 2, 3, $cr, $lf]); + expect(results, equals([[1, 2, 3]])); + + sink.add([$3, $cr, $lf, 2, 3, 4, $cr, $lf]); + expect(results, equals([[1, 2, 3], [2, 3, 4]])); + }); + test("after empty chunk size", () { sink.add([$0]); expect(results, isEmpty); From 4ecad216d34125b26277d8d6e5064055c65d95f9 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 14 Dec 2016 17:40:41 -0800 Subject: [PATCH 038/126] Bump the version to 3.1.1. (dart-lang/http_parser#10) --- pkgs/http_parser/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index d88dec9a17..e2faf4c3e7 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,5 +1,5 @@ name: http_parser -version: 3.1.0 +version: 3.1.1 author: "Dart Team " homepage: https://github.com/dart-lang/http_parser description: > From d64470f6a0e8b96c8f2c72e4b441ee61dc93df19 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Mon, 10 Apr 2017 11:22:25 -0700 Subject: [PATCH 039/126] Fix strong mode errors in chunked_coding_test --- pkgs/http_parser/test/chunked_coding_test.dart | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pkgs/http_parser/test/chunked_coding_test.dart b/pkgs/http_parser/test/chunked_coding_test.dart index 241cfc9a29..afe9b51473 100644 --- a/pkgs/http_parser/test/chunked_coding_test.dart +++ b/pkgs/http_parser/test/chunked_coding_test.dart @@ -18,7 +18,7 @@ void main() { }); test("uses hex for chunk size", () { - var data = new Iterable.generate(0xA7).toList(); + var data = new Iterable.generate(0xA7).toList(); expect(chunkedCoding.encode(data), equals([$a, $7, $cr, $lf] ..addAll(data) @@ -31,7 +31,7 @@ void main() { group("with chunked conversion", () { List> results; - ByteConversionSink> sink; + ByteConversionSink sink; setUp(() { results = []; var controller = new StreamController>(sync: true); @@ -126,7 +126,7 @@ void main() { }); test("parses hex size", () { - var data = new Iterable.generate(0xA7).toList(); + var data = new Iterable.generate(0xA7).toList(); expect( chunkedCoding.decode([$a, $7, $cr, $lf] ..addAll(data) @@ -135,7 +135,7 @@ void main() { }); test("parses capital hex size", () { - var data = new Iterable.generate(0xA7).toList(); + var data = new Iterable.generate(0xA7).toList(); expect( chunkedCoding.decode([$A, $7, $cr, $lf] ..addAll(data) @@ -209,7 +209,7 @@ void main() { group("with chunked conversion", () { List> results; - ByteConversionSink> sink; + ByteConversionSink sink; setUp(() { results = []; var controller = new StreamController>(sync: true); @@ -254,7 +254,7 @@ void main() { sink.add([$a]); expect(results, isEmpty); - var data = new Iterable.generate(0xA7).toList(); + var data = new Iterable.generate(0xA7).toList(); sink.add([$7, $cr, $lf]..addAll(data)); expect(results, equals([data])); }); From 28ab0adfc4fb0d28fae5b8b4a9327414e05a3191 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 10 Apr 2017 18:33:19 -0700 Subject: [PATCH 040/126] rename analysis_options to right name --- pkgs/http_parser/{.analysis_options => analysis_options.yaml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename pkgs/http_parser/{.analysis_options => analysis_options.yaml} (100%) diff --git a/pkgs/http_parser/.analysis_options b/pkgs/http_parser/analysis_options.yaml similarity index 100% rename from pkgs/http_parser/.analysis_options rename to pkgs/http_parser/analysis_options.yaml From 43fc525add7b0bcec5b6a99767cdd2995f632c16 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 10 Apr 2017 18:33:27 -0700 Subject: [PATCH 041/126] add travis file --- pkgs/http_parser/.travis.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 pkgs/http_parser/.travis.yml diff --git a/pkgs/http_parser/.travis.yml b/pkgs/http_parser/.travis.yml new file mode 100644 index 0000000000..b5590b3aed --- /dev/null +++ b/pkgs/http_parser/.travis.yml @@ -0,0 +1,20 @@ +language: dart +sudo: false +dart: + - dev + - stable + - 1.22.1 + - 1.21.1 + - 1.20.1 + - 1.19.1 +cache: + directories: + - $HOME/.pub-cache +dart_task: + - test: --platform vm + xvfb: false + - test: --platform firefox + - test: --platform dartium + install_dartium: true + - dartfmt + - dartanalyzer From 3b791a76d956a6620af5bf3fe5fee9c89d12ac59 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 10 Apr 2017 18:36:41 -0700 Subject: [PATCH 042/126] dartfmt --- .../lib/src/authentication_challenge.dart | 16 +- .../lib/src/chunked_coding/decoder.dart | 6 +- pkgs/http_parser/lib/src/http_date.dart | 16 +- pkgs/http_parser/lib/src/media_type.dart | 17 +- pkgs/http_parser/lib/src/scan.dart | 2 +- pkgs/http_parser/lib/src/utils.dart | 4 +- .../test/authentication_challenge_test.dart | 81 ++----- .../http_parser/test/chunked_coding_test.dart | 226 ++++++++++++++---- pkgs/http_parser/test/media_type_test.dart | 11 +- 9 files changed, 244 insertions(+), 135 deletions(-) diff --git a/pkgs/http_parser/lib/src/authentication_challenge.dart b/pkgs/http_parser/lib/src/authentication_challenge.dart index 66841b4c8c..17955df9a6 100644 --- a/pkgs/http_parser/lib/src/authentication_challenge.dart +++ b/pkgs/http_parser/lib/src/authentication_challenge.dart @@ -73,9 +73,9 @@ class AuthenticationChallenge { if (scanner.scan(token)) { params[name] = scanner.lastMatch[0]; - } else { - params[name] = expectQuotedString( - scanner, name: "a token or a quoted string"); + } else { + params[name] = + expectQuotedString(scanner, name: "a token or a quoted string"); } scanner.scan(whitespace); @@ -136,9 +136,9 @@ class AuthenticationChallenge { if (scanner.scan(token)) { params[name] = scanner.lastMatch[0]; - } else { - params[name] = expectQuotedString( - scanner, name: "a token or a quoted string"); + } else { + params[name] = + expectQuotedString(scanner, name: "a token or a quoted string"); } scanner.scan(whitespace); @@ -146,6 +146,6 @@ class AuthenticationChallenge { /// Creates a new challenge value with [scheme] and [parameters]. AuthenticationChallenge(this.scheme, Map parameters) - : parameters = new UnmodifiableMapView( - new CaseInsensitiveMap.from(parameters)); + : parameters = + new UnmodifiableMapView(new CaseInsensitiveMap.from(parameters)); } diff --git a/pkgs/http_parser/lib/src/chunked_coding/decoder.dart b/pkgs/http_parser/lib/src/chunked_coding/decoder.dart index 18530bc019..3ffa520f97 100644 --- a/pkgs/http_parser/lib/src/chunked_coding/decoder.dart +++ b/pkgs/http_parser/lib/src/chunked_coding/decoder.dart @@ -21,8 +21,7 @@ class ChunkedCodingDecoder extends Converter, List> { var output = sink._decode(bytes, 0, bytes.length); if (sink._state == _State.end) return output; - throw new FormatException( - "Input ended unexpectedly.", bytes, bytes.length); + throw new FormatException("Input ended unexpectedly.", bytes, bytes.length); } ByteConversionSink startChunkedConversion(Sink> sink) => @@ -167,7 +166,8 @@ class _Sink extends ByteConversionSinkBase { throw new FormatException( "Invalid hexadecimal byte 0x${byte.toRadixString(16).toUpperCase()}.", - bytes, index); + bytes, + index); } } diff --git a/pkgs/http_parser/lib/src/http_date.dart b/pkgs/http_parser/lib/src/http_date.dart index 8751b78c8f..299f9e9c28 100644 --- a/pkgs/http_parser/lib/src/http_date.dart +++ b/pkgs/http_parser/lib/src/http_date.dart @@ -7,8 +7,20 @@ import 'package:string_scanner/string_scanner.dart'; import 'utils.dart'; const _WEEKDAYS = const ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]; -const _MONTHS = const ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", - "Sep", "Oct", "Nov", "Dec"]; +const _MONTHS = const [ + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec" +]; final _shortWeekdayRegExp = new RegExp(r"Mon|Tue|Wed|Thu|Fri|Sat|Sun"); final _longWeekdayRegExp = diff --git a/pkgs/http_parser/lib/src/media_type.dart b/pkgs/http_parser/lib/src/media_type.dart index 37dd6be1fb..22283d67ac 100644 --- a/pkgs/http_parser/lib/src/media_type.dart +++ b/pkgs/http_parser/lib/src/media_type.dart @@ -21,12 +21,12 @@ final _escapedChar = new RegExp(r'["\x00-\x1F\x7F]'); class MediaType { /// The primary identifier of the MIME type. /// - /// This is always lowercase. + /// This is always lowercase. final String type; /// The secondary identifier of the MIME type. /// - /// This is always lowercase. + /// This is always lowercase. final String subtype; /// The parameters to the media type. @@ -91,8 +91,12 @@ class MediaType { /// [parameters] overwrites and adds to the corresponding field. If /// [clearParameters] is passed, it replaces the corresponding field entirely /// instead. - MediaType change({String type, String subtype, String mimeType, - Map parameters, bool clearParameters: false}) { + MediaType change( + {String type, + String subtype, + String mimeType, + Map parameters, + bool clearParameters: false}) { if (mimeType != null) { if (type != null) { throw new ArgumentError("You may not pass both [type] and [mimeType]."); @@ -127,10 +131,7 @@ class MediaType { /// /// This will produce a valid HTTP media type. String toString() { - var buffer = new StringBuffer() - ..write(type) - ..write("/") - ..write(subtype); + var buffer = new StringBuffer()..write(type)..write("/")..write(subtype); parameters.forEach((attribute, value) { buffer.write("; $attribute="); diff --git a/pkgs/http_parser/lib/src/scan.dart b/pkgs/http_parser/lib/src/scan.dart index 9af8428a21..6ba7a725fb 100644 --- a/pkgs/http_parser/lib/src/scan.dart +++ b/pkgs/http_parser/lib/src/scan.dart @@ -38,7 +38,7 @@ final whitespace = new RegExp("(?:${_lws.pattern})*"); /// Once this is finished, [scanner] will be at the next non-LWS character in /// the string, or the end of the string. List/**/ parseList/**/(StringScanner scanner, /*=T*/ parseElement()) { - var result = /**/[]; + var result = /**/ []; // Consume initial empty values. while (scanner.scan(",")) { diff --git a/pkgs/http_parser/lib/src/utils.dart b/pkgs/http_parser/lib/src/utils.dart index 79ce448682..3bc6b6625a 100644 --- a/pkgs/http_parser/lib/src/utils.dart +++ b/pkgs/http_parser/lib/src/utils.dart @@ -16,8 +16,6 @@ import 'package:source_span/source_span.dart'; 'Invalid $name: ${error.message}', error.span, error.source); } on FormatException catch (error) { throw new FormatException( - 'Invalid $name "$value": ${error.message}', - error.source, - error.offset); + 'Invalid $name "$value": ${error.message}', error.source, error.offset); } } diff --git a/pkgs/http_parser/test/authentication_challenge_test.dart b/pkgs/http_parser/test/authentication_challenge_test.dart index 4be4444aba..b6478078d7 100644 --- a/pkgs/http_parser/test/authentication_challenge_test.dart +++ b/pkgs/http_parser/test/authentication_challenge_test.dart @@ -21,8 +21,8 @@ void main() { }); test("parses multiple challenges", () { - var challenges = AuthenticationChallenge.parseHeader( - "scheme1 realm=fblthp, scheme2 realm=asdfg"); + var challenges = AuthenticationChallenge + .parseHeader("scheme1 realm=fblthp, scheme2 realm=asdfg"); expect(challenges, hasLength(2)); expect(challenges.first.scheme, equals("scheme1")); expect(challenges.first.parameters, equals({"realm": "fblthp"})); @@ -36,16 +36,12 @@ void main() { expect(challenges, hasLength(2)); expect(challenges.first.scheme, equals("scheme1")); - expect(challenges.first.parameters, equals({ - "realm": "fblthp", - "foo": "bar" - })); + expect(challenges.first.parameters, + equals({"realm": "fblthp", "foo": "bar"})); expect(challenges.last.scheme, equals("scheme2")); - expect(challenges.last.parameters, equals({ - "realm": "asdfg", - "baz": "bang" - })); + expect(challenges.last.parameters, + equals({"realm": "asdfg", "baz": "bang"})); }); }); } @@ -66,20 +62,15 @@ void _singleChallengeTests( test("parses multiple parameters", () { var challenge = parseChallenge("scheme realm=fblthp, foo=bar, baz=qux"); expect(challenge.scheme, equals("scheme")); - expect(challenge.parameters, equals({ - "realm": "fblthp", - "foo": "bar", - "baz": "qux" - })); + expect(challenge.parameters, + equals({"realm": "fblthp", "foo": "bar", "baz": "qux"})); }); test("parses quoted string parameters", () { var challenge = parseChallenge('scheme realm="fblthp, foo=bar", baz="qux"'); expect(challenge.scheme, equals("scheme")); - expect(challenge.parameters, equals({ - "realm": "fblthp, foo=bar", - "baz": "qux" - })); + expect(challenge.parameters, + equals({"realm": "fblthp, foo=bar", "baz": "qux"})); }); test("normalizes the case of the scheme", () { @@ -105,66 +96,46 @@ void _singleChallengeTests( var challenge = parseChallenge( " scheme\t \trealm\t = \tfblthp\t, \tfoo\t\r\n =\tbar\t"); expect(challenge.scheme, equals("scheme")); - expect(challenge.parameters, equals({ - "realm": "fblthp", - "foo": "bar" - })); + expect(challenge.parameters, equals({"realm": "fblthp", "foo": "bar"})); }); test("allows an empty parameter", () { - var challenge = parseChallenge( - "scheme realm=fblthp, , foo=bar"); + var challenge = parseChallenge("scheme realm=fblthp, , foo=bar"); expect(challenge.scheme, equals("scheme")); - expect(challenge.parameters, equals({ - "realm": "fblthp", - "foo": "bar" - })); + expect(challenge.parameters, equals({"realm": "fblthp", "foo": "bar"})); }); test("allows a leading comma", () { - var challenge = parseChallenge( - "scheme , realm=fblthp, foo=bar,"); + var challenge = parseChallenge("scheme , realm=fblthp, foo=bar,"); expect(challenge.scheme, equals("scheme")); - expect(challenge.parameters, equals({ - "realm": "fblthp", - "foo": "bar" - })); + expect(challenge.parameters, equals({"realm": "fblthp", "foo": "bar"})); }); test("allows a trailing comma", () { - var challenge = parseChallenge( - "scheme realm=fblthp, foo=bar, ,"); + var challenge = parseChallenge("scheme realm=fblthp, foo=bar, ,"); expect(challenge.scheme, equals("scheme")); - expect(challenge.parameters, equals({ - "realm": "fblthp", - "foo": "bar" - })); + expect(challenge.parameters, equals({"realm": "fblthp", "foo": "bar"})); }); test("disallows only a scheme", () { - expect(() => parseChallenge("scheme"), - throwsFormatException); + expect(() => parseChallenge("scheme"), throwsFormatException); }); test("disallows a valueless parameter", () { - expect(() => parseChallenge("scheme realm"), - throwsFormatException); - expect(() => parseChallenge("scheme realm="), - throwsFormatException); - expect(() => parseChallenge("scheme realm, foo=bar"), - throwsFormatException); + expect(() => parseChallenge("scheme realm"), throwsFormatException); + expect(() => parseChallenge("scheme realm="), throwsFormatException); + expect( + () => parseChallenge("scheme realm, foo=bar"), throwsFormatException); }); test("requires a space after the scheme", () { - expect(() => parseChallenge("scheme\trealm"), - throwsFormatException); - expect(() => parseChallenge("scheme\r\n\trealm="), - throwsFormatException); + expect(() => parseChallenge("scheme\trealm"), throwsFormatException); + expect(() => parseChallenge("scheme\r\n\trealm="), throwsFormatException); }); test("disallows junk after the parameters", () { - expect(() => parseChallenge("scheme realm=fblthp foo"), - throwsFormatException); + expect( + () => parseChallenge("scheme realm=fblthp foo"), throwsFormatException); expect(() => parseChallenge("scheme realm=fblthp, foo=bar baz"), throwsFormatException); }); diff --git a/pkgs/http_parser/test/chunked_coding_test.dart b/pkgs/http_parser/test/chunked_coding_test.dart index afe9b51473..4d4a602587 100644 --- a/pkgs/http_parser/test/chunked_coding_test.dart +++ b/pkgs/http_parser/test/chunked_coding_test.dart @@ -19,7 +19,8 @@ void main() { test("uses hex for chunk size", () { var data = new Iterable.generate(0xA7).toList(); - expect(chunkedCoding.encode(data), + expect( + chunkedCoding.encode(data), equals([$a, $7, $cr, $lf] ..addAll(data) ..addAll([$cr, $lf, $0, $cr, $lf, $cr, $lf]))); @@ -41,20 +42,28 @@ void main() { test("adds headers to each chunk of bytes", () { sink.add([1, 2, 3, 4]); - expect(results, equals([[$4, $cr, $lf, 1, 2, 3, 4, $cr, $lf]])); + expect( + results, + equals([ + [$4, $cr, $lf, 1, 2, 3, 4, $cr, $lf] + ])); sink.add([5, 6, 7]); - expect(results, equals([ - [$4, $cr, $lf, 1, 2, 3, 4, $cr, $lf], - [$3, $cr, $lf, 5, 6, 7, $cr, $lf], - ])); + expect( + results, + equals([ + [$4, $cr, $lf, 1, 2, 3, 4, $cr, $lf], + [$3, $cr, $lf, 5, 6, 7, $cr, $lf], + ])); sink.close(); - expect(results, equals([ - [$4, $cr, $lf, 1, 2, 3, 4, $cr, $lf], - [$3, $cr, $lf, 5, 6, 7, $cr, $lf], - [$0, $cr, $lf, $cr, $lf], - ])); + expect( + results, + equals([ + [$4, $cr, $lf, 1, 2, 3, 4, $cr, $lf], + [$3, $cr, $lf, 5, 6, 7, $cr, $lf], + [$0, $cr, $lf, $cr, $lf], + ])); }); test("handles empty chunks", () { @@ -62,24 +71,41 @@ void main() { expect(results, equals([[]])); sink.add([1, 2, 3]); - expect(results, equals([[], [$3, $cr, $lf, 1, 2, 3, $cr, $lf]])); + expect( + results, + equals([ + [], + [$3, $cr, $lf, 1, 2, 3, $cr, $lf] + ])); sink.add([]); - expect(results, equals([[], [$3, $cr, $lf, 1, 2, 3, $cr, $lf], []])); + expect( + results, + equals([ + [], + [$3, $cr, $lf, 1, 2, 3, $cr, $lf], + [] + ])); sink.close(); - expect(results, equals([ - [], - [$3, $cr, $lf, 1, 2, 3, $cr, $lf], - [], - [$0, $cr, $lf, $cr, $lf], - ])); + expect( + results, + equals([ + [], + [$3, $cr, $lf, 1, 2, 3, $cr, $lf], + [], + [$0, $cr, $lf, $cr, $lf], + ])); }); group("addSlice()", () { test("adds bytes from the specified slice", () { sink.addSlice([1, 2, 3, 4, 5], 1, 4, false); - expect(results, equals([[$3, $cr, $lf, 2, 3, 4, $cr, $lf]])); + expect( + results, + equals([ + [$3, $cr, $lf, 2, 3, 4, $cr, $lf] + ])); }); test("doesn't add a header if the slice is empty", () { @@ -89,8 +115,11 @@ void main() { test("adds a footer if isLast is true", () { sink.addSlice([1, 2, 3, 4, 5], 1, 4, true); - expect(results, equals( - [[$3, $cr, $lf, 2, 3, 4, $cr, $lf, $0, $cr, $lf, $cr, $lf]])); + expect( + results, + equals([ + [$3, $cr, $lf, 2, 3, 4, $cr, $lf, $0, $cr, $lf, $cr, $lf] + ])); // Setting isLast shuld close the sink. expect(() => sink.add([]), throwsStateError); @@ -118,11 +147,32 @@ void main() { group("decoder", () { test("parses chunked data", () { - expect(chunkedCoding.decode([ - $3, $cr, $lf, 1, 2, 3, $cr, $lf, - $4, $cr, $lf, 4, 5, 6, 7, $cr, $lf, - $0, $cr, $lf, $cr, $lf, - ]), equals([1, 2, 3, 4, 5, 6, 7])); + expect( + chunkedCoding.decode([ + $3, + $cr, + $lf, + 1, + 2, + 3, + $cr, + $lf, + $4, + $cr, + $lf, + 4, + 5, + 6, + 7, + $cr, + $lf, + $0, + $cr, + $lf, + $cr, + $lf, + ]), + equals([1, 2, 3, 4, 5, 6, 7])); }); test("parses hex size", () { @@ -161,8 +211,8 @@ void main() { }); test("that ends after LF", () { - expect(() => chunkedCoding.decode([$a, $cr, $lf]), - throwsFormatException); + expect( + () => chunkedCoding.decode([$a, $cr, $lf]), throwsFormatException); }); test("that ends after insufficient bytes", () { @@ -186,8 +236,8 @@ void main() { }); test("that ends after the empty chunk", () { - expect(() => chunkedCoding.decode([$0, $cr, $lf]), - throwsFormatException); + expect( + () => chunkedCoding.decode([$0, $cr, $lf]), throwsFormatException); }); test("that ends after the closing CR", () { @@ -219,14 +269,28 @@ void main() { test("decodes each chunk of bytes", () { sink.add([$4, $cr, $lf, 1, 2, 3, 4, $cr, $lf]); - expect(results, equals([[1, 2, 3, 4]])); + expect( + results, + equals([ + [1, 2, 3, 4] + ])); sink.add([$3, $cr, $lf, 5, 6, 7, $cr, $lf]); - expect(results, equals([[1, 2, 3, 4], [5, 6, 7]])); + expect( + results, + equals([ + [1, 2, 3, 4], + [5, 6, 7] + ])); sink.add([$0, $cr, $lf, $cr, $lf]); sink.close(); - expect(results, equals([[1, 2, 3, 4], [5, 6, 7]])); + expect( + results, + equals([ + [1, 2, 3, 4], + [5, 6, 7] + ])); }); test("handles empty chunks", () { @@ -234,14 +298,26 @@ void main() { expect(results, isEmpty); sink.add([$3, $cr, $lf, 1, 2, 3, $cr, $lf]); - expect(results, equals([[1, 2, 3]])); + expect( + results, + equals([ + [1, 2, 3] + ])); sink.add([]); - expect(results, equals([[1, 2, 3]])); + expect( + results, + equals([ + [1, 2, 3] + ])); sink.add([$0, $cr, $lf, $cr, $lf]); sink.close(); - expect(results, equals([[1, 2, 3]])); + expect( + results, + equals([ + [1, 2, 3] + ])); }); test("throws if the sink is closed before the message is done", () { @@ -264,7 +340,11 @@ void main() { expect(results, isEmpty); sink.add([$cr, $lf, 1, 2, 3]); - expect(results, equals([[1, 2, 3]])); + expect( + results, + equals([ + [1, 2, 3] + ])); }); test("after CR", () { @@ -272,7 +352,11 @@ void main() { expect(results, isEmpty); sink.add([$lf, 1, 2, 3]); - expect(results, equals([[1, 2, 3]])); + expect( + results, + equals([ + [1, 2, 3] + ])); }); test("after LF", () { @@ -280,39 +364,79 @@ void main() { expect(results, isEmpty); sink.add([1, 2, 3]); - expect(results, equals([[1, 2, 3]])); + expect( + results, + equals([ + [1, 2, 3] + ])); }); test("after some bytes", () { sink.add([$3, $cr, $lf, 1, 2]); - expect(results, equals([[1, 2]])); + expect( + results, + equals([ + [1, 2] + ])); sink.add([3]); - expect(results, equals([[1, 2], [3]])); + expect( + results, + equals([ + [1, 2], + [3] + ])); }); test("after all bytes", () { sink.add([$3, $cr, $lf, 1, 2, 3]); - expect(results, equals([[1, 2, 3]])); + expect( + results, + equals([ + [1, 2, 3] + ])); sink.add([$cr, $lf, $3, $cr, $lf, 2, 3, 4, $cr, $lf]); - expect(results, equals([[1, 2, 3], [2, 3, 4]])); + expect( + results, + equals([ + [1, 2, 3], + [2, 3, 4] + ])); }); test("after a post-chunk CR", () { sink.add([$3, $cr, $lf, 1, 2, 3, $cr]); - expect(results, equals([[1, 2, 3]])); + expect( + results, + equals([ + [1, 2, 3] + ])); sink.add([$lf, $3, $cr, $lf, 2, 3, 4, $cr, $lf]); - expect(results, equals([[1, 2, 3], [2, 3, 4]])); + expect( + results, + equals([ + [1, 2, 3], + [2, 3, 4] + ])); }); test("after a post-chunk LF", () { sink.add([$3, $cr, $lf, 1, 2, 3, $cr, $lf]); - expect(results, equals([[1, 2, 3]])); + expect( + results, + equals([ + [1, 2, 3] + ])); sink.add([$3, $cr, $lf, 2, 3, 4, $cr, $lf]); - expect(results, equals([[1, 2, 3], [2, 3, 4]])); + expect( + results, + equals([ + [1, 2, 3], + [2, 3, 4] + ])); }); test("after empty chunk size", () { @@ -363,7 +487,11 @@ void main() { group("addSlice()", () { test("adds bytes from the specified slice", () { sink.addSlice([1, $3, $cr, $lf, 2, 3, 4, 5], 1, 7, false); - expect(results, equals([[2, 3, 4]])); + expect( + results, + equals([ + [2, 3, 4] + ])); }); test("doesn't decode if the slice is empty", () { diff --git a/pkgs/http_parser/test/media_type_test.dart b/pkgs/http_parser/test/media_type_test.dart index 01c58d4d9e..be30b63037 100644 --- a/pkgs/http_parser/test/media_type_test.dart +++ b/pkgs/http_parser/test/media_type_test.dart @@ -78,10 +78,7 @@ void main() { test("records parameters as case-insensitive", () { var type = new MediaType.parse('test/plain;FoO=bar;bAz=bang'); - expect(type.parameters, equals({ - "FoO": "bar", - "bAz": "bang" - })); + expect(type.parameters, equals({"FoO": "bar", "bAz": "bang"})); expect(type.parameters, containsPair("foo", "bar")); expect(type.parameters, containsPair("baz", "bang")); }); @@ -162,8 +159,10 @@ void main() { }); test("serializes multiple parameters", () { - expect(new MediaType("text", "plain", {"foo": "bar", "baz": "bang"}) - .toString(), equals("text/plain; foo=bar; baz=bang")); + expect( + new MediaType("text", "plain", {"foo": "bar", "baz": "bang"}) + .toString(), + equals("text/plain; foo=bar; baz=bang")); }); }); } From 784b251fd2db294601651271503e60a048918470 Mon Sep 17 00:00:00 2001 From: Keerti Parthasarathy Date: Mon, 31 Jul 2017 10:33:36 -0700 Subject: [PATCH 043/126] Update comment style generic syntax BUG= R=nbosch@google.com Review-Url: https://codereview.chromium.org//2993483002 . --- pkgs/http_parser/lib/src/scan.dart | 11 +++++++++-- pkgs/http_parser/lib/src/utils.dart | 2 +- pkgs/http_parser/pubspec.yaml | 4 ++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/pkgs/http_parser/lib/src/scan.dart b/pkgs/http_parser/lib/src/scan.dart index 6ba7a725fb..32310725f5 100644 --- a/pkgs/http_parser/lib/src/scan.dart +++ b/pkgs/http_parser/lib/src/scan.dart @@ -10,6 +10,13 @@ /// /// [spec]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html import 'package:string_scanner/string_scanner.dart'; +/// HTTP entities. +/// +/// Many of the regular expressions come from [section 2.2 of the HTTP +/// spec][spec]. +/// +/// [spec]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html + /// An HTTP token. final token = new RegExp(r'[^()<>@,;:"\\/[\]?={} \t\x00-\x1F\x7F]+'); @@ -37,8 +44,8 @@ final whitespace = new RegExp("(?:${_lws.pattern})*"); /// /// Once this is finished, [scanner] will be at the next non-LWS character in /// the string, or the end of the string. -List/**/ parseList/**/(StringScanner scanner, /*=T*/ parseElement()) { - var result = /**/ []; +List parseList(StringScanner scanner, T parseElement()) { + var result = []; // Consume initial empty values. while (scanner.scan(",")) { diff --git a/pkgs/http_parser/lib/src/utils.dart b/pkgs/http_parser/lib/src/utils.dart index 3bc6b6625a..f2189f247a 100644 --- a/pkgs/http_parser/lib/src/utils.dart +++ b/pkgs/http_parser/lib/src/utils.dart @@ -8,7 +8,7 @@ import 'package:source_span/source_span.dart'; /// /// [name] should describe the type of thing being parsed, and [value] should be /// its actual value. -/*=T*/ wrapFormatException/**/(String name, String value, /*=T*/ body()) { +T wrapFormatException(String name, String value, T body()) { try { return body(); } on SourceSpanFormatException catch (error) { diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index e2faf4c3e7..7e20b41d38 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,5 +1,5 @@ name: http_parser -version: 3.1.1 +version: 3.1.2-dev author: "Dart Team " homepage: https://github.com/dart-lang/http_parser description: > @@ -13,4 +13,4 @@ dependencies: dev_dependencies: test: "^0.12.0" environment: - sdk: ">=1.8.0 <2.0.0" + sdk: ">=1.21.0 <2.0.0-dev.infinity" From baf17debb8279beb9ec1eece1efd21616bf98b24 Mon Sep 17 00:00:00 2001 From: Keerti Parthasarathy Date: Mon, 31 Jul 2017 11:15:16 -0700 Subject: [PATCH 044/126] format file using 1.25.0-dev.8.0 (dart-lang/http_parser#14) * format file using 1.25.0-dev.8.0 * remove unsupported stable sdk version from travis cfg --- pkgs/http_parser/.travis.yml | 2 -- pkgs/http_parser/lib/src/scan.dart | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/pkgs/http_parser/.travis.yml b/pkgs/http_parser/.travis.yml index b5590b3aed..df0d92c8c0 100644 --- a/pkgs/http_parser/.travis.yml +++ b/pkgs/http_parser/.travis.yml @@ -5,8 +5,6 @@ dart: - stable - 1.22.1 - 1.21.1 - - 1.20.1 - - 1.19.1 cache: directories: - $HOME/.pub-cache diff --git a/pkgs/http_parser/lib/src/scan.dart b/pkgs/http_parser/lib/src/scan.dart index 32310725f5..3710062dbc 100644 --- a/pkgs/http_parser/lib/src/scan.dart +++ b/pkgs/http_parser/lib/src/scan.dart @@ -10,6 +10,7 @@ /// /// [spec]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html import 'package:string_scanner/string_scanner.dart'; + /// HTTP entities. /// /// Many of the regular expressions come from [section 2.2 of the HTTP @@ -17,7 +18,6 @@ import 'package:string_scanner/string_scanner.dart'; /// /// [spec]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html - /// An HTTP token. final token = new RegExp(r'[^()<>@,;:"\\/[\]?={} \t\x00-\x1F\x7F]+'); From 7eba0eb0f2535fd70cc25fed3eaf4c5bfaea3b8c Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 12 Sep 2017 15:38:32 -0700 Subject: [PATCH 045/126] Fix doc comment references --- pkgs/http_parser/lib/src/chunked_coding.dart | 10 +++++----- pkgs/http_parser/lib/src/chunked_coding/decoder.dart | 2 +- pkgs/http_parser/lib/src/media_type.dart | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pkgs/http_parser/lib/src/chunked_coding.dart b/pkgs/http_parser/lib/src/chunked_coding.dart index 8d2326cbfc..11353393ff 100644 --- a/pkgs/http_parser/lib/src/chunked_coding.dart +++ b/pkgs/http_parser/lib/src/chunked_coding.dart @@ -10,7 +10,7 @@ import 'chunked_coding/decoder.dart'; export 'chunked_coding/encoder.dart' hide chunkedCodingEncoder; export 'chunked_coding/decoder.dart' hide chunkedCodingDecoder; -/// The canonical instance of [ChunkedCodec]. +/// The canonical instance of [ChunkedCodingCodec]. const chunkedCoding = const ChunkedCodingCodec._(); /// A codec that encodes and decodes the [chunked transfer coding][]. @@ -18,10 +18,10 @@ const chunkedCoding = const ChunkedCodingCodec._(); /// [chunked transfer coding]: https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1 /// /// The [encoder] creates a *single* chunked message for each call to -/// [ChunkedEncoder.convert] or [ChunkedEncoder.startChunkedConversion]. This -/// means that it will always add an end-of-message footer once conversion has -/// finished. It doesn't support generating chunk extensions or trailing -/// headers. +/// [ChunkedCodingEncoder.convert] or +/// [ChunkedCodingEncoder.startChunkedConversion]. This means that it will +/// always add an end-of-message footer once conversion has finished. It doesn't +/// support generating chunk extensions or trailing headers. /// /// Similarly, the [decoder] decodes a *single* chunked message into a stream of /// byte arrays that must be concatenated to get the full list (like most Dart diff --git a/pkgs/http_parser/lib/src/chunked_coding/decoder.dart b/pkgs/http_parser/lib/src/chunked_coding/decoder.dart index 3ffa520f97..b20c2d3819 100644 --- a/pkgs/http_parser/lib/src/chunked_coding/decoder.dart +++ b/pkgs/http_parser/lib/src/chunked_coding/decoder.dart @@ -139,7 +139,7 @@ class _Sink extends ByteConversionSinkBase { } /// Returns the hex digit (0 through 15) corresponding to the byte at index - /// [i] in [bytes]. + /// [index] in [bytes]. /// /// If the given byte isn't a hexadecimal ASCII character, throws a /// [FormatException]. diff --git a/pkgs/http_parser/lib/src/media_type.dart b/pkgs/http_parser/lib/src/media_type.dart index 22283d67ac..7fb29300c8 100644 --- a/pkgs/http_parser/lib/src/media_type.dart +++ b/pkgs/http_parser/lib/src/media_type.dart @@ -60,7 +60,7 @@ class MediaType { var attribute = scanner.lastMatch[0]; scanner.expect('='); - var value; + String value; if (scanner.scan(token)) { value = scanner.lastMatch[0]; } else { From e9ff785778a4a68a1747dae3c2c806320e0563d6 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 12 Sep 2017 15:41:42 -0700 Subject: [PATCH 046/126] Only test oldest supported and dev + stable Speed up travis times --- pkgs/http_parser/.travis.yml | 1 - pkgs/http_parser/pubspec.yaml | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pkgs/http_parser/.travis.yml b/pkgs/http_parser/.travis.yml index df0d92c8c0..d2c97e387e 100644 --- a/pkgs/http_parser/.travis.yml +++ b/pkgs/http_parser/.travis.yml @@ -3,7 +3,6 @@ sudo: false dart: - dev - stable - - 1.22.1 - 1.21.1 cache: directories: diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index 7e20b41d38..c5920d6a66 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -4,6 +4,8 @@ author: "Dart Team " homepage: https://github.com/dart-lang/http_parser description: > A platform-independent package for parsing and serializing HTTP formats. +environment: + sdk: ">=1.21.0 <2.0.0-dev.infinity" dependencies: charcode: "^1.1.0" collection: ">=0.9.1 <2.0.0" @@ -12,5 +14,3 @@ dependencies: typed_data: "^1.1.0" dev_dependencies: test: "^0.12.0" -environment: - sdk: ">=1.21.0 <2.0.0-dev.infinity" From bad13d9b41343f29ef678c227b1334c2e982de1b Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 12 Sep 2017 15:50:00 -0700 Subject: [PATCH 047/126] Travis: only test master branch and pull requests --- pkgs/http_parser/.travis.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pkgs/http_parser/.travis.yml b/pkgs/http_parser/.travis.yml index d2c97e387e..f636d45017 100644 --- a/pkgs/http_parser/.travis.yml +++ b/pkgs/http_parser/.travis.yml @@ -4,9 +4,7 @@ dart: - dev - stable - 1.21.1 -cache: - directories: - - $HOME/.pub-cache + dart_task: - test: --platform vm xvfb: false @@ -15,3 +13,11 @@ dart_task: install_dartium: true - 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 From 6c7582ad4cec1643b0af2af2b869220f67a59abf Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 12 Sep 2017 16:24:13 -0700 Subject: [PATCH 048/126] Bump min SDK and update changelog (dart-lang/http_parser#16) We cannot reliably test on Travis pre 1.23.0 --- pkgs/http_parser/.travis.yml | 2 +- pkgs/http_parser/CHANGELOG.md | 6 ++++++ pkgs/http_parser/pubspec.yaml | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pkgs/http_parser/.travis.yml b/pkgs/http_parser/.travis.yml index f636d45017..24b2b60ce3 100644 --- a/pkgs/http_parser/.travis.yml +++ b/pkgs/http_parser/.travis.yml @@ -3,7 +3,7 @@ sudo: false dart: - dev - stable - - 1.21.1 + - 1.23.0 dart_task: - test: --platform vm diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index 2c50995f58..717cbe9594 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,3 +1,9 @@ +## 3.1.2 + +* Require Dart SDK 1.23 or greater. + +* A number of strong-mode fixes. + ## 3.1.1 * Fix a logic bug in the `chunkedCoding` codec. It had been producing invalid diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index c5920d6a66..1b87c767e0 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -5,7 +5,7 @@ homepage: https://github.com/dart-lang/http_parser description: > A platform-independent package for parsing and serializing HTTP formats. environment: - sdk: ">=1.21.0 <2.0.0-dev.infinity" + sdk: ">=1.23.0 <2.0.0-dev.infinity" dependencies: charcode: "^1.1.0" collection: ">=0.9.1 <2.0.0" From 73ac35ea954ef63fb1eaa69dd14c3449dfa3b3cf Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 18 Sep 2017 18:10:46 -0700 Subject: [PATCH 049/126] Remove -dev.infinity in SDK upper constraint --- pkgs/http_parser/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index 1b87c767e0..0c209d22b4 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -5,7 +5,7 @@ homepage: https://github.com/dart-lang/http_parser description: > A platform-independent package for parsing and serializing HTTP formats. environment: - sdk: ">=1.23.0 <2.0.0-dev.infinity" + sdk: ">=1.23.0 <2.0.0" dependencies: charcode: "^1.1.0" collection: ">=0.9.1 <2.0.0" From 40aea369412f3e54aef1d570fa53079ddf2c2c82 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 13 Feb 2018 09:09:38 -0800 Subject: [PATCH 050/126] Stop running tests with Dartium --- pkgs/http_parser/.travis.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pkgs/http_parser/.travis.yml b/pkgs/http_parser/.travis.yml index 24b2b60ce3..8239344da7 100644 --- a/pkgs/http_parser/.travis.yml +++ b/pkgs/http_parser/.travis.yml @@ -1,5 +1,5 @@ language: dart -sudo: false + dart: - dev - stable @@ -9,8 +9,6 @@ dart_task: - test: --platform vm xvfb: false - test: --platform firefox - - test: --platform dartium - install_dartium: true - dartfmt - dartanalyzer From cdbce462563217c9a359aa177d4152b494f6d9f3 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 2 May 2018 14:00:04 -0700 Subject: [PATCH 051/126] Release 3.1.2 (dart-lang/http_parser#18) --- pkgs/http_parser/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index 0c209d22b4..2a5fcebe63 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,5 +1,5 @@ name: http_parser -version: 3.1.2-dev +version: 3.1.2 author: "Dart Team " homepage: https://github.com/dart-lang/http_parser description: > From b4c70ffde17faf9e93077728a271b99491313b10 Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Wed, 23 May 2018 09:13:35 +0200 Subject: [PATCH 052/126] Remove upper case constants (dart-lang/http_parser#17) * Remove usage of upper-case constants. * update SDK version * Remove 1.23.0 from travis config. --- pkgs/http_parser/.travis.yml | 2 -- pkgs/http_parser/CHANGELOG.md | 2 +- pkgs/http_parser/pubspec.yaml | 2 +- pkgs/http_parser/test/http_date_test.dart | 8 ++++---- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/pkgs/http_parser/.travis.yml b/pkgs/http_parser/.travis.yml index 8239344da7..c724bab191 100644 --- a/pkgs/http_parser/.travis.yml +++ b/pkgs/http_parser/.travis.yml @@ -2,8 +2,6 @@ language: dart dart: - dev - - stable - - 1.23.0 dart_task: - test: --platform vm diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index 717cbe9594..11e0c50c22 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,6 +1,6 @@ ## 3.1.2 -* Require Dart SDK 1.23 or greater. +* Require Dart SDK 2.0.0-dev.17.0 or greater. * A number of strong-mode fixes. diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index 2a5fcebe63..b33887c6a3 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -5,7 +5,7 @@ homepage: https://github.com/dart-lang/http_parser description: > A platform-independent package for parsing and serializing HTTP formats. environment: - sdk: ">=1.23.0 <2.0.0" + sdk: ">=2.0.0-dev.17.0 <2.0.0" dependencies: charcode: "^1.1.0" collection: ">=0.9.1 <2.0.0" diff --git a/pkgs/http_parser/test/http_date_test.dart b/pkgs/http_parser/test/http_date_test.dart index 02b7c6f8d1..887a2c4cb9 100644 --- a/pkgs/http_parser/test/http_date_test.dart +++ b/pkgs/http_parser/test/http_date_test.dart @@ -43,7 +43,7 @@ void main() { test("parses the example date", () { var date = parseHttpDate("Sun, 06 Nov 1994 08:49:37 GMT"); expect(date.day, equals(6)); - expect(date.month, equals(DateTime.NOVEMBER)); + expect(date.month, equals(DateTime.november)); expect(date.year, equals(1994)); expect(date.hour, equals(8)); expect(date.minute, equals(49)); @@ -147,7 +147,7 @@ void main() { test("parses the example date", () { var date = parseHttpDate("Sunday, 06-Nov-94 08:49:37 GMT"); expect(date.day, equals(6)); - expect(date.month, equals(DateTime.NOVEMBER)); + expect(date.month, equals(DateTime.november)); expect(date.year, equals(1994)); expect(date.hour, equals(8)); expect(date.minute, equals(49)); @@ -239,7 +239,7 @@ void main() { test("parses the example date", () { var date = parseHttpDate("Sun Nov 6 08:49:37 1994"); expect(date.day, equals(6)); - expect(date.month, equals(DateTime.NOVEMBER)); + expect(date.month, equals(DateTime.november)); expect(date.year, equals(1994)); expect(date.hour, equals(8)); expect(date.minute, equals(49)); @@ -250,7 +250,7 @@ void main() { test("parses a date with a two-digit day", () { var date = parseHttpDate("Sun Nov 16 08:49:37 1994"); expect(date.day, equals(16)); - expect(date.month, equals(DateTime.NOVEMBER)); + expect(date.month, equals(DateTime.november)); expect(date.year, equals(1994)); expect(date.hour, equals(8)); expect(date.minute, equals(49)); From ff6d6127e4a97ea69524fb67a6c49b330fbdaa21 Mon Sep 17 00:00:00 2001 From: BC Ko Date: Thu, 24 May 2018 09:02:59 -0700 Subject: [PATCH 053/126] Update .gitignore to new `dart_tool` pub cache (dart-lang/http_parser#19) dart-lang/sdkdart-lang/http_parser#32030 --- pkgs/http_parser/.gitignore | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/pkgs/http_parser/.gitignore b/pkgs/http_parser/.gitignore index 7dbf0350d6..fb97bdebdc 100644 --- a/pkgs/http_parser/.gitignore +++ b/pkgs/http_parser/.gitignore @@ -1,15 +1,5 @@ # Don’t commit the following directories created by pub. -.buildlog +.dart_tool/ .pub/ -build/ -packages .packages - -# Or the files created by dart2js. -*.dart.js -*.js_ -*.js.deps -*.js.map - -# Include when developing application packages. -pubspec.lock \ No newline at end of file +pubspec.lock From 999c2c60f4476b8dc54f0ea867d56176a5ba7d8c Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 9 Jul 2018 08:36:50 -0700 Subject: [PATCH 054/126] dartfmt --- pkgs/http_parser/test/authentication_challenge_test.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http_parser/test/authentication_challenge_test.dart b/pkgs/http_parser/test/authentication_challenge_test.dart index b6478078d7..68a49cae02 100644 --- a/pkgs/http_parser/test/authentication_challenge_test.dart +++ b/pkgs/http_parser/test/authentication_challenge_test.dart @@ -21,8 +21,8 @@ void main() { }); test("parses multiple challenges", () { - var challenges = AuthenticationChallenge - .parseHeader("scheme1 realm=fblthp, scheme2 realm=asdfg"); + var challenges = AuthenticationChallenge.parseHeader( + "scheme1 realm=fblthp, scheme2 realm=asdfg"); expect(challenges, hasLength(2)); expect(challenges.first.scheme, equals("scheme1")); expect(challenges.first.parameters, equals({"realm": "fblthp"})); From fc17b283444c122319d697f5e831664e351b0fb9 Mon Sep 17 00:00:00 2001 From: Patrice Chalin Date: Thu, 19 Jul 2018 15:08:23 -0400 Subject: [PATCH 055/126] chore: set max SDK version to <3.0.0 (dart-lang/http_parser#22) --- pkgs/http_parser/CHANGELOG.md | 4 ++++ pkgs/http_parser/analysis_options.yaml | 2 -- pkgs/http_parser/pubspec.yaml | 24 ++++++++++++++---------- 3 files changed, 18 insertions(+), 12 deletions(-) delete mode 100644 pkgs/http_parser/analysis_options.yaml diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index 11e0c50c22..7f84286625 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.1.3 + +* Set max SDK version to `<3.0.0`, and adjust other dependencies. + ## 3.1.2 * Require Dart SDK 2.0.0-dev.17.0 or greater. diff --git a/pkgs/http_parser/analysis_options.yaml b/pkgs/http_parser/analysis_options.yaml deleted file mode 100644 index a10d4c5a05..0000000000 --- a/pkgs/http_parser/analysis_options.yaml +++ /dev/null @@ -1,2 +0,0 @@ -analyzer: - strong-mode: true diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index b33887c6a3..585c91c678 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,16 +1,20 @@ name: http_parser -version: 3.1.2 -author: "Dart Team " -homepage: https://github.com/dart-lang/http_parser +version: 3.1.3 + description: > A platform-independent package for parsing and serializing HTTP formats. +author: Dart Team +homepage: https://github.com/dart-lang/http_parser + environment: - sdk: ">=2.0.0-dev.17.0 <2.0.0" + sdk: '>=2.0.0-dev.17.0 <3.0.0' + dependencies: - charcode: "^1.1.0" - collection: ">=0.9.1 <2.0.0" - source_span: "^1.0.0" - string_scanner: ">=0.0.0 <2.0.0" - typed_data: "^1.1.0" + charcode: ^1.1.0 + collection: '>=0.9.1 <2.0.0' + source_span: ^1.0.0 + string_scanner: '>=0.0.0 <2.0.0' + typed_data: ^1.1.0 + dev_dependencies: - test: "^0.12.0" + test: '>=0.12.42 <2.0.0' From 7e8c4915d4b0feac3d4b7a956e77ed727424398b Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 30 Apr 2019 08:56:47 -0700 Subject: [PATCH 056/126] Enable and fix standard lints, test on oldest supported SDK (dart-lang/http_parser#25) --- pkgs/http_parser/.gitignore | 1 - pkgs/http_parser/.travis.yml | 10 ++- pkgs/http_parser/analysis_options.yaml | 43 +++++++++++++ pkgs/http_parser/codereview.settings | 3 - .../lib/src/authentication_challenge.dart | 11 ++-- pkgs/http_parser/lib/src/chunked_coding.dart | 7 ++- .../lib/src/chunked_coding/decoder.dart | 36 +++++------ .../lib/src/chunked_coding/encoder.dart | 10 +-- pkgs/http_parser/lib/src/http_date.dart | 29 +++++---- pkgs/http_parser/lib/src/media_type.dart | 24 ++++---- pkgs/http_parser/lib/src/scan.dart | 12 ++-- pkgs/http_parser/lib/src/utils.dart | 4 +- pkgs/http_parser/pubspec.yaml | 4 +- .../test/authentication_challenge_test.dart | 2 +- .../test/case_insensitive_map_test.dart | 6 +- .../http_parser/test/chunked_coding_test.dart | 12 ++-- pkgs/http_parser/test/http_date_test.dart | 6 +- pkgs/http_parser/test/media_type_test.dart | 61 +++++++++---------- 18 files changed, 161 insertions(+), 120 deletions(-) create mode 100644 pkgs/http_parser/analysis_options.yaml delete mode 100644 pkgs/http_parser/codereview.settings diff --git a/pkgs/http_parser/.gitignore b/pkgs/http_parser/.gitignore index fb97bdebdc..ec8eae3f1c 100644 --- a/pkgs/http_parser/.gitignore +++ b/pkgs/http_parser/.gitignore @@ -1,5 +1,4 @@ # Don’t commit the following directories created by pub. .dart_tool/ -.pub/ .packages pubspec.lock diff --git a/pkgs/http_parser/.travis.yml b/pkgs/http_parser/.travis.yml index c724bab191..c15d431b84 100644 --- a/pkgs/http_parser/.travis.yml +++ b/pkgs/http_parser/.travis.yml @@ -1,14 +1,20 @@ language: dart dart: + - 2.0.0 - dev dart_task: - test: --platform vm xvfb: false - test: --platform firefox - - 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/http_parser/analysis_options.yaml b/pkgs/http_parser/analysis_options.yaml new file mode 100644 index 0000000000..0711acad5d --- /dev/null +++ b/pkgs/http_parser/analysis_options.yaml @@ -0,0 +1,43 @@ +include: package:pedantic/analysis_options.yaml +analyzer: + strong-mode: + implicit-casts: false +linter: + rules: + - avoid_empty_else + - avoid_init_to_null + - avoid_null_checks_in_equality_operators + - avoid_unused_constructor_parameters + - await_only_futures + - camel_case_types + - cancel_subscriptions + - constant_identifier_names + - control_flow_in_finally + - directives_ordering + - empty_catches + - empty_constructor_bodies + - empty_statements + - hash_and_equals + - implementation_imports + - iterable_contains_unrelated_type + - library_names + - library_prefixes + - list_remove_unrelated_type + - non_constant_identifier_names + - overridden_fields + - package_api_docs + - package_names + - package_prefixed_library_names + - prefer_equal_for_default_values + - prefer_final_fields + - prefer_generic_function_type_aliases + - prefer_is_not_empty + - slash_for_doc_comments + - test_types_in_equals + - throw_in_finally + - type_init_formals + - unnecessary_brace_in_string_interps + - unnecessary_const + - unnecessary_new + - unrelated_type_equality_checks + - valid_regexps diff --git a/pkgs/http_parser/codereview.settings b/pkgs/http_parser/codereview.settings deleted file mode 100644 index d8547c6570..0000000000 --- a/pkgs/http_parser/codereview.settings +++ /dev/null @@ -1,3 +0,0 @@ -CODE_REVIEW_SERVER: https://codereview.chromium.org/ -VIEW_VC: https://github.com/dart-lang/http_parser/commit/ -CC_LIST: reviews@dartlang.org \ No newline at end of file diff --git a/pkgs/http_parser/lib/src/authentication_challenge.dart b/pkgs/http_parser/lib/src/authentication_challenge.dart index 17955df9a6..1c9a722d6c 100644 --- a/pkgs/http_parser/lib/src/authentication_challenge.dart +++ b/pkgs/http_parser/lib/src/authentication_challenge.dart @@ -35,7 +35,7 @@ class AuthenticationChallenge { /// Throws a [FormatException] if the header is invalid. static List parseHeader(String header) { return wrapFormatException("authentication header", header, () { - var scanner = new StringScanner(header); + var scanner = StringScanner(header); scanner.scan(whitespace); var challenges = parseList(scanner, () { var scheme = _scanScheme(scanner, whitespaceName: '" " or "="'); @@ -82,7 +82,7 @@ class AuthenticationChallenge { beforeComma = scanner.position; } - return new AuthenticationChallenge(scheme, params); + return AuthenticationChallenge(scheme, params); }); scanner.expectDone(); @@ -95,7 +95,7 @@ class AuthenticationChallenge { /// Throws a [FormatException] if the challenge is invalid. factory AuthenticationChallenge.parse(String challenge) { return wrapFormatException("authentication challenge", challenge, () { - var scanner = new StringScanner(challenge); + var scanner = StringScanner(challenge); scanner.scan(whitespace); var scheme = _scanScheme(scanner); @@ -103,7 +103,7 @@ class AuthenticationChallenge { parseList(scanner, () => _scanAuthParam(scanner, params)); scanner.expectDone(); - return new AuthenticationChallenge(scheme, params); + return AuthenticationChallenge(scheme, params); }); } @@ -146,6 +146,5 @@ class AuthenticationChallenge { /// Creates a new challenge value with [scheme] and [parameters]. AuthenticationChallenge(this.scheme, Map parameters) - : parameters = - new UnmodifiableMapView(new CaseInsensitiveMap.from(parameters)); + : parameters = UnmodifiableMapView(CaseInsensitiveMap.from(parameters)); } diff --git a/pkgs/http_parser/lib/src/chunked_coding.dart b/pkgs/http_parser/lib/src/chunked_coding.dart index 11353393ff..7a2ab6643e 100644 --- a/pkgs/http_parser/lib/src/chunked_coding.dart +++ b/pkgs/http_parser/lib/src/chunked_coding.dart @@ -4,14 +4,14 @@ import 'dart:convert'; -import 'chunked_coding/encoder.dart'; import 'chunked_coding/decoder.dart'; +import 'chunked_coding/encoder.dart'; -export 'chunked_coding/encoder.dart' hide chunkedCodingEncoder; export 'chunked_coding/decoder.dart' hide chunkedCodingDecoder; +export 'chunked_coding/encoder.dart' hide chunkedCodingEncoder; /// The canonical instance of [ChunkedCodingCodec]. -const chunkedCoding = const ChunkedCodingCodec._(); +const chunkedCoding = ChunkedCodingCodec._(); /// A codec that encodes and decodes the [chunked transfer coding][]. /// @@ -33,6 +33,7 @@ const chunkedCoding = const ChunkedCodingCodec._(); /// headers. It may be updated to silently ignore them in the future. class ChunkedCodingCodec extends Codec, List> { ChunkedCodingEncoder get encoder => chunkedCodingEncoder; + ChunkedCodingDecoder get decoder => chunkedCodingDecoder; const ChunkedCodingCodec._(); diff --git a/pkgs/http_parser/lib/src/chunked_coding/decoder.dart b/pkgs/http_parser/lib/src/chunked_coding/decoder.dart index b20c2d3819..b5a0afc63d 100644 --- a/pkgs/http_parser/lib/src/chunked_coding/decoder.dart +++ b/pkgs/http_parser/lib/src/chunked_coding/decoder.dart @@ -10,22 +10,22 @@ import 'package:charcode/ascii.dart'; import 'package:typed_data/typed_data.dart'; /// The canonical instance of [ChunkedCodingDecoder]. -const chunkedCodingDecoder = const ChunkedCodingDecoder._(); +const chunkedCodingDecoder = ChunkedCodingDecoder._(); /// A converter that decodes byte arrays into chunks with size tags. class ChunkedCodingDecoder extends Converter, List> { const ChunkedCodingDecoder._(); List convert(List bytes) { - var sink = new _Sink(null); + var sink = _Sink(null); var output = sink._decode(bytes, 0, bytes.length); if (sink._state == _State.end) return output; - throw new FormatException("Input ended unexpectedly.", bytes, bytes.length); + throw FormatException("Input ended unexpectedly.", bytes, bytes.length); } ByteConversionSink startChunkedConversion(Sink> sink) => - new _Sink(sink); + _Sink(sink); } /// A conversion sink for the chunked transfer encoding. @@ -57,7 +57,7 @@ class _Sink extends ByteConversionSinkBase { /// one is thrown. void _close([List chunk, int index]) { if (_state != _State.end) { - throw new FormatException("Input ended unexpectedly.", chunk, index); + throw FormatException("Input ended unexpectedly.", chunk, index); } _sink.close(); @@ -69,11 +69,11 @@ class _Sink extends ByteConversionSinkBase { /// describe the character in the exception text. assertCurrentChar(int char, String name) { if (bytes[start] != char) { - throw new FormatException("Expected $name.", bytes, start); + throw FormatException("Expected $name.", bytes, start); } } - var buffer = new Uint8Buffer(); + var buffer = Uint8Buffer(); while (start != end) { switch (_state) { case _State.boundary: @@ -132,7 +132,7 @@ class _Sink extends ByteConversionSinkBase { break; case _State.end: - throw new FormatException("Expected no more data.", bytes, start); + throw FormatException("Expected no more data.", bytes, start); } } return buffer.buffer.asUint8List(0, buffer.length); @@ -164,7 +164,7 @@ class _Sink extends ByteConversionSinkBase { if ($a <= letter && letter <= $f) return letter - $a + 10; } - throw new FormatException( + throw FormatException( "Invalid hexadecimal byte 0x${byte.toRadixString(16).toUpperCase()}.", bytes, index); @@ -178,53 +178,53 @@ class _State { /// next chunk. /// /// Transitions to [size]. - static const boundary = const _State._("boundary"); + static const boundary = _State._("boundary"); /// The parser has parsed at least one digit of the chunk size header, but has /// not yet parsed the `CR LF` sequence that indicates the end of that header. /// /// Transitions to [sizeBeforeLF]. - static const size = const _State._("size"); + static const size = _State._("size"); /// The parser has parsed the chunk size header and the CR character after it, /// but not the LF. /// /// Transitions to [body] or [bodyBeforeCR]. - static const sizeBeforeLF = const _State._("size before LF"); + static const sizeBeforeLF = _State._("size before LF"); /// The parser has parsed a chunk header and possibly some of the body, but /// still needs to consume more bytes. /// /// Transitions to [bodyBeforeCR]. - static const body = const _State._("body"); + static const body = _State._("body"); // The parser has parsed all the bytes in a chunk body but not the CR LF // sequence that follows it. // // Transitions to [bodyBeforeLF]. - static const bodyBeforeCR = const _State._("body before CR"); + static const bodyBeforeCR = _State._("body before CR"); // The parser has parsed all the bytes in a chunk body and the CR that follows // it, but not the LF after that. // // Transitions to [bounday]. - static const bodyBeforeLF = const _State._("body before LF"); + static const bodyBeforeLF = _State._("body before LF"); /// The parser has parsed the final empty chunk but not the CR LF sequence /// that follows it. /// /// Transitions to [endBeforeLF]. - static const endBeforeCR = const _State._("end before CR"); + static const endBeforeCR = _State._("end before CR"); /// The parser has parsed the final empty chunk and the CR that follows it, /// but not the LF after that. /// /// Transitions to [end]. - static const endBeforeLF = const _State._("end before LF"); + static const endBeforeLF = _State._("end before LF"); /// The parser has parsed the final empty chunk as well as the CR LF that /// follows, and expects no more data. - static const end = const _State._("end"); + static const end = _State._("end"); final String _name; diff --git a/pkgs/http_parser/lib/src/chunked_coding/encoder.dart b/pkgs/http_parser/lib/src/chunked_coding/encoder.dart index 183ecfcd2f..01cbdd6c04 100644 --- a/pkgs/http_parser/lib/src/chunked_coding/encoder.dart +++ b/pkgs/http_parser/lib/src/chunked_coding/encoder.dart @@ -8,10 +8,10 @@ import 'dart:typed_data'; import 'package:charcode/ascii.dart'; /// The canonical instance of [ChunkedCodingEncoder]. -const chunkedCodingEncoder = const ChunkedCodingEncoder._(); +const chunkedCodingEncoder = ChunkedCodingEncoder._(); /// The chunk indicating that the chunked message has finished. -final _doneChunk = new Uint8List.fromList([$0, $cr, $lf, $cr, $lf]); +final _doneChunk = Uint8List.fromList([$0, $cr, $lf, $cr, $lf]); /// A converter that encodes byte arrays into chunks with size tags. class ChunkedCodingEncoder extends Converter, List> { @@ -21,7 +21,7 @@ class ChunkedCodingEncoder extends Converter, List> { _convert(bytes, 0, bytes.length, isLast: true); ByteConversionSink startChunkedConversion(Sink> sink) => - new _Sink(sink); + _Sink(sink); } /// A conversion sink for the chunked transfer encoding. @@ -52,7 +52,7 @@ class _Sink extends ByteConversionSinkBase { /// /// If [isLast] is `true`, this adds the footer that indicates that the chunked /// message is complete. -List _convert(List bytes, int start, int end, {bool isLast: false}) { +List _convert(List bytes, int start, int end, {bool isLast = false}) { if (end == start) return isLast ? _doneChunk : const []; var size = end - start; @@ -60,7 +60,7 @@ List _convert(List bytes, int start, int end, {bool isLast: false}) { var footerSize = isLast ? _doneChunk.length : 0; // Add 4 for the CRLF sequences that follow the size header and the bytes. - var list = new Uint8List(sizeInHex.length + 4 + size + footerSize); + var list = Uint8List(sizeInHex.length + 4 + size + footerSize); list.setRange(0, sizeInHex.length, sizeInHex.codeUnits); var cursor = sizeInHex.length; diff --git a/pkgs/http_parser/lib/src/http_date.dart b/pkgs/http_parser/lib/src/http_date.dart index 299f9e9c28..d08ccbbd59 100644 --- a/pkgs/http_parser/lib/src/http_date.dart +++ b/pkgs/http_parser/lib/src/http_date.dart @@ -6,8 +6,8 @@ import 'package:string_scanner/string_scanner.dart'; import 'utils.dart'; -const _WEEKDAYS = const ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]; -const _MONTHS = const [ +const _weekdays = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]; +const _months = [ "Jan", "Feb", "Mar", @@ -22,12 +22,11 @@ const _MONTHS = const [ "Dec" ]; -final _shortWeekdayRegExp = new RegExp(r"Mon|Tue|Wed|Thu|Fri|Sat|Sun"); +final _shortWeekdayRegExp = RegExp(r"Mon|Tue|Wed|Thu|Fri|Sat|Sun"); final _longWeekdayRegExp = - new RegExp(r"Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday"); -final _monthRegExp = - new RegExp(r"Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec"); -final _digitRegExp = new RegExp(r"\d+"); + RegExp(r"Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday"); +final _monthRegExp = RegExp(r"Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec"); +final _digitRegExp = RegExp(r"\d+"); /// Return a HTTP-formatted string representation of [date]. /// @@ -35,13 +34,13 @@ final _digitRegExp = new RegExp(r"\d+"); /// 1123](http://tools.ietf.org/html/rfc1123). String formatHttpDate(DateTime date) { date = date.toUtc(); - var buffer = new StringBuffer() - ..write(_WEEKDAYS[date.weekday - 1]) + var buffer = StringBuffer() + ..write(_weekdays[date.weekday - 1]) ..write(", ") ..write(date.day <= 9 ? "0" : "") ..write(date.day.toString()) ..write(" ") - ..write(_MONTHS[date.month - 1]) + ..write(_months[date.month - 1]) ..write(" ") ..write(date.year.toString()) ..write(date.hour <= 9 ? " 0" : " ") @@ -61,7 +60,7 @@ String formatHttpDate(DateTime date) { /// throw a [FormatException] if [date] is invalid. DateTime parseHttpDate(String date) { return wrapFormatException("HTTP date", date, () { - var scanner = new StringScanner(date); + var scanner = StringScanner(date); if (scanner.scan(_longWeekdayRegExp)) { // RFC 850 starts with a long weekday. @@ -115,7 +114,7 @@ DateTime parseHttpDate(String date) { int _parseMonth(StringScanner scanner) { scanner.expect(_monthRegExp); // DateTime uses 1-indexed months. - return _MONTHS.indexOf(scanner.lastMatch[0]) + 1; + return _months.indexOf(scanner.lastMatch[0]) + 1; } /// Parses an int an enforces that it has exactly [digits] digits. @@ -141,7 +140,7 @@ DateTime _parseTime(StringScanner scanner) { var seconds = _parseInt(scanner, 2); if (seconds >= 60) scanner.error("seconds may not be greater than 60."); - return new DateTime(1, 1, 1, hours, minutes, seconds); + return DateTime(1, 1, 1, hours, minutes, seconds); } /// Returns a UTC [DateTime] from the given components. @@ -150,11 +149,11 @@ DateTime _parseTime(StringScanner scanner) { /// [FormatException]. DateTime _makeDateTime(int year, int month, int day, DateTime time) { var dateTime = - new DateTime.utc(year, month, day, time.hour, time.minute, time.second); + DateTime.utc(year, month, day, time.hour, time.minute, time.second); // If [day] was too large, it will cause [month] to overflow. if (dateTime.month != month) { - throw new FormatException("invalid day '$day' for month '$month'."); + throw FormatException("invalid day '$day' for month '$month'."); } return dateTime; } diff --git a/pkgs/http_parser/lib/src/media_type.dart b/pkgs/http_parser/lib/src/media_type.dart index 7fb29300c8..cd0b22ed8d 100644 --- a/pkgs/http_parser/lib/src/media_type.dart +++ b/pkgs/http_parser/lib/src/media_type.dart @@ -11,7 +11,7 @@ import 'utils.dart'; /// A regular expression matching a character that needs to be backslash-escaped /// in a quoted string. -final _escapedChar = new RegExp(r'["\x00-\x1F\x7F]'); +final _escapedChar = RegExp(r'["\x00-\x1F\x7F]'); /// A class representing an HTTP media type, as used in Accept and Content-Type /// headers. @@ -44,7 +44,7 @@ class MediaType { // This parsing is based on sections 3.6 and 3.7 of the HTTP spec: // http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html. return wrapFormatException("media type", mediaType, () { - var scanner = new StringScanner(mediaType); + var scanner = StringScanner(mediaType); scanner.scan(whitespace); scanner.expect(token); var type = scanner.lastMatch[0]; @@ -72,15 +72,15 @@ class MediaType { } scanner.expectDone(); - return new MediaType(type, subtype, parameters); + return MediaType(type, subtype, parameters); }); } MediaType(String type, String subtype, [Map parameters]) : type = type.toLowerCase(), subtype = subtype.toLowerCase(), - parameters = new UnmodifiableMapView( - parameters == null ? {} : new CaseInsensitiveMap.from(parameters)); + parameters = UnmodifiableMapView( + parameters == null ? {} : CaseInsensitiveMap.from(parameters)); /// Returns a copy of this [MediaType] with some fields altered. /// @@ -96,18 +96,18 @@ class MediaType { String subtype, String mimeType, Map parameters, - bool clearParameters: false}) { + bool clearParameters = false}) { if (mimeType != null) { if (type != null) { - throw new ArgumentError("You may not pass both [type] and [mimeType]."); + throw ArgumentError("You may not pass both [type] and [mimeType]."); } else if (subtype != null) { - throw new ArgumentError("You may not pass both [subtype] and " + throw ArgumentError("You may not pass both [subtype] and " "[mimeType]."); } var segments = mimeType.split('/'); if (segments.length != 2) { - throw new FormatException('Invalid mime type "$mimeType".'); + throw FormatException('Invalid mime type "$mimeType".'); } type = segments[0]; @@ -120,18 +120,18 @@ class MediaType { if (!clearParameters) { var newParameters = parameters; - parameters = new Map.from(this.parameters); + parameters = Map.from(this.parameters); parameters.addAll(newParameters); } - return new MediaType(type, subtype, parameters); + return MediaType(type, subtype, parameters); } /// Converts the media type to a string. /// /// This will produce a valid HTTP media type. String toString() { - var buffer = new StringBuffer()..write(type)..write("/")..write(subtype); + var buffer = StringBuffer()..write(type)..write("/")..write(subtype); parameters.forEach((attribute, value) { buffer.write("; $attribute="); diff --git a/pkgs/http_parser/lib/src/scan.dart b/pkgs/http_parser/lib/src/scan.dart index 3710062dbc..26eb0db315 100644 --- a/pkgs/http_parser/lib/src/scan.dart +++ b/pkgs/http_parser/lib/src/scan.dart @@ -19,22 +19,22 @@ import 'package:string_scanner/string_scanner.dart'; /// [spec]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html /// An HTTP token. -final token = new RegExp(r'[^()<>@,;:"\\/[\]?={} \t\x00-\x1F\x7F]+'); +final token = RegExp(r'[^()<>@,;:"\\/[\]?={} \t\x00-\x1F\x7F]+'); /// Linear whitespace. -final _lws = new RegExp(r"(?:\r\n)?[ \t]+"); +final _lws = RegExp(r"(?:\r\n)?[ \t]+"); /// A quoted string. -final _quotedString = new RegExp(r'"(?:[^"\x00-\x1F\x7F]|\\.)*"'); +final _quotedString = RegExp(r'"(?:[^"\x00-\x1F\x7F]|\\.)*"'); /// A quoted pair. -final _quotedPair = new RegExp(r'\\(.)'); +final _quotedPair = RegExp(r'\\(.)'); /// A character that is *not* a valid HTTP token. -final nonToken = new RegExp(r'[()<>@,;:"\\/\[\]?={} \t\x00-\x1F\x7F]'); +final nonToken = RegExp(r'[()<>@,;:"\\/\[\]?={} \t\x00-\x1F\x7F]'); /// A regular expression matching any number of [_lws] productions in a row. -final whitespace = new RegExp("(?:${_lws.pattern})*"); +final whitespace = RegExp("(?:${_lws.pattern})*"); /// Parses a list of elements, as in `1#element` in the HTTP spec. /// diff --git a/pkgs/http_parser/lib/src/utils.dart b/pkgs/http_parser/lib/src/utils.dart index f2189f247a..98643d5838 100644 --- a/pkgs/http_parser/lib/src/utils.dart +++ b/pkgs/http_parser/lib/src/utils.dart @@ -12,10 +12,10 @@ T wrapFormatException(String name, String value, T body()) { try { return body(); } on SourceSpanFormatException catch (error) { - throw new SourceSpanFormatException( + throw SourceSpanFormatException( 'Invalid $name: ${error.message}', error.span, error.source); } on FormatException catch (error) { - throw new FormatException( + throw FormatException( 'Invalid $name "$value": ${error.message}', error.source, error.offset); } } diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index 585c91c678..73365bebbb 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -7,7 +7,7 @@ author: Dart Team homepage: https://github.com/dart-lang/http_parser environment: - sdk: '>=2.0.0-dev.17.0 <3.0.0' + sdk: '>=2.0.0 <3.0.0' dependencies: charcode: ^1.1.0 @@ -17,4 +17,4 @@ dependencies: typed_data: ^1.1.0 dev_dependencies: - test: '>=0.12.42 <2.0.0' + test: ^1.0.0 diff --git a/pkgs/http_parser/test/authentication_challenge_test.dart b/pkgs/http_parser/test/authentication_challenge_test.dart index 68a49cae02..026d1e4b41 100644 --- a/pkgs/http_parser/test/authentication_challenge_test.dart +++ b/pkgs/http_parser/test/authentication_challenge_test.dart @@ -8,7 +8,7 @@ import 'package:test/test.dart'; void main() { group("parse", () { _singleChallengeTests( - (challenge) => new AuthenticationChallenge.parse(challenge)); + (challenge) => AuthenticationChallenge.parse(challenge)); }); group("parseHeader", () { diff --git a/pkgs/http_parser/test/case_insensitive_map_test.dart b/pkgs/http_parser/test/case_insensitive_map_test.dart index 9bfa00a173..6f18d78814 100644 --- a/pkgs/http_parser/test/case_insensitive_map_test.dart +++ b/pkgs/http_parser/test/case_insensitive_map_test.dart @@ -7,7 +7,7 @@ import 'package:test/test.dart'; void main() { test("provides case-insensitive access to the map", () { - var map = new CaseInsensitiveMap(); + var map = CaseInsensitiveMap(); map["fOo"] = "bAr"; expect(map, containsPair("FoO", "bAr")); @@ -16,13 +16,13 @@ void main() { }); test("stores the original key cases", () { - var map = new CaseInsensitiveMap(); + var map = CaseInsensitiveMap(); map["fOo"] = "bAr"; expect(map, equals({"fOo": "bAr"})); }); test(".from() converts an existing map", () { - var map = new CaseInsensitiveMap.from({"fOo": "bAr"}); + var map = CaseInsensitiveMap.from({"fOo": "bAr"}); expect(map, containsPair("FoO", "bAr")); expect(map, equals({"fOo": "bAr"})); }); diff --git a/pkgs/http_parser/test/chunked_coding_test.dart b/pkgs/http_parser/test/chunked_coding_test.dart index 4d4a602587..83319cf8f4 100644 --- a/pkgs/http_parser/test/chunked_coding_test.dart +++ b/pkgs/http_parser/test/chunked_coding_test.dart @@ -18,7 +18,7 @@ void main() { }); test("uses hex for chunk size", () { - var data = new Iterable.generate(0xA7).toList(); + var data = Iterable.generate(0xA7).toList(); expect( chunkedCoding.encode(data), equals([$a, $7, $cr, $lf] @@ -35,7 +35,7 @@ void main() { ByteConversionSink sink; setUp(() { results = []; - var controller = new StreamController>(sync: true); + var controller = StreamController>(sync: true); controller.stream.listen(results.add); sink = chunkedCoding.encoder.startChunkedConversion(controller.sink); }); @@ -176,7 +176,7 @@ void main() { }); test("parses hex size", () { - var data = new Iterable.generate(0xA7).toList(); + var data = Iterable.generate(0xA7).toList(); expect( chunkedCoding.decode([$a, $7, $cr, $lf] ..addAll(data) @@ -185,7 +185,7 @@ void main() { }); test("parses capital hex size", () { - var data = new Iterable.generate(0xA7).toList(); + var data = Iterable.generate(0xA7).toList(); expect( chunkedCoding.decode([$A, $7, $cr, $lf] ..addAll(data) @@ -262,7 +262,7 @@ void main() { ByteConversionSink sink; setUp(() { results = []; - var controller = new StreamController>(sync: true); + var controller = StreamController>(sync: true); controller.stream.listen(results.add); sink = chunkedCoding.decoder.startChunkedConversion(controller.sink); }); @@ -330,7 +330,7 @@ void main() { sink.add([$a]); expect(results, isEmpty); - var data = new Iterable.generate(0xA7).toList(); + var data = Iterable.generate(0xA7).toList(); sink.add([$7, $cr, $lf]..addAll(data)); expect(results, equals([data])); }); diff --git a/pkgs/http_parser/test/http_date_test.dart b/pkgs/http_parser/test/http_date_test.dart index 887a2c4cb9..ba9748186e 100644 --- a/pkgs/http_parser/test/http_date_test.dart +++ b/pkgs/http_parser/test/http_date_test.dart @@ -8,7 +8,7 @@ import 'package:test/test.dart'; void main() { group('format', () { test('many values with 9', () { - var date = new DateTime.utc(2014, 9, 9, 9, 9, 9); + var date = DateTime.utc(2014, 9, 9, 9, 9, 9); var formatted = formatHttpDate(date); expect(formatted, 'Tue, 09 Sep 2014 09:09:09 GMT'); @@ -18,7 +18,7 @@ void main() { }); test('end of year', () { - var date = new DateTime.utc(1999, 12, 31, 23, 59, 59); + var date = DateTime.utc(1999, 12, 31, 23, 59, 59); var formatted = formatHttpDate(date); expect(formatted, 'Fri, 31 Dec 1999 23:59:59 GMT'); @@ -28,7 +28,7 @@ void main() { }); test('start of year', () { - var date = new DateTime.utc(2000, 1, 1, 0, 0, 0); + var date = DateTime.utc(2000, 1, 1, 0, 0, 0); var formatted = formatHttpDate(date); expect(formatted, 'Sat, 01 Jan 2000 00:00:00 GMT'); diff --git a/pkgs/http_parser/test/media_type_test.dart b/pkgs/http_parser/test/media_type_test.dart index be30b63037..ff12cc6e51 100644 --- a/pkgs/http_parser/test/media_type_test.dart +++ b/pkgs/http_parser/test/media_type_test.dart @@ -8,76 +8,74 @@ import 'package:test/test.dart'; void main() { group("parse", () { test("parses a simple MIME type", () { - var type = new MediaType.parse("text/plain"); + var type = MediaType.parse("text/plain"); expect(type.type, equals("text")); expect(type.subtype, equals("plain")); }); test("allows leading whitespace", () { - expect(new MediaType.parse(" text/plain").mimeType, equals("text/plain")); - expect( - new MediaType.parse("\ttext/plain").mimeType, equals("text/plain")); + expect(MediaType.parse(" text/plain").mimeType, equals("text/plain")); + expect(MediaType.parse("\ttext/plain").mimeType, equals("text/plain")); }); test("allows trailing whitespace", () { - expect(new MediaType.parse("text/plain ").mimeType, equals("text/plain")); - expect( - new MediaType.parse("text/plain\t").mimeType, equals("text/plain")); + expect(MediaType.parse("text/plain ").mimeType, equals("text/plain")); + expect(MediaType.parse("text/plain\t").mimeType, equals("text/plain")); }); test("disallows separators in the MIME type", () { - expect(() => new MediaType.parse("te(xt/plain"), throwsFormatException); - expect(() => new MediaType.parse("text/pla=in"), throwsFormatException); + expect(() => MediaType.parse("te(xt/plain"), throwsFormatException); + expect(() => MediaType.parse("text/pla=in"), throwsFormatException); }); test("disallows whitespace around the slash", () { - expect(() => new MediaType.parse("text /plain"), throwsFormatException); - expect(() => new MediaType.parse("text/ plain"), throwsFormatException); + expect(() => MediaType.parse("text /plain"), throwsFormatException); + expect(() => MediaType.parse("text/ plain"), throwsFormatException); }); test("parses parameters", () { - var type = new MediaType.parse("text/plain;foo=bar;baz=bang"); + var type = MediaType.parse("text/plain;foo=bar;baz=bang"); expect(type.mimeType, equals("text/plain")); expect(type.parameters, equals({"foo": "bar", "baz": "bang"})); }); test("allows whitespace around the semicolon", () { - var type = new MediaType.parse("text/plain ; foo=bar ; baz=bang"); + var type = MediaType.parse("text/plain ; foo=bar ; baz=bang"); expect(type.mimeType, equals("text/plain")); expect(type.parameters, equals({"foo": "bar", "baz": "bang"})); }); test("disallows whitespace around the equals", () { - expect(() => new MediaType.parse("text/plain; foo =bar"), - throwsFormatException); - expect(() => new MediaType.parse("text/plain; foo= bar"), - throwsFormatException); + expect( + () => MediaType.parse("text/plain; foo =bar"), throwsFormatException); + expect( + () => MediaType.parse("text/plain; foo= bar"), throwsFormatException); }); test("disallows separators in the parameters", () { - expect(() => new MediaType.parse("text/plain; fo:o=bar"), - throwsFormatException); - expect(() => new MediaType.parse("text/plain; foo=b@ar"), - throwsFormatException); + expect( + () => MediaType.parse("text/plain; fo:o=bar"), throwsFormatException); + expect( + () => MediaType.parse("text/plain; foo=b@ar"), throwsFormatException); }); test("parses quoted parameters", () { - var type = new MediaType.parse( - 'text/plain; foo="bar space"; baz="bang\\\\escape"'); + var type = + MediaType.parse('text/plain; foo="bar space"; baz="bang\\\\escape"'); expect(type.mimeType, equals("text/plain")); expect( type.parameters, equals({"foo": "bar space", "baz": "bang\\escape"})); }); test("lower-cases type and subtype", () { - var type = new MediaType.parse('TeXt/pLaIn'); + var type = MediaType.parse('TeXt/pLaIn'); expect(type.type, equals("text")); expect(type.subtype, equals("plain")); expect(type.mimeType, equals("text/plain")); }); test("records parameters as case-insensitive", () { - var type = new MediaType.parse('test/plain;FoO=bar;bAz=bang'); + var type = MediaType.parse('test/plain;FoO=bar;bAz=bang'); expect(type.parameters, equals({"FoO": "bar", "bAz": "bang"})); expect(type.parameters, containsPair("foo", "bar")); expect(type.parameters, containsPair("baz", "bang")); @@ -87,7 +85,7 @@ void main() { group("change", () { var type; setUp(() { - type = new MediaType.parse("text/plain; foo=bar; baz=bang"); + type = MediaType.parse("text/plain; foo=bar; baz=bang"); }); test("uses the existing fields by default", () { @@ -140,28 +138,27 @@ void main() { group("toString", () { test("serializes a simple MIME type", () { - expect(new MediaType("text", "plain").toString(), equals("text/plain")); + expect(MediaType("text", "plain").toString(), equals("text/plain")); }); test("serializes a token parameter as a token", () { - expect(new MediaType("text", "plain", {"foo": "bar"}).toString(), + expect(MediaType("text", "plain", {"foo": "bar"}).toString(), equals("text/plain; foo=bar")); }); test("serializes a non-token parameter as a quoted string", () { - expect(new MediaType("text", "plain", {"foo": "bar baz"}).toString(), + expect(MediaType("text", "plain", {"foo": "bar baz"}).toString(), equals('text/plain; foo="bar baz"')); }); test("escapes a quoted string as necessary", () { - expect(new MediaType("text", "plain", {"foo": 'bar"\x7Fbaz'}).toString(), + expect(MediaType("text", "plain", {"foo": 'bar"\x7Fbaz'}).toString(), equals('text/plain; foo="bar\\"\\\x7Fbaz"')); }); test("serializes multiple parameters", () { expect( - new MediaType("text", "plain", {"foo": "bar", "baz": "bang"}) - .toString(), + MediaType("text", "plain", {"foo": "bar", "baz": "bang"}).toString(), equals("text/plain; foo=bar; baz=bang")); }); }); From 016f0072c6688178f5ccd7d6660358373c4bfd35 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 27 Nov 2019 20:54:04 -0800 Subject: [PATCH 057/126] Enable and fix some more standard lints (dart-lang/http_parser#26) --- pkgs/http_parser/analysis_options.yaml | 50 +++++++++++++++++++ pkgs/http_parser/lib/src/chunked_coding.dart | 2 + .../lib/src/chunked_coding/decoder.dart | 14 ++++-- .../lib/src/chunked_coding/encoder.dart | 9 +++- pkgs/http_parser/lib/src/media_type.dart | 7 +-- pkgs/http_parser/lib/src/scan.dart | 4 +- pkgs/http_parser/lib/src/utils.dart | 2 +- pkgs/http_parser/pubspec.yaml | 1 + .../test/authentication_challenge_test.dart | 2 +- pkgs/http_parser/test/media_type_test.dart | 2 +- 10 files changed, 79 insertions(+), 14 deletions(-) diff --git a/pkgs/http_parser/analysis_options.yaml b/pkgs/http_parser/analysis_options.yaml index 0711acad5d..0f4b864612 100644 --- a/pkgs/http_parser/analysis_options.yaml +++ b/pkgs/http_parser/analysis_options.yaml @@ -4,40 +4,90 @@ analyzer: implicit-casts: false linter: rules: + - always_declare_return_types + - annotate_overrides + - avoid_bool_literals_in_conditional_expressions + - avoid_classes_with_only_static_members - avoid_empty_else + - avoid_function_literals_in_foreach_calls - avoid_init_to_null - avoid_null_checks_in_equality_operators + - avoid_relative_lib_imports + - avoid_renaming_method_parameters + - avoid_return_types_on_setters + - avoid_returning_null + - avoid_returning_null_for_future + - avoid_returning_null_for_void + - avoid_returning_this + - avoid_shadowing_type_parameters + - avoid_single_cascade_in_expression_statements + - avoid_types_as_parameter_names - avoid_unused_constructor_parameters - await_only_futures - camel_case_types - cancel_subscriptions + #- cascade_invocations + #- comment_references - constant_identifier_names - control_flow_in_finally - directives_ordering - empty_catches - empty_constructor_bodies - empty_statements + - file_names - hash_and_equals - implementation_imports + - invariant_booleans - iterable_contains_unrelated_type + - join_return_with_assignment - library_names - library_prefixes - list_remove_unrelated_type + - literal_only_boolean_expressions + - no_adjacent_strings_in_list + - no_duplicate_case_values - non_constant_identifier_names + - null_closures + - omit_local_variable_types + - only_throw_errors - overridden_fields - package_api_docs - package_names - package_prefixed_library_names + - prefer_adjacent_string_concatenation + - prefer_collection_literals + - prefer_conditional_assignment + - prefer_const_constructors + - prefer_contains - prefer_equal_for_default_values - prefer_final_fields + #- prefer_final_locals - prefer_generic_function_type_aliases + - prefer_initializing_formals + #- prefer_interpolation_to_compose_strings + - prefer_is_empty - prefer_is_not_empty + - prefer_null_aware_operators + #- prefer_single_quotes + - prefer_typing_uninitialized_variables + - recursive_getters - slash_for_doc_comments - test_types_in_equals - throw_in_finally - type_init_formals + - unawaited_futures + - unnecessary_await_in_return - unnecessary_brace_in_string_interps - unnecessary_const + - unnecessary_getters_setters + - unnecessary_lambdas - unnecessary_new + - unnecessary_null_aware_assignments + - unnecessary_parenthesis + - unnecessary_statements + - unnecessary_this - unrelated_type_equality_checks + - use_function_type_syntax_for_parameters + - use_rethrow_when_possible - valid_regexps + - void_checks diff --git a/pkgs/http_parser/lib/src/chunked_coding.dart b/pkgs/http_parser/lib/src/chunked_coding.dart index 7a2ab6643e..2b496a9bf9 100644 --- a/pkgs/http_parser/lib/src/chunked_coding.dart +++ b/pkgs/http_parser/lib/src/chunked_coding.dart @@ -32,8 +32,10 @@ const chunkedCoding = ChunkedCodingCodec._(); /// Currently, [decoder] will fail to parse chunk extensions and trailing /// headers. It may be updated to silently ignore them in the future. class ChunkedCodingCodec extends Codec, List> { + @override ChunkedCodingEncoder get encoder => chunkedCodingEncoder; + @override ChunkedCodingDecoder get decoder => chunkedCodingDecoder; const ChunkedCodingCodec._(); diff --git a/pkgs/http_parser/lib/src/chunked_coding/decoder.dart b/pkgs/http_parser/lib/src/chunked_coding/decoder.dart index b5a0afc63d..7a455e668d 100644 --- a/pkgs/http_parser/lib/src/chunked_coding/decoder.dart +++ b/pkgs/http_parser/lib/src/chunked_coding/decoder.dart @@ -16,14 +16,16 @@ const chunkedCodingDecoder = ChunkedCodingDecoder._(); class ChunkedCodingDecoder extends Converter, List> { const ChunkedCodingDecoder._(); - List convert(List bytes) { + @override + List convert(List input) { var sink = _Sink(null); - var output = sink._decode(bytes, 0, bytes.length); + var output = sink._decode(input, 0, input.length); if (sink._state == _State.end) return output; - throw FormatException("Input ended unexpectedly.", bytes, bytes.length); + throw FormatException("Input ended unexpectedly.", input, input.length); } + @override ByteConversionSink startChunkedConversion(Sink> sink) => _Sink(sink); } @@ -42,8 +44,10 @@ class _Sink extends ByteConversionSinkBase { _Sink(this._sink); + @override void add(List chunk) => addSlice(chunk, 0, chunk.length, false); + @override void addSlice(List chunk, int start, int end, bool isLast) { RangeError.checkValidRange(start, end, chunk.length); var output = _decode(chunk, start, end); @@ -51,6 +55,7 @@ class _Sink extends ByteConversionSinkBase { if (isLast) _close(chunk, end); } + @override void close() => _close(); /// Like [close], but includes [chunk] and [index] in the [FormatException] if @@ -67,7 +72,7 @@ class _Sink extends ByteConversionSinkBase { Uint8List _decode(List bytes, int start, int end) { /// Throws a [FormatException] if `bytes[start] != $char`. Uses [name] to /// describe the character in the exception text. - assertCurrentChar(int char, String name) { + void assertCurrentChar(int char, String name) { if (bytes[start] != char) { throw FormatException("Expected $name.", bytes, start); } @@ -230,5 +235,6 @@ class _State { const _State._(this._name); + @override String toString() => _name; } diff --git a/pkgs/http_parser/lib/src/chunked_coding/encoder.dart b/pkgs/http_parser/lib/src/chunked_coding/encoder.dart index 01cbdd6c04..e1f2750e91 100644 --- a/pkgs/http_parser/lib/src/chunked_coding/encoder.dart +++ b/pkgs/http_parser/lib/src/chunked_coding/encoder.dart @@ -17,9 +17,11 @@ final _doneChunk = Uint8List.fromList([$0, $cr, $lf, $cr, $lf]); class ChunkedCodingEncoder extends Converter, List> { const ChunkedCodingEncoder._(); - List convert(List bytes) => - _convert(bytes, 0, bytes.length, isLast: true); + @override + List convert(List input) => + _convert(input, 0, input.length, isLast: true); + @override ByteConversionSink startChunkedConversion(Sink> sink) => _Sink(sink); } @@ -31,16 +33,19 @@ class _Sink extends ByteConversionSinkBase { _Sink(this._sink); + @override void add(List chunk) { _sink.add(_convert(chunk, 0, chunk.length)); } + @override void addSlice(List chunk, int start, int end, bool isLast) { RangeError.checkValidRange(start, end, chunk.length); _sink.add(_convert(chunk, start, end, isLast: isLast)); if (isLast) _sink.close(); } + @override void close() { _sink.add(_doneChunk); _sink.close(); diff --git a/pkgs/http_parser/lib/src/media_type.dart b/pkgs/http_parser/lib/src/media_type.dart index cd0b22ed8d..8cbaaa483a 100644 --- a/pkgs/http_parser/lib/src/media_type.dart +++ b/pkgs/http_parser/lib/src/media_type.dart @@ -114,9 +114,9 @@ class MediaType { subtype = segments[1]; } - if (type == null) type = this.type; - if (subtype == null) subtype = this.subtype; - if (parameters == null) parameters = {}; + type ??= this.type; + subtype ??= this.subtype; + parameters ??= {}; if (!clearParameters) { var newParameters = parameters; @@ -130,6 +130,7 @@ class MediaType { /// Converts the media type to a string. /// /// This will produce a valid HTTP media type. + @override String toString() { var buffer = StringBuffer()..write(type)..write("/")..write(subtype); diff --git a/pkgs/http_parser/lib/src/scan.dart b/pkgs/http_parser/lib/src/scan.dart index 26eb0db315..84d47b885c 100644 --- a/pkgs/http_parser/lib/src/scan.dart +++ b/pkgs/http_parser/lib/src/scan.dart @@ -44,7 +44,7 @@ final whitespace = RegExp("(?:${_lws.pattern})*"); /// /// Once this is finished, [scanner] will be at the next non-LWS character in /// the string, or the end of the string. -List parseList(StringScanner scanner, T parseElement()) { +List parseList(StringScanner scanner, T Function() parseElement) { var result = []; // Consume initial empty values. @@ -73,7 +73,7 @@ List parseList(StringScanner scanner, T parseElement()) { /// If [name] is passed, it's used to describe the expected value if it's not /// found. String expectQuotedString(StringScanner scanner, {String name}) { - if (name == null) name = "quoted string"; + name ??= "quoted string"; scanner.expect(_quotedString, name: name); var string = scanner.lastMatch[0]; return string diff --git a/pkgs/http_parser/lib/src/utils.dart b/pkgs/http_parser/lib/src/utils.dart index 98643d5838..ca00fd3af5 100644 --- a/pkgs/http_parser/lib/src/utils.dart +++ b/pkgs/http_parser/lib/src/utils.dart @@ -8,7 +8,7 @@ import 'package:source_span/source_span.dart'; /// /// [name] should describe the type of thing being parsed, and [value] should be /// its actual value. -T wrapFormatException(String name, String value, T body()) { +T wrapFormatException(String name, String value, T Function() body) { try { return body(); } on SourceSpanFormatException catch (error) { diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index 73365bebbb..c5af296117 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -17,4 +17,5 @@ dependencies: typed_data: ^1.1.0 dev_dependencies: + pedantic: ^1.0.0 test: ^1.0.0 diff --git a/pkgs/http_parser/test/authentication_challenge_test.dart b/pkgs/http_parser/test/authentication_challenge_test.dart index 026d1e4b41..311ca43e97 100644 --- a/pkgs/http_parser/test/authentication_challenge_test.dart +++ b/pkgs/http_parser/test/authentication_challenge_test.dart @@ -52,7 +52,7 @@ void main() { /// [AuthenticationChallenge.parseHeader], since they use almost entirely /// separate code paths. void _singleChallengeTests( - AuthenticationChallenge parseChallenge(String challenge)) { + AuthenticationChallenge Function(String challenge) parseChallenge) { test("parses a simple challenge", () { var challenge = parseChallenge("scheme realm=fblthp"); expect(challenge.scheme, equals("scheme")); diff --git a/pkgs/http_parser/test/media_type_test.dart b/pkgs/http_parser/test/media_type_test.dart index ff12cc6e51..87e4cee6dd 100644 --- a/pkgs/http_parser/test/media_type_test.dart +++ b/pkgs/http_parser/test/media_type_test.dart @@ -83,7 +83,7 @@ void main() { }); group("change", () { - var type; + MediaType type; setUp(() { type = MediaType.parse("text/plain; foo=bar; baz=bang"); }); From e0599f50ba8d39f2b22e2ea4d511797e939ebdc7 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Mon, 9 Dec 2019 13:26:19 -0800 Subject: [PATCH 058/126] Fix newly enforced package:pedantic lints (dart-lang/http_parser#27) - prefer_single_quotes - prefer_spread_collections Bump minimum SDK to 2.3.0 to allow spreads in collection literals. --- pkgs/http_parser/.travis.yml | 2 +- .../lib/src/authentication_challenge.dart | 24 +- .../lib/src/chunked_coding/decoder.dart | 38 +-- pkgs/http_parser/lib/src/http_date.dart | 90 ++++---- pkgs/http_parser/lib/src/media_type.dart | 16 +- pkgs/http_parser/lib/src/scan.dart | 12 +- pkgs/http_parser/pubspec.yaml | 4 +- .../test/authentication_challenge_test.dart | 132 +++++------ .../test/case_insensitive_map_test.dart | 24 +- .../http_parser/test/chunked_coding_test.dart | 123 +++++----- pkgs/http_parser/test/http_date_test.dart | 216 +++++++++--------- pkgs/http_parser/test/media_type_test.dart | 160 ++++++------- 12 files changed, 419 insertions(+), 422 deletions(-) diff --git a/pkgs/http_parser/.travis.yml b/pkgs/http_parser/.travis.yml index c15d431b84..51308e50b9 100644 --- a/pkgs/http_parser/.travis.yml +++ b/pkgs/http_parser/.travis.yml @@ -1,7 +1,7 @@ language: dart dart: - - 2.0.0 + - 2.3.0 - dev dart_task: diff --git a/pkgs/http_parser/lib/src/authentication_challenge.dart b/pkgs/http_parser/lib/src/authentication_challenge.dart index 1c9a722d6c..ec368e57a2 100644 --- a/pkgs/http_parser/lib/src/authentication_challenge.dart +++ b/pkgs/http_parser/lib/src/authentication_challenge.dart @@ -34,7 +34,7 @@ class AuthenticationChallenge { /// /// Throws a [FormatException] if the header is invalid. static List parseHeader(String header) { - return wrapFormatException("authentication header", header, () { + return wrapFormatException('authentication header', header, () { var scanner = StringScanner(header); scanner.scan(whitespace); var challenges = parseList(scanner, () { @@ -45,20 +45,20 @@ class AuthenticationChallenge { var params = {}; // Consume initial empty values. - while (scanner.scan(",")) { + while (scanner.scan(',')) { scanner.scan(whitespace); } _scanAuthParam(scanner, params); var beforeComma = scanner.position; - while (scanner.scan(",")) { + while (scanner.scan(',')) { scanner.scan(whitespace); // Empty elements are allowed, but excluded from the results. - if (scanner.matches(",") || scanner.isDone) continue; + if (scanner.matches(',') || scanner.isDone) continue; - scanner.expect(token, name: "a token"); + scanner.expect(token, name: 'a token'); var name = scanner.lastMatch[0]; scanner.scan(whitespace); @@ -75,7 +75,7 @@ class AuthenticationChallenge { params[name] = scanner.lastMatch[0]; } else { params[name] = - expectQuotedString(scanner, name: "a token or a quoted string"); + expectQuotedString(scanner, name: 'a token or a quoted string'); } scanner.scan(whitespace); @@ -94,7 +94,7 @@ class AuthenticationChallenge { /// /// Throws a [FormatException] if the challenge is invalid. factory AuthenticationChallenge.parse(String challenge) { - return wrapFormatException("authentication challenge", challenge, () { + return wrapFormatException('authentication challenge', challenge, () { var scanner = StringScanner(challenge); scanner.scan(whitespace); var scheme = _scanScheme(scanner); @@ -112,15 +112,15 @@ class AuthenticationChallenge { /// If [whitespaceName] is passed, it's used as the name for exceptions thrown /// due to invalid trailing whitespace. static String _scanScheme(StringScanner scanner, {String whitespaceName}) { - scanner.expect(token, name: "a token"); + scanner.expect(token, name: 'a token'); var scheme = scanner.lastMatch[0].toLowerCase(); scanner.scan(whitespace); // The spec specifically requires a space between the scheme and its // params. - if (scanner.lastMatch == null || !scanner.lastMatch[0].contains(" ")) { - scanner.expect(" ", name: whitespaceName); + if (scanner.lastMatch == null || !scanner.lastMatch[0].contains(' ')) { + scanner.expect(' ', name: whitespaceName); } return scheme; @@ -128,7 +128,7 @@ class AuthenticationChallenge { /// Scans a single authentication parameter and stores its result in [params]. static void _scanAuthParam(StringScanner scanner, Map params) { - scanner.expect(token, name: "a token"); + scanner.expect(token, name: 'a token'); var name = scanner.lastMatch[0]; scanner.scan(whitespace); scanner.expect('='); @@ -138,7 +138,7 @@ class AuthenticationChallenge { params[name] = scanner.lastMatch[0]; } else { params[name] = - expectQuotedString(scanner, name: "a token or a quoted string"); + expectQuotedString(scanner, name: 'a token or a quoted string'); } scanner.scan(whitespace); diff --git a/pkgs/http_parser/lib/src/chunked_coding/decoder.dart b/pkgs/http_parser/lib/src/chunked_coding/decoder.dart index 7a455e668d..9b2b194219 100644 --- a/pkgs/http_parser/lib/src/chunked_coding/decoder.dart +++ b/pkgs/http_parser/lib/src/chunked_coding/decoder.dart @@ -22,7 +22,7 @@ class ChunkedCodingDecoder extends Converter, List> { var output = sink._decode(input, 0, input.length); if (sink._state == _State.end) return output; - throw FormatException("Input ended unexpectedly.", input, input.length); + throw FormatException('Input ended unexpectedly.', input, input.length); } @override @@ -62,7 +62,7 @@ class _Sink extends ByteConversionSinkBase { /// one is thrown. void _close([List chunk, int index]) { if (_state != _State.end) { - throw FormatException("Input ended unexpectedly.", chunk, index); + throw FormatException('Input ended unexpectedly.', chunk, index); } _sink.close(); @@ -74,7 +74,7 @@ class _Sink extends ByteConversionSinkBase { /// describe the character in the exception text. void assertCurrentChar(int char, String name) { if (bytes[start] != char) { - throw FormatException("Expected $name.", bytes, start); + throw FormatException('Expected $name.', bytes, start); } } @@ -99,7 +99,7 @@ class _Sink extends ByteConversionSinkBase { break; case _State.sizeBeforeLF: - assertCurrentChar($lf, "LF"); + assertCurrentChar($lf, 'LF'); _state = _size == 0 ? _State.endBeforeCR : _State.body; start++; break; @@ -113,31 +113,31 @@ class _Sink extends ByteConversionSinkBase { break; case _State.bodyBeforeCR: - assertCurrentChar($cr, "CR"); + assertCurrentChar($cr, 'CR'); _state = _State.bodyBeforeLF; start++; break; case _State.bodyBeforeLF: - assertCurrentChar($lf, "LF"); + assertCurrentChar($lf, 'LF'); _state = _State.boundary; start++; break; case _State.endBeforeCR: - assertCurrentChar($cr, "CR"); + assertCurrentChar($cr, 'CR'); _state = _State.endBeforeLF; start++; break; case _State.endBeforeLF: - assertCurrentChar($lf, "LF"); + assertCurrentChar($lf, 'LF'); _state = _State.end; start++; break; case _State.end: - throw FormatException("Expected no more data.", bytes, start); + throw FormatException('Expected no more data.', bytes, start); } } return buffer.buffer.asUint8List(0, buffer.length); @@ -170,7 +170,7 @@ class _Sink extends ByteConversionSinkBase { } throw FormatException( - "Invalid hexadecimal byte 0x${byte.toRadixString(16).toUpperCase()}.", + 'Invalid hexadecimal byte 0x${byte.toRadixString(16).toUpperCase()}.', bytes, index); } @@ -183,53 +183,53 @@ class _State { /// next chunk. /// /// Transitions to [size]. - static const boundary = _State._("boundary"); + static const boundary = _State._('boundary'); /// The parser has parsed at least one digit of the chunk size header, but has /// not yet parsed the `CR LF` sequence that indicates the end of that header. /// /// Transitions to [sizeBeforeLF]. - static const size = _State._("size"); + static const size = _State._('size'); /// The parser has parsed the chunk size header and the CR character after it, /// but not the LF. /// /// Transitions to [body] or [bodyBeforeCR]. - static const sizeBeforeLF = _State._("size before LF"); + static const sizeBeforeLF = _State._('size before LF'); /// The parser has parsed a chunk header and possibly some of the body, but /// still needs to consume more bytes. /// /// Transitions to [bodyBeforeCR]. - static const body = _State._("body"); + static const body = _State._('body'); // The parser has parsed all the bytes in a chunk body but not the CR LF // sequence that follows it. // // Transitions to [bodyBeforeLF]. - static const bodyBeforeCR = _State._("body before CR"); + static const bodyBeforeCR = _State._('body before CR'); // The parser has parsed all the bytes in a chunk body and the CR that follows // it, but not the LF after that. // // Transitions to [bounday]. - static const bodyBeforeLF = _State._("body before LF"); + static const bodyBeforeLF = _State._('body before LF'); /// The parser has parsed the final empty chunk but not the CR LF sequence /// that follows it. /// /// Transitions to [endBeforeLF]. - static const endBeforeCR = _State._("end before CR"); + static const endBeforeCR = _State._('end before CR'); /// The parser has parsed the final empty chunk and the CR that follows it, /// but not the LF after that. /// /// Transitions to [end]. - static const endBeforeLF = _State._("end before LF"); + static const endBeforeLF = _State._('end before LF'); /// The parser has parsed the final empty chunk as well as the CR LF that /// follows, and expects no more data. - static const end = _State._("end"); + static const end = _State._('end'); final String _name; diff --git a/pkgs/http_parser/lib/src/http_date.dart b/pkgs/http_parser/lib/src/http_date.dart index d08ccbbd59..ebdb4aaef4 100644 --- a/pkgs/http_parser/lib/src/http_date.dart +++ b/pkgs/http_parser/lib/src/http_date.dart @@ -6,27 +6,27 @@ import 'package:string_scanner/string_scanner.dart'; import 'utils.dart'; -const _weekdays = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]; +const _weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']; const _months = [ - "Jan", - "Feb", - "Mar", - "Apr", - "May", - "Jun", - "Jul", - "Aug", - "Sep", - "Oct", - "Nov", - "Dec" + 'Jan', + 'Feb', + 'Mar', + 'Apr', + 'May', + 'Jun', + 'Jul', + 'Aug', + 'Sep', + 'Oct', + 'Nov', + 'Dec' ]; -final _shortWeekdayRegExp = RegExp(r"Mon|Tue|Wed|Thu|Fri|Sat|Sun"); +final _shortWeekdayRegExp = RegExp(r'Mon|Tue|Wed|Thu|Fri|Sat|Sun'); final _longWeekdayRegExp = - RegExp(r"Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday"); -final _monthRegExp = RegExp(r"Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec"); -final _digitRegExp = RegExp(r"\d+"); + RegExp(r'Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday'); +final _monthRegExp = RegExp(r'Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec'); +final _digitRegExp = RegExp(r'\d+'); /// Return a HTTP-formatted string representation of [date]. /// @@ -36,20 +36,20 @@ String formatHttpDate(DateTime date) { date = date.toUtc(); var buffer = StringBuffer() ..write(_weekdays[date.weekday - 1]) - ..write(", ") - ..write(date.day <= 9 ? "0" : "") + ..write(', ') + ..write(date.day <= 9 ? '0' : '') ..write(date.day.toString()) - ..write(" ") + ..write(' ') ..write(_months[date.month - 1]) - ..write(" ") + ..write(' ') ..write(date.year.toString()) - ..write(date.hour <= 9 ? " 0" : " ") + ..write(date.hour <= 9 ? ' 0' : ' ') ..write(date.hour.toString()) - ..write(date.minute <= 9 ? ":0" : ":") + ..write(date.minute <= 9 ? ':0' : ':') ..write(date.minute.toString()) - ..write(date.second <= 9 ? ":0" : ":") + ..write(date.second <= 9 ? ':0' : ':') ..write(date.second.toString()) - ..write(" GMT"); + ..write(' GMT'); return buffer.toString(); } @@ -59,20 +59,20 @@ String formatHttpDate(DateTime date) { /// 2616](http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3). It will /// throw a [FormatException] if [date] is invalid. DateTime parseHttpDate(String date) { - return wrapFormatException("HTTP date", date, () { + return wrapFormatException('HTTP date', date, () { var scanner = StringScanner(date); if (scanner.scan(_longWeekdayRegExp)) { // RFC 850 starts with a long weekday. - scanner.expect(", "); + scanner.expect(', '); var day = _parseInt(scanner, 2); - scanner.expect("-"); + scanner.expect('-'); var month = _parseMonth(scanner); - scanner.expect("-"); + scanner.expect('-'); var year = 1900 + _parseInt(scanner, 2); - scanner.expect(" "); + scanner.expect(' '); var time = _parseTime(scanner); - scanner.expect(" GMT"); + scanner.expect(' GMT'); scanner.expectDone(); return _makeDateTime(year, month, day, time); @@ -80,29 +80,29 @@ DateTime parseHttpDate(String date) { // RFC 1123 and asctime both start with a short weekday. scanner.expect(_shortWeekdayRegExp); - if (scanner.scan(", ")) { + if (scanner.scan(', ')) { // RFC 1123 follows the weekday with a comma. var day = _parseInt(scanner, 2); - scanner.expect(" "); + scanner.expect(' '); var month = _parseMonth(scanner); - scanner.expect(" "); + scanner.expect(' '); var year = _parseInt(scanner, 4); - scanner.expect(" "); + scanner.expect(' '); var time = _parseTime(scanner); - scanner.expect(" GMT"); + scanner.expect(' GMT'); scanner.expectDone(); return _makeDateTime(year, month, day, time); } // asctime follows the weekday with a space. - scanner.expect(" "); + scanner.expect(' '); var month = _parseMonth(scanner); - scanner.expect(" "); - var day = scanner.scan(" ") ? _parseInt(scanner, 1) : _parseInt(scanner, 2); - scanner.expect(" "); + scanner.expect(' '); + var day = scanner.scan(' ') ? _parseInt(scanner, 1) : _parseInt(scanner, 2); + scanner.expect(' '); var time = _parseTime(scanner); - scanner.expect(" "); + scanner.expect(' '); var year = _parseInt(scanner, 4); scanner.expectDone(); @@ -121,7 +121,7 @@ int _parseMonth(StringScanner scanner) { int _parseInt(StringScanner scanner, int digits) { scanner.expect(_digitRegExp); if (scanner.lastMatch[0].length != digits) { - scanner.error("expected a $digits-digit number."); + scanner.error('expected a $digits-digit number.'); } return int.parse(scanner.lastMatch[0]); @@ -130,15 +130,15 @@ int _parseInt(StringScanner scanner, int digits) { /// Parses an timestamp of the form "HH:MM:SS" on a 24-hour clock. DateTime _parseTime(StringScanner scanner) { var hours = _parseInt(scanner, 2); - if (hours >= 24) scanner.error("hours may not be greater than 24."); + if (hours >= 24) scanner.error('hours may not be greater than 24.'); scanner.expect(':'); var minutes = _parseInt(scanner, 2); - if (minutes >= 60) scanner.error("minutes may not be greater than 60."); + if (minutes >= 60) scanner.error('minutes may not be greater than 60.'); scanner.expect(':'); var seconds = _parseInt(scanner, 2); - if (seconds >= 60) scanner.error("seconds may not be greater than 60."); + if (seconds >= 60) scanner.error('seconds may not be greater than 60.'); return DateTime(1, 1, 1, hours, minutes, seconds); } diff --git a/pkgs/http_parser/lib/src/media_type.dart b/pkgs/http_parser/lib/src/media_type.dart index 8cbaaa483a..1d004953ad 100644 --- a/pkgs/http_parser/lib/src/media_type.dart +++ b/pkgs/http_parser/lib/src/media_type.dart @@ -35,7 +35,7 @@ class MediaType { final Map parameters; /// The media type's MIME type. - String get mimeType => "$type/$subtype"; + String get mimeType => '$type/$subtype'; /// Parses a media type. /// @@ -43,7 +43,7 @@ class MediaType { factory MediaType.parse(String mediaType) { // This parsing is based on sections 3.6 and 3.7 of the HTTP spec: // http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html. - return wrapFormatException("media type", mediaType, () { + return wrapFormatException('media type', mediaType, () { var scanner = StringScanner(mediaType); scanner.scan(whitespace); scanner.expect(token); @@ -99,10 +99,10 @@ class MediaType { bool clearParameters = false}) { if (mimeType != null) { if (type != null) { - throw ArgumentError("You may not pass both [type] and [mimeType]."); + throw ArgumentError('You may not pass both [type] and [mimeType].'); } else if (subtype != null) { - throw ArgumentError("You may not pass both [subtype] and " - "[mimeType]."); + throw ArgumentError('You may not pass both [subtype] and ' + '[mimeType].'); } var segments = mimeType.split('/'); @@ -132,15 +132,15 @@ class MediaType { /// This will produce a valid HTTP media type. @override String toString() { - var buffer = StringBuffer()..write(type)..write("/")..write(subtype); + var buffer = StringBuffer()..write(type)..write('/')..write(subtype); parameters.forEach((attribute, value) { - buffer.write("; $attribute="); + buffer.write('; $attribute='); if (nonToken.hasMatch(value)) { buffer ..write('"') ..write( - value.replaceAllMapped(_escapedChar, (match) => "\\" + match[0])) + value.replaceAllMapped(_escapedChar, (match) => '\\' + match[0])) ..write('"'); } else { buffer.write(value); diff --git a/pkgs/http_parser/lib/src/scan.dart b/pkgs/http_parser/lib/src/scan.dart index 84d47b885c..2881e48fbb 100644 --- a/pkgs/http_parser/lib/src/scan.dart +++ b/pkgs/http_parser/lib/src/scan.dart @@ -22,7 +22,7 @@ import 'package:string_scanner/string_scanner.dart'; final token = RegExp(r'[^()<>@,;:"\\/[\]?={} \t\x00-\x1F\x7F]+'); /// Linear whitespace. -final _lws = RegExp(r"(?:\r\n)?[ \t]+"); +final _lws = RegExp(r'(?:\r\n)?[ \t]+'); /// A quoted string. final _quotedString = RegExp(r'"(?:[^"\x00-\x1F\x7F]|\\.)*"'); @@ -34,7 +34,7 @@ final _quotedPair = RegExp(r'\\(.)'); final nonToken = RegExp(r'[()<>@,;:"\\/\[\]?={} \t\x00-\x1F\x7F]'); /// A regular expression matching any number of [_lws] productions in a row. -final whitespace = RegExp("(?:${_lws.pattern})*"); +final whitespace = RegExp('(?:${_lws.pattern})*'); /// Parses a list of elements, as in `1#element` in the HTTP spec. /// @@ -48,18 +48,18 @@ List parseList(StringScanner scanner, T Function() parseElement) { var result = []; // Consume initial empty values. - while (scanner.scan(",")) { + while (scanner.scan(',')) { scanner.scan(whitespace); } result.add(parseElement()); scanner.scan(whitespace); - while (scanner.scan(",")) { + while (scanner.scan(',')) { scanner.scan(whitespace); // Empty elements are allowed, but excluded from the results. - if (scanner.matches(",") || scanner.isDone) continue; + if (scanner.matches(',') || scanner.isDone) continue; result.add(parseElement()); scanner.scan(whitespace); @@ -73,7 +73,7 @@ List parseList(StringScanner scanner, T Function() parseElement) { /// If [name] is passed, it's used to describe the expected value if it's not /// found. String expectQuotedString(StringScanner scanner, {String name}) { - name ??= "quoted string"; + name ??= 'quoted string'; scanner.expect(_quotedString, name: name); var string = scanner.lastMatch[0]; return string diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index c5af296117..4a416fabf9 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,5 +1,5 @@ name: http_parser -version: 3.1.3 +version: 3.1.4-dev description: > A platform-independent package for parsing and serializing HTTP formats. @@ -7,7 +7,7 @@ author: Dart Team homepage: https://github.com/dart-lang/http_parser environment: - sdk: '>=2.0.0 <3.0.0' + sdk: '>=2.3.0 <3.0.0' dependencies: charcode: ^1.1.0 diff --git a/pkgs/http_parser/test/authentication_challenge_test.dart b/pkgs/http_parser/test/authentication_challenge_test.dart index 311ca43e97..35559e04f0 100644 --- a/pkgs/http_parser/test/authentication_challenge_test.dart +++ b/pkgs/http_parser/test/authentication_challenge_test.dart @@ -6,13 +6,13 @@ import 'package:http_parser/http_parser.dart'; import 'package:test/test.dart'; void main() { - group("parse", () { + group('parse', () { _singleChallengeTests( (challenge) => AuthenticationChallenge.parse(challenge)); }); - group("parseHeader", () { - group("with a single challenge", () { + group('parseHeader', () { + group('with a single challenge', () { _singleChallengeTests((challenge) { var challenges = AuthenticationChallenge.parseHeader(challenge); expect(challenges, hasLength(1)); @@ -20,28 +20,28 @@ void main() { }); }); - test("parses multiple challenges", () { + test('parses multiple challenges', () { var challenges = AuthenticationChallenge.parseHeader( - "scheme1 realm=fblthp, scheme2 realm=asdfg"); + 'scheme1 realm=fblthp, scheme2 realm=asdfg'); expect(challenges, hasLength(2)); - expect(challenges.first.scheme, equals("scheme1")); - expect(challenges.first.parameters, equals({"realm": "fblthp"})); - expect(challenges.last.scheme, equals("scheme2")); - expect(challenges.last.parameters, equals({"realm": "asdfg"})); + expect(challenges.first.scheme, equals('scheme1')); + expect(challenges.first.parameters, equals({'realm': 'fblthp'})); + expect(challenges.last.scheme, equals('scheme2')); + expect(challenges.last.parameters, equals({'realm': 'asdfg'})); }); - test("parses multiple challenges with multiple parameters", () { + test('parses multiple challenges with multiple parameters', () { var challenges = AuthenticationChallenge.parseHeader( - "scheme1 realm=fblthp, foo=bar, scheme2 realm=asdfg, baz=bang"); + 'scheme1 realm=fblthp, foo=bar, scheme2 realm=asdfg, baz=bang'); expect(challenges, hasLength(2)); - expect(challenges.first.scheme, equals("scheme1")); + expect(challenges.first.scheme, equals('scheme1')); expect(challenges.first.parameters, - equals({"realm": "fblthp", "foo": "bar"})); + equals({'realm': 'fblthp', 'foo': 'bar'})); - expect(challenges.last.scheme, equals("scheme2")); + expect(challenges.last.scheme, equals('scheme2')); expect(challenges.last.parameters, - equals({"realm": "asdfg", "baz": "bang"})); + equals({'realm': 'asdfg', 'baz': 'bang'})); }); }); } @@ -53,90 +53,90 @@ void main() { /// separate code paths. void _singleChallengeTests( AuthenticationChallenge Function(String challenge) parseChallenge) { - test("parses a simple challenge", () { - var challenge = parseChallenge("scheme realm=fblthp"); - expect(challenge.scheme, equals("scheme")); - expect(challenge.parameters, equals({"realm": "fblthp"})); + test('parses a simple challenge', () { + var challenge = parseChallenge('scheme realm=fblthp'); + expect(challenge.scheme, equals('scheme')); + expect(challenge.parameters, equals({'realm': 'fblthp'})); }); - test("parses multiple parameters", () { - var challenge = parseChallenge("scheme realm=fblthp, foo=bar, baz=qux"); - expect(challenge.scheme, equals("scheme")); + test('parses multiple parameters', () { + var challenge = parseChallenge('scheme realm=fblthp, foo=bar, baz=qux'); + expect(challenge.scheme, equals('scheme')); expect(challenge.parameters, - equals({"realm": "fblthp", "foo": "bar", "baz": "qux"})); + equals({'realm': 'fblthp', 'foo': 'bar', 'baz': 'qux'})); }); - test("parses quoted string parameters", () { + test('parses quoted string parameters', () { var challenge = parseChallenge('scheme realm="fblthp, foo=bar", baz="qux"'); - expect(challenge.scheme, equals("scheme")); + expect(challenge.scheme, equals('scheme')); expect(challenge.parameters, - equals({"realm": "fblthp, foo=bar", "baz": "qux"})); + equals({'realm': 'fblthp, foo=bar', 'baz': 'qux'})); }); - test("normalizes the case of the scheme", () { - var challenge = parseChallenge("ScHeMe realm=fblthp"); - expect(challenge.scheme, equals("scheme")); - expect(challenge.parameters, equals({"realm": "fblthp"})); + test('normalizes the case of the scheme', () { + var challenge = parseChallenge('ScHeMe realm=fblthp'); + expect(challenge.scheme, equals('scheme')); + expect(challenge.parameters, equals({'realm': 'fblthp'})); }); - test("normalizes the case of the parameter name", () { - var challenge = parseChallenge("scheme ReAlM=fblthp"); - expect(challenge.scheme, equals("scheme")); - expect(challenge.parameters, containsPair("realm", "fblthp")); + test('normalizes the case of the parameter name', () { + var challenge = parseChallenge('scheme ReAlM=fblthp'); + expect(challenge.scheme, equals('scheme')); + expect(challenge.parameters, containsPair('realm', 'fblthp')); }); test("doesn't normalize the case of the parameter value", () { - var challenge = parseChallenge("scheme realm=FbLtHp"); - expect(challenge.scheme, equals("scheme")); - expect(challenge.parameters, containsPair("realm", "FbLtHp")); - expect(challenge.parameters, isNot(containsPair("realm", "fblthp"))); + var challenge = parseChallenge('scheme realm=FbLtHp'); + expect(challenge.scheme, equals('scheme')); + expect(challenge.parameters, containsPair('realm', 'FbLtHp')); + expect(challenge.parameters, isNot(containsPair('realm', 'fblthp'))); }); - test("allows extra whitespace", () { + test('allows extra whitespace', () { var challenge = parseChallenge( - " scheme\t \trealm\t = \tfblthp\t, \tfoo\t\r\n =\tbar\t"); - expect(challenge.scheme, equals("scheme")); - expect(challenge.parameters, equals({"realm": "fblthp", "foo": "bar"})); + ' scheme\t \trealm\t = \tfblthp\t, \tfoo\t\r\n =\tbar\t'); + expect(challenge.scheme, equals('scheme')); + expect(challenge.parameters, equals({'realm': 'fblthp', 'foo': 'bar'})); }); - test("allows an empty parameter", () { - var challenge = parseChallenge("scheme realm=fblthp, , foo=bar"); - expect(challenge.scheme, equals("scheme")); - expect(challenge.parameters, equals({"realm": "fblthp", "foo": "bar"})); + test('allows an empty parameter', () { + var challenge = parseChallenge('scheme realm=fblthp, , foo=bar'); + expect(challenge.scheme, equals('scheme')); + expect(challenge.parameters, equals({'realm': 'fblthp', 'foo': 'bar'})); }); - test("allows a leading comma", () { - var challenge = parseChallenge("scheme , realm=fblthp, foo=bar,"); - expect(challenge.scheme, equals("scheme")); - expect(challenge.parameters, equals({"realm": "fblthp", "foo": "bar"})); + test('allows a leading comma', () { + var challenge = parseChallenge('scheme , realm=fblthp, foo=bar,'); + expect(challenge.scheme, equals('scheme')); + expect(challenge.parameters, equals({'realm': 'fblthp', 'foo': 'bar'})); }); - test("allows a trailing comma", () { - var challenge = parseChallenge("scheme realm=fblthp, foo=bar, ,"); - expect(challenge.scheme, equals("scheme")); - expect(challenge.parameters, equals({"realm": "fblthp", "foo": "bar"})); + test('allows a trailing comma', () { + var challenge = parseChallenge('scheme realm=fblthp, foo=bar, ,'); + expect(challenge.scheme, equals('scheme')); + expect(challenge.parameters, equals({'realm': 'fblthp', 'foo': 'bar'})); }); - test("disallows only a scheme", () { - expect(() => parseChallenge("scheme"), throwsFormatException); + test('disallows only a scheme', () { + expect(() => parseChallenge('scheme'), throwsFormatException); }); - test("disallows a valueless parameter", () { - expect(() => parseChallenge("scheme realm"), throwsFormatException); - expect(() => parseChallenge("scheme realm="), throwsFormatException); + test('disallows a valueless parameter', () { + expect(() => parseChallenge('scheme realm'), throwsFormatException); + expect(() => parseChallenge('scheme realm='), throwsFormatException); expect( - () => parseChallenge("scheme realm, foo=bar"), throwsFormatException); + () => parseChallenge('scheme realm, foo=bar'), throwsFormatException); }); - test("requires a space after the scheme", () { - expect(() => parseChallenge("scheme\trealm"), throwsFormatException); - expect(() => parseChallenge("scheme\r\n\trealm="), throwsFormatException); + test('requires a space after the scheme', () { + expect(() => parseChallenge('scheme\trealm'), throwsFormatException); + expect(() => parseChallenge('scheme\r\n\trealm='), throwsFormatException); }); - test("disallows junk after the parameters", () { + test('disallows junk after the parameters', () { expect( - () => parseChallenge("scheme realm=fblthp foo"), throwsFormatException); - expect(() => parseChallenge("scheme realm=fblthp, foo=bar baz"), + () => parseChallenge('scheme realm=fblthp foo'), throwsFormatException); + expect(() => parseChallenge('scheme realm=fblthp, foo=bar baz'), throwsFormatException); }); } diff --git a/pkgs/http_parser/test/case_insensitive_map_test.dart b/pkgs/http_parser/test/case_insensitive_map_test.dart index 6f18d78814..25cd02b950 100644 --- a/pkgs/http_parser/test/case_insensitive_map_test.dart +++ b/pkgs/http_parser/test/case_insensitive_map_test.dart @@ -6,24 +6,24 @@ import 'package:http_parser/http_parser.dart'; import 'package:test/test.dart'; void main() { - test("provides case-insensitive access to the map", () { + test('provides case-insensitive access to the map', () { var map = CaseInsensitiveMap(); - map["fOo"] = "bAr"; - expect(map, containsPair("FoO", "bAr")); + map['fOo'] = 'bAr'; + expect(map, containsPair('FoO', 'bAr')); - map["foo"] = "baz"; - expect(map, containsPair("FOO", "baz")); + map['foo'] = 'baz'; + expect(map, containsPair('FOO', 'baz')); }); - test("stores the original key cases", () { + test('stores the original key cases', () { var map = CaseInsensitiveMap(); - map["fOo"] = "bAr"; - expect(map, equals({"fOo": "bAr"})); + map['fOo'] = 'bAr'; + expect(map, equals({'fOo': 'bAr'})); }); - test(".from() converts an existing map", () { - var map = CaseInsensitiveMap.from({"fOo": "bAr"}); - expect(map, containsPair("FoO", "bAr")); - expect(map, equals({"fOo": "bAr"})); + test('.from() converts an existing map', () { + var map = CaseInsensitiveMap.from({'fOo': 'bAr'}); + expect(map, containsPair('FoO', 'bAr')); + expect(map, equals({'fOo': 'bAr'})); }); } diff --git a/pkgs/http_parser/test/chunked_coding_test.dart b/pkgs/http_parser/test/chunked_coding_test.dart index 83319cf8f4..ec84614b5a 100644 --- a/pkgs/http_parser/test/chunked_coding_test.dart +++ b/pkgs/http_parser/test/chunked_coding_test.dart @@ -11,26 +11,25 @@ import 'package:charcode/charcode.dart'; import 'package:test/test.dart'; void main() { - group("encoder", () { - test("adds a header to the chunk of bytes", () { + group('encoder', () { + test('adds a header to the chunk of bytes', () { expect(chunkedCoding.encode([1, 2, 3]), equals([$3, $cr, $lf, 1, 2, 3, $cr, $lf, $0, $cr, $lf, $cr, $lf])); }); - test("uses hex for chunk size", () { + test('uses hex for chunk size', () { var data = Iterable.generate(0xA7).toList(); expect( chunkedCoding.encode(data), - equals([$a, $7, $cr, $lf] - ..addAll(data) - ..addAll([$cr, $lf, $0, $cr, $lf, $cr, $lf]))); + equals( + [$a, $7, $cr, $lf, ...data, $cr, $lf, $0, $cr, $lf, $cr, $lf])); }); - test("just generates a footer for an empty input", () { + test('just generates a footer for an empty input', () { expect(chunkedCoding.encode([]), equals([$0, $cr, $lf, $cr, $lf])); }); - group("with chunked conversion", () { + group('with chunked conversion', () { List> results; ByteConversionSink sink; setUp(() { @@ -40,7 +39,7 @@ void main() { sink = chunkedCoding.encoder.startChunkedConversion(controller.sink); }); - test("adds headers to each chunk of bytes", () { + test('adds headers to each chunk of bytes', () { sink.add([1, 2, 3, 4]); expect( results, @@ -66,7 +65,7 @@ void main() { ])); }); - test("handles empty chunks", () { + test('handles empty chunks', () { sink.add([]); expect(results, equals([[]])); @@ -98,8 +97,8 @@ void main() { ])); }); - group("addSlice()", () { - test("adds bytes from the specified slice", () { + group('addSlice()', () { + test('adds bytes from the specified slice', () { sink.addSlice([1, 2, 3, 4, 5], 1, 4, false); expect( results, @@ -113,7 +112,7 @@ void main() { expect(results, equals([[]])); }); - test("adds a footer if isLast is true", () { + test('adds a footer if isLast is true', () { sink.addSlice([1, 2, 3, 4, 5], 1, 4, true); expect( results, @@ -125,18 +124,18 @@ void main() { expect(() => sink.add([]), throwsStateError); }); - group("disallows", () { - test("start < 0", () { + group('disallows', () { + test('start < 0', () { expect(() => sink.addSlice([1, 2, 3, 4, 5], -1, 4, false), throwsRangeError); }); - test("start > end", () { + test('start > end', () { expect(() => sink.addSlice([1, 2, 3, 4, 5], 3, 2, false), throwsRangeError); }); - test("end > length", () { + test('end > length', () { expect(() => sink.addSlice([1, 2, 3, 4, 5], 1, 10, false), throwsRangeError); }); @@ -145,8 +144,8 @@ void main() { }); }); - group("decoder", () { - test("parses chunked data", () { + group('decoder', () { + test('parses chunked data', () { expect( chunkedCoding.decode([ $3, @@ -175,47 +174,45 @@ void main() { equals([1, 2, 3, 4, 5, 6, 7])); }); - test("parses hex size", () { + test('parses hex size', () { var data = Iterable.generate(0xA7).toList(); expect( - chunkedCoding.decode([$a, $7, $cr, $lf] - ..addAll(data) - ..addAll([$cr, $lf, $0, $cr, $lf, $cr, $lf])), + chunkedCoding.decode( + [$a, $7, $cr, $lf, ...data, $cr, $lf, $0, $cr, $lf, $cr, $lf]), equals(data)); }); - test("parses capital hex size", () { + test('parses capital hex size', () { var data = Iterable.generate(0xA7).toList(); expect( - chunkedCoding.decode([$A, $7, $cr, $lf] - ..addAll(data) - ..addAll([$cr, $lf, $0, $cr, $lf, $cr, $lf])), + chunkedCoding.decode( + [$A, $7, $cr, $lf, ...data, $cr, $lf, $0, $cr, $lf, $cr, $lf]), equals(data)); }); - test("parses an empty message", () { + test('parses an empty message', () { expect(chunkedCoding.decode([$0, $cr, $lf, $cr, $lf]), isEmpty); }); - group("disallows a message", () { - test("that ends without any input", () { + group('disallows a message', () { + test('that ends without any input', () { expect(() => chunkedCoding.decode([]), throwsFormatException); }); - test("that ends after the size", () { + test('that ends after the size', () { expect(() => chunkedCoding.decode([$a]), throwsFormatException); }); - test("that ends after CR", () { + test('that ends after CR', () { expect(() => chunkedCoding.decode([$a, $cr]), throwsFormatException); }); - test("that ends after LF", () { + test('that ends after LF', () { expect( () => chunkedCoding.decode([$a, $cr, $lf]), throwsFormatException); }); - test("that ends after insufficient bytes", () { + test('that ends after insufficient bytes', () { expect(() => chunkedCoding.decode([$a, $cr, $lf, 1, 2, 3]), throwsFormatException); }); @@ -235,29 +232,29 @@ void main() { throwsFormatException); }); - test("that ends after the empty chunk", () { + test('that ends after the empty chunk', () { expect( () => chunkedCoding.decode([$0, $cr, $lf]), throwsFormatException); }); - test("that ends after the closing CR", () { + test('that ends after the closing CR', () { expect(() => chunkedCoding.decode([$0, $cr, $lf, $cr]), throwsFormatException); }); - test("with a chunk without a size", () { + test('with a chunk without a size', () { expect(() => chunkedCoding.decode([$cr, $lf, $0, $cr, $lf, $cr, $lf]), throwsFormatException); }); - test("with a chunk with a non-hex size", () { + test('with a chunk with a non-hex size', () { expect( () => chunkedCoding.decode([$q, $cr, $lf, $0, $cr, $lf, $cr, $lf]), throwsFormatException); }); }); - group("with chunked conversion", () { + group('with chunked conversion', () { List> results; ByteConversionSink sink; setUp(() { @@ -267,7 +264,7 @@ void main() { sink = chunkedCoding.decoder.startChunkedConversion(controller.sink); }); - test("decodes each chunk of bytes", () { + test('decodes each chunk of bytes', () { sink.add([$4, $cr, $lf, 1, 2, 3, 4, $cr, $lf]); expect( results, @@ -293,7 +290,7 @@ void main() { ])); }); - test("handles empty chunks", () { + test('handles empty chunks', () { sink.add([]); expect(results, isEmpty); @@ -320,22 +317,22 @@ void main() { ])); }); - test("throws if the sink is closed before the message is done", () { + test('throws if the sink is closed before the message is done', () { sink.add([$3, $cr, $lf, 1, 2, 3]); expect(() => sink.close(), throwsFormatException); }); - group("preserves state when a byte array ends", () { - test("within chunk size", () { + group('preserves state when a byte array ends', () { + test('within chunk size', () { sink.add([$a]); expect(results, isEmpty); var data = Iterable.generate(0xA7).toList(); - sink.add([$7, $cr, $lf]..addAll(data)); + sink.add([$7, $cr, $lf, ...data]); expect(results, equals([data])); }); - test("after chunk size", () { + test('after chunk size', () { sink.add([$3]); expect(results, isEmpty); @@ -347,7 +344,7 @@ void main() { ])); }); - test("after CR", () { + test('after CR', () { sink.add([$3, $cr]); expect(results, isEmpty); @@ -359,7 +356,7 @@ void main() { ])); }); - test("after LF", () { + test('after LF', () { sink.add([$3, $cr, $lf]); expect(results, isEmpty); @@ -371,7 +368,7 @@ void main() { ])); }); - test("after some bytes", () { + test('after some bytes', () { sink.add([$3, $cr, $lf, 1, 2]); expect( results, @@ -388,7 +385,7 @@ void main() { ])); }); - test("after all bytes", () { + test('after all bytes', () { sink.add([$3, $cr, $lf, 1, 2, 3]); expect( results, @@ -405,7 +402,7 @@ void main() { ])); }); - test("after a post-chunk CR", () { + test('after a post-chunk CR', () { sink.add([$3, $cr, $lf, 1, 2, 3, $cr]); expect( results, @@ -422,7 +419,7 @@ void main() { ])); }); - test("after a post-chunk LF", () { + test('after a post-chunk LF', () { sink.add([$3, $cr, $lf, 1, 2, 3, $cr, $lf]); expect( results, @@ -439,7 +436,7 @@ void main() { ])); }); - test("after empty chunk size", () { + test('after empty chunk size', () { sink.add([$0]); expect(results, isEmpty); @@ -450,7 +447,7 @@ void main() { expect(results, isEmpty); }); - test("after first empty chunk CR", () { + test('after first empty chunk CR', () { sink.add([$0, $cr]); expect(results, isEmpty); @@ -461,7 +458,7 @@ void main() { expect(results, isEmpty); }); - test("after first empty chunk LF", () { + test('after first empty chunk LF', () { sink.add([$0, $cr, $lf]); expect(results, isEmpty); @@ -472,7 +469,7 @@ void main() { expect(results, isEmpty); }); - test("after second empty chunk CR", () { + test('after second empty chunk CR', () { sink.add([$0, $cr, $lf, $cr]); expect(results, isEmpty); @@ -484,8 +481,8 @@ void main() { }); }); - group("addSlice()", () { - test("adds bytes from the specified slice", () { + group('addSlice()', () { + test('adds bytes from the specified slice', () { sink.addSlice([1, $3, $cr, $lf, 2, 3, 4, 5], 1, 7, false); expect( results, @@ -499,23 +496,23 @@ void main() { expect(results, isEmpty); }); - test("closes the sink if isLast is true", () { + test('closes the sink if isLast is true', () { sink.addSlice([1, $0, $cr, $lf, $cr, $lf, 7], 1, 6, true); expect(results, isEmpty); }); - group("disallows", () { - test("start < 0", () { + group('disallows', () { + test('start < 0', () { expect(() => sink.addSlice([1, 2, 3, 4, 5], -1, 4, false), throwsRangeError); }); - test("start > end", () { + test('start > end', () { expect(() => sink.addSlice([1, 2, 3, 4, 5], 3, 2, false), throwsRangeError); }); - test("end > length", () { + test('end > length', () { expect(() => sink.addSlice([1, 2, 3, 4, 5], 1, 10, false), throwsRangeError); }); diff --git a/pkgs/http_parser/test/http_date_test.dart b/pkgs/http_parser/test/http_date_test.dart index ba9748186e..3c54472bed 100644 --- a/pkgs/http_parser/test/http_date_test.dart +++ b/pkgs/http_parser/test/http_date_test.dart @@ -38,306 +38,306 @@ void main() { }); }); - group("parse", () { - group("RFC 1123", () { - test("parses the example date", () { - var date = parseHttpDate("Sun, 06 Nov 1994 08:49:37 GMT"); + group('parse', () { + group('RFC 1123', () { + test('parses the example date', () { + var date = parseHttpDate('Sun, 06 Nov 1994 08:49:37 GMT'); expect(date.day, equals(6)); expect(date.month, equals(DateTime.november)); expect(date.year, equals(1994)); expect(date.hour, equals(8)); expect(date.minute, equals(49)); expect(date.second, equals(37)); - expect(date.timeZoneName, equals("UTC")); + expect(date.timeZoneName, equals('UTC')); }); - test("whitespace is required", () { - expect(() => parseHttpDate("Sun,06 Nov 1994 08:49:37 GMT"), + test('whitespace is required', () { + expect(() => parseHttpDate('Sun,06 Nov 1994 08:49:37 GMT'), throwsFormatException); - expect(() => parseHttpDate("Sun, 06Nov 1994 08:49:37 GMT"), + expect(() => parseHttpDate('Sun, 06Nov 1994 08:49:37 GMT'), throwsFormatException); - expect(() => parseHttpDate("Sun, 06 Nov1994 08:49:37 GMT"), + expect(() => parseHttpDate('Sun, 06 Nov1994 08:49:37 GMT'), throwsFormatException); - expect(() => parseHttpDate("Sun, 06 Nov 199408:49:37 GMT"), + expect(() => parseHttpDate('Sun, 06 Nov 199408:49:37 GMT'), throwsFormatException); - expect(() => parseHttpDate("Sun, 06 Nov 1994 08:49:37GMT"), + expect(() => parseHttpDate('Sun, 06 Nov 1994 08:49:37GMT'), throwsFormatException); }); - test("exactly one space is required", () { - expect(() => parseHttpDate("Sun, 06 Nov 1994 08:49:37 GMT"), + test('exactly one space is required', () { + expect(() => parseHttpDate('Sun, 06 Nov 1994 08:49:37 GMT'), throwsFormatException); - expect(() => parseHttpDate("Sun, 06 Nov 1994 08:49:37 GMT"), + expect(() => parseHttpDate('Sun, 06 Nov 1994 08:49:37 GMT'), throwsFormatException); - expect(() => parseHttpDate("Sun, 06 Nov 1994 08:49:37 GMT"), + expect(() => parseHttpDate('Sun, 06 Nov 1994 08:49:37 GMT'), throwsFormatException); - expect(() => parseHttpDate("Sun, 06 Nov 1994 08:49:37 GMT"), + expect(() => parseHttpDate('Sun, 06 Nov 1994 08:49:37 GMT'), throwsFormatException); - expect(() => parseHttpDate("Sun, 06 Nov 1994 08:49:37 GMT"), + expect(() => parseHttpDate('Sun, 06 Nov 1994 08:49:37 GMT'), throwsFormatException); }); - test("requires precise number lengths", () { - expect(() => parseHttpDate("Sun, 6 Nov 1994 08:49:37 GMT"), + test('requires precise number lengths', () { + expect(() => parseHttpDate('Sun, 6 Nov 1994 08:49:37 GMT'), throwsFormatException); - expect(() => parseHttpDate("Sun, 06 Nov 94 08:49:37 GMT"), + expect(() => parseHttpDate('Sun, 06 Nov 94 08:49:37 GMT'), throwsFormatException); - expect(() => parseHttpDate("Sun, 06 Nov 1994 8:49:37 GMT"), + expect(() => parseHttpDate('Sun, 06 Nov 1994 8:49:37 GMT'), throwsFormatException); - expect(() => parseHttpDate("Sun, 06 Nov 1994 08:9:37 GMT"), + expect(() => parseHttpDate('Sun, 06 Nov 1994 08:9:37 GMT'), throwsFormatException); - expect(() => parseHttpDate("Sun, 06 Nov 1994 08:49:7 GMT"), + expect(() => parseHttpDate('Sun, 06 Nov 1994 08:49:7 GMT'), throwsFormatException); }); - test("requires reasonable numbers", () { - expect(() => parseHttpDate("Sun, 00 Nov 1994 08:49:37 GMT"), + test('requires reasonable numbers', () { + expect(() => parseHttpDate('Sun, 00 Nov 1994 08:49:37 GMT'), throwsFormatException); - expect(() => parseHttpDate("Sun, 31 Nov 1994 08:49:37 GMT"), + expect(() => parseHttpDate('Sun, 31 Nov 1994 08:49:37 GMT'), throwsFormatException); - expect(() => parseHttpDate("Sun, 32 Aug 1994 08:49:37 GMT"), + expect(() => parseHttpDate('Sun, 32 Aug 1994 08:49:37 GMT'), throwsFormatException); - expect(() => parseHttpDate("Sun, 06 Nov 1994 24:49:37 GMT"), + expect(() => parseHttpDate('Sun, 06 Nov 1994 24:49:37 GMT'), throwsFormatException); - expect(() => parseHttpDate("Sun, 06 Nov 1994 08:60:37 GMT"), + expect(() => parseHttpDate('Sun, 06 Nov 1994 08:60:37 GMT'), throwsFormatException); - expect(() => parseHttpDate("Sun, 06 Nov 1994 08:49:60 GMT"), + expect(() => parseHttpDate('Sun, 06 Nov 1994 08:49:60 GMT'), throwsFormatException); }); - test("only allows short weekday names", () { - expect(() => parseHttpDate("Sunday, 6 Nov 1994 08:49:37 GMT"), + test('only allows short weekday names', () { + expect(() => parseHttpDate('Sunday, 6 Nov 1994 08:49:37 GMT'), throwsFormatException); }); - test("only allows short month names", () { - expect(() => parseHttpDate("Sun, 6 November 1994 08:49:37 GMT"), + test('only allows short month names', () { + expect(() => parseHttpDate('Sun, 6 November 1994 08:49:37 GMT'), throwsFormatException); }); - test("only allows GMT", () { - expect(() => parseHttpDate("Sun, 6 Nov 1994 08:49:37 PST"), + test('only allows GMT', () { + expect(() => parseHttpDate('Sun, 6 Nov 1994 08:49:37 PST'), throwsFormatException); }); - test("disallows trailing whitespace", () { - expect(() => parseHttpDate("Sun, 6 Nov 1994 08:49:37 GMT "), + test('disallows trailing whitespace', () { + expect(() => parseHttpDate('Sun, 6 Nov 1994 08:49:37 GMT '), throwsFormatException); }); }); - group("RFC 850", () { - test("parses the example date", () { - var date = parseHttpDate("Sunday, 06-Nov-94 08:49:37 GMT"); + group('RFC 850', () { + test('parses the example date', () { + var date = parseHttpDate('Sunday, 06-Nov-94 08:49:37 GMT'); expect(date.day, equals(6)); expect(date.month, equals(DateTime.november)); expect(date.year, equals(1994)); expect(date.hour, equals(8)); expect(date.minute, equals(49)); expect(date.second, equals(37)); - expect(date.timeZoneName, equals("UTC")); + expect(date.timeZoneName, equals('UTC')); }); - test("whitespace is required", () { - expect(() => parseHttpDate("Sunday,06-Nov-94 08:49:37 GMT"), + test('whitespace is required', () { + expect(() => parseHttpDate('Sunday,06-Nov-94 08:49:37 GMT'), throwsFormatException); - expect(() => parseHttpDate("Sunday, 06-Nov-9408:49:37 GMT"), + expect(() => parseHttpDate('Sunday, 06-Nov-9408:49:37 GMT'), throwsFormatException); - expect(() => parseHttpDate("Sunday, 06-Nov-94 08:49:37GMT"), + expect(() => parseHttpDate('Sunday, 06-Nov-94 08:49:37GMT'), throwsFormatException); }); - test("exactly one space is required", () { - expect(() => parseHttpDate("Sunday, 06-Nov-94 08:49:37 GMT"), + test('exactly one space is required', () { + expect(() => parseHttpDate('Sunday, 06-Nov-94 08:49:37 GMT'), throwsFormatException); - expect(() => parseHttpDate("Sunday, 06-Nov-94 08:49:37 GMT"), + expect(() => parseHttpDate('Sunday, 06-Nov-94 08:49:37 GMT'), throwsFormatException); - expect(() => parseHttpDate("Sunday, 06-Nov-94 08:49:37 GMT"), + expect(() => parseHttpDate('Sunday, 06-Nov-94 08:49:37 GMT'), throwsFormatException); }); - test("requires precise number lengths", () { - expect(() => parseHttpDate("Sunday, 6-Nov-94 08:49:37 GMT"), + test('requires precise number lengths', () { + expect(() => parseHttpDate('Sunday, 6-Nov-94 08:49:37 GMT'), throwsFormatException); - expect(() => parseHttpDate("Sunday, 06-Nov-1994 08:49:37 GMT"), + expect(() => parseHttpDate('Sunday, 06-Nov-1994 08:49:37 GMT'), throwsFormatException); - expect(() => parseHttpDate("Sunday, 06-Nov-94 8:49:37 GMT"), + expect(() => parseHttpDate('Sunday, 06-Nov-94 8:49:37 GMT'), throwsFormatException); - expect(() => parseHttpDate("Sunday, 06-Nov-94 08:9:37 GMT"), + expect(() => parseHttpDate('Sunday, 06-Nov-94 08:9:37 GMT'), throwsFormatException); - expect(() => parseHttpDate("Sunday, 06-Nov-94 08:49:7 GMT"), + expect(() => parseHttpDate('Sunday, 06-Nov-94 08:49:7 GMT'), throwsFormatException); }); - test("requires reasonable numbers", () { - expect(() => parseHttpDate("Sunday, 00-Nov-94 08:49:37 GMT"), + test('requires reasonable numbers', () { + expect(() => parseHttpDate('Sunday, 00-Nov-94 08:49:37 GMT'), throwsFormatException); - expect(() => parseHttpDate("Sunday, 31-Nov-94 08:49:37 GMT"), + expect(() => parseHttpDate('Sunday, 31-Nov-94 08:49:37 GMT'), throwsFormatException); - expect(() => parseHttpDate("Sunday, 32-Aug-94 08:49:37 GMT"), + expect(() => parseHttpDate('Sunday, 32-Aug-94 08:49:37 GMT'), throwsFormatException); - expect(() => parseHttpDate("Sunday, 06-Nov-94 24:49:37 GMT"), + expect(() => parseHttpDate('Sunday, 06-Nov-94 24:49:37 GMT'), throwsFormatException); - expect(() => parseHttpDate("Sunday, 06-Nov-94 08:60:37 GMT"), + expect(() => parseHttpDate('Sunday, 06-Nov-94 08:60:37 GMT'), throwsFormatException); - expect(() => parseHttpDate("Sunday, 06-Nov-94 08:49:60 GMT"), + expect(() => parseHttpDate('Sunday, 06-Nov-94 08:49:60 GMT'), throwsFormatException); }); - test("only allows long weekday names", () { - expect(() => parseHttpDate("Sun, 6-Nov-94 08:49:37 GMT"), + test('only allows long weekday names', () { + expect(() => parseHttpDate('Sun, 6-Nov-94 08:49:37 GMT'), throwsFormatException); }); - test("only allows short month names", () { - expect(() => parseHttpDate("Sunday, 6-November-94 08:49:37 GMT"), + test('only allows short month names', () { + expect(() => parseHttpDate('Sunday, 6-November-94 08:49:37 GMT'), throwsFormatException); }); - test("only allows GMT", () { - expect(() => parseHttpDate("Sunday, 6-Nov-94 08:49:37 PST"), + test('only allows GMT', () { + expect(() => parseHttpDate('Sunday, 6-Nov-94 08:49:37 PST'), throwsFormatException); }); - test("disallows trailing whitespace", () { - expect(() => parseHttpDate("Sunday, 6-Nov-94 08:49:37 GMT "), + test('disallows trailing whitespace', () { + expect(() => parseHttpDate('Sunday, 6-Nov-94 08:49:37 GMT '), throwsFormatException); }); }); - group("asctime()", () { - test("parses the example date", () { - var date = parseHttpDate("Sun Nov 6 08:49:37 1994"); + group('asctime()', () { + test('parses the example date', () { + var date = parseHttpDate('Sun Nov 6 08:49:37 1994'); expect(date.day, equals(6)); expect(date.month, equals(DateTime.november)); expect(date.year, equals(1994)); expect(date.hour, equals(8)); expect(date.minute, equals(49)); expect(date.second, equals(37)); - expect(date.timeZoneName, equals("UTC")); + expect(date.timeZoneName, equals('UTC')); }); - test("parses a date with a two-digit day", () { - var date = parseHttpDate("Sun Nov 16 08:49:37 1994"); + test('parses a date with a two-digit day', () { + var date = parseHttpDate('Sun Nov 16 08:49:37 1994'); expect(date.day, equals(16)); expect(date.month, equals(DateTime.november)); expect(date.year, equals(1994)); expect(date.hour, equals(8)); expect(date.minute, equals(49)); expect(date.second, equals(37)); - expect(date.timeZoneName, equals("UTC")); + expect(date.timeZoneName, equals('UTC')); }); - test("whitespace is required", () { - expect(() => parseHttpDate("SunNov 6 08:49:37 1994"), + test('whitespace is required', () { + expect(() => parseHttpDate('SunNov 6 08:49:37 1994'), throwsFormatException); - expect(() => parseHttpDate("Sun Nov6 08:49:37 1994"), + expect(() => parseHttpDate('Sun Nov6 08:49:37 1994'), throwsFormatException); - expect(() => parseHttpDate("Sun Nov 608:49:37 1994"), + expect(() => parseHttpDate('Sun Nov 608:49:37 1994'), throwsFormatException); - expect(() => parseHttpDate("Sun Nov 6 08:49:371994"), + expect(() => parseHttpDate('Sun Nov 6 08:49:371994'), throwsFormatException); }); - test("the right amount of whitespace is required", () { - expect(() => parseHttpDate("Sun Nov 6 08:49:37 1994"), + test('the right amount of whitespace is required', () { + expect(() => parseHttpDate('Sun Nov 6 08:49:37 1994'), throwsFormatException); - expect(() => parseHttpDate("Sun Nov 6 08:49:37 1994"), + expect(() => parseHttpDate('Sun Nov 6 08:49:37 1994'), throwsFormatException); - expect(() => parseHttpDate("Sun Nov 6 08:49:37 1994"), + expect(() => parseHttpDate('Sun Nov 6 08:49:37 1994'), throwsFormatException); - expect(() => parseHttpDate("Sun Nov 6 08:49:37 1994"), + expect(() => parseHttpDate('Sun Nov 6 08:49:37 1994'), throwsFormatException); - expect(() => parseHttpDate("Sun Nov 6 08:49:37 1994"), + expect(() => parseHttpDate('Sun Nov 6 08:49:37 1994'), throwsFormatException); }); - test("requires precise number lengths", () { - expect(() => parseHttpDate("Sun Nov 016 08:49:37 1994"), + test('requires precise number lengths', () { + expect(() => parseHttpDate('Sun Nov 016 08:49:37 1994'), throwsFormatException); - expect(() => parseHttpDate("Sun Nov 6 8:49:37 1994"), + expect(() => parseHttpDate('Sun Nov 6 8:49:37 1994'), throwsFormatException); - expect(() => parseHttpDate("Sun Nov 6 08:9:37 1994"), + expect(() => parseHttpDate('Sun Nov 6 08:9:37 1994'), throwsFormatException); - expect(() => parseHttpDate("Sun Nov 6 08:49:7 1994"), + expect(() => parseHttpDate('Sun Nov 6 08:49:7 1994'), throwsFormatException); - expect(() => parseHttpDate("Sun Nov 6 08:49:37 94"), + expect(() => parseHttpDate('Sun Nov 6 08:49:37 94'), throwsFormatException); }); - test("requires reasonable numbers", () { - expect(() => parseHttpDate("Sun Nov 0 08:49:37 1994"), + test('requires reasonable numbers', () { + expect(() => parseHttpDate('Sun Nov 0 08:49:37 1994'), throwsFormatException); - expect(() => parseHttpDate("Sun Nov 31 08:49:37 1994"), + expect(() => parseHttpDate('Sun Nov 31 08:49:37 1994'), throwsFormatException); - expect(() => parseHttpDate("Sun Aug 32 08:49:37 1994"), + expect(() => parseHttpDate('Sun Aug 32 08:49:37 1994'), throwsFormatException); - expect(() => parseHttpDate("Sun Nov 6 24:49:37 1994"), + expect(() => parseHttpDate('Sun Nov 6 24:49:37 1994'), throwsFormatException); - expect(() => parseHttpDate("Sun Nov 6 08:60:37 1994"), + expect(() => parseHttpDate('Sun Nov 6 08:60:37 1994'), throwsFormatException); - expect(() => parseHttpDate("Sun Nov 6 08:49:60 1994"), + expect(() => parseHttpDate('Sun Nov 6 08:49:60 1994'), throwsFormatException); }); - test("only allows short weekday names", () { - expect(() => parseHttpDate("Sunday Nov 0 08:49:37 1994"), + test('only allows short weekday names', () { + expect(() => parseHttpDate('Sunday Nov 0 08:49:37 1994'), throwsFormatException); }); - test("only allows short month names", () { - expect(() => parseHttpDate("Sun November 0 08:49:37 1994"), + test('only allows short month names', () { + expect(() => parseHttpDate('Sun November 0 08:49:37 1994'), throwsFormatException); }); - test("disallows trailing whitespace", () { - expect(() => parseHttpDate("Sun November 0 08:49:37 1994 "), + test('disallows trailing whitespace', () { + expect(() => parseHttpDate('Sun November 0 08:49:37 1994 '), throwsFormatException); }); }); diff --git a/pkgs/http_parser/test/media_type_test.dart b/pkgs/http_parser/test/media_type_test.dart index 87e4cee6dd..a310f81d33 100644 --- a/pkgs/http_parser/test/media_type_test.dart +++ b/pkgs/http_parser/test/media_type_test.dart @@ -6,160 +6,160 @@ import 'package:http_parser/http_parser.dart'; import 'package:test/test.dart'; void main() { - group("parse", () { - test("parses a simple MIME type", () { - var type = MediaType.parse("text/plain"); - expect(type.type, equals("text")); - expect(type.subtype, equals("plain")); + group('parse', () { + test('parses a simple MIME type', () { + var type = MediaType.parse('text/plain'); + expect(type.type, equals('text')); + expect(type.subtype, equals('plain')); }); - test("allows leading whitespace", () { - expect(MediaType.parse(" text/plain").mimeType, equals("text/plain")); - expect(MediaType.parse("\ttext/plain").mimeType, equals("text/plain")); + test('allows leading whitespace', () { + expect(MediaType.parse(' text/plain').mimeType, equals('text/plain')); + expect(MediaType.parse('\ttext/plain').mimeType, equals('text/plain')); }); - test("allows trailing whitespace", () { - expect(MediaType.parse("text/plain ").mimeType, equals("text/plain")); - expect(MediaType.parse("text/plain\t").mimeType, equals("text/plain")); + test('allows trailing whitespace', () { + expect(MediaType.parse('text/plain ').mimeType, equals('text/plain')); + expect(MediaType.parse('text/plain\t').mimeType, equals('text/plain')); }); - test("disallows separators in the MIME type", () { - expect(() => MediaType.parse("te(xt/plain"), throwsFormatException); - expect(() => MediaType.parse("text/pla=in"), throwsFormatException); + test('disallows separators in the MIME type', () { + expect(() => MediaType.parse('te(xt/plain'), throwsFormatException); + expect(() => MediaType.parse('text/pla=in'), throwsFormatException); }); - test("disallows whitespace around the slash", () { - expect(() => MediaType.parse("text /plain"), throwsFormatException); - expect(() => MediaType.parse("text/ plain"), throwsFormatException); + test('disallows whitespace around the slash', () { + expect(() => MediaType.parse('text /plain'), throwsFormatException); + expect(() => MediaType.parse('text/ plain'), throwsFormatException); }); - test("parses parameters", () { - var type = MediaType.parse("text/plain;foo=bar;baz=bang"); - expect(type.mimeType, equals("text/plain")); - expect(type.parameters, equals({"foo": "bar", "baz": "bang"})); + test('parses parameters', () { + var type = MediaType.parse('text/plain;foo=bar;baz=bang'); + expect(type.mimeType, equals('text/plain')); + expect(type.parameters, equals({'foo': 'bar', 'baz': 'bang'})); }); - test("allows whitespace around the semicolon", () { - var type = MediaType.parse("text/plain ; foo=bar ; baz=bang"); - expect(type.mimeType, equals("text/plain")); - expect(type.parameters, equals({"foo": "bar", "baz": "bang"})); + test('allows whitespace around the semicolon', () { + var type = MediaType.parse('text/plain ; foo=bar ; baz=bang'); + expect(type.mimeType, equals('text/plain')); + expect(type.parameters, equals({'foo': 'bar', 'baz': 'bang'})); }); - test("disallows whitespace around the equals", () { + test('disallows whitespace around the equals', () { expect( - () => MediaType.parse("text/plain; foo =bar"), throwsFormatException); + () => MediaType.parse('text/plain; foo =bar'), throwsFormatException); expect( - () => MediaType.parse("text/plain; foo= bar"), throwsFormatException); + () => MediaType.parse('text/plain; foo= bar'), throwsFormatException); }); - test("disallows separators in the parameters", () { + test('disallows separators in the parameters', () { expect( - () => MediaType.parse("text/plain; fo:o=bar"), throwsFormatException); + () => MediaType.parse('text/plain; fo:o=bar'), throwsFormatException); expect( - () => MediaType.parse("text/plain; foo=b@ar"), throwsFormatException); + () => MediaType.parse('text/plain; foo=b@ar'), throwsFormatException); }); - test("parses quoted parameters", () { + test('parses quoted parameters', () { var type = MediaType.parse('text/plain; foo="bar space"; baz="bang\\\\escape"'); - expect(type.mimeType, equals("text/plain")); + expect(type.mimeType, equals('text/plain')); expect( - type.parameters, equals({"foo": "bar space", "baz": "bang\\escape"})); + type.parameters, equals({'foo': 'bar space', 'baz': 'bang\\escape'})); }); - test("lower-cases type and subtype", () { + test('lower-cases type and subtype', () { var type = MediaType.parse('TeXt/pLaIn'); - expect(type.type, equals("text")); - expect(type.subtype, equals("plain")); - expect(type.mimeType, equals("text/plain")); + expect(type.type, equals('text')); + expect(type.subtype, equals('plain')); + expect(type.mimeType, equals('text/plain')); }); - test("records parameters as case-insensitive", () { + test('records parameters as case-insensitive', () { var type = MediaType.parse('test/plain;FoO=bar;bAz=bang'); - expect(type.parameters, equals({"FoO": "bar", "bAz": "bang"})); - expect(type.parameters, containsPair("foo", "bar")); - expect(type.parameters, containsPair("baz", "bang")); + expect(type.parameters, equals({'FoO': 'bar', 'bAz': 'bang'})); + expect(type.parameters, containsPair('foo', 'bar')); + expect(type.parameters, containsPair('baz', 'bang')); }); }); - group("change", () { + group('change', () { MediaType type; setUp(() { - type = MediaType.parse("text/plain; foo=bar; baz=bang"); + type = MediaType.parse('text/plain; foo=bar; baz=bang'); }); - test("uses the existing fields by default", () { + test('uses the existing fields by default', () { var newType = type.change(); - expect(newType.type, equals("text")); - expect(newType.subtype, equals("plain")); - expect(newType.parameters, equals({"foo": "bar", "baz": "bang"})); + expect(newType.type, equals('text')); + expect(newType.subtype, equals('plain')); + expect(newType.parameters, equals({'foo': 'bar', 'baz': 'bang'})); }); - test("[type] overrides the existing type", () { - expect(type.change(type: "new").type, equals("new")); + test('[type] overrides the existing type', () { + expect(type.change(type: 'new').type, equals('new')); }); - test("[subtype] overrides the existing subtype", () { - expect(type.change(subtype: "new").subtype, equals("new")); + test('[subtype] overrides the existing subtype', () { + expect(type.change(subtype: 'new').subtype, equals('new')); }); - test("[mimeType] overrides the existing type and subtype", () { - var newType = type.change(mimeType: "image/png"); - expect(newType.type, equals("image")); - expect(newType.subtype, equals("png")); + test('[mimeType] overrides the existing type and subtype', () { + var newType = type.change(mimeType: 'image/png'); + expect(newType.type, equals('image')); + expect(newType.subtype, equals('png')); }); - test("[parameters] overrides and adds to existing parameters", () { + test('[parameters] overrides and adds to existing parameters', () { expect( - type.change(parameters: {"foo": "zap", "qux": "fblthp"}).parameters, - equals({"foo": "zap", "baz": "bang", "qux": "fblthp"})); + type.change(parameters: {'foo': 'zap', 'qux': 'fblthp'}).parameters, + equals({'foo': 'zap', 'baz': 'bang', 'qux': 'fblthp'})); }); - test("[clearParameters] removes existing parameters", () { + test('[clearParameters] removes existing parameters', () { expect(type.change(clearParameters: true).parameters, isEmpty); }); - test("[clearParameters] with [parameters] removes before adding", () { + test('[clearParameters] with [parameters] removes before adding', () { var newType = - type.change(parameters: {"foo": "zap"}, clearParameters: true); - expect(newType.parameters, equals({"foo": "zap"})); + type.change(parameters: {'foo': 'zap'}, clearParameters: true); + expect(newType.parameters, equals({'foo': 'zap'})); }); - test("[type] with [mimeType] is illegal", () { - expect(() => type.change(type: "new", mimeType: "image/png"), + test('[type] with [mimeType] is illegal', () { + expect(() => type.change(type: 'new', mimeType: 'image/png'), throwsArgumentError); }); - test("[subtype] with [mimeType] is illegal", () { - expect(() => type.change(subtype: "new", mimeType: "image/png"), + test('[subtype] with [mimeType] is illegal', () { + expect(() => type.change(subtype: 'new', mimeType: 'image/png'), throwsArgumentError); }); }); - group("toString", () { - test("serializes a simple MIME type", () { - expect(MediaType("text", "plain").toString(), equals("text/plain")); + group('toString', () { + test('serializes a simple MIME type', () { + expect(MediaType('text', 'plain').toString(), equals('text/plain')); }); - test("serializes a token parameter as a token", () { - expect(MediaType("text", "plain", {"foo": "bar"}).toString(), - equals("text/plain; foo=bar")); + test('serializes a token parameter as a token', () { + expect(MediaType('text', 'plain', {'foo': 'bar'}).toString(), + equals('text/plain; foo=bar')); }); - test("serializes a non-token parameter as a quoted string", () { - expect(MediaType("text", "plain", {"foo": "bar baz"}).toString(), + test('serializes a non-token parameter as a quoted string', () { + expect(MediaType('text', 'plain', {'foo': 'bar baz'}).toString(), equals('text/plain; foo="bar baz"')); }); - test("escapes a quoted string as necessary", () { - expect(MediaType("text", "plain", {"foo": 'bar"\x7Fbaz'}).toString(), + test('escapes a quoted string as necessary', () { + expect(MediaType('text', 'plain', {'foo': 'bar"\x7Fbaz'}).toString(), equals('text/plain; foo="bar\\"\\\x7Fbaz"')); }); - test("serializes multiple parameters", () { + test('serializes multiple parameters', () { expect( - MediaType("text", "plain", {"foo": "bar", "baz": "bang"}).toString(), - equals("text/plain; foo=bar; baz=bang")); + MediaType('text', 'plain', {'foo': 'bar', 'baz': 'bang'}).toString(), + equals('text/plain; foo=bar; baz=bang')); }); }); } From 9cc7340d65fae38d4dff8c6f948ae286b59f3fab Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 16 Mar 2020 13:21:43 -0700 Subject: [PATCH 059/126] Remove dartlang.org links (dart-lang/http_parser#29) --- pkgs/http_parser/CHANGELOG.md | 2 +- pkgs/http_parser/pubspec.yaml | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index 7f84286625..aaf7aa4583 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -46,7 +46,7 @@ [the `web_socket_channel` package][web_socket_channel]. The implementation here is now deprecated. -[web_socket_channel]: https://pub.dartlang.org/packages/web_socket_channel +[web_socket_channel]: https://pub.dev/packages/web_socket_channel ## 2.1.0 diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index 4a416fabf9..1444f72fa2 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,9 +1,8 @@ name: http_parser version: 3.1.4-dev -description: > +description: >- A platform-independent package for parsing and serializing HTTP formats. -author: Dart Team homepage: https://github.com/dart-lang/http_parser environment: From aef6ee47a8037f9553ca86b3c3b2e37971e56016 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 23 Mar 2020 14:44:32 -0700 Subject: [PATCH 060/126] Remove lints duplicated in pkg:pednatic --- pkgs/http_parser/analysis_options.yaml | 39 ++------------------------ 1 file changed, 2 insertions(+), 37 deletions(-) diff --git a/pkgs/http_parser/analysis_options.yaml b/pkgs/http_parser/analysis_options.yaml index 0f4b864612..99eb23020a 100644 --- a/pkgs/http_parser/analysis_options.yaml +++ b/pkgs/http_parser/analysis_options.yaml @@ -1,27 +1,20 @@ include: package:pedantic/analysis_options.yaml + analyzer: strong-mode: implicit-casts: false + linter: rules: - - always_declare_return_types - - annotate_overrides - avoid_bool_literals_in_conditional_expressions - avoid_classes_with_only_static_members - - avoid_empty_else - avoid_function_literals_in_foreach_calls - - avoid_init_to_null - - avoid_null_checks_in_equality_operators - - avoid_relative_lib_imports - avoid_renaming_method_parameters - - avoid_return_types_on_setters - avoid_returning_null - avoid_returning_null_for_future - avoid_returning_null_for_void - avoid_returning_this - - avoid_shadowing_type_parameters - avoid_single_cascade_in_expression_statements - - avoid_types_as_parameter_names - avoid_unused_constructor_parameters - await_only_futures - camel_case_types @@ -31,8 +24,6 @@ linter: - constant_identifier_names - control_flow_in_finally - directives_ordering - - empty_catches - - empty_constructor_bodies - empty_statements - file_names - hash_and_equals @@ -40,54 +31,28 @@ linter: - invariant_booleans - iterable_contains_unrelated_type - join_return_with_assignment - - library_names - - library_prefixes - list_remove_unrelated_type - literal_only_boolean_expressions - no_adjacent_strings_in_list - - no_duplicate_case_values - non_constant_identifier_names - - null_closures - - omit_local_variable_types - only_throw_errors - overridden_fields - package_api_docs - package_names - package_prefixed_library_names - - prefer_adjacent_string_concatenation - - prefer_collection_literals - - prefer_conditional_assignment - prefer_const_constructors - - prefer_contains - - prefer_equal_for_default_values - - prefer_final_fields #- prefer_final_locals - - prefer_generic_function_type_aliases - prefer_initializing_formals #- prefer_interpolation_to_compose_strings - - prefer_is_empty - - prefer_is_not_empty - prefer_null_aware_operators - #- prefer_single_quotes - prefer_typing_uninitialized_variables - - recursive_getters - - slash_for_doc_comments - test_types_in_equals - throw_in_finally - - type_init_formals - - unawaited_futures - unnecessary_await_in_return - unnecessary_brace_in_string_interps - - unnecessary_const - unnecessary_getters_setters - unnecessary_lambdas - - unnecessary_new - unnecessary_null_aware_assignments - unnecessary_parenthesis - unnecessary_statements - - unnecessary_this - - unrelated_type_equality_checks - - use_function_type_syntax_for_parameters - - use_rethrow_when_possible - - valid_regexps - void_checks From c094d1ed8ec888de4666f9a44be2125206eaa289 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 23 Mar 2020 14:46:10 -0700 Subject: [PATCH 061/126] Enable and fix comment references prefer_interpolation lints Deleted lint we're not going to enable --- pkgs/http_parser/analysis_options.yaml | 5 ++--- pkgs/http_parser/lib/src/http_date.dart | 9 ++++----- pkgs/http_parser/lib/src/media_type.dart | 2 +- pkgs/http_parser/lib/src/scan.dart | 14 -------------- 4 files changed, 7 insertions(+), 23 deletions(-) diff --git a/pkgs/http_parser/analysis_options.yaml b/pkgs/http_parser/analysis_options.yaml index 99eb23020a..a71a4ae2d2 100644 --- a/pkgs/http_parser/analysis_options.yaml +++ b/pkgs/http_parser/analysis_options.yaml @@ -19,8 +19,7 @@ linter: - await_only_futures - camel_case_types - cancel_subscriptions - #- cascade_invocations - #- comment_references + - comment_references - constant_identifier_names - control_flow_in_finally - directives_ordering @@ -43,7 +42,7 @@ linter: - prefer_const_constructors #- prefer_final_locals - prefer_initializing_formals - #- prefer_interpolation_to_compose_strings + - prefer_interpolation_to_compose_strings - prefer_null_aware_operators - prefer_typing_uninitialized_variables - test_types_in_equals diff --git a/pkgs/http_parser/lib/src/http_date.dart b/pkgs/http_parser/lib/src/http_date.dart index ebdb4aaef4..76d1d2b0e0 100644 --- a/pkgs/http_parser/lib/src/http_date.dart +++ b/pkgs/http_parser/lib/src/http_date.dart @@ -30,8 +30,8 @@ final _digitRegExp = RegExp(r'\d+'); /// Return a HTTP-formatted string representation of [date]. /// -/// This follows [RFC 822](http://tools.ietf.org/html/rfc822) as updated by [RFC -/// 1123](http://tools.ietf.org/html/rfc1123). +/// This follows [RFC 822](http://tools.ietf.org/html/rfc822) as updated by +/// [RFC 1123](http://tools.ietf.org/html/rfc1123). String formatHttpDate(DateTime date) { date = date.toUtc(); var buffer = StringBuffer() @@ -55,9 +55,8 @@ String formatHttpDate(DateTime date) { /// Parses an HTTP-formatted date into a UTC [DateTime]. /// -/// This follows [RFC -/// 2616](http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3). It will -/// throw a [FormatException] if [date] is invalid. +/// This follows [RFC 2616](http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3). +/// It will throw a [FormatException] if [date] is invalid. DateTime parseHttpDate(String date) { return wrapFormatException('HTTP date', date, () { var scanner = StringScanner(date); diff --git a/pkgs/http_parser/lib/src/media_type.dart b/pkgs/http_parser/lib/src/media_type.dart index 1d004953ad..2ad699a112 100644 --- a/pkgs/http_parser/lib/src/media_type.dart +++ b/pkgs/http_parser/lib/src/media_type.dart @@ -140,7 +140,7 @@ class MediaType { buffer ..write('"') ..write( - value.replaceAllMapped(_escapedChar, (match) => '\\' + match[0])) + value.replaceAllMapped(_escapedChar, (match) => '\\${match[0]}')) ..write('"'); } else { buffer.write(value); diff --git a/pkgs/http_parser/lib/src/scan.dart b/pkgs/http_parser/lib/src/scan.dart index 2881e48fbb..38cec4952b 100644 --- a/pkgs/http_parser/lib/src/scan.dart +++ b/pkgs/http_parser/lib/src/scan.dart @@ -2,22 +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. -/// A library for broadly-useful functions and regular expressions for scanning -/// HTTP entities. -/// -/// Many of the regular expressions come from [section 2.2 of the HTTP -/// spec][spec]. -/// -/// [spec]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html import 'package:string_scanner/string_scanner.dart'; -/// HTTP entities. -/// -/// Many of the regular expressions come from [section 2.2 of the HTTP -/// spec][spec]. -/// -/// [spec]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html - /// An HTTP token. final token = RegExp(r'[^()<>@,;:"\\/[\]?={} \t\x00-\x1F\x7F]+'); From 0518ae9c45796c9112f8b91b06d7925db7e56c5d Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 23 Mar 2020 14:49:01 -0700 Subject: [PATCH 062/126] lint: enable and fix prefer_final_locals --- pkgs/http_parser/analysis_options.yaml | 2 +- .../lib/src/authentication_challenge.dart | 20 +++++----- .../lib/src/chunked_coding/decoder.dart | 16 ++++---- .../lib/src/chunked_coding/encoder.dart | 8 ++-- pkgs/http_parser/lib/src/http_date.dart | 37 ++++++++++--------- pkgs/http_parser/lib/src/media_type.dart | 16 ++++---- pkgs/http_parser/lib/src/scan.dart | 4 +- .../test/authentication_challenge_test.dart | 27 +++++++------- .../test/case_insensitive_map_test.dart | 6 +-- .../http_parser/test/chunked_coding_test.dart | 12 +++--- pkgs/http_parser/test/http_date_test.dart | 26 ++++++------- pkgs/http_parser/test/media_type_test.dart | 18 ++++----- 12 files changed, 97 insertions(+), 95 deletions(-) diff --git a/pkgs/http_parser/analysis_options.yaml b/pkgs/http_parser/analysis_options.yaml index a71a4ae2d2..c2977b5d52 100644 --- a/pkgs/http_parser/analysis_options.yaml +++ b/pkgs/http_parser/analysis_options.yaml @@ -40,7 +40,7 @@ linter: - package_names - package_prefixed_library_names - prefer_const_constructors - #- prefer_final_locals + - prefer_final_locals - prefer_initializing_formals - prefer_interpolation_to_compose_strings - prefer_null_aware_operators diff --git a/pkgs/http_parser/lib/src/authentication_challenge.dart b/pkgs/http_parser/lib/src/authentication_challenge.dart index ec368e57a2..e4b6619b46 100644 --- a/pkgs/http_parser/lib/src/authentication_challenge.dart +++ b/pkgs/http_parser/lib/src/authentication_challenge.dart @@ -35,14 +35,14 @@ class AuthenticationChallenge { /// Throws a [FormatException] if the header is invalid. static List parseHeader(String header) { return wrapFormatException('authentication header', header, () { - var scanner = StringScanner(header); + final scanner = StringScanner(header); scanner.scan(whitespace); - var challenges = parseList(scanner, () { - var scheme = _scanScheme(scanner, whitespaceName: '" " or "="'); + final challenges = parseList(scanner, () { + final scheme = _scanScheme(scanner, whitespaceName: '" " or "="'); // Manually parse the inner list. We need to do some lookahead to // disambiguate between an auth param and another challenge. - var params = {}; + final params = {}; // Consume initial empty values. while (scanner.scan(',')) { @@ -59,7 +59,7 @@ class AuthenticationChallenge { if (scanner.matches(',') || scanner.isDone) continue; scanner.expect(token, name: 'a token'); - var name = scanner.lastMatch[0]; + final name = scanner.lastMatch[0]; scanner.scan(whitespace); // If there's no "=", then this is another challenge rather than a @@ -95,11 +95,11 @@ class AuthenticationChallenge { /// Throws a [FormatException] if the challenge is invalid. factory AuthenticationChallenge.parse(String challenge) { return wrapFormatException('authentication challenge', challenge, () { - var scanner = StringScanner(challenge); + final scanner = StringScanner(challenge); scanner.scan(whitespace); - var scheme = _scanScheme(scanner); + final scheme = _scanScheme(scanner); - var params = {}; + final params = {}; parseList(scanner, () => _scanAuthParam(scanner, params)); scanner.expectDone(); @@ -113,7 +113,7 @@ class AuthenticationChallenge { /// due to invalid trailing whitespace. static String _scanScheme(StringScanner scanner, {String whitespaceName}) { scanner.expect(token, name: 'a token'); - var scheme = scanner.lastMatch[0].toLowerCase(); + final scheme = scanner.lastMatch[0].toLowerCase(); scanner.scan(whitespace); @@ -129,7 +129,7 @@ class AuthenticationChallenge { /// Scans a single authentication parameter and stores its result in [params]. static void _scanAuthParam(StringScanner scanner, Map params) { scanner.expect(token, name: 'a token'); - var name = scanner.lastMatch[0]; + final name = scanner.lastMatch[0]; scanner.scan(whitespace); scanner.expect('='); scanner.scan(whitespace); diff --git a/pkgs/http_parser/lib/src/chunked_coding/decoder.dart b/pkgs/http_parser/lib/src/chunked_coding/decoder.dart index 9b2b194219..cd06f397f4 100644 --- a/pkgs/http_parser/lib/src/chunked_coding/decoder.dart +++ b/pkgs/http_parser/lib/src/chunked_coding/decoder.dart @@ -18,8 +18,8 @@ class ChunkedCodingDecoder extends Converter, List> { @override List convert(List input) { - var sink = _Sink(null); - var output = sink._decode(input, 0, input.length); + final sink = _Sink(null); + final output = sink._decode(input, 0, input.length); if (sink._state == _State.end) return output; throw FormatException('Input ended unexpectedly.', input, input.length); @@ -50,7 +50,7 @@ class _Sink extends ByteConversionSinkBase { @override void addSlice(List chunk, int start, int end, bool isLast) { RangeError.checkValidRange(start, end, chunk.length); - var output = _decode(chunk, start, end); + final output = _decode(chunk, start, end); if (output.isNotEmpty) _sink.add(output); if (isLast) _close(chunk, end); } @@ -78,7 +78,7 @@ class _Sink extends ByteConversionSinkBase { } } - var buffer = Uint8Buffer(); + final buffer = Uint8Buffer(); while (start != end) { switch (_state) { case _State.boundary: @@ -105,7 +105,7 @@ class _Sink extends ByteConversionSinkBase { break; case _State.body: - var chunkEnd = math.min(end, start + _size); + final chunkEnd = math.min(end, start + _size); buffer.addAll(bytes, start, chunkEnd); _size -= chunkEnd - start; start = chunkEnd; @@ -156,8 +156,8 @@ class _Sink extends ByteConversionSinkBase { // We check for digits first because it ensures there's only a single branch // for 10 out of 16 of the expected cases. We don't count the `digit >= 0` // check because branch prediction will always work on it for valid data. - var byte = bytes[index]; - var digit = $0 ^ byte; + final byte = bytes[index]; + final digit = $0 ^ byte; if (digit <= 9) { if (digit >= 0) return digit; } else { @@ -165,7 +165,7 @@ class _Sink extends ByteConversionSinkBase { // because uppercase letters in ASCII are exactly `0b100000 = 0x20` less // than lowercase letters, so if we ensure that that bit is 1 we ensure that // the letter is lowercase. - var letter = 0x20 | byte; + final letter = 0x20 | byte; if ($a <= letter && letter <= $f) return letter - $a + 10; } diff --git a/pkgs/http_parser/lib/src/chunked_coding/encoder.dart b/pkgs/http_parser/lib/src/chunked_coding/encoder.dart index e1f2750e91..81e4dc8fb3 100644 --- a/pkgs/http_parser/lib/src/chunked_coding/encoder.dart +++ b/pkgs/http_parser/lib/src/chunked_coding/encoder.dart @@ -60,12 +60,12 @@ class _Sink extends ByteConversionSinkBase { List _convert(List bytes, int start, int end, {bool isLast = false}) { if (end == start) return isLast ? _doneChunk : const []; - var size = end - start; - var sizeInHex = size.toRadixString(16); - var footerSize = isLast ? _doneChunk.length : 0; + final size = end - start; + final sizeInHex = size.toRadixString(16); + final footerSize = isLast ? _doneChunk.length : 0; // Add 4 for the CRLF sequences that follow the size header and the bytes. - var list = Uint8List(sizeInHex.length + 4 + size + footerSize); + final list = Uint8List(sizeInHex.length + 4 + size + footerSize); list.setRange(0, sizeInHex.length, sizeInHex.codeUnits); var cursor = sizeInHex.length; diff --git a/pkgs/http_parser/lib/src/http_date.dart b/pkgs/http_parser/lib/src/http_date.dart index 76d1d2b0e0..88ada63ae7 100644 --- a/pkgs/http_parser/lib/src/http_date.dart +++ b/pkgs/http_parser/lib/src/http_date.dart @@ -34,7 +34,7 @@ final _digitRegExp = RegExp(r'\d+'); /// [RFC 1123](http://tools.ietf.org/html/rfc1123). String formatHttpDate(DateTime date) { date = date.toUtc(); - var buffer = StringBuffer() + final buffer = StringBuffer() ..write(_weekdays[date.weekday - 1]) ..write(', ') ..write(date.day <= 9 ? '0' : '') @@ -59,18 +59,18 @@ String formatHttpDate(DateTime date) { /// It will throw a [FormatException] if [date] is invalid. DateTime parseHttpDate(String date) { return wrapFormatException('HTTP date', date, () { - var scanner = StringScanner(date); + final scanner = StringScanner(date); if (scanner.scan(_longWeekdayRegExp)) { // RFC 850 starts with a long weekday. scanner.expect(', '); - var day = _parseInt(scanner, 2); + final day = _parseInt(scanner, 2); scanner.expect('-'); - var month = _parseMonth(scanner); + final month = _parseMonth(scanner); scanner.expect('-'); - var year = 1900 + _parseInt(scanner, 2); + final year = 1900 + _parseInt(scanner, 2); scanner.expect(' '); - var time = _parseTime(scanner); + final time = _parseTime(scanner); scanner.expect(' GMT'); scanner.expectDone(); @@ -81,13 +81,13 @@ DateTime parseHttpDate(String date) { scanner.expect(_shortWeekdayRegExp); if (scanner.scan(', ')) { // RFC 1123 follows the weekday with a comma. - var day = _parseInt(scanner, 2); + final day = _parseInt(scanner, 2); scanner.expect(' '); - var month = _parseMonth(scanner); + final month = _parseMonth(scanner); scanner.expect(' '); - var year = _parseInt(scanner, 4); + final year = _parseInt(scanner, 4); scanner.expect(' '); - var time = _parseTime(scanner); + final time = _parseTime(scanner); scanner.expect(' GMT'); scanner.expectDone(); @@ -96,13 +96,14 @@ DateTime parseHttpDate(String date) { // asctime follows the weekday with a space. scanner.expect(' '); - var month = _parseMonth(scanner); + final month = _parseMonth(scanner); scanner.expect(' '); - var day = scanner.scan(' ') ? _parseInt(scanner, 1) : _parseInt(scanner, 2); + final day = + scanner.scan(' ') ? _parseInt(scanner, 1) : _parseInt(scanner, 2); scanner.expect(' '); - var time = _parseTime(scanner); + final time = _parseTime(scanner); scanner.expect(' '); - var year = _parseInt(scanner, 4); + final year = _parseInt(scanner, 4); scanner.expectDone(); return _makeDateTime(year, month, day, time); @@ -128,15 +129,15 @@ int _parseInt(StringScanner scanner, int digits) { /// Parses an timestamp of the form "HH:MM:SS" on a 24-hour clock. DateTime _parseTime(StringScanner scanner) { - var hours = _parseInt(scanner, 2); + final hours = _parseInt(scanner, 2); if (hours >= 24) scanner.error('hours may not be greater than 24.'); scanner.expect(':'); - var minutes = _parseInt(scanner, 2); + final minutes = _parseInt(scanner, 2); if (minutes >= 60) scanner.error('minutes may not be greater than 60.'); scanner.expect(':'); - var seconds = _parseInt(scanner, 2); + final seconds = _parseInt(scanner, 2); if (seconds >= 60) scanner.error('seconds may not be greater than 60.'); return DateTime(1, 1, 1, hours, minutes, seconds); @@ -147,7 +148,7 @@ DateTime _parseTime(StringScanner scanner) { /// Validates that [day] is a valid day for [month]. If it's not, throws a /// [FormatException]. DateTime _makeDateTime(int year, int month, int day, DateTime time) { - var dateTime = + final dateTime = DateTime.utc(year, month, day, time.hour, time.minute, time.second); // If [day] was too large, it will cause [month] to overflow. diff --git a/pkgs/http_parser/lib/src/media_type.dart b/pkgs/http_parser/lib/src/media_type.dart index 2ad699a112..8d0b082890 100644 --- a/pkgs/http_parser/lib/src/media_type.dart +++ b/pkgs/http_parser/lib/src/media_type.dart @@ -44,20 +44,20 @@ class MediaType { // This parsing is based on sections 3.6 and 3.7 of the HTTP spec: // http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html. return wrapFormatException('media type', mediaType, () { - var scanner = StringScanner(mediaType); + final scanner = StringScanner(mediaType); scanner.scan(whitespace); scanner.expect(token); - var type = scanner.lastMatch[0]; + final type = scanner.lastMatch[0]; scanner.expect('/'); scanner.expect(token); - var subtype = scanner.lastMatch[0]; + final subtype = scanner.lastMatch[0]; scanner.scan(whitespace); - var parameters = {}; + final parameters = {}; while (scanner.scan(';')) { scanner.scan(whitespace); scanner.expect(token); - var attribute = scanner.lastMatch[0]; + final attribute = scanner.lastMatch[0]; scanner.expect('='); String value; @@ -105,7 +105,7 @@ class MediaType { '[mimeType].'); } - var segments = mimeType.split('/'); + final segments = mimeType.split('/'); if (segments.length != 2) { throw FormatException('Invalid mime type "$mimeType".'); } @@ -119,7 +119,7 @@ class MediaType { parameters ??= {}; if (!clearParameters) { - var newParameters = parameters; + final newParameters = parameters; parameters = Map.from(this.parameters); parameters.addAll(newParameters); } @@ -132,7 +132,7 @@ class MediaType { /// This will produce a valid HTTP media type. @override String toString() { - var buffer = StringBuffer()..write(type)..write('/')..write(subtype); + final buffer = StringBuffer()..write(type)..write('/')..write(subtype); parameters.forEach((attribute, value) { buffer.write('; $attribute='); diff --git a/pkgs/http_parser/lib/src/scan.dart b/pkgs/http_parser/lib/src/scan.dart index 38cec4952b..1bdf87787b 100644 --- a/pkgs/http_parser/lib/src/scan.dart +++ b/pkgs/http_parser/lib/src/scan.dart @@ -31,7 +31,7 @@ final whitespace = RegExp('(?:${_lws.pattern})*'); /// Once this is finished, [scanner] will be at the next non-LWS character in /// the string, or the end of the string. List parseList(StringScanner scanner, T Function() parseElement) { - var result = []; + final result = []; // Consume initial empty values. while (scanner.scan(',')) { @@ -61,7 +61,7 @@ List parseList(StringScanner scanner, T Function() parseElement) { String expectQuotedString(StringScanner scanner, {String name}) { name ??= 'quoted string'; scanner.expect(_quotedString, name: name); - var string = scanner.lastMatch[0]; + final string = scanner.lastMatch[0]; return string .substring(1, string.length - 1) .replaceAllMapped(_quotedPair, (match) => match[1]); diff --git a/pkgs/http_parser/test/authentication_challenge_test.dart b/pkgs/http_parser/test/authentication_challenge_test.dart index 35559e04f0..6184d3e1ef 100644 --- a/pkgs/http_parser/test/authentication_challenge_test.dart +++ b/pkgs/http_parser/test/authentication_challenge_test.dart @@ -14,14 +14,14 @@ void main() { group('parseHeader', () { group('with a single challenge', () { _singleChallengeTests((challenge) { - var challenges = AuthenticationChallenge.parseHeader(challenge); + final challenges = AuthenticationChallenge.parseHeader(challenge); expect(challenges, hasLength(1)); return challenges.single; }); }); test('parses multiple challenges', () { - var challenges = AuthenticationChallenge.parseHeader( + final challenges = AuthenticationChallenge.parseHeader( 'scheme1 realm=fblthp, scheme2 realm=asdfg'); expect(challenges, hasLength(2)); expect(challenges.first.scheme, equals('scheme1')); @@ -31,7 +31,7 @@ void main() { }); test('parses multiple challenges with multiple parameters', () { - var challenges = AuthenticationChallenge.parseHeader( + final challenges = AuthenticationChallenge.parseHeader( 'scheme1 realm=fblthp, foo=bar, scheme2 realm=asdfg, baz=bang'); expect(challenges, hasLength(2)); @@ -54,65 +54,66 @@ void main() { void _singleChallengeTests( AuthenticationChallenge Function(String challenge) parseChallenge) { test('parses a simple challenge', () { - var challenge = parseChallenge('scheme realm=fblthp'); + final challenge = parseChallenge('scheme realm=fblthp'); expect(challenge.scheme, equals('scheme')); expect(challenge.parameters, equals({'realm': 'fblthp'})); }); test('parses multiple parameters', () { - var challenge = parseChallenge('scheme realm=fblthp, foo=bar, baz=qux'); + final challenge = parseChallenge('scheme realm=fblthp, foo=bar, baz=qux'); expect(challenge.scheme, equals('scheme')); expect(challenge.parameters, equals({'realm': 'fblthp', 'foo': 'bar', 'baz': 'qux'})); }); test('parses quoted string parameters', () { - var challenge = parseChallenge('scheme realm="fblthp, foo=bar", baz="qux"'); + final challenge = + parseChallenge('scheme realm="fblthp, foo=bar", baz="qux"'); expect(challenge.scheme, equals('scheme')); expect(challenge.parameters, equals({'realm': 'fblthp, foo=bar', 'baz': 'qux'})); }); test('normalizes the case of the scheme', () { - var challenge = parseChallenge('ScHeMe realm=fblthp'); + final challenge = parseChallenge('ScHeMe realm=fblthp'); expect(challenge.scheme, equals('scheme')); expect(challenge.parameters, equals({'realm': 'fblthp'})); }); test('normalizes the case of the parameter name', () { - var challenge = parseChallenge('scheme ReAlM=fblthp'); + final challenge = parseChallenge('scheme ReAlM=fblthp'); expect(challenge.scheme, equals('scheme')); expect(challenge.parameters, containsPair('realm', 'fblthp')); }); test("doesn't normalize the case of the parameter value", () { - var challenge = parseChallenge('scheme realm=FbLtHp'); + final challenge = parseChallenge('scheme realm=FbLtHp'); expect(challenge.scheme, equals('scheme')); expect(challenge.parameters, containsPair('realm', 'FbLtHp')); expect(challenge.parameters, isNot(containsPair('realm', 'fblthp'))); }); test('allows extra whitespace', () { - var challenge = parseChallenge( + final challenge = parseChallenge( ' scheme\t \trealm\t = \tfblthp\t, \tfoo\t\r\n =\tbar\t'); expect(challenge.scheme, equals('scheme')); expect(challenge.parameters, equals({'realm': 'fblthp', 'foo': 'bar'})); }); test('allows an empty parameter', () { - var challenge = parseChallenge('scheme realm=fblthp, , foo=bar'); + final challenge = parseChallenge('scheme realm=fblthp, , foo=bar'); expect(challenge.scheme, equals('scheme')); expect(challenge.parameters, equals({'realm': 'fblthp', 'foo': 'bar'})); }); test('allows a leading comma', () { - var challenge = parseChallenge('scheme , realm=fblthp, foo=bar,'); + final challenge = parseChallenge('scheme , realm=fblthp, foo=bar,'); expect(challenge.scheme, equals('scheme')); expect(challenge.parameters, equals({'realm': 'fblthp', 'foo': 'bar'})); }); test('allows a trailing comma', () { - var challenge = parseChallenge('scheme realm=fblthp, foo=bar, ,'); + final challenge = parseChallenge('scheme realm=fblthp, foo=bar, ,'); expect(challenge.scheme, equals('scheme')); expect(challenge.parameters, equals({'realm': 'fblthp', 'foo': 'bar'})); }); diff --git a/pkgs/http_parser/test/case_insensitive_map_test.dart b/pkgs/http_parser/test/case_insensitive_map_test.dart index 25cd02b950..b08d89b233 100644 --- a/pkgs/http_parser/test/case_insensitive_map_test.dart +++ b/pkgs/http_parser/test/case_insensitive_map_test.dart @@ -7,7 +7,7 @@ import 'package:test/test.dart'; void main() { test('provides case-insensitive access to the map', () { - var map = CaseInsensitiveMap(); + final map = CaseInsensitiveMap(); map['fOo'] = 'bAr'; expect(map, containsPair('FoO', 'bAr')); @@ -16,13 +16,13 @@ void main() { }); test('stores the original key cases', () { - var map = CaseInsensitiveMap(); + final map = CaseInsensitiveMap(); map['fOo'] = 'bAr'; expect(map, equals({'fOo': 'bAr'})); }); test('.from() converts an existing map', () { - var map = CaseInsensitiveMap.from({'fOo': 'bAr'}); + final map = CaseInsensitiveMap.from({'fOo': 'bAr'}); expect(map, containsPair('FoO', 'bAr')); expect(map, equals({'fOo': 'bAr'})); }); diff --git a/pkgs/http_parser/test/chunked_coding_test.dart b/pkgs/http_parser/test/chunked_coding_test.dart index ec84614b5a..0b178ec68c 100644 --- a/pkgs/http_parser/test/chunked_coding_test.dart +++ b/pkgs/http_parser/test/chunked_coding_test.dart @@ -18,7 +18,7 @@ void main() { }); test('uses hex for chunk size', () { - var data = Iterable.generate(0xA7).toList(); + final data = Iterable.generate(0xA7).toList(); expect( chunkedCoding.encode(data), equals( @@ -34,7 +34,7 @@ void main() { ByteConversionSink sink; setUp(() { results = []; - var controller = StreamController>(sync: true); + final controller = StreamController>(sync: true); controller.stream.listen(results.add); sink = chunkedCoding.encoder.startChunkedConversion(controller.sink); }); @@ -175,7 +175,7 @@ void main() { }); test('parses hex size', () { - var data = Iterable.generate(0xA7).toList(); + final data = Iterable.generate(0xA7).toList(); expect( chunkedCoding.decode( [$a, $7, $cr, $lf, ...data, $cr, $lf, $0, $cr, $lf, $cr, $lf]), @@ -183,7 +183,7 @@ void main() { }); test('parses capital hex size', () { - var data = Iterable.generate(0xA7).toList(); + final data = Iterable.generate(0xA7).toList(); expect( chunkedCoding.decode( [$A, $7, $cr, $lf, ...data, $cr, $lf, $0, $cr, $lf, $cr, $lf]), @@ -259,7 +259,7 @@ void main() { ByteConversionSink sink; setUp(() { results = []; - var controller = StreamController>(sync: true); + final controller = StreamController>(sync: true); controller.stream.listen(results.add); sink = chunkedCoding.decoder.startChunkedConversion(controller.sink); }); @@ -327,7 +327,7 @@ void main() { sink.add([$a]); expect(results, isEmpty); - var data = Iterable.generate(0xA7).toList(); + final data = Iterable.generate(0xA7).toList(); sink.add([$7, $cr, $lf, ...data]); expect(results, equals([data])); }); diff --git a/pkgs/http_parser/test/http_date_test.dart b/pkgs/http_parser/test/http_date_test.dart index 3c54472bed..dfcdb4f3dc 100644 --- a/pkgs/http_parser/test/http_date_test.dart +++ b/pkgs/http_parser/test/http_date_test.dart @@ -8,31 +8,31 @@ import 'package:test/test.dart'; void main() { group('format', () { test('many values with 9', () { - var date = DateTime.utc(2014, 9, 9, 9, 9, 9); - var formatted = formatHttpDate(date); + final date = DateTime.utc(2014, 9, 9, 9, 9, 9); + final formatted = formatHttpDate(date); expect(formatted, 'Tue, 09 Sep 2014 09:09:09 GMT'); - var parsed = parseHttpDate(formatted); + final parsed = parseHttpDate(formatted); expect(parsed, date); }); test('end of year', () { - var date = DateTime.utc(1999, 12, 31, 23, 59, 59); - var formatted = formatHttpDate(date); + final date = DateTime.utc(1999, 12, 31, 23, 59, 59); + final formatted = formatHttpDate(date); expect(formatted, 'Fri, 31 Dec 1999 23:59:59 GMT'); - var parsed = parseHttpDate(formatted); + final parsed = parseHttpDate(formatted); expect(parsed, date); }); test('start of year', () { - var date = DateTime.utc(2000, 1, 1, 0, 0, 0); - var formatted = formatHttpDate(date); + final date = DateTime.utc(2000, 1, 1, 0, 0, 0); + final formatted = formatHttpDate(date); expect(formatted, 'Sat, 01 Jan 2000 00:00:00 GMT'); - var parsed = parseHttpDate(formatted); + final parsed = parseHttpDate(formatted); expect(parsed, date); }); @@ -41,7 +41,7 @@ void main() { group('parse', () { group('RFC 1123', () { test('parses the example date', () { - var date = parseHttpDate('Sun, 06 Nov 1994 08:49:37 GMT'); + final date = parseHttpDate('Sun, 06 Nov 1994 08:49:37 GMT'); expect(date.day, equals(6)); expect(date.month, equals(DateTime.november)); expect(date.year, equals(1994)); @@ -145,7 +145,7 @@ void main() { group('RFC 850', () { test('parses the example date', () { - var date = parseHttpDate('Sunday, 06-Nov-94 08:49:37 GMT'); + final date = parseHttpDate('Sunday, 06-Nov-94 08:49:37 GMT'); expect(date.day, equals(6)); expect(date.month, equals(DateTime.november)); expect(date.year, equals(1994)); @@ -237,7 +237,7 @@ void main() { group('asctime()', () { test('parses the example date', () { - var date = parseHttpDate('Sun Nov 6 08:49:37 1994'); + final date = parseHttpDate('Sun Nov 6 08:49:37 1994'); expect(date.day, equals(6)); expect(date.month, equals(DateTime.november)); expect(date.year, equals(1994)); @@ -248,7 +248,7 @@ void main() { }); test('parses a date with a two-digit day', () { - var date = parseHttpDate('Sun Nov 16 08:49:37 1994'); + final date = parseHttpDate('Sun Nov 16 08:49:37 1994'); expect(date.day, equals(16)); expect(date.month, equals(DateTime.november)); expect(date.year, equals(1994)); diff --git a/pkgs/http_parser/test/media_type_test.dart b/pkgs/http_parser/test/media_type_test.dart index a310f81d33..ceca7b974a 100644 --- a/pkgs/http_parser/test/media_type_test.dart +++ b/pkgs/http_parser/test/media_type_test.dart @@ -8,7 +8,7 @@ import 'package:test/test.dart'; void main() { group('parse', () { test('parses a simple MIME type', () { - var type = MediaType.parse('text/plain'); + final type = MediaType.parse('text/plain'); expect(type.type, equals('text')); expect(type.subtype, equals('plain')); }); @@ -34,13 +34,13 @@ void main() { }); test('parses parameters', () { - var type = MediaType.parse('text/plain;foo=bar;baz=bang'); + final type = MediaType.parse('text/plain;foo=bar;baz=bang'); expect(type.mimeType, equals('text/plain')); expect(type.parameters, equals({'foo': 'bar', 'baz': 'bang'})); }); test('allows whitespace around the semicolon', () { - var type = MediaType.parse('text/plain ; foo=bar ; baz=bang'); + final type = MediaType.parse('text/plain ; foo=bar ; baz=bang'); expect(type.mimeType, equals('text/plain')); expect(type.parameters, equals({'foo': 'bar', 'baz': 'bang'})); }); @@ -60,7 +60,7 @@ void main() { }); test('parses quoted parameters', () { - var type = + final type = MediaType.parse('text/plain; foo="bar space"; baz="bang\\\\escape"'); expect(type.mimeType, equals('text/plain')); expect( @@ -68,14 +68,14 @@ void main() { }); test('lower-cases type and subtype', () { - var type = MediaType.parse('TeXt/pLaIn'); + final type = MediaType.parse('TeXt/pLaIn'); expect(type.type, equals('text')); expect(type.subtype, equals('plain')); expect(type.mimeType, equals('text/plain')); }); test('records parameters as case-insensitive', () { - var type = MediaType.parse('test/plain;FoO=bar;bAz=bang'); + final type = MediaType.parse('test/plain;FoO=bar;bAz=bang'); expect(type.parameters, equals({'FoO': 'bar', 'bAz': 'bang'})); expect(type.parameters, containsPair('foo', 'bar')); expect(type.parameters, containsPair('baz', 'bang')); @@ -89,7 +89,7 @@ void main() { }); test('uses the existing fields by default', () { - var newType = type.change(); + final newType = type.change(); expect(newType.type, equals('text')); expect(newType.subtype, equals('plain')); expect(newType.parameters, equals({'foo': 'bar', 'baz': 'bang'})); @@ -104,7 +104,7 @@ void main() { }); test('[mimeType] overrides the existing type and subtype', () { - var newType = type.change(mimeType: 'image/png'); + final newType = type.change(mimeType: 'image/png'); expect(newType.type, equals('image')); expect(newType.subtype, equals('png')); }); @@ -120,7 +120,7 @@ void main() { }); test('[clearParameters] with [parameters] removes before adding', () { - var newType = + final newType = type.change(parameters: {'foo': 'zap'}, clearParameters: true); expect(newType.parameters, equals({'foo': 'zap'})); }); From 1ed3c12cacc8a924f15735bc93927aec37431034 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 23 Mar 2020 14:52:40 -0700 Subject: [PATCH 063/126] Enable and fix some more lints --- pkgs/http_parser/analysis_options.yaml | 20 ++++ .../lib/src/authentication_challenge.dart | 112 +++++++++--------- .../lib/src/chunked_coding/decoder.dart | 4 +- pkgs/http_parser/lib/src/http_date.dart | 87 +++++++------- pkgs/http_parser/lib/src/media_type.dart | 61 +++++----- 5 files changed, 150 insertions(+), 134 deletions(-) diff --git a/pkgs/http_parser/analysis_options.yaml b/pkgs/http_parser/analysis_options.yaml index c2977b5d52..69009d35df 100644 --- a/pkgs/http_parser/analysis_options.yaml +++ b/pkgs/http_parser/analysis_options.yaml @@ -7,8 +7,11 @@ analyzer: linter: rules: - avoid_bool_literals_in_conditional_expressions + - avoid_catching_errors - avoid_classes_with_only_static_members - avoid_function_literals_in_foreach_calls + - avoid_private_typedef_functions + - avoid_redundant_argument_values - avoid_renaming_method_parameters - avoid_returning_null - avoid_returning_null_for_future @@ -16,6 +19,7 @@ linter: - avoid_returning_this - avoid_single_cascade_in_expression_statements - avoid_unused_constructor_parameters + - avoid_void_async - await_only_futures - camel_case_types - cancel_subscriptions @@ -30,21 +34,34 @@ linter: - invariant_booleans - iterable_contains_unrelated_type - join_return_with_assignment + - lines_longer_than_80_chars - list_remove_unrelated_type - literal_only_boolean_expressions + - missing_whitespace_between_adjacent_strings - no_adjacent_strings_in_list + - no_runtimeType_toString - non_constant_identifier_names - only_throw_errors - overridden_fields - package_api_docs - package_names - package_prefixed_library_names + - prefer_asserts_in_initializer_lists - prefer_const_constructors + - prefer_const_declarations + - prefer_expression_function_bodies - prefer_final_locals + - prefer_function_declarations_over_variables - prefer_initializing_formals + - prefer_inlined_adds - prefer_interpolation_to_compose_strings + - prefer_is_not_operator - prefer_null_aware_operators + - prefer_relative_imports - prefer_typing_uninitialized_variables + - prefer_void_to_null + - provide_deprecation_message + - sort_pub_dependencies - test_types_in_equals - throw_in_finally - unnecessary_await_in_return @@ -52,6 +69,9 @@ linter: - unnecessary_getters_setters - unnecessary_lambdas - unnecessary_null_aware_assignments + - unnecessary_overrides - unnecessary_parenthesis - unnecessary_statements + - unnecessary_string_interpolations + - use_string_buffers - void_checks diff --git a/pkgs/http_parser/lib/src/authentication_challenge.dart b/pkgs/http_parser/lib/src/authentication_challenge.dart index e4b6619b46..9b554e8b32 100644 --- a/pkgs/http_parser/lib/src/authentication_challenge.dart +++ b/pkgs/http_parser/lib/src/authentication_challenge.dart @@ -33,79 +33,77 @@ class AuthenticationChallenge { /// challenges. /// /// Throws a [FormatException] if the header is invalid. - static List parseHeader(String header) { - return wrapFormatException('authentication header', header, () { - final scanner = StringScanner(header); - scanner.scan(whitespace); - final challenges = parseList(scanner, () { - final scheme = _scanScheme(scanner, whitespaceName: '" " or "="'); - - // Manually parse the inner list. We need to do some lookahead to - // disambiguate between an auth param and another challenge. - final params = {}; + static List parseHeader(String header) => + wrapFormatException('authentication header', header, () { + final scanner = StringScanner(header); + scanner.scan(whitespace); + final challenges = parseList(scanner, () { + final scheme = _scanScheme(scanner, whitespaceName: '" " or "="'); + + // Manually parse the inner list. We need to do some lookahead to + // disambiguate between an auth param and another challenge. + final params = {}; + + // Consume initial empty values. + while (scanner.scan(',')) { + scanner.scan(whitespace); + } - // Consume initial empty values. - while (scanner.scan(',')) { - scanner.scan(whitespace); - } + _scanAuthParam(scanner, params); - _scanAuthParam(scanner, params); + var beforeComma = scanner.position; + while (scanner.scan(',')) { + scanner.scan(whitespace); - var beforeComma = scanner.position; - while (scanner.scan(',')) { - scanner.scan(whitespace); + // Empty elements are allowed, but excluded from the results. + if (scanner.matches(',') || scanner.isDone) continue; - // Empty elements are allowed, but excluded from the results. - if (scanner.matches(',') || scanner.isDone) continue; + scanner.expect(token, name: 'a token'); + final name = scanner.lastMatch[0]; + scanner.scan(whitespace); - scanner.expect(token, name: 'a token'); - final name = scanner.lastMatch[0]; - scanner.scan(whitespace); + // If there's no "=", then this is another challenge rather than a + // parameter for the current challenge. + if (!scanner.scan('=')) { + scanner.position = beforeComma; + break; + } - // If there's no "=", then this is another challenge rather than a - // parameter for the current challenge. - if (!scanner.scan('=')) { - scanner.position = beforeComma; - break; - } + scanner.scan(whitespace); - scanner.scan(whitespace); + if (scanner.scan(token)) { + params[name] = scanner.lastMatch[0]; + } else { + params[name] = expectQuotedString(scanner, + name: 'a token or a quoted string'); + } - if (scanner.scan(token)) { - params[name] = scanner.lastMatch[0]; - } else { - params[name] = - expectQuotedString(scanner, name: 'a token or a quoted string'); + scanner.scan(whitespace); + beforeComma = scanner.position; } - scanner.scan(whitespace); - beforeComma = scanner.position; - } + return AuthenticationChallenge(scheme, params); + }); - return AuthenticationChallenge(scheme, params); + scanner.expectDone(); + return challenges; }); - scanner.expectDone(); - return challenges; - }); - } - /// Parses a single WWW-Authenticate challenge value. /// /// Throws a [FormatException] if the challenge is invalid. - factory AuthenticationChallenge.parse(String challenge) { - return wrapFormatException('authentication challenge', challenge, () { - final scanner = StringScanner(challenge); - scanner.scan(whitespace); - final scheme = _scanScheme(scanner); - - final params = {}; - parseList(scanner, () => _scanAuthParam(scanner, params)); - - scanner.expectDone(); - return AuthenticationChallenge(scheme, params); - }); - } + factory AuthenticationChallenge.parse(String challenge) => + wrapFormatException('authentication challenge', challenge, () { + final scanner = StringScanner(challenge); + scanner.scan(whitespace); + final scheme = _scanScheme(scanner); + + final params = {}; + parseList(scanner, () => _scanAuthParam(scanner, params)); + + scanner.expectDone(); + return AuthenticationChallenge(scheme, params); + }); /// Scans a single scheme name and asserts that it's followed by a space. /// diff --git a/pkgs/http_parser/lib/src/chunked_coding/decoder.dart b/pkgs/http_parser/lib/src/chunked_coding/decoder.dart index cd06f397f4..dcc0188291 100644 --- a/pkgs/http_parser/lib/src/chunked_coding/decoder.dart +++ b/pkgs/http_parser/lib/src/chunked_coding/decoder.dart @@ -163,8 +163,8 @@ class _Sink extends ByteConversionSinkBase { } else { // If the byte is an uppercase letter, convert it to lowercase. This works // because uppercase letters in ASCII are exactly `0b100000 = 0x20` less - // than lowercase letters, so if we ensure that that bit is 1 we ensure that - // the letter is lowercase. + // than lowercase letters, so if we ensure that that bit is 1 we ensure + // that the letter is lowercase. final letter = 0x20 | byte; if ($a <= letter && letter <= $f) return letter - $a + 10; } diff --git a/pkgs/http_parser/lib/src/http_date.dart b/pkgs/http_parser/lib/src/http_date.dart index 88ada63ae7..71f6babd2d 100644 --- a/pkgs/http_parser/lib/src/http_date.dart +++ b/pkgs/http_parser/lib/src/http_date.dart @@ -57,58 +57,57 @@ String formatHttpDate(DateTime date) { /// /// This follows [RFC 2616](http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3). /// It will throw a [FormatException] if [date] is invalid. -DateTime parseHttpDate(String date) { - return wrapFormatException('HTTP date', date, () { - final scanner = StringScanner(date); - - if (scanner.scan(_longWeekdayRegExp)) { - // RFC 850 starts with a long weekday. - scanner.expect(', '); - final day = _parseInt(scanner, 2); - scanner.expect('-'); - final month = _parseMonth(scanner); - scanner.expect('-'); - final year = 1900 + _parseInt(scanner, 2); - scanner.expect(' '); - final time = _parseTime(scanner); - scanner.expect(' GMT'); - scanner.expectDone(); - - return _makeDateTime(year, month, day, time); - } - - // RFC 1123 and asctime both start with a short weekday. - scanner.expect(_shortWeekdayRegExp); - if (scanner.scan(', ')) { - // RFC 1123 follows the weekday with a comma. - final day = _parseInt(scanner, 2); +DateTime parseHttpDate(String date) => + wrapFormatException('HTTP date', date, () { + final scanner = StringScanner(date); + + if (scanner.scan(_longWeekdayRegExp)) { + // RFC 850 starts with a long weekday. + scanner.expect(', '); + final day = _parseInt(scanner, 2); + scanner.expect('-'); + final month = _parseMonth(scanner); + scanner.expect('-'); + final year = 1900 + _parseInt(scanner, 2); + scanner.expect(' '); + final time = _parseTime(scanner); + scanner.expect(' GMT'); + scanner.expectDone(); + + return _makeDateTime(year, month, day, time); + } + + // RFC 1123 and asctime both start with a short weekday. + scanner.expect(_shortWeekdayRegExp); + if (scanner.scan(', ')) { + // RFC 1123 follows the weekday with a comma. + final day = _parseInt(scanner, 2); + scanner.expect(' '); + final month = _parseMonth(scanner); + scanner.expect(' '); + final year = _parseInt(scanner, 4); + scanner.expect(' '); + final time = _parseTime(scanner); + scanner.expect(' GMT'); + scanner.expectDone(); + + return _makeDateTime(year, month, day, time); + } + + // asctime follows the weekday with a space. scanner.expect(' '); final month = _parseMonth(scanner); scanner.expect(' '); - final year = _parseInt(scanner, 4); + final day = + scanner.scan(' ') ? _parseInt(scanner, 1) : _parseInt(scanner, 2); scanner.expect(' '); final time = _parseTime(scanner); - scanner.expect(' GMT'); + scanner.expect(' '); + final year = _parseInt(scanner, 4); scanner.expectDone(); return _makeDateTime(year, month, day, time); - } - - // asctime follows the weekday with a space. - scanner.expect(' '); - final month = _parseMonth(scanner); - scanner.expect(' '); - final day = - scanner.scan(' ') ? _parseInt(scanner, 1) : _parseInt(scanner, 2); - scanner.expect(' '); - final time = _parseTime(scanner); - scanner.expect(' '); - final year = _parseInt(scanner, 4); - scanner.expectDone(); - - return _makeDateTime(year, month, day, time); - }); -} + }); /// Parses a short-form month name to a form accepted by [DateTime]. int _parseMonth(StringScanner scanner) { diff --git a/pkgs/http_parser/lib/src/media_type.dart b/pkgs/http_parser/lib/src/media_type.dart index 8d0b082890..71b16b2f40 100644 --- a/pkgs/http_parser/lib/src/media_type.dart +++ b/pkgs/http_parser/lib/src/media_type.dart @@ -40,41 +40,40 @@ class MediaType { /// Parses a media type. /// /// This will throw a FormatError if the media type is invalid. - factory MediaType.parse(String mediaType) { - // This parsing is based on sections 3.6 and 3.7 of the HTTP spec: - // http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html. - return wrapFormatException('media type', mediaType, () { - final scanner = StringScanner(mediaType); - scanner.scan(whitespace); - scanner.expect(token); - final type = scanner.lastMatch[0]; - scanner.expect('/'); - scanner.expect(token); - final subtype = scanner.lastMatch[0]; - scanner.scan(whitespace); - - final parameters = {}; - while (scanner.scan(';')) { + factory MediaType.parse(String mediaType) => + // This parsing is based on sections 3.6 and 3.7 of the HTTP spec: + // http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html. + wrapFormatException('media type', mediaType, () { + final scanner = StringScanner(mediaType); scanner.scan(whitespace); scanner.expect(token); - final attribute = scanner.lastMatch[0]; - scanner.expect('='); - - String value; - if (scanner.scan(token)) { - value = scanner.lastMatch[0]; - } else { - value = expectQuotedString(scanner); - } - + final type = scanner.lastMatch[0]; + scanner.expect('/'); + scanner.expect(token); + final subtype = scanner.lastMatch[0]; scanner.scan(whitespace); - parameters[attribute] = value; - } - scanner.expectDone(); - return MediaType(type, subtype, parameters); - }); - } + final parameters = {}; + while (scanner.scan(';')) { + scanner.scan(whitespace); + scanner.expect(token); + final attribute = scanner.lastMatch[0]; + scanner.expect('='); + + String value; + if (scanner.scan(token)) { + value = scanner.lastMatch[0]; + } else { + value = expectQuotedString(scanner); + } + + scanner.scan(whitespace); + parameters[attribute] = value; + } + + scanner.expectDone(); + return MediaType(type, subtype, parameters); + }); MediaType(String type, String subtype, [Map parameters]) : type = type.toLowerCase(), From 89061764da663082241df707db6eb4388bc90641 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 24 Mar 2020 10:22:47 -0700 Subject: [PATCH 064/126] Readme: Add badges, update links --- pkgs/http_parser/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkgs/http_parser/README.md b/pkgs/http_parser/README.md index 1bd5a90a0c..18a3256567 100644 --- a/pkgs/http_parser/README.md +++ b/pkgs/http_parser/README.md @@ -1,3 +1,6 @@ +[![Pub Package](https://img.shields.io/pub/v/http_parser.svg)](https://pub.dartlang.org/packages/http_parser) +[![Build Status](https://travis-ci.org/dart-lang/http_parser.svg?branch=master)](https://travis-ci.org/dart-lang/http_parser) + `http_parser` is a platform-independent package for parsing and serializing various HTTP-related formats. It's designed to be usable on both the browser and the server, and thus avoids referencing any types from `dart:io` or `dart:html`. @@ -14,5 +17,5 @@ It includes: the client and server sides of the [WebSocket protocol][6455] independently of any specific server implementation. -[2616]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html +[2616]: https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html [6455]: https://tools.ietf.org/html/rfc6455 From 0a21bdb7a47cbb7a017526fc0219f68618905ec7 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 24 Mar 2020 10:31:53 -0700 Subject: [PATCH 065/126] Add an example --- pkgs/http_parser/example/example.dart | 16 ++++++++++++++++ pkgs/http_parser/test/example_test.dart | 19 +++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 pkgs/http_parser/example/example.dart create mode 100644 pkgs/http_parser/test/example_test.dart diff --git a/pkgs/http_parser/example/example.dart b/pkgs/http_parser/example/example.dart new file mode 100644 index 0000000000..9230b6a542 --- /dev/null +++ b/pkgs/http_parser/example/example.dart @@ -0,0 +1,16 @@ +// Copyright (c) 2020, 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:http_parser/http_parser.dart'; + +void main() { + final date = DateTime.utc(2014, 9, 9, 9, 9, 9); + print(date); // 2014-09-09 09:09:09.000Z + + final httpDateFormatted = formatHttpDate(date); + print(httpDateFormatted); // Tue, 09 Sep 2014 09:09:09 GMT + + final nowParsed = parseHttpDate(httpDateFormatted); + print(nowParsed); // 2014-09-09 09:09:09.000Z +} diff --git a/pkgs/http_parser/test/example_test.dart b/pkgs/http_parser/test/example_test.dart new file mode 100644 index 0000000000..ef61060907 --- /dev/null +++ b/pkgs/http_parser/test/example_test.dart @@ -0,0 +1,19 @@ +import 'dart:io'; + +import 'package:test/test.dart'; + +void main() { + test('validate example', () { + final result = Process.runSync( + Platform.executable, + ['example/example.dart'], + ); + + expect(result.exitCode, 0); + expect(result.stdout, ''' +2014-09-09 09:09:09.000Z +Tue, 09 Sep 2014 09:09:09 GMT +2014-09-09 09:09:09.000Z +'''); + }, testOn: 'vm'); +} From ebb6b6aad0988cd9fe69d569b20e2fd3e25cc725 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 24 Mar 2020 10:33:59 -0700 Subject: [PATCH 066/126] Prepare to release 3.1.4 --- pkgs/http_parser/CHANGELOG.md | 5 +++++ pkgs/http_parser/pubspec.yaml | 3 +-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index aaf7aa4583..6b34efdd15 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,3 +1,8 @@ +## 3.1.4 + +* Fixed lints affecting package health score. +* Added an example. + ## 3.1.3 * Set max SDK version to `<3.0.0`, and adjust other dependencies. diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index 1444f72fa2..b3df0c4f69 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,6 +1,5 @@ name: http_parser -version: 3.1.4-dev - +version: 3.1.4 description: >- A platform-independent package for parsing and serializing HTTP formats. homepage: https://github.com/dart-lang/http_parser From 081fb350659d31ed4c253cd08b7dc07eca1448aa Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 2 Jun 2020 09:10:40 -0700 Subject: [PATCH 067/126] fix lint that is now better enforced --- pkgs/http_parser/test/http_date_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/http_parser/test/http_date_test.dart b/pkgs/http_parser/test/http_date_test.dart index dfcdb4f3dc..d117663f50 100644 --- a/pkgs/http_parser/test/http_date_test.dart +++ b/pkgs/http_parser/test/http_date_test.dart @@ -28,7 +28,7 @@ void main() { }); test('start of year', () { - final date = DateTime.utc(2000, 1, 1, 0, 0, 0); + final date = DateTime.utc(2000); final formatted = formatHttpDate(date); expect(formatted, 'Sat, 01 Jan 2000 00:00:00 GMT'); From 38fe0724ecb7d99add302d41fec5fc6b4a4047f0 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 7 Jul 2020 13:53:13 -0700 Subject: [PATCH 068/126] update min SDK to 2.7 (dart-lang/http_parser#32) --- pkgs/http_parser/.travis.yml | 2 +- pkgs/http_parser/CHANGELOG.md | 4 ++++ pkgs/http_parser/pubspec.yaml | 4 ++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/pkgs/http_parser/.travis.yml b/pkgs/http_parser/.travis.yml index 51308e50b9..59d861dd28 100644 --- a/pkgs/http_parser/.travis.yml +++ b/pkgs/http_parser/.travis.yml @@ -1,7 +1,7 @@ language: dart dart: - - 2.3.0 + - 2.7.0 - dev dart_task: diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index 6b34efdd15..b9ad843406 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.1.5-dev + +* Require minimum Dart SDK of `2.7.0`. + ## 3.1.4 * Fixed lints affecting package health score. diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index b3df0c4f69..eccff2c597 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,11 +1,11 @@ name: http_parser -version: 3.1.4 +version: 3.1.5-dev description: >- A platform-independent package for parsing and serializing HTTP formats. homepage: https://github.com/dart-lang/http_parser environment: - sdk: '>=2.3.0 <3.0.0' + sdk: '>=2.7.0 <3.0.0' dependencies: charcode: ^1.1.0 From 819f582adaa63efe570049ab33bf330f00fe49f4 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 28 Jul 2020 20:33:50 -0700 Subject: [PATCH 069/126] Delete .test_config No longer used --- pkgs/http_parser/.test_config | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 pkgs/http_parser/.test_config diff --git a/pkgs/http_parser/.test_config b/pkgs/http_parser/.test_config deleted file mode 100644 index 412fc5c5cf..0000000000 --- a/pkgs/http_parser/.test_config +++ /dev/null @@ -1,3 +0,0 @@ -{ - "test_package": true -} \ No newline at end of file From a0b01c16dfd419f5ce7f47419132134693d21481 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Thu, 17 Sep 2020 09:34:53 -0700 Subject: [PATCH 070/126] Enable null safety (dart-lang/http_parser#33) --- pkgs/http_parser/.travis.yml | 34 ++++++++++++------- pkgs/http_parser/CHANGELOG.md | 11 ++++-- pkgs/http_parser/analysis_options.yaml | 2 ++ .../lib/src/authentication_challenge.dart | 14 ++++---- .../lib/src/case_insensitive_map.dart | 6 ++-- .../lib/src/chunked_coding/decoder.dart | 12 ++++--- pkgs/http_parser/lib/src/http_date.dart | 6 ++-- pkgs/http_parser/lib/src/media_type.dart | 18 +++++----- pkgs/http_parser/lib/src/scan.dart | 6 ++-- pkgs/http_parser/pubspec.yaml | 18 +++++----- .../http_parser/test/chunked_coding_test.dart | 8 ++--- pkgs/http_parser/test/example_test.dart | 5 ++- pkgs/http_parser/test/media_type_test.dart | 2 +- 13 files changed, 82 insertions(+), 60 deletions(-) diff --git a/pkgs/http_parser/.travis.yml b/pkgs/http_parser/.travis.yml index 59d861dd28..cc94606934 100644 --- a/pkgs/http_parser/.travis.yml +++ b/pkgs/http_parser/.travis.yml @@ -1,24 +1,34 @@ language: dart dart: - - 2.7.0 - dev -dart_task: - - test: --platform vm - xvfb: false - - test: --platform firefox - - dartanalyzer: --fatal-infos --fatal-warnings . - -matrix: +jobs: include: - # Only validate formatting using the dev release - - dart: dev - dart_task: dartfmt + - stage: analyze_and_format + name: "Analyze" + os: linux + script: dartanalyzer --enable-experiment=non-nullable --fatal-warnings --fatal-infos . + - stage: analyze_and_format + name: "Format" + os: linux + script: dartfmt -n --set-exit-if-changed . + - stage: test + name: "Vm Tests" + os: linux + script: pub run --enable-experiment=non-nullable test -p vm + - stage: test + name: "Web Tests" + os: linux + script: pub run --enable-experiment=non-nullable test -p chrome + +stages: + - analyze_and_format + - test # Only building master means that we don't run two builds for each pull request. branches: - only: [master] + only: [master, null_safety] cache: directories: diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index b9ad843406..1bd1ae5121 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,6 +1,13 @@ -## 3.1.5-dev +## 3.2.0-nullsafety -* Require minimum Dart SDK of `2.7.0`. +Pre-release for the null safety migration of this package. + +Note that 3.2.0 may not be the final stable null safety release version, we +reserve the right to release it as a 4.0.0 breaking change. + +This release will be pinned to only allow pre-release sdk versions starting from +2.10.0-2.0.dev, which is the first version where this package will appear in the +null safety allow list. ## 3.1.4 diff --git a/pkgs/http_parser/analysis_options.yaml b/pkgs/http_parser/analysis_options.yaml index 69009d35df..f98644b025 100644 --- a/pkgs/http_parser/analysis_options.yaml +++ b/pkgs/http_parser/analysis_options.yaml @@ -3,6 +3,8 @@ include: package:pedantic/analysis_options.yaml analyzer: strong-mode: implicit-casts: false + enable-experiment: + - non-nullable linter: rules: diff --git a/pkgs/http_parser/lib/src/authentication_challenge.dart b/pkgs/http_parser/lib/src/authentication_challenge.dart index 9b554e8b32..356e0a25da 100644 --- a/pkgs/http_parser/lib/src/authentication_challenge.dart +++ b/pkgs/http_parser/lib/src/authentication_challenge.dart @@ -59,7 +59,7 @@ class AuthenticationChallenge { if (scanner.matches(',') || scanner.isDone) continue; scanner.expect(token, name: 'a token'); - final name = scanner.lastMatch[0]; + final name = scanner.lastMatch![0]!; scanner.scan(whitespace); // If there's no "=", then this is another challenge rather than a @@ -72,7 +72,7 @@ class AuthenticationChallenge { scanner.scan(whitespace); if (scanner.scan(token)) { - params[name] = scanner.lastMatch[0]; + params[name] = scanner.lastMatch![0]!; } else { params[name] = expectQuotedString(scanner, name: 'a token or a quoted string'); @@ -109,15 +109,15 @@ class AuthenticationChallenge { /// /// If [whitespaceName] is passed, it's used as the name for exceptions thrown /// due to invalid trailing whitespace. - static String _scanScheme(StringScanner scanner, {String whitespaceName}) { + static String _scanScheme(StringScanner scanner, {String? whitespaceName}) { scanner.expect(token, name: 'a token'); - final scheme = scanner.lastMatch[0].toLowerCase(); + final scheme = scanner.lastMatch![0]!.toLowerCase(); scanner.scan(whitespace); // The spec specifically requires a space between the scheme and its // params. - if (scanner.lastMatch == null || !scanner.lastMatch[0].contains(' ')) { + if (scanner.lastMatch == null || !scanner.lastMatch![0]!.contains(' ')) { scanner.expect(' ', name: whitespaceName); } @@ -127,13 +127,13 @@ class AuthenticationChallenge { /// Scans a single authentication parameter and stores its result in [params]. static void _scanAuthParam(StringScanner scanner, Map params) { scanner.expect(token, name: 'a token'); - final name = scanner.lastMatch[0]; + final name = scanner.lastMatch![0]; scanner.scan(whitespace); scanner.expect('='); scanner.scan(whitespace); if (scanner.scan(token)) { - params[name] = scanner.lastMatch[0]; + params[name] = scanner.lastMatch![0]; } else { params[name] = expectQuotedString(scanner, name: 'a token or a quoted string'); diff --git a/pkgs/http_parser/lib/src/case_insensitive_map.dart b/pkgs/http_parser/lib/src/case_insensitive_map.dart index 870f84c042..88a190e8a0 100644 --- a/pkgs/http_parser/lib/src/case_insensitive_map.dart +++ b/pkgs/http_parser/lib/src/case_insensitive_map.dart @@ -8,10 +8,8 @@ import 'package:collection/collection.dart'; /// /// Much of HTTP is case-insensitive, so this is useful to have pre-defined. class CaseInsensitiveMap extends CanonicalizedMap { - CaseInsensitiveMap() - : super((key) => key.toLowerCase(), isValidKey: (key) => key != null); + CaseInsensitiveMap() : super((key) => key.toLowerCase()); CaseInsensitiveMap.from(Map other) - : super.from(other, (key) => key.toLowerCase(), - isValidKey: (key) => key != null); + : super.from(other, (key) => key.toLowerCase()); } diff --git a/pkgs/http_parser/lib/src/chunked_coding/decoder.dart b/pkgs/http_parser/lib/src/chunked_coding/decoder.dart index dcc0188291..a916870484 100644 --- a/pkgs/http_parser/lib/src/chunked_coding/decoder.dart +++ b/pkgs/http_parser/lib/src/chunked_coding/decoder.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 'dart:async'; import 'dart:convert'; import 'dart:math' as math; import 'dart:typed_data'; @@ -18,7 +19,7 @@ class ChunkedCodingDecoder extends Converter, List> { @override List convert(List input) { - final sink = _Sink(null); + final sink = _Sink(StreamController()); final output = sink._decode(input, 0, input.length); if (sink._state == _State.end) return output; @@ -38,9 +39,10 @@ class _Sink extends ByteConversionSinkBase { /// The current state of the sink's parsing. var _state = _State.boundary; - /// The size of the chunk being parsed, or `null` if the size hasn't been - /// parsed yet. - int _size; + /// The size of the chunk being parsed. + /// + /// Only assigned and used within [_decode]. + late int _size; _Sink(this._sink); @@ -60,7 +62,7 @@ class _Sink extends ByteConversionSinkBase { /// Like [close], but includes [chunk] and [index] in the [FormatException] if /// one is thrown. - void _close([List chunk, int index]) { + void _close([List? chunk, int? index]) { if (_state != _State.end) { throw FormatException('Input ended unexpectedly.', chunk, index); } diff --git a/pkgs/http_parser/lib/src/http_date.dart b/pkgs/http_parser/lib/src/http_date.dart index 71f6babd2d..0cedd9a6d8 100644 --- a/pkgs/http_parser/lib/src/http_date.dart +++ b/pkgs/http_parser/lib/src/http_date.dart @@ -113,17 +113,17 @@ DateTime parseHttpDate(String date) => int _parseMonth(StringScanner scanner) { scanner.expect(_monthRegExp); // DateTime uses 1-indexed months. - return _months.indexOf(scanner.lastMatch[0]) + 1; + return _months.indexOf(scanner.lastMatch![0]!) + 1; } /// Parses an int an enforces that it has exactly [digits] digits. int _parseInt(StringScanner scanner, int digits) { scanner.expect(_digitRegExp); - if (scanner.lastMatch[0].length != digits) { + if (scanner.lastMatch![0]!.length != digits) { scanner.error('expected a $digits-digit number.'); } - return int.parse(scanner.lastMatch[0]); + return int.parse(scanner.lastMatch![0]!); } /// Parses an timestamp of the form "HH:MM:SS" on a 24-hour clock. diff --git a/pkgs/http_parser/lib/src/media_type.dart b/pkgs/http_parser/lib/src/media_type.dart index 71b16b2f40..59e692887b 100644 --- a/pkgs/http_parser/lib/src/media_type.dart +++ b/pkgs/http_parser/lib/src/media_type.dart @@ -47,22 +47,22 @@ class MediaType { final scanner = StringScanner(mediaType); scanner.scan(whitespace); scanner.expect(token); - final type = scanner.lastMatch[0]; + final type = scanner.lastMatch![0]!; scanner.expect('/'); scanner.expect(token); - final subtype = scanner.lastMatch[0]; + final subtype = scanner.lastMatch![0]!; scanner.scan(whitespace); final parameters = {}; while (scanner.scan(';')) { scanner.scan(whitespace); scanner.expect(token); - final attribute = scanner.lastMatch[0]; + final attribute = scanner.lastMatch![0]!; scanner.expect('='); String value; if (scanner.scan(token)) { - value = scanner.lastMatch[0]; + value = scanner.lastMatch![0]!; } else { value = expectQuotedString(scanner); } @@ -75,7 +75,7 @@ class MediaType { return MediaType(type, subtype, parameters); }); - MediaType(String type, String subtype, [Map parameters]) + MediaType(String type, String subtype, [Map? parameters]) : type = type.toLowerCase(), subtype = subtype.toLowerCase(), parameters = UnmodifiableMapView( @@ -91,10 +91,10 @@ class MediaType { /// [clearParameters] is passed, it replaces the corresponding field entirely /// instead. MediaType change( - {String type, - String subtype, - String mimeType, - Map parameters, + {String? type, + String? subtype, + String? mimeType, + Map? parameters, bool clearParameters = false}) { if (mimeType != null) { if (type != null) { diff --git a/pkgs/http_parser/lib/src/scan.dart b/pkgs/http_parser/lib/src/scan.dart index 1bdf87787b..d7de54d8d8 100644 --- a/pkgs/http_parser/lib/src/scan.dart +++ b/pkgs/http_parser/lib/src/scan.dart @@ -58,11 +58,11 @@ List parseList(StringScanner scanner, T Function() parseElement) { /// /// If [name] is passed, it's used to describe the expected value if it's not /// found. -String expectQuotedString(StringScanner scanner, {String name}) { +String expectQuotedString(StringScanner scanner, {String? name}) { name ??= 'quoted string'; scanner.expect(_quotedString, name: name); - final string = scanner.lastMatch[0]; + final string = scanner.lastMatch![0]!; return string .substring(1, string.length - 1) - .replaceAllMapped(_quotedPair, (match) => match[1]); + .replaceAllMapped(_quotedPair, (match) => match[1]!); } diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index eccff2c597..d292ea184b 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,19 +1,19 @@ name: http_parser -version: 3.1.5-dev +version: 3.2.0-nullsafety description: >- A platform-independent package for parsing and serializing HTTP formats. homepage: https://github.com/dart-lang/http_parser environment: - sdk: '>=2.7.0 <3.0.0' + sdk: '>=2.10.0-0 <2.10.0' dependencies: - charcode: ^1.1.0 - collection: '>=0.9.1 <2.0.0' - source_span: ^1.0.0 - string_scanner: '>=0.0.0 <2.0.0' - typed_data: ^1.1.0 + charcode: ^1.2.0-nullsafety + collection: ^1.15.0-nullsafety.2 + source_span: ^1.8.0-nullsafety + string_scanner: ^1.1.0-nullsafety + typed_data: ^1.3.0-nullsafety.2 dev_dependencies: - pedantic: ^1.0.0 - test: ^1.0.0 + pedantic: ^1.10.0-nullsafety + test: ^1.16.0-nullsafety.4 diff --git a/pkgs/http_parser/test/chunked_coding_test.dart b/pkgs/http_parser/test/chunked_coding_test.dart index 0b178ec68c..045557ab8e 100644 --- a/pkgs/http_parser/test/chunked_coding_test.dart +++ b/pkgs/http_parser/test/chunked_coding_test.dart @@ -30,8 +30,8 @@ void main() { }); group('with chunked conversion', () { - List> results; - ByteConversionSink sink; + late List> results; + late ByteConversionSink sink; setUp(() { results = []; final controller = StreamController>(sync: true); @@ -255,8 +255,8 @@ void main() { }); group('with chunked conversion', () { - List> results; - ByteConversionSink sink; + late List> results; + late ByteConversionSink sink; setUp(() { results = []; final controller = StreamController>(sync: true); diff --git a/pkgs/http_parser/test/example_test.dart b/pkgs/http_parser/test/example_test.dart index ef61060907..bbf869f1cb 100644 --- a/pkgs/http_parser/test/example_test.dart +++ b/pkgs/http_parser/test/example_test.dart @@ -6,7 +6,10 @@ void main() { test('validate example', () { final result = Process.runSync( Platform.executable, - ['example/example.dart'], + [ + '--enable-experiment=non-nullable', + 'example/example.dart', + ], ); expect(result.exitCode, 0); diff --git a/pkgs/http_parser/test/media_type_test.dart b/pkgs/http_parser/test/media_type_test.dart index ceca7b974a..5ee9313305 100644 --- a/pkgs/http_parser/test/media_type_test.dart +++ b/pkgs/http_parser/test/media_type_test.dart @@ -83,7 +83,7 @@ void main() { }); group('change', () { - MediaType type; + late MediaType type; setUp(() { type = MediaType.parse('text/plain; foo=bar; baz=bang'); }); From 4e28f6960dcf6e60aebabd1350294a31e452f953 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Thu, 17 Sep 2020 09:59:12 -0700 Subject: [PATCH 071/126] Remove `?` from positional arg in implementation function --- pkgs/http_parser/lib/src/scan.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkgs/http_parser/lib/src/scan.dart b/pkgs/http_parser/lib/src/scan.dart index d7de54d8d8..96fb8ae3f5 100644 --- a/pkgs/http_parser/lib/src/scan.dart +++ b/pkgs/http_parser/lib/src/scan.dart @@ -58,8 +58,10 @@ List parseList(StringScanner scanner, T Function() parseElement) { /// /// If [name] is passed, it's used to describe the expected value if it's not /// found. -String expectQuotedString(StringScanner scanner, {String? name}) { - name ??= 'quoted string'; +String expectQuotedString( + StringScanner scanner, { + String name = 'quoted string', +}) { scanner.expect(_quotedString, name: name); final string = scanner.lastMatch![0]!; return string From bddcf281108a7c86e4bb22c76978b6ee53edd73c Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Thu, 17 Sep 2020 10:46:41 -0700 Subject: [PATCH 072/126] Remove null_safety branch from .travis.yml --- pkgs/http_parser/.travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/http_parser/.travis.yml b/pkgs/http_parser/.travis.yml index cc94606934..32b9f1301b 100644 --- a/pkgs/http_parser/.travis.yml +++ b/pkgs/http_parser/.travis.yml @@ -28,7 +28,7 @@ stages: # Only building master means that we don't run two builds for each pull request. branches: - only: [master, null_safety] + only: [master] cache: directories: From 367e61fe5db60138fce5894243e23b6281cd6a49 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 29 Sep 2020 10:35:35 -0700 Subject: [PATCH 073/126] Support latest Dart dev SDK (dart-lang/http_parser#35) --- pkgs/http_parser/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index d292ea184b..630578a58d 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -5,7 +5,7 @@ description: >- homepage: https://github.com/dart-lang/http_parser environment: - sdk: '>=2.10.0-0 <2.10.0' + sdk: '>=2.10.0-0 <2.11.0' dependencies: charcode: ^1.2.0-nullsafety From f3870f077e8b6cd8af7dad3272d238b4b4cb5ab0 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 1 Oct 2020 13:34:36 -0700 Subject: [PATCH 074/126] Block publish for now, use major version bump (dart-lang/http_parser#36) - Append `-dev` to the version since this is unpublished. - Add `publish_to: none` since this package is not in the allow list. - Switch to a major version bump since this package is not pinned by flutter. - Bump the min SDK to `2.11.0-0` since we won't ever publish a version compatible with `2.10` and we don't need to run against old dev SDKs on travis. --- pkgs/http_parser/CHANGELOG.md | 11 ++--------- pkgs/http_parser/pubspec.yaml | 8 ++++++-- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index 1bd1ae5121..224474220f 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,13 +1,6 @@ -## 3.2.0-nullsafety +## 4.0.0-nullsafety-dev -Pre-release for the null safety migration of this package. - -Note that 3.2.0 may not be the final stable null safety release version, we -reserve the right to release it as a 4.0.0 breaking change. - -This release will be pinned to only allow pre-release sdk versions starting from -2.10.0-2.0.dev, which is the first version where this package will appear in the -null safety allow list. +* Migrate to null safety. ## 3.1.4 diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index 630578a58d..9ee28a48d3 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,11 +1,15 @@ name: http_parser -version: 3.2.0-nullsafety +version: 3.2.0-nullsafety-dev description: >- A platform-independent package for parsing and serializing HTTP formats. homepage: https://github.com/dart-lang/http_parser +# Unsafe to publish until null safety is stable or this package is in +# the allow list +publish_to: none + environment: - sdk: '>=2.10.0-0 <2.11.0' + sdk: '>=2.11.0-0 <2.11.0' dependencies: charcode: ^1.2.0-nullsafety From 31e04ca0d88897bfbe5c2298ad820a18d79ac54d Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 3 Nov 2020 07:11:00 -0800 Subject: [PATCH 075/126] Support Dart 2.11 --- pkgs/http_parser/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index 9ee28a48d3..5db173f1e0 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -9,7 +9,7 @@ homepage: https://github.com/dart-lang/http_parser publish_to: none environment: - sdk: '>=2.11.0-0 <2.11.0' + sdk: '>=2.11.0-0 <2.12.0' dependencies: charcode: ^1.2.0-nullsafety From 74d011e3def4a67fd0ed8ce1d49ca793a7fec426 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Fri, 6 Nov 2020 13:44:40 -0800 Subject: [PATCH 076/126] Prepare to publish null-safe version (dart-lang/http_parser#38) --- pkgs/http_parser/.travis.yml | 30 ++++++------------------------ pkgs/http_parser/CHANGELOG.md | 2 +- pkgs/http_parser/pubspec.yaml | 14 ++++++++------ 3 files changed, 15 insertions(+), 31 deletions(-) diff --git a/pkgs/http_parser/.travis.yml b/pkgs/http_parser/.travis.yml index 32b9f1301b..fe711263b6 100644 --- a/pkgs/http_parser/.travis.yml +++ b/pkgs/http_parser/.travis.yml @@ -1,30 +1,12 @@ language: dart dart: - - dev +- dev -jobs: - include: - - stage: analyze_and_format - name: "Analyze" - os: linux - script: dartanalyzer --enable-experiment=non-nullable --fatal-warnings --fatal-infos . - - stage: analyze_and_format - name: "Format" - os: linux - script: dartfmt -n --set-exit-if-changed . - - stage: test - name: "Vm Tests" - os: linux - script: pub run --enable-experiment=non-nullable test -p vm - - stage: test - name: "Web Tests" - os: linux - script: pub run --enable-experiment=non-nullable test -p chrome - -stages: - - analyze_and_format - - test +dart_task: +- dartfmt +- dartanalyzer: --fatal-warnings --fatal-infos . +- test: -p chrome,vm # Only building master means that we don't run two builds for each pull request. branches: @@ -32,4 +14,4 @@ branches: cache: directories: - - $HOME/.pub-cache + - $HOME/.pub-cache diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index 224474220f..0ade9fff43 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,4 +1,4 @@ -## 4.0.0-nullsafety-dev +## 4.0.0-nullsafety * Migrate to null safety. diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index 5db173f1e0..95925406a5 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,15 +1,11 @@ name: http_parser -version: 3.2.0-nullsafety-dev +version: 4.0.0-nullsafety description: >- A platform-independent package for parsing and serializing HTTP formats. homepage: https://github.com/dart-lang/http_parser -# Unsafe to publish until null safety is stable or this package is in -# the allow list -publish_to: none - environment: - sdk: '>=2.11.0-0 <2.12.0' + sdk: '>=2.12.0-0 <3.0.0' dependencies: charcode: ^1.2.0-nullsafety @@ -21,3 +17,9 @@ dependencies: dev_dependencies: pedantic: ^1.10.0-nullsafety test: ^1.16.0-nullsafety.4 + +# Because these packages depend on the prev4 version of this package +dependency_overrides: + shelf: 0.7.9 + shelf_static: 0.2.8 + test: 1.16.0-nullsafety.9 From 402b5404d074fbd49672bdf99a0825117d0816c3 Mon Sep 17 00:00:00 2001 From: Alexander Thomas Date: Mon, 1 Feb 2021 17:56:16 +0100 Subject: [PATCH 077/126] Migrate to GitHub Actions (dart-lang/http_parser#41) * Migrate to GitHub Actions * Delete .travis.yml * Replace Travis badge --- .../.github/workflows/test-package.yml | 64 +++++++++++++++++++ pkgs/http_parser/.travis.yml | 17 ----- pkgs/http_parser/README.md | 2 +- 3 files changed, 65 insertions(+), 18 deletions(-) create mode 100644 pkgs/http_parser/.github/workflows/test-package.yml delete mode 100644 pkgs/http_parser/.travis.yml diff --git a/pkgs/http_parser/.github/workflows/test-package.yml b/pkgs/http_parser/.github/workflows/test-package.yml new file mode 100644 index 0000000000..0a2a874338 --- /dev/null +++ b/pkgs/http_parser/.github/workflows/test-package.yml @@ -0,0 +1,64 @@ +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' + - name: Run Chrome tests + run: dart test --platform chrome + if: always() && steps.install.outcome == 'success' diff --git a/pkgs/http_parser/.travis.yml b/pkgs/http_parser/.travis.yml deleted file mode 100644 index fe711263b6..0000000000 --- a/pkgs/http_parser/.travis.yml +++ /dev/null @@ -1,17 +0,0 @@ -language: dart - -dart: -- dev - -dart_task: -- dartfmt -- dartanalyzer: --fatal-warnings --fatal-infos . -- test: -p chrome,vm - -# 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/http_parser/README.md b/pkgs/http_parser/README.md index 18a3256567..763de43b7d 100644 --- a/pkgs/http_parser/README.md +++ b/pkgs/http_parser/README.md @@ -1,5 +1,5 @@ [![Pub Package](https://img.shields.io/pub/v/http_parser.svg)](https://pub.dartlang.org/packages/http_parser) -[![Build Status](https://travis-ci.org/dart-lang/http_parser.svg?branch=master)](https://travis-ci.org/dart-lang/http_parser) +[![Build Status](https://github.com/dart-lang/http_parser/workflows/Dart%20CI/badge.svg)](https://github.com/dart-lang/http_parser/actions?query=workflow%3A"Dart+CI"+branch%3Amaster) `http_parser` is a platform-independent package for parsing and serializing various HTTP-related formats. It's designed to be usable on both the browser and From 45c06f66149d70775b46ac21d38ab50a094efd9c Mon Sep 17 00:00:00 2001 From: Jacob MacDonald Date: Fri, 5 Feb 2021 11:07:48 -0800 Subject: [PATCH 078/126] stable null safety release (dart-lang/http_parser#42) --- pkgs/http_parser/CHANGELOG.md | 4 ++++ pkgs/http_parser/pubspec.yaml | 20 +++++++------------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index 0ade9fff43..3c942aa96e 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,3 +1,7 @@ +## 4.0.0 + +* Stable null safety stable release. + ## 4.0.0-nullsafety * Migrate to null safety. diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index 95925406a5..b2939aaec6 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,5 +1,5 @@ name: http_parser -version: 4.0.0-nullsafety +version: 4.0.0 description: >- A platform-independent package for parsing and serializing HTTP formats. homepage: https://github.com/dart-lang/http_parser @@ -8,18 +8,12 @@ environment: sdk: '>=2.12.0-0 <3.0.0' dependencies: - charcode: ^1.2.0-nullsafety - collection: ^1.15.0-nullsafety.2 - source_span: ^1.8.0-nullsafety - string_scanner: ^1.1.0-nullsafety - typed_data: ^1.3.0-nullsafety.2 + charcode: ^1.2.0 + collection: ^1.15.0 + source_span: ^1.8.0 + string_scanner: ^1.1.0 + typed_data: ^1.3.0 dev_dependencies: - pedantic: ^1.10.0-nullsafety + pedantic: ^1.10.0 test: ^1.16.0-nullsafety.4 - -# Because these packages depend on the prev4 version of this package -dependency_overrides: - shelf: 0.7.9 - shelf_static: 0.2.8 - test: 1.16.0-nullsafety.9 From 7dd72c69b9b9a229de53f48cb4444a019e0bf014 Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Mon, 1 Mar 2021 20:07:53 +0100 Subject: [PATCH 079/126] Remove dependency on `package:charcode`. (dart-lang/http_parser#43) --- pkgs/http_parser/CHANGELOG.md | 4 ++++ .../lib/src/chunked_coding/charcodes.dart | 21 +++++++++++++++++++ .../lib/src/chunked_coding/decoder.dart | 3 ++- .../lib/src/chunked_coding/encoder.dart | 2 +- pkgs/http_parser/pubspec.yaml | 4 ++-- 5 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 pkgs/http_parser/lib/src/chunked_coding/charcodes.dart diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index 3c942aa96e..2bb3d5546f 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,3 +1,7 @@ +## 4.0.1 + +* Remove dependency on `package:charcode`. + ## 4.0.0 * Stable null safety stable release. diff --git a/pkgs/http_parser/lib/src/chunked_coding/charcodes.dart b/pkgs/http_parser/lib/src/chunked_coding/charcodes.dart new file mode 100644 index 0000000000..a7a8519e17 --- /dev/null +++ b/pkgs/http_parser/lib/src/chunked_coding/charcodes.dart @@ -0,0 +1,21 @@ +// 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. + +/// "Line feed" control character. +const int $lf = 0x0a; + +/// "Carriage return" control character. +const int $cr = 0x0d; + +/// Character `0`. +const int $0 = 0x30; + +/// Character `9`. +const int $9 = 0x39; + +/// Character `a`. +const int $a = 0x61; + +/// Character `f`. +const int $f = 0x66; diff --git a/pkgs/http_parser/lib/src/chunked_coding/decoder.dart b/pkgs/http_parser/lib/src/chunked_coding/decoder.dart index a916870484..ac22c1b991 100644 --- a/pkgs/http_parser/lib/src/chunked_coding/decoder.dart +++ b/pkgs/http_parser/lib/src/chunked_coding/decoder.dart @@ -7,9 +7,10 @@ import 'dart:convert'; import 'dart:math' as math; import 'dart:typed_data'; -import 'package:charcode/ascii.dart'; import 'package:typed_data/typed_data.dart'; +import 'charcodes.dart'; + /// The canonical instance of [ChunkedCodingDecoder]. const chunkedCodingDecoder = ChunkedCodingDecoder._(); diff --git a/pkgs/http_parser/lib/src/chunked_coding/encoder.dart b/pkgs/http_parser/lib/src/chunked_coding/encoder.dart index 81e4dc8fb3..deb4906608 100644 --- a/pkgs/http_parser/lib/src/chunked_coding/encoder.dart +++ b/pkgs/http_parser/lib/src/chunked_coding/encoder.dart @@ -5,7 +5,7 @@ import 'dart:convert'; import 'dart:typed_data'; -import 'package:charcode/ascii.dart'; +import 'charcodes.dart'; /// The canonical instance of [ChunkedCodingEncoder]. const chunkedCodingEncoder = ChunkedCodingEncoder._(); diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index b2939aaec6..2ca441bc07 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,5 +1,5 @@ name: http_parser -version: 4.0.0 +version: 4.0.1 description: >- A platform-independent package for parsing and serializing HTTP formats. homepage: https://github.com/dart-lang/http_parser @@ -8,12 +8,12 @@ environment: sdk: '>=2.12.0-0 <3.0.0' dependencies: - charcode: ^1.2.0 collection: ^1.15.0 source_span: ^1.8.0 string_scanner: ^1.1.0 typed_data: ^1.3.0 dev_dependencies: + charcode: ^1.2.0 pedantic: ^1.10.0 test: ^1.16.0-nullsafety.4 From 7ebf7ed517df5e6a975fdf924e8e8097bcf32391 Mon Sep 17 00:00:00 2001 From: Franklin Yow <58489007+franklinyow@users.noreply.github.com> Date: Wed, 31 Mar 2021 15:14:37 -0700 Subject: [PATCH 080/126] Update LICENSE Changes to comply to internal review --- pkgs/http_parser/LICENSE | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkgs/http_parser/LICENSE b/pkgs/http_parser/LICENSE index 5c60afea39..000cd7beca 100644 --- a/pkgs/http_parser/LICENSE +++ b/pkgs/http_parser/LICENSE @@ -1,4 +1,5 @@ -Copyright 2014, the Dart project authors. All rights reserved. +Copyright 2014, 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 302bd3749c548b8fdf3f64769480d37e7dded210 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 26 Apr 2021 11:09:59 -0700 Subject: [PATCH 081/126] Fix newly enforced lint (dart-lang/http_parser#46) --- pkgs/http_parser/test/chunked_coding_test.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkgs/http_parser/test/chunked_coding_test.dart b/pkgs/http_parser/test/chunked_coding_test.dart index 045557ab8e..93b4de4e02 100644 --- a/pkgs/http_parser/test/chunked_coding_test.dart +++ b/pkgs/http_parser/test/chunked_coding_test.dart @@ -5,9 +5,8 @@ import 'dart:async'; import 'dart:convert'; -import 'package:http_parser/http_parser.dart'; - import 'package:charcode/charcode.dart'; +import 'package:http_parser/http_parser.dart'; import 'package:test/test.dart'; void main() { From 51b5d47032cfb5c6cef17c8bc94aa6c3d60533d3 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 26 Apr 2021 11:16:38 -0700 Subject: [PATCH 082/126] Update CI (dart-lang/http_parser#47) --- pkgs/http_parser/.github/workflows/test-package.yml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/pkgs/http_parser/.github/workflows/test-package.yml b/pkgs/http_parser/.github/workflows/test-package.yml index 0a2a874338..2f5a52c545 100644 --- a/pkgs/http_parser/.github/workflows/test-package.yml +++ b/pkgs/http_parser/.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.0 with: sdk: ${{ matrix.sdk }} - id: install @@ -36,21 +36,17 @@ jobs: 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] + sdk: [2.12.0, dev] steps: - uses: actions/checkout@v2 - - uses: dart-lang/setup-dart@v0.3 + - uses: dart-lang/setup-dart@v1.0 with: sdk: ${{ matrix.sdk }} - id: install From 48c2070477ca66c72eff971fde1e7f4ca4e219cc Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Sat, 10 Jul 2021 20:57:42 -0700 Subject: [PATCH 083/126] Dart format with latest SDK (dart-lang/http_parser#49) --- pkgs/http_parser/lib/src/media_type.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkgs/http_parser/lib/src/media_type.dart b/pkgs/http_parser/lib/src/media_type.dart index 59e692887b..814de63467 100644 --- a/pkgs/http_parser/lib/src/media_type.dart +++ b/pkgs/http_parser/lib/src/media_type.dart @@ -131,7 +131,10 @@ class MediaType { /// This will produce a valid HTTP media type. @override String toString() { - final buffer = StringBuffer()..write(type)..write('/')..write(subtype); + final buffer = StringBuffer() + ..write(type) + ..write('/') + ..write(subtype); parameters.forEach((attribute, value) { buffer.write('; $attribute='); From fdd09518690e668a29de06ae2c0f10cec8641b4b Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 22 Jul 2021 13:01:32 -0700 Subject: [PATCH 084/126] Move from pedantic to lints package (dart-lang/http_parser#50) --- pkgs/http_parser/CHANGELOG.md | 2 ++ pkgs/http_parser/analysis_options.yaml | 4 +--- pkgs/http_parser/pubspec.yaml | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index 2bb3d5546f..60da466da1 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,3 +1,5 @@ +## 4.0.2-dev + ## 4.0.1 * Remove dependency on `package:charcode`. diff --git a/pkgs/http_parser/analysis_options.yaml b/pkgs/http_parser/analysis_options.yaml index f98644b025..a193a62c29 100644 --- a/pkgs/http_parser/analysis_options.yaml +++ b/pkgs/http_parser/analysis_options.yaml @@ -1,10 +1,8 @@ -include: package:pedantic/analysis_options.yaml +include: package:lints/recommended.yaml analyzer: strong-mode: implicit-casts: false - enable-experiment: - - non-nullable linter: rules: diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index 2ca441bc07..b8914480bf 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,11 +1,11 @@ name: http_parser -version: 4.0.1 +version: 4.0.2-dev description: >- A platform-independent package for parsing and serializing HTTP formats. homepage: https://github.com/dart-lang/http_parser environment: - sdk: '>=2.12.0-0 <3.0.0' + sdk: '>=2.12.0 <3.0.0' dependencies: collection: ^1.15.0 @@ -15,5 +15,5 @@ dependencies: dev_dependencies: charcode: ^1.2.0 - pedantic: ^1.10.0 - test: ^1.16.0-nullsafety.4 + lints: ^1.0.0 + test: ^1.16.0 From 2877d82084cc5b8885f3280f60842519adb0ad28 Mon Sep 17 00:00:00 2001 From: Sigurd Meldgaard Date: Fri, 10 Dec 2021 08:23:36 +0000 Subject: [PATCH 085/126] Prepare for publishing 4.0.1 --- pkgs/http_parser/CHANGELOG.md | 2 -- pkgs/http_parser/pubspec.yaml | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index 60da466da1..2bb3d5546f 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,5 +1,3 @@ -## 4.0.2-dev - ## 4.0.1 * Remove dependency on `package:charcode`. diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index b8914480bf..08b2b52de6 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,5 +1,5 @@ name: http_parser -version: 4.0.2-dev +version: 4.0.1 description: >- A platform-independent package for parsing and serializing HTTP formats. homepage: https://github.com/dart-lang/http_parser From 5c5d9d0206da9f11633a274a835f4a0575c36e2a Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Wed, 20 Apr 2022 14:44:19 -0700 Subject: [PATCH 086/126] Switch from homepage to repository in pubspec (dart-lang/http_parser#54) --- pkgs/http_parser/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index b8914480bf..60573f1718 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -2,7 +2,7 @@ name: http_parser version: 4.0.2-dev description: >- A platform-independent package for parsing and serializing HTTP formats. -homepage: https://github.com/dart-lang/http_parser +repository: https://github.com/dart-lang/http_parser environment: sdk: '>=2.12.0 <3.0.0' From b776e549b50d8ee3316dea1ba6e6a64440cd855f Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Thu, 5 May 2022 10:00:13 -0700 Subject: [PATCH 087/126] Update actions (dart-lang/http_parser#55) --- pkgs/http_parser/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http_parser/.github/workflows/test-package.yml b/pkgs/http_parser/.github/workflows/test-package.yml index 2f5a52c545..6caf97cef8 100644 --- a/pkgs/http_parser/.github/workflows/test-package.yml +++ b/pkgs/http_parser/.github/workflows/test-package.yml @@ -45,8 +45,8 @@ jobs: os: [ubuntu-latest] sdk: [2.12.0, dev] steps: - - uses: actions/checkout@v2 - - uses: dart-lang/setup-dart@v1.0 + - uses: actions/checkout@v3 + - uses: dart-lang/setup-dart@v1.3 with: sdk: ${{ matrix.sdk }} - id: install From 9f5a49f5b891d412567a28d9366eef9fdaab992e Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Mon, 16 May 2022 14:06:20 -0700 Subject: [PATCH 088/126] Update README.md --- pkgs/http_parser/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkgs/http_parser/README.md b/pkgs/http_parser/README.md index 763de43b7d..4c61d9d5ea 100644 --- a/pkgs/http_parser/README.md +++ b/pkgs/http_parser/README.md @@ -1,5 +1,6 @@ -[![Pub Package](https://img.shields.io/pub/v/http_parser.svg)](https://pub.dartlang.org/packages/http_parser) [![Build Status](https://github.com/dart-lang/http_parser/workflows/Dart%20CI/badge.svg)](https://github.com/dart-lang/http_parser/actions?query=workflow%3A"Dart+CI"+branch%3Amaster) +[![Pub Package](https://img.shields.io/pub/v/http_parser.svg)](https://pub.dartlang.org/packages/http_parser) +[![package publisher](https://img.shields.io/pub/publisher/http_parser.svg)](https://pub.dev/packages/http_parser/publisher) `http_parser` is a platform-independent package for parsing and serializing various HTTP-related formats. It's designed to be usable on both the browser and From 723bb5b0e470a7075dd173bfe69cd99619c5dd93 Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Mon, 25 Jul 2022 13:25:53 -0700 Subject: [PATCH 089/126] Remove deprecated experimental invariant_booleans lint rule --- pkgs/http_parser/analysis_options.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/pkgs/http_parser/analysis_options.yaml b/pkgs/http_parser/analysis_options.yaml index a193a62c29..c4dc6ae631 100644 --- a/pkgs/http_parser/analysis_options.yaml +++ b/pkgs/http_parser/analysis_options.yaml @@ -31,7 +31,6 @@ linter: - file_names - hash_and_equals - implementation_imports - - invariant_booleans - iterable_contains_unrelated_type - join_return_with_assignment - lines_longer_than_80_chars From 31f225a6fe78dac8685b0e8d7157f1fd6d2ebdfd Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Fri, 29 Jul 2022 15:52:05 -0700 Subject: [PATCH 090/126] Add permissions to CI script (dart-lang/http_parser#58) --- pkgs/http_parser/.github/workflows/test-package.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/pkgs/http_parser/.github/workflows/test-package.yml b/pkgs/http_parser/.github/workflows/test-package.yml index 6caf97cef8..72912f9c5d 100644 --- a/pkgs/http_parser/.github/workflows/test-package.yml +++ b/pkgs/http_parser/.github/workflows/test-package.yml @@ -11,6 +11,7 @@ on: env: PUB_ENVIRONMENT: bot.github +permissions: read-all jobs: # Check code formatting and static analysis on a single OS (linux) From 713e60f594b53c96859fd46630608e4a1aeaad61 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 1 Aug 2022 11:07:41 -0700 Subject: [PATCH 091/126] Drop pkg:charcode usage in dev_dependencies --- pkgs/http_parser/CHANGELOG.md | 2 ++ .../lib/src/chunked_coding/charcodes.dart | 19 +++++++++++++++++-- pkgs/http_parser/pubspec.yaml | 3 +-- .../http_parser/test/chunked_coding_test.dart | 2 +- 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index 2bb3d5546f..60da466da1 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,3 +1,5 @@ +## 4.0.2-dev + ## 4.0.1 * Remove dependency on `package:charcode`. diff --git a/pkgs/http_parser/lib/src/chunked_coding/charcodes.dart b/pkgs/http_parser/lib/src/chunked_coding/charcodes.dart index a7a8519e17..4d5e451f93 100644 --- a/pkgs/http_parser/lib/src/chunked_coding/charcodes.dart +++ b/pkgs/http_parser/lib/src/chunked_coding/charcodes.dart @@ -11,8 +11,23 @@ const int $cr = 0x0d; /// Character `0`. const int $0 = 0x30; -/// Character `9`. -const int $9 = 0x39; +/// Character `1`. +const int $1 = 0x31; + +/// Character `3`. +const int $3 = 0x33; + +/// Character `4`. +const int $4 = 0x34; + +/// Character `7`. +const int $7 = 0x37; + +/// Character `A`. +const int $A = 0x41; + +/// Character `q`. +const int $q = 0x71; /// Character `a`. const int $a = 0x61; diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index 9ceb4287e7..a768409328 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,5 +1,5 @@ name: http_parser -version: 4.0.1 +version: 4.0.2-dev description: >- A platform-independent package for parsing and serializing HTTP formats. repository: https://github.com/dart-lang/http_parser @@ -14,6 +14,5 @@ dependencies: typed_data: ^1.3.0 dev_dependencies: - charcode: ^1.2.0 lints: ^1.0.0 test: ^1.16.0 diff --git a/pkgs/http_parser/test/chunked_coding_test.dart b/pkgs/http_parser/test/chunked_coding_test.dart index 93b4de4e02..476a2d2688 100644 --- a/pkgs/http_parser/test/chunked_coding_test.dart +++ b/pkgs/http_parser/test/chunked_coding_test.dart @@ -5,8 +5,8 @@ import 'dart:async'; import 'dart:convert'; -import 'package:charcode/charcode.dart'; import 'package:http_parser/http_parser.dart'; +import 'package:http_parser/src/chunked_coding/charcodes.dart'; import 'package:test/test.dart'; void main() { From 1f3365665df9ca49856bf79ffb27423c7a8d0d4d Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Tue, 4 Oct 2022 13:43:41 -0700 Subject: [PATCH 092/126] update CI; prep for publishing --- pkgs/http_parser/.github/dependabot.yaml | 8 ++++++++ pkgs/http_parser/.github/workflows/test-package.yml | 8 ++++---- pkgs/http_parser/CHANGELOG.md | 4 +++- pkgs/http_parser/README.md | 3 ++- pkgs/http_parser/pubspec.yaml | 2 +- 5 files changed, 18 insertions(+), 7 deletions(-) create mode 100644 pkgs/http_parser/.github/dependabot.yaml diff --git a/pkgs/http_parser/.github/dependabot.yaml b/pkgs/http_parser/.github/dependabot.yaml new file mode 100644 index 0000000000..214481934b --- /dev/null +++ b/pkgs/http_parser/.github/dependabot.yaml @@ -0,0 +1,8 @@ +# Dependabot configuration file. +version: 2 + +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" diff --git a/pkgs/http_parser/.github/workflows/test-package.yml b/pkgs/http_parser/.github/workflows/test-package.yml index 72912f9c5d..bdc0994d23 100644 --- a/pkgs/http_parser/.github/workflows/test-package.yml +++ b/pkgs/http_parser/.github/workflows/test-package.yml @@ -23,8 +23,8 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@v2 - - uses: dart-lang/setup-dart@v1.0 + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d with: sdk: ${{ matrix.sdk }} - id: install @@ -46,8 +46,8 @@ jobs: os: [ubuntu-latest] sdk: [2.12.0, dev] steps: - - uses: actions/checkout@v3 - - uses: dart-lang/setup-dart@v1.3 + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d with: sdk: ${{ matrix.sdk }} - id: install diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index 60da466da1..29b349172e 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,4 +1,6 @@ -## 4.0.2-dev +## 4.0.2 + +* Remove `package:charcode` from dev_dependencies. ## 4.0.1 diff --git a/pkgs/http_parser/README.md b/pkgs/http_parser/README.md index 4c61d9d5ea..73f2f1cfe1 100644 --- a/pkgs/http_parser/README.md +++ b/pkgs/http_parser/README.md @@ -5,7 +5,8 @@ `http_parser` is a platform-independent package for parsing and serializing various HTTP-related formats. It's designed to be usable on both the browser and the server, and thus avoids referencing any types from `dart:io` or `dart:html`. -It includes: + +## Features * Support for parsing and formatting dates according to [HTTP/1.1][2616], the HTTP/1.1 standard. diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index a768409328..fc15f69816 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,5 +1,5 @@ name: http_parser -version: 4.0.2-dev +version: 4.0.2 description: >- A platform-independent package for parsing and serializing HTTP formats. repository: https://github.com/dart-lang/http_parser From 5618f8898459190ac1b9e9212ca26bc2a0e65fe8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Jan 2023 09:24:52 -0800 Subject: [PATCH 093/126] Bump actions/checkout from 3.1.0 to 3.2.0 (dart-lang/http_parser#65) 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/http_parser/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http_parser/.github/workflows/test-package.yml b/pkgs/http_parser/.github/workflows/test-package.yml index bdc0994d23..248af94426 100644 --- a/pkgs/http_parser/.github/workflows/test-package.yml +++ b/pkgs/http_parser/.github/workflows/test-package.yml @@ -23,7 +23,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + - uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d with: sdk: ${{ matrix.sdk }} @@ -46,7 +46,7 @@ jobs: os: [ubuntu-latest] sdk: [2.12.0, dev] steps: - - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + - uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d with: sdk: ${{ matrix.sdk }} From e791040ac41641156f326b6d6f681fdf3862a6b4 Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Mon, 9 Jan 2023 21:30:38 -0800 Subject: [PATCH 094/126] Migrate from no-implicit-casts to strict-casts (dart-lang/http_parser#66) --- pkgs/http_parser/analysis_options.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http_parser/analysis_options.yaml b/pkgs/http_parser/analysis_options.yaml index c4dc6ae631..acc502ffc6 100644 --- a/pkgs/http_parser/analysis_options.yaml +++ b/pkgs/http_parser/analysis_options.yaml @@ -1,8 +1,8 @@ include: package:lints/recommended.yaml analyzer: - strong-mode: - implicit-casts: false + language: + strict-casts: true linter: rules: From 0448e864b071d7d96c06fe5315dfe2d005a467b1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Feb 2023 15:32:09 -0800 Subject: [PATCH 095/126] Bump dart-lang/setup-dart from 1.3 to 1.4 (dart-lang/http_parser#67) 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/http_parser/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http_parser/.github/workflows/test-package.yml b/pkgs/http_parser/.github/workflows/test-package.yml index 248af94426..6805bca75b 100644 --- a/pkgs/http_parser/.github/workflows/test-package.yml +++ b/pkgs/http_parser/.github/workflows/test-package.yml @@ -24,7 +24,7 @@ jobs: sdk: [dev] steps: - uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b - - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d + - uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46 with: sdk: ${{ matrix.sdk }} - id: install @@ -47,7 +47,7 @@ jobs: sdk: [2.12.0, dev] steps: - uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b - - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d + - uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46 with: sdk: ${{ matrix.sdk }} - id: install From c421ef044b1689d8070c514b190dc9939957d49e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Feb 2023 15:37:01 -0800 Subject: [PATCH 096/126] Bump actions/checkout from 3.2.0 to 3.3.0 (dart-lang/http_parser#68) 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pkgs/http_parser/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http_parser/.github/workflows/test-package.yml b/pkgs/http_parser/.github/workflows/test-package.yml index 6805bca75b..f13a5f2fbe 100644 --- a/pkgs/http_parser/.github/workflows/test-package.yml +++ b/pkgs/http_parser/.github/workflows/test-package.yml @@ -23,7 +23,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c - uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46 with: sdk: ${{ matrix.sdk }} @@ -46,7 +46,7 @@ jobs: os: [ubuntu-latest] sdk: [2.12.0, dev] steps: - - uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c - uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46 with: sdk: ${{ matrix.sdk }} From c5aa23bebf57d56fe5bd5ec5853334dd00a70dc5 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 8 Feb 2023 08:01:43 -0800 Subject: [PATCH 097/126] Bump min SDK to 2.17 (dart-lang/http_parser#69) Move to dart_flutter_team_lints and fix related issues --- .../.github/workflows/test-package.yml | 2 +- pkgs/http_parser/CHANGELOG.md | 4 ++ pkgs/http_parser/analysis_options.yaml | 51 +++---------------- .../lib/src/authentication_challenge.dart | 7 +-- pkgs/http_parser/pubspec.yaml | 6 +-- .../test/authentication_challenge_test.dart | 3 +- .../test/case_insensitive_map_test.dart | 4 +- .../http_parser/test/chunked_coding_test.dart | 14 ++--- pkgs/http_parser/test/media_type_test.dart | 4 +- 9 files changed, 31 insertions(+), 64 deletions(-) diff --git a/pkgs/http_parser/.github/workflows/test-package.yml b/pkgs/http_parser/.github/workflows/test-package.yml index f13a5f2fbe..356b8d621e 100644 --- a/pkgs/http_parser/.github/workflows/test-package.yml +++ b/pkgs/http_parser/.github/workflows/test-package.yml @@ -44,7 +44,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - sdk: [2.12.0, dev] + sdk: [2.17.0, dev] steps: - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c - uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46 diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index 29b349172e..9f856f932f 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,3 +1,7 @@ +## 4.0.3-dev + +* Require Dart 2.17 + ## 4.0.2 * Remove `package:charcode` from dev_dependencies. diff --git a/pkgs/http_parser/analysis_options.yaml b/pkgs/http_parser/analysis_options.yaml index acc502ffc6..a18a7f7d94 100644 --- a/pkgs/http_parser/analysis_options.yaml +++ b/pkgs/http_parser/analysis_options.yaml @@ -1,76 +1,39 @@ -include: package:lints/recommended.yaml +# https://dart.dev/guides/language/analysis-options +include: package:dart_flutter_team_lints/analysis_options.yaml analyzer: language: strict-casts: true + strict-inference: true + strict-raw-types: true linter: rules: - avoid_bool_literals_in_conditional_expressions - - avoid_catching_errors - avoid_classes_with_only_static_members - - avoid_function_literals_in_foreach_calls - avoid_private_typedef_functions - avoid_redundant_argument_values - - avoid_renaming_method_parameters - avoid_returning_null - avoid_returning_null_for_future - - avoid_returning_null_for_void - avoid_returning_this - - avoid_single_cascade_in_expression_statements - avoid_unused_constructor_parameters - avoid_void_async - - await_only_futures - - camel_case_types - cancel_subscriptions - comment_references - - constant_identifier_names - - control_flow_in_finally - - directives_ordering - - empty_statements - - file_names - - hash_and_equals - - implementation_imports - - iterable_contains_unrelated_type - join_return_with_assignment - - lines_longer_than_80_chars - - list_remove_unrelated_type - literal_only_boolean_expressions - missing_whitespace_between_adjacent_strings - no_adjacent_strings_in_list - no_runtimeType_toString - - non_constant_identifier_names - - only_throw_errors - - overridden_fields - package_api_docs - - package_names - - package_prefixed_library_names - - prefer_asserts_in_initializer_lists - prefer_const_constructors - prefer_const_declarations - prefer_expression_function_bodies - prefer_final_locals - - prefer_function_declarations_over_variables - - prefer_initializing_formals - - prefer_inlined_adds - - prefer_interpolation_to_compose_strings - - prefer_is_not_operator - - prefer_null_aware_operators - prefer_relative_imports - - prefer_typing_uninitialized_variables - - prefer_void_to_null - - provide_deprecation_message - - sort_pub_dependencies - test_types_in_equals - - throw_in_finally - unnecessary_await_in_return - - unnecessary_brace_in_string_interps - - unnecessary_getters_setters - - unnecessary_lambdas - - unnecessary_null_aware_assignments - - unnecessary_overrides - - unnecessary_parenthesis - - unnecessary_statements - - unnecessary_string_interpolations + - use_if_null_to_convert_nulls_to_bools + - use_raw_strings - use_string_buffers - - void_checks + - use_super_parameters diff --git a/pkgs/http_parser/lib/src/authentication_challenge.dart b/pkgs/http_parser/lib/src/authentication_challenge.dart index 356e0a25da..7eebc431f9 100644 --- a/pkgs/http_parser/lib/src/authentication_challenge.dart +++ b/pkgs/http_parser/lib/src/authentication_challenge.dart @@ -125,15 +125,16 @@ class AuthenticationChallenge { } /// Scans a single authentication parameter and stores its result in [params]. - static void _scanAuthParam(StringScanner scanner, Map params) { + static void _scanAuthParam( + StringScanner scanner, Map params) { scanner.expect(token, name: 'a token'); - final name = scanner.lastMatch![0]; + final name = scanner.lastMatch![0]!; scanner.scan(whitespace); scanner.expect('='); scanner.scan(whitespace); if (scanner.scan(token)) { - params[name] = scanner.lastMatch![0]; + params[name] = scanner.lastMatch![0]!; } else { params[name] = expectQuotedString(scanner, name: 'a token or a quoted string'); diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index fc15f69816..4e349b1378 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,11 +1,11 @@ name: http_parser -version: 4.0.2 +version: 4.0.3-dev description: >- A platform-independent package for parsing and serializing HTTP formats. repository: https://github.com/dart-lang/http_parser environment: - sdk: '>=2.12.0 <3.0.0' + sdk: '>=2.17.0 <3.0.0' dependencies: collection: ^1.15.0 @@ -14,5 +14,5 @@ dependencies: typed_data: ^1.3.0 dev_dependencies: - lints: ^1.0.0 + dart_flutter_team_lints: ^0.1.0 test: ^1.16.0 diff --git a/pkgs/http_parser/test/authentication_challenge_test.dart b/pkgs/http_parser/test/authentication_challenge_test.dart index 6184d3e1ef..52d798ca3a 100644 --- a/pkgs/http_parser/test/authentication_challenge_test.dart +++ b/pkgs/http_parser/test/authentication_challenge_test.dart @@ -7,8 +7,7 @@ import 'package:test/test.dart'; void main() { group('parse', () { - _singleChallengeTests( - (challenge) => AuthenticationChallenge.parse(challenge)); + _singleChallengeTests(AuthenticationChallenge.parse); }); group('parseHeader', () { diff --git a/pkgs/http_parser/test/case_insensitive_map_test.dart b/pkgs/http_parser/test/case_insensitive_map_test.dart index b08d89b233..f62d4fec6d 100644 --- a/pkgs/http_parser/test/case_insensitive_map_test.dart +++ b/pkgs/http_parser/test/case_insensitive_map_test.dart @@ -7,7 +7,7 @@ import 'package:test/test.dart'; void main() { test('provides case-insensitive access to the map', () { - final map = CaseInsensitiveMap(); + final map = CaseInsensitiveMap(); map['fOo'] = 'bAr'; expect(map, containsPair('FoO', 'bAr')); @@ -16,7 +16,7 @@ void main() { }); test('stores the original key cases', () { - final map = CaseInsensitiveMap(); + final map = CaseInsensitiveMap(); map['fOo'] = 'bAr'; expect(map, equals({'fOo': 'bAr'})); }); diff --git a/pkgs/http_parser/test/chunked_coding_test.dart b/pkgs/http_parser/test/chunked_coding_test.dart index 476a2d2688..fc4c13ff39 100644 --- a/pkgs/http_parser/test/chunked_coding_test.dart +++ b/pkgs/http_parser/test/chunked_coding_test.dart @@ -66,13 +66,13 @@ void main() { test('handles empty chunks', () { sink.add([]); - expect(results, equals([[]])); + expect(results, equals([[]])); sink.add([1, 2, 3]); expect( results, equals([ - [], + [], [$3, $cr, $lf, 1, 2, 3, $cr, $lf] ])); @@ -80,18 +80,18 @@ void main() { expect( results, equals([ - [], + [], [$3, $cr, $lf, 1, 2, 3, $cr, $lf], - [] + [] ])); sink.close(); expect( results, equals([ - [], + [], [$3, $cr, $lf, 1, 2, 3, $cr, $lf], - [], + [], [$0, $cr, $lf, $cr, $lf], ])); }); @@ -108,7 +108,7 @@ void main() { test("doesn't add a header if the slice is empty", () { sink.addSlice([1, 2, 3, 4, 5], 1, 1, false); - expect(results, equals([[]])); + expect(results, equals([[]])); }); test('adds a footer if isLast is true', () { diff --git a/pkgs/http_parser/test/media_type_test.dart b/pkgs/http_parser/test/media_type_test.dart index 5ee9313305..9a4226c67b 100644 --- a/pkgs/http_parser/test/media_type_test.dart +++ b/pkgs/http_parser/test/media_type_test.dart @@ -61,10 +61,10 @@ void main() { test('parses quoted parameters', () { final type = - MediaType.parse('text/plain; foo="bar space"; baz="bang\\\\escape"'); + MediaType.parse(r'text/plain; foo="bar space"; baz="bang\\escape"'); expect(type.mimeType, equals('text/plain')); expect( - type.parameters, equals({'foo': 'bar space', 'baz': 'bang\\escape'})); + type.parameters, equals({'foo': 'bar space', 'baz': r'bang\escape'})); }); test('lower-cases type and subtype', () { From a8ad724bc67fd12bcfce71e4e568d074b6d3fdd2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Apr 2023 12:03:02 -0700 Subject: [PATCH 098/126] Bump dart-lang/setup-dart from 1.4.0 to 1.5.0 (dart-lang/http_parser#71) 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/http_parser/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http_parser/.github/workflows/test-package.yml b/pkgs/http_parser/.github/workflows/test-package.yml index 356b8d621e..75283d9d54 100644 --- a/pkgs/http_parser/.github/workflows/test-package.yml +++ b/pkgs/http_parser/.github/workflows/test-package.yml @@ -24,7 +24,7 @@ jobs: sdk: [dev] steps: - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c - - uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46 + - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} - id: install @@ -47,7 +47,7 @@ jobs: sdk: [2.17.0, dev] steps: - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c - - uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46 + - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} - id: install From 1d10ca510bf2f47b24d4b8a66fa573d40c68835c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Apr 2023 14:35:10 -0700 Subject: [PATCH 099/126] Bump actions/checkout from 3.3.0 to 3.5.0 (dart-lang/http_parser#70) 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/http_parser/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http_parser/.github/workflows/test-package.yml b/pkgs/http_parser/.github/workflows/test-package.yml index 75283d9d54..a3f6d30dc7 100644 --- a/pkgs/http_parser/.github/workflows/test-package.yml +++ b/pkgs/http_parser/.github/workflows/test-package.yml @@ -23,7 +23,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c + - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} @@ -46,7 +46,7 @@ jobs: os: [ubuntu-latest] sdk: [2.17.0, dev] steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c + - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} From 2195e5cdb073e60f94c557908268fc08db963f40 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 May 2023 12:21:56 -0700 Subject: [PATCH 100/126] Bump actions/checkout from 3.5.0 to 3.5.2 (dart-lang/http_parser#72) Bumps [actions/checkout](https://github.com/actions/checkout) from 3.5.0 to 3.5.2. - [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/8f4b7f84864484a7bf31766abe9204da3cbe65b3...8e5e7e5ab8b370d6c329ec480221332ada57f0ab) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pkgs/http_parser/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http_parser/.github/workflows/test-package.yml b/pkgs/http_parser/.github/workflows/test-package.yml index a3f6d30dc7..ff779ee0d3 100644 --- a/pkgs/http_parser/.github/workflows/test-package.yml +++ b/pkgs/http_parser/.github/workflows/test-package.yml @@ -23,7 +23,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} @@ -46,7 +46,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 23bd5e902485149db8f9c099a10d94f2f2596b84 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Wed, 17 May 2023 11:10:21 -0700 Subject: [PATCH 101/126] blast_repo fixes (dart-lang/http_parser#73) dependabot --- pkgs/http_parser/.github/dependabot.yaml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pkgs/http_parser/.github/dependabot.yaml b/pkgs/http_parser/.github/dependabot.yaml index 214481934b..439e796b48 100644 --- a/pkgs/http_parser/.github/dependabot.yaml +++ b/pkgs/http_parser/.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 a3a0d35db9a462e7f1a9006d81fc2fff8dbd6734 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 7 Jun 2023 16:48:48 -0700 Subject: [PATCH 102/126] Require Dart 3.0, update lints (dart-lang/http_parser#74) --- pkgs/http_parser/.github/workflows/test-package.yml | 2 +- pkgs/http_parser/CHANGELOG.md | 2 +- pkgs/http_parser/analysis_options.yaml | 1 + pkgs/http_parser/lib/src/chunked_coding/decoder.dart | 8 -------- pkgs/http_parser/pubspec.yaml | 4 ++-- 5 files changed, 5 insertions(+), 12 deletions(-) diff --git a/pkgs/http_parser/.github/workflows/test-package.yml b/pkgs/http_parser/.github/workflows/test-package.yml index ff779ee0d3..13d2250d64 100644 --- a/pkgs/http_parser/.github/workflows/test-package.yml +++ b/pkgs/http_parser/.github/workflows/test-package.yml @@ -44,7 +44,7 @@ jobs: fail-fast: false matrix: 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/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index 9f856f932f..4f9e2a6611 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,6 +1,6 @@ ## 4.0.3-dev -* Require Dart 2.17 +* Require Dart 3.0 ## 4.0.2 diff --git a/pkgs/http_parser/analysis_options.yaml b/pkgs/http_parser/analysis_options.yaml index a18a7f7d94..fc4afe346f 100644 --- a/pkgs/http_parser/analysis_options.yaml +++ b/pkgs/http_parser/analysis_options.yaml @@ -33,6 +33,7 @@ linter: - prefer_relative_imports - test_types_in_equals - unnecessary_await_in_return + - unnecessary_breaks - use_if_null_to_convert_nulls_to_bools - use_raw_strings - use_string_buffers diff --git a/pkgs/http_parser/lib/src/chunked_coding/decoder.dart b/pkgs/http_parser/lib/src/chunked_coding/decoder.dart index ac22c1b991..5388ae1f53 100644 --- a/pkgs/http_parser/lib/src/chunked_coding/decoder.dart +++ b/pkgs/http_parser/lib/src/chunked_coding/decoder.dart @@ -88,7 +88,6 @@ class _Sink extends ByteConversionSinkBase { _size = _digitForByte(bytes, start); _state = _State.size; start++; - break; case _State.size: if (bytes[start] == $cr) { @@ -99,13 +98,11 @@ class _Sink extends ByteConversionSinkBase { _size = (_size << 4) + _digitForByte(bytes, start); } start++; - break; case _State.sizeBeforeLF: assertCurrentChar($lf, 'LF'); _state = _size == 0 ? _State.endBeforeCR : _State.body; start++; - break; case _State.body: final chunkEnd = math.min(end, start + _size); @@ -113,31 +110,26 @@ class _Sink extends ByteConversionSinkBase { _size -= chunkEnd - start; start = chunkEnd; if (_size == 0) _state = _State.bodyBeforeCR; - break; case _State.bodyBeforeCR: assertCurrentChar($cr, 'CR'); _state = _State.bodyBeforeLF; start++; - break; case _State.bodyBeforeLF: assertCurrentChar($lf, 'LF'); _state = _State.boundary; start++; - break; case _State.endBeforeCR: assertCurrentChar($cr, 'CR'); _state = _State.endBeforeLF; start++; - break; case _State.endBeforeLF: assertCurrentChar($lf, 'LF'); _state = _State.end; start++; - break; case _State.end: throw FormatException('Expected no more data.', bytes, start); diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index 4e349b1378..951b82f7ab 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -5,7 +5,7 @@ description: >- repository: https://github.com/dart-lang/http_parser environment: - sdk: '>=2.17.0 <3.0.0' + sdk: ^3.0.0 dependencies: collection: ^1.15.0 @@ -14,5 +14,5 @@ dependencies: typed_data: ^1.3.0 dev_dependencies: - dart_flutter_team_lints: ^0.1.0 + dart_flutter_team_lints: ^1.0.0 test: ^1.16.0 From 7bef32b8ddfa10360182f16dec847c6719c72dca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jul 2023 23:52:18 +0000 Subject: [PATCH 103/126] Bump actions/checkout from 3.5.2 to 3.5.3 (dart-lang/http_parser#75) 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/http_parser/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http_parser/.github/workflows/test-package.yml b/pkgs/http_parser/.github/workflows/test-package.yml index 13d2250d64..0963745626 100644 --- a/pkgs/http_parser/.github/workflows/test-package.yml +++ b/pkgs/http_parser/.github/workflows/test-package.yml @@ -23,7 +23,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} @@ -46,7 +46,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 c8e39fb19cccecc12facda32455146e2ec5c4ebc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 2 Sep 2023 00:01:03 +0000 Subject: [PATCH 104/126] Bump actions/checkout from 3.5.3 to 3.6.0 (dart-lang/http_parser#77) 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/http_parser/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http_parser/.github/workflows/test-package.yml b/pkgs/http_parser/.github/workflows/test-package.yml index 0963745626..8280244bd4 100644 --- a/pkgs/http_parser/.github/workflows/test-package.yml +++ b/pkgs/http_parser/.github/workflows/test-package.yml @@ -23,7 +23,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 + - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} @@ -46,7 +46,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 7ccbe7ac76a887613351a64064829baab0fbff03 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Oct 2023 23:38:31 +0000 Subject: [PATCH 105/126] Bump dart-lang/setup-dart from 1.5.0 to 1.5.1 (dart-lang/http_parser#79) 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

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/http_parser/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http_parser/.github/workflows/test-package.yml b/pkgs/http_parser/.github/workflows/test-package.yml index 8280244bd4..96728b62c1 100644 --- a/pkgs/http_parser/.github/workflows/test-package.yml +++ b/pkgs/http_parser/.github/workflows/test-package.yml @@ -24,7 +24,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 @@ -47,7 +47,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 e7be02a788e27cb5ecabde6a22518f25fa4339a1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Oct 2023 18:37:08 +0000 Subject: [PATCH 106/126] Bump actions/checkout from 3.6.0 to 4.1.0 (dart-lang/http_parser#78) 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/http_parser/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http_parser/.github/workflows/test-package.yml b/pkgs/http_parser/.github/workflows/test-package.yml index 96728b62c1..ad36dae6ef 100644 --- a/pkgs/http_parser/.github/workflows/test-package.yml +++ b/pkgs/http_parser/.github/workflows/test-package.yml @@ -23,7 +23,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 - uses: dart-lang/setup-dart@8a4b97ea2017cc079571daec46542f76189836b1 with: sdk: ${{ matrix.sdk }} @@ -46,7 +46,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 9648d653859b73f017c341ad950a64390f7def73 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Nov 2023 23:33:54 +0000 Subject: [PATCH 107/126] Bump actions/checkout from 4.1.0 to 4.1.1 (dart-lang/http_parser#81) 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/http_parser/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http_parser/.github/workflows/test-package.yml b/pkgs/http_parser/.github/workflows/test-package.yml index ad36dae6ef..0f91440dba 100644 --- a/pkgs/http_parser/.github/workflows/test-package.yml +++ b/pkgs/http_parser/.github/workflows/test-package.yml @@ -23,7 +23,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - uses: dart-lang/setup-dart@8a4b97ea2017cc079571daec46542f76189836b1 with: sdk: ${{ matrix.sdk }} @@ -46,7 +46,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 0c4555ee027ea0f8f4d8cd32dd5a5c42133fd64f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Nov 2023 16:11:54 +0000 Subject: [PATCH 108/126] Bump dart-lang/setup-dart from 1.5.1 to 1.6.0 (dart-lang/http_parser#80) 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

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/http_parser/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http_parser/.github/workflows/test-package.yml b/pkgs/http_parser/.github/workflows/test-package.yml index 0f91440dba..4d44890070 100644 --- a/pkgs/http_parser/.github/workflows/test-package.yml +++ b/pkgs/http_parser/.github/workflows/test-package.yml @@ -24,7 +24,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 @@ -47,7 +47,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 245a8ddc84bbbbd32d6c6481fddf0158668a0da5 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 4 Dec 2023 10:43:08 -0800 Subject: [PATCH 109/126] drop outdated lints (dart-lang/http_parser#82) --- pkgs/http_parser/analysis_options.yaml | 7 ------- pkgs/http_parser/pubspec.yaml | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/pkgs/http_parser/analysis_options.yaml b/pkgs/http_parser/analysis_options.yaml index fc4afe346f..d16ad82510 100644 --- a/pkgs/http_parser/analysis_options.yaml +++ b/pkgs/http_parser/analysis_options.yaml @@ -13,28 +13,21 @@ linter: - avoid_classes_with_only_static_members - avoid_private_typedef_functions - avoid_redundant_argument_values - - avoid_returning_null - - avoid_returning_null_for_future - avoid_returning_this - avoid_unused_constructor_parameters - avoid_void_async - cancel_subscriptions - - comment_references - join_return_with_assignment - literal_only_boolean_expressions - missing_whitespace_between_adjacent_strings - no_adjacent_strings_in_list - no_runtimeType_toString - package_api_docs - - prefer_const_constructors - prefer_const_declarations - prefer_expression_function_bodies - prefer_final_locals - - prefer_relative_imports - - test_types_in_equals - unnecessary_await_in_return - unnecessary_breaks - use_if_null_to_convert_nulls_to_bools - use_raw_strings - use_string_buffers - - use_super_parameters diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index 951b82f7ab..d07e753264 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -14,5 +14,5 @@ dependencies: typed_data: ^1.3.0 dev_dependencies: - dart_flutter_team_lints: ^1.0.0 + dart_flutter_team_lints: ^2.0.0 test: ^1.16.0 From 7ebcbd4cfddcabc48cb0e7a3c47a83b02170245f Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Thu, 1 Feb 2024 11:55:30 -0800 Subject: [PATCH 110/126] Test dart2wasm (dart-lang/http_parser#85) --- pkgs/http_parser/.github/workflows/test-package.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkgs/http_parser/.github/workflows/test-package.yml b/pkgs/http_parser/.github/workflows/test-package.yml index 4d44890070..496ac9ea83 100644 --- a/pkgs/http_parser/.github/workflows/test-package.yml +++ b/pkgs/http_parser/.github/workflows/test-package.yml @@ -59,3 +59,7 @@ jobs: - name: Run Chrome tests run: dart test --platform chrome if: always() && steps.install.outcome == 'success' + - name: Run Chrome tests - wasm + run: dart test --platform chrome --compiler dart2wasm + # TODO: drop `dev` filter when dart2wasm is working on stable + if: always() && steps.install.outcome == 'success' && matrix.sdk == 'dev' From beb0e626627efe1d56f3ff391a5e6d79dc2d4c49 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Feb 2024 23:19:05 +0000 Subject: [PATCH 111/126] Bump dart-lang/setup-dart from 1.6.0 to 1.6.2 (dart-lang/http_parser#86) 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.0 to 1.6.2.
Release notes

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

v1.6.2

v1.6.1

  • Updated the google storage url for main channel releases.
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

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.0&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/http_parser/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http_parser/.github/workflows/test-package.yml b/pkgs/http_parser/.github/workflows/test-package.yml index 496ac9ea83..cf9201a8ff 100644 --- a/pkgs/http_parser/.github/workflows/test-package.yml +++ b/pkgs/http_parser/.github/workflows/test-package.yml @@ -24,7 +24,7 @@ jobs: sdk: [dev] steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - - uses: dart-lang/setup-dart@b64355ae6ca0b5d484f0106a033dd1388965d06d + - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 with: sdk: ${{ matrix.sdk }} - id: install @@ -47,7 +47,7 @@ jobs: sdk: [3.0.0, dev] steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - - uses: dart-lang/setup-dart@b64355ae6ca0b5d484f0106a033dd1388965d06d + - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 with: sdk: ${{ matrix.sdk }} - id: install From b0ec7c696e7366f40c4584ca7160c218c3aca4b9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Apr 2024 23:18:05 +0000 Subject: [PATCH 112/126] Bump actions/checkout from 4.1.1 to 4.1.2 (dart-lang/http_parser#87) 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/http_parser/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http_parser/.github/workflows/test-package.yml b/pkgs/http_parser/.github/workflows/test-package.yml index cf9201a8ff..87eda49464 100644 --- a/pkgs/http_parser/.github/workflows/test-package.yml +++ b/pkgs/http_parser/.github/workflows/test-package.yml @@ -23,7 +23,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 + - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 with: sdk: ${{ matrix.sdk }} @@ -46,7 +46,7 @@ jobs: os: [ubuntu-latest] sdk: [3.0.0, dev] steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 + - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 with: sdk: ${{ matrix.sdk }} From f8515e5e0bfa27ca2272f4c5020d58392cbe07f7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 23:26:12 +0000 Subject: [PATCH 113/126] Bump dart-lang/setup-dart from 1.6.2 to 1.6.4 (dart-lang/http_parser#88) 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/http_parser/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http_parser/.github/workflows/test-package.yml b/pkgs/http_parser/.github/workflows/test-package.yml index 87eda49464..a105022f84 100644 --- a/pkgs/http_parser/.github/workflows/test-package.yml +++ b/pkgs/http_parser/.github/workflows/test-package.yml @@ -24,7 +24,7 @@ jobs: sdk: [dev] steps: - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 - - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 + - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 with: sdk: ${{ matrix.sdk }} - id: install @@ -47,7 +47,7 @@ jobs: sdk: [3.0.0, dev] steps: - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 - - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 + - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 with: sdk: ${{ matrix.sdk }} - id: install From 062e4473f6ee29bab3cf49b664ab3a40f4c3cc0f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 May 2024 02:58:42 +0000 Subject: [PATCH 114/126] Bump actions/checkout from 4.1.2 to 4.1.4 (dart-lang/http_parser#89) 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/http_parser/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http_parser/.github/workflows/test-package.yml b/pkgs/http_parser/.github/workflows/test-package.yml index a105022f84..26403afcbe 100644 --- a/pkgs/http_parser/.github/workflows/test-package.yml +++ b/pkgs/http_parser/.github/workflows/test-package.yml @@ -23,7 +23,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 with: sdk: ${{ matrix.sdk }} @@ -46,7 +46,7 @@ jobs: os: [ubuntu-latest] sdk: [3.0.0, dev] steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 with: sdk: ${{ matrix.sdk }} From 3826b210c7325843170daaf7c51b2c40619e3d5c Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Mon, 6 May 2024 15:05:19 -0700 Subject: [PATCH 115/126] blast_repo fixes (dart-lang/http_parser#90) dependabot --- pkgs/http_parser/.github/dependabot.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkgs/http_parser/.github/dependabot.yaml b/pkgs/http_parser/.github/dependabot.yaml index 439e796b48..bf6b38a4d8 100644 --- a/pkgs/http_parser/.github/dependabot.yaml +++ b/pkgs/http_parser/.github/dependabot.yaml @@ -8,3 +8,7 @@ updates: interval: monthly labels: - autosubmit + groups: + github-actions: + patterns: + - "*" From 131903e22d41907dc67192819988f28750802426 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 22:07:40 +0000 Subject: [PATCH 116/126] Bump actions/checkout from 4.1.4 to 4.1.5 in the github-actions group (dart-lang/http_parser#91) 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/http_parser/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http_parser/.github/workflows/test-package.yml b/pkgs/http_parser/.github/workflows/test-package.yml index 26403afcbe..b320296193 100644 --- a/pkgs/http_parser/.github/workflows/test-package.yml +++ b/pkgs/http_parser/.github/workflows/test-package.yml @@ -23,7 +23,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 with: sdk: ${{ matrix.sdk }} @@ -46,7 +46,7 @@ jobs: os: [ubuntu-latest] sdk: [3.0.0, dev] steps: - - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 with: sdk: ${{ matrix.sdk }} From a63d064d6bbfe1badf608f013cb1f674abfe175d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 2 Jun 2024 17:17:49 +0000 Subject: [PATCH 117/126] Bump actions/checkout from 4.1.5 to 4.1.6 in the github-actions group (dart-lang/http_parser#93) 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/http_parser/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http_parser/.github/workflows/test-package.yml b/pkgs/http_parser/.github/workflows/test-package.yml index b320296193..57b6214921 100644 --- a/pkgs/http_parser/.github/workflows/test-package.yml +++ b/pkgs/http_parser/.github/workflows/test-package.yml @@ -23,7 +23,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 with: sdk: ${{ matrix.sdk }} @@ -46,7 +46,7 @@ jobs: os: [ubuntu-latest] sdk: [3.0.0, dev] steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 with: sdk: ${{ matrix.sdk }} From cb9f4a5509e86ff1b4b0fade9a1bda151af86df5 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 10 Jun 2024 08:55:46 -0700 Subject: [PATCH 118/126] Update min SDK, test wasm on 3.4 (dart-lang/http_parser#97) --- pkgs/http_parser/.github/workflows/test-package.yml | 5 ++--- pkgs/http_parser/CHANGELOG.md | 4 ++-- pkgs/http_parser/pubspec.yaml | 8 ++++---- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/pkgs/http_parser/.github/workflows/test-package.yml b/pkgs/http_parser/.github/workflows/test-package.yml index 57b6214921..b1e20f5db0 100644 --- a/pkgs/http_parser/.github/workflows/test-package.yml +++ b/pkgs/http_parser/.github/workflows/test-package.yml @@ -44,7 +44,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - sdk: [3.0.0, dev] + sdk: [3.4, dev] steps: - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 @@ -61,5 +61,4 @@ jobs: if: always() && steps.install.outcome == 'success' - name: Run Chrome tests - wasm run: dart test --platform chrome --compiler dart2wasm - # TODO: drop `dev` filter when dart2wasm is working on stable - if: always() && steps.install.outcome == 'success' && matrix.sdk == 'dev' + if: always() && steps.install.outcome == 'success' diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index 4f9e2a6611..07f4bf5e7b 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,6 +1,6 @@ -## 4.0.3-dev +## 4.0.3-wip -* Require Dart 3.0 +* Require Dart 3.4 ## 4.0.2 diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index d07e753264..a6d6452574 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,11 +1,11 @@ name: http_parser -version: 4.0.3-dev +version: 4.0.3-wip description: >- A platform-independent package for parsing and serializing HTTP formats. repository: https://github.com/dart-lang/http_parser environment: - sdk: ^3.0.0 + sdk: ^3.4.0 dependencies: collection: ^1.15.0 @@ -14,5 +14,5 @@ dependencies: typed_data: ^1.3.0 dev_dependencies: - dart_flutter_team_lints: ^2.0.0 - test: ^1.16.0 + dart_flutter_team_lints: ^3.0.0 + test: ^1.16.6 From d3b2a6976411efae533d663e4e204ea498d629d1 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 10 Jun 2024 09:38:52 -0700 Subject: [PATCH 119/126] blast_repo fixes (dart-lang/http_parser#98) auto-publish, github-actions, no-response --- .../.github/workflows/no-response.yml | 37 +++++++++++++++++++ .../.github/workflows/publish.yaml | 17 +++++++++ 2 files changed, 54 insertions(+) create mode 100644 pkgs/http_parser/.github/workflows/no-response.yml create mode 100644 pkgs/http_parser/.github/workflows/publish.yaml diff --git a/pkgs/http_parser/.github/workflows/no-response.yml b/pkgs/http_parser/.github/workflows/no-response.yml new file mode 100644 index 0000000000..ab1ac49842 --- /dev/null +++ b/pkgs/http_parser/.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/http_parser/.github/workflows/publish.yaml b/pkgs/http_parser/.github/workflows/publish.yaml new file mode 100644 index 0000000000..27157a046a --- /dev/null +++ b/pkgs/http_parser/.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 From 0fd17728db7ef30e7b633f35667592aef8271203 Mon Sep 17 00:00:00 2001 From: Graciliano Monteiro Passos Date: Thu, 13 Jun 2024 23:01:37 -0300 Subject: [PATCH 120/126] `CaseInsensitiveMap`: added constructor `fromEntries`. (dart-lang/http_parser#99) --- pkgs/http_parser/CHANGELOG.md | 4 ++++ pkgs/http_parser/lib/src/case_insensitive_map.dart | 14 ++++++++++++-- pkgs/http_parser/pubspec.yaml | 2 +- .../test/case_insensitive_map_test.dart | 6 ++++++ 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index 07f4bf5e7b..5be84528ea 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,7 +1,11 @@ ## 4.0.3-wip +* `CaseInsensitiveMap`: added constructor `fromEntries`. + * Require Dart 3.4 +* collection: ^1.19.0 + ## 4.0.2 * Remove `package:charcode` from dev_dependencies. diff --git a/pkgs/http_parser/lib/src/case_insensitive_map.dart b/pkgs/http_parser/lib/src/case_insensitive_map.dart index 88a190e8a0..ed344e6f24 100644 --- a/pkgs/http_parser/lib/src/case_insensitive_map.dart +++ b/pkgs/http_parser/lib/src/case_insensitive_map.dart @@ -8,8 +8,18 @@ import 'package:collection/collection.dart'; /// /// Much of HTTP is case-insensitive, so this is useful to have pre-defined. class CaseInsensitiveMap extends CanonicalizedMap { - CaseInsensitiveMap() : super((key) => key.toLowerCase()); + /// Creates an empty case-insensitive map. + CaseInsensitiveMap() : super(_canonicalizer); + /// Creates a case-insensitive map that is initialized with the key/value + /// pairs of [other]. CaseInsensitiveMap.from(Map other) - : super.from(other, (key) => key.toLowerCase()); + : super.from(other, _canonicalizer); + + /// Creates a case-insensitive map that is initialized with the key/value + /// pairs of [entries]. + CaseInsensitiveMap.fromEntries(Iterable> entries) + : super.fromEntries(entries, _canonicalizer); + + static String _canonicalizer(String key) => key.toLowerCase(); } diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index a6d6452574..ef5baead83 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -8,7 +8,7 @@ environment: sdk: ^3.4.0 dependencies: - collection: ^1.15.0 + collection: ^1.19.0 source_span: ^1.8.0 string_scanner: ^1.1.0 typed_data: ^1.3.0 diff --git a/pkgs/http_parser/test/case_insensitive_map_test.dart b/pkgs/http_parser/test/case_insensitive_map_test.dart index f62d4fec6d..7c65850228 100644 --- a/pkgs/http_parser/test/case_insensitive_map_test.dart +++ b/pkgs/http_parser/test/case_insensitive_map_test.dart @@ -26,4 +26,10 @@ void main() { expect(map, containsPair('FoO', 'bAr')); expect(map, equals({'fOo': 'bAr'})); }); + + test('.fromEntries() converts an existing map', () { + final map = CaseInsensitiveMap.fromEntries({'fOo': 'bAr'}.entries); + expect(map, containsPair('FoO', 'bAr')); + expect(map, equals({'fOo': 'bAr'})); + }); } From 73336b905b760eeab13589db724c830775912d3a Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 17 Jun 2024 11:57:49 -0700 Subject: [PATCH 121/126] Prepare release v4.1.0 (dart-lang/http_parser#100) * Also make private _State class an enum * minor bump with new features --- pkgs/http_parser/CHANGELOG.md | 8 ++---- pkgs/http_parser/analysis_options.yaml | 2 +- .../lib/src/chunked_coding/decoder.dart | 28 +++++++++---------- pkgs/http_parser/pubspec.yaml | 2 +- 4 files changed, 19 insertions(+), 21 deletions(-) diff --git a/pkgs/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index 5be84528ea..3670ab8144 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,10 +1,8 @@ -## 4.0.3-wip +## 4.1.0 * `CaseInsensitiveMap`: added constructor `fromEntries`. - -* Require Dart 3.4 - -* collection: ^1.19.0 +* Require `package:collection` `^1.19.0` +* Require Dart `^3.4.0` ## 4.0.2 diff --git a/pkgs/http_parser/analysis_options.yaml b/pkgs/http_parser/analysis_options.yaml index d16ad82510..c0bcfca91e 100644 --- a/pkgs/http_parser/analysis_options.yaml +++ b/pkgs/http_parser/analysis_options.yaml @@ -1,4 +1,4 @@ -# https://dart.dev/guides/language/analysis-options +# https://dart.dev/tools/analysis#the-analysis-options-file include: package:dart_flutter_team_lints/analysis_options.yaml analyzer: diff --git a/pkgs/http_parser/lib/src/chunked_coding/decoder.dart b/pkgs/http_parser/lib/src/chunked_coding/decoder.dart index 5388ae1f53..9eb8e93b4c 100644 --- a/pkgs/http_parser/lib/src/chunked_coding/decoder.dart +++ b/pkgs/http_parser/lib/src/chunked_coding/decoder.dart @@ -173,63 +173,63 @@ class _Sink extends ByteConversionSinkBase { /// An enumeration of states that [_Sink] can exist in when decoded a chunked /// message. -class _State { +enum _State { /// The parser has fully parsed one chunk and is expecting the header for the /// next chunk. /// /// Transitions to [size]. - static const boundary = _State._('boundary'); + boundary('boundary'), /// The parser has parsed at least one digit of the chunk size header, but has /// not yet parsed the `CR LF` sequence that indicates the end of that header. /// /// Transitions to [sizeBeforeLF]. - static const size = _State._('size'); + size('size'), /// The parser has parsed the chunk size header and the CR character after it, /// but not the LF. /// /// Transitions to [body] or [bodyBeforeCR]. - static const sizeBeforeLF = _State._('size before LF'); + sizeBeforeLF('size before LF'), /// The parser has parsed a chunk header and possibly some of the body, but /// still needs to consume more bytes. /// /// Transitions to [bodyBeforeCR]. - static const body = _State._('body'); + body('body'), // The parser has parsed all the bytes in a chunk body but not the CR LF // sequence that follows it. // // Transitions to [bodyBeforeLF]. - static const bodyBeforeCR = _State._('body before CR'); + bodyBeforeCR('body before CR'), // The parser has parsed all the bytes in a chunk body and the CR that follows // it, but not the LF after that. // - // Transitions to [bounday]. - static const bodyBeforeLF = _State._('body before LF'); + // Transitions to [boundary]. + bodyBeforeLF('body before LF'), /// The parser has parsed the final empty chunk but not the CR LF sequence /// that follows it. /// /// Transitions to [endBeforeLF]. - static const endBeforeCR = _State._('end before CR'); + endBeforeCR('end before CR'), /// The parser has parsed the final empty chunk and the CR that follows it, /// but not the LF after that. /// /// Transitions to [end]. - static const endBeforeLF = _State._('end before LF'); + endBeforeLF('end before LF'), /// The parser has parsed the final empty chunk as well as the CR LF that /// follows, and expects no more data. - static const end = _State._('end'); + end('end'); - final String _name; + const _State(this.name); - const _State._(this._name); + final String name; @override - String toString() => _name; + String toString() => name; } diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index ef5baead83..7bf5b8ba0c 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,5 +1,5 @@ name: http_parser -version: 4.0.3-wip +version: 4.1.0 description: >- A platform-independent package for parsing and serializing HTTP formats. repository: https://github.com/dart-lang/http_parser From 86b434a346afbfd1a54c6da6693b6feb4b7ff504 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 23:54:18 +0000 Subject: [PATCH 122/126] Bump the github-actions group with 2 updates (dart-lang/http_parser#101) 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/http_parser#118: dart-lang/setup-dartdart-lang/http_parser#118

Changelog

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

v1.6.5

dart-lang/http_parser#118: dart-lang/setup-dartdart-lang/http_parser#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/http_parser/.github/workflows/test-package.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkgs/http_parser/.github/workflows/test-package.yml b/pkgs/http_parser/.github/workflows/test-package.yml index b1e20f5db0..300e8a474b 100644 --- a/pkgs/http_parser/.github/workflows/test-package.yml +++ b/pkgs/http_parser/.github/workflows/test-package.yml @@ -23,8 +23,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 @@ -46,8 +46,8 @@ jobs: os: [ubuntu-latest] sdk: [3.4, 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 85d26513d5f24f59894f08b9889d7e4fee84aaa2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 4 Oct 2024 15:14:02 -0700 Subject: [PATCH 123/126] Bump actions/checkout from 4.1.7 to 4.2.0 in the github-actions group (dart-lang/http_parser#102) 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](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...d632683dd7b4114ad314bca15554477dd762a938) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pkgs/http_parser/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/http_parser/.github/workflows/test-package.yml b/pkgs/http_parser/.github/workflows/test-package.yml index 300e8a474b..5014c53186 100644 --- a/pkgs/http_parser/.github/workflows/test-package.yml +++ b/pkgs/http_parser/.github/workflows/test-package.yml @@ -23,7 +23,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672 with: sdk: ${{ matrix.sdk }} @@ -46,7 +46,7 @@ jobs: os: [ubuntu-latest] sdk: [3.4, dev] steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672 with: sdk: ${{ matrix.sdk }} From 37b7a1994632642529f6ad9aab3a0229465ad2cc Mon Sep 17 00:00:00 2001 From: Moritz Date: Thu, 17 Oct 2024 15:54:30 +0200 Subject: [PATCH 124/126] Add issue template and other fixes --- .github/ISSUE_TEMPLATE/http_parser.md | 5 +++++ pkgs/http_parser/pubspec.yaml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .github/ISSUE_TEMPLATE/http_parser.md diff --git a/.github/ISSUE_TEMPLATE/http_parser.md b/.github/ISSUE_TEMPLATE/http_parser.md new file mode 100644 index 0000000000..cf34cdc533 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/http_parser.md @@ -0,0 +1,5 @@ +--- +name: "package:http_parser" +about: "Create a bug or file a feature request against package:http_parser." +labels: "package:http_parser" +--- \ No newline at end of file diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index 7bf5b8ba0c..ec3b050ac8 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -2,7 +2,7 @@ name: http_parser version: 4.1.0 description: >- A platform-independent package for parsing and serializing HTTP formats. -repository: https://github.com/dart-lang/http_parser +repository: https://github.com/dart-lang/http/tree/master/pkgs/http_parser environment: sdk: ^3.4.0 From ec4cdb04318134033526432784d8a0d4461a1375 Mon Sep 17 00:00:00 2001 From: Moritz Date: Thu, 17 Oct 2024 15:58:26 +0200 Subject: [PATCH 125/126] Moving fixes --- .github/labeler.yml | 6 ++- .../workflows/http_parser.yaml | 15 ++++++-- README.md | 1 + pkgs/http_parser/.github/dependabot.yaml | 14 ------- .../.github/workflows/no-response.yml | 37 ------------------- .../.github/workflows/publish.yaml | 17 --------- pkgs/http_parser/CHANGELOG.md | 4 ++ pkgs/http_parser/README.md | 1 - pkgs/http_parser/pubspec.yaml | 2 +- 9 files changed, 22 insertions(+), 75 deletions(-) rename pkgs/http_parser/.github/workflows/test-package.yml => .github/workflows/http_parser.yaml (87%) delete mode 100644 pkgs/http_parser/.github/dependabot.yaml delete mode 100644 pkgs/http_parser/.github/workflows/no-response.yml delete mode 100644 pkgs/http_parser/.github/workflows/publish.yaml diff --git a/.github/labeler.yml b/.github/labeler.yml index 6d477b3173..b1143c6f1a 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -16,6 +16,10 @@ - changed-files: - any-glob-to-any-file: 'pkgs/http/**' +'package:http_parser': + - changed-files: + - any-glob-to-any-file: 'pkgs/http_parser/**' + '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/http_parser/.github/workflows/test-package.yml b/.github/workflows/http_parser.yaml similarity index 87% rename from pkgs/http_parser/.github/workflows/test-package.yml rename to .github/workflows/http_parser.yaml index 5014c53186..7865ed200a 100644 --- a/pkgs/http_parser/.github/workflows/test-package.yml +++ b/.github/workflows/http_parser.yaml @@ -1,16 +1,23 @@ -name: Dart CI +name: package:http_parser on: - # Run on PRs and pushes to the default branch. push: - branches: [ master ] + branches: + - main + - master + paths: + - '.github/workflows/http_parser.yaml' + - 'pkgs/http_parser/**' pull_request: - branches: [ master ] + paths: + - '.github/workflows/http_parser.yaml' + - 'pkgs/http_parser/**' schedule: - cron: "0 0 * * 0" env: PUB_ENVIRONMENT: bot.github + permissions: read-all jobs: diff --git a/README.md b/README.md index 14e6a7d6b0..a5e5ad44d0 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ and the browser. | [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) | | [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_parser](pkgs/http_parser/) | A platform-independent package for parsing and serializing HTTP formats. | [![pub package](https://img.shields.io/pub/v/http_parser.svg)](https://pub.dev/packages/http_parser) | | [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) | | [web_socket](pkgs/web_socket/) | Any easy-to-use library for communicating with WebSockets that has multiple implementations. | [![pub package](https://img.shields.io/pub/v/web_socket.svg)](https://pub.dev/packages/web_socket) | diff --git a/pkgs/http_parser/.github/dependabot.yaml b/pkgs/http_parser/.github/dependabot.yaml deleted file mode 100644 index bf6b38a4d8..0000000000 --- a/pkgs/http_parser/.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/http_parser/.github/workflows/no-response.yml b/pkgs/http_parser/.github/workflows/no-response.yml deleted file mode 100644 index ab1ac49842..0000000000 --- a/pkgs/http_parser/.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/http_parser/.github/workflows/publish.yaml b/pkgs/http_parser/.github/workflows/publish.yaml deleted file mode 100644 index 27157a046a..0000000000 --- a/pkgs/http_parser/.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/http_parser/CHANGELOG.md b/pkgs/http_parser/CHANGELOG.md index 3670ab8144..5c56cf7113 100644 --- a/pkgs/http_parser/CHANGELOG.md +++ b/pkgs/http_parser/CHANGELOG.md @@ -1,3 +1,7 @@ +## 4.1.1 + +* Move to `dart-lang/http` monorepo. + ## 4.1.0 * `CaseInsensitiveMap`: added constructor `fromEntries`. diff --git a/pkgs/http_parser/README.md b/pkgs/http_parser/README.md index 73f2f1cfe1..e9d330699b 100644 --- a/pkgs/http_parser/README.md +++ b/pkgs/http_parser/README.md @@ -1,4 +1,3 @@ -[![Build Status](https://github.com/dart-lang/http_parser/workflows/Dart%20CI/badge.svg)](https://github.com/dart-lang/http_parser/actions?query=workflow%3A"Dart+CI"+branch%3Amaster) [![Pub Package](https://img.shields.io/pub/v/http_parser.svg)](https://pub.dartlang.org/packages/http_parser) [![package publisher](https://img.shields.io/pub/publisher/http_parser.svg)](https://pub.dev/packages/http_parser/publisher) diff --git a/pkgs/http_parser/pubspec.yaml b/pkgs/http_parser/pubspec.yaml index ec3b050ac8..13888276f1 100644 --- a/pkgs/http_parser/pubspec.yaml +++ b/pkgs/http_parser/pubspec.yaml @@ -1,5 +1,5 @@ name: http_parser -version: 4.1.0 +version: 4.1.1 description: >- A platform-independent package for parsing and serializing HTTP formats. repository: https://github.com/dart-lang/http/tree/master/pkgs/http_parser From f1e57b411ca73c7666d4f06a52a54625cefd1942 Mon Sep 17 00:00:00 2001 From: Moritz Date: Fri, 18 Oct 2024 10:56:58 +0200 Subject: [PATCH 126/126] Fixes as per review --- .github/workflows/http_parser.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/http_parser.yaml b/.github/workflows/http_parser.yaml index 7865ed200a..b4dd271d31 100644 --- a/.github/workflows/http_parser.yaml +++ b/.github/workflows/http_parser.yaml @@ -3,7 +3,6 @@ name: package:http_parser on: push: branches: - - main - master paths: - '.github/workflows/http_parser.yaml' @@ -18,6 +17,10 @@ on: env: PUB_ENVIRONMENT: bot.github +defaults: + run: + working-directory: pkgs/http_parser/ + permissions: read-all jobs: