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.
This commit is contained in:
Diego Nehab 2005-08-23 05:53:14 +00:00
parent 5e8ae76248
commit 773e35ced3
20 changed files with 454 additions and 364 deletions

9
TODO
View File

@ -1,11 +1,9 @@
new instalation scheme??? new instalation scheme???
test empty socket.select no windows. test empty socket.select no windows.
bug by mathew percival? bug by mathew percival?
arranjar um jeito de fazer multipart/alternative
what the hell does __unload do?
test it on Windows!!! test it on Windows!!!
leave code for losers that don't have nanosleep leave code for losers that don't have nanosleep
@ -45,3 +43,6 @@ testar os options!
* received connections * received connections
* - this function is invoked with the client socket * - this function is invoked with the client socket
* - it calls special send and receive functions that yield on timeout * - 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.

12
config
View File

@ -8,8 +8,8 @@
EXT=so EXT=so
SOCKET_V=2.0.0 SOCKET_V=2.0.0
MIME_V=1.0.0 MIME_V=1.0.0
SOCKET_SO=socket-core.$(EXT).$(SOCKET_V) SOCKET_SO=socket.$(EXT).$(SOCKET_V)
MIME_SO=mime-core.$(EXT).$(MIME_V) MIME_SO=mime.$(EXT).$(MIME_V)
UNIX_SO=unix.$(EXT) UNIX_SO=unix.$(EXT)
#------ #------
@ -25,13 +25,15 @@ COMPAT=compat-5.1r4
#------ #------
# Top of your Lua installation # 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_DATA=cp
INSTALL_EXEC=cp INSTALL_EXEC=cp
INSTALL_LINK=ln
#------ #------
# Compiler and linker settings # Compiler and linker settings

View File

@ -168,6 +168,8 @@ support.
<li> Improved: <tt>tcp:send(data, i, j)</tt> to return <tt>(i+sent-1)</tt>. This is great for non-blocking I/O, but might break some code; <li> Improved: <tt>tcp:send(data, i, j)</tt> to return <tt>(i+sent-1)</tt>. This is great for non-blocking I/O, but might break some code;
<li> Improved: HTTP, SMTP, and FTP functions to accept a new field <li> Improved: HTTP, SMTP, and FTP functions to accept a new field
<tt>create</tt> that overrides the function used to create socket objects; <tt>create</tt> that overrides the function used to create socket objects;
<li> Improved: <tt>smtp.message</tt> now supports multipart/alternative
(for the HTML messages we all love so much);
<li> Fixed: <tt>smtp.send</tt> was hanging on errors returned by LTN12 sources; <li> Fixed: <tt>smtp.send</tt> was hanging on errors returned by LTN12 sources;
<li> Fixed: <tt>url.absolute()</tt> to work when <tt>base_url</tt> is in <li> Fixed: <tt>url.absolute()</tt> to work when <tt>base_url</tt> is in
parsed form; parsed form;
@ -183,7 +185,7 @@ group;
<li> Improved: Socket and MIME binaries are called 'core' each inside their <li> Improved: Socket and MIME binaries are called 'core' each inside their
directory (ex. "socket/core.dll"). The 'l' prefix was just a bad idea; directory (ex. "socket/core.dll"). The 'l' prefix was just a bad idea;
<li> Improved: Using bundles in Mac OS X, instead of dylibs; <li> Improved: Using bundles in Mac OS X, instead of dylibs;
<li> Fixed: <tt>luasocket.h</tt> to export <tt>luaopen_socketcore</tt>; <li> Fixed: <tt>luasocket.h</tt> to export <tt>luaopen_socket_core</tt>;
<li> Fixed: <tt>udp:setpeername()</tt> so you can "disconnect" an <li> Fixed: <tt>udp:setpeername()</tt> so you can "disconnect" an
<tt>UDP</tt> socket; <tt>UDP</tt> socket;
<li> Fixed: A weird bug in HTTP support that caused some requests to <li> Fixed: A weird bug in HTTP support that caused some requests to

View File

