1
2
3
4
5 package resty
6
7 import (
8 "bytes"
9 "fmt"
10 "io"
11 "log"
12 "mime/multipart"
13 "net/http"
14 "net/textproto"
15 "os"
16 "path/filepath"
17 "reflect"
18 "runtime"
19 "sort"
20 "strings"
21 "sync"
22 )
23
24
25
26
27
28
29
30 type Logger interface {
31 Errorf(format string, v ...interface{})
32 Warnf(format string, v ...interface{})
33 Debugf(format string, v ...interface{})
34 }
35
36 func createLogger() *logger {
37 l := &logger{l: log.New(os.Stderr, "", log.Ldate|log.Lmicroseconds)}
38 return l
39 }
40
41 var _ Logger = (*logger)(nil)
42
43 type logger struct {
44 l *log.Logger
45 }
46
47 func (l *logger) Errorf(format string, v ...interface{}) {
48 l.output("ERROR RESTY "+format, v...)
49 }
50
51 func (l *logger) Warnf(format string, v ...interface{}) {
52 l.output("WARN RESTY "+format, v...)
53 }
54
55 func (l *logger) Debugf(format string, v ...interface{}) {
56 l.output("DEBUG RESTY "+format, v...)
57 }
58
59 func (l *logger) output(format string, v ...interface{}) {
60 if len(v) == 0 {
61 l.l.Print(format)
62 return
63 }
64 l.l.Printf(format, v...)
65 }
66
67
68
69
70
71
72 func IsStringEmpty(str string) bool {
73 return len(strings.TrimSpace(str)) == 0
74 }
75
76
77 func DetectContentType(body interface{}) string {
78 contentType := plainTextType
79 kind := kindOf(body)
80 switch kind {
81 case reflect.Struct, reflect.Map:
82 contentType = jsonContentType
83 case reflect.String:
84 contentType = plainTextType
85 default:
86 if b, ok := body.([]byte); ok {
87 contentType = http.DetectContentType(b)
88 } else if kind == reflect.Slice {
89 contentType = jsonContentType
90 }
91 }
92
93 return contentType
94 }
95
96
97 func IsJSONType(ct string) bool {
98 return jsonCheck.MatchString(ct)
99 }
100
101
102 func IsXMLType(ct string) bool {
103 return xmlCheck.MatchString(ct)
104 }
105
106
107 func Unmarshalc(c *Client, ct string, b []byte, d interface{}) (err error) {
108 if IsJSONType(ct) {
109 err = c.JSONUnmarshal(b, d)
110 } else if IsXMLType(ct) {
111 err = c.XMLUnmarshal(b, d)
112 }
113
114 return
115 }
116
117
118
119
120
121
122
123
124 type RequestLog struct {
125 Header http.Header
126 Body string
127 }
128
129
130
131
132 type ResponseLog struct {
133 Header http.Header
134 Body string
135 }
136
137
138
139
140
141
142 func jsonMarshal(c *Client, r *Request, d interface{}) (*bytes.Buffer, error) {
143 if !r.jsonEscapeHTML || !c.jsonEscapeHTML {
144 return noescapeJSONMarshal(d)
145 }
146
147 data, err := c.JSONMarshal(d)
148 if err != nil {
149 return nil, err
150 }
151
152 buf := acquireBuffer()
153 _, _ = buf.Write(data)
154 return buf, nil
155 }
156
157 func firstNonEmpty(v ...string) string {
158 for _, s := range v {
159 if !IsStringEmpty(s) {
160 return s
161 }
162 }
163 return ""
164 }
165
166 var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")
167
168 func escapeQuotes(s string) string {
169 return quoteEscaper.Replace(s)
170 }
171
172 func createMultipartHeader(param, fileName, contentType string) textproto.MIMEHeader {
173 hdr := make(textproto.MIMEHeader)
174
175 var contentDispositionValue string
176 if IsStringEmpty(fileName) {
177 contentDispositionValue = fmt.Sprintf(`form-data; name="%s"`, param)
178 } else {
179 contentDispositionValue = fmt.Sprintf(`form-data; name="%s"; filename="%s"`,
180 param, escapeQuotes(fileName))
181 }
182 hdr.Set("Content-Disposition", contentDispositionValue)
183
184 if !IsStringEmpty(contentType) {
185 hdr.Set(hdrContentTypeKey, contentType)
186 }
187 return hdr
188 }
189
190 func addMultipartFormField(w *multipart.Writer, mf *MultipartField) error {
191 partWriter, err := w.CreatePart(createMultipartHeader(mf.Param, mf.FileName, mf.ContentType))
192 if err != nil {
193 return err
194 }
195
196 _, err = io.Copy(partWriter, mf.Reader)
197 return err
198 }
199
200 func writeMultipartFormFile(w *multipart.Writer, fieldName, fileName string, r io.Reader) error {
201
202 cbuf := make([]byte, 512)
203 size, err := r.Read(cbuf)
204 if err != nil && err != io.EOF {
205 return err
206 }
207
208 partWriter, err := w.CreatePart(createMultipartHeader(fieldName, fileName, http.DetectContentType(cbuf)))
209 if err != nil {
210 return err
211 }
212
213 if _, err = partWriter.Write(cbuf[:size]); err != nil {
214 return err
215 }
216
217 _, err = io.Copy(partWriter, r)
218 return err
219 }
220
221 func addFile(w *multipart.Writer, fieldName, path string) error {
222 file, err := os.Open(path)
223 if err != nil {
224 return err
225 }
226 defer closeq(file)
227 return writeMultipartFormFile(w, fieldName, filepath.Base(path), file)
228 }
229
230 func addFileReader(w *multipart.Writer, f *File) error {
231 return writeMultipartFormFile(w, f.ParamName, f.Name, f.Reader)
232 }
233
234 func getPointer(v interface{}) interface{} {
235 vv := valueOf(v)
236 if vv.Kind() == reflect.Ptr {
237 return v
238 }
239 return reflect.New(vv.Type()).Interface()
240 }
241
242 func isPayloadSupported(m string, allowMethodGet bool) bool {
243 return !(m == MethodHead || m == MethodOptions || (m == MethodGet && !allowMethodGet))
244 }
245
246 func typeOf(i interface{}) reflect.Type {
247 return indirect(valueOf(i)).Type()
248 }
249
250 func valueOf(i interface{}) reflect.Value {
251 return reflect.ValueOf(i)
252 }
253
254 func indirect(v reflect.Value) reflect.Value {
255 return reflect.Indirect(v)
256 }
257
258 func kindOf(v interface{}) reflect.Kind {
259 return typeOf(v).Kind()
260 }
261
262 func createDirectory(dir string) (err error) {
263 if _, err = os.Stat(dir); err != nil {
264 if os.IsNotExist(err) {
265 if err = os.MkdirAll(dir, 0755); err != nil {
266 return
267 }
268 }
269 }
270 return
271 }
272
273 func canJSONMarshal(contentType string, kind reflect.Kind) bool {
274 return IsJSONType(contentType) && (kind == reflect.Struct || kind == reflect.Map || kind == reflect.Slice)
275 }
276
277 func functionName(i interface{}) string {
278 return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
279 }
280
281 func acquireBuffer() *bytes.Buffer {
282 return bufPool.Get().(*bytes.Buffer)
283 }
284
285 func releaseBuffer(buf *bytes.Buffer) {
286 if buf != nil {
287 buf.Reset()
288 bufPool.Put(buf)
289 }
290 }
291
292
293
294 type requestBodyReleaser struct {
295 releaseOnce sync.Once
296 reqBuf *bytes.Buffer
297 io.ReadCloser
298 }
299
300 func newRequestBodyReleaser(respBody io.ReadCloser, reqBuf *bytes.Buffer) io.ReadCloser {
301 if reqBuf == nil {
302 return respBody
303 }
304
305 return &requestBodyReleaser{
306 reqBuf: reqBuf,
307 ReadCloser: respBody,
308 }
309 }
310
311 func (rr *requestBodyReleaser) Close() error {
312 err := rr.ReadCloser.Close()
313 rr.releaseOnce.Do(func() {
314 releaseBuffer(rr.reqBuf)
315 })
316
317 return err
318 }
319
320 func closeq(v interface{}) {
321 if c, ok := v.(io.Closer); ok {
322 silently(c.Close())
323 }
324 }
325
326 func silently(_ ...interface{}) {}
327
328 func composeHeaders(c *Client, r *Request, hdrs http.Header) string {
329 str := make([]string, 0, len(hdrs))
330 for _, k := range sortHeaderKeys(hdrs) {
331 var v string
332 if k == "Cookie" {
333 cv := strings.TrimSpace(strings.Join(hdrs[k], ", "))
334 if c.GetClient().Jar != nil {
335 for _, c := range c.GetClient().Jar.Cookies(r.RawRequest.URL) {
336 if cv != "" {
337 cv = cv + "; " + c.String()
338 } else {
339 cv = c.String()
340 }
341 }
342 }
343 v = strings.TrimSpace(fmt.Sprintf("%25s: %s", k, cv))
344 } else {
345 v = strings.TrimSpace(fmt.Sprintf("%25s: %s", k, strings.Join(hdrs[k], ", ")))
346 }
347 if v != "" {
348 str = append(str, "\t"+v)
349 }
350 }
351 return strings.Join(str, "\n")
352 }
353
354 func sortHeaderKeys(hdrs http.Header) []string {
355 keys := make([]string, 0, len(hdrs))
356 for key := range hdrs {
357 keys = append(keys, key)
358 }
359 sort.Strings(keys)
360 return keys
361 }
362
363 func copyHeaders(hdrs http.Header) http.Header {
364 nh := http.Header{}
365 for k, v := range hdrs {
366 nh[k] = v
367 }
368 return nh
369 }
370
371 type noRetryErr struct {
372 err error
373 }
374
375 func (e *noRetryErr) Error() string {
376 return e.err.Error()
377 }
378
379 func wrapNoRetryErr(err error) error {
380 if err != nil {
381 err = &noRetryErr{err: err}
382 }
383 return err
384 }
385
386 func unwrapNoRetryErr(err error) error {
387 if e, ok := err.(*noRetryErr); ok {
388 err = e.err
389 }
390 return err
391 }
392
View as plain text