-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathresponse.go
138 lines (121 loc) · 2.99 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
package gohttp
import (
"bytes"
"compress/flate"
"compress/gzip"
"encoding/json"
"errors"
"io"
"mime"
"net/http"
"os"
"golang.org/x/net/html/charset"
)
var _ io.ReadCloser = &Response{}
// Response represents the response from an HTTP request.
type Response struct {
resp *http.Response
body io.Reader
// StatusCode represents the response status code.
StatusCode int
// Header maps header keys to values.
Header http.Header
// ContentLength records the length of the associated content.
ContentLength int64
buf *bytes.Buffer
cached bool
}
func buildResponse(resp *http.Response) (*Response, error) {
var reader io.Reader = resp.Body
switch resp.Header.Get("Content-Encoding") {
case "gzip":
var err error
reader, err = gzip.NewReader(reader)
if err != nil {
resp.Body.Close()
return nil, err
}
case "deflate":
reader = flate.NewReader(reader)
}
contentType := resp.Header.Get("Content-Type")
mediatype, params, _ := mime.ParseMediaType(contentType)
if _, ok := params["charset"]; mediatype == "text/html" || ok {
r, err := charset.NewReader(reader, contentType)
switch err {
case nil:
reader = r
case io.EOF:
default:
resp.Body.Close()
return nil, err
}
}
buf := new(bytes.Buffer)
reader = io.TeeReader(reader, buf)
return &Response{
resp: resp,
body: reader,
StatusCode: resp.StatusCode,
Header: resp.Header,
ContentLength: resp.ContentLength,
buf: buf,
}, nil
}
// Read reads the response body.
func (r *Response) Read(p []byte) (int, error) {
if r.cached {
return 0, errors.New("the entire response body has already been read")
}
n, err := r.body.Read(p)
if err == io.EOF {
r.cached = true
r.Close()
}
return n, err
}
// Close closes the response body.
func (r *Response) Close() error {
return r.resp.Body.Close()
}
// Raw returns origin *http.Response.
func (r *Response) Raw() *http.Response {
return r.resp
}
// Request is the request that was sent to obtain this Response.
func (r *Response) Request() *http.Request {
return r.resp.Request
}
// Cookies parses and returns the cookies set in the Set-Cookie headers.
func (r *Response) Cookies() []*http.Cookie {
return r.resp.Cookies()
}
// Bytes returns a slice of byte of the response body.
func (r *Response) Bytes() []byte {
if r.cached {
return r.buf.Bytes()
}
if _, err := io.ReadAll(r); err != nil {
return nil
}
return r.buf.Bytes()
}
// String returns the contents of the response body as a string.
func (r *Response) String() string {
return string(r.Bytes())
}
// JSON parses the response body as JSON-encoded data
// and stores the result in the value pointed to by data.
func (r *Response) JSON(data any) error {
return json.Unmarshal(r.Bytes(), data)
}
// Save saves the response data to file. It returns the number
// of bytes written and an error, if any.
func (r *Response) Save(file string) (int, error) {
f, err := os.Create(file)
if err != nil {
return 0, err
}
defer f.Close()
return f.Write(r.Bytes())
}