@ -5,33 +5,26 @@
-- Author: Diego Nehab -- Author: Diego Nehab
-- RCS ID: $$ -- 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 {} arg = arg or {}
if table.getn(arg) < 1 then if table.getn(arg) < 1 then
print("Usage:\n luasocket check-links.lua [-n] {<url>}") print("Usage:\n luasocket check-links.lua [-n] {<url>}")
exit() exit()
end end
if arg[1] ~= "-n" then -- '-n' means we are running in non-blocking mode
-- if using blocking I/O, simulate dispatcher interface if arg[1] == "-n" then
url = require("socket.url") -- if non-blocking I/O was requested, use real dispatcher interface
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
table.remove(arg, 1) table.remove(arg, 1)
dispatch = require("dispatch") handler = dispatch.newhandler("coroutine")
dispatch.TIMEOUT = 10 else
url = require("socket.url") -- if using blocking I/O, use fake dispatcher interface
http = require("socket.http") handler = dispatch.newhandler("sequential")
handler = dispatch.newhandler()
end end
local nthreads = 0 local nthreads = 0

View File

@ -11,23 +11,33 @@ module("dispatch")
-- if too much time goes by without any activity in one of our sockets, we -- if too much time goes by without any activity in one of our sockets, we
-- just kill it -- 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 local handlert = {}
-- hack to emulate it in Lua itself
-- This is very inefficient, but is very good for debugging. -- default handler is coroutine
local running function newhandler(mode)
local resume = coroutine.resume mode = mode or "coroutine"
function coroutine.resume(co, ...) return handlert[mode]()
running = co
return resume(co, unpack(arg))
end end
function coroutine.running() local function seqstart(self, func)
return running 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 end
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
@ -36,15 +46,11 @@ end
-- we can't yield across calls to protect, so we rewrite it with coxpcall -- 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 -- make sure you don't require any module that uses socket.protect before
-- loading our hack -- loading our hack
function socket.protect(f)
return f
end
function socket.protect(f) function socket.protect(f)
return function(...) return function(...)
local co = coroutine.create(f) local co = coroutine.create(f)
while true do while true do
local results = {resume(co, unpack(arg))} local results = {coroutine.resume(co, unpack(arg))}
local status = table.remove(results, 1) local status = table.remove(results, 1)
if not status then if not status then
if type(results[1]) == 'table' then if type(results[1]) == 'table' then
@ -61,133 +67,7 @@ function socket.protect(f)
end end
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- socket.tcp() replacement for non-blocking I/O -- Simple set data structure. O(1) everything.
-----------------------------------------------------------------------------
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
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
local function newset() local function newset()
local reverse = {} local reverse = {}
@ -214,54 +94,208 @@ local function newset()
end end
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Our dispatcher API. -- socket.tcp() wrapper for the coroutine dispatcher
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
local metat = { __index = {} } local function cowrap(dispatcher, tcp, error)
if not tcp then return nil, error end
function metat.__index:start(func) -- put it in non-blocking mode right away
local co = coroutine.create(func) tcp:settimeout(0)
assert(coroutine.resume(co)) -- metatable for wrap produces new methods on demand for those that we
end -- don't override explicitly.
local metat = { __index = function(table, key)
function newhandler() table[key] = function(...)
local dispatcher = { arg[1] = tcp
context = {}, return tcp[key](unpack(arg))
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))
end 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 end
-- for all writable connections, do the same -- for all writable connections, do the same
for _, who in ipairs(writable) do for _, tcp in ipairs(writable) do
if self.context[who] then schedule(wakeup(self.sending, tcp))
self.sending:remove(who)
assert(coroutine.resume(self.context[who].thread))
end
end 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 -- return reporting a timeout
local now = socket.gettime() local now = socket.gettime()
for who, data in pairs(self.context) do for tcp, stamp in pairs(self.stamp) do
if data.last and now - data.last > TIMEOUT then if tcp.class == "tcp{client}" and now - stamp > TIMEOUT then
self.sending:remove(who) abort(self.sending, tcp)
self.receiving:remove(who) abort(self.receiving, tcp)
assert(coroutine.resume(self.context[who].thread, "timeout"))
end end
end 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

