@@ -106,32 +106,47 @@ func NewFarcasterConfig(token string, apiURLs []string, logger *zap.SugaredLogge
106106
107107// Load fetches and parses the agent configuration from Probely's API.
108108func (c * FarcasterConfig ) Load (mustResolve bool ) error {
109- var err error
109+ type fetchResult struct {
110+ url string
111+ data []byte
112+ headers http.Header
113+ err error
114+ }
110115
111- // Fetch the config using the API .
112- var data []byte
116+ // Try each API URL, collecting diagnostics to report if all attempts fail .
117+ var results []fetchResult
113118 for _ , url := range c .apiURLs {
114- if data , err = c .fetch (url ); err == nil {
115- c .log .Infof ("Fetched configuration from %s" , url )
116- break
119+ data , headers , err := c .fetch (url )
120+ if err != nil {
121+ results = append (results , fetchResult {
122+ url : url ,
123+ data : data ,
124+ headers : headers ,
125+ err : err ,
126+ })
127+ continue
117128 }
118- }
119- if err != nil {
120- c .log .Errorf ("Error fetching configuration. Tried: %s" , strings .Join (c .apiURLs , ", " ))
121- return fmt .Errorf ("could not fetch configuration: %s" , err )
122- }
123129
124- // Decrypt the keys and build the raw configuration files.
125- if err = c .build (data ); err != nil {
126- return fmt .Errorf ("could not build configuration: %s" , err )
130+ c .log .Infof ("Fetched configuration from %s" , url )
131+ if err = c .build (data ); err != nil {
132+ return fmt .Errorf ("could not build configuration: %w" , err )
133+ }
134+ if err = c .parse (mustResolve ); err != nil {
135+ return fmt .Errorf ("could not parse configuration: %w" , err )
136+ }
137+ return nil
127138 }
128139
129- // Parse the configuration files.
130- if err = c .parse (mustResolve ); err != nil {
131- return fmt .Errorf ("could not parse configuration: %s" , err )
140+ // All attempts failed. Log diagnostics for each failure.
141+ for _ , result := range results {
142+ c .log .Errorf ("Failed to fetch configuration from %s: %v" , result .url , result .err )
143+ for k , v := range result .headers {
144+ c .log .Debugf (" %s: %s" , k , strings .Join (v , ", " ))
145+ }
146+ c .log .Debugf ("Response body from %s: %s" , result .url , result .data )
132147 }
133148
134- return nil
149+ return fmt . Errorf ( "could not fetch configuration from any of %d URL(s)" , len ( c . apiURLs ))
135150}
136151
137152func (c * FarcasterConfig ) parse (mustResolve bool ) error {
@@ -315,52 +330,51 @@ func (c *FarcasterConfig) getHTTPClient(timeout time.Duration) *http.Client {
315330 }
316331}
317332
318- // Fetch the configuration from the API .
319- func ( c * FarcasterConfig ) fetch ( url string ) ([] byte , error ) {
320- var data []byte
321- var err error
333+ // fetch returns the agent configuration along with response headers .
334+ // Headers are returned even on error to aid in troubleshooting network issues.
335+ func ( c * FarcasterConfig ) fetch ( url string ) ( data []byte , headers http. Header , err error ) {
336+ var tokenData [] byte
322337
323338 // Create a public token. A public token is an identifier that allows us
324339 // to fetch the configuration for this agent.
325- if data , err = base58 .Decode (c .token ); err != nil {
326- return nil , err
340+ if tokenData , err = base58 .Decode (c .token ); err != nil {
341+ return nil , nil , err
327342 }
328- cksum := sha256 .Sum256 (data )
343+ cksum := sha256 .Sum256 (tokenData )
329344 pubToken := base58 .Encode (cksum [:])
330345
331- // Prepare the request.
332346 u := fmt .Sprintf ("%s/scanning-agents/%s/config-files/" , url , pubToken )
333347 req , err := http .NewRequest ("GET" , u , nil )
334348 if err != nil {
335- return nil , err
349+ return nil , nil , err
336350 }
337351
338- // Set the user agent.
339352 userAgent := fmt .Sprintf ("%s/%s (%s %s)" , settings .Name , settings .Version , runtime .GOOS , runtime .GOARCH )
340353 req .Header .Set ("User-Agent" , userAgent )
341354
342- // Send the request.
343355 client := c .getHTTPClient (defaultTimeout )
344356 resp , err := client .Do (req )
345357 if err != nil {
346- return nil , err
358+ return nil , nil , err
347359 }
348360 defer resp .Body .Close ()
349361
350- // Check the response.
362+ headers = resp .Header .Clone ()
363+
364+ // Read response body before checking status to capture error pages.
365+ data , err = io .ReadAll (resp .Body )
366+ if err != nil {
367+ return data , headers , fmt .Errorf ("could not read response body: %w" , err )
368+ }
369+
351370 if resp .StatusCode < 200 || resp .StatusCode >= 400 {
352371 if resp .StatusCode == 404 {
353- return nil , fmt .Errorf ("agent token not found" )
372+ return data , headers , fmt .Errorf ("agent token not found (HTTP %d)" , resp . StatusCode )
354373 }
355- return nil , fmt .Errorf ("server response code: %d" , resp .StatusCode )
374+ return data , headers , fmt .Errorf ("HTTP %d" , resp .StatusCode )
356375 }
357376
358- // Read the response body.
359- if data , err = io .ReadAll (resp .Body ); err != nil {
360- return nil , fmt .Errorf ("could not download config: %s" , err )
361- }
362-
363- return data , nil
377+ return data , headers , nil
364378}
365379
366380// Decrypt configuration secrets, such as private keys.
0 commit comments