You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[Optional] Sponsorship to speed up the bug fix or feature request (example)
Description
I believe I have found a bug in the C# generic host generator's RateLimitProvider implementation.
I have an API that requires 2 headers:
ClientId
Authorization
Following the instructions in the generated README.md file, I configure my API as such:
builder.Host.ConfigureApi((context,services,options)=>{varconfig=context.Configuration.GetSection("[REDACTED]");varbaseAddress=config.GetValue<string>("BaseAddress",string.Empty);varapiKey=config.GetValue<string>("ApiKey",string.Empty);varclientId=config.GetValue<string>("ClientId",string.Empty);options.AddTokens([// "prefix: string.Empty" removes the "Bearer " prefix (The API doesn't support it).newApiKeyToken(clientId,ClientUtils.ApiKeyHeader.ClientId,prefix:string.Empty),newApiKeyToken(apiKey,ClientUtils.ApiKeyHeader.Authorization,prefix:string.Empty),]);options.UseProvider<RateLimitProvider<ApiKeyToken>,ApiKeyToken>();options.AddApiHttpClients(
httpClient =>{httpClient.BaseAddress=newUri(baseAddress);},
httpClientBuilder =>httpClientBuilder.AddRetryPolicy(2).AddTimeoutPolicy(TimeSpan.FromSeconds(5)).AddCircuitBreakerPolicy(10,TimeSpan.FromSeconds(30)));});
When making calls to the API in a for loop, I've run into an issue where the API will either:
Return a 401, stating that my Authorization header was missing.
Throw an exception, stating that the Authorization header only supports 1 value.
The above is inconsistent, which led me suspect a threading or timing issue (the default token provider is the RateLimitProvider, after all).
I eventually found this code block inside the RateLimitProvider class:
_tokens is an array of ApiKeyToken (handled by the base TokenProvider class). In my case: ClientId and Authorization.
AvailableTokens is a Dictionary<string, Channel<ApiKeyToken>>, which becomes: {"ClientId": Channel(ApiKeyToken), "Authorization": Channel(ApiKeyToken)}
However, the foreach loop goes over every value in AvailableTokens and then the for loop goes over every value in _tokens, so you end up overwriting all the tokens with whichever token happens to be the last one.
Sometimes (it's timing-based), it will fail by trying to add Authorization twice. Other times, it will fail by creating an invalid ClientId, which will have the same value twice.
When adding the TokenBecameAvailable handler, make sure the token that is being written to matches the one doing the writing (consider modifying the for loop instead of iterating over all the values in _tokens).
The text was updated successfully, but these errors were encountered:
This is an excellent write up, thanks for that. The section of code you suspect is in the constructor of a class which is a singleton, so it can't really be the issue. I think the section you're interested in is the GetAsync method in the rate limit provider. It should always retrieve the correct token as long as the header parameter was provided, so I'm not really sure what is wrong here. If you figure it out, you can provide your own class to work around the issue now, but I hope we can send a PR once we identify the issue.
Bug Report Checklist
Description
I believe I have found a bug in the C# generic host generator's
RateLimitProvider
implementation.I have an API that requires 2 headers:
ClientId
Authorization
Following the instructions in the generated
README.md
file, I configure my API as such:When making calls to the API in a for loop, I've run into an issue where the API will either:
401
, stating that myAuthorization
header was missing.Authorization
header only supports 1 value.The above is inconsistent, which led me suspect a threading or timing issue (the default token provider is the
RateLimitProvider
, after all).I eventually found this code block inside the
RateLimitProvider
class:A quick breakdown of the problem:
_tokens
is an array ofApiKeyToken
(handled by the baseTokenProvider
class). In my case:ClientId
andAuthorization
.AvailableTokens
is aDictionary<string, Channel<ApiKeyToken>>
, which becomes:{"ClientId": Channel(ApiKeyToken), "Authorization": Channel(ApiKeyToken)}
foreach
loop goes over every value inAvailableTokens
and then thefor
loop goes over every value in_tokens
, so you end up overwriting all the tokens with whichever token happens to be the last one.Authorization
twice. Other times, it will fail by creating an invalidClientId
, which will have the same value twice.openapi-generator version
openapi-generator-cli 7.14.0-SNAPSHOT
commit : 65c3126
built : -999999999-01-01T00:00:00+18:00
source : https://github.com/openapitools/openapi-generator
docs : https://openapi-generator.tech/
Generation Details
Generator:
csharp
Additional properties:
packageName=[REDACTED]
targetFramework=net9.0
nullableReferenceTypes=true
useDateTimeOffset=true
useSourceGeneration=true
netCoreProjectFile=true
apiName=[REDACTED]
equatable=false
Steps to reproduce
README.md
file):for
loop.Suggest a fix
When adding the
TokenBecameAvailable
handler, make sure the token that is being written to matches the one doing the writing (consider modifying thefor
loop instead of iterating over all the values in_tokens
).The text was updated successfully, but these errors were encountered: