diff --git a/src/Middleware/RequestDecompression/src/RequestDecompressionMiddleware.cs b/src/Middleware/RequestDecompression/src/RequestDecompressionMiddleware.cs index 71cd050492ce..3f7c99368e83 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, static (long sizeLimit) => throw new BadHttpRequestException( + $"The decompressed request body 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..4d249a36b360 100644 --- a/src/Shared/SizeLimitedStream.cs +++ b/src/Shared/SizeLimitedStream.cs @@ -1,19 +1,22 @@ // 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 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 +51,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(_sizeLimit.Value); + } + else + { + throw new InvalidOperationException("The maximum number of bytes have been read."); + } } return bytesRead; @@ -81,7 +91,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(_sizeLimit.Value); + } + else + { + throw new InvalidOperationException("The maximum number of bytes have been read."); + } } return bytesRead;