Files
2026-11-07 10:47:32 +08:00

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")
}
}