Fusillade
Fusillade copied to clipboard
[BUG] Refit + Fusillade fails with System.ObjectDisposedException
Describe the bug Actually the same as #34 witch is closed but not fixed. Using Refit with Fusillade fails with System.ObjectDisposedException: Cannot access a disposed object. Object name: 'System.Net.Http.HttpConnectionResponseContent'. The exception is thrown at Refit.RequestBuilderImplementation.<DeserializeContentAsync>d__15`1.MoveNext() in /_/Refit/RequestBuilderImplementation.cs:line 324 It seems that when Fusillade deduplicates requests returning val.Response.ToTask(cancellationToken), Refit fails deserializing HttpContent because it's disposed.
As @clairernovotny on the Refit side with issue #1048 think it looks like a Fusillade bug, I opened the same issue on the Fusillade side. I don't know if the bug comes from Refit or Fusillade, but I know it throws when using one with each other.
Steps To Reproduce Create a Refit RestService with any Fusillade's NetCache HttpMessageHandler into the HttpClient, and send the same request at least twice at the same time. Something dummy like:
try
{
var reqResService = RestService.For<IReqResService>(new HttpClient(NetCache.UserInitiated)
{
BaseAddress = new Uri("https://reqres.in/api")
});
var task1 = reqResService.GetUsersAsync();
var task2 = reqResService.GetUsersAsync();
var result = await Task.WhenAll(task1, task2);
var userList = result.FirstOrDefault();
}
catch (Exception e)
{
throw;
}
with IReqResService:
public interface IReqResService
{
[Get("/users")]
Task<UserList> GetUsersAsync();
}
and Models:
public class UserList
{
[JsonProperty("page")]
public int Page { get; set; }
[JsonProperty("per_page")]
public int PerPage { get; set; }
[JsonProperty("total")]
public int Total { get; set; }
[JsonProperty("total_pages")]
public int TotalPages { get; set; }
[JsonProperty("data")]
public List<User> Data { get; set; }
}
public class User
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("first_name")]
public string FirstName { get; set; }
[JsonProperty("last_name")]
public string LastName { get; set; }
[JsonProperty("avatar")]
public string Avatar { get; set; }
[JsonProperty("email")]
public string Email { get; set; }
}
Well interface and models are not the point, it's just to illustrate the bug. Note that everything succeed when using Fusillade without Refit, I mean using HttpClient directly like:
try
{
var client = new HttpClient(NetCache.UserInitiated)
{
BaseAddress = new Uri("https://reqres.in/api/users")
};
var task1 = client.GetAsync("");
var task2 = client.GetAsync("");
var responses = await Task.WhenAll(task1, task2);
var jsonString = await responses.First().Content.ReadAsStringAsync().ConfigureAwait(false);
var userList = JsonConvert.DeserializeObject<UserList>(jsonString);
}
catch (Exception e)
{
throw;
}
Note that everything succeed to, when using Refit without Fusillade like:
try
{
var reqResService = RestService.For<IReqResService>("https://reqres.in/api");
var task1 = reqResService.GetUsersAsync();
var task2 = reqResService.GetUsersAsync();
var result = await Task.WhenAll(task1, task2);
var userList = result.FirstOrDefault();
}
catch (Exception e)
{
throw;
}
So it's only when using both of it.
Expected behavior Should return result
Environment
- OS: All
- Device: All
- Version: All
Looking a little closer, Refit always disposes it's underlying HttpRequestMessage when a concrete type is returned. If Fusillade is caching things, that would break.
Is there any possibility to use refit with fusillade together ?
I'm not using refit, just fusilade and it was causing exceptions to be thrown in Android when using httpclient concurrently:
Object name: 'System.Net.Http.StreamContent'.) ---> System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'System.Net.Http.StreamContent'.
at System.Net.Http.HttpContent.CheckDisposed () <0xa17d609c + 0x00048> in <6ec2f38857a34d7ca25a155c6e4c5a96>:0
at System.Net.Http.HttpContent.LoadIntoBufferAsync (System.Int64 maxBufferSize, System.Threading.CancellationToken cancellationToken) <0xa17d5404 + 0x0003f> in <6ec2f38857a34d7ca25a155c6e4c5a96>:0
at System.Net.Http.HttpClient.FinishSendAsyncBuffered (System.Threading.Tasks.Task`1[TResult] sendTask, System.Net.Http.HttpRequestMessage request, System.Threading.CancellationTokenSource cts, System.Boolean disposeCts) <0xa17d2c50 + 0x00628> in <6ec2f38857a34d7ca25a155c6e4c5a96>:0
Given this and it's issue on iOS #161 I'm thinking about looking at other ways to rate limit the httpclient.