package httputil import ( "91porn-server/common/log" "91porn-server/middleware/requestid" "context" "encoding/json" "errors" "fmt" "github.com/ddliu/go-httpclient" "io" "net/http" "strconv" ) // HTTPClient htttp客户端 type HTTPClient struct { client *httpclient.HttpClient } func NewHTTPClient(client *httpclient.HttpClient) *HTTPClient { return &HTTPClient{client} } type httpClientPool struct { cliPool map[string]*httpclient.HttpClient poolSize int offset int } // HTTPResponse http响应 type HTTPResponse struct { StatusCode int `json:"statusCode"` Body *httpclient.Response `json:"boby"` } var httpCliPool = httpClientPool{cliPool: map[string]*httpclient.HttpClient{}, poolSize: 50} func New() *HTTPClient { h := httpclient.NewHttpClient().WithOptions(httpclient.Map{ httpclient.OPT_CONNECTTIMEOUT: 5, httpclient.OPT_TIMEOUT: 10, }) return &HTTPClient{client: h} } func NewCtx(ctx context.Context) *HTTPClient { reqID, _ := ctx.Value(requestid.ContextKey).(string) h := httpclient.NewHttpClient().WithOptions(httpclient.Map{ httpclient.OPT_CONNECTTIMEOUT: 5, httpclient.OPT_TIMEOUT: 10, httpclient.OPT_CONTEXT: ctx, }).WithHeader(requestid.HeaderKey, reqID) return &HTTPClient{client: h.Begin()} } // GetHTTPClient 获取httpclient func Client() *HTTPClient { cliID := "CLIENT-ID-" if len(httpCliPool.cliPool) < httpCliPool.poolSize { h := httpclient.NewHttpClient().Defaults(httpclient.Map{ httpclient.OPT_CONNECTTIMEOUT: 5, }) cliID = cliID + strconv.FormatInt(int64(len(httpCliPool.cliPool)+1), 10) httpCliPool.cliPool[cliID] = h httpCliPool.offset = httpCliPool.poolSize return &HTTPClient{client: h} } if httpCliPool.offset == httpCliPool.poolSize+1 { httpCliPool.offset = 1 } cliID = cliID + strconv.FormatInt(int64(httpCliPool.offset), 10) httpCliPool.offset = httpCliPool.offset + 1 h, ok := httpCliPool.cliPool[cliID] if !ok || h == nil { h = httpclient.NewHttpClient().WithOption(httpclient.OPT_CONNECTTIMEOUT, 10) httpCliPool.cliPool[cliID] = h return &HTTPClient{client: h} } return &HTTPClient{client: h} } func jsonUnmarshalResp(bind interface{}, resp *HTTPResponse) error { if bind == nil { //传入空表示只执行 defer return nil } b, err := resp.Body.ReadAll() if err != nil { log.Error("httputil jsonUnmarshalResp readall err", log.E(err)) return err } err = json.Unmarshal(b, bind) if err != nil { log.Error("httputil jsonUnmarshalResp Unmarshal err", log.E(err)) } return err } // Get Get方法 func (h *HTTPClient) Get(url string, headers map[string]string, params ...interface{}) (*HTTPResponse, error) { h.client.WithHeaders(headers) response, err := h.client.Get(url, params...) if err != nil { log.Error(fmt.Sprintf("http client Get method failed %+v:", err)) return nil, err } return &HTTPResponse{StatusCode: response.StatusCode, Body: response}, nil } func (h *HTTPClient) PGet(p *ProxyCfg, url string, headers map[string]string, params ...interface{}) (*HTTPResponse, error) { purl, pheaders, err := p.Build(url, headers) if err != nil { return nil, err } return h.Get(purl, pheaders, params) } // Get GetBytes func (h *HTTPClient) GetBytes(url string, headers map[string]string, params ...interface{}) ([]byte, error) { h.client.WithHeaders(headers) response, err := h.client.Get(url, params...) if err != nil { log.Error(fmt.Sprintf("http client Get method failed %+v:", err)) return nil, err } if response.StatusCode != http.StatusOK { log.Error("http client Get method status code not ok", log.Any("url", url), log.Any("headers", headers), log.Any("params", params), log.Any("res", response)) return nil, errors.New("http response satus code not ok statusCoe:" + response.Status) } data, err := response.ReadAll() if err != nil { log.ZapLog.Error("resp readAll errror", log.Any("Error", err)) return data, err } return data, nil } // GetWithJResp 结果json.Unmarshal到bind中 func (h *HTTPClient) GetWithJResp(bind interface{}, url string, headers map[string]string, params ...interface{}) (int, error) { resp, err := h.Get(url, headers, params...) if err != nil { return 0, err } defer func() { _ = resp.Body.Body.Close() }() if resp.StatusCode == http.StatusOK { err = jsonUnmarshalResp(bind, resp) } return resp.StatusCode, err } func (h *HTTPClient) PGetWithJResp(p *ProxyCfg, bind interface{}, url string, headers map[string]string, params ...interface{}) (int, error) { purl, pheaders, err := p.Build(url, headers) if err != nil { return 0, err } return h.GetWithJResp(bind, purl, pheaders, params...) } // Post Post方法 func (h *HTTPClient) Post(url string, headers map[string]string, params interface{}) (*HTTPResponse, error) { h.client.WithHeaders(headers) response, err := h.client.Post(url, params) if err != nil { log.Error(fmt.Sprintf("http client Post method failed %+v:", err)) return nil, err } return &HTTPResponse{StatusCode: response.StatusCode, Body: response}, nil } func (h *HTTPClient) PPost(p *ProxyCfg, bind interface{}, url string, headers map[string]string, params interface{}) (*HTTPResponse, error) { purl, pheaders, err := p.Build(url, headers) if err != nil { return nil, err } return h.Post(purl, pheaders, params) } // POSTJson PostJson方法 func (h *HTTPClient) POSTJson(url string, headers map[string]string, data interface{}) (*HTTPResponse, error) { h.client.WithHeaders(headers) response, err := h.client.PostJson(url, data) if err != nil { log.Error(fmt.Sprintf("http client POSTJson method failed %+v:", err)) return nil, err } return &HTTPResponse{StatusCode: response.StatusCode, Body: response}, nil } // POSTJson PostJson方法, bind json.Unmarshal func (h *HTTPClient) POSTJsonWithJResp(bind interface{}, url string, headers map[string]string, data interface{}) (int, error) { resp, err := h.POSTJson(url, headers, data) if err != nil { log.Error("POSTJsonWithResp err", log.E(err)) return 0, err } defer resp.Body.Body.Close() if resp.StatusCode == http.StatusOK { err = jsonUnmarshalResp(bind, resp) } return resp.StatusCode, err } func (h *HTTPClient) PPOSTJsonWithJResp(p *ProxyCfg, bind interface{}, url string, headers map[string]string, data interface{}) (int, error) { if p == nil { return 0, fmt.Errorf("ProxyCfg is nil") } purl, pheaders, err := p.Build(url, headers) if err != nil { return 0, err } return h.POSTJsonWithJResp(bind, purl, pheaders, data) } // PostMultipart PostMultipart 上传文件 func (h *HTTPClient) PostMultipart(url string, headers map[string]string, params interface{}) (*HTTPResponse, error) { h.client.WithHeaders(headers) response, err := h.client.PostMultipart(url, params) if err != nil { log.Error(fmt.Sprintf("http client PostMultipart method failed %+v:", err)) return nil, err } return &HTTPResponse{StatusCode: response.StatusCode, Body: response}, nil } // Put 流式传输 可用于上传文件 func (h *HTTPClient) Put(url string, headers map[string]string, body io.Reader) (*HTTPResponse, error) { h.client.WithHeaders(headers) response, err := h.client.Put(url, body) if err != nil { log.Error(fmt.Sprintf("http client PostMultipart method failed %+v:", err)) return nil, err } return &HTTPResponse{StatusCode: response.StatusCode, Body: response}, nil } // PutJSON PutJSON方法 func (h *HTTPClient) PutJSON(url string, headers map[string]string, data interface{}) (*HTTPResponse, error) { h.client.WithHeaders(headers) response, err := h.client.PutJson(url, data) if err != nil { log.Error(fmt.Sprintf("http client PostMultipart method failed %+v:", err)) return nil, err } return &HTTPResponse{StatusCode: response.StatusCode, Body: response}, nil } // Header Header func (h *HTTPClient) Header(url string) (*HTTPResponse, error) { response, err := h.client.Head(url) if err != nil { log.Error(fmt.Sprintf("http client PostMultipart method failed %+v:", err)) return nil, err } return &HTTPResponse{StatusCode: response.StatusCode, Body: response}, nil } // POSTWithJResp 结果json.Unmarshal到bind中 用于上传文件, content-type :"application/x-www-form-urlencoded" 如参数以@开头 则为上传文件 func (h *HTTPClient) POSTWithJResp(bind interface{}, url string, headers map[string]string, params interface{}) (int, error) { resp, err := h.Post(url, headers, params) if err != nil { return 0, err } defer resp.Body.Body.Close() if resp.StatusCode == http.StatusOK { err = jsonUnmarshalResp(bind, resp) } return resp.StatusCode, err } // PPOSTWithJResp 结果json.Unmarshal到bind中 用于上传文件, content-type :"application/x-www-form-urlencoded" 如参数以@开头 则为上传文件 func (h *HTTPClient) PPOSTWithJResp(p *ProxyCfg, bind interface{}, url string, headers map[string]string, params interface{}) (int, error) { resp, err := h.PPost(p, bind, url, headers, params) if err != nil { return 0, err } defer resp.Body.Body.Close() if resp.StatusCode == http.StatusOK { err = jsonUnmarshalResp(bind, resp) } return resp.StatusCode, err }