Almost ready for distribution...

This commit is contained in:
Diego Nehab 2004-06-17 21:46:22 +00:00
parent eac26d2c8d
commit 597a062b1b
14 changed files with 323 additions and 161 deletions

101
NEW
View File

@ -1,29 +1,86 @@
Major C code rewrite. Code is modular and extensible. Hopefully, next What's New
versions will include code for local domain sockets, file descriptors,
pipes (on unix) and named pipes (on windows) as a bonus.
All functions provided by the library are in the namespace "socket". Everything is new! Many changes for 2.0 happened in the C layer,
Functions such as send/receive/timeout/close etc do not exist anymore as which has been almost completely rewritten. The code has been ported to
stand alone functions. They are now only available as methods of the Lua 5.0 and greatly improved. There have also been some API changes
appropriate objects. that made the interface simpler and more consistent. Here are some of
the changes that made it into version 2.0:
TCP has been changed to become more uniform. First create an object, then <> Major C code rewrite. Code is modular and extensible. Hopefully, other
connect or bind if needed. Then use IO functions. The "socket.connect" and developers will be motivated to provide code for SSL, local domain
"socket.bind" functions are provided for simplicity, but they just call sockets, file descriptors, pipes (on Unix) and named pipes etc;
"socket.tcp" followed by the ":connect" or ":bind" methods.
All functions return a non-nil value as first return value if successful. <> Everything that is exported by the library is exported inside
All functions return whatever could be retrieved followed by error message namespaces. These should be obtained with calls to the
in case of error. The best way to check for errors is to check for the 'require' function;
presence of an error message. WARNING: The send function was affected.
Better error messages and parameter checking. <> Functions such as
send/receive/timeout/close etc do not exist anymore as stand-alone
functions. They are now only available as methods of the appropriate
objects;
UDP connected udp sockets can break association with peer by calling <> All functions return a non-nil value as first return value if successful.
setpeername with address "*". All functions return 'nil' followed by error message
in case of error. This made the library much easier to use;
socket.sleep and socket.time are now part of the library and are <> Greatly reduced the number of times the C select is called
supported. during data transfers, by calling only on failure. This might
improve a lot the maximum throughput;
<> TCP has been changed to become more uniform. It's possible to first
create a TCP object,
then connect or bind if needed, and finally use I/O functions.
'socket.connect' and 'socket.bind' functions are still
provided for simplicity;
<> This allows for setting a timeout value before connecting;
<> And also allows binding to a local address before connecting;
<> New 'socket.dns.gethostname' function and 'shutdown'
method;
<> Better error messages and parameter checking;
<> Should be interrupt safe;
<> UDP connected sockets can break association with peer by calling
'setpeername' with address ''*'';
<> Sets returned by 'socket.select' are associative;
<> Select checks if sockets have buffered data and returns immediately;
<> 'socket.sleep' and 'socket.time' are now part of the
library and are supported. They used to be available only when
LUASOCKET_DEBUG was defined, but it turns out they might be useful for
applications;
<> 'socket.try' and 'socket.protect' provide a simple
interface to exceptions that proved very in the implementation of
high-level modules;
<> Socket options interface has been improved. TCP objects also
support socket options and many new options were added.
Lots of changes in the Lua modules, too!
<> Every module loads only the modules that it needs. There is no waste
of memory. LuaSocket core takes only 20k of memory;
<> New MIME and LTN12 modules make all other modules much more powerful;
<> Support for multipart messages in the SMTP module;
<> The old callback mechanism of FTP and HTTP has been replaced with LTN12
sources and sinks, with advantage;
<> Common implementation for low-level FTP and SMTP;
<> FTP, HTTP, and SMTP are implemented in multiple levels in such a way
that users will have no problems extending the functionality to satisfy
personal needs;
<> SMTP knows how to perform LOGIN and PLAIN authentication.
Socket options interface has been improved and TCP now also supports socket
options.

12
README
View File

