-
Notifications
You must be signed in to change notification settings - Fork 60
Description
Hi.
I've found that we are getting a ObjectDisposedException
Cannot access a disposed object. Object name: 'System.Net.Http.StreamContent
at this line when we get more than one consecutive 429 response for a request towards the contentful api due to request limit.
response = await _httpClient.SendAsync(clonedMessage, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false); |
I think I understand why and I think it is because of the introduced using
statment here
using var clonedMessage = await CloneHttpRequest(response.RequestMessage); |
If the MaxNumberOfRateLimitRetries
is set to allow more than 1 retry, when do the retry we overwrite the response
object with a new response based on the clonedMessage
. This would result in the response.RequestMessage
being a reference to this clonedMessage
. And since the introduction of the using
statement, this clonedMessage
will be disposed directly after the retry which also would result in the response.RequestMessage
being disposed as well since it is a reference.
So every time we reach the request limit and get multiple 429 messages, we will get the ObjectDisposedException
exception when trying to make a clone out of the response.RequestMessage
So this will always fail if the response of the request get 2 or more 429 responses.
What is it that is failing? It is when the message is being cloned in the CloneHttpRequest
method. It is trying to copy the request message content into a MemoryStream here
await message.Content.CopyToAsync(ms).ConfigureAwait(false); |
Since the request message is being disposed so is this StreamContent object in the message that is being copied.
A possible solution could be to store the response.RequestMessage
in a variable at the begining of this method and always clone based of this variable. Something kind of like this:
protected async Task<HttpResponseMessage> EnsureSuccessfulResult(HttpResponseMessage response)
{
if (!response.IsSuccessStatusCode)
{
var requestMessage = response.RequestMessage;
if(response.StatusCode == System.Net.HttpStatusCode.NotModified)
{
return response;
}
if((int)response.StatusCode == 429 && _options.MaxNumberOfRateLimitRetries > 0)
{
//Limit retries to 10 regardless of config
for (var i = 0; i < _options.MaxNumberOfRateLimitRetries && i < 10; i++)
{
try
{
await CreateExceptionForFailedRequest(response).ConfigureAwait(false); ;
}
catch (ContentfulRateLimitException ex)
{
await Task.Delay(ex.SecondsUntilNextRequest * 1000).ConfigureAwait(false);
}
using var clonedMessage = await CloneHttpRequest(requestMessage);
response = await _httpClient.SendAsync(clonedMessage, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
if (response.IsSuccessStatusCode)
{
return response;
}
}
}
await CreateExceptionForFailedRequest(response);
}
return response;
}