Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/Middleware/OutputCaching/src/OutputCacheMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,16 @@ private async Task InvokeAwaited(HttpContext httpContext, IReadOnlyList<IOutputC
{
await _next(httpContext);

// If the request was aborted, do not cache the response — it may be truncated.
// httpContext.Abort() is the standard pattern used by action results when
// OperationCanceledException is caught mid-write, so RequestAborted being
// canceled here is a strong signal that the response body is incomplete.
if (httpContext.RequestAborted.IsCancellationRequested)
{
_logger.ResponseNotCached();
return null;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be setting executed = true; here so the aborted request doesn't try to serve a cached response or re-run the request pipeline.

}

// The next middleware might change the policy
foreach (var policy in policies)
{
Expand Down Expand Up @@ -419,6 +429,17 @@ internal async ValueTask FinalizeCacheBodyAsync(OutputCacheContext context)
if (context.AllowCacheStorage && context.OutputCacheStream.BufferingEnabled
&& context.CachedResponse is not null)
{
// Do not cache a response whose originating request was aborted. The response body
// may be truncated: a decorator stream above OutputCacheStream (e.g. GZipStream from
// ResponseCompression) can throw OperationCanceledException before the write reaches
// OutputCacheStream, leaving BufferingEnabled=true with a partial buffer. The caller
// is expected to call httpContext.Abort() in that case, which cancels RequestAborted.
if (context.HttpContext.RequestAborted.IsCancellationRequested)
{
_logger.ResponseNotCached();
return;
}

// If AllowCacheLookup is false, the cache key was not created
CreateCacheKey(context);

Expand Down
Loading