-
Notifications
You must be signed in to change notification settings - Fork 7
/
client.go
146 lines (129 loc) · 4.5 KB
/
client.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package httpretry
import (
"errors"
"net/http"
)
const (
defaultMaxRetryCount = 5
)
// NewDefaultClient returns a default http client with retry functionality wrapped around the Roundtripper (client.Transport).
//
// You should not replace the client.Transport field, otherwise you will lose the retry functionality.
//
// If you need to set / change the original client.Transport field you have two options:
//
// 1. create your own http client and use NewCustomClient() function to enrich the client with retry functionality.
// client := &http.Client{}
// client.Transport = &http.Transport{ ... }
// retryClient := httpretry.NewCustomClient(client)
// 2. use one of the helper functions (e.g. httpretry.ModifyOriginalTransport(retryClient)) to retrieve and change the Transport.
// retryClient := httpretry.NewDefaultClient()
// err := httpretry.ModifyOriginalTransport(retryClient, func(t *http.Transport){t.TLSHandshakeTimeout = 5 * time.Second})
// if err != nil { ... } // will be nil if embedded Roundtripper was not of type http.Transport
func NewDefaultClient(opts ...Option) *http.Client {
return NewCustomClient(&http.Client{}, opts...)
}
// NewCustomClient returns the provided http client with retry functionality wrapped around the Roundtripper (client.Transport).
//
// You should not replace the client.Transport field after creating the retry client, otherwise you will lose the retry functionality.
//
// If you need to change the original client.Transport field you may use the helper functions:
//
// err := httpretry.ModifyTransport(retryClient, func(t *http.Transport){t.TLSHandshakeTimeout = 5 * time.Second})
// if err != nil { ... } // will be nil if embedded Roundtripper was not of type http.Transport
func NewCustomClient(client *http.Client, opts ...Option) *http.Client {
if client == nil {
panic("client must not be nil")
}
nextRoundtripper := client.Transport
if nextRoundtripper == nil {
nextRoundtripper = http.DefaultTransport
}
// set defaults
retryRoundtripper := &RetryRoundtripper{
Next: nextRoundtripper,
MaxRetryCount: defaultMaxRetryCount,
ShouldRetry: defaultRetryPolicy,
CalculateBackoff: defaultBackoffPolicy,
}
// overwrite defaults with user provided configuration
for _, o := range opts {
o(retryRoundtripper)
}
client.Transport = retryRoundtripper
return client
}
// GetOriginalRoundtripper returns the original roundtripper that was embedded in the retry roundtripper.
func GetOriginalRoundtripper(client *http.Client) http.RoundTripper {
if client == nil {
panic("client must not be nil")
}
switch r := client.Transport.(type) {
case *RetryRoundtripper:
return r.Next
default: // also catches Transport == nil
return client.Transport
}
}
// ReplaceOriginalRoundtripper replaces the original roundtripper that was embedded in the retry roundtripper
func ReplaceOriginalRoundtripper(client *http.Client, roundtripper http.RoundTripper) error {
if client == nil {
panic("client must not be nil")
}
switch r := client.Transport.(type) {
case *RetryRoundtripper:
r.Next = roundtripper
return nil
default:
client.Transport = roundtripper
return nil
}
}
// GetOriginalTransport retrieves the original http.Transport that was mebedded in the retry roundtripper.
func GetOriginalTransport(client *http.Client) (*http.Transport, error) {
if client == nil {
panic("client must not be nil")
}
switch r := client.Transport.(type) {
case *RetryRoundtripper:
switch t := r.Next.(type) {
case *http.Transport:
return t, nil
case nil:
return nil, nil
default:
return nil, errors.New("embedded roundtripper is not of type *http.Transport")
}
case *http.Transport:
return r, nil
case nil:
return nil, nil
default:
return nil, errors.New("roundtripper is not of type *http.Transport")
}
}
// ModifyOriginalTransport allows to modify the original http.Transport that was embedded in the retry roundtipper.
func ModifyOriginalTransport(client *http.Client, f func(transport *http.Transport)) error {
if client == nil {
panic("client must not be nil")
}
switch r := client.Transport.(type) {
case *http.Transport:
f(r)
return nil
case *RetryRoundtripper:
switch t := r.Next.(type) {
case nil:
return errors.New("embedded transport was nil")
case *http.Transport:
f(t)
return nil
default:
return errors.New("embedded roundtripper is not of type *http.Transport")
}
case nil:
return errors.New("transport was nil")
default:
return errors.New("transport is not of type *http.Transport")
}
}