2016-09-24 18:30:01 +00:00
|
|
|
import 'dart:async';
|
|
|
|
import 'dart:io';
|
2019-10-09 18:59:52 +00:00
|
|
|
import 'dart:typed_data';
|
2018-08-11 02:08:44 +00:00
|
|
|
|
|
|
|
import 'package:dart2_constant/convert.dart';
|
2018-03-04 22:32:14 +00:00
|
|
|
import 'package:http_parser/http_parser.dart';
|
2016-09-24 18:30:01 +00:00
|
|
|
import 'package:http_server/http_server.dart';
|
|
|
|
import 'package:mime/mime.dart';
|
2018-08-11 02:08:44 +00:00
|
|
|
|
2016-09-24 18:30:01 +00:00
|
|
|
import 'body_parse_result.dart';
|
|
|
|
import 'file_upload_info.dart';
|
|
|
|
import 'map_from_uri.dart';
|
|
|
|
|
2018-03-04 22:32:14 +00:00
|
|
|
/// Forwards to [parseBodyFromStream].
|
|
|
|
@deprecated
|
|
|
|
Future<BodyParseResult> parseBody(HttpRequest request,
|
|
|
|
{bool storeOriginalBuffer: false}) {
|
|
|
|
return parseBodyFromStream(
|
|
|
|
request,
|
|
|
|
request.headers.contentType != null
|
|
|
|
? new MediaType.parse(request.headers.contentType.toString())
|
|
|
|
: null,
|
|
|
|
request.uri,
|
|
|
|
storeOriginalBuffer: storeOriginalBuffer);
|
|
|
|
}
|
|
|
|
|
2016-09-24 18:30:01 +00:00
|
|
|
/// Grabs data from an incoming request.
|
|
|
|
///
|
|
|
|
/// Supports URL-encoded and JSON, as well as multipart/* forms.
|
|
|
|
/// On a file upload request, only fields with the name **'file'** are processed
|
|
|
|
/// as files. Anything else is put in the body. You can change the upload file name
|
|
|
|
/// via the *fileUploadName* parameter. :)
|
2017-01-14 13:50:02 +00:00
|
|
|
///
|
|
|
|
/// Use [storeOriginalBuffer] to add the original request bytes to the result.
|
2018-03-04 22:32:14 +00:00
|
|
|
Future<BodyParseResult> parseBodyFromStream(
|
2019-10-09 18:59:52 +00:00
|
|
|
Stream<Uint8List> data, MediaType contentType, Uri requestUri,
|
2017-01-14 13:50:02 +00:00
|
|
|
{bool storeOriginalBuffer: false}) async {
|
|
|
|
var result = new _BodyParseResultImpl();
|
|
|
|
|
2019-10-09 18:59:52 +00:00
|
|
|
Future<Uint8List> getBytes() {
|
2018-03-04 22:32:14 +00:00
|
|
|
return data
|
2017-10-12 02:32:42 +00:00
|
|
|
.fold<BytesBuilder>(new BytesBuilder(copy: false), (a, b) => a..add(b))
|
|
|
|
.then((b) => b.takeBytes());
|
2017-01-14 13:50:02 +00:00
|
|
|
}
|
|
|
|
|
2017-10-12 02:32:42 +00:00
|
|
|
Future<String> getBody() {
|
2017-01-14 13:50:02 +00:00
|
|
|
if (storeOriginalBuffer) {
|
2017-10-12 02:32:42 +00:00
|
|
|
return getBytes().then((bytes) {
|
|
|
|
result.originalBuffer = bytes;
|
2018-08-11 02:08:44 +00:00
|
|
|
return utf8.decode(bytes);
|
2017-10-12 02:32:42 +00:00
|
|
|
});
|
2019-10-09 18:59:52 +00:00
|
|
|
} else {
|
|
|
|
return utf8.decoder.bind(data).join();
|
|
|
|
}
|
2017-01-14 13:50:02 +00:00
|
|
|
}
|
2016-09-24 18:30:01 +00:00
|
|
|
|
2016-12-05 02:31:25 +00:00
|
|
|
try {
|
2018-03-04 22:32:14 +00:00
|
|
|
if (contentType != null) {
|
|
|
|
if (contentType.type == 'multipart' &&
|
|
|
|
contentType.parameters.containsKey('boundary')) {
|
2019-10-09 18:59:52 +00:00
|
|
|
Stream<Uint8List> stream;
|
2017-01-14 13:50:02 +00:00
|
|
|
|
|
|
|
if (storeOriginalBuffer) {
|
|
|
|
var bytes = result.originalBuffer = await getBytes();
|
2019-10-09 18:59:52 +00:00
|
|
|
var ctrl = new StreamController<Uint8List>()
|
2017-01-14 13:50:02 +00:00
|
|
|
..add(bytes)
|
|
|
|
..close();
|
|
|
|
stream = ctrl.stream;
|
|
|
|
} else {
|
2018-03-04 22:32:14 +00:00
|
|
|
stream = data;
|
2017-01-14 13:50:02 +00:00
|
|
|
}
|
|
|
|
|
2019-10-09 18:59:52 +00:00
|
|
|
var parts = MimeMultipartTransformer(
|
|
|
|
contentType.parameters['boundary']).bind(stream)
|
2016-12-05 02:31:25 +00:00
|
|
|
.map((part) =>
|
2018-08-11 02:08:44 +00:00
|
|
|
HttpMultipartFormData.parse(part, defaultEncoding: utf8));
|
2016-09-24 18:30:01 +00:00
|
|
|
|
2016-12-05 02:31:25 +00:00
|
|
|
await for (HttpMultipartFormData part in parts) {
|
|
|
|
if (part.isBinary ||
|
|
|
|
part.contentDisposition.parameters.containsKey("filename")) {
|
2017-10-12 02:32:42 +00:00
|
|
|
BytesBuilder builder = await part.fold(
|
|
|
|
new BytesBuilder(copy: false),
|
2018-08-11 02:08:44 +00:00
|
|
|
(BytesBuilder b, d) => b
|
|
|
|
..add(d is! String
|
|
|
|
? (d as List<int>)
|
|
|
|
: (d as String).codeUnits));
|
2016-12-05 02:31:25 +00:00
|
|
|
var upload = new FileUploadInfo(
|
|
|
|
mimeType: part.contentType.mimeType,
|
|
|
|
name: part.contentDisposition.parameters['name'],
|
|
|
|
filename:
|
|
|
|
part.contentDisposition.parameters['filename'] ?? 'file',
|
|
|
|
data: builder.takeBytes());
|
|
|
|
result.files.add(upload);
|
|
|
|
} else if (part.isText) {
|
|
|
|
var text = await part.join();
|
|
|
|
buildMapFromUri(result.body,
|
|
|
|
'${part.contentDisposition.parameters["name"]}=$text');
|
|
|
|
}
|
2016-09-24 18:30:01 +00:00
|
|
|
}
|
2018-08-11 02:08:44 +00:00
|
|
|
} else if (contentType.mimeType == 'application/json') {
|
|
|
|
result.body
|
|
|
|
.addAll(_foldToStringDynamic(json.decode(await getBody()) as Map));
|
2018-03-04 22:32:14 +00:00
|
|
|
} else if (contentType.mimeType == 'application/x-www-form-urlencoded') {
|
2017-01-14 13:50:02 +00:00
|
|
|
String body = await getBody();
|
2016-12-05 02:31:25 +00:00
|
|
|
buildMapFromUri(result.body, body);
|
2017-07-19 21:20:57 +00:00
|
|
|
} else if (storeOriginalBuffer == true) {
|
|
|
|
result.originalBuffer = await getBytes();
|
|
|
|
}
|
|
|
|
} else {
|
2018-03-04 22:32:14 +00:00
|
|
|
if (requestUri.hasQuery) {
|
|
|
|
buildMapFromUri(result.query, requestUri.query);
|
2017-07-19 21:20:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (storeOriginalBuffer == true) {
|
|
|
|
result.originalBuffer = await getBytes();
|
2016-09-24 18:30:01 +00:00
|
|
|
}
|
|
|
|
}
|
2017-07-19 21:20:57 +00:00
|
|
|
} catch (e, st) {
|
|
|
|
result.error = e;
|
|
|
|
result.stack = st;
|
2016-09-24 18:30:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
2017-01-14 13:50:02 +00:00
|
|
|
|
|
|
|
class _BodyParseResultImpl implements BodyParseResult {
|
|
|
|
@override
|
|
|
|
Map<String, dynamic> body = {};
|
|
|
|
|
|
|
|
@override
|
|
|
|
List<FileUploadInfo> files = [];
|
|
|
|
|
|
|
|
@override
|
|
|
|
List<int> originalBuffer;
|
|
|
|
|
|
|
|
@override
|
|
|
|
Map<String, dynamic> query = {};
|
2017-07-19 21:20:57 +00:00
|
|
|
|
|
|
|
@override
|
|
|
|
var error = null;
|
|
|
|
|
|
|
|
@override
|
|
|
|
StackTrace stack = null;
|
2017-01-14 13:50:02 +00:00
|
|
|
}
|
2018-08-11 02:08:44 +00:00
|
|
|
|
|
|
|
Map<String, dynamic> _foldToStringDynamic(Map map) {
|
|
|
|
return map == null
|
|
|
|
? null
|
|
|
|
: map.keys.fold<Map<String, dynamic>>(
|
|
|
|
<String, dynamic>{}, (out, k) => out..[k.toString()] = map[k]);
|
|
|
|
}
|