github.com/kaydxh/golang@v0.0.131/go/net/http/http_client.go (about) 1 /* 2 *Copyright (c) 2022, kaydxh 3 * 4 *Permission is hereby granted, free of charge, to any person obtaining a copy 5 *of this software and associated documentation files (the "Software"), to deal 6 *in the Software without restriction, including without limitation the rights 7 *to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 *copies of the Software, and to permit persons to whom the Software is 9 *furnished to do so, subject to the following conditions: 10 * 11 *The above copyright notice and this permission notice shall be included in all 12 *copies or substantial portions of the Software. 13 * 14 *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 *IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 *FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 *AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 *LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 *OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 *SOFTWARE. 21 */ 22 package http 23 24 import ( 25 "bytes" 26 "context" 27 "fmt" 28 "io" 29 "io/ioutil" 30 "log" 31 "net" 32 "net/http" 33 "time" 34 35 "github.com/gin-gonic/gin/binding" 36 ) 37 38 type Client struct { 39 http.Client 40 opts struct { 41 // Timeout specifies a time limit for requests made by this 42 // Client. The timeout includes connection time, any 43 // redirects, and reading the response body. The timer remains 44 // running after Get, Head, Post, or Do return and will 45 // interrupt reading of the Response.Body. 46 // 47 // A Timeout of zero means no timeout. 48 // 49 // The Client cancels requests to the underlying Transport 50 // as if the Request's Context ended. 51 // 52 // For compatibility, the Client will also use the deprecated 53 // CancelRequest method on Transport if found. New 54 // RoundTripper implementations should use the Request's Context 55 // for cancellation instead of implementing CancelRequest. 56 timeout time.Duration 57 dialTimeout time.Duration 58 responseHeaderTimeout time.Duration 59 idleConnTimeout time.Duration 60 maxIdleConns int 61 disableKeepAlives bool 62 63 // Proxy specifies a function to return a proxy for a given 64 // Request. If the function returns a non-nil error, the 65 // request is aborted with the provided error. 66 // 67 // The proxy type is determined by the URL scheme. "http", 68 // "https", and "socks5" are supported. If the scheme is empty, 69 // "http" is assumed. 70 // 71 // If Proxy is nil or returns a nil *URL, no proxy is used. 72 //proxy func(*http.Request) (*url.URL, error) 73 // like forward proxy 74 proxyURL string 75 76 // proxyHost is host:port, or domain, replace host in proxy 77 proxyHost string 78 79 // targetHost is host:port, redirct to it, like reverse proxy 80 targetHost string 81 82 ErrorLog *log.Logger 83 } 84 } 85 86 func NewClient(options ...ClientOption) (*Client, error) { 87 c := &Client{} 88 c.ApplyOptions(options...) 89 transport := DefaultTransportInsecure 90 /* 91 transport := &http.Transport{ 92 93 // ProxyFromEnvironment returns the URL of the proxy to use for a 94 // given request, as indicated by the environment variables 95 // HTTP_PROXY, HTTPS_PROXY and NO_PROXY (or the lowercase versions 96 // thereof). HTTPS_PROXY takes precedence over HTTP_PROXY for https 97 // requests. 98 // 99 // The environment values may be either a complete URL or a 100 // "host[:port]", in which case the "http" scheme is assumed. 101 // The schemes "http", "https", and "socks5" are supported. 102 // An error is returned if the value is a different form. 103 // 104 // A nil URL and nil error are returned if no proxy is defined in the 105 // environment, or a proxy should not be used for the given request, 106 // as defined by NO_PROXY. 107 // 108 // As a special case, if req.URL.Host is "localhost" (with or without 109 // a port number), then a nil URL and nil error will be returned. 110 Proxy: http.ProxyFromEnvironment, 111 // skip verify for https 112 TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 113 } 114 */ 115 if c.opts.timeout != 0 { 116 c.Client.Timeout = c.opts.timeout 117 } 118 if c.opts.dialTimeout != 0 { 119 transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) { 120 conn, err := net.DialTimeout( 121 network, 122 addr, 123 c.opts.dialTimeout, 124 ) 125 if nil != err { 126 return nil, err 127 } 128 return conn, nil 129 } 130 } 131 132 if c.opts.responseHeaderTimeout != 0 { 133 transport.ResponseHeaderTimeout = c.opts.responseHeaderTimeout 134 } 135 if c.opts.maxIdleConns != 0 { 136 transport.MaxIdleConns = c.opts.maxIdleConns 137 } 138 if c.opts.idleConnTimeout != 0 { 139 transport.IdleConnTimeout = c.opts.idleConnTimeout 140 } 141 if c.opts.disableKeepAlives { 142 transport.DisableKeepAlives = c.opts.disableKeepAlives 143 } 144 c.Transport = RoundTripFunc(func(req *http.Request) (resp *http.Response, err error) { 145 if c.opts.proxyURL != "" || c.opts.proxyHost != "" { 146 transport.Proxy = ProxyFuncFromContextOrEnvironment 147 148 proxyUrl := "http://" 149 if c.opts.proxyURL != "" { 150 proxyUrl = c.opts.proxyURL 151 } 152 proxy := &Proxy{ 153 ProxyUrl: proxyUrl, 154 ProxyTarget: c.opts.proxyHost, 155 } 156 req = RequestWithContextProxy(req, proxy) 157 } 158 159 if c.opts.targetHost != "" { 160 host := &Host{ 161 HostTarget: c.opts.targetHost, 162 ReplaceHostInRequest: true, 163 } 164 req = RequestWithContextTargetHost(req, host) 165 } 166 167 return RoundTripperWithTarget(transport).RoundTrip(req) 168 169 }) 170 171 return c, nil 172 } 173 174 func (c *Client) Get(ctx context.Context, url string) ([]byte, error) { 175 r, err := c.get(ctx, url) 176 if err != nil { 177 return nil, err 178 } 179 defer r.Body.Close() 180 181 data, err := ioutil.ReadAll(r.Body) 182 if err != nil { 183 return nil, err 184 } 185 186 return data, nil 187 } 188 189 func (c *Client) Post( 190 ctx context.Context, 191 url, contentType string, 192 headers map[string]string, 193 body []byte, 194 ) ([]byte, error) { 195 bodyReader := bytes.NewReader(body) 196 return c.PostReader(ctx, url, contentType, headers, nil, bodyReader) 197 } 198 199 func (c *Client) Put( 200 ctx context.Context, 201 url, contentType string, 202 headers map[string]string, 203 body []byte, 204 ) ([]byte, error) { 205 bodyReader := bytes.NewReader(body) 206 return c.PutReader(ctx, url, contentType, headers, nil, bodyReader) 207 } 208 209 func (c *Client) PostJson( 210 ctx context.Context, 211 url string, 212 headers map[string]string, 213 body []byte, 214 ) ([]byte, error) { 215 bodyReader := bytes.NewReader(body) 216 return c.PostReader(ctx, url, binding.MIMEJSON, headers, nil, bodyReader) 217 } 218 219 func (c *Client) PostPb( 220 ctx context.Context, 221 url string, 222 headers map[string]string, 223 body []byte, 224 ) ([]byte, error) { 225 bodyReader := bytes.NewReader(body) 226 return c.PostReader(ctx, url, binding.MIMEPROTOBUF, headers, nil, bodyReader) 227 } 228 229 func (c *Client) PostJsonWithAuthorize( 230 ctx context.Context, 231 url string, 232 headers map[string]string, 233 auth func(r *http.Request) error, 234 body []byte, 235 ) ([]byte, error) { 236 bodyReader := bytes.NewReader(body) 237 return c.PostReader(ctx, url, binding.MIMEJSON, headers, auth, bodyReader) 238 } 239 240 func (c *Client) PostReader( 241 ctx context.Context, 242 url, contentType string, 243 headers map[string]string, 244 auth func(r *http.Request) error, 245 body io.Reader, 246 ) ([]byte, error) { 247 return c.HttpReader(ctx, http.MethodPost, url, contentType, headers, auth, body) 248 } 249 250 func (c *Client) PutReader( 251 ctx context.Context, 252 url, contentType string, 253 headers map[string]string, 254 auth func(r *http.Request) error, 255 body io.Reader, 256 ) ([]byte, error) { 257 return c.HttpReader(ctx, http.MethodPut, url, contentType, headers, auth, body) 258 } 259 260 func (c *Client) HttpReader( 261 ctx context.Context, 262 method, url, contentType string, 263 headers map[string]string, 264 auth func(r *http.Request) error, 265 body io.Reader, 266 ) ([]byte, error) { 267 r, err := c.HttpDo(ctx, method, url, contentType, headers, auth, body) 268 if err != nil { 269 return nil, err 270 } 271 defer r.Body.Close() 272 273 data, err := ioutil.ReadAll(r.Body) 274 if err != nil { 275 return nil, err 276 } 277 if r.StatusCode >= http.StatusBadRequest { 278 return data, fmt.Errorf("http status code: %v", r.StatusCode) 279 } 280 281 return data, nil 282 } 283 284 func (c *Client) logf(format string, args ...interface{}) { 285 if c.opts.ErrorLog != nil { 286 c.opts.ErrorLog.Printf(format, args...) 287 } else { 288 log.Printf(format, args...) 289 } 290 }