2.0 alpha RELEASED!

This commit is contained in:
Diego Nehab 2004-06-18 21:41:44 +00:00
parent ac4aac0909
commit 7ed89c97f7
14 changed files with 480 additions and 244 deletions

2
NEW
View File

@ -56,7 +56,7 @@ the changes that made it into version 2.0:
LUASOCKET_DEBUG was defined, but it turns out they might be useful for LUASOCKET_DEBUG was defined, but it turns out they might be useful for
applications; applications;
<> 'socket.try' and 'socket.protect' provide a simple <> 'socket.newtry' and 'socket.protect' provide a simple
interface to exceptions that proved very in the implementation of interface to exceptions that proved very in the implementation of
high-level modules; high-level modules;

157
TODO
View File

@ -1,163 +1,12 @@
make select interrupt safe
adicionar exemplos de expansão: pipe, local, named pipe
ajeitar os README.*
ajeitar as referencias a RFCS e LTNS em todos os arquivos.
make sure sockets are closed when exceptions are raised
check garbage collection in test*.lua
manual
socket.newtry
*socket.skip
*send return convention changed.
* compatibility: select sets are associative
* add socket.connect and socket.bind to the manual
* add shutdown
* add gethostname
check all occurences of it's
* the need of a content-length header in the post method...
* notice the change in callback conventions
* the callback.lua module and the new mime module.
* escape and unescape in url, not in code!
* add timeout and proxy to request table
* change stay to redirect
* socket.time and socket.sleep
* - connect with timeout
local connect
* add thanks to 'carlos cassino' and 'david burgess'
* add new ip- options and reuseaddr option
* - add listen to manual
* bind method doesn't do listen anymore
* bind doesn't turn an object into a server object: listen does.
tests
checar todos os metodos
checar todas as globais
checar garbage collection
check for interrupts
wrp can't break lines in the middle of a line break.
add comments into each C module.
testar os options!
Read about
250-ENHANCEDSTATUSCODES
250-PIPELINING
250-8BITMIME
250-SIZE
250-DSN
250-ETRN
250-AUTH GSSAPI
250-DELIVERBY
250 HELP
Change return of send and receive callbacks to allow for
new functions. "" signals end of transmission. Pass total
number of bytes in request table for HTTP. Callback has nothing
to do with it.
Make sure nobody can fuck up with the metatables...
Adjust dates in all files
Test the library on every system possible
Add service name translation. Add service name translation.
Ajeitar o protocolo da luaopen_socket()... sei lá qual é. testar os options!
- adicionar exemplos de expansão: pipe, local, named pipe
- Fazer compilar com g++ - Fazer compilar com g++
- Thread-safe - Thread-safe
- proteger get*by*.* com um mutex GLOBAL! - proteger get*by*.* com um mutex GLOBAL!
- proteger ou atomizar o conjunto (timedout, receive), (timedout, send) - proteger ou atomizar o conjunto (timedout, receive), (timedout, send)
- inet_ntoa também é uma merda. - inet_ntoa também é uma merda.
- SSL - SSL
- unix 92 bytes maximo no endereço, incluindo o zero - unix 92 bytes maximo no endereço, incluindo o zero
- unix 9216 maximo de datagram size - unix 9216 maximo de datagram size
--------------
these are done
--------------
* tirar socket.url socket.ftp etc do manual. agora os namespaces
estao liberados.
* falar sobre o novo esquema de namespace
* ajeitar o manual sobre select, mais liberal agora
* make sure filter.chain fails gracefully.
* ajeitar select. upvalue nao tem nada a ver...
* should be interrupt-safe
* notice the change in callback conventions
* new mime module replacing old code module (faster, more functionality)
* new socket options (many)
* only allocate in case of success
* optimize for success (only call select if fails)
* add proxy support to http
* add gethostname
* local connect
* connect with timeout
* change code to mime
* change stay to redirect
* add shutdown
* change send/recv to avoid using select
* O location do "redirect" pode ser relativo ao servidor atual (não pode,
mas os servidores fazem merda...)
* Ajeitar para Lua 5.0
* Padronizar os retornos de funccao
* Separar as classes em arquivos
* Retorno de sendto em datagram sockets pode ser refused
* select sets are now associative
* colocar pump.all, pump.step
* mudar ltn12.html e usar o exemplo source.cat que está muito melhor.
* break smtp.send into c = smtp.open, c:send() c:close()
* fazer com que a socket.source e socket.sink sejam "selectable".
* change mime.eol to output marker on detection of first candidate, instead
of on the second. that way it works in one pass for strings that end with
one candidate.
* unify backbone of smtp and ftp
* unify filter and send/receive callback. new sink/source/pump idea.
* get rid of aux_optlstring
* get rid of unpack in mime.lua
* create socket.(sink|source).simplify
* break chain into a simpler binary chain and a complex (recursive) one.
* Create a passive mode option for the FTP (good for firewall).
* Modules should return their namespace table in the end of the chunk.
* get.lua precisa de ftp.get com url e sink
* conjunto associativo
* colocar um userdata com gc metamethod pra chamar sock_close (WSAClose);
* call select before accept, not after, dumbass!
* get rid of setnonblocking/setblocking in the bind function
* close has to block...
* fmt is not a good name
* change wrap() to accept a number and default to "character"
* move gethostname to dns table
* get rid of _cb in name of functions?
* trust character constants in mime.c? yup.
* smtp.lua needs stuff filter
* new option.c module to put all options (TCP and UDP share...)?
* add _tostring methods!
* change all modules to use the new namespace scheme
* write some utilities that use the code.lua module and put them
* in etc, modify the README.etc file and makefile.dist (eol.lua is done)
* proxy no ftp? no
* ajeitar < e-mail > no smtp? no
* ajeitar referencias a LTN12 nos manuais
* RECEIVE MUDOU!!! (partial stuff) COLOCAR NO MANUAL.
* HTTP.lua mudou bastante também.
* falar sobre encodet/wrapt/decodet no manual sobre mime? no.
* pump.step usado em todo mundo que recebe source ou sink
* sources ans sinks are always simple in http and ftp and smtp
* expose encode/decode tables to provide extensibility for mime module
* use coroutines instead of fancy filters
* add socket.TIMEOUT to be default timeout? no.
* use gethostname it in SMTP
* smtp.o goes to mime.dll

View File

@ -1,24 +1,10 @@
This directory contains code that is more useful than the examples. This code This directory contains code that is more useful than the examples. This code
*is* supported. *is* supported.
lua.lua lua.lua -- new require and requirelib implementations
These are modules to suport dynamic loading of LuaSocket by the stand alone This is to support dynamic loading of LuaSocket. Check the INSTALL
Lua Interpreter with the use of new "require" and "requirelib" functions. file for more information.
For my Mac OS X box, for instance, I place all files in
/Users/diego/tec/luasocket and set the following environment variables:
LUA_INIT=@/Users/diego/tec/luasocket/lua.lua
LUA_PATH=/Users/diego/tec/luasocket/?.lua;?.lua
LUA_PATHLIB=/Users/diego/tec/luasocket/?.dylib;?.dylib
With that, I can run any luasocket application with the command line:
lua <script>
as long as the script uses "require" to load the needed namespaces.
Much nicer than having to build a new executable just to initialize
LuaSocket!
tftp.lua -- Trivial FTP client tftp.lua -- Trivial FTP client

402
etc/lp.lua Normal file
View File

@ -0,0 +1,402 @@
-- make sure LuaSocket is loaded
local socket = require("socket")
local ltn12 = require("ltn12")
local lp = {}
--socket.lp = lp
-- make all module globals fall into lp namespace
setmetatable(lp, { __index = _G })
setfenv(1, lp)
-- default port
PORT = 515
SERVER = os.getenv("SERVER_NAME") or os.getenv("COMPUTERNAME") or "localhost"
PRINTER = os.getenv("PRINTER") or "printer"
--[[
RFC 1179
5.3 03 - Send queue state (short)
+----+-------+----+------+----+
| 03 | Queue | SP | List | LF |
+----+-------+----+------+----+
Command code - 3
Operand 1 - Printer queue name
Other operands - User names or job numbers
If the user names or job numbers or both are supplied then only those
jobs for those users or with those numbers will be sent.
The response is an ASCII stream which describes the printer queue.
The stream continues until the connection closes. Ends of lines are
indicated with ASCII LF control characters. The lines may also
contain ASCII HT control characters.
5.4 04 - Send queue state (long)
+----+-------+----+------+----+
| 04 | Queue | SP | List | LF |
+----+-------+----+------+----+
Command code - 4
Operand 1 - Printer queue name
Other operands - User names or job numbers
If the user names or job numbers or both are supplied then only those
jobs for those users or with those numbers will be sent.
The response is an ASCII stream which describes the printer queue.
The stream continues until the connection closes. Ends of lines are
indicated with ASCII LF control characters. The lines may also
contain ASCII HT control characters.
]]
-- gets server acknowledement
local function recv_ack(connection)
local code, current, separator, _
local ack = socket.try(connection:receive(1))
if string.char(0) ~= ack then
connection:close(); error"failed to receive server acknowledement"
end
end
-- sends client acknowledement
local function send_ack(connection)
local sent = socket.try(connection:send(string.char(0)))
if not sent or sent ~= 1 then
connection:close();
error"failed to send acknowledgement"
end
end
-- sends queue request
-- 5.2 02 - Receive a printer job
--
-- +----+-------+----+
-- | 02 | Queue | LF |
-- +----+-------+----+
-- Command code - 2
-- Operand - Printer queue name
--
-- Receiving a job is controlled by a second level of commands. The
-- daemon is given commands by sending them over the same connection.
-- The commands are described in the next section (6).
--
-- After this command is sent, the client must read an acknowledgement
-- octet from the daemon. A positive acknowledgement is an octet of
-- zero bits. A negative acknowledgement is an octet of any other
-- pattern.
local function send_queue(connection,queue)
if not queue then queue=PRINTER end
local str = string.format("\2%s\10",queue)
local sent = socket.try(connection:send(str))
if not sent or sent ~= string.len(str) then
error "failed to send print request"
end
recv_ack(connection)
end
-- sends control file
-- 6.2 02 - Receive control file
--
-- +----+-------+----+------+----+
-- | 02 | Count | SP | Name | LF |
-- +----+-------+----+------+----+
-- Command code - 2
-- Operand 1 - Number of bytes in control file
-- Operand 2 - Name of control file
--
-- The control file must be an ASCII stream with the ends of lines
-- indicated by ASCII LF. The total number of bytes in the stream is
-- sent as the first operand. The name of the control file is sent as
-- the second. It should start with ASCII "cfA", followed by a three
-- digit job number, followed by the host name which has constructed the
-- control file. Acknowledgement processing must occur as usual after
-- the command is sent.
--
-- The next "Operand 1" octets over the same TCP connection are the
-- intended contents of the control file. Once all of the contents have
-- been delivered, an octet of zero bits is sent as an indication that
-- the file being sent is complete. A second level of acknowledgement
-- processing must occur at this point.
-- sends data file
-- 6.3 03 - Receive data file
--
-- +----+-------+----+------+----+
-- | 03 | Count | SP | Name | LF |
-- +----+-------+----+------+----+
-- Command code - 3
-- Operand 1 - Number of bytes in data file
-- Operand 2 - Name of data file
--
-- The data file may contain any 8 bit values at all. The total number
-- of bytes in the stream may be sent as the first operand, otherwise
-- the field should be cleared to 0. The name of the data file should
-- start with ASCII "dfA". This should be followed by a three digit job
-- number. The job number should be followed by the host name which has
-- constructed the data file. Interpretation of the contents of the
-- data file is determined by the contents of the corresponding control
-- file. If a data file length has been specified, the next "Operand 1"
-- octets over the same TCP connection are the intended contents of the
-- data file. In this case, once all of the contents have been
-- delivered, an octet of zero bits is sent as an indication that the
-- file being sent is complete. A second level of acknowledgement
-- processing must occur at this point.
local function send_hdr(connection,control)
local sent = socket.try(connection:send(control))
if not sent or sent < 1 then
error "failed to send file"
end
recv_ack(connection)
end
local function send_control(connection,control)
local sent = socket.try(connection:send(control))
if not sent or sent < 1 then
error "failed to send file"
end
send_ack(connection)
end
local function send_data(connection,fh,size)
-- local sink = socket.sink("keep-open", connection)
-- ltn12.pump.all(source, sink)
local buf, st, message
st = true
while size > 0 do
buf,message = fh:read(8192)
if buf then
st = socket.try(connection:send(buf))
size = size - st
else
if size ~= 0 then
connection:close()
return nil, "file size mismatch"
end
end
end
send_ack(connection)
recv_ack(connection)
return size,nil
end
--[[
local control_dflt = {
"H"..string.sub(socket.hostname,1,31).."\10", -- host
"C"..string.sub(socket.hostname,1,31).."\10", -- class
"J"..string.sub(filename,1,99).."\10", -- jobname
"L"..string.sub(user,1,31).."\10", -- print banner page
"I"..tonumber(indent).."\10", -- indent column count ('f' only)
"M"..string.sub(mail,1,128).."\10", -- mail when printed user@host
"N"..string.sub(filename,1,131).."\10", -- name of source file
"P"..string.sub(user,1,31).."\10", -- user name
"T"..string.sub(title,1,79).."\10", -- title for banner ('p' only)
"W"..tonumber(width or 132).."\10", -- width of print f,l,p only
"f"..file.."\10", -- formatted print (remove control chars)
"l"..file.."\10", -- print
"o"..file.."\10", -- postscript
"p"..file.."\10", -- pr format - requires T, L
"r"..file.."\10", -- fortran format
"U"..file.."\10", -- Unlink (data file only)
}
]]
-- generate a varying job number
local function getjobno(connection)
-- print(math.mod(socket.time() * 1000, port)) -- ok for windows
-- print(os.time() / port,math.random(0,999))
return math.random(0,999)
end
local function getcon(localhost,option)
local skt, st, message
local localport = 721
if not option then
error('no options',0)
end
if option.localbind then
repeat
-- bind to a local port (if we can)
skt = socket.try(socket.tcp())
skt:settimeout(30)
st, message = skt:bind(localhost,localport,-1);
-- print("bind",st,message)
if st then
st,message = skt:connect(option.host or SERVER, option.port or PORT)
-- print("connect",st,message)
end
-- print(st,localport,message)
if not st then
localport = localport + 1
skt:close()
end
until st or localport > 731 or (not st and message ~= "local address already in use")
if st then return skt end
end
return socket.try(socket.connect(option.host or SERVER, option.port or PORT))
end
local format_codes = {
binary = 'l',
text = 'f',
ps = 'o',
pr = 'p',
fortran = 'r',
l = 'l',
r = 'r',
o = 'o',
p = 'p',
f = 'f'
}
lp.send = socket.protect(function(file, option)
if not file then error "invalid file name" end
if not option or type(option) ~= "table" then error "invalid options" end
local fh = socket.try(io.open(file,"rb"))
-- get total size
local datafile_size = fh:seek("end")
-- go back to start of file
fh:seek("set")
math.randomseed(socket.time() * 1000)
local localhost = socket.dns.gethostname() or os.getenv("COMPUTERNAME") or "localhost"
-- local connection, message = skt:connect(option.host or SERVER, option.port or PORT)
local connection = getcon(localhost,option)
-- format the control file
local jobno = getjobno(connection)
local localip = socket.dns.toip(localhost)
localhost = string.sub(localhost,1,31)
local user = string.sub(option.user or os.getenv("LPRUSER") or os.getenv("USERNAME")
or os.getenv("USER") or "anonymous",1,31)
local lpfile = string.format("dfA%3.3d%-s", jobno, localhost);
local fmt = format_codes[option.format] or 'l'
local class = string.sub(option.class or localip or localhost,1,31)
local _,_,ctlfn = string.find(file,".*[%/%\\](.*)")
ctlfn = string.sub(ctlfn or file,1,131)
local cfile =
string.format("H%-s\nC%-s\nJ%-s\nP%-s\n%.1s%-s\nU%-s\nN%-s\n",
localhost,
class,
option.job or ctlfn,
user,
fmt, lpfile,
lpfile,
ctlfn); -- mandatory part of ctl file
if (option.banner) then cfile = cfile .. 'L'..user..'\10' end
if (option.indent) then cfile = cfile .. 'I'..tonumber(option.indent)..'\10' end
if (option.mail) then cfile = cfile .. 'M'..string.sub((option.mail),1,128)..'\10' end
if (fmt == 'p' and option.title) then cfile = cfile .. 'T'..string.sub((option.title),1,79)..'\10' end
if ((fmt == 'p' or fmt == 'l' or fmt == 'f') and option.width) then
cfile = cfile .. 'W'..tonumber(option,width)..'\10'
end
connection:settimeout(option.timeout or 65)
-- send the queue header
send_queue(connection,option.queue)
-- send the control file header
local cfilecmd = string.format("\2%d cfA%3.3d%-s\n",string.len(cfile), jobno, localhost);
send_hdr(connection,cfilecmd)
-- send the control file
send_control(connection,cfile)
-- send the data file header
local dfilecmd = string.format("\3%d dfA%3.3d%-s\n",datafile_size, jobno, localhost);
send_hdr(connection,dfilecmd)
-- send the data file
send_data(connection,fh,datafile_size)
fh:close()
connection:close();
return datafile_size
end)
--socket.lpq({host=,queue=printer|'*', format='l'|'s', list=})
lp.query = socket.protect(function(p)
if not p then p={} end
local localhost = socket.dns.gethostname() or os.getenv("COMPUTERNAME") or "localhost"
local connection = getcon(localhost,p)
local fmt,data
if string.sub(p.format or 's',1,1) == 's' then fmt = 3 else fmt = 4 end
local sent = socket.try(connection:send(string.format("%c%s %s\n", fmt, p.queue or "*", p.list or "")))
local data = socket.try(connection:receive("*a"))
io.write(data)
connection:close()
return tostring(string.len(data))
end)
--for k,v in arg do print(k,v) end
local function usage()
print('\nUsage: lp filename [keyword=val...]\n')
print('Valid keywords are :')
print(
' host=remote host or IP address (default "localhost")\n' ..
' queue=remote queue or printer name (default "printer")\n' ..
' port=remote port number (default 515)\n' ..
' user=sending user name\n' ..
' format=["binary" | "text" | "ps" | "pr" | "fortran"] (default "binary")\n' ..
' banner=true|false\n' ..
' indent=number of columns to indent\n' ..
' mail=email of address to notify when print is complete\n' ..
' title=title to use for "pr" format\n' ..
' width=width for "text" or "pr" formats\n' ..
' class=\n' ..
' job=\n' ..
' name=\n' ..
' localbind=true|false\n'
)
return nil
end
if not arg or not arg[1] then
return usage()
end
do
local s="opt = {"
for i = 2 , table.getn(arg), 1 do
s = s .. string.gsub(arg[i],"[%s%c%p]*([%w]*)=([\"]?[%w%s_!@#$%%^&*()<>:;]+[\"]\?\.?)","%1%=\"%2\",\n")
end
s = s .. "};\n"
assert(loadstring(s))();
if not arg[2] then
return usage()
end
if arg[1] ~= "query" then
r,e=lp.send(arg[1],opt)
io.stderr:write(tostring(r or e),'\n')
else
r,e=lp.query(opt)
io.stderr:write(tostring(r or e),'\n')
end
end
-- trivial tests
--lua lp.lua lp.lua queue=default host=localhost
--lua lp.lua lp.lua queue=default host=localhost format=binary localbind=1
--lua lp.lua query queue=default host=localhost
collectgarbage()
collectgarbage()
--print(socket.lp.query{host='localhost', queue="default"})
return nil

View File

@ -29,6 +29,7 @@ EXAMPLES = \
eol.lua \ eol.lua \
listener.lua \ listener.lua \
qp.lua \ qp.lua \
lp.lua \
talker.lua \ talker.lua \
tinyirc.lua tinyirc.lua
@ -101,14 +102,13 @@ dist:
mkdir -p $(DIST)/tests mkdir -p $(DIST)/tests
mkdir -p $(DIST)/etc mkdir -p $(DIST)/etc
mkdir -p $(DIST)/lua mkdir -p $(DIST)/lua
mkdir -p $(DIST)/make
mkdir -p $(DIST)/manual mkdir -p $(DIST)/manual
cp -vf $(CORE) $(DIST) cp -vf $(CORE) $(DIST)
cp -vf README $(DIST) cp -vf README $(DIST)
cp -vf NEW $(DIST) cp -vf NEW $(DIST)
cp -vf LICENSE $(DIST) cp -vf LICENSE $(DIST)
cp -vf $(MAKE) $(DIST)/make cp -vf $(MAKE) $(DIST)
cp -vf make.README $(DIST)/make/README cp -vf make.README $(DIST)/INSTALL
cp -vf $(LUA) $(DIST)/lua cp -vf $(LUA) $(DIST)/lua
cp -vf lua.README $(DIST)/lua/README cp -vf lua.README $(DIST)/lua/README
cp -vf $(EXAMPLES) $(DIST)/examples cp -vf $(EXAMPLES) $(DIST)/examples

View File

@ -19,6 +19,12 @@ printed by listen.lua.
These are tiny programs that perform Base64, Quoted-Printable and These are tiny programs that perform Base64, Quoted-Printable and
end-of-line marker conversions. end-of-line marker conversions.
lp.lua -- lp client
This is a cool program written by David Burgess to print files using the
Line Printer Daemon protocol, widely used in Unix machines.
Just run 'lua lp.lua <filename> queue=<printername>' and the file will print!
cddb.lua -- CDDB client cddb.lua -- CDDB client
This is the first try on a simple CDDB client. Not really useful, but one This is the first try on a simple CDDB client. Not really useful, but one
@ -26,7 +32,6 @@ day it might become a module.
daytimeclnt.lua -- day time client daytimeclnt.lua -- day time client
Just run the program to retrieve the hour and date in readable form from Just run the program to retrieve the hour and date in readable form from
any server running an UDP daytime daemon. any server running an UDP daytime daemon.

View File

@ -12,33 +12,21 @@
/*=========================================================================*\ /*=========================================================================*\
* Internal function prototypes. * Internal function prototypes.
\*=========================================================================*/ \*=========================================================================*/
static int global_try(lua_State *L);
static int global_protect(lua_State *L); static int global_protect(lua_State *L);
static int global_newtry(lua_State *L); static int global_newtry(lua_State *L);
static int protected(lua_State *L); static int protected(lua_State *L);
static int finalize(lua_State *L); static int finalize(lua_State *L);
static int do_nothing(lua_State *L);
/* except functions */ /* except functions */
static luaL_reg func[] = { static luaL_reg func[] = {
{"try", global_try},
{"newtry", global_newtry}, {"newtry", global_newtry},
{"protect", global_protect}, {"protect", global_protect},
{NULL, NULL} {NULL, NULL}
}; };
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Try method * Try factory
\*-------------------------------------------------------------------------*/
static int global_try(lua_State *L) {
if (lua_isnil(L, 1) || (lua_isboolean(L, 1) && !lua_toboolean(L, 1))) {
lua_settop(L, 2);
lua_error(L);
return 0;
} else return lua_gettop(L);
}
/*-------------------------------------------------------------------------*\
* Finalizer factory
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
static int finalize(lua_State *L) { static int finalize(lua_State *L) {
if (lua_isnil(L, 1) || (lua_isboolean(L, 1) && !lua_toboolean(L, 1))) { if (lua_isnil(L, 1) || (lua_isboolean(L, 1) && !lua_toboolean(L, 1))) {
@ -50,7 +38,14 @@ static int finalize(lua_State *L) {
} else return lua_gettop(L); } else return lua_gettop(L);
} }
static int do_nothing(lua_State *L) {
(void) L;
return 0;
}
static int global_newtry(lua_State *L) { static int global_newtry(lua_State *L) {
lua_settop(L, 1);
if (lua_isnil(L, 1)) lua_pushcfunction(L, do_nothing);
lua_pushcclosure(L, finalize, 1); lua_pushcclosure(L, finalize, 1);
return 1; return 1;
} }

View File

@ -32,14 +32,10 @@ local metat = { __index = {} }
function open(server, port) function open(server, port)
local tp = socket.try(tp.connect(server, port or PORT, TIMEOUT)) local tp = socket.try(tp.connect(server, port or PORT, TIMEOUT))
local f = { tp = tp } local f = setmetat({ tp = tp }, metat)
-- make sure everything gets closed in an exception -- make sure everything gets closed in an exception
f.try = socket.newtry(function() f.try = socket.newtry(function() f:close() end)
tp:close() return f
if f.data then f.data:close() end
if f.server then f.server:close() end
end)
return setmetatable(f, metat)
end end
function metat.__index:portconnect() function metat.__index:portconnect()
@ -173,13 +169,9 @@ function metat.__index:quit()
end end
function metat.__index:close() function metat.__index:close()
self.tp:close()
if self.data then self.data:close() end if self.data then self.data:close() end
if self.server then self.server:close() end if self.server then self.server:close() end
self.tp = nil return self.tp:close()
self.data = nil
self.server = nil
return 1
end end
----------------------------------------------------------------------------- -----------------------------------------------------------------------------

View File

@ -32,11 +32,12 @@ local metat = { __index = {} }
function open(host, port) function open(host, port)
local c = socket.try(socket.tcp()) local c = socket.try(socket.tcp())
local h = setmetatable({ c = c }, metat)
-- make sure the connection gets closed on exception -- make sure the connection gets closed on exception
local try = socket.newtry(function() c:close() end) h.try = socket.newtry(function() h:close() end)
try(c:settimeout(TIMEOUT)) h.try(c:settimeout(TIMEOUT))
try(c:connect(host, port or PORT)) h.try(c:connect(host, port or PORT))
return setmetatable({ c = c, try = try }, metat) return h
end end
function metat.__index:sendrequestline(method, uri) function metat.__index:sendrequestline(method, uri)
@ -57,9 +58,8 @@ function metat.__index:sendbody(headers, source, step)
source = source or ltn12.source.empty() source = source or ltn12.source.empty()
step = step or ltn12.pump.step step = step or ltn12.pump.step
-- if we don't know the size in advance, send chunked and hope for the best -- if we don't know the size in advance, send chunked and hope for the best
local mode local mode = "http-chunked"
if headers["content-length"] then mode = "keep-open" if headers["content-length"] then mode = "keep-open" end
else mode = "http-chunked" end
return self.try(ltn12.pump.all(source, socket.sink(mode, self.c), step)) return self.try(ltn12.pump.all(source, socket.sink(mode, self.c), step))
end end
@ -99,10 +99,9 @@ function metat.__index:receivebody(headers, sink, step)
step = step or ltn12.pump.step step = step or ltn12.pump.step
local length = tonumber(headers["content-length"]) local length = tonumber(headers["content-length"])
local TE = headers["transfer-encoding"] local TE = headers["transfer-encoding"]
local mode local mode = "default" -- connection close
if TE and TE ~= "identity" then mode = "http-chunked" if TE and TE ~= "identity" then mode = "http-chunked"
elseif tonumber(headers["content-length"]) then mode = "by-length" elseif tonumber(headers["content-length"]) then mode = "by-length" end
else mode = "default" end
return self.try(ltn12.pump.all(socket.source(mode, self.c, length), return self.try(ltn12.pump.all(socket.source(mode, self.c, length),
sink, step)) sink, step))
end end
@ -191,9 +190,9 @@ function tauthorize(reqt)
end end
function tredirect(reqt, headers) function tredirect(reqt, headers)
-- the RFC says the redirect URL has to be absolute, but some
-- servers do not respect that
return trequest { return trequest {
-- the RFC says the redirect URL has to be absolute, but some
-- servers do not respect that
url = url.absolute(reqt, headers["location"]), url = url.absolute(reqt, headers["location"]),
source = reqt.source, source = reqt.source,
sink = reqt.sink, sink = reqt.sink,

View File

@ -60,7 +60,7 @@ function metat.__index:quit()
end end
function metat.__index:close() function metat.__index:close()
return self.try(self.tp:close()) return self.tp:close()
end end
function metat.__index:login(user, password) function metat.__index:login(user, password)
@ -104,9 +104,10 @@ end
function open(server, port) function open(server, port)
local tp = socket.try(tp.connect(server or SERVER, port or PORT, TIMEOUT)) local tp = socket.try(tp.connect(server or SERVER, port or PORT, TIMEOUT))
local s = setmetatable({tp = tp}, metat)
-- make sure tp is closed if we get an exception -- make sure tp is closed if we get an exception
local try = socket.newtry(function() tp:close() end) local try = socket.newtry(function() s:close() end)
return setmetatable({ tp = tp, try = try}, metat) return s
end end
--------------------------------------------------------------------------- ---------------------------------------------------------------------------

View File

@ -37,6 +37,8 @@ function socket.bind(host, port, backlog)
return sock return sock
end end
socket.try = socket.newtry()
function socket.choose(table) function socket.choose(table)
return function(name, opt1, opt2) return function(name, opt1, opt2)
if type(name) ~= "string" then if type(name) ~= "string" then

View File

@ -20,16 +20,16 @@ TIMEOUT = 60
-- Implementation -- Implementation
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- gets server reply (works for SMTP and FTP) -- gets server reply (works for SMTP and FTP)
local function get_reply(control) local function get_reply(c)
local code, current, sep local code, current, sep
local line, err = control:receive() local line, err = c:receive()
local reply = line local reply = line
if err then return nil, err end if err then return nil, err end
code, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)")) code, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)"))
if not code then return nil, "invalid server reply" end if not code then return nil, "invalid server reply" end
if sep == "-" then -- reply is multiline if sep == "-" then -- reply is multiline
repeat repeat
line, err = control:receive() line, err = c:receive()
if err then return nil, err end if err then return nil, err end
current, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)")) current, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)"))
reply = reply .. "\n" .. line reply = reply .. "\n" .. line
@ -43,7 +43,7 @@ end
local metat = { __index = {} } local metat = { __index = {} }
function metat.__index:check(ok) function metat.__index:check(ok)
local code, reply = get_reply(self.control) local code, reply = get_reply(self.c)
if not code then return nil, reply end if not code then return nil, reply end
if type(ok) ~= "function" then if type(ok) ~= "function" then
if type(ok) == "table" then if type(ok) == "table" then
@ -59,50 +59,55 @@ function metat.__index:check(ok)
end end
function metat.__index:command(cmd, arg) function metat.__index:command(cmd, arg)
if arg then return self.control:send(cmd .. " " .. arg.. "\r\n") if arg then return self.c:send(cmd .. " " .. arg.. "\r\n")
else return self.control:send(cmd .. "\r\n") end else return self.c:send(cmd .. "\r\n") end
end end
function metat.__index:sink(snk, pat) function metat.__index:sink(snk, pat)
local chunk, err = control:receive(pat) local chunk, err = c:receive(pat)
return snk(chunk, err) return snk(chunk, err)
end end
function metat.__index:send(data) function metat.__index:send(data)
return self.control:send(data) return self.c:send(data)
end end
function metat.__index:receive(pat) function metat.__index:receive(pat)
return self.control:receive(pat) return self.c:receive(pat)
end end
function metat.__index:getfd() function metat.__index:getfd()
return self.control:getfd() return self.c:getfd()
end end
function metat.__index:dirty() function metat.__index:dirty()
return self.control:dirty() return self.c:dirty()
end end
function metat.__index:getcontrol() function metat.__index:getcontrol()
return self.control return self.c
end end
function metat.__index:source(source, step) function metat.__index:source(source, step)
local sink = socket.sink("keep-open", self.control) local sink = socket.sink("keep-open", self.c)
return ltn12.pump.all(source, sink, step or ltn12.pump.step) return ltn12.pump.all(source, sink, step or ltn12.pump.step)
end end
-- closes the underlying control -- closes the underlying c
function metat.__index:close() function metat.__index:close()
self.control:close() self.c:close()
return 1 return 1
end end
-- connect with server and return control object -- connect with server and return c object
connect = socket.protect(function(host, port, timeout) function connect(host, port, timeout)
local control = socket.try(socket.tcp()) local c, e = socket.tcp()
socket.try(control:settimeout(timeout or TIMEOUT)) if not c then return nil, e end
socket.try(control:connect(host, port)) c:settimeout(timeout or TIMEOUT)
return setmetatable({control = control}, metat) local r, e = c:connect(host, port)
end) if not r then
c:close()
return nil, e
end
return setmetatable({c = c}, metat)
end

View File

@ -155,14 +155,15 @@ check_request(request, expect, ignore)
io.write("testing simple post function: ") io.write("testing simple post function: ")
back = http.request("http://" .. host .. cgiprefix .. "/cat", index) back = http.request("http://" .. host .. cgiprefix .. "/cat", index)
assert(back == index) assert(back == index)
print("ok")
------------------------------------------------------------------------ ------------------------------------------------------------------------
io.write("testing ltn12.(sink|source).file: ") io.write("testing ltn12.(sink|source).file: ")
request = { request = {
url = "http://" .. host .. cgiprefix .. "/cat", url = "http://" .. host .. cgiprefix .. "/cat",
method = "POST", method = "POST",
source = ltn12.source.file(io.open(index_file, "r")), source = ltn12.source.file(io.open(index_file, "rb")),
sink = ltn12.sink.file(io.open(index_file .. "-back", "w")), sink = ltn12.sink.file(io.open(index_file .. "-back", "wb")),
headers = { ["content-length"] = string.len(index) } headers = { ["content-length"] = string.len(index) }
} }
expect = { expect = {
@ -187,7 +188,7 @@ local function b64length(len)
end end
local source = ltn12.source.chain( local source = ltn12.source.chain(
ltn12.source.file(io.open(index_file, "r")), ltn12.source.file(io.open(index_file, "rb")),
ltn12.filter.chain( ltn12.filter.chain(
mime.encode("base64"), mime.encode("base64"),
mime.wrap("base64") mime.wrap("base64")
@ -196,7 +197,7 @@ local source = ltn12.source.chain(
local sink = ltn12.sink.chain( local sink = ltn12.sink.chain(
mime.decode("base64"), mime.decode("base64"),
ltn12.sink.file(io.open(index_file .. "-back", "w")) ltn12.sink.file(io.open(index_file .. "-back", "wb"))
) )
request = { request = {

View File

@ -13,7 +13,6 @@ while 1 do
-- control:setoption("nodelay", true) -- control:setoption("nodelay", true)
while 1 do while 1 do
command, error = control:receive() command, error = control:receive()
print(error)
if error then if error then
control:close() control:close()
print("server: closing connection...") print("server: closing connection...")