aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDiego Nehab <diego@tecgraf.puc-rio.br>2001-06-06 20:55:45 +0000
committerDiego Nehab <diego@tecgraf.puc-rio.br>2001-06-06 20:55:45 +0000
commitc53ad62b00b9dbf2a54214c2c6bf3f06d7c43eea (patch)
tree07d34dbcd03c4ac81353fb716ea4e7c914e0e633 /src
parent77090c53fe558c48680516410a928bece08dd0d5 (diff)
downloadluasocket-c53ad62b00b9dbf2a54214c2c6bf3f06d7c43eea.tar.gz
luasocket-c53ad62b00b9dbf2a54214c2c6bf3f06d7c43eea.tar.bz2
luasocket-c53ad62b00b9dbf2a54214c2c6bf3f06d7c43eea.zip
Streaming by callbacks implemented.
Diffstat (limited to 'src')
-rw-r--r--src/ftp.lua173
-rw-r--r--src/http.lua389
2 files changed, 366 insertions, 196 deletions
diff --git a/src/ftp.lua b/src/ftp.lua
index a9dac71..229bbc6 100644
--- a/src/ftp.lua
+++ b/src/ftp.lua
@@ -15,6 +15,8 @@ local PORT = 21
15-- this is the default anonymous password. used when no password is 15-- this is the default anonymous password. used when no password is
16-- provided in url. should be changed for your e-mail. 16-- provided in url. should be changed for your e-mail.
17local EMAIL = "anonymous@anonymous.org" 17local EMAIL = "anonymous@anonymous.org"
18-- block size used in transfers
19local BLOCKSIZE = 4096
18 20
19----------------------------------------------------------------------------- 21-----------------------------------------------------------------------------
20-- Parses a url and returns its scheme, user, password, host, port 22-- Parses a url and returns its scheme, user, password, host, port
@@ -68,7 +70,7 @@ local get_pasv = function(pasv)
68 local ip, port 70 local ip, port
69 _,_, a, b, c, d, p1, p2 = 71 _,_, a, b, c, d, p1, p2 =
70 strfind(pasv, "(%d*),(%d*),(%d*),(%d*),(%d*),(%d*)") 72 strfind(pasv, "(%d*),(%d*),(%d*),(%d*),(%d*),(%d*)")
71 if not a or not b or not c or not d or not p1 or not p2 then 73 if not (a and b and c and d and p1 and p2) then
72 return nil, nil 74 return nil, nil
73 end 75 end
74 ip = format("%d.%d.%d.%d", a, b, c, d) 76 ip = format("%d.%d.%d.%d", a, b, c, d)
@@ -82,6 +84,8 @@ end
82-- control: control connection socket 84-- control: control connection socket
83-- cmd: command 85-- cmd: command
84-- arg: command argument if any 86-- arg: command argument if any
87-- Returns
88-- error message in case of error, nil otherwise
85----------------------------------------------------------------------------- 89-----------------------------------------------------------------------------
86local send_command = function(control, cmd, arg) 90local send_command = function(control, cmd, arg)
87 local line, err 91 local line, err
@@ -284,35 +288,84 @@ local logout = function(control)
284end 288end
285 289
286----------------------------------------------------------------------------- 290-----------------------------------------------------------------------------
291-- Receives data and send it to a callback
292-- Input
293-- data: data connection
294-- callback: callback to return file contents
295-- Returns
296-- nil if successfull, or an error message in case of error
297-----------------------------------------------------------------------------
298local receive_indirect = function(data, callback)
299 local chunk, err, res
300 while not err do
301 chunk, err = data:receive(%BLOCKSIZE)
302 if err == "closed" then err = "done" end
303 res = callback(chunk, err)
304 if not res then break end
305 end
306end
307
308-----------------------------------------------------------------------------
287-- Retrieves file or directory listing 309-- Retrieves file or directory listing
288-- Input 310-- Input
289-- control: control connection with server 311-- control: control connection with server
290-- server: server socket bound to local address 312-- server: server socket bound to local address
291-- file: file name under current directory 313-- file: file name under current directory
292-- isdir: is file a directory name? 314-- isdir: is file a directory name?
315-- callback: callback to receive file contents
293-- Returns 316-- Returns
294-- file: string with file contents, nil if error 317-- err: error message in case of error, nil otherwise
295-- answer: server answer or error message
296----------------------------------------------------------------------------- 318-----------------------------------------------------------------------------
297local retrieve_file = function(control, server, file, isdir) 319local retrieve = function(control, server, file, isdir, callback)
320 local code, answer
298 local data 321 local data
299 -- ask server for file or directory listing accordingly 322 -- ask server for file or directory listing accordingly
300 if isdir then code, answer = %try_command(control, "nlst", file, {150, 125}) 323 if isdir then code, answer = %try_command(control, "nlst", file, {150, 125})
301 else code, answer = %try_command(control, "retr", file, {150, 125}) end 324 else code, answer = %try_command(control, "retr", file, {150, 125}) end
302 data, answer = server:accept() 325 data, answer = server:accept()
303 server:close() 326 server:close()
304 if not data then return nil, answer end 327 if not data then
305 -- download whole file
306 file, err = data:receive("*a")
307 data:close()
308 if err then
309 control:close() 328 control:close()
310 return nil, err 329 return answer
311 end 330 end
331 answer = %receive_indirect(data, callback)
332 if answer then
333 control:close()
334 return answer
335 end
336 data:close()
312 -- make sure file transfered ok 337 -- make sure file transfered ok
313 code, answer = %check_answer(control, {226, 250}) 338 code, answer = %check_answer(control, {226, 250})
314 if not code then return nil, answer 339 if not code then return answer end
315 else return file, answer end 340end
341
342-----------------------------------------------------------------------------
343-- Sends data comming from a callback
344-- Input
345-- data: data connection
346-- send_cb: callback to produce file contents
347-- chunk, size: first callback results
348-- Returns
349-- nil if successfull, or an error message in case of error
350-----------------------------------------------------------------------------
351local try_sendindirect = function(data, send_cb, chunk, size)
352 local sent, err
353 sent = 0
354 while 1 do
355 if type(chunk) ~= "string" or type(size) ~= "number" then
356 data:close()
357 if not chunk and type(size) == "string" then return size
358 else return "invalid callback return" end
359 end
360 err = data:send(chunk)
361 if err then
362 data:close()
363 return err
364 end
365 sent = sent + strlen(chunk)
366 if sent >= size then break end
367 chunk, size = send_cb()
368 end
316end 369end
317 370
318----------------------------------------------------------------------------- 371-----------------------------------------------------------------------------
@@ -321,29 +374,34 @@ end
321-- control: control connection with server 374-- control: control connection with server
322-- server: server socket bound to local address 375-- server: server socket bound to local address
323-- file: file name under current directory 376-- file: file name under current directory
324-- bytes: file contents in string 377-- send_cb: callback to produce the file contents
325-- Returns 378-- Returns
326-- code: return code, nil if error 379-- code: return code, nil if error
327-- answer: server answer or error message 380-- answer: server answer or error message
328----------------------------------------------------------------------------- 381-----------------------------------------------------------------------------
329local store_file = function (control, server, file, bytes) 382local store = function(control, server, file, send_cb)
330 local data 383 local data
331 local code, answer = %try_command(control, "stor", file, {150, 125}) 384 local code, answer = %try_command(control, "stor", file, {150, 125})
332 if not code then 385 if not code then
333 data:close() 386 control:close()
334 return nil, answer 387 return nil, answer
335 end 388 end
389 -- start data connection
336 data, answer = server:accept() 390 data, answer = server:accept()
337 server:close() 391 server:close()
338 if not data then return nil, answer end 392 if not data then
339 -- send whole file and close connection to mark file end
340 answer = data:send(bytes)
341 data:close()
342 if answer then
343 control:close() 393 control:close()
344 return nil, answer 394 return nil, answer
395 end
396 -- send whole file
397 err = %try_sendindirect(data, send_cb, send_cb())
398 if err then
399 control:close()
400 return nil, err
345 end 401 end
346 -- check if file was received right 402 -- close connection to inform that file transmission is complete
403 data:close()
404 -- check if file was received correctly
347 return %check_answer(control, {226, 250}) 405 return %check_answer(control, {226, 250})
348end 406end
349 407
@@ -365,55 +423,53 @@ end
365-- Retrieve a file from a ftp server 423-- Retrieve a file from a ftp server
366-- Input 424-- Input
367-- url: file location 425-- url: file location
426-- receive_cb: callback to receive file contents
368-- type: "binary" or "ascii" 427-- type: "binary" or "ascii"
369-- Returns 428-- Returns
370-- file: downloaded file or nil in case of error
371-- err: error message if any 429-- err: error message if any
372----------------------------------------------------------------------------- 430-----------------------------------------------------------------------------
373function ftp_get(url, type) 431function ftp_getindirect(url, receive_cb, type)
374 local control, server, data, err 432 local control, server, data, err
375 local answer, code, server, pfile, file 433 local answer, code, server, pfile, file
376 parsed = %split_url(url, {user = "anonymous", port = 21, pass = %EMAIL}) 434 parsed = %split_url(url, {user = "anonymous", port = 21, pass = %EMAIL})
377 -- start control connection 435 -- start control connection
378 control, err = connect(parsed.host, parsed.port) 436 control, err = connect(parsed.host, parsed.port)
379 if not control then return nil, err end 437 if not control then return err end
380 control:timeout(%TIMEOUT) 438 control:timeout(%TIMEOUT)
381 -- get and check greeting 439 -- get and check greeting
382 code, answer = %check_greeting(control) 440 code, answer = %check_greeting(control)
383 if not code then return nil, answer end 441 if not code then return answer end
384 -- try to log in 442 -- try to log in
385 code, answer = %login(control, parsed.user, parsed.pass) 443 code, answer = %login(control, parsed.user, parsed.pass)
386 if not code then return nil, answer end 444 if not code then return answer end
387 -- go to directory 445 -- go to directory
388 pfile = %split_path(parsed.path) 446 pfile = %split_path(parsed.path)
389 if not pfile then return nil, "invalid path" end 447 if not pfile then return "invalid path" end
390 code, answer = %cwd(control, pfile.path) 448 code, answer = %cwd(control, pfile.path)
391 if not code then return nil, answer end 449 if not code then return answer end
392 -- change to binary type? 450 -- change to binary type?
393 code, answer = %change_type(control, type) 451 code, answer = %change_type(control, type)
394 if not code then return nil, answer end 452 if not code then return answer end
395 -- setup passive connection 453 -- setup passive connection
396 server, answer = %port(control) 454 server, answer = %port(control)
397 if not server then return nil, answer end 455 if not server then return answer end
398 -- ask server to send file or directory listing 456 -- ask server to send file or directory listing
399 file, answer = %retrieve_file(control, server, pfile.name, pfile.isdir) 457 err = %retrieve(control, server, pfile.name, pfile.isdir, receive_cb)
400 if not file then return nil, answer end 458 if err then return err end
401 -- disconnect 459 -- disconnect
402 %logout(control) 460 %logout(control)
403 -- return whatever file we received plus a possible error message
404 return file, answer
405end 461end
406 462
407----------------------------------------------------------------------------- 463-----------------------------------------------------------------------------
408-- Uploads a file to a FTP server 464-- Uploads a file to a FTP server
409-- Input 465-- Input
410-- url: file location 466-- url: file location
411-- bytes: file contents 467-- send_cb: callback to produce the file contents
412-- type: "binary" or "ascii" 468-- type: "binary" or "ascii"
413-- Returns 469-- Returns
414-- err: error message if any 470-- err: error message if any
415----------------------------------------------------------------------------- 471-----------------------------------------------------------------------------
416function ftp_put(url, bytes, type) 472function ftp_putindirect(url, send_cb, type)
417 local control, data 473 local control, data
418 local answer, code, server, file, pfile 474 local answer, code, server, file, pfile
419 parsed = %split_url(url, {user = "anonymous", port = 21, pass = %EMAIL}) 475 parsed = %split_url(url, {user = "anonymous", port = 21, pass = %EMAIL})
@@ -439,10 +495,51 @@ function ftp_put(url, bytes, type)
439 server, answer = %port(control) 495 server, answer = %port(control)
440 if not server then return answer end 496 if not server then return answer end
441 -- ask server to send file 497 -- ask server to send file
442 code, answer = %store_file(control, server, pfile.name, bytes) 498 code, answer = %store(control, server, pfile.name, send_cb)
443 if not code then return answer end 499 if not code then return answer end
444 -- disconnect 500 -- disconnect
445 %logout(control) 501 %logout(control)
446 -- no errors 502 -- no errors
447 return nil 503 return nil
448end 504end
505
506-----------------------------------------------------------------------------
507-- Uploads a file to a FTP server
508-- Input
509-- url: file location
510-- bytes: file contents
511-- type: "binary" or "ascii"
512-- Returns
513-- err: error message if any
514-----------------------------------------------------------------------------
515function ftp_put(url, bytes, type)
516 local send_cb = function()
517 return %bytes, strlen(%bytes)
518 end
519 return ftp_putindirect(url, send_cb, type)
520end
521
522-----------------------------------------------------------------------------
523-- We need fast concatenation routines for direct requests
524-----------------------------------------------------------------------------
525dofile("buffer.lua")
526
527-----------------------------------------------------------------------------
528-- Retrieve a file from a ftp server
529-- Input
530-- url: file location
531-- type: "binary" or "ascii"
532-- Returns
533-- data: file contents as a string
534-- err: error message in case of error, nil otherwise
535-----------------------------------------------------------------------------
536function ftp_get(url, type)
537 local bytes = { buf = buf_create() }
538 local receive_cb = function(chunk, err)
539 if not chunk then %bytes.buf = nil end
540 buf_addstring(%bytes.buf, chunk)
541 return 1
542 end
543 err = ftp_getindirect(url, receive_cb, type)
544 return buf_getresult(bytes.buf), err
545end
diff --git a/src/http.lua b/src/http.lua
index d1e4894..38d54d0 100644
--- a/src/http.lua
+++ b/src/http.lua
@@ -14,7 +14,9 @@ local TIMEOUT = 60
14-- default port for document retrieval 14-- default port for document retrieval
15local PORT = 80 15local PORT = 80
16-- user agent field sent in request 16-- user agent field sent in request
17local USERAGENT = "LuaSocket 1.3 HTTP 1.1" 17local USERAGENT = "LuaSocket 1.3b HTTP 1.1"
18-- block size used in transfers
19local BLOCKSIZE = 4096
18 20
19----------------------------------------------------------------------------- 21-----------------------------------------------------------------------------
20-- Tries to get a pattern from the server and closes socket on error 22-- Tries to get a pattern from the server and closes socket on error
@@ -26,7 +28,7 @@ local USERAGENT = "LuaSocket 1.3 HTTP 1.1"
26----------------------------------------------------------------------------- 28-----------------------------------------------------------------------------
27local try_get = function(...) 29local try_get = function(...)
28 local sock = arg[1] 30 local sock = arg[1]
29 local data, err = call(sock.receive, arg) 31 local data, err = call(sock.receive, arg)
30 if err then 32 if err then
31 sock:close() 33 sock:close()
32 return nil, err 34 return nil, err
@@ -79,14 +81,14 @@ end
79-- Receive and parse responce header fields 81-- Receive and parse responce header fields
80-- Input 82-- Input
81-- sock: socket connected to the server 83-- sock: socket connected to the server
82-- headers: a table that might already contain headers 84-- hdrs: a table that might already contain headers
83-- Returns 85-- Returns
84-- headers: a table with all headers fields in the form 86-- hdrs: a table with all headers fields in the form
85-- {name_1 = "value_1", name_2 = "value_2" ... name_n = "value_n"} 87-- {name_1 = "value_1", name_2 = "value_2" ... name_n = "value_n"}
86-- all name_i are lowercase 88-- all name_i are lowercase
87-- nil and error message in case of error 89-- nil and error message in case of error
88----------------------------------------------------------------------------- 90-----------------------------------------------------------------------------
89local get_hdrs = function(sock, headers) 91local get_hdrs = function(sock, hdrs)
90 local line, err 92 local line, err
91 local name, value 93 local name, value
92 -- get first line 94 -- get first line
@@ -111,106 +113,114 @@ local get_hdrs = function(sock, headers)
111 if err then return nil, err end 113 if err then return nil, err end
112 end 114 end
113 -- save pair in table 115 -- save pair in table
114 if headers[name] then headers[name] = headers[name] .. ", " .. value 116 if hdrs[name] then hdrs[name] = hdrs[name] .. ", " .. value
115 else headers[name] = value end 117 else hdrs[name] = value end
116 end 118 end
117 return headers 119 return hdrs
118end 120end
119 121
120----------------------------------------------------------------------------- 122-----------------------------------------------------------------------------
121-- Receives a chunked message body 123-- Receives a chunked message body
122-- Input 124-- Input
123-- sock: socket connected to the server 125-- sock: socket connected to the server
124-- callback: function to receive chunks 126-- receive_cb: function to receive chunks
125-- Returns 127-- Returns
126-- nil if successfull or an error message in case of error 128-- nil if successfull or an error message in case of error
127----------------------------------------------------------------------------- 129-----------------------------------------------------------------------------
128local try_getchunked = function(sock, callback) 130local try_getchunked = function(sock, receive_cb)
129 local chunk, size, line, err 131 local chunk, size, line, err, go, uerr, _
130 repeat 132 repeat
131 -- get chunk size, skip extention 133 -- get chunk size, skip extention
132 line, err = %try_get(sock) 134 line, err = %try_get(sock)
133 if err then 135 if err then
134 callback(nil, err) 136 local _, uerr = receive_cb(nil, err)
135 return err 137 return uerr or err
136 end 138 end
137 size = tonumber(gsub(line, ";.*", ""), 16) 139 size = tonumber(gsub(line, ";.*", ""), 16)
138 if not size then 140 if not size then
141 err = "invalid chunk size"
139 sock:close() 142 sock:close()
140 callback(nil, "invalid chunk size") 143 _, uerr = receive_cb(nil, err)
141 return "invalid chunk size" 144 return uerr or err
142 end 145 end
143 -- get chunk 146 -- get chunk
144 chunk, err = %try_get(sock, size) 147 chunk, err = %try_get(sock, size)
145 if err then 148 if err then
146 callback(nil, err) 149 _, uerr = receive_cb(nil, err)
147 return err 150 return uerr or err
148 end 151 end
149 -- pass chunk to callback 152 -- pass chunk to callback
150 if not callback(chunk) then 153 go, uerr = receive_cb(chunk)
151 sock:close() 154 if not go then
152 return "aborted by callback" 155 sock:close()
153 end 156 return uerr or "aborted by callback"
157 end
154 -- skip blank line 158 -- skip blank line
155 _, err = %try_get(sock) 159 _, err = %try_get(sock)
156 if err then 160 if err then
157 callback(nil, err) 161 _, uerr = receive_cb(nil, err)
158 return err 162 return uerr or err
159 end 163 end
160 until size <= 0 164 until size <= 0
161 -- let callback know we are done 165 -- let callback know we are done
162 callback("", "done") 166 _, uerr = receive_cb("")
167 return uerr
163end 168end
164 169
165----------------------------------------------------------------------------- 170-----------------------------------------------------------------------------
166-- Receives a message body by content-length 171-- Receives a message body by content-length
167-- Input 172-- Input
168-- sock: socket connected to the server 173-- sock: socket connected to the server
169-- callback: function to receive chunks 174-- receive_cb: function to receive chunks
170-- Returns 175-- Returns
171-- nil if successfull or an error message in case of error 176-- nil if successfull or an error message in case of error
172----------------------------------------------------------------------------- 177-----------------------------------------------------------------------------
173local try_getbylength = function(sock, length, callback) 178local try_getbylength = function(sock, length, receive_cb)
174 while length > 0 do 179 local uerr, go
175 local size = min(4096, length) 180 while length > 0 do
176 local chunk, err = sock:receive(size) 181 local size = min(%BLOCKSIZE, length)
177 if err then 182 local chunk, err = sock:receive(size)
178 callback(nil, err) 183 if err then
179 return err 184 go, uerr = receive_cb(nil, err)
180 end 185 return uerr or err
181 if not callback(chunk) then 186 end
182 sock:close() 187 go, uerr = receive_cb(chunk)
183 return "aborted by callback" 188 if not go then
184 end 189 sock:close()
185 length = length - size 190 return uerr or "aborted by callback"
186 end 191 end
187 callback("", "done") 192 length = length - size
193 end
194 go, uerr = receive_cb("")
195 return uerr
188end 196end
189 197
190----------------------------------------------------------------------------- 198-----------------------------------------------------------------------------
191-- Receives a message body by content-length 199-- Receives a message body by content-length
192-- Input 200-- Input
193-- sock: socket connected to the server 201-- sock: socket connected to the server
194-- callback: function to receive chunks 202-- receive_cb: function to receive chunks
195-- Returns 203-- Returns
196-- nil if successfull or an error message in case of error 204-- nil if successfull or an error message in case of error
197----------------------------------------------------------------------------- 205-----------------------------------------------------------------------------
198local try_getuntilclosed = function(sock, callback) 206local try_getuntilclosed = function(sock, receive_cb)
199 local err 207 local err, go, uerr
200 while 1 do 208 while 1 do
201 local chunk, err = sock:receive(4096) 209 local chunk, err = sock:receive(%BLOCKSIZE)
202 if err == "closed" or not err then 210 if err == "closed" or not err then
203 if not callback(chunk) then 211 go, uerr = receive_cb(chunk)
204 sock:close() 212 if not go then
205 return "aborted by callback" 213 sock:close()
206 end 214 return uerr or "aborted by callback"
207 if err then break end 215 end
208 else 216 if err then break end
209 callback(nil, err) 217 else
210 return err 218 go, uerr = callback(nil, err)
211 end 219 return uerr or err
212 end 220 end
213 callback("", "done") 221 end
222 go, uerr = receive_cb("")
223 return uerr
214end 224end
215 225
216----------------------------------------------------------------------------- 226-----------------------------------------------------------------------------
@@ -218,22 +228,22 @@ end
218-- Input 228-- Input
219-- sock: socket connected to the server 229-- sock: socket connected to the server
220-- resp_hdrs: response header fields 230-- resp_hdrs: response header fields
221-- callback: function to receive chunks 231-- receive_cb: function to receive chunks
222-- Returns 232-- Returns
223-- nil if successfull or an error message in case of error 233-- nil if successfull or an error message in case of error
224----------------------------------------------------------------------------- 234-----------------------------------------------------------------------------
225local try_getbody = function(sock, resp_hdrs, callback) 235local try_getbody = function(sock, resp_hdrs, receive_cb)
226 local err 236 local err
227 if resp_hdrs["transfer-encoding"] == "chunked" then 237 if resp_hdrs["transfer-encoding"] == "chunked" then
228 -- get by chunked transfer-coding of message body 238 -- get by chunked transfer-coding of message body
229 return %try_getchunked(sock, callback) 239 return %try_getchunked(sock, receive_cb)
230 elseif tonumber(resp_hdrs["content-length"]) then 240 elseif tonumber(resp_hdrs["content-length"]) then
231 -- get by content-length 241 -- get by content-length
232 local length = tonumber(resp_hdrs["content-length"]) 242 local length = tonumber(resp_hdrs["content-length"])
233 return %try_getbylength(sock, length, callback) 243 return %try_getbylength(sock, length, receive_cb)
234 else 244 else
235 -- get it all until connection closes 245 -- get it all until connection closes
236 return %try_getuntilclosed(sock, callback) 246 return %try_getuntilclosed(sock, receive_cb)
237 end 247 end
238end 248end
239 249
@@ -276,51 +286,73 @@ local split_url = function(url, default)
276end 286end
277 287
278----------------------------------------------------------------------------- 288-----------------------------------------------------------------------------
289-- Sends data comming from a callback
290-- Input
291-- data: data connection
292-- send_cb: callback to produce file contents
293-- chunk, size: first callback results
294-- Returns
295-- nil if successfull, or an error message in case of error
296-----------------------------------------------------------------------------
297local try_sendindirect = function(data, send_cb, chunk, size)
298 local sent, err
299 sent = 0
300 while 1 do
301 if type(chunk) ~= "string" or type(size) ~= "number" then
302 data:close()
303 if not chunk and type(size) == "string" then return size
304 else return "invalid callback return" end
305 end
306 err = data:send(chunk)
307 if err then
308 data:close()
309 return err
310 end
311 sent = sent + strlen(chunk)
312 if sent >= size then break end
313 chunk, size = send_cb()
314 end
315end
316
317-----------------------------------------------------------------------------
279-- Sends a http request message through socket 318-- Sends a http request message through socket
280-- Input 319-- Input
281-- sock: socket connected to the server 320-- sock: socket connected to the server
282-- method: request method to be used 321-- method: request method to be used
283-- path: url path 322-- path: url path
284-- req_hdrs: request headers to be sent 323-- req_hdrs: request headers to be sent
285-- callback: callback to send request message body 324-- req_body_cb: callback to send request message body
286-- Returns 325-- Returns
287-- err: nil in case of success, error message otherwise 326-- err: nil in case of success, error message otherwise
288----------------------------------------------------------------------------- 327-----------------------------------------------------------------------------
289local send_request = function(sock, method, path, req_hdrs, callback) 328local send_request = function(sock, method, path, req_hdrs, req_body_cb)
290 local chunk, size, done 329 local chunk, size, done, err
291 -- send request line 330 -- send request line
292 local err = %try_send(sock, method .. " " .. path .. " HTTP/1.1\r\n") 331 err = %try_send(sock, method .. " " .. path .. " HTTP/1.1\r\n")
293 if err then return err end 332 if err then return err end
294 -- send request headers 333 -- if there is a request message body, add content-length header
334 if req_body_cb then
335 chunk, size = req_body_cb()
336 if type(chunk) == "string" and type(size) == "number" then
337 req_hdrs["content-length"] = tostring(size)
338 else
339 sock:close()
340 if not chunk and type(size) == "string" then return size
341 else return "invalid callback return" end
342 end
343 end
344 -- send request headers
295 for i, v in req_hdrs do 345 for i, v in req_hdrs do
296 err = %try_send(sock, i .. ": " .. v .. "\r\n") 346 err = %try_send(sock, i .. ": " .. v .. "\r\n")
297 if err then return err end 347 if err then return err end
298 end 348 end
299 -- if there is a request message body, add content-length header 349 -- mark end of request headers
300 if callback then
301 chunk, size = callback()
302 if chunk and size then
303 err = %try_send(sock, "content-length: "..tostring(size).."\r\n")
304 if err then return err end
305 else
306 sock:close()
307 return size or "invalid callback return"
308 end
309 end
310 -- mark end of request headers
311 err = %try_send(sock, "\r\n") 350 err = %try_send(sock, "\r\n")
312 if err then return err end 351 if err then return err end
313 -- send message request body, getting it chunk by chunk from callback 352 -- send request message body, if any
314 if callback then 353 if req_body_cb then
315 done = 0 354 return %try_sendindirect(sock, req_body_cb, chunk, size)
316 while chunk and chunk ~= "" and done < size do 355 end
317 err = %try_send(sock, chunk)
318 if err then return err end
319 done = done + strlen(chunk)
320 chunk, err = callback()
321 end
322 if not chunk then return err end
323 end
324end 356end
325 357
326----------------------------------------------------------------------------- 358-----------------------------------------------------------------------------
@@ -344,18 +376,17 @@ end
344dofile("base64.lua") 376dofile("base64.lua")
345 377
346----------------------------------------------------------------------------- 378-----------------------------------------------------------------------------
347-- Converts field names to lowercase and add message body size specification 379-- Converts field names to lowercase and adds a few needed headers
348-- Input 380-- Input
349-- headers: request header fields 381-- hdrs: request header fields
350-- parsed: parsed url components 382-- parsed: parsed url components
351-- body: request message body, if any
352-- Returns 383-- Returns
353-- lower: a table with the same headers, but with lowercase field names 384-- lower: a table with the same headers, but with lowercase field names
354----------------------------------------------------------------------------- 385-----------------------------------------------------------------------------
355local fill_hdrs = function(headers, parsed, body) 386local fill_hdrs = function(hdrs, parsed)
356 local lower = {} 387 local lower = {}
357 headers = headers or {} 388 hdrs = hdrs or {}
358 for i,v in headers do 389 for i,v in hdrs do
359 lower[strlower(i)] = v 390 lower[strlower(i)] = v
360 end 391 end
361 lower["connection"] = "close" 392 lower["connection"] = "close"
@@ -374,15 +405,17 @@ end
374-- Input 405-- Input
375-- method: "GET", "PUT", "POST" etc 406-- method: "GET", "PUT", "POST" etc
376-- url: target uniform resource locator 407-- url: target uniform resource locator
377-- req_hdrs: request headers to send 408-- resp_body_cb: response message body receive callback
378-- req_body: function to return request message body 409-- req_hdrs: request headers to send, or nil if none
379-- resp_body: function to receive response message body 410-- req_body_cb: request message body send callback, or nil if none
411-- stay: should we refrain from following a server redirect message?
380-- Returns 412-- Returns
381-- resp_hdrs: response header fields received, if sucessfull 413-- resp_hdrs: response header fields received, or nil if failed
382-- resp_line: server response status line, if successfull 414-- resp_line: server response status line, or nil if failed
383-- err: error message if any 415-- err: error message, or nil if successfull
384----------------------------------------------------------------------------- 416-----------------------------------------------------------------------------
385function http_requestindirect(method, url, req_hdrs, req_body, resp_body) 417function http_requestindirect(method, url, resp_body_cb, req_hdrs,
418 req_body_cb, stay)
386 local sock, err 419 local sock, err
387 local resp_hdrs 420 local resp_hdrs
388 local resp_line, resp_code 421 local resp_line, resp_code
@@ -398,7 +431,7 @@ function http_requestindirect(method, url, req_hdrs, req_body, resp_body)
398 -- set connection timeout 431 -- set connection timeout
399 sock:timeout(%TIMEOUT) 432 sock:timeout(%TIMEOUT)
400 -- send request 433 -- send request
401 err = %send_request(sock, method, parsed.path, req_hdrs, req_body) 434 err = %send_request(sock, method, parsed.path, req_hdrs, req_body_cb)
402 if err then return nil, nil, err end 435 if err then return nil, nil, err end
403 -- get server message 436 -- get server message
404 resp_code, resp_line, err = %get_status(sock) 437 resp_code, resp_line, err = %get_status(sock)
@@ -407,15 +440,15 @@ function http_requestindirect(method, url, req_hdrs, req_body, resp_body)
407 resp_hdrs, err = %get_hdrs(sock, {}) 440 resp_hdrs, err = %get_hdrs(sock, {})
408 if err then return nil, line, err end 441 if err then return nil, line, err end
409 -- did we get a redirect? should we automatically retry? 442 -- did we get a redirect? should we automatically retry?
410 if (resp_code == 301 or resp_code == 302) and 443 if not stay and (resp_code == 301 or resp_code == 302) and
411 (method == "GET" or method == "HEAD") then 444 (method == "GET" or method == "HEAD") then
412 sock:close() 445 sock:close()
413 return http_requestindirect(method, resp_hdrs["location"], req_hdrs, 446 return http_requestindirect(method, resp_hdrs["location"],
414 req_body, resp_body) 447 resp_body_cb, req_hdrs, req_body_cb, stay)
415 end 448 end
416 -- get body if status and method combination allow one 449 -- get response message body if status and method combination allow one
417 if has_respbody(method, resp_code) then 450 if has_respbody(method, resp_code) then
418 err = %try_getbody(sock, resp_hdrs, resp_body) 451 err = %try_getbody(sock, resp_hdrs, resp_body_cb)
419 if err then return resp_hdrs, resp_line, err end 452 if err then return resp_hdrs, resp_line, err end
420 end 453 end
421 sock:close() 454 sock:close()
@@ -423,67 +456,107 @@ function http_requestindirect(method, url, req_hdrs, req_body, resp_body)
423end 456end
424 457
425----------------------------------------------------------------------------- 458-----------------------------------------------------------------------------
459-- We need fast concatenation routines for direct requests
460-----------------------------------------------------------------------------
461dofile("buffer.lua")
462
463-----------------------------------------------------------------------------
426-- Sends a HTTP request and retrieves the server reply 464-- Sends a HTTP request and retrieves the server reply
427-- Input 465-- Input
428-- method: "GET", "PUT", "POST" etc 466-- method: "GET", "PUT", "POST" etc
429-- url: target uniform resource locator 467-- url: target uniform resource locator
430-- headers: request headers to send 468-- req_hdrs: request headers to send, or nil if none
431-- body: request message body 469-- req_body: request message body as a string, or nil if none
470-- stay: should we refrain from following a server redirect message?
432-- Returns 471-- Returns
433-- resp_body: response message body, if successfull 472-- resp_body: response message body, or nil if failed
434-- resp_hdrs: response header fields received, if sucessfull 473-- resp_hdrs: response header fields received, or nil if failed
435-- resp_line: server response status line, if successfull 474-- resp_line: server response status line, or nil if failed
436-- err: error message if any 475-- err: error message, or nil if successfull
437----------------------------------------------------------------------------- 476-----------------------------------------------------------------------------
438function http_request(method, url, req_hdrs, body) 477function http_request(method, url, req_hdrs, req_body, stay)
439 local resp_hdrs, resp_line, err 478 local resp_hdrs, resp_line, err
440 local req_callback = function() 479 local req_body_cb = function()
441 return %body, strlen(%body) 480 return %req_body, strlen(%req_body)
442 end 481 end
443 local resp_aux = { resp_body = "" } 482 local resp_body = { buf = buf_create() }
444 local resp_callback = function(chunk, err) 483 local resp_body_cb = function(chunk, err)
445 if not chunk then 484 if not chunk then %resp_body.buf = nil end
446 %resp_aux.resp_body = nil 485 buf_addstring(%resp_body.buf, chunk)
447 %resp_aux.err = err 486 return 1
448 return nil 487 end
449 end 488 if not req_body then req_body_cb = nil end
450 %resp_aux.resp_body = %resp_aux.resp_body .. chunk 489 resp_hdrs, resp_line, err = http_requestindirect(method, url, resp_body_cb,
451 return 1 490 req_hdrs, req_body_cb, stay)
452 end 491 return buf_getresult(resp_body.buf), resp_hdrs, resp_line, err
453 if not body then resp_callback = nil end
454 resp_hdrs, resp_line, err = http_requestindirect(method, url, req_hdrs,
455 req_callback, resp_callback)
456 if err then return nil, resp_hdrs, resp_line, err
457 else return resp_aux.resp_body, resp_hdrs, resp_line, resp_aux.err end
458end 492end
459 493
460----------------------------------------------------------------------------- 494-----------------------------------------------------------------------------
461-- Retrieves a URL by the method "GET" 495-- Retrieves a URL by the method "GET"
462-- Input 496-- Input
463-- url: target uniform resource locator 497-- url: target uniform resource locator
464-- headers: request headers to send 498-- req_hdrs: request headers to send, or nil if none
499-- stay: should we refrain from following a server redirect message?
465-- Returns 500-- Returns
466-- body: response message body, if successfull 501-- resp_body: response message body, or nil if failed
467-- headers: response header fields, if sucessfull 502-- resp_hdrs: response header fields received, or nil if failed
468-- line: response status line, if successfull 503-- resp_line: server response status line, or nil if failed
469-- err: error message, if any 504-- err: error message, or nil if successfull
470----------------------------------------------------------------------------- 505-----------------------------------------------------------------------------
471function http_get(url, headers) 506function http_get(url, req_hdrs, stay)
472 return http_request("GET", url, headers) 507 return http_request("GET", url, req_hdrs, stay)
473end 508end
474 509
475----------------------------------------------------------------------------- 510-----------------------------------------------------------------------------
476-- Retrieves a URL by the method "GET" 511-- Retrieves a URL by the method "GET"
477-- Input 512-- Input
478-- url: target uniform resource locator 513-- url: target uniform resource locator
479-- body: request message body 514-- resp_body_cb: response message body receive callback
480-- headers: request headers to send 515-- req_hdrs: request headers to send, or nil if none
516-- stay: should we refrain from following a server redirect message?
481-- Returns 517-- Returns
482-- body: response message body, if successfull 518-- resp_body: response message body, or nil if failed
483-- headers: response header fields, if sucessfull 519-- resp_hdrs: response header fields received, or nil if failed
484-- line: response status line, if successfull 520-- resp_line: server response status line, or nil if failed
485-- err: error message, if any 521-- err: error message, or nil if successfull
522-----------------------------------------------------------------------------
523function http_getindirect(url, resp_body_cb, req_hdrs, stay)
524 return http_requestindirect("GET", url, resp_body_cb, req_hdrs, nil, stay)
525end
526
486----------------------------------------------------------------------------- 527-----------------------------------------------------------------------------
487function http_post(url, body, headers) 528-- Retrieves a URL by the method "POST"
488 return http_request("POST", url, headers, body) 529-- Input
530-- method: "GET", "PUT", "POST" etc
531-- url: target uniform resource locator
532-- req_hdrs: request headers to send, or nil if none
533-- req_body: request message body, or nil if none
534-- stay: should we refrain from following a server redirect message?
535-- Returns
536-- resp_body: response message body, or nil if failed
537-- resp_hdrs: response header fields received, or nil if failed
538-- resp_line: server response status line, or nil if failed
539-- err: error message, or nil if successfull
540-----------------------------------------------------------------------------
541function http_post(url, req_body, req_hdrs, stay)
542 return http_request("POST", url, req_hdrs, req_body, stay)
543end
544
545-----------------------------------------------------------------------------
546-- Retrieves a URL by the method "POST"
547-- Input
548-- url: target uniform resource locator
549-- resp_body_cb: response message body receive callback
550-- req_body_cb: request message body send callback
551-- req_hdrs: request headers to send, or nil if none
552-- stay: should we refrain from following a server redirect message?
553-- Returns
554-- resp_body: response message body, or nil if failed
555-- resp_hdrs: response header fields received, or nil if failed
556-- resp_line: server response status line, or nil if failed
557-- err: error message, or nil if successfull
558-----------------------------------------------------------------------------
559function http_getindirect(url, resp_body_cb, req_body_cb, req_hdrs, stay)
560 return http_requestindirect("GET", url, resp_body_cb, req_hdrs,
561 req_body_cb, stay)
489end 562end