I'm using async/await to make multiple requests per second to a service using their API. The problem I am running into is when I need to refresh the token (it expries every hour). After the token has expired, I get a 401 unauthorized error back from the service. That's when I refresh the token, and retry the failed request again. The token gets refreshed fine, but what I'm finding is that even after the token is refreshed, a lot of subsequent requests are still sent with the old token. Following are the methods that are used in this functionality. Wondering if anything standouts as being the culprit for this unintended behavior.
public void Process(id)
{
var tasks = items.Select(async item =>
{
var response = await SendRequestAsync(() => CreateRequest(item.Url));
//do something with response
await Process(item.subId); //recursive call to process sub items.
}).ToList();
if (tasks.Any())
await Task.WhenAll(tasks);
}
public HttpRequestMessage CreateRequest(string url)
{
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Add("Authorization", "Bearer " + AppSettings.AccessToken);
return request;
}
public async Task<HttpResponseMessage> SendRequestAsync(Func<HttpRequestMessage> funcReq)
{
var response = await ExecuteRequestAsync(funcReq());
while (response.StatusCode == HttpStatusCode.Unauthorized)
{
await RefreshTokenAsync();
return await ExecuteRequestAsync(funcReq()); //assuming func ensures that CreateRequest is called each time, so I'll always have a new request with the updated token.
}
return response;
}
private async Task<HttpResponseMessage> ExecuteRequestAsync(HttpRequestMessage request)
{
var client = new HttpClient();
var response = await client.SendAsync(request);
return response;
}
public async Task RefreshTokenAsync()
{
await semaphoreSlim.WaitAsync();
try
{
if ((DateTime.Now - refreshTime).TotalMinutes < 60) //tokens last for an hour, so after the refresh is made by the first request that failed, subsequent requests should have the latest token.
return;
Token newToken = GetNewToken();
AppSettings.AccessToken = newToken.AccessToken //AppSettings is a singleton wrapper class for app.cofig app settings
refreshTime = DateTime.Now
}
finally
{
semaphoreSlim.Release();
}
}