diff --git a/CHANGELOG.md b/CHANGELOG.md index cf26714..907525b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +- Adds `ConfigureAwait(false)` calls to async calls (https://github.com/microsoft/kiota-http-dotnet/issues/240). + ## [1.3.11] - 2024-04-19 ## Changed diff --git a/src/Extensions/HttpRequestMessageExtensions.cs b/src/Extensions/HttpRequestMessageExtensions.cs index 60e2173..5d7cf7b 100644 --- a/src/Extensions/HttpRequestMessageExtensions.cs +++ b/src/Extensions/HttpRequestMessageExtensions.cs @@ -72,9 +72,9 @@ internal static async Task CloneAsync(this HttpRequestMessag // HttpClient doesn't rewind streams and we have to explicitly do so. var contentStream = new MemoryStream(); #if NET5_0_OR_GREATER - await originalRequest.Content.CopyToAsync(contentStream, cancellationToken); + await originalRequest.Content.CopyToAsync(contentStream, cancellationToken).ConfigureAwait(false); #else - await originalRequest.Content.CopyToAsync(contentStream); + await originalRequest.Content.CopyToAsync(contentStream).ConfigureAwait(false); #endif if(contentStream.CanSeek) diff --git a/src/HttpClientRequestAdapter.cs b/src/HttpClientRequestAdapter.cs index 5930733..bf30bf3 100644 --- a/src/HttpClientRequestAdapter.cs +++ b/src/HttpClientRequestAdapter.cs @@ -90,16 +90,16 @@ public string? BaseUrl public async Task?> SendCollectionAsync(RequestInformation requestInfo, ParsableFactory factory, Dictionary>? errorMapping = default, CancellationToken cancellationToken = default) where ModelType : IParsable { using var span = startTracingSpan(requestInfo, nameof(SendCollectionAsync)); - var response = await GetHttpResponseMessage(requestInfo, cancellationToken, span); + var response = await GetHttpResponseMessage(requestInfo, cancellationToken, span).ConfigureAwait(false); requestInfo.Content?.Dispose(); var responseHandler = GetResponseHandler(requestInfo); if(responseHandler == null) { try { - await ThrowIfFailedResponse(response, errorMapping, span, cancellationToken); + await ThrowIfFailedResponse(response, errorMapping, span, cancellationToken).ConfigureAwait(false); if(shouldReturnNull(response)) return default; - var rootNode = await GetRootParseNode(response, cancellationToken); + var rootNode = await GetRootParseNode(response, cancellationToken).ConfigureAwait(false); using var spanForDeserialization = activitySource?.StartActivity(nameof(IParseNode.GetCollectionOfObjectValues)); var result = rootNode?.GetCollectionOfObjectValues(factory); SetResponseType(result, span); @@ -107,13 +107,13 @@ public string? BaseUrl } finally { - await DrainAsync(response, cancellationToken); + await DrainAsync(response, cancellationToken).ConfigureAwait(false); } } else { span?.AddEvent(new ActivityEvent(EventResponseHandlerInvokedKey)); - return await responseHandler.HandleResponseAsync>(response, errorMapping); + return await responseHandler.HandleResponseAsync>(response, errorMapping).ConfigureAwait(false); } } /// @@ -126,16 +126,16 @@ public string? BaseUrl public async Task?> SendPrimitiveCollectionAsync(RequestInformation requestInfo, Dictionary>? errorMapping = default, CancellationToken cancellationToken = default) { using var span = startTracingSpan(requestInfo, nameof(SendPrimitiveCollectionAsync)); - var response = await GetHttpResponseMessage(requestInfo, cancellationToken, span); + var response = await GetHttpResponseMessage(requestInfo, cancellationToken, span).ConfigureAwait(false); requestInfo.Content?.Dispose(); var responseHandler = GetResponseHandler(requestInfo); if(responseHandler == null) { try { - await ThrowIfFailedResponse(response, errorMapping, span, cancellationToken); + await ThrowIfFailedResponse(response, errorMapping, span, cancellationToken).ConfigureAwait(false); if(shouldReturnNull(response)) return default; - var rootNode = await GetRootParseNode(response, cancellationToken); + var rootNode = await GetRootParseNode(response, cancellationToken).ConfigureAwait(false); using var spanForDeserialization = activitySource?.StartActivity(nameof(IParseNode.GetCollectionOfPrimitiveValues)); var result = rootNode?.GetCollectionOfPrimitiveValues(); SetResponseType(result, span); @@ -143,13 +143,13 @@ public string? BaseUrl } finally { - await DrainAsync(response, cancellationToken); + await DrainAsync(response, cancellationToken).ConfigureAwait(false); } } else { span?.AddEvent(new ActivityEvent(EventResponseHandlerInvokedKey)); - return await responseHandler.HandleResponseAsync>(response, errorMapping); + return await responseHandler.HandleResponseAsync>(response, errorMapping).ConfigureAwait(false); } } /// @@ -167,16 +167,16 @@ public string? BaseUrl public async Task SendAsync(RequestInformation requestInfo, ParsableFactory factory, Dictionary>? errorMapping = default, CancellationToken cancellationToken = default) where ModelType : IParsable { using var span = startTracingSpan(requestInfo, nameof(SendAsync)); - var response = await GetHttpResponseMessage(requestInfo, cancellationToken, span); + var response = await GetHttpResponseMessage(requestInfo, cancellationToken, span).ConfigureAwait(false); requestInfo.Content?.Dispose(); var responseHandler = GetResponseHandler(requestInfo); if(responseHandler == null) { try { - await ThrowIfFailedResponse(response, errorMapping, span, cancellationToken); + await ThrowIfFailedResponse(response, errorMapping, span, cancellationToken).ConfigureAwait(false); if(shouldReturnNull(response)) return default; - var rootNode = await GetRootParseNode(response, cancellationToken); + var rootNode = await GetRootParseNode(response, cancellationToken).ConfigureAwait(false); if(rootNode == null) return default; using var spanForDeserialization = activitySource?.StartActivity(nameof(IParseNode.GetObjectValue)); var result = rootNode.GetObjectValue(factory); @@ -187,14 +187,14 @@ public string? BaseUrl { if(typeof(ModelType) != typeof(Stream)) { - await DrainAsync(response, cancellationToken); + await DrainAsync(response, cancellationToken).ConfigureAwait(false); } } } else { span?.AddEvent(new ActivityEvent(EventResponseHandlerInvokedKey)); - return await responseHandler.HandleResponseAsync(response, errorMapping); + return await responseHandler.HandleResponseAsync(response, errorMapping).ConfigureAwait(false); } } /// @@ -209,21 +209,21 @@ public string? BaseUrl using var span = startTracingSpan(requestInfo, nameof(SendPrimitiveAsync)); var modelType = typeof(ModelType); var isStreamResponse = modelType == typeof(Stream); - var response = await GetHttpResponseMessage(requestInfo, cancellationToken, span, isStreamResponse: isStreamResponse); + var response = await GetHttpResponseMessage(requestInfo, cancellationToken, span, isStreamResponse: isStreamResponse).ConfigureAwait(false); requestInfo.Content?.Dispose(); var responseHandler = GetResponseHandler(requestInfo); if(responseHandler == null) { try { - await ThrowIfFailedResponse(response, errorMapping, span, cancellationToken); + await ThrowIfFailedResponse(response, errorMapping, span, cancellationToken).ConfigureAwait(false); if(shouldReturnNull(response)) return default; if(isStreamResponse) { #if NET5_0_OR_GREATER - var result = await response.Content.ReadAsStreamAsync(cancellationToken); + var result = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); #else - var result = await response.Content.ReadAsStreamAsync(); + var result = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); #endif if(result.CanSeek && result.Length == 0) { @@ -235,7 +235,7 @@ public string? BaseUrl } else { - var rootNode = await GetRootParseNode(response, cancellationToken); + var rootNode = await GetRootParseNode(response, cancellationToken).ConfigureAwait(false); object? result; using var spanForDeserialization = activitySource?.StartActivity($"Get{modelType.Name.TrimEnd('?')}Value"); if(rootNode == null) @@ -303,14 +303,14 @@ public string? BaseUrl { if(typeof(ModelType) != typeof(Stream)) { - await DrainAsync(response, cancellationToken); + await DrainAsync(response, cancellationToken).ConfigureAwait(false); } } } else { span?.AddEvent(new ActivityEvent(EventResponseHandlerInvokedKey)); - return await responseHandler.HandleResponseAsync(response, errorMapping); + return await responseHandler.HandleResponseAsync(response, errorMapping).ConfigureAwait(false); } } /// @@ -323,24 +323,24 @@ public string? BaseUrl public async Task SendNoContentAsync(RequestInformation requestInfo, Dictionary>? errorMapping = default, CancellationToken cancellationToken = default) { using var span = startTracingSpan(requestInfo, nameof(SendNoContentAsync)); - var response = await GetHttpResponseMessage(requestInfo, cancellationToken, span); + var response = await GetHttpResponseMessage(requestInfo, cancellationToken, span).ConfigureAwait(false); requestInfo.Content?.Dispose(); var responseHandler = GetResponseHandler(requestInfo); if(responseHandler == null) { try { - await ThrowIfFailedResponse(response, errorMapping, span, cancellationToken); + await ThrowIfFailedResponse(response, errorMapping, span, cancellationToken).ConfigureAwait(false); } finally { - await DrainAsync(response, cancellationToken); + await DrainAsync(response, cancellationToken).ConfigureAwait(false); } } else { span?.AddEvent(new ActivityEvent(EventResponseHandlerInvokedKey)); - await responseHandler.HandleResponseAsync(response, errorMapping); + await responseHandler.HandleResponseAsync(response, errorMapping).ConfigureAwait(false); } } private static void SetResponseType(object? result, Activity? activity) @@ -355,9 +355,9 @@ private static async Task DrainAsync(HttpResponseMessage response, CancellationT if(response.Content != null) { #if NET5_0_OR_GREATER - using var discard = await response.Content.ReadAsStreamAsync(cancellationToken); + using var discard = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); #else - using var discard = await response.Content.ReadAsStreamAsync(); + using var discard = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); #endif response.Content.Dispose(); } @@ -405,7 +405,7 @@ private async Task ThrowIfFailedResponse(HttpResponseMessage response, Dictionar } activityForAttributes?.SetTag(ErrorMappingFoundAttributeName, true); - var rootNode = await GetRootParseNode(response, cancellationToken); + var rootNode = await GetRootParseNode(response, cancellationToken).ConfigureAwait(false); activityForAttributes?.SetTag(ErrorBodyFoundAttributeName, rootNode != null); var spanForDeserialization = activitySource?.StartActivity(nameof(IParseNode.GetObjectValue)); var result = rootNode?.GetObjectValue(errorFactory); @@ -436,9 +436,9 @@ private async Task ThrowIfFailedResponse(HttpResponseMessage response, Dictionar if(string.IsNullOrEmpty(responseContentType)) return null; #if NET5_0_OR_GREATER - using var contentStream = await (response.Content?.ReadAsStreamAsync(cancellationToken) ?? Task.FromResult(Stream.Null)); + using var contentStream = await (response.Content?.ReadAsStreamAsync(cancellationToken) ?? Task.FromResult(Stream.Null)).ConfigureAwait(false); #else - using var contentStream = await (response.Content?.ReadAsStreamAsync() ?? Task.FromResult(Stream.Null)); + using var contentStream = await (response.Content?.ReadAsStreamAsync() ?? Task.FromResult(Stream.Null)).ConfigureAwait(false); #endif if(contentStream == Stream.Null || (contentStream.CanSeek && contentStream.Length == 0)) return null;// ensure a usefule stream is passed to the factory @@ -457,11 +457,11 @@ private async Task GetHttpResponseMessage(RequestInformatio SetBaseUrlForRequestInformation(requestInfo); var additionalAuthenticationContext = string.IsNullOrEmpty(claims) ? null : new Dictionary { { ClaimsKey, claims! } }; - await authProvider.AuthenticateRequestAsync(requestInfo, additionalAuthenticationContext, cancellationToken); + await authProvider.AuthenticateRequestAsync(requestInfo, additionalAuthenticationContext, cancellationToken).ConfigureAwait(false); using var message = GetRequestMessageFromRequestInformation(requestInfo, activityForAttributes); - var response = isStreamResponse ? await this.client.SendAsync(message, HttpCompletionOption.ResponseHeadersRead, cancellationToken) : - await this.client.SendAsync(message, cancellationToken); + var response = isStreamResponse ? await this.client.SendAsync(message, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false) : + await this.client.SendAsync(message, cancellationToken).ConfigureAwait(false); if(response == null) { var ex = new InvalidOperationException("Could not get a response after calling the service"); @@ -483,7 +483,7 @@ private async Task GetHttpResponseMessage(RequestInformatio activityForAttributes?.SetTag("http.status_code", (int)response.StatusCode); activityForAttributes?.SetTag("http.flavor", $"{response.Version.Major}.{response.Version.Minor}"); - return await RetryCAEResponseIfRequired(response, requestInfo, cancellationToken, claims, activityForAttributes); + return await RetryCAEResponseIfRequired(response, requestInfo, cancellationToken, claims, activityForAttributes).ConfigureAwait(false); } private static readonly Regex caeValueRegex = new("\"([^\"]*)\"", RegexOptions.Compiled, TimeSpan.FromMilliseconds(100)); /// @@ -507,8 +507,8 @@ private async Task RetryCAEResponseIfRequired(HttpResponseM span?.AddEvent(new ActivityEvent(AuthenticateChallengedEventKey)); activityForAttributes?.SetTag("http.retry_count", 1); requestInfo.Content?.Seek(0, SeekOrigin.Begin); - await DrainAsync(response, cancellationToken); - return await GetHttpResponseMessage(requestInfo, cancellationToken, activityForAttributes, responseClaims); + await DrainAsync(response, cancellationToken).ConfigureAwait(false); + return await GetHttpResponseMessage(requestInfo, cancellationToken, activityForAttributes, responseClaims).ConfigureAwait(false); } return response; } @@ -519,7 +519,7 @@ private void SetBaseUrlForRequestInformation(RequestInformation requestInfo) /// public async Task ConvertToNativeRequestAsync(RequestInformation requestInfo, CancellationToken cancellationToken = default) { - await authProvider.AuthenticateRequestAsync(requestInfo, null, cancellationToken); + await authProvider.AuthenticateRequestAsync(requestInfo, null, cancellationToken).ConfigureAwait(false); if(GetRequestMessageFromRequestInformation(requestInfo, null) is T result) return result; else throw new InvalidOperationException($"Could not convert the request information to a {typeof(T).Name}"); diff --git a/src/Middleware/ChaosHandler.cs b/src/Middleware/ChaosHandler.cs index ed791cb..89cd67d 100644 --- a/src/Middleware/ChaosHandler.cs +++ b/src/Middleware/ChaosHandler.cs @@ -87,7 +87,7 @@ protected override async Task SendAsync(HttpRequestMessage return chaosResponse; } - return await base.SendAsync(request, cancellationToken); + return await base.SendAsync(request, cancellationToken).ConfigureAwait(false); } finally { diff --git a/src/Middleware/CompressionHandler.cs b/src/Middleware/CompressionHandler.cs index 006c99b..1529fd7 100644 --- a/src/Middleware/CompressionHandler.cs +++ b/src/Middleware/CompressionHandler.cs @@ -54,7 +54,7 @@ protected override async Task SendAsync(HttpRequestMessage request.Headers.AcceptEncoding.Add(gzipQHeaderValue); } - HttpResponseMessage response = await base.SendAsync(request, cancellationToken); + HttpResponseMessage response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false); // Decompress response content when Content-Encoding: gzip header is present. if(ShouldDecompressContent(response)) diff --git a/src/Middleware/HeadersInspectionHandler.cs b/src/Middleware/HeadersInspectionHandler.cs index 2da2a16..96e4f14 100644 --- a/src/Middleware/HeadersInspectionHandler.cs +++ b/src/Middleware/HeadersInspectionHandler.cs @@ -59,7 +59,7 @@ protected override async Task SendAsync(HttpRequestMessage options.RequestHeaders[contentHeaders.Key] = contentHeaders.Value.ToArray(); } } - var response = await base.SendAsync(request, cancellationToken); + var response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false); if(options.InspectResponseHeaders) { foreach(var header in response.Headers) diff --git a/src/Middleware/RedirectHandler.cs b/src/Middleware/RedirectHandler.cs index 63f3281..7200aee 100644 --- a/src/Middleware/RedirectHandler.cs +++ b/src/Middleware/RedirectHandler.cs @@ -64,7 +64,7 @@ protected override async Task SendAsync(HttpRequestMessage { // send request first time to get response - var response = await base.SendAsync(request, cancellationToken); + var response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false); // check response status code and redirect handler option if(ShouldRedirect(response, redirectOption)) @@ -99,7 +99,7 @@ protected override async Task SendAsync(HttpRequestMessage { return response;// We can't clone the original request to replay it. } - var newRequest = await originalRequest.CloneAsync(cancellationToken); + var newRequest = await originalRequest.CloneAsync(cancellationToken).ConfigureAwait(false); // status code == 303: change request method from post to get and content to be null if(response.StatusCode == HttpStatusCode.SeeOther) @@ -135,7 +135,7 @@ protected override async Task SendAsync(HttpRequestMessage } // Send redirect request to get response - response = await base.SendAsync(newRequest, cancellationToken); + response = await base.SendAsync(newRequest, cancellationToken).ConfigureAwait(false); // Check response status code if(ShouldRedirect(response, redirectOption)) diff --git a/src/Middleware/RetryHandler.cs b/src/Middleware/RetryHandler.cs index b98c652..48f8a0d 100644 --- a/src/Middleware/RetryHandler.cs +++ b/src/Middleware/RetryHandler.cs @@ -105,7 +105,7 @@ private async Task SendRetryAsync(HttpResponseMessage respo while(retryCount < retryOption.MaxRetry) { - exceptions.Add(await GetInnerExceptionAsync(response)); + exceptions.Add(await GetInnerExceptionAsync(response).ConfigureAwait(false)); using var retryActivity = activitySource?.StartActivity($"{nameof(RetryHandler)}_{nameof(SendAsync)} - attempt {retryCount}"); retryActivity?.SetTag("http.retry_count", retryCount); retryActivity?.SetTag("http.status_code", response.StatusCode); @@ -139,7 +139,7 @@ private async Task SendRetryAsync(HttpResponseMessage respo AddOrUpdateRetryAttempt(request, retryCount); // Delay time - await delay; + await delay.ConfigureAwait(false); // Call base.SendAsync to send the request response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false); @@ -150,7 +150,7 @@ private async Task SendRetryAsync(HttpResponseMessage respo } } - exceptions.Add(await GetInnerExceptionAsync(response)); + exceptions.Add(await GetInnerExceptionAsync(response).ConfigureAwait(false)); throw new AggregateException($"Too many retries performed. More than {retryCount} retries encountered while sending the request.", exceptions); } diff --git a/src/Middleware/TelemetryHandler.cs b/src/Middleware/TelemetryHandler.cs index c1e67bd..c113c66 100644 --- a/src/Middleware/TelemetryHandler.cs +++ b/src/Middleware/TelemetryHandler.cs @@ -44,11 +44,11 @@ protected override async Task SendAsync(HttpRequestMessage if(telemetryHandlerOption.TelemetryConfigurator != null) { var enrichedRequest = telemetryHandlerOption.TelemetryConfigurator(request); - return await base.SendAsync(enrichedRequest, cancellationToken); + return await base.SendAsync(enrichedRequest, cancellationToken).ConfigureAwait(false); } // Just forward the request if TelemetryConfigurator was intentionally set to null - return await base.SendAsync(request, cancellationToken); + return await base.SendAsync(request, cancellationToken).ConfigureAwait(false); } } } diff --git a/src/Middleware/UriReplacementHandler.cs b/src/Middleware/UriReplacementHandler.cs index daadd3b..c1fe66d 100644 --- a/src/Middleware/UriReplacementHandler.cs +++ b/src/Middleware/UriReplacementHandler.cs @@ -42,7 +42,7 @@ protected override async Task SendAsync( try { request.RequestUri = uriReplacement.Replace(request.RequestUri); - return await base.SendAsync(request, cancellationToken); + return await base.SendAsync(request, cancellationToken).ConfigureAwait(false); } finally {