65
etc/forward.lua Normal file
View File

@ -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 <iport:ohost:oport> ...")
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

View File

@ -1,5 +1,5 @@
Microsoft Visual Studio Solution File, Format Version 8.00 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 ProjectSection(ProjectDependencies) = postProject
EndProjectSection EndProjectSection
EndProject EndProject

View File

@ -6,8 +6,10 @@ include config
#------ #------
# Hopefully no need to change anything below this line # Hopefully no need to change anything below this line
# #
INSTALL_SOCKET=$(INSTALL_TOP)/socket INSTALL_SOCKET_LUA=$(INSTALL_TOP_LUA)/socket
INSTALL_MIME=$(INSTALL_TOP)/mime INSTALL_SOCKET_LIB=$(INSTALL_TOP_LIB)/socket
INSTALL_MIME_LUA=$(INSTALL_TOP_LUA)/mime
INSTALL_MIME_LIB=$(INSTALL_TOP_LIB)/mime
all clean: all clean:
cd src; $(MAKE) $@ cd src; $(MAKE) $@
@ -15,7 +17,7 @@ all clean:
#------ #------
# Files to install # Files to install
# #
TO_SOCKET:= \ TO_SOCKET_LUA:= \
socket.lua \ socket.lua \
http.lua \ http.lua \
url.lua \ url.lua \
@ -23,29 +25,28 @@ TO_SOCKET:= \
ftp.lua \ ftp.lua \
smtp.lua smtp.lua
TO_TOP:= \ TO_TOP_LUA:= \
ltn12.lua ltn12.lua
TO_MIME:= \ TO_MIME_LUA:= \
$(MIME_SO) \
mime.lua mime.lua
#------ #------
# Install LuaSocket according to recommendation # Install LuaSocket according to recommendation
# #
install: all install: all
cd src; mkdir -p $(INSTALL_TOP) cd src; mkdir -p $(INSTALL_TOP_LUA)
cd src; $(INSTALL_DATA) $(COMPAT)/compat-5.1.lua $(INSTALL_TOP) cd src; mkdir -p $(INSTALL_TOP_LIB)
cd src; $(INSTALL_DATA) ltn12.lua $(INSTALL_TOP) cd src; $(INSTALL_DATA) $(COMPAT)/compat-5.1.lua $(INSTALL_TOP_LUA)
cd src; mkdir -p $(INSTALL_SOCKET) cd src; $(INSTALL_DATA) ltn12.lua $(INSTALL_TOP_LUA)
cd src; $(INSTALL_EXEC) $(SOCKET_SO) $(INSTALL_SOCKET) cd src; mkdir -p $(INSTALL_SOCKET_LUA)
cd src; $(INSTALL_DATA) $(TO_SOCKET) $(INSTALL_SOCKET) cd src; mkdir -p $(INSTALL_SOCKET_LIB)
cd src; cd $(INSTALL_SOCKET); $(INSTALL_LINK) -s $(SOCKET_SO) core.$(EXT) cd src; $(INSTALL_DATA) $(TO_SOCKET_LUA) $(INSTALL_SOCKET_LUA)
cd src; cd $(INSTALL_SOCKET); $(INSTALL_LINK) -s socket.lua init.lua cd src; $(INSTALL_EXEC) $(SOCKET_SO) $(INSTALL_SOCKET_LIB)/core.$(EXT)
cd src; mkdir -p $(INSTALL_MIME) cd src; mkdir -p $(INSTALL_MIME_LUA)
cd src; $(INSTALL_DATA) $(TO_MIME) $(INSTALL_MIME) cd src; mkdir -p $(INSTALL_MIME_LIB)
cd src; cd $(INSTALL_MIME); $(INSTALL_LINK) -s $(MIME_SO) core.$(EXT) cd src; $(INSTALL_DATA) $(TO_MIME_LUA) $(INSTALL_MIME_LUA)
cd src; cd $(INSTALL_MIME); $(INSTALL_LINK) -s mime.lua init.lua cd src; $(INSTALL_EXEC) $(MIME_SO) $(INSTALL_MIME_LIB)/core.$(EXT)
#------ #------
# End of makefile # End of makefile

