Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for cancellation tokens #178

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

euantorano
Copy link

@euantorano euantorano commented Sep 27, 2023

What problem does the pull request solve?

Fixes #176 by adding support for passing CancellationToken parameters to asynchronous methods.

Also fixes #130 - rather than accessing a Task result directly the task should be awaited.

These changes allow early cancellation of requests.

Checklist

  • I’ve used the pull request template
  • I’ve written unit tests for these changes
  • I’ve update the documentation (in DOCUMENATION.md and CHANGELOG.md)
  • I’ve bumped the version number (in src/GovukNotify/GovukNotify.csproj)

@@ -34,55 +35,71 @@ public BaseClient(IHttpClient client, string apiKey, string baseUrl = "https://a
this.client.BaseAddress = ValidateBaseUri(BaseUrl);
this.client.AddContentHeader("application/json");

var productVersion = typeof(BaseClient).GetTypeInfo().Assembly.GetName().Version.ToString();
var productVersion = typeof(BaseClient).GetTypeInfo().Assembly.GetName().Version?.ToString();
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is required as there was the possibility of a NullReferenceException in the original code - the Version attribute may be null.

If nullable types were enabled for the project it would likely help catch other similar problems.

Comment on lines +64 to +70
#if NET6_0_OR_GREATER
var responseContent = await response.Content.ReadAsByteArrayAsync(cancellationToken)
.ConfigureAwait(false);
#else
var responseContent = await response.Content.ReadAsByteArrayAsync()
.ConfigureAwait(false);
#endif
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passing cancellation tokens when reading content is only supported in .NET standard 2.1 and up. This project currently targets .net standard 2.0 and .net 6, so this conditional compilation should work.

Comment on lines +86 to +92
#if NET6_0_OR_GREATER
var responseContent = await response.Content.ReadAsStringAsync(cancellationToken)
.ConfigureAwait(false);
#else
var responseContent = await response.Content.ReadAsStringAsync()
.ConfigureAwait(false);
#endif
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, as before this conditional compilation is required due to the target framework.

}
catch (AggregateException ae)
{
ae.Handle(x =>
{
if (x is HttpRequestException)
{
throw new NotifyClientException(x.InnerException.Message);
throw new NotifyClientException(x.InnerException?.Message ?? x.Message);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's possible for InnerException to be null here, which would lead to an uncaught NullReferenceException.

Comment on lines +151 to +153
if (string.IsNullOrWhiteSpace(fromApiKey) || fromApiKey.Length < 74 || fromApiKey.Contains(' '))
#else
if (string.IsNullOrWhiteSpace(fromApiKey) || fromApiKey.Length < 74 || fromApiKey.Contains(" "))
Copy link
Author

@euantorano euantorano Sep 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the original code the .Length property was accessed before checking for null, which led to NullReferenceExceptions when an api key was not set.

Comment on lines 33 to 52
public void Dispose()
{
_client.Dispose();
Dispose(true);
GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}

if (disposing)
{
_client.Dispose();
}

_disposed = true;
}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

if (ex.InnerExceptions != null && ex.InnerExceptions.Count == 1)
if (ex.InnerExceptions.Count == 1)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

InnerExceptions cannot be null, so there's no need for a null check here.

@euantorano euantorano marked this pull request as ready for review September 27, 2023 11:40
@euantorano
Copy link
Author

I'm not sure why but it looks like the CI may be stalled on this one. Would someone mind giving it a nudge?

The changes mentioned in the previous comment will be explored in a future PR after this one has been merged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Cancellation support for async methods Task result obtained synchronously inside an asynchronous method
1 participant