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

Async tweaks #40

Merged
merged 3 commits into from
Jan 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions MetaBrainz.MusicBrainz/Interfaces/IPagedQueryResults.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public interface IPagedQueryResults<TResults, out TItem> : IJsonBasedObject
/// <returns>This result set (with updated values).</returns>
/// <exception cref="QueryException">When the web service reports an error.</exception>
/// <exception cref="WebException">When something goes wrong with the web request.</exception>
Task<TResults> NextAsync(CancellationToken cancellationToken = new());
Task<TResults> NextAsync(CancellationToken cancellationToken = default);

/// <summary>
/// The offset to use for the next request (via <see cref="Next()"/> and/or <see cref="Previous()"/>), or <see langword="null"/>
Expand Down Expand Up @@ -89,7 +89,7 @@ public interface IPagedQueryResults<TResults, out TItem> : IJsonBasedObject
/// <returns>This result set (with updated values).</returns>
/// <exception cref="QueryException">When the web service reports an error.</exception>
/// <exception cref="WebException">When something goes wrong with the web request.</exception>
Task<TResults> PreviousAsync(CancellationToken cancellationToken = new());
Task<TResults> PreviousAsync(CancellationToken cancellationToken = default);

/// <summary>The current results.</summary>
IReadOnlyList<TItem> Results { get; }
Expand Down
86 changes: 36 additions & 50 deletions MetaBrainz.MusicBrainz/OAuth2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ public IAuthorizationToken GetBearerToken(string code, string clientSecret, Uri
/// <param name="cancellationToken">The cancellation token to cancel the operation.</param>
/// <returns>The obtained bearer token.</returns>
public Task<IAuthorizationToken> GetBearerTokenAsync(string code, string clientSecret, Uri redirectUri,
CancellationToken cancellationToken = new())
CancellationToken cancellationToken = default)
=> this.RequestTokenAsync("bearer", code, clientSecret, redirectUri, cancellationToken);

/// <summary>Refreshes a bearer token.</summary>
Expand All @@ -235,7 +235,7 @@ public IAuthorizationToken RefreshBearerToken(string refreshToken, string client
/// <param name="cancellationToken">The cancellation token to cancel the operation.</param>
/// <returns>The obtained bearer token.</returns>
public Task<IAuthorizationToken> RefreshBearerTokenAsync(string refreshToken, string clientSecret,
CancellationToken cancellationToken = new())
CancellationToken cancellationToken = default)
=> this.RefreshTokenAsync("bearer", refreshToken, clientSecret, cancellationToken);

