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  }