Skip to content

Commit cf14157

Browse files
authored
fix: Ensure we drain the request data stream completely, even if we realize it is too large (serverpod#3291)
1 parent 751efbb commit cf14157

File tree

7 files changed

+311
-147
lines changed

7 files changed

+311
-147
lines changed

packages/serverpod/lib/src/server/server.dart

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -428,16 +428,25 @@ class Server {
428428
}
429429

430430
Future<String> _readBody(HttpRequest request) async {
431-
var builder = BytesBuilder();
431+
var builder = BytesBuilder(copy: false);
432432
var len = 0;
433-
await for (var segment in request) {
434-
len += segment.length;
435-
if (len > serverpod.config.maxRequestSize) {
436-
throw _RequestTooLargeException(serverpod.config.maxRequestSize);
433+
var maxRequestSize = serverpod.config.maxRequestSize;
434+
var tooLargeForSure = request.contentLength > maxRequestSize;
435+
if (!tooLargeForSure) {
436+
await for (var segment in request) {
437+
if (tooLargeForSure) continue; // always drain request, if reading begun
438+
len += segment.length;
439+
tooLargeForSure = len > maxRequestSize;
440+
builder.add(segment);
437441
}
438-
builder.add(segment);
439442
}
440-
return const Utf8Decoder().convert(builder.toBytes());
443+
if (tooLargeForSure) {
444+
// We defer raising the exception until we have drained the request stream
445+
// This is a workaround for https://github.com/dart-lang/sdk/issues/60271
446+
// and fixes: https://github.com/serverpod/serverpod/issues/3213 for us.
447+
throw _RequestTooLargeException(maxRequestSize);
448+
}
449+
return const Utf8Decoder().convert(builder.takeBytes());
441450
}
442451

443452
Future<Result> _handleUriCall(

tests/serverpod_test_client/lib/src/protocol/client.dart

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3064,6 +3064,27 @@ class EndpointAuthenticatedTestTools extends _i1.EndpointRef {
30643064
);
30653065
}
30663066

3067+
/// {@category Endpoint}
3068+
class EndpointUpload extends _i1.EndpointRef {
3069+
EndpointUpload(_i1.EndpointCaller caller) : super(caller);
3070+
3071+
@override
3072+
String get name => 'upload';
3073+
3074+
_i2.Future<bool> uploadByteData(
3075+
String path,
3076+
_i4.ByteData data,
3077+
) =>
3078+
caller.callServerEndpoint<bool>(
3079+
'upload',
3080+
'uploadByteData',
3081+
{
3082+
'path': path,
3083+
'data': data,
3084+
},
3085+
);
3086+
}
3087+
30673088
/// {@category Endpoint}
30683089
class EndpointMyFeature extends _i1.EndpointRef {
30693090
EndpointMyFeature(_i1.EndpointCaller caller) : super(caller);
@@ -3167,6 +3188,7 @@ class Client extends _i1.ServerpodClientShared {
31673188
subDirTest = EndpointSubDirTest(this);
31683189
testTools = EndpointTestTools(this);
31693190
authenticatedTestTools = EndpointAuthenticatedTestTools(this);
3191+
upload = EndpointUpload(this);
31703192
myFeature = EndpointMyFeature(this);
31713193
modules = Modules(this);
31723194
}
@@ -3260,6 +3282,8 @@ class Client extends _i1.ServerpodClientShared {
32603282

32613283
late final EndpointAuthenticatedTestTools authenticatedTestTools;
32623284

3285+
late final EndpointUpload upload;
3286+
32633287
late final EndpointMyFeature myFeature;
32643288

32653289
late final Modules modules;
@@ -3310,6 +3334,7 @@ class Client extends _i1.ServerpodClientShared {
33103334
'subDirTest': subDirTest,
33113335
'testTools': testTools,
33123336
'authenticatedTestTools': authenticatedTestTools,
3337+
'upload': upload,
33133338
'myFeature': myFeature,
33143339
};
33153340

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import 'dart:typed_data';
2+
3+
import 'package:serverpod/serverpod.dart';
4+
5+
class UploadEndpoint extends Endpoint {
6+
Future<bool> uploadByteData(
7+
Session session,
8+
String path,
9+
ByteData data,
10+
) async {
11+
print('path: "$path", lengthInBytes: ${data.lengthInBytes}');
12+
return true;
13+
}
14+
}

0 commit comments

Comments
 (0)