@ -1,9 +1,11 @@
This release is work in progress. It has been tested on WinXP, Mac OS X, This release is a "beta" version. It has been tested on WinXP, Mac OS X,
SunOS and Linux. SunOS and Linux. Although no major API changes should happen before the
final version is released, please look for and report any bugs (or
"features") you encounter.
In this version, all Lua code should be built into the binary. For that, you For this version, all modules should be loaded with the provided "require"
will need a working versions of luac and bin2c, both available with your function, and the binaries should be compliled as shared libraries. Check
Lua distribution. Check the makefile for details. the makefiles in the distribution and the readme in the 'etc' directory.
Have fun, Have fun,
Diego Nehab. Diego Nehab.

View File

@ -4,34 +4,42 @@ This directory contains code that is more useful than the examples. This code
lua.lua lua.lua
These are modules to suport dynamic loading of LuaSocket by the stand alone These are modules to suport dynamic loading of LuaSocket by the stand alone
Lua Interpreter with the use of the "require" function. For my Mac OS X Lua Interpreter with the use of new "require" and "requirelib" functions.
system, I place all files in /Users/diego/tec/luasocket For my Mac OS X box, for instance, I place all files in
and set the following environment variables: /Users/diego/tec/luasocket and set the following environment variables:
LUA_PATH=/Users/diego/tec/luasocket/?.lua
LUA_INIT=@/Users/diego/tec/luasocket/lua.lua LUA_INIT=@/Users/diego/tec/luasocket/lua.lua
LUA_FUNCNAME=? LUA_PATH=/Users/diego/tec/luasocket/?.lua;?.lua
LUA_LIBNAME=/Users/diego/tec/luasocket/?.dylib LUA_PATHLIB=/Users/diego/tec/luasocket/?.dylib;?.dylib
With that, I can run any luasocket application with the command line: With that, I can run any luasocket application with the command line:
lua -l socket <script> 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 Much nicer than having to build a new executable just to initialize
LuaSocket! LuaSocket!
tftp.lua -- Trivial FTP client tftp.lua -- Trivial FTP client
This module implements file retrieval by the TFTP protocol. Its main use This module implements file retrieval by the TFTP protocol. Its main use
is to test the UDP code, but someone might find it usefull. was to test the UDP code, but since someone found it usefull, I turned it
into a module that is almost official (no uploads, yet).
dict.lua -- Dict client
The dict.lua module started with a cool simple client for the DICT
protocol, written by Luiz Henrique Figueiredo. This new version has been
converted into a library, similar to the HTTP and FTP libraries, that can
be used from within any luasocket application. Take a look on the source
code and you will be able to figure out how to use it.
get.lua -- file retriever get.lua -- file retriever
This little program is a client that uses the FTP and HTTP code to This little program is a client that uses the FTP and HTTP code to
implement a command line file graber. Just run implement a command line file graber. Just run
lua -l socket get.lua <remote-file> [<local-file>] lua get.lua <remote-file> [<local-file>]
to download a remote file (either ftp:// or http://) to the specified to download a remote file (either ftp:// or http://) to the specified
local file. The program also prints the download throughput, elapsed local file. The program also prints the download throughput, elapsed
@ -44,7 +52,7 @@ similar to check-links.pl by Jamie Zawinski, but uses all facilities of
the LuaSocket library and the Lua language. It has not been thoroughly the LuaSocket library and the Lua language. It has not been thoroughly
tested, but it should work. Just run tested, but it should work. Just run
lua -l socket check-links.lua {<url>} > output lua check-links.lua {<url>} > output
and open the result to see a list of broken links. and open the result to see a list of broken links.

View File

@ -4,78 +4,144 @@
-- Author: Diego Nehab -- Author: Diego Nehab
-- RCS ID: $Id$ -- RCS ID: $Id$
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Load required modules
-----------------------------------------------------------------------------
local socket = require("socket") local socket = require("socket")
local url = require("url")
local tp = require("tp")
function get_status(sock, valid) -----------------------------------------------------------------------------
local line, err = sock:receive() -- Globals
local code, par -----------------------------------------------------------------------------
if not line then sock:close() return err end HOST = "dict.org"
code = socket.skip(2, string.find(line, "^(%d%d%d)")) PORT = 2628
code = tonumber(code) TIMEOUT = 10
if code ~= valid then return code end
if code == 150 then -----------------------------------------------------------------------------
par = tonumber(socket.skip(2, string.find(line, "^%d%d%d (%d*)"))) -- Low-level dict API
end -----------------------------------------------------------------------------
return nil, par local metat = { __index = {} }
function open(host, port)
local tp = socket.try(tp.connect(host or HOST, port or PORT, TIMEOUT))
return setmetatable({tp = tp}, metat)
end end
function get_def(sock) function metat.__index:greet()
local line, err = sock:receive() return socket.try(self.tp:check(220))
local def = ""
while (not err) and line ~= "." do
def = def .. line .. "\n"
line, err = sock:receive()
end end
if err then sock:close() return nil, err
function metat.__index:check(ok)
local code, status = socket.try(self.tp:check(ok))
return code, tonumber(socket.skip(2, string.find(status, "^%d%d%d (%d*)")))
end
function metat.__index:getdef()
local line = socket.try(self.tp:receive())
local def = {}
while line ~= "." do
table.insert(def, line)
line = socket.try(self.tp:receive())
end
return table.concat(def, "\n")
end
function metat.__index:define(database, word)
database = database or "!"
socket.try(self.tp:command("DEFINE", database .. " " .. word))
local code, count = self:check(150)
local defs = {}
for i = 1, count do
self:check(151)
table.insert(defs, self:getdef())
end
self:check(250)
return defs
end
function metat.__index:match(database, strat, word)
database = database or "!"
strat = strat or "."
socket.try(self.tp:command("MATCH", database .." ".. strat .." ".. word))
self:check(152)
local mat = {}
local line = socket.try(self.tp:receive())
while line ~= '.' do
database, word = socket.skip(2, string.find(line, "(%S+) (.*)"))
if not mat[database] then mat[database] = {} end
table.insert(mat[database], word)
line = socket.try(self.tp:receive())
end
self:check(250)
return mat
end
function metat.__index:quit()
self.tp:command("QUIT")
return self:check(221)
end
function metat.__index:close()
return self.tp:close()
end
-----------------------------------------------------------------------------
-- High-level dict API
-----------------------------------------------------------------------------
local default = {
scheme = "dict",
host = "dict.org"
}
local function there(f)
if f == "" then return nil
else return f end
end
local function parse(u)
local t = socket.try(url.parse(u, default))
socket.try(t.scheme == "dict", "invalid scheme '" .. t.scheme .. "'")
socket.try(t.path, "invalid path in url")
local cmd, arg = socket.skip(2, string.find(t.path, "^/(.)(.*)$"))
socket.try(cmd == "d" or cmd == "m", "<command> should be 'm' or 'd'")
socket.try(arg and arg ~= "", "need at least <word> in URL")
t.command, t.argument = cmd, arg
arg = string.gsub(arg, "^:([^:]+)", function(f) t.word = f end)
socket.try(t.word, "need at least <word> in URL")
arg = string.gsub(arg, "^:([^:]*)", function(f) t.database = there(f) end)
if cmd == "m" then
arg = string.gsub(arg, "^:([^:]*)", function(f) t.strat = there(f) end)
end
string.gsub(arg, ":([^:]*)$", function(f) t.n = tonumber(f) end)
return t
end
local function tget(gett)
local con = open(gett.host, gett.port)
con:greet()
if gett.command == "d" then
local def = con:define(gett.database, gett.word)
con:quit()
con:close()
if gett.n then return def[gett.n]
else return def end else return def end
elseif gett.command == "m" then
local mat = con:match(gett.database, gett.strat, gett.word)
con:quit()
con:close()
return mat
else return nil, "invalid command" end
end end
function dict_open() local function sget(u)
local sock, err = socket.connect("dict.org", 2628) local gett = parse(u)
if not sock then return nil, err end return tget(gett)
sock:settimeout(10)
local code, par = get_status(sock, 220)
if code then return nil, code end
return sock
end end
function dict_define(sock, word, dict) --function socket.protect(f) return f end
dict = dict or "web1913" get = socket.protect(function(gett)
sock:send("DEFINE " .. dict .. " " .. word .. "\r\n") if type(gett) == "string" then return sget(gett)
local code, par = get_status(sock, 150) else return tget(gett) end
if code or not par then return nil, code end end)
local defs = ""
for i = 1, par do
local def
code, par = get_status(sock, 151)
if code then return nil, code end
def, err = get_def(sock)
if not def then return nil, err end
defs = defs .. def .. "\n"
end
code, par = get_status(sock, 250)
if code then return nil, code end
return string.gsub(defs, "%s%s$", "")
end
function dict_close(sock)
sock:send("QUIT\r\n")
local code, par = get_status(sock, 221)
sock:close()
return code
end
function dict_get(word, dict)
local sock, err = dict_open()
if not sock then return nil, err end
local defs, err = dict_define(sock, word, dict)
dict_close(sock)
return defs, err
end
if arg and arg[1] then
defs, err = dict_get(arg[1], arg[2])
print(defs or err)
else
io.write("Usage:\n lua dict.lua <word> [<dictionary>]\n")
end

View File

@ -1,5 +1,11 @@
require("ltn12") -----------------------------------------------------------------------------
require("mime") -- Little program to convert to and from Quoted-Printable
-- LuaSocket sample files
-- Author: Diego Nehab
-- RCS ID: $Id$
-----------------------------------------------------------------------------
local ltn12 = require("ltn12")
local mime = require("mime")
local convert local convert
arg = arg or {} arg = arg or {}
local mode = arg and arg[1] or "-et" local mode = arg and arg[1] or "-et"

View File

@ -2,62 +2,60 @@
# Distribution makefile # Distribution makefile
#-------------------------------------------------------------------------- #--------------------------------------------------------------------------
DIST = luasocket-2.0-alpha DIST = luasocket-2.0-beta
LUA = \ LUA = \
auxiliar.lua \
code.lua \
concat.lua \
ftp.lua \ ftp.lua \
http.lua \ http.lua \
select.lua \ ltn12.lua \
mime.lua \
smtp.lua \ smtp.lua \
socket.lua \
tp.lua \
url.lua url.lua
TESTS = \ TESTS = \
codetest.lua \
concattest.lua \
ftptest.lua \
mbox.lua \
httptest.lua \
noglobals.lua \
smtptest.lua \
testclnt.lua \ testclnt.lua \
testsrvr.lua \ testsrvr.lua \
udptest.lua \ testsupport.lua
urltest.lua
EXAMPLES = \ EXAMPLES = \
check-memory.lua \
b64.lua \
cddb.lua \
daytimeclnt.lua \ daytimeclnt.lua \
echoclnt.lua \ echoclnt.lua \
echosrvr.lua \ echosrvr.lua \
dict.lua \ eol.lua \
listener.lua \ listener.lua \
qp.lua \
talker.lua \ talker.lua \
tinyirc.lua tinyirc.lua
ETC = \ ETC = \
check-links.lua \ check-links.lua \
cl-compat.lua \ dict.lua \
get.lua \ get.lua \
lua.lua \ lua.lua \
luasocket.lua \ tftp.lua
tftp.lua \
CORE = \
MAIN = \
auxiliar.c \ auxiliar.c \
auxiliar.h \ auxiliar.h \
buffer.c \ buffer.c \
buffer.h \ buffer.h \
error.c \ except.c \
error.h \ except.h \
inet.c \ inet.c \
inet.h \ inet.h \
io.c \ io.c \
io.h \ io.h \
luasocket.c \ luasocket.c \
luasocket.h \ luasocket.h \
mime.c \
mime.h \
options.c \
options.h \
select.c \ select.c \
select.h \ select.h \
socket.h \ socket.h \
@ -72,25 +70,56 @@ MAIN = \
wsocket.c \ wsocket.c \
wsocket.h wsocket.h
MAKE = \
makefile.Darwin \
makefile.Linux \
luasocket.export \
mime.export \
luasocket.sln \
luasocket.vcproj \
mime.vcproj
MANUAL = \
manual/dns.html \
manual/ftp.html \
manual/home.html \
manual/http.html \
manual/introduction.html \
manual/ltn12.html \
manual/luasocket.png \
manual/mime.html \
manual/reference.css \
manual/reference.html \
manual/smtp.html \
manual/socket.html \
manual/tcp.html \
manual/udp.html \
manual/url.html
dist: dist:
mkdir -p $(DIST)/examples mkdir -p $(DIST)/examples
mkdir -p $(DIST)/tests mkdir -p $(DIST)/tests
mkdir -p $(DIST)/etc mkdir -p $(DIST)/etc
cp -vf $(MAIN) $(DIST) mkdir -p $(DIST)/lua
cp -vf $(LUA) $(DIST) mkdir -p $(DIST)/make
cp -vf makefile $(DIST) mkdir -p $(DIST)/manual
cp -vf $(CORE) $(DIST)
cp -vf README $(DIST) cp -vf README $(DIST)
cp -vf lua.README $(DIST)
cp -vf NEW $(DIST) cp -vf NEW $(DIST)
cp -vf LICENSE $(DIST)
cp -vf $(MAKE) $(DIST)/make
cp -vf make.README $(DIST)/make/README
cp -vf $(LUA) $(DIST)/lua
cp -vf lua.README $(DIST)/lua/README
cp -vf $(EXAMPLES) $(DIST)/examples cp -vf $(EXAMPLES) $(DIST)/examples
cp -vf examples.README $(DIST)/examples/README cp -vf examples.README $(DIST)/examples/README
cp -vf $(TESTS) $(DIST)/tests cp -vf $(TESTS) $(DIST)/tests
cp -vf tests.README $(DIST)/tests/README cp -vf tests.README $(DIST)/tests/README
cp -vf $(ETC) $(DIST)/etc cp -vf $(ETC) $(DIST)/etc
cp -vf etc.README $(DIST)/etc/README cp -vf etc.README $(DIST)/etc/README
cp -vf $(MANUAL) $(DIST)/manual
tar -zcvf $(DIST).tar.gz $(DIST) tar -zcvf $(DIST).tar.gz $(DIST)
zip -r $(DIST).zip $(DIST) zip -r $(DIST).zip $(DIST)
clean: clean:
\rm -rf $(DIST) $(DIST).tar.gz $(DIST).zip \rm -rf $(DIST) $(DIST).tar.gz $(DIST).zip

View File

@ -7,21 +7,26 @@ is not supported.
listener.lua and talker.lua are about the simplest applications you can listener.lua and talker.lua are about the simplest applications you can
write using LuaSocket. Run write using LuaSocket. Run
'lua -l luasocket listen.lua' and 'lua -l luasocket talk.lua' 'lua listen.lua' and 'lua talk.lua'
on different terminals. Whatever you type on talk.lua will be on different terminals. Whatever you type on talk.lua will be
printed by listen.lua. printed by listen.lua.
dict.lua -- dict client b64.lua
qp.lua
eol.lua
The dict.lua module was a cool simple client for the DICT protocol, These are tiny programs that perform Base64, Quoted-Printable and
written by Luiz Henrique Figueiredo. This new version has been converted end-of-line marker conversions.
into a library, similar to the HTTP and FTP libraries, that can be used
from within any luasocket application. Take a look on the source code cddb.lua -- CDDB client
and you will be able to figure out how to use it.
This is the first try on a simple CDDB client. Not really useful, but one
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.
@ -40,5 +45,9 @@ function and shows how to create a simple server whith LuaSocket. Just
run tinyirc.lua and then open as many telnet connections as you want run tinyirc.lua and then open as many telnet connections as you want
to ports 8080 and 8081. to ports 8080 and 8081.
check-memory.lua -- checks memory consumption
This is just to see how much memory each module uses.
Good luck, Good luck,
Diego. Diego.

View File

@ -16,6 +16,7 @@
* Initializes the module * Initializes the module
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
int aux_open(lua_State *L) { int aux_open(lua_State *L) {
(void) L;
return 0; return 0;
} }

View File

@ -176,7 +176,7 @@ end
-- High level FTP API -- High level FTP API
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
local function tput(putt) local function tput(putt)
local con = ftp.open(putt.host, putt.port) local con = open(putt.host, putt.port)
con:greet() con:greet()
con:login(putt.user, putt.password) con:login(putt.user, putt.password)
if putt.type then con:type(putt.type) end if putt.type then con:type(putt.type) end
@ -216,7 +216,7 @@ put = socket.protect(function(putt, body)
end) end)
local function tget(gett) local function tget(gett)
local con = ftp.open(gett.host, gett.port) local con = open(gett.host, gett.port)
con:greet() con:greet()
con:login(gett.user, gett.password) con:login(gett.user, gett.password)
if gett.type then con:type(gett.type) end if gett.type then con:type(gett.type) end

