platform/packages/http/lib/src/request_body.dart

106 lines
3.1 KiB
Dart
Raw Normal View History

2024-09-03 20:17:30 +00:00
import 'dart:async';
import 'dart:io';
import 'package:protevus_http/http.dart';
/// Objects that represent a request body, and can be decoded into Dart objects.
///
/// Every instance of [Request] has a [Request.body] property of this type. Use
/// [decode] to convert the contents of this object into a Dart type (e.g, [Map] or [List]).
///
/// See also [CodecRegistry] for how decoding occurs.
class RequestBody extends BodyDecoder {
/// Creates a new instance of this type.
///
/// Instances of this type decode [request]'s body based on its content-type.
///
/// See [CodecRegistry] for more information about how data is decoded.
///
/// Decoded data is cached the after it is decoded.
RequestBody(HttpRequest super.request)
: _request = request,
_originalByteStream = request;
/// The maximum size of a request body.
///
/// A request with a body larger than this size will be rejected. Value is in bytes. Defaults to 10MB (1024 * 1024 * 10).
static int maxSize = 1024 * 1024 * 10;
final HttpRequest _request;
bool get _hasContent =>
_hasContentLength || _request.headers.chunkedTransferEncoding;
bool get _hasContentLength => (_request.headers.contentLength) > 0;
@override
Stream<List<int>> get bytes {
// If content-length is specified, then we can check it for maxSize
// and just return the original stream.
if (_hasContentLength) {
if (_request.headers.contentLength > maxSize) {
throw Response(
HttpStatus.requestEntityTooLarge,
null,
{"error": "entity length exceeds maximum"},
);
}
return _originalByteStream;
}
// If content-length is not specified (e.g., chunked),
// then we need to check how many bytes we've read to ensure we haven't
// crossed maxSize
if (_bufferingController == null) {
_bufferingController = StreamController<List<int>>(sync: true);
_originalByteStream.listen(
(chunk) {
_bytesRead += chunk.length;
if (_bytesRead > maxSize) {
_bufferingController!.addError(
Response(
HttpStatus.requestEntityTooLarge,
null,
{"error": "entity length exceeds maximum"},
),
);
_bufferingController!.close();
return;
}
_bufferingController!.add(chunk);
},
onDone: () {
_bufferingController!.close();
},
onError: (Object e, StackTrace st) {
if (!_bufferingController!.isClosed) {
_bufferingController!.addError(e, st);
_bufferingController!.close();
}
},
cancelOnError: true,
);
}
return _bufferingController!.stream;
}
@override
ContentType? get contentType => _request.headers.contentType;
@override
bool get isEmpty => !_hasContent;
bool get isFormData =>
contentType != null &&
contentType!.primaryType == "application" &&
contentType!.subType == "x-www-form-urlencoded";
final Stream<List<int>> _originalByteStream;
StreamController<List<int>>? _bufferingController;
int _bytesRead = 0;
}