Skip to content

Commit 3138ae3

Browse files
adding cancellation to async send methods and better timeout error handling.
1 parent 31234c4 commit 3138ae3

File tree

2 files changed

+68
-33
lines changed

2 files changed

+68
-33
lines changed

src/SocketLabs/InjectionApi/ISocketLabsClient.cs

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

45
namespace SocketLabs.InjectionApi
@@ -14,15 +15,17 @@ public interface ISocketLabsClient
1415
/// Asynchronously sends a basic email message and returns the response from the Injection API.
1516
/// </summary>
1617
/// <param name="message">A <c>BasicMessage</c> object to be sent.</param>
18+
/// <param name="cancellationToken">A <c>CancellationToken</c> to handle cancellation between async threads.</param>
1719
/// <returns>A <c>SendResponse</c> of an SocketLabsClient send request.</returns>
18-
Task<SendResponse> SendAsync(IBasicMessage message);
20+
Task<SendResponse> SendAsync(IBasicMessage message, CancellationToken cancellationToken);
1921

2022
/// <summary>
2123
/// Asynchronously sends a bulk email message and returns the response from the Injection API.
2224
/// </summary>
2325
/// <param name="message">A <c>BulkMessage</c> object to be sent.</param>
26+
/// <param name="cancellationToken">A <c>CancellationToken</c> to handle cancellation between async threads.</param>
2427
/// <returns>A <c>SendResponse</c> of an SocketLabsClient send request.</returns>
25-
Task<SendResponse> SendAsync(IBulkMessage message);
28+
Task<SendResponse> SendAsync(IBulkMessage message, CancellationToken cancellationToken);
2629

2730
/// <summary>
2831
/// Synchronously sends a basic email message and returns the response from the Injection API.

src/SocketLabs/InjectionApi/SocketLabsClient.cs

