| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172 |
- package dara
- import (
- "bytes"
- "context"
- "crypto/tls"
- "crypto/x509"
- "encoding/base64"
- "encoding/hex"
- "encoding/json"
- "errors"
- "fmt"
- "io"
- "math"
- "math/rand"
- "net"
- "net/http"
- "net/url"
- "os"
- "reflect"
- "strconv"
- "strings"
- "sync"
- "time"
- "github.com/alibabacloud-go/debug/debug"
- util "github.com/alibabacloud-go/tea-utils/v2/service"
- "github.com/alibabacloud-go/tea/utils"
- "golang.org/x/net/proxy"
- )
- type RuntimeOptions = util.RuntimeOptions
- type ExtendsParameters = util.ExtendsParameters
- var debugLog = debug.Init("dara")
- type HttpRequest interface {
- }
- type HttpResponse interface {
- }
- type HttpClient interface {
- Call(request *http.Request, transport *http.Transport) (response *http.Response, err error)
- }
- type daraClient struct {
- sync.Mutex
- httpClient *http.Client
- ifInit bool
- }
- func (client *daraClient) Call(request *http.Request, transport *http.Transport) (response *http.Response, err error) {
- response, err = client.httpClient.Do(request)
- return
- }
- var hookDo = func(fn func(req *http.Request, transport *http.Transport) (*http.Response, error)) func(req *http.Request, transport *http.Transport) (*http.Response, error) {
- return fn
- }
- var basicTypes = []string{
- "int", "int16", "int64", "int32", "float32", "float64", "string", "bool", "uint64", "uint32", "uint16",
- }
- // Verify whether the parameters meet the requirements
- var validateParams = []string{"require", "pattern", "maxLength", "minLength", "maximum", "minimum", "maxItems", "minItems"}
- var clientPool = &sync.Map{}
- // Request is used wrap http request
- type Request struct {
- Protocol *string
- Port *int
- Method *string
- Pathname *string
- Domain *string
- Headers map[string]*string
- Query map[string]*string
- Body io.Reader
- }
- // Response is use d wrap http response
- type Response struct {
- Body io.ReadCloser
- StatusCode *int
- StatusMessage *string
- Headers map[string]*string
- }
- // RuntimeObject is used for converting http configuration
- type RuntimeObject struct {
- IgnoreSSL *bool `json:"ignoreSSL" xml:"ignoreSSL"`
- ReadTimeout *int `json:"readTimeout" xml:"readTimeout"`
- ConnectTimeout *int `json:"connectTimeout" xml:"connectTimeout"`
- IdleTimeout *int `json:"idleTimeout" xml:"idleTimeout"`
- LocalAddr *string `json:"localAddr" xml:"localAddr"`
- HttpProxy *string `json:"httpProxy" xml:"httpProxy"`
- HttpsProxy *string `json:"httpsProxy" xml:"httpsProxy"`
- NoProxy *string `json:"noProxy" xml:"noProxy"`
- MaxIdleConns *int `json:"maxIdleConns" xml:"maxIdleConns"`
- Key *string `json:"key" xml:"key"`
- Cert *string `json:"cert" xml:"cert"`
- Ca *string `json:"ca" xml:"ca"`
- Socks5Proxy *string `json:"socks5Proxy" xml:"socks5Proxy"`
- Socks5NetWork *string `json:"socks5NetWork" xml:"socks5NetWork"`
- Listener utils.ProgressListener `json:"listener" xml:"listener"`
- Tracker *utils.ReaderTracker `json:"tracker" xml:"tracker"`
- Logger *utils.Logger `json:"logger" xml:"logger"`
- RetryOptions *RetryOptions `json:"retryOptions" xml:"retryOptions"`
- ExtendsParameters *ExtendsParameters `json:"extendsParameters,omitempty" xml:"extendsParameters,omitempty"`
- HttpClient
- }
- func (r *RuntimeObject) getClientTag(domain string) string {
- return strconv.FormatBool(BoolValue(r.IgnoreSSL)) + strconv.Itoa(IntValue(r.ReadTimeout)) +
- strconv.Itoa(IntValue(r.ConnectTimeout)) + strconv.Itoa(IntValue(r.IdleTimeout)) + StringValue(r.LocalAddr) + StringValue(r.HttpProxy) +
- StringValue(r.HttpsProxy) + StringValue(r.NoProxy) + StringValue(r.Socks5Proxy) + StringValue(r.Socks5NetWork) + domain
- }
- // NewRuntimeObject is used for shortly create runtime object
- func NewRuntimeObject(runtime map[string]interface{}) *RuntimeObject {
- if runtime == nil {
- return &RuntimeObject{}
- }
- runtimeObject := &RuntimeObject{
- IgnoreSSL: TransInterfaceToBool(runtime["ignoreSSL"]),
- ReadTimeout: TransInterfaceToInt(runtime["readTimeout"]),
- ConnectTimeout: TransInterfaceToInt(runtime["connectTimeout"]),
- IdleTimeout: TransInterfaceToInt(runtime["idleTimeout"]),
- LocalAddr: TransInterfaceToString(runtime["localAddr"]),
- HttpProxy: TransInterfaceToString(runtime["httpProxy"]),
- HttpsProxy: TransInterfaceToString(runtime["httpsProxy"]),
- NoProxy: TransInterfaceToString(runtime["noProxy"]),
- MaxIdleConns: TransInterfaceToInt(runtime["maxIdleConns"]),
- Socks5Proxy: TransInterfaceToString(runtime["socks5Proxy"]),
- Socks5NetWork: TransInterfaceToString(runtime["socks5NetWork"]),
- Key: TransInterfaceToString(runtime["key"]),
- Cert: TransInterfaceToString(runtime["cert"]),
- Ca: TransInterfaceToString(runtime["ca"]),
- }
- if runtime["listener"] != nil {
- runtimeObject.Listener = runtime["listener"].(utils.ProgressListener)
- }
- if runtime["tracker"] != nil {
- runtimeObject.Tracker = runtime["tracker"].(*utils.ReaderTracker)
- }
- if runtime["logger"] != nil {
- runtimeObject.Logger = runtime["logger"].(*utils.Logger)
- }
- if runtime["httpClient"] != nil {
- runtimeObject.HttpClient = runtime["httpClient"].(HttpClient)
- }
- if runtime["retryOptions"] != nil {
- runtimeObject.RetryOptions = runtime["retryOptions"].(*RetryOptions)
- }
- return runtimeObject
- }
- // NewRequest is used shortly create Request
- func NewRequest() (req *Request) {
- return &Request{
- Headers: map[string]*string{},
- Query: map[string]*string{},
- }
- }
- // NewResponse is create response with http response
- func NewResponse(httpResponse *http.Response) (res *Response) {
- res = &Response{}
- res.Body = httpResponse.Body
- res.Headers = make(map[string]*string)
- res.StatusCode = Int(httpResponse.StatusCode)
- res.StatusMessage = String(httpResponse.Status)
- return
- }
- // Convert is use convert map[string]interface object to struct
- func Convert(in interface{}, out interface{}) error {
- byt, _ := json.Marshal(in)
- decoder := jsonParser.NewDecoder(bytes.NewReader(byt))
- decoder.UseNumber()
- err := decoder.Decode(&out)
- return err
- }
- // ConvertChan converts the source data to the target type and sends it to the specified channel.
- // @param src - source data
- // @param destChan - target channel
- // @return error - error during the conversion process
- func ConvertChan(src interface{}, destChan interface{}) error {
- destChanValue := reflect.ValueOf(destChan)
- if destChanValue.Kind() != reflect.Chan {
- return fmt.Errorf("destChan must be a channel")
- }
- if destChanValue.Type().ChanDir() == reflect.SendDir {
- return fmt.Errorf("destChan must be a receive or bidirectional channel")
- }
- elemType := destChanValue.Type().Elem()
- destValue := reflect.New(elemType).Interface()
- err := Convert(src, destValue)
- if err != nil {
- return err
- }
- destValueElem := reflect.ValueOf(destValue).Elem()
- defer func() {
- if r := recover(); r != nil {
- }
- }()
- destChanValue.TrySend(destValueElem)
- return nil
- }
- // Recover is used to format error
- func Recover(in interface{}) error {
- if in == nil {
- return nil
- }
- return errors.New(fmt.Sprint(in))
- }
- // ReadBody is used read response body
- func (response *Response) ReadBody() (body []byte, err error) {
- var buffer [512]byte
- defer response.Body.Close()
- result := bytes.NewBuffer(nil)
- for {
- n, err := response.Body.Read(buffer[0:])
- result.Write(buffer[0:n])
- if err != nil && err == io.EOF {
- break
- } else if err != nil {
- return nil, err
- }
- }
- return result.Bytes(), nil
- }
- func getDaraClient(tag string) *daraClient {
- client, ok := clientPool.Load(tag)
- if client == nil && !ok {
- client = &daraClient{
- httpClient: &http.Client{},
- ifInit: false,
- }
- clientPool.Store(tag, client)
- }
- return client.(*daraClient)
- }
- // DoRequest is used send request to server
- func DoRequest(request *Request, runtimeObject *RuntimeObject) (response *Response, err error) {
- if runtimeObject == nil {
- runtimeObject = &RuntimeObject{}
- }
- fieldMap := make(map[string]string)
- utils.InitLogMsg(fieldMap)
- defer func() {
- if runtimeObject.Logger != nil {
- runtimeObject.Logger.PrintLog(fieldMap, err)
- }
- }()
- if request.Method == nil {
- request.Method = String("GET")
- }
- if request.Protocol == nil {
- request.Protocol = String("http")
- } else {
- request.Protocol = String(strings.ToLower(StringValue(request.Protocol)))
- }
- requestURL := ""
- request.Domain = request.Headers["host"]
- if request.Port != nil {
- request.Domain = String(fmt.Sprintf("%s:%d", StringValue(request.Domain), IntValue(request.Port)))
- }
- requestURL = fmt.Sprintf("%s://%s%s", StringValue(request.Protocol), StringValue(request.Domain), StringValue(request.Pathname))
- queryParams := request.Query
- // sort QueryParams by key
- q := url.Values{}
- for key, value := range queryParams {
- q.Add(key, StringValue(value))
- }
- querystring := q.Encode()
- if len(querystring) > 0 {
- if strings.Contains(requestURL, "?") {
- requestURL = fmt.Sprintf("%s&%s", requestURL, querystring)
- } else {
- requestURL = fmt.Sprintf("%s?%s", requestURL, querystring)
- }
- }
- debugLog("> %s %s", StringValue(request.Method), requestURL)
- httpRequest, err := http.NewRequest(StringValue(request.Method), requestURL, request.Body)
- if err != nil {
- return
- }
- httpRequest.Host = StringValue(request.Domain)
- var client HttpClient
- if runtimeObject.HttpClient == nil {
- client = getDaraClient(runtimeObject.getClientTag(StringValue(request.Domain)))
- } else {
- client = runtimeObject.HttpClient
- }
- trans, err := getHttpTransport(request, runtimeObject)
- if err != nil {
- return
- }
- if defaultClient, ok := client.(*daraClient); ok {
- defaultClient.Lock()
- if !defaultClient.ifInit || defaultClient.httpClient.Transport == nil {
- defaultClient.httpClient.Transport = trans
- }
- defaultClient.httpClient.Timeout = time.Duration(IntValue(runtimeObject.ConnectTimeout)+IntValue(runtimeObject.ReadTimeout)) * time.Millisecond
- defaultClient.ifInit = true
- defaultClient.Unlock()
- }
- for key, value := range request.Headers {
- if value == nil || key == "content-length" {
- continue
- } else if key == "host" {
- httpRequest.Header["Host"] = []string{*value}
- delete(httpRequest.Header, "host")
- } else if key == "user-agent" {
- httpRequest.Header["User-Agent"] = []string{*value}
- delete(httpRequest.Header, "user-agent")
- } else {
- httpRequest.Header[key] = []string{*value}
- }
- debugLog("> %s: %s", key, StringValue(value))
- }
- contentlength, _ := strconv.Atoi(StringValue(request.Headers["content-length"]))
- event := utils.NewProgressEvent(utils.TransferStartedEvent, 0, int64(contentlength), 0)
- utils.PublishProgress(runtimeObject.Listener, event)
- putMsgToMap(fieldMap, httpRequest)
- startTime := time.Now()
- fieldMap["{start_time}"] = startTime.Format("2006-01-02 15:04:05")
- res, err := hookDo(client.Call)(httpRequest, trans)
- fieldMap["{cost}"] = time.Since(startTime).String()
- completedBytes := int64(0)
- if runtimeObject.Tracker != nil {
- completedBytes = runtimeObject.Tracker.CompletedBytes
- }
- if err != nil {
- event = utils.NewProgressEvent(utils.TransferFailedEvent, completedBytes, int64(contentlength), 0)
- utils.PublishProgress(runtimeObject.Listener, event)
- return
- }
- event = utils.NewProgressEvent(utils.TransferCompletedEvent, completedBytes, int64(contentlength), 0)
- utils.PublishProgress(runtimeObject.Listener, event)
- response = NewResponse(res)
- fieldMap["{code}"] = strconv.Itoa(res.StatusCode)
- fieldMap["{res_headers}"] = Stringify(res.Header)
- debugLog("< HTTP/1.1 %s", res.Status)
- for key, value := range res.Header {
- debugLog("< %s: %s", key, strings.Join(value, ""))
- if len(value) != 0 {
- response.Headers[strings.ToLower(key)] = String(value[0])
- }
- }
- return
- }
- func DoRequestWithCtx(ctx context.Context, request *Request, runtimeObject *RuntimeObject) (response *Response, err error) {
- if runtimeObject == nil {
- runtimeObject = &RuntimeObject{}
- }
- fieldMap := make(map[string]string)
- utils.InitLogMsg(fieldMap)
- defer func() {
- if runtimeObject.Logger != nil {
- runtimeObject.Logger.PrintLog(fieldMap, err)
- }
- }()
- if request.Method == nil {
- request.Method = String("GET")
- }
- if request.Protocol == nil {
- request.Protocol = String("http")
- } else {
- request.Protocol = String(strings.ToLower(StringValue(request.Protocol)))
- }
- requestURL := ""
- request.Domain = request.Headers["host"]
- if request.Port != nil {
- request.Domain = String(fmt.Sprintf("%s:%d", StringValue(request.Domain), IntValue(request.Port)))
- }
- requestURL = fmt.Sprintf("%s://%s%s", StringValue(request.Protocol), StringValue(request.Domain), StringValue(request.Pathname))
- queryParams := request.Query
- // sort QueryParams by key
- q := url.Values{}
- for key, value := range queryParams {
- q.Add(key, StringValue(value))
- }
- querystring := q.Encode()
- if len(querystring) > 0 {
- if strings.Contains(requestURL, "?") {
- requestURL = fmt.Sprintf("%s&%s", requestURL, querystring)
- } else {
- requestURL = fmt.Sprintf("%s?%s", requestURL, querystring)
- }
- }
- debugLog("> %s %s", StringValue(request.Method), requestURL)
- httpRequest, err := http.NewRequestWithContext(ctx, StringValue(request.Method), requestURL, request.Body)
- if err != nil {
- return
- }
- httpRequest.Host = StringValue(request.Domain)
- var client HttpClient
- if runtimeObject.HttpClient == nil {
- client = getDaraClient(runtimeObject.getClientTag(StringValue(request.Domain)))
- } else {
- client = runtimeObject.HttpClient
- }
- trans, err := getHttpTransport(request, runtimeObject)
- if err != nil {
- return
- }
- if defaultClient, ok := client.(*daraClient); ok {
- defaultClient.Lock()
- if !defaultClient.ifInit || defaultClient.httpClient.Transport == nil {
- defaultClient.httpClient.Transport = trans
- }
- defaultClient.httpClient.Timeout = time.Duration(IntValue(runtimeObject.ConnectTimeout)+IntValue(runtimeObject.ReadTimeout)) * time.Millisecond
- defaultClient.ifInit = true
- defaultClient.Unlock()
- }
- for key, value := range request.Headers {
- if value == nil || key == "content-length" {
- continue
- } else if key == "host" {
- httpRequest.Header["Host"] = []string{*value}
- delete(httpRequest.Header, "host")
- } else if key == "user-agent" {
- httpRequest.Header["User-Agent"] = []string{*value}
- delete(httpRequest.Header, "user-agent")
- } else {
- httpRequest.Header[key] = []string{*value}
- }
- debugLog("> %s: %s", key, StringValue(value))
- }
- contentlength, _ := strconv.Atoi(StringValue(request.Headers["content-length"]))
- event := utils.NewProgressEvent(utils.TransferStartedEvent, 0, int64(contentlength), 0)
- utils.PublishProgress(runtimeObject.Listener, event)
- putMsgToMap(fieldMap, httpRequest)
- startTime := time.Now()
- fieldMap["{start_time}"] = startTime.Format("2006-01-02 15:04:05")
- res, err := hookDo(client.Call)(httpRequest, trans)
- fieldMap["{cost}"] = time.Since(startTime).String()
- completedBytes := int64(0)
- if runtimeObject.Tracker != nil {
- completedBytes = runtimeObject.Tracker.CompletedBytes
- }
- if err != nil {
- select {
- case <-ctx.Done():
- err = TeaSDKError(ctx.Err())
- default:
- }
- event = utils.NewProgressEvent(utils.TransferFailedEvent, completedBytes, int64(contentlength), 0)
- utils.PublishProgress(runtimeObject.Listener, event)
- return
- }
- event = utils.NewProgressEvent(utils.TransferCompletedEvent, completedBytes, int64(contentlength), 0)
- utils.PublishProgress(runtimeObject.Listener, event)
- response = NewResponse(res)
- fieldMap["{code}"] = strconv.Itoa(res.StatusCode)
- fieldMap["{res_headers}"] = Stringify(res.Header)
- debugLog("< HTTP/1.1 %s", res.Status)
- for key, value := range res.Header {
- debugLog("< %s: %s", key, strings.Join(value, ""))
- if len(value) != 0 {
- response.Headers[strings.ToLower(key)] = String(value[0])
- }
- }
- return
- }
- func getHttpTransport(req *Request, runtime *RuntimeObject) (*http.Transport, error) {
- trans := new(http.Transport)
- trans.ResponseHeaderTimeout = time.Duration(IntValue(runtime.ReadTimeout)) * time.Millisecond
- httpProxy, err := getHttpProxy(StringValue(req.Protocol), StringValue(req.Domain), runtime)
- if err != nil {
- return nil, err
- }
- if strings.ToLower(*req.Protocol) == "https" {
- if BoolValue(runtime.IgnoreSSL) != true {
- trans.TLSClientConfig = &tls.Config{
- InsecureSkipVerify: false,
- }
- if runtime.Key != nil && runtime.Cert != nil && StringValue(runtime.Key) != "" && StringValue(runtime.Cert) != "" {
- cert, err := tls.X509KeyPair([]byte(StringValue(runtime.Cert)), []byte(StringValue(runtime.Key)))
- if err != nil {
- return nil, err
- }
- trans.TLSClientConfig.Certificates = []tls.Certificate{cert}
- }
- if runtime.Ca != nil && StringValue(runtime.Ca) != "" {
- clientCertPool := x509.NewCertPool()
- ok := clientCertPool.AppendCertsFromPEM([]byte(StringValue(runtime.Ca)))
- if !ok {
- return nil, errors.New("Failed to parse root certificate")
- }
- trans.TLSClientConfig.RootCAs = clientCertPool
- }
- } else {
- trans.TLSClientConfig = &tls.Config{
- InsecureSkipVerify: true,
- }
- }
- }
- if httpProxy != nil {
- trans.Proxy = http.ProxyURL(httpProxy)
- if httpProxy.User != nil {
- password, _ := httpProxy.User.Password()
- auth := httpProxy.User.Username() + ":" + password
- basic := "Basic " + base64.StdEncoding.EncodeToString([]byte(auth))
- req.Headers["Proxy-Authorization"] = String(basic)
- }
- }
- if runtime.Socks5Proxy != nil && StringValue(runtime.Socks5Proxy) != "" {
- socks5Proxy, err := getSocks5Proxy(runtime)
- if err != nil {
- return nil, err
- }
- if socks5Proxy != nil {
- var auth *proxy.Auth
- if socks5Proxy.User != nil {
- password, _ := socks5Proxy.User.Password()
- auth = &proxy.Auth{
- User: socks5Proxy.User.Username(),
- Password: password,
- }
- }
- dialer, err := proxy.SOCKS5(strings.ToLower(StringValue(runtime.Socks5NetWork)), socks5Proxy.Host, auth,
- &net.Dialer{
- Timeout: time.Duration(IntValue(runtime.ConnectTimeout)) * time.Millisecond,
- DualStack: true,
- LocalAddr: getLocalAddr(StringValue(runtime.LocalAddr)),
- })
- if err != nil {
- return nil, err
- }
- trans.Dial = dialer.Dial
- }
- } else {
- trans.DialContext = setDialContext(runtime)
- }
- if runtime.MaxIdleConns != nil && *runtime.MaxIdleConns > 0 {
- trans.MaxIdleConns = IntValue(runtime.MaxIdleConns)
- trans.MaxIdleConnsPerHost = IntValue(runtime.MaxIdleConns)
- }
- if runtime.IdleTimeout != nil && *runtime.IdleTimeout > 0 {
- trans.IdleConnTimeout = time.Duration(IntValue(runtime.IdleTimeout)) * time.Millisecond
- }
- return trans, nil
- }
- func putMsgToMap(fieldMap map[string]string, request *http.Request) {
- fieldMap["{host}"] = request.Host
- fieldMap["{method}"] = request.Method
- fieldMap["{uri}"] = request.URL.RequestURI()
- fieldMap["{pid}"] = strconv.Itoa(os.Getpid())
- fieldMap["{version}"] = strings.Split(request.Proto, "/")[1]
- hostname, _ := os.Hostname()
- fieldMap["{hostname}"] = hostname
- fieldMap["{req_headers}"] = Stringify(request.Header)
- fieldMap["{target}"] = request.URL.Path + request.URL.RawQuery
- }
- func getNoProxy(protocol string, runtime *RuntimeObject) []string {
- var urls []string
- if runtime.NoProxy != nil && StringValue(runtime.NoProxy) != "" {
- urls = strings.Split(StringValue(runtime.NoProxy), ",")
- } else if rawurl := os.Getenv("NO_PROXY"); rawurl != "" {
- urls = strings.Split(rawurl, ",")
- } else if rawurl := os.Getenv("no_proxy"); rawurl != "" {
- urls = strings.Split(rawurl, ",")
- }
- return urls
- }
- func ToReader(obj interface{}) io.Reader {
- switch obj.(type) {
- case string:
- tmp := obj.(string)
- return strings.NewReader(tmp)
- case *string:
- tmp := obj.(*string)
- return strings.NewReader(StringValue(tmp))
- case []byte:
- return strings.NewReader(string(obj.([]byte)))
- case io.Reader:
- return obj.(io.Reader)
- default:
- panic("Invalid Body. Please set a valid Body.")
- }
- }
- func ToWriter(obj interface{}) io.Writer {
- switch obj.(type) {
- case string:
- var buf bytes.Buffer
- buf.WriteString(obj.(string))
- return &buf
- case *string:
- var buf bytes.Buffer
- tmp := obj.(*string)
- buf.WriteString(*tmp)
- return &buf
- case []byte:
- var buf bytes.Buffer
- buf.Write(obj.([]byte))
- return &buf
- case io.Writer:
- return obj.(io.Writer)
- case *bytes.Buffer:
- return obj.(*bytes.Buffer)
- case *os.File:
- return obj.(*os.File)
- default:
- panic("Invalid Writer. Please provide a valid Writer.")
- }
- }
- func ToString(val interface{}) string {
- switch v := val.(type) {
- case []byte:
- return string(v) // 将 []byte 转换为字符串
- default:
- return fmt.Sprintf("%v", v) // 处理其他类型
- }
- }
- func getHttpProxy(protocol, host string, runtime *RuntimeObject) (proxy *url.URL, err error) {
- urls := getNoProxy(protocol, runtime)
- for _, url := range urls {
- if url == host {
- return nil, nil
- }
- }
- if protocol == "https" {
- if runtime.HttpsProxy != nil && StringValue(runtime.HttpsProxy) != "" {
- proxy, err = url.Parse(StringValue(runtime.HttpsProxy))
- } else if rawurl := os.Getenv("HTTPS_PROXY"); rawurl != "" {
- proxy, err = url.Parse(rawurl)
- } else if rawurl := os.Getenv("https_proxy"); rawurl != "" {
- proxy, err = url.Parse(rawurl)
- }
- } else {
- if runtime.HttpProxy != nil && StringValue(runtime.HttpProxy) != "" {
- proxy, err = url.Parse(StringValue(runtime.HttpProxy))
- } else if rawurl := os.Getenv("HTTP_PROXY"); rawurl != "" {
- proxy, err = url.Parse(rawurl)
- } else if rawurl := os.Getenv("http_proxy"); rawurl != "" {
- proxy, err = url.Parse(rawurl)
- }
- }
- return proxy, err
- }
- func getSocks5Proxy(runtime *RuntimeObject) (proxy *url.URL, err error) {
- if runtime.Socks5Proxy != nil && StringValue(runtime.Socks5Proxy) != "" {
- proxy, err = url.Parse(StringValue(runtime.Socks5Proxy))
- }
- return proxy, err
- }
- func getLocalAddr(localAddr string) (addr *net.TCPAddr) {
- if localAddr != "" {
- addr = &net.TCPAddr{
- IP: net.ParseIP(localAddr),
- }
- }
- return addr
- }
- func setDialContext(runtime *RuntimeObject) func(cxt context.Context, net, addr string) (c net.Conn, err error) {
- return func(ctx context.Context, network, address string) (net.Conn, error) {
- timeout := time.Duration(IntValue(runtime.ConnectTimeout)) * time.Millisecond
- dialer := &net.Dialer{
- Timeout: timeout,
- Resolver: &net.Resolver{
- PreferGo: false,
- },
- }
- if runtime.LocalAddr != nil && StringValue(runtime.LocalAddr) != "" {
- dialer.LocalAddr = getLocalAddr(StringValue(runtime.LocalAddr))
- }
- return dialer.DialContext(ctx, network, address)
- }
- }
- func ToObject(obj interface{}) map[string]interface{} {
- result := make(map[string]interface{})
- byt, _ := json.Marshal(obj)
- err := json.Unmarshal(byt, &result)
- if err != nil {
- return nil
- }
- return result
- }
- func AllowRetry(retry interface{}, retryTimes *int) *bool {
- if IntValue(retryTimes) == 0 {
- return Bool(true)
- }
- retryMap, ok := retry.(map[string]interface{})
- if !ok {
- return Bool(false)
- }
- retryable, ok := retryMap["retryable"].(bool)
- if !ok || !retryable {
- return Bool(false)
- }
- maxAttempts, ok := retryMap["maxAttempts"].(int)
- if !ok || maxAttempts < IntValue(retryTimes) {
- return Bool(false)
- }
- return Bool(true)
- }
- func Merge(args ...interface{}) map[string]*string {
- finalArg := make(map[string]*string)
- for _, obj := range args {
- switch obj.(type) {
- case map[string]*string:
- arg := obj.(map[string]*string)
- for key, value := range arg {
- if value != nil {
- finalArg[key] = value
- }
- }
- default:
- byt, _ := json.Marshal(obj)
- arg := make(map[string]string)
- err := json.Unmarshal(byt, &arg)
- if err != nil {
- return finalArg
- }
- for key, value := range arg {
- if value != "" {
- finalArg[key] = String(value)
- }
- }
- }
- }
- return finalArg
- }
- func IsNil(val interface{}) bool {
- defer func() {
- recover()
- }()
- if val == nil {
- return true
- }
- v := reflect.ValueOf(val)
- if v.Kind() == reflect.Ptr || v.Kind() == reflect.Slice || v.Kind() == reflect.Map {
- return v.IsNil()
- }
- valType := reflect.TypeOf(val)
- valZero := reflect.Zero(valType)
- return valZero == v
- }
- func isNil(a interface{}) bool {
- defer func() {
- recover()
- }()
- vi := reflect.ValueOf(a)
- return vi.IsNil()
- }
- func isNilOrZero(value interface{}) bool {
- if value == nil {
- return true
- }
- v := reflect.ValueOf(value)
- switch v.Kind() {
- case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Slice:
- return v.IsNil()
- default:
- // Check for zero value
- return reflect.DeepEqual(value, reflect.Zero(v.Type()).Interface())
- }
- }
- func Default(inputValue interface{}, defaultValue interface{}) (_result interface{}) {
- if isNilOrZero(inputValue) {
- _result = defaultValue
- return _result
- }
- _result = inputValue
- return _result
- }
- func ToMap(args ...interface{}) map[string]interface{} {
- isNotNil := false
- finalArg := make(map[string]interface{})
- for _, obj := range args {
- if obj == nil {
- continue
- }
- if isNil(obj) {
- continue
- }
- isNotNil = true
- switch obj.(type) {
- case map[string]*string:
- arg := obj.(map[string]*string)
- for key, value := range arg {
- if value != nil {
- finalArg[key] = StringValue(value)
- }
- }
- case map[string]interface{}:
- arg := obj.(map[string]interface{})
- for key, value := range arg {
- if value != nil {
- finalArg[key] = value
- }
- }
- case *string:
- str := obj.(*string)
- arg := make(map[string]interface{})
- err := json.Unmarshal([]byte(StringValue(str)), &arg)
- if err == nil {
- for key, value := range arg {
- if value != nil {
- finalArg[key] = value
- }
- }
- }
- tmp := make(map[string]string)
- err = json.Unmarshal([]byte(StringValue(str)), &tmp)
- if err == nil {
- for key, value := range arg {
- if value != "" {
- finalArg[key] = value
- }
- }
- }
- case []byte:
- byt := obj.([]byte)
- arg := make(map[string]interface{})
- err := json.Unmarshal(byt, &arg)
- if err == nil {
- for key, value := range arg {
- if value != nil {
- finalArg[key] = value
- }
- }
- break
- }
- default:
- val := reflect.ValueOf(obj)
- if val.Kind().String() == "map" {
- tmp := val.MapKeys()
- for _, key := range tmp {
- finalArg[key.String()] = val.MapIndex(key).Interface()
- }
- } else {
- res := structToMap(val)
- for key, value := range res {
- if value != nil {
- finalArg[key] = value
- }
- }
- }
- }
- }
- if !isNotNil {
- return nil
- }
- return finalArg
- }
- func structToMap(dataValue reflect.Value) map[string]interface{} {
- out := make(map[string]interface{})
- if !dataValue.IsValid() {
- return out
- }
- if dataValue.Kind().String() == "ptr" {
- if dataValue.IsNil() {
- return out
- }
- dataValue = dataValue.Elem()
- }
- if !dataValue.IsValid() {
- return out
- }
- dataType := dataValue.Type()
- if dataType.Kind().String() != "struct" {
- return out
- }
- for i := 0; i < dataType.NumField(); i++ {
- field := dataType.Field(i)
- name, containsNameTag := field.Tag.Lookup("json")
- if !containsNameTag {
- name = field.Name
- } else {
- strs := strings.Split(name, ",")
- name = strs[0]
- }
- fieldValue := dataValue.FieldByName(field.Name)
- if !fieldValue.IsValid() || fieldValue.IsNil() {
- continue
- }
- if field.Type.String() == "io.Reader" || field.Type.String() == "io.Writer" {
- continue
- } else if field.Type.Kind().String() == "struct" {
- out[name] = structToMap(fieldValue)
- } else if field.Type.Kind().String() == "ptr" &&
- field.Type.Elem().Kind().String() == "struct" {
- if fieldValue.Elem().IsValid() {
- out[name] = structToMap(fieldValue)
- }
- } else if field.Type.Kind().String() == "ptr" {
- if fieldValue.IsValid() && !fieldValue.IsNil() {
- out[name] = fieldValue.Elem().Interface()
- }
- } else if field.Type.Kind().String() == "slice" {
- tmp := make([]interface{}, 0)
- num := fieldValue.Len()
- for i := 0; i < num; i++ {
- value := fieldValue.Index(i)
- if !value.IsValid() {
- continue
- }
- if value.Type().Kind().String() == "ptr" &&
- value.Type().Elem().Kind().String() == "struct" {
- if value.IsValid() && !value.IsNil() {
- tmp = append(tmp, structToMap(value))
- }
- } else if value.Type().Kind().String() == "struct" {
- tmp = append(tmp, structToMap(value))
- } else if value.Type().Kind().String() == "ptr" {
- if value.IsValid() && !value.IsNil() {
- tmp = append(tmp, value.Elem().Interface())
- }
- } else {
- tmp = append(tmp, value.Interface())
- }
- }
- if len(tmp) > 0 {
- out[name] = tmp
- }
- } else {
- out[name] = fieldValue.Interface()
- }
- }
- return out
- }
- func GetBackoffTime(backoff interface{}, retrytimes *int) *int {
- backoffMap, ok := backoff.(map[string]interface{})
- if !ok {
- return Int(0)
- }
- policy, ok := backoffMap["policy"].(string)
- if !ok || policy == "no" {
- return Int(0)
- }
- period, ok := backoffMap["period"].(int)
- if !ok || period == 0 {
- return Int(0)
- }
- maxTime := math.Pow(2.0, float64(IntValue(retrytimes)))
- return Int(rand.Intn(int(maxTime-1)) * period)
- }
- func Sleep(backoffTime int) {
- sleeptime := time.Duration(backoffTime) * time.Second
- time.Sleep(sleeptime)
- }
- // Determines whether realType is in filterTypes
- func isFilterType(realType string, filterTypes []string) bool {
- for _, value := range filterTypes {
- if value == realType {
- return true
- }
- }
- return false
- }
- func TransInterfaceToBool(val interface{}) *bool {
- if val == nil {
- return nil
- }
- return Bool(val.(bool))
- }
- func TransInterfaceToInt(val interface{}) *int {
- if val == nil {
- return nil
- }
- return Int(val.(int))
- }
- func TransInterfaceToString(val interface{}) *string {
- if val == nil {
- return nil
- }
- return String(val.(string))
- }
- func Prettify(i interface{}) string {
- resp, _ := json.MarshalIndent(i, "", " ")
- return string(resp)
- }
- func ToInt(a *int32) *int {
- return Int(int(Int32Value(a)))
- }
- func ToInt32(a *int) *int32 {
- return Int32(int32(IntValue(a)))
- }
- func ToBytes(s, encodingType string) []byte {
- switch encodingType {
- case "utf8":
- return []byte(s)
- case "base64":
- data, err := base64.StdEncoding.DecodeString(s)
- if err != nil {
- return nil
- }
- return data
- case "hex":
- data, err := hex.DecodeString(s)
- if err != nil {
- return nil
- }
- return data
- default:
- return nil
- }
- }
- func BytesFromString(str string, typeStr string) []byte {
- switch typeStr {
- case "utf8":
- return []byte(str)
- case "hex":
- bytes, err := hex.DecodeString(str)
- if err == nil {
- return bytes
- }
- case "base64":
- bytes, err := base64.StdEncoding.DecodeString(str)
- if err == nil {
- return bytes
- }
- }
- // 对于不支持的类型或解码失败,返回 nil
- return nil
- }
- func ForceInt(a interface{}) int {
- num, _ := a.(int)
- return num
- }
- func ForceBoolean(a interface{}) bool {
- b, _ := a.(bool)
- return b
- }
- func ForceInt64(a interface{}) int64 {
- b, _ := a.(int64)
- return b
- }
- func ForceUint64(a interface{}) uint64 {
- b, _ := a.(uint64)
- return b
- }
- // ForceInt32 attempts to assert that a is of type int32.
- func ForceInt32(a interface{}) int32 {
- b, _ := a.(int32)
- return b
- }
- // ForceUInt32 attempts to assert that a is of type uint32.
- func ForceUInt32(a interface{}) uint32 {
- b, _ := a.(uint32)
- return b
- }
- // ForceInt16 attempts to assert that a is of type int16.
- func ForceInt16(a interface{}) int16 {
- b, _ := a.(int16)
- return b
- }
- // ForceUInt16 attempts to assert that a is of type uint16.
- func ForceUInt16(a interface{}) uint16 {
- b, _ := a.(uint16)
- return b
- }
- // ForceInt8 attempts to assert that a is of type int8.
- func ForceInt8(a interface{}) int8 {
- b, _ := a.(int8)
- return b
- }
- // ForceUInt8 attempts to assert that a is of type uint8.
- func ForceUInt8(a interface{}) uint8 {
- b, _ := a.(uint8)
- return b
- }
- // ForceFloat32 attempts to assert that a is of type float32.
- func ForceFloat32(a interface{}) float32 {
- b, _ := a.(float32)
- return b
- }
- // ForceFloat64 attempts to assert that a is of type float64.
- func ForceFloat64(a interface{}) float64 {
- b, _ := a.(float64)
- return b
- }
|