@@ -10,6 +10,12 @@ import rand
1010import strings
1111import time
1212
13+ pub type RequestRedirectFn = fn (request & Request, nredirects int , new_url string ) !
14+
15+ pub type RequestProgressFn = fn (request & Request, chunk []u8 , read_so_far u64 ) !
16+
17+ pub type RequestFinishFn = fn (request & Request, final_size u64 ) !
18+
1319// Request holds information about an HTTP request (either received by
1420// a server or to be sent by a client)
1521pub struct Request {
@@ -35,6 +41,10 @@ pub mut:
3541 cert_key string
3642 in_memory_verification bool // if true, verify, cert, and cert_key are read from memory, not from a file
3743 allow_redirect bool = true // whether to allow redirect
44+ // callbacks to allow custom reporting code to run, while the request is running
45+ on_redirect RequestRedirectFn = unsafe { nil }
46+ on_progress RequestProgressFn = unsafe { nil }
47+ on_finish RequestFinishFn = unsafe { nil }
3848}
3949
4050fn (mut req Request) free () {
@@ -58,9 +68,9 @@ pub fn (req &Request) do() !Response {
5868 mut url := urllib.parse (req.url) or { return error ('http.Request.do: invalid url ${req.url} ' ) }
5969 mut rurl := url
6070 mut resp := Response{}
61- mut no_redirects := 0
71+ mut nredirects := 0
6272 for {
63- if no_redirects == max_redirects {
73+ if nredirects == max_redirects {
6474 return error ('http.request.do: maximum number of redirects reached (${max_redirects} )' )
6575 }
6676 qresp := req.method_and_url_to_response (req.method, rurl)!
@@ -80,11 +90,14 @@ pub fn (req &Request) do() !Response {
8090 }
8191 redirect_url = url.str ()
8292 }
93+ if req.on_redirect != unsafe { nil } {
94+ req.on_redirect (req, nredirects, redirect_url)!
95+ }
8396 qrurl := urllib.parse (redirect_url) or {
8497 return error ('http.request.do: invalid URL in redirect "${redirect_url} "' )
8598 }
8699 rurl = qrurl
87- no_redirects ++
100+ nredirects ++
88101 }
89102 return resp
90103}
@@ -164,15 +177,35 @@ fn (req &Request) http_do(host string, method Method, path string) !Response {
164177 $if trace_http_request ? {
165178 eprintln ('> ${s} ' )
166179 }
167- mut bytes := io. read_all (reader: client)!
180+ mut bytes := req. read_all_from_client_connection ( client)!
168181 client.close ()!
169182 response_text := bytes.bytestr ()
170183 $if trace_http_response ? {
171184 eprintln ('< ${response_text} ' )
172185 }
186+ if req.on_finish != unsafe { nil } {
187+ req.on_finish (req, u64 (response_text.len))!
188+ }
173189 return parse_response (response_text)
174190}
175191
192+ fn (req &Request) read_all_from_client_connection (r & net.TcpConn) ! []u8 {
193+ mut read := i64 (0 )
194+ mut b := []u8 {len: 32768 }
195+ for {
196+ old_read := read
197+ new_read := r.read (mut b[read..]) or { break }
198+ read + = new_read
199+ if req.on_progress != unsafe { nil } {
200+ req.on_progress (req, b[old_read..read], u64 (read))!
201+ }
202+ for b.len < = read {
203+ unsafe { b.grow_len (4096 ) }
204+ }
205+ }
206+ return b[..read]
207+ }
208+
176209// referer returns 'Referer' header value of the given request
177210pub fn (req &Request) referer () string {
178211 return req.header.get (.referer) or { '' }
0 commit comments