Skip to content

Commit

Permalink
Add DISABLE_HTTP_2 env var, sanitize webhook/interaction tokens from …
Browse files Browse the repository at this point in the history
…logs, make context deadline exceededs a warning, document new env vars, handle unversioned endpoints properly
  • Loading branch information
germanoeich committed Apr 13, 2022
1 parent c6bdce5 commit 206c54e
Show file tree
Hide file tree
Showing 9 changed files with 98 additions and 36 deletions.
6 changes: 6 additions & 0 deletions CONFIG.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ Bearer token queues max size. Internally, bearer queues are put in an LRU map, t
Requests are never interrupted midway, even when an entry is evicted. A low LRU size may cause increased 429s if a bearer token has too many requests queued and fires another one after eviction.
Default: 1024

##### DISABLE_HTTP_2
Flag to disable HTTP/2 requests on the client that makes discord requests. Does not impact the http server.
This flag defaults to true due to go http2 support having a few minor issues that can cause requests to fail.

Default: true

## Unstable env vars
Collection of env vars that may be removed at any time, mainly used for Discord introducing new behaviour on their edge api versions

Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ Configuration options are
| CLUSTER_PORT | number | 7946 |
| CLUSTER_MEMBERS | string list (comma separated) | "" |
| CLUSTER_DNS | string | "" |
| MAX_BEARER_COUNT| number | 1024 |
| DISABLE_HTTP_2 | bool | true |

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

Expand Down
4 changes: 4 additions & 0 deletions lib/bucketpath.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,11 @@ func GetOptimisticBucketPath(url string, method string) string {
l := len(cleanUrl)
i := strings.Index(cleanUrl, "/")
cleanUrl = cleanUrl[i+1:l]
} else {
// Handle unversioned endpoints
cleanUrl = strings.ReplaceAll(cleanUrl, "/api/", "")
}

parts := strings.Split(cleanUrl, "/")
numParts := len(parts)

