20 | 20 |
// Client wraps a URL and provides a method that implements endpoint.Endpoint.
|
21 | 21 |
type Client struct {
|
22 | 22 |
client HTTPClient
|
23 | |
method string
|
24 | |
tgt *url.URL
|
25 | |
enc EncodeRequestFunc
|
|
23 |
req CreateRequestFunc
|
26 | 24 |
dec DecodeResponseFunc
|
27 | 25 |
before []RequestFunc
|
28 | 26 |
after []ClientResponseFunc
|
|
31 | 29 |
}
|
32 | 30 |
|
33 | 31 |
// NewClient constructs a usable Client for a single remote method.
|
34 | |
func NewClient(
|
35 | |
method string,
|
36 | |
tgt *url.URL,
|
37 | |
enc EncodeRequestFunc,
|
38 | |
dec DecodeResponseFunc,
|
39 | |
options ...ClientOption,
|
40 | |
) *Client {
|
|
32 |
func NewClient(method string, tgt *url.URL, enc EncodeRequestFunc, dec DecodeResponseFunc, options ...ClientOption) *Client {
|
|
33 |
return NewExplicitClient(makeCreateRequestFunc(method, tgt, enc), dec, options...)
|
|
34 |
}
|
|
35 |
|
|
36 |
// NewExplicitClient is like NewClient but uses a CreateRequestFunc instead of a
|
|
37 |
// method, target URL, and EncodeRequestFunc, which allows for more control over
|
|
38 |
// the outgoing HTTP request.
|
|
39 |
func NewExplicitClient(req CreateRequestFunc, dec DecodeResponseFunc, options ...ClientOption) *Client {
|
41 | 40 |
c := &Client{
|
42 | |
client: http.DefaultClient,
|
43 | |
method: method,
|
44 | |
tgt: tgt,
|
45 | |
enc: enc,
|
46 | |
dec: dec,
|
47 | |
before: []RequestFunc{},
|
48 | |
after: []ClientResponseFunc{},
|
49 | |
bufferedStream: false,
|
|
41 |
client: http.DefaultClient,
|
|
42 |
req: req,
|
|
43 |
dec: dec,
|
50 | 44 |
}
|
51 | 45 |
for _, option := range options {
|
52 | 46 |
option(c)
|
|
63 | 57 |
return func(c *Client) { c.client = client }
|
64 | 58 |
}
|
65 | 59 |
|
66 | |
// ClientBefore sets the RequestFuncs that are applied to the outgoing HTTP
|
|
60 |
// ClientBefore adds one or more RequestFuncs to be applied to the outgoing HTTP
|
67 | 61 |
// request before it's invoked.
|
68 | 62 |
func ClientBefore(before ...RequestFunc) ClientOption {
|
69 | 63 |
return func(c *Client) { c.before = append(c.before, before...) }
|
70 | 64 |
}
|
71 | 65 |
|
72 | |
// ClientAfter sets the ClientResponseFuncs applied to the incoming HTTP
|
73 | |
// request prior to it being decoded. This is useful for obtaining anything off
|
74 | |
// of the response and adding onto the context prior to decoding.
|
|
66 |
// ClientAfter adds one or more ClientResponseFuncs, which are applied to the
|
|
67 |
// incoming HTTP response prior to it being decoded. This is useful for
|
|
68 |
// obtaining anything off of the response and adding it into the context prior
|
|
69 |
// to decoding.
|
75 | 70 |
func ClientAfter(after ...ClientResponseFunc) ClientOption {
|
76 | 71 |
return func(c *Client) { c.after = append(c.after, after...) }
|
77 | 72 |
}
|
78 | 73 |
|
79 | |
// ClientFinalizer is executed at the end of every HTTP request.
|
80 | |
// By default, no finalizer is registered.
|
|
74 |
// ClientFinalizer adds one or more ClientFinalizerFuncs to be executed at the
|
|
75 |
// end of every HTTP request. Finalizers are executed in the order in which they
|
|
76 |
// were added. By default, no finalizer is registered.
|
81 | 77 |
func ClientFinalizer(f ...ClientFinalizerFunc) ClientOption {
|
82 | 78 |
return func(s *Client) { s.finalizer = append(s.finalizer, f...) }
|
83 | 79 |
}
|
84 | 80 |
|
85 | |
// BufferedStream sets whether the Response.Body is left open, allowing it
|
|
81 |
// BufferedStream sets whether the HTTP response body is left open, allowing it
|
86 | 82 |
// to be read from later. Useful for transporting a file as a buffered stream.
|
87 | |
// That body has to be Closed to propery end the request.
|
|
83 |
// That body has to be drained and closed to properly end the request.
|
88 | 84 |
func BufferedStream(buffered bool) ClientOption {
|
89 | 85 |
return func(c *Client) { c.bufferedStream = buffered }
|
90 | 86 |
}
|
91 | 87 |
|
92 | |
// Endpoint returns a usable endpoint that invokes the remote endpoint.
|
|
88 |
// Endpoint returns a usable Go kit endpoint that calls the remote HTTP endpoint.
|
93 | 89 |
func (c Client) Endpoint() endpoint.Endpoint {
|
94 | 90 |
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
95 | 91 |
ctx, cancel := context.WithCancel(ctx)
|
|
110 | 106 |
}()
|
111 | 107 |
}
|
112 | 108 |
|
113 | |
req, err := http.NewRequest(c.method, c.tgt.String(), nil)
|
114 | |
if err != nil {
|
115 | |
cancel()
|
116 | |
return nil, err
|
117 | |
}
|
118 | |
|
119 | |
if err = c.enc(ctx, req, request); err != nil {
|
|
109 |
req, err := c.req(ctx, request)
|
|
110 |
if err != nil {
|
120 | 111 |
cancel()
|
121 | 112 |
return nil, err
|
122 | 113 |
}
|
|
126 | 117 |
}
|
127 | 118 |
|
128 | 119 |
resp, err = c.client.Do(req.WithContext(ctx))
|
129 | |
|
130 | 120 |
if err != nil {
|
131 | 121 |
cancel()
|
132 | 122 |
return nil, err
|
133 | 123 |
}
|
134 | 124 |
|
135 | |
// If we expect a buffered stream, we don't cancel the context when the endpoint returns.
|
136 | |
// Instead, we should call the cancel func when closing the response body.
|
|
125 |
// If the caller asked for a buffered stream, we don't cancel the
|
|
126 |
// context when the endpoint returns. Instead, we should call the
|
|
127 |
// cancel func when closing the response body.
|
137 | 128 |
if c.bufferedStream {
|
138 | 129 |
resp.Body = bodyWithCancel{ReadCloser: resp.Body, cancel: cancel}
|
139 | 130 |
} else {
|
|
206 | 197 |
r.Body = ioutil.NopCloser(&b)
|
207 | 198 |
return xml.NewEncoder(&b).Encode(request)
|
208 | 199 |
}
|
|
200 |
|
|
201 |
//
|
|
202 |
//
|
|
203 |
//
|
|
204 |
|
|
205 |
func makeCreateRequestFunc(method string, target *url.URL, enc EncodeRequestFunc) CreateRequestFunc {
|
|
206 |
return func(ctx context.Context, request interface{}) (*http.Request, error) {
|
|
207 |
req, err := http.NewRequest(method, target.String(), nil)
|
|
208 |
if err != nil {
|
|
209 |
return nil, err
|
|
210 |
}
|
|
211 |
|
|
212 |
if err = enc(ctx, req, request); err != nil {
|
|
213 |
return nil, err
|
|
214 |
}
|
|
215 |
|
|
216 |
return req, nil
|
|
217 |
}
|
|
218 |
}
|