Validate Weather API endpoints and retries
This commit is contained in:
@@ -75,6 +75,9 @@ func New(cfg config.Config, opts ...Option) (*Client, error) {
|
||||
if err != nil || baseURL.Scheme == "" || baseURL.Host == "" {
|
||||
return nil, fmt.Errorf("weather_api.base_url must be an absolute URL")
|
||||
}
|
||||
if !strings.EqualFold(baseURL.Scheme, "http") && !strings.EqualFold(baseURL.Scheme, "https") {
|
||||
return nil, fmt.Errorf("weather_api.base_url must use http or https")
|
||||
}
|
||||
|
||||
timeout := cfg.WeatherAPI.Timeout
|
||||
if timeout <= 0 {
|
||||
@@ -453,22 +456,27 @@ func (c *Client) warmup(ctx context.Context) error {
|
||||
}
|
||||
attempts := positiveAttemptCount(c.warmupAttempts)
|
||||
var lastErr error
|
||||
var lastRetryable bool
|
||||
for attempt := 1; attempt <= attempts; attempt++ {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return fmt.Errorf("warm up weather API via %s: %w", endpoint, err)
|
||||
}
|
||||
if err := c.warmupOnce(ctx, endpoint); err != nil {
|
||||
lastErr = err
|
||||
lastRetryable = isRetryableRequestError(err)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
if attempt == attempts {
|
||||
if !lastRetryable || attempt == attempts {
|
||||
break
|
||||
}
|
||||
if err := waitForRetry(ctx, c.warmupDelay); err != nil {
|
||||
return fmt.Errorf("warm up weather API via %s after %d attempt(s): %w", endpoint, attempt, err)
|
||||
}
|
||||
}
|
||||
if !lastRetryable {
|
||||
return lastErr
|
||||
}
|
||||
return fmt.Errorf("warm up weather API via %s failed after %d attempts: %w", endpoint, attempts, lastErr)
|
||||
}
|
||||
|
||||
@@ -481,16 +489,28 @@ func (c *Client) warmupOnce(ctx context.Context, endpoint string) error {
|
||||
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("fetch %s: %w", endpoint, err)
|
||||
err = fmt.Errorf("fetch %s: %w", endpoint, err)
|
||||
if ctx.Err() != nil {
|
||||
return err
|
||||
}
|
||||
return retryableRequestError{err: err}
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(io.LimitReader(resp.Body, 10<<20))
|
||||
if err != nil {
|
||||
return fmt.Errorf("read %s response: %w", endpoint, err)
|
||||
err = fmt.Errorf("read %s response: %w", endpoint, err)
|
||||
if ctx.Err() != nil {
|
||||
return err
|
||||
}
|
||||
return retryableRequestError{err: err}
|
||||
}
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
return fmt.Errorf("fetch %s: unexpected HTTP status %d: %s", endpoint, resp.StatusCode, strings.TrimSpace(string(body)))
|
||||
err := fmt.Errorf("fetch %s: unexpected HTTP status %d: %s", endpoint, resp.StatusCode, strings.TrimSpace(string(body)))
|
||||
if isRetryableHTTPStatus(resp.StatusCode) {
|
||||
return retryableRequestError{err: err}
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user