Skip to content

Commit

Permalink
caddyconfig: dependency-free http config loading retries
Browse files Browse the repository at this point in the history
  • Loading branch information
yroc92 committed Sep 25, 2022
1 parent 7006e2e commit a4739f4
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 22 deletions.
43 changes: 26 additions & 17 deletions caddyconfig/httploader.go
Expand Up @@ -15,17 +15,16 @@
package caddyconfig

import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"io"
"math"
"net/http"
"os"
"time"

"github.com/caddyserver/caddy/v2"
"github.com/hashicorp/go-retryablehttp"
)

func init() {
Expand All @@ -47,7 +46,8 @@ type HTTPLoader struct {
// Maximum time allowed for a complete connection and request.
Timeout caddy.Duration `json:"timeout,omitempty"`

// Maximum number of retries for a successful call to URL. Defaults to 0.
// Maximum number of retries for a successful call to URL.
// Does exponential back-offs (2...4...8...16... seconds) between retries. Defaults to 0.
MaxRetries int `json:"max_retries,omitempty"`

TLS *struct {
Expand Down Expand Up @@ -99,7 +99,7 @@ func (hl HTTPLoader) LoadConfig(ctx caddy.Context) ([]byte, error) {
}
}

resp, err := client.Do(req)
resp, err := doHttpCallWithRetries(client, req, hl.MaxRetries)
if err != nil {
return nil, err
}
Expand All @@ -124,25 +124,34 @@ func (hl HTTPLoader) LoadConfig(ctx caddy.Context) ([]byte, error) {
return result, nil
}

func getRetryClient(ctx caddy.Context, maxRetries int, timeout caddy.Duration) (*http.Client, error) {
retryClient := retryablehttp.NewClient()
retryClient.RetryMax = maxRetries

retryClient.CheckRetry = func(ctx context.Context, resp *http.Response, err error) (bool, error) {
if err != nil || (resp.StatusCode < 200 || resp.StatusCode > 499) {
return true, err
// Reattempts the http call using exponential back off when maxRetries is greater than 0.
func doHttpCallWithRetries(client *http.Client, request *http.Request, maxRetries int) (*http.Response, error) {
var resp *http.Response
var err error

for retry := 0; retry <= maxRetries; retry++ {
if retry > 0 {
caddy.Log().Error(err.Error())
exponentialBackOff := math.Pow(2, float64(retry))
caddy.Log().Sugar().Infof("reattempting http load in %g seconds (%v retries remain)", exponentialBackOff, maxRetries-retry)
time.Sleep(time.Duration(exponentialBackOff) * time.Second)
}
resp, err = client.Do(request)
if err != nil {
err = fmt.Errorf("problem calling http loader url: %v", err)
} else if resp.StatusCode < 200 || resp.StatusCode > 499 {
err = fmt.Errorf("bad response status code from http loader url: %v", resp.StatusCode)
} else {
return resp, nil
}
return false, err
}
retryClient.HTTPClient.Timeout = time.Duration(timeout)

return retryClient.StandardClient(), nil
return resp, err
}

func (hl HTTPLoader) makeClient(ctx caddy.Context) (*http.Client, error) {
client, err := getRetryClient(ctx, hl.MaxRetries, hl.Timeout)
if err != nil {
return nil, err
client := &http.Client{
Timeout: time.Duration(hl.Timeout),
}

if hl.TLS != nil {
Expand Down
2 changes: 0 additions & 2 deletions go.mod
Expand Up @@ -41,7 +41,6 @@ require (

require (
github.com/golang/mock v1.6.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
Expand Down Expand Up @@ -74,7 +73,6 @@ require (
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
github.com/hashicorp/go-retryablehttp v0.7.1
github.com/huandu/xstrings v1.3.2 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
Expand Down
3 changes: 0 additions & 3 deletions go.sum
Expand Up @@ -398,7 +398,6 @@ github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brv
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
Expand All @@ -412,8 +411,6 @@ github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9
github.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ=
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ=
github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/go-secure-stdlib/base62 v0.1.1/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw=
Expand Down

0 comments on commit a4739f4

Please sign in to comment.