diff --git a/go.mod b/go.mod index 62e2d73..f249edd 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.20 require ( github.com/BurntSushi/toml v1.3.2 - github.com/aliyun/aliyun-log-go-sdk v0.1.59 + github.com/aliyun/aliyun-log-go-sdk v0.1.60 github.com/apache/rocketmq-client-go/v2 v2.1.2 github.com/bytedance/sonic v1.10.1 github.com/eclipse/paho.mqtt.golang v1.4.3 @@ -12,11 +12,11 @@ require ( github.com/google/uuid v1.3.1 github.com/gorilla/websocket v1.5.0 github.com/opentracing/opentracing-go v1.2.0 - github.com/redis/go-redis/v9 v9.1.0 + github.com/redis/go-redis/v9 v9.2.0 github.com/uber/jaeger-client-go v2.30.0+incompatible - go.uber.org/zap v1.25.0 - google.golang.org/genproto/googleapis/rpc v0.0.0-20230911183012-2d3300fd4832 - google.golang.org/grpc v1.58.0 + go.uber.org/zap v1.26.0 + google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 + google.golang.org/grpc v1.58.2 google.golang.org/protobuf v1.31.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/yaml.v3 v3.0.1 @@ -38,7 +38,7 @@ require ( github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.15.3 // indirect + github.com/go-playground/validator/v10 v10.15.4 // indirect github.com/go-sql-driver/mysql v1.7.1 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect @@ -57,7 +57,7 @@ require ( github.com/pierrec/lz4 v2.6.1+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect github.com/sirupsen/logrus v1.9.3 // indirect - github.com/tidwall/gjson v1.16.0 // indirect + github.com/tidwall/gjson v1.17.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect diff --git a/go.sum b/go.sum index 44b1ced..b006cc0 100644 --- a/go.sum +++ b/go.sum @@ -43,8 +43,8 @@ github.com/alibabacloud-go/tea-utils/v2 v2.0.1 h1:K6kwgo+UiYx+/kr6CO0PN5ACZDzE3n github.com/alibabacloud-go/tea-utils/v2 v2.0.1/go.mod h1:U5MTY10WwlquGPS34DOeomUGBB0gXbLueiq5Trwu0C4= github.com/alibabacloud-go/tea-xml v1.1.2 h1:oLxa7JUXm2EDFzMg+7oRsYc+kutgCVwm+bZlhhmvW5M= github.com/alibabacloud-go/tea-xml v1.1.2/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8= -github.com/aliyun/aliyun-log-go-sdk v0.1.59 h1:Dz8y41zfkoCM3PtW8zv7kYOhosL5gS3i1TDVBcmjnaY= -github.com/aliyun/aliyun-log-go-sdk v0.1.59/go.mod h1:FSKcIjukUy+LeUKhRk13PCO+9gPMTfGsYhFBHQbDqmM= +github.com/aliyun/aliyun-log-go-sdk v0.1.60 h1:Ob4Md0Zy2pAGLZIS7NhjqOd4BkLkhy1G/PqzFtBCmJY= +github.com/aliyun/aliyun-log-go-sdk v0.1.60/go.mod h1:FSKcIjukUy+LeUKhRk13PCO+9gPMTfGsYhFBHQbDqmM= github.com/aliyun/credentials-go v1.1.2 h1:qU1vwGIBb3UJ8BwunHDRFtAhS6jnQLnde/yk0+Ih2GY= github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw= github.com/apache/rocketmq-client-go/v2 v2.1.2 h1:yt73olKe5N6894Dbm+ojRf/JPiP0cxfDNNffKwhpJVg= @@ -58,13 +58,12 @@ github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6l github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= -github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bsm/ginkgo/v2 v2.9.5 h1:rtVBYPs3+TC5iLUVOis1B9tjLTup7Cj5IfzosKtvTJ0= -github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= github.com/bytedance/sonic v1.10.1 h1:7a1wuFXL1cMy7a3f7/VFcEtriuXQnUBhtoVfOZiaysc= @@ -150,8 +149,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.15.3 h1:S+sSpunYjNPDuXkWbK+x+bA7iXiW296KG4dL3X7xUZo= -github.com/go-playground/validator/v10 v10.15.3/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-playground/validator/v10 v10.15.4 h1:zMXza4EpOdooxPel5xDqXEdXG5r+WggpvnAKMsalBjs= +github.com/go-playground/validator/v10 v10.15.4/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= @@ -374,8 +373,8 @@ github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/redis/go-redis/v9 v9.1.0 h1:137FnGdk+EQdCbye1FW+qOEcY5S+SpY9T0NiuqvtfMY= -github.com/redis/go-redis/v9 v9.1.0/go.mod h1:urWj3He21Dj5k4TK1y59xH8Uj6ATueP8AH1cY3lZl4c= +github.com/redis/go-redis/v9 v9.2.0 h1:zwMdX0A4eVzse46YN18QhuDiM4uf3JmkOB4VZrdt5uI= +github.com/redis/go-redis/v9 v9.2.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -418,8 +417,8 @@ github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/tidwall/gjson v1.13.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/gjson v1.16.0 h1:SyXa+dsSPpUlcwEDuKuEBJEz5vzTvOea+9rjyYodQFg= -github.com/tidwall/gjson v1.16.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM= +github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= @@ -461,8 +460,8 @@ go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN8 go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c= -go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.5.0 h1:jpGode6huXQxcskEIpOCvrU+tzo81b6+oFLUYXWtH/Y= golang.org/x/arch v0.5.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= @@ -602,8 +601,8 @@ google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230911183012-2d3300fd4832 h1:o4LtQxebKIJ4vkzyhtD2rfUNZ20Zf0ik5YVP5E7G7VE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230911183012-2d3300fd4832/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 h1:N3bU/SQDCDyD6R528GJ/PwW9KjYcJA3dgyH+MovAkIM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:KSqppvjFjtoCI+KGd4PELB0qLNxdJHRGqRI09mB6pQA= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -614,8 +613,8 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.58.0 h1:32JY8YpPMSR45K+c3o6b8VL73V+rR8k+DeMIr4vRH8o= -google.golang.org/grpc v1.58.0/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I= +google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/xhttp/client.go b/xhttp/client.go index c8daa89..8bdad21 100644 --- a/xhttp/client.go +++ b/xhttp/client.go @@ -1,40 +1,17 @@ package xhttp import ( - "bytes" - "context" "crypto/tls" - "encoding/xml" - "errors" - "fmt" - "io" - "mime/multipart" "net" "net/http" - "net/url" - "sort" - "strings" "time" - - "github.com/bytedance/sonic" - "github.com/go-pay/gopher/bm" - "github.com/go-pay/gopher/util" ) type Client struct { - HttpClient *http.Client - Header http.Header - Host string - bodySize int // body size limit(MB), default is 10MB - url string - method string - requestType RequestType - FormString string - ContentType string - unmarshalType string - multipartBodyMap map[string]any - jsonByte []byte - err error + HttpClient *http.Client + req *Request + bodySize int // body size limit(MB), default is 10MB + err error } // NewClient , default tls.Config{InsecureSkipVerify: true} @@ -58,10 +35,7 @@ func NewClient() (client *Client) { ForceAttemptHTTP2: true, }, }, - Header: make(http.Header), - bodySize: 10, // default is 10MB - requestType: TypeJSON, - unmarshalType: string(TypeJSON), + bodySize: 10, // default is 10MB } return client } @@ -81,290 +55,133 @@ func (c *Client) SetTimeout(timeout time.Duration) (client *Client) { return c } -func (c *Client) SetHost(host string) (client *Client) { - c.Host = host - return c -} - // set body size (MB), default is 10MB func (c *Client) SetBodySize(sizeMB int) (client *Client) { c.bodySize = sizeMB return c } -func (c *Client) Type(typeStr RequestType) (client *Client) { - if _, ok := types[typeStr]; ok { - c.requestType = typeStr - } - return c -} - -func (c *Client) Get(url string) (client *Client) { - c.method = GET - c.url = url - return c -} - -func (c *Client) Post(url string) (client *Client) { - c.method = POST - c.url = url - return c -} - -func (c *Client) Put(url string) (client *Client) { - c.method = PUT - c.url = url - return c -} - -func (c *Client) Delete(url string) (client *Client) { - c.method = DELETE - c.url = url - return c -} - -func (c *Client) Patch(url string) (client *Client) { - c.method = PATCH - c.url = url - return c -} - -func (c *Client) SendStruct(v any) (client *Client) { - if v == nil { - return c - } - bs, err := sonic.Marshal(v) - if err != nil { - c.err = fmt.Errorf("json.Marshal(%+v):%w", v, err) - return c - } - switch c.requestType { - case TypeJSON: - c.jsonByte = bs - case TypeXML, TypeUrlencoded, TypeForm, TypeFormData: - body := make(map[string]any) - if err = sonic.Unmarshal(bs, &body); err != nil { - c.err = fmt.Errorf("json.Unmarshal(%s, %+v):%w", string(bs), body, err) - return c - } - c.FormString = FormatURLParam(body) - } - return c -} - -func (c *Client) SendBodyMap(bm map[string]any) (client *Client) { - if bm == nil { - return c - } - switch c.requestType { - case TypeJSON: - bs, err := sonic.Marshal(bm) - if err != nil { - c.err = fmt.Errorf("json.Marshal(%+v):%w", bm, err) - return c - } - c.jsonByte = bs - case TypeXML, TypeUrlencoded, TypeForm, TypeFormData: - c.FormString = FormatURLParam(bm) - } - return c -} - -func (c *Client) SendMultipartBodyMap(bm map[string]any) (client *Client) { - if bm == nil { - return c - } - switch c.requestType { - case TypeJSON: - bs, err := sonic.Marshal(bm) - if err != nil { - c.err = fmt.Errorf("json.Marshal(%+v):%w", bm, err) - return c - } - c.jsonByte = bs - case TypeXML, TypeUrlencoded, TypeForm, TypeFormData: - c.FormString = FormatURLParam(bm) - case TypeMultipartFormData: - c.multipartBodyMap = bm - } - return c -} - -// encodeStr: url.Values.Encode() or jsonBody -func (c *Client) SendString(encodeStr string) (client *Client) { - switch c.requestType { - case TypeJSON: - c.jsonByte = []byte(encodeStr) - case TypeXML, TypeUrlencoded, TypeForm, TypeFormData: - c.FormString = encodeStr - } - return c -} - -func (c *Client) EndStruct(ctx context.Context, v any) (res *http.Response, err error) { - res, bs, err := c.EndBytes(ctx) - if err != nil { - return nil, err - } - if res.StatusCode != http.StatusOK { - return res, fmt.Errorf("StatusCode(%d) != 200", res.StatusCode) - } - - switch c.unmarshalType { - case string(TypeJSON): - err = sonic.Unmarshal(bs, &v) - if err != nil { - return nil, fmt.Errorf("json.Unmarshal(%s, %+v):%w", string(bs), v, err) - } - return res, nil - case string(TypeXML): - err = xml.Unmarshal(bs, &v) - if err != nil { - return nil, fmt.Errorf("xml.Unmarshal(%s, %+v):%w", string(bs), v, err) +func (c *Client) Req(typeStr ...RequestType) *Request { + tp := TypeJSON + if len(typeStr) == 1 { + tpp := typeStr[0] + if _, ok := types[tpp]; ok { + tp = tpp } - return res, nil - default: - return nil, errors.New("unmarshalType Type Wrong") - } -} - -func (c *Client) EndBytes(ctx context.Context) (res *http.Response, bs []byte, err error) { - if c.err != nil { - return nil, nil, c.err } - var ( - body io.Reader - bw *multipart.Writer - ) - // multipart-form-data - if c.requestType == TypeMultipartFormData { - body = &bytes.Buffer{} - bw = multipart.NewWriter(body.(io.Writer)) - } - - reqFunc := func() (err error) { - switch c.method { - case GET: - switch c.requestType { - case TypeJSON: - c.ContentType = types[TypeJSON] - case TypeForm, TypeFormData, TypeUrlencoded: - c.ContentType = types[TypeForm] - case TypeMultipartFormData: - c.ContentType = bw.FormDataContentType() - case TypeXML: - c.ContentType = types[TypeXML] - c.unmarshalType = string(TypeXML) - default: - return errors.New("Request type Error ") - } - case POST, PUT, DELETE, PATCH: - switch c.requestType { - case TypeJSON: - if c.jsonByte != nil { - body = strings.NewReader(string(c.jsonByte)) - } - c.ContentType = types[TypeJSON] - case TypeForm, TypeFormData, TypeUrlencoded: - body = strings.NewReader(c.FormString) - c.ContentType = types[TypeForm] - case TypeMultipartFormData: - for k, v := range c.multipartBodyMap { - // file 参数 - if file, ok := v.(*bm.File); ok { - fw, e := bw.CreateFormFile(k, file.Name) - if e != nil { - return e - } - _, _ = fw.Write(file.Content) - continue - } - // text 参数 - vs, ok2 := v.(string) - if ok2 { - _ = bw.WriteField(k, vs) - } else if ss := util.ConvertToString(v); ss != "" { - _ = bw.WriteField(k, ss) - } - } - _ = bw.Close() - c.ContentType = bw.FormDataContentType() - case TypeXML: - body = strings.NewReader(c.FormString) - c.ContentType = types[TypeXML] - c.unmarshalType = string(TypeXML) - default: - return errors.New("Request type Error ") - } - default: - return errors.New("Only support GET and POST and PUT and DELETE ") - } - - req, err := http.NewRequestWithContext(ctx, c.method, c.url, body) - if err != nil { - return err - } - req.Header = c.Header - req.Header.Set("Content-Type", c.ContentType) - if c.Host != "" { - req.Host = c.Host - } - res, err = c.HttpClient.Do(req) - if err != nil { - return err - } - defer res.Body.Close() - bs, err = io.ReadAll(io.LimitReader(res.Body, int64(c.bodySize<<20))) // default 10MB change the size you want - if err != nil { - return err - } - return nil - } - - if err = reqFunc(); err != nil { - return nil, nil, err - } - return res, bs, nil -} - -func FormatURLParam(body map[string]any) (urlParam string) { - var ( - buf strings.Builder - keys []string - ) - for k := range body { - keys = append(keys, k) - } - sort.Strings(keys) - for _, k := range keys { - v, ok := body[k].(string) - if !ok { - v = convertToString(body[k]) - } - if v != "" { - buf.WriteString(url.QueryEscape(k)) - buf.WriteByte('=') - buf.WriteString(url.QueryEscape(v)) - buf.WriteByte('&') - } - } - if buf.Len() <= 0 { - return "" - } - return buf.String()[:buf.Len()-1] -} - -func convertToString(v any) (str string) { - if v == nil { - return "" - } - var ( - bs []byte - err error - ) - if bs, err = sonic.Marshal(v); err != nil { - return "" - } - str = string(bs) - return -} + r := &Request{ + client: c, + Header: make(http.Header), + requestType: tp, + unmarshalType: string(tp), + } + r.Header.Set("Content-Type", types[tp]) + return r +} + +//func (c *Client) EndStruct(ctx context.Context, r *Request, v any) (res *http.Response, err error) { +// res, bs, err := c.EndBytes(ctx, r) +// if err != nil { +// return nil, err +// } +// if res.StatusCode != http.StatusOK { +// return res, fmt.Errorf("StatusCode(%d) != 200", res.StatusCode) +// } +// +// switch r.unmarshalType { +// case string(TypeJSON): +// err = sonic.Unmarshal(bs, &v) +// if err != nil { +// return nil, fmt.Errorf("json.Unmarshal(%s, %+v):%w", string(bs), v, err) +// } +// return res, nil +// case string(TypeXML): +// err = xml.Unmarshal(bs, &v) +// if err != nil { +// return nil, fmt.Errorf("xml.Unmarshal(%s, %+v):%w", string(bs), v, err) +// } +// return res, nil +// default: +// return nil, errors.New("unmarshalType Type Wrong") +// } +//} +// +//func (c *Client) EndBytes(ctx context.Context, r *Request) (res *http.Response, bs []byte, err error) { +// if c.err != nil { +// return nil, nil, c.err +// } +// var ( +// body io.Reader +// bw *multipart.Writer +// ) +// // multipart-form-data +// if r.requestType == TypeMultipartFormData { +// body = &bytes.Buffer{} +// bw = multipart.NewWriter(body.(io.Writer)) +// } +// +// reqFunc := func() (err error) { +// switch r.method { +// case GET: +// // do nothing +// case POST, PUT, DELETE, PATCH: +// switch r.requestType { +// case TypeJSON: +// if r.jsonByte != nil { +// body = strings.NewReader(string(r.jsonByte)) +// } +// case TypeForm, TypeFormData, TypeUrlencoded: +// body = strings.NewReader(r.formString) +// case TypeMultipartFormData: +// for k, v := range r.multipartBodyMap { +// // file 参数 +// if file, ok := v.(*bm.File); ok { +// fw, e := bw.CreateFormFile(k, file.Name) +// if e != nil { +// return e +// } +// _, _ = fw.Write(file.Content) +// continue +// } +// // text 参数 +// vs, ok2 := v.(string) +// if ok2 { +// _ = bw.WriteField(k, vs) +// } else if ss := util.ConvertToString(v); ss != "" { +// _ = bw.WriteField(k, ss) +// } +// } +// _ = bw.Close() +// r.Header.Set("Content-Type", bw.FormDataContentType()) +// case TypeXML: +// body = strings.NewReader(r.formString) +// default: +// return errors.New("Request type Error ") +// } +// default: +// return errors.New("Only support GET and POST and PUT and DELETE ") +// } +// +// // request +// req, err := http.NewRequestWithContext(ctx, r.method, r.url, body) +// if err != nil { +// return err +// } +// req.Header = r.Header +// res, err = c.HttpClient.Do(req) +// if err != nil { +// return err +// } +// defer res.Body.Close() +// bs, err = io.ReadAll(io.LimitReader(res.Body, int64(c.bodySize<<20))) // default 10MB change the size you want +// if err != nil { +// return err +// } +// return nil +// } +// +// if err = reqFunc(); err != nil { +// return nil, nil, err +// } +// return res, bs, nil +//} diff --git a/xhttp/client_test.go b/xhttp/client_test.go index fed6ef9..3e20662 100644 --- a/xhttp/client_test.go +++ b/xhttp/client_test.go @@ -2,18 +2,17 @@ package xhttp import ( "context" - "io/ioutil" + "os" "testing" - "time" "github.com/go-pay/gopher/bm" "github.com/go-pay/gopher/xlog" ) type HttpGet struct { - Code int `json:"code"` - Message string `json:"message"` - Data any `json:"data,omitempty"` + Code int `json:"code"` + Message string `json:"message"` + Data any `json:"data,omitempty"` } var ctx = context.Background() @@ -21,9 +20,8 @@ var ctx = context.Background() func TestHttpGet(t *testing.T) { xlog.Level = xlog.DebugLevel client := NewClient() - client.Timeout = 10 * time.Second // test - _, bs, err := client.Get("http://www.baidu.com").EndBytes(ctx) + _, bs, err := client.Req().Get("http://www.baidu.com").EndBytes(ctx) if err != nil { xlog.Error(err) return @@ -41,7 +39,7 @@ func TestHttpGet(t *testing.T) { func TestHttpUploadFile(t *testing.T) { xlog.Level = xlog.DebugLevel - fileContent, err := ioutil.ReadFile("logo.png") + fileContent, err := os.ReadFile("logo.png") if err != nil { xlog.Error(err) return @@ -55,10 +53,9 @@ func TestHttpUploadFile(t *testing.T) { }).SetFormFile("image", &bm.File{Name: "logo.png", Content: fileContent}) client := NewClient() - client.Timeout = 10 * time.Second rsp := new(HttpGet) - _, err = client.Type(TypeMultipartFormData). + _, err = client.Req(TypeMultipartFormData). Post("http://localhost:2233/admin/v1/oss/uploadImage"). SendMultipartBodyMap(bmm). EndStruct(ctx, rsp) diff --git a/xhttp/request.go b/xhttp/request.go new file mode 100644 index 0000000..2462561 --- /dev/null +++ b/xhttp/request.go @@ -0,0 +1,265 @@ +package xhttp + +import ( + "bytes" + "context" + "encoding/xml" + "errors" + "fmt" + "io" + "mime/multipart" + "net/http" + "net/url" + "sort" + "strings" + + "github.com/bytedance/sonic" + "github.com/go-pay/gopher/bm" + "github.com/go-pay/gopher/util" +) + +type Request struct { + client *Client + Header http.Header + formString string + jsonByte []byte + url string + method string + requestType RequestType + unmarshalType string + multipartBodyMap map[string]any + err error +} + +func (r *Request) Get(url string) *Request { + r.method = GET + r.url = url + return r +} + +func (r *Request) Post(url string) *Request { + r.method = POST + r.url = url + return r +} + +func (r *Request) Put(url string) *Request { + r.method = PUT + r.url = url + return r +} + +func (r *Request) Delete(url string) *Request { + r.method = DELETE + r.url = url + return r +} + +func (r *Request) Patch(url string) *Request { + r.method = PATCH + r.url = url + return r +} + +// ===================================================================================================================== + +func (r *Request) SendStruct(v any) (c *Request) { + if v == nil { + return r + } + bs, err := sonic.Marshal(v) + if err != nil { + r.err = fmt.Errorf("json.Marshal(%+v):%w", v, err) + return r + } + switch r.requestType { + case TypeJSON: + r.jsonByte = bs + case TypeXML, TypeUrlencoded, TypeForm, TypeFormData: + body := make(map[string]any) + if err = sonic.Unmarshal(bs, &body); err != nil { + r.err = fmt.Errorf("json.Unmarshal(%s, %+v):%w", string(bs), body, err) + return r + } + r.formString = FormatURLParam(body) + } + return r +} + +func (r *Request) SendBodyMap(bm map[string]any) (client *Request) { + if bm == nil { + return r + } + switch r.requestType { + case TypeJSON: + bs, err := sonic.Marshal(bm) + if err != nil { + r.err = fmt.Errorf("json.Marshal(%+v):%w", bm, err) + return r + } + r.jsonByte = bs + case TypeXML, TypeUrlencoded, TypeForm, TypeFormData: + r.formString = FormatURLParam(bm) + } + return r +} + +func (r *Request) SendMultipartBodyMap(bm map[string]any) (client *Request) { + if bm == nil { + return r + } + switch r.requestType { + case TypeJSON: + bs, err := sonic.Marshal(bm) + if err != nil { + r.err = fmt.Errorf("json.Marshal(%+v):%w", bm, err) + return r + } + r.jsonByte = bs + case TypeXML, TypeUrlencoded, TypeForm, TypeFormData: + r.formString = FormatURLParam(bm) + case TypeMultipartFormData: + r.multipartBodyMap = bm + } + return r +} + +// encodeStr: url.Values.Encode() or jsonBody +func (r *Request) SendString(encodeStr string) (client *Request) { + switch r.requestType { + case TypeJSON: + r.jsonByte = []byte(encodeStr) + case TypeXML, TypeUrlencoded, TypeForm, TypeFormData: + r.formString = encodeStr + } + return r +} + +// ===================================================================================================================== + +func (r *Request) EndBytes(ctx context.Context) (res *http.Response, bs []byte, err error) { + if r.err != nil { + return nil, nil, r.err + } + var ( + body io.Reader + bw *multipart.Writer + ) + // multipart-form-data + if r.requestType == TypeMultipartFormData { + body = &bytes.Buffer{} + bw = multipart.NewWriter(body.(io.Writer)) + } + + switch r.method { + case GET: + // do nothing + case POST, PUT, DELETE, PATCH: + switch r.requestType { + case TypeJSON: + if r.jsonByte != nil { + body = strings.NewReader(string(r.jsonByte)) + } + case TypeForm, TypeFormData, TypeUrlencoded: + body = strings.NewReader(r.formString) + case TypeMultipartFormData: + for k, v := range r.multipartBodyMap { + // file 参数 + if file, ok := v.(*bm.File); ok { + fw, e := bw.CreateFormFile(k, file.Name) + if e != nil { + return nil, nil, e + } + _, _ = fw.Write(file.Content) + continue + } + // text 参数 + vs, ok2 := v.(string) + if ok2 { + _ = bw.WriteField(k, vs) + } else if ss := util.ConvertToString(v); ss != "" { + _ = bw.WriteField(k, ss) + } + } + _ = bw.Close() + r.Header.Set("Content-Type", bw.FormDataContentType()) + case TypeXML: + body = strings.NewReader(r.formString) + default: + return nil, nil, errors.New("Request type Error ") + } + default: + return nil, nil, errors.New("Only support GET and POST and PUT and DELETE ") + } + + // request + req, err := http.NewRequestWithContext(ctx, r.method, r.url, body) + if err != nil { + return nil, nil, err + } + req.Header = r.Header + res, err = r.client.HttpClient.Do(req) + if err != nil { + return nil, nil, err + } + defer res.Body.Close() + bs, err = io.ReadAll(io.LimitReader(res.Body, int64(r.client.bodySize<<20))) // default 10MB change the size you want + if err != nil { + return nil, nil, err + } + return res, bs, nil +} + +func (r *Request) EndStruct(ctx context.Context, v any) (res *http.Response, err error) { + res, bs, err := r.EndBytes(ctx) + if err != nil { + return nil, err + } + if res.StatusCode != http.StatusOK { + return res, fmt.Errorf("StatusCode(%d) != 200", res.StatusCode) + } + + switch r.unmarshalType { + case string(TypeJSON): + err = sonic.Unmarshal(bs, &v) + if err != nil { + return nil, fmt.Errorf("json.Unmarshal(%s, %+v):%w", string(bs), v, err) + } + return res, nil + case string(TypeXML): + err = xml.Unmarshal(bs, &v) + if err != nil { + return nil, fmt.Errorf("xml.Unmarshal(%s, %+v):%w", string(bs), v, err) + } + return res, nil + default: + return nil, errors.New("unmarshalType Type Wrong") + } +} + +func FormatURLParam(body map[string]any) (urlParam string) { + var ( + buf strings.Builder + keys []string + ) + for k := range body { + keys = append(keys, k) + } + sort.Strings(keys) + for _, k := range keys { + v, ok := body[k].(string) + if !ok { + v = util.ConvertToString(body[k]) + } + if v != "" { + buf.WriteString(url.QueryEscape(k)) + buf.WriteByte('=') + buf.WriteString(url.QueryEscape(v)) + buf.WriteByte('&') + } + } + if buf.Len() <= 0 { + return "" + } + return buf.String()[:buf.Len()-1] +}