Skip to content

Commit

Permalink
Merge pull request #15 from ejball/disable-sse-buffering
Browse files Browse the repository at this point in the history
  • Loading branch information
ejball authored Jul 4, 2024
2 parents 572bd6e + f8e93f1 commit e165fff
Show file tree
Hide file tree
Showing 5 changed files with 23 additions and 14 deletions.
4 changes: 2 additions & 2 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<Project>

<PropertyGroup>
<VersionPrefix>3.10.0</VersionPrefix>
<PackageValidationBaselineVersion>3.9.0</PackageValidationBaselineVersion>
<VersionPrefix>3.10.1</VersionPrefix>
<PackageValidationBaselineVersion>3.10.0</PackageValidationBaselineVersion>
<LangVersion>12.0</LangVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
Expand Down
4 changes: 4 additions & 0 deletions ReleaseNotes.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Release Notes

## 3.10.1

* Disable buffering for text/event-stream.

## 3.10.0

* Support events.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,15 @@ public void Configuration(IAppBuilder app)
{
var configuration = new HttpConfiguration();
configuration.MapHttpAttributeRoutes();
configuration.Services.Replace(typeof(IHostBufferPolicySelector), new NoBufferPolicySelector());
configuration.Services.Replace(typeof(IHostBufferPolicySelector), new SseBufferPolicySelector(configuration.Services.GetHostBufferPolicySelector()));
app.UseWebApi(configuration);
}

private sealed class NoBufferPolicySelector : IHostBufferPolicySelector
private sealed class SseBufferPolicySelector(IHostBufferPolicySelector? inner) : IHostBufferPolicySelector
{
public bool UseBufferedInputStream(object hostContext) => false;
public bool UseBufferedOutputStream(HttpResponseMessage response) => false; //// required for event streaming
public bool UseBufferedInputStream(object hostContext) => inner?.UseBufferedInputStream(hostContext) ?? false;
public bool UseBufferedOutputStream(HttpResponseMessage response) =>
response.Content.Headers.ContentType?.MediaType != "text/event-stream" && (inner?.UseBufferedOutputStream(response) ?? false);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,15 @@ public void Configuration(IAppBuilder app)
Tests = LoadTests(),
JsonSerializer = JsonSerializer,
})));
configuration.Services.Replace(typeof(IHostBufferPolicySelector), new NoBufferPolicySelector());
configuration.Services.Replace(typeof(IHostBufferPolicySelector), new SseBufferPolicySelector(configuration.Services.GetHostBufferPolicySelector()));
app.UseWebApi(configuration);
}

private sealed class NoBufferPolicySelector : IHostBufferPolicySelector
private sealed class SseBufferPolicySelector(IHostBufferPolicySelector? inner) : IHostBufferPolicySelector
{
public bool UseBufferedInputStream(object hostContext) => false;
public bool UseBufferedOutputStream(HttpResponseMessage response) => false; //// required for event streaming
public bool UseBufferedInputStream(object hostContext) => inner?.UseBufferedInputStream(hostContext) ?? false;
public bool UseBufferedOutputStream(HttpResponseMessage response) =>
response.Content.Headers.ContentType?.MediaType != "text/event-stream" && (inner?.UseBufferedOutputStream(response) ?? false);
}
}

Expand Down
11 changes: 7 additions & 4 deletions src/Facility.AspNetCore/FacilityAspNetCoreUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Facility.Core.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Http.Features;

namespace Facility.AspNetCore;

Expand Down Expand Up @@ -62,20 +63,22 @@ public static HttpResponseMessage CreateHttpResponseMessage(ServiceErrorDto erro
/// </summary>
public static async Task WriteHttpResponseMessageAsync(HttpResponseMessage httpResponseMessage, HttpResponse contextResponse)
{
contextResponse.StatusCode = (int) httpResponseMessage.StatusCode;
// disable buffering for text/event-stream
var contentHeaders = httpResponseMessage.Content.Headers;
if (contentHeaders.ContentType?.MediaType == "text/event-stream")
contextResponse.HttpContext.Features.Get<IHttpResponseBodyFeature>()?.DisableBuffering();

var responseHeaders = httpResponseMessage.Headers;
contextResponse.StatusCode = (int) httpResponseMessage.StatusCode;

// Ignore the Transfer-Encoding header if it is just "chunked".
// We let the host decide about whether the response should be chunked or not.
var responseHeaders = httpResponseMessage.Headers;
if (responseHeaders is { TransferEncodingChunked: true, TransferEncoding.Count: 1 })
responseHeaders.TransferEncoding.Clear();

foreach (var header in responseHeaders)
contextResponse.Headers.Append(header.Key, header.Value.ToArray());

var contentHeaders = httpResponseMessage.Content.Headers;

// Copy the response content headers only after ensuring they are complete.
// We ask for Content-Length first because HttpContent lazily computes this
// and only afterward writes the value into the content headers.
Expand Down

0 comments on commit e165fff

Please sign in to comment.