View File

@ -75,6 +75,7 @@ static int global_skip(lua_State *L) {
* Unloads the library * Unloads the library
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
static int global_unload(lua_State *L) { static int global_unload(lua_State *L) {
(void) L;
sock_close(); sock_close();
return 0; return 0;
} }

View File

@ -5,19 +5,8 @@ The files provided are:
testsrvr.lua -- test server testsrvr.lua -- test server
testclnt.lua -- test client testclnt.lua -- test client
testcmd.lua -- test command definitions
To run the automatic tests on your system, make sure to compile the To run these tests, just run lua on the server and then on the client.
library with _DEBUG defined (check makefile) and then open two
terminals. Run 'luasocket testsrvr.lua' on one of them and 'luasocket
testclnt.lua' on the other. The programs should start talking to each
other and report any failure. The tests can also be used as a benchmark.
urltest.lua -- url.lua test module
codetest.lua -- code.lua test module
concattest.lua -- concat.lua test module
To run these tests, just run luasocket on them and see the results.
Good luck, Good luck,
Diego. Diego.

View File

@ -1,5 +0,0 @@
a = ltn12.source.file(io.open("luasocket.lua"))
b = ltn12.source.file(io.open("auxiliar.lua"))
c = ltn12.source.cat(a, b)
d = ltn12.sink.file(io.stdout)
socket.try(ltn12.pump.all(c, d))

View File

@ -1,13 +1,13 @@
smtp = require("smtp") mime = require("mime")
function test_dot(original, right) function test_dot(original, right)
local result, n = smtp.dot(2, original) local result, n = mime.dot(2, original)
assert(result == right, "->" .. result .. "<-") assert(result == right, "->" .. result .. "<-")
print("ok") print("ok")
end end
function test_stuff(original, right) function test_stuff(original, right)
local result, n = smtp.dot(2, original) local result, n = mime.dot(2, original)
assert(result == right, "->" .. result .. "<-") assert(result == right, "->" .. result .. "<-")
print("ok") print("ok")
end end

View File

@ -50,10 +50,9 @@ source = smtp.message{
-- finally send it -- finally send it
r, e = smtp.send{ r, e = smtp.send{
rcpt = "<diego@cs.princeton.edu>", rcpt = "<fulano@tecgraf.puc-rio.br>",
from = "<diego@cs.princeton.edu>", from = "<sicrano@tecgraf.puc-rio.br>",
source = source, source = source
server = "mail.cs.princeton.edu"
} }
print(r, e) print(r, e)