Skip to content

Don't cache truncated/aborted responses in OutputCacheMiddleware - #68683

Open
karimsalem1 wants to merge 1 commit into
dotnet:mainfrom
karimsalem1:karimsalem/output-caching-truncated-responses
Open

Don't cache truncated/aborted responses in OutputCacheMiddleware#68683
karimsalem1 wants to merge 1 commit into
dotnet:mainfrom
karimsalem1:karimsalem/output-caching-truncated-responses

Conversation

@karimsalem1

@karimsalem1 karimsalem1 commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Don't cache truncated/aborted responses in OutputCacheMiddleware

Fixes #66877
Fixes #56427

Issue

When a request is aborted, OutputCacheMiddleware can still store the partial response and serve it to subsequent clients until the entry expires.

Two safeguards are meant to prevent this, and neither is reliable:

  • The byte count is compared against Content-Length. With no Content-Length set there is nothing to compare against, and the check passes.
  • OutputCacheStream disables buffering on a write failure, but only sees failures raised at its own layer. When another stream is layered above it, the exception is thrown before the write ever reaches OutputCacheStream.

Locking makes this worse. With SetLocking(true), requests waiting on an in-flight response are handed it directly, without the cacheability checks running at all. On that path, even a response carrying a Content-Length can be shared truncated.

Fix

  1. Don't cache a response when httpContext.RequestAborted is canceled.
  2. Only share a response with waiting requests when it actually passed the cacheability checks and was cached.

FinalizeCacheBodyAsync now reports whether the response was stored, and the caller releases the pending entry if it wasn't, forcing waiters to execute the pipeline themselves instead of receiving the incomplete entry.

The release condition is just !isResponseCached. The previous !context.AllowCacheStorage check is now redundant, since FinalizeCacheBodyAsync will return false when storage is disabled.

Tests

Validates scenarios: an aborted response isn't stored, isn't served to later requests, and isn't handed to a request waiting on the locking path. Also ensures the aborted request itself still runs the pipeline exactly once.

OutputCacheMiddleware could store a response whose body was cut short,
and share that entry with requests waiting on the same cache key.

FinalizeCacheBodyAsync now skips storage when the request was aborted and
reports whether the response was cached. The caller releases the pending
entry unless it was actually stored, so waiters re-execute instead of
receiving a truncated body.

Fixes dotnet#66877
@dotnet-policy-service dotnet-policy-service Bot added the community-contribution Indicates that the PR has been added by a community member label Aug 21, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Thanks for your PR, @karimsalem1. Someone from the team will get assigned to your PR shortly and we'll get it reviewed.

@DeagleGross DeagleGross left a comment

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.

LGTM!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community-contribution Indicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OutputCache caches truncated responses for aborted requests Output caching middleware caches empty response body when request cancelled

2 participants