Skip to content

Commit 5bf1672

Browse files
authored
Merge pull request #5 from ovh/dev/phsym/retryable-http
feat: add built-in support for retryable-http
2 parents 49e6fff + 187140d commit 5bf1672

File tree

3 files changed

+116
-3
lines changed

3 files changed

+116
-3
lines changed

client.go

+97-2
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,120 @@ package okms
1111

1212
import (
1313
"context"
14+
"crypto/tls"
1415
"errors"
1516
"fmt"
17+
"io"
1618
"net/http"
19+
"net/http/httputil"
20+
"os"
1721
"reflect"
1822
"strings"
23+
"time"
1924

2025
"github.com/google/uuid"
26+
"github.com/hashicorp/go-retryablehttp"
2127
"github.com/ovh/okms-sdk-go/internal"
2228
"github.com/ovh/okms-sdk-go/types"
2329
)
2430

31+
const DefaultHTTPClientTimeout = 30 * time.Second
32+
2533
// RestAPIClient is the main client to the KMS rest api.
2634
type RestAPIClient struct {
2735
inner internal.ClientWithResponsesInterface
2836
customHeaders map[string]string
2937
}
3038

31-
// NewRestAPIClientWithHttp creates and initialize a new HTTP connection to the KMS at url `endpoint`
32-
// using the provided [http.Client].
39+
// LeveledLogger represents loggers that can be used inside the client.
40+
type LeveledLogger retryablehttp.LeveledLogger
41+
42+
// ClientConfig is used to configure Rest clients created using NewRestAPIClient().
43+
type ClientConfig struct {
44+
Timeout *time.Duration
45+
Retry *RetryConfig
46+
Logger LeveledLogger
47+
TlsCfg *tls.Config
48+
Middleware func(http.RoundTripper) http.RoundTripper
49+
}
50+
51+
type RetryConfig struct {
52+
RetryMax int
53+
RetryWaitMin time.Duration
54+
RetryWaitMax time.Duration
55+
}
56+
57+
type debugTransport struct {
58+
next http.RoundTripper
59+
out io.Writer
60+
}
61+
62+
// RoundTrip implements http.RoundTripper.
63+
func (t *debugTransport) RoundTrip(r *http.Request) (*http.Response, error) {
64+
data, _ := httputil.DumpRequestOut(r, true)
65+
fmt.Fprintf(os.Stderr, "REQUEST:\n%s\n", data)
66+
resp, err := t.next.RoundTrip(r)
67+
if err != nil {
68+
return resp, err
69+
}
70+
data, _ = httputil.DumpResponse(resp, true)
71+
fmt.Fprintf(os.Stderr, "RESPONSE:\n%s\n", data)
72+
return resp, nil
73+
}
74+
75+
// DebugTransport creates an http client middleware that will dump all the HTTP resquests and
76+
// responses to the giver io.Writer. It can be passed to ClientConfig.Middleware.
77+
func DebugTransport(out io.Writer) func(http.RoundTripper) http.RoundTripper {
78+
return func(rt http.RoundTripper) http.RoundTripper {
79+
if rt == nil {
80+
rt = http.DefaultTransport
81+
}
82+
if out == nil {
83+
out = os.Stderr
84+
}
85+
return &debugTransport{
86+
next: rt,
87+
out: out,
88+
}
89+
}
90+
}
91+
92+
// NewRestAPIClient creates and initializes a new HTTP connection to the KMS at url `endpoint`
93+
// using the provided client configuration. It allows configuring retries, timeouts and loggers.
94+
func NewRestAPIClient(endpoint string, clientCfg ClientConfig) (*RestAPIClient, error) {
95+
client := retryablehttp.NewClient()
96+
client.HTTPClient.Timeout = DefaultHTTPClientTimeout
97+
client.Logger = nil
98+
99+
client.HTTPClient.Transport.(*http.Transport).TLSClientConfig = clientCfg.TlsCfg
100+
if clientCfg.Logger != nil {
101+
client.Logger = clientCfg.Logger
102+
}
103+
104+
if clientCfg.Timeout != nil {
105+
client.HTTPClient.Timeout = *clientCfg.Timeout
106+
}
107+
108+
if clientCfg.Retry != nil {
109+
client.RetryMax = clientCfg.Retry.RetryMax
110+
if clientCfg.Retry.RetryWaitMin > 0 {
111+
client.RetryWaitMin = clientCfg.Retry.RetryWaitMin
112+
}
113+
if clientCfg.Retry.RetryWaitMax > 0 {
114+
client.RetryWaitMax = clientCfg.Retry.RetryWaitMax
115+
}
116+
}
117+
if clientCfg.Middleware != nil {
118+
client.HTTPClient.Transport = clientCfg.Middleware(client.HTTPClient.Transport)
119+
}
120+
121+
client.ErrorHandler = retryablehttp.PassthroughErrorHandler
122+
123+
return NewRestAPIClientWithHttp(endpoint, client.StandardClient())
124+
}
125+
126+
// NewRestAPIClientWithHttp is a lower level constructor to create and initialize a new HTTP
127+
// connection to the KMS at url `endpoint` using the provided [http.Client].
33128
//
34129
// The client must be configured with an appropriate tls.Config using client TLS certificates for authentication.
35130
func NewRestAPIClientWithHttp(endpoint string, c *http.Client) (*RestAPIClient, error) {

go.mod

+5-1
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@ go 1.23.0
44

55
require (
66
github.com/google/uuid v1.6.0
7+
github.com/hashicorp/go-retryablehttp v0.7.7
78
github.com/oapi-codegen/runtime v1.1.1
89
golang.org/x/crypto v0.28.0
910
)
1011

11-
require github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
12+
require (
13+
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
14+
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
15+
)

go.sum

+14
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,21 @@ github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvF
55
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
66
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
77
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
8+
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
9+
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
810
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
911
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
12+
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
13+
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
14+
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
15+
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
16+
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
17+
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
1018
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
19+
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
20+
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
21+
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
22+
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
1123
github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro=
1224
github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg=
1325
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -19,5 +31,7 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU
1931
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
2032
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
2133
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
34+
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
35+
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
2236
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
2337
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

0 commit comments

Comments
 (0)