Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Buffering issues when returning an SSE response #55654

Closed
kikaragyozov opened this issue May 10, 2024 · 6 comments
Closed

Buffering issues when returning an SSE response #55654

kikaragyozov opened this issue May 10, 2024 · 6 comments
Labels
area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions Needs: Attention 👋 This issue needs the attention of a contributor, typically because the OP has provided an update.

Comments

@kikaragyozov
Copy link

kikaragyozov commented May 10, 2024

In the controller I'm basically returning a server-sent event by manually setting the content type, starting the response with Response.StartAsync(cancellationToken) and then directly writing data to Response.Body, finishing with a Response.CompleteAsync(). For additional context - another service is returning the SSE in a Stream, so I'm effectively just calling Stream.CopyToAsync(Response.Body, cancellationToken)

The issue I'm facing is that when the above code runs on Kestrel, the events in the stream are immediately returned and it seems like no buffering is being done.

The moment I switch to IIS or IIS Express though, something weird is going on as the client receives the response after writing to the response body has finished.

After investigating, I thought the Dynamic Response Caching module for IIS was at fault here. But it turns out that it was actually not installed on the system as shown here:
image

If Dynamic Response Caching is not involved, what else is going on that would screw up the SSE like that? If I have to follow common sense, does the above mean that:

  1. By default, Kestrel is configured to NOT buffer the response body. (How do you control this?)
  2. By default, IIS is configured to buffer the response body. (How do you control this?)

I find it weird that Kestrel would have no response body buffering by default. Is this really the case? If not, what's going on here? Am I perhaps implementing SSE in a weird way?

Calling HttpContext!.Features.Get<IHttpResponseBodyFeature>()!.DisableBuffering() fixes the issue when running under IIS/IIS Express.

.NET Version: 8.0.300-preview.24203.14

@dotnet-issue-labeler dotnet-issue-labeler bot added the area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions label May 10, 2024
@kikaragyozov kikaragyozov changed the title Buffering issues with returning an SSE response Buffering issues when returning an SSE response May 10, 2024
@Tratcher
Copy link
Member

Calling HttpContext!.Features.Get<IHttpResponseBodyFeature>()!.DisableBuffering() fixes the issue when running under IIS/IIS Express.

That's expected, SignalR's SSE implementation has to do the same. Are you still having issues, or are you just trying to understand what component was causing the problem?

@BrennanConroy BrennanConroy added the Needs: Author Feedback The author of this issue needs to respond in order for us to continue investigating this issue. label May 13, 2024
@kikaragyozov
Copy link
Author

kikaragyozov commented May 14, 2024

Calling HttpContext!.Features.Get<IHttpResponseBodyFeature>()!.DisableBuffering() fixes the issue when running under IIS/IIS Express.

That's expected, SignalR's SSE implementation has to do the same. Are you still having issues, or are you just trying to understand what component was causing the problem?

I'm just trying to understand how things work under the hood, especially when switching between Kestrel and IIS. It seems that when running under Kestrel, there's no buffering involved? The SSE stream is being returned in chunks, unlike in IIS where everything's returned at once.

@dotnet-policy-service dotnet-policy-service bot added Needs: Attention 👋 This issue needs the attention of a contributor, typically because the OP has provided an update. and removed Needs: Author Feedback The author of this issue needs to respond in order for us to continue investigating this issue. labels May 14, 2024
@davidfowl
Copy link
Member

Here's the implementation for IIS:

void IHttpResponseBodyFeature.DisableBuffering()
{
NativeMethods.HttpDisableBuffering(_requestNativeHandle);
DisableCompression();
}
private void DisableCompression()
{
var serverVariableFeature = (IServerVariablesFeature)this;
serverVariableFeature["IIS_EnableDynamicCompression"] = "0";
}

Which calls https://learn.microsoft.com/en-us/iis/web-development-reference/native-code-api-reference/ihttpresponse-disablebuffering-method

Kestrel we control the entire stack and there's not explicit buffering (there's a "write behind buffer", but it's flushed immediately).

@kikaragyozov
Copy link
Author

kikaragyozov commented May 14, 2024

Here's the implementation for IIS:

void IHttpResponseBodyFeature.DisableBuffering()
{
NativeMethods.HttpDisableBuffering(_requestNativeHandle);
DisableCompression();
}
private void DisableCompression()
{
var serverVariableFeature = (IServerVariablesFeature)this;
serverVariableFeature["IIS_EnableDynamicCompression"] = "0";
}

Which calls https://learn.microsoft.com/en-us/iis/web-development-reference/native-code-api-reference/ihttpresponse-disablebuffering-method

Kestrel we control the entire stack and there's not explicit buffering (there's a "write behind buffer", but it's flushed immediately).

Thank you @davidfowl for the information! This is what I needed. Am I correct in understanding that IIS buffering cannot be disabled from IIS? Compression can be though?

And finally - can one control how buffering is done in IIS, like tuning the buffer size for example?

In Kestrel, there's KestrelServerLimits.MaxResponseBufferSize - How does that come into the picture?

@davidfowl
Copy link
Member

Thank you @davidfowl for the information! This is what I needed. Am I correct in understanding that IIS buffering cannot be disabled from IIS? Compression can be though?

That's right. AFAIK you have to call this API.

In Kestrel, there's KestrelServerLimits.MaxResponseBufferSize - How does that come into the picture?

It doesn't. This is about internal buffer sizes, not the operation of buffering content.

@kikaragyozov
Copy link
Author

Closing this as I got what I was looking for. Thanks for the clarifications!

If anyone wouldn't mind sharing internally how IIS response buffering works, that'd be cool 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions Needs: Attention 👋 This issue needs the attention of a contributor, typically because the OP has provided an update.
Projects
None yet
Development

No branches or pull requests

4 participants