From 0546dc21f4f22e95174702ec719cf70fd1751815 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Mon, 10 Apr 2017 19:05:06 -0700 Subject: [PATCH] Disable response buffering via the IHttpBufferingFeature (#379) * Disable response buffering via the IHttpBufferingFeature - To make sure SignalR works with servers and middleware that do perform response buffering, disable it via the IHttpBufferingFeature for SSE. - Added test to verify buffering is disabled --- .../Transports/ServerSentEventsTransport.cs | 6 ++++ .../ServerSentEventsTests.cs | 35 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/src/Microsoft.AspNetCore.Sockets/Transports/ServerSentEventsTransport.cs b/src/Microsoft.AspNetCore.Sockets/Transports/ServerSentEventsTransport.cs index 2b4e6cbf32..eaa5b98186 100644 --- a/src/Microsoft.AspNetCore.Sockets/Transports/ServerSentEventsTransport.cs +++ b/src/Microsoft.AspNetCore.Sockets/Transports/ServerSentEventsTransport.cs @@ -9,6 +9,7 @@ using System.Threading; using System.Threading.Tasks; using System.Threading.Tasks.Channels; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Sockets.Internal.Formatters; using Microsoft.Extensions.Logging; @@ -29,6 +30,11 @@ namespace Microsoft.AspNetCore.Sockets.Transports { context.Response.ContentType = "text/event-stream"; context.Response.Headers["Cache-Control"] = "no-cache"; + + // Make sure we disable all response buffering for SSE + var bufferingFeature = context.Features.Get(); + bufferingFeature?.DisableResponseBuffering(); + context.Response.Headers["Content-Encoding"] = "identity"; await context.Response.Body.FlushAsync(); diff --git a/test/Microsoft.AspNetCore.Sockets.Tests/ServerSentEventsTests.cs b/test/Microsoft.AspNetCore.Sockets.Tests/ServerSentEventsTests.cs index ae4a0b99b3..05dc925f5c 100644 --- a/test/Microsoft.AspNetCore.Sockets.Tests/ServerSentEventsTests.cs +++ b/test/Microsoft.AspNetCore.Sockets.Tests/ServerSentEventsTests.cs @@ -1,12 +1,14 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.IO; using System.IO.Pipelines; using System.Text; using System.Threading.Tasks; using System.Threading.Tasks.Channels; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Sockets.Transports; using Microsoft.Extensions.Logging; using Xunit; @@ -30,6 +32,22 @@ namespace Microsoft.AspNetCore.Sockets.Tests Assert.Equal("no-cache", context.Response.Headers["Cache-Control"]); } + [Fact] + public async Task SSETurnsResponseBufferingOff() + { + var channel = Channel.CreateUnbounded(); + var context = new DefaultHttpContext(); + var feature = new HttpBufferingFeature(); + context.Features.Set(feature); + var sse = new ServerSentEventsTransport(channel, new LoggerFactory()); + + Assert.True(channel.Out.TryComplete()); + + await sse.ProcessRequestAsync(context, context.RequestAborted); + + Assert.True(feature.ResponseBufferingDisabled); + } + [Theory] [InlineData("Hello World", "data: T\r\ndata: Hello World\r\n\r\n")] [InlineData("Hello\nWorld", "data: T\r\ndata: Hello\r\ndata: World\r\n\r\n")] @@ -53,5 +71,22 @@ namespace Microsoft.AspNetCore.Sockets.Tests Assert.Equal(expected, Encoding.UTF8.GetString(ms.ToArray())); } + + private class HttpBufferingFeature : IHttpBufferingFeature + { + public bool RequestBufferingDisabled { get; set; } + + public bool ResponseBufferingDisabled { get; set; } + + public void DisableRequestBuffering() + { + RequestBufferingDisabled = true; + } + + public void DisableResponseBuffering() + { + ResponseBufferingDisabled = true; + } + } } }