Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
284 lines
7.2 KiB
Go
284 lines
7.2 KiB
Go
package utils
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
var once sync.Once
|
|
var _client *http.Client
|
|
|
|
func GetHttpClient() *http.Client {
|
|
once.Do(func() {
|
|
_client = &http.Client{
|
|
Timeout: time.Minute * 1, //设置超时时间
|
|
Transport: &http.Transport{
|
|
Proxy: http.ProxyFromEnvironment,
|
|
DialContext: (&net.Dialer{
|
|
Timeout: 30 * time.Second,
|
|
KeepAlive: 30 * time.Second,
|
|
}).DialContext,
|
|
TLSHandshakeTimeout: 10 * time.Second, //限制 TLS握手的时间
|
|
ResponseHeaderTimeout: 60 * time.Second, //限制读取response header的时间,默认 timeout + 5*time.Second
|
|
ExpectContinueTimeout: 1 * time.Second, //限制client在发送包含 Expect: 100-continue的header到收到继续发送body的response之间的时间等待。
|
|
MaxIdleConns: 100, //所有host的连接池最大连接数量,默认无穷大
|
|
MaxIdleConnsPerHost: 300, //每个host的连接池最大空闲连接数,默认2
|
|
MaxConnsPerHost: 100, //每个host的最大连接数量
|
|
IdleConnTimeout: 3 * time.Minute, //how long an idle connection is kept in the connection pool.
|
|
},
|
|
}
|
|
})
|
|
return _client
|
|
}
|
|
|
|
func getBytes(request interface{}) ([]byte, error) {
|
|
var body []byte
|
|
switch t := request.(type) {
|
|
case []byte:
|
|
body = t
|
|
case string:
|
|
body = []byte(t)
|
|
default:
|
|
var err error
|
|
body, err = json.Marshal(request)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return body, nil
|
|
}
|
|
|
|
type OnHttpResponse func(res *http.Response) error
|
|
|
|
func HttpRequest(req *http.Request, onResponse OnHttpResponse) error {
|
|
client := GetHttpClient()
|
|
res, err := client.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer res.Body.Close()
|
|
if onResponse != nil {
|
|
err = onResponse(res)
|
|
}
|
|
return err
|
|
}
|
|
|
|
func HttpMethod(url, method string, header http.Header, body io.Reader, onResponse OnHttpResponse) error {
|
|
req, err := http.NewRequest(method, url, body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if header != nil {
|
|
req.Header = header
|
|
}
|
|
return HttpRequest(req, onResponse)
|
|
}
|
|
|
|
func HttpGet(url string, header http.Header, body io.Reader, onResponse OnHttpResponse) error {
|
|
return HttpMethod(url, http.MethodGet, header, body, onResponse)
|
|
}
|
|
|
|
func HttpPost(url string, header http.Header, body io.Reader, onResponse OnHttpResponse) error {
|
|
return HttpMethod(url, http.MethodPost, header, body, onResponse)
|
|
}
|
|
|
|
func HttpDelete(url string, header http.Header, body io.Reader, onResponse OnHttpResponse) error {
|
|
return HttpMethod(url, http.MethodDelete, header, body, onResponse)
|
|
}
|
|
|
|
func HttpPostRequestJson(url string, request interface{}, resp interface{}) error {
|
|
header := make(http.Header)
|
|
header.Set("Content-Type", "application/json")
|
|
body, err := getBytes(request)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return HttpPost(url, header, bytes.NewReader(body), func(res *http.Response) error {
|
|
if res.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("http request (%s) error %s", url, res.Status)
|
|
}
|
|
content, err := io.ReadAll(res.Body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return json.Unmarshal(content, resp)
|
|
})
|
|
}
|
|
|
|
func POSTJson(url string, headers map[string]string, data interface{}) (*http.Response, error) {
|
|
header := make(http.Header)
|
|
for k, v := range headers {
|
|
header.Set(k, v)
|
|
}
|
|
body, err := getBytes(data)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
req, err := http.NewRequest("POST", url, bytes.NewReader(body))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
req.Header = header
|
|
client := GetHttpClient()
|
|
return client.Do(req)
|
|
}
|
|
|
|
func GetWithParam(url string, headers map[string]string, params ...interface{}) (*http.Response, error) {
|
|
header := make(http.Header)
|
|
for k, v := range headers {
|
|
header.Set(k, v)
|
|
}
|
|
for _, p := range params {
|
|
url = addParams(url, toUrlValues(p))
|
|
}
|
|
req, err := http.NewRequest("GET", url, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
req.Header = header
|
|
client := GetHttpClient()
|
|
return client.Do(req)
|
|
}
|
|
|
|
func GetWithJResp(bind interface{}, url string, headers map[string]string, params ...interface{}) (int, error) {
|
|
header := make(http.Header)
|
|
for k, v := range headers {
|
|
header.Set(k, v)
|
|
}
|
|
for _, p := range params {
|
|
url = addParams(url, toUrlValues(p))
|
|
}
|
|
code := http.StatusBadRequest
|
|
err := HttpGet(url, header, nil, func(res *http.Response) error {
|
|
code = res.StatusCode
|
|
if res.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("http request (%s) error %s", url, res.Status)
|
|
}
|
|
content, err := io.ReadAll(res.Body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return json.Unmarshal(content, bind)
|
|
})
|
|
return code, err
|
|
}
|
|
|
|
func POSTWithJResp(bind interface{}, url string, headers map[string]string, params interface{}) (int, error) {
|
|
header := make(http.Header)
|
|
for k, v := range headers {
|
|
header.Set(k, v)
|
|
}
|
|
body, err := getBytes(params)
|
|
if err != nil {
|
|
return http.StatusBadRequest, err
|
|
}
|
|
code := http.StatusBadRequest
|
|
err = HttpPost(url, header, bytes.NewReader(body), func(res *http.Response) error {
|
|
code = res.StatusCode
|
|
if res.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("http request (%s) error %s", url, res.Status)
|
|
}
|
|
content, err := io.ReadAll(res.Body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return json.Unmarshal(content, bind)
|
|
})
|
|
return code, err
|
|
}
|
|
|
|
func POSTJsonWithJResp(bind interface{}, url string, headers map[string]string, data interface{}) (int, error) {
|
|
header := make(http.Header)
|
|
for k, v := range headers {
|
|
header.Set(k, v)
|
|
}
|
|
header.Set("Content-Type", "application/json")
|
|
body, err := getBytes(data)
|
|
if err != nil {
|
|
return http.StatusBadRequest, err
|
|
}
|
|
code := http.StatusBadRequest
|
|
err = HttpPost(url, header, bytes.NewReader(body), func(res *http.Response) error {
|
|
code = res.StatusCode
|
|
if res.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("http request (%s) error %s", url, res.Status)
|
|
}
|
|
content, err := io.ReadAll(res.Body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return json.Unmarshal(content, bind)
|
|
})
|
|
return code, err
|
|
}
|
|
|
|
func DeleteWithJResp(bind interface{}, url string, headers map[string]string, params interface{}) (int, error) {
|
|
header := make(http.Header)
|
|
for k, v := range headers {
|
|
header.Set(k, v)
|
|
}
|
|
body, err := getBytes(params)
|
|
if err != nil {
|
|
return http.StatusBadRequest, err
|
|
}
|
|
code := http.StatusBadRequest
|
|
err = HttpDelete(url, header, bytes.NewReader(body), func(res *http.Response) error {
|
|
code = res.StatusCode
|
|
if res.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("http request (%s) error %s", url, res.Status)
|
|
}
|
|
content, err := io.ReadAll(res.Body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return json.Unmarshal(content, bind)
|
|
})
|
|
return code, err
|
|
}
|
|
|
|
// Add params to a url string.
|
|
func addParams(url_ string, params url.Values) string {
|
|
if len(params) == 0 {
|
|
return url_
|
|
}
|
|
|
|
if !strings.Contains(url_, "?") {
|
|
url_ += "?"
|
|
}
|
|
|
|
if strings.HasSuffix(url_, "?") || strings.HasSuffix(url_, "&") {
|
|
url_ += params.Encode()
|
|
} else {
|
|
url_ += "&" + params.Encode()
|
|
}
|
|
|
|
return url_
|
|
}
|
|
|
|
func toUrlValues(v interface{}) url.Values {
|
|
switch t := v.(type) {
|
|
case url.Values:
|
|
return t
|
|
case map[string][]string:
|
|
return url.Values(t)
|
|
case map[string]string:
|
|
rst := make(url.Values)
|
|
for k, v := range t {
|
|
rst.Add(k, v)
|
|
}
|
|
return rst
|
|
case nil:
|
|
return make(url.Values)
|
|
default:
|
|
panic("Invalid value")
|
|
}
|
|
}
|