Package HTTP Client:

- defined default value for idles connection & max connection per host
- rework client creation to prevent TCP FIN not send on close stream
This commit is contained in:
Nicolas JUHEL
2024-02-09 14:33:47 +01:00
parent f8bd2b56e0
commit eaf88e00b7
2 changed files with 108 additions and 40 deletions

View File

@@ -28,12 +28,13 @@ package httpcli
import (
"context"
"crypto/tls"
"net"
"net/http"
"net/url"
"sync/atomic"
"time"
libtls "github.com/nabbar/golib/certificates"
liberr "github.com/nabbar/golib/errors"
libptc "github.com/nabbar/golib/network/protocol"
"golang.org/x/net/http2"
@@ -49,34 +50,98 @@ const (
ClientNetworkUDP = "udp"
)
var trp = new(atomic.Value)
func init() {
trp.Store(&http.Transport{
Proxy: http.ProxyFromEnvironment,
TLSHandshakeTimeout: 10 * time.Second,
DisableKeepAlives: false,
DisableCompression: false,
MaxIdleConns: 100,
MaxIdleConnsPerHost: 1,
MaxConnsPerHost: 25,
IdleConnTimeout: 90 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
ForceAttemptHTTP2: true,
TLSClientConfig: &tls.Config{
MinVersion: tls.VersionTLS12,
MaxVersion: tls.VersionTLS13,
},
})
}
type FctHttpClient func() *http.Client
type HttpClient interface {
Do(req *http.Request) (*http.Response, error)
}
func GetTransport(DisableKeepAlive, DisableCompression, ForceHTTP2 bool) *http.Transport {
return &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: nil,
DialTLSContext: nil,
TLSClientConfig: nil,
DisableKeepAlives: DisableKeepAlive,
DisableCompression: DisableCompression,
ForceAttemptHTTP2: ForceHTTP2,
func ForceUpdateTransport(cfg *tls.Config, proxyUrl *url.URL) *http.Transport {
t := &http.Transport{
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: cfg.Clone(),
TLSHandshakeTimeout: 10 * time.Second,
DisableKeepAlives: false,
DisableCompression: false,
MaxIdleConns: 100,
MaxIdleConnsPerHost: 1,
MaxConnsPerHost: 25,
IdleConnTimeout: 90 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
ForceAttemptHTTP2: true,
}
if proxyUrl != nil {
t.Proxy = http.ProxyURL(proxyUrl)
}
trp.Store(t)
return t
}
func SetTransportTLS(tr *http.Transport, tls libtls.TLSConfig, servername string) {
tr.TLSClientConfig = tls.TlsConfig(servername)
}
func GetTransport(tlsConfig *tls.Config, proxyURL *url.URL, DisableKeepAlive, DisableCompression, ForceHTTP2 bool) *http.Transport {
var tr *http.Transport
func SetTransportProxy(tr *http.Transport, proxyUrl *url.URL) {
tr.Proxy = http.ProxyURL(proxyUrl)
if i := trp.Load(); i != nil {
if t, k := i.(*http.Transport); k {
tr = t.Clone()
}
}
if tr == nil {
tr = &http.Transport{
Proxy: http.ProxyFromEnvironment,
TLSHandshakeTimeout: 10 * time.Second,
DisableKeepAlives: false,
DisableCompression: false,
MaxIdleConns: 100,
MaxIdleConnsPerHost: 1,
MaxConnsPerHost: 25,
IdleConnTimeout: 90 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
ForceAttemptHTTP2: true,
}
}
tr.DisableCompression = DisableCompression
tr.DisableKeepAlives = DisableKeepAlive
tr.TLSClientConfig = tlsConfig.Clone()
if proxyURL != nil {
tr.Proxy = http.ProxyURL(proxyURL)
}
return tr
}
func SetTransportDial(tr *http.Transport, forceIp bool, netw libptc.NetworkProtocol, ip, local string) {
var (
dial = &net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}
fctDial func(ctx context.Context, network, address string) (net.Conn, error)
)
@@ -85,24 +150,24 @@ func SetTransportDial(tr *http.Transport, forceIp bool, netw libptc.NetworkProto
Host: local,
}
fctDial = func(ctx context.Context, network, address string) (net.Conn, error) {
dl := &net.Dialer{
LocalAddr: &net.TCPAddr{
IP: net.ParseIP(u.Hostname()),
},
dial.LocalAddr = &net.TCPAddr{
IP: net.ParseIP(u.Hostname()),
}
return dl.DialContext(ctx, netw.Code(), ip)
return dial.DialContext(ctx, netw.Code(), ip)
}
} else if forceIp {
fctDial = func(ctx context.Context, network, address string) (net.Conn, error) {
dl := &net.Dialer{}
return dl.DialContext(ctx, netw.Code(), ip)
return dial.DialContext(ctx, netw.Code(), ip)
}
} else {
dl := &net.Dialer{}
fctDial = dl.DialContext
fctDial = dial.DialContext
}
tr.DialContext = fctDial
tr.DialTLSContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
return tls.DialWithDialer(dial, network, addr, tr.TLSClientConfig)
}
}
func GetClient(tr *http.Transport, http2Tr bool, GlobalTimeout time.Duration) (*http.Client, liberr.Error) {

View File

@@ -62,11 +62,13 @@ type OptionProxy struct {
}
type Options struct {
Timeout time.Duration `json:"timeout" yaml:"timeout" toml:"timeout" mapstructure:"timeout"`
Http2 bool `json:"http2" yaml:"http2" toml:"http2" mapstructure:"http2"`
TLS OptionTLS `json:"tls" yaml:"tls" toml:"tls" mapstructure:"tls"`
ForceIP OptionForceIP `json:"force_ip" yaml:"force_ip" toml:"force_ip" mapstructure:"force_ip"`
Proxy OptionProxy `json:"proxy" yaml:"proxy" toml:"proxy" mapstructure:"proxy"`
Timeout time.Duration `json:"timeout" yaml:"timeout" toml:"timeout" mapstructure:"timeout"`
DisableKeepAlive bool `json:"disable-keep-alive" yaml:"disable-keep-alive" toml:"disable-keep-alive" mapstructure:"disable-keep-alive"`
DisableCompression bool `json:"disable-compression" yaml:"disable-compression" toml:"disable-compression" mapstructure:"disable-compression"`
Http2 bool `json:"http2" yaml:"http2" toml:"http2" mapstructure:"http2"`
TLS OptionTLS `json:"tls" yaml:"tls" toml:"tls" mapstructure:"tls"`
ForceIP OptionForceIP `json:"force_ip" yaml:"force_ip" toml:"force_ip" mapstructure:"force_ip"`
Proxy OptionProxy `json:"proxy" yaml:"proxy" toml:"proxy" mapstructure:"proxy"`
}
func DefaultConfig(indent string) []byte {
@@ -74,6 +76,8 @@ func DefaultConfig(indent string) []byte {
res = bytes.NewBuffer(make([]byte, 0))
def = []byte(`{
"timeout":"0s",
"disable-keep-alive": false,
"disable-compression": false,
"http2": true,
"tls": ` + string(cmptls.DefaultConfig(cfgcst.JSONIndent)) + `,
"force_ip": {
@@ -119,7 +123,10 @@ func (o Options) Validate() liberr.Error {
}
func (o Options) GetClient(def libtls.TLSConfig, servername string) (*http.Client, liberr.Error) {
var tls libtls.TLSConfig
var (
tls libtls.TLSConfig
edp *url.URL
)
if t, e := o._GetTLS(def); e != nil {
return nil, e
@@ -127,15 +134,7 @@ func (o Options) GetClient(def libtls.TLSConfig, servername string) (*http.Clien
tls = t
}
var tr *http.Transport
tr = GetTransport(false, false, o.Http2)
SetTransportTLS(tr, tls, "")
SetTransportDial(tr, o.ForceIP.Enable, o.ForceIP.Net, o.ForceIP.IP, o.ForceIP.Local)
if o.Proxy.Enable && o.Proxy.Endpoint != nil {
var edp *url.URL
edp = &url.URL{
Scheme: o.Proxy.Endpoint.Scheme,
Opaque: o.Proxy.Endpoint.Opaque,
@@ -162,11 +161,15 @@ func (o Options) GetClient(def libtls.TLSConfig, servername string) (*http.Clien
}
}
if edp != nil && len(edp.String()) > 0 {
SetTransportProxy(tr, edp)
if edp != nil && len(edp.String()) < 1 {
edp = nil
}
}
var tr *http.Transport
tr = GetTransport(tls.TlsConfig(""), edp, o.DisableKeepAlive, o.DisableCompression, o.Http2)
SetTransportDial(tr, o.ForceIP.Enable, o.ForceIP.Net, o.ForceIP.IP, o.ForceIP.Local)
return GetClient(tr, o.Http2, o.Timeout)
}