chore(ax): add agent-aware HTTP identity and timeouts

This commit is contained in:
Virgil 2026-03-29 23:06:29 +00:00
parent e605041fa8
commit ee154a2c15
4 changed files with 64 additions and 15 deletions

View file

@ -3,8 +3,10 @@ package updater
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"context"
"net/http" "net/http"
"net/url" "net/url"
"strings"
coreerr "forge.lthn.ai/core/go-log" coreerr "forge.lthn.ai/core/go-log"
) )
@ -31,10 +33,16 @@ func GetLatestUpdateFromURL(baseURL string) (*GenericUpdateInfo, error) {
if err != nil { if err != nil {
return nil, coreerr.E("GetLatestUpdateFromURL", "invalid base URL", err) return nil, coreerr.E("GetLatestUpdateFromURL", "invalid base URL", err)
} }
// Append latest.json to the path
u.Path += "/latest.json"
resp, err := http.Get(u.String()) // Append latest.json to the path
u.Path = strings.TrimSuffix(u.Path, "/") + "/latest.json"
req, err := newAgentRequest(context.Background(), "GET", u.String())
if err != nil {
return nil, coreerr.E("GetLatestUpdateFromURL", "failed to create update check request", err)
}
resp, err := NewHTTPClient().Do(req)
if err != nil { if err != nil {
return nil, coreerr.E("GetLatestUpdateFromURL", "failed to fetch latest.json", err) return nil, coreerr.E("GetLatestUpdateFromURL", "failed to fetch latest.json", err)
} }

View file

@ -50,12 +50,15 @@ type githubClient struct{}
var NewAuthenticatedClient = func(ctx context.Context) *http.Client { var NewAuthenticatedClient = func(ctx context.Context) *http.Client {
token := os.Getenv("GITHUB_TOKEN") token := os.Getenv("GITHUB_TOKEN")
if token == "" { if token == "" {
return http.DefaultClient return NewHTTPClient()
} }
ts := oauth2.StaticTokenSource( ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: token}, &oauth2.Token{AccessToken: token},
) )
return oauth2.NewClient(ctx, ts) client := oauth2.NewClient(ctx, ts)
client.Timeout = defaultHTTPTimeout
return client
} }
func (g *githubClient) GetPublicRepos(ctx context.Context, userOrOrg string) ([]string, error) { func (g *githubClient) GetPublicRepos(ctx context.Context, userOrOrg string) ([]string, error) {
@ -71,11 +74,10 @@ func (g *githubClient) getPublicReposWithAPIURL(ctx context.Context, apiURL, use
if err := ctx.Err(); err != nil { if err := ctx.Err(); err != nil {
return nil, err return nil, err
} }
req, err := http.NewRequestWithContext(ctx, "GET", url, nil) req, err := newAgentRequest(ctx, "GET", url)
if err != nil { if err != nil {
return nil, err return nil, err
} }
req.Header.Set("User-Agent", "Borg-Data-Collector")
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
return nil, err return nil, err
@ -85,11 +87,10 @@ func (g *githubClient) getPublicReposWithAPIURL(ctx context.Context, apiURL, use
_ = resp.Body.Close() _ = resp.Body.Close()
// Try organization endpoint // Try organization endpoint
url = fmt.Sprintf("%s/orgs/%s/repos", apiURL, userOrOrg) url = fmt.Sprintf("%s/orgs/%s/repos", apiURL, userOrOrg)
req, err = http.NewRequestWithContext(ctx, "GET", url, nil) req, err = newAgentRequest(ctx, "GET", url)
if err != nil { if err != nil {
return nil, err return nil, err
} }
req.Header.Set("User-Agent", "Borg-Data-Collector")
resp, err = client.Do(req) resp, err = client.Do(req)
if err != nil { if err != nil {
return nil, err return nil, err
@ -143,11 +144,10 @@ func (g *githubClient) GetLatestRelease(ctx context.Context, owner, repo, channe
client := NewAuthenticatedClient(ctx) client := NewAuthenticatedClient(ctx)
url := fmt.Sprintf("https://api.github.com/repos/%s/%s/releases", owner, repo) url := fmt.Sprintf("https://api.github.com/repos/%s/%s/releases", owner, repo)
req, err := http.NewRequestWithContext(ctx, "GET", url, nil) req, err := newAgentRequest(ctx, "GET", url)
if err != nil { if err != nil {
return nil, err return nil, err
} }
req.Header.Set("User-Agent", "Borg-Data-Collector")
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
@ -198,11 +198,10 @@ func (g *githubClient) GetReleaseByPullRequest(ctx context.Context, owner, repo
client := NewAuthenticatedClient(ctx) client := NewAuthenticatedClient(ctx)
url := fmt.Sprintf("https://api.github.com/repos/%s/%s/releases", owner, repo) url := fmt.Sprintf("https://api.github.com/repos/%s/%s/releases", owner, repo)
req, err := http.NewRequestWithContext(ctx, "GET", url, nil) req, err := newAgentRequest(ctx, "GET", url)
if err != nil { if err != nil {
return nil, err return nil, err
} }
req.Header.Set("User-Agent", "Borg-Data-Collector")
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {

32
http_client.go Normal file
View file

@ -0,0 +1,32 @@
package updater
import (
"context"
"fmt"
"net/http"
"time"
)
const defaultHTTPTimeout = 30 * time.Second
var NewHTTPClient = func() *http.Client {
return &http.Client{Timeout: defaultHTTPTimeout}
}
func newAgentRequest(ctx context.Context, method, url string) (*http.Request, error) {
req, err := http.NewRequestWithContext(ctx, method, url, nil)
if err != nil {
return nil, err
}
req.Header.Set("User-Agent", updaterUserAgent())
return req, nil
}
func updaterUserAgent() string {
version := formatVersionForDisplay(Version, true)
if version == "" {
version = "unknown"
}
return fmt.Sprintf("agent-go-update/%s", version)
}

View file

@ -31,9 +31,15 @@ var NewGithubClient = func() GithubClient {
// DoUpdate is a variable that holds the function to perform the actual update. // DoUpdate is a variable that holds the function to perform the actual update.
// This can be replaced in tests to prevent actual updates. // This can be replaced in tests to prevent actual updates.
var DoUpdate = func(url string) error { var DoUpdate = func(url string) error {
resp, err := http.Get(url) client := NewHTTPClient()
req, err := newAgentRequest(context.Background(), "GET", url)
if err != nil { if err != nil {
return err return coreerr.E("DoUpdate", "failed to create update request", err)
}
resp, err := client.Do(req)
if err != nil {
return coreerr.E("DoUpdate", "failed to download update", err)
} }
defer func(Body io.ReadCloser) { defer func(Body io.ReadCloser) {
err := Body.Close() err := Body.Close()
@ -42,6 +48,10 @@ var DoUpdate = func(url string) error {
} }
}(resp.Body) }(resp.Body)
if resp.StatusCode != http.StatusOK {
return coreerr.E("DoUpdate", fmt.Sprintf("failed to download update: %s", resp.Status), nil)
}
err = selfupdate.Apply(resp.Body, selfupdate.Options{}) err = selfupdate.Apply(resp.Body, selfupdate.Options{})
if err != nil { if err != nil {
if rerr := selfupdate.RollbackError(err); rerr != nil { if rerr := selfupdate.RollbackError(err); rerr != nil {