View File

@ -2,9 +2,9 @@
# Distribution makefile # Distribution makefile
#-------------------------------------------------------------------------- #--------------------------------------------------------------------------
DIST = luasocket-2.0-beta3 DIST = luasocket-2.0
COMPAT = compat-5.1r2 COMPAT = compat-5.1r4
LUA = \ LUA = \
ftp.lua \ ftp.lua \
@ -37,6 +37,7 @@ EXAMPLES = \
ETC = \ ETC = \
check-links.lua \ check-links.lua \
check-links-nb.lua \
dict.lua \ dict.lua \
get.lua \ get.lua \
unix.c \ unix.c \
@ -76,8 +77,8 @@ CORE = \
wsocket.h wsocket.h
MAKE = \ MAKE = \
makefile.Darwin \ makefile \
makefile.Linux \ config \
luasocket.sln \ luasocket.sln \
luasocket.vcproj \ luasocket.vcproj \
mime.vcproj mime.vcproj

View File

@ -12,18 +12,18 @@
<Configurations> <Configurations>
<Configuration <Configuration
Name="Debug|Win32" Name="Debug|Win32"
OutputDirectory=".\" OutputDirectory="src"
IntermediateDirectory=".\" IntermediateDirectory="src"
ConfigurationType="2" ConfigurationType="2"
CharacterSet="2"> CharacterSet="2">
<Tool <Tool
Name="VCCLCompilerTool" Name="VCCLCompilerTool"
Optimization="0" Optimization="0"
AdditionalIncludeDirectories="..\..\include, compat-5.1r2" AdditionalIncludeDirectories="src\compat-5.1r4, ..\lua-5.0.2\include"
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;MIME_EXPORTS;MIME_API=__declspec(dllexport)" PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;MIME_EXPORTS;MIME_API=__declspec(dllexport)"
MinimalRebuild="TRUE" MinimalRebuild="TRUE"
BasicRuntimeChecks="3" BasicRuntimeChecks="3"
RuntimeLibrary="5" RuntimeLibrary="3"
UsePrecompiledHeader="0" UsePrecompiledHeader="0"
WarningLevel="3" WarningLevel="3"
Detect64BitPortabilityProblems="TRUE" Detect64BitPortabilityProblems="TRUE"
@ -32,7 +32,7 @@
Name="VCCustomBuildTool"/> Name="VCCustomBuildTool"/>
<Tool <Tool
Name="VCLinkerTool" Name="VCLinkerTool"
OutputFile="$(OutDir)/cmime.dll" OutputFile="$(OutDir)/mime.dll"
LinkIncremental="2" LinkIncremental="2"
GenerateDebugInformation="TRUE" GenerateDebugInformation="TRUE"
ProgramDatabaseFile="$(OutDir)/mime.pdb" ProgramDatabaseFile="$(OutDir)/mime.pdb"
@ -62,15 +62,15 @@
</Configuration> </Configuration>
<Configuration <Configuration
Name="Release|Win32" Name="Release|Win32"
OutputDirectory="." OutputDirectory="src"
IntermediateDirectory="." IntermediateDirectory="src"
ConfigurationType="2" ConfigurationType="2"
CharacterSet="2"> CharacterSet="2">
<Tool <Tool
Name="VCCLCompilerTool" Name="VCCLCompilerTool"
AdditionalIncludeDirectories="../../include, compat-5.1r2" AdditionalIncludeDirectories="src\compat-5.1r4, ..\lua-5.0.2\include"
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;MIME_EXPORTS; MIME_API=__declspec(dllexport)" PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;MIME_EXPORTS; MIME_API=__declspec(dllexport)"
RuntimeLibrary="4" RuntimeLibrary="2"
UsePrecompiledHeader="0" UsePrecompiledHeader="0"
WarningLevel="3" WarningLevel="3"
Detect64BitPortabilityProblems="TRUE" Detect64BitPortabilityProblems="TRUE"
@ -79,7 +79,7 @@
Name="VCCustomBuildTool"/> Name="VCCustomBuildTool"/>
<Tool <Tool
Name="VCLinkerTool" Name="VCLinkerTool"
OutputFile="$(OutDir)/cmime.dll" OutputFile="$(OutDir)/mime.dll"
LinkIncremental="1" LinkIncremental="1"
GenerateDebugInformation="TRUE" GenerateDebugInformation="TRUE"
SubSystem="2" SubSystem="2"
@ -117,19 +117,16 @@
Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx" Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"> UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
<File <File
RelativePath=".\compat-5.1r2\compat-5.1.c"> RelativePath="src\compat-5.1r4\compat-5.1.c">
</File> </File>
<File <File
RelativePath=".\mime.c"> RelativePath="src\mime.c">
</File> </File>
</Filter> </Filter>
<Filter <Filter
Name="Header Files" Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd" Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"> UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}">
<File
RelativePath=".\mime.h">
</File>
</Filter> </Filter>
<Filter <Filter
Name="Resource Files" Name="Resource Files"
@ -137,10 +134,7 @@
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"> UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}">
</Filter> </Filter>
<File <File
RelativePath="..\..\lib\liblua.lib"> RelativePath="..\lua-5.0.2\lib\lua50.lib">
</File>
<File
RelativePath="..\..\lib\liblualib.lib">
</File> </File>
</Files> </Files>
<Globals> <Globals>

