@@ -12,19 +12,29 @@ import (
1212 "prostaff-riot-gateway/internal/ratelimit"
1313)
1414
15+ // RateLimitError is returned when Riot responds with 429. It carries the Retry-After
16+ // value from the Riot header so callers can propagate it to their own clients.
17+ type RateLimitError struct {
18+ RetryAfter string
19+ }
20+
21+ func (e * RateLimitError ) Error () string {
22+ return fmt .Sprintf ("riot api rate limited, retry after %s seconds" , e .RetryAfter )
23+ }
24+
1525// Client calls the Riot Games API with rate limiting and circuit breaking.
1626type Client struct {
1727 http * http.Client
1828 apiKey string
19- limiter * ratelimit.RegionLimiter
29+ limiter * ratelimit.AppLimiter
2030 breakers * circuit.RegionBreakers
2131 logger * slog.Logger
2232}
2333
2434func NewClient (
2535 timeout time.Duration ,
2636 apiKey string ,
27- limiter * ratelimit.RegionLimiter ,
37+ limiter * ratelimit.AppLimiter ,
2838 breakers * circuit.RegionBreakers ,
2939 logger * slog.Logger ,
3040) * Client {
@@ -51,7 +61,7 @@ func (c *Client) Do(ctx context.Context, region, path string) ([]byte, int, erro
5161 return nil , http .StatusServiceUnavailable , fmt .Errorf ("riot api circuit open for region %s" , region )
5262 }
5363
54- if err := c .limiter .Wait (ctx , region ); err != nil {
64+ if err := c .limiter .Wait (ctx ); err != nil {
5565 return nil , http .StatusGatewayTimeout , fmt .Errorf ("rate limit wait cancelled: %w" , err )
5666 }
5767
@@ -76,12 +86,17 @@ func (c *Client) Do(ctx context.Context, region, path string) ([]byte, int, erro
7686 return nil , http .StatusBadGateway , fmt .Errorf ("failed to read riot response: %w" , err )
7787 }
7888
79- status := mapRiotStatus (resp .StatusCode )
89+ // 429 is a rate limit signal, not an infrastructure failure — do not trip the circuit breaker.
90+ if resp .StatusCode == http .StatusTooManyRequests {
91+ retryAfter := resp .Header .Get ("Retry-After" )
92+ c .logger .Warn ("riot api rate limited" , "region" , region , "retry_after" , retryAfter )
93+ return nil , http .StatusTooManyRequests , & RateLimitError {RetryAfter : retryAfter }
94+ }
8095
81- if resp .StatusCode >= 500 || resp . StatusCode == 429 {
96+ if resp .StatusCode >= 500 {
8297 breaker .RecordFailure ()
83- c .logger .Warn ("riot api returned error" , "region" , region , "riot_status" , resp .StatusCode )
84- return nil , status , fmt .Errorf ("riot api returned %d" , resp .StatusCode )
98+ c .logger .Warn ("riot api server error" , "region" , region , "riot_status" , resp .StatusCode )
99+ return nil , http . StatusBadGateway , fmt .Errorf ("riot api returned %d" , resp .StatusCode )
85100 }
86101
87102 if resp .StatusCode == http .StatusNotFound {
@@ -92,17 +107,3 @@ func (c *Client) Do(ctx context.Context, region, path string) ([]byte, int, erro
92107 return body , http .StatusOK , nil
93108}
94109
95- func mapRiotStatus (riotStatus int ) int {
96- switch {
97- case riotStatus == http .StatusOK :
98- return http .StatusOK
99- case riotStatus == http .StatusNotFound :
100- return http .StatusNotFound
101- case riotStatus == 429 :
102- return 429
103- case riotStatus >= 500 :
104- return http .StatusBadGateway
105- default :
106- return riotStatus
107- }
108- }
0 commit comments