github.com/sohaha/zlsgo@v1.7.13-0.20240501141223-10dd1a906f76/zhttp/client.go (about)

     1  package zhttp
     2  
     3  import (
     4  	"crypto/tls"
     5  	"crypto/x509"
     6  	"errors"
     7  	"io/ioutil"
     8  	"net"
     9  	"net/http"
    10  	"net/http/cookiejar"
    11  	"net/url"
    12  	"time"
    13  
    14  	"github.com/sohaha/zlsgo/zlog"
    15  	"github.com/sohaha/zlsgo/zstring"
    16  	"github.com/sohaha/zlsgo/zutil"
    17  )
    18  
    19  func newClient() *http.Client {
    20  	jar, _ := cookiejar.New(nil)
    21  	transport := &http.Transport{
    22  		Proxy: http.ProxyFromEnvironment,
    23  		DialContext: (&net.Dialer{
    24  			Timeout:   30 * time.Second,
    25  			KeepAlive: 30 * time.Second,
    26  			DualStack: true,
    27  		}).DialContext,
    28  		MaxIdleConns:          100,
    29  		IdleConnTimeout:       90 * time.Second,
    30  		TLSHandshakeTimeout:   10 * time.Second,
    31  		ExpectContinueTimeout: 1 * time.Second,
    32  	}
    33  	return &http.Client{
    34  		Jar:       jar,
    35  		Transport: transport,
    36  		Timeout:   10 * time.Minute,
    37  	}
    38  }
    39  
    40  func (e *Engine) Client() *http.Client {
    41  	if e.client == nil {
    42  		e.client = newClient()
    43  	}
    44  	return e.client
    45  }
    46  
    47  func (e *Engine) SetClient(client *http.Client) {
    48  	e.client = client
    49  }
    50  
    51  func (e *Engine) DisableChunke(enable ...bool) {
    52  	state := true
    53  	if len(enable) > 0 && enable[0] {
    54  		state = false
    55  	}
    56  	e.disableChunke = state
    57  }
    58  
    59  func (e *Engine) Get(url string, v ...interface{}) (*Res, error) {
    60  	return e.Do(http.MethodGet, url, v...)
    61  }
    62  
    63  func (e *Engine) Post(url string, v ...interface{}) (*Res, error) {
    64  	return e.Do(http.MethodPost, url, v...)
    65  }
    66  
    67  func (e *Engine) Put(url string, v ...interface{}) (*Res, error) {
    68  	return e.Do(http.MethodPut, url, v...)
    69  }
    70  
    71  func (e *Engine) Patch(url string, v ...interface{}) (*Res, error) {
    72  	return e.Do(http.MethodPatch, url, v...)
    73  }
    74  
    75  func (e *Engine) Delete(url string, v ...interface{}) (*Res, error) {
    76  	return e.Do(http.MethodDelete, url, v...)
    77  }
    78  
    79  func (e *Engine) Head(url string, v ...interface{}) (*Res, error) {
    80  	return e.Do(http.MethodHead, url, v...)
    81  }
    82  
    83  func (e *Engine) Options(url string, v ...interface{}) (*Res, error) {
    84  	return e.Do(http.MethodOptions, url, v...)
    85  }
    86  
    87  func (e *Engine) Trace(url string, v ...interface{}) (*Res, error) {
    88  	return e.Do(http.MethodTrace, url, v...)
    89  }
    90  
    91  func (e *Engine) Connect(url string, v ...interface{}) (*Res, error) {
    92  	return e.Do(http.MethodConnect, url, v...)
    93  }
    94  
    95  func (e *Engine) DoRetry(attempt int, sleep time.Duration, fn func() (*Res, error)) (res *Res, err error) {
    96  	if !zutil.DoRetry(attempt, func() bool {
    97  		res, err = fn()
    98  		return err == nil
    99  	}, func(rc *zutil.RetryConf) {
   100  		if sleep == 0 {
   101  			rc.BackOffDelay = true
   102  		} else {
   103  			rc.Interval = sleep
   104  		}
   105  	}) {
   106  		return res, errors.New("the number of retries has been exhausted")
   107  	}
   108  
   109  	return
   110  }
   111  
   112  func (e *Engine) EnableInsecureTLS(enable bool) {
   113  	trans := e.getTransport()
   114  	if trans == nil {
   115  		return
   116  	}
   117  	if trans.TLSClientConfig == nil {
   118  		trans.TLSClientConfig = &tls.Config{}
   119  	}
   120  	trans.TLSClientConfig.InsecureSkipVerify = enable
   121  }
   122  
   123  type Certificate struct {
   124  	CertFile string
   125  	KeyFile  string
   126  }
   127  
   128  func (e *Engine) TlsCertificate(certs ...Certificate) error {
   129  	trans := e.getTransport()
   130  	if trans == nil {
   131  		return nil
   132  	}
   133  	if trans.TLSClientConfig == nil {
   134  		trans.TLSClientConfig = &tls.Config{}
   135  	}
   136  	l := len(certs)
   137  	certificates := make([]tls.Certificate, 0, l)
   138  	for i := 0; i < l; i++ {
   139  		x509KeyPair, err := tls.LoadX509KeyPair(certs[i].CertFile, certs[i].KeyFile)
   140  		if err != nil {
   141  			return err
   142  		}
   143  		certificates = append(certificates, x509KeyPair)
   144  	}
   145  	trans.TLSClientConfig.Certificates = certificates
   146  	return nil
   147  }
   148  
   149  func (e *Engine) EnableCookie(enable bool) {
   150  	if enable {
   151  		jar, _ := cookiejar.New(nil)
   152  		e.Client().Jar = jar
   153  	} else {
   154  		e.Client().Jar = nil
   155  	}
   156  }
   157  
   158  func (e *Engine) CheckRedirect(fn ...func(req *http.Request, via []*http.Request) error) {
   159  	if len(fn) > 0 {
   160  		e.Client().CheckRedirect = fn[0]
   161  	} else {
   162  		e.Client().CheckRedirect = func(_ *http.Request, via []*http.Request) error {
   163  			return http.ErrUseLastResponse
   164  		}
   165  	}
   166  }
   167  
   168  func (e *Engine) SetTimeout(d time.Duration) {
   169  	e.Client().Timeout = d
   170  }
   171  
   172  func (e *Engine) SetTransport(transport func(*http.Transport)) error {
   173  	trans := e.getTransport()
   174  	if trans == nil {
   175  		return ErrNoTransport
   176  	}
   177  	transport(trans)
   178  	return nil
   179  }
   180  
   181  func (e *Engine) SetProxyUrl(proxyUrl ...string) error {
   182  	l := len(proxyUrl)
   183  	if l == 0 {
   184  		return errors.New("proxy url cannot be empty")
   185  	}
   186  	u := proxyUrl[0]
   187  	return e.SetProxy(func(request *http.Request) (*url.URL, error) {
   188  		if l > 1 {
   189  			u = proxyUrl[zstring.RandInt(0, l-1)]
   190  		}
   191  		return url.Parse(u)
   192  	})
   193  }
   194  
   195  func (e *Engine) SetProxy(proxy func(*http.Request) (*url.URL, error)) error {
   196  	return e.SetTransport(func(transport *http.Transport) {
   197  		transport.Proxy = proxy
   198  	})
   199  }
   200  
   201  func (e *Engine) RemoveProxy() error {
   202  	trans := e.getTransport()
   203  	if trans == nil {
   204  		return ErrNoTransport
   205  	}
   206  	trans.Proxy = http.ProxyFromEnvironment
   207  	return nil
   208  }
   209  
   210  func (e *Engine) getJSONEncOpts() *jsonEncOpts {
   211  	if e.jsonEncOpts == nil {
   212  		e.jsonEncOpts = &jsonEncOpts{escapeHTML: true}
   213  	}
   214  	return e.jsonEncOpts
   215  }
   216  
   217  func (e *Engine) SetJSONEscapeHTML(escape bool) {
   218  	opts := e.getJSONEncOpts()
   219  	opts.escapeHTML = escape
   220  }
   221  
   222  func (e *Engine) SetJSONIndent(prefix, indent string) {
   223  	opts := e.getJSONEncOpts()
   224  	opts.indentPrefix = prefix
   225  	opts.indentValue = indent
   226  }
   227  
   228  func (e *Engine) SetXMLIndent(prefix, indent string) {
   229  	opts := e.getXMLEncOpts()
   230  	opts.prefix = prefix
   231  	opts.indent = indent
   232  }
   233  
   234  func (e *Engine) SetSsl(certPath, keyPath, CAPath string) (*tls.Config, error) {
   235  	cert, err := tls.LoadX509KeyPair(certPath, keyPath)
   236  	if err != nil {
   237  		zlog.Error("load keys fail", err)
   238  		return nil, err
   239  	}
   240  
   241  	caData, err := ioutil.ReadFile(CAPath)
   242  	if err != nil {
   243  		zlog.Error("read ca fail", err)
   244  		return nil, err
   245  	}
   246  	pool := x509.NewCertPool()
   247  	pool.AppendCertsFromPEM(caData)
   248  
   249  	trans := e.getTransport()
   250  	if trans == nil {
   251  		return nil, ErrTransEmpty
   252  	}
   253  
   254  	trans.TLSClientConfig = &tls.Config{
   255  		Certificates: []tls.Certificate{cert},
   256  		RootCAs:      pool,
   257  	}
   258  	return trans.TLSClientConfig, nil
   259  }
   260  
   261  func (e *Engine) getTransport() *http.Transport {
   262  	trans, _ := e.Client().Transport.(*http.Transport)
   263  	return trans
   264  }
   265  
   266  func (e *Engine) getXMLEncOpts() *xmlEncOpts {
   267  	if e.xmlEncOpts == nil {
   268  		e.xmlEncOpts = &xmlEncOpts{}
   269  	}
   270  	return e.xmlEncOpts
   271  }