github.com/fumiama/terasu@v0.0.0-20240507144117-547a591149c0/http2/http2.go (about) 1 package http2 2 3 import ( 4 "context" 5 "crypto/tls" 6 "errors" 7 "io" 8 "net" 9 "net/http" 10 "net/url" 11 "time" 12 13 "github.com/FloatTech/ttl" 14 "golang.org/x/net/http2" 15 16 "github.com/fumiama/terasu" 17 "github.com/fumiama/terasu/dns" 18 "github.com/fumiama/terasu/ip" 19 ) 20 21 var ( 22 ErrEmptyHostAddress = errors.New("empty host addr") 23 ) 24 25 var defaultDialer = net.Dialer{ 26 Timeout: time.Minute, 27 } 28 29 func SetDefaultClientTimeout(t time.Duration) { 30 defaultDialer.Timeout = t 31 } 32 33 var lookupTable = ttl.NewCache[string, []string](time.Hour) 34 35 var DefaultClient = http.Client{ 36 Transport: &http2.Transport{ 37 DialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) { 38 if defaultDialer.Timeout != 0 { 39 var cancel context.CancelFunc 40 ctx, cancel = context.WithTimeout(ctx, defaultDialer.Timeout) 41 defer cancel() 42 } 43 44 if !defaultDialer.Deadline.IsZero() { 45 var cancel context.CancelFunc 46 ctx, cancel = context.WithDeadline(ctx, defaultDialer.Deadline) 47 defer cancel() 48 } 49 50 host, port, err := net.SplitHostPort(addr) 51 if err != nil { 52 return nil, err 53 } 54 addrs := lookupTable.Get(host) 55 if len(addrs) == 0 { 56 addrs, err = dns.DefaultResolver.LookupHost(ctx, host) 57 if err != nil { 58 if ip.IsIPv6Available.Get() { 59 addrs, err = dns.IPv6Servers.LookupHostFallback(ctx, host) 60 } else { 61 addrs, err = dns.IPv4Servers.LookupHostFallback(ctx, host) 62 } 63 if err != nil { 64 return nil, err 65 } 66 } 67 lookupTable.Set(host, addrs) 68 } 69 if len(addr) == 0 { 70 return nil, ErrEmptyHostAddress 71 } 72 var conn net.Conn 73 var tlsConn *tls.Conn 74 for _, a := range addrs { 75 conn, err = defaultDialer.DialContext(ctx, network, net.JoinHostPort(a, port)) 76 if err != nil { 77 continue 78 } 79 tlsConn = tls.Client(conn, cfg) 80 err = terasu.Use(tlsConn).HandshakeContext(ctx, terasu.DefaultFirstFragmentLen) 81 if err == nil { 82 break 83 } 84 _ = tlsConn.Close() 85 tlsConn = nil 86 conn, err = defaultDialer.DialContext(ctx, network, net.JoinHostPort(a, port)) 87 if err != nil { 88 continue 89 } 90 tlsConn = tls.Client(conn, cfg) 91 err = tlsConn.HandshakeContext(ctx) 92 if err == nil { 93 break 94 } 95 _ = tlsConn.Close() 96 tlsConn = nil 97 } 98 return tlsConn, err 99 }, 100 }, 101 } 102 103 func Get(url string) (resp *http.Response, err error) { 104 return DefaultClient.Get(url) 105 } 106 107 func Head(url string) (resp *http.Response, err error) { 108 return DefaultClient.Head(url) 109 } 110 111 func Post(url string, contentType string, body io.Reader) (resp *http.Response, err error) { 112 return DefaultClient.Post(url, contentType, body) 113 } 114 115 func PostForm(url string, data url.Values) (resp *http.Response, err error) { 116 return DefaultClient.PostForm(url, data) 117 }