fix: address review findings from rounds 2834-2838
PR Ready Gate / clear-labels (pull_request) Successful in 2s
CI / test (pull_request) Successful in 17s
CI / review (anthropic--claude-4.6-sonnet, sonnet, SONNET_REVIEW_TOKEN) (pull_request) Successful in 49s
CI / review (gpt-5, security, ., rodin/security-patterns, SECURITY_REVIEW.md, SECURITY_REVIEW_TOKEN) (pull_request) Successful in 2m6s
CI / review (gpt-5, gpt, GPT_REVIEW_TOKEN) (pull_request) Successful in 2m19s

- Unexport RetryBackoff, add SetRetryBackoff method (#17286)
- Rename http field to httpClient to avoid shadowing (#17289)
- Group const blocks into single declaration (#17291)
- Fix CheckRedirect to compare against previous hop, not first (#17302)
- Strip auth header on protocol downgrade https→http (#17297)
- Add maxPages safeguard to pagination loops (#17299, #17300)
- Document mapCheckRunStatus unused second parameter (#17287, #17303)
This commit is contained in:
claw
2026-05-12 16:11:58 -07:00
parent 75f65fbf5d
commit ae91c8aef5
5 changed files with 52 additions and 40 deletions
+28 -19
View File
@@ -14,11 +14,13 @@ import (
"time"
)
const defaultBaseURL = "https://api.github.com"
const userAgent = "review-bot/1.0"
const (
defaultBaseURL = "https://api.github.com"
userAgent = "review-bot/1.0"
// maxResponseBytes limits successful response body reads to 10 MiB.
const maxResponseBytes = 10 * 1024 * 1024
// maxResponseBytes limits successful response body reads to 10 MiB.
maxResponseBytes = 10 * 1024 * 1024
)
// APIError represents an HTTP error response from the GitHub API.
// It carries the status code so callers can distinguish between
@@ -68,12 +70,12 @@ func asAPIError(err error) (*APIError, bool) {
type Client struct {
baseURL string
token string
http *http.Client
httpClient *http.Client
// RetryBackoff defines the delays between retry attempts for 429 responses.
// RetryBackoff[i] is the delay before attempt i+1 (after attempt i fails).
// If nil, defaults to {1s, 2s}. Set to shorter durations in tests.
RetryBackoff []time.Duration
// retryBackoff defines the delays between retry attempts for 429 responses.
// retryBackoff[i] is the delay before attempt i+1 (after attempt i fails).
// If nil, defaults to {1s, 2s}. Set to shorter durations in tests via SetRetryBackoff.
retryBackoff []time.Duration
}
// NewClient creates a new GitHub API client.
@@ -86,16 +88,17 @@ func NewClient(token, baseURL string) *Client {
return &Client{
baseURL: strings.TrimRight(baseURL, "/"),
token: token,
http: &http.Client{
httpClient: &http.Client{
Timeout: 30 * time.Second,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
// Prevent forwarding Authorization header to different hosts on redirect.
if len(via) > 0 && req.URL.Host != via[0].URL.Host {
req.Header.Del("Authorization")
}
if len(via) >= 10 {
return fmt.Errorf("stopped after 10 redirects")
}
// Strip Authorization on cross-host redirect or protocol downgrade (https→http).
prev := via[len(via)-1]
if req.URL.Host != prev.URL.Host || (prev.URL.Scheme == "https" && req.URL.Scheme == "http") {
req.Header.Del("Authorization")
}
return nil
},
},
@@ -105,7 +108,13 @@ func NewClient(token, baseURL string) *Client {
// SetHTTPClient sets the underlying HTTP client used for requests.
// This is intended for testing to inject mock transports.
func (c *Client) SetHTTPClient(hc *http.Client) {
c.http = hc
c.httpClient = hc
}
// SetRetryBackoff configures the retry backoff durations for testing.
// In production the default {1s, 2s} applies.
func (c *Client) SetRetryBackoff(d []time.Duration) {
c.retryBackoff = d
}
// doRequest performs an HTTP request with retry on 429 rate limit responses.
@@ -116,9 +125,9 @@ func (c *Client) doRequest(ctx context.Context, method, url string, accept strin
const maxRetryAfter = 120 * time.Second
var backoff []time.Duration
if c.RetryBackoff != nil {
backoff = make([]time.Duration, len(c.RetryBackoff))
copy(backoff, c.RetryBackoff)
if c.retryBackoff != nil {
backoff = make([]time.Duration, len(c.retryBackoff))
copy(backoff, c.retryBackoff)
} else {
backoff = []time.Duration{1 * time.Second, 2 * time.Second}
}
@@ -157,7 +166,7 @@ func (c *Client) doRequest(ctx context.Context, method, url string, accept strin
req.Header.Set("Accept", "application/vnd.github+json")
}
resp, err := c.http.Do(req)
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("do request: %w", err)
}