View File

@ -2,7 +2,7 @@
<VisualStudioProject <VisualStudioProject
ProjectType="Visual C++" ProjectType="Visual C++"
Version="7.10" Version="7.10"
Name="luasocket" Name="socket"
ProjectGUID="{66E3CE14-884D-4AEA-9F20-15A0BEAF8C5A}" ProjectGUID="{66E3CE14-884D-4AEA-9F20-15A0BEAF8C5A}"
Keyword="Win32Proj"> Keyword="Win32Proj">
<Platforms> <Platforms>
@ -12,18 +12,18 @@
<Configurations> <Configurations>
<Configuration <Configuration
Name="Debug|Win32" Name="Debug|Win32"
OutputDirectory=".\" OutputDirectory="src"
IntermediateDirectory=".\" IntermediateDirectory="src"
ConfigurationType="2" ConfigurationType="2"
CharacterSet="2"> CharacterSet="2">
<Tool <Tool
Name="VCCLCompilerTool" Name="VCCLCompilerTool"
Optimization="0" Optimization="0"
AdditionalIncludeDirectories="..\..\include, compat-5.1r2" AdditionalIncludeDirectories="src\compat-5.1r4, ..\lua-5.0.2\include, src"
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;LUASOCKET_EXPORTS;LUASOCKET_API=__declspec(dllexport)" PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;LUASOCKET_EXPORTS;LUASOCKET_API=__declspec(dllexport)"
MinimalRebuild="TRUE" MinimalRebuild="TRUE"
BasicRuntimeChecks="3" BasicRuntimeChecks="3"
RuntimeLibrary="5" RuntimeLibrary="3"
UsePrecompiledHeader="0" UsePrecompiledHeader="0"
WarningLevel="3" WarningLevel="3"
Detect64BitPortabilityProblems="TRUE" Detect64BitPortabilityProblems="TRUE"
@ -33,12 +33,12 @@
<Tool <Tool
Name="VCLinkerTool" Name="VCLinkerTool"
AdditionalDependencies="ws2_32.lib" AdditionalDependencies="ws2_32.lib"
OutputFile="$(OutDir)/csocket.dll" OutputFile="$(OutDir)/socket.dll"
LinkIncremental="2" LinkIncremental="2"
GenerateDebugInformation="TRUE" GenerateDebugInformation="TRUE"
ProgramDatabaseFile="$(OutDir)/luasocket.pdb" ProgramDatabaseFile="$(OutDir)/socket.pdb"
SubSystem="2" SubSystem="2"
ImportLibrary="$(OutDir)/luasocket.lib" ImportLibrary="$(OutDir)/socket.lib"
TargetMachine="1"/> TargetMachine="1"/>
<Tool <Tool
Name="VCMIDLTool"/> Name="VCMIDLTool"/>
@ -63,15 +63,15 @@
</Configuration> </Configuration>
<Configuration <Configuration
Name="Release|Win32" Name="Release|Win32"
OutputDirectory="." OutputDirectory="./src"
IntermediateDirectory="." IntermediateDirectory="./src"
ConfigurationType="2" ConfigurationType="2"
CharacterSet="2"> CharacterSet="2">
<Tool <Tool
Name="VCCLCompilerTool" Name="VCCLCompilerTool"
AdditionalIncludeDirectories="../../include, compat-5.1r2" AdditionalIncludeDirectories="src\compat-5.1r4, ..\lua-5.0.2\include"
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;LUASOCKET_EXPORTS;LUASOCKET_API=__declspec(dllexport); LUASOCKET_DEBUG" PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;LUASOCKET_EXPORTS;LUASOCKET_API=__declspec(dllexport); LUASOCKET_DEBUG"
RuntimeLibrary="4" RuntimeLibrary="2"
UsePrecompiledHeader="0" UsePrecompiledHeader="0"
WarningLevel="3" WarningLevel="3"
Detect64BitPortabilityProblems="TRUE" Detect64BitPortabilityProblems="TRUE"
@ -81,13 +81,13 @@
<Tool <Tool
Name="VCLinkerTool" Name="VCLinkerTool"
AdditionalDependencies="ws2_32.lib" AdditionalDependencies="ws2_32.lib"
OutputFile="$(OutDir)/csocket.dll" OutputFile="$(OutDir)/socket.dll"
LinkIncremental="1" LinkIncremental="1"
GenerateDebugInformation="TRUE" GenerateDebugInformation="TRUE"
SubSystem="2" SubSystem="2"
OptimizeReferences="2" OptimizeReferences="2"
EnableCOMDATFolding="2" EnableCOMDATFolding="2"
ImportLibrary="$(OutDir)/luasocket.lib" ImportLibrary="$(OutDir)/socket.lib"
TargetMachine="1"/> TargetMachine="1"/>
<Tool <Tool
Name="VCMIDLTool"/> Name="VCMIDLTool"/>
@ -119,91 +119,49 @@
Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx" Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"> UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
<File <File
RelativePath=".\auxiliar.c"> RelativePath="src\auxiliar.c">
</File> </File>
<File <File
RelativePath=".\buffer.c"> RelativePath="src\buffer.c">
</File> </File>
<File <File
RelativePath=".\compat-5.1r2\compat-5.1.c"> RelativePath="src\compat-5.1r4\compat-5.1.c">
</File> </File>
<File <File
RelativePath=".\except.c"> RelativePath="src\except.c">
</File> </File>
<File <File
RelativePath=".\inet.c"> RelativePath="src\inet.c">
</File> </File>
<File <File
RelativePath=".\io.c"> RelativePath="src\io.c">
</File> </File>
<File <File
RelativePath=".\luasocket.c"> RelativePath="src\luasocket.c">
</File> </File>
<File <File
RelativePath=".\options.c"> RelativePath="src\options.c">
</File> </File>
<File <File
RelativePath=".\select.c"> RelativePath="src\select.c">
</File> </File>
<File <File
RelativePath=".\tcp.c"> RelativePath="src\tcp.c">
</File> </File>
<File <File
RelativePath=".\timeout.c"> RelativePath="src\timeout.c">
</File> </File>
<File <File
RelativePath=".\udp.c"> RelativePath="src\udp.c">
</File> </File>
<File <File
RelativePath=".\wsocket.c"> RelativePath="src\wsocket.c">
</File> </File>
</Filter> </Filter>
<Filter <Filter
Name="Header Files" Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd" Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"> UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}">
<File
RelativePath=".\auxiliar.h">
</File>
<File
RelativePath=".\base.h">
</File>
<File
RelativePath=".\buffer.h">
</File>
<File
RelativePath=".\inet.h">
</File>
<File
RelativePath=".\io.h">
</File>
<File
RelativePath=".\luasocket.h">
</File>
<File
RelativePath=".\options.h">
</File>
<File
RelativePath=".\select.h">
</File>
<File
RelativePath=".\smtp.h">
</File>
<File
RelativePath=".\socket.h">
</File>
<File
RelativePath=".\tcp.h">
</File>
<File
RelativePath=".\timeout.h">
</File>
<File
RelativePath=".\udp.h">
</File>
<File
RelativePath=".\wsocket.h">
</File>
</Filter> </Filter>
<Filter <Filter
Name="Resource Files" Name="Resource Files"
@ -211,10 +169,7 @@
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"> UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}">
</Filter> </Filter>
<File <File
RelativePath="..\..\lib\liblua.lib"> RelativePath="..\lua-5.0.2\lib\lua50.lib">
</File>
<File
RelativePath="..\..\lib\liblualib.lib">
</File> </File>
</Files> </Files>
<Globals> <Globals>

