592 lines
14 KiB
Go
592 lines
14 KiB
Go
|
package plugin
|
|||
|
|
|||
|
import (
|
|||
|
"bufio"
|
|||
|
"bytes"
|
|||
|
"fmt"
|
|||
|
"io"
|
|||
|
"mime"
|
|||
|
"mime/multipart"
|
|||
|
"net/http"
|
|||
|
"net/textproto"
|
|||
|
"net/url"
|
|||
|
"os"
|
|||
|
"path/filepath"
|
|||
|
"strings"
|
|||
|
"sync"
|
|||
|
"time"
|
|||
|
|
|||
|
xml2json "github.com/basgys/goxml2json"
|
|||
|
"github.com/ssgo/httpclient"
|
|||
|
"github.com/ssgo/u"
|
|||
|
)
|
|||
|
|
|||
|
var Signers = map[string]func(req *Request, cfg *SignerConfig) error{
|
|||
|
// 基础认证
|
|||
|
"basic": makeBasicAuthSign,
|
|||
|
"bearer": makeBearerSign,
|
|||
|
"jwt": makeJWTSign,
|
|||
|
// "tc3": makeTC3Sign,
|
|||
|
// "cos": makeCOSSign,
|
|||
|
// "hmac": makeHmacSign,
|
|||
|
}
|
|||
|
var signersLock = sync.RWMutex{}
|
|||
|
|
|||
|
func GetSigner(name string) func(req *Request, cfg *SignerConfig) error {
|
|||
|
if name == "" {
|
|||
|
return nil
|
|||
|
}
|
|||
|
signersLock.RLock()
|
|||
|
defer signersLock.RUnlock()
|
|||
|
return Signers[name]
|
|||
|
}
|
|||
|
|
|||
|
func RegisterSigner(name string, f func(req *Request, cfg *SignerConfig) error) {
|
|||
|
signersLock.Lock()
|
|||
|
defer signersLock.Unlock()
|
|||
|
Signers[name] = f
|
|||
|
}
|
|||
|
|
|||
|
type SignerConfig struct {
|
|||
|
data map[string]any
|
|||
|
}
|
|||
|
|
|||
|
var signerConfig = map[string]*map[string]any{}
|
|||
|
var confLock = sync.RWMutex{}
|
|||
|
|
|||
|
func GetSignerConfig(signer string) *SignerConfig {
|
|||
|
confLock.RLock()
|
|||
|
cfg := signerConfig[signer]
|
|||
|
confLock.RUnlock()
|
|||
|
if cfg == nil {
|
|||
|
cfg = &map[string]any{}
|
|||
|
}
|
|||
|
return &SignerConfig{data: *cfg}
|
|||
|
}
|
|||
|
|
|||
|
// func (cfg *SignerConfig) Signer() string {
|
|||
|
// return u.String(cfg.data["signer"])
|
|||
|
// }
|
|||
|
|
|||
|
func (cfg *SignerConfig) Get(k string, defaultValue any) any {
|
|||
|
if v, ok := cfg.data[k]; ok {
|
|||
|
return v
|
|||
|
}
|
|||
|
return defaultValue
|
|||
|
}
|
|||
|
|
|||
|
func (cfg *SignerConfig) Set(k string, v any) {
|
|||
|
cfg.data[k] = v
|
|||
|
}
|
|||
|
|
|||
|
func (cfg *SignerConfig) String(k string, defaultValue string) string {
|
|||
|
return u.String(cfg.Get(k, defaultValue))
|
|||
|
}
|
|||
|
|
|||
|
func (cfg *SignerConfig) Int(k string, defaultValue int64) int64 {
|
|||
|
return u.Int64(cfg.Get(k, defaultValue))
|
|||
|
}
|
|||
|
|
|||
|
func (cfg *SignerConfig) Float(k string, defaultValue float64) float64 {
|
|||
|
return u.Float64(cfg.Get(k, defaultValue))
|
|||
|
}
|
|||
|
|
|||
|
func (cfg *SignerConfig) Bool(k string, defaultValue bool) bool {
|
|||
|
return u.Bool(cfg.Get(k, defaultValue))
|
|||
|
}
|
|||
|
|
|||
|
func (cfg *SignerConfig) To(k string, to any) {
|
|||
|
from := cfg.Get(k, nil)
|
|||
|
if from != nil {
|
|||
|
u.Convert(from, to)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func (cfg *SignerConfig) Map(k string) map[string]any {
|
|||
|
m := map[string]any{}
|
|||
|
cfg.To(k, &m)
|
|||
|
return m
|
|||
|
}
|
|||
|
|
|||
|
func getConfigValue(cfg map[string]any, k string) string {
|
|||
|
a := u.SplitWithoutNoneN(k, ".", 2)
|
|||
|
if len(a) > 1 {
|
|||
|
m := map[string]any{}
|
|||
|
u.Convert(cfg[a[0]], &m)
|
|||
|
return u.String(m[a[1]])
|
|||
|
} else if len(a) == 1 {
|
|||
|
return u.String(cfg[a[0]])
|
|||
|
}
|
|||
|
return ""
|
|||
|
}
|
|||
|
|
|||
|
// func (cfg *SignerConfig) Value(k string) string {
|
|||
|
// a := u.SplitWithoutNoneN(k, ".", 2)
|
|||
|
// if len(a) > 1 {
|
|||
|
// m := cfg.Map(a[0])
|
|||
|
// return u.String(m[a[1]])
|
|||
|
// } else if len(a) == 1 {
|
|||
|
// return cfg.String(a[0], "")
|
|||
|
// }
|
|||
|
// return ""
|
|||
|
// }
|
|||
|
|
|||
|
var httpclients = map[uint]*httpclient.ClientPool{}
|
|||
|
var httpclientsLock = sync.RWMutex{}
|
|||
|
|
|||
|
func GetHttpClient(timeout uint) *httpclient.ClientPool {
|
|||
|
httpclientsLock.RLock()
|
|||
|
c := httpclients[timeout]
|
|||
|
httpclientsLock.RUnlock()
|
|||
|
if c != nil {
|
|||
|
return c
|
|||
|
}
|
|||
|
c = httpclient.GetClient(time.Duration(timeout) * time.Millisecond)
|
|||
|
httpclientsLock.Lock()
|
|||
|
httpclients[timeout] = c
|
|||
|
httpclientsLock.Unlock()
|
|||
|
return c
|
|||
|
}
|
|||
|
|
|||
|
var RequestType = struct {
|
|||
|
Json string // JSON请求体(默认)
|
|||
|
Form string // 表单请求体
|
|||
|
Multi string // 多部分请求体
|
|||
|
Text string // 文本请求体
|
|||
|
Binary string // 二进制请求体
|
|||
|
}{
|
|||
|
Json: "json",
|
|||
|
Form: "form",
|
|||
|
Multi: "multi",
|
|||
|
Text: "text",
|
|||
|
Binary: "binary",
|
|||
|
}
|
|||
|
|
|||
|
var ResponseType = struct {
|
|||
|
Json string // JSON响应体(默认)
|
|||
|
Text string // 文本响应体
|
|||
|
Binary string // 二进制响应体
|
|||
|
}{
|
|||
|
Json: "json",
|
|||
|
Text: "text",
|
|||
|
Binary: "binary",
|
|||
|
}
|
|||
|
|
|||
|
type Request struct {
|
|||
|
Url string
|
|||
|
Method string
|
|||
|
Config map[string]string // 配置信息,可以被 {{}} 引用
|
|||
|
Headers map[string]string // 请求头
|
|||
|
Query map[string]string // URL中的参数
|
|||
|
Data map[string]any // 用JSON发送的请求主体对象
|
|||
|
Form map[string]string // 用表单发送的主体内容
|
|||
|
File map[string]any // 用multi-form发送的文件对象
|
|||
|
Text string // 纯文本格式的主体内容
|
|||
|
Binary []byte // 二进制格式的主体内容
|
|||
|
RequestType string
|
|||
|
ResponseType string
|
|||
|
Callback func(data any)
|
|||
|
Timeout uint // 单位毫秒,默认0表示不超时
|
|||
|
FinalUrl string // 提前处理好的完整URL
|
|||
|
FinalHost string // 主机名
|
|||
|
FinalPath string // URL路径
|
|||
|
FinalQuery string // 查询字符串
|
|||
|
FinalBody []byte // 提前处理好的请求主体
|
|||
|
}
|
|||
|
|
|||
|
func (req *Request) SetUrl(v string) {
|
|||
|
req.Url = v
|
|||
|
}
|
|||
|
|
|||
|
func (req *Request) GetHost() string {
|
|||
|
return req.FinalHost
|
|||
|
}
|
|||
|
|
|||
|
func (req *Request) SetMethod(v string) {
|
|||
|
req.Method = v
|
|||
|
}
|
|||
|
|
|||
|
func (req *Request) SetQuery(k string, v string) {
|
|||
|
req.Query[k] = v
|
|||
|
}
|
|||
|
|
|||
|
func (req *Request) SetForm(k string, v string) {
|
|||
|
req.Form[k] = v
|
|||
|
}
|
|||
|
|
|||
|
func (req *Request) SetText(v string) {
|
|||
|
req.Text = v
|
|||
|
}
|
|||
|
|
|||
|
func (req *Request) SetBinary(v []byte) {
|
|||
|
req.Binary = v
|
|||
|
}
|
|||
|
|
|||
|
func (req *Request) SetFile(k string, v any) {
|
|||
|
req.File[k] = v
|
|||
|
}
|
|||
|
|
|||
|
func (req *Request) SetData(k string, v any) {
|
|||
|
req.Data[k] = v
|
|||
|
}
|
|||
|
|
|||
|
func (req *Request) SetHeader(key, value string) {
|
|||
|
req.Headers[key] = value
|
|||
|
}
|
|||
|
|
|||
|
func (req *Request) MakeQuery() {
|
|||
|
if urlInfo, err := url.Parse(req.Url); err == nil {
|
|||
|
q := url.Values{}
|
|||
|
for k, v := range req.Query {
|
|||
|
q.Set(k, v)
|
|||
|
}
|
|||
|
urlInfo.RawQuery = q.Encode()
|
|||
|
req.FinalUrl = urlInfo.String()
|
|||
|
req.FinalHost = urlInfo.Host
|
|||
|
req.FinalPath = urlInfo.Path
|
|||
|
req.FinalQuery = urlInfo.RawQuery
|
|||
|
if req.FinalPath == "" {
|
|||
|
req.FinalPath = "/"
|
|||
|
}
|
|||
|
} else {
|
|||
|
req.FinalUrl = req.Url
|
|||
|
req.FinalHost = ""
|
|||
|
req.FinalPath = "/"
|
|||
|
req.FinalQuery = ""
|
|||
|
}
|
|||
|
// fmt.Println(">>>a", req.Url, req.FinalUrl)
|
|||
|
}
|
|||
|
|
|||
|
func (req *Request) makeConfig(str string) string {
|
|||
|
if strings.Contains(str, "{{config.") {
|
|||
|
if m := configMatcher.FindAllStringSubmatch(str, 100); m != nil {
|
|||
|
for _, m1 := range m {
|
|||
|
if v1, ok := req.Config[m1[1]]; ok && v1 != "" {
|
|||
|
str = strings.ReplaceAll(str, m1[0], v1)
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
if strings.Contains(str, "{{/") {
|
|||
|
if m := fnMatcher.FindAllStringSubmatch(str, 100); m != nil {
|
|||
|
for _, m1 := range m {
|
|||
|
fn := m1[1]
|
|||
|
args := m1[2]
|
|||
|
switch fn {
|
|||
|
case "date":
|
|||
|
str = strings.ReplaceAll(str, m1[0], time.Now().Format(args))
|
|||
|
case "timestamp":
|
|||
|
str = strings.ReplaceAll(str, m1[0], u.String(time.Now().Unix()))
|
|||
|
case "timestampMilli":
|
|||
|
str = strings.ReplaceAll(str, m1[0], u.String(time.Now().UnixMilli()))
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
return str
|
|||
|
}
|
|||
|
|
|||
|
func (req *Request) MakeBody() {
|
|||
|
switch req.RequestType {
|
|||
|
case RequestType.Json:
|
|||
|
data := map[string]any{}
|
|||
|
if req.Data != nil {
|
|||
|
for k, v := range req.Data {
|
|||
|
if str, ok := v.(string); ok {
|
|||
|
data[k] = req.makeConfig(str)
|
|||
|
} else {
|
|||
|
data[k] = v
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
req.FinalBody = u.JsonBytesP(data)
|
|||
|
case RequestType.Form:
|
|||
|
formData := url.Values{}
|
|||
|
for k, v := range req.Form {
|
|||
|
formData.Set(k, req.makeConfig(v))
|
|||
|
}
|
|||
|
req.FinalBody = []byte(formData.Encode())
|
|||
|
case RequestType.Multi:
|
|||
|
body := &bytes.Buffer{}
|
|||
|
writer := multipart.NewWriter(body)
|
|||
|
if len(req.Form) > 0 {
|
|||
|
for k, v := range req.Form {
|
|||
|
writer.WriteField(k, req.makeConfig(v))
|
|||
|
}
|
|||
|
}
|
|||
|
if len(req.File) > 0 {
|
|||
|
for k, v := range req.File {
|
|||
|
if filename, ok := v.(string); ok && u.FileExists(filename) {
|
|||
|
if fp, err := os.Open(filename); err == nil {
|
|||
|
if part, err := writer.CreateFormFile(k, filepath.Base(filename)); err == nil {
|
|||
|
io.Copy(part, fp)
|
|||
|
}
|
|||
|
_ = fp.Close()
|
|||
|
}
|
|||
|
} else if dataUrl, ok := v.(string); ok && strings.HasPrefix(dataUrl, "data:") && strings.ContainsRune(dataUrl, ',') {
|
|||
|
parts := strings.SplitN(dataUrl, ",", 2)
|
|||
|
metaPart := strings.TrimSuffix(parts[0], "data:")
|
|||
|
metaParts := strings.SplitN(metaPart, ";", 2)
|
|||
|
mimeType := "application/octet-stream"
|
|||
|
if metaParts[0] != "" {
|
|||
|
mimeType = metaParts[0]
|
|||
|
}
|
|||
|
var data []byte
|
|||
|
if len(metaParts) > 1 && metaParts[1] == "base64" {
|
|||
|
data = u.UnBase64(parts[1])
|
|||
|
} else {
|
|||
|
data = []byte(parts[1])
|
|||
|
}
|
|||
|
|
|||
|
h := make(textproto.MIMEHeader)
|
|||
|
filename := k
|
|||
|
if exts, err := mime.ExtensionsByType(mimeType); err == nil && len(exts) > 0 {
|
|||
|
filename = k + exts[0]
|
|||
|
}
|
|||
|
|
|||
|
h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="%s"; filename="%s"`, k, filename))
|
|||
|
h.Set("Content-Type", mimeType)
|
|||
|
if part, err := writer.CreatePart(h); err == nil {
|
|||
|
part.Write(data)
|
|||
|
}
|
|||
|
} else {
|
|||
|
h := make(textproto.MIMEHeader)
|
|||
|
buf := u.Bytes(v)
|
|||
|
h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="%s"; filename="%s"`, k, k))
|
|||
|
mt := mime.TypeByExtension(filepath.Ext(k))
|
|||
|
if mt == "" {
|
|||
|
mt = "application/octet-stream"
|
|||
|
}
|
|||
|
h.Set("Content-Type", mt)
|
|||
|
if part, err := writer.CreatePart(h); err == nil {
|
|||
|
part.Write(buf)
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
writer.Close()
|
|||
|
req.FinalBody = body.Bytes()
|
|||
|
case RequestType.Binary:
|
|||
|
req.FinalBody = req.Binary
|
|||
|
default:
|
|||
|
req.FinalBody = []byte(req.makeConfig(req.Text))
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func MakeSign(cfg *SignerConfig, req *Request) error {
|
|||
|
if req.RequestType == "" {
|
|||
|
if req.Data != nil {
|
|||
|
req.RequestType = RequestType.Json
|
|||
|
} else if req.Form != nil {
|
|||
|
req.RequestType = RequestType.Form
|
|||
|
} else if req.File != nil {
|
|||
|
req.RequestType = RequestType.Multi
|
|||
|
} else if req.Binary != nil {
|
|||
|
req.RequestType = RequestType.Binary
|
|||
|
} else {
|
|||
|
req.RequestType = RequestType.Text
|
|||
|
}
|
|||
|
}
|
|||
|
if req.Method == "" {
|
|||
|
req.Method = "POST"
|
|||
|
}
|
|||
|
req.Method = strings.ToUpper(req.Method)
|
|||
|
if req.Headers == nil {
|
|||
|
req.Headers = make(map[string]string)
|
|||
|
}
|
|||
|
if req.Query == nil {
|
|||
|
req.Query = make(map[string]string)
|
|||
|
}
|
|||
|
// 自动将URL中的参数放入req.Query
|
|||
|
if strings.ContainsRune(req.Url, '?') {
|
|||
|
if urlInfo, err := url.Parse(req.Url); err == nil {
|
|||
|
for k, v := range urlInfo.Query() {
|
|||
|
if len(v) > 0 {
|
|||
|
req.Query[k] = v[0]
|
|||
|
}
|
|||
|
}
|
|||
|
urlInfo.RawQuery = ""
|
|||
|
req.Url = urlInfo.String()
|
|||
|
}
|
|||
|
}
|
|||
|
if (req.Method == "POST" || req.Method == "PUT") && req.Headers["Content-Type"] == "" {
|
|||
|
switch req.RequestType {
|
|||
|
case RequestType.Json:
|
|||
|
req.Headers["Content-Type"] = "application/json"
|
|||
|
case RequestType.Form:
|
|||
|
req.Headers["Content-Type"] = "application/x-www-form-urlencoded"
|
|||
|
case RequestType.Multi:
|
|||
|
req.Headers["Content-Type"] = "multipart/form-data"
|
|||
|
case RequestType.Binary:
|
|||
|
req.Headers["Content-Type"] = "application/octet-stream"
|
|||
|
default:
|
|||
|
req.Headers["Content-Type"] = "text/plain"
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if cfg == nil {
|
|||
|
cfg = &SignerConfig{}
|
|||
|
}
|
|||
|
|
|||
|
// 从signer中设置header
|
|||
|
for k, v := range cfg.Map("headers") {
|
|||
|
req.Headers[k] = req.makeConfig(u.String(v))
|
|||
|
// fmt.Println(u.BCyan(k), req.Headers[k])
|
|||
|
}
|
|||
|
|
|||
|
// 从signer中设置query
|
|||
|
for k, v := range cfg.Map("query") {
|
|||
|
req.Query[k] = req.makeConfig(u.String(v))
|
|||
|
}
|
|||
|
|
|||
|
// 从signer中设置data
|
|||
|
for k, v := range cfg.Map("data") {
|
|||
|
req.Data[k] = v
|
|||
|
}
|
|||
|
|
|||
|
// 从signer中设置form
|
|||
|
for k, v := range cfg.Map("form") {
|
|||
|
req.Form[k] = u.String(v)
|
|||
|
}
|
|||
|
|
|||
|
req.MakeQuery()
|
|||
|
req.MakeBody()
|
|||
|
|
|||
|
if signer := GetSigner(u.String(cfg.data["signer"])); signer != nil {
|
|||
|
// fmt.Println(111, req.Method, 111)
|
|||
|
if err := signer(req, cfg); err != nil {
|
|||
|
// fmt.Println(333, err)
|
|||
|
return err
|
|||
|
// } else {
|
|||
|
// fmt.Println(333, 1)
|
|||
|
}
|
|||
|
}
|
|||
|
// 拼接生成完整的URL
|
|||
|
return nil
|
|||
|
}
|
|||
|
|
|||
|
func Get(cfg *SignerConfig, req *Request) (any, *http.Response, error) {
|
|||
|
req.Method = "GET"
|
|||
|
return Do(cfg, req)
|
|||
|
}
|
|||
|
|
|||
|
func Post(cfg *SignerConfig, req *Request) (any, *http.Response, error) {
|
|||
|
req.Method = "POST"
|
|||
|
return Do(cfg, req)
|
|||
|
}
|
|||
|
|
|||
|
func Put(cfg *SignerConfig, req *Request) (any, *http.Response, error) {
|
|||
|
req.Method = "PUT"
|
|||
|
return Do(cfg, req)
|
|||
|
}
|
|||
|
|
|||
|
func Delete(cfg *SignerConfig, req *Request) (any, *http.Response, error) {
|
|||
|
req.Method = "DELETE"
|
|||
|
return Do(cfg, req)
|
|||
|
}
|
|||
|
|
|||
|
func Head(cfg *SignerConfig, req *Request) (any, *http.Response, error) {
|
|||
|
req.Method = "HEAD"
|
|||
|
return Do(cfg, req)
|
|||
|
}
|
|||
|
|
|||
|
func Options(cfg *SignerConfig, req *Request) (any, *http.Response, error) {
|
|||
|
req.Method = "OPTIONS"
|
|||
|
return Do(cfg, req)
|
|||
|
}
|
|||
|
|
|||
|
func Do(cfg *SignerConfig, req *Request) (any, *http.Response, error) {
|
|||
|
err := MakeSign(cfg, req)
|
|||
|
if err != nil {
|
|||
|
return nil, nil, err
|
|||
|
}
|
|||
|
// fmt.Println(u.BMagenta(u.JsonP(req.Headers)))
|
|||
|
// return nil, nil, err
|
|||
|
|
|||
|
headers := make([]string, 0)
|
|||
|
for k, v := range req.Headers {
|
|||
|
headers = append(headers, k, v)
|
|||
|
}
|
|||
|
c := GetHttpClient(req.Timeout)
|
|||
|
// c.Debug = true
|
|||
|
|
|||
|
// fmt.Println(req.Url, req.finalUrl, u.BMagenta(string(req.finalBody)), "===")
|
|||
|
// fmt.Println(u.BCyan(u.JsonP(req.Headers)), "===")
|
|||
|
|
|||
|
var r *httpclient.Result
|
|||
|
var lastData any
|
|||
|
if req.Callback != nil {
|
|||
|
r = c.ManualDo(req.Method, req.FinalUrl, req.FinalBody, headers...)
|
|||
|
if r.Error != nil {
|
|||
|
return nil, r.Response, r.Error
|
|||
|
}
|
|||
|
respType := req.ResponseType
|
|||
|
if respType == "" {
|
|||
|
respType = strings.Split(r.Response.Header.Get("Content-Type"), ";")[0]
|
|||
|
}
|
|||
|
|
|||
|
if r.Response.Body != nil {
|
|||
|
func() {
|
|||
|
reader := bufio.NewScanner(r.Response.Body)
|
|||
|
defer r.Response.Body.Close()
|
|||
|
for reader.Scan() {
|
|||
|
str := strings.TrimSpace(reader.Text())
|
|||
|
if str == "" {
|
|||
|
continue
|
|||
|
}
|
|||
|
switch respType {
|
|||
|
case "text/event-stream":
|
|||
|
if strings.HasPrefix(str, "data:") {
|
|||
|
str = strings.TrimPrefix(str, "data:")
|
|||
|
str = strings.TrimSpace(str)
|
|||
|
}
|
|||
|
var d any
|
|||
|
if str == "[DONE]" {
|
|||
|
break
|
|||
|
}
|
|||
|
d = u.UnJson(str, nil)
|
|||
|
req.Callback(d)
|
|||
|
if d != nil {
|
|||
|
lastData = d
|
|||
|
}
|
|||
|
case "application/json":
|
|||
|
req.Callback(u.UnJson(str, nil))
|
|||
|
case "application/xml":
|
|||
|
if jData, err := xml2json.Convert(strings.NewReader(str)); err == nil {
|
|||
|
req.Callback(u.UnJsonBytes(jData.Bytes(), nil))
|
|||
|
}
|
|||
|
req.Callback(str)
|
|||
|
default:
|
|||
|
req.Callback(str)
|
|||
|
}
|
|||
|
}
|
|||
|
}()
|
|||
|
}
|
|||
|
return lastData, r.Response, nil
|
|||
|
} else {
|
|||
|
r = c.Do(req.Method, req.FinalUrl, req.FinalBody, headers...)
|
|||
|
if r.Error != nil {
|
|||
|
return nil, r.Response, r.Error
|
|||
|
}
|
|||
|
|
|||
|
respType := req.ResponseType
|
|||
|
if respType == "" {
|
|||
|
respType = strings.Split(r.Response.Header.Get("Content-Type"), ";")[0]
|
|||
|
}
|
|||
|
switch respType {
|
|||
|
case "application/json":
|
|||
|
return u.UnJsonBytes(r.Bytes(), nil), r.Response, nil
|
|||
|
case "application/xml":
|
|||
|
if jData, err := xml2json.Convert(strings.NewReader(string(r.Bytes()))); err == nil {
|
|||
|
return u.UnJsonBytes(jData.Bytes(), nil), r.Response, nil
|
|||
|
}
|
|||
|
return string(r.Bytes()), r.Response, nil
|
|||
|
case "application/octet-stream":
|
|||
|
return r.Bytes(), r.Response, nil
|
|||
|
default:
|
|||
|
return r.String(), r.Response, nil
|
|||
|
}
|
|||
|
}
|
|||
|
}
|