88 "os/exec"
99 "slices"
1010 "strings"
11+ "time"
1112
1213 "github.com/goccy/go-json"
1314 "golang.org/x/mod/semver"
@@ -81,6 +82,9 @@ func getLatestVersion() (string, error) {
8182 proxies = append (proxies , goproxyDefault )
8283 }
8384
85+ client := & http.Client {Timeout : 10 * time .Second }
86+
87+ var lastErr error
8488 for _ , proxy := range proxies {
8589 proxy = strings .TrimSpace (proxy )
8690 proxy = strings .TrimRight (proxy , "/" )
@@ -89,24 +93,61 @@ func getLatestVersion() (string, error) {
8993 }
9094
9195 url := fmt .Sprintf ("%s/github.com/%s/%s/@latest" , proxy , repoOwner , repoName )
92- resp , err := http . Get ( url )
93- if err ! = nil {
94- continue
96+ version , err := fetchLatestWithRetry ( client , url )
97+ if err = = nil {
98+ return version , nil
9599 }
96- defer resp .Body .Close ()
100+ lastErr = err
101+ }
97102
98- body , err := io .ReadAll (resp .Body )
99- if err != nil {
100- continue
101- }
103+ if lastErr != nil {
104+ return "" , fmt .Errorf ("failed to fetch latest version: %w" , lastErr )
105+ }
106+ return "" , fmt .Errorf ("failed to fetch latest version" )
107+ }
102108
103- var version struct { Version string }
104- if err = json .Unmarshal (body , & version ); err != nil {
105- continue
109+ // fetchLatestWithRetry queries a single proxy, retrying a few times because
110+ // proxy.golang.org occasionally resets the connection or returns a transient
111+ // 5xx. A single drop should not fail the whole version check.
112+ func fetchLatestWithRetry (client * http.Client , url string ) (string , error ) {
113+ const maxAttempts = 3
114+ var lastErr error
115+ for attempt := 1 ; attempt <= maxAttempts ; attempt ++ {
116+ version , err := fetchLatestFromProxy (client , url )
117+ if err == nil {
118+ return version , nil
106119 }
120+ lastErr = err
121+ if attempt < maxAttempts {
122+ time .Sleep (time .Duration (attempt ) * 200 * time .Millisecond )
123+ }
124+ }
125+ return "" , lastErr
126+ }
107127
108- return version .Version , nil
128+ func fetchLatestFromProxy (client * http.Client , url string ) (string , error ) {
129+ resp , err := client .Get (url )
130+ if err != nil {
131+ return "" , err
109132 }
133+ defer resp .Body .Close ()
110134
111- return "" , fmt .Errorf ("failed to fetch latest version" )
135+ body , err := io .ReadAll (resp .Body )
136+ if err != nil {
137+ return "" , err
138+ }
139+
140+ if resp .StatusCode != http .StatusOK {
141+ return "" , fmt .Errorf ("unexpected status %s from %s" , resp .Status , url )
142+ }
143+
144+ var version struct { Version string }
145+ if err := json .Unmarshal (body , & version ); err != nil {
146+ return "" , fmt .Errorf ("invalid response from %s: %w" , url , err )
147+ }
148+ if version .Version == "" {
149+ return "" , fmt .Errorf ("empty version in response from %s" , url )
150+ }
151+
152+ return version .Version , nil
112153}
0 commit comments