OSDN Git Service

new repo
[bytom/vapor.git] / vendor / github.com / go-kit / kit / transport / http / client.go
1 package http
2
3 import (
4         "bytes"
5         "context"
6         "encoding/json"
7         "encoding/xml"
8         "io/ioutil"
9         "net/http"
10         "net/url"
11
12         "github.com/go-kit/kit/endpoint"
13 )
14
15 // Client wraps a URL and provides a method that implements endpoint.Endpoint.
16 type Client struct {
17         client         *http.Client
18         method         string
19         tgt            *url.URL
20         enc            EncodeRequestFunc
21         dec            DecodeResponseFunc
22         before         []RequestFunc
23         after          []ClientResponseFunc
24         finalizer      ClientFinalizerFunc
25         bufferedStream bool
26 }
27
28 // NewClient constructs a usable Client for a single remote method.
29 func NewClient(
30         method string,
31         tgt *url.URL,
32         enc EncodeRequestFunc,
33         dec DecodeResponseFunc,
34         options ...ClientOption,
35 ) *Client {
36         c := &Client{
37                 client:         http.DefaultClient,
38                 method:         method,
39                 tgt:            tgt,
40                 enc:            enc,
41                 dec:            dec,
42                 before:         []RequestFunc{},
43                 after:          []ClientResponseFunc{},
44                 bufferedStream: false,
45         }
46         for _, option := range options {
47                 option(c)
48         }
49         return c
50 }
51
52 // ClientOption sets an optional parameter for clients.
53 type ClientOption func(*Client)
54
55 // SetClient sets the underlying HTTP client used for requests.
56 // By default, http.DefaultClient is used.
57 func SetClient(client *http.Client) ClientOption {
58         return func(c *Client) { c.client = client }
59 }
60
61 // ClientBefore sets the RequestFuncs that are applied to the outgoing HTTP
62 // request before it's invoked.
63 func ClientBefore(before ...RequestFunc) ClientOption {
64         return func(c *Client) { c.before = append(c.before, before...) }
65 }
66
67 // ClientAfter sets the ClientResponseFuncs applied to the incoming HTTP
68 // request prior to it being decoded. This is useful for obtaining anything off
69 // of the response and adding onto the context prior to decoding.
70 func ClientAfter(after ...ClientResponseFunc) ClientOption {
71         return func(c *Client) { c.after = append(c.after, after...) }
72 }
73
74 // ClientFinalizer is executed at the end of every HTTP request.
75 // By default, no finalizer is registered.
76 func ClientFinalizer(f ClientFinalizerFunc) ClientOption {
77         return func(s *Client) { s.finalizer = f }
78 }
79
80 // BufferedStream sets whether the Response.Body is left open, allowing it
81 // to be read from later. Useful for transporting a file as a buffered stream.
82 func BufferedStream(buffered bool) ClientOption {
83         return func(c *Client) { c.bufferedStream = buffered }
84 }
85
86 // Endpoint returns a usable endpoint that invokes the remote endpoint.
87 func (c Client) Endpoint() endpoint.Endpoint {
88         return func(ctx context.Context, request interface{}) (interface{}, error) {
89                 ctx, cancel := context.WithCancel(ctx)
90                 defer cancel()
91
92                 var (
93                         resp *http.Response
94                         err  error
95                 )
96                 if c.finalizer != nil {
97                         defer func() {
98                                 if resp != nil {
99                                         ctx = context.WithValue(ctx, ContextKeyResponseHeaders, resp.Header)
100                                         ctx = context.WithValue(ctx, ContextKeyResponseSize, resp.ContentLength)
101                                 }
102                                 c.finalizer(ctx, err)
103                         }()
104                 }
105
106                 req, err := http.NewRequest(c.method, c.tgt.String(), nil)
107                 if err != nil {
108                         return nil, err
109                 }
110
111                 if err = c.enc(ctx, req, request); err != nil {
112                         return nil, err
113                 }
114
115                 for _, f := range c.before {
116                         ctx = f(ctx, req)
117                 }
118
119                 resp, err = c.client.Do(req.WithContext(ctx))
120
121                 if err != nil {
122                         return nil, err
123                 }
124
125                 if !c.bufferedStream {
126                         defer resp.Body.Close()
127                 }
128
129                 for _, f := range c.after {
130                         ctx = f(ctx, resp)
131                 }
132
133                 response, err := c.dec(ctx, resp)
134                 if err != nil {
135                         return nil, err
136                 }
137
138                 return response, nil
139         }
140 }
141
142 // ClientFinalizerFunc can be used to perform work at the end of a client HTTP
143 // request, after the response is returned. The principal
144 // intended use is for error logging. Additional response parameters are
145 // provided in the context under keys with the ContextKeyResponse prefix.
146 // Note: err may be nil. There maybe also no additional response parameters depending on
147 // when an error occurs.
148 type ClientFinalizerFunc func(ctx context.Context, err error)
149
150 // EncodeJSONRequest is an EncodeRequestFunc that serializes the request as a
151 // JSON object to the Request body. Many JSON-over-HTTP services can use it as
152 // a sensible default. If the request implements Headerer, the provided headers
153 // will be applied to the request.
154 func EncodeJSONRequest(c context.Context, r *http.Request, request interface{}) error {
155         r.Header.Set("Content-Type", "application/json; charset=utf-8")
156         if headerer, ok := request.(Headerer); ok {
157                 for k := range headerer.Headers() {
158                         r.Header.Set(k, headerer.Headers().Get(k))
159                 }
160         }
161         var b bytes.Buffer
162         r.Body = ioutil.NopCloser(&b)
163         return json.NewEncoder(&b).Encode(request)
164 }
165
166 // EncodeXMLRequest is an EncodeRequestFunc that serializes the request as a
167 // XML object to the Request body. If the request implements Headerer,
168 // the provided headers will be applied to the request.
169 func EncodeXMLRequest(c context.Context, r *http.Request, request interface{}) error {
170         r.Header.Set("Content-Type", "text/xml; charset=utf-8")
171         if headerer, ok := request.(Headerer); ok {
172                 for k := range headerer.Headers() {
173                         r.Header.Set(k, headerer.Headers().Get(k))
174                 }
175         }
176         var b bytes.Buffer
177         r.Body = ioutil.NopCloser(&b)
178         return xml.NewEncoder(&b).Encode(request)
179 }