Skip to content

Commit 9711f99

Browse files
committed
Add DISABLE_GLOBAL_RATELIMIT_DETECTION flag, return a 429 not a 500 when /users/@me or /gateway/bot calls 429
1 parent 3a912d0 commit 9711f99

File tree

6 files changed

+57
-31
lines changed

6 files changed

+57
-31
lines changed

CONFIG.md

+10
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,16 @@ Allows you to define custom global request limits for one or multiple bots. The
6565
Format: Command separated list of **user id** and limit combo, separated by `:` and with no spaces at all. Don't use application ids.
6666
Example: `392827169497284619:100,227115752396685313:80`
6767

68+
69+
##### DISABLE_GLOBAL_RATELIMIT_DETECTION
70+
Disables the optimistic global rest limit detection. This detection uses the /gateway/bot endpoint, which has a low ratelimit and can cause issues with requests being dropped/delayed as cluster size grows.
71+
72+
You probably want to set BOT_RATELIMIT_OVERRIDES if you set this to true.
73+
74+
Default: false
75+
76+
In the future, this will be the only possible behavior.
77+
6878
## Unstable env vars
6979
Collection of env vars that may be removed at any time, mainly used for Discord introducing new behaviour on their edge api versions
7080

README.md

+18-17
Original file line numberDiff line numberDiff line change
@@ -24,23 +24,24 @@ The proxy sits between the client and discord. Instead of pointing to discord.co
2424

2525
Configuration options are
2626

27-
| Variable | Value | Default |
28-
|-----------------|-----------------------------------------------|-------------------------|
29-
| LOG_LEVEL | panic, fatal, error, warn, info, debug, trace | info |
30-
| PORT | number | 8080 |
31-
| METRICS_PORT | number | 9000 |
32-
| ENABLE_METRICS | boolean | true |
33-
| ENABLE_PPROF | boolean | false |
34-
| BUFFER_SIZE | number | 50 |
35-
| OUTBOUND_IP | string | "" |
36-
| BIND_IP | string | 0.0.0.0 |
37-
| REQUEST_TIMEOUT | number (milliseconds) | 5000 |
38-
| CLUSTER_PORT | number | 7946 |
39-
| CLUSTER_MEMBERS | string list (comma separated) | "" |
40-
| CLUSTER_DNS | string | "" |
41-
| MAX_BEARER_COUNT| number | 1024 |
42-
| DISABLE_HTTP_2 | bool | true |
43-
| BOT_RATELIMIT_OVERRIDES | string list (comma separated) | "" |
27+
| Variable | Value | Default |
28+
|-----------------|---------------------------------------------|---------|
29+
| LOG_LEVEL | panic, fatal, error, warn, info, debug, trace | info |
30+
| PORT | number | 8080 |
31+
| METRICS_PORT | number | 9000 |
32+
| ENABLE_METRICS | boolean | true |
33+
| ENABLE_PPROF | boolean | false |
34+
| BUFFER_SIZE | number | 50 |
35+
| OUTBOUND_IP | string | "" |
36+
| BIND_IP | string | 0.0.0.0 |
37+
| REQUEST_TIMEOUT | number (milliseconds) | 5000 |
38+
| CLUSTER_PORT | number | 7946 |
39+
| CLUSTER_MEMBERS | string list (comma separated) | "" |
40+
| CLUSTER_DNS | string | "" |
41+
| MAX_BEARER_COUNT| number | 1024 |
42+
| DISABLE_HTTP_2 | bool | true |
43+
| BOT_RATELIMIT_OVERRIDES | string list (comma separated) | "" |
44+
| DISABLE_GLOBAL_RATELIMIT_DETECTION | boolean | false |
4445

