github.com/gogf/gf@v1.16.9/net/ghttp/internal/client/client.go (about) 1 // Copyright GoFrame Author(https://goframe.org). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/gogf/gf. 6 7 package client 8 9 import ( 10 "context" 11 "crypto/rand" 12 "crypto/tls" 13 "fmt" 14 "github.com/gogf/gf" 15 "github.com/gogf/gf/errors/gcode" 16 "github.com/gogf/gf/errors/gerror" 17 "github.com/gogf/gf/os/gfile" 18 "github.com/gogf/gf/text/gstr" 19 "golang.org/x/net/proxy" 20 "net" 21 "net/http" 22 "net/http/cookiejar" 23 "net/url" 24 "strings" 25 "time" 26 27 "github.com/gogf/gf/text/gregex" 28 ) 29 30 // Client is the HTTP client for HTTP request management. 31 type Client struct { 32 http.Client // Underlying HTTP Client. 33 ctx context.Context // Context for each request. 34 dump bool // Mark this request will be dumped. 35 parent *Client // Parent http client, this is used for chaining operations. 36 header map[string]string // Custom header map. 37 cookies map[string]string // Custom cookie map. 38 prefix string // Prefix for request. 39 authUser string // HTTP basic authentication: user. 40 authPass string // HTTP basic authentication: pass. 41 retryCount int // Retry count when request fails. 42 retryInterval time.Duration // Retry interval when request fails. 43 middlewareHandler []HandlerFunc // Interceptor handlers 44 } 45 46 var ( 47 defaultClientAgent = fmt.Sprintf(`GoFrameHTTPClient %s`, gf.VERSION) 48 ) 49 50 // New creates and returns a new HTTP client object. 51 func New() *Client { 52 client := &Client{ 53 Client: http.Client{ 54 Transport: &http.Transport{ 55 // No validation for https certification of the server in default. 56 TLSClientConfig: &tls.Config{ 57 InsecureSkipVerify: true, 58 }, 59 DisableKeepAlives: true, 60 }, 61 }, 62 header: make(map[string]string), 63 cookies: make(map[string]string), 64 } 65 client.header["User-Agent"] = defaultClientAgent 66 return client 67 } 68 69 // Clone deeply clones current client and returns a new one. 70 func (c *Client) Clone() *Client { 71 newClient := New() 72 *newClient = *c 73 newClient.header = make(map[string]string) 74 newClient.cookies = make(map[string]string) 75 for k, v := range c.header { 76 newClient.header[k] = v 77 } 78 for k, v := range c.cookies { 79 newClient.cookies[k] = v 80 } 81 return newClient 82 } 83 84 // SetBrowserMode enables browser mode of the client. 85 // When browser mode is enabled, it automatically saves and sends cookie content 86 // from and to server. 87 func (c *Client) SetBrowserMode(enabled bool) *Client { 88 if enabled { 89 jar, _ := cookiejar.New(nil) 90 c.Jar = jar 91 } 92 return c 93 } 94 95 // SetHeader sets a custom HTTP header pair for the client. 96 func (c *Client) SetHeader(key, value string) *Client { 97 c.header[key] = value 98 return c 99 } 100 101 // SetHeaderMap sets custom HTTP headers with map. 102 func (c *Client) SetHeaderMap(m map[string]string) *Client { 103 for k, v := range m { 104 c.header[k] = v 105 } 106 return c 107 } 108 109 // SetAgent sets the User-Agent header for client. 110 func (c *Client) SetAgent(agent string) *Client { 111 c.header["User-Agent"] = agent 112 return c 113 } 114 115 // SetContentType sets HTTP content type for the client. 116 func (c *Client) SetContentType(contentType string) *Client { 117 c.header["Content-Type"] = contentType 118 return c 119 } 120 121 // SetHeaderRaw sets custom HTTP header using raw string. 122 func (c *Client) SetHeaderRaw(headers string) *Client { 123 for _, line := range gstr.SplitAndTrim(headers, "\n") { 124 array, _ := gregex.MatchString(`^([\w\-]+):\s*(.+)`, line) 125 if len(array) >= 3 { 126 c.header[array[1]] = array[2] 127 } 128 } 129 return c 130 } 131 132 // SetCookie sets a cookie pair for the client. 133 func (c *Client) SetCookie(key, value string) *Client { 134 c.cookies[key] = value 135 return c 136 } 137 138 // SetDump enables/disables dump feature for this request. 139 func (c *Client) SetDump(dump bool) *Client { 140 c.dump = dump 141 return c 142 } 143 144 // SetCookieMap sets cookie items with map. 145 func (c *Client) SetCookieMap(m map[string]string) *Client { 146 for k, v := range m { 147 c.cookies[k] = v 148 } 149 return c 150 } 151 152 // SetPrefix sets the request server URL prefix. 153 func (c *Client) SetPrefix(prefix string) *Client { 154 c.prefix = prefix 155 return c 156 } 157 158 // SetTimeout sets the request timeout for the client. 159 func (c *Client) SetTimeout(t time.Duration) *Client { 160 c.Client.Timeout = t 161 return c 162 } 163 164 // SetBasicAuth sets HTTP basic authentication information for the client. 165 func (c *Client) SetBasicAuth(user, pass string) *Client { 166 c.authUser = user 167 c.authPass = pass 168 return c 169 } 170 171 // SetCtx sets context for each request of this client. 172 func (c *Client) SetCtx(ctx context.Context) *Client { 173 c.ctx = ctx 174 return c 175 } 176 177 // SetRetry sets retry count and interval. 178 func (c *Client) SetRetry(retryCount int, retryInterval time.Duration) *Client { 179 c.retryCount = retryCount 180 c.retryInterval = retryInterval 181 return c 182 } 183 184 // SetRedirectLimit limit the number of jumps 185 func (c *Client) SetRedirectLimit(redirectLimit int) *Client { 186 c.CheckRedirect = func(req *http.Request, via []*http.Request) error { 187 if len(via) >= redirectLimit { 188 return http.ErrUseLastResponse 189 } 190 return nil 191 } 192 return c 193 } 194 195 // SetProxy set proxy for the client. 196 // This func will do nothing when the parameter `proxyURL` is empty or in wrong pattern. 197 // The correct pattern is like `http://USER:PASSWORD@IP:PORT` or `socks5://USER:PASSWORD@IP:PORT`. 198 // Only `http` and `socks5` proxies are supported currently. 199 func (c *Client) SetProxy(proxyURL string) { 200 if strings.TrimSpace(proxyURL) == "" { 201 return 202 } 203 _proxy, err := url.Parse(proxyURL) 204 if err != nil { 205 return 206 } 207 if _proxy.Scheme == "http" { 208 if v, ok := c.Transport.(*http.Transport); ok { 209 v.Proxy = http.ProxyURL(_proxy) 210 } 211 } else { 212 var auth = &proxy.Auth{} 213 user := _proxy.User.Username() 214 215 if user != "" { 216 auth.User = user 217 password, hasPassword := _proxy.User.Password() 218 if hasPassword && password != "" { 219 auth.Password = password 220 } 221 } else { 222 auth = nil 223 } 224 // refer to the source code, error is always nil 225 dialer, err := proxy.SOCKS5( 226 "tcp", 227 _proxy.Host, 228 auth, 229 &net.Dialer{ 230 Timeout: c.Client.Timeout, 231 KeepAlive: c.Client.Timeout, 232 }, 233 ) 234 if err != nil { 235 return 236 } 237 if v, ok := c.Transport.(*http.Transport); ok { 238 v.DialContext = func(ctx context.Context, network, addr string) (conn net.Conn, e error) { 239 return dialer.Dial(network, addr) 240 } 241 } 242 //c.SetTimeout(10*time.Second) 243 } 244 } 245 246 // SetTLSKeyCrt sets the certificate and key file for TLS configuration of client. 247 func (c *Client) SetTLSKeyCrt(crtFile, keyFile string) error { 248 tlsConfig, err := LoadKeyCrt(crtFile, keyFile) 249 if err != nil { 250 return gerror.WrapCode(gcode.CodeInternalError, err, "LoadKeyCrt failed") 251 } 252 if v, ok := c.Transport.(*http.Transport); ok { 253 tlsConfig.InsecureSkipVerify = true 254 v.TLSClientConfig = tlsConfig 255 return nil 256 } 257 return gerror.NewCode(gcode.CodeInternalError, `cannot set TLSClientConfig for custom Transport of the client`) 258 } 259 260 // SetTLSConfig sets the TLS configuration of client. 261 func (c *Client) SetTLSConfig(tlsConfig *tls.Config) error { 262 if v, ok := c.Transport.(*http.Transport); ok { 263 v.TLSClientConfig = tlsConfig 264 return nil 265 } 266 return gerror.NewCode(gcode.CodeInternalError, `cannot set TLSClientConfig for custom Transport of the client`) 267 } 268 269 // LoadKeyCrt creates and returns a TLS configuration object with given certificate and key files. 270 func LoadKeyCrt(crtFile, keyFile string) (*tls.Config, error) { 271 crtPath, err := gfile.Search(crtFile) 272 if err != nil { 273 return nil, err 274 } 275 keyPath, err := gfile.Search(keyFile) 276 if err != nil { 277 return nil, err 278 } 279 crt, err := tls.LoadX509KeyPair(crtPath, keyPath) 280 if err != nil { 281 return nil, err 282 } 283 tlsConfig := &tls.Config{} 284 tlsConfig.Certificates = []tls.Certificate{crt} 285 tlsConfig.Time = time.Now 286 tlsConfig.Rand = rand.Reader 287 return tlsConfig, nil 288 }