From 773e35ced30fa2c03ddb2a332bf8a9aebb56aa44 Mon Sep 17 00:00:00 2001 From: Diego Nehab Date: Tue, 23 Aug 2005 05:53:14 +0000 Subject: [PATCH] Compiled on Windows. Fixed a bunch of stuff. Almost ready to release. Implemented a nice dispatcher! Non-blocking check-links and forward server use the dispatcher. --- TODO | 9 +- config | 12 +- doc/index.html | 4 +- etc/check-links.lua | 31 +-- etc/dispatch.lua | 404 ++++++++++++++++-------------- etc/forward.lua | 65 +++++ luasocket.sln | 2 +- makefile | 37 +-- makefile.dist | 9 +- mime.vcproj | 32 +-- luasocket.vcproj => socket.vcproj | 101 +++----- src/auxiliar.c | 8 + src/http.lua | 1 - src/luasocket.h | 2 +- src/mime.c | 2 +- src/mime.h | 2 +- src/smtp.lua | 49 ++-- src/wsocket.c | 2 +- test/ftptest.lua | 4 +- test/testmesg.lua | 42 +++- 20 files changed, 454 insertions(+), 364 deletions(-) create mode 100644 etc/forward.lua rename luasocket.vcproj => socket.vcproj (63%) diff --git a/TODO b/TODO index b2a167e..bd60aaa 100644 --- a/TODO +++ b/TODO @@ -1,11 +1,9 @@ new instalation scheme??? + test empty socket.select no windows. + bug by mathew percival? -arranjar um jeito de fazer multipart/alternative - -what the hell does __unload do? - test it on Windows!!! leave code for losers that don't have nanosleep @@ -45,3 +43,6 @@ testar os options! * received connections * - this function is invoked with the client socket * - it calls special send and receive functions that yield on timeout +* arranjar um jeito de fazer multipart/alternative +* what the hell does __unload do? + * it's there just in case someone wants to use it. diff --git a/config b/config index da9fdf4..a85e8e6 100644 --- a/config +++ b/config @@ -8,8 +8,8 @@ EXT=so SOCKET_V=2.0.0 MIME_V=1.0.0 -SOCKET_SO=socket-core.$(EXT).$(SOCKET_V) -MIME_SO=mime-core.$(EXT).$(MIME_V) +SOCKET_SO=socket.$(EXT).$(SOCKET_V) +MIME_SO=mime.$(EXT).$(MIME_V) UNIX_SO=unix.$(EXT) #------ @@ -25,13 +25,15 @@ COMPAT=compat-5.1r4 #------ # Top of your Lua installation -# Relative paths will be inside src tree +# Relative paths will be inside the src tree # -INSTALL_TOP=/usr/local/share/lua/5.0 +#INSTALL_TOP_LUA=/usr/local/share/lua/5.0 +#INSTALL_TOP_LIB=/usr/local/lib/lua/5.0 +INSTALL_TOP_LUA=share +INSTALL_TOP_LIB=lib INSTALL_DATA=cp INSTALL_EXEC=cp -INSTALL_LINK=ln #------ # Compiler and linker settings diff --git a/doc/index.html b/doc/index.html index e596f8e..3d5acb3 100644 --- a/doc/index.html +++ b/doc/index.html @@ -168,6 +168,8 @@ support.
  • Improved: tcp:send(data, i, j) to return (i+sent-1). This is great for non-blocking I/O, but might break some code;
  • Improved: HTTP, SMTP, and FTP functions to accept a new field create that overrides the function used to create socket objects; +
  • Improved: smtp.message now supports multipart/alternative +(for the HTML messages we all love so much);
  • Fixed: smtp.send was hanging on errors returned by LTN12 sources;
  • Fixed: url.absolute() to work when base_url is in parsed form; @@ -183,7 +185,7 @@ group;
  • Improved: Socket and MIME binaries are called 'core' each inside their directory (ex. "socket/core.dll"). The 'l' prefix was just a bad idea;
  • Improved: Using bundles in Mac OS X, instead of dylibs; -
  • Fixed: luasocket.h to export luaopen_socketcore; +
  • Fixed: luasocket.h to export luaopen_socket_core;
  • Fixed: udp:setpeername() so you can "disconnect" an UDP socket;
  • Fixed: A weird bug in HTTP support that caused some requests to diff --git a/etc/check-links.lua b/etc/check-links.lua index e06cc91..725cd2a 100644 --- a/etc/check-links.lua +++ b/etc/check-links.lua @@ -5,33 +5,26 @@ -- Author: Diego Nehab -- RCS ID: $$ ----------------------------------------------------------------------------- -local dispatch, url, http, handler +local url = require("socket.url") +local dispatch = require("dispatch") +local http = require("socket.http") +dispatch.TIMEOUT = 10 +-- make sure the user knows how to invoke us arg = arg or {} if table.getn(arg) < 1 then print("Usage:\n luasocket check-links.lua [-n] {}") exit() end -if arg[1] ~= "-n" then - -- if using blocking I/O, simulate dispatcher interface - url = require("socket.url") - http = require("socket.http") - handler = { - start = function(self, f) - f() - end, - tcp = socket.tcp - } - http.TIMEOUT = 10 -else - -- if non-blocking I/O was requested, disable dispatcher +-- '-n' means we are running in non-blocking mode +if arg[1] == "-n" then + -- if non-blocking I/O was requested, use real dispatcher interface table.remove(arg, 1) - dispatch = require("dispatch") - dispatch.TIMEOUT = 10 - url = require("socket.url") - http = require("socket.http") - handler = dispatch.newhandler() + handler = dispatch.newhandler("coroutine") +else + -- if using blocking I/O, use fake dispatcher interface + handler = dispatch.newhandler("sequential") end local nthreads = 0 diff --git a/etc/dispatch.lua b/etc/dispatch.lua index e6c14a6..98fa8a8 100644 --- a/etc/dispatch.lua +++ b/etc/dispatch.lua @@ -11,23 +11,33 @@ module("dispatch") -- if too much time goes by without any activity in one of our sockets, we -- just kill it -TIMEOUT = 10 +TIMEOUT = 60 ----------------------------------------------------------------------------- --- Mega hack. Don't try to do this at home. +-- We implement 3 types of dispatchers: +-- sequential +-- coroutine +-- threaded +-- The user can choose whatever one is needed ----------------------------------------------------------------------------- --- Lua 5.1 has coroutine.running(). We need it here, so we use this terrible --- hack to emulate it in Lua itself --- This is very inefficient, but is very good for debugging. -local running -local resume = coroutine.resume -function coroutine.resume(co, ...) - running = co - return resume(co, unpack(arg)) +local handlert = {} + +-- default handler is coroutine +function newhandler(mode) + mode = mode or "coroutine" + return handlert[mode]() end -function coroutine.running() - return running +local function seqstart(self, func) + return func() +end + +-- sequential handler simply calls the functions and doesn't wrap I/O +function handlert.sequential() + return { + tcp = socket.tcp, + start = seqstart + } end ----------------------------------------------------------------------------- @@ -36,15 +46,11 @@ end -- we can't yield across calls to protect, so we rewrite it with coxpcall -- make sure you don't require any module that uses socket.protect before -- loading our hack -function socket.protect(f) - return f -end - function socket.protect(f) return function(...) local co = coroutine.create(f) while true do - local results = {resume(co, unpack(arg))} + local results = {coroutine.resume(co, unpack(arg))} local status = table.remove(results, 1) if not status then if type(results[1]) == 'table' then @@ -61,133 +67,7 @@ function socket.protect(f) end ----------------------------------------------------------------------------- --- socket.tcp() replacement for non-blocking I/O ------------------------------------------------------------------------------ -local function newtrap(dispatcher) - -- try to create underlying socket - local tcp, error = socket.tcp() - if not tcp then return nil, error end - -- put it in non-blocking mode right away - tcp:settimeout(0) - -- metatable for trap produces new methods on demand for those that we - -- don't override explicitly. - local metat = { __index = function(table, key) - table[key] = function(...) - return tcp[key](tcp, unpack(arg)) - end - end} - -- does user want to do his own non-blocking I/O? - local zero = false - -- create a trap object that will behave just like a real socket object - local trap = { } - -- we ignore settimeout to preserve our 0 timeout, but record whether - -- the user wants to do his own non-blocking I/O - function trap:settimeout(mode, value) - if value == 0 then - zero = true - else - zero = false - end - return 1 - end - -- send in non-blocking mode and yield on timeout - function trap:send(data, first, last) - first = (first or 1) - 1 - local result, error - while true do - -- tell dispatcher we want to keep sending before we yield - dispatcher.sending:insert(tcp) - -- mark time we started waiting - dispatcher.context[tcp].last = socket.gettime() - -- return control to dispatcher - -- if upon return the dispatcher tells us we timed out, - -- return an error to whoever called us - if coroutine.yield() == "timeout" then - return nil, "timeout" - end - -- try sending - result, error, first = tcp:send(data, first+1, last) - -- if we are done, or there was an unexpected error, - -- break away from loop - if error ~= "timeout" then return result, error, first end - end - end - -- receive in non-blocking mode and yield on timeout - -- or simply return partial read, if user requested timeout = 0 - function trap:receive(pattern, partial) - local error = "timeout" - local value - while true do - -- tell dispatcher we want to keep receiving before we yield - dispatcher.receiving:insert(tcp) - -- mark time we started waiting - dispatcher.context[tcp].last = socket.gettime() - -- return control to dispatcher - -- if upon return the dispatcher tells us we timed out, - -- return an error to whoever called us - if coroutine.yield() == "timeout" then - return nil, "timeout" - end - -- try receiving - value, error, partial = tcp:receive(pattern, partial) - -- if we are done, or there was an unexpected error, - -- break away from loop - if (error ~= "timeout") or zero then - return value, error, partial - end - end - end - -- connect in non-blocking mode and yield on timeout - function trap:connect(host, port) - local result, error = tcp:connect(host, port) - -- mark time we started waiting - dispatcher.context[tcp].last = socket.gettime() - if error == "timeout" then - -- tell dispatcher we will be able to write uppon connection - dispatcher.sending:insert(tcp) - -- return control to dispatcher - -- if upon return the dispatcher tells us we have a - -- timeout, just abort - if coroutine.yield() == "timeout" then - return nil, "timeout" - end - -- when we come back, check if connection was successful - result, error = tcp:connect(host, port) - if result or error == "already connected" then return 1 - else return nil, "non-blocking connect failed" end - else return result, error end - end - -- accept in non-blocking mode and yield on timeout - function trap:accept() - local result, error = tcp:accept() - while error == "timeout" do - -- mark time we started waiting - dispatcher.context[tcp].last = socket.gettime() - -- tell dispatcher we will be able to read uppon connection - dispatcher.receiving:insert(tcp) - -- return control to dispatcher - -- if upon return the dispatcher tells us we have a - -- timeout, just abort - if coroutine.yield() == "timeout" then - return nil, "timeout" - end - end - return result, error - end - -- remove thread from context - function trap:close() - dispatcher.context[tcp] = nil - return tcp:close() - end - -- add newly created socket to context - dispatcher.context[tcp] = { - thread = coroutine.running() - } - return setmetatable(trap, metat) -end - ------------------------------------------------------------------------------ --- Our set data structure +-- Simple set data structure. O(1) everything. ----------------------------------------------------------------------------- local function newset() local reverse = {} @@ -214,54 +94,208 @@ local function newset() end ----------------------------------------------------------------------------- --- Our dispatcher API. +-- socket.tcp() wrapper for the coroutine dispatcher ----------------------------------------------------------------------------- -local metat = { __index = {} } - -function metat.__index:start(func) - local co = coroutine.create(func) - assert(coroutine.resume(co)) -end - -function newhandler() - local dispatcher = { - context = {}, - sending = newset(), - receiving = newset() - } - function dispatcher.tcp() - return newtrap(dispatcher) - end - return setmetatable(dispatcher, metat) -end - --- step through all active threads -function metat.__index:step() - -- check which sockets are interesting and act on them - local readable, writable = socket.select(self.receiving, - self.sending, 1) - -- for all readable connections, resume their threads - for _, who in ipairs(readable) do - if self.context[who] then - self.receiving:remove(who) - assert(coroutine.resume(self.context[who].thread)) +local function cowrap(dispatcher, tcp, error) + if not tcp then return nil, error end + -- put it in non-blocking mode right away + tcp:settimeout(0) + -- metatable for wrap produces new methods on demand for those that we + -- don't override explicitly. + local metat = { __index = function(table, key) + table[key] = function(...) + arg[1] = tcp + return tcp[key](unpack(arg)) end + return table[key] + end} + -- does our user want to do his own non-blocking I/O? + local zero = false + -- create a wrap object that will behave just like a real socket object + local wrap = { } + -- we ignore settimeout to preserve our 0 timeout, but record whether + -- the user wants to do his own non-blocking I/O + function wrap:settimeout(value, mode) + if value == 0 then zero = true + else zero = false end + return 1 + end + -- send in non-blocking mode and yield on timeout + function wrap:send(data, first, last) + first = (first or 1) - 1 + local result, error + while true do + -- return control to dispatcher and tell it we want to send + -- if upon return the dispatcher tells us we timed out, + -- return an error to whoever called us + if coroutine.yield(dispatcher.sending, tcp) == "timeout" then + return nil, "timeout" + end + -- try sending + result, error, first = tcp:send(data, first+1, last) + -- if we are done, or there was an unexpected error, + -- break away from loop + if error ~= "timeout" then return result, error, first end + end + end + -- receive in non-blocking mode and yield on timeout + -- or simply return partial read, if user requested timeout = 0 + function wrap:receive(pattern, partial) + local error = "timeout" + local value + while true do + -- return control to dispatcher and tell it we want to receive + -- if upon return the dispatcher tells us we timed out, + -- return an error to whoever called us + if coroutine.yield(dispatcher.receiving, tcp) == "timeout" then + return nil, "timeout" + end + -- try receiving + value, error, partial = tcp:receive(pattern, partial) + -- if we are done, or there was an unexpected error, + -- break away from loop. also, if the user requested + -- zero timeout, return all we got + if (error ~= "timeout") or zero then + return value, error, partial + end + end + end + -- connect in non-blocking mode and yield on timeout + function wrap:connect(host, port) + local result, error = tcp:connect(host, port) + if error == "timeout" then + -- return control to dispatcher. we will be writable when + -- connection succeeds. + -- if upon return the dispatcher tells us we have a + -- timeout, just abort + if coroutine.yield(dispatcher.sending, tcp) == "timeout" then + return nil, "timeout" + end + -- when we come back, check if connection was successful + result, error = tcp:connect(host, port) + if result or error == "already connected" then return 1 + else return nil, "non-blocking connect failed" end + else return result, error end + end + -- accept in non-blocking mode and yield on timeout + function wrap:accept() + while 1 do + -- return control to dispatcher. we will be readable when a + -- connection arrives. + -- if upon return the dispatcher tells us we have a + -- timeout, just abort + if coroutine.yield(dispatcher.receiving, tcp) == "timeout" then + return nil, "timeout" + end + local client, error = tcp:accept() + if error ~= "timeout" then + return cowrap(dispatcher, client, error) + end + end + end + -- remove cortn from context + function wrap:close() + dispatcher.stamp[tcp] = nil + dispatcher.sending.set:remove(tcp) + dispatcher.sending.cortn[tcp] = nil + dispatcher.receiving.set:remove(tcp) + dispatcher.receiving.cortn[tcp] = nil + return tcp:close() + end + return setmetatable(wrap, metat) +end + + +----------------------------------------------------------------------------- +-- Our coroutine dispatcher +----------------------------------------------------------------------------- +local cometat = { __index = {} } + +function schedule(cortn, status, operation, tcp) + if status then + if cortn and operation then + operation.set:insert(tcp) + operation.cortn[tcp] = cortn + operation.stamp[tcp] = socket.gettime() + end + else error(operation) end +end + +function kick(operation, tcp) + operation.cortn[tcp] = nil + operation.set:remove(tcp) +end + +function wakeup(operation, tcp) + local cortn = operation.cortn[tcp] + -- if cortn is still valid, wake it up + if cortn then + kick(operation, tcp) + return cortn, coroutine.resume(cortn) + -- othrewise, just get scheduler not to do anything + else + return nil, true + end +end + +function abort(operation, tcp) + local cortn = operation.cortn[tcp] + if cortn then + kick(operation, tcp) + coroutine.resume(cortn, "timeout") + end +end + +-- step through all active cortns +function cometat.__index:step() + -- check which sockets are interesting and act on them + local readable, writable = socket.select(self.receiving.set, + self.sending.set, 1) + -- for all readable connections, resume their cortns and reschedule + -- when they yield back to us + for _, tcp in ipairs(readable) do + schedule(wakeup(self.receiving, tcp)) end -- for all writable connections, do the same - for _, who in ipairs(writable) do - if self.context[who] then - self.sending:remove(who) - assert(coroutine.resume(self.context[who].thread)) - end + for _, tcp in ipairs(writable) do + schedule(wakeup(self.sending, tcp)) end - -- politely ask replacement I/O functions in idle threads to + -- politely ask replacement I/O functions in idle cortns to -- return reporting a timeout local now = socket.gettime() - for who, data in pairs(self.context) do - if data.last and now - data.last > TIMEOUT then - self.sending:remove(who) - self.receiving:remove(who) - assert(coroutine.resume(self.context[who].thread, "timeout")) + for tcp, stamp in pairs(self.stamp) do + if tcp.class == "tcp{client}" and now - stamp > TIMEOUT then + abort(self.sending, tcp) + abort(self.receiving, tcp) end end end + +function cometat.__index:start(func) + local cortn = coroutine.create(func) + schedule(cortn, coroutine.resume(cortn)) +end + +function handlert.coroutine() + local stamp = {} + local dispatcher = { + stamp = stamp, + sending = { + name = "sending", + set = newset(), + cortn = {}, + stamp = stamp + }, + receiving = { + name = "receiving", + set = newset(), + cortn = {}, + stamp = stamp + }, + } + function dispatcher.tcp() + return cowrap(dispatcher, socket.tcp()) + end + return setmetatable(dispatcher, cometat) +end + diff --git a/etc/forward.lua b/etc/forward.lua new file mode 100644 index 0000000..eac98ae --- /dev/null +++ b/etc/forward.lua @@ -0,0 +1,65 @@ +-- load our favourite library +local dispatch = require("dispatch") +local handler = dispatch.newhandler() + +-- make sure the user knows how to invoke us +if table.getn(arg) < 1 then + print("Usage") + print(" lua forward.lua ...") + os.exit(1) +end + +-- function to move data from one socket to the other +local function move(foo, bar) + local live + while 1 do + local data, error, partial = foo:receive(2048) + live = data or error == "timeout" + data = data or partial + local result, error = bar:send(data) + if not live or not result then + foo:close() + bar:close() + break + end + end +end + +-- for each tunnel, start a new server +for i, v in ipairs(arg) do + -- capture forwarding parameters + local _, _, iport, ohost, oport = string.find(v, "([^:]+):([^:]+):([^:]+)") + assert(iport, "invalid arguments") + -- create our server socket + local server = assert(handler.tcp()) + assert(server:setoption("reuseaddr", true)) + assert(server:bind("*", iport)) + assert(server:listen(32)) + -- handler for the server object loops accepting new connections + handler:start(function() + while 1 do + local client = assert(server:accept()) + assert(client:settimeout(0)) + -- for each new connection, start a new client handler + handler:start(function() + -- handler tries to connect to peer + local peer = assert(handler.tcp()) + assert(peer:settimeout(0)) + assert(peer:connect(ohost, oport)) + -- if sucessful, starts a new handler to send data from + -- client to peer + handler:start(function() + move(client, peer) + end) + -- afte starting new handler, enter in loop sending data from + -- peer to client + move(peer, client) + end) + end + end) +end + +-- simply loop stepping the server +while 1 do + handler:step() +end diff --git a/luasocket.sln b/luasocket.sln index 6a1d0b8..006b7f0 100644 --- a/luasocket.sln +++ b/luasocket.sln @@ -1,5 +1,5 @@ Microsoft Visual Studio Solution File, Format Version 8.00 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "luasocket", "luasocket.vcproj", "{66E3CE14-884D-4AEA-9F20-15A0BEAF8C5A}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "socket", "socket.vcproj", "{66E3CE14-884D-4AEA-9F20-15A0BEAF8C5A}" ProjectSection(ProjectDependencies) = postProject EndProjectSection EndProject diff --git a/makefile b/makefile index 234e162..c84ef10 100644 --- a/makefile +++ b/makefile @@ -6,8 +6,10 @@ include config #------ # Hopefully no need to change anything below this line # -INSTALL_SOCKET=$(INSTALL_TOP)/socket -INSTALL_MIME=$(INSTALL_TOP)/mime +INSTALL_SOCKET_LUA=$(INSTALL_TOP_LUA)/socket +INSTALL_SOCKET_LIB=$(INSTALL_TOP_LIB)/socket +INSTALL_MIME_LUA=$(INSTALL_TOP_LUA)/mime +INSTALL_MIME_LIB=$(INSTALL_TOP_LIB)/mime all clean: cd src; $(MAKE) $@ @@ -15,7 +17,7 @@ all clean: #------ # Files to install # -TO_SOCKET:= \ +TO_SOCKET_LUA:= \ socket.lua \ http.lua \ url.lua \ @@ -23,29 +25,28 @@ TO_SOCKET:= \ ftp.lua \ smtp.lua -TO_TOP:= \ +TO_TOP_LUA:= \ ltn12.lua -TO_MIME:= \ - $(MIME_SO) \ +TO_MIME_LUA:= \ mime.lua #------ # Install LuaSocket according to recommendation # install: all - cd src; mkdir -p $(INSTALL_TOP) - cd src; $(INSTALL_DATA) $(COMPAT)/compat-5.1.lua $(INSTALL_TOP) - cd src; $(INSTALL_DATA) ltn12.lua $(INSTALL_TOP) - cd src; mkdir -p $(INSTALL_SOCKET) - cd src; $(INSTALL_EXEC) $(SOCKET_SO) $(INSTALL_SOCKET) - cd src; $(INSTALL_DATA) $(TO_SOCKET) $(INSTALL_SOCKET) - cd src; cd $(INSTALL_SOCKET); $(INSTALL_LINK) -s $(SOCKET_SO) core.$(EXT) - cd src; cd $(INSTALL_SOCKET); $(INSTALL_LINK) -s socket.lua init.lua - cd src; mkdir -p $(INSTALL_MIME) - cd src; $(INSTALL_DATA) $(TO_MIME) $(INSTALL_MIME) - cd src; cd $(INSTALL_MIME); $(INSTALL_LINK) -s $(MIME_SO) core.$(EXT) - cd src; cd $(INSTALL_MIME); $(INSTALL_LINK) -s mime.lua init.lua + cd src; mkdir -p $(INSTALL_TOP_LUA) + cd src; mkdir -p $(INSTALL_TOP_LIB) + cd src; $(INSTALL_DATA) $(COMPAT)/compat-5.1.lua $(INSTALL_TOP_LUA) + cd src; $(INSTALL_DATA) ltn12.lua $(INSTALL_TOP_LUA) + cd src; mkdir -p $(INSTALL_SOCKET_LUA) + cd src; mkdir -p $(INSTALL_SOCKET_LIB) + cd src; $(INSTALL_DATA) $(TO_SOCKET_LUA) $(INSTALL_SOCKET_LUA) + cd src; $(INSTALL_EXEC) $(SOCKET_SO) $(INSTALL_SOCKET_LIB)/core.$(EXT) + cd src; mkdir -p $(INSTALL_MIME_LUA) + cd src; mkdir -p $(INSTALL_MIME_LIB) + cd src; $(INSTALL_DATA) $(TO_MIME_LUA) $(INSTALL_MIME_LUA) + cd src; $(INSTALL_EXEC) $(MIME_SO) $(INSTALL_MIME_LIB)/core.$(EXT) #------ # End of makefile diff --git a/makefile.dist b/makefile.dist index 4ccddbc..8b79653 100644 --- a/makefile.dist +++ b/makefile.dist @@ -2,9 +2,9 @@ # Distribution makefile #-------------------------------------------------------------------------- -DIST = luasocket-2.0-beta3 +DIST = luasocket-2.0 -COMPAT = compat-5.1r2 +COMPAT = compat-5.1r4 LUA = \ ftp.lua \ @@ -37,6 +37,7 @@ EXAMPLES = \ ETC = \ check-links.lua \ + check-links-nb.lua \ dict.lua \ get.lua \ unix.c \ @@ -76,8 +77,8 @@ CORE = \ wsocket.h MAKE = \ - makefile.Darwin \ - makefile.Linux \ + makefile \ + config \ luasocket.sln \ luasocket.vcproj \ mime.vcproj diff --git a/mime.vcproj b/mime.vcproj index 43d289a..c4e328f 100644 --- a/mime.vcproj +++ b/mime.vcproj @@ -12,18 +12,18 @@ + RelativePath="src\compat-5.1r4\compat-5.1.c"> + RelativePath="src\mime.c"> - - - - + RelativePath="..\lua-5.0.2\lib\lua50.lib"> diff --git a/luasocket.vcproj b/socket.vcproj similarity index 63% rename from luasocket.vcproj rename to socket.vcproj index 71486d6..ddfb46e 100644 --- a/luasocket.vcproj +++ b/socket.vcproj @@ -2,7 +2,7 @@ @@ -12,18 +12,18 @@ @@ -63,15 +63,15 @@ @@ -119,91 +119,49 @@ Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx" UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"> + RelativePath="src\auxiliar.c"> + RelativePath="src\buffer.c"> + RelativePath="src\compat-5.1r4\compat-5.1.c"> + RelativePath="src\except.c"> + RelativePath="src\inet.c"> + RelativePath="src\io.c"> + RelativePath="src\luasocket.c"> + RelativePath="src\options.c"> + RelativePath="src\select.c"> + RelativePath="src\tcp.c"> + RelativePath="src\timeout.c"> + RelativePath="src\udp.c"> + RelativePath="src\wsocket.c"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + RelativePath="..\lua-5.0.2\lib\lua50.lib"> diff --git a/src/auxiliar.c b/src/auxiliar.c index 2ebcdd6..b228785 100644 --- a/src/auxiliar.c +++ b/src/auxiliar.c @@ -127,6 +127,9 @@ void aux_setclass(lua_State *L, const char *classname, int objidx) { * otherwise \*-------------------------------------------------------------------------*/ void *aux_getgroupudata(lua_State *L, const char *groupname, int objidx) { +#if 0 + return lua_touserdata(L, objidx); +#else if (!lua_getmetatable(L, objidx)) return NULL; lua_pushstring(L, groupname); @@ -138,6 +141,7 @@ void *aux_getgroupudata(lua_State *L, const char *groupname, int objidx) { lua_pop(L, 2); return lua_touserdata(L, objidx); } +#endif } /*-------------------------------------------------------------------------*\ @@ -145,5 +149,9 @@ void *aux_getgroupudata(lua_State *L, const char *groupname, int objidx) { * otherwise \*-------------------------------------------------------------------------*/ void *aux_getclassudata(lua_State *L, const char *classname, int objidx) { +#if 0 + return lua_touserdata(L, objidx); +#else return luaL_checkudata(L, objidx, classname); +#endif } diff --git a/src/http.lua b/src/http.lua index 9434d97..fe779a3 100644 --- a/src/http.lua +++ b/src/http.lua @@ -325,4 +325,3 @@ request = socket.protect(function(reqt, body) if base.type(reqt) == "string" then return srequest(reqt, body) else return trequest(reqt) end end) - diff --git a/src/luasocket.h b/src/luasocket.h index 34a7693..c7d09d8 100644 --- a/src/luasocket.h +++ b/src/luasocket.h @@ -27,6 +27,6 @@ /*-------------------------------------------------------------------------*\ * Initializes the library. \*-------------------------------------------------------------------------*/ -LUASOCKET_API int luaopen_socketcore(lua_State *L); +LUASOCKET_API int luaopen_socket_core(lua_State *L); #endif /* LUASOCKET_H */ diff --git a/src/mime.c b/src/mime.c index 70e0db9..4539e2c 100644 --- a/src/mime.c +++ b/src/mime.c @@ -78,7 +78,7 @@ static UC b64unbase[256]; /*-------------------------------------------------------------------------*\ * Initializes module \*-------------------------------------------------------------------------*/ -MIME_API int luaopen_mimecore(lua_State *L) +MIME_API int luaopen_mime_core(lua_State *L) { luaL_openlib(L, "mime", func, 0); /* initialize lookup tables */ diff --git a/src/mime.h b/src/mime.h index 34031a1..eda0898 100644 --- a/src/mime.h +++ b/src/mime.h @@ -19,6 +19,6 @@ #define MIME_API extern #endif -MIME_API int luaopen_mimecore(lua_State *L); +MIME_API int luaopen_mime_core(lua_State *L); #endif /* MIME_H */ diff --git a/src/smtp.lua b/src/smtp.lua index 46df1ab..5c485c2 100644 --- a/src/smtp.lua +++ b/src/smtp.lua @@ -137,12 +137,24 @@ end -- send_message forward declaration local send_message +-- yield the headers all at once, it's faster +local function send_headers(headers) + local h = "\r\n" + for i,v in base.pairs(headers) do + h = i .. ': ' .. v .. "\r\n" .. h + end + coroutine.yield(h) +end + -- yield multipart message body from a multipart message table local function send_multipart(mesgt) + -- make sure we have our boundary and send headers local bd = newboundary() - -- define boundary and finish headers - coroutine.yield('content-type: multipart/mixed; boundary="' .. - bd .. '"\r\n\r\n') + local headers = mesgt.headers or {} + headers['content-type'] = headers['content-type'] or 'multipart/mixed' + headers['content-type'] = headers['content-type'] .. + '; boundary="' .. bd .. '"' + send_headers(headers) -- send preamble if mesgt.body.preamble then coroutine.yield(mesgt.body.preamble) @@ -164,11 +176,11 @@ end -- yield message body from a source local function send_source(mesgt) - -- set content-type if user didn't override - if not mesgt.headers or not mesgt.headers["content-type"] then - coroutine.yield('content-type: text/plain; charset="iso-8859-1"\r\n\r\n') - else coroutine.yield("\r\n") end - -- finish headers + -- make sure we have a content-type + local headers = mesgt.headers or {} + headers['content-type'] = headers['content-type'] or + 'text/plain; charset="iso-8859-1"' + send_headers(headers) -- send body from source while true do local chunk, err = mesgt.body() @@ -180,28 +192,17 @@ end -- yield message body from a string local function send_string(mesgt) - -- set content-type if user didn't override - if not mesgt.headers or not mesgt.headers["content-type"] then - coroutine.yield('content-type: text/plain; charset="iso-8859-1"\r\n\r\n') - else coroutine.yield("\r\n") end + -- make sure we have a content-type + local headers = mesgt.headers or {} + headers['content-type'] = headers['content-type'] or + 'text/plain; charset="iso-8859-1"' + send_headers(headers) -- send body from string coroutine.yield(mesgt.body) end --- yield the headers all at once -local function send_headers(mesgt) - if mesgt.headers then - local h = "" - for i,v in base.pairs(mesgt.headers) do - h = i .. ': ' .. v .. "\r\n" .. h - end - coroutine.yield(h) - end -end - -- message source function send_message(mesgt) - send_headers(mesgt) if base.type(mesgt.body) == "table" then send_multipart(mesgt) elseif base.type(mesgt.body) == "function" then send_source(mesgt) else send_string(mesgt) end diff --git a/src/wsocket.c b/src/wsocket.c index c4c51b4..0f6005f 100644 --- a/src/wsocket.c +++ b/src/wsocket.c @@ -75,7 +75,7 @@ int sock_select(int n, fd_set *rfds, fd_set *wfds, fd_set *efds, p_tm tm) { tv.tv_sec = (int) t; tv.tv_usec = (int) ((t - tv.tv_sec) * 1.0e6); if (n <= 0) { - Sleep(1000*t); + Sleep((DWORD) (1000*t)); return 0; } else return select(0, rfds, wfds, efds, t >= 0.0? &tv: NULL); } diff --git a/test/ftptest.lua b/test/ftptest.lua index ef1bf0f..63d20e6 100644 --- a/test/ftptest.lua +++ b/test/ftptest.lua @@ -12,8 +12,8 @@ local host, port, index_file, index, back, err, ret local t = socket.gettime() -host = host or "diego.student.princeton.edu" -index_file = "test/index.html" +host = host or "localhost" +index_file = "index.html" -- a function that returns a directory listing diff --git a/test/testmesg.lua b/test/testmesg.lua index 37e8c11..1dd9a97 100644 --- a/test/testmesg.lua +++ b/test/testmesg.lua @@ -3,6 +3,42 @@ local smtp = require("socket.smtp") local mime = require("mime") local ltn12 = require("ltn12") +function filter(s) + if s then io.write(s) end + return s +end + +source = smtp.message { + headers = { ['content-type'] = 'multipart/alternative' }, + body = { + [1] = { + headers = { ['content-type'] = 'text/html' }, + body = " Hi, there... " + }, + [2] = { + headers = { ['content-type'] = 'text/plain' }, + body = "Hi, there..." + } + } +} + +r, e = smtp.send{ + rcpt = {"", + "" }, + from = "", + source = ltn12.source.chain(source, filter), + --server = "mail.cs.princeton.edu" + server = "localhost", + port = 2525 +} + + +os.exit() + + + + + -- creates a source to send a message with two parts. The first part is -- plain text, the second part is a PNG image, encoded as base64. source = smtp.message{ @@ -48,10 +84,6 @@ source = smtp.message{ } } -function filter(s) - if s then io.write(s) end - return s -end r, e = smtp.send{ rcpt = {"", @@ -64,3 +96,5 @@ r, e = smtp.send{ } print(r, e) + +