#endregion
Expand All @@ -256,8 +256,6 @@ public Task<IAuthorizationToken> RefreshBearerTokenAsync(string refreshToken, st

private Func<HttpClient>? _clientCreation;

private readonly SemaphoreSlim _clientLock = new(1);

private readonly bool _clientOwned;

private bool _disposed;
Expand Down Expand Up @@ -285,14 +283,7 @@ public void Close() {
if (!this._clientOwned) {
throw new InvalidOperationException("An explicitly provided client instance is in use.");
}
this._clientLock.Wait();
try {
this._client?.Dispose();
this._client = null;
}
finally {
this._clientLock.Release();
}
Interlocked.Exchange(ref this._client, null)?.Dispose();
}

/// <summary>Sets up code to run to configure a newly-created HTTP client.</summary>
Expand Down Expand Up @@ -328,7 +319,6 @@ private void Dispose(bool disposing) {
this.Close();
}
this._client = null;
this._clientLock.Dispose();
}
finally {
this._disposed = true;
Expand All @@ -348,52 +338,48 @@ private void Dispose(bool disposing) {
private async Task<HttpResponseMessage> PerformRequestAsync(Uri uri, HttpMethod method, HttpContent? body,
CancellationToken cancellationToken) {
Debug.Print($"[{DateTime.UtcNow}] WEB SERVICE REQUEST: {method.Method} {uri}");
await this._clientLock.WaitAsync(cancellationToken);
try {
var client = this.Client;
using var request = new HttpRequestMessage(method, uri) {
Content = body,
Headers = {
Accept = {
OAuth2.AcceptHeader,
},
}
};
// Use whatever user agent the client has set, plus our own.
foreach (var userAgent in client.DefaultRequestHeaders.UserAgent) {
request.Headers.UserAgent.Add(userAgent);
var client = this.Client;
using var request = new HttpRequestMessage(method, uri) {
Content = body,
Headers = {
Accept = {
OAuth2.AcceptHeader,
},
}
request.Headers.UserAgent.Add(OAuth2.LibraryProductInfo);
request.Headers.UserAgent.Add(OAuth2.LibraryComment);
Debug.Print($"[{DateTime.UtcNow}] => HEADERS: {Utils.FormatMultiLine(request.Headers.ToString())}");
if (body is not null) {
// FIXME: Should this include the actual body text too?
Debug.Print($"[{DateTime.UtcNow}] => BODY ({body.Headers.ContentType}): {body.Headers.ContentLength ?? 0} bytes");
}
var response = await client.SendAsync(request, cancellationToken);
Debug.Print($"[{DateTime.UtcNow}] WEB SERVICE RESPONSE: {(int) response.StatusCode}/{response.StatusCode} " +
$"'{response.ReasonPhrase}' (v{response.Version})");
Debug.Print($"[{DateTime.UtcNow}] => HEADERS: {Utils.FormatMultiLine(response.Headers.ToString())}");
Debug.Print($"[{DateTime.UtcNow}] => CONTENT ({response.Content.Headers.ContentType}): " +
$"{response.Content.Headers.ContentLength ?? 0} bytes");
return response;
};
// Use whatever user agent the client has set, plus our own.
foreach (var userAgent in client.DefaultRequestHeaders.UserAgent) {
request.Headers.UserAgent.Add(userAgent);
}
finally {
this._clientLock.Release();
request.Headers.UserAgent.Add(OAuth2.LibraryProductInfo);
request.Headers.UserAgent.Add(OAuth2.LibraryComment);
Debug.Print($"[{DateTime.UtcNow}] => HEADERS: {Utils.FormatMultiLine(request.Headers.ToString())}");
if (body is not null) {
// FIXME: Should this include the actual body text too?
Debug.Print($"[{DateTime.UtcNow}] => BODY ({body.Headers.ContentType}): {body.Headers.ContentLength ?? 0} bytes");
}
var response = await client.SendAsync(request, cancellationToken).ConfigureAwait(false);
Debug.Print($"[{DateTime.UtcNow}] WEB SERVICE RESPONSE: {(int) response.StatusCode}/{response.StatusCode} " +
$"'{response.ReasonPhrase}' (v{response.Version})");
Debug.Print($"[{DateTime.UtcNow}] => HEADERS: {Utils.FormatMultiLine(response.Headers.ToString())}");
Debug.Print($"[{DateTime.UtcNow}] => CONTENT ({response.Content.Headers.ContentType}): " +
$"{response.Content.Headers.ContentLength ?? 0} bytes");
return response;
}

private async Task<AuthorizationToken> PostAsync(HttpContent body, CancellationToken cancellationToken) {
private async Task<AuthorizationToken> PostAsync(HttpContent content, CancellationToken cancellationToken) {
var uri = new UriBuilder(this.UrlScheme, this.Server, this.Port, OAuth2.TokenEndPoint).Uri;
var response = await this.PerformRequestAsync(uri, HttpMethod.Post, body, cancellationToken);
var response = await this.PerformRequestAsync(uri, HttpMethod.Post, content, cancellationToken).ConfigureAwait(false);
if (!response.IsSuccessStatusCode) {
throw await Utils.CreateQueryExceptionForAsync(response, cancellationToken);
throw await Utils.CreateQueryExceptionForAsync(response, cancellationToken).ConfigureAwait(false);
}
return await Utils.GetJsonContentAsync<AuthorizationToken>(response, OAuth2.JsonReaderOptions, cancellationToken);
var jsonTask = Utils.GetJsonContentAsync<AuthorizationToken>(response, OAuth2.JsonReaderOptions, cancellationToken);
return await jsonTask.ConfigureAwait(false);
}

private async Task<IAuthorizationToken> PostAsync(string type, string body, CancellationToken cancellationToken) {
var token = await this.PostAsync(new StringContent(body, Encoding.UTF8, OAuth2.TokenRequestBodyType), cancellationToken);
var content = new StringContent(body, Encoding.UTF8, OAuth2.TokenRequestBodyType);
var token = await this.PostAsync(content, cancellationToken).ConfigureAwait(false);
if (token.TokenType != type) {
throw new InvalidOperationException($"Token request returned a token of the wrong type ('{token.TokenType}' != '{type}').");
}
Expand All @@ -408,7 +394,7 @@ private async Task<IAuthorizationToken> RefreshTokenAsync(string type, string co
body.Append("&token_type=").Append(Uri.EscapeDataString(type));
body.Append("&grant_type=refresh_token");
body.Append("&refresh_token=").Append(Uri.EscapeDataString(codeOrToken));
return await this.PostAsync(type, body.ToString(), cancellationToken);
return await this.PostAsync(type, body.ToString(), cancellationToken).ConfigureAwait(false);
}

private async Task<IAuthorizationToken> RequestTokenAsync(string type, string codeOrToken, string clientSecret, Uri redirectUri,
Expand All @@ -420,7 +406,7 @@ private async Task<IAuthorizationToken> RequestTokenAsync(string type, string co
body.Append("&grant_type=authorization_code");
body.Append("&code=").Append(Uri.EscapeDataString(codeOrToken));
body.Append("&redirect_uri=").Append(Uri.EscapeDataString(redirectUri.ToString()));
return await this.PostAsync(type, body.ToString(), cancellationToken);
return await this.PostAsync(type, body.ToString(), cancellationToken).ConfigureAwait(false);
}

private static IEnumerable<string> ScopeStrings(AuthorizationScope scope) {
Expand Down
3 changes: 2 additions & 1 deletion MetaBrainz.MusicBrainz/Objects/Browses/BrowseResults.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ protected BrowseResults(Query query, string endpoint, string? value, string extr

protected sealed override async Task<IBrowseResults<TResult>> DeserializeAsync(HttpResponseMessage response,
CancellationToken cancellationToken) {
this.CurrentResult = await Utils.GetJsonContentAsync<BrowseResult>(response, Query.JsonReaderOptions, cancellationToken);
var task = Utils.GetJsonContentAsync<BrowseResult>(response, Query.JsonReaderOptions, cancellationToken);
this.CurrentResult = await task.ConfigureAwait(false);
return this;
}

Expand Down
4 changes: 2 additions & 2 deletions MetaBrainz.MusicBrainz/Objects/PagedQueryResults.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ protected PagedQueryResults(Query query, string endpoint, string? value, int? li

public TResults Next() => Utils.ResultOf(this.NextAsync());

public async Task<TResults> NextAsync(CancellationToken cancellationToken = new()) {
public async Task<TResults> NextAsync(CancellationToken cancellationToken = default) {
this.UpdateOffset(this.Results.Count);
return await this.PerformRequestAsync(cancellationToken).ConfigureAwait(false);
}
Expand All @@ -44,7 +44,7 @@ protected PagedQueryResults(Query query, string endpoint, string? value, int? li

public TResults Previous() => Utils.ResultOf(this.PreviousAsync());

public async Task<TResults> PreviousAsync(CancellationToken cancellationToken = new()) {
public async Task<TResults> PreviousAsync(CancellationToken cancellationToken = default) {
this.UpdateOffset();
return await this.PerformRequestAsync(cancellationToken).ConfigureAwait(false);
}
Expand Down
3 changes: 2 additions & 1 deletion MetaBrainz.MusicBrainz/Objects/Searches/SearchResults.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ protected SearchResults(Query query, string endpoint, string queryString, int? l

protected sealed override async Task<ISearchResults<TInterface>> DeserializeAsync(HttpResponseMessage response,
CancellationToken cancellationToken) {
this.CurrentResult = await Utils.GetJsonContentAsync<SearchResults>(response, Query.JsonReaderOptions, cancellationToken);
var task = Utils.GetJsonContentAsync<SearchResults>(response, Query.JsonReaderOptions, cancellationToken);
this.CurrentResult = await task.ConfigureAwait(false);
if (this.Offset != this.CurrentResult.Offset) {
Debug.Print($"Unexpected offset in search results: {this.Offset} != {this.CurrentResult.Offset}.");
}
Expand Down
6 changes: 3 additions & 3 deletions MetaBrainz.MusicBrainz/Objects/StreamingQueryResults.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ public StreamingQueryResults(PagedQueryResults<TResult, TItem, TResultObject> pa

#region IAsyncEnumerable

public async IAsyncEnumerator<TItem> GetAsyncEnumerator(CancellationToken cancellationToken = new()) {
public async IAsyncEnumerator<TItem> GetAsyncEnumerator(CancellationToken cancellationToken = default) {
IPagedQueryResults<TResult, TItem> currentPage = this._pagedResults;
if (!currentPage.IsActive) {
currentPage = await currentPage.NextAsync(cancellationToken);
currentPage = await currentPage.NextAsync(cancellationToken).ConfigureAwait(false);
if (cancellationToken.IsCancellationRequested) {
yield break;
}
Expand All @@ -36,7 +36,7 @@ public StreamingQueryResults(PagedQueryResults<TResult, TItem, TResultObject> pa
if (currentPage.Offset + currentPage.Results.Count >= currentPage.TotalResults || cancellationToken.IsCancellationRequested) {
break;
}
currentPage = await currentPage.NextAsync(cancellationToken);
currentPage = await currentPage.NextAsync(cancellationToken).ConfigureAwait(false);
}
}

Expand Down
2 changes: 1 addition & 1 deletion MetaBrainz.MusicBrainz/Objects/Submissions/Submission.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public abstract class Submission : ISubmission {
/// <returns>A message describing the result (usually "OK").</returns>
/// <exception cref="QueryException">When the MusicBrainz web service reports an error.</exception>
/// <exception cref="System.Net.WebException">When the MusicBrainz web service could not be contacted.</exception>
public async Task<string> SubmitAsync(CancellationToken cancellationToken = new())
public async Task<string> SubmitAsync(CancellationToken cancellationToken = default)
=> await this._query.PerformSubmissionAsync(this, cancellationToken).ConfigureAwait(false);

#endregion
Expand Down
4 changes: 2 additions & 2 deletions MetaBrainz.MusicBrainz/Query.Browse.Areas.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public IBrowseResults<IArea> BrowseAreas(ICollection collection, int? limit = nu
/// <exception cref="QueryException">When the web service reports an error.</exception>
/// <exception cref="WebException">When something goes wrong with the web request.</exception>
public Task<IBrowseResults<IArea>> BrowseAreasAsync(ICollection collection, int? limit = null, int? offset = null,
Include inc = Include.None, CancellationToken cancellationToken = new())
Include inc = Include.None, CancellationToken cancellationToken = default)
=> new BrowseAreas(this, Query.BuildExtraText(inc, "collection", collection.Id), limit, offset).NextAsync(cancellationToken);

/// <summary>Returns (the specified subset of) the areas in the given collection.</summary>
Expand All @@ -93,7 +93,7 @@ public IBrowseResults<IArea> BrowseCollectionAreas(Guid mbid, int? limit = null,
/// <exception cref="WebException">When something goes wrong with the web request.</exception>
public Task<IBrowseResults<IArea>> BrowseCollectionAreasAsync(Guid mbid, int? limit = null, int? offset = null,
Include inc = Include.None,
CancellationToken cancellationToken = new())
CancellationToken cancellationToken = default)
=> new BrowseAreas(this, Query.BuildExtraText(inc, "collection", mbid), limit, offset).NextAsync(cancellationToken);

}
Loading