diff --git a/curlx.go b/curlx.go index be29ce5..76ecc2c 100644 --- a/curlx.go +++ b/curlx.go @@ -3,7 +3,7 @@ package curlx import ( "bufio" "context" - "fmt" + "errors" "net" "net/http" "net/url" @@ -20,7 +20,7 @@ import ( // type DialContext func(ctx context.Context, network, addr string) (net.Conn, error) type Curlx struct { - opts clientOptions + opts ClientOptions transport *http.Transport } @@ -31,31 +31,13 @@ func NewCurlx(opts ...Option) *Curlx { } transport := &http.Transport{ - // Dial: func(netw, addr string) (net.Conn, error) { - // // 这里指定域名访问的IP - // // if addr == "api.hk.blueoceanpay.com:443" { - // // addr = "47.56.200.21:443" - // // } - // conn, err := net.DialTimeout(netw, addr, time.Second*time.Duration(timeOut)) // 设置建立连接超时 - // if err != nil { - // return nil, err - // } - // // conn.RemoteAddr().String() - // conn.SetDeadline(time.Now().Add(time.Second * time.Duration(timeOut))) // 设置发送接收数据超时 - // return conn, nil - // }, - // DialContext: (&net.Dialer{ - // Timeout: 3 * time.Second, // 建立TCP链接的超时时间 - // KeepAlive: 30 * time.Second, // TCP keepalive超时时间 - // }).DialContext, - // TLSHandshakeTimeout: time.Second * 10, // TLS握手超时 - // ResponseHeaderTimeout: time.Second * 10, // 接收响应头的超时时间 - // ExpectContinueTimeout: time.Second * 10, // 发送请求头超时时间 100-continue状态码超时时间 - DisableKeepAlives: false, // 短连接(默认是使用长连接,连接过多时会造成服务器拒绝服务问题) - MaxIdleConns: 0, // 所有host的连接池最大连接数量,默认无穷大 - MaxIdleConnsPerHost: 5, // 每个host的连接池最大空闲连接收,默认2 - MaxConnsPerHost: 0, // 每个host的最大连接数量 - IdleConnTimeout: time.Second * 2, // 空闲连接超时关闭的时间 + DisableKeepAlives: false, // 启用keep-alive连接复用 + MaxIdleConns: defaultOpts.MaxIdleConns, + MaxIdleConnsPerHost: defaultOpts.MaxIdleConnsPerHost, + MaxConnsPerHost: defaultOpts.MaxConnsPerHost, + IdleConnTimeout: defaultOpts.IdleConnTimeout, + ExpectContinueTimeout: 1 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, } if defaultOpts.InsecureSkipVerify { @@ -84,7 +66,7 @@ func (c *Curlx) WithProxySocks5(address string) error { } dialSocksProxy, err := proxy.SOCKS5("tcp", address, nil, baseDialer) if err != nil { - fmt.Println("proxy.SOCKS5 err", err) + c.opts.Logger.Errorf(context.Background(), "proxy.SOCKS5 err: %v", err) return err } dialContext := (baseDialer).DialContext @@ -102,6 +84,7 @@ func (c *Curlx) WithProxySocks5(address string) error { func (c *Curlx) WithProxyHttp(proxyAddr string) error { proxy, err := url.Parse(proxyAddr) if err != nil { + c.opts.Logger.Errorf(context.Background(), "proxy.HTTP/HTTPS err: %v", err) return err } c.transport.Proxy = http.ProxyURL(proxy) @@ -182,6 +165,14 @@ func (c *Curlx) exec(ctx context.Context, ps ...Param) Response { Transport: c.transport, // } + // 在http.Client中添加CheckRedirect函数 实现重定向控制 + client.CheckRedirect = func(req *http.Request, via []*http.Request) error { + if len(via) >= 10 { // 限制重定向次数 + return errors.New("stopped after 10 redirects") + } + return nil + } + p := defaultParams() for _, param := range ps { param(&p) diff --git a/options.go b/options.go index b061e07..6f17209 100644 --- a/options.go +++ b/options.go @@ -6,37 +6,55 @@ import ( "time" ) -type clientOptions struct { - TimeOut time.Duration - InsecureSkipVerify bool - Logger OptionLogger - LoggerLength int // 日志输出长度 +type ClientOptions struct { + TimeOut time.Duration + InsecureSkipVerify bool + Logger OptionLogger + LoggerLength int // 日志输出长度 + CertFingerprint string // 证书指纹验证 + + // 连接池配置 + MaxIdleConns int + MaxIdleConnsPerHost int + MaxConnsPerHost int + IdleConnTimeout time.Duration } -func defaultOptions() clientOptions { - return clientOptions{ - TimeOut: time.Second * 120, // 默认超时120 - Logger: defaultLogger{}, - LoggerLength: 100, +func defaultOptions() ClientOptions { + return ClientOptions{ + TimeOut: time.Second * 120, // 默认超时120秒 + Logger: defaultLogger{}, + LoggerLength: 100, + MaxIdleConns: 100, // 默认连接池大小 + MaxIdleConnsPerHost: 10, // 每主机默认空闲连接数 + MaxConnsPerHost: 50, // 每主机最大连接数 + IdleConnTimeout: 90 * time.Second, // 空闲连接超时 } } -type Option func(*clientOptions) +type Option func(*ClientOptions) /** * 设置超时时间 */ -func SetOptionTimeOut(t time.Duration) Option { - return func(options *clientOptions) { +func WithOptionTimeOut(t time.Duration) Option { + return func(options *ClientOptions) { options.TimeOut = t } } +// 添加证书指纹验证选项 +func WithOptionTLSPin(certFingerprint string) Option { + return func(options *ClientOptions) { + options.CertFingerprint = certFingerprint + } +} + /** * 不校验HTTPS证书 */ -func SetOptionTLSInsecureSkipVerify() Option { - return func(options *clientOptions) { +func WithOptionTLSInsecureSkipVerify() Option { + return func(options *ClientOptions) { options.InsecureSkipVerify = true } } @@ -44,8 +62,8 @@ func SetOptionTLSInsecureSkipVerify() Option { /** * 设置日志输出 */ -func SetOptionLog(log OptionLogger) Option { - return func(options *clientOptions) { +func WithOptionLog(log OptionLogger) Option { + return func(options *ClientOptions) { options.Logger = log } } @@ -53,12 +71,37 @@ func SetOptionLog(log OptionLogger) Option { /** * 设置日志输出长度 */ -func WithLoggerLength(length int) Option { - return func(options *clientOptions) { +func WithOptionLoggerLength(length int) Option { + return func(options *ClientOptions) { options.LoggerLength = length } } +// 连接池配置选项 +func WithMaxIdleConns(maxIdleConns int) Option { + return func(options *ClientOptions) { + options.MaxIdleConns = maxIdleConns + } +} + +func WithMaxIdleConnsPerHost(maxIdleConnsPerHost int) Option { + return func(options *ClientOptions) { + options.MaxIdleConnsPerHost = maxIdleConnsPerHost + } +} + +func WithMaxConnsPerHost(maxConnsPerHost int) Option { + return func(options *ClientOptions) { + options.MaxConnsPerHost = maxConnsPerHost + } +} + +func WithIdleConnTimeout(timeout time.Duration) Option { + return func(options *ClientOptions) { + options.IdleConnTimeout = timeout + } +} + type OptionLogger interface { Infof(ctx context.Context, format string, args ...any) Errorf(ctx context.Context, format string, args ...any)