4546
Information on each config var can be found [here](https://github.com/germanoeich/nirn-proxy/blob/main/CONFIG.md)
4647

lib/discord.go

+16-9
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,16 @@ var contextTimeout time.Duration
2222

2323
var globalOverrideMap = make(map[string]uint)
2424

25+
var disableRestLimitDetection = false
26+
2527
type BotGatewayResponse struct {
2628
SessionStartLimit map[string]int `json:"session_start_limit"`
2729
}
2830

2931
type BotUserResponse struct {
30-
Id string `json:"id"`
32+
Id string `json:"id"`
3133
Username string `json:"username"`
32-
Discrim string `json:"discriminator"`
34+
Discrim string `json:"discriminator"`
3335
}
3436

3537
func createTransport(ip string, disableHttp2 bool) http.RoundTripper {
@@ -56,9 +58,9 @@ func createTransport(ip string, disableHttp2 bool) http.RoundTripper {
5658
}
5759

5860
dialer := &net.Dialer{
59-
LocalAddr: addr,
60-
Timeout: 30 * time.Second,
61-
KeepAlive: 30 * time.Second,
61+
LocalAddr: addr,
62+
Timeout: 30 * time.Second,
63+
KeepAlive: 30 * time.Second,
6264
}
6365

6466
dialContext := func(ctx context.Context, network, addr string) (net.Conn, error) {
@@ -109,15 +111,17 @@ func parseGlobalOverrides(overrides string) {
109111
}
110112
}
111113

112-
func ConfigureDiscordHTTPClient(ip string, timeout time.Duration, disableHttp2 bool, globalOverrides string) {
114+
func ConfigureDiscordHTTPClient(ip string, timeout time.Duration, disableHttp2 bool, globalOverrides string, disableRestDetection bool) {
113115
transport := createTransport(ip, disableHttp2)
114116
client = &http.Client{
115117
Transport: transport,
116-
Timeout: 90 * time.Second,
118+
Timeout: 90 * time.Second,
117119
}
118120

119121
contextTimeout = timeout
120122

123+
disableRestLimitDetection = disableRestDetection
124+
121125
parseGlobalOverrides(globalOverrides)
122126
}
123127

@@ -137,6 +141,10 @@ func GetBotGlobalLimit(token string, user *BotUserResponse) (uint, error) {
137141
return 50, nil
138142
}
139143

144+
if disableRestLimitDetection {
145+
return 50, nil
146+
}
147+
140148
bot, err := doDiscordReq(context.Background(), "/api/v9/gateway/bot", "GET", nil, map[string][]string{"Authorization": {token}}, "")
141149

142150
if err != nil {
@@ -205,7 +213,7 @@ func GetBotUser(token string) (*BotUserResponse, error) {
205213
}
206214

207215
func doDiscordReq(ctx context.Context, path string, method string, body io.ReadCloser, header http.Header, query string) (*http.Response, error) {
208-
discordReq, err := http.NewRequestWithContext(ctx, method, "https://discord.com" + path + "?" + query, body)
216+
discordReq, err := http.NewRequestWithContext(ctx, method, "https://discord.com"+path+"?"+query, body)
209217
if err != nil {
210218
return nil, err
211219
}
@@ -231,7 +239,6 @@ func doDiscordReq(ctx context.Context, path string, method string, body io.ReadC
231239
status = "429 Shared"
232240
}
233241
}
234-
235242

236243
RequestHistogram.With(map[string]string{"route": route, "status": status, "method": method, "clientId": identifier.(string)}).Observe(elapsed)
237244
}

lib/queue.go

+1
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ func NewRequestQueue(processor func(ctx context.Context, item *QueueItem) (*http
7575
botLimit: limit,
7676
}, nil
7777
}
78+
7879
return nil, err
7980
}
8081

lib/queue_manager.go

+9-4
Original file line numberDiff line numberDiff line change
@@ -297,10 +297,15 @@ func (m *QueueManager) fulfillRequest(resp *http.ResponseWriter, req *http.Reque
297297
}
298298

299299
if err != nil {
300-
(*resp).WriteHeader(500)
301-
(*resp).Write([]byte(err.Error()))
302-
ErrorCounter.Inc()
303-
logEntry.WithFields(logrus.Fields{"function": "getOrCreateQueue", "queueType": queueType}).Error(err)
300+
if strings.HasPrefix(err.Error(), "429") {
301+
Generate429(resp)
302+
logEntry.WithFields(logrus.Fields{"function": "getOrCreateQueue", "queueType": queueType}).Warn(err)
303+
} else {
304+
(*resp).WriteHeader(500)
305+
(*resp).Write([]byte(err.Error()))
306+
ErrorCounter.Inc()
307+
logEntry.WithFields(logrus.Fields{"function": "getOrCreateQueue", "queueType": queueType}).Error(err)
308+
}
304309
return
305310
}
306311

main.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,9 @@ func main() {
7474

7575
globalOverrides := lib.EnvGet("BOT_RATELIMIT_OVERRIDES", "")
7676

77-
lib.ConfigureDiscordHTTPClient(outboundIp, time.Duration(timeout)*time.Millisecond, disableHttp2, globalOverrides)
77+
disableGlobalRatelimitDetection := lib.EnvGetBool("DISABLE_GLOBAL_RATELIMIT_DETECTION", false)
78+
79+
lib.ConfigureDiscordHTTPClient(outboundIp, time.Duration(timeout)*time.Millisecond, disableHttp2, globalOverrides, disableGlobalRatelimitDetection)
7880

7981
port := lib.EnvGet("PORT", "8080")
8082
bindIp := lib.EnvGet("BIND_IP", "0.0.0.0")

0 commit comments

Comments
 (0)