11// Licensed to the .NET Foundation under one or more agreements.
22// The .NET Foundation licenses this file to you under the MIT license.
33
4+ using System . IO . Pipelines ;
45using System . Net . ServerSentEvents ;
56using System . Reflection ;
67using System . Runtime . CompilerServices ;
78using System . Text ;
89using Microsoft . AspNetCore . Builder ;
10+ using Microsoft . AspNetCore . Http . Features ;
911using Microsoft . AspNetCore . Http . Json ;
1012using Microsoft . AspNetCore . Http . Metadata ;
1113using Microsoft . AspNetCore . Routing ;
@@ -31,6 +33,7 @@ public async Task ExecuteAsync_SetsContentTypeAndHeaders()
3133 Assert . Equal ( "text/event-stream" , httpContext . Response . ContentType ) ;
3234 Assert . Equal ( "no-cache,no-store" , httpContext . Response . Headers . CacheControl ) ;
3335 Assert . Equal ( "no-cache" , httpContext . Response . Headers . Pragma ) ;
36+ Assert . Equal ( "identity" , httpContext . Response . Headers . ContentEncoding ) ;
3437 }
3538
3639 [ Fact ]
@@ -259,6 +262,75 @@ async IAsyncEnumerable<string> GetEvents([EnumeratorCancellation] CancellationTo
259262 }
260263 }
261264
265+ [ Fact ]
266+ public async Task ExecuteAsync_DisablesBuffering ( )
267+ {
268+ // Arrange
269+ var httpContext = GetHttpContext ( ) ;
270+ var events = AsyncEnumerable . Empty < string > ( ) ;
271+ var result = TypedResults . ServerSentEvents ( events ) ;
272+ var bufferingDisabled = false ;
273+
274+ var mockBufferingFeature = new MockHttpResponseBodyFeature (
275+ onDisableBuffering : ( ) => bufferingDisabled = true ) ;
276+
277+ httpContext . Features . Set < IHttpResponseBodyFeature > ( mockBufferingFeature ) ;
278+
279+ // Act
280+ await result . ExecuteAsync ( httpContext ) ;
281+
282+ // Assert
283+ Assert . True ( bufferingDisabled ) ;
284+ }
285+
286+ [ Fact ]
287+ public async Task ExecuteAsync_WithByteArrayData_WritesDataDirectly ( )
288+ {
289+ // Arrange
290+ var httpContext = GetHttpContext ( ) ;
291+ var bytes = "event1"u8 . ToArray ( ) ;
292+ var events = new [ ] { new SseItem < byte [ ] > ( bytes ) } . ToAsyncEnumerable ( ) ;
293+ var result = TypedResults . ServerSentEvents ( events ) ;
294+
295+ // Act
296+ await result . ExecuteAsync ( httpContext ) ;
297+
298+ // Assert
299+ var responseBody = Encoding . UTF8 . GetString ( ( ( MemoryStream ) httpContext . Response . Body ) . ToArray ( ) ) ;
300+ Assert . Contains ( "data: event1\n \n " , responseBody ) ;
301+
302+ // Assert that string is not JSON serialized
303+ Assert . DoesNotContain ( "data: \" event1" , responseBody ) ;
304+ }
305+
306+ [ Fact ]
307+ public async Task ExecuteAsync_WithByteArrayData_HandlesNullData ( )
308+ {
309+ // Arrange
310+ var httpContext = GetHttpContext ( ) ;
311+ var events = new [ ] { new SseItem < byte [ ] > ( null ) } . ToAsyncEnumerable ( ) ;
312+ var result = TypedResults . ServerSentEvents ( events ) ;
313+
314+ // Act
315+ await result . ExecuteAsync ( httpContext ) ;
316+
317+ // Assert
318+ var responseBody = Encoding . UTF8 . GetString ( ( ( MemoryStream ) httpContext . Response . Body ) . ToArray ( ) ) ;
319+ Assert . Contains ( "data: \n \n " , responseBody ) ;
320+ }
321+
322+ private class MockHttpResponseBodyFeature ( Action onDisableBuffering ) : IHttpResponseBodyFeature
323+ {
324+ public Stream Stream => new MemoryStream ( ) ;
325+ public PipeWriter Writer => throw new NotImplementedException ( ) ;
326+ public Task CompleteAsync ( ) => throw new NotImplementedException ( ) ;
327+ public void DisableBuffering ( ) => onDisableBuffering ( ) ;
328+ public Task SendFileAsync ( string path , long offset , long ? count , CancellationToken cancellationToken = default )
329+ => throw new NotImplementedException ( ) ;
330+ public Task StartAsync ( CancellationToken cancellationToken = default )
331+ => throw new NotImplementedException ( ) ;
332+ }
333+
262334 private static void PopulateMetadata < TResult > ( MethodInfo method , EndpointBuilder builder )
263335 where TResult : IEndpointMetadataProvider => TResult . PopulateMetadata ( method , builder ) ;
264336
0 commit comments