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