diff --git a/Contentful.AspNetCore/Contentful.AspNetCore.csproj b/Contentful.AspNetCore/Contentful.AspNetCore.csproj index 995bda4..9fcdc13 100644 --- a/Contentful.AspNetCore/Contentful.AspNetCore.csproj +++ b/Contentful.AspNetCore/Contentful.AspNetCore.csproj @@ -3,7 +3,7 @@ Official .NET SDK for the Contentful Content Delivery and Management API for ASP.NET core. contentful.aspnetcore en-US - 8.3.1 + 8.3.2 netstandard2.0 Contentful Contentful GmbH. @@ -13,10 +13,10 @@ https://github.com/contentful/contentful.net MIT git - 8.3.1 - 8.3.1.0 + 8.3.2 + 8.3.2.0 https://github.com/contentful/contentful.net - 8.3.1.0 + 8.3.2.0 bin\Release\netstandard1.5\Contentful.AspNetCore.xml @@ -25,7 +25,7 @@ $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb - + diff --git a/Contentful.Core.Tests/ContentfulClientTests.cs b/Contentful.Core.Tests/ContentfulClientTests.cs index 4215045..5c48e84 100644 --- a/Contentful.Core.Tests/ContentfulClientTests.cs +++ b/Contentful.Core.Tests/ContentfulClientTests.cs @@ -1695,6 +1695,38 @@ public async Task CreateEmbargoedAssetKeyShouldReturnCorrectKey() Assert.Equal("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjE6MSJ9.eyJleHAiOjE2Mzc2MjM4MDAsInN1YiI6InRlc3RzcGFjZXZhbHVlIiwiYXVkIjoiYWRuIiwianRpIjoiMDAwMDAwMDAtMDAwMC0wMDAwLTAwMDAtMDAwMDAwMDAwMDAwIiwiY3RmOnVucHViIjp0cnVlfQ.BHtBHdrInzu3KtGxTJ7FLxGF0WN9HEdJ9C5CeB3hx7g", res.Policy); } + [Fact] + public async Task RateLimitWithMultipleRetriesShouldNotThrowObjectDisposedException() + { + //Arrange + _handler = new FakeMessageHandler(); + var httpClient = new HttpClient(_handler); + _client = new ContentfulClient(httpClient, new ContentfulOptions() + { + DeliveryApiKey = "123", + ManagementApiKey = "123", + SpaceId = "666", + UsePreviewApi = false, + MaxNumberOfRateLimitRetries = 3 + }); + + var response = GetResponseFromFile(@"ErrorRateLimit.json"); + response.StatusCode = (HttpStatusCode)429; + response.Headers.Add("X-Contentful-RateLimit-Reset", "1"); + + _handler.Response = response; + var numberOfTimesCalled = 0; + _handler.VerificationBeforeSend = () => { numberOfTimesCalled++; }; + _handler.VerifyRequest = (HttpRequestMessage msg) => { response.RequestMessage = msg; }; + + //Act & Assert + // This should not throw ObjectDisposedException + var ex = await Assert.ThrowsAsync(async () => await _client.GetEntry("12")); + + Assert.Equal(1, ex.SecondsUntilNextRequest); + Assert.Equal(4, numberOfTimesCalled); // 1 initial request + 3 retries + } + private ContentfulClient GetClientWithEnvironment(string env = "special") { var httpClient = new HttpClient(_handler); diff --git a/Contentful.Core/Contentful.Core.csproj b/Contentful.Core/Contentful.Core.csproj index 193261c..f909cfb 100644 --- a/Contentful.Core/Contentful.Core.csproj +++ b/Contentful.Core/Contentful.Core.csproj @@ -4,7 +4,7 @@ contentful.csharp contentful.net en-US - 8.3.1 + 8.3.2 netstandard2.0 Contentful Contentful GmbH. diff --git a/Contentful.Core/ContentfulClientBase.cs b/Contentful.Core/ContentfulClientBase.cs index a8b83c8..7381f40 100644 --- a/Contentful.Core/ContentfulClientBase.cs +++ b/Contentful.Core/ContentfulClientBase.cs @@ -309,6 +309,9 @@ protected async Task EnsureSuccessfulResult(HttpResponseMes { if (!response.IsSuccessStatusCode) { + // Store the original request message to prevent disposal issues during retries + var requestMessage = response.RequestMessage; + if(response.StatusCode == System.Net.HttpStatusCode.NotModified) { return response; @@ -320,14 +323,15 @@ protected async Task EnsureSuccessfulResult(HttpResponseMes { try { - await CreateExceptionForFailedRequest(response).ConfigureAwait(false); ; + await CreateExceptionForFailedRequest(response).ConfigureAwait(false); } catch (ContentfulRateLimitException ex) { await Task.Delay(ex.SecondsUntilNextRequest * 1000).ConfigureAwait(false); } - using var clonedMessage = await CloneHttpRequest(response.RequestMessage); + // Clone from the original request message to avoid disposed message issues + using var clonedMessage = await CloneHttpRequest(requestMessage); response = await _httpClient.SendAsync(clonedMessage, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);