View File

@ -127,6 +127,9 @@ void aux_setclass(lua_State *L, const char *classname, int objidx) {
* otherwise * otherwise
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
void *aux_getgroupudata(lua_State *L, const char *groupname, int objidx) { void *aux_getgroupudata(lua_State *L, const char *groupname, int objidx) {
#if 0
return lua_touserdata(L, objidx);
#else
if (!lua_getmetatable(L, objidx)) if (!lua_getmetatable(L, objidx))
return NULL; return NULL;
lua_pushstring(L, groupname); lua_pushstring(L, groupname);
@ -138,6 +141,7 @@ void *aux_getgroupudata(lua_State *L, const char *groupname, int objidx) {
lua_pop(L, 2); lua_pop(L, 2);
return lua_touserdata(L, objidx); return lua_touserdata(L, objidx);
} }
#endif
} }
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
@ -145,5 +149,9 @@ void *aux_getgroupudata(lua_State *L, const char *groupname, int objidx) {
* otherwise * otherwise
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
void *aux_getclassudata(lua_State *L, const char *classname, int objidx) { 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); return luaL_checkudata(L, objidx, classname);
#endif
} }

View File

@ -325,4 +325,3 @@ request = socket.protect(function(reqt, body)
if base.type(reqt) == "string" then return srequest(reqt, body) if base.type(reqt) == "string" then return srequest(reqt, body)
else return trequest(reqt) end else return trequest(reqt) end
end) end)

