gitee.com/h79/goutils@v1.22.10/common/http/http.go (about)

     1  package http
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"flag"
     7  	"fmt"
     8  	"gitee.com/h79/goutils/common/json"
     9  	"gitee.com/h79/goutils/common/logger"
    10  	"io"
    11  	"mime/multipart"
    12  	"net/http"
    13  	"net/http/httptrace"
    14  	"net/url"
    15  	"strconv"
    16  	"strings"
    17  	"sync/atomic"
    18  	"time"
    19  )
    20  
    21  const (
    22  	kTimeOut            = time.Second * 10
    23  	KContentType        = "Content-Type"
    24  	KContentDisposition = "Content-Disposition"
    25  	KContentLength      = "Content-Length"
    26  )
    27  
    28  type HeaderFunc func(h *http.Header)
    29  
    30  type RequestOption func(req *http.Request) (*http.Request, error)
    31  
    32  func Method(m string) RequestOption {
    33  	return func(req *http.Request) (*http.Request, error) {
    34  		req.Method = strings.ToUpper(m)
    35  		return req, nil
    36  	}
    37  }
    38  
    39  var (
    40  	HEAD   = Method("HEAD")
    41  	GET    = Method("GET")
    42  	POST   = Method("POST")
    43  	PUT    = Method("PUT")
    44  	DELETE = Method("DELETE")
    45  )
    46  
    47  func URL(u *url.URL) RequestOption {
    48  	return func(req *http.Request) (*http.Request, error) {
    49  		req.URL = u
    50  		return req, nil
    51  	}
    52  }
    53  
    54  func URLString(rawUrl string) RequestOption {
    55  	return func(req *http.Request) (*http.Request, error) {
    56  		u, err := url.Parse(rawUrl)
    57  		if err != nil {
    58  			return nil, err
    59  		}
    60  		req.URL = u
    61  		return req, nil
    62  	}
    63  }
    64  
    65  func Path(path string) RequestOption {
    66  	return func(req *http.Request) (*http.Request, error) {
    67  		if req.URL != nil {
    68  			u, err := req.URL.Parse(path)
    69  			if err != nil {
    70  				return nil, err
    71  			}
    72  			req.URL = u
    73  			return req, nil
    74  		}
    75  		u, err := url.Parse(path)
    76  		if err != nil {
    77  			return nil, err
    78  		}
    79  		req.URL = u
    80  		return req, nil
    81  	}
    82  }
    83  
    84  func Params(params map[string]interface{}) RequestOption {
    85  	return func(req *http.Request) (*http.Request, error) {
    86  		u := req.URL.String()
    87  		req.URL = URLWithParams(u, params)
    88  		return req, nil
    89  	}
    90  }
    91  
    92  func StringParams(params map[string]string) RequestOption {
    93  	return func(req *http.Request) (*http.Request, error) {
    94  		q := req.URL.Query()
    95  		for k, v := range params {
    96  			q.Set(k, v)
    97  		}
    98  		req.URL.RawQuery = q.Encode()
    99  		return req, nil
   100  	}
   101  }
   102  
   103  func Body(r io.Reader) RequestOption {
   104  	return func(req *http.Request) (*http.Request, error) {
   105  		if r == nil {
   106  			req.Body = http.NoBody
   107  			req.ContentLength = 0
   108  			return req, nil
   109  		}
   110  
   111  		if rc, ok := r.(io.ReadCloser); ok {
   112  			req.Body = rc
   113  		} else {
   114  			req.Body = io.NopCloser(r)
   115  		}
   116  
   117  		if v, ok := r.(interface{ Len() int }); ok {
   118  			req.ContentLength = int64(v.Len())
   119  		} else if v, ok := r.(interface{ Size() int64 }); ok {
   120  			req.ContentLength = v.Size()
   121  		}
   122  
   123  		return req, nil
   124  	}
   125  }
   126  
   127  func JsonBody(body interface{}) RequestOption {
   128  	return func(req *http.Request) (*http.Request, error) {
   129  		b, err := json.DumpBytes(body)
   130  		if err != nil {
   131  			return nil, err
   132  		}
   133  		req.Body = io.NopCloser(bytes.NewBuffer(b))
   134  		req.ContentLength = int64(len(b))
   135  		req.Header.Set(KContentType, "application/json; charset=utf-8")
   136  		return req, nil
   137  	}
   138  }
   139  
   140  func FormBody(params map[string]interface{}) RequestOption {
   141  	return func(req *http.Request) (*http.Request, error) {
   142  		u := WithParams(params)
   143  		r := strings.NewReader(u.Encode())
   144  		req.Body = io.NopCloser(r)
   145  		req.ContentLength = int64(r.Len())
   146  		req.Header.Set(KContentType, MimeForm)
   147  		return req, nil
   148  	}
   149  }
   150  
   151  func Accept(ct string) RequestOption {
   152  	return func(req *http.Request) (*http.Request, error) {
   153  		req.Header.Set("Accept", ct)
   154  		return req, nil
   155  	}
   156  }
   157  
   158  func ContentType(ct string) RequestOption {
   159  	return func(req *http.Request) (*http.Request, error) {
   160  		req.Header.Set(KContentType, ct)
   161  		return req, nil
   162  	}
   163  }
   164  
   165  func ContentLength(l int64) RequestOption {
   166  	return func(req *http.Request) (*http.Request, error) {
   167  		if l >= 0 {
   168  			req.ContentLength = l
   169  		}
   170  		return req, nil
   171  	}
   172  }
   173  
   174  func Header(headers map[string]string) RequestOption {
   175  	return func(req *http.Request) (*http.Request, error) {
   176  		for k, v := range headers {
   177  			if strings.ToLower(k) == KContentLength {
   178  				if l, err := strconv.Atoi(v); err == nil && req.ContentLength <= 0 {
   179  					req.ContentLength = int64(l)
   180  				}
   181  			} else if v == "" {
   182  				req.Header.Del(k)
   183  			} else {
   184  				req.Header.Set(k, v)
   185  			}
   186  		}
   187  
   188  		return req, nil
   189  	}
   190  }
   191  
   192  func Context(ctx context.Context) RequestOption {
   193  	return func(req *http.Request) (*http.Request, error) {
   194  		return req.WithContext(ctx), nil
   195  	}
   196  }
   197  
   198  func Trace(tracer *httptrace.ClientTrace) RequestOption {
   199  	return func(req *http.Request) (*http.Request, error) {
   200  		return req.WithContext(httptrace.WithClientTrace(req.Context(), tracer)), nil
   201  	}
   202  }
   203  
   204  // Response 请求回应
   205  type Response struct {
   206  	StatusCode         int
   207  	err                error
   208  	contentDisposition string
   209  	contentType        string
   210  	contentLength      int64
   211  	body               []byte
   212  }
   213  
   214  func (res *Response) GetContentDisposition() string {
   215  	return res.contentDisposition
   216  }
   217  
   218  func (res *Response) IsFile() bool {
   219  	return strings.HasPrefix(res.contentDisposition, "attachment;")
   220  }
   221  
   222  func (res *Response) FileName() string {
   223  	filename := strings.TrimPrefix(res.contentDisposition, "attachment;")
   224  	filename = strings.TrimPrefix(filename, " ")
   225  	if strings.HasPrefix(filename, "filename=") {
   226  		filename = strings.TrimPrefix(filename, "filename=")
   227  	}
   228  	filename = strings.TrimPrefix(filename, "\"")
   229  	filename = strings.TrimSuffix(filename, "\"")
   230  	f := strings.Split(filename, "/")
   231  	if len(f) > 0 {
   232  		filename = f[len(f)-1]
   233  	}
   234  	if filename == "*" {
   235  		filename = ""
   236  	}
   237  	return filename
   238  }
   239  func (res *Response) Error() error {
   240  	return res.err
   241  }
   242  
   243  func (res *Response) GetContentLength() int64 {
   244  	return res.contentLength
   245  }
   246  
   247  func (res *Response) GetContentType() string {
   248  	return res.contentType
   249  }
   250  
   251  func (res *Response) GetBody() []byte {
   252  	return res.body
   253  }
   254  
   255  func (res *Response) Reset() {
   256  	res.err = nil
   257  	res.StatusCode = 0
   258  	res.contentLength = 0
   259  	res.contentType = ""
   260  	res.contentDisposition = ""
   261  	res.body = []byte{}
   262  }
   263  
   264  var EnableLog bool = true
   265  
   266  func init() {
   267  	flag.BoolVar(&EnableLog, "httpLog", true, "http log")
   268  }
   269  
   270  type NewHttpClientFunc func(h *Http, timeout time.Duration) *http.Client
   271  
   272  var defaultNewHttpClient = func(h *Http, timeout time.Duration) *http.Client {
   273  	return &http.Client{
   274  		Timeout: timeout,
   275  	}
   276  }
   277  
   278  //Http
   279  
   280  type Http struct {
   281  	DisableLog bool
   282  	SeqNo      string
   283  	TimeOut    time.Duration
   284  	newClient  NewHttpClientFunc
   285  	response   Response
   286  }
   287  
   288  func (hp *Http) SetNewHttpClient(fn NewHttpClientFunc) {
   289  	hp.newClient = fn
   290  }
   291  
   292  func (hp *Http) LogEnabled() bool {
   293  	return !hp.DisableLog && EnableLog
   294  }
   295  
   296  func (hp *Http) GetResponse() *Response {
   297  	return &hp.response
   298  }
   299  
   300  func (hp *Http) WithTimeout(duration time.Duration) *Http {
   301  	hp.TimeOut = duration
   302  	return hp
   303  }
   304  
   305  func (hp *Http) DoString(method string, url string, buf []byte, reqOpts ...RequestOption) (string, error) {
   306  	ret, err := hp.DoBytes(method, url, buf, reqOpts...)
   307  	if err != nil {
   308  		return "", err
   309  	}
   310  	return string(ret), nil
   311  }
   312  
   313  func (hp *Http) DoBytes(method string, url string, buf []byte, reqOpts ...RequestOption) ([]byte, error) {
   314  	return hp.DoWithHead(method, url, buf, nil, reqOpts...)
   315  }
   316  
   317  func (hp *Http) DoWithHead(method string, url string, buf []byte, headerFunc HeaderFunc, opts ...RequestOption) ([]byte, error) {
   318  	body, err := hp.DoV2(method, url, bytes.NewBuffer(buf), func(h *http.Header) {
   319  		h.Set(KContentType, MimeJSONUtf8)
   320  		if headerFunc != nil {
   321  			headerFunc(h)
   322  		}
   323  	}, func(in bool, method string, url string, seqNo string, no int64, res *Response) {
   324  		if in {
   325  			logger.N("Http", "REQ method= '%s', url= '%s', seqNo= '%s', no= '%d', body= '%s'", method, url, seqNo, no, logger.Byte2(logger.NDebugLevel, buf))
   326  			return
   327  		}
   328  		logger.N("Http", "RESP status= %d, seqNo= '%s', no= '%d', body= '%s', err= '%v'", res.StatusCode, seqNo, no, logger.Byte2(logger.NDebugLevel, res.body), res.err)
   329  	}, opts...)
   330  	if err != nil {
   331  		return nil, err
   332  	}
   333  	return body, nil
   334  }
   335  
   336  func (hp *Http) DoMultiForm(url string, reqOpts ...RequestOption) ([]byte, error) {
   337  	sp := strings.SplitN(url, "?", 2)
   338  	if len(sp) <= 1 {
   339  		return hp.DoFormWith(sp[0], "")
   340  	}
   341  	return hp.DoFormWith(sp[0], sp[1], reqOpts...)
   342  }
   343  
   344  func (hp *Http) DoMultiFormWith(rawUrl, query string, reqOpts ...RequestOption) ([]byte, error) {
   345  	val, er := url.ParseQuery(query)
   346  	if er != nil {
   347  		return nil, er
   348  	}
   349  	return hp.DoMultiFormWithValues(rawUrl, val, reqOpts...)
   350  }
   351  
   352  func (hp *Http) DoMultiFormWithValues(url string, form url.Values, opts ...RequestOption) ([]byte, error) {
   353  	body := new(bytes.Buffer)
   354  	w := multipart.NewWriter(body)
   355  	for k, v := range form {
   356  		_ = w.WriteField(k, v[0])
   357  	}
   358  	_ = w.Close()
   359  	return hp.DoV2("POST", url, body, func(h *http.Header) {
   360  		h.Set(KContentType, w.FormDataContentType())
   361  	}, func(in bool, method string, url string, seqNo string, no int64, res *Response) {
   362  		if in {
   363  			logger.N("Http", "REQ method= '%s', url= '%s', seqNo= '%s', no= '%d', body= '%s'", method, url, seqNo, no, logger.Byte2(logger.NDebugLevel, body.Bytes()))
   364  			return
   365  		}
   366  		logger.N("Http", "RESP status= %d, seqNo= '%s', no= '%d', body= '%s', err= '%v'", res.StatusCode, seqNo, no, logger.Byte2(logger.NDebugLevel, res.body), res.err)
   367  	}, opts...)
   368  }
   369  
   370  // DoForm
   371  // url http://api.xxx.com/ssddd?key=ddd&ddd=sfsf
   372  func (hp *Http) DoForm(url string, reqOpts ...RequestOption) ([]byte, error) {
   373  	sp := strings.SplitN(url, "?", 2)
   374  	if len(sp) <= 1 {
   375  		return hp.DoFormWith(sp[0], "")
   376  	}
   377  	return hp.DoFormWith(sp[0], sp[1], reqOpts...)
   378  }
   379  
   380  // DoFormWith
   381  // query ssddd=sdsfs&sfssfs=sfsssfs&sfsfsfs&
   382  func (hp *Http) DoFormWith(rawUrl, query string, reqOpts ...RequestOption) ([]byte, error) {
   383  	val, er := url.ParseQuery(query)
   384  	if er != nil {
   385  		return nil, er
   386  	}
   387  	return hp.DoFormWithValues(rawUrl, val, reqOpts...)
   388  }
   389  
   390  func (hp *Http) DoFormWithValues(url string, values url.Values, opts ...RequestOption) ([]byte, error) {
   391  	body := values.Encode()
   392  	return hp.DoV2("POST", url, strings.NewReader(body), func(h *http.Header) {
   393  		h.Set(KContentType, MimeForm)
   394  	}, func(in bool, method string, url string, seqNo string, no int64, res *Response) {
   395  		if in {
   396  			logger.N("Http", "REQ method= '%s', url= '%s', seqNo= '%s', no= '%d', body= '%s'", method, url, seqNo, no, body)
   397  			return
   398  		}
   399  		logger.N("Http", "RESP status= %d, seqNo= '%s', no= '%d', body= '%s', err= '%v'", res.StatusCode, seqNo, no, logger.Byte2(logger.NDebugLevel, res.body), res.err)
   400  	}, opts...)
   401  }
   402  
   403  var httpNo = int64(0)
   404  
   405  func (hp *Http) Do(method string, url string, body io.Reader, headerFunc HeaderFunc, opts ...RequestOption) ([]byte, error) {
   406  	return hp.DoV2(method, url, body, headerFunc, DefaultLog, opts...)
   407  }
   408  
   409  type LogFunc func(in bool, method string, url string, seqNo string, no int64, res *Response)
   410  
   411  func DefaultLog(in bool, method string, url string, seqNo string, no int64, res *Response) {
   412  	if in {
   413  		logger.N("Http", "REQ method= '%s',url= '%s', seqNo= '%s', no= '%d'", method, url, seqNo, no)
   414  		return
   415  	}
   416  	logger.N("Http", "RESP status= %d, seqNo= '%s', no= '%d', body= '%s', err= '%v'", res.StatusCode, seqNo, no, logger.Byte2(logger.NDebugLevel, res.body), res.err)
   417  }
   418  
   419  func (hp *Http) DoV2(method string, url string, body io.Reader, headerFunc HeaderFunc, logger LogFunc, opts ...RequestOption) ([]byte, error) {
   420  	var no = httpNo
   421  	if hp.LogEnabled() && logger != nil {
   422  		no = atomic.AddInt64(&httpNo, 1)
   423  		logger(true, method, url, hp.SeqNo, no, nil)
   424  	}
   425  	hp.response.Reset()
   426  	hp.response.StatusCode = 200
   427  	// Now that you have a form, you can submit it to your handler.
   428  	req, err := http.NewRequest(method, url, body)
   429  	if err != nil {
   430  		fmt.Printf("Http:Do  url= '%s', err= '%s',", url, err)
   431  		return nil, err
   432  	}
   433  	if headerFunc != nil {
   434  		headerFunc(&req.Header)
   435  	}
   436  	for i := range opts {
   437  		req, err = opts[i](req)
   438  		if err != nil {
   439  			return nil, err
   440  		}
   441  	}
   442  	timeout := kTimeOut
   443  	if hp.TimeOut > 0 {
   444  		timeout = time.Second * hp.TimeOut
   445  	}
   446  	var fn = hp.newClient
   447  	if fn == nil {
   448  		fn = defaultNewHttpClient
   449  	}
   450  	// Submit the request
   451  	client := fn(hp, timeout)
   452  	res, err := client.Do(req)
   453  	if err != nil {
   454  		fmt.Printf("Http:Do  url= '%s', err= '%s',", url, err)
   455  		return nil, err
   456  	}
   457  	defer res.Body.Close()
   458  
   459  	hp.response.StatusCode = res.StatusCode
   460  	hp.response.contentDisposition = res.Header.Get(KContentDisposition)
   461  	hp.response.contentType = res.Header.Get(KContentType)
   462  	hp.response.contentLength = res.ContentLength
   463  
   464  	hp.response.body, hp.response.err = io.ReadAll(res.Body)
   465  	if hp.LogEnabled() && logger != nil {
   466  		logger(false, method, url, hp.SeqNo, no, &hp.response)
   467  	}
   468  	return hp.response.body, err
   469  }