Skip to content

Commit

Permalink
Add rate-limiter per access token
Browse files Browse the repository at this point in the history
  • Loading branch information
richardrandak committed Oct 1, 2020
1 parent a1c1d07 commit c263718
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 22 deletions.
2 changes: 2 additions & 0 deletions FortnoxAPILibrary.Tests/TestErrorScenarios.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public void Test_NoRateLimiter_TooManyRequest_Error()
ErrorInformation error = null;
for (var i = 0; i < 200; i++)
{
connector.City = TestUtils.RandomString();
connector.Find();
if (connector.HasError)
{
Expand All @@ -57,6 +58,7 @@ public void Test_NoRateLimiter_TooManyRequest_Error()
ConnectionSettings.UseRateLimiter = true;

//Assert
//Assert.IsTrue(failed > 0);
Assert.IsNotNull(error);
Assert.IsTrue(error.Message.Contains("Too Many Requests")); }
}
Expand Down
43 changes: 21 additions & 22 deletions FortnoxAPILibrary/BaseClient.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
Expand All @@ -11,25 +12,18 @@ public class BaseClient
{
private const string AccessTokenHeader = "Access-Token";
private const string ClientSecretHeader = "Client-Secret";


private const int LimitPerSecond = 3;
private static readonly Dictionary<string, TimeLimiter> RateLimiters = new Dictionary<string, TimeLimiter>();

/// <summary>
/// Http client used under-the-hood for all request (except file upload due to a server-side limitation)
/// </summary>
private HttpClient HttpClient { get; }

/// <summary>
/// Optional Fortnox Access Token, if used it will override the static version.
/// </summary>
public string AccessToken { get; set; }

/// <summary>
/// Optional Fortnox Client Secret, if used it will override the static version.
/// </summary>
public string ClientSecret { get; set; }

/// <summary>
/// Timeout of requests sent to the Fortnox API in miliseconds
/// </summary>
public int Timeout
{
get => (int) HttpClient.Timeout.TotalMilliseconds;
Expand All @@ -39,25 +33,18 @@ public int Timeout
/// <summary>
/// Base fortnox server URI
/// </summary>
public string BaseUrl
{
get => HttpClient.BaseAddress.AbsoluteUri;
set => HttpClient.BaseAddress = new Uri(value);
}
public string BaseUrl { get; set; }

public bool UseRateLimiter { get; set; }

private const int LimitPerSecond = 3;
private static readonly TimeLimiter RateLimiter = TimeLimiter.GetFromMaxCountByInterval(LimitPerSecond, TimeSpan.FromSeconds(1));

public BaseClient()
{
HttpClient = new HttpClient();

AccessToken = ConnectionCredentials.AccessToken;
ClientSecret = ConnectionCredentials.ClientSecret;
BaseUrl = ConnectionSettings.FortnoxAPIServer;
Timeout = 30 * 10000;
Timeout = ConnectionSettings.Timeout;
UseRateLimiter = ConnectionSettings.UseRateLimiter;
}

Expand All @@ -67,7 +54,7 @@ public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request)
request.Headers.Add(ClientSecretHeader, ClientSecret);

if (UseRateLimiter)
await RateLimiter; //throttle every call to Fortnox
await Throttle();

return await HttpClient.SendAsync(request).ConfigureAwait(false);
}
Expand All @@ -79,9 +66,21 @@ public async Task<HttpWebResponse> SendAsync(HttpWebRequest request)
request.Timeout = Timeout;

if (UseRateLimiter)
await RateLimiter; //throttle every call to Fortnox
await Throttle();

return (HttpWebResponse) await request.GetResponseAsync().ConfigureAwait(false);
}

private async Task Throttle()
{
if (string.IsNullOrEmpty(AccessToken))
return;

//Add ratelimiter for access token if does not exist
if (!RateLimiters.ContainsKey(AccessToken))
RateLimiters.Add(AccessToken, TimeLimiter.GetFromMaxCountByInterval(LimitPerSecond, TimeSpan.FromSeconds(1)));

await RateLimiters[AccessToken];
}
}
}
5 changes: 5 additions & 0 deletions FortnoxAPILibrary/ConnectionSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,10 @@ public static class ConnectionSettings
/// API Server URL
/// </summary>
public static string FortnoxAPIServer { get; set; } = "https://api.fortnox.se/3";

/// <summary>
/// Timeout for getting response. Default value is 30 seconds
/// </summary>
public static int Timeout { get; set; } = 30 * 10000;
}
}

0 comments on commit c263718

Please sign in to comment.