From ee154a2c157120244d28de1a72e7f01e42261617 Mon Sep 17 00:00:00 2001 From: Virgil Date: Sun, 29 Mar 2026 23:06:29 +0000 Subject: [PATCH] chore(ax): add agent-aware HTTP identity and timeouts --- generic_http.go | 14 +++++++++++--- github.go | 19 +++++++++---------- http_client.go | 32 ++++++++++++++++++++++++++++++++ updater.go | 14 ++++++++++++-- 4 files changed, 64 insertions(+), 15 deletions(-) create mode 100644 http_client.go diff --git a/generic_http.go b/generic_http.go index d833783..29189bc 100644 --- a/generic_http.go +++ b/generic_http.go @@ -3,8 +3,10 @@ package updater import ( "encoding/json" "fmt" + "context" "net/http" "net/url" + "strings" coreerr "forge.lthn.ai/core/go-log" ) @@ -31,10 +33,16 @@ func GetLatestUpdateFromURL(baseURL string) (*GenericUpdateInfo, error) { if err != nil { 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 { return nil, coreerr.E("GetLatestUpdateFromURL", "failed to fetch latest.json", err) } diff --git a/github.go b/github.go index 3f85903..31354f1 100644 --- a/github.go +++ b/github.go @@ -50,12 +50,15 @@ type githubClient struct{} var NewAuthenticatedClient = func(ctx context.Context) *http.Client { token := os.Getenv("GITHUB_TOKEN") if token == "" { - return http.DefaultClient + return NewHTTPClient() } + ts := oauth2.StaticTokenSource( &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) { @@ -71,11 +74,10 @@ func (g *githubClient) getPublicReposWithAPIURL(ctx context.Context, apiURL, use if err := ctx.Err(); err != nil { return nil, err } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + req, err := newAgentRequest(ctx, "GET", url) if err != nil { return nil, err } - req.Header.Set("User-Agent", "Borg-Data-Collector") resp, err := client.Do(req) if err != nil { return nil, err @@ -85,11 +87,10 @@ func (g *githubClient) getPublicReposWithAPIURL(ctx context.Context, apiURL, use _ = resp.Body.Close() // Try organization endpoint 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 { return nil, err } - req.Header.Set("User-Agent", "Borg-Data-Collector") resp, err = client.Do(req) if err != nil { return nil, err @@ -143,11 +144,10 @@ func (g *githubClient) GetLatestRelease(ctx context.Context, owner, repo, channe client := NewAuthenticatedClient(ctx) 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 { return nil, err } - req.Header.Set("User-Agent", "Borg-Data-Collector") resp, err := client.Do(req) if err != nil { @@ -198,11 +198,10 @@ func (g *githubClient) GetReleaseByPullRequest(ctx context.Context, owner, repo client := NewAuthenticatedClient(ctx) 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 { return nil, err } - req.Header.Set("User-Agent", "Borg-Data-Collector") resp, err := client.Do(req) if err != nil { diff --git a/http_client.go b/http_client.go new file mode 100644 index 0000000..99125b4 --- /dev/null +++ b/http_client.go @@ -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) +} diff --git a/updater.go b/updater.go index 2487e49..8435d29 100644 --- a/updater.go +++ b/updater.go @@ -31,9 +31,15 @@ var NewGithubClient = func() GithubClient { // DoUpdate is a variable that holds the function to perform the actual update. // This can be replaced in tests to prevent actual updates. var DoUpdate = func(url string) error { - resp, err := http.Get(url) + client := NewHTTPClient() + req, err := newAgentRequest(context.Background(), "GET", url) 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) { err := Body.Close() @@ -42,6 +48,10 @@ var DoUpdate = func(url string) error { } }(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{}) if err != nil { if rerr := selfupdate.RollbackError(err); rerr != nil {