Commit 9f020d61 authored by Yegor's avatar Yegor Committed by GitHub

upgrade package:http so we no longer need custom MultipartRequest (#8715)

parent 03d31865
......@@ -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
......@@ -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:
......
......@@ -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
......
......@@ -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<String, String> fields;
/// The private version of [files].
final List<http.MultipartFile> _files;
/// Creates a new [MultipartRequest].
_MultipartRequest(String method, Uri url)
: fields = <String, String>{},
_files = <http.MultipartFile>[],
super(method, url);
/// The list of files to upload for this request.
List<http.MultipartFile> 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<List<int>> controller = new StreamController<List<int>>(sync: true);
void writeAscii(String string) {
controller.add(UTF8.encode(string));
}
void writeUtf8(String string) {
controller.add(UTF8.encode(string));
}
void writeLine() {
controller.add(<int>[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<Null>((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<int> _BOUNDARY_CHARACTERS = const <int>[
// 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<int> list = new List<int>.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<Null> writeStreamToSink<O, I extends O>(Stream<I> stream, EventSink<O> sink) {
final Completer<Null> completer = new Completer<Null>();
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);
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment