aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDiego Nehab <diego@tecgraf.puc-rio.br>2004-06-18 21:41:44 +0000
committerDiego Nehab <diego@tecgraf.puc-rio.br>2004-06-18 21:41:44 +0000
commit7ed89c97f760600df238f9853ee453570935870f (patch)
treea17573a43815071e35f85557519fefca739ef7ef
parentac4aac0909da26befaaeb6b415f66cf35b6980e0 (diff)
downloadluasocket-7ed89c97f760600df238f9853ee453570935870f.tar.gz
luasocket-7ed89c97f760600df238f9853ee453570935870f.tar.bz2
luasocket-7ed89c97f760600df238f9853ee453570935870f.zip
2.0 alpha RELEASED!
-rw-r--r--NEW2
-rw-r--r--TODO157
-rw-r--r--etc/README20
-rw-r--r--etc/lp.lua402
-rw-r--r--makefile.dist6
-rw-r--r--samples/README7
-rw-r--r--src/except.c23
-rw-r--r--src/ftp.lua16
-rw-r--r--src/http.lua23
-rw-r--r--src/smtp.lua7
-rw-r--r--src/socket.lua2
-rw-r--r--src/tp.lua49
-rw-r--r--test/httptest.lua9
-rw-r--r--test/testsrvr.lua1
14 files changed, 480 insertions, 244 deletions
diff --git a/NEW b/NEW
index 035aab7..302974e 100644
--- a/NEW
+++ b/NEW
@@ -56,7 +56,7 @@ the changes that made it into version 2.0:
56 LUASOCKET_DEBUG was defined, but it turns out they might be useful for 56 LUASOCKET_DEBUG was defined, but it turns out they might be useful for
57 applications; 57 applications;
58 58
59<> 'socket.try' and 'socket.protect' provide a simple 59<> 'socket.newtry' and 'socket.protect' provide a simple
60 interface to exceptions that proved very in the implementation of 60 interface to exceptions that proved very in the implementation of
61 high-level modules; 61 high-level modules;
62 62
diff --git a/TODO b/TODO
index 7a45569..054e5f3 100644
--- a/TODO
+++ b/TODO
@@ -1,163 +1,12 @@
1 1make select interrupt safe
2 2adicionar exemplos de expansão: pipe, local, named pipe
3ajeitar os README.*
4ajeitar as referencias a RFCS e LTNS em todos os arquivos.
5
6make sure sockets are closed when exceptions are raised
7
8check garbage collection in test*.lua
9
10
11manual
12 socket.newtry
13 *socket.skip
14 *send return convention changed.
15 * compatibility: select sets are associative
16 * add socket.connect and socket.bind to the manual
17 * add shutdown
18 * add gethostname
19 check all occurences of it's
20 * the need of a content-length header in the post method...
21 * notice the change in callback conventions
22 * the callback.lua module and the new mime module.
23 * escape and unescape in url, not in code!
24 * add timeout and proxy to request table
25 * change stay to redirect
26 * socket.time and socket.sleep
27 * - connect with timeout
28 local connect
29 * add thanks to 'carlos cassino' and 'david burgess'
30 * add new ip- options and reuseaddr option
31 * - add listen to manual
32 * bind method doesn't do listen anymore
33 * bind doesn't turn an object into a server object: listen does.
34
35tests
36 checar todos os metodos
37 checar todas as globais
38 checar garbage collection
39 check for interrupts
40
41wrp can't break lines in the middle of a line break.
42
43add comments into each C module.
44testar os options!
45
46
47Read about
48250-ENHANCEDSTATUSCODES
49250-PIPELINING
50250-8BITMIME
51250-SIZE
52250-DSN
53250-ETRN
54250-AUTH GSSAPI
55250-DELIVERBY
56250 HELP
57
58Change return of send and receive callbacks to allow for
59new functions. "" signals end of transmission. Pass total
60number of bytes in request table for HTTP. Callback has nothing
61to do with it.
62
63Make sure nobody can fuck up with the metatables...
64
65
66
67Adjust dates in all files
68
69Test the library on every system possible
70
71Add service name translation. 3Add service name translation.
72Ajeitar o protocolo da luaopen_socket()... sei lá qual é. 4testar os options!
73
74
75- adicionar exemplos de expansão: pipe, local, named pipe
76
77
78- Fazer compilar com g++ 5- Fazer compilar com g++
79- Thread-safe 6- Thread-safe
80 - proteger get*by*.* com um mutex GLOBAL! 7 - proteger get*by*.* com um mutex GLOBAL!
81 - proteger ou atomizar o conjunto (timedout, receive), (timedout, send) 8 - proteger ou atomizar o conjunto (timedout, receive), (timedout, send)
82 - inet_ntoa também é uma merda. 9 - inet_ntoa também é uma merda.
83- SSL 10- SSL
84
85- unix 92 bytes maximo no endereço, incluindo o zero 11- unix 92 bytes maximo no endereço, incluindo o zero
86- unix 9216 maximo de datagram size 12- unix 9216 maximo de datagram size
87
88--------------
89these are done
90--------------
91
92* tirar socket.url socket.ftp etc do manual. agora os namespaces
93 estao liberados.
94* falar sobre o novo esquema de namespace
95* ajeitar o manual sobre select, mais liberal agora
96* make sure filter.chain fails gracefully.
97* ajeitar select. upvalue nao tem nada a ver...
98* should be interrupt-safe
99* notice the change in callback conventions
100* new mime module replacing old code module (faster, more functionality)
101* new socket options (many)
102* only allocate in case of success
103* optimize for success (only call select if fails)
104* add proxy support to http
105* add gethostname
106* local connect
107* connect with timeout
108* change code to mime
109* change stay to redirect
110* add shutdown
111* change send/recv to avoid using select
112* O location do "redirect" pode ser relativo ao servidor atual (não pode,
113 mas os servidores fazem merda...)
114* Ajeitar para Lua 5.0
115* Padronizar os retornos de funccao
116* Separar as classes em arquivos
117* Retorno de sendto em datagram sockets pode ser refused
118* select sets are now associative
119* colocar pump.all, pump.step
120* mudar ltn12.html e usar o exemplo source.cat que está muito melhor.
121* break smtp.send into c = smtp.open, c:send() c:close()
122* fazer com que a socket.source e socket.sink sejam "selectable".
123* change mime.eol to output marker on detection of first candidate, instead
124 of on the second. that way it works in one pass for strings that end with
125 one candidate.
126* unify backbone of smtp and ftp
127* unify filter and send/receive callback. new sink/source/pump idea.
128* get rid of aux_optlstring
129* get rid of unpack in mime.lua
130* create socket.(sink|source).simplify
131* break chain into a simpler binary chain and a complex (recursive) one.
132* Create a passive mode option for the FTP (good for firewall).
133* Modules should return their namespace table in the end of the chunk.
134* get.lua precisa de ftp.get com url e sink
135* conjunto associativo
136* colocar um userdata com gc metamethod pra chamar sock_close (WSAClose);
137* call select before accept, not after, dumbass!
138* get rid of setnonblocking/setblocking in the bind function
139* close has to block...
140* fmt is not a good name
141* change wrap() to accept a number and default to "character"
142* move gethostname to dns table
143* get rid of _cb in name of functions?
144* trust character constants in mime.c? yup.
145* smtp.lua needs stuff filter
146* new option.c module to put all options (TCP and UDP share...)?
147* add _tostring methods!
148* change all modules to use the new namespace scheme
149* write some utilities that use the code.lua module and put them
150* in etc, modify the README.etc file and makefile.dist (eol.lua is done)
151* proxy no ftp? no
152* ajeitar < e-mail > no smtp? no
153* ajeitar referencias a LTN12 nos manuais
154* RECEIVE MUDOU!!! (partial stuff) COLOCAR NO MANUAL.
155* HTTP.lua mudou bastante também.
156* falar sobre encodet/wrapt/decodet no manual sobre mime? no.
157* pump.step usado em todo mundo que recebe source ou sink
158* sources ans sinks are always simple in http and ftp and smtp
159* expose encode/decode tables to provide extensibility for mime module
160* use coroutines instead of fancy filters
161* add socket.TIMEOUT to be default timeout? no.
162* use gethostname it in SMTP
163* smtp.o goes to mime.dll
diff --git a/etc/README b/etc/README
index d650843..c459a46 100644
--- a/etc/README
+++ b/etc/README
@@ -1,24 +1,10 @@
1This directory contains code that is more useful than the examples. This code 1This directory contains code that is more useful than the examples. This code
2*is* supported. 2*is* supported.
3 3
4 lua.lua 4 lua.lua -- new require and requirelib implementations
5 5
6These are modules to suport dynamic loading of LuaSocket by the stand alone 6This is to support dynamic loading of LuaSocket. Check the INSTALL
7Lua Interpreter with the use of new "require" and "requirelib" functions. 7file for more information.
8For my Mac OS X box, for instance, I place all files in
9/Users/diego/tec/luasocket and set the following environment variables:
10
11 LUA_INIT=@/Users/diego/tec/luasocket/lua.lua
12 LUA_PATH=/Users/diego/tec/luasocket/?.lua;?.lua
13 LUA_PATHLIB=/Users/diego/tec/luasocket/?.dylib;?.dylib
14
15With that, I can run any luasocket application with the command line:
16
17 lua <script>
18
19as long as the script uses "require" to load the needed namespaces.
20Much nicer than having to build a new executable just to initialize
21LuaSocket!
22 8
23 tftp.lua -- Trivial FTP client 9 tftp.lua -- Trivial FTP client
24 10
diff --git a/etc/lp.lua b/etc/lp.lua
new file mode 100644
index 0000000..80acf86
--- /dev/null
+++ b/etc/lp.lua
@@ -0,0 +1,402 @@
1-- make sure LuaSocket is loaded
2local socket = require("socket")
3local ltn12 = require("ltn12")
4local lp = {}
5--socket.lp = lp
6-- make all module globals fall into lp namespace
7setmetatable(lp, { __index = _G })
8setfenv(1, lp)
9
10-- default port
11PORT = 515
12SERVER = os.getenv("SERVER_NAME") or os.getenv("COMPUTERNAME") or "localhost"
13PRINTER = os.getenv("PRINTER") or "printer"
14
15
16--[[
17RFC 1179
185.3 03 - Send queue state (short)
19
20 +----+-------+----+------+----+
21 | 03 | Queue | SP | List | LF |
22 +----+-------+----+------+----+
23 Command code - 3
24 Operand 1 - Printer queue name
25 Other operands - User names or job numbers
26
27 If the user names or job numbers or both are supplied then only those
28 jobs for those users or with those numbers will be sent.
29
30 The response is an ASCII stream which describes the printer queue.
31 The stream continues until the connection closes. Ends of lines are
32 indicated with ASCII LF control characters. The lines may also
33 contain ASCII HT control characters.
34
355.4 04 - Send queue state (long)
36
37 +----+-------+----+------+----+
38 | 04 | Queue | SP | List | LF |
39 +----+-------+----+------+----+
40 Command code - 4
41 Operand 1 - Printer queue name
42 Other operands - User names or job numbers
43
44 If the user names or job numbers or both are supplied then only those
45 jobs for those users or with those numbers will be sent.
46
47 The response is an ASCII stream which describes the printer queue.
48 The stream continues until the connection closes. Ends of lines are
49 indicated with ASCII LF control characters. The lines may also
50 contain ASCII HT control characters.
51]]
52
53
54-- gets server acknowledement
55local function recv_ack(connection)
56 local code, current, separator, _
57 local ack = socket.try(connection:receive(1))
58 if string.char(0) ~= ack then
59 connection:close(); error"failed to receive server acknowledement"
60 end
61end
62
63-- sends client acknowledement
64local function send_ack(connection)
65 local sent = socket.try(connection:send(string.char(0)))
66 if not sent or sent ~= 1 then
67 connection:close();
68 error"failed to send acknowledgement"
69 end
70end
71
72-- sends queue request
73-- 5.2 02 - Receive a printer job
74--
75-- +----+-------+----+
76-- | 02 | Queue | LF |
77-- +----+-------+----+
78-- Command code - 2
79-- Operand - Printer queue name
80--
81-- Receiving a job is controlled by a second level of commands. The
82-- daemon is given commands by sending them over the same connection.
83-- The commands are described in the next section (6).
84--
85-- After this command is sent, the client must read an acknowledgement
86-- octet from the daemon. A positive acknowledgement is an octet of
87-- zero bits. A negative acknowledgement is an octet of any other
88-- pattern.
89local function send_queue(connection,queue)
90 if not queue then queue=PRINTER end
91 local str = string.format("\2%s\10",queue)
92 local sent = socket.try(connection:send(str))
93 if not sent or sent ~= string.len(str) then
94 error "failed to send print request"
95 end
96 recv_ack(connection)
97end
98
99-- sends control file
100-- 6.2 02 - Receive control file
101--
102-- +----+-------+----+------+----+
103-- | 02 | Count | SP | Name | LF |
104-- +----+-------+----+------+----+
105-- Command code - 2
106-- Operand 1 - Number of bytes in control file
107-- Operand 2 - Name of control file
108--
109-- The control file must be an ASCII stream with the ends of lines
110-- indicated by ASCII LF. The total number of bytes in the stream is
111-- sent as the first operand. The name of the control file is sent as
112-- the second. It should start with ASCII "cfA", followed by a three
113-- digit job number, followed by the host name which has constructed the
114-- control file. Acknowledgement processing must occur as usual after
115-- the command is sent.
116--
117-- The next "Operand 1" octets over the same TCP connection are the
118-- intended contents of the control file. Once all of the contents have
119-- been delivered, an octet of zero bits is sent as an indication that
120-- the file being sent is complete. A second level of acknowledgement
121-- processing must occur at this point.
122
123-- sends data file
124-- 6.3 03 - Receive data file
125--
126-- +----+-------+----+------+----+
127-- | 03 | Count | SP | Name | LF |
128-- +----+-------+----+------+----+
129-- Command code - 3
130-- Operand 1 - Number of bytes in data file
131-- Operand 2 - Name of data file
132--
133-- The data file may contain any 8 bit values at all. The total number
134-- of bytes in the stream may be sent as the first operand, otherwise
135-- the field should be cleared to 0. The name of the data file should
136-- start with ASCII "dfA". This should be followed by a three digit job
137-- number. The job number should be followed by the host name which has
138-- constructed the data file. Interpretation of the contents of the
139-- data file is determined by the contents of the corresponding control
140-- file. If a data file length has been specified, the next "Operand 1"
141-- octets over the same TCP connection are the intended contents of the
142-- data file. In this case, once all of the contents have been
143-- delivered, an octet of zero bits is sent as an indication that the
144-- file being sent is complete. A second level of acknowledgement
145-- processing must occur at this point.
146
147
148local function send_hdr(connection,control)
149 local sent = socket.try(connection:send(control))
150 if not sent or sent < 1 then
151 error "failed to send file"
152 end
153 recv_ack(connection)
154end
155
156
157local function send_control(connection,control)
158 local sent = socket.try(connection:send(control))
159 if not sent or sent < 1 then
160 error "failed to send file"
161 end
162 send_ack(connection)
163end
164
165local function send_data(connection,fh,size)
166-- local sink = socket.sink("keep-open", connection)
167-- ltn12.pump.all(source, sink)
168 local buf, st, message
169 st = true
170 while size > 0 do
171 buf,message = fh:read(8192)
172 if buf then
173 st = socket.try(connection:send(buf))
174 size = size - st
175 else
176 if size ~= 0 then
177 connection:close()
178 return nil, "file size mismatch"
179 end
180 end
181 end
182 send_ack(connection)
183 recv_ack(connection)
184 return size,nil
185end
186
187
188--[[
189
190local control_dflt = {
191 "H"..string.sub(socket.hostname,1,31).."\10", -- host
192 "C"..string.sub(socket.hostname,1,31).."\10", -- class
193 "J"..string.sub(filename,1,99).."\10", -- jobname
194 "L"..string.sub(user,1,31).."\10", -- print banner page
195 "I"..tonumber(indent).."\10", -- indent column count ('f' only)
196 "M"..string.sub(mail,1,128).."\10", -- mail when printed user@host
197 "N"..string.sub(filename,1,131).."\10", -- name of source file
198 "P"..string.sub(user,1,31).."\10", -- user name
199 "T"..string.sub(title,1,79).."\10", -- title for banner ('p' only)
200 "W"..tonumber(width or 132).."\10", -- width of print f,l,p only
201
202 "f"..file.."\10", -- formatted print (remove control chars)
203 "l"..file.."\10", -- print
204 "o"..file.."\10", -- postscript
205 "p"..file.."\10", -- pr format - requires T, L
206 "r"..file.."\10", -- fortran format
207 "U"..file.."\10", -- Unlink (data file only)
208}
209
210]]
211
212-- generate a varying job number
213local function getjobno(connection)
214-- print(math.mod(socket.time() * 1000, port)) -- ok for windows
215-- print(os.time() / port,math.random(0,999))
216 return math.random(0,999)
217end
218
219local function getcon(localhost,option)
220 local skt, st, message
221 local localport = 721
222 if not option then
223 error('no options',0)
224 end
225 if option.localbind then
226 repeat
227 -- bind to a local port (if we can)
228 skt = socket.try(socket.tcp())
229 skt:settimeout(30)
230
231 st, message = skt:bind(localhost,localport,-1);
232 -- print("bind",st,message)
233 if st then
234 st,message = skt:connect(option.host or SERVER, option.port or PORT)
235 -- print("connect",st,message)
236 end
237 -- print(st,localport,message)
238 if not st then
239 localport = localport + 1
240 skt:close()
241 end
242 until st or localport > 731 or (not st and message ~= "local address already in use")
243 if st then return skt end
244 end
245 return socket.try(socket.connect(option.host or SERVER, option.port or PORT))
246end
247
248local format_codes = {
249 binary = 'l',
250 text = 'f',
251 ps = 'o',
252 pr = 'p',
253 fortran = 'r',
254 l = 'l',
255 r = 'r',
256 o = 'o',
257 p = 'p',
258 f = 'f'
259}
260
261lp.send = socket.protect(function(file, option)
262 if not file then error "invalid file name" end
263 if not option or type(option) ~= "table" then error "invalid options" end
264 local fh = socket.try(io.open(file,"rb"))
265 -- get total size
266 local datafile_size = fh:seek("end")
267 -- go back to start of file
268 fh:seek("set")
269 math.randomseed(socket.time() * 1000)
270 local localhost = socket.dns.gethostname() or os.getenv("COMPUTERNAME") or "localhost"
271
272-- local connection, message = skt:connect(option.host or SERVER, option.port or PORT)
273
274 local connection = getcon(localhost,option)
275
276-- format the control file
277 local jobno = getjobno(connection)
278 local localip = socket.dns.toip(localhost)
279 localhost = string.sub(localhost,1,31)
280
281 local user = string.sub(option.user or os.getenv("LPRUSER") or os.getenv("USERNAME")
282 or os.getenv("USER") or "anonymous",1,31)
283
284 local lpfile = string.format("dfA%3.3d%-s", jobno, localhost);
285
286 local fmt = format_codes[option.format] or 'l'
287
288 local class = string.sub(option.class or localip or localhost,1,31)
289
290 local _,_,ctlfn = string.find(file,".*[%/%\\](.*)")
291 ctlfn = string.sub(ctlfn or file,1,131)
292
293 local cfile =
294 string.format("H%-s\nC%-s\nJ%-s\nP%-s\n%.1s%-s\nU%-s\nN%-s\n",
295 localhost,
296 class,
297 option.job or ctlfn,
298 user,
299 fmt, lpfile,
300 lpfile,
301 ctlfn); -- mandatory part of ctl file
302 if (option.banner) then cfile = cfile .. 'L'..user..'\10' end
303 if (option.indent) then cfile = cfile .. 'I'..tonumber(option.indent)..'\10' end
304 if (option.mail) then cfile = cfile .. 'M'..string.sub((option.mail),1,128)..'\10' end
305 if (fmt == 'p' and option.title) then cfile = cfile .. 'T'..string.sub((option.title),1,79)..'\10' end
306 if ((fmt == 'p' or fmt == 'l' or fmt == 'f') and option.width) then
307 cfile = cfile .. 'W'..tonumber(option,width)..'\10'
308 end
309
310 connection:settimeout(option.timeout or 65)
311
312-- send the queue header
313 send_queue(connection,option.queue)
314
315-- send the control file header
316 local cfilecmd = string.format("\2%d cfA%3.3d%-s\n",string.len(cfile), jobno, localhost);
317 send_hdr(connection,cfilecmd)
318
319-- send the control file
320 send_control(connection,cfile)
321
322-- send the data file header
323 local dfilecmd = string.format("\3%d dfA%3.3d%-s\n",datafile_size, jobno, localhost);
324 send_hdr(connection,dfilecmd)
325
326-- send the data file
327 send_data(connection,fh,datafile_size)
328 fh:close()
329 connection:close();
330 return datafile_size
331end)
332
333
334--socket.lpq({host=,queue=printer|'*', format='l'|'s', list=})
335lp.query = socket.protect(function(p)
336 if not p then p={} end
337 local localhost = socket.dns.gethostname() or os.getenv("COMPUTERNAME") or "localhost"
338 local connection = getcon(localhost,p)
339 local fmt,data
340 if string.sub(p.format or 's',1,1) == 's' then fmt = 3 else fmt = 4 end
341 local sent = socket.try(connection:send(string.format("%c%s %s\n", fmt, p.queue or "*", p.list or "")))
342 local data = socket.try(connection:receive("*a"))
343 io.write(data)
344 connection:close()
345 return tostring(string.len(data))
346end)
347
348--for k,v in arg do print(k,v) end
349local function usage()
350 print('\nUsage: lp filename [keyword=val...]\n')
351 print('Valid keywords are :')
352 print(
353 ' host=remote host or IP address (default "localhost")\n' ..
354 ' queue=remote queue or printer name (default "printer")\n' ..
355 ' port=remote port number (default 515)\n' ..
356 ' user=sending user name\n' ..
357 ' format=["binary" | "text" | "ps" | "pr" | "fortran"] (default "binary")\n' ..
358 ' banner=true|false\n' ..
359 ' indent=number of columns to indent\n' ..
360 ' mail=email of address to notify when print is complete\n' ..
361 ' title=title to use for "pr" format\n' ..
362 ' width=width for "text" or "pr" formats\n' ..
363 ' class=\n' ..
364 ' job=\n' ..
365 ' name=\n' ..
366 ' localbind=true|false\n'
367 )
368 return nil
369end
370
371if not arg or not arg[1] then
372 return usage()
373end
374
375do
376 local s="opt = {"
377 for i = 2 , table.getn(arg), 1 do
378 s = s .. string.gsub(arg[i],"[%s%c%p]*([%w]*)=([\"]?[%w%s_!@#$%%^&*()<>:;]+[\"]\?\.?)","%1%=\"%2\",\n")
379 end
380 s = s .. "};\n"
381 assert(loadstring(s))();
382 if not arg[2] then
383 return usage()
384 end
385 if arg[1] ~= "query" then
386 r,e=lp.send(arg[1],opt)
387 io.stderr:write(tostring(r or e),'\n')
388 else
389 r,e=lp.query(opt)
390 io.stderr:write(tostring(r or e),'\n')
391 end
392end
393
394-- trivial tests
395--lua lp.lua lp.lua queue=default host=localhost
396--lua lp.lua lp.lua queue=default host=localhost format=binary localbind=1
397--lua lp.lua query queue=default host=localhost
398collectgarbage()
399collectgarbage()
400--print(socket.lp.query{host='localhost', queue="default"})
401
402return nil
diff --git a/makefile.dist b/makefile.dist
index 19c85b5..fec6579 100644
--- a/makefile.dist
+++ b/makefile.dist
@@ -29,6 +29,7 @@ EXAMPLES = \
29 eol.lua \ 29 eol.lua \
30 listener.lua \ 30 listener.lua \
31 qp.lua \ 31 qp.lua \
32 lp.lua \
32 talker.lua \ 33 talker.lua \
33 tinyirc.lua 34 tinyirc.lua
34 35
@@ -101,14 +102,13 @@ dist:
101 mkdir -p $(DIST)/tests 102 mkdir -p $(DIST)/tests
102 mkdir -p $(DIST)/etc 103 mkdir -p $(DIST)/etc
103 mkdir -p $(DIST)/lua 104 mkdir -p $(DIST)/lua
104 mkdir -p $(DIST)/make
105 mkdir -p $(DIST)/manual 105 mkdir -p $(DIST)/manual
106 cp -vf $(CORE) $(DIST) 106 cp -vf $(CORE) $(DIST)
107 cp -vf README $(DIST) 107 cp -vf README $(DIST)
108 cp -vf NEW $(DIST) 108 cp -vf NEW $(DIST)
109 cp -vf LICENSE $(DIST) 109 cp -vf LICENSE $(DIST)
110 cp -vf $(MAKE) $(DIST)/make 110 cp -vf $(MAKE) $(DIST)
111 cp -vf make.README $(DIST)/make/README 111 cp -vf make.README $(DIST)/INSTALL
112 cp -vf $(LUA) $(DIST)/lua 112 cp -vf $(LUA) $(DIST)/lua
113 cp -vf lua.README $(DIST)/lua/README 113 cp -vf lua.README $(DIST)/lua/README
114 cp -vf $(EXAMPLES) $(DIST)/examples 114 cp -vf $(EXAMPLES) $(DIST)/examples
diff --git a/samples/README b/samples/README
index b5802e5..6e7ee77 100644
--- a/samples/README
+++ b/samples/README
@@ -19,6 +19,12 @@ printed by listen.lua.
19These are tiny programs that perform Base64, Quoted-Printable and 19These are tiny programs that perform Base64, Quoted-Printable and
20end-of-line marker conversions. 20end-of-line marker conversions.
21 21
22 lp.lua -- lp client
23
24This is a cool program written by David Burgess to print files using the
25Line Printer Daemon protocol, widely used in Unix machines.
26Just run 'lua lp.lua <filename> queue=<printername>' and the file will print!
27
22 cddb.lua -- CDDB client 28 cddb.lua -- CDDB client
23 29
24This is the first try on a simple CDDB client. Not really useful, but one 30This is the first try on a simple CDDB client. Not really useful, but one
@@ -26,7 +32,6 @@ day it might become a module.
26 32
27 daytimeclnt.lua -- day time client 33 daytimeclnt.lua -- day time client
28 34
29
30Just run the program to retrieve the hour and date in readable form from 35Just run the program to retrieve the hour and date in readable form from
31any server running an UDP daytime daemon. 36any server running an UDP daytime daemon.
32 37
diff --git a/src/except.c b/src/except.c
index 53a65ac..68abf70 100644
--- a/src/except.c
+++ b/src/except.c
@@ -12,33 +12,21 @@
12/*=========================================================================*\ 12/*=========================================================================*\
13* Internal function prototypes. 13* Internal function prototypes.
14\*=========================================================================*/ 14\*=========================================================================*/
15static int global_try(lua_State *L);
16static int global_protect(lua_State *L); 15static int global_protect(lua_State *L);
17static int global_newtry(lua_State *L); 16static int global_newtry(lua_State *L);
18static int protected(lua_State *L); 17static int protected(lua_State *L);
19static int finalize(lua_State *L); 18static int finalize(lua_State *L);
19static int do_nothing(lua_State *L);
20 20
21/* except functions */ 21/* except functions */
22static luaL_reg func[] = { 22static luaL_reg func[] = {
23 {"try", global_try},
24 {"newtry", global_newtry}, 23 {"newtry", global_newtry},
25 {"protect", global_protect}, 24 {"protect", global_protect},
26 {NULL, NULL} 25 {NULL, NULL}
27}; 26};
28 27
29/*-------------------------------------------------------------------------*\ 28/*-------------------------------------------------------------------------*\
30* Try method 29* Try factory
31\*-------------------------------------------------------------------------*/
32static int global_try(lua_State *L) {
33 if (lua_isnil(L, 1) || (lua_isboolean(L, 1) && !lua_toboolean(L, 1))) {
34 lua_settop(L, 2);
35 lua_error(L);
36 return 0;
37 } else return lua_gettop(L);
38}
39
40/*-------------------------------------------------------------------------*\
41* Finalizer factory
42\*-------------------------------------------------------------------------*/ 30\*-------------------------------------------------------------------------*/
43static int finalize(lua_State *L) { 31static int finalize(lua_State *L) {
44 if (lua_isnil(L, 1) || (lua_isboolean(L, 1) && !lua_toboolean(L, 1))) { 32 if (lua_isnil(L, 1) || (lua_isboolean(L, 1) && !lua_toboolean(L, 1))) {
@@ -50,7 +38,14 @@ static int finalize(lua_State *L) {
50 } else return lua_gettop(L); 38 } else return lua_gettop(L);
51} 39}
52 40
41static int do_nothing(lua_State *L) {
42 (void) L;
43 return 0;
44}
45
53static int global_newtry(lua_State *L) { 46static int global_newtry(lua_State *L) {
47 lua_settop(L, 1);
48 if (lua_isnil(L, 1)) lua_pushcfunction(L, do_nothing);
54 lua_pushcclosure(L, finalize, 1); 49 lua_pushcclosure(L, finalize, 1);
55 return 1; 50 return 1;
56} 51}
diff --git a/src/ftp.lua b/src/ftp.lua
index ad02c72..1c7ea71 100644
--- a/src/ftp.lua
+++ b/src/ftp.lua
@@ -32,14 +32,10 @@ local metat = { __index = {} }
32 32
33function open(server, port) 33function open(server, port)
34 local tp = socket.try(tp.connect(server, port or PORT, TIMEOUT)) 34 local tp = socket.try(tp.connect(server, port or PORT, TIMEOUT))
35 local f = { tp = tp } 35 local f = setmetat({ tp = tp }, metat)
36 -- make sure everything gets closed in an exception 36 -- make sure everything gets closed in an exception
37 f.try = socket.newtry(function() 37 f.try = socket.newtry(function() f:close() end)
38 tp:close() 38 return f
39 if f.data then f.data:close() end
40 if f.server then f.server:close() end
41 end)
42 return setmetatable(f, metat)
43end 39end
44 40
45function metat.__index:portconnect() 41function metat.__index:portconnect()
@@ -173,13 +169,9 @@ function metat.__index:quit()
173end 169end
174 170
175function metat.__index:close() 171function metat.__index:close()
176 self.tp:close()
177 if self.data then self.data:close() end 172 if self.data then self.data:close() end
178 if self.server then self.server:close() end 173 if self.server then self.server:close() end
179 self.tp = nil 174 return self.tp:close()
180 self.data = nil
181 self.server = nil
182 return 1
183end 175end
184 176
185----------------------------------------------------------------------------- 177-----------------------------------------------------------------------------
diff --git a/src/http.lua b/src/http.lua
index d8889e1..9c568bc 100644
--- a/src/http.lua
+++ b/src/http.lua
@@ -32,11 +32,12 @@ local metat = { __index = {} }
32 32
33function open(host, port) 33function open(host, port)
34 local c = socket.try(socket.tcp()) 34 local c = socket.try(socket.tcp())
35 local h = setmetatable({ c = c }, metat)
35 -- make sure the connection gets closed on exception 36 -- make sure the connection gets closed on exception
36 local try = socket.newtry(function() c:close() end) 37 h.try = socket.newtry(function() h:close() end)
37 try(c:settimeout(TIMEOUT)) 38 h.try(c:settimeout(TIMEOUT))
38 try(c:connect(host, port or PORT)) 39 h.try(c:connect(host, port or PORT))
39 return setmetatable({ c = c, try = try }, metat) 40 return h
40end 41end
41 42
42function metat.__index:sendrequestline(method, uri) 43function metat.__index:sendrequestline(method, uri)
@@ -57,9 +58,8 @@ function metat.__index:sendbody(headers, source, step)
57 source = source or ltn12.source.empty() 58 source = source or ltn12.source.empty()
58 step = step or ltn12.pump.step 59 step = step or ltn12.pump.step
59 -- if we don't know the size in advance, send chunked and hope for the best 60 -- if we don't know the size in advance, send chunked and hope for the best
60 local mode 61 local mode = "http-chunked"
61 if headers["content-length"] then mode = "keep-open" 62 if headers["content-length"] then mode = "keep-open" end
62 else mode = "http-chunked" end
63 return self.try(ltn12.pump.all(source, socket.sink(mode, self.c), step)) 63 return self.try(ltn12.pump.all(source, socket.sink(mode, self.c), step))
64end 64end
65 65
@@ -99,10 +99,9 @@ function metat.__index:receivebody(headers, sink, step)
99 step = step or ltn12.pump.step 99 step = step or ltn12.pump.step
100 local length = tonumber(headers["content-length"]) 100 local length = tonumber(headers["content-length"])
101 local TE = headers["transfer-encoding"] 101 local TE = headers["transfer-encoding"]
102 local mode 102 local mode = "default" -- connection close
103 if TE and TE ~= "identity" then mode = "http-chunked" 103 if TE and TE ~= "identity" then mode = "http-chunked"
104 elseif tonumber(headers["content-length"]) then mode = "by-length" 104 elseif tonumber(headers["content-length"]) then mode = "by-length" end
105 else mode = "default" end
106 return self.try(ltn12.pump.all(socket.source(mode, self.c, length), 105 return self.try(ltn12.pump.all(socket.source(mode, self.c, length),
107 sink, step)) 106 sink, step))
108end 107end
@@ -191,9 +190,9 @@ function tauthorize(reqt)
191end 190end
192 191
193function tredirect(reqt, headers) 192function tredirect(reqt, headers)
194 -- the RFC says the redirect URL has to be absolute, but some
195 -- servers do not respect that
196 return trequest { 193 return trequest {
194 -- the RFC says the redirect URL has to be absolute, but some
195 -- servers do not respect that
197 url = url.absolute(reqt, headers["location"]), 196 url = url.absolute(reqt, headers["location"]),
198 source = reqt.source, 197 source = reqt.source,
199 sink = reqt.sink, 198 sink = reqt.sink,
diff --git a/src/smtp.lua b/src/smtp.lua
index d6357d2..70b511c 100644
--- a/src/smtp.lua
+++ b/src/smtp.lua
@@ -60,7 +60,7 @@ function metat.__index:quit()
60end 60end
61 61
62function metat.__index:close() 62function metat.__index:close()
63 return self.try(self.tp:close()) 63 return self.tp:close()
64end 64end
65 65
66function metat.__index:login(user, password) 66function metat.__index:login(user, password)
@@ -104,9 +104,10 @@ end
104 104
105function open(server, port) 105function open(server, port)
106 local tp = socket.try(tp.connect(server or SERVER, port or PORT, TIMEOUT)) 106 local tp = socket.try(tp.connect(server or SERVER, port or PORT, TIMEOUT))
107 local s = setmetatable({tp = tp}, metat)
107 -- make sure tp is closed if we get an exception 108 -- make sure tp is closed if we get an exception
108 local try = socket.newtry(function() tp:close() end) 109 local try = socket.newtry(function() s:close() end)
109 return setmetatable({ tp = tp, try = try}, metat) 110 return s
110end 111end
111 112
112--------------------------------------------------------------------------- 113---------------------------------------------------------------------------
diff --git a/src/socket.lua b/src/socket.lua
index 0a681bf..4d64651 100644
--- a/src/socket.lua
+++ b/src/socket.lua
@@ -37,6 +37,8 @@ function socket.bind(host, port, backlog)
37 return sock 37 return sock
38end 38end
39 39
40socket.try = socket.newtry()
41
40function socket.choose(table) 42function socket.choose(table)
41 return function(name, opt1, opt2) 43 return function(name, opt1, opt2)
42 if type(name) ~= "string" then 44 if type(name) ~= "string" then
diff --git a/src/tp.lua b/src/tp.lua
index 7b581b9..153541a 100644
--- a/src/tp.lua
+++ b/src/tp.lua
@@ -20,16 +20,16 @@ TIMEOUT = 60
20-- Implementation 20-- Implementation
21----------------------------------------------------------------------------- 21-----------------------------------------------------------------------------
22-- gets server reply (works for SMTP and FTP) 22-- gets server reply (works for SMTP and FTP)
23local function get_reply(control) 23local function get_reply(c)
24 local code, current, sep 24 local code, current, sep
25 local line, err = control:receive() 25 local line, err = c:receive()
26 local reply = line 26 local reply = line
27 if err then return nil, err end 27 if err then return nil, err end
28 code, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)")) 28 code, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)"))
29 if not code then return nil, "invalid server reply" end 29 if not code then return nil, "invalid server reply" end
30 if sep == "-" then -- reply is multiline 30 if sep == "-" then -- reply is multiline
31 repeat 31 repeat
32 line, err = control:receive() 32 line, err = c:receive()
33 if err then return nil, err end 33 if err then return nil, err end
34 current, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)")) 34 current, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)"))
35 reply = reply .. "\n" .. line 35 reply = reply .. "\n" .. line
@@ -43,7 +43,7 @@ end
43local metat = { __index = {} } 43local metat = { __index = {} }
44 44
45function metat.__index:check(ok) 45function metat.__index:check(ok)
46 local code, reply = get_reply(self.control) 46 local code, reply = get_reply(self.c)
47 if not code then return nil, reply end 47 if not code then return nil, reply end
48 if type(ok) ~= "function" then 48 if type(ok) ~= "function" then
49 if type(ok) == "table" then 49 if type(ok) == "table" then
@@ -59,50 +59,55 @@ function metat.__index:check(ok)
59end 59end
60 60
61function metat.__index:command(cmd, arg) 61function metat.__index:command(cmd, arg)
62 if arg then return self.control:send(cmd .. " " .. arg.. "\r\n") 62 if arg then return self.c:send(cmd .. " " .. arg.. "\r\n")
63 else return self.control:send(cmd .. "\r\n") end 63 else return self.c:send(cmd .. "\r\n") end
64end 64end
65 65
66function metat.__index:sink(snk, pat) 66function metat.__index:sink(snk, pat)
67 local chunk, err = control:receive(pat) 67 local chunk, err = c:receive(pat)
68 return snk(chunk, err) 68 return snk(chunk, err)
69end 69end
70 70
71function metat.__index:send(data) 71function metat.__index:send(data)
72 return self.control:send(data) 72 return self.c:send(data)
73end 73end
74 74
75function metat.__index:receive(pat) 75function metat.__index:receive(pat)
76 return self.control:receive(pat) 76 return self.c:receive(pat)
77end 77end
78 78
79function metat.__index:getfd() 79function metat.__index:getfd()
80 return self.control:getfd() 80 return self.c:getfd()
81end 81end
82 82
83function metat.__index:dirty() 83function metat.__index:dirty()
84 return self.control:dirty() 84 return self.c:dirty()
85end 85end
86 86
87function metat.__index:getcontrol() 87function metat.__index:getcontrol()
88 return self.control 88 return self.c
89end 89end
90 90
91function metat.__index:source(source, step) 91function metat.__index:source(source, step)
92 local sink = socket.sink("keep-open", self.control) 92 local sink = socket.sink("keep-open", self.c)
93 return ltn12.pump.all(source, sink, step or ltn12.pump.step) 93 return ltn12.pump.all(source, sink, step or ltn12.pump.step)
94end 94end
95 95
96-- closes the underlying control 96-- closes the underlying c
97function metat.__index:close() 97function metat.__index:close()
98 self.control:close() 98 self.c:close()
99 return 1 99 return 1
100end 100end
101 101
102-- connect with server and return control object 102-- connect with server and return c object
103connect = socket.protect(function(host, port, timeout) 103function connect(host, port, timeout)
104 local control = socket.try(socket.tcp()) 104 local c, e = socket.tcp()
105 socket.try(control:settimeout(timeout or TIMEOUT)) 105 if not c then return nil, e end
106 socket.try(control:connect(host, port)) 106 c:settimeout(timeout or TIMEOUT)
107 return setmetatable({control = control}, metat) 107 local r, e = c:connect(host, port)
108end) 108 if not r then
109 c:close()
110 return nil, e
111 end
112 return setmetatable({c = c}, metat)
113end
diff --git a/test/httptest.lua b/test/httptest.lua
index 45d7e8d..0ba6f56 100644
--- a/test/httptest.lua
+++ b/test/httptest.lua
@@ -155,14 +155,15 @@ check_request(request, expect, ignore)
155io.write("testing simple post function: ") 155io.write("testing simple post function: ")
156back = http.request("http://" .. host .. cgiprefix .. "/cat", index) 156back = http.request("http://" .. host .. cgiprefix .. "/cat", index)
157assert(back == index) 157assert(back == index)
158print("ok")
158 159
159------------------------------------------------------------------------ 160------------------------------------------------------------------------
160io.write("testing ltn12.(sink|source).file: ") 161io.write("testing ltn12.(sink|source).file: ")
161request = { 162request = {
162 url = "http://" .. host .. cgiprefix .. "/cat", 163 url = "http://" .. host .. cgiprefix .. "/cat",
163 method = "POST", 164 method = "POST",
164 source = ltn12.source.file(io.open(index_file, "r")), 165 source = ltn12.source.file(io.open(index_file, "rb")),
165 sink = ltn12.sink.file(io.open(index_file .. "-back", "w")), 166 sink = ltn12.sink.file(io.open(index_file .. "-back", "wb")),
166 headers = { ["content-length"] = string.len(index) } 167 headers = { ["content-length"] = string.len(index) }
167} 168}
168expect = { 169expect = {
@@ -187,7 +188,7 @@ local function b64length(len)
187end 188end
188 189
189local source = ltn12.source.chain( 190local source = ltn12.source.chain(
190 ltn12.source.file(io.open(index_file, "r")), 191 ltn12.source.file(io.open(index_file, "rb")),
191 ltn12.filter.chain( 192 ltn12.filter.chain(
192 mime.encode("base64"), 193 mime.encode("base64"),
193 mime.wrap("base64") 194 mime.wrap("base64")
@@ -196,7 +197,7 @@ local source = ltn12.source.chain(
196 197
197local sink = ltn12.sink.chain( 198local sink = ltn12.sink.chain(
198 mime.decode("base64"), 199 mime.decode("base64"),
199 ltn12.sink.file(io.open(index_file .. "-back", "w")) 200 ltn12.sink.file(io.open(index_file .. "-back", "wb"))
200) 201)
201 202
202request = { 203request = {
diff --git a/test/testsrvr.lua b/test/testsrvr.lua
index 6e786c8..5b842f1 100644
--- a/test/testsrvr.lua
+++ b/test/testsrvr.lua
@@ -13,7 +13,6 @@ while 1 do
13 -- control:setoption("nodelay", true) 13 -- control:setoption("nodelay", true)
14 while 1 do 14 while 1 do
15 command, error = control:receive() 15 command, error = control:receive()
16print(error)
17 if error then 16 if error then
18 control:close() 17 control:close()
19 print("server: closing connection...") 18 print("server: closing connection...")