From 9f020d6104d5a30a0f33fa8cf0377fafa511bd6c Mon Sep 17 00:00:00 2001 From: Yegor Date: Fri, 10 Mar 2017 11:05:06 -0800 Subject: [PATCH] upgrade package:http so we no longer need custom MultipartRequest (#8715) --- dev/tools/pubspec.yaml | 2 +- examples/stocks/pubspec.yaml | 2 +- packages/flutter/pubspec.yaml | 2 +- .../lib/src/crash_reporting.dart | 257 +----------------- packages/flutter_tools/pubspec.yaml | 2 +- 5 files changed, 5 insertions(+), 260 deletions(-) diff --git a/dev/tools/pubspec.yaml b/dev/tools/pubspec.yaml index 8d29d2184d..41d0f91e0e 100644 --- a/dev/tools/pubspec.yaml +++ b/dev/tools/pubspec.yaml @@ -4,6 +4,6 @@ description: Various repository development tools for flutter. dependencies: archive: ^1.0.20 args: ^0.13.4 - http: ^0.11.3 + http: ^0.11.3+12 intl: ^0.14.0 path: ^1.4.0 diff --git a/examples/stocks/pubspec.yaml b/examples/stocks/pubspec.yaml index f15f87d457..112c94df61 100644 --- a/examples/stocks/pubspec.yaml +++ b/examples/stocks/pubspec.yaml @@ -3,7 +3,7 @@ dependencies: flutter: sdk: flutter intl: '>=0.14.0 <0.15.0' - http: '>=0.11.3+11' + http: '>=0.11.3+12' dev_dependencies: flutter_test: diff --git a/packages/flutter/pubspec.yaml b/packages/flutter/pubspec.yaml index 31f124d025..d17efa656d 100644 --- a/packages/flutter/pubspec.yaml +++ b/packages/flutter/pubspec.yaml @@ -6,7 +6,7 @@ homepage: http://flutter.io dependencies: collection: '>=1.9.1 <2.0.0' - http: '>=0.11.3+11' + http: '>=0.11.3+12' intl: '>=0.14.0 <0.15.0' meta: ^1.0.4 typed_data: ^1.1.3 diff --git a/packages/flutter_tools/lib/src/crash_reporting.dart b/packages/flutter_tools/lib/src/crash_reporting.dart index b7e9bb6064..27edeadb1d 100644 --- a/packages/flutter_tools/lib/src/crash_reporting.dart +++ b/packages/flutter_tools/lib/src/crash_reporting.dart @@ -3,8 +3,6 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:convert'; -import 'dart:math'; import 'package:http/http.dart' as http; import 'package:meta/meta.dart'; @@ -94,7 +92,7 @@ class CrashReportSender { }, ); - final _MultipartRequest req = new _MultipartRequest('POST', uri); + final http.MultipartRequest req = new http.MultipartRequest('POST', uri); req.fields['product'] = _kProductId; req.fields['version'] = flutterVersion; req.fields['type'] = _kDartTypeId; @@ -141,256 +139,3 @@ void enterTestingMode() { void exitTestingMode() { _testing = false; } - - -// Below is a patched version of the MultipartRequest class from package:http -// made to conform to Flutter's style guide and to comply with the crash -// reporting backend. Specifically, the backend does not correctly handle quoted -// boundary values. The implementation below: -// - reduces boundary character set to those that do not need quotes -// - compensates for the smaller set by generating a longer boundary value - -final RegExp _newlineRegExp = new RegExp(r"\r\n|\r|\n"); - -/// A `multipart/form-data` request. Such a request has both string [fields], -/// which function as normal form fields, and (potentially streamed) binary -/// [files]. -/// -/// This request automatically sets the Content-Type header to -/// `multipart/form-data`. This value will override any value set by the user. -/// -/// var uri = Uri.parse("http://pub.dartlang.org/packages/create"); -/// var request = new http.MultipartRequest("POST", url); -/// request.fields['user'] = 'nweiz@google.com'; -/// request.files.add(new http.MultipartFile.fromFile( -/// 'package', -/// new File('build/package.tar.gz'), -/// contentType: new MediaType('application', 'x-tar')); -/// request.send().then((response) { -/// if (response.statusCode == 200) print("Uploaded!"); -/// }); -class _MultipartRequest extends http.BaseRequest { - /// The total length of the multipart boundaries used when building the - /// request body. According to http://tools.ietf.org/html/rfc1341.html, this - /// can't be longer than 70. - static const int _BOUNDARY_LENGTH = 70; - - static final Random _random = new Random(); - - /// The form fields to send for this request. - final Map fields; - - /// The private version of [files]. - final List _files; - - /// Creates a new [MultipartRequest]. - _MultipartRequest(String method, Uri url) - : fields = {}, - _files = [], - super(method, url); - - /// The list of files to upload for this request. - List get files => _files; - - /// The total length of the request body, in bytes. This is calculated from - /// [fields] and [files] and cannot be set manually. - @override - int get contentLength { - int length = 0; - - fields.forEach((String name, String value) { - length += "--".length + _BOUNDARY_LENGTH + "\r\n".length + - UTF8.encode(_headerForField(name, value)).length + - UTF8.encode(value).length + "\r\n".length; - }); - - for (http.MultipartFile file in _files) { - length += "--".length + _BOUNDARY_LENGTH + "\r\n".length + - UTF8.encode(_headerForFile(file)).length + - file.length + "\r\n".length; - } - - return length + "--".length + _BOUNDARY_LENGTH + "--\r\n".length; - } - - @override - set contentLength(int value) { - throw new UnsupportedError("Cannot set the contentLength property of " - "multipart requests."); - } - - /// Freezes all mutable fields and returns a single-subscription [ByteStream] - /// that will emit the request body. - @override - http.ByteStream finalize() { - final String boundary = _boundaryString(); - headers['content-type'] = 'multipart/form-data; boundary=$boundary'; - super.finalize(); - - final StreamController> controller = new StreamController>(sync: true); - - void writeAscii(String string) { - controller.add(UTF8.encode(string)); - } - - void writeUtf8(String string) { - controller.add(UTF8.encode(string)); - } - void writeLine() { - controller.add([13, 10]); // \r\n - } - - fields.forEach((String name, String value) { - writeAscii('--$boundary\r\n'); - writeAscii(_headerForField(name, value)); - writeUtf8(value); - writeLine(); - }); - - Future.forEach(_files, (http.MultipartFile file) { - writeAscii('--$boundary\r\n'); - writeAscii(_headerForFile(file)); - return writeStreamToSink(file.finalize(), controller) - .then((dynamic _) => writeLine()); - }).then((dynamic _) { - writeAscii('--$boundary--\r\n'); - controller.close(); - }); - - return new http.ByteStream(controller.stream); - } - - /// Valid boundary character codes that do not need to be quoted. From - /// http://tools.ietf.org/html/rfc2046#section-5.1.1. - static const List _BOUNDARY_CHARACTERS = const [ - // Digits - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - // Capital letters - 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, - // Small letters - 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) { - String header = - 'content-disposition: form-data; name="${_browserEncode(name)}"'; - if (!isPlainAscii(value)) { - header = '$header\r\n' - 'content-type: text/plain; charset=utf-8\r\n' - 'content-transfer-encoding: binary'; - } - return '$header\r\n\r\n'; - } - - /// Returns the header string for a file. The return value is guaranteed to - /// contain only ASCII characters. - String _headerForFile(http.MultipartFile file) { - String header = 'content-type: ${file.contentType}\r\n' - 'content-disposition: form-data; name="${_browserEncode(file.field)}"'; - - if (file.filename != null) { - header = '$header; filename="${_browserEncode(file.filename)}"'; - } - return '$header\r\n\r\n'; - } - - /// Encode [value] in the same way browsers do. - String _browserEncode(String value) { - // http://tools.ietf.org/html/rfc2388 mandates some complex encodings for - // field names and file names, but in practice user agents seem not to - // follow this at all. Instead, they URL-encode `\r`, `\n`, and `\r\n` as - // `\r\n`; URL-encode `"`; and do nothing else (even for `%` or non-ASCII - // characters). We follow their behavior. - return value.replaceAll(_newlineRegExp, "%0D%0A").replaceAll('"', "%22"); - } - - /// Returns a randomly-generated multipart boundary string - String _boundaryString() { - final String prefix = "dart-"; - final List list = new List.generate(_BOUNDARY_LENGTH - prefix.length, - (int index) => - _BOUNDARY_CHARACTERS[_random.nextInt(_BOUNDARY_CHARACTERS.length)], - growable: false); - return "$prefix${new String.fromCharCodes(list)}"; - } -} - -/// Pipes all data and errors from [stream] into [sink]. Completes [Future] once -/// [stream] is done. Unlike [store], [sink] remains open after [stream] is -/// done. -Future writeStreamToSink(Stream stream, EventSink sink) { - final Completer completer = new Completer(); - stream.listen(sink.add, - onError: sink.addError, - onDone: () => completer.complete()); - return completer.future; -} - -/// A regular expression that matches strings that are composed entirely of -/// ASCII-compatible characters. -final RegExp _kAsciiOnly = new RegExp(r"^[\x00-\x7F]+$"); - -/// Returns whether [string] is composed entirely of ASCII-compatible -/// characters. -bool isPlainAscii(String string) => _kAsciiOnly.hasMatch(string); diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 4c66ffe08b..7d333845ad 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -13,7 +13,7 @@ dependencies: coverage: ^0.8.0 crypto: '>=1.1.1 <3.0.0' file: 2.3.0 - http: ^0.11.3 + http: ^0.11.3+12 intl: '>=0.14.0 <0.15.0' json_rpc_2: ^2.0.0 json_schema: 1.0.6