-
Notifications
You must be signed in to change notification settings - Fork 350
/
decode.go
112 lines (101 loc) · 2.35 KB
/
decode.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
package req
import (
"github.com/imroc/req/v3/internal/charsets"
"io"
"strings"
)
var textContentTypes = []string{"text", "json", "xml", "html", "java"}
var autoDecodeText = autoDecodeContentTypeFunc(textContentTypes...)
func autoDecodeContentTypeFunc(contentTypes ...string) func(contentType string) bool {
return func(contentType string) bool {
for _, ct := range contentTypes {
if strings.Contains(contentType, ct) {
return true
}
}
return false
}
}
type decodeReaderCloser struct {
io.ReadCloser
decodeReader io.Reader
}
func (d *decodeReaderCloser) Read(p []byte) (n int, err error) {
return d.decodeReader.Read(p)
}
func newAutoDecodeReadCloser(input io.ReadCloser, t *Transport) *autoDecodeReadCloser {
return &autoDecodeReadCloser{ReadCloser: input, t: t}
}
type autoDecodeReadCloser struct {
io.ReadCloser
t *Transport
decodeReader io.Reader
detected bool
peek []byte
}
func (a *autoDecodeReadCloser) peekRead(p []byte) (n int, err error) {
n, err = a.ReadCloser.Read(p)
if n == 0 || (err != nil && err != io.EOF) {
return
}
a.detected = true
enc, name := charsets.FindEncoding(p)
if enc == nil {
return
}
if a.t.Debugf != nil {
a.t.Debugf("charset %s found in body's meta, auto-decode to utf-8", name)
}
dc := enc.NewDecoder()
a.decodeReader = dc.Reader(a.ReadCloser)
var pp []byte
pp, err = dc.Bytes(p[:n])
if err != nil {
return
}
if len(pp) > len(p) {
a.peek = make([]byte, len(pp)-len(p))
copy(a.peek, pp[len(p):])
copy(p, pp[:len(p)])
n = len(p)
return
}
copy(p, pp)
n = len(p)
return
}
func (a *autoDecodeReadCloser) peekDrain(p []byte) (n int, err error) {
if len(a.peek) > len(p) {
copy(p, a.peek[:len(p)])
peek := make([]byte, len(a.peek)-len(p))
copy(peek, a.peek[len(p):])
a.peek = peek
n = len(p)
return
}
if len(a.peek) == len(p) {
copy(p, a.peek)
n = len(p)
a.peek = nil
return
}
pp := make([]byte, len(p)-len(a.peek))
nn, err := a.decodeReader.Read(pp)
n = len(a.peek) + nn
copy(p[:len(a.peek)], a.peek)
copy(p[len(a.peek):], pp[:nn])
a.peek = nil
return
}
func (a *autoDecodeReadCloser) Read(p []byte) (n int, err error) {
if !a.detected {
return a.peekRead(p)
}
if a.peek != nil {
return a.peekDrain(p)
}
if a.decodeReader != nil {
return a.decodeReader.Read(p)
}
return a.ReadCloser.Read(p) // can not determine charset, not decode
}