-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathclient.go
More file actions
122 lines (112 loc) · 2.47 KB
/
client.go
File metadata and controls
122 lines (112 loc) · 2.47 KB
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
package graceful
import (
"bytes"
"context"
"errors"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"strings"
"time"
"github.com/eapache/go-resiliency/breaker"
)
type Client struct {
url url.URL
codec Codec
breaker *breaker.Breaker
do func(*http.Request) (*http.Response, error)
}
func NewClient(baseurl string, codec Codec, breaker *breaker.Breaker) (*Client, error) {
if !strings.HasSuffix(baseurl, "/") {
baseurl += "/"
}
url, err := url.Parse(baseurl)
if err != nil {
return nil, err
}
c := &Client{
url: *url,
codec: codec,
breaker: breaker,
do: http.DefaultClient.Do,
}
return c, nil
}
func (c *Client) Call(ctx context.Context, method string, args, reply interface{}) error {
if c.breaker == nil {
return c.call(ctx, method, args, reply)
}
return c.breaker.Run(func() error {
return c.call(ctx, method, args, reply)
})
}
func (c *Client) call(ctx context.Context, method string, args, reply interface{}) error {
var data []byte
var err error
if args != nil {
data, err = c.codec.Marshal(args)
if err != nil {
return err
}
}
// URL
url := c.url
url.Path += method
// http.Request
req := &http.Request{}
req = req.WithContext(ctx)
req.URL = &url
if args != nil {
req.Method = "POST"
req.Body = ioutil.NopCloser(bytes.NewReader(data))
req.ContentLength = int64(len(data))
// req.Header.Set("Content-Type", c.codec.MIME())
} else {
req.Method = "GET"
}
// start request
return c.request(reply, req)
}
func (c *Client) request(reply interface{}, req *http.Request) error {
// c.timeoutToHeader(req)
resp, err := c.do(req)
if err != nil {
return err
}
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
if err = resp.Body.Close(); err != nil {
return err
}
if resp.StatusCode != http.StatusOK {
for len(data) != 0 && data[len(data)-1] == '\n' {
data = data[:len(data)-1]
}
err = errors.New(string(data))
return err
}
err = c.codec.Unmarshal(data, reply)
if err != nil {
return err
}
return nil
}
func (c *Client) SetRequestProcessor(p func(*http.Request) (*http.Response, error)) {
c.do = p
}
// timeoutToHeader write context timeout to http-header
func (c *Client) timeoutToHeader(req *http.Request) {
ctx := req.Context()
deadline, ok := ctx.Deadline()
if ok {
timeout := -1 * time.Since(deadline)
str := strconv.FormatInt(int64(timeout), 16)
if req.Header == nil {
req.Header = make(http.Header, 1)
}
req.Header.Add(contextTimeoutHeader, str)
}
}