-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #12358 from abpframework/RequestSizeLimit
Introduce `AbpRequestSizeLimitMiddleware `
- Loading branch information
Showing
8 changed files
with
301 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
81 changes: 81 additions & 0 deletions
81
...Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/RequestSizeLimit/AbpRequestSizeLimitMiddleware.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
using System.Globalization; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using Microsoft.AspNetCore.Http; | ||
using Microsoft.AspNetCore.Http.Features; | ||
using Microsoft.AspNetCore.Mvc; | ||
using Microsoft.Extensions.Logging; | ||
using Volo.Abp.DependencyInjection; | ||
|
||
namespace Volo.Abp.AspNetCore.RequestSizeLimit; | ||
|
||
public class AbpRequestSizeLimitMiddleware : IMiddleware, ITransientDependency | ||
{ | ||
private readonly ILogger<AbpRequestSizeLimitMiddleware> _logger; | ||
|
||
public AbpRequestSizeLimitMiddleware(ILogger<AbpRequestSizeLimitMiddleware> logger) | ||
{ | ||
_logger = logger; | ||
} | ||
|
||
public async Task InvokeAsync(HttpContext context, RequestDelegate next) | ||
{ | ||
var endpoint = context.GetEndpoint(); | ||
if (endpoint != null) | ||
{ | ||
if (!HasDisableRequestSizeLimitAttribute(endpoint)) | ||
{ | ||
var attribute = endpoint.Metadata.GetMetadata<AbpRequestSizeLimitAttribute>(); | ||
if (attribute != null) | ||
{ | ||
var maxRequestBodySizeFeature = context.Features.Get<IHttpMaxRequestBodySizeFeature>(); | ||
if (maxRequestBodySizeFeature == null) | ||
{ | ||
_logger.LogInformation("A request body size limit could not be applied. This server does not support the IHttpRequestBodySizeFeature."); | ||
} | ||
else if (maxRequestBodySizeFeature.IsReadOnly) | ||
{ | ||
_logger.LogInformation("A request body size limit could not be applied. The IHttpRequestBodySizeFeature for the server is read-only."); | ||
} | ||
else | ||
{ | ||
_logger.LogInformation($"The maximum request body size has been set to {attribute.GetBytes().ToString(CultureInfo.InvariantCulture)}."); | ||
maxRequestBodySizeFeature.MaxRequestBodySize = attribute.GetBytes(); | ||
} | ||
} | ||
else | ||
{ | ||
_logger.LogInformation($"AbpRequestSizeLimitAttribute does not exist in endpoint, Skipping."); | ||
} | ||
} | ||
else | ||
{ | ||
_logger.LogInformation($"Endpoint has DisableRequestSizeLimitAttribute, Skipping."); | ||
} | ||
} | ||
else | ||
{ | ||
_logger.LogInformation($"Endpoint is null, Skipping."); | ||
} | ||
|
||
await next(context); | ||
} | ||
|
||
protected virtual bool HasDisableRequestSizeLimitAttribute(Endpoint endpoint) | ||
{ | ||
foreach (var metadata in endpoint.Metadata.Reverse()) | ||
{ | ||
if (metadata is AbpRequestSizeLimitAttribute) | ||
{ | ||
return false; | ||
} | ||
|
||
if (metadata is DisableRequestSizeLimitAttribute) | ||
{ | ||
return true; | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/RequestSizeLimit/RequestSizeLimit.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
using System; | ||
|
||
namespace Volo.Abp.AspNetCore.RequestSizeLimit; | ||
|
||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] | ||
public class AbpRequestSizeLimitAttribute : Attribute | ||
{ | ||
private readonly long _bytes; | ||
|
||
public AbpRequestSizeLimitAttribute(long bytes) | ||
{ | ||
_bytes = bytes; | ||
} | ||
|
||
public long GetBytes() | ||
{ | ||
return _bytes; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
94 changes: 94 additions & 0 deletions
94
...tCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/RequestSizeLimit/RequestBodySizeCheckingStream.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
using System; | ||
using System.IO; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.AspNetCore.Http.Features; | ||
|
||
namespace Volo.Abp.AspNetCore.Mvc.RequestSizeLimit; | ||
|
||
public class RequestBodySizeCheckingStream : Stream | ||
{ | ||
private readonly Stream _innerStream; | ||
private readonly IHttpMaxRequestBodySizeFeature _maxRequestBodySizeFeature; | ||
private long _totalRead; | ||
|
||
public RequestBodySizeCheckingStream(Stream innerStream, IHttpMaxRequestBodySizeFeature maxRequestBodySizeFeature) | ||
{ | ||
_innerStream = innerStream; | ||
_maxRequestBodySizeFeature = maxRequestBodySizeFeature; | ||
} | ||
|
||
public override bool CanRead => _innerStream.CanRead; | ||
|
||
public override bool CanSeek => _innerStream.CanSeek; | ||
|
||
public override bool CanWrite => _innerStream.CanWrite; | ||
|
||
public override long Length => _innerStream.Length; | ||
|
||
public override long Position | ||
{ | ||
get { return _innerStream.Position; } | ||
set { _innerStream.Position = value; } | ||
} | ||
|
||
public override void Flush() | ||
{ | ||
_innerStream.Flush(); | ||
} | ||
|
||
public override int Read(byte[] buffer, int offset, int count) | ||
{ | ||
if (_maxRequestBodySizeFeature.MaxRequestBodySize != null && _innerStream.CanSeek && _innerStream.Length > _maxRequestBodySizeFeature.MaxRequestBodySize) | ||
{ | ||
throw new BusinessException("Request content size is greater than the limit size"); | ||
} | ||
|
||
var read = _innerStream.Read(buffer, offset, count); | ||
_totalRead += read; | ||
|
||
if (_maxRequestBodySizeFeature.MaxRequestBodySize != null && _totalRead > _maxRequestBodySizeFeature.MaxRequestBodySize) | ||
{ | ||
throw new BusinessException("Request content size is greater than the limit size"); | ||
} | ||
return read; | ||
} | ||
|
||
public async override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) | ||
{ | ||
if (_maxRequestBodySizeFeature.MaxRequestBodySize != null && _innerStream.CanSeek && _innerStream.Length > _maxRequestBodySizeFeature.MaxRequestBodySize) | ||
{ | ||
throw new BusinessException("Request content size is greater than the limit size"); | ||
} | ||
|
||
var read = await _innerStream.ReadAsync(buffer, offset, count, cancellationToken); | ||
_totalRead += read; | ||
|
||
if (_maxRequestBodySizeFeature.MaxRequestBodySize != null && _totalRead > _maxRequestBodySizeFeature.MaxRequestBodySize) | ||
{ | ||
throw new BusinessException("Request content size is greater than the limit size"); | ||
} | ||
return read; | ||
} | ||
|
||
public override long Seek(long offset, SeekOrigin origin) | ||
{ | ||
return _innerStream.Seek(offset, origin); | ||
} | ||
|
||
public override void SetLength(long value) | ||
{ | ||
_innerStream.SetLength(value); | ||
} | ||
|
||
public override void Write(byte[] buffer, int offset, int count) | ||
{ | ||
_innerStream.Write(buffer, offset, count); | ||
} | ||
} | ||
|
||
public class TestHttpMaxRequestBodySizeFeature : IHttpMaxRequestBodySizeFeature | ||
{ | ||
public bool IsReadOnly => false; | ||
public long? MaxRequestBodySize { get; set; } | ||
} |
25 changes: 25 additions & 0 deletions
25
...pNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/RequestSizeLimit/RequestSizeLimitController.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
using Microsoft.AspNetCore.Mvc; | ||
using Volo.Abp.AspNetCore.RequestSizeLimit; | ||
using Volo.Abp.Content; | ||
|
||
namespace Volo.Abp.AspNetCore.Mvc.RequestSizeLimit; | ||
|
||
[Route("api/request-siz-limit")] | ||
[AbpRequestSizeLimit(5)] | ||
public class RequestSizeLimitController : AbpController | ||
{ | ||
[HttpPost] | ||
[Route("check")] | ||
public string RequestSizeLimitCheck(IRemoteStreamContent file) | ||
{ | ||
return !ModelState.IsValid ? ModelState.ToString() : "ok"; | ||
} | ||
|
||
[HttpPost] | ||
[DisableRequestSizeLimit] | ||
[Route("disable")] | ||
public string DisableRequestSizeLimit(IRemoteStreamContent file) | ||
{ | ||
return !ModelState.IsValid ? ModelState.ToString() : "ok"; | ||
} | ||
} |
53 changes: 53 additions & 0 deletions
53
...re.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/RequestSizeLimit/RequestSizeLimitController_Tests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
using System.Globalization; | ||
using System.IO; | ||
using System.Net; | ||
using System.Net.Http; | ||
using System.Net.Http.Headers; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
using Shouldly; | ||
using Xunit; | ||
|
||
namespace Volo.Abp.AspNetCore.Mvc.RequestSizeLimit; | ||
|
||
public class RequestSizeLimitController_Tests : AspNetCoreMvcTestBase | ||
{ | ||
[Fact] | ||
public async Task RequestSizeLimitCheck_Test() | ||
{ | ||
using (var requestMessage = new HttpRequestMessage(HttpMethod.Post, "api/request-siz-limit/check")) | ||
{ | ||
var memoryStream = new MemoryStream(); | ||
await memoryStream.WriteAsync(Encoding.UTF8.GetBytes("0123456789")); | ||
memoryStream.Position = 0; | ||
|
||
var streamContent = new StreamContent(memoryStream); | ||
streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); | ||
requestMessage.Content = new MultipartFormDataContent { { streamContent, "file", "text.txt" } }; | ||
|
||
var response = await Client.SendAsync(requestMessage); | ||
|
||
(await response.Content.ReadAsStringAsync()).ShouldContain("Request content size is greater than the limit size"); | ||
} | ||
} | ||
|
||
[Fact] | ||
public async Task DisableRequestSizeLimit_Test() | ||
{ | ||
using (var requestMessage = new HttpRequestMessage(HttpMethod.Post, "api/request-siz-limit/disable")) | ||
{ | ||
var memoryStream = new MemoryStream(); | ||
await memoryStream.WriteAsync(Encoding.UTF8.GetBytes("0123456789")); | ||
memoryStream.Position = 0; | ||
|
||
var streamContent = new StreamContent(memoryStream); | ||
streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); | ||
requestMessage.Content = new MultipartFormDataContent { { streamContent, "file", "text.txt" } }; | ||
|
||
var response = await Client.SendAsync(requestMessage); | ||
|
||
(await response.Content.ReadAsStringAsync()).ShouldContain("ok"); | ||
} | ||
} | ||
} |