Skip to content

Commit b479a54

Browse files
committed
Adding keepalive format options (#46)
1 parent 15fb8c8 commit b479a54

File tree

4 files changed

+123
-12
lines changed

4 files changed

+123
-12
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace Lib.AspNetCore.ServerSentEvents
2+
{
3+
/// <summary>
4+
/// The kind of content for keepalive.
5+
/// </summary>
6+
public enum ServerSentEventsKeepaliveKind
7+
{
8+
/// <summary>
9+
/// The keepalive will be send as a comment.
10+
/// </summary>
11+
Comment,
12+
/// <summary>
13+
/// The keepalive will be send as an event.
14+
/// </summary>
15+
Event
16+
}
17+
}

Lib.AspNetCore.ServerSentEvents/ServerSentEventsKeepaliveService.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Threading;
33
using System.Threading.Tasks;
4+
using System.Collections.Generic;
45
using Microsoft.Extensions.Hosting;
56
using Microsoft.Extensions.Options;
67
using Lib.AspNetCore.ServerSentEvents.Internals;
@@ -12,7 +13,7 @@ internal class ServerSentEventsKeepaliveService<TServerSentEventsService> : IHos
1213
{
1314
#region Fields
1415
private readonly bool _isBehindAncm = IsBehindAncm();
15-
private readonly static ServerSentEventBytes _keepaliveServerSentEventBytes = ServerSentEventsHelper.GetCommentBytes("KEEPALIVE");
16+
private readonly ServerSentEventBytes _keepaliveServerSentEventBytes;
1617

1718
private readonly CancellationTokenSource _stoppingCts = new CancellationTokenSource();
1819

@@ -26,7 +27,11 @@ internal class ServerSentEventsKeepaliveService<TServerSentEventsService> : IHos
2627
public ServerSentEventsKeepaliveService(TServerSentEventsService serverSentEventsService, IOptions<ServerSentEventsServiceOptions<TServerSentEventsService>> options)
2728
{
2829
_options = options?.Value ?? throw new ArgumentNullException(nameof(options));
30+
2931
_serverSentEventsService = serverSentEventsService;
32+
_keepaliveServerSentEventBytes = (_options.KeepaliveKind == ServerSentEventsKeepaliveKind.Comment)
33+
? ServerSentEventsHelper.GetCommentBytes(_options.KeepaliveContent)
34+
: ServerSentEventsHelper.GetEventBytes(new ServerSentEvent { Type = _options.KeepaliveContent, Data = new List<string> { String.Empty } });
3035
}
3136
#endregion
3237

Lib.AspNetCore.ServerSentEvents/ServerSentEventsServiceOptions.cs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ namespace Lib.AspNetCore.ServerSentEvents
88
/// </summary>
99
public class ServerSentEventsServiceOptions<TServerSentEventsService> where TServerSentEventsService : ServerSentEventsService
1010
{
11-
internal const int DEFAULT_KEEPALIVE_INTERVAL = 30;
11+
private const int DEFAULT_KEEPALIVE_INTERVAL = 30;
12+
private const string DEFAULT_KEEPALIVE_CONTENT = "KEEPALIVE";
1213

1314
private int _keepaliveInterval = DEFAULT_KEEPALIVE_INTERVAL;
15+
private string _keepaliveContent = DEFAULT_KEEPALIVE_CONTENT;
1416

1517
/// <summary>
1618
/// Gets or sets the keepalive sending mode.
@@ -35,6 +37,29 @@ public int KeepaliveInterval
3537
}
3638
}
3739

40+
/// <summary>
41+
/// Gets or sets the kind of content for keepalive.
42+
/// </summary>
43+
public ServerSentEventsKeepaliveKind KeepaliveKind { get; set; } = ServerSentEventsKeepaliveKind.Comment;
44+
45+
/// <summary>
46+
/// Gets or sets the content for keepalive. If the <see cref="KeepaliveKind"/> is <see cref="ServerSentEventsKeepaliveKind.Comment"/> it will be the content of the comment. If the <see cref="KeepaliveKind"/> is <see cref="ServerSentEventsKeepaliveKind.Event"/> it will be the type of the event.
47+
/// </summary>
48+
public string KeepaliveContent
49+
{
50+
get { return _keepaliveContent; }
51+
52+
set
53+
{
54+
if (String.IsNullOrWhiteSpace(value))
55+
{
56+
throw new ArgumentNullException(nameof(value));
57+
}
58+
59+
_keepaliveContent = value;
60+
}
61+
}
62+
3863
/// <summary>
3964
/// Gets or sets the interval after which clients will attempt to reestablish failed connections.
4065
/// </summary>

Test.AspNetCore.ServerSentEvents/Functional/KeepaliveTests.cs

Lines changed: 74 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,18 @@ public class KeepAliveTests
1717
{
1818
#region Fields
1919
private const int KEEPALIVE_INTERVAL = 1;
20-
private readonly static TimeSpan KEEPALIVE_TIMESPAN = TimeSpan.FromSeconds(KEEPALIVE_INTERVAL);
20+
private readonly static TimeSpan KEEPALIVE_TIMESPAN = TimeSpan.FromSeconds(KEEPALIVE_INTERVAL + 1);
2121

2222
private const string DEFAULT_KEEPALIVE = ": KEEPALIVE\r\n\r\n";
23+
private const string CUSTOM_KEEPALIVE_CONTENT = "PING";
24+
private const string CUSTOM_KEEPALIVE_COMMENT = ": PING\r\n\r\n";
25+
private const string CUSTOM_KEEPALIVE_EVENT = "event: PING\r\ndata: \r\n\r\n";
2326
#endregion
2427

2528
#region SUT
26-
private class KeepaliveModeNeverServerSentEventsServerStartup : FakeServerSentEventsServerStartup
29+
private class KeepaliveNeverServerSentEventsServerStartup : FakeServerSentEventsServerStartup
2730
{
28-
public KeepaliveModeNeverServerSentEventsServerStartup(IConfiguration configuration) : base(configuration)
31+
public KeepaliveNeverServerSentEventsServerStartup(IConfiguration configuration) : base(configuration)
2932
{ }
3033

3134
protected override Action<ServerSentEventsServiceOptions<ServerSentEventsService>> ConfigureServerSentEventsOption
@@ -41,9 +44,9 @@ protected override Action<ServerSentEventsServiceOptions<ServerSentEventsService
4144
}
4245
}
4346

44-
private class KeepaliveModeAlwaysServerSentEventsServerStartup : FakeServerSentEventsServerStartup
47+
private class KeepaliveDefultAlwaysServerSentEventsServerStartup : FakeServerSentEventsServerStartup
4548
{
46-
public KeepaliveModeAlwaysServerSentEventsServerStartup(IConfiguration configuration) : base(configuration)
49+
public KeepaliveDefultAlwaysServerSentEventsServerStartup(IConfiguration configuration) : base(configuration)
4750
{ }
4851

4952
protected override Action<ServerSentEventsServiceOptions<ServerSentEventsService>> ConfigureServerSentEventsOption
@@ -58,13 +61,53 @@ protected override Action<ServerSentEventsServiceOptions<ServerSentEventsService
5861
}
5962
}
6063
}
64+
65+
private class KeepaliveCustomCommentAlwaysServerSentEventsServerStartup : FakeServerSentEventsServerStartup
66+
{
67+
public KeepaliveCustomCommentAlwaysServerSentEventsServerStartup(IConfiguration configuration) : base(configuration)
68+
{ }
69+
70+
protected override Action<ServerSentEventsServiceOptions<ServerSentEventsService>> ConfigureServerSentEventsOption
71+
{
72+
get
73+
{
74+
return options =>
75+
{
76+
options.KeepaliveMode = ServerSentEventsKeepaliveMode.Always;
77+
options.KeepaliveInterval = KEEPALIVE_INTERVAL;
78+
options.KeepaliveKind = ServerSentEventsKeepaliveKind.Comment;
79+
options.KeepaliveContent = CUSTOM_KEEPALIVE_CONTENT;
80+
};
81+
}
82+
}
83+
}
84+
85+
private class KeepaliveCustomEventAlwaysServerSentEventsServerStartup : FakeServerSentEventsServerStartup
86+
{
87+
public KeepaliveCustomEventAlwaysServerSentEventsServerStartup(IConfiguration configuration) : base(configuration)
88+
{ }
89+
90+
protected override Action<ServerSentEventsServiceOptions<ServerSentEventsService>> ConfigureServerSentEventsOption
91+
{
92+
get
93+
{
94+
return options =>
95+
{
96+
options.KeepaliveMode = ServerSentEventsKeepaliveMode.Always;
97+
options.KeepaliveInterval = KEEPALIVE_INTERVAL;
98+
options.KeepaliveKind = ServerSentEventsKeepaliveKind.Event;
99+
options.KeepaliveContent = CUSTOM_KEEPALIVE_CONTENT;
100+
};
101+
}
102+
}
103+
}
61104
#endregion
62105

