...
1
2
3
4
5 package ldhttp
6
7 import (
8 "crypto/tls"
9 "crypto/x509"
10 "errors"
11 "fmt"
12 "net"
13 "net/http"
14 "net/url"
15 "os"
16 "time"
17 )
18
19 const defaultConnectTimeout = 10 * time.Second
20
21 type transportExtraOptions struct {
22 caCerts *x509.CertPool
23 connectTimeout time.Duration
24 proxyURL *url.URL
25 }
26
27
28 type TransportOption interface {
29 apply(opts *transportExtraOptions) error
30 }
31
32 type connectTimeoutOption struct {
33 timeout time.Duration
34 }
35
36 func (o connectTimeoutOption) apply(opts *transportExtraOptions) error {
37 opts.connectTimeout = o.timeout
38 return nil
39 }
40
41
42
43 func ConnectTimeoutOption(timeout time.Duration) TransportOption {
44 return connectTimeoutOption{timeout: timeout}
45 }
46
47 type caCertOption struct {
48 certData []byte
49 }
50
51 func (o caCertOption) apply(opts *transportExtraOptions) error {
52 if opts.caCerts == nil {
53 var err error
54 opts.caCerts, err = x509.SystemCertPool()
55 if err != nil {
56 opts.caCerts = x509.NewCertPool()
57 }
58 }
59 if !opts.caCerts.AppendCertsFromPEM(o.certData) {
60 return errors.New("invalid CA certificate data")
61 }
62 return nil
63 }
64
65
66
67 func CACertOption(certData []byte) TransportOption {
68 return caCertOption{certData: certData}
69 }
70
71 type caCertFileOption struct {
72 filePath string
73 }
74
75 func (o caCertFileOption) apply(opts *transportExtraOptions) error {
76 bytes, err := os.ReadFile(o.filePath)
77 if err != nil {
78 return fmt.Errorf("can't read CA certificate file: %v", err)
79 }
80 return caCertOption{certData: bytes}.apply(opts)
81 }
82
83
84
85 func CACertFileOption(filePath string) TransportOption {
86 return caCertFileOption{filePath: filePath}
87 }
88
89
90
91 func ProxyOption(url url.URL) TransportOption {
92 return proxyOption{url}
93 }
94
95 type proxyOption struct {
96 url url.URL
97 }
98
99 func (o proxyOption) apply(opts *transportExtraOptions) error {
100 opts.proxyURL = &o.url
101 return nil
102 }
103
104
105
106
107
108
109 func NewHTTPTransport(options ...TransportOption) (*http.Transport, *net.Dialer, error) {
110 extraOptions := transportExtraOptions{
111 connectTimeout: defaultConnectTimeout,
112 }
113 for _, o := range options {
114 err := o.apply(&extraOptions)
115 if err != nil {
116 return nil, nil, err
117 }
118 }
119 dialer := &net.Dialer{
120 Timeout: extraOptions.connectTimeout,
121 KeepAlive: 1 * time.Minute,
122 }
123 transport := newDefaultTransport()
124 transport.DialContext = dialer.DialContext
125 if extraOptions.caCerts != nil {
126 transport.TLSClientConfig = &tls.Config{RootCAs: extraOptions.caCerts}
127 }
128 if extraOptions.proxyURL != nil {
129 transport.Proxy = http.ProxyURL(extraOptions.proxyURL)
130 }
131 return transport, dialer, nil
132 }
133
134 func newDefaultTransport() *http.Transport {
135
136
137 return &http.Transport{
138 Proxy: http.ProxyFromEnvironment,
139 MaxIdleConns: 100,
140 IdleConnTimeout: 90 * time.Second,
141 TLSHandshakeTimeout: 10 * time.Second,
142 ExpectContinueTimeout: 1 * time.Second,
143 }
144 }
145
View as plain text