Expand Down
6 changes: 6 additions & 0 deletions lib/bucketpath_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ func TestPaths(t *testing.T) {
{"/api/v9/guilds/203039963636301824/channels", "GET", "/guilds/!/channels"},
// Wierd routes
{"/api/v9/guilds/templates/203039963636301824", "GET", "/guilds/templates/!"},
// Unversioned routes
{"/api/webhooks/203039963636301824/VSOzAqY1OZFF5WJVtbIzFtmjGupk-84Hn0A_ZzToF_CHsPIeCk0Q9Uok_mjxR0dNtApI", "POST", "/webhooks/203039963636301824/!"},
{"/api/interactions/203039963636301824/aW50ZXJhY3Rpb246ODg3NTU5MDA01AY4NTUxNDU0OnZwS3QycDhvREk2aVF3U1BqN2prcXBkRmNqNlp4VEhGRjZvSVlXSGh4WG4yb3l6Z3B6NTBPNVc3OHphV05OULLMOHBMa2RTZmVKd3lzVDA2b2h3OTUxaFJ4QlN0dkxXallPcmhnSHNJb0tSV0M5ZzY1NkN4VGRvemFOSHY4b05c/callback", "GET", "/interactions/203039963636301824/!/callback"},
{"/api/channels/872712139712913438/messages/872712150509047809/reactions/PandaOhShit:863985751205085195", "GET", "/channels/872712139712913438/messages/!/reactions/!/!"},
{"/api/invites/dyno", "GET", "/invites/!"},

}
for _, tt := range tests {
testname := fmt.Sprintf("%s-%s", tt.method, tt.path)
Expand Down
68 changes: 35 additions & 33 deletions lib/discord.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,59 +29,61 @@ type BotUserResponse struct {
Discrim string `json:"discriminator"`
}

func createTransport(ip string) http.RoundTripper {
func createTransport(ip string, disableHttp2 bool) http.RoundTripper {
var transport http.Transport
if ip == "" {
// http.DefaultTransport options, with http2 disabled
return &http.Transport{
// http.DefaultTransport options
transport = http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
ForceAttemptHTTP2: false,
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
TLSNextProto: map[string]func(string, *tls.Conn) http.RoundTripper{},
}
}
addr, err := net.ResolveTCPAddr("tcp", ip+":0")
} else {
addr, err := net.ResolveTCPAddr("tcp", ip+":0")

if err != nil {
panic(err)
}
if err != nil {
panic(err)
}

dialer := &net.Dialer{
Deadline: time.Time{},
LocalAddr: addr,
FallbackDelay: 0,
Resolver: nil,
Control: nil,
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}
dialer := &net.Dialer{
LocalAddr: addr,
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}

dialContext := func(ctx context.Context, network, addr string) (net.Conn, error) {
conn, err := dialer.Dial(network, addr)
return conn, err
dialContext := func(ctx context.Context, network, addr string) (net.Conn, error) {
conn, err := dialer.Dial(network, addr)
return conn, err
}

transport = http.Transport{
ForceAttemptHTTP2: true,
MaxIdleConns: 1000,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 2 * time.Second,
DialContext: dialContext,
ResponseHeaderTimeout: 0,
}
}

transport := http.Transport{
ForceAttemptHTTP2: false,
MaxIdleConns: 1000,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 2 * time.Second,
DialContext: dialContext,
ResponseHeaderTimeout: 0,
TLSNextProto: map[string]func(string, *tls.Conn) http.RoundTripper{},
if disableHttp2 {
transport.TLSNextProto = map[string]func(string, *tls.Conn) http.RoundTripper{}
transport.ForceAttemptHTTP2 = false
}

return &transport
}

func ConfigureDiscordHTTPClient(ip string, timeout time.Duration) {
transport := createTransport(ip)
func ConfigureDiscordHTTPClient(ip string, timeout time.Duration, disableHttp2 bool) {
transport := createTransport(ip, disableHttp2)
client = &http.Client{
Transport: transport,
Timeout: 90 * time.Second,
Expand Down
13 changes: 13 additions & 0 deletions lib/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,19 @@ func EnvGet(name string, defaultVal string) string {
return val
}

func EnvGetBool(name string, defaultVal bool) bool {
val := os.Getenv(name)
if val == "" {
return defaultVal
}

if val != "true" && val != "false" {
panic("Invalid env var, expected true or false, got " + val + " for " + name)
}
return val == "true"
}


func EnvMustGet(name string) string {
val := os.Getenv(name)
if val == "" {
Expand Down
20 changes: 20 additions & 0 deletions lib/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,30 @@ package lib

import (
"github.com/sirupsen/logrus"
"regexp"
)

type GlobalHook struct {
}

var loggerHookRegex = regexp.MustCompile("(\\/[0-9]{17,26}\\/)[a-zA-Z0-9\\-_]{63,}")

func (h *GlobalHook) Levels() []logrus.Level {
return logrus.AllLevels
}

func (h *GlobalHook) Fire(e *logrus.Entry) error {
e.Message = loggerHookRegex.ReplaceAllString(e.Message, "$1:token")
if e.Data["path"] != nil {
e.Data["path"] = loggerHookRegex.ReplaceAllString(e.Data["path"].(string), "$1:token")
}
return nil
}


var logger *logrus.Logger

func SetLogger(l *logrus.Logger) {
logger = l
logger.AddHook(&GlobalHook{})
}
11 changes: 9 additions & 2 deletions lib/queue_manager.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package lib

import (
"context"
"errors"
lru "github.com/hashicorp/golang-lru"
"github.com/hashicorp/memberlist"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -171,7 +173,7 @@ func (m *QueueManager) Generate429(resp *http.ResponseWriter) {
writer.Header().Set("x-ratelimit-scope", "user")
writer.Header().Set("x-ratelimit-limit", "1")
writer.Header().Set("x-ratelimit-remaining", "0")
writer.Header().Set("x-ratelimit-reset", string(time.Now().Add(1 * time.Second).Unix()))
writer.Header().Set("x-ratelimit-reset", strconv.FormatInt(time.Now().Add(1*time.Second).Unix(), 10))
writer.Header().Set("x-ratelimit-after", "1")
writer.Header().Set("retry-after", "1")
writer.Header().Set("content-type", "application/json")
Expand Down Expand Up @@ -301,7 +303,12 @@ func (m *QueueManager) fulfillRequest(resp *http.ResponseWriter, req *http.Reque
}
_, _, err = q.Queue(req, resp, path, pathHash)
if err != nil {
logger.WithFields(logrus.Fields{"function": "Queue"}).Error(err)
log := logger.WithFields(logrus.Fields{"function": "Queue"})
if errors.Is(err, context.DeadlineExceeded) {
log.Warn(err)
} else {
log.Error(err)
}
}
} else {
var res *http.Response
Expand Down
4 changes: 3 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@ func main() {

timeout := lib.EnvGetInt("REQUEST_TIMEOUT", 5000)

lib.ConfigureDiscordHTTPClient(outboundIp, time.Duration(timeout) * time.Millisecond)
disableHttp2 := lib.EnvGetBool("DISABLE_HTTP_2", true)

lib.ConfigureDiscordHTTPClient(outboundIp, time.Duration(timeout) * time.Millisecond, disableHttp2)

port := lib.EnvGet("PORT", "8080")
bindIp := lib.EnvGet("BIND_IP", "0.0.0.0")
Expand Down

0 comments on commit 206c54e

Please sign in to comment.