Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] Refit + Fusillade fails with System.ObjectDisposedException #1048

Closed
JeremyBP opened this issue Feb 2, 2021 · 5 comments
Closed

[BUG] Refit + Fusillade fails with System.ObjectDisposedException #1048

JeremyBP opened this issue Feb 2, 2021 · 5 comments
Labels

Comments

@JeremyBP
Copy link
Contributor

JeremyBP commented Feb 2, 2021

Describe the bug
Actually the same as #460 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.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.

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
@clairernovotny
Copy link
Member

clairernovotny commented Feb 2, 2021

This looks like a bug in Fusillade. I added a test for parallel requests and it's passing with the mock handler:

https://github.com/reactiveui/refit/blob/main/Refit.Tests/RestService.cs#L1003-L1033

If Fusillade is improperly disposing things, that could be interfering.

GitHub
The automatic type-safe REST library for .NET Core, Xamarin and .NET. Heavily inspired by Square's Retrofit library, Refit turns your REST API into a live interface. - reactiveui/refit

@clairernovotny
Copy link
Member

clairernovotny commented Feb 2, 2021

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.

@JeremyBP
Copy link
Contributor Author

JeremyBP commented Feb 2, 2021

Thanks for your timely reply! 👍
I agree that parallel requests with Refit and without Fusillade works as expected.
I have to admit that parallel requests with Fusillade and without Refit works as expected to.
I opened the same issue on the Fusillade side so that we could cross investigate things.
Seems to me like the middle of a bridge :)

@clairernovotny
Copy link
Member

Closing as external.

@github-actions
Copy link

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 13, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

2 participants