View File

@ -27,6 +27,6 @@
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Initializes the library. * Initializes the library.
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
LUASOCKET_API int luaopen_socketcore(lua_State *L); LUASOCKET_API int luaopen_socket_core(lua_State *L);
#endif /* LUASOCKET_H */ #endif /* LUASOCKET_H */

View File

@ -78,7 +78,7 @@ static UC b64unbase[256];
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Initializes module * 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); luaL_openlib(L, "mime", func, 0);
/* initialize lookup tables */ /* initialize lookup tables */

View File

@ -19,6 +19,6 @@
#define MIME_API extern #define MIME_API extern
#endif #endif
MIME_API int luaopen_mimecore(lua_State *L); MIME_API int luaopen_mime_core(lua_State *L);
#endif /* MIME_H */ #endif /* MIME_H */

View File

@ -137,12 +137,24 @@ end
-- send_message forward declaration -- send_message forward declaration
local send_message 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 -- yield multipart message body from a multipart message table
local function send_multipart(mesgt) local function send_multipart(mesgt)
-- make sure we have our boundary and send headers
local bd = newboundary() local bd = newboundary()
-- define boundary and finish headers local headers = mesgt.headers or {}
coroutine.yield('content-type: multipart/mixed; boundary="' .. headers['content-type'] = headers['content-type'] or 'multipart/mixed'
bd .. '"\r\n\r\n') headers['content-type'] = headers['content-type'] ..
'; boundary="' .. bd .. '"'
send_headers(headers)
-- send preamble -- send preamble
if mesgt.body.preamble then if mesgt.body.preamble then
coroutine.yield(mesgt.body.preamble) coroutine.yield(mesgt.body.preamble)
@ -164,11 +176,11 @@ end
-- yield message body from a source -- yield message body from a source
local function send_source(mesgt) local function send_source(mesgt)
-- set content-type if user didn't override -- make sure we have a content-type
if not mesgt.headers or not mesgt.headers["content-type"] then local headers = mesgt.headers or {}
coroutine.yield('content-type: text/plain; charset="iso-8859-1"\r\n\r\n') headers['content-type'] = headers['content-type'] or
else coroutine.yield("\r\n") end 'text/plain; charset="iso-8859-1"'
-- finish headers send_headers(headers)
-- send body from source -- send body from source
while true do while true do
local chunk, err = mesgt.body() local chunk, err = mesgt.body()
@ -180,28 +192,17 @@ end
-- yield message body from a string -- yield message body from a string
local function send_string(mesgt) local function send_string(mesgt)
-- set content-type if user didn't override -- make sure we have a content-type
if not mesgt.headers or not mesgt.headers["content-type"] then local headers = mesgt.headers or {}
coroutine.yield('content-type: text/plain; charset="iso-8859-1"\r\n\r\n') headers['content-type'] = headers['content-type'] or
else coroutine.yield("\r\n") end 'text/plain; charset="iso-8859-1"'
send_headers(headers)
-- send body from string -- send body from string
coroutine.yield(mesgt.body) coroutine.yield(mesgt.body)
end 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 -- message source
function send_message(mesgt) function send_message(mesgt)
send_headers(mesgt)
if base.type(mesgt.body) == "table" then send_multipart(mesgt) if base.type(mesgt.body) == "table" then send_multipart(mesgt)
elseif base.type(mesgt.body) == "function" then send_source(mesgt) elseif base.type(mesgt.body) == "function" then send_source(mesgt)
else send_string(mesgt) end else send_string(mesgt) end

