diff --git a/libs/TrybeSDK/ApiClient.cs b/libs/TrybeSDK/ApiClient.cs index 1278f28..19e2649 100644 --- a/libs/TrybeSDK/ApiClient.cs +++ b/libs/TrybeSDK/ApiClient.cs @@ -2,8 +2,10 @@ // For a copy, see . using System.Net; +using System.Net.Http; using System.Net.Http.Headers; using System.Net.Http.Json; +using System.Text; using System.Text.Json; using System.Text.Json.Serialization; @@ -33,7 +35,7 @@ protected internal async Task SendAsync( CancellationToken cancellationToken = default) { Ensure.IsNotNull(request, nameof(request)); - var httpReq = CreateHttpRequest(request); + using var httpReq = CreateHttpRequest(request); HttpResponseMessage? httpResp = null; try @@ -41,22 +43,15 @@ protected internal async Task SendAsync( httpResp = await _http.SendAsync(httpReq, cancellationToken) .ConfigureAwait(false); - var transformedResponse = await TransformResponse( + var (transformedResponse, capturedResponseContent) = await TransformResponse( httpReq.Method, httpReq.RequestUri, httpResp) .ConfigureAwait(false); - if (_settings.CaptureRequestContent && httpReq.Content is not null) - { - transformedResponse.RequestContent = await httpReq.Content.ReadAsStringAsync() - .ConfigureAwait(false); - } - - if (_settings.CaptureResponseContent && httpResp.Content is not null) + if (_settings.CaptureResponseContent || !httpResp.IsSuccessStatusCode) { - transformedResponse.ResponseContent = await httpResp.Content.ReadAsStringAsync() - .ConfigureAwait(false); ; + transformedResponse.ResponseContent = capturedResponseContent; } return transformedResponse; @@ -92,7 +87,7 @@ protected internal async Task SendAsync( where TRequest : notnull { Ensure.IsNotNull(request, nameof(request)); - var httpReq = CreateHttpRequest(request); + var (httpReq, capturedRequestContent) = CreateHttpRequest(request); HttpResponseMessage? httpResp = null; try @@ -100,22 +95,20 @@ protected internal async Task SendAsync( httpResp = await _http.SendAsync(httpReq, cancellationToken) .ConfigureAwait(false); - var transformedResponse = await TransformResponse( + var (transformedResponse, capturedResponseContent) = await TransformResponse( httpReq.Method, httpReq.RequestUri, httpResp) .ConfigureAwait(false); ; - if (_settings.CaptureRequestContent && httpReq.Content is not null) + if (_settings.CaptureRequestContent) { - transformedResponse.RequestContent = await httpReq.Content.ReadAsStringAsync() - .ConfigureAwait(false); + transformedResponse.RequestContent = capturedRequestContent; } - if (_settings.CaptureResponseContent && httpResp.Content is not null) + if (_settings.CaptureResponseContent || !httpResp.IsSuccessStatusCode) { - transformedResponse.ResponseContent = await httpResp.Content.ReadAsStringAsync() - .ConfigureAwait(false); + transformedResponse.ResponseContent = capturedResponseContent; } return transformedResponse; @@ -163,13 +156,7 @@ protected internal async Task> FetchAsync( httpReq.Method, httpReq.RequestUri, httpResp) - .ConfigureAwait(false); ; - - if ((_settings.CaptureRequestContent || !httpResp.IsSuccessStatusCode) && httpReq.Content is not null) - { - transformedResponse.RequestContent = await httpReq.Content.ReadAsStringAsync() - .ConfigureAwait(false); ; - } + .ConfigureAwait(false); if (_settings.CaptureResponseContent || !httpResp.IsSuccessStatusCode) { @@ -201,6 +188,18 @@ protected internal async Task> FetchAsync( return response; } + finally + { + if (httpReq is not null) + { + httpReq.Dispose(); + } + + if (httpResp is not null) + { + httpResp.Dispose(); + } + } } protected internal async Task> FetchAsync( @@ -210,7 +209,7 @@ protected internal async Task> FetchAsync> FetchAsync> FetchAsync( + protected internal (HttpRequestMessage, string?) CreateHttpRequest( TrybeRequest request) where TRequest : notnull { var message = CreateHttpRequest((TrybeRequest)request); + string? capturedContent = null; - message.Content = JsonContent.Create( - inputValue: request.Data, options: _serializerOptions); + if (_settings.CaptureRequestContent) + { + capturedContent = JsonSerializer.Serialize(request.Data, _serializerOptions); + message.Content = new StringContent(capturedContent, Encoding.UTF8, "application/json"); + } + else + { + message.Content = JsonContent.Create( + inputValue: request.Data, options: _serializerOptions); + } - return message; + return (message, capturedContent); } #endregion #region Postprocessing - protected internal async Task TransformResponse( + protected internal async Task<(TrybeResponse, string?)> TransformResponse( HttpMethod method, Uri uri, HttpResponseMessage response, CancellationToken cancellationToken = default) { - async Task GetTrybeError() + var rateLimiting = GetRateLimiting(response); + + if (response.IsSuccessStatusCode) + { + return (new TrybeResponse( + method, + uri, + response.IsSuccessStatusCode, + response.StatusCode, + rateLimiting: rateLimiting), null); + } + else { Error error; - if (response.Content is not null) - { - var result = await response.Content.ReadFromJsonAsync(cancellationToken) + string? stringContent = await response.Content.ReadAsStringAsync() .ConfigureAwait(false); + if (stringContent is { Length: >0 }) + { + var result = JsonSerializer.Deserialize(stringContent, _deserializerOptions); if (result?.Message is not { Length: > 0 }) { @@ -323,29 +354,14 @@ async Task GetTrybeError() error = new Error(Resources.ApiClient_NoErrorMessage); } - return error; - } - - if (response.IsSuccessStatusCode) - { - return new TrybeResponse( - method, - uri, - response.IsSuccessStatusCode, - response.StatusCode); - } - else - { - Error? error = await GetTrybeError() - .ConfigureAwait(false); - - return new TrybeResponse( + return (new TrybeResponse( method, uri, response.IsSuccessStatusCode, response.StatusCode, + rateLimiting: rateLimiting, error: error - ); + ), stringContent); } } @@ -356,32 +372,6 @@ async Task GetTrybeError() CancellationToken cancellationToken = default) where TResponse : class { - async Task GetTrybeError() - { - Error error; - if (response.Content is not null) - { - var result = await response.Content.ReadFromJsonAsync( - _deserializerOptions, cancellationToken) - .ConfigureAwait(false); - - if (result?.Message is not { Length: > 0 }) - { - error = new(Resources.ApiClient_UnknownResponse, result?.Errors); - } - else - { - error = new(result.Message, result.Errors); - } - } - else - { - error = new Error(Resources.ApiClient_NoErrorMessage); - } - - return error; - } - var rateLimiting = GetRateLimiting(response); Stream? content = null; @@ -404,11 +394,11 @@ async Task GetTrybeError() { DataContainer? data = default; Meta? meta = default; - if (content is not null) + if (content is not null || stringContent is { Length: >0 }) { data = stringContent is { Length: > 0 } ? JsonSerializer.Deserialize>(stringContent, _deserializerOptions) - : await JsonSerializer.DeserializeAsync>(content, _deserializerOptions).ConfigureAwait(false); + : await JsonSerializer.DeserializeAsync>(content!, _deserializerOptions).ConfigureAwait(false); if (data?.Meta is not null) { @@ -434,8 +424,24 @@ async Task GetTrybeError() } else { - Error? error = await GetTrybeError() - .ConfigureAwait(false); + Error error; + if (stringContent is not null) + { + var result = JsonSerializer.Deserialize(stringContent, _deserializerOptions); + + if (result?.Message is not { Length: > 0 }) + { + error = new(Resources.ApiClient_UnknownResponse, result?.Errors); + } + else + { + error = new(result.Message, result.Errors); + } + } + else + { + error = new Error(Resources.ApiClient_NoErrorMessage); + } return (new TrybeResponse( method,