diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e4ebd8ffe..8545e484a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +## 0.11.3+13 + +* remove boundary characters that package:http_parser cannot parse. + +## 0.11.3+12 + +* Don't quote the boundary header for `MultipartRequest`. This is more + compatible with server quirks. + +## 0.11.3+11 + +* Fix the SDK constraint to only include SDK versions that support importing + `dart:io` everywhere. + ## 0.11.3+10 * Stop using `dart:mirrors`. diff --git a/lib/src/boundary_characters.dart b/lib/src/boundary_characters.dart new file mode 100644 index 0000000000..03b7ac2d5e --- /dev/null +++ b/lib/src/boundary_characters.dart @@ -0,0 +1,18 @@ +// 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. + +/// All character codes that are valid in multipart boundaries. This is the +/// intersection of the characters allowed in the `bcharsnospace` production +/// defined in [RFC 2046][] and those allowed in the `token` production +/// defined in [RFC 1521][]. +/// +/// [RFC 2046]: http://tools.ietf.org/html/rfc2046#section-5.1.1. +/// [RFC 1521]: https://tools.ietf.org/html/rfc1521#section-4 +const List BOUNDARY_CHARACTERS = const [ + 39, 43, 95, 45, 46, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122 +]; diff --git a/lib/src/multipart_request.dart b/lib/src/multipart_request.dart index 2d3b318c9d..8132f80924 100644 --- a/lib/src/multipart_request.dart +++ b/lib/src/multipart_request.dart @@ -7,6 +7,7 @@ import 'dart:convert'; import 'dart:math'; import 'base_request.dart'; +import 'boundary_characters.dart'; import 'byte_stream.dart'; import 'multipart_file.dart'; import 'utils.dart'; @@ -83,7 +84,7 @@ class MultipartRequest extends BaseRequest { ByteStream finalize() { // TODO(nweiz): freeze fields and files var boundary = _boundaryString(); - headers['content-type'] = 'multipart/form-data; boundary="$boundary"'; + headers['content-type'] = 'multipart/form-data; boundary=$boundary'; super.finalize(); var controller = new StreamController>(sync: true); @@ -117,16 +118,6 @@ class MultipartRequest extends BaseRequest { return new ByteStream(controller.stream); } - /// All character codes that are valid in multipart boundaries. From - /// http://tools.ietf.org/html/rfc2046#section-5.1.1. - static const List _BOUNDARY_CHARACTERS = const [ - 39, 40, 41, 43, 95, 44, 45, 46, 47, 58, 61, 63, 48, 49, 50, 51, 52, 53, 54, - 55, 56, 57, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, - 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, - 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, - 119, 120, 121, 122 - ]; - /// Returns the header string for a field. The return value is guaranteed to /// contain only ASCII characters. String _headerForField(String name, String value) { @@ -167,7 +158,7 @@ class MultipartRequest extends BaseRequest { var prefix = "dart-http-boundary-"; var list = new List.generate(_BOUNDARY_LENGTH - prefix.length, (index) => - _BOUNDARY_CHARACTERS[_random.nextInt(_BOUNDARY_CHARACTERS.length)], + BOUNDARY_CHARACTERS[_random.nextInt(BOUNDARY_CHARACTERS.length)], growable: false); return "$prefix${new String.fromCharCodes(list)}"; } diff --git a/lib/src/utils.dart b/lib/src/utils.dart index c19d354b9c..3e9fdf39bb 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -95,7 +95,7 @@ Uint8List toUint8List(List input) { /// Calls [onDone] once [stream] (a single-subscription [Stream]) is finished. /// The return value, also a single-subscription [Stream] should be used in /// place of [stream] after calling this method. -Stream/**/ onDone/**/(Stream/**/ stream, void onDone()) => +Stream onDone(Stream stream, void onDone()) => stream.transform(new StreamTransformer.fromHandlers(handleDone: (sink) { sink.close(); onDone();