import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; import 'package:http_parser/http_parser.dart'; import 'package:belatuk_http_server/belatuk_http_server.dart'; import 'package:mime/mime.dart'; import 'body_parse_result.dart'; import 'file_upload_info.dart'; import 'map_from_uri.dart'; /// Forwards to [parseBodyFromStream]. @Deprecated("parseBodyFromStream") Future parseBody(HttpRequest request, {bool storeOriginalBuffer = false}) { return parseBodyFromStream( request, request.headers.contentType != null ? MediaType.parse(request.headers.contentType.toString()) : null, request.uri, storeOriginalBuffer: storeOriginalBuffer); } /// 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. :) /// /// Use [storeOriginalBuffer] to add the original request bytes to the result. Future parseBodyFromStream( Stream data, MediaType? contentType, Uri requestUri, {bool storeOriginalBuffer = false}) async { var result = _BodyParseResultImpl(); Future getBytes() { return data .fold(BytesBuilder(copy: false), (a, b) => a..add(b)) .then((b) => b.takeBytes()); } Future getBody() { if (storeOriginalBuffer) { return getBytes().then((bytes) { result.originalBuffer = bytes; return utf8.decode(bytes); }); } else { return utf8.decoder.bind(data).join(); } } try { if (contentType != null) { if (contentType.type == 'multipart' && contentType.parameters.containsKey('boundary')) { Stream stream; if (storeOriginalBuffer) { var bytes = result.originalBuffer = await getBytes(); var ctrl = StreamController()..add(bytes); await ctrl.close(); stream = ctrl.stream; } else { stream = data; } var parts = MimeMultipartTransformer(contentType.parameters['boundary']!) .bind(stream) .map((part) => HttpMultipartFormData.parse(part, defaultEncoding: utf8)); await for (HttpMultipartFormData part in parts) { if (part.isBinary || part.contentDisposition.parameters.containsKey('filename')) { var builder = await part.fold( BytesBuilder(copy: false), (BytesBuilder b, d) => b..add(d is! String ? (d as List?)! : d.codeUnits)); var upload = 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'); } } } else if (contentType.mimeType == 'application/json') { result.body.addAll( _foldToStringDynamic(json.decode(await getBody()) as Map?)!); } else if (contentType.mimeType == 'application/x-www-form-urlencoded') { var body = await getBody(); buildMapFromUri(result.body, body); } else if (storeOriginalBuffer == true) { result.originalBuffer = await getBytes(); } } else { if (requestUri.hasQuery) { buildMapFromUri(result.query, requestUri.query); } if (storeOriginalBuffer == true) { result.originalBuffer = await getBytes(); } } } catch (e, st) { result.error = e; result.stack = st; } return result; } class _BodyParseResultImpl implements BodyParseResult { @override Map body = {}; @override List files = []; @override List? originalBuffer; @override Map query = {}; @override dynamic error; @override StackTrace? stack; } Map? _foldToStringDynamic(Map? map) { return map?.keys.fold>( {}, (out, k) => out..[k.toString()] = map[k]); }