View File

@ -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_sec = (int) t;
tv.tv_usec = (int) ((t - tv.tv_sec) * 1.0e6); tv.tv_usec = (int) ((t - tv.tv_sec) * 1.0e6);
if (n <= 0) { if (n <= 0) {
Sleep(1000*t); Sleep((DWORD) (1000*t));
return 0; return 0;
} else return select(0, rfds, wfds, efds, t >= 0.0? &tv: NULL); } else return select(0, rfds, wfds, efds, t >= 0.0? &tv: NULL);
} }

View File

@ -12,8 +12,8 @@ local host, port, index_file, index, back, err, ret
local t = socket.gettime() local t = socket.gettime()
host = host or "diego.student.princeton.edu" host = host or "localhost"
index_file = "test/index.html" index_file = "index.html"
-- a function that returns a directory listing -- a function that returns a directory listing

View File

@ -3,6 +3,42 @@ local smtp = require("socket.smtp")
local mime = require("mime") local mime = require("mime")
local ltn12 = require("ltn12") 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 = "<html> <body> Hi, <b>there</b>...</body> </html>"
},
[2] = {
headers = { ['content-type'] = 'text/plain' },
body = "Hi, there..."
}
}
}
r, e = smtp.send{
rcpt = {"<diego@tecgraf.puc-rio.br>",
"<diego@princeton.edu>" },
from = "<diego@princeton.edu>",
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 -- 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. -- plain text, the second part is a PNG image, encoded as base64.
source = smtp.message{ 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{ r, e = smtp.send{
rcpt = {"<diego@tecgraf.puc-rio.br>", rcpt = {"<diego@tecgraf.puc-rio.br>",
@ -64,3 +96,5 @@ r, e = smtp.send{
} }
print(r, e) print(r, e)