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

Retry proxied request #84

Open
joaocpribeiro opened this issue Oct 6, 2021 · 7 comments
Open

Retry proxied request #84

joaocpribeiro opened this issue Oct 6, 2021 · 7 comments

Comments

@joaocpribeiro
Copy link

joaocpribeiro commented Oct 6, 2021

Hi, I am trying to proxy some requests with a different authorization header (bearer token).
Now I would like to retry the request if the response is 401, so that I could use refresh the token first. Also I would like to retry only once. Is there a way to do it?

Note: For now I am defining the proxy on the controller.

@joaocpribeiro
Copy link
Author

I found a solution by myself after discussing with colleagues. Calling the proxy again from the WithAfterReceive function seems to work.

@twitchax
Copy link
Owner

twitchax commented Oct 6, 2021

Hmmmm. Glad you found a solution, but retry logic might be interesting to add.

@joaocpribeiro
Copy link
Author

Sorry for commenting here again after one week, but it looks like I did not test as much as I should.
My code to test the retry was something like the following (changing status codes for testing purposes):

private HttpProxyOptions GetHttpProxyOptions(bool refreshTokenIfUnauthorized = true)
{
	var httpProxyOptions = HttpProxyOptionsBuilder.Instance
		.WithAfterReceive(async (c, hrm) =>
		{
			if (!refreshTokenIfUnauthorized)
			{
				hrm.StatusCode = HttpStatusCode.Forbidden;
				return;
			}

			if (hrm.StatusCode == HttpStatusCode.OK) hrm.StatusCode = HttpStatusCode.NotAcceptable;
			await this.HttpProxyAsync(hrm.RequestMessage.RequestUri.AbsoluteUri, GetHttpProxyOptions(refreshTokenIfUnauthorized: false));
		})
		.Build();
	return httpProxyOptions;
}

And the main call in the controller was:

public async Task Test()
{
	await this.HttpProxyAsync(testUrl, GetHttpProxyOptions());
}

As expected, this Test action is responding with 403 (Forbidden)... but only when the response has body. When the response has no body, the status code in the response is 406 (NotAcceptable). In both cases (with and without body) the 'WithAfterReceive' is reached twice, as expected. This is quite strange, and after I briefly check the code I did not find any reason for this to happen.
Another finding is that, if I read the response body - even though I don't need it for anything - (adapted code following), I always get the desired 403 response.

			if (!refreshTokenIfUnauthorized)
			{
				hrm.StatusCode = HttpStatusCode.Forbidden;
				await hrm.Content.ReadAsStringAsync();
				return;
			}

Even though I found a way to obtain the desired behavior, I would like to avoid this hack. :)
Any thoughts?

@twitchax
Copy link
Owner

How would you feel about a WithRetryIf handler?

@joaocpribeiro
Copy link
Author

joaocpribeiro commented Oct 21, 2021

@twitchax It sounds great to me. Important (IMO) would be the following acceptance criteria:

  • Retry if a condition is satisfied.
  • Allow other code to run before retry or if we decide to not retry.

Are you thinking about bringing up this feature? Do you have an estimation when it would be ready?
Thanks in advance!

@joaocpribeiro joaocpribeiro reopened this Oct 21, 2021
@h82258652
Copy link

I think we can do this with Polly.
Install Microsoft.Extensions.Http.Polly nuget package to your project.

public void ConfigureServices(IServiceCollection services)
{
-    services.AddProxies();

+    services.AddRouting();
+    services
+        .AddHttpClient("AspNetCore.Proxy.HttpProxyClient")// due to Helpers.HttpProxyClientName is internel.
+        .AddTransientHttpErrorPolicy(policy => policy
+            .OrResult(response => response.StatusCode == HttpStatusCode.NotFound)
+            .RetryAsync(3));// if response failed or not found then retry 3 times.
}

@joaocpribeiro
Copy link
Author

@h82258652 Sorry for the delayed response. Maybe it works, but I am struggling with an operation that I need to do between attempts. I need to access the user's claims. I noticed the RetryAsync has an onRetry action as parameter, but I don't find a way to access the claims from there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants