-
Notifications
You must be signed in to change notification settings - Fork 45
/
response.go
143 lines (116 loc) · 2.82 KB
/
response.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
package rest
import (
"fmt"
"io"
"net/http"
"strings"
"sync"
"github.com/infraboard/mcube/v2/client/compressor"
"github.com/infraboard/mcube/v2/client/negotiator"
"github.com/infraboard/mcube/v2/ioc/config/log"
"github.com/rs/zerolog"
)
func NewResponse(c *RESTClient) *Response {
return &Response{
log: log.Sub("http.response"),
}
}
// Response contains the result of calling Request.Do().
type Response struct {
body io.ReadCloser
headers http.Header
statusCode int
err error
bf []byte
contentType string
isRead bool
log *zerolog.Logger
lock sync.Mutex
}
func (r *Response) withBody(body io.ReadCloser) {
r.body = body
}
func (r *Response) withHeader(headers http.Header) {
r.headers = headers
}
func (r *Response) withStatusCode(code int) {
r.statusCode = code
}
func (r *Response) readBody() {
r.lock.Lock()
defer r.lock.Unlock()
if r.body == nil || r.isRead {
return
}
r.isRead = true
defer r.body.Close()
var bodyReader io.Reader = r.body
// 解压缩
et := HeaderFilterFlags(r.headers.Get(CONTENT_ENCODING_HEADER))
if et != "" {
cp := compressor.GetCompressor(et)
dp, err := cp.Decompress(r.body)
if err != nil {
r.err = err
return
}
bodyReader = dp
}
// 读取数据
body, err := io.ReadAll(bodyReader)
if err != nil {
r.err = err
return
}
r.debug(body)
r.bf = body
}
func (r *Response) debug(body []byte) {
r.log.Debug().Msgf("Status Code: %d", r.statusCode)
r.log.Debug().Msgf("Response Headers:")
for k, v := range r.headers {
r.log.Debug().Msgf("%s=%s", k, strings.Join(v, ","))
}
r.log.Debug().Msgf("Body: %s", string(body))
}
func (r *Response) Header(header string, v *string) *Response {
*v = r.headers.Get(header)
return r
}
// 请求正常的情况下, 获取返回的数据, 不做解析
func (r *Response) Raw() ([]byte, error) {
if err := r.Error(); err != nil {
return nil, err
}
return r.bf, r.err
}
// 直接返回stream, 常用于websocket
func (r *Response) Stream() (io.ReadCloser, error) {
return r.body, r.err
}
// 默认不设置通过Content-Type获取, 如果设定, 以设定为准
func (r *Response) ContentType(m negotiator.MIME) *Response {
r.contentType = string(m)
return r
}
// 请求正常的情况下, 获取返回的数据, 会根据Content-Type做解析
func (r *Response) Into(v any) error {
if err := r.Error(); err != nil {
return err
}
// 解析数据
if r.contentType == "" {
r.contentType = HeaderFilterFlags(r.headers.Get(CONTENT_TYPE_HEADER))
}
nt := negotiator.GetNegotiator(r.contentType)
return nt.Decode(r.bf, v)
}
// 不处理返回, 直接判断请求是否正常
func (r *Response) Error() error {
r.readBody()
// 判断status code
if r.statusCode/100 != 2 {
r.err = fmt.Errorf("status code is %d, not 2xx, response: %s", r.statusCode, string(r.bf))
}
return r.err
}