123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- package httpclient
- import (
- "bytes"
- "context"
- "crypto/tls"
- "io/ioutil"
- "net/http"
- "net/url"
- "time"
- "git.bvbej.com/bvbej/base-golang/pkg/trace"
- "github.com/pkg/errors"
- "go.uber.org/zap"
- )
- const (
- // _StatusReadRespErr read resp body err, should re-call doHTTP again.
- _StatusReadRespErr = -204
- // _StatusDoReqErr do req err, should re-call doHTTP again.
- _StatusDoReqErr = -500
- )
- var defaultClient = &http.Client{
- Transport: &http.Transport{
- DisableKeepAlives: true,
- DisableCompression: true,
- TLSClientConfig: &tls.Config{
- InsecureSkipVerify: true,
- },
- MaxIdleConns: 100,
- MaxConnsPerHost: 100,
- MaxIdleConnsPerHost: 100,
- },
- }
- func doHTTP(ctx context.Context, method, url string, payload []byte, opt *option) ([]byte, int, error) {
- ts := time.Now()
- if mock := opt.mock; mock != nil {
- if opt.dialog != nil {
- opt.dialog.AppendResponse(&trace.Response{
- HttpCode: http.StatusOK,
- HttpCodeMsg: http.StatusText(http.StatusOK),
- Body: string(mock()),
- CostSeconds: time.Since(ts).Seconds(),
- })
- }
- return mock(), http.StatusOK, nil
- }
- req, err := http.NewRequestWithContext(ctx, method, url, bytes.NewReader(payload))
- if err != nil {
- return nil, -1, errors.Wrapf(err, "new request [%s %s] err", method, url)
- }
- for key, value := range opt.header {
- req.Header.Set(key, value[0])
- }
- if opt.basicAuth.enabled {
- req.SetBasicAuth(opt.basicAuth.username, opt.basicAuth.password)
- }
- //tcp-too-many-open-files-debug
- req.Close = true
- resp, err := defaultClient.Do(req)
- if err != nil {
- err = errors.Wrapf(err, "do request [%s %s] err", method, url)
- if opt.dialog != nil {
- opt.dialog.AppendResponse(&trace.Response{
- Body: err.Error(),
- CostSeconds: time.Since(ts).Seconds(),
- })
- }
- if opt.logger != nil {
- opt.logger.Warn("doHTTP got err", zap.Error(err))
- }
- return nil, _StatusDoReqErr, err
- }
- defer resp.Body.Close()
- body, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- err = errors.Wrapf(err, "read resp body from [%s %s] err", method, url)
- if opt.dialog != nil {
- opt.dialog.AppendResponse(&trace.Response{
- Body: err.Error(),
- CostSeconds: time.Since(ts).Seconds(),
- })
- }
- if opt.logger != nil {
- opt.logger.Warn("doHTTP got err", zap.Error(err))
- }
- return nil, _StatusReadRespErr, err
- }
- defer func() {
- if opt.dialog != nil {
- opt.dialog.AppendResponse(&trace.Response{
- Header: resp.Header,
- HttpCode: resp.StatusCode,
- HttpCodeMsg: resp.Status,
- Body: string(body), // unsafe
- CostSeconds: time.Since(ts).Seconds(),
- })
- }
- }()
- if resp.StatusCode != http.StatusOK {
- return nil, resp.StatusCode, newReplyErr(
- resp.StatusCode,
- body,
- errors.Errorf("do [%s %s] return code: %d message: %s", method, url, resp.StatusCode, string(body)),
- )
- }
- return body, http.StatusOK, nil
- }
- // addFormValuesIntoURL append url.Values into url string
- func addFormValuesIntoURL(rawURL string, form url.Values) (string, error) {
- if rawURL == "" {
- return "", errors.New("rawURL required")
- }
- if len(form) == 0 {
- return "", errors.New("form required")
- }
- target, err := url.Parse(rawURL)
- if err != nil {
- return "", errors.Wrapf(err, "parse rawURL `%s` err", rawURL)
- }
- urlValues := target.Query()
- for key, values := range form {
- for _, value := range values {
- urlValues.Add(key, value)
- }
- }
- target.RawQuery = urlValues.Encode()
- return target.String(), nil
- }
|