Skip to content
This repository was archived by the owner on Mar 20, 2026. It is now read-only.
Merged
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
6 changes: 5 additions & 1 deletion lib/discord.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,11 @@ func GetBotGlobalLimit(token string, user *BotUserResponse) (uint, error) {
}
}

if strings.HasPrefix(token, "Bearer") {
if HasAuthPrefix(token, "Bearer") {
return 50, nil
}

if HasAuthPrefix(token, "Basic") {
return 50, nil
}

Expand Down
9 changes: 5 additions & 4 deletions lib/queue.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,14 @@ func NewRequestQueue(processor func(ctx context.Context, item *QueueItem) (*http
queueType := NoAuth
var user *BotUserResponse
var err error
if !strings.HasPrefix(token, "Bearer") {
switch {
case HasAuthPrefix(token, "Bearer"):
queueType = Bearer
case token != "" && !HasAuthPrefix(token, "Basic"):
user, err = GetBotUser(token)
if err != nil && token != "" {
if err != nil {
return nil, err
}
} else {
queueType = Bearer
}

limit, err := GetBotGlobalLimit(token, user)
Expand Down
8 changes: 5 additions & 3 deletions lib/queue_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,12 +261,14 @@ func (m *QueueManager) DiscordRequestHandler(resp http.ResponseWriter, req *http
func (m *QueueManager) GetRequestRoutingInfo(req *http.Request, token string) (routingHash uint64, path string, queueType QueueType) {
path = GetOptimisticBucketPath(req.URL.Path, req.Method)
queueType = NoAuth
if strings.HasPrefix(token, "Bearer") {
routingHash = HashCRC64(path)

switch {
case HasAuthPrefix(token, "Bearer"):
queueType = Bearer
routingHash = HashCRC64(token)
} else {
case token != "" && !HasAuthPrefix(token, "Basic"):
queueType = Bot
routingHash = HashCRC64(path)
}
return
Comment thread
germanoeich marked this conversation as resolved.
}
Expand Down
57 changes: 57 additions & 0 deletions lib/queue_manager_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package lib

import (
"net/http"
"testing"
)

func TestGetRequestRoutingInfoBasicAuth(t *testing.T) {
manager := &QueueManager{}
Comment thread
germanoeich marked this conversation as resolved.
req, err := http.NewRequest("POST", "https://discord.com/api/v10/oauth2/token/revoke", nil)
if err != nil {
t.Fatalf("failed to create request: %v", err)
}

hash, path, queueType := manager.GetRequestRoutingInfo(req, "Basic ZmFrZVRva2Vu")
if queueType != NoAuth {
t.Fatalf("expected queue type %v, got %v", NoAuth, queueType)
}

if hash != HashCRC64(path) {
t.Fatalf("expected routing hash to match path hash")
}
Comment thread
germanoeich marked this conversation as resolved.
}

func TestGetRequestRoutingInfoBearerAuth(t *testing.T) {
manager := &QueueManager{}
req, err := http.NewRequest("GET", "https://discord.com/api/v10/users/@me", nil)
if err != nil {
t.Fatalf("failed to create request: %v", err)
}

hash, _, queueType := manager.GetRequestRoutingInfo(req, "Bearer some-token")
if queueType != Bearer {
t.Fatalf("expected queue type %v, got %v", Bearer, queueType)
}

if hash != HashCRC64("Bearer some-token") {
t.Fatalf("expected bearer routing hash to use token")
}
Comment thread
germanoeich marked this conversation as resolved.
}

func TestGetRequestRoutingInfoBotToken(t *testing.T) {
manager := &QueueManager{}
req, err := http.NewRequest("GET", "https://discord.com/api/v10/channels/123/messages", nil)
if err != nil {
t.Fatalf("failed to create request: %v", err)
}

hash, path, queueType := manager.GetRequestRoutingInfo(req, "Bot Abc")
if queueType != Bot {
t.Fatalf("expected queue type %v, got %v", Bot, queueType)
}

if hash != HashCRC64(path) {
t.Fatalf("expected bot routing hash to match path hash")
}
Comment thread
germanoeich marked this conversation as resolved.
}
23 changes: 22 additions & 1 deletion lib/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func GetBotId(token string) string {
} else {
token = strings.ReplaceAll(token, "Bot ", "")
token = strings.ReplaceAll(token, "Bearer ", "")
token = strings.ReplaceAll(token, "Basic ", "")
token = strings.Split(token, ".")[0]
token, err := base64.StdEncoding.DecodeString(token)
if err != nil {
Expand All @@ -43,4 +44,24 @@ func GetBotId(token string) string {
}
}
return clientId
}
}

// HasAuthPrefix checks if the provided authorization header value starts with the
// given scheme. Comparison is performed case-insensitively and requires the
// scheme to be followed by at least one space as mandated by RFC 7235.
func HasAuthPrefix(token, scheme string) bool {
if len(token) <= len(scheme) {
return false
}

if !strings.EqualFold(token[:len(scheme)], scheme) {
return false
}

switch token[len(scheme)] {
case ' ', '\t':
return true
default:
return false
}
}
Comment thread
germanoeich marked this conversation as resolved.
31 changes: 28 additions & 3 deletions lib/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,25 @@ import (
)

const knownData = "test data"

// Calculated using ISO table
const knownHash = 10232006911339297906

func TestHashWorks(t *testing.T) {
HashCRC64(knownData)
if HashCRC64(knownData) != knownHash {
t.Fatalf("Invalid hash returned")
}
}

//Test for correctness
// Test for correctness
func TestHashIsConsistent(t *testing.T) {
ret := HashCRC64(knownData)
if ret != knownHash {
t.Errorf("Invalid hash returned")
}
}

//Test for consistency when function is used for other data
// Test for consistency when function is used for other data
func TestHashIsConsistentAcrossMultipleRuns(t *testing.T) {
for i := 0; i < 50000; i++ {
HashCRC64(strconv.Itoa(i))
Expand All @@ -33,3 +36,25 @@ func TestHashIsConsistentAcrossMultipleRuns(t *testing.T) {
}
}

func TestHasAuthPrefix(t *testing.T) {
tests := []struct {
name string
token string
scheme string
expected bool
}{
{name: "basic with space", token: "Basic Zm9vOmJhcg==", scheme: "Basic", expected: true},
{name: "basic lowercase", token: "basic Zm9vOmJhcg==", scheme: "Basic", expected: true},
{name: "basic with tab", token: "Basic\tZm9vOmJhcg==", scheme: "Basic", expected: true},
{name: "basic only scheme", token: "Basic ", scheme: "Basic", expected: true},
{name: "missing space", token: "BasicZm9v", scheme: "Basic", expected: false},
{name: "different scheme", token: "Bot foo", scheme: "Basic", expected: false},
{name: "bearer", token: "Bearer token", scheme: "Bearer", expected: true},
}
Comment on lines +39 to +53

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick

Add a few more edge cases to lock in prefix semantics.

Recommend covering empty token, no delimiter, double‑space, and tab‑only delimiter for both Basic and Bearer.

 	tests := []struct {
 		name     string
 		token    string
 		scheme   string
 		expected bool
 	}{
 		{name: "basic with space", token: "Basic Zm9vOmJhcg==", scheme: "Basic", expected: true},
 		{name: "basic lowercase", token: "basic Zm9vOmJhcg==", scheme: "Basic", expected: true},
 		{name: "basic with tab", token: "Basic\tZm9vOmJhcg==", scheme: "Basic", expected: true},
 		{name: "basic only scheme", token: "Basic ", scheme: "Basic", expected: true},
+		{name: "basic only scheme with tab", token: "Basic\t", scheme: "Basic", expected: true},
+		{name: "basic without delimiter", token: "Basic", scheme: "Basic", expected: false},
+		{name: "basic with double space", token: "Basic  Zm9v", scheme: "Basic", expected: true},
+		{name: "empty token", token: "", scheme: "Basic", expected: false},
 		{name: "missing space", token: "BasicZm9v", scheme: "Basic", expected: false},
 		{name: "different scheme", token: "Bot foo", scheme: "Basic", expected: false},
 		{name: "bearer", token: "Bearer token", scheme: "Bearer", expected: true},
+		{name: "bearer with tab", token: "Bearer\ttoken", scheme: "Bearer", expected: true},
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func TestHasAuthPrefix(t *testing.T) {
tests := []struct {
name string
token string
scheme string
expected bool
}{
{name: "basic with space", token: "Basic Zm9vOmJhcg==", scheme: "Basic", expected: true},
{name: "basic lowercase", token: "basic Zm9vOmJhcg==", scheme: "Basic", expected: true},
{name: "basic with tab", token: "Basic\tZm9vOmJhcg==", scheme: "Basic", expected: true},
{name: "basic only scheme", token: "Basic ", scheme: "Basic", expected: true},
{name: "missing space", token: "BasicZm9v", scheme: "Basic", expected: false},
{name: "different scheme", token: "Bot foo", scheme: "Basic", expected: false},
{name: "bearer", token: "Bearer token", scheme: "Bearer", expected: true},
}
func TestHasAuthPrefix(t *testing.T) {
tests := []struct {
name string
token string
scheme string
expected bool
}{
{name: "basic with space", token: "Basic Zm9vOmJhcg==", scheme: "Basic", expected: true},
{name: "basic lowercase", token: "basic Zm9vOmJhcg==", scheme: "Basic", expected: true},
{name: "basic with tab", token: "Basic\tZm9vOmJhcg==", scheme: "Basic", expected: true},
{name: "basic only scheme", token: "Basic ", scheme: "Basic", expected: true},
{name: "basic only scheme with tab", token: "Basic\t", scheme: "Basic", expected: true},
{name: "basic without delimiter", token: "Basic", scheme: "Basic", expected: false},
{name: "basic with double space", token: "Basic Zm9v", scheme: "Basic", expected: true},
{name: "empty token", token: "", scheme: "Basic", expected: false},
{name: "missing space", token: "BasicZm9v", scheme: "Basic", expected: false},
{name: "different scheme", token: "Bot foo", scheme: "Basic", expected: false},
{name: "bearer", token: "Bearer token", scheme: "Bearer", expected: true},
{name: "bearer with tab", token: "Bearer\ttoken", scheme: "Bearer", expected: true},
}
🤖 Prompt for AI Agents
In lib/util_test.go around lines 37 to 51, the test table for TestHasAuthPrefix
is missing edge cases that assert exact prefix semantics; add tests for empty
token (""), no delimiter (e.g., "BasicZm9v" and "Bearertoken"), double-space
delimiter (e.g., "Basic  Zm9v" and "Bearer  token"), and tab-only delimiter
(e.g., "Basic\t" and "Bearer\t") for both Basic and Bearer schemes, setting
expected true only when the scheme is present followed by at least one
whitespace delimiter and possibly an empty credential when appropriate, and
false for no-delimiter cases and malformed spacing as described.


for _, tc := range tests {
if result := HasAuthPrefix(tc.token, tc.scheme); result != tc.expected {
t.Errorf("%s: expected %v, got %v", tc.name, tc.expected, result)
}
}
}
Comment on lines +55 to +60

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick

Use subtests for clearer reporting.

Subtests make failures easier to pinpoint and enable parallelism if needed.

-	for _, tc := range tests {
-		if result := HasAuthPrefix(tc.token, tc.scheme); result != tc.expected {
-			t.Errorf("%s: expected %v, got %v", tc.name, tc.expected, result)
-		}
-	}
+	for _, tc := range tests {
+		tc := tc
+		t.Run(tc.name, func(t *testing.T) {
+			if result := HasAuthPrefix(tc.token, tc.scheme); result != tc.expected {
+				t.Fatalf("expected %v, got %v (token=%q, scheme=%q)", tc.expected, result, tc.token, tc.scheme)
+			}
+		})
+	}
🤖 Prompt for AI Agents
In lib/util_test.go around lines 53 to 58, the table-driven loop uses a plain
for loop which makes failures harder to pinpoint; convert each iteration to a
subtest by calling t.Run(tc.name, func(t *testing.T) { tc := tc; /* optional:
t.Parallel() */ if result := HasAuthPrefix(tc.token, tc.scheme); result !=
tc.expected { t.Errorf("%s: expected %v, got %v", tc.name, tc.expected, result)
} }) so each case reports separately (remember to capture tc into the closure
and add t.Parallel() only if the test is safe to run concurrently).

Loading