@@ -7,54 +7,59 @@ package httplib
77import (
88 "bytes"
99 "context"
10- "crypto/tls"
11- "errors"
1210 "fmt"
1311 "io"
1412 "net"
1513 "net/http"
1614 "net/url"
1715 "strings"
16+ "sync"
1817 "time"
1918)
2019
21- var defaultSetting = Settings {"GiteaServer" , 60 * time .Second , 60 * time .Second , nil , nil }
22-
23- // newRequest returns *Request with specific method
24- func newRequest (url , method string ) * Request {
25- var resp http.Response
26- req := http.Request {
27- Method : method ,
28- Header : make (http.Header ),
29- Proto : "HTTP/1.1" ,
30- ProtoMajor : 1 ,
31- ProtoMinor : 1 ,
20+ var defaultTransport = sync .OnceValue (func () http.RoundTripper {
21+ return & http.Transport {
22+ Proxy : http .ProxyFromEnvironment ,
23+ DialContext : DialContextWithTimeout (10 * time .Second ), // it is good enough in modern days
3224 }
33- return & Request {url , & req , map [string ]string {}, defaultSetting , & resp , nil }
34- }
25+ })
3526
36- // NewRequest returns *Request with specific method
37- func NewRequest (url , method string ) * Request {
38- return newRequest (url , method )
27+ func DialContextWithTimeout (timeout time.Duration ) func (ctx context.Context , netw , addr string ) (net.Conn , error ) {
28+ return func (ctx context.Context , netw , addr string ) (net.Conn , error ) {
29+ d := net.Dialer {Timeout : timeout }
30+ conn , err := d .DialContext (ctx , netw , addr )
31+ if err != nil {
32+ return nil , err
33+ }
34+ return conn , nil
35+ }
3936}
4037
41- // Settings is the default settings for http client
42- type Settings struct {
43- UserAgent string
44- ConnectTimeout time.Duration
45- ReadWriteTimeout time.Duration
46- TLSClientConfig * tls.Config
47- Transport http.RoundTripper
38+ func NewRequest (url , method string ) * Request {
39+ return & Request {
40+ url : url ,
41+ req : & http.Request {
42+ Method : method ,
43+ Header : make (http.Header ),
44+ Proto : "HTTP/1.1" , // FIXME: from legacy httplib, it shouldn't be hardcoded
45+ ProtoMajor : 1 ,
46+ ProtoMinor : 1 ,
47+ },
48+ params : map [string ]string {},
49+
50+ // from legacy httplib, caller's must pay more attention to it, it will cause annoying bugs when the response takes a long time
51+ readWriteTimeout : 60 * time .Second ,
52+ }
4853}
4954
50- // Request provides more useful methods for requesting one url than http.Request.
5155type Request struct {
52- url string
53- req * http.Request
54- params map [string ]string
55- setting Settings
56- resp * http.Response
57- body []byte
56+ url string
57+ req * http.Request
58+ params map [string ]string
59+ body []byte
60+
61+ readWriteTimeout time.Duration
62+ transport http.RoundTripper
5863}
5964
6065// SetContext sets the request's Context
@@ -63,36 +68,24 @@ func (r *Request) SetContext(ctx context.Context) *Request {
6368 return r
6469}
6570
66- // SetTimeout sets connect time out and read-write time out for BeegoRequest.
67- func ( r * Request ) SetTimeout ( connectTimeout , readWriteTimeout time. Duration ) * Request {
68- r . setting . ConnectTimeout = connectTimeout
69- r .setting . ReadWriteTimeout = readWriteTimeout
71+ // SetTransport sets the request transport, if not set, will use httplib's default transport with environment proxy support
72+ // ATTENTION: the http.Transport has connection pool, so you should reuse it as much as possible, do not create a lot of transports
73+ func ( r * Request ) SetTransport ( transport http. RoundTripper ) * Request {
74+ r .transport = transport
7075 return r
7176}
7277
7378func (r * Request ) SetReadWriteTimeout (readWriteTimeout time.Duration ) * Request {
74- r .setting .ReadWriteTimeout = readWriteTimeout
75- return r
76- }
77-
78- // SetTLSClientConfig sets tls connection configurations if visiting https url.
79- func (r * Request ) SetTLSClientConfig (config * tls.Config ) * Request {
80- r .setting .TLSClientConfig = config
79+ r .readWriteTimeout = readWriteTimeout
8180 return r
8281}
8382
84- // Header add header item string in request.
83+ // Header set header item string in request.
8584func (r * Request ) Header (key , value string ) * Request {
8685 r .req .Header .Set (key , value )
8786 return r
8887}
8988
90- // SetTransport sets transport to
91- func (r * Request ) SetTransport (transport http.RoundTripper ) * Request {
92- r .setting .Transport = transport
93- return r
94- }
95-
9689// Param adds query param in to request.
9790// params build query string as ?key1=value1&key2=value2...
9891func (r * Request ) Param (key , value string ) * Request {
@@ -125,11 +118,9 @@ func (r *Request) Body(data any) *Request {
125118 return r
126119}
127120
128- func (r * Request ) getResponse () (* http.Response , error ) {
129- if r .resp .StatusCode != 0 {
130- return r .resp , nil
131- }
132-
121+ // Response executes request client and returns the response.
122+ // Caller MUST close the response body if no error occurs.
123+ func (r * Request ) Response () (* http.Response , error ) {
133124 var paramBody string
134125 if len (r .params ) > 0 {
135126 var buf bytes.Buffer
@@ -160,59 +151,19 @@ func (r *Request) getResponse() (*http.Response, error) {
160151 return nil , err
161152 }
162153
163- trans := r .setting .Transport
164- if trans == nil {
165- // create default transport
166- trans = & http.Transport {
167- TLSClientConfig : r .setting .TLSClientConfig ,
168- Proxy : http .ProxyFromEnvironment ,
169- DialContext : TimeoutDialer (r .setting .ConnectTimeout ),
170- }
171- } else if t , ok := trans .(* http.Transport ); ok {
172- if t .TLSClientConfig == nil {
173- t .TLSClientConfig = r .setting .TLSClientConfig
174- }
175- if t .DialContext == nil {
176- t .DialContext = TimeoutDialer (r .setting .ConnectTimeout )
177- }
178- }
179-
180154 client := & http.Client {
181- Transport : trans ,
182- Timeout : r .setting . ReadWriteTimeout ,
155+ Transport : r . transport ,
156+ Timeout : r .readWriteTimeout ,
183157 }
184-
185- if len (r .setting .UserAgent ) > 0 && len (r .req .Header .Get ("User-Agent" )) == 0 {
186- r .req .Header .Set ("User-Agent" , r .setting .UserAgent )
187- }
188-
189- resp , err := client .Do (r .req )
190- if err != nil {
191- return nil , err
158+ if client .Transport == nil {
159+ client .Transport = defaultTransport ()
192160 }
193- r .resp = resp
194- return resp , nil
195- }
196161
197- // Response executes request client gets response manually.
198- // Caller MUST close the response body if no error occurs
199- func (r * Request ) Response () (* http.Response , error ) {
200- if r == nil {
201- return nil , errors .New ("invalid request" )
162+ if r .req .Header .Get ("User-Agent" ) == "" {
163+ r .req .Header .Set ("User-Agent" , "GiteaHttpLib" )
202164 }
203- return r .getResponse ()
204- }
205165
206- // TimeoutDialer returns functions of connection dialer with timeout settings for http.Transport Dial field.
207- func TimeoutDialer (cTimeout time.Duration ) func (ctx context.Context , net , addr string ) (c net.Conn , err error ) {
208- return func (ctx context.Context , netw , addr string ) (net.Conn , error ) {
209- d := net.Dialer {Timeout : cTimeout }
210- conn , err := d .DialContext (ctx , netw , addr )
211- if err != nil {
212- return nil , err
213- }
214- return conn , nil
215- }
166+ return client .Do (r .req )
216167}
217168
218169func (r * Request ) GoString () string {
0 commit comments