package resty
import (
"bytes"
"errors"
"fmt"
"io"
"log"
"mime/multipart"
"net/http"
"net/textproto"
"os"
"path/filepath"
"reflect"
"runtime"
"sort"
"strings"
"sync"
)
type Logger interface {
Errorf (format string , v ...interface {})
Warnf (format string , v ...interface {})
Debugf (format string , v ...interface {})
}
func createLogger() *logger {
l := &logger {l : log .New (os .Stderr , "" , log .Ldate |log .Lmicroseconds )}
return l
}
var _ Logger = (*logger )(nil )
type logger struct {
l *log .Logger
}
func (l *logger ) Errorf (format string , v ...interface {}) {
l .output ("ERROR RESTY " +format , v ...)
}
func (l *logger ) Warnf (format string , v ...interface {}) {
l .output ("WARN RESTY " +format , v ...)
}
func (l *logger ) Debugf (format string , v ...interface {}) {
l .output ("DEBUG RESTY " +format , v ...)
}
func (l *logger ) output (format string , v ...interface {}) {
if len (v ) == 0 {
l .l .Print (format )
return
}
l .l .Printf (format , v ...)
}
type RateLimiter interface {
Allow () bool
}
var ErrRateLimitExceeded = errors .New ("rate limit exceeded" )
func IsStringEmpty (str string ) bool {
return len (strings .TrimSpace (str )) == 0
}
func DetectContentType (body interface {}) string {
contentType := plainTextType
kind := kindOf (body )
switch kind {
case reflect .Struct , reflect .Map :
contentType = jsonContentType
case reflect .String :
contentType = plainTextType
default :
if b , ok := body .([]byte ); ok {
contentType = http .DetectContentType (b )
} else if kind == reflect .Slice {
contentType = jsonContentType
}
}
return contentType
}
func IsJSONType (ct string ) bool {
return jsonCheck .MatchString (ct )
}
func IsXMLType (ct string ) bool {
return xmlCheck .MatchString (ct )
}
func Unmarshalc (c *Client , ct string , b []byte , d interface {}) (err error ) {
if IsJSONType (ct ) {
err = c .JSONUnmarshal (b , d )
} else if IsXMLType (ct ) {
err = c .XMLUnmarshal (b , d )
}
return
}
type RequestLog struct {
Header http .Header
Body string
}
type ResponseLog struct {
Header http .Header
Body string
}
func jsonMarshal(c *Client , r *Request , d interface {}) (*bytes .Buffer , error ) {
if !r .jsonEscapeHTML || !c .jsonEscapeHTML {
return noescapeJSONMarshal (d )
}
data , err := c .JSONMarshal (d )
if err != nil {
return nil , err
}
buf := acquireBuffer ()
_, _ = buf .Write (data )
return buf , nil
}
func firstNonEmpty(v ...string ) string {
for _ , s := range v {
if !IsStringEmpty (s ) {
return s
}
}
return ""
}
var quoteEscaper = strings .NewReplacer ("\\" , "\\\\" , `"` , "\\\"" )
func escapeQuotes(s string ) string {
return quoteEscaper .Replace (s )
}
func createMultipartHeader(param , fileName , contentType string ) textproto .MIMEHeader {
hdr := make (textproto .MIMEHeader )
var contentDispositionValue string
if IsStringEmpty (fileName ) {
contentDispositionValue = fmt .Sprintf (`form-data; name="%s"` , param )
} else {
contentDispositionValue = fmt .Sprintf (`form-data; name="%s"; filename="%s"` ,
param , escapeQuotes (fileName ))
}
hdr .Set ("Content-Disposition" , contentDispositionValue )
if !IsStringEmpty (contentType ) {
hdr .Set (hdrContentTypeKey , contentType )
}
return hdr
}
func addMultipartFormField(w *multipart .Writer , mf *MultipartField ) error {
partWriter , err := w .CreatePart (createMultipartHeader (mf .Param , mf .FileName , mf .ContentType ))
if err != nil {
return err
}
_, err = io .Copy (partWriter , mf .Reader )
return err
}
func writeMultipartFormFile(w *multipart .Writer , fieldName , fileName string , r io .Reader ) error {
cbuf := make ([]byte , 512 )
size , err := r .Read (cbuf )
if err != nil && err != io .EOF {
return err
}
partWriter , err := w .CreatePart (createMultipartHeader (fieldName , fileName , http .DetectContentType (cbuf )))
if err != nil {
return err
}
if _, err = partWriter .Write (cbuf [:size ]); err != nil {
return err
}
_, err = io .Copy (partWriter , r )
return err
}
func addFile(w *multipart .Writer , fieldName , path string ) error {
file , err := os .Open (path )
if err != nil {
return err
}
defer closeq (file )
return writeMultipartFormFile (w , fieldName , filepath .Base (path ), file )
}
func addFileReader(w *multipart .Writer , f *File ) error {
return writeMultipartFormFile (w , f .ParamName , f .Name , f .Reader )
}
func getPointer(v interface {}) interface {} {
vv := valueOf (v )
if vv .Kind () == reflect .Ptr {
return v
}
return reflect .New (vv .Type ()).Interface ()
}
func isPayloadSupported(m string , allowMethodGet bool ) bool {
return !(m == MethodHead || m == MethodOptions || (m == MethodGet && !allowMethodGet ))
}
func typeOf(i interface {}) reflect .Type {
return indirect (valueOf (i )).Type ()
}
func valueOf(i interface {}) reflect .Value {
return reflect .ValueOf (i )
}
func indirect(v reflect .Value ) reflect .Value {
return reflect .Indirect (v )
}
func kindOf(v interface {}) reflect .Kind {
return typeOf (v ).Kind ()
}
func createDirectory(dir string ) (err error ) {
if _, err = os .Stat (dir ); err != nil {
if os .IsNotExist (err ) {
if err = os .MkdirAll (dir , 0755 ); err != nil {
return
}
}
}
return
}
func canJSONMarshal(contentType string , kind reflect .Kind ) bool {
return IsJSONType (contentType ) && (kind == reflect .Struct || kind == reflect .Map || kind == reflect .Slice )
}
func functionName(i interface {}) string {
return runtime .FuncForPC (reflect .ValueOf (i ).Pointer ()).Name ()
}
func acquireBuffer() *bytes .Buffer {
return bufPool .Get ().(*bytes .Buffer )
}
func releaseBuffer(buf *bytes .Buffer ) {
if buf != nil {
buf .Reset ()
bufPool .Put (buf )
}
}
type requestBodyReleaser struct {
releaseOnce sync .Once
reqBuf *bytes .Buffer
io .ReadCloser
}
func newRequestBodyReleaser(respBody io .ReadCloser , reqBuf *bytes .Buffer ) io .ReadCloser {
if reqBuf == nil {
return respBody
}
return &requestBodyReleaser {
reqBuf : reqBuf ,
ReadCloser : respBody ,
}
}
func (rr *requestBodyReleaser ) Close () error {
err := rr .ReadCloser .Close ()
rr .releaseOnce .Do (func () {
releaseBuffer (rr .reqBuf )
})
return err
}
func closeq(v interface {}) {
if c , ok := v .(io .Closer ); ok {
silently (c .Close ())
}
}
func silently(_ ...interface {}) {}
func composeHeaders(c *Client , r *Request , hdrs http .Header ) string {
str := make ([]string , 0 , len (hdrs ))
for _ , k := range sortHeaderKeys (hdrs ) {
str = append (str , "\t" +strings .TrimSpace (fmt .Sprintf ("%25s: %s" , k , strings .Join (hdrs [k ], ", " ))))
}
return strings .Join (str , "\n" )
}
func sortHeaderKeys(hdrs http .Header ) []string {
keys := make ([]string , 0 , len (hdrs ))
for key := range hdrs {
keys = append (keys , key )
}
sort .Strings (keys )
return keys
}
func copyHeaders(hdrs http .Header ) http .Header {
nh := http .Header {}
for k , v := range hdrs {
nh [k ] = v
}
return nh
}
type noRetryErr struct {
err error
}
func (e *noRetryErr ) Error () string {
return e .err .Error()
}
func wrapNoRetryErr(err error ) error {
if err != nil {
err = &noRetryErr {err : err }
}
return err
}
func unwrapNoRetryErr(err error ) error {
if e , ok := err .(*noRetryErr ); ok {
err = e .err
}
return err
}
The pages are generated with Golds v0.6.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds .