Skip to content
This repository was archived by the owner on Jul 19, 2021. It is now read-only.

Commit 4d6986c

Browse files
committed
Improve support and use of cancellationtokens
1 parent 9d399ee commit 4d6986c

8 files changed

Lines changed: 62 additions & 58 deletions

File tree

src/RedHttpServer/Extensions/BodyParser.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.IO;
4+
using System.Threading;
45
using System.Threading.Tasks;
56
using Red.Interfaces;
67

@@ -35,7 +36,7 @@ public Task<string> ReadAsync(Request request)
3536
return default;
3637

3738
var converter = request.Context.Plugins.Get<IBodyConverter>(converterType);
38-
return await converter.DeserializeAsync<T>(request.BodyStream);
39+
return await converter.DeserializeAsync<T>(request.BodyStream, request.Aborted);
3940
}
4041

4142
public void Initialize(RedHttpServer server)

src/RedHttpServer/Extensions/JsonConverter.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.IO;
22
using System.Text.Json;
3+
using System.Threading;
34
using System.Threading.Tasks;
45
using Red.Interfaces;
56

@@ -40,12 +41,12 @@ internal sealed class JsonConverter : IJsonConverter, IRedExtension
4041
}
4142

4243
/// <inheritdoc />
43-
public async Task<T?> DeserializeAsync<T>(Stream jsonStream)
44+
public async Task<T?> DeserializeAsync<T>(Stream jsonStream, CancellationToken cancellationToken = default)
4445
where T : class
4546
{
4647
try
4748
{
48-
return await JsonSerializer.DeserializeAsync<T>(jsonStream);
49+
return await JsonSerializer.DeserializeAsync<T>(jsonStream, cancellationToken: cancellationToken);
4950
}
5051
catch (JsonException)
5152
{
@@ -54,11 +55,11 @@ internal sealed class JsonConverter : IJsonConverter, IRedExtension
5455
}
5556

5657
/// <inheritdoc />
57-
public async Task SerializeAsync<T>(T obj, Stream jsonStream)
58+
public async Task SerializeAsync<T>(T obj, Stream jsonStream, CancellationToken cancellationToken = default)
5859
{
5960
try
6061
{
61-
await JsonSerializer.SerializeAsync(jsonStream, obj);
62+
await JsonSerializer.SerializeAsync(jsonStream, obj, cancellationToken: cancellationToken);
6263
}
6364
catch (JsonException)
6465
{

src/RedHttpServer/Extensions/XmlConverter.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.IO;
33
using System.Text;
4+
using System.Threading;
45
using System.Threading.Tasks;
56
using System.Xml;
67
using System.Xml.Serialization;
@@ -57,7 +58,7 @@ public void Initialize(RedHttpServer server)
5758
}
5859

5960
/// <inheritdoc />
60-
public async Task<T?> DeserializeAsync<T>(Stream xmlStream)
61+
public async Task<T?> DeserializeAsync<T>(Stream xmlStream, CancellationToken cancellationToken = default)
6162
where T : class
6263
{
6364
try
@@ -76,7 +77,7 @@ public void Initialize(RedHttpServer server)
7677
}
7778

7879
/// <inheritdoc />
79-
public async Task SerializeAsync<T>(T obj, Stream output)
80+
public async Task SerializeAsync<T>(T obj, Stream output, CancellationToken cancellationToken = default)
8081
{
8182
try
8283
{

src/RedHttpServer/Interfaces/IBodyConverter.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.IO;
2+
using System.Threading;
23
using System.Threading.Tasks;
34

45
namespace Red.Interfaces
@@ -22,12 +23,12 @@ public interface IBodyConverter
2223
/// <summary>
2324
/// Deserialize data from a stream to specified type
2425
/// </summary>
25-
Task<T?> DeserializeAsync<T>(Stream jsonStream)
26+
Task<T?> DeserializeAsync<T>(Stream jsonStream, CancellationToken cancellationToken = default)
2627
where T : class;
2728

2829
/// <summary>
2930
/// Serialize data to a stream
3031
/// </summary>
31-
Task SerializeAsync<T>(T obj, Stream jsonStream);
32+
Task SerializeAsync<T>(T obj, Stream jsonStream, CancellationToken cancellationToken = default);
3233
}
3334
}

src/RedHttpServer/RedHttpServerPrivate.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ private async Task<HandlerType> ExecuteHandler(HttpContext aspNetContext,
112112
if (aspNetContext.WebSockets.IsWebSocketRequest)
113113
{
114114
var webSocket = await aspNetContext.WebSockets.AcceptWebSocketAsync();
115-
var webSocketDialog = new WebSocketDialog(webSocket);
115+
var webSocketDialog = new WebSocketDialog(webSocket, request.Aborted);
116116

117117
foreach (var middleware in _wsMiddle.Concat(handlers))
118118
{

src/RedHttpServer/Request.cs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.IO;
33
using System.Linq;
4+
using System.Threading;
45
using System.Threading.Tasks;
56
using Microsoft.AspNetCore.Http;
67
using Microsoft.AspNetCore.Http.Headers;
@@ -32,6 +33,11 @@ internal Request(Context context) : base(context)
3233
/// </summary>
3334
public IQueryCollection Queries => AspNetRequest.Query;
3435

36+
/// <summary>
37+
/// The cancellation token the request being aborted
38+
/// </summary>
39+
public CancellationToken Aborted => AspNetRequest.HttpContext.RequestAborted;
40+
3541
/// <summary>
3642
/// The headers contained in the request
3743
/// </summary>
@@ -69,7 +75,7 @@ internal Request(Context context) : base(context)
6975
if (_form != null)
7076
return _form;
7177

72-
_form = await AspNetRequest.ReadFormAsync();
78+
_form = await AspNetRequest.ReadFormAsync(Aborted);
7379
return _form;
7480
}
7581

@@ -84,7 +90,7 @@ public async Task<bool> SaveFiles(string saveDir, Func<string, string>? fileRena
8490
long maxSizeKb = 50000)
8591
{
8692
if (!AspNetRequest.HasFormContentType) return false;
87-
var form = await AspNetRequest.ReadFormAsync();
93+
var form = await AspNetRequest.ReadFormAsync(Aborted);
8894
if (form.Files.Sum(file => file.Length) > maxSizeKb << 10)
8995
return false;
9096

@@ -94,11 +100,10 @@ public async Task<bool> SaveFiles(string saveDir, Func<string, string>? fileRena
94100
var filename = fileRenamer == null ? formFile.FileName : fileRenamer(formFile.FileName);
95101
filename = Path.GetFileName(filename);
96102
if (string.IsNullOrWhiteSpace(filename)) continue;
103+
97104
var filepath = Path.Combine(fullSaveDir, filename);
98-
using (var fileStream = File.Create(filepath))
99-
{
100-
await formFile.CopyToAsync(fileStream);
101-
}
105+
await using var fileStream = File.Create(filepath);
106+
await formFile.CopyToAsync(fileStream, Aborted);
102107
}
103108

104109
return true;

src/RedHttpServer/Response.cs

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.IO;
33
using System.Linq;
44
using System.Net;
5+
using System.Threading;
56
using System.Threading.Tasks;
67
using Microsoft.AspNetCore.Http;
78
using Red.Interfaces;
@@ -29,6 +30,11 @@ internal Response(Context context) : base(context)
2930
/// </summary>
3031
public IHeaderDictionary Headers => AspNetResponse.Headers;
3132

33+
/// <summary>
34+
/// The cancellation token the request being aborted
35+
/// </summary>
36+
public CancellationToken Aborted => AspNetResponse.HttpContext.RequestAborted;
37+
3238
/// <summary>
3339
/// Obsolete. Please used Headers property instead.
3440
/// This method Will be removed in a later version
@@ -63,14 +69,14 @@ public Task<HandlerType> Redirect(string redirectPath, bool permanent = false)
6369
public Task<HandlerType> SendString(string data, string contentType = "text/plain", string fileName = "",
6470
bool attachment = false, HttpStatusCode status = HttpStatusCode.OK)
6571
{
66-
return SendString(AspNetResponse, data, contentType, fileName, attachment, status);
72+
return SendString(AspNetResponse, data, contentType, fileName, attachment, status, Aborted);
6773
}
6874

6975
/// <summary>
7076
/// Static helper for use in middleware
7177
/// </summary>
7278
public static async Task<HandlerType> SendString(HttpResponse response, string data, string contentType,
73-
string fileName, bool attachment, HttpStatusCode status)
79+
string fileName, bool attachment, HttpStatusCode status, CancellationToken cancellationToken)
7480
{
7581
response.StatusCode = (int) status;
7682
response.ContentType = contentType;
@@ -80,7 +86,7 @@ public static async Task<HandlerType> SendString(HttpResponse response, string d
8086
response.Headers.Add("Content-disposition", contentDisposition);
8187
}
8288

83-
await response.WriteAsync(data);
89+
await response.WriteAsync(data, cancellationToken);
8490
return HandlerType.Final;
8591
}
8692

@@ -89,9 +95,9 @@ public static async Task<HandlerType> SendString(HttpResponse response, string d
8995
/// </summary>
9096
/// <param name="response">The HttpResponse object</param>
9197
/// <param name="status">The status code for the response</param>
92-
public static Task<HandlerType> SendStatus(HttpResponse response, HttpStatusCode status)
98+
public static Task<HandlerType> SendStatus(HttpResponse response, HttpStatusCode status, CancellationToken cancellationToken)
9399
{
94-
return SendString(response, status.ToString(), "text/plain", "", false, status);
100+
return SendString(response, status.ToString(), "text/plain", "", false, status, cancellationToken);
95101
}
96102

97103
/// <summary>
@@ -112,7 +118,7 @@ public async Task<HandlerType> SendJson<T>(T data, HttpStatusCode status = HttpS
112118
{
113119
AspNetResponse.StatusCode = (int) status;
114120
AspNetResponse.ContentType = "application/json";
115-
await Context.Plugins.Get<IJsonConverter>().SerializeAsync(data, AspNetResponse.Body);
121+
await Context.Plugins.Get<IJsonConverter>().SerializeAsync(data, AspNetResponse.Body, Aborted);
116122
return HandlerType.Final;
117123
}
118124

@@ -125,7 +131,7 @@ public async Task<HandlerType> SendXml<T>(T data, HttpStatusCode status = HttpSt
125131
{
126132
AspNetResponse.StatusCode = (int) status;
127133
AspNetResponse.ContentType = "application/xml";
128-
await Context.Plugins.Get<IXmlConverter>().SerializeAsync(data, AspNetResponse.Body);
134+
await Context.Plugins.Get<IXmlConverter>().SerializeAsync(data, AspNetResponse.Body, Aborted);
129135
return HandlerType.Final;
130136
}
131137

@@ -146,7 +152,7 @@ public async Task<HandlerType> SendStream(Stream dataStream, string contentType,
146152
if (!string.IsNullOrEmpty(fileName))
147153
Headers["Content-disposition"] = $"{(attachment ? "attachment" : "inline")}; filename=\"{fileName}\"";
148154

149-
await dataStream.CopyToAsync(AspNetResponse.Body);
155+
await dataStream.CopyToAsync(AspNetResponse.Body, Aborted);
150156
if (dispose) dataStream.Dispose();
151157

152158
return HandlerType.Final;
@@ -192,14 +198,14 @@ public async Task<HandlerType> SendFile(string filePath, string? contentType = n
192198
AspNetResponse.ContentType = Handlers.GetMimeType(contentType, filePath);
193199
AspNetResponse.ContentLength = length;
194200
Headers["Content-Range"] = $"bytes {offset}-{offset + length - 1}/{fileSize}";
195-
await AspNetResponse.SendFileAsync(filePath, offset, length);
201+
await AspNetResponse.SendFileAsync(filePath, offset, length, Aborted);
196202
}
197203
else
198204
{
199205
AspNetResponse.StatusCode = (int) status;
200206
AspNetResponse.ContentType = Handlers.GetMimeType(contentType, filePath);
201207
AspNetResponse.ContentLength = fileSize;
202-
await AspNetResponse.SendFileAsync(filePath);
208+
await AspNetResponse.SendFileAsync(filePath, Aborted);
203209
}
204210

205211
return HandlerType.Final;
@@ -222,7 +228,7 @@ public async Task<HandlerType> Download(string filePath, string? fileName = "",
222228
AspNetResponse.ContentType = Handlers.GetMimeType(contentType, filePath);
223229
var name = string.IsNullOrEmpty(fileName) ? Path.GetFileName(filePath) : fileName;
224230
Headers["Content-disposition"] = $"attachment; filename=\"{name}\"";
225-
await AspNetResponse.SendFileAsync(filePath);
231+
await AspNetResponse.SendFileAsync(filePath, Aborted);
226232
return HandlerType.Final;
227233
}
228234
}

src/RedHttpServer/WebSocketDialog.cs

Lines changed: 20 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Buffers;
3+
using System.Linq;
34
using System.Net.WebSockets;
45
using System.Text;
56
using System.Threading;
@@ -18,9 +19,12 @@ public sealed class WebSocketDialog
1819
/// </summary>
1920
public readonly WebSocket WebSocket;
2021

21-
internal WebSocketDialog(WebSocket webSocket)
22+
private readonly CancellationToken _requestAborted;
23+
24+
internal WebSocketDialog(WebSocket webSocket, CancellationToken requestAborted)
2225
{
2326
WebSocket = webSocket;
27+
_requestAborted = requestAborted;
2428
}
2529

2630

@@ -47,7 +51,7 @@ internal WebSocketDialog(WebSocket webSocket)
4751
public Task SendText(string text, bool endOfMessage = true)
4852
{
4953
return WebSocket.SendAsync(new ArraySegment<byte>(Encoding.UTF8.GetBytes(text)),
50-
WebSocketMessageType.Text, endOfMessage, CancellationToken.None);
54+
WebSocketMessageType.Text, endOfMessage, _requestAborted);
5155
}
5256

5357
/// <summary>
@@ -57,8 +61,7 @@ public Task SendText(string text, bool endOfMessage = true)
5761
/// <param name="endOfMessage"></param>
5862
public Task SendBytes(ArraySegment<byte> data, bool endOfMessage = true)
5963
{
60-
return WebSocket.SendAsync(data, WebSocketMessageType.Binary, endOfMessage,
61-
CancellationToken.None);
64+
return WebSocket.SendAsync(data, WebSocketMessageType.Binary, endOfMessage, _requestAborted);
6265
}
6366

6467
/// <summary>
@@ -71,16 +74,15 @@ public Task Close(
7174
WebSocketCloseStatus status = WebSocketCloseStatus.NormalClosure,
7275
string description = "")
7376
{
74-
return WebSocket.CloseAsync(status, description, CancellationToken.None);
77+
return WebSocket.CloseAsync(status, description, _requestAborted);
7578
}
7679

7780
internal async Task ReadFromWebSocket()
7881
{
7982
var buffer = ArrayPool<byte>.Shared.Rent(0x1000);
8083
try
8184
{
82-
var received =
83-
await WebSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
85+
var received = await WebSocket.ReceiveAsync(new ArraySegment<byte>(buffer), _requestAborted);
8486
while (!received.CloseStatus.HasValue)
8587
{
8688
switch (received.MessageType)
@@ -96,45 +98,32 @@ internal async Task ReadFromWebSocket()
9698
received.EndOfMessage));
9799
break;
98100
case WebSocketMessageType.Close:
99-
await WebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "",
100-
CancellationToken.None);
101+
await WebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "", _requestAborted);
101102
break;
102103
}
103104

104-
received = await WebSocket.ReceiveAsync(new ArraySegment<byte>(buffer),
105-
CancellationToken.None);
105+
received = await WebSocket.ReceiveAsync(new ArraySegment<byte>(buffer), _requestAborted);
106106
}
107107
}
108108
catch (WebSocketException)
109109
{
110110
}
111111
finally
112112
{
113+
try
114+
{
115+
if (!new [] { WebSocketState.Aborted, WebSocketState.Closed}.Contains(WebSocket.State))
116+
{
117+
await WebSocket.CloseAsync(WebSocketCloseStatus.InternalServerError, "", _requestAborted);
118+
}
119+
}
120+
catch (WebSocketException) { }
121+
113122
OnClosed?.Invoke(this, EventArgs.Empty);
114-
await WebSocket.CloseAsync(WebSocketCloseStatus.InternalServerError, "",
115-
CancellationToken.None);
116123
WebSocket.Dispose();
117124
}
118125
}
119126

120-
/// <summary>
121-
/// Convenience method for ending a dialog handler
122-
/// </summary>
123-
/// <returns></returns>
124-
public HandlerType Final()
125-
{
126-
return HandlerType.Final;
127-
}
128-
129-
/// <summary>
130-
/// Convenience method for ending a dialog handler
131-
/// </summary>
132-
/// <returns></returns>
133-
public HandlerType Continue()
134-
{
135-
return HandlerType.Continue;
136-
}
137-
138127
/// <inheritdoc />
139128
/// <summary>
140129
/// Represents a binary message received from websocket

0 commit comments

Comments
 (0)