Lines changed: 62 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Net.Http;
44
using System.Reflection;
55
using System.Runtime.ExceptionServices;
6+
using System.Threading;
67
using System.Threading.Tasks;
78
using SocketLabs.InjectionApi.Core;
89
using SocketLabs.InjectionApi.Message;
@@ -193,6 +194,7 @@ public static SendResponse QuickSend(
193194
/// Asynchronously sends a basic email message and returns the response from the Injection API.
194195
/// </summary>
195196
/// <param name="message">A <c>BasicMessage</c> object to be sent.</param>
197+
/// <param name="cancellationToken">A <c>CancellationToken</c> to handle cancellation between async threads.</param>
196198
/// <returns>A <c>SendResponse</c> of an SocketLabsClient send request.</returns>
197199
/// <example>
198200
/// This sample shows you how to Send a Basic Message
@@ -216,31 +218,39 @@ public static SendResponse QuickSend(
216218
/// }
217219
///</code>
218220
/// </example>
219-
public async Task<SendResponse> SendAsync(IBasicMessage message)
220-
{
221-
var validator = new SendValidator();
221+
public async Task<SendResponse> SendAsync(IBasicMessage message, CancellationToken cancellationToken)
222+
{
223+
try
224+
{
225+
var validator = new SendValidator();
222226

223-
var validationResult = validator.ValidateCredentials(_serverId, _apiKey);
224-
if (validationResult.Result != SendResult.Success) return validationResult;
225-
226-
validationResult = validator.ValidateMessage(message);
227-
if(validationResult.Result != SendResult.Success) return validationResult;
227+
var validationResult = validator.ValidateCredentials(_serverId, _apiKey);
228+
if (validationResult.Result != SendResult.Success) return validationResult;
228229

229-
var factory = new InjectionRequestFactory(_serverId, _apiKey);
230-
var injectionRequest = factory.GenerateRequest(message);
231-
var json = injectionRequest.GetAsJson();
230+
validationResult = validator.ValidateMessage(message);
231+
if (validationResult.Result != SendResult.Success) return validationResult;
232232

233-
_httpClient.Timeout = TimeSpan.FromSeconds(RequestTimeout);
234-
var httpResponse = await _httpClient.PostAsync(EndpointUrl,json);
233+
var factory = new InjectionRequestFactory(_serverId, _apiKey);
234+
var injectionRequest = factory.GenerateRequest(message);
235+
var json = injectionRequest.GetAsJson();
235236

236-
var response = new InjectionResponseParser().Parse(httpResponse);
237-
return response;
237+
_httpClient.Timeout = TimeSpan.FromSeconds(RequestTimeout);
238+
var httpResponse = await _httpClient.PostAsync(EndpointUrl, json, cancellationToken);
239+
240+
var response = new InjectionResponseParser().Parse(httpResponse);
241+
return response;
242+
}
243+
catch (OperationCanceledException) when (!cancellationToken.IsCancellationRequested)
244+
{
245+
throw new TimeoutException();
246+
}
238247
}
239-
248+
240249
/// <summary>
241250
/// Asynchronously sends a bulk email message and returns the response from the Injection API.
242251
/// </summary>
243252
/// <param name="message">A <c>BulkMessage</c> object to be sent.</param>
253+
/// <param name="cancellationToken">A <c>CancellationToken</c> to handle cancellation between async threads.</param>
244254
/// <returns>A <c>SendResponse</c> of an SocketLabsClient send request.</returns>
245255
/// <example>
246256
/// This sample shows you how to Send a Bulk Message
@@ -268,24 +278,31 @@ public async Task<SendResponse> SendAsync(IBasicMessage message)
268278
/// }
269279
///</code>
270280
/// </example>
271-
public async Task<SendResponse> SendAsync(IBulkMessage message)
281+
public async Task<SendResponse> SendAsync(IBulkMessage message, CancellationToken cancellationToken)
272282
{
273-
var validator = new SendValidator();
283+
try
284+
{
285+
var validator = new SendValidator();
274286

275-
var validationResult = validator.ValidateCredentials(_serverId, _apiKey);
276-
if (validationResult.Result != SendResult.Success) return validationResult;
287+
var validationResult = validator.ValidateCredentials(_serverId, _apiKey);
288+
if (validationResult.Result != SendResult.Success) return validationResult;
277289

278-
validationResult = validator.ValidateMessage(message);
279-
if (validationResult.Result != SendResult.Success) return validationResult;
290+
validationResult = validator.ValidateMessage(message);
291+
if (validationResult.Result != SendResult.Success) return validationResult;
280292

281-
var factory = new InjectionRequestFactory(_serverId, _apiKey);
282-
var injectionRequest = factory.GenerateRequest(message);
293+
var factory = new InjectionRequestFactory(_serverId, _apiKey);
294+
var injectionRequest = factory.GenerateRequest(message);
283295

284-
_httpClient.Timeout = TimeSpan.FromSeconds(RequestTimeout);
285-
var httpResponse = await _httpClient.PostAsync(EndpointUrl, injectionRequest.GetAsJson());
296+
_httpClient.Timeout = TimeSpan.FromSeconds(RequestTimeout);
297+
var httpResponse = await _httpClient.PostAsync(EndpointUrl, injectionRequest.GetAsJson(), cancellationToken);
286298

287-
var response = new InjectionResponseParser().Parse(httpResponse);
288-
return response;
299+
var response = new InjectionResponseParser().Parse(httpResponse);
300+
return response;
301+
}
302+
catch (OperationCanceledException) when (!cancellationToken.IsCancellationRequested)
303+
{
304+
throw new TimeoutException();
305+
}
289306
}
290307

291308
/// <summary>
@@ -319,8 +336,15 @@ public SendResponse Send(IBasicMessage message)
319336
{
320337
try
321338
{
339+
var source = new CancellationTokenSource();
322340
//Read this if you have questions: https://blogs.msdn.microsoft.com/pfxteam/2012/04/13/should-i-expose-synchronous-wrappers-for-asynchronous-methods/
323-
return Task.Run(() => SendAsync(message)).Result;
341+
var sendTask = Task.Run(() => SendAsync(message, source.Token));
342+
343+
while (!sendTask.IsCompleted) { }
344+
if (sendTask.Status == TaskStatus.Faulted) throw sendTask.Exception;
345+
346+
return sendTask.Result;
347+
324348
}
325349
//for synchronous usage, try to simplify exceptions being thrown
326350
catch (AggregateException e)
@@ -366,9 +390,15 @@ public SendResponse Send(IBulkMessage message)
366390
{
367391
try
368392
{
393+
var source = new CancellationTokenSource();
394+
369395
//Read this if you have questions: https://blogs.msdn.microsoft.com/pfxteam/2012/04/13/should-i-expose-synchronous-wrappers-for-asynchronous-methods/
370-
return Task.Run(() => SendAsync(message)).Result;
396+
var sendTask = Task.Run(() => SendAsync(message, source.Token));
397+
398+
while (!sendTask.IsCompleted) { }
399+
if (sendTask.Status == TaskStatus.Faulted) throw sendTask.Exception;
371400

401+
return sendTask.Result;
372402
}
373403
//for synchronous usage, try to simplify exceptions being thrown
374404
catch (AggregateException e)
@@ -386,5 +416,7 @@ public void Dispose()
386416
{
387417
_httpClient?.Dispose();
388418
}
419+
420+
389421
}
390422
}

0 commit comments

Comments
 (0)