@@ -7,54 +7,53 @@ 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 , network , address  string ) (net.Conn , error ) {
28+ 	return  func (ctx  context.Context , network , address  string ) (net.Conn , error ) {
29+ 		return  (& net.Dialer {Timeout : timeout }).DialContext (ctx , network , address )
30+ 	}
3931}
4032
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 
33+ func  NewRequest (url , method  string ) * Request  {
34+ 	return  & Request {
35+ 		url : url ,
36+ 		req : & http.Request {
37+ 			Method :     method ,
38+ 			Header :     make (http.Header ),
39+ 			Proto :      "HTTP/1.1" , // FIXME: from legacy httplib, it shouldn't be hardcoded 
40+ 			ProtoMajor : 1 ,
41+ 			ProtoMinor : 1 ,
42+ 		},
43+ 		params : map [string ]string {},
44+ 
45+ 		// ATTENTION: from legacy httplib, callers must pay more attention to it, it will cause annoying bugs when the response takes a long time 
46+ 		readWriteTimeout : 60  *  time .Second ,
47+ 	}
4848}
4949
50- // Request provides more useful methods for requesting one url than http.Request. 
5150type  Request  struct  {
52- 	url       string 
53- 	req       * http.Request 
54- 	params    map [string ]string 
55- 	 setting   Settings 
56- 	resp      * http. Response 
57- 	body     [] byte 
51+ 	url     string 
52+ 	req     * http.Request 
53+ 	params  map [string ]string 
54+ 
55+ 	readWriteTimeout  time. Duration 
56+ 	transport         http. RoundTripper 
5857}
5958
6059// SetContext sets the request's Context 
@@ -63,36 +62,24 @@ func (r *Request) SetContext(ctx context.Context) *Request {
6362	return  r 
6463}
6564
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 
65+ // SetTransport  sets the request transport, if not set, will use httplib's default transport with environment proxy support  
66+ // ATTENTION: the http.Transport has a connection pool, so it should be reused as much as possible, do not create a lot of transports 
67+ func  ( r   * Request )  SetTransport ( transport  http. RoundTripper )  * Request  { 
68+ 	r .transport  =  transport 
7069	return  r 
7170}
7271
7372func  (r  * Request ) SetReadWriteTimeout (readWriteTimeout  time.Duration ) * Request  {
74- 	r .setting . ReadWriteTimeout  =  readWriteTimeout 
73+ 	r .readWriteTimeout  =  readWriteTimeout 
7574	return  r 
7675}
7776
78- // SetTLSClientConfig sets tls connection configurations if visiting https url. 
79- func  (r  * Request ) SetTLSClientConfig (config  * tls.Config ) * Request  {
80- 	r .setting .TLSClientConfig  =  config 
81- 	return  r 
82- }
83- 
84- // Header add header item string in request. 
77+ // Header set header item string in request. 
8578func  (r  * Request ) Header (key , value  string ) * Request  {
8679	r .req .Header .Set (key , value )
8780	return  r 
8881}
8982
90- // SetTransport sets transport to 
91- func  (r  * Request ) SetTransport (transport  http.RoundTripper ) * Request  {
92- 	r .setting .Transport  =  transport 
93- 	return  r 
94- }
95- 
9683// Param adds query param in to request. 
9784// params build query string as ?key1=value1&key2=value2... 
9885func  (r  * Request ) Param (key , value  string ) * Request  {
@@ -125,11 +112,9 @@ func (r *Request) Body(data any) *Request {
125112	return  r 
126113}
127114
128- func  (r  * Request ) getResponse () (* http.Response , error ) {
129- 	if  r .resp .StatusCode  !=  0  {
130- 		return  r .resp , nil 
131- 	}
132- 
115+ // Response executes request client and returns the response. 
116+ // Caller MUST close the response body if no error occurs. 
117+ func  (r  * Request ) Response () (* http.Response , error ) {
133118	var  paramBody  string 
134119	if  len (r .params ) >  0  {
135120		var  buf  bytes.Buffer 
@@ -160,59 +145,19 @@ func (r *Request) getResponse() (*http.Response, error) {
160145		return  nil , err 
161146	}
162147
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- 
180148	client  :=  & http.Client {
181- 		Transport : trans ,
182- 		Timeout :   r .setting .ReadWriteTimeout ,
183- 	}
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 )
149+ 		Transport : r .transport ,
150+ 		Timeout :   r .readWriteTimeout ,
187151	}
188- 
189- 	resp , err  :=  client .Do (r .req )
190- 	if  err  !=  nil  {
191- 		return  nil , err 
152+ 	if  client .Transport  ==  nil  {
153+ 		client .Transport  =  defaultTransport ()
192154	}
193- 	r .resp  =  resp 
194- 	return  resp , nil 
195- }
196155
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" )
156+ 	if  r .req .Header .Get ("User-Agent" ) ==  ""  {
157+ 		r .req .Header .Set ("User-Agent" , "GiteaHttpLib" )
202158	}
203- 	return  r .getResponse ()
204- }
205159
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- 	}
160+ 	return  client .Do (r .req )
216161}
217162
218163func  (r  * Request ) GoString () string  {
0 commit comments