7
7
"fmt"
8
8
"io"
9
9
"net/http"
10
+ "strconv"
10
11
"strings"
11
12
"time"
12
13
@@ -21,9 +22,10 @@ const (
21
22
type Client struct {
22
23
client * http.Client
23
24
24
- BaseURL string
25
- Token string
26
- RateLimiter * rate.Limiter
25
+ BaseURL string
26
+ Token string
27
+ ReadRateLimiter * rate.Limiter
28
+ UpdateRateLimiter * rate.Limiter
27
29
28
30
UserAgent string
29
31
@@ -115,11 +117,12 @@ func NewClient(baseURL, clientID, clientSecret string) (*Client, error) {
115
117
}
116
118
117
119
c := & Client {
118
- client : httpClient ,
119
- BaseURL : baseURL ,
120
- Token : credentials .AccessToken ,
121
- UserAgent : userAgent ,
122
- RateLimiter : rate .NewLimiter (rate .Every (1 * time .Second ), 5 ),
120
+ client : httpClient ,
121
+ BaseURL : baseURL ,
122
+ Token : credentials .AccessToken ,
123
+ UserAgent : userAgent ,
124
+ ReadRateLimiter : rate .NewLimiter (rate .Every (1 * time .Second ), 1 ),
125
+ UpdateRateLimiter : rate .NewLimiter (rate .Every (4 * time .Second ), 1 ),
123
126
}
124
127
c .common .client = c
125
128
c .HostConnectors = (* HostConnectorsService )(& c .common )
@@ -158,7 +161,13 @@ func (c *Client) setCommonHeaders(req *http.Request) {
158
161
// DoRequest executes an HTTP request with authentication and rate limiting.
159
162
// It automatically adds the Bearer token, sets headers, and handles errors.
160
163
func (c * Client ) DoRequest (req * http.Request ) ([]byte , error ) {
161
- err := c .RateLimiter .Wait (context .Background ())
164
+ var rateLimiter * rate.Limiter
165
+ if req .Method == "GET" {
166
+ rateLimiter = c .ReadRateLimiter
167
+ } else {
168
+ rateLimiter = c .UpdateRateLimiter
169
+ }
170
+ err := rateLimiter .Wait (context .Background ())
162
171
if err != nil {
163
172
return nil , err
164
173
}
@@ -185,9 +194,42 @@ func (c *Client) DoRequest(req *http.Request) ([]byte, error) {
185
194
return nil , & ErrClientResponse {status : res .StatusCode , body : string (body )}
186
195
}
187
196
197
+ err = c .AssignLimits (res , rateLimiter )
198
+ if err != nil {
199
+ return nil , err
200
+ }
201
+
188
202
return body , nil
189
203
}
190
204
205
+ // AssignLimits adjusts the rate limiter according to values received in response headers from the API
206
+ func (c * Client ) AssignLimits (res * http.Response , rateLimiter * rate.Limiter ) error {
207
+ rateHeader := res .Header .Get ("X-RateLimit-Replenish-Rate" )
208
+ timeHeader := res .Header .Get ("X-RateLimit-Replenish-Time" )
209
+ remainingHeader := res .Header .Get ("X-RateLimit-Remaining" )
210
+
211
+ if rateHeader != "" && timeHeader != "" && remainingHeader != "" {
212
+ rateValue , err := strconv .Atoi (rateHeader )
213
+ if err != nil {
214
+ return err
215
+ }
216
+ timeValue , err := strconv .Atoi (timeHeader )
217
+ if err != nil {
218
+ return err
219
+ }
220
+ remainingValue , err := strconv .Atoi (remainingHeader )
221
+ if err != nil {
222
+ return err
223
+ }
224
+ if remainingValue <= 0 {
225
+ remainingValue = 1
226
+ }
227
+ rateLimiter .SetLimit (rate .Every (time .Duration (timeValue * 1_000_000_000 / rateValue )))
228
+ rateLimiter .SetBurst (remainingValue )
229
+ }
230
+ return nil
231
+ }
232
+
191
233
// GetV1Url returns the base URL for CloudConnexa API v1 endpoints.
192
234
func (c * Client ) GetV1Url () string {
193
235
return c .BaseURL + "/api/v1"
0 commit comments