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 }