-
Notifications
You must be signed in to change notification settings - Fork 1
/
response.go
91 lines (78 loc) · 2.07 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
// Copyright (c) 2024 0x9ef. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
package clientx
import (
"bytes"
"compress/flate"
"compress/gzip"
"io"
"net/http"
)
// Empty is an empty payload for request/response decoding.
type Empty struct{}
func responseReader(resp *http.Response) (io.ReadCloser, []byte, error) {
// Duplicate response body to two readers,
// the r1 we use to replace resp.Body, and r2 to build flate/gzip readers
r1, r2, b, err := drainBody(resp.Body)
if err != nil {
return nil, nil, err
}
var reader io.ReadCloser
switch resp.Header.Get("Content-Encoding") {
case "deflate":
reader = flate.NewReader(r2)
case "gzip":
reader, err = gzip.NewReader(r2)
default:
reader = r2
}
resp.Body = r1
return reader, b, err
}
func drainBody(r io.ReadCloser) (r1, r2 io.ReadCloser, b []byte, err error) {
if r == nil || r == http.NoBody {
// No copying needed. Preserve the magic sentinel meaning of NoBody.
return http.NoBody, http.NoBody, nil, nil
}
var buf bytes.Buffer
if _, err = buf.ReadFrom(r); err != nil {
return nil, r, nil, err
}
if err = r.Close(); err != nil {
return nil, r, nil, err
}
return io.NopCloser(&buf), io.NopCloser(bytes.NewReader(buf.Bytes())), buf.Bytes(), nil
}
func decodeResponse[T any](enc EncoderDecoder, r io.ReadCloser, dst T) error {
return enc.Decode(r, dst)
}
type reusableReader struct {
io.Reader
readBuf *bytes.Buffer
backBuf *bytes.Buffer
}
// https://blog.flexicondev.com/read-go-http-request-body-multiple-times
func ReusableReader(r io.Reader) io.ReadCloser {
readBuf := bytes.Buffer{}
readBuf.ReadFrom(r) // error handling ignored for brevity
backBuf := bytes.Buffer{}
return reusableReader{
io.TeeReader(&readBuf, &backBuf),
&readBuf,
&backBuf,
}
}
func (r reusableReader) Read(p []byte) (int, error) {
n, err := r.Reader.Read(p)
if err == io.EOF {
r.reset()
}
return n, err
}
func (r reusableReader) Close() error {
return nil
}
func (r reusableReader) reset() {
io.Copy(r.readBuf, r.backBuf) // nolint: errcheck
}