63106
#region Tests
64107
[Fact]
65108
public async Task ServerSentEventsServer_KeepaliveModeNever_DoesNotSendKeepalive()
66109
{
67-
using FakeServerSentEventsServerrApplicationFactory<KeepaliveModeNeverServerSentEventsServerStartup> serverSentEventsServerApplicationFactory = new();
110+
using FakeServerSentEventsServerrApplicationFactory<KeepaliveNeverServerSentEventsServerStartup> serverSentEventsServerApplicationFactory = new();
68111
HttpClient serverSentEventsClient = serverSentEventsServerApplicationFactory.CreateClient();
69112

70113
string serverSentEvents = await GetServerSentEvents(serverSentEventsClient).ConfigureAwait(false);
@@ -75,14 +118,36 @@ public async Task ServerSentEventsServer_KeepaliveModeNever_DoesNotSendKeepalive
75118
[Fact]
76119
public async Task ServerSentEventsServer_KeepaliveModeAlways_SendsDefaultKeepalive()
77120
{
78-
using FakeServerSentEventsServerrApplicationFactory<KeepaliveModeAlwaysServerSentEventsServerStartup> serverSentEventsServerApplicationFactory = new ();
121+
using FakeServerSentEventsServerrApplicationFactory<KeepaliveDefultAlwaysServerSentEventsServerStartup> serverSentEventsServerApplicationFactory = new ();
79122
HttpClient serverSentEventsClient = serverSentEventsServerApplicationFactory.CreateClient();
80123

81124
string serverSentEvents = await GetServerSentEvents(serverSentEventsClient).ConfigureAwait(false);
82125

83126
Assert.Matches($"^({DEFAULT_KEEPALIVE})+$", serverSentEvents);
84127
}
85128

129+
[Fact]
130+
public async Task ServerSentEventsServer_KeepaliveModeAlwaysKeepaliveKindCommentKeepaliveContentCustom_SendsCustomCommentKeepalive()
131+
{
132+
using FakeServerSentEventsServerrApplicationFactory<KeepaliveCustomCommentAlwaysServerSentEventsServerStartup> serverSentEventsServerApplicationFactory = new();
133+
HttpClient serverSentEventsClient = serverSentEventsServerApplicationFactory.CreateClient();
134+
135+
string serverSentEvents = await GetServerSentEvents(serverSentEventsClient).ConfigureAwait(false);
136+
137+
Assert.Matches($"^({CUSTOM_KEEPALIVE_COMMENT})+$", serverSentEvents);
138+
}
139+
140+
[Fact]
141+
public async Task ServerSentEventsServer_KeepaliveModeAlwaysKeepaliveKindEventKeepaliveContentCustom_SendsCustomEventKeepalive()
142+
{
143+
using FakeServerSentEventsServerrApplicationFactory<KeepaliveCustomEventAlwaysServerSentEventsServerStartup> serverSentEventsServerApplicationFactory = new();
144+
HttpClient serverSentEventsClient = serverSentEventsServerApplicationFactory.CreateClient();
145+
146+
string serverSentEvents = await GetServerSentEvents(serverSentEventsClient).ConfigureAwait(false);
147+
148+
Assert.Matches($"^({CUSTOM_KEEPALIVE_EVENT})+$", serverSentEvents);
149+
}
150+
86151
private static async Task<string> GetServerSentEvents(HttpClient serverSentEventsClient)
87152
{
88153
Stopwatch keepaliveStopwatch = new Stopwatch();
@@ -103,9 +168,8 @@ private static async Task<string> GetServerSentEvents(HttpClient serverSentEvent
103168

104169
try
105170
{
106-
CancellationTokenSource keepaliveCancellationTokenSource = new CancellationTokenSource();
107-
keepaliveCancellationTokenSource.CancelAfter(KEEPALIVE_TIMESPAN);
108-
171+
using CancellationTokenSource keepaliveCancellationTokenSource = new CancellationTokenSource(KEEPALIVE_TIMESPAN);
172+
109173
int bytesRead = await responseStream.ReadAsync(buffer, 0, buffer.Length, keepaliveCancellationTokenSource.Token).ConfigureAwait(false);
110174
serverSentEventsResponseContent += Encoding.UTF8.GetString(buffer, 0, bytesRead);
111175
}

0 commit comments

Comments
 (0)