forked from gofiber/fiber
-
Notifications
You must be signed in to change notification settings - Fork 1
/
response.go
210 lines (177 loc) · 5.49 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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
package client
import (
"bytes"
"errors"
"fmt"
"io"
"io/fs"
"iter"
"os"
"path/filepath"
"sync"
"github.com/gofiber/utils/v2"
"github.com/valyala/fasthttp"
)
// Response represents the result of a request. It provides access to the response data.
type Response struct {
client *Client
request *Request
RawResponse *fasthttp.Response
cookie []*fasthttp.Cookie
}
// setClient sets the client instance in the response. The client object is used by core functionalities.
func (r *Response) setClient(c *Client) {
r.client = c
}
// setRequest sets the request object in the response. The request is released when Response.Close is called.
func (r *Response) setRequest(req *Request) {
r.request = req
}
// Status returns the HTTP status message of the executed request.
func (r *Response) Status() string {
return string(r.RawResponse.Header.StatusMessage())
}
// StatusCode returns the HTTP status code of the executed request.
func (r *Response) StatusCode() int {
return r.RawResponse.StatusCode()
}
// Protocol returns the HTTP protocol used for the request.
func (r *Response) Protocol() string {
return string(r.RawResponse.Header.Protocol())
}
// Header returns the value of the specified response header field.
func (r *Response) Header(key string) string {
return utils.UnsafeString(r.RawResponse.Header.Peek(key))
}
// Headers returns all headers in the response using an iterator.
// Use maps.Collect() to gather them into a map if needed.
//
// The returned values are valid only until the response object is released.
// Do not store references to returned values; make copies instead.
func (r *Response) Headers() iter.Seq2[string, []string] {
return func(yield func(string, []string) bool) {
keys := r.RawResponse.Header.PeekKeys()
for _, key := range keys {
vals := r.RawResponse.Header.PeekAll(utils.UnsafeString(key))
valsStr := make([]string, len(vals))
for i, v := range vals {
valsStr[i] = utils.UnsafeString(v)
}
if !yield(utils.UnsafeString(key), valsStr) {
return
}
}
}
}
// Cookies returns all cookies set by the response.
//
// The returned values are valid only until the response object is released.
// Do not store references to returned values; make copies instead.
func (r *Response) Cookies() []*fasthttp.Cookie {
return r.cookie
}
// Body returns the HTTP response body as a byte slice.
func (r *Response) Body() []byte {
return r.RawResponse.Body()
}
// String returns the response body as a trimmed string.
func (r *Response) String() string {
return utils.Trim(string(r.Body()), ' ')
}
// JSON unmarshals the response body into the given interface{} using JSON.
func (r *Response) JSON(v any) error {
return r.client.jsonUnmarshal(r.Body(), v)
}
// CBOR unmarshals the response body into the given interface{} using CBOR.
func (r *Response) CBOR(v any) error {
return r.client.cborUnmarshal(r.Body(), v)
}
// XML unmarshals the response body into the given interface{} using XML.
func (r *Response) XML(v any) error {
return r.client.xmlUnmarshal(r.Body(), v)
}
// Save writes the response body to a file or io.Writer.
// If a string path is provided, it creates directories if needed, then writes to a file.
// If an io.Writer is provided, it writes directly to it.
func (r *Response) Save(v any) error {
switch p := v.(type) {
case string:
file := filepath.Clean(p)
dir := filepath.Dir(file)
// Create directory if it doesn't exist
if _, err := os.Stat(dir); err != nil {
if !errors.Is(err, fs.ErrNotExist) {
return fmt.Errorf("failed to check directory: %w", err)
}
if err = os.MkdirAll(dir, 0o750); err != nil {
return fmt.Errorf("failed to create directory: %w", err)
}
}
// Create and write to file
outFile, err := os.Create(file)
if err != nil {
return fmt.Errorf("failed to create file: %w", err)
}
defer func() { _ = outFile.Close() }() //nolint:errcheck // not needed
if _, err = io.Copy(outFile, bytes.NewReader(r.Body())); err != nil {
return fmt.Errorf("failed to write response body to file: %w", err)
}
return nil
case io.Writer:
if _, err := io.Copy(p, bytes.NewReader(r.Body())); err != nil {
return fmt.Errorf("failed to write response body to io.Writer: %w", err)
}
defer func() {
if pc, ok := p.(io.WriteCloser); ok {
_ = pc.Close() //nolint:errcheck // not needed
}
}()
return nil
default:
return ErrNotSupportSaveMethod
}
}
// Reset clears the Response object, making it ready for reuse.
func (r *Response) Reset() {
r.client = nil
r.request = nil
for len(r.cookie) != 0 {
t := r.cookie[0]
r.cookie = r.cookie[1:]
fasthttp.ReleaseCookie(t)
}
r.RawResponse.Reset()
}
// Close releases both the Request and Response objects back to their pools.
// After calling Close, do not use these objects.
func (r *Response) Close() {
if r.request != nil {
tmp := r.request
r.request = nil
ReleaseRequest(tmp)
}
ReleaseResponse(r)
}
var responsePool = &sync.Pool{
New: func() any {
return &Response{
cookie: []*fasthttp.Cookie{},
RawResponse: fasthttp.AcquireResponse(),
}
},
}
// AcquireResponse returns a new (pooled) Response object.
// When done, release it with ReleaseResponse to reduce GC load.
func AcquireResponse() *Response {
resp, ok := responsePool.Get().(*Response)
if !ok {
panic("unexpected type from responsePool.Get()")
}
return resp
}
// ReleaseResponse returns the Response object to the pool.
// Do not use the released Response afterward to avoid data races.
func ReleaseResponse(resp *Response) {
resp.Reset()
responsePool.Put(resp)
}