parseBodyFromStream function

Future<BodyParseResult> parseBodyFromStream(
  1. Stream<Uint8List> data,
  2. MediaType? contentType,
  3. Uri requestUri, {
  4. bool storeOriginalBuffer = false,
})

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.

Implementation

Future<BodyParseResult> parseBodyFromStream(
    Stream<Uint8List> data, MediaType? contentType, Uri requestUri,
    {bool storeOriginalBuffer = false}) async {
  var result = _BodyParseResultImpl();

  Future<Uint8List> getBytes() {
    return data
        .fold<BytesBuilder>(BytesBuilder(copy: false), (a, b) => a..add(b))
        .then((b) => b.takeBytes());
  }

  Future<String> 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<Uint8List> stream;

        if (storeOriginalBuffer) {
          var bytes = result.originalBuffer = await getBytes();
          var ctrl = StreamController<Uint8List>()..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<int>?)! : 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;
}