fix: timer leak and http field shadowing in github client
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 46s
CI / review (gpt-5, gpt, GPT_REVIEW_TOKEN) (pull_request) Successful in 1m57s
CI / review (gpt-5, security, ., rodin/security-patterns, SECURITY_REVIEW.md, SECURITY_REVIEW_TOKEN) (pull_request) Successful in 2m17s

- Add timer.Stop() to the timer.C branch to prevent timer leak on the
  normal path (previously only called in ctx.Done branch)
- Rename struct field 'http' to 'httpClient' to avoid shadowing the
  net/http import

Addresses self-review findings on PR #110.
This commit is contained in:
claw
2026-05-13 06:22:30 -07:00
parent 31a28b1dd5
commit 9f8e9aa8d3
+5 -4
View File
@@ -95,7 +95,7 @@ type Client struct {
// accepting full URLs instead. // accepting full URLs instead.
baseURL string baseURL string
token string token string
http *http.Client httpClient *http.Client
// retryBackoff defines the delays between retry attempts for 429 responses. // retryBackoff defines the delays between retry attempts for 429 responses.
// retryBackoff[i] is the delay before attempt i+1 (after attempt i fails). // retryBackoff[i] is the delay before attempt i+1 (after attempt i fails).
@@ -117,7 +117,7 @@ func NewClient(token, baseURL string) *Client {
return &Client{ return &Client{
baseURL: strings.TrimRight(baseURL, "/"), baseURL: strings.TrimRight(baseURL, "/"),
token: token, token: token,
http: &http.Client{Timeout: 30 * time.Second}, httpClient: &http.Client{Timeout: 30 * time.Second},
now: time.Now, now: time.Now,
} }
} }
@@ -125,7 +125,7 @@ func NewClient(token, baseURL string) *Client {
// SetHTTPClient sets the underlying HTTP client used for requests. // SetHTTPClient sets the underlying HTTP client used for requests.
// This is intended for testing to inject mock transports. // This is intended for testing to inject mock transports.
func (c *Client) SetHTTPClient(hc *http.Client) { func (c *Client) SetHTTPClient(hc *http.Client) {
c.http = hc c.httpClient = hc
} }
// SetRetryBackoff sets the delays between retry attempts. // SetRetryBackoff sets the delays between retry attempts.
@@ -192,6 +192,7 @@ func (c *Client) doRequest(ctx context.Context, method, reqURL string, accept st
timer := time.NewTimer(delay) timer := time.NewTimer(delay)
select { select {
case <-timer.C: case <-timer.C:
timer.Stop()
case <-ctx.Done(): case <-ctx.Done():
timer.Stop() timer.Stop()
return nil, ctx.Err() return nil, ctx.Err()
@@ -210,7 +211,7 @@ func (c *Client) doRequest(ctx context.Context, method, reqURL string, accept st
req.Header.Set("Accept", "application/vnd.github+json") req.Header.Set("Accept", "application/vnd.github+json")
} }
resp, err := c.http.Do(req) resp, err := c.httpClient.Do(req)
if err != nil { if err != nil {
return nil, fmt.Errorf("do request: %w", err) return nil, fmt.Errorf("do request: %w", err)
} }