Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions go.work.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
30 changes: 29 additions & 1 deletion utils/httputil/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ import (
// StatusTooManyRequests is the HTTP status code discord sends on rate-limiting.
const StatusTooManyRequests = 429

// StatusNirnTimeout is the HTTP status code Nirn Proxy sends when the request
// times out.
const StatusNirnTimeout = 408

// Retries is the default attempts to retry if the API returns an error before
// giving up. If the value is smaller than 1, then requests will retry forever.
var Retries uint = 5
Expand Down Expand Up @@ -230,10 +234,34 @@ func (c *Client) request(
continue
}

if status = r.GetStatus(); status == StatusTooManyRequests || status >= 500 {
status = r.GetStatus()

if status == StatusNirnTimeout || status >= 500 {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the case of a 5xx response, I think we should do our own exponential backoff to prevent spamming Discord.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than adding a StatusNirnTimeout, I think we should just write status == http.StatusRequestTimeout.

continue
}

if status == StatusTooManyRequests {
var body = r.GetBody()
defer body.Close()

buf := bytes.Buffer{}
buf.ReadFrom(body)

var errBody struct {
RetryAfter float32 `json:"retry_after"`
}

err := json.Unmarshal(buf.Bytes(), &errBody)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can shorten this to:

Suggested change
err := json.Unmarshal(buf.Bytes(), &errBody)
err := json.NewDecoder(body).Decode(&errBody)


if err == nil {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Happy path: put the if err != nil check first, then put the other case after the check.

time.Sleep(time.Duration(errBody.RetryAfter) * time.Second)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

time.Sleep is not context-aware. Prefer writing:

select {
case <-ctx.Done():
    doErr = ctx.Err()
    continue // or break out of the loop, but this will work too
case <-time.After(...):
    continue
}

continue
} else {
doErr = fmt.Errorf("failed to parse rate limit body: %w", err)
continue
}
}

break
}

Expand Down