-
Notifications
You must be signed in to change notification settings - Fork 1
/
api.go
152 lines (137 loc) · 3.85 KB
/
api.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// Copyright (c) 2024 0x9ef. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
//
// Package clientx provides functions to build and maintain your own HTTP client.
package clientx
import (
"net/http"
"time"
"golang.org/x/time/rate"
)
// API represents general base API which has to be inherited.
//
// type DuffelAPI struct {
// *clientx.API
// }
type API struct {
httpClient *http.Client
options *Options
retry Retrier
limiter Limiter
}
type (
Option func(*Options)
Options struct {
BaseURL string
HttpClient *http.Client
Headers http.Header
// Debug prints responses into os.Stdout.
Debug bool
// RateLimitParseFn is a custom function that parses rate limits from HTTP response.
// For example from X-Ratelimit-Limit, X-Ratelimit-Remaining headers.
RateLimitParseFn func(*http.Response) (limit int, remaining int, resetAt time.Time, err error)
RateLimit *OptionRateLimit
Retry *OptionRetry
}
OptionRateLimit struct {
Limit int
Burst int
// Per allows configuring limits for different time windows.
Per time.Duration
}
OptionRetry struct {
MaxAttempts int
MinWaitTime time.Duration
MaxWaitTime time.Duration
// Conditions that will be applied to retry mechanism.
Conditions []RetryCond
// Retry function which will be used as main retry logic.
Fn RetryFunc
}
)
// NewAPI returns new base API structure with preselected http.DefaultClient
// and options. Applies all options, overwrites HttpClient if such option is presented.
func NewAPI(opts ...Option) *API {
options := &Options{
HttpClient: http.DefaultClient,
}
for _, opt := range opts {
opt(options)
}
api := &API{
httpClient: options.HttpClient,
options: options,
}
if options.Retry != nil {
api.retry = &backoff{
minWaitTime: options.Retry.MinWaitTime,
maxWaitTime: options.Retry.MaxWaitTime,
maxAttempts: int64(options.Retry.MaxAttempts),
attempts: 0,
f: options.Retry.Fn,
}
}
if options.RateLimit != nil {
limit := rate.Every(options.RateLimit.Per / time.Duration(options.RateLimit.Limit))
api.limiter = newAdaptiveBucketLimiter(limit, options.RateLimit.Burst)
} else {
api.limiter = newUnlimitedAdaptiveBucketLimiter()
}
return api
}
// WithDebug enables debug logging of requests and responses.
// DO NOT USE IN PRODUCTION.
func WithDebug() Option {
return func(o *Options) {
o.Debug = true
}
}
// WithBaseURL sets base URL to perform requests.
func WithBaseURL(url string) Option {
return func(o *Options) {
o.BaseURL = url
}
}
// WithHTTPClient allows you to specify a custom http.Client to use for making requests.
// This is useful if you want to use a custom transport or proxy.
func WithHTTPClient(client *http.Client) Option {
return func(o *Options) {
o.HttpClient = client
}
}
// WithRetry sets custom retrier implementation. Also enables retrying mechanism.
// If f retry function isn't provided ExponentalBackoff algorithm will be used.
func WithRetry(maxAttempts int, minWaitTime, maxWaitTime time.Duration, f RetryFunc, conditions ...RetryCond) Option {
return func(o *Options) {
if f == nil {
f = ExponentalBackoff // uses as default
}
o.Retry = &OptionRetry{
MaxAttempts: maxAttempts,
MinWaitTime: minWaitTime,
MaxWaitTime: maxWaitTime,
Conditions: conditions,
Fn: f,
}
}
}
// WithRateLimit sets burst and limit for a ratelimiter.
func WithRateLimit(limit int, burst int, per time.Duration) Option {
return func(o *Options) {
o.RateLimit = &OptionRateLimit{
Limit: limit,
Burst: burst,
Per: per,
}
}
}
// WithHeaders sets global headers. Overwrites previously defined header set.
func WithHeaders(headers map[string][]string) Option {
return func(o *Options) {
if len(o.Headers) == 0 {
o.Headers = make(http.Header)
}
o.Headers = headers
}
}