From 6185c01838f6d5794fcaf209fdb1915d619c5536 Mon Sep 17 00:00:00 2001 From: feiyun0112 Date: Tue, 6 May 2025 21:17:30 +0800 Subject: [PATCH 1/3] 413 status when size limit --- .../src/RequestDecompressionMiddleware.cs | 4 +++- .../RequestDecompressionMiddlewareTests.cs | 8 +++---- src/Shared/SizeLimitedStream.cs | 23 +++++++++++++++---- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/Middleware/RequestDecompression/src/RequestDecompressionMiddleware.cs b/src/Middleware/RequestDecompression/src/RequestDecompressionMiddleware.cs index 71cd050492ce..c43f9b06486b 100644 --- a/src/Middleware/RequestDecompression/src/RequestDecompressionMiddleware.cs +++ b/src/Middleware/RequestDecompression/src/RequestDecompressionMiddleware.cs @@ -62,7 +62,9 @@ private async Task InvokeCore(HttpContext context, Stream decompressionStream) context.GetEndpoint()?.Metadata?.GetMetadata()?.MaxRequestBodySize ?? context.Features.Get()?.MaxRequestBodySize; - context.Request.Body = new SizeLimitedStream(decompressionStream, sizeLimit); + context.Request.Body = new SizeLimitedStream(decompressionStream, sizeLimit, (long totalBytesRead, long sizeLimit) => throw new BadHttpRequestException( + $"The request's Content-Length {totalBytesRead} is larger than the request body size limit {sizeLimit}.", + StatusCodes.Status413PayloadTooLarge)); await _next(context); } finally diff --git a/src/Middleware/RequestDecompression/test/RequestDecompressionMiddlewareTests.cs b/src/Middleware/RequestDecompression/test/RequestDecompressionMiddlewareTests.cs index 0a612fca8531..54e0c685adc7 100644 --- a/src/Middleware/RequestDecompression/test/RequestDecompressionMiddlewareTests.cs +++ b/src/Middleware/RequestDecompression/test/RequestDecompressionMiddlewareTests.cs @@ -499,8 +499,8 @@ public async Task Endpoint_HasRequestSizeLimit_UsedForRequest(bool exceedsLimit) if (exceedsLimit) { Assert.NotNull(exception); - Assert.IsAssignableFrom(exception); - Assert.Equal("The maximum number of bytes have been read.", exception.Message); + Assert.IsAssignableFrom(exception); + Assert.Equal(StatusCodes.Status413PayloadTooLarge, ((BadHttpRequestException)exception).StatusCode); } else { @@ -583,8 +583,8 @@ public async Task Feature_HasRequestSizeLimit_UsedForRequest(bool exceedsLimit) if (exceedsLimit) { Assert.NotNull(exception); - Assert.IsAssignableFrom(exception); - Assert.Equal("The maximum number of bytes have been read.", exception.Message); + Assert.IsAssignableFrom(exception); + Assert.Equal(StatusCodes.Status413PayloadTooLarge, ((BadHttpRequestException)exception).StatusCode); } else { diff --git a/src/Shared/SizeLimitedStream.cs b/src/Shared/SizeLimitedStream.cs index 6ee758e57de3..51c1d4177c2a 100644 --- a/src/Shared/SizeLimitedStream.cs +++ b/src/Shared/SizeLimitedStream.cs @@ -5,15 +5,16 @@ internal sealed class SizeLimitedStream : Stream { private readonly Stream _innerStream; private readonly long? _sizeLimit; - + private readonly Action? _handleSizeLimit; private long _totalBytesRead; - public SizeLimitedStream(Stream innerStream, long? sizeLimit) + public SizeLimitedStream(Stream innerStream, long? sizeLimit, Action? handleSizeLimit = null) { ArgumentNullException.ThrowIfNull(innerStream); _innerStream = innerStream; _sizeLimit = sizeLimit; + _handleSizeLimit = handleSizeLimit; } public override bool CanRead => _innerStream.CanRead; @@ -48,7 +49,14 @@ public override int Read(byte[] buffer, int offset, int count) _totalBytesRead += bytesRead; if (_totalBytesRead > _sizeLimit) { - throw new InvalidOperationException("The maximum number of bytes have been read."); + if (_handleSizeLimit != null) + { + _handleSizeLimit(_totalBytesRead, _sizeLimit.Value); + } + else + { + throw new InvalidOperationException("The maximum number of bytes have been read."); + } } return bytesRead; @@ -81,7 +89,14 @@ public override async ValueTask ReadAsync(Memory buffer, Cancellation _totalBytesRead += bytesRead; if (_totalBytesRead > _sizeLimit) { - throw new InvalidOperationException("The maximum number of bytes have been read."); + if (_handleSizeLimit != null) + { + _handleSizeLimit(_totalBytesRead, _sizeLimit.Value); + } + else + { + throw new InvalidOperationException("The maximum number of bytes have been read."); + } } return bytesRead; From 99067e20a2b6a05d9b2714e6b11d06bc4fb02ff9 Mon Sep 17 00:00:00 2001 From: feiyun0112 Date: Thu, 8 May 2025 11:52:37 +0800 Subject: [PATCH 2/3] Update src/Middleware/RequestDecompression/src/RequestDecompressionMiddleware.cs Co-authored-by: mus65 --- .../RequestDecompression/src/RequestDecompressionMiddleware.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Middleware/RequestDecompression/src/RequestDecompressionMiddleware.cs b/src/Middleware/RequestDecompression/src/RequestDecompressionMiddleware.cs index c43f9b06486b..c08694405d70 100644 --- a/src/Middleware/RequestDecompression/src/RequestDecompressionMiddleware.cs +++ b/src/Middleware/RequestDecompression/src/RequestDecompressionMiddleware.cs @@ -63,7 +63,7 @@ private async Task InvokeCore(HttpContext context, Stream decompressionStream) ?? context.Features.Get()?.MaxRequestBodySize; context.Request.Body = new SizeLimitedStream(decompressionStream, sizeLimit, (long totalBytesRead, long sizeLimit) => throw new BadHttpRequestException( - $"The request's Content-Length {totalBytesRead} is larger than the request body size limit {sizeLimit}.", + $"The decompressed request body is larger than the request body size limit {sizeLimit}.", StatusCodes.Status413PayloadTooLarge)); await _next(context); } From 251002882c005cfb59244fa54d53e5649edd34e4 Mon Sep 17 00:00:00 2001 From: feiyun0112 Date: Fri, 9 May 2025 12:02:25 +0800 Subject: [PATCH 3/3] Resolve conversation --- .../src/RequestDecompressionMiddleware.cs | 2 +- src/Shared/SizeLimitedStream.cs | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Middleware/RequestDecompression/src/RequestDecompressionMiddleware.cs b/src/Middleware/RequestDecompression/src/RequestDecompressionMiddleware.cs index c08694405d70..3f7c99368e83 100644 --- a/src/Middleware/RequestDecompression/src/RequestDecompressionMiddleware.cs +++ b/src/Middleware/RequestDecompression/src/RequestDecompressionMiddleware.cs @@ -62,7 +62,7 @@ private async Task InvokeCore(HttpContext context, Stream decompressionStream) context.GetEndpoint()?.Metadata?.GetMetadata()?.MaxRequestBodySize ?? context.Features.Get()?.MaxRequestBodySize; - context.Request.Body = new SizeLimitedStream(decompressionStream, sizeLimit, (long totalBytesRead, long sizeLimit) => throw new BadHttpRequestException( + context.Request.Body = new SizeLimitedStream(decompressionStream, sizeLimit, static (long sizeLimit) => throw new BadHttpRequestException( $"The decompressed request body is larger than the request body size limit {sizeLimit}.", StatusCodes.Status413PayloadTooLarge)); await _next(context); diff --git a/src/Shared/SizeLimitedStream.cs b/src/Shared/SizeLimitedStream.cs index 51c1d4177c2a..4d249a36b360 100644 --- a/src/Shared/SizeLimitedStream.cs +++ b/src/Shared/SizeLimitedStream.cs @@ -1,14 +1,16 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#nullable enable + internal sealed class SizeLimitedStream : Stream { private readonly Stream _innerStream; private readonly long? _sizeLimit; - private readonly Action? _handleSizeLimit; + private readonly Action? _handleSizeLimit; private long _totalBytesRead; - public SizeLimitedStream(Stream innerStream, long? sizeLimit, Action? handleSizeLimit = null) + public SizeLimitedStream(Stream innerStream, long? sizeLimit, Action? handleSizeLimit = null) { ArgumentNullException.ThrowIfNull(innerStream); @@ -51,7 +53,7 @@ public override int Read(byte[] buffer, int offset, int count) { if (_handleSizeLimit != null) { - _handleSizeLimit(_totalBytesRead, _sizeLimit.Value); + _handleSizeLimit(_sizeLimit.Value); } else { @@ -91,7 +93,7 @@ public override async ValueTask ReadAsync(Memory buffer, Cancellation { if (_handleSizeLimit != null) { - _handleSizeLimit(_totalBytesRead, _sizeLimit.Value); + _handleSizeLimit(_sizeLimit.Value); } else {