-
Notifications
You must be signed in to change notification settings - Fork 0
/
client.go
129 lines (108 loc) · 3.23 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
package httpclient
import (
"context"
"fmt"
"net/http"
)
// RequestOption defines an interface for types that can be passed to requests
// to customize request execution and processing.
type RequestOption interface {
reqOpt()
}
// ClientOption defines an interface for types that can be passed when
// constructing a Client to customize its behaviour.
type ClientOption interface {
clientOpt()
}
// HTTPClientOption is a ClientOption that customizes the http.Client in use.
type HTTPClientOption func(*http.Client)
func (HTTPClientOption) clientOpt() {}
// WithTransport creates a ClientOption using t for the Client to be created.
func WithTransport(t http.RoundTripper) ClientOption {
return HTTPClientOption(func(c *http.Client) {
c.Transport = t
})
}
// Client implements a convenient HTTP client.
type Client struct {
c *http.Client
reqInterceptors []RequestInterceptor
resInterceptors []ResponseInterceptor
}
// New create a new Client using the given opts to customize the client.
// Calling New() with no options creates a fully usable Client using defaults.
func New(opts ...ClientOption) *Client {
c := &Client{
c: new(http.Client),
}
for _, opt := range opts {
switch o := opt.(type) {
case HTTPClientOption:
o(c.c)
case RequestInterceptor:
c.reqInterceptors = append(c.reqInterceptors, o)
case ResponseInterceptor:
c.resInterceptors = append(c.resInterceptors, o)
default:
panic(fmt.Sprintf("unexpected option: %v", opt))
}
}
return c
}
// Get executes a HTTP GET request for url using ctx and opts. It returns the
// received and (potentially processed) response as well as any error received.
func (c *Client) Get(ctx context.Context, url string, opts ...RequestOption) (*http.Response, error) {
return c.Execute(ctx, http.MethodGet, url, opts...)
}
// Post executes a HTTP POST request for url using ctx and opts. In contrast to
// the standard lib's Post function this method sets the request body using a
// RequestInterceptor.
func (c *Client) Post(ctx context.Context, url string, opts ...RequestOption) (*http.Response, error) {
return c.Execute(ctx, http.MethodPost, url, opts...)
}
func (c *Client) Execute(ctx context.Context, method string, url string, opts ...RequestOption) (*http.Response, error) {
req, err := http.NewRequestWithContext(ctx, method, url, nil)
if err != nil {
return nil, err
}
return c.Do(req, opts...)
}
// Do executes req applying any opts and returns the received response as well
// as any error.
func (c *Client) Do(req *http.Request, opts ...RequestOption) (*http.Response, error) {
var err error
for _, i := range c.reqInterceptors {
req, err = i.InterceptRequest(req)
if err != nil {
return nil, err
}
}
for _, opt := range opts {
if i, ok := opt.(RequestInterceptor); ok {
req, err = i.InterceptRequest(req)
if err != nil {
return nil, err
}
}
}
res, err := c.c.Do(req)
if err != nil {
return res, err
}
defer res.Body.Close()
for _, i := range c.resInterceptors {
res, err = i.InterceptResponse(res)
if err != nil {
return res, err
}
}
for _, opt := range opts {
if i, ok := opt.(ResponseInterceptor); ok {
res, err = i.InterceptResponse(res)
if err != nil {
return res, err
}
}
}
return res, nil
}