Mercurial > luasocket
changeset 0:4b915342e2a8
LuaSocket 2.0.2 + CMake build description.
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CMakeLists.txt Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,106 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +PROJECT(LuaSocket) + +IF(USING_LUA_MULTI_SET) + IF(NOT LUA_LIBRARIES) + IF(lua_library_dynamic) + SET(LUA_LIBRARIES lua_library_dynamic) + ELSE(lua_library_dynamic) + SET(LUA_LIBRARIES lua_library_static) + ENDIF(lua_library_dynamic) + ENDIF(NOT LUA_LIBRARIES) + IF(NOT LUA_INCLUDE_DIR) + SET(LUA_INCLUDE_DIR ${Lua_SOURCE_DIR}/src) + ENDIF(NOT LUA_INCLUDE_DIR) +ELSE(USING_LUA_MULTI_SET) + FIND_PACKAGE(Lua51 REQUIRED) +ENDIF(USING_LUA_MULTI_SET) + +OPTION(WANTS_BUILD_SHARED_LIBRARY "Set to ON to build dynamic library." ON) +#OPTION(WANTS_BUILD_STATIC_LIBRARY "Set to ON to build static library." ON) +OPTION(WANTS_BUILD_FRAMEWORK "Set to ON to build framework instead of dylib. Only valid if BUILD_SHARED_LIBRARY is ON an is OS X." ON) + +SET(LUAPACKAGE_CDIR "lib/lua/5.1" CACHE STRING "Path for Lua packaged platform specific things.") +SET(LUAPACKAGE_LDIR "share/lua/5.1" CACHE STRING "Path for Lua packages platform independent things.") + +IF(APPLE) + SET(LUASOCKET_LINK_FLAGS "-undefined dynamic_lookup") + SET(LUASOCKET_C_FLAGS "-fno-common -pedantic") + ADD_DEFINITIONS(-DLUASOCKET_DEBUG -DUNIX_HAS_SUN_LEN) +ENDIF(APPLE) + + +INCLUDE_DIRECTORIES( + ${LUA_INCLUDE_DIR} +) + + + +#IF(WANTS_BUILD_SHARED_LIBRARY AND WANTS_BUILD_FRAMEWORK) +# SET_TARGET_PROPERTIES(luasocket_library_dynamic PROPERTIES +# FRAMEWORK TRUE +# # FRAMEWORK_VERSION 5.1 +# # PRIVATE_HEADER "fooPrivate.h;fooBoth.h" +# PUBLIC_HEADER "${luasocket_PUBLIC_HEADERS}" + # RESOURCE "${RESOURCE_FILES}" +# INSTALL_NAME_DIR "@executable_path/../Frameworks" +# BUILD_WITH_INSTALL_RPATH 1 # FIXME: User option or Xcode=1 +# ) +#ENDIF(WANTS_BUILD_SHARED_LIBRARY AND WANTS_BUILD_FRAMEWORK) + +#SET_TARGET_PROPERTIES(luasocket_library_dynamic PROPERTIES +# PUBLIC_HEADER "${luasocket_PUBLIC_HEADERS}" +# RESOURCE "${luasocket_RESOURCE_FILES}" +#) + + +IF(NOT USING_LUA_MULTI_SET) +# For uninstall (needs cmake_uninstall.cmake.in in the top-level directory) +CONFIGURE_FILE( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" + IMMEDIATE @ONLY) +ADD_CUSTOM_TARGET(uninstall + "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") +ENDIF(NOT USING_LUA_MULTI_SET) + + + +# Set defaults for Universal Binaries. We want 32-bit Intel/PPC on 10.4 +# and 32/64-bit Intel/PPC on >= 10.5. Anything <= 10.3 doesn't support. +IF(APPLE) + # These are just defaults/recommendations, but how we want to build + # out of the box. But the user needs to be able to change these options. + # So we must only set the values the first time CMake is run, or we + # will overwrite any changes the user sets. + # FORCE is used because the options are not reflected in the UI otherwise. + # Seems like a good place to add version specific compiler flags too. + IF(NOT LUA_CONFIG_HAS_BEEN_RUN_BEFORE) + # This is really fragile, but CMake doesn't provide the OS system + # version information we need. (Darwin versions can be changed + # independently of OS X versions.) + # It does look like CMake handles the CMAKE_OSX_SYSROOT automatically. + IF(EXISTS /Developer/SDKs/MacOSX10.5.sdk) + SET(CMAKE_OSX_ARCHITECTURES "ppc;i386;ppc64;x86_64" CACHE STRING "Build architectures for OSX" FORCE) + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=10.5" CACHE STRING "Flags used by the compiler during all build types." FORCE) + ELSE(EXISTS /Developer/SDKs/MacOSX10.5.sdk) + IF(EXISTS /Developer/SDKs/MacOSX10.4u.sdk) + SET(CMAKE_OSX_ARCHITECTURES "ppc;i386" CACHE STRING "Build architectures for OSX" FORCE) + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=10.4" CACHE STRING "Flags used by the compiler during all build types." FORCE) + ELSE(EXISTS /Developer/SDKs/MacOSX10.4u.sdk) + # No Universal Binary support + # Should break down further to set the -mmacosx-version-min, + # but the SDK detection is too unreliable here. + ENDIF(EXISTS /Developer/SDKs/MacOSX10.4u.sdk) + ENDIF(EXISTS /Developer/SDKs/MacOSX10.5.sdk) + ENDIF(NOT LUA_CONFIG_HAS_BEEN_RUN_BEFORE) +ENDIF(APPLE) + +# This needs to be run very last so other parts of the scripts can take +# advantage of this. +IF(NOT LUA_CONFIG_HAS_BEEN_RUN_BEFORE) + SET(LUA_CONFIG_HAS_BEEN_RUN_BEFORE 1 CACHE INTERNAL "Flag to track whether this is the first time running CMake or if CMake has been configured before") +ENDIF(NOT LUA_CONFIG_HAS_BEEN_RUN_BEFORE) + + +ADD_SUBDIRECTORY(src)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LICENSE Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,20 @@ +LuaSocket 2.0.2 license +Copyright © 2004-2007 Diego Nehab + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NEW Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,14 @@ +What's New + +This is just a bug-fix/update release. + + * Improved: http.request() now supports deprecated HTTP/0.9 + servers (Florian Berger) + * Fixed: could return "timedout" instead of "timeout" (Leo Leo) + * Fixed: crash when reading '*a' on closed socket (Paul Ducklin); + * Fixed: return values are consistent when reading from closed sockets; + * Fixed: case sensitivity in headers of multipart messages in + smtp.message() (Graham Henstridge); + * Fixed a couple instances of error() being called instead of base.error(). These would cause an error when an error was reported. :) (Ketmar Dark); + * Fixed: test script now uses pairs() iterator instead of the old + Lua syntax (Robert Dodier).
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,6 @@ +This is the LuaSocket 2.0.1. It has been tested on WinXP, Mac OS X, +and Linux. Please use the Lua mailing list to report any bugs +(or "features") you encounter. + +Have fun, +Diego Nehab.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cmake_uninstall.cmake.in Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,22 @@ +IF(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt") + MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_BINARY_DIR@/install_manifest.txt\"") +ENDIF(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt") + +FILE(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files) +STRING(REGEX REPLACE "\n" ";" files "${files}") +FOREACH(file ${files}) + MESSAGE(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") + IF(EXISTS "$ENV{DESTDIR}${file}") + EXEC_PROGRAM( + "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" + OUTPUT_VARIABLE rm_out + RETURN_VALUE rm_retval + ) + IF(NOT "${rm_retval}" STREQUAL 0) + MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") + ENDIF(NOT "${rm_retval}" STREQUAL 0) + ELSE(EXISTS "$ENV{DESTDIR}${file}") + MESSAGE(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") + ENDIF(EXISTS "$ENV{DESTDIR}${file}") +ENDFOREACH(file) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/config Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,60 @@ +#------ +# LuaSocket makefile configuration +# + +#------ +# Output file names +# +EXT=so +SOCKET_V=2.0.2 +MIME_V=1.0.2 +SOCKET_SO=socket.$(EXT).$(SOCKET_V) +MIME_SO=mime.$(EXT).$(MIME_V) +UNIX_SO=unix.$(EXT) + +#------ +# Lua includes and libraries +# +#LUAINC=-I/usr/local/include/lua50 +#LUAINC=-I/usr/local/include/lua5.1 +#LUAINC=-Ilua-5.1.1/src + +#------ +# Compat-5.1 directory +# +#COMPAT=compat-5.1r5 + +#------ +# Top of your Lua installation +# Relative paths will be inside the src tree +# +#INSTALL_TOP_SHARE=/usr/local/share/lua/5.0 +#INSTALL_TOP_LIB=/usr/local/lib/lua/5.0 +INSTALL_TOP_SHARE=/usr/local/share/lua/5.1 +INSTALL_TOP_LIB=/usr/local/lib/lua/5.1 + +INSTALL_DATA=cp +INSTALL_EXEC=cp + +#------ +# Compiler and linker settings +# for Mac OS X +# +#CC=gcc +#DEF= -DLUASOCKET_DEBUG -DUNIX_HAS_SUN_LEN +#CFLAGS= $(LUAINC) -I$(COMPAT) $(DEF) -pedantic -Wall -O2 -fno-common +#LDFLAGS=-bundle -undefined dynamic_lookup +#LD=export MACOSX_DEPLOYMENT_TARGET="10.3"; gcc + +#------ +# Compiler and linker settings +# for Linux +CC=gcc +DEF=-DLUASOCKET_DEBUG +CFLAGS= $(LUAINC) $(DEF) -pedantic -Wall -O2 -fpic +LDFLAGS=-O -shared -fpic +LD=gcc + +#------ +# End of makefile configuration +#
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/dns.html Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,132 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> +<html> + +<head> +<meta name="description" content="LuaSocket: DNS support"> +<meta name="keywords" content="Lua, LuaSocket, DNS, Network, Library, Support"> +<title>LuaSocket: DNS support</title> +<link rel="stylesheet" href="reference.css" type="text/css"> +</head> + +<body> + +<!-- header +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<div class=header> +<hr> +<center> +<table summary="LuaSocket logo"> +<tr><td align=center><a href="http://www.lua.org"> +<img width=128 height=128 border=0 alt="LuaSocket" src="luasocket.png"> +</a></td></tr> +<tr><td align=center valign=top>Network support for the Lua language +</td></tr> +</table> +<p class=bar> +<a href="home.html">home</a> · +<a href="home.html#download">download</a> · +<a href="installation.html">installation</a> · +<a href="introduction.html">introduction</a> · +<a href="reference.html">reference</a> +</p> +</center> +<hr> +</div> + +<!-- dns ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<h2 id=dns>DNS</h2> + +<p> +Name resolution functions return <em>all</em> information obtained from the +resolver in a table of the form: +</p> + +<blockquote><tt> +resolved = {<br> + name = <i>canonic-name</i>,<br> + alias = <i>alias-list</i>,<br> + ip = <i>ip-address-list</i><br> +} +</tt> </blockquote> + +<p> +Note that the <tt>alias</tt> list can be empty. +</p> + +<!-- gethostname ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=gethostname> +socket.dns.<b>gethostname()</b> +</p> + +<p class=description> +Returns the standard host name for the machine as a string. +</p> + +<!-- tohostname +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=tohostname> +socket.dns.<b>tohostname(</b>address<b>)</b> +</p> + +<p class=description> +Converts from IP address to host name. +</p> + +<p class=parameters> +<tt>Address</tt> can be an IP address or host name. +</p> + +<p class=return> +The function returns a string with the canonic host name of the given +<tt>address</tt>, followed by a table with all information returned by +the resolver. In case of error, the function returns <b><tt>nil</tt></b> +followed by an error message. +</p> + +<!-- toip +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=toip> +socket.dns.<b>toip(</b>address<b>)</b> +</p> + +<p class=description> +Converts from host name to IP address. +</p> + +<p class=parameters> +<tt>Address</tt> can be an IP address or host name. +</p> + +<p class=return> +Returns a string with the first IP address found for <tt>address</tt>, +followed by a table with all information returned by the resolver. +In case of error, the function returns <b><tt>nil</tt></b> followed by an error +message. +</p> + +<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<div class=footer> +<hr> +<center> +<p class=bar> +<a href="home.html">home</a> · +<a href="home.html#down">download</a> · +<a href="installation.html">installation</a> · +<a href="introduction.html">introduction</a> · +<a href="reference.html">reference</a> +</p> +<p> +<small> +Last modified by Diego Nehab on <br> +Thu Apr 20 00:25:07 EDT 2006 +</small> +</p> +</center> +</div> + +</body> +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/ftp.html Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,289 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> +<html> + +<head> +<meta name="description" content="LuaSocket: FTP support"> +<meta name="keywords" content="Lua, LuaSocket, FTP, Network, Library, Support"> +<title>LuaSocket: FTP support</title> +<link rel="stylesheet" href="reference.css" type="text/css"> +</head> + +<body> + +<!-- header ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<div class=header> +<hr> +<center> +<table summary="LuaSocket logo"> +<tr><td align=center><a href="http://www.lua.org"> +<img width=128 height=128 border=0 alt="LuaSocket" src="luasocket.png"> +</a></td></tr> +<tr><td align=center valign=top>Network support for the Lua language +</td></tr> +</table> +<p class=bar> +<a href="home.html">home</a> · +<a href="home.html#download">download</a> · +<a href="installation.html">installation</a> · +<a href="introduction.html">introduction</a> · +<a href="reference.html">reference</a> +</p> +</center> +<hr> +</div> + +<!-- ftp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<h2 id=ftp>FTP</h2> + +<p> +FTP (File Transfer Protocol) is a protocol used to transfer files +between hosts. The <tt>ftp</tt> namespace offers thorough support +to FTP, under a simple interface. The implementation conforms to +<a href="http://www.cs.princeton.edu/~diego/rfc/rfc0959.txt">RFC 959</a>. +</p> + +<p> +High level functions are provided supporting the most common operations. +These high level functions are implemented on top of a lower level +interface. Using the low-level interface, users can easily create their +own functions to access <em>any</em> operation supported by the FTP +protocol. For that, check the implementation. +</p> + +<p> +To really benefit from this module, a good understanding of +<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks"> +LTN012, Filters sources and sinks</a> is necessary. +</p> + +<p> +To obtain the <tt>ftp</tt> namespace, run: +</p> + +<pre class=example> +-- loads the FTP module and any libraries it requires +local ftp = require("socket.ftp") +</pre> + +<p> +URLs MUST conform to +<a href="http://www.cs.princeton.edu/~diego/rfc/rfc1738.txt">RFC +1738</a>, that is, an URL is a string in the form: +</p> + +<blockquote> +<tt> +[ftp://][<user>[:<password>]@]<host>[:<port>][/<path>][<i>type</i>=a|i]</tt> +</blockquote> + +<p> +The following constants in the namespace can be set to control the default behavior of +the FTP module: +</p> + +<ul> +<li> <tt>PASSWORD</tt>: default anonymous password. +<li> <tt>PORT</tt>: default port used for the control connection; +<li> <tt>TIMEOUT</tt>: sets the timeout for all I/O operations; +<li> <tt>USER</tt>: default anonymous user; +</ul> + + +<!-- ftp.get ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=get> +ftp.<b>get(</b>url<b>)</b><br> +ftp.<b>get{</b><br> + host = <i>string</i>,<br> + sink = <i>LTN12 sink</i>,<br> + argument <i>or</i> path = <i>string</i>,<br> + [user = <i>string</i>,]<br> + [password = <i>string</i>]<br> + [command = <i>string</i>,]<br> + [port = <i>number</i>,]<br> + [type = <i>string</i>,]<br> + [step = <i>LTN12 pump step</i>,]<br> + [create = <i>function</i>]<br> +<b>}</b> +</p> + +<p class=description> +The <tt>get</tt> function has two forms. The simple form has fixed +functionality: it downloads the contents of a URL and returns it as a +string. The generic form allows a <em>lot</em> more control, as explained +below. +</p> + +<p class=parameters> +If the argument of the <tt>get</tt> function is a table, the function +expects at least the fields <tt>host</tt>, <tt>sink</tt>, and one of +<tt>argument</tt> or <tt>path</tt> (<tt>argument</tt> takes +precedence). <tt>Host</tt> is the server to connect to. <tt>Sink</tt> is +the <em>simple</em> +<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a> +sink that will receive the downloaded data. <tt>Argument</tt> or +<tt>path</tt> give the target path to the resource in the server. The +optional arguments are the following: +</p> +<ul> +<li><tt>user</tt>, <tt>password</tt>: User name and password used for +authentication. Defaults to "<tt>ftp:anonymous@anonymous.org</tt>"; +<li><tt>command</tt>: The FTP command used to obtain data. Defaults to +"<tt>retr</tt>", but see example below; +<li><tt>port</tt>: The port to used for the control connection. Defaults to 21; +<li><tt>type</tt>: The transfer mode. Can take values "<tt>i</tt>" or +"<tt>a</tt>". Defaults to whatever is the server default; +<li><tt>step</tt>: +<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a> +pump step function used to pass data from the +server to the sink. Defaults to the LTN12 <tt>pump.step</tt> function; +<li><tt>create</tt>: An optional function to be used instead of +<a href=tcp.html#socket.tcp><tt>socket.tcp</tt></a> when the communications socket is created. +</ul> + +<p class=return> +If successful, the simple version returns the URL contents as a +string, and the generic function returns 1. In case of error, both +functions return <b><tt>nil</tt></b> and an error message describing the +error. +</p> + +<pre class=example> +-- load the ftp support +local ftp = require("socket.ftp") + +-- Log as user "anonymous" on server "ftp.tecgraf.puc-rio.br", +-- and get file "lua.tar.gz" from directory "pub/lua" as binary. +f, e = ftp.get("ftp://ftp.tecgraf.puc-rio.br/pub/lua/lua.tar.gz;type=i") +</pre> + +<pre class=example> +-- load needed modules +local ftp = require("socket.ftp") +local ltn12 = require("ltn12") +local url = require("socket.url") + +-- a function that returns a directory listing +function nlst(u) + local t = {} + local p = url.parse(u) + p.command = "nlst" + p.sink = ltn12.sink.table(t) + local r, e = ftp.get(p) + return r and table.concat(t), e +end +</pre> + +<!-- put ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=put> +ftp.<b>put(</b>url, content<b>)</b><br> +ftp.<b>put{</b><br> + host = <i>string</i>,<br> + source = <i>LTN12 sink</i>,<br> + argument <i>or</i> path = <i>string</i>,<br> + [user = <i>string</i>,]<br> + [password = <i>string</i>]<br> + [command = <i>string</i>,]<br> + [port = <i>number</i>,]<br> + [type = <i>string</i>,]<br> + [step = <i>LTN12 pump step</i>,]<br> + [create = <i>function</i>]<br> +<b>}</b> +</p> + +<p class=description> +The <tt>put</tt> function has two forms. The simple form has fixed +functionality: it uploads a string of content into a URL. The generic form +allows a <em>lot</em> more control, as explained below. +</p> + +<p class=parameters> +If the argument of the <tt>put</tt> function is a table, the function +expects at least the fields <tt>host</tt>, <tt>source</tt>, and one of +<tt>argument</tt> or <tt>path</tt> (<tt>argument</tt> takes +precedence). <tt>Host</tt> is the server to connect to. <tt>Source</tt> is +the <em>simple</em> +<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a> +source that will provide the contents to be uploaded. +<tt>Argument</tt> or +<tt>path</tt> give the target path to the resource in the server. The +optional arguments are the following: +</p> +<ul> +<li><tt>user</tt>, <tt>password</tt>: User name and password used for +authentication. Defaults to "<tt>ftp:anonymous@anonymous.org</tt>"; +<li><tt>command</tt>: The FTP command used to send data. Defaults to +"<tt>stor</tt>", but see example below; +<li><tt>port</tt>: The port to used for the control connection. Defaults to 21; +<li><tt>type</tt>: The transfer mode. Can take values "<tt>i</tt>" or +"<tt>a</tt>". Defaults to whatever is the server default; +<li><tt>step</tt>: +<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a> +pump step function used to pass data from the +server to the sink. Defaults to the LTN12 <tt>pump.step</tt> function; +<li><tt>create</tt>: An optional function to be used instead of +<a href=tcp.html#socket.tcp><tt>socket.tcp</tt></a> when the communications socket is created. +</ul> + +<p class=return> +Both functions return 1 if successful, or <b><tt>nil</tt></b> and an error +message describing the reason for failure. +</p> + +<pre class=example> +-- load the ftp support +local ftp = require("socket.ftp") + +-- Log as user "fulano" on server "ftp.example.com", +-- using password "silva", and store a file "README" with contents +-- "wrong password, of course" +f, e = ftp.put("ftp://fulano:silva@ftp.example.com/README", + "wrong password, of course") +</pre> + +<pre class=example> +-- load the ftp support +local ftp = require("socket.ftp") +local ltn12 = require("ltn12") + +-- Log as user "fulano" on server "ftp.example.com", +-- using password "silva", and append to the remote file "LOG", sending the +-- contents of the local file "LOCAL-LOG" +f, e = ftp.put{ + host = "ftp.example.com", + user = "fulano", + password = "silva", + command = "appe", + argument = "LOG", + source = ltn12.source.file(io.open("LOCAL-LOG", "r")) +} +</pre> + + +<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<div class=footer> +<hr> +<center> +<p class=bar> +<a href="home.html">home</a> · +<a href="home.html#download">download</a> · +<a href="installation.html">installation</a> · +<a href="introduction.html">introduction</a> · +<a href="reference.html">reference</a> +</p> +<p> +<small> +Last modified by Diego Nehab on <br> +Thu Apr 20 00:25:18 EDT 2006 +</small> +</p> +</center> +</div> + +</body> +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/http.html Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,333 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> +<html> + +<head> +<meta name="description" content="LuaSocket: HTTP support"> +<meta name="keywords" content="Lua, HTTP, Library, WWW, Browser, Network, Support"> +<title>LuaSocket: HTTP support</title> +<link rel="stylesheet" href="reference.css" type="text/css"> +</head> + +<body> + +<!-- header ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<div class=header> +<hr> +<center> +<table summary="LuaSocket logo"> +<tr><td align=center><a href="http://www.lua.org"> +<img width=128 height=128 border=0 alt="LuaSocket" src="luasocket.png"> +</a></td></tr> +<tr><td align=center valign=top>Network support for the Lua language +</td></tr> +</table> +<p class=bar> +<a href="home.html">home</a> · +<a href="home.html#download">download</a> · +<a href="introduction.html">introduction</a> · +<a href="introduction.html">introduction</a> · +<a href="reference.html">reference</a> +</p> +</center> +<hr> +</div> + +<!-- http +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<h2 id=http>HTTP</h2> + +<p> +HTTP (Hyper Text Transfer Protocol) is the protocol used to exchange +information between web-browsers and servers. The <tt>http</tt> +namespace offers full support for the client side of the HTTP +protocol (i.e., +the facilities that would be used by a web-browser implementation). The +implementation conforms to the HTTP/1.1 standard, +<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2616.txt">RFC +2616</a>. +</p> + +<p> +The module exports functions that provide HTTP functionality in different +levels of abstraction. From the simple +string oriented requests, through generic +<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a> based, down to even lower-level if you bother to look through the source code. +</p> + +<p> +To obtain the <tt>http</tt> namespace, run: +</p> + +<pre class=example> +-- loads the HTTP module and any libraries it requires +local http = require("socket.http") +</pre> + +<p> +URLs must conform to +<a href="http://www.cs.princeton.edu/~diego/rfc/rfc1738.txt">RFC +1738</a>, +that is, an URL is a string in the form: +</p> + +<blockquote> +<pre> +[http://][<user>[:<password>]@]<host>[:<port>][/<path>] +</pre> +</blockquote> + +<p> +MIME headers are represented as a Lua table in the form: +</p> + +<blockquote> +<table summary="MIME headers in Lua table"> +<tr><td><tt> +headers = {<br> + field-1-name = <i>field-1-value</i>,<br> + field-2-name = <i>field-2-value</i>,<br> + field-3-name = <i>field-3-value</i>,<br> + ...<br> + field-n-name = <i>field-n-value</i><br> +} +</tt></td></tr> +</table> +</blockquote> + +<p> +Field names are case insensitive (as specified by the standard) and all +functions work with lowercase field names. +Field values are left unmodified. +</p> + +<p class=note> +Note: MIME headers are independent of order. Therefore, there is no problem +in representing them in a Lua table. +</p> + +<p> +The following constants can be set to control the default behavior of +the HTTP module: +</p> + +<ul> +<li> <tt>PORT</tt>: default port used for connections; +<li> <tt>PROXY</tt>: default proxy used for connections; +<li> <tt>TIMEOUT</tt>: sets the timeout for all I/O operations; +<li> <tt>USERAGENT</tt>: default user agent reported to server. +</ul> + +<!-- http.request ++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=request> +http.<b>request(</b>url [, body]<b>)</b><br> +http.<b>request{</b><br> + url = <i>string</i>,<br> + [sink = <i>LTN12 sink</i>,]<br> + [method = <i>string</i>,]<br> + [headers = <i>header-table</i>,]<br> + [source = <i>LTN12 source</i>],<br> + [step = <i>LTN12 pump step</i>,]<br> + [proxy = <i>string</i>,]<br> + [redirect = <i>boolean</i>,]<br> + [create = <i>function</i>]<br> +<b>}</b> +</p> + +<p class=description> +The request function has two forms. The simple form downloads +a URL using the <tt>GET</tt> or <tt>POST</tt> method and is based +on strings. The generic form performs any HTTP method and is +<a href=http://lua-users.org/wiki/FiltersSourcesAndSinks>LTN12</a> based. +</p> + +<p class=parameters> +If the first argument of the <tt>request</tt> function is a string, it +should be an <tt>url</tt>. In that case, if a <tt>body</tt> +is provided as a string, the function will perform a <tt>POST</tt> method +in the <tt>url</tt>. Otherwise, it performs a <tt>GET</tt> in the +<tt>url</tt> +</p> + +<p class=parameters> +If the first argument is instead a table, the most important fields are +the <tt>url</tt> and the <em>simple</em> +<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a> +<tt>sink</tt> that will receive the downloaded content. +Any part of the <tt>url</tt> can be overridden by including +the appropriate field in the request table. +If authentication information is provided, the function +uses the Basic Authentication Scheme (see <a href="#authentication">note</a>) +to retrieve the document. If <tt>sink</tt> is <tt><b>nil</b></tt>, the +function discards the downloaded data. The optional parameters are the +following: +</p> +<ul> +<li><tt>method</tt>: The HTTP request method. Defaults to "GET"; +<li><tt>headers</tt>: Any additional HTTP headers to send with the request; +<li><tt>source</tt>: <em>simple</em> +<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a> +source to provide the request body. If there +is a body, you need to provide an appropriate "<tt>content-length</tt>" +request header field, or the function will attempt to send the body as +"<tt>chunked</tt>" (something few servers support). Defaults to the empty source; +<li><tt>step</tt>: +<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a> +pump step function used to move data. +Defaults to the LTN12 <tt>pump.step</tt> function. +<li><tt>proxy</tt>: The URL of a proxy server to use. Defaults to no proxy; +<li><tt>redirect</tt>: Set to <tt><b>false</b></tt> to prevent the +function from automatically following 301 or 302 server redirect messages; +<li><tt>create</tt>: An optional function to be used instead of +<a href=tcp.html#socket.tcp><tt>socket.tcp</tt></a> when the communications socket is created. +</ul> + +<p class=return> +In case of failure, the function returns <tt><b>nil</b></tt> followed by an +error message. If successful, the simple form returns the response +body as a string, followed by the response status code, the response +headers and the response status line. The generic function returns the same +information, except the first return value is just the number 1 (the body +goes to the <tt>sink</tt>). +</p> + +<p class=return> +Even when the server fails to provide the contents of the requested URL (URL not found, for example), +it usually returns a message body (a web page informing the +URL was not found or some other useless page). To make sure the +operation was successful, check the returned status <tt>code</tt>. For +a list of the possible values and their meanings, refer to <a +href="http://www.cs.princeton.edu/~diego/rfc/rfc2616.txt">RFC +2616</a>. +</p> + +<p class=description> +Here are a few examples with the simple interface: +</p> + +<pre class=example> +-- load the http module +local io = require("io") +local http = require("socket.http") +local ltn12 = require("ltn12") + +-- connect to server "www.cs.princeton.edu" and retrieves this manual +-- file from "~diego/professional/luasocket/http.html" and print it to stdout +http.request{ + url = "http://www.cs.princeton.edu/~diego/professional/luasocket/http.html", + sink = ltn12.sink.file(io.stdout) +} + +-- connect to server "www.example.com" and tries to retrieve +-- "/private/index.html". Fails because authentication is needed. +b, c, h = http.request("http://www.example.com/private/index.html") +-- b returns some useless page telling about the denied access, +-- h returns authentication information +-- and c returns with value 401 (Authentication Required) + +-- tries to connect to server "wrong.host" to retrieve "/" +-- and fails because the host does not exist. +r, e = http.request("http://wrong.host/") +-- r is nil, and e returns with value "host not found" +</pre> + +<p class=description> +And here is an example using the generic interface: +</p> + +<pre class=example> +-- load the http module +http = require("socket.http") + +-- Requests information about a document, without downloading it. +-- Useful, for example, if you want to display a download gauge and need +-- to know the size of the document in advance +r, c, h = http.request { + method = "HEAD", + url = "http://www.tecgraf.puc-rio.br/~diego" +} +-- r is 1, c is 200, and h would return the following headers: +-- h = { +-- date = "Tue, 18 Sep 2001 20:42:21 GMT", +-- server = "Apache/1.3.12 (Unix) (Red Hat/Linux)", +-- ["last-modified"] = "Wed, 05 Sep 2001 06:11:20 GMT", +-- ["content-length"] = 15652, +-- ["connection"] = "close", +-- ["content-Type"] = "text/html" +-- } +</pre> + +<p class=note id=post> +Note: When sending a POST request, simple interface adds a +"<tt>Content-type: application/x-www-form-urlencoded</tt>" +header to the request. This is the type used by +HTML forms. If you need another type, use the generic +interface. +</p> + +<p class=note id=authentication> +Note: Some URLs are protected by their +servers from anonymous download. For those URLs, the server must receive +some sort of authentication along with the request or it will deny +download and return status "401 Authentication Required". +</p> + +<p class=note> +The HTTP/1.1 standard defines two authentication methods: the Basic +Authentication Scheme and the Digest Authentication Scheme, both +explained in detail in +<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2068.txt">RFC 2068</a>. +</p> + +<p class=note>The Basic Authentication Scheme sends +<tt><user></tt> and +<tt><password></tt> unencrypted to the server and is therefore +considered unsafe. Unfortunately, by the time of this implementation, +the wide majority of servers and browsers support the Basic Scheme only. +Therefore, this is the method used by the toolkit whenever +authentication is required. +</p> + +<pre class=example> +-- load required modules +http = require("socket.http") +mime = require("mime") + +-- Connect to server "www.example.com" and tries to retrieve +-- "/private/index.html", using the provided name and password to +-- authenticate the request +b, c, h = http.request("http://fulano:silva@www.example.com/private/index.html") + +-- Alternatively, one could fill the appropriate header and authenticate +-- the request directly. +r, c = http.request { + url = "http://www.example.com/private/index.html", + headers = { authentication = "Basic " .. (mime.b64("fulano:silva")) } +} +</pre> + +<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<div class=footer> +<hr> +<center> +<p class=bar> +<a href="home.html">home</a> · +<a href="home.html#download">download</a> · +<a href="installation.html">installation</a> · +<a href="introduction.html">introduction</a> · +<a href="reference.html">reference</a> +</p> +<p> +<small> +Last modified by Diego Nehab on <br> +Thu Apr 20 00:25:26 EDT 2006 +</small> +</p> +</center> +</div> + +</body> +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/index.html Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,208 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> +<html> + +<head> +<meta name="description" content="The LuaSocket Homepage"> +<meta name="keywords" content="Lua, LuaSocket, Network, Library, Support, Internet"> +<title>LuaSocket: Network support for the Lua language </title> +<link rel="stylesheet" href="reference.css" type="text/css"> +</head> + +<body> + +<!-- header +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<div class=header> +<hr> +<center> +<table summary="LuaSocket logo"> +<tr><td align=center><a href="http://www.lua.org"> +<img width=128 height=128 border=0 alt="LuaSocket" src="luasocket.png"> +</a></td></tr> +<tr><td align=center valign=top>Network support for the Lua language +</td></tr> +</table> +<p class=bar> +<a href="home.html">home</a> · +<a href="home.html#download">download</a> · +<a href="installation.html">installation</a> · +<a href="introduction.html">introduction</a> · +<a href="reference.html">reference</a> +</p> +</center> +<hr> +</div> + +<!-- whatis +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<h2 id=whatis>What is LuaSocket?</h2> + +<p> +LuaSocket is a <a href="http://www.lua.org">Lua</a> extension library +that is composed by two parts: a C core that provides support for the TCP +and UDP transport layers, and a set of Lua modules that add support for +functionality commonly needed by applications that deal with the Internet. +</p> + +<p> +The core support has been implemented so that it is both efficient and +simple to use. It is available to any Lua application once it has been +properly initialized by the interpreter in use. The code has been tested +and runs well on several Windows and Unix platforms. </p> + +<p> +Among the support modules, the most commonly used implement the +<a href=smtp.html>SMTP</a> +(sending e-mails), +<a href=http.html>HTTP</a> +(WWW access) and +<a href=ftp.html>FTP</a> +(uploading and downloading files) client +protocols. These provide a very natural and generic interface to the +functionality defined by each protocol. +In addition, you will find that the +<a href=mime.html>MIME</a> (common encodings), +<a href=url.html>URL</a> +(anything you could possible want to do with one) and +<a href=ltn12.html>LTN12</a> +(filters, sinks, sources and pumps) modules can be very handy. +</p> + +<p> +The library is available under the same +<a href="http://www.lua.org/copyright.html"> +terms and conditions</a> as the Lua language, the MIT license. The idea is +that if you can use Lua in a project, you should also be able to use +LuaSocket. +</p> + +<p> +Copyright © 2004-2007 Diego Nehab. All rights reserved. <br> +Author: <A href="http://www.cs.princeton.edu/~diego">Diego Nehab</a> +</p> + +<!-- download +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<h2 id=download>Download</h2> + +<p> +LuaSocket version 2.0.2 is now available for download! It is +compatible with Lua 5.1, and has +been tested on Windows XP, Linux, and Mac OS X. Chances +are it works well on most UNIX distributions and Windows flavors. +</p> + +<p> +The library can be downloaded in source code from the +<a href=http://luaforge.net/projects/luasocket/>LuaSocket +project page</a> at LuaForge. +Besides the full C and Lua source code for the library, the distribution +contains several examples, this user's manual and basic test procedures. +</p> + +<p> +Danilo Tuler is maintaining Win32 binaries for LuaSocket, which are also +available from LuaForge. These are compatible with the +<a href=http://luaforge.net/projects/luabinaries>LuaBinaries</a>, +also available from LuaForge. +</p> + +<p> Take a look at the <a +href=installation.html>installation</a> section of the +manual to find out how to properly install the library. +</p> + +<!-- thanks +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<h2 id=thanks>Special thanks</h2> + +<p> +Throughout LuaSocket's history, many people gave suggestions that helped +improve it. For that, I thank the Lua community. +Special thanks go to +David Burgess, who has helped push the library to a new level of quality and +from whom I have learned a lot of stuff that doesn't show up in RFCs. +Special thanks also to Carlos Cassino, who played a big part in the +extensible design seen in the C core of LuaSocket 2.0. Mike Pall +has been helping a lot too! Thanks to you all! +</p> + +<!-- whatsnew +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<h2 id=new>What's New</h2> + +<p> +2.0.2 is just a bug-fix/update release. +</p> + +<ul> +<li> Improved: http.request() now supports deprecated +HTTP/0.9 servers (Florian Berger); +<li> Fixed: could return "timedout" instead of "timeout" (Leo Leo); +<li> Fixed: crash when reading '*a' on closed socket (Paul Ducklin); +<li> Fixed: return values are consistent when reading from closed sockets; +<li> Fixed: case sensitivity in headers of multipart +messages in smtp.message() (Graham Henstridge); +<li> Fixed a couple instances of error() being called instead of +base.error(). These would cause an error when an error was +reported :) (Ketmar Dark); +<li> Fixed: test script now uses pairs() iterator instead +of the old Lua syntax (Robert Dodier). +</ul> + +<p> +2.0.1 is just a bug-fix/update release. +</p> + +<ul> +<li> Updated: now using <tt>compat-5.1r5</tt>; +<li> Improved: <tt>http.request</tt> is more robust to +malformed URLs (Adrian Sietsma); +<li> Improved: the simple <tt>http.request</tt> interface sends a +"<tt>Content-type: application/x-www-form-urlencoded</tt>" +header (William Trenker); +<li> Improved: <tt>http.request</tt> is robust to evil +servers that send inappropriate 100-continue messages +(David Burgess); +<li> Fixed: <tt>http.request</tt> was using the old host header during +redirects (Florian Berger); +<li> Fixed: sample <tt>unix.c</tt> had fallen through the +cracks during development (Matthew Percival); +<li> Fixed: error code was not being propagated correctly in +ftp.lua (David Burgess). +</ul> + +<!-- old ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<h2 id=old>Old Versions</h2> + +<p> +All previous versions of the LuaSocket library can be downloaded <a +href="http://www.cs.princeton.edu/~diego/professional/luasocket/old"> +here</a>. Although these versions are no longer supported, they are +still available for those that have compatibility issues. +</p> + +<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<div class=footer> +<hr> +<center> +<p class=bar> +<a href="home.html#download">download</a> · +<a href="installation.html">installation</a> · +<a href="introduction.html">introduction</a> · +<a href="reference.html">reference</a> +</p> +<p> +<small> +Last modified by Diego Nehab on <br> +Wed Oct 3 02:07:59 BRT 2007 +</small> +</p> +</center> +</div> + +</body> +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/installation.html Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,163 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> +<html> + +<head> +<meta name="description" content="LuaSocket: Introduction to the core"> +<meta name="keywords" content="Lua, LuaSocket, TCP, UDP, Network, Support, +Installation"> +<title>LuaSocket: Installation</title> +<link rel="stylesheet" href="reference.css" type="text/css"> +</head> + +<body> + +<!-- header +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<div class=header> +<hr> +<center> +<table summary="LuaSocket logo"> +<tr><td align=center><a href="http://www.lua.org"> +<img width=128 height=128 border=0 alt="LuaSocket" src="luasocket.png"> +</a></td></tr> +<tr><td align=center valign=top>Network support for the Lua language +</td></tr> +</table> +<p class=bar> +<a href="home.html">home</a> · +<a href="home.html#download">download</a> · +<a href="installation.html">installation</a> · +<a href="introduction.html">introduction</a> · +<a href="reference.html">reference</a> +</p> +</center> +<hr> +</div> + +<!-- installation ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<h2>Installation</h2> + +<p> LuaSocket 2.0.2 uses the new package system for Lua 5.1. +All Lua library developers are encouraged to update their libraries so that +all libraries can coexist peacefully and users can benefit from the +standardization and flexibility of the standard. +</p> + +<p> +Those stuck with Lua 5.0 will need the +<a href=http://www.keplerproject.org/compat/>compat-5.1</a> +module. It is maintained by +<a href=http://www.keplerproject.org/>The Kepler +Project</a>'s team, and implements the Lua 5.1 package proposal +on top of Lua 5.0. </p> + +<p> Here we will only describe the standard distribution. +If the standard doesn't meet your needs, we refer you to the +Lua discussion list, where any question about the package +scheme will likely already have been answered. </p> + +<h3>Directory structure</h3> + +<p> On Unix systems, the standard distribution uses two base +directories, one for system dependent files, and another for system +independent files. Let's call these directories <tt><CDIR></tt> +and <tt><LDIR></tt>, respectively. +For instance, in my laptop, I use '<tt>/usr/local/lib/lua/5.0</tt>' for +<tt><CDIR></tt> and '<tt>/usr/local/share/lua/5.0</tt>' for +<tt><LDIR></tt>. On Windows, sometimes only one directory is used, say +'<tt>c:\program files\lua\5.0</tt>'. Here is the standard LuaSocket +distribution directory structure:</p> + +<pre class=example> +<LDIR>/compat-5.1.lua +<LDIR>/ltn12.lua +<LDIR>/socket.lua +<CDIR>/socket/core.dll +<LDIR>/socket/http.lua +<LDIR>/socket/tp.lua +<LDIR>/socket/ftp.lua +<LDIR>/socket/smtp.lua +<LDIR>/socket/url.lua +<LDIR>/mime.lua +<CDIR>/mime/core.dll +</pre> + +<p> Naturally, on Unix systems, <tt>core.dll</tt> +would be replaced by <tt>core.so</tt>. +</p> + +<p> In order for the interpreter to find all LuaSocket components, three +environment variables need to be set. The first environment variable tells +the interpreter to load the <tt>compat-5.1.lua</tt> module at startup: </p> + +<pre class=example> +LUA_INIT=@<LDIR>/compat-5.1.lua +</pre> + +<p> +This is only need for Lua 5.0! Lua 5.1 comes with +the package system built in, of course. +</p> + +<p> +The other two environment variables instruct the compatibility module to +look for dynamic libraries and extension modules in the appropriate +directories and with the appropriate filename extensions. +</p> + +<pre class=example> +LUA_PATH=<LDIR>/?.lua;?.lua +LUA_CPATH=<CDIR>/?.dll;?.dll +</pre> + +<p> Again, naturally, on Unix systems the shared library extension would be +<tt>.so</tt> instead of <tt>.dll</tt>.</p> + +<h3>Using LuaSocket</h3> + +<p> With the above setup, and an interpreter with shared library support, +it should be easy to use LuaSocket. Just fire the interpreter and use the +<tt>require</tt> function to gain access to whatever module you need:</p> + +<pre class=example> +Lua 5.1.2 Copyright (C) 1994-2007 Lua.org, PUC-Rio +> socket = require("socket") +> print(socket._VERSION) +--> LuaSocket 2.0.2 +</pre> + +<p> Each module loads their dependencies automatically, so you only need to +load the modules you directly depend upon: </p> + +<pre class=example> +Lua 5.1.2 Copyright (C) 1994-2007 Lua.org, PUC-Rio +> http = require("socket.http") +> print(http.request("http://www.cs.princeton.edu/~diego/professional/luasocket")) +--> homepage gets dumped to terminal +</pre> + +<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<div class=footer> +<hr> +<center> +<p class=bar> +<a href="home.html">home</a> · +<a href="home.html#down">download</a> · +<a href="installation.html">installation</a> · +<a href="introduction.html">introduction</a> · +<a href="reference.html">reference</a> +</p> +<p> +<small> +Last modified by Diego Nehab on <br> +Thu Apr 20 00:25:30 EDT 2006 +</small> +</p> +</center> +</div> + +</body> +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/introduction.html Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,333 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> +<html> + +<head> +<meta name="description" content="LuaSocket: Introduction to the core"> +<meta name="keywords" content="Lua, LuaSocket, TCP, UDP, Network, +Library, Support"> +<title>LuaSocket: Introduction to the core</title> +<link rel="stylesheet" href="reference.css" type="text/css"> +</head> + +<body> + +<!-- header +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<div class=header> +<hr> +<center> +<table summary="LuaSocket logo"> +<tr><td align=center><a href="http://www.lua.org"> +<img width=128 height=128 border=0 alt="LuaSocket" src="luasocket.png"> +</a></td></tr> +<tr><td align=center valign=top>Network support for the Lua language +</td></tr> +</table> +<p class=bar> +<a href="home.html">home</a> · +<a href="home.html#download">download</a> · +<a href="installation.html">installation</a> · +<a href="introduction.html">introduction</a> · +<a href="reference.html">reference</a> +</p> +</center> +<hr> +</div> + +<!-- introduction +++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<h2>Introduction</h2> + +<p> +LuaSocket is a <a href="http://www.lua.org">Lua</a> extension library +that is composed by two parts: a C core that provides support for the TCP +and UDP transport layers, and a set of Lua modules that add support for +the SMTP (sending e-mails), HTTP (WWW access) and FTP (uploading and +downloading files) protocols and other functionality commonly needed by +applications that deal with the Internet. This introduction is about the C +core. +</p> + +<p> +Communication in LuaSocket is performed via I/O objects. These can +represent different network domains. Currently, support is provided for TCP +and UDP, but nothing prevents other developers from implementing SSL, Local +Domain, Pipes, File Descriptors etc. I/O objects provide a standard +interface to I/O across different domains and operating systems. +</p> + +<p> +The API design had two goals in mind. First, users +experienced with the C API to sockets should feel comfortable using LuaSocket. +Second, the simplicity and the feel of the Lua language should be +preserved. To achieve these goals, the LuaSocket API keeps the function names and semantics the C API whenever possible, but their usage in Lua has been greatly simplified. +</p> + + +<p> +One of the simplifications is the receive pattern capability. +Applications can read data from stream domains (such as TCP) +line by line, block by block, or until the connection is closed. +All I/O reads are buffered and the performance differences between +different receive patterns are negligible. +</p> + +<p> +Another advantage is the flexible timeout control +mechanism. As in C, all I/O operations are blocking by default. For +example, the <a href=tcp.html#send><tt>send</tt></a>, +<a href=tcp.html#receive><tt>receive</tt></a> and +<a href=tcp.html#accept><tt>accept</tt></a> methods +of the TCP domain will block the caller application until +the operation is completed (if ever!). However, with a call to the +<a href=tcp.html#settimeout><tt>settimeout</tt></a> +method, an application can specify upper limits on +the time it can be blocked by LuaSocket (the "<tt>total</tt>" timeout), on +the time LuaSocket can internally be blocked by any OS call (the +"<tt>block</tt>" timeout) or a combination of the two. Each LuaSocket +call might perform several OS calls, so that the two timeout values are +<em>not</em> equivalent. +</p> + +<p> +Finally, the host name resolution is transparent, meaning that most +functions and methods accept both IP addresses and host names. In case a +host name is given, the library queries the system's resolver and +tries the main IP address returned. Note that direct use of IP addresses +is more efficient, of course. The +<a href=dns.html#toip><tt>toip</tt></a> +and <a href=dns.html#tohostname><tt>tohostname</tt></a> +functions from the DNS module are provided to convert between host names and IP addresses. +</p> + +<p> +Together, these changes make network programming in LuaSocket much simpler +than it is in C, as the following sections will show. +</p> + +<!-- tcp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<h3 id=tcp>TCP</h3> + +<p> +TCP (Transfer Control Protocol) is reliable stream protocol. In other +words, applications communicating through TCP can send and receive data as +an error free stream of bytes. Data is split in one end and +reassembled transparently on the other end. There are no boundaries in +the data transfers. The library allows users to read data from the +sockets in several different granularities: patterns are available for +lines, arbitrary sized blocks or "read up to connection closed", all with +good performance. +</p> + +<p> +The library distinguishes three types of TCP sockets: <em>master</em>, +<em>client</em> and <em>server</em> sockets. +</p> + +<p> +Master sockets are newly created TCP sockets returned by the function +<a href=tcp.html#tcp><tt>socket.tcp</tt></a>. A master socket is +transformed into a server socket +after it is associated with a <em>local</em> address by a call to the +<a href=tcp.html#bind><tt>bind</tt></a> method followed by a call to the +<a href=tcp.html#listen><tt>listen</tt></a>. Conversely, a master socket +can be changed into a client socket with the method +<a href=tcp.html#connect><tt>connect</tt></a>, +which associates it with a <em>remote</em> address. +</p> + +<p> +On server sockets, applications can use the +<a href=tcp.html#accept><tt>accept</tt></a> method +to wait for a client connection. Once a connection is established, a +client socket object is returned representing this connection. The +other methods available for server socket objects are +<a href=tcp.html#getsockname><tt>getsockname</tt></a>, +<a href=tcp.html#setoption><tt>setoption</tt></a>, +<a href=tcp.html#settimeout><tt>settimeout</tt></a>, and +<a href=tcp.html#close><tt>close</tt></a>. +</p> + +<p> +Client sockets are used to exchange data between two applications over +the Internet. Applications can call the methods +<a href=tcp.html#send><tt>send</tt></a> and +<a href=tcp.html#receive><tt>receive</tt></a> +to send and receive data. The other methods +available for client socket objects are +<a href=tcp.html#getsockname><tt>getsockname</tt></a>, +<a href=tcp.html#getpeername><tt>getpeername</tt></a>, +<a href=tcp.html#setoption><tt>setoption</tt></a>, +<a href=tcp.html#settimeout><tt>settimeout</tt></a>, +<a href=tcp.html#shutdown><tt>shutdown</tt></a>, and +<a href=tcp.html#close><tt>close</tt></a>. +</p> + +<p> +Example: +</p> +<blockquote> +<p> +A simple echo server, using LuaSocket. The program binds to an ephemeral +port (one that is chosen by the operating system) on the local host and +awaits client connections on that port. When a connection is established, +the program reads a line from the remote end and sends it back, closing +the connection immediately. You can test it using the telnet +program. +</p> + +<pre class=example> +-- load namespace +local socket = require("socket") +-- create a TCP socket and bind it to the local host, at any port +local server = assert(socket.bind("*", 0)) +-- find out which port the OS chose for us +local ip, port = server:getsockname() +-- print a message informing what's up +print("Please telnet to localhost on port " .. port) +print("After connecting, you have 10s to enter a line to be echoed") +-- loop forever waiting for clients +while 1 do + -- wait for a connection from any client + local client = server:accept() + -- make sure we don't block waiting for this client's line + client:settimeout(10) + -- receive the line + local line, err = client:receive() + -- if there was no error, send it back to the client + if not err then client:send(line .. "\n") end + -- done with client, close the object + client:close() +end +</pre> +</blockquote> + +<!-- udp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<h3 id=udp>UDP</h3> + +<p> +UDP (User Datagram Protocol) is a non-reliable datagram protocol. In +other words, applications communicating through UDP send and receive +data as independent blocks, which are not guaranteed to reach the other +end. Even when they do reach the other end, they are not guaranteed to be +error free. Data transfers are atomic, one datagram at a time. Reading +only part of a datagram discards the rest, so that the following read +operation will act on the next datagram. The advantages are in +simplicity (no connection setup) and performance (no error checking or +error correction). +</p> + +<p> +Note that although no guarantees are made, these days +networks are so good that, under normal circumstances, few errors +happen in practice. +</p> + +<p> +An UDP socket object is created by the +<a href=udp.html#udp><tt>socket.udp</tt></a> function. UDP +sockets do not need to be connected before use. The method +<a href=udp.html#sendto><tt>sendto</tt></a> +can be used immediately after creation to +send a datagram to IP address and port. Host names are not allowed +because performing name resolution for each packet would be forbiddingly +slow. Methods +<a href=udp.html#receive><tt>receive</tt></a> and +<a href=udp.html#receivefrom><tt>receivefrom</tt></a> +can be used to retrieve datagrams, the latter returning the IP and port of +the sender as extra return values (thus being slightly less +efficient). +</p> + +<p> +When communication is performed repeatedly with a single peer, an +application should call the +<a href=udp.html#setpeername><tt>setpeername</tt></a> method to specify a +permanent partner. Methods +<a href=udp.html#sendto><tt>sendto</tt></a> and +<a href=udp.html#receivefrom><tt>receivefrom</tt></a> +can no longer be used, but the method +<a href=udp.html#send><tt>send</tt></a> can be used to send data +directly to the peer, and the method +<a href=udp.html#receive><tt>receive</tt></a> +will only return datagrams originating +from that peer. There is about 30% performance gain due to this practice. +</p> + +<p> +To associate an UDP socket with a local address, an application calls the +<a href=udp.html#setsockname><tt>setsockname</tt></a> +method <em>before</em> sending any datagrams. Otherwise, the socket is +automatically bound to an ephemeral address before the first data +transmission and once bound the local address cannot be changed. +The other methods available for UDP sockets are +<a href=udp.html#getpeername><tt>getpeername</tt></a>, +<a href=udp.html#getsockname><tt>getsockname</tt></a>, +<a href=udp.html#settimeout><tt>settimeout</tt></a>, +<a href=udp.html#setoption><tt>setoption</tt></a> and +<a href=udp.html#close><tt>close</tt></a>. +</p> + +<p> +Example: +</p> +<blockquote> +<p> +A simple daytime client, using LuaSocket. The program connects to a remote +server and tries to retrieve the daytime, printing the answer it got or an +error message. +</p> + +<pre class=example> +-- change here to the host an port you want to contact +local host, port = "localhost", 13 +-- load namespace +local socket = require("socket") +-- convert host name to ip address +local ip = assert(socket.dns.toip(host)) +-- create a new UDP object +local udp = assert(socket.udp()) +-- contact daytime host +assert(udp:sendto("anything", ip, port)) +-- retrieve the answer and print results +io.write(assert(udp:receive())) +</pre> +</blockquote> + +<!-- More +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<h3 id=more>Support modules</h3> + +<p> Although not covered in the introduction, LuaSocket offers +much more than TCP and UDP functionality. As the library +evolved, support for <a href=http.html>HTTP</a>, <a href=ftp.html>FTP</a>, +and <a href=smtp.html>SMTP</a> were built on top of these. These modules +and many others are covered by the <a href=reference.html>reference manual</a>. +</p> + +<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<div class=footer> +<hr> +<center> +<p class=bar> +<a href="home.html">home</a> · +<a href="home.html#down">download</a> · +<a href="installation.html">installation</a> · +<a href="introduction.html">introduction</a> · +<a href="reference.html">reference</a> +</p> +<p> +<small> +Last modified by Diego Nehab on <br> +Thu Apr 20 00:25:36 EDT 2006 +</small> +</p> +</center> +</div> + +</body> +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/ltn12.html Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,430 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> +<html> + +<head> +<meta name="description" content="LuaSocket: LTN12 support"> +<meta name="keywords" content="Lua, LuaSocket, Filters, Source, Sink, +Pump, Support, Library"> +<title>LuaSocket: LTN12 module</title> +<link rel="stylesheet" href="reference.css" type="text/css"> +</head> + +<body> + +<!-- header +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<div class=header> +<hr> +<center> +<table summary="LuaSocket logo"> +<tr><td align=center><a href="http://www.lua.org"> +<img width=128 height=128 border=0 alt="LuaSocket" src="luasocket.png"> +</a></td></tr> +<tr><td align=center valign=top>Network support for the Lua language +</td></tr> +</table> +<p class=bar> +<a href="home.html">home</a> · +<a href="home.html#download">download</a> · +<a href="installation.html">installation</a> · +<a href="introduction.html">introduction</a> · +<a href="reference.html">reference</a> +</p> +</center> +<hr> +</div> + +<!-- ltn12 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<h2 id=ltn12>LTN12</h2> + +<p> The <tt>ltn12</tt> namespace implements the ideas described in +<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks"> +LTN012, Filters sources and sinks</a>. This manual simply describes the +functions. Please refer to the LTN for a deeper explanation of the +functionality provided by this module. +</p> + +<p> +To obtain the <tt>ltn12</tt> namespace, run: +</p> + +<pre class=example> +-- loads the LTN21 module +local ltn12 = require("ltn12") +</pre> + +<!-- filters ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<h3 id="filter">Filters</h3> + +<!-- chain ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id="filter.chain"> +ltn12.filter.<b>chain(</b>filter<sub>1</sub>, filter<sub>2</sub> +[, ... filter<sub>N</sub>]<b>)</b> +</p> + +<p class=description> +Returns a filter that passes all data it receives through each of a +series of given filters. +</p> + +<p class=parameters> +<tt>Filter<sub>1</sub></tt> to <tt>filter<sub>N</sub></tt> are simple +filters. +</p> + +<p class=return> +The function returns the chained filter. +</p> + +<p class=note> +The nesting of filters can be arbitrary. For instance, the useless filter +below doesn't do anything but return the data that was passed to it, +unaltered. +</p> + +<pre class=example> +-- load required modules +local ltn12 = require("ltn12") +local mime = require("mime") + +-- create a silly identity filter +id = ltn12.filter.chain( + mime.encode("quoted-printable"), + mime.encode("base64"), + mime.decode("base64"), + mime.decode("quoted-printable") +) +</pre> + +<!-- cycle ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id="filter.cycle"> +ltn12.filter.<b>cycle(</b>low [, ctx, extra]<b>)</b> +</p> + +<p class=description> +Returns a high-level filter that cycles though a low-level filter by +passing it each chunk and updating a context between calls. +</p> + +<p class=parameters> +<tt>Low</tt> is the low-level filter to be cycled, +<tt>ctx</tt> is the initial context and <tt>extra</tt> is any extra +argument the low-level filter might take. +</p> + +<p class=return> +The function returns the high-level filter. +</p> + +<pre class=example> +-- load the ltn12 module +local ltn12 = require("ltn12") + +-- the base64 mime filter factory +encodet['base64'] = function() + return ltn12.filter.cycle(b64, "") +end +</pre> + +<!-- pumps ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<h3 id="pump">Pumps</h3> + +<!-- all ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id="pump.all"> +ltn12.pump.<b>all(</b>source, sink<b>)</b> +</p> + +<p class=description> +Pumps <em>all</em> data from a <tt>source</tt> to a <tt>sink</tt>. +</p> + +<p class=return> +If successful, the function returns a value that evaluates to +<b><tt>true</tt></b>. In case +of error, the function returns a <b><tt>false</tt></b> value, followed by an error message. +</p> + +<!-- step +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id="pump.step"> +ltn12.pump.<b>step(</b>source, sink<b>)</b> +</p> + +<p class=description> +Pumps <em>one</em> chunk of data from a <tt>source</tt> to a <tt>sink</tt>. +</p> + +<p class=return> +If successful, the function returns a value that evaluates to +<b><tt>true</tt></b>. In case +of error, the function returns a <b><tt>false</tt></b> value, followed by an error message. +</p> + +<!-- sinks ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<h3 id="sink">Sinks</h3> + +<!-- chain ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id="sink.chain"> +ltn12.sink.<b>chain(</b>filter, sink<b>)</b> +</p> + +<p class=description> +Creates and returns a new sink that passes data through a <tt>filter</tt> before sending it to a given <tt>sink</tt>. +</p> + +<!-- error ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id="sink.error"> +ltn12.sink.<b>error(</b>message<b>)</b> +</p> + +<p class=description> +Creates and returns a sink that aborts transmission with the error +<tt>message</tt>. +</p> + +<!-- file +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id="sink.file"> +ltn12.sink.<b>file(</b>handle, message<b>)</b> +</p> + +<p class=description> +Creates a sink that sends data to a file. +</p> + +<p class=parameters> +<tt>Handle</tt> is a file handle. If <tt>handle</tt> is <tt><b>nil</b></tt>, +<tt>message</tt> should give the reason for failure. +</p> + +<p class=return> +The function returns a sink that sends all data to the given <tt>handle</tt> +and closes the file when done, or a sink that aborts the transmission with +the error <tt>message</tt> +</p> + +<p class=note> +In the following example, notice how the prototype is designed to +fit nicely with the <tt>io.open</tt> function. +</p> + +<pre class=example> +-- load the ltn12 module +local ltn12 = require("ltn12") + +-- copy a file +ltn12.pump.all( + ltn12.source.file(io.open("original.png")), + ltn12.sink.file(io.open("copy.png")) +) +</pre> + +<!-- null +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id="sink.null"> +ltn12.sink.<b>null()</b> +</p> + +<p class=description> +Returns a sink that ignores all data it receives. +</p> + +<!-- simplify +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id="sink.simplify"> +ltn12.sink.<b>simplify(</b>sink<b>)</b> +</p> + +<p class=description> +Creates and returns a simple sink given a fancy <tt>sink</tt>. +</p> + +<!-- table ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id="sink.table"> +ltn12.sink.<b>table(</b>[table]<b>)</b> +</p> + +<p class=description> +Creates a sink that stores all chunks in a table. The chunks can later be +efficiently concatenated into a single string. +</p> + +<p class=parameters> +<tt>Table</tt> is used to hold the chunks. If +<tt><b>nil</b></tt>, the function creates its own table. +</p> + +<p class=return> +The function returns the sink and the table used to store the chunks. +</p> + +<pre class=example> +-- load needed modules +local http = require("socket.http") +local ltn12 = require("ltn12") + +-- a simplified http.get function +function http.get(u) + local t = {} + local respt = request{ + url = u, + sink = ltn12.sink.table(t) + } + return table.concat(t), respt.headers, respt.code +end +</pre> + +<!-- sinks ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<h3 id="source">Sources</h3> + +<!-- cat ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id="source.cat"> +ltn12.source.<b>cat(</b>source<sub>1</sub> [, source<sub>2</sub>, ..., +source<sub>N</sub>]<b>)</b> +</p> + +<p class=description> +Creates a new source that produces the concatenation of the data produced +by a number of sources. +</p> + +<p class=parameters> +<tt>Source<sub>1</sub></tt> to <tt>source<sub>N</sub></tt> are the original +sources. +</p> + +<p class=return> +The function returns the new source. +</p> + +<!-- chain ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id="source.chain"> +ltn12.source.<b>chain(</b>source, filter<b>)</b> +</p> + +<p class=description> +Creates a new <tt>source</tt> that passes data through a <tt>filter</tt> +before returning it. +</p> + +<p class=return> +The function returns the new source. +</p> + +<!-- empty ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id="source.empty"> +ltn12.source.<b>empty()</b> +</p> + +<p class=description> +Creates and returns an empty source. +</p> + +<!-- error ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id="source.error"> +ltn12.source.<b>error(</b>message<b>)</b> +</p> + +<p class=description> +Creates and returns a source that aborts transmission with the error +<tt>message</tt>. +</p> + +<!-- file +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id="source.file"> +ltn12.source.<b>file(</b>handle, message<b>)</b> +</p> + +<p class=description> +Creates a source that produces the contents of a file. +</p> + +<p class=parameters> +<tt>Handle</tt> is a file handle. If <tt>handle</tt> is <tt><b>nil</b></tt>, +<tt>message</tt> should give the reason for failure. +</p> + +<p class=return> +The function returns a source that reads chunks of data from +given <tt>handle</tt> and returns it to the user, +closing the file when done, or a source that aborts the transmission with +the error <tt>message</tt> +</p> + +<p class=note> +In the following example, notice how the prototype is designed to +fit nicely with the <tt>io.open</tt> function. +</p> + +<pre class=example> +-- load the ltn12 module +local ltn12 = require("ltn12") + +-- copy a file +ltn12.pump.all( + ltn12.source.file(io.open("original.png")), + ltn12.sink.file(io.open("copy.png")) +) +</pre> + +<!-- simplify +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id="source.simplify"> +ltn12.source.<b>simplify(</b>source<b>)</b> +</p> + +<p class=description> +Creates and returns a simple source given a fancy <tt>source</tt>. +</p> + +<!-- string +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id="source.string"> +ltn12.source.<b>string(</b>string<b>)</b> +</p> + +<p class=description> +Creates and returns a source that produces the contents of a +<tt>string</tt>, chunk by chunk. +</p> + +<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<div class=footer> +<hr> +<center> +<p class=bar> +<a href="home.html">home</a> · +<a href="home.html#down">download</a> · +<a href="installation.html">installation</a> · +<a href="introduction.html">introduction</a> · +<a href="reference.html">reference</a> +</p> +<p> +<small> +Last modified by Diego Nehab on <br> +Thu Apr 20 00:25:41 EDT 2006 +</small> +</p> +</center> +</div> + +</body> +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/mime.html Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,476 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> +<html> + +<head> +<meta name="description" content="LuaSocket: MIME support"> +<meta name="keywords" content="Lua, LuaSocket, MIME, Library, Support"> +<title>LuaSocket: MIME module</title> +<link rel="stylesheet" href="reference.css" type="text/css"> +</head> + +<body> + +<!-- header +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<div class=header> +<hr> +<center> +<table summary="LuaSocket logo"> +<tr><td align=center><a href="http://www.lua.org"> +<img width=128 height=128 border=0 alt="LuaSocket" src="luasocket.png"> +</a></td></tr> +<tr><td align=center valign=top>Network support for the Lua language +</td></tr> +</table> +<p class=bar> +<a href="home.html">home</a> · +<a href="home.html#download">download</a> · +<a href="installation.html">installation</a> · +<a href="introduction.html">introduction</a> · +<a href="reference.html">reference</a> +</p> +</center> +<hr> +</div> + +<!-- mime +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<h2 id=mime>MIME</h2> + +<p> +The <tt>mime</tt> namespace offers filters that apply and remove common +content transfer encodings, such as Base64 and Quoted-Printable. +It also provides functions to break text into lines and change +the end-of-line convention. +MIME is described mainly in +<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2045.txt">RFC 2045</a>, +<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2046.txt">2046</a>, +<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2047.txt">2047</a>, +<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2047.txt">2048</a>, and +<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2048.txt">2049</a>. +</p> + +<p> +All functionality provided by the MIME module +follows the ideas presented in +<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks"> +LTN012, Filters sources and sinks</a>. +</p> + +<p> +To obtain the <tt>mime</tt> namespace, run: +</p> + +<pre class=example> +-- loads the MIME module and everything it requires +local mime = require("mime") +</pre> + + +<!-- High-level +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<h3 id=high>High-level filters</h3> + +<!-- normalize ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id="normalize"> +mime.<b>normalize(</b>[marker]<b>)</b> +</p> + +<p class=description> +Converts most common end-of-line markers to a specific given marker. +</p> + +<p class=parameters> +<tt>Marker</tt> is the new marker. It defaults to CRLF, the canonic +end-of-line marker defined by the MIME standard. +</p> + +<p class=return> +The function returns a filter that performs the conversion. +</p> + +<p class=note> +Note: There is no perfect solution to this problem. Different end-of-line +markers are an evil that will probably plague developers forever. +This function, however, will work perfectly for text created with any of +the most common end-of-line markers, i.e. the Mac OS (CR), the Unix (LF), +or the DOS (CRLF) conventions. Even if the data has mixed end-of-line +markers, the function will still work well, although it doesn't +guarantee that the number of empty lines will be correct. +</p> + +<!-- decode +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id="decode"> +mime.<b>decode(</b>"base64"<b>)</b><br> +mime.<b>decode(</b>"quoted-printable"<b>)</b> +</p> + +<p class=description> +Returns a filter that decodes data from a given transfer content +encoding. +</p> + +<!-- encode +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id="encode"> +mime.<b>encode(</b>"base64"<b>)</b><br> +mime.<b>encode(</b>"quoted-printable" [, mode]<b>)</b> +</p> + +<p class=description> +Returns a filter that encodes data according to a given transfer content +encoding. +</p> + +<p class=parameters> +In the Quoted-Printable case, the user can specify whether the data is +textual or binary, by passing the <tt>mode</tt> strings "<tt>text</tt>" or +"<tt>binary</tt>". <tt>Mode</tt> defaults to "<tt>text</tt>". +</p> + +<p class=note> +Although both transfer content encodings specify a limit for the line +length, the encoding filters do <em>not</em> break text into lines (for +added flexibility). +Below is a filter that converts binary data to the Base64 transfer content +encoding and breaks it into lines of the correct size. +</p> + +<pre class=example> +base64 = ltn12.filter.chain( + mime.encode("base64"), + mime.wrap("base64") +) +</pre> + +<p class=note> +Note: Text data <em>has</em> to be converted to canonic form +<em>before</em> being encoded. +</p> + +<pre class=example> +base64 = ltn12.filter.chain( + mime.normalize(), + mime.encode("base64"), + mime.wrap("base64") +) +</pre> + +<!-- stuff +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id="stuff"> +mime.<b>stuff()</b><br> +</p> + +<p class=description> +Creates and returns a filter that performs stuffing of SMTP messages. +</p> + +<p class=note> +Note: The <a href=smtp.html#send><tt>smtp.send</tt></a> function +uses this filter automatically. You don't need to chain it with your +source, or apply it to your message body. +</p> + +<!-- wrap +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id="wrap"> +mime.<b>wrap(</b>"text" [, length]<b>)</b><br> +mime.<b>wrap(</b>"base64"<b>)</b><br> +mime.<b>wrap(</b>"quoted-printable"<b>)</b> +</p> + +<p class=description> +Returns a filter that breaks data into lines. +</p> + +<p class=parameters> +The "<tt>text</tt>" line-wrap filter simply breaks text into lines by +inserting CRLF end-of-line markers at appropriate positions. +<tt>Length</tt> defaults 76. +The "<tt>base64</tt>" line-wrap filter works just like the default +"<tt>text</tt>" line-wrap filter with default length. +The function can also wrap "<tt>quoted-printable</tt>" lines, taking care +not to break lines in the middle of an escaped character. In that case, the +line length is fixed at 76. +</p> + +<p class=note> +For example, to create an encoding filter for the Quoted-Printable transfer content encoding of text data, do the following: +</p> + +<pre class=example> +qp = ltn12.filter.chain( + mime.normalize(), + mime.encode("quoted-printable"), + mime.wrap("quoted-printable") +) +</pre> + +<p class=note> +Note: To break into lines with a different end-of-line convention, apply +a normalization filter after the line break filter. +</p> + +<!-- Low-level ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<h3 id=low>Low-level filters</h3> + +<!-- b64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id="b64"> +A, B = mime.<b>b64(</b>C [, D]<b>)</b> +</p> + +<p class=description> +Low-level filter to perform Base64 encoding. +</p> + +<p class=description> +<tt>A</tt> is the encoded version of the largest prefix of +<tt>C..D</tt> +that can be encoded unambiguously. <tt>B</tt> has the remaining bytes of +<tt>C..D</tt>, <em>before</em> encoding. +If <tt>D</tt> is <tt><b>nil</b></tt>, <tt>A</tt> is padded with +the encoding of the remaining bytes of <tt>C</tt>. +</p> + +<p class=note> +Note: The simplest use of this function is to encode a string into it's +Base64 transfer content encoding. Notice the extra parenthesis around the +call to <tt>mime.b64</tt>, to discard the second return value. +</p> + +<pre class=example> +print((mime.b64("diego:password"))) +--> ZGllZ286cGFzc3dvcmQ= +</pre> + +<!-- dot +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> +<p class=name id="dot"> +A, n = mime.<b>dot(</b>m [, B]<b>)</b> +</p> + +<p class=description> +Low-level filter to perform SMTP stuffing and enable transmission of +messages containing the sequence "CRLF.CRLF". +</p> + +<p class=parameters> +<tt>A</tt> is the stuffed version of <tt>B</tt>. '<tt>n</tt>' gives the +number of characters from the sequence CRLF seen in the end of <tt>B</tt>. +'<tt>m</tt>' should tell the same, but for the previous chunk. +</p> + +<p class=note>Note: The message body is defined to begin with +an implicit CRLF. Therefore, to stuff a message correctly, the +first <tt>m</tt> should have the value 2. +</p> + +<pre class=example> +print((string.gsub(mime.dot(2, ".\r\nStuffing the message.\r\n.\r\n."), "\r\n", "\\n"))) +--> ..\nStuffing the message.\n..\n.. +</pre> + +<p class=note> +Note: The <a href=smtp.html#send><tt>smtp.send</tt></a> function +uses this filter automatically. You don't need to +apply it again. +</p> + +<!-- eol ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id="eol"> +A, B = mime.<b>eol(</b>C [, D, marker]<b>)</b> +</p> + +<p class=description> +Low-level filter to perform end-of-line marker translation. +For each chunk, the function needs to know if the last character of the +previous chunk could be part of an end-of-line marker or not. This is the +context the function receives besides the chunk. An updated version of +the context is returned after each new chunk. +</p> + +<p class=parameters> +<tt>A</tt> is the translated version of <tt>D</tt>. <tt>C</tt> is the +ASCII value of the last character of the previous chunk, if it was a +candidate for line break, or 0 otherwise. +<tt>B</tt> is the same as <tt>C</tt>, but for the current +chunk. <tt>Marker</tt> gives the new end-of-line marker and defaults to CRLF. +</p> + +<pre class=example> +-- translates the end-of-line marker to UNIX +unix = mime.eol(0, dos, "\n") +</pre> + +<!-- qp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id="qp"> +A, B = mime.<b>qp(</b>C [, D, marker]<b>)</b> +</p> + +<p class=description> +Low-level filter to perform Quoted-Printable encoding. +</p> + +<p class=parameters> +<tt>A</tt> is the encoded version of the largest prefix of +<tt>C..D</tt> +that can be encoded unambiguously. <tt>B</tt> has the remaining bytes of +<tt>C..D</tt>, <em>before</em> encoding. +If <tt>D</tt> is <tt><b>nil</b></tt>, <tt>A</tt> is padded with +the encoding of the remaining bytes of <tt>C</tt>. +Throughout encoding, occurrences of CRLF are replaced by the +<tt>marker</tt>, which itself defaults to CRLF. +</p> + +<p class=note> +Note: The simplest use of this function is to encode a string into it's +Quoted-Printable transfer content encoding. +Notice the extra parenthesis around the call to <tt>mime.qp</tt>, to discard the second return value. +</p> + +<pre class=example> +print((mime.qp("maçã"))) +--> ma=E7=E3= +</pre> + +<!-- qpwrp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id="qpwrp"> +A, m = mime.<b>qpwrp(</b>n [, B, length]<b>)</b> +</p> + +<p class=description> +Low-level filter to break Quoted-Printable text into lines. +</p> + +<p class=parameters> +<tt>A</tt> is a copy of <tt>B</tt>, broken into lines of at most +<tt>length</tt> bytes (defaults to 76). +'<tt>n</tt>' should tell how many bytes are left for the first +line of <tt>B</tt> and '<tt>m</tt>' returns the number of bytes +left in the last line of <tt>A</tt>. +</p> + +<p class=note> +Note: Besides breaking text into lines, this function makes sure the line +breaks don't fall in the middle of an escaped character combination. Also, +this function only breaks lines that are bigger than <tt>length</tt> bytes. +</p> + +<!-- unb64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id="unb64"> +A, B = mime.<b>unb64(</b>C [, D]<b>)</b> +</p> + +<p class=description> +Low-level filter to perform Base64 decoding. +</p> + +<p class=parameters> +<tt>A</tt> is the decoded version of the largest prefix of +<tt>C..D</tt> +that can be decoded unambiguously. <tt>B</tt> has the remaining bytes of +<tt>C..D</tt>, <em>before</em> decoding. +If <tt>D</tt> is <tt><b>nil</b></tt>, <tt>A</tt> is the empty string +and <tt>B</tt> returns whatever couldn't be decoded. +</p> + +<p class=note> +Note: The simplest use of this function is to decode a string from it's +Base64 transfer content encoding. +Notice the extra parenthesis around the call to <tt>mime.unqp</tt>, to discard the second return value. +</p> + +<pre class=example> +print((mime.unb64("ZGllZ286cGFzc3dvcmQ="))) +--> diego:password +</pre> + +<!-- unqp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id="unqp"> +A, B = mime.<b>unqp(</b>C [, D]<b>)</b> +</p> + +<p class=description> +Low-level filter to remove the Quoted-Printable transfer content encoding +from data. +</p> + +<p class=parameters> +<tt>A</tt> is the decoded version of the largest prefix of +<tt>C..D</tt> +that can be decoded unambiguously. <tt>B</tt> has the remaining bytes of +<tt>C..D</tt>, <em>before</em> decoding. +If <tt>D</tt> is <tt><b>nil</b></tt>, <tt>A</tt> is augmented with +the encoding of the remaining bytes of <tt>C</tt>. +</p> + +<p class=note> +Note: The simplest use of this function is to decode a string from it's +Quoted-Printable transfer content encoding. +Notice the extra parenthesis around the call to <tt>mime.unqp</tt>, to discard the second return value. +</p> + +<pre class=example> +print((mime.qp("ma=E7=E3="))) +--> maçã +</pre> + +<!-- wrp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id="wrp"> +A, m = mime.<b>wrp(</b>n [, B, length]<b>)</b> +</p> + +<p class=description> +Low-level filter to break text into lines with CRLF marker. +Text is assumed to be in the <a href=#normalize><tt>normalize</tt></a> form. +</p> + +<p class=parameters> +<tt>A</tt> is a copy of <tt>B</tt>, broken into lines of at most +<tt>length</tt> bytes (defaults to 76). +'<tt>n</tt>' should tell how many bytes are left for the first +line of <tt>B</tt> and '<tt>m</tt>' returns the number of bytes +left in the last line of <tt>A</tt>. +</p> + +<p class=note> +Note: This function only breaks lines that are bigger than +<tt>length</tt> bytes. The resulting line length does not include the CRLF +marker. +</p> + + +<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<div class=footer> +<hr> +<center> +<p class=bar> +<a href="home.html">home</a> · +<a href="home.html#down">download</a> · +<a href="installation.html">installation</a> · +<a href="introduction.html">introduction</a> · +<a href="reference.html">reference</a> +</p> +<p> +<small> +Last modified by Diego Nehab on <br> +Thu Apr 20 00:25:44 EDT 2006 +</small> +</p> +</center> +</div> + +</body> +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/reference.css Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,54 @@ +body { + margin-left: 1em; + margin-right: 1em; + font-family: "Verdana", sans-serif; +} + +tt { + font-family: "Andale Mono", monospace; +} + +h1, h2, h3, h4 { margin-left: 0em; } + + +h3 { padding-top: 1em; } + +p { margin-left: 1em; } + +p.name { + font-family: "Andale Mono", monospace; + padding-top: 1em; + margin-left: 0em; +} + +a[href] { color: #00007f; } + +blockquote { margin-left: 3em; } + +pre.example { + background: #ccc; + padding: 1em; + margin-left: 1em; + font-family: "Andale Mono", monospace; + font-size: small; +} + +hr { + margin-left: 0em; + background: #00007f; + border: 0px; + height: 1px; +} + +ul { list-style-type: disc; } + +table.index { border: 1px #00007f; } +table.index td { text-align: left; vertical-align: top; } +table.index ul { padding-top: 0em; margin-top: 0em; } + +h1:first-letter, +h2:first-letter, +h2:first-letter, +h3:first-letter { color: #00007f; } + +div.header, div.footer { margin-left: 0em; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/reference.html Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,239 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> +<html> + +<head> +<meta name="description" content="LuaSocket: Index to reference manual"> +<meta name="keywords" content="Lua, LuaSocket, Index, Manual, Network, Library, +Support, Manual"> +<title>LuaSocket: Index to reference manual</title> +<link rel="stylesheet" href="reference.css" type="text/css"> +</head> + +<body> + +<!-- header ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<div class=header> +<hr> +<center> +<table summary="LuaSocket logo"> +<tr><td align=center><a href="http://www.lua.org"> +<img width=128 height=128 border=0 alt="LuaSocket" src="luasocket.png"> +</a></td></tr> +<tr><td align=center valign=top>Network support for the Lua language +</td></tr> +</table> +<p class=bar> +<a href="home.html">home</a> · +<a href="home.html#download">download</a> · +<a href="installation.html">installation</a> · +<a href="introduction.html">introduction</a> · +<a href="reference.html">reference</a> +</p> +</center> +<hr> +</div> + +<!-- reference +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<h2>Reference</h2> + +<blockquote> +<a href="dns.html">DNS (in socket)</a> +<blockquote> +<a href="dns.html#toip">toip</a>, +<a href="dns.html#tohostname">tohostname</a>, +<a href="dns.html#gethostname">gethostname</a>. +</blockquote> +</blockquote> + +<!-- ftp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<blockquote> +<a href="ftp.html">FTP</a> +<blockquote> +<a href="ftp.html#get">get</a>, +<a href="ftp.html#put">put</a>. +</blockquote> +</blockquote> + +<!-- http +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<blockquote> +<a href="http.html">HTTP</a> +<blockquote> +<a href="http.html#request">request</a>. +</blockquote> +</blockquote> + +<!-- ltn12 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<blockquote> +<a href="ltn12.html">LTN12</a> +<blockquote> +<a href="ltn12.html#filter">filter</a>: +<a href="ltn12.html#filter.chain">chain</a>, +<a href="ltn12.html#filter.cycle">cycle</a>. +</blockquote> +<blockquote> +<a href="ltn12.html#pump">pump</a>: +<a href="ltn12.html#pump.all">all</a>, +<a href="ltn12.html#pump.step">step</a>. +</blockquote> +<blockquote> +<a href="ltn12.html#sink">sink</a>: +<a href="ltn12.html#sink.chain">chain</a>, +<a href="ltn12.html#sink.error">error</a>, +<a href="ltn12.html#sink.file">file</a>, +<a href="ltn12.html#sink.null">null</a>, +<a href="ltn12.html#sink.simplify">simplify</a>, +<a href="ltn12.html#sink.table">table</a>. +</blockquote> +<blockquote> +<a href="ltn12.html#source">source</a>: +<a href="ltn12.html#source.cat">cat</a>, +<a href="ltn12.html#source.chain">chain</a>, +<a href="ltn12.html#source.empty">empty</a>, +<a href="ltn12.html#source.error">error</a>, +<a href="ltn12.html#source.file">file</a>, +<a href="ltn12.html#source.simplify">simplify</a>, +<a href="ltn12.html#source.string">string</a>. +</blockquote> +</blockquote> + +<!-- mime +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<blockquote> +<a href="mime.html">MIME</a> +<blockquote> +<a href="mime.html#high">high-level</a>: +<a href="mime.html#normalize">normalize</a>, +<a href="mime.html#decode">decode</a>, +<a href="mime.html#encode">encode</a>, +<a href="mime.html#stuff">stuff</a>, +<a href="mime.html#wrap">wrap</a>. +</blockquote> +<blockquote> +<a href="mime.html#low">low-level</a>: +<a href="mime.html#b64">b64</a>, +<a href="mime.html#dot">dot</a>, +<a href="mime.html#eol">eol</a>, +<a href="mime.html#qp">qp</a>, +<a href="mime.html#wrp">wrp</a>, +<a href="mime.html#qpwrp">qpwrp</a>. +<a href="mime.html#unb64">unb64</a>, +<a href="mime.html#unqp">unqp</a>, +</blockquote> +</blockquote> + +<!-- smtp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<blockquote> +<a href="smtp.html">SMTP</a> +<blockquote> +<a href="smtp.html#message">message</a>, +<a href="smtp.html#send">send</a>. +</blockquote> +</blockquote> + +<!-- socket +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<blockquote> +<a href="socket.html">Socket</a> +<blockquote> +<a href="socket.html#debug">_DEBUG</a>, +<a href="dns.html#dns">dns</a>, +<a href="socket.html#gettime">gettime</a>, +<a href="socket.html#newtry">newtry</a>, +<a href="socket.html#protect">protect</a>, +<a href="socket.html#select">select</a>, +<a href="socket.html#sink">sink</a>, +<a href="socket.html#skip">skip</a>, +<a href="socket.html#sleep">sleep</a>, +<a href="socket.html#source">source</a>, +<a href="tcp.html#tcp">tcp</a>, +<a href="socket.html#try">try</a>, +<a href="udp.html#udp">udp</a>, +<a href="socket.html#version">_VERSION</a>. +</blockquote> +</blockquote> + +<!-- tcp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<blockquote> +<a href="tcp.html">TCP (in socket)</a> +<blockquote> +<a href="tcp.html#accept">accept</a>, +<a href="tcp.html#bind">bind</a>, +<a href="tcp.html#close">close</a>, +<a href="tcp.html#connect">connect</a>, +<a href="tcp.html#getpeername">getpeername</a>, +<a href="tcp.html#getsockname">getsockname</a>, +<a href="tcp.html#getstats">getstats</a>, +<a href="tcp.html#receive">receive</a>, +<a href="tcp.html#send">send</a>, +<a href="tcp.html#setoption">setoption</a>, +<a href="tcp.html#setstats">setstats</a>, +<a href="tcp.html#settimeout">settimeout</a>, +<a href="tcp.html#shutdown">shutdown</a>. +</blockquote> +</blockquote> + +<!-- udp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<blockquote> +<a href="udp.html">UDP (in socket)</a> +<blockquote> +<a href="udp.html#close">close</a>, +<a href="udp.html#getpeername">getpeername</a>, +<a href="udp.html#getsockname">getsockname</a>, +<a href="udp.html#receive">receive</a>, +<a href="udp.html#receivefrom">receivefrom</a>, +<a href="udp.html#send">send</a>, +<a href="udp.html#sendto">sendto</a>, +<a href="udp.html#setpeername">setpeername</a>, +<a href="udp.html#setsockname">setsockname</a>, +<a href="udp.html#setoption">setoption</a>, +<a href="udp.html#settimeout">settimeout</a>. +</blockquote> +</blockquote> + +<!-- url ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<blockquote> +<a href="url.html">URL</a> +<blockquote> +<a href="url.html#absolute">absolute</a>, +<a href="url.html#build">build</a>, +<a href="url.html#build_path">build_path</a>, +<a href="url.html#escape">escape</a>, +<a href="url.html#parse">parse</a>, +<a href="url.html#parse_path">parse_path</a>, +<a href="url.html#unescape">unescape</a>. +</blockquote> +</blockquote> + +<!-- footer ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<div class=footer> +<hr> +<center> +<p class=bar> +<a href="home.html">home</a> · +<a href="home.html#down">download</a> · +<a href="installation.html">installation</a> · +<a href="introduction.html">introduction</a> · +<a href="reference.html">reference</a> +</p> +<p> +<small> +Last modified by Diego Nehab on <br> +Thu Apr 20 00:25:47 EDT 2006 +</small> +</p> +</center> +</div> + +</body> +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/smtp.html Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,417 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> +<html> + +<head> +<meta name="description" content="LuaSocket: SMTP support"> +<meta name="keywords" content="Lua, LuaSocket, SMTP, E-Mail, MIME, Multipart, +Library, Support"> +<title>LuaSocket: SMTP support</title> +<link rel="stylesheet" href="reference.css" type="text/css"> +</head> + +<body> + +<!-- header +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<div class=header> +<hr> +<center> +<table summary="LuaSocket logo"> +<tr><td align=center><a href="http://www.lua.org"> +<img width=128 height=128 border=0 alt="LuaSocket" src="luasocket.png"> +</a></td></tr> +<tr><td align=center valign=top>Network support for the Lua language +</td></tr> +</table> +<p class=bar> +<a href="home.html">home</a> · +<a href="home.html#download">download</a> · +<a href="installation.html">installation</a> · +<a href="introduction.html">introduction</a> · +<a href="reference.html">reference</a> +</p> +</center> +<hr> +</div> + +<!-- smtp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<h2 id=smtp>SMTP</h2> + +<p> The <tt>smtp</tt> namespace provides functionality to send e-mail +messages. The high-level API consists of two functions: one to +define an e-mail message, and another to actually send the message. +Although almost all users will find that these functions provide more than +enough functionality, the underlying implementation allows for even more +control (if you bother to read the code). +</p> + +<p>The implementation conforms to the Simple Mail Transfer Protocol, +<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2821.txt">RFC 2821</a>. +Another RFC of interest is <a +href="http://www.cs.princeton.edu/~diego/rfc/rfc2822.txt">RFC 2822</a>, +which governs the Internet Message Format. +Multipart messages (those that contain attachments) are part +of the MIME standard, but described mainly +in <a href="http://www.cs.princeton.edu/~diego/rfc/rfc2046.txt">RFC +2046</a> + +<p> In the description below, good understanding of <a +href="http://lua-users.org/wiki/FiltersSourcesAndSinks"> LTN012, Filters +sources and sinks</a> and the <a href=mime.html>MIME</a> module is +assumed. In fact, the SMTP module was the main reason for their +creation. </p> + +<p> +To obtain the <tt>smtp</tt> namespace, run: +</p> + +<pre class=example> +-- loads the SMTP module and everything it requires +local smtp = require("socket.smtp") +</pre> + +<p> +MIME headers are represented as a Lua table in the form: +</p> + +<blockquote> +<table summary="MIME headers in Lua table"> +<tr><td><tt> +headers = {<br> + field-1-name = <i>field-1-value</i>,<br> + field-2-name = <i>field-2-value</i>,<br> + field-3-name = <i>field-3-value</i>,<br> + ...<br> + field-n-name = <i>field-n-value</i><br> +} +</tt></td></tr> +</table> +</blockquote> + +<p> +Field names are case insensitive (as specified by the standard) and all +functions work with lowercase field names. +Field values are left unmodified. +</p> + +<p class=note> +Note: MIME headers are independent of order. Therefore, there is no problem +in representing them in a Lua table. +</p> + +<p> +The following constants can be set to control the default behavior of +the SMTP module: +</p> + +<ul> +<li> <tt>DOMAIN</tt>: domain used to greet the server; +<li> <tt>PORT</tt>: default port used for the connection; +<li> <tt>SERVER</tt>: default server used for the connection; +<li> <tt>TIMEOUT</tt>: default timeout for all I/O operations; +<li> <tt>ZONE</tt>: default time zone. +</ul> + +<!-- send +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=send> +smtp.<b>send{</b><br> + from = <i>string</i>,<br> + rcpt = <i>string</i> or <i>string-table</i>,<br> + source = <i>LTN12 source</i>,<br> + [user = <i>string</i>,]<br> + [password = <i>string</i>,]<br> + [server = <i>string</i>,]<br> + [port = <i>number</i>,]<br> + [domain = <i>string</i>,]<br> + [step = <i>LTN12 pump step</i>,]<br> + [create = <i>function</i>]<br> +<b>}</b> +</p> + +<p class=description> +Sends a message to a recipient list. Since sending messages is not as +simple as downloading an URL from a FTP or HTTP server, this function +doesn't have a simple interface. However, see the +<a href=#message><tt>message</tt></a> source factory for +a very powerful way to define the message contents. +</p> + + +<p class=parameters> +The sender is given by the e-mail address in the <tt>from</tt> field. +<tt>Rcpt</tt> is a Lua table with one entry for each recipient e-mail +address, or a string +in case there is just one recipient. +The contents of the message are given by a <em>simple</em> +<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a> +<tt>source</tt>. Several arguments are optional: +</p> +<ul> +<li> <tt>user</tt>, <tt>password</tt>: User and password for +authentication. The function will attempt LOGIN and PLAIN authentication +methods if supported by the server (both are unsafe); +<li> <tt>server</tt>: Server to connect to. Defaults to "localhost"; +<li> <tt>port</tt>: Port to connect to. Defaults to 25; +<li> <tt>domain</tt>: Domain name used to greet the server; Defaults to the +local machine host name; +<li> <tt>step</tt>: +<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a> +pump step function used to pass data from the +source to the server. Defaults to the LTN12 <tt>pump.step</tt> function; +<li><tt>create</tt>: An optional function to be used instead of +<a href=tcp.html#socket.tcp><tt>socket.tcp</tt></a> when the communications socket is created. +</ul> + +<p class=return> +If successful, the function returns 1. Otherwise, the function returns +<b><tt>nil</tt></b> followed by an error message. +</p> + +<p class=note> +Note: SMTP servers can be very picky with the format of e-mail +addresses. To be safe, use only addresses of the form +"<tt><fulano@example.com></tt>" in the <tt>from</tt> and +<tt>rcpt</tt> arguments to the <tt>send</tt> function. In headers, e-mail +addresses can take whatever form you like. </p> + +<p class=note> +Big note: There is a good deal of misconception with the use of the +destination address field headers, i.e., the '<tt>To</tt>', '<tt>Cc</tt>', +and, more importantly, the '<tt>Bcc</tt>' headers. Do <em>not</em> add a +'<tt>Bcc</tt>' header to your messages because it will probably do the +exact opposite of what you expect. +</p> + +<p class=note> +Only recipients specified in the <tt>rcpt</tt> list will receive a copy of the +message. Each recipient of an SMTP mail message receives a copy of the +message body along with the headers, and nothing more. The headers +<em>are</em> part of the message and should be produced by the +<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a> +<tt>source</tt> function. The <tt>rcpt</tt> list is <em>not</em> +part of the message and will not be sent to anyone. +</p> + +<p class=note> +<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2822.txt">RFC 2822</a> +has two <em>important and short</em> sections, "3.6.3. Destination address +fields" and "5. Security considerations", explaining the proper +use of these headers. Here is a summary of what it says: +</p> + +<ul> +<li> <tt>To</tt>: contains the address(es) of the primary recipient(s) +of the message; +<li> <tt>Cc</tt>: (where the "Cc" means "Carbon Copy" in the sense of +making a copy on a typewriter using carbon paper) contains the +addresses of others who are to receive the message, though the +content of the message may not be directed at them; +<li> <tt>Bcc</tt>: (where the "Bcc" means "Blind Carbon +Copy") contains addresses of recipients of the message whose addresses are not to be revealed to other recipients of the message. +</ul> + +<p class=note> +The LuaSocket <tt>send</tt> function does not care or interpret the +headers you send, but it gives you full control over what is sent and +to whom it is sent: +</p> +<ul> +<li> If someone is to receive the message, the e-mail address <em>has</em> +to be in the recipient list. This is the only parameter that controls who +gets a copy of the message; +<li> If there are multiple recipients, none of them will automatically +know that someone else got that message. That is, the default behavior is +similar to the <tt>Bcc</tt> field of popular e-mail clients; +<li> It is up to you to add the <tt>To</tt> header with the list of primary +recipients so that other recipients can see it; +<li> It is also up to you to add the <tt>Cc</tt> header with the +list of additional recipients so that everyone else sees it; +<li> Adding a header <tt>Bcc</tt> is nonsense, unless it is +empty. Otherwise, everyone receiving the message will see it and that is +exactly what you <em>don't</em> want to happen! +</ul> + +<p class=note> +I hope this clarifies the issue. Otherwise, please refer to +<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2821.txt">RFC 2821</a> +and +<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2822.txt">RFC 2822</a>. +</p> + +<pre class=example> +-- load the smtp support +local smtp = require("socket.smtp") + +-- Connects to server "localhost" and sends a message to users +-- "fulano@example.com", "beltrano@example.com", +-- and "sicrano@example.com". +-- Note that "fulano" is the primary recipient, "beltrano" receives a +-- carbon copy and neither of them knows that "sicrano" received a blind +-- carbon copy of the message. +from = "<luasocket@example.com>" + +rcpt = { + "<fulano@example.com>", + "<beltrano@example.com>", + "<sicrano@example.com>" +} + +mesgt = { + headers = { + to = "Fulano da Silva <fulano@example.com>", + cc = '"Beltrano F. Nunes" <beltrano@example.com>', + subject = "My first message" + }, + body = "I hope this works. If it does, I can send you another 1000 copies." +} + +r, e = smtp.send{ + from = from, + rcpt = rcpt, + source = smtp.message(mesgt) +} +</pre> + +<!-- message ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=message> +smtp.<b>message(</b>mesgt<b>)</b> +</p> + +<p class=description> +Returns a <em>simple</em> +<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a> source that sends an SMTP message body, possibly multipart (arbitrarily deep). +</p> + +<p class=parameters> +The only parameter of the function is a table describing the message. +<tt>Mesgt</tt> has the following form (notice the recursive structure): +</p> + +<blockquote> +<table summary="Mesgt table structure"> +<tr><td><tt> +mesgt = {<br> + headers = <i>header-table</i>,<br> + body = <i>LTN12 source</i> or <i>string</i> or +<i>multipart-mesgt</i><br> +}<br> + <br> +multipart-mesgt = {<br> + [preamble = <i>string</i>,]<br> + [1] = <i>mesgt</i>,<br> + [2] = <i>mesgt</i>,<br> + ...<br> + [<i>n</i>] = <i>mesgt</i>,<br> + [epilogue = <i>string</i>,]<br> +}<br> +</tt></td></tr> +</table> +</blockquote> + +<p class=parameters> +For a simple message, all that is needed is a set of <tt>headers</tt> +and the <tt>body</tt>. The message <tt>body</tt> can be given as a string +or as a <em>simple</em> +<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a> +source. For multipart messages, the body is a table that +recursively defines each part as an independent message, plus an optional +<tt>preamble</tt> and <tt>epilogue</tt>. +</p> + +<p class=return> +The function returns a <em>simple</em> +<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a> +source that produces the +message contents as defined by <tt>mesgt</tt>, chunk by chunk. +Hopefully, the following +example will make things clear. When in doubt, refer to the appropriate RFC +as listed in the introduction. </p> + +<pre class=example> +-- load the smtp support and its friends +local smtp = require("socket.smtp") +local mime = require("mime") +local ltn12 = require("ltn12") + +-- 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{ + headers = { + -- Remember that headers are *ignored* by smtp.send. + from = "Sicrano de Oliveira <sicrano@example.com>", + to = "Fulano da Silva <fulano@example.com>", + subject = "Here is a message with attachments" + }, + body = { + preamble = "If your client doesn't understand attachments, \r\n" .. + "it will still display the preamble and the epilogue.\r\n" .. + "Preamble will probably appear even in a MIME enabled client.", + -- first part: no headers means plain text, us-ascii. + -- The mime.eol low-level filter normalizes end-of-line markers. + [1] = { + body = mime.eol(0, [[ + Lines in a message body should always end with CRLF. + The smtp module will *NOT* perform translation. However, the + send function *DOES* perform SMTP stuffing, whereas the message + function does *NOT*. + ]]) + }, + -- second part: headers describe content to be a png image, + -- sent under the base64 transfer content encoding. + -- notice that nothing happens until the message is actually sent. + -- small chunks are loaded into memory right before transmission and + -- translation happens on the fly. + [2] = { + headers = { + ["content-type"] = 'image/png; name="image.png"', + ["content-disposition"] = 'attachment; filename="image.png"', + ["content-description"] = 'a beautiful image', + ["content-transfer-encoding"] = "BASE64" + }, + body = ltn12.source.chain( + ltn12.source.file(io.open("image.png", "rb")), + ltn12.filter.chain( + mime.encode("base64"), + mime.wrap() + ) + ) + }, + epilogue = "This might also show up, but after the attachments" + } +} + +-- finally send it +r, e = smtp.send{ + from = "<sicrano@example.com>", + rcpt = "<fulano@example.com>", + source = source, +} +</pre> + +<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<div class=footer> +<hr> +<center> +<p class=bar> +<a href="home.html">home</a> · +<a href="home.html#down">download</a> · +<a href="installation.html">installation</a> · +<a href="introduction.html">introduction</a> · +<a href="reference.html">reference</a> +</p> +<p> +<small> +Last modified by Diego Nehab on <br> +Thu Apr 20 00:25:51 EDT 2006 +</small> +</p> +</center> +</div> + +</body> +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/socket.html Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,404 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> +<html> + +<head> +<meta name="description" content="LuaSocket: The core namespace"> +<meta name="keywords" content="Lua, LuaSocket, Socket, Network, Library, Support"> +<title>LuaSocket: The socket namespace</title> +<link rel="stylesheet" href="reference.css" type="text/css"> +</head> + +<body> + +<!-- header +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<div class=header> +<hr> +<center> +<table summary="LuaSocket logo"> +<tr><td align=center><a href="http://www.lua.org"> +<img width=128 height=128 border=0 alt="LuaSocket" src="luasocket.png"> +</a></td></tr> +<tr><td align=center valign=top>Network support for the Lua language +</td></tr> +</table> +<p class=bar> +<a href="home.html">home</a> · +<a href="home.html#download">download</a> · +<a href="installation.html">installation</a> · +<a href="introduction.html">introduction</a> · +<a href="reference.html">reference</a> +</p> +</center> +<hr> +</div> + +<!-- socket +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<h2 id=socket>The socket namespace</h2> + +<p> +The <tt>socket</tt> namespace contains the core functionality of LuaSocket. +</p> + +<p> +To obtain the <tt>socket</tt> namespace, run: +</p> + +<pre class=example> +-- loads the socket module +local socket = require("socket") +</pre> + +<!-- bind ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=bind> +socket.<b>bind(</b>address, port [, backlog]<b>)</b> +</p> + +<p class=description> +This function is a shortcut that creates and returns a TCP server object +bound to a local <tt>address</tt> and <tt>port</tt>, ready to +accept client connections. Optionally, +user can also specify the <tt>backlog</tt> argument to the +<a href=tcp.html#listen><tt>listen</tt></a> method (defaults to 32). +</p> + +<p class=note> +Note: The server object returned will have the option "<tt>reuseaddr</tt>" +set to <tt><b>true</b></tt>. +</p> + +<!-- connect ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=connect> +socket.<b>connect(</b>address, port [, locaddr, locport]<b>)</b> +</p> + +<p class=description> +This function is a shortcut that creates and returns a TCP client object +connected to a remote <tt>host</tt> at a given <tt>port</tt>. Optionally, +the user can also specify the local address and port to bind +(<tt>locaddr</tt> and <tt>locport</tt>). +</p> + +<!-- debug ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=debug> +socket.<b>_DEBUG</b> +</p> + +<p class=description> +This constant is set to <tt><b>true</b></tt> if the library was compiled +with debug support. +</p> + +<!-- newtry +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=newtry> +socket.<b>newtry(</b>finalizer<b>)</b> +</p> + +<p class=description> +Creates and returns a <em>clean</em> +<a href="#try"><tt>try</tt></a> +function that allows for cleanup before the exception +is raised. +</p> + +<p class=parameters> +<tt>Finalizer</tt> is a function that will be called before +<tt>try</tt> throws the exception. It will be called +in <em>protected</em> mode. +</p> + +<p class=return> +The function returns your customized <tt>try</tt> function. +</p> + +<p class=note> +Note: This idea saved a <em>lot</em> of work with the +implementation of protocols in LuaSocket: +</p> + +<pre class=example> +foo = socket.protect(function() + -- connect somewhere + local c = socket.try(socket.connect("somewhere", 42)) + -- create a try function that closes 'c' on error + local try = socket.newtry(function() c:close() end) + -- do everything reassured c will be closed + try(c:send("hello there?\r\n")) + local answer = try(c:receive()) + ... + try(c:send("good bye\r\n")) + c:close() +end) +</pre> + + +<!-- protect +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=protect> +socket.<b>protect(</b>func<b>)</b> +</p> + +<p class=description> +Converts a function that throws exceptions into a safe function. This +function only catches exceptions thrown by the <a href=#try><tt>try</tt></a> +and <a href=#newtry><tt>newtry</tt></a> functions. It does not catch normal +Lua errors. +</p> + +<p class=parameters> +<tt>Func</tt> is a function that calls +<a href=#try><tt>try</tt></a> (or <tt>assert</tt>, or <tt>error</tt>) +to throw exceptions. +</p> + +<p class=return> +Returns an equivalent function that instead of throwing exceptions, +returns <tt><b>nil</b></tt> followed by an error message. +</p> + +<p class=note> +Note: Beware that if your function performs some illegal operation that +raises an error, the protected function will catch the error and return it +as a string. This is because the <a href=#try><tt>try</tt></a> function +uses errors as the mechanism to throw exceptions. +</p> + +<!-- select +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=select> +socket.<b>select(</b>recvt, sendt [, timeout]<b>)</b> +</p> + +<p class=description> +Waits for a number of sockets to change status. +</p> + +<p class=parameters> +<tt>Recvt</tt> is an array with the sockets to test for characters +available for reading. Sockets in the <tt>sendt</tt> array are watched to +see if it is OK to immediately write on them. <tt>Timeout</tt> is the +maximum amount of time (in seconds) to wait for a change in status. A +<tt><b>nil</b></tt>, negative or omitted <tt>timeout</tt> value allows the +function to block indefinitely. <tt>Recvt</tt> and <tt>sendt</tt> can also +be empty tables or <tt><b>nil</b></tt>. Non-socket values (or values with +non-numeric indices) in the arrays will be silently ignored. +</p> + +<p class=return> The function returns a list with the sockets ready for +reading, a list with the sockets ready for writing and an error message. +The error message is "<tt>timeout</tt>" if a timeout condition was met and +<tt><b>nil</b></tt> otherwise. The returned tables are +doubly keyed both by integers and also by the sockets +themselves, to simplify the test if a specific socket has +changed status. +</p> + +<p class=note> +<b>Important note</b>: a known bug in WinSock causes <tt>select</tt> to fail +on non-blocking TCP sockets. The function may return a socket as +writable even though the socket is <em>not</em> ready for sending. +</p> + +<p class=note> +<b>Another important note</b>: calling select with a server socket in the receive parameter before a call to accept does <em>not</em> guarantee +<a href=tcp.html#accept><tt>accept</tt></a> will return immediately. +Use the <a href=tcp.html#settimeout><tt>settimeout</tt></a> +method or <tt>accept</tt> might block forever. +</p> + +<p class=note> +<b>Yet another note</b>: If you close a socket and pass +it to <tt>select</tt>, it will be ignored. +</p> + +<!-- sink ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=sink> +socket.<b>sink(</b>mode, socket<b>)</b> +</p> + +<p class=description> +Creates an +<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a> +sink from a stream socket object. +</p> + +<p class=parameters> +<tt>Mode</tt> defines the behavior of the sink. The following +options are available: +</p> +<ul> +<li> <tt>"http-chunked"</tt>: sends data through socket after applying the +<em>chunked transfer coding</em>, closing the socket when done; +<li> <tt>"close-when-done"</tt>: sends all received data through the +socket, closing the socket when done; +<li> <tt>"keep-open"</tt>: sends all received data through the +socket, leaving it open when done. +</ul> +<p> +<tt>Socket</tt> is the stream socket object used to send the data. +</p> + +<p class=return> +The function returns a sink with the appropriate behavior. +</p> + +<!-- skip ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=skip> +socket.<b>skip(</b>d [, ret<sub>1</sub>, ret<sub>2</sub> ... ret<sub>N</sub>]<b>)</b> +</p> + +<p class=description> +Drops a number of arguments and returns the remaining. +</p> + +<p class=parameters> +<tt>D</tt> is the number of arguments to drop. <tt>Ret<sub>1</sub></tt> to +<tt>ret<sub>N</sub></tt> are the arguments. +</p> + +<p class=return> +The function returns <tt>ret<sub>d+1</sub></tt> to <tt>ret<sub>N</sub></tt>. +</p> + +<p class=note> +Note: This function is useful to avoid creation of dummy variables: +</p> + +<pre class=example> +-- get the status code and separator from SMTP server reply +local code, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)")) +</pre> + +<!-- sleep ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=sleep> +socket.<b>sleep(</b>time<b>)</b> +</p> + +<p class=description> +Freezes the program execution during a given amount of time. +</p> + +<p class=parameters> +<tt>Time</tt> is the number of seconds to sleep for. +</p> + +<!-- source +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=source> +socket.<b>source(</b>mode, socket [, length]<b>)</b> +</p> + +<p class=description> +Creates an +<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a> +source from a stream socket object. +</p> + +<p class=parameters> +<tt>Mode</tt> defines the behavior of the source. The following +options are available: +</p> +<ul> +<li> <tt>"http-chunked"</tt>: receives data from socket and removes the +<em>chunked transfer coding</em> before returning the data; +<li> <tt>"by-length"</tt>: receives a fixed number of bytes from the +socket. This mode requires the extra argument <tt>length</tt>; +<li> <tt>"until-closed"</tt>: receives data from a socket until the other +side closes the connection. +</ul> +<p> +<tt>Socket</tt> is the stream socket object used to receive the data. +</p> + +<p class=return> +The function returns a source with the appropriate behavior. +</p> + +<!-- time ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=gettime> +socket.<b>gettime()</b> +</p> + +<p class=description> +Returns the time in seconds, relative to the origin of the +universe. You should subtract the values returned by this function +to get meaningful values. +</p> + +<pre class=example> +t = socket.gettime() +-- do stuff +print(socket.gettime() - t .. " seconds elapsed") +</pre> + +<!-- try ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=try> +socket.<b>try(</b>ret<sub>1</sub> [, ret<sub>2</sub> ... ret<sub>N</sub>]<b>)</b> +</p> + +<p class=description> +Throws an exception in case of error. The exception can only be caught +by the <a href=#protect><tt>protect</tt></a> function. It does not explode +into an error message. +</p> + +<p class=parameters> +<tt>Ret<sub>1</sub></tt> to <tt>ret<sub>N</sub></tt> can be arbitrary +arguments, but are usually the return values of a function call +nested with <tt>try</tt>. +</p> + +<p class=return> +The function returns <tt>ret</tt><sub>1</sub> to <tt>ret</tt><sub>N</sub> if +<tt>ret</tt><sub>1</sub> is not <tt><b>nil</b></tt>. Otherwise, it calls <tt>error</tt> passing <tt>ret</tt><sub>2</sub>. +</p> + +<pre class=example> +-- connects or throws an exception with the appropriate error message +c = socket.try(socket.connect("localhost", 80)) +</pre> + +<!-- version ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=version> +socket.<b>_VERSION</b> +</p> + +<p class=description> +This constant has a string describing the current LuaSocket version. +</p> + +<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<div class=footer> +<hr> +<center> +<p class=bar> +<a href="home.html">home</a> · +<a href="home.html#down">download</a> · +<a href="installation.html">installation</a> · +<a href="introduction.html">introduction</a> · +<a href="reference.html">reference</a> +</p> +<p> +<small> +Last modified by Diego Nehab on <br> +Thu Apr 20 00:25:54 EDT 2006 +</small> +</p> +</center> +</div> + +</body> +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/tcp.html Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,533 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> +<html> + +<head> +<meta name="description" content="LuaSocket: The TCP/IP support"> +<meta name="keywords" content="Lua, LuaSocket, Socket, TCP, Library, Network, Support"> +<title>LuaSocket: TCP/IP support</title> +<link rel="stylesheet" href="reference.css" type="text/css"> +</head> + +<body> + +<!-- header +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<div class=header> +<hr> +<center> +<table summary="LuaSocket logo"> +<tr><td align=center><a href="http://www.lua.org"> +<img width=128 height=128 border=0 alt="LuaSocket" src="luasocket.png"> +</a></td></tr> +<tr><td align=center valign=top>Network support for the Lua language +</td></tr> +</table> +<p class=bar> +<a href="home.html">home</a> · +<a href="home.html#download">download</a> · +<a href="installation.html">installation</a> · +<a href="introduction.html">introduction</a> · +<a href="reference.html">reference</a> +</p> +</center> +<hr> +</div> + +<!-- tcp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<h2 id=tcp>TCP</h2> + +<!-- socket.tcp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=socket.tcp> +socket.<b>tcp()</b> +</p> + +<p class=description> +Creates and returns a TCP master object. A master object can +be transformed into a server object with the method +<a href=#listen><tt>listen</tt></a> (after a call to <a +href=#bind><tt>bind</tt></a>) or into a client object with +the method <a href=#connect><tt>connect</tt></a>. The only other +method supported by a master object is the +<a href=#close><tt>close</tt></a> method.</p> + +<p class=return> +In case of success, a new master object is returned. In case of error, +<b><tt>nil</tt></b> is returned, followed by an error message. +</p> + +<!-- accept +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=accept> +server:<b>accept()</b> +</p> + +<p class=description> +Waits for a remote connection on the server +object and returns a client object representing that connection. +</p> + +<p class=return> +If a connection is successfully initiated, a client object is returned. +If a timeout condition is met, the method returns <b><tt>nil</tt></b> +followed by the error string '<tt>timeout</tt>'. Other errors are +reported by <b><tt>nil</tt></b> followed by a message describing the error. +</p> + +<p class=note> +Note: calling <a href=socket.html#select><tt>socket.select</tt></a> +with a server object in +the <tt>recvt</tt> parameter before a call to <tt>accept</tt> does +<em>not</em> guarantee <tt>accept</tt> will return immediately. Use the <a +href=#settimeout><tt>settimeout</tt></a> method or <tt>accept</tt> +might block until <em>another</em> client shows up. +</p> + +<!-- bind +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=bind> +master:<b>bind(</b>address, port<b>)</b> +</p> + +<p class=description> +Binds a master object to <tt>address</tt> and <tt>port</tt> on the +local host. + +<p class=parameters> +<tt>Address</tt> can be an IP address or a host name. +<tt>Port</tt> must be an integer number in the range [0..64K). +If <tt>address</tt> +is '<tt>*</tt>', the system binds to all local interfaces +using the <tt>INADDR_ANY</tt> constant. If <tt>port</tt> is 0, the system automatically +chooses an ephemeral port. +</p> + +<p class=return> +In case of success, the method returns 1. In case of error, the +method returns <b><tt>nil</tt></b> followed by an error message. +</p> + +<p class=note> +Note: The function <a href=socket.html#bind><tt>socket.bind</tt></a> +is available and is a shortcut for the creation of server sockets. +</p> + +<!-- close ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=close> +master:<b>close()</b><br> +client:<b>close()</b><br> +server:<b>close()</b> +</p> + +<p class=description> +Closes a TCP object. The internal socket used by the object is closed +and the local address to which the object was +bound is made available to other applications. No further operations +(except for further calls to the <tt>close</tt> method) are allowed on +a closed socket. +</p> + +<p class=note> +Note: It is important to close all used sockets once they are not +needed, since, in many systems, each socket uses a file descriptor, +which are limited system resources. Garbage-collected objects are +automatically closed before destruction, though. +</p> + +<!-- connect ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=connect> +master:<b>connect(</b>address, port<b>)</b> +</p> + +<p class=description> +Attempts to connect a master object to a remote host, transforming it into a +client object. +Client objects support methods +<a href=#send><tt>send</tt></a>, +<a href=#receive><tt>receive</tt></a>, +<a href=#getsockname><tt>getsockname</tt></a>, +<a href=#getpeername><tt>getpeername</tt></a>, +<a href=#settimeout><tt>settimeout</tt></a>, +and <a href=#close><tt>close</tt></a>. +</p> + +<p class=parameters> +<tt>Address</tt> can be an IP address or a host name. +<tt>Port</tt> must be an integer number in the range [1..64K). +</p> + +<p class=return> +In case of error, the method returns <b><tt>nil</tt></b> followed by a string +describing the error. In case of success, the method returns 1. +</p> + +<p class=note> +Note: The function <a href=socket.html#connect><tt>socket.connect</tt></a> +is available and is a shortcut for the creation of client sockets. +</p> + +<p class=note> +Note: Starting with LuaSocket 2.0, +the <a href=#settimeout><tt>settimeout</tt></a> +method affects the behavior of <tt>connect</tt>, causing it to return +with an error in case of a timeout. If that happens, you can still call <a +href=socket.html#select><tt>socket.select</tt></a> with the socket in the +<tt>sendt</tt> table. The socket will be writable when the connection is +established. +</p> + +<!-- getpeername ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=getpeername> +client:<b>getpeername()</b> +</p> + +<p class=description> +Returns information about the remote side of a connected client object. +</p> + +<p class=return> +Returns a string with the IP address of the peer, followed by the +port number that peer is using for the connection. +In case of error, the method returns <b><tt>nil</tt></b>. +</p> + +<p class=note> +Note: It makes no sense to call this method on server objects. +</p> + +<!-- getsockname ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=getsockname> +master:<b>getsockname()</b><br> +client:<b>getsockname()</b><br> +server:<b>getsockname()</b> +</p> + +<p class=description> +Returns the local address information associated to the object. +</p> + +<p class=return> +The method returns a string with local IP address and a number with +the port. In case of error, the method returns <b><tt>nil</tt></b>. +</p> + +<!-- getstats +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=getstats> +master:<b>getstats()</b><br> +client:<b>getstats()</b><br> +server:<b>getstats()</b><br> +</p> + +<p class=description> +Returns accounting information on the socket, useful for throttling +of bandwidth. +</p> + +<p class=return> +The method returns the number of bytes received, the number of bytes sent, +and the age of the socket object in seconds. +</p> + +<!-- listen ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=listen> +master:<b>listen(</b>backlog<b>)</b> +</p> + +<p class=description> +Specifies the socket is willing to receive connections, transforming the +object into a server object. Server objects support the +<a href=#accept><tt>accept</tt></a>, +<a href=#getsockname><tt>getsockname</tt></a>, +<a href=#setoption><tt>setoption</tt></a>, +<a href=#settimeout><tt>settimeout</tt></a>, +and <a href=#close><tt>close</tt></a> methods. +</p> + +<p class=parameters> +The parameter <tt>backlog</tt> specifies the number of client +connections that can +be queued waiting for service. If the queue is full and another client +attempts connection, the connection is refused. +</p> + +<p class=return> +In case of success, the method returns 1. In case of error, the +method returns <b><tt>nil</tt></b> followed by an error message. +</p> + +<!-- receive ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=receive> +client:<b>receive(</b>[pattern [, prefix]]<b>)</b> +</p> + +<p class=description> +Reads data from a client object, according to the specified <em>read +pattern</em>. Patterns follow the Lua file I/O format, and the difference in performance between all patterns is negligible. +</p> + +<p class=parameters> +<tt>Pattern</tt> can be any of the following: +</p> + +<ul> +<li> '<tt>*a</tt>': reads from the socket until the connection is +closed. No end-of-line translation is performed; +<li> '<tt>*l</tt>': reads a line of text from the socket. The line is +terminated by a LF character (ASCII 10), optionally preceded by a +CR character (ASCII 13). The CR and LF characters are not included in +the returned line. In fact, <em>all</em> CR characters are +ignored by the pattern. This is the default pattern; +<li> <tt>number</tt>: causes the method to read a specified <tt>number</tt> +of bytes from the socket. +</ul> + +<p class=parameters> +<tt>Prefix</tt> is an optional string to be concatenated to the beginning +of any received data before return. +</p> + +<p class=return> +If successful, the method returns the received pattern. In case of error, +the method returns <tt><b>nil</b></tt> followed by an error message which +can be the string '<tt>closed</tt>' in case the connection was +closed before the transmission was completed or the string +'<tt>timeout</tt>' in case there was a timeout during the operation. +Also, after the error message, the function returns the partial result of +the transmission. +</p> + +<p class=note> +<b>Important note</b>: This function was changed <em>severely</em>. It used +to support multiple patterns (but I have never seen this feature used) and +now it doesn't anymore. Partial results used to be returned in the same +way as successful results. This last feature violated the idea that all +functions should return <tt><b>nil</b></tt> on error. Thus it was changed +too. +</p> + +<!-- send +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=send> +client:<b>send(</b>data [, i [, j]]<b>)</b> +</p> + +<p class=description> +Sends <tt>data</tt> through client object. +</p> + +<p class=parameters> +<tt>Data</tt> is the string to be sent. The optional arguments +<tt>i</tt> and <tt>j</tt> work exactly like the standard +<tt>string.sub</tt> Lua function to allow the selection of a +substring to be sent. +</p> + +<p class=return> +If successful, the method returns the index of the last byte +within <tt>[i, j]</tt> that has been sent. Notice that, if +<tt>i</tt> is 1 or absent, this is effectively the total +number of bytes sent. In case of error, the method returns +<b><tt>nil</tt></b>, followed by an error message, followed +by the index of the last byte within <tt>[i, j]</tt> that +has been sent. You might want to try again from the byte +following that. The error message can be '<tt>closed</tt>' +in case the connection was closed before the transmission +was completed or the string '<tt>timeout</tt>' in case +there was a timeout during the operation. +</p> + +<p class=note> +Note: Output is <em>not</em> buffered. For small strings, +it is always better to concatenate them in Lua +(with the '<tt>..</tt>' operator) and send the result in one call +instead of calling the method several times. +</p> + +<!-- setoption ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=setoption> +client:<b>setoption(</b>option [, value]<b>)</b><br> +server:<b>setoption(</b>option [, value]<b>)</b> +</p> + +<p class=description> +Sets options for the TCP object. Options are only needed by low-level or +time-critical applications. You should only modify an option if you +are sure you need it. +</p> + +<p class=parameters> +<tt>Option</tt> is a string with the option name, and <tt>value</tt> +depends on the option being set: + +<ul> + +<li> '<tt>keepalive</tt>': Setting this option to <tt>true</tt> enables +the periodic transmission of messages on a connected socket. Should the +connected party fail to respond to these messages, the connection is +considered broken and processes using the socket are notified; + +<li> '<tt>linger</tt>': Controls the action taken when unsent data are +queued on a socket and a close is performed. The value is a table with a +boolean entry '<tt>on</tt>' and a numeric entry for the time interval +'<tt>timeout</tt>' in seconds. If the '<tt>on</tt>' field is set to +<tt>true</tt>, the system will block the process on the close attempt until +it is able to transmit the data or until '<tt>timeout</tt>' has passed. If +'<tt>on</tt>' is <tt>false</tt> and a close is issued, the system will +process the close in a manner that allows the process to continue as +quickly as possible. I do not advise you to set this to anything other than +zero; + +<li> '<tt>reuseaddr</tt>': Setting this option indicates that the rules +used in validating addresses supplied in a call to +<a href=#bind><tt>bind</tt></a> should allow reuse of local addresses; + +<li> '<tt>tcp-nodelay</tt>': Setting this option to <tt>true</tt> +disables the Nagle's algorithm for the connection. + +</ul> + +<p class=return> +The method returns 1 in case of success, or <b><tt>nil</tt></b> otherwise. +</p> + +<p class=note> +Note: The descriptions above come from the man pages. +</p> + +<!-- setstats +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=setstats> +master:<b>setstats(</b>received, sent, age<b>)</b><br> +client:<b>setstats(</b>received, sent, age<b>)</b><br> +server:<b>setstats(</b>received, sent, age<b>)</b><br> +</p> + +<p class=description> +Resets accounting information on the socket, useful for throttling +of bandwidth. +</p> + +<p class=parameters> +<tt>Received</tt> is a number with the new number of bytes received. +<tt>Sent</tt> is a number with the new number of bytes sent. +<tt>Age</tt> is the new age in seconds. +</p> + +<p class=return> +The method returns 1 in case of success and <tt><b>nil</b></tt> otherwise. +</p> + +<!-- settimeout +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=settimeout> +master:<b>settimeout(</b>value [, mode]<b>)</b><br> +client:<b>settimeout(</b>value [, mode]<b>)</b><br> +server:<b>settimeout(</b>value [, mode]<b>)</b> +</p> + +<p class=description> +Changes the timeout values for the object. By default, +all I/O operations are blocking. That is, any call to the methods +<a href=#send><tt>send</tt></a>, +<a href=#receive><tt>receive</tt></a>, and +<a href=#accept><tt>accept</tt></a> +will block indefinitely, until the operation completes. The +<tt>settimeout</tt> method defines a limit on the amount of time the +I/O methods can block. When a timeout is set and the specified amount of +time has elapsed, the affected methods give up and fail with an error code. +</p> + +<p class=parameters> +The amount of time to wait is specified as the +<tt>value</tt> parameter, in seconds. There are two timeout modes and +both can be used together for fine tuning: +</p> + +<ul> +<li> '<tt>b</tt>': <em>block</em> timeout. Specifies the upper limit on +the amount of time LuaSocket can be blocked by the operating system +while waiting for completion of any single I/O operation. This is the +default mode;</li> + +<li> '<tt>t</tt>': <em>total</em> timeout. Specifies the upper limit on +the amount of time LuaSocket can block a Lua script before returning from +a call.</li> +</ul> + +<p class=parameters> +The <b><tt>nil</tt></b> timeout <tt>value</tt> allows operations to block +indefinitely. Negative timeout values have the same effect. +</p> + +<p class=note> +Note: although timeout values have millisecond precision in LuaSocket, +large blocks can cause I/O functions not to respect timeout values due +to the time the library takes to transfer blocks to and from the OS +and to and from the Lua interpreter. Also, function that accept host names +and perform automatic name resolution might be blocked by the resolver for +longer than the specified timeout value. +</p> + +<p class=note> +Note: The old <tt>timeout</tt> method is deprecated. The name has been +changed for sake of uniformity, since all other method names already +contained verbs making their imperative nature obvious. +</p> + +<!-- shutdown +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=shutdown> +client:<b>shutdown(</b>mode<b>)</b><br> +</p> + +<p class=description> +Shuts down part of a full-duplex connection. +</p> + +<p class=parameters> +Mode tells which way of the connection should be shut down and can +take the value: +<ul> +<li>"<tt>both</tt>": disallow further sends and receives on the object. +This is the default mode; +<li>"<tt>send</tt>": disallow further sends on the object; +<li>"<tt>receive</tt>": disallow further receives on the object. +</ul> + +<p class=return> +This function returns 1. +</p> + +<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<div class=footer> +<hr> +<center> +<p class=bar> +<a href="home.html">home</a> · +<a href="home.html#down">download</a> · +<a href="installation.html">installation</a> · +<a href="introduction.html">introduction</a> · +<a href="reference.html">reference</a> +</p> +<p> +<small> +Last modified by Diego Nehab on <br> +Thu Apr 20 00:25:57 EDT 2006 +</small> +</p> +</center> +</div> + +</body> +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/udp.html Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,416 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> +<html> + +<head> +<meta name="description" content="LuaSocket: The UDP support"> +<meta name="keywords" content="Lua, LuaSocket, Socket, UDP, Library, Network, Support"> +<title>LuaSocket: UDP support</title> +<link rel="stylesheet" href="reference.css" type="text/css"> +</head> + +<body> + +<!-- header ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<div class=header> +<hr> +<center> +<table summary="LuaSocket logo"> +<tr><td align=center><a href="http://www.lua.org"> +<img width=128 height=128 border=0 alt="LuaSocket" src="luasocket.png"> +</a></td></tr> +<tr><td align=center valign=top>Network support for the Lua language +</td></tr> +</table> +<p class=bar> +<a href="home.html">home</a> · +<a href="home.html#download">download</a> · +<a href="installation.html">installation</a> · +<a href="introduction.html">introduction</a> · +<a href="reference.html">reference</a> +</p> +</center> +<hr> +</div> + + +<!-- udp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<h2 id=udp>UDP</h2> + +<!-- socket.udp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class="name" id="socket.udp"> +socket.<b>udp()</b> +</p> + +<p class="description"> +Creates and returns an unconnected UDP object. Unconnected objects support the +<a href="#sendto"><tt>sendto</tt></a>, +<a href="#receive"><tt>receive</tt></a>, +<a href="#receivefrom"><tt>receivefrom</tt></a>, +<a href="#getsockname"><tt>getsockname</tt></a>, +<a href="#setoption"><tt>setoption</tt></a>, +<a href="#settimeout"><tt>settimeout</tt></a>, +<a href="#setpeername"><tt>setpeername</tt></a>, +<a href="#setsockname"><tt>setsockname</tt></a>, and +<a href="#close"><tt>close</tt></a>. +The <a href="#setpeername"><tt>setpeername</tt></a> +is used to connect the object. +</p> + +<p class="return"> +In case of success, a new unconnected UDP object +returned. In case of error, <b><tt>nil</tt></b> is returned, followed by +an error message. +</p> + +<!-- close +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class="name" id="close"> +connected:<b>close()</b><br> +unconnected:<b>close()</b> +</p> + +<p class="description"> +Closes a UDP object. The internal socket +used by the object is closed and the local address to which the +object was bound is made available to other applications. No +further operations (except for further calls to the <tt>close</tt> +method) are allowed on a closed socket. +</p> + +<p class="note"> +Note: It is important to close all used sockets +once they are not needed, since, in many systems, each socket uses +a file descriptor, which are limited system resources. +Garbage-collected objects are automatically closed before +destruction, though. +</p> + +<!-- getpeername +++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class="name" id="getpeername"> +connected:<b>getpeername()</b> +</p> + +<p class="description"> +Retrieves information about the peer +associated with a connected UDP object. +</p> + +<p class="return"> +Returns the IP address and port number of the peer. +</p> + +<p class="note"> +Note: It makes no sense to call this method on unconnected objects. +</p> + +<!-- getsockname +++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class="name" id="getsockname"> +connected:<b>getsockname()</b><br> +unconnected:<b>getsockname()</b> +</p> + +<p class="description"> +Returns the local address information associated to the object. +</p> + +<p class="return"> +The method returns a string with local IP +address and a number with the port. In case of error, the method +returns <b><tt>nil</tt></b>. +</p> + +<p class="note"> +Note: UDP sockets are not bound to any address +until the <a href="#setsockname"><tt>setsockname</tt></a> or the +<a href="#sendto"><tt>sendto</tt></a> method is called for the +first time (in which case it is bound to an ephemeral port and the +wild-card address). +</p> + +<!-- receive +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class="name" id="receive"> +connected:<b>receive(</b>[size]<b>)</b><br> +unconnected:<b>receive(</b>[size]<b>)</b> +</p> + +<p class="description"> +Receives a datagram from the UDP object. If +the UDP object is connected, only datagrams coming from the peer +are accepted. Otherwise, the returned datagram can come from any +host. +</p> + +<p class="parameters"> +The optional <tt>size</tt> parameter +specifies the maximum size of the datagram to be retrieved. If +there are more than <tt>size</tt> bytes available in the datagram, +the excess bytes are discarded. If there are less then +<tt>size</tt> bytes available in the current datagram, the +available bytes are returned. If <tt>size</tt> is omitted, the +maximum datagram size is used (which is currently limited by the +implementation to 8192 bytes). +</p> + +<p class="return"> +In case of success, the method returns the +received datagram. In case of timeout, the method returns +<b><tt>nil</tt></b> followed by the string '<tt>timeout</tt>'. +</p> + +<!-- receivefrom +++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class="name" id="receivefrom"> +unconnected:<b>receivefrom(</b>[size]<b>)</b> +</p> + +<p class="description"> +Works exactly as the <a href="#receive"><tt>receive</tt></a> +method, except it returns the IP +address and port as extra return values (and is therefore slightly less +efficient). +</p> + +<!-- send ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class="name" id="send"> +connected:<b>send(</b>datagram<b>)</b> +</p> + +<p class="description"> +Sends a datagram to the UDP peer of a connected object. +</p> + +<p class="parameters"> +<tt>Datagram</tt> is a string with the datagram contents. +The maximum datagram size for UDP is 64K minus IP layer overhead. +However datagrams larger than the link layer packet size will be +fragmented, which may deteriorate performance and/or reliability. +</p> + +<p class="return"> +If successful, the method returns 1. In case of +error, the method returns <b><tt>nil</tt></b> followed by an error message. +</p> + +<p class="note"> +Note: In UDP, the <tt>send</tt> method never blocks +and the only way it can fail is if the underlying transport layer +refuses to send a message to the specified address (i.e. no +interface accepts the address). +</p> + +<!-- sendto ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class="name" id="sendto"> +unconnected:<b>sendto(</b>datagram, ip, port<b>)</b> +</p> + +<p class="description"> +Sends a datagram to the specified IP address and port number. +</p> + +<p class="parameters"> +<tt>Datagram</tt> is a string with the +datagram contents. +The maximum datagram size for UDP is 64K minus IP layer overhead. +However datagrams larger than the link layer packet size will be +fragmented, which may deteriorate performance and/or reliability. +<tt>Ip</tt> is the IP address of the recipient. +Host names are <em>not</em> allowed for performance reasons. + +<tt>Port</tt> is the port number at the recipient. +</p> + +<p class="return"> +If successful, the method returns 1. In case of +error, the method returns <b><tt>nil</tt></b> followed by an error message. +</p> + +<p class="note"> +Note: In UDP, the <tt>send</tt> method never blocks +and the only way it can fail is if the underlying transport layer +refuses to send a message to the specified address (i.e. no +interface accepts the address). +</p> + +<!-- setpeername +++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class="name" id="setpeername"> +connected:<b>setpeername(</b>'*'<b>)</b><br> +unconnected:<b>setpeername(</b>address, port<b>)</b> +</p> + +<p class="description"> +Changes the peer of a UDP object. This +method turns an unconnected UDP object into a connected UDP +object or vice versa. +</p> + +<p class="description"> +For connected objects, outgoing datagrams +will be sent to the specified peer, and datagrams received from +other peers will be discarded by the OS. Connected UDP objects must +use the <a href="#send"><tt>send</tt></a> and +<a href="#receive"><tt>receive</tt></a> methods instead of +<a href="#sendto"><tt>sendto</tt></a> and +<a href="#receivefrom"><tt>receivefrom</tt></a>. +</p> + +<p class="parameters"> +<tt>Address</tt> can be an IP address or a +host name. <tt>Port</tt> is the port number. If <tt>address</tt> is +'<tt>*</tt>' and the object is connected, the peer association is +removed and the object becomes an unconnected object again. In that +case, the <tt>port</tt> argument is ignored. +</p> + +<p class="return"> +In case of error the method returns +<b><tt>nil</tt></b> followed by an error message. In case of success, the +method returns 1. +</p> + +<p class="note"> +Note: Since the address of the peer does not have +to be passed to and from the OS, the use of connected UDP objects +is recommended when the same peer is used for several transmissions +and can result in up to 30% performance gains. +</p> + +<!-- setsockname +++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class="name" id="setsockname"> +unconnected:<b>setsockname(</b>address, port<b>)</b> +</p> + +<p class="description"> +Binds the UDP object to a local address. +</p> + +<p class="parameters"> +<tt>Address</tt> can be an IP address or a +host name. If <tt>address</tt> is '<tt>*</tt>' the system binds to +all local interfaces using the constant <tt>INADDR_ANY</tt>. If +<tt>port</tt> is 0, the system chooses an ephemeral port. +</p> + +<p class="return"> +If successful, the method returns 1. In case of +error, the method returns <b><tt>nil</tt></b> followed by an error +message. +</p> + +<p class="note"> +Note: This method can only be called before any +datagram is sent through the UDP object, and only once. Otherwise, +the system automatically binds the object to all local interfaces +and chooses an ephemeral port as soon as the first datagram is +sent. After the local address is set, either automatically by the +system or explicitly by <tt>setsockname</tt>, it cannot be +changed. +</p> + +<!-- setoption +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class="name" id="setoption"> +connected:<b>setoption(</b>option [, value]<b>)</b><br> +unconnected:<b>setoption(</b>option [, value]<b>)</b> +</p> + +<p class="description"> +Sets options for the UDP object. Options are +only needed by low-level or time-critical applications. You should +only modify an option if you are sure you need it.</p> +<p class="parameters"><tt>Option</tt> is a string with the option +name, and <tt>value</tt> depends on the option being set: +</p> + +<ul> +<li>'<tt>dontroute</tt>': Setting this option to <tt>true</tt> +indicates that outgoing messages should bypass the standard routing +facilities;</li> +<li>'<tt>broadcast</tt>': Setting this option to <tt>true</tt> +requests permission to send broadcast datagrams on the +socket.</li> +</ul> + +<p class="return"> +The method returns 1 in case of success, or +<b><tt>nil</tt></b> followed by an error message otherwise. +</p> + +<p class="note"> +Note: The descriptions above come from the man +pages. +</p> + +<!-- settimeout +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class="name" id="settimeout"> +connected:<b>settimeout(</b>value<b>)</b><br> +unconnected:<b>settimeout(</b>value<b>)</b> +</p> + +<p class="description"> +Changes the timeout values for the object. By default, the +<a href="#receive"><tt>receive</tt></a> and +<a href="#receivefrom"><tt>receivefrom</tt></a> +operations are blocking. That is, any call to the methods will block +indefinitely, until data arrives. The <tt>settimeout</tt> function defines +a limit on the amount of time the functions can block. When a timeout is +set and the specified amount of time has elapsed, the affected methods +give up and fail with an error code. +</p> + +<p class="parameters"> +The amount of time to wait is specified as +the <tt>value</tt> parameter, in seconds. The <b><tt>nil</tt></b> timeout +<tt>value</tt> allows operations to block indefinitely. Negative +timeout values have the same effect. +</p> + +<p class="note"> +Note: In UDP, the <a href="#send"><tt>send</tt></a> +and <a href="#sentdo"><tt>sendto</tt></a> methods never block (the +datagram is just passed to the OS and the call returns +immediately). Therefore, the <tt>settimeout</tt> method has no +effect on them. +</p> + +<p class="note"> +Note: The old <tt>timeout</tt> method is +deprecated. The name has been changed for sake of uniformity, since +all other method names already contained verbs making their +imperative nature obvious. +</p> + +<!-- footer ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<div class=footer> +<hr> +<center> +<p class=bar> +<a href="home.html">home</a> · +<a href="home.html#download">download</a> · +<a href="installation.html">installation</a> · +<a href="introduction.html">introduction</a> · +<a href="reference.html">reference</a> +</p> +<p> +<small> +Last modified by Diego Nehab on <br> +Thu Apr 20 00:26:01 EDT 2006 +</small> +</p> +</center> +</div> + +</body> +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/url.html Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,329 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> +<html> + +<head> +<meta name="description" content="LuaSocket: URL manipulation"> +<meta name="keywords" content="Lua, LuaSocket, URL, Library, Link, Network, Support"> +<title>LuaSocket: URL support</title> +<link rel="stylesheet" href="reference.css" type="text/css"> +</head> + +<body> + +<!-- header +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<div class=header> +<hr> +<center> +<table summary="LuaSocket logo"> +<tr><td align=center><a href="http://www.lua.org"> +<img width=128 height=128 border=0 alt="LuaSocket" src="luasocket.png"> +</a></td></tr> +<tr><td align=center valign=top>Network support for the Lua language +</td></tr> +</table> +<p class=bar> +<a href="home.html">home</a> · +<a href="home.html#download">download</a> · +<a href="installation.html">installation</a> · +<a href="introduction.html">introduction</a> · +<a href="reference.html">reference</a> +</p> +</center> +<hr> +</div> + +<!-- url ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<h2 id=url>URL</h2> + +<p> +The <tt>url</tt> namespace provides functions to parse, protect, +and build URLs, as well as functions to compose absolute URLs +from base and relative URLs, according to +<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2396.txt">RFC +2396</a>. +</p> + +<p> +To obtain the <tt>url</tt> namespace, run: +</p> + +<pre class=example> +-- loads the URL module +local url = require("socket.url") +</pre> + +<p> +An URL is defined by the following grammar: +</p> + +<blockquote> +<tt> +<url> ::= [<scheme>:][//<authority>][/<path>][;<params>][?<query>][#<fragment>]<br> +<authority> ::= [<userinfo>@]<host>[:<port>]<br> +<userinfo> ::= <user>[:<password>]<br> +<path> ::= {<segment>/}<segment><br> +</tt> +</blockquote> + +<!-- absolute +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=absolute> +url.<b>absolute(</b>base, relative<b>)</b> +</p> + +<p class=description> +Builds an absolute URL from a base URL and a relative URL. +</p> + +<p class=parameters> +<tt>Base</tt> is a string with the base URL or +a parsed URL table. <tt>Relative</tt> is a +string with the relative URL. +</p> + +<p class=return> +The function returns a string with the absolute URL. +</p> + +<p class=note> +Note: The rules that +govern the composition are fairly complex, and are described in detail in +<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2396.txt">RFC 2396</a>. +The example bellow should give an idea of what the rules are. +</p> + +<pre class=example> +http://a/b/c/d;p?q + ++ + +g:h = g:h +g = http://a/b/c/g +./g = http://a/b/c/g +g/ = http://a/b/c/g/ +/g = http://a/g +//g = http://g +?y = http://a/b/c/?y +g?y = http://a/b/c/g?y +#s = http://a/b/c/d;p?q#s +g#s = http://a/b/c/g#s +g?y#s = http://a/b/c/g?y#s +;x = http://a/b/c/;x +g;x = http://a/b/c/g;x +g;x?y#s = http://a/b/c/g;x?y#s +. = http://a/b/c/ +./ = http://a/b/c/ +.. = http://a/b/ +../ = http://a/b/ +../g = http://a/b/g +../.. = http://a/ +../../ = http://a/ +../../g = http://a/g +</pre> + +<!-- build ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=build> +url.<b>build(</b>parsed_url<b>)</b> +</p> + +<p class=description> +Rebuilds an URL from its parts. +</p> + +<p class=parameters> +<tt>Parsed_url</tt> is a table with same components returned by +<a href="#parse"><tt>parse</tt></a>. +Lower level components, if specified, +take precedence over high level components of the URL grammar. +</p> + +<p class=return> +The function returns a string with the built URL. +</p> + +<!-- build_path +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=build_path> +url.<b>build_path(</b>segments, unsafe<b>)</b> +</p> + +<p class=description> +Builds a <tt><path></tt> component from a list of +<tt><segment></tt> parts. +Before composition, any reserved characters found in a segment are escaped into +their protected form, so that the resulting path is a valid URL path +component. +</p> + +<p class=parameters> +<tt>Segments</tt> is a list of strings with the <tt><segment></tt> +parts. If <tt>unsafe</tt> is anything but <b><tt>nil</tt></b>, reserved +characters are left untouched. +</p> + +<p class=return> +The function returns a string with the +built <tt><path></tt> component. +</p> + +<!-- escape +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id="escape"> +url.<b>escape(</b>content<b>)</b> +</p> + +<p class=description> +Applies the URL escaping content coding to a string +Each byte is encoded as a percent character followed +by the two byte hexadecimal representation of its integer +value. +</p> + +<p class=parameters> +<tt>Content</tt> is the string to be encoded. +</p> + +<p class=result> +The function returns the encoded string. +</p> + +<pre class=example> +-- load url module +url = require("socket.url") + +code = url.escape("/#?;") +-- code = "%2f%23%3f%3b" +</pre> + +<!-- parse ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=parse> +url.<b>parse(</b>url, default<b>)</b> +</p> + +<p class=description> +Parses an URL given as a string into a Lua table with its components. +</p> + +<p class=parameters> +<tt>Url</tt> is the URL to be parsed. If the <tt>default</tt> table is +present, it is used to store the parsed fields. Only fields present in the +URL are overwritten. Therefore, this table can be used to pass default +values for each field. +</p> + +<p class=return> +The function returns a table with all the URL components: +</p> + +<blockquote><tt> +parsed_url = {<br> + url = <i>string</i>,<br> + scheme = <i>string</i>,<br> + authority = <i>string</i>,<br> + path = <i>string</i>,<br> + params = <i>string</i>,<br> + query = <i>string</i>,<br> + fragment = <i>string</i>,<br> + userinfo = <i>string</i>,<br> + host = <i>string</i>,<br> + port = <i>string</i>,<br> + user = <i>string</i>,<br> + password = <i>string</i><br> +} +</tt></blockquote> + +<pre class=example> +-- load url module +url = require("socket.url") + +parsed_url = url.parse("http://www.example.com/cgilua/index.lua?a=2#there") +-- parsed_url = { +-- scheme = "http", +-- authority = "www.example.com", +-- path = "/cgilua/index.lua" +-- query = "a=2", +-- fragment = "there", +-- host = "www.puc-rio.br", +-- } + +parsed_url = url.parse("ftp://root:passwd@unsafe.org/pub/virus.exe;type=i") +-- parsed_url = { +-- scheme = "ftp", +-- authority = "root:passwd@unsafe.org", +-- path = "/pub/virus.exe", +-- params = "type=i", +-- userinfo = "root:passwd", +-- host = "unsafe.org", +-- user = "root", +-- password = "passwd", +-- } +</pre> + +<!-- parse_path +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id=parse_path> +url.<b>parse_path(</b>path<b>)</b> +</p> + +<p class=description> +Breaks a <tt><path></tt> URL component into all its +<tt><segment></tt> parts. +</p> + +<p class=description> +<tt>Path</tt> is a string with the path to be parsed. +</p> + +<p class=return> +Since some characters are reserved in URLs, they must be escaped +whenever present in a <tt><path></tt> component. Therefore, before +returning a list with all the parsed segments, the function removes +escaping from all of them. +</p> + +<!-- unescape +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<p class=name id="unescape"> +url.<b>unescape(</b>content<b>)</b> +</p> + +<p class=description> +Removes the URL escaping content coding from a string. +</p> + +<p class=parameters> +<tt>Content</tt> is the string to be decoded. +</p> + +<p class=return> +The function returns the decoded string. +</p> + +<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +<div class=footer> +<hr> +<center> +<p class=bar> +<a href="home.html">home</a> · +<a href="home.html#down">download</a> · +<a href="installation.html">installation</a> · +<a href="introduction.html">introduction</a> · +<a href="reference.html">reference</a> +</p> +<p> +<small> +Last modified by Diego Nehab on <br> +Thu Apr 20 00:26:05 EDT 2006 +</small> +</p> +</center> +</div> + +</body> +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/etc/README Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,89 @@ +This directory contains code that is more useful than the +samples. This code *is* supported. + + tftp.lua -- Trivial FTP client + +This module implements file retrieval by the TFTP protocol. +Its main use 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. + + lp.lua -- LPD client library + +The lp.lua module implements the client part of the Line +Printer Daemon protocol, used to print files on Unix +machines. It is courtesy of David Burgess! See the source +code and the lpr.lua in the examples directory. + + b64.lua + qp.lua + eol.lua + +These are tiny programs that perform Base64, +Quoted-Printable and end-of-line marker conversions. + + get.lua -- file retriever + +This little program is a client that uses the FTP and +HTTP code to implement a command line file graber. Just +run + + lua get.lua <remote-file> [<local-file>] + +to download a remote file (either ftp:// or http://) to +the specified local file. The program also prints the +download throughput, elapsed time, bytes already downloaded +etc during download. + + check-memory.lua -- checks memory consumption + +This is just to see how much memory each module uses. + + dispatch.lua -- coroutine based dispatcher + +This is a first try at a coroutine based non-blocking +dispatcher for LuaSocket. Take a look at 'check-links.lua' +and at 'forward.lua' to see how to use it. + + check-links.lua -- HTML link checker program + +This little program scans a HTML file and checks for broken +links. It is 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 tested, but it should +work. Just run + + lua check-links.lua [-n] {<url>} > output + +and open the result to see a list of broken links. Make sure +you check the '-n' switch. It runs in non-blocking mode, +using coroutines, and is MUCH faster! + + forward.lua -- coroutine based forward server + +This is a forward server that can accept several connections +and transfers simultaneously using non-blocking I/O and the +coroutine-based dispatcher. You can run, for example + + lua forward.lua 8080:proxy.com:3128 + +to redirect all local conections to port 8080 to the host +'proxy.com' at port 3128. + + unix.c and unix.h + +This is an implementation of Unix local domain sockets and +demonstrates how to extend LuaSocket with a new type of +transport. It has been tested on Linux and on Mac OS X. + +Good luck, +Diego.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/etc/b64.lua Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,20 @@ +----------------------------------------------------------------------------- +-- Little program to convert to and from Base64 +-- LuaSocket sample files +-- Author: Diego Nehab +-- RCS ID: $Id: b64.lua,v 1.8 2004/06/16 04:28:21 diego Exp $ +----------------------------------------------------------------------------- +local ltn12 = require("ltn12") +local mime = require("mime") +local source = ltn12.source.file(io.stdin) +local sink = ltn12.sink.file(io.stdout) +local convert +if arg and arg[1] == '-d' then + convert = mime.decode("base64") +else + local base64 = mime.encode("base64") + local wrap = mime.wrap() + convert = ltn12.filter.chain(base64, wrap) +end +sink = ltn12.sink.chain(convert, sink) +ltn12.pump.all(source, sink)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/etc/check-links.lua Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,112 @@ +----------------------------------------------------------------------------- +-- Little program that checks links in HTML files, using coroutines and +-- non-blocking I/O via the dispatcher module. +-- LuaSocket sample files +-- Author: Diego Nehab +-- RCS ID: $$ +----------------------------------------------------------------------------- +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] {<url>}") + exit() +end + +-- '-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) + handler = dispatch.newhandler("coroutine") +else + -- if using blocking I/O, use fake dispatcher interface + handler = dispatch.newhandler("sequential") +end + +local nthreads = 0 + +-- get the status of a URL using the dispatcher +function getstatus(link) + local parsed = url.parse(link, {scheme = "file"}) + if parsed.scheme == "http" then + nthreads = nthreads + 1 + handler:start(function() + local r, c, h, s = http.request{ + method = "HEAD", + url = link, + create = handler.tcp + } + if r and c == 200 then io.write('\t', link, '\n') + else io.write('\t', link, ': ', tostring(c), '\n') end + nthreads = nthreads - 1 + end) + end +end + +function readfile(path) + path = url.unescape(path) + local file, error = io.open(path, "r") + if file then + local body = file:read("*a") + file:close() + return body + else return nil, error end +end + +function load(u) + local parsed = url.parse(u, { scheme = "file" }) + local body, headers, code, error + local base = u + if parsed.scheme == "http" then + body, code, headers = http.request(u) + if code == 200 then + -- if there was a redirect, update base to reflect it + base = headers.location or base + end + if not body then + error = code + end + elseif parsed.scheme == "file" then + body, error = readfile(parsed.path) + else error = string.format("unhandled scheme '%s'", parsed.scheme) end + return base, body, error +end + +function getlinks(body, base) + -- get rid of comments + body = string.gsub(body, "%<%!%-%-.-%-%-%>", "") + local links = {} + -- extract links + body = string.gsub(body, '[Hh][Rr][Ee][Ff]%s*=%s*"([^"]*)"', function(href) + table.insert(links, url.absolute(base, href)) + end) + body = string.gsub(body, "[Hh][Rr][Ee][Ff]%s*=%s*'([^']*)'", function(href) + table.insert(links, url.absolute(base, href)) + end) + string.gsub(body, "[Hh][Rr][Ee][Ff]%s*=%s*(.-)>", function(href) + table.insert(links, url.absolute(base, href)) + end) + return links +end + +function checklinks(address) + local base, body, error = load(address) + if not body then print(error) return end + print("Checking ", base) + local links = getlinks(body, base) + for _, link in ipairs(links) do + getstatus(link) + end +end + +for _, address in ipairs(arg) do + checklinks(url.absolute("file:", address)) +end + +while nthreads > 0 do + handler:step() +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/etc/check-memory.lua Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,17 @@ +function load(s) + collectgarbage() + local a = gcinfo() + _G[s] = require(s) + collectgarbage() + local b = gcinfo() + print(s .. ":\t " .. (b-a) .. "k") +end + +load("socket.url") +load("ltn12") +load("socket") +load("mime") +load("socket.tp") +load("socket.smtp") +load("socket.http") +load("socket.ftp")
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/etc/dict.lua Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,152 @@ +----------------------------------------------------------------------------- +-- Little program to download DICT word definitions +-- LuaSocket sample files +-- Author: Diego Nehab +-- RCS ID: $Id: dict.lua,v 1.22 2005/11/22 08:33:29 diego Exp $ +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Load required modules +----------------------------------------------------------------------------- +local base = _G +local string = require("string") +local table = require("table") +local socket = require("socket") +local url = require("socket.url") +local tp = require("socket.tp") +module("socket.dict") + +----------------------------------------------------------------------------- +-- Globals +----------------------------------------------------------------------------- +HOST = "dict.org" +PORT = 2628 +TIMEOUT = 10 + +----------------------------------------------------------------------------- +-- Low-level dict API +----------------------------------------------------------------------------- +local metat = { __index = {} } + +function open(host, port) + local tp = socket.try(tp.connect(host or HOST, port or PORT, TIMEOUT)) + return base.setmetatable({tp = tp}, metat) +end + +function metat.__index:greet() + return socket.try(self.tp:check(220)) +end + +function metat.__index:check(ok) + local code, status = socket.try(self.tp:check(ok)) + return code, + base.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 = base.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 + 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 + +local function sget(u) + local gett = parse(u) + return tget(gett) +end + +get = socket.protect(function(gett) + if base.type(gett) == "string" then return sget(gett) + else return tget(gett) end +end) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/etc/dispatch.lua Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,302 @@ +----------------------------------------------------------------------------- +-- A hacked dispatcher module +-- LuaSocket sample files +-- Author: Diego Nehab +-- RCS ID: $$ +----------------------------------------------------------------------------- +local base = _G +local table = require("table") +local socket = require("socket") +local coroutine = require("coroutine") +module("dispatch") + +-- if too much time goes by without any activity in one of our sockets, we +-- just kill it +TIMEOUT = 60 + +----------------------------------------------------------------------------- +-- We implement 3 types of dispatchers: +-- sequential +-- coroutine +-- threaded +-- The user can choose whatever one is needed +----------------------------------------------------------------------------- +local handlert = {} + +-- default handler is coroutine +function newhandler(mode) + mode = mode or "coroutine" + return handlert[mode]() +end + +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 + +----------------------------------------------------------------------------- +-- Mega hack. Don't try to do this at home. +----------------------------------------------------------------------------- +-- 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 function(...) + local co = coroutine.create(f) + while true do + local results = {coroutine.resume(co, base.unpack(arg))} + local status = table.remove(results, 1) + if not status then + if type(results[1]) == 'table' then + return nil, results[1][1] + else base.error(results[1]) end + end + if coroutine.status(co) == "suspended" then + arg = {coroutine.yield(base.unpack(results))} + else + return base.unpack(results) + end + end + end +end + +----------------------------------------------------------------------------- +-- Simple set data structure. O(1) everything. +----------------------------------------------------------------------------- +local function newset() + local reverse = {} + local set = {} + return base.setmetatable(set, {__index = { + insert = function(set, value) + if not reverse[value] then + table.insert(set, value) + reverse[value] = table.getn(set) + end + end, + remove = function(set, value) + local index = reverse[value] + if index then + reverse[value] = nil + local top = table.remove(set) + if top ~= value then + reverse[top] = index + set[index] = top + end + end + end + }}) +end + +----------------------------------------------------------------------------- +-- socket.tcp() wrapper for the coroutine dispatcher +----------------------------------------------------------------------------- +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](base.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 base.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 base.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 base.ipairs(readable) do + schedule(wakeup(self.receiving, tcp)) + end + -- for all writable connections, do the same + for _, tcp in base.ipairs(writable) do + schedule(wakeup(self.sending, tcp)) + end + -- politely ask replacement I/O functions in idle cortns to + -- return reporting a timeout + local now = socket.gettime() + for tcp, stamp in base.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 base.setmetatable(dispatcher, cometat) +end +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/etc/eol.lua Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,14 @@ +----------------------------------------------------------------------------- +-- Little program to adjust end of line markers. +-- LuaSocket sample files +-- Author: Diego Nehab +-- RCS ID: $Id: eol.lua,v 1.8 2005/11/22 08:33:29 diego Exp $ +----------------------------------------------------------------------------- +local mime = require("mime") +local ltn12 = require("ltn12") +local marker = '\n' +if arg and arg[1] == '-d' then marker = '\r\n' end +local filter = mime.normalize(marker) +local source = ltn12.source.chain(ltn12.source.file(io.stdin), filter) +local sink = ltn12.sink.file(io.stdout) +ltn12.pump.all(source, sink)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/etc/forward.lua Tue Aug 26 18:40:01 2008 -0700 @@ -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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/etc/get.lua Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,142 @@ +----------------------------------------------------------------------------- +-- Little program to download files from URLs +-- LuaSocket sample files +-- Author: Diego Nehab +-- RCS ID: $Id: get.lua,v 1.25 2007/03/12 04:08:40 diego Exp $ +----------------------------------------------------------------------------- +local socket = require("socket") +local http = require("socket.http") +local ftp = require("socket.ftp") +local url = require("socket.url") +local ltn12 = require("ltn12") + +-- formats a number of seconds into human readable form +function nicetime(s) + local l = "s" + if s > 60 then + s = s / 60 + l = "m" + if s > 60 then + s = s / 60 + l = "h" + if s > 24 then + s = s / 24 + l = "d" -- hmmm + end + end + end + if l == "s" then return string.format("%5.0f%s", s, l) + else return string.format("%5.2f%s", s, l) end +end + +-- formats a number of bytes into human readable form +function nicesize(b) + local l = "B" + if b > 1024 then + b = b / 1024 + l = "KB" + if b > 1024 then + b = b / 1024 + l = "MB" + if b > 1024 then + b = b / 1024 + l = "GB" -- hmmm + end + end + end + return string.format("%7.2f%2s", b, l) +end + +-- returns a string with the current state of the download +local remaining_s = "%s received, %s/s throughput, %2.0f%% done, %s remaining" +local elapsed_s = "%s received, %s/s throughput, %s elapsed " +function gauge(got, delta, size) + local rate = got / delta + if size and size >= 1 then + return string.format(remaining_s, nicesize(got), nicesize(rate), + 100*got/size, nicetime((size-got)/rate)) + else + return string.format(elapsed_s, nicesize(got), + nicesize(rate), nicetime(delta)) + end +end + +-- creates a new instance of a receive_cb that saves to disk +-- kind of copied from luasocket's manual callback examples +function stats(size) + local start = socket.gettime() + local last = start + local got = 0 + return function(chunk) + -- elapsed time since start + local current = socket.gettime() + if chunk then + -- total bytes received + got = got + string.len(chunk) + -- not enough time for estimate + if current - last > 1 then + io.stderr:write("\r", gauge(got, current - start, size)) + io.stderr:flush() + last = current + end + else + -- close up + io.stderr:write("\r", gauge(got, current - start), "\n") + end + return chunk + end +end + +-- determines the size of a http file +function gethttpsize(u) + local r, c, h = http.request {method = "HEAD", url = u} + if c == 200 then + return tonumber(h["content-length"]) + end +end + +-- downloads a file using the http protocol +function getbyhttp(u, file) + local save = ltn12.sink.file(file or io.stdout) + -- only print feedback if output is not stdout + if file then save = ltn12.sink.chain(stats(gethttpsize(u)), save) end + local r, c, h, s = http.request {url = u, sink = save } + if c ~= 200 then io.stderr:write(s or c, "\n") end +end + +-- downloads a file using the ftp protocol +function getbyftp(u, file) + local save = ltn12.sink.file(file or io.stdout) + -- only print feedback if output is not stdout + -- and we don't know how big the file is + if file then save = ltn12.sink.chain(stats(), save) end + local gett = url.parse(u) + gett.sink = save + gett.type = "i" + local ret, err = ftp.get(gett) + if err then print(err) end +end + +-- determines the scheme +function getscheme(u) + -- this is an heuristic to solve a common invalid url poblem + if not string.find(u, "//") then u = "//" .. u end + local parsed = url.parse(u, {scheme = "http"}) + return parsed.scheme +end + +-- gets a file either by http or ftp, saving as <name> +function get(u, name) + local fout = name and io.open(name, "wb") + local scheme = getscheme(u) + if scheme == "ftp" then getbyftp(u, fout) + elseif scheme == "http" then getbyhttp(u, fout) + else print("unknown scheme" .. scheme) end +end + +-- main program +arg = arg or {} +if table.getn(arg) < 1 then + io.write("Usage:\n lua get.lua <remote-url> [<local-file>]\n") + os.exit(1) +else get(arg[1], arg[2]) end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/etc/lp.lua Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,324 @@ +----------------------------------------------------------------------------- +-- LPD support for the Lua language +-- LuaSocket toolkit. +-- Author: David Burgess +-- Modified by Diego Nehab, but David is in charge +-- RCS ID: $Id: lp.lua,v 1.14 2005/11/21 07:04:44 diego Exp $ +----------------------------------------------------------------------------- +--[[ + if you have any questions: RFC 1179 +]] +-- make sure LuaSocket is loaded +local io = require("io") +local base = _G +local os = require("os") +local math = require("math") +local string = require("string") +local socket = require("socket") +local ltn12 = require("ltn12") +module("socket.lp") + +-- default port +PORT = 515 +SERVER = os.getenv("SERVER_NAME") or os.getenv("COMPUTERNAME") or "localhost" +PRINTER = os.getenv("PRINTER") or "printer" + +local function connect(localhost, option) + local host = option.host or SERVER + local port = option.port or PORT + local skt + local try = socket.newtry(function() if skt then skt:close() end end) + if option.localbind then + -- bind to a local port (if we can) + local localport = 721 + local done, err + repeat + skt = socket.try(socket.tcp()) + try(skt:settimeout(30)) + done, err = skt:bind(localhost, localport) + if not done then + localport = localport + 1 + skt:close() + skt = nil + else break end + until localport > 731 + socket.try(skt, err) + else skt = socket.try(socket.tcp()) end + try(skt:connect(host, port)) + return { skt = skt, try = try } +end + +--[[ +RFC 1179 +5.3 03 - Send queue state (short) + + +----+-------+----+------+----+ + | 03 | Queue | SP | List | LF | + +----+-------+----+------+----+ + Command code - 3 + Operand 1 - Printer queue name + Other operands - User names or job numbers + + If the user names or job numbers or both are supplied then only those + jobs for those users or with those numbers will be sent. + + The response is an ASCII stream which describes the printer queue. + The stream continues until the connection closes. Ends of lines are + indicated with ASCII LF control characters. The lines may also + contain ASCII HT control characters. + +5.4 04 - Send queue state (long) + + +----+-------+----+------+----+ + | 04 | Queue | SP | List | LF | + +----+-------+----+------+----+ + Command code - 4 + Operand 1 - Printer queue name + Other operands - User names or job numbers + + If the user names or job numbers or both are supplied then only those + jobs for those users or with those numbers will be sent. + + The response is an ASCII stream which describes the printer queue. + The stream continues until the connection closes. Ends of lines are + indicated with ASCII LF control characters. The lines may also + contain ASCII HT control characters. +]] + +-- gets server acknowledement +local function recv_ack(con) + local ack = con.skt:receive(1) + con.try(string.char(0) == ack, "failed to receive server acknowledgement") +end + +-- sends client acknowledement +local function send_ack(con) + local sent = con.skt:send(string.char(0)) + con.try(sent == 1, "failed to send acknowledgement") +end + +-- sends queue request +-- 5.2 02 - Receive a printer job +-- +-- +----+-------+----+ +-- | 02 | Queue | LF | +-- +----+-------+----+ +-- Command code - 2 +-- Operand - Printer queue name +-- +-- Receiving a job is controlled by a second level of commands. The +-- daemon is given commands by sending them over the same connection. +-- The commands are described in the next section (6). +-- +-- After this command is sent, the client must read an acknowledgement +-- octet from the daemon. A positive acknowledgement is an octet of +-- zero bits. A negative acknowledgement is an octet of any other +-- pattern. +local function send_queue(con, queue) + queue = queue or PRINTER + local str = string.format("\2%s\10", queue) + local sent = con.skt:send(str) + con.try(sent == string.len(str), "failed to send print request") + recv_ack(con) +end + +-- sends control file +-- 6.2 02 - Receive control file +-- +-- +----+-------+----+------+----+ +-- | 02 | Count | SP | Name | LF | +-- +----+-------+----+------+----+ +-- Command code - 2 +-- Operand 1 - Number of bytes in control file +-- Operand 2 - Name of control file +-- +-- The control file must be an ASCII stream with the ends of lines +-- indicated by ASCII LF. The total number of bytes in the stream is +-- sent as the first operand. The name of the control file is sent as +-- the second. It should start with ASCII "cfA", followed by a three +-- digit job number, followed by the host name which has constructed the +-- control file. Acknowledgement processing must occur as usual after +-- the command is sent. +-- +-- The next "Operand 1" octets over the same TCP connection are the +-- intended contents of the control file. Once all of the contents have +-- been delivered, an octet of zero bits is sent as an indication that +-- the file being sent is complete. A second level of acknowledgement +-- processing must occur at this point. + +-- sends data file +-- 6.3 03 - Receive data file +-- +-- +----+-------+----+------+----+ +-- | 03 | Count | SP | Name | LF | +-- +----+-------+----+------+----+ +-- Command code - 3 +-- Operand 1 - Number of bytes in data file +-- Operand 2 - Name of data file +-- +-- The data file may contain any 8 bit values at all. The total number +-- of bytes in the stream may be sent as the first operand, otherwise +-- the field should be cleared to 0. The name of the data file should +-- start with ASCII "dfA". This should be followed by a three digit job +-- number. The job number should be followed by the host name which has +-- constructed the data file. Interpretation of the contents of the +-- data file is determined by the contents of the corresponding control +-- file. If a data file length has been specified, the next "Operand 1" +-- octets over the same TCP connection are the intended contents of the +-- data file. In this case, once all of the contents have been +-- delivered, an octet of zero bits is sent as an indication that the +-- file being sent is complete. A second level of acknowledgement +-- processing must occur at this point. + + +local function send_hdr(con, control) + local sent = con.skt:send(control) + con.try(sent and sent >= 1 , "failed to send header file") + recv_ack(con) +end + +local function send_control(con, control) + local sent = con.skt:send(control) + con.try(sent and sent >= 1, "failed to send control file") + send_ack(con) +end + +local function send_data(con,fh,size) + local buf + while size > 0 do + buf,message = fh:read(8192) + if buf then + st = con.try(con.skt:send(buf)) + size = size - st + else + con.try(size == 0, "file size mismatch") + end + end + recv_ack(con) -- note the double acknowledgement + send_ack(con) + recv_ack(con) + return size +end + + +--[[ +local control_dflt = { + "H"..string.sub(socket.hostname,1,31).."\10", -- host + "C"..string.sub(socket.hostname,1,31).."\10", -- class + "J"..string.sub(filename,1,99).."\10", -- jobname + "L"..string.sub(user,1,31).."\10", -- print banner page + "I"..tonumber(indent).."\10", -- indent column count ('f' only) + "M"..string.sub(mail,1,128).."\10", -- mail when printed user@host + "N"..string.sub(filename,1,131).."\10", -- name of source file + "P"..string.sub(user,1,31).."\10", -- user name + "T"..string.sub(title,1,79).."\10", -- title for banner ('p' only) + "W"..tonumber(width or 132).."\10", -- width of print f,l,p only + + "f"..file.."\10", -- formatted print (remove control chars) + "l"..file.."\10", -- print + "o"..file.."\10", -- postscript + "p"..file.."\10", -- pr format - requires T, L + "r"..file.."\10", -- fortran format + "U"..file.."\10", -- Unlink (data file only) +} +]] + +-- generate a varying job number +local seq = 0 +local function newjob(connection) + seq = seq + 1 + return math.floor(socket.gettime() * 1000 + seq)%1000 +end + + +local format_codes = { + binary = 'l', + text = 'f', + ps = 'o', + pr = 'p', + fortran = 'r', + l = 'l', + r = 'r', + o = 'o', + p = 'p', + f = 'f' +} + +-- lp.send{option} +-- requires option.file + +send = socket.protect(function(option) + socket.try(option and base.type(option) == "table", "invalid options") + local file = option.file + socket.try(file, "invalid file name") + local fh = socket.try(io.open(file,"rb")) + local datafile_size = fh:seek("end") -- get total size + fh:seek("set") -- go back to start of file + local localhost = socket.dns.gethostname() or os.getenv("COMPUTERNAME") + or "localhost" + local con = connect(localhost, option) +-- format the control file + local jobno = newjob() + local localip = socket.dns.toip(localhost) + localhost = string.sub(localhost,1,31) + local user = string.sub(option.user or os.getenv("LPRUSER") or + os.getenv("USERNAME") or os.getenv("USER") or "anonymous", 1,31) + local lpfile = string.format("dfA%3.3d%-s", jobno, localhost); + local fmt = format_codes[option.format] or 'l' + local class = string.sub(option.class or localip or localhost,1,31) + local _,_,ctlfn = string.find(file,".*[%/%\\](.*)") + ctlfn = string.sub(ctlfn or file,1,131) + local cfile = + string.format("H%-s\nC%-s\nJ%-s\nP%-s\n%.1s%-s\nU%-s\nN%-s\n", + localhost, + class, + option.job or "LuaSocket", + user, + fmt, lpfile, + lpfile, + ctlfn); -- mandatory part of ctl file + if (option.banner) then cfile = cfile .. 'L'..user..'\10' end + if (option.indent) then cfile = cfile .. 'I'..base.tonumber(option.indent)..'\10' end + if (option.mail) then cfile = cfile .. 'M'..string.sub((option.mail),1,128)..'\10' end + if (fmt == 'p' and option.title) then cfile = cfile .. 'T'..string.sub((option.title),1,79)..'\10' end + if ((fmt == 'p' or fmt == 'l' or fmt == 'f') and option.width) then + cfile = cfile .. 'W'..base.tonumber(option,width)..'\10' + end + + con.skt:settimeout(option.timeout or 65) +-- send the queue header + send_queue(con, option.queue) +-- send the control file header + local cfilecmd = string.format("\2%d cfA%3.3d%-s\n",string.len(cfile), jobno, localhost); + send_hdr(con,cfilecmd) + +-- send the control file + send_control(con,cfile) + +-- send the data file header + local dfilecmd = string.format("\3%d dfA%3.3d%-s\n",datafile_size, jobno, localhost); + send_hdr(con,dfilecmd) + +-- send the data file + send_data(con,fh,datafile_size) + fh:close() + con.skt:close(); + return jobno, datafile_size +end) + +-- +-- lp.query({host=,queue=printer|'*', format='l'|'s', list=}) +-- +query = socket.protect(function(p) + p = p or {} + local localhost = socket.dns.gethostname() or os.getenv("COMPUTERNAME") + or "localhost" + local con = connect(localhost,p) + local fmt + if string.sub(p.format or 's',1,1) == 's' then fmt = 3 else fmt = 4 end + con.try(con.skt:send(string.format("%c%s %s\n", fmt, p.queue or "*", + p.list or ""))) + local data = con.try(con.skt:receive("*a")) + con.skt:close() + return data +end)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/etc/qp.lua Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,24 @@ +----------------------------------------------------------------------------- +-- Little program to convert to and from Quoted-Printable +-- LuaSocket sample files +-- Author: Diego Nehab +-- RCS ID: $Id: qp.lua,v 1.5 2004/06/17 21:46:22 diego Exp $ +----------------------------------------------------------------------------- +local ltn12 = require("ltn12") +local mime = require("mime") +local convert +arg = arg or {} +local mode = arg and arg[1] or "-et" +if mode == "-et" then + local normalize = mime.normalize() + local qp = mime.encode("quoted-printable") + local wrap = mime.wrap("quoted-printable") + convert = ltn12.filter.chain(normalize, qp, wrap) +elseif mode == "-eb" then + local qp = mime.encode("quoted-printable", "binary") + local wrap = mime.wrap("quoted-printable") + convert = ltn12.filter.chain(qp, wrap) +else convert = mime.decode("quoted-printable") end +local source = ltn12.source.chain(ltn12.source.file(io.stdin), convert) +local sink = ltn12.sink.file(io.stdout) +ltn12.pump.all(source, sink)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/etc/tftp.lua Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,155 @@ +----------------------------------------------------------------------------- +-- TFTP support for the Lua language +-- LuaSocket toolkit. +-- Author: Diego Nehab +-- RCS ID: $Id: tftp.lua,v 1.16 2005/11/22 08:33:29 diego Exp $ +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Load required files +----------------------------------------------------------------------------- +local base = _G +local table = require("table") +local math = require("math") +local string = require("string") +local socket = require("socket") +local ltn12 = require("ltn12") +local url = require("socket.url") +module("socket.tftp") + +----------------------------------------------------------------------------- +-- Program constants +----------------------------------------------------------------------------- +local char = string.char +local byte = string.byte + +PORT = 69 +local OP_RRQ = 1 +local OP_WRQ = 2 +local OP_DATA = 3 +local OP_ACK = 4 +local OP_ERROR = 5 +local OP_INV = {"RRQ", "WRQ", "DATA", "ACK", "ERROR"} + +----------------------------------------------------------------------------- +-- Packet creation functions +----------------------------------------------------------------------------- +local function RRQ(source, mode) + return char(0, OP_RRQ) .. source .. char(0) .. mode .. char(0) +end + +local function WRQ(source, mode) + return char(0, OP_RRQ) .. source .. char(0) .. mode .. char(0) +end + +local function ACK(block) + local low, high + low = math.mod(block, 256) + high = (block - low)/256 + return char(0, OP_ACK, high, low) +end + +local function get_OP(dgram) + local op = byte(dgram, 1)*256 + byte(dgram, 2) + return op +end + +----------------------------------------------------------------------------- +-- Packet analysis functions +----------------------------------------------------------------------------- +local function split_DATA(dgram) + local block = byte(dgram, 3)*256 + byte(dgram, 4) + local data = string.sub(dgram, 5) + return block, data +end + +local function get_ERROR(dgram) + local code = byte(dgram, 3)*256 + byte(dgram, 4) + local msg + _,_, msg = string.find(dgram, "(.*)\000", 5) + return string.format("error code %d: %s", code, msg) +end + +----------------------------------------------------------------------------- +-- The real work +----------------------------------------------------------------------------- +local function tget(gett) + local retries, dgram, sent, datahost, dataport, code + local last = 0 + socket.try(gett.host, "missing host") + local con = socket.try(socket.udp()) + local try = socket.newtry(function() con:close() end) + -- convert from name to ip if needed + gett.host = try(socket.dns.toip(gett.host)) + con:settimeout(1) + -- first packet gives data host/port to be used for data transfers + local path = string.gsub(gett.path or "", "^/", "") + path = url.unescape(path) + retries = 0 + repeat + sent = try(con:sendto(RRQ(path, "octet"), gett.host, gett.port)) + dgram, datahost, dataport = con:receivefrom() + retries = retries + 1 + until dgram or datahost ~= "timeout" or retries > 5 + try(dgram, datahost) + -- associate socket with data host/port + try(con:setpeername(datahost, dataport)) + -- default sink + local sink = gett.sink or ltn12.sink.null() + -- process all data packets + while 1 do + -- decode packet + code = get_OP(dgram) + try(code ~= OP_ERROR, get_ERROR(dgram)) + try(code == OP_DATA, "unhandled opcode " .. code) + -- get data packet parts + local block, data = split_DATA(dgram) + -- if not repeated, write + if block == last+1 then + try(sink(data)) + last = block + end + -- last packet brings less than 512 bytes of data + if string.len(data) < 512 then + try(con:send(ACK(block))) + try(con:close()) + try(sink(nil)) + return 1 + end + -- get the next packet + retries = 0 + repeat + sent = try(con:send(ACK(last))) + dgram, err = con:receive() + retries = retries + 1 + until dgram or err ~= "timeout" or retries > 5 + try(dgram, err) + end +end + +local default = { + port = PORT, + path ="/", + scheme = "tftp" +} + +local function parse(u) + local t = socket.try(url.parse(u, default)) + socket.try(t.scheme == "tftp", "invalid scheme '" .. t.scheme .. "'") + socket.try(t.host, "invalid host") + return t +end + +local function sget(u) + local gett = parse(u) + local t = {} + gett.sink = ltn12.sink.table(t) + tget(gett) + return table.concat(t) +end + +get = socket.protect(function(gett) + if base.type(gett) == "string" then return sget(gett) + else return tget(gett) end +end) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/luasocket.sln Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,37 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "socket", "socket.vcproj", "{66E3CE14-884D-4AEA-9F20-15A0BEAF8C5A}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mime", "mime.vcproj", "{128E8BD0-174A-48F0-8771-92B1E8D18713}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libluasocket", "libluasocket.vcproj", "{599EAD40-60EE-4043-9C14-AE090A8A092D}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {66E3CE14-884D-4AEA-9F20-15A0BEAF8C5A}.Debug.ActiveCfg = Debug|Win32 + {66E3CE14-884D-4AEA-9F20-15A0BEAF8C5A}.Debug.Build.0 = Debug|Win32 + {66E3CE14-884D-4AEA-9F20-15A0BEAF8C5A}.Release.ActiveCfg = Release|Win32 + {66E3CE14-884D-4AEA-9F20-15A0BEAF8C5A}.Release.Build.0 = Release|Win32 + {128E8BD0-174A-48F0-8771-92B1E8D18713}.Debug.ActiveCfg = Debug|Win32 + {128E8BD0-174A-48F0-8771-92B1E8D18713}.Debug.Build.0 = Debug|Win32 + {128E8BD0-174A-48F0-8771-92B1E8D18713}.Release.ActiveCfg = Release|Win32 + {128E8BD0-174A-48F0-8771-92B1E8D18713}.Release.Build.0 = Release|Win32 + {599EAD40-60EE-4043-9C14-AE090A8A092D}.Debug.ActiveCfg = Debug|Win32 + {599EAD40-60EE-4043-9C14-AE090A8A092D}.Debug.Build.0 = Debug|Win32 + {599EAD40-60EE-4043-9C14-AE090A8A092D}.Release.ActiveCfg = Release|Win32 + {599EAD40-60EE-4043-9C14-AE090A8A092D}.Release.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/makefile Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,51 @@ +#------ +# Load configuration +# +include config + +#------ +# Hopefully no need to change anything below this line +# +INSTALL_SOCKET_SHARE=$(INSTALL_TOP_SHARE)/socket +INSTALL_SOCKET_LIB=$(INSTALL_TOP_LIB)/socket +INSTALL_MIME_SHARE=$(INSTALL_TOP_SHARE)/mime +INSTALL_MIME_LIB=$(INSTALL_TOP_LIB)/mime + +all clean: + cd src; $(MAKE) $@ + +#------ +# Files to install +# +TO_SOCKET_SHARE:= \ + http.lua \ + url.lua \ + tp.lua \ + ftp.lua \ + smtp.lua + +TO_TOP_SHARE:= \ + ltn12.lua \ + socket.lua \ + mime.lua + +TO_MIME_SHARE:= + +#------ +# Install LuaSocket according to recommendation +# +install: all + cd src; mkdir -p $(INSTALL_TOP_SHARE) + cd src; $(INSTALL_DATA) $(TO_TOP_SHARE) $(INSTALL_TOP_SHARE) + cd src; mkdir -p $(INSTALL_SOCKET_SHARE) + cd src; $(INSTALL_DATA) $(TO_SOCKET_SHARE) $(INSTALL_SOCKET_SHARE) + cd src; mkdir -p $(INSTALL_SOCKET_LIB) + cd src; $(INSTALL_EXEC) $(SOCKET_SO) $(INSTALL_SOCKET_LIB)/core.$(EXT) + #cd src; mkdir -p $(INSTALL_MIME_SHARE) + #cd src; $(INSTALL_DATA) $(TO_MIME_SHARE) $(INSTALL_MIME_SHARE) + cd src; mkdir -p $(INSTALL_MIME_LIB) + cd src; $(INSTALL_EXEC) $(MIME_SO) $(INSTALL_MIME_LIB)/core.$(EXT) + +#------ +# End of makefile +#
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mime.vcproj Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,141 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="7.10" + Name="mime" + ProjectGUID="{128E8BD0-174A-48F0-8771-92B1E8D18713}" + Keyword="Win32Proj"> + <Platforms> + <Platform + Name="Win32"/> + </Platforms> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="src" + IntermediateDirectory="src" + ConfigurationType="2" + CharacterSet="2"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="h:\include\lua5.1" + PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;MIME_EXPORTS;MIME_API=__declspec(dllexport)" + MinimalRebuild="TRUE" + BasicRuntimeChecks="3" + RuntimeLibrary="3" + UsePrecompiledHeader="0" + WarningLevel="3" + Detect64BitPortabilityProblems="TRUE" + DebugInformationFormat="4"/> + <Tool + Name="VCCustomBuildTool"/> + <Tool + Name="VCLinkerTool" + OutputFile="$(OutDir)/mime.dll" + LinkIncremental="2" + AdditionalLibraryDirectories="h:\lib" + GenerateDebugInformation="TRUE" + ProgramDatabaseFile="$(OutDir)/mime.pdb" + SubSystem="2" + ImportLibrary="$(OutDir)/mime.lib" + TargetMachine="1"/> + <Tool + Name="VCMIDLTool"/> + <Tool + Name="VCPostBuildEventTool"/> + <Tool + Name="VCPreBuildEventTool"/> + <Tool + Name="VCPreLinkEventTool"/> + <Tool + Name="VCResourceCompilerTool"/> + <Tool + Name="VCWebServiceProxyGeneratorTool"/> + <Tool + Name="VCXMLDataGeneratorTool"/> + <Tool + Name="VCWebDeploymentTool"/> + <Tool + Name="VCManagedWrapperGeneratorTool"/> + <Tool + Name="VCAuxiliaryManagedWrapperGeneratorTool"/> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="src" + IntermediateDirectory="src" + ConfigurationType="2" + CharacterSet="2"> + <Tool + Name="VCCLCompilerTool" + AdditionalIncludeDirectories="h:\include\lua5.1" + PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;MIME_EXPORTS; MIME_API=__declspec(dllexport)" + RuntimeLibrary="2" + UsePrecompiledHeader="0" + WarningLevel="4" + Detect64BitPortabilityProblems="TRUE" + DebugInformationFormat="0"/> + <Tool + Name="VCCustomBuildTool"/> + <Tool + Name="VCLinkerTool" + OutputFile="$(OutDir)/mime.dll" + LinkIncremental="1" + AdditionalLibraryDirectories="h:\lib" + GenerateDebugInformation="TRUE" + SubSystem="2" + OptimizeReferences="2" + EnableCOMDATFolding="2" + ImportLibrary="$(OutDir)/mime.lib" + TargetMachine="1"/> + <Tool + Name="VCMIDLTool"/> + <Tool + Name="VCPostBuildEventTool"/> + <Tool + Name="VCPreBuildEventTool"/> + <Tool + Name="VCPreLinkEventTool"/> + <Tool + Name="VCResourceCompilerTool"/> + <Tool + Name="VCWebServiceProxyGeneratorTool"/> + <Tool + Name="VCXMLDataGeneratorTool"/> + <Tool + Name="VCWebDeploymentTool"/> + <Tool + Name="VCManagedWrapperGeneratorTool"/> + <Tool + Name="VCAuxiliaryManagedWrapperGeneratorTool"/> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"> + <File + RelativePath="src\mime.c"> + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"> + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"> + </Filter> + <File + RelativePath="..\..\lib\lua5.1.dll.lib"> + </File> + </Files> + <Globals> + </Globals> +</VisualStudioProject>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/README Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,50 @@ +This directory contains some sample programs using +LuaSocket. This code is not supported. + + listener.lua -- socket to stdout + talker.lua -- stdin to socket + +listener.lua and talker.lua are about the simplest +applications you can write using LuaSocket. Run + + 'lua listener.lua' and 'lua talker.lua' + +on different terminals. Whatever you type on talk.lua will +be printed by listen.lua. + + lpr.lua -- lpr client + +This is a cool program written by David Burgess to print +files using the Line Printer Daemon protocol, widely used in +Unix machines. It uses the lp.lua implementation, in the +etc directory. Just run 'lua lpr.lua <filename> +queue=<printername>' and the file will print! + + cddb.lua -- CDDB client + +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 + +Just run the program to retrieve the hour and date in +readable form from any server running an UDP daytime daemon. + + echoclnt.lua -- UDP echo client + echosrvr.lua -- UDP echo server + +These are a UDP echo client/server pair. They work with +other client and servers as well. + + tinyirc.lua -- irc like broadcast server + +This is a simple server that waits simultaneously on two +server sockets for telnet connections. Everything it +receives from the telnet clients is broadcasted to every +other connected client. It tests the select 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 to ports 8080 and 8081. + +Good luck, +Diego.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/cddb.lua Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,46 @@ +local socket = require("socket") +local http = require("socket.http") + +if not arg or not arg[1] or not arg[2] then + print("luasocket cddb.lua <category> <disc-id> [<server>]") + os.exit(1) +end + +local server = arg[3] or "http://freedb.freedb.org/~cddb/cddb.cgi" + +function parse(body) + local lines = string.gfind(body, "(.-)\r\n") + local status = lines() + local code, message = socket.skip(2, string.find(status, "(%d%d%d) (.*)")) + if tonumber(code) ~= 210 then + return nil, code, message + end + local data = {} + for l in lines do + local c = string.sub(l, 1, 1) + if c ~= '#' and c ~= '.' then + local key, value = socket.skip(2, string.find(l, "(.-)=(.*)")) + value = string.gsub(value, "\\n", "\n") + value = string.gsub(value, "\\\\", "\\") + value = string.gsub(value, "\\t", "\t") + data[key] = value + end + end + return data, code, message +end + +local host = socket.dns.gethostname() +local query = "%s?cmd=cddb+read+%s+%s&hello=LuaSocket+%s+LuaSocket+2.0&proto=6" +local url = string.format(query, server, arg[1], arg[2], host) +local body, headers, code = http.get(url) + +if code == 200 then + local data, code, error = parse(body) + if not data then + print(error or code) + else + for i,v in pairs(data) do + io.write(i, ': ', v, '\n') + end + end +else print(error) end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/daytimeclnt.lua Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,23 @@ +----------------------------------------------------------------------------- +-- UDP sample: daytime protocol client +-- LuaSocket sample files +-- Author: Diego Nehab +-- RCS ID: $Id: daytimeclnt.lua,v 1.11 2004/06/21 06:07:57 diego Exp $ +----------------------------------------------------------------------------- +local socket = require"socket" +host = host or "127.0.0.1" +port = port or 13 +if arg then + host = arg[1] or host + port = arg[2] or port +end +host = socket.dns.toip(host) +udp = socket.udp() +print("Using host '" ..host.. "' and port " ..port.. "...") +udp:setpeername(host, port) +udp:settimeout(3) +sent, err = udp:send("anything") +if err then print(err) os.exit() end +dgram, err = udp:receive() +if not dgram then print(err) os.exit() end +io.write(dgram)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/echoclnt.lua Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,24 @@ +----------------------------------------------------------------------------- +-- UDP sample: echo protocol client +-- LuaSocket sample files +-- Author: Diego Nehab +-- RCS ID: $Id: echoclnt.lua,v 1.10 2005/01/02 22:44:00 diego Exp $ +----------------------------------------------------------------------------- +local socket = require("socket") +host = host or "localhost" +port = port or 7 +if arg then + host = arg[1] or host + port = arg[2] or port +end +host = socket.dns.toip(host) +udp = assert(socket.udp()) +assert(udp:setpeername(host, port)) +print("Using remote host '" ..host.. "' and port " .. port .. "...") +while 1 do + line = io.read() + if not line or line == "" then os.exit() end + assert(udp:send(line)) + dgram = assert(udp:receive()) + print(dgram) +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/echosrvr.lua Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,29 @@ +----------------------------------------------------------------------------- +-- UDP sample: echo protocol server +-- LuaSocket sample files +-- Author: Diego Nehab +-- RCS ID: $Id: echosrvr.lua,v 1.12 2005/11/22 08:33:29 diego Exp $ +----------------------------------------------------------------------------- +local socket = require("socket") +host = host or "127.0.0.1" +port = port or 7 +if arg then + host = arg[1] or host + port = arg[2] or port +end +print("Binding to host '" ..host.. "' and port " ..port.. "...") +udp = assert(socket.udp()) +assert(udp:setsockname(host, port)) +assert(udp:settimeout(5)) +ip, port = udp:getsockname() +assert(ip, port) +print("Waiting packets on " .. ip .. ":" .. port .. "...") +while 1 do + dgram, ip, port = udp:receivefrom() + if dgram then + print("Echoing '" .. dgram .. "' to " .. ip .. ":" .. port) + udp:sendto(dgram, ip, port) + else + print(ip) + end +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/listener.lua Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,26 @@ +----------------------------------------------------------------------------- +-- TCP sample: Little program to dump lines received at a given port +-- LuaSocket sample files +-- Author: Diego Nehab +-- RCS ID: $Id: listener.lua,v 1.11 2005/01/02 22:44:00 diego Exp $ +----------------------------------------------------------------------------- +local socket = require("socket") +host = host or "*" +port = port or 8080 +if arg then + host = arg[1] or host + port = arg[2] or port +end +print("Binding to host '" ..host.. "' and port " ..port.. "...") +s = assert(socket.bind(host, port)) +i, p = s:getsockname() +assert(i, p) +print("Waiting connection from talker on " .. i .. ":" .. p .. "...") +c = assert(s:accept()) +print("Connected. Here is the stuff:") +l, e = c:receive() +while not e do + print(l) + l, e = c:receive() +end +print(e)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/lpr.lua Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,51 @@ +local lp = require("socket.lp") + +local function usage() + print('\nUsage: lua lpr.lua [filename] [keyword=val...]\n') + print('Valid keywords are :') + print( + ' host=remote host or IP address (default "localhost")\n' .. + ' queue=remote queue or printer name (default "printer")\n' .. + ' port=remote port number (default 515)\n' .. + ' user=sending user name\n' .. + ' format=["binary" | "text" | "ps" | "pr" | "fortran"] (default "binary")\n' .. + ' banner=true|false\n' .. + ' indent=number of columns to indent\n' .. + ' mail=email of address to notify when print is complete\n' .. + ' title=title to use for "pr" format\n' .. + ' width=width for "text" or "pr" formats\n' .. + ' class=\n' .. + ' job=\n' .. + ' name=\n' .. + ' localbind=true|false\n' + ) + return nil +end + +if not arg or not arg[1] then + return usage() +end + +do + local opt = {} + local pat = "[%s%c%p]*([%w]*)=([\"]?[%w%s_!@#$%%^&*()<>:;]+[\"]\?\.?)" + for i = 2, table.getn(arg), 1 do + string.gsub(arg[i], pat, function(name, value) opt[name] = value end) + end + if not arg[2] then + return usage() + end + if arg[1] ~= "query" then + opt.file = arg[1] + r,e=lp.send(opt) + io.stdout:write(tostring(r or e),'\n') + else + r,e=lp.query(opt) + io.stdout:write(tostring(r or e), '\n') + end +end + +-- trivial tests +--lua lp.lua lp.lua queue=default host=localhost +--lua lp.lua lp.lua queue=default host=localhost format=binary localbind=1 +--lua lp.lua query queue=default host=localhost
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/talker.lua Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,21 @@ +----------------------------------------------------------------------------- +-- TCP sample: Little program to send text lines to a given host/port +-- LuaSocket sample files +-- Author: Diego Nehab +-- RCS ID: $Id: talker.lua,v 1.9 2005/01/02 22:44:00 diego Exp $ +----------------------------------------------------------------------------- +local socket = require("socket") +host = host or "localhost" +port = port or 8080 +if arg then + host = arg[1] or host + port = arg[2] or port +end +print("Attempting connection to host '" ..host.. "' and port " ..port.. "...") +c = assert(socket.connect(host, port)) +print("Connected! Please type stuff (empty line to stop):") +l = io.read() +while l and l ~= "" and not e do + assert(c:send(l .. "\n")) + l = io.read() +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/tinyirc.lua Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,90 @@ +----------------------------------------------------------------------------- +-- Select sample: simple text line server +-- LuaSocket sample files. +-- Author: Diego Nehab +-- RCS ID: $Id: tinyirc.lua,v 1.14 2005/11/22 08:33:29 diego Exp $ +----------------------------------------------------------------------------- +local socket = require("socket") +host = host or "*" +port1 = port1 or 8080 +port2 = port2 or 8181 +if arg then + host = arg[1] or host + port1 = arg[2] or port1 + port2 = arg[3] or port2 +end + +server1 = assert(socket.bind(host, port1)) +server2 = assert(socket.bind(host, port2)) +server1:settimeout(1) -- make sure we don't block in accept +server2:settimeout(1) + +io.write("Servers bound\n") + +-- simple set implementation +-- the select function doesn't care about what is passed to it as long as +-- it behaves like a table +-- creates a new set data structure +function newset() + local reverse = {} + local set = {} + return setmetatable(set, {__index = { + insert = function(set, value) + if not reverse[value] then + table.insert(set, value) + reverse[value] = table.getn(set) + end + end, + remove = function(set, value) + local index = reverse[value] + if index then + reverse[value] = nil + local top = table.remove(set) + if top ~= value then + reverse[top] = index + set[index] = top + end + end + end + }}) +end + +set = newset() + +io.write("Inserting servers in set\n") +set:insert(server1) +set:insert(server2) + +while 1 do + local readable, _, error = socket.select(set, nil) + for _, input in ipairs(readable) do + -- is it a server socket? + if input == server1 or input == server2 then + io.write("Waiting for clients\n") + local new = input:accept() + if new then + new:settimeout(1) + io.write("Inserting client in set\n") + set:insert(new) + end + -- it is a client socket + else + local line, error = input:receive() + if error then + input:close() + io.write("Removing client from set\n") + set:remove(input) + else + io.write("Broadcasting line '", line, "'\n") + writable, error = socket.skip(1, socket.select(nil, set, 1)) + if not error then + for __, output in ipairs(writable) do + if output ~= input then + output:send(line .. "\n") + end + end + else io.write("No client ready to receive!!!\n") end + end + end + end +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/socket.vcproj Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,182 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="7.10" + Name="socket" + ProjectGUID="{66E3CE14-884D-4AEA-9F20-15A0BEAF8C5A}" + Keyword="Win32Proj"> + <Platforms> + <Platform + Name="Win32"/> + </Platforms> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="src" + IntermediateDirectory="src" + ConfigurationType="2" + CharacterSet="2"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="h:\include\lua5.1" + PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;LUASOCKET_EXPORTS;LUASOCKET_API=__declspec(dllexport)" + MinimalRebuild="TRUE" + BasicRuntimeChecks="3" + RuntimeLibrary="3" + UsePrecompiledHeader="0" + WarningLevel="3" + Detect64BitPortabilityProblems="TRUE" + DebugInformationFormat="4"/> + <Tool + Name="VCCustomBuildTool"/> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="ws2_32.lib" + OutputFile="$(OutDir)/socket.dll" + LinkIncremental="2" + AdditionalLibraryDirectories="h:\lib" + GenerateDebugInformation="TRUE" + ProgramDatabaseFile="$(OutDir)/socket.pdb" + SubSystem="2" + ImportLibrary="$(OutDir)/socket.lib" + TargetMachine="1"/> + <Tool + Name="VCMIDLTool"/> + <Tool + Name="VCPostBuildEventTool"/> + <Tool + Name="VCPreBuildEventTool"/> + <Tool + Name="VCPreLinkEventTool"/> + <Tool + Name="VCResourceCompilerTool"/> + <Tool + Name="VCWebServiceProxyGeneratorTool"/> + <Tool + Name="VCXMLDataGeneratorTool"/> + <Tool + Name="VCWebDeploymentTool"/> + <Tool + Name="VCManagedWrapperGeneratorTool"/> + <Tool + Name="VCAuxiliaryManagedWrapperGeneratorTool"/> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="./src" + IntermediateDirectory="./src" + ConfigurationType="2" + CharacterSet="2"> + <Tool + Name="VCCLCompilerTool" + AdditionalIncludeDirectories="h:\include\lua5.1" + PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;LUASOCKET_EXPORTS;LUASOCKET_API=__declspec(dllexport); LUASOCKET_DEBUG" + RuntimeLibrary="2" + UsePrecompiledHeader="0" + WarningLevel="3" + Detect64BitPortabilityProblems="TRUE" + DebugInformationFormat="0"/> + <Tool + Name="VCCustomBuildTool"/> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="ws2_32.lib" + OutputFile="$(OutDir)/socket.dll" + LinkIncremental="1" + AdditionalLibraryDirectories="h:\lib" + GenerateDebugInformation="TRUE" + SubSystem="2" + OptimizeReferences="2" + EnableCOMDATFolding="2" + ImportLibrary="$(OutDir)/socket.lib" + TargetMachine="1"/> + <Tool + Name="VCMIDLTool"/> + <Tool + Name="VCPostBuildEventTool"/> + <Tool + Name="VCPreBuildEventTool"/> + <Tool + Name="VCPreLinkEventTool"/> + <Tool + Name="VCResourceCompilerTool"/> + <Tool + Name="VCWebServiceProxyGeneratorTool"/> + <Tool + Name="VCXMLDataGeneratorTool"/> + <Tool + Name="VCWebDeploymentTool"/> + <Tool + Name="VCManagedWrapperGeneratorTool"/> + <Tool + Name="VCAuxiliaryManagedWrapperGeneratorTool"/> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"> + <File + RelativePath="src\auxiliar.c"> + </File> + <File + RelativePath="src\buffer.c"> + </File> + <File + RelativePath="src\except.c"> + </File> + <File + RelativePath="src\inet.c"> + </File> + <File + RelativePath="src\io.c"> + </File> + <File + RelativePath="src\luasocket.c"> + </File> + <File + RelativePath="src\options.c"> + </File> + <File + RelativePath="src\select.c"> + </File> + <File + RelativePath="src\tcp.c"> + </File> + <File + RelativePath="src\timeout.c"> + </File> + <File + RelativePath="src\udp.c"> + </File> + <File + RelativePath="src\wsocket.c"> + <FileConfiguration + Name="Release|Win32"> + <Tool + Name="VCCLCompilerTool" + GeneratePreprocessedFile="0"/> + </FileConfiguration> + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"> + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"> + </Filter> + <File + RelativePath="..\..\lib\lua5.1.dll.lib"> + </File> + </Files> + <Globals> + </Globals> +</VisualStudioProject>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/CMakeLists.txt Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,87 @@ + + +SET(luasocket_SOCKET + ${LuaSocket_SOURCE_DIR}/src/luasocket.c + ${LuaSocket_SOURCE_DIR}/src/inet.h + ${LuaSocket_SOURCE_DIR}/src/inet.c + ${LuaSocket_SOURCE_DIR}/src/tcp.h + ${LuaSocket_SOURCE_DIR}/src/tcp.c + ${LuaSocket_SOURCE_DIR}/src/udp.h + ${LuaSocket_SOURCE_DIR}/src/udp.c + ${LuaSocket_SOURCE_DIR}/src/except.h + ${LuaSocket_SOURCE_DIR}/src/except.c + ${LuaSocket_SOURCE_DIR}/src/select.h + ${LuaSocket_SOURCE_DIR}/src/select.c +) + +SET(luasocket_UNIX + ${LuaSocket_SOURCE_DIR}/src/buffer.h + ${LuaSocket_SOURCE_DIR}/src/buffer.c + ${LuaSocket_SOURCE_DIR}/src/auxiliar.h + ${LuaSocket_SOURCE_DIR}/src/auxiliar.c + ${LuaSocket_SOURCE_DIR}/src/options.h + ${LuaSocket_SOURCE_DIR}/src/options.c + ${LuaSocket_SOURCE_DIR}/src/timeout.h + ${LuaSocket_SOURCE_DIR}/src/timeout.c + ${LuaSocket_SOURCE_DIR}/src/io.h + ${LuaSocket_SOURCE_DIR}/src/io.c + ${LuaSocket_SOURCE_DIR}/src/usocket.h + ${LuaSocket_SOURCE_DIR}/src/usocket.c + ${LuaSocket_SOURCE_DIR}/src/unix.h + ${LuaSocket_SOURCE_DIR}/src/unix.c +) + +SET(luasocket_PUBLIC_HEADERS + ${LuaSocket_SOURCE_DIR}/src/luasocket.h +) + +SET(luasocket_RESOURCE_FILES + ${LuaSocket_SOURCE_DIR}/src/http.lua + ${LuaSocket_SOURCE_DIR}/src/tp.lua + ${LuaSocket_SOURCE_DIR}/src/ftp.lua + ${LuaSocket_SOURCE_DIR}/src/smtp.lua + ${LuaSocket_SOURCE_DIR}/src/url.lua +) + +SET(luasocketcommon_RESOURCE_FILES + ${LuaSocket_SOURCE_DIR}/src/ltn12.lua + ${LuaSocket_SOURCE_DIR}/src/socket.lua + ${LuaSocket_SOURCE_DIR}/src/mime.lua +) + +IF(WANTS_BUILD_SHARED_LIBRARY) + ADD_LIBRARY(luasocket_library_dynamic MODULE ${luasocket_PUBLIC_HEADERS} ${luasocket_SOCKET} ${luasocket_UNIX}) + SET_TARGET_PROPERTIES(luasocket_library_dynamic PROPERTIES + PREFIX "" + OUTPUT_NAME "core" + ) + TARGET_LINK_LIBRARIES(luasocket_library_dynamic ${LUA_LIBRARIES} ${LUASOCKET_LINK_FLAGS}) + SET_TARGET_PROPERTIES(luasocket_library_dynamic PROPERTIES COMPILE_FLAGS "${LUASOCKET_C_FLAGS}") +ENDIF(WANTS_BUILD_SHARED_LIBRARY) +#IF(WANTS_BUILD_STATIC_LIBRARY) + ADD_LIBRARY(luasocket_library_static STATIC ${luasocket_PUBLIC_HEADERS} ${luasocket_SOCKET} ${luasocket_UNIX}) + SET_TARGET_PROPERTIES(luasocket_library_static PROPERTIES OUTPUT_NAME "socket") + SET_TARGET_PROPERTIES(luasocket_library_static PROPERTIES COMPILE_FLAGS "${LUASOCKET_C_FLAGS}") + TARGET_LINK_LIBRARIES(luasocket_library_static ${LUA_LIBRARIES} ${LUASOCKET_LINK_FLAGS}) + +#ENDIF(WANTS_BUILD_STATIC_LIBRARY) + + +INSTALL(TARGETS + luasocket_library_static + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib + # ARCHIVE DESTINATION lib/static +) + +IF(WANTS_BUILD_SHARED_LIBRARY) + INSTALL(TARGETS luasocket_library_dynamic DESTINATION ${LUAPACKAGE_CDIR}/socket) +ENDIF(WANTS_BUILD_SHARED_LIBRARY) + +INSTALL(FILES ${luasocket_PUBLIC_HEADERS} DESTINATION include) + +INSTALL(FILES ${luasocket_RESOURCE_FILES} DESTINATION ${LUAPACKAGE_LDIR}/socket) +INSTALL(FILES ${luasocketcommon_RESOURCE_FILES} DESTINATION ${LUAPACKAGE_LDIR}) + +ADD_SUBDIRECTORY(mime)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/auxiliar.c Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,149 @@ +/*=========================================================================*\ +* Auxiliar routines for class hierarchy manipulation +* LuaSocket toolkit +* +* RCS ID: $Id: auxiliar.c,v 1.14 2005/10/07 04:40:59 diego Exp $ +\*=========================================================================*/ +#include <string.h> +#include <stdio.h> + +#include "auxiliar.h" + +/*=========================================================================*\ +* Exported functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Initializes the module +\*-------------------------------------------------------------------------*/ +int auxiliar_open(lua_State *L) { + (void) L; + return 0; +} + +/*-------------------------------------------------------------------------*\ +* Creates a new class with given methods +* Methods whose names start with __ are passed directly to the metatable. +\*-------------------------------------------------------------------------*/ +void auxiliar_newclass(lua_State *L, const char *classname, luaL_reg *func) { + luaL_newmetatable(L, classname); /* mt */ + /* create __index table to place methods */ + lua_pushstring(L, "__index"); /* mt,"__index" */ + lua_newtable(L); /* mt,"__index",it */ + /* put class name into class metatable */ + lua_pushstring(L, "class"); /* mt,"__index",it,"class" */ + lua_pushstring(L, classname); /* mt,"__index",it,"class",classname */ + lua_rawset(L, -3); /* mt,"__index",it */ + /* pass all methods that start with _ to the metatable, and all others + * to the index table */ + for (; func->name; func++) { /* mt,"__index",it */ + lua_pushstring(L, func->name); + lua_pushcfunction(L, func->func); + lua_rawset(L, func->name[0] == '_' ? -5: -3); + } + lua_rawset(L, -3); /* mt */ + lua_pop(L, 1); +} + +/*-------------------------------------------------------------------------*\ +* Prints the value of a class in a nice way +\*-------------------------------------------------------------------------*/ +int auxiliar_tostring(lua_State *L) { + char buf[32]; + if (!lua_getmetatable(L, 1)) goto error; + lua_pushstring(L, "__index"); + lua_gettable(L, -2); + if (!lua_istable(L, -1)) goto error; + lua_pushstring(L, "class"); + lua_gettable(L, -2); + if (!lua_isstring(L, -1)) goto error; + sprintf(buf, "%p", lua_touserdata(L, 1)); + lua_pushfstring(L, "%s: %s", lua_tostring(L, -1), buf); + return 1; +error: + lua_pushstring(L, "invalid object passed to 'auxiliar.c:__tostring'"); + lua_error(L); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Insert class into group +\*-------------------------------------------------------------------------*/ +void auxiliar_add2group(lua_State *L, const char *classname, const char *groupname) { + luaL_getmetatable(L, classname); + lua_pushstring(L, groupname); + lua_pushboolean(L, 1); + lua_rawset(L, -3); + lua_pop(L, 1); +} + +/*-------------------------------------------------------------------------*\ +* Make sure argument is a boolean +\*-------------------------------------------------------------------------*/ +int auxiliar_checkboolean(lua_State *L, int objidx) { + if (!lua_isboolean(L, objidx)) + luaL_typerror(L, objidx, lua_typename(L, LUA_TBOOLEAN)); + return lua_toboolean(L, objidx); +} + +/*-------------------------------------------------------------------------*\ +* Return userdata pointer if object belongs to a given class, abort with +* error otherwise +\*-------------------------------------------------------------------------*/ +void *auxiliar_checkclass(lua_State *L, const char *classname, int objidx) { + void *data = auxiliar_getclassudata(L, classname, objidx); + if (!data) { + char msg[45]; + sprintf(msg, "%.35s expected", classname); + luaL_argerror(L, objidx, msg); + } + return data; +} + +/*-------------------------------------------------------------------------*\ +* Return userdata pointer if object belongs to a given group, abort with +* error otherwise +\*-------------------------------------------------------------------------*/ +void *auxiliar_checkgroup(lua_State *L, const char *groupname, int objidx) { + void *data = auxiliar_getgroupudata(L, groupname, objidx); + if (!data) { + char msg[45]; + sprintf(msg, "%.35s expected", groupname); + luaL_argerror(L, objidx, msg); + } + return data; +} + +/*-------------------------------------------------------------------------*\ +* Set object class +\*-------------------------------------------------------------------------*/ +void auxiliar_setclass(lua_State *L, const char *classname, int objidx) { + luaL_getmetatable(L, classname); + if (objidx < 0) objidx--; + lua_setmetatable(L, objidx); +} + +/*-------------------------------------------------------------------------*\ +* Get a userdata pointer if object belongs to a given group. Return NULL +* otherwise +\*-------------------------------------------------------------------------*/ +void *auxiliar_getgroupudata(lua_State *L, const char *groupname, int objidx) { + if (!lua_getmetatable(L, objidx)) + return NULL; + lua_pushstring(L, groupname); + lua_rawget(L, -2); + if (lua_isnil(L, -1)) { + lua_pop(L, 2); + return NULL; + } else { + lua_pop(L, 2); + return lua_touserdata(L, objidx); + } +} + +/*-------------------------------------------------------------------------*\ +* Get a userdata pointer if object belongs to a given class. Return NULL +* otherwise +\*-------------------------------------------------------------------------*/ +void *auxiliar_getclassudata(lua_State *L, const char *classname, int objidx) { + return luaL_checkudata(L, objidx, classname); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/auxiliar.h Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,48 @@ +#ifndef AUXILIAR_H +#define AUXILIAR_H +/*=========================================================================*\ +* Auxiliar routines for class hierarchy manipulation +* LuaSocket toolkit (but completely independent of other LuaSocket modules) +* +* A LuaSocket class is a name associated with Lua metatables. A LuaSocket +* group is a name associated with a class. A class can belong to any number +* of groups. This module provides the functionality to: +* +* - create new classes +* - add classes to groups +* - set the class of objects +* - check if an object belongs to a given class or group +* - get the userdata associated to objects +* - print objects in a pretty way +* +* LuaSocket class names follow the convention <module>{<class>}. Modules +* can define any number of classes and groups. The module tcp.c, for +* example, defines the classes tcp{master}, tcp{client} and tcp{server} and +* the groups tcp{client,server} and tcp{any}. Module functions can then +* perform type-checking on their arguments by either class or group. +* +* LuaSocket metatables define the __index metamethod as being a table. This +* table has one field for each method supported by the class, and a field +* "class" with the class name. +* +* The mapping from class name to the corresponding metatable and the +* reverse mapping are done using lauxlib. +* +* RCS ID: $Id: auxiliar.h,v 1.9 2005/10/07 04:40:59 diego Exp $ +\*=========================================================================*/ + +#include "lua.h" +#include "lauxlib.h" + +int auxiliar_open(lua_State *L); +void auxiliar_newclass(lua_State *L, const char *classname, luaL_reg *func); +void auxiliar_add2group(lua_State *L, const char *classname, const char *group); +void auxiliar_setclass(lua_State *L, const char *classname, int objidx); +void *auxiliar_checkclass(lua_State *L, const char *classname, int objidx); +void *auxiliar_checkgroup(lua_State *L, const char *groupname, int objidx); +void *auxiliar_getclassudata(lua_State *L, const char *groupname, int objidx); +void *auxiliar_getgroupudata(lua_State *L, const char *groupname, int objidx); +int auxiliar_checkboolean(lua_State *L, int objidx); +int auxiliar_tostring(lua_State *L); + +#endif /* AUXILIAR_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/buffer.c Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,268 @@ +/*=========================================================================*\ +* Input/Output interface for Lua programs +* LuaSocket toolkit +* +* RCS ID: $Id: buffer.c,v 1.28 2007/06/11 23:44:54 diego Exp $ +\*=========================================================================*/ +#include "lua.h" +#include "lauxlib.h" + +#include "buffer.h" + +/*=========================================================================*\ +* Internal function prototypes +\*=========================================================================*/ +static int recvraw(p_buffer buf, size_t wanted, luaL_Buffer *b); +static int recvline(p_buffer buf, luaL_Buffer *b); +static int recvall(p_buffer buf, luaL_Buffer *b); +static int buffer_get(p_buffer buf, const char **data, size_t *count); +static void buffer_skip(p_buffer buf, size_t count); +static int sendraw(p_buffer buf, const char *data, size_t count, size_t *sent); + +/* min and max macros */ +#ifndef MIN +#define MIN(x, y) ((x) < (y) ? x : y) +#endif +#ifndef MAX +#define MAX(x, y) ((x) > (y) ? x : y) +#endif + +/*=========================================================================*\ +* Exported functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int buffer_open(lua_State *L) { + (void) L; + return 0; +} + +/*-------------------------------------------------------------------------*\ +* Initializes C structure +\*-------------------------------------------------------------------------*/ +void buffer_init(p_buffer buf, p_io io, p_timeout tm) { + buf->first = buf->last = 0; + buf->io = io; + buf->tm = tm; + buf->received = buf->sent = 0; + buf->birthday = timeout_gettime(); +} + +/*-------------------------------------------------------------------------*\ +* object:getstats() interface +\*-------------------------------------------------------------------------*/ +int buffer_meth_getstats(lua_State *L, p_buffer buf) { + lua_pushnumber(L, buf->received); + lua_pushnumber(L, buf->sent); + lua_pushnumber(L, timeout_gettime() - buf->birthday); + return 3; +} + +/*-------------------------------------------------------------------------*\ +* object:setstats() interface +\*-------------------------------------------------------------------------*/ +int buffer_meth_setstats(lua_State *L, p_buffer buf) { + buf->received = (long) luaL_optnumber(L, 2, buf->received); + buf->sent = (long) luaL_optnumber(L, 3, buf->sent); + if (lua_isnumber(L, 4)) buf->birthday = timeout_gettime() - lua_tonumber(L, 4); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* object:send() interface +\*-------------------------------------------------------------------------*/ +int buffer_meth_send(lua_State *L, p_buffer buf) { + int top = lua_gettop(L); + int err = IO_DONE; + size_t size = 0, sent = 0; + const char *data = luaL_checklstring(L, 2, &size); + long start = (long) luaL_optnumber(L, 3, 1); + long end = (long) luaL_optnumber(L, 4, -1); + p_timeout tm = timeout_markstart(buf->tm); + if (start < 0) start = (long) (size+start+1); + if (end < 0) end = (long) (size+end+1); + if (start < 1) start = (long) 1; + if (end > (long) size) end = (long) size; + if (start <= end) err = sendraw(buf, data+start-1, end-start+1, &sent); + /* check if there was an error */ + if (err != IO_DONE) { + lua_pushnil(L); + lua_pushstring(L, buf->io->error(buf->io->ctx, err)); + lua_pushnumber(L, sent+start-1); + } else { + lua_pushnumber(L, sent+start-1); + lua_pushnil(L); + lua_pushnil(L); + } +#ifdef LUASOCKET_DEBUG + /* push time elapsed during operation as the last return value */ + lua_pushnumber(L, timeout_gettime() - timeout_getstart(tm)); +#endif + return lua_gettop(L) - top; +} + +/*-------------------------------------------------------------------------*\ +* object:receive() interface +\*-------------------------------------------------------------------------*/ +int buffer_meth_receive(lua_State *L, p_buffer buf) { + int err = IO_DONE, top = lua_gettop(L); + luaL_Buffer b; + size_t size; + const char *part = luaL_optlstring(L, 3, "", &size); + p_timeout tm = timeout_markstart(buf->tm); + /* initialize buffer with optional extra prefix + * (useful for concatenating previous partial results) */ + luaL_buffinit(L, &b); + luaL_addlstring(&b, part, size); + /* receive new patterns */ + if (!lua_isnumber(L, 2)) { + const char *p= luaL_optstring(L, 2, "*l"); + if (p[0] == '*' && p[1] == 'l') err = recvline(buf, &b); + else if (p[0] == '*' && p[1] == 'a') err = recvall(buf, &b); + else luaL_argcheck(L, 0, 2, "invalid receive pattern"); + /* get a fixed number of bytes (minus what was already partially + * received) */ + } else err = recvraw(buf, (size_t) lua_tonumber(L, 2)-size, &b); + /* check if there was an error */ + if (err != IO_DONE) { + /* we can't push anyting in the stack before pushing the + * contents of the buffer. this is the reason for the complication */ + luaL_pushresult(&b); + lua_pushstring(L, buf->io->error(buf->io->ctx, err)); + lua_pushvalue(L, -2); + lua_pushnil(L); + lua_replace(L, -4); + } else { + luaL_pushresult(&b); + lua_pushnil(L); + lua_pushnil(L); + } +#ifdef LUASOCKET_DEBUG + /* push time elapsed during operation as the last return value */ + lua_pushnumber(L, timeout_gettime() - timeout_getstart(tm)); +#endif + return lua_gettop(L) - top; +} + +/*-------------------------------------------------------------------------*\ +* Determines if there is any data in the read buffer +\*-------------------------------------------------------------------------*/ +int buffer_isempty(p_buffer buf) { + return buf->first >= buf->last; +} + +/*=========================================================================*\ +* Internal functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Sends a block of data (unbuffered) +\*-------------------------------------------------------------------------*/ +#define STEPSIZE 8192 +static int sendraw(p_buffer buf, const char *data, size_t count, size_t *sent) { + p_io io = buf->io; + p_timeout tm = buf->tm; + size_t total = 0; + int err = IO_DONE; + while (total < count && err == IO_DONE) { + size_t done; + size_t step = (count-total <= STEPSIZE)? count-total: STEPSIZE; + err = io->send(io->ctx, data+total, step, &done, tm); + total += done; + } + *sent = total; + buf->sent += total; + return err; +} + +/*-------------------------------------------------------------------------*\ +* Reads a fixed number of bytes (buffered) +\*-------------------------------------------------------------------------*/ +static int recvraw(p_buffer buf, size_t wanted, luaL_Buffer *b) { + int err = IO_DONE; + size_t total = 0; + while (err == IO_DONE) { + size_t count; const char *data; + err = buffer_get(buf, &data, &count); + count = MIN(count, wanted - total); + luaL_addlstring(b, data, count); + buffer_skip(buf, count); + total += count; + if (total >= wanted) break; + } + return err; +} + +/*-------------------------------------------------------------------------*\ +* Reads everything until the connection is closed (buffered) +\*-------------------------------------------------------------------------*/ +static int recvall(p_buffer buf, luaL_Buffer *b) { + int err = IO_DONE; + size_t total = 0; + while (err == IO_DONE) { + const char *data; size_t count; + err = buffer_get(buf, &data, &count); + total += count; + luaL_addlstring(b, data, count); + buffer_skip(buf, count); + } + if (err == IO_CLOSED) { + if (total > 0) return IO_DONE; + else return IO_CLOSED; + } else return err; +} + +/*-------------------------------------------------------------------------*\ +* Reads a line terminated by a CR LF pair or just by a LF. The CR and LF +* are not returned by the function and are discarded from the buffer +\*-------------------------------------------------------------------------*/ +static int recvline(p_buffer buf, luaL_Buffer *b) { + int err = IO_DONE; + while (err == IO_DONE) { + size_t count, pos; const char *data; + err = buffer_get(buf, &data, &count); + pos = 0; + while (pos < count && data[pos] != '\n') { + /* we ignore all \r's */ + if (data[pos] != '\r') luaL_putchar(b, data[pos]); + pos++; + } + if (pos < count) { /* found '\n' */ + buffer_skip(buf, pos+1); /* skip '\n' too */ + break; /* we are done */ + } else /* reached the end of the buffer */ + buffer_skip(buf, pos); + } + return err; +} + +/*-------------------------------------------------------------------------*\ +* Skips a given number of bytes from read buffer. No data is read from the +* transport layer +\*-------------------------------------------------------------------------*/ +static void buffer_skip(p_buffer buf, size_t count) { + buf->received += count; + buf->first += count; + if (buffer_isempty(buf)) + buf->first = buf->last = 0; +} + +/*-------------------------------------------------------------------------*\ +* Return any data available in buffer, or get more data from transport layer +* if buffer is empty +\*-------------------------------------------------------------------------*/ +static int buffer_get(p_buffer buf, const char **data, size_t *count) { + int err = IO_DONE; + p_io io = buf->io; + p_timeout tm = buf->tm; + if (buffer_isempty(buf)) { + size_t got; + err = io->recv(io->ctx, buf->data, BUF_SIZE, &got, tm); + buf->first = 0; + buf->last = got; + } + *count = buf->last - buf->first; + *data = buf->data + buf->first; + return err; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/buffer.h Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,47 @@ +#ifndef BUF_H +#define BUF_H +/*=========================================================================*\ +* Input/Output interface for Lua programs +* LuaSocket toolkit +* +* Line patterns require buffering. Reading one character at a time involves +* too many system calls and is very slow. This module implements the +* LuaSocket interface for input/output on connected objects, as seen by +* Lua programs. +* +* Input is buffered. Output is *not* buffered because there was no simple +* way of making sure the buffered output data would ever be sent. +* +* The module is built on top of the I/O abstraction defined in io.h and the +* timeout management is done with the timeout.h interface. +* +* RCS ID: $Id: buffer.h,v 1.12 2005/10/07 04:40:59 diego Exp $ +\*=========================================================================*/ +#include "lua.h" + +#include "io.h" +#include "timeout.h" + +/* buffer size in bytes */ +#define BUF_SIZE 8192 + +/* buffer control structure */ +typedef struct t_buffer_ { + double birthday; /* throttle support info: creation time, */ + size_t sent, received; /* bytes sent, and bytes received */ + p_io io; /* IO driver used for this buffer */ + p_timeout tm; /* timeout management for this buffer */ + size_t first, last; /* index of first and last bytes of stored data */ + char data[BUF_SIZE]; /* storage space for buffer data */ +} t_buffer; +typedef t_buffer *p_buffer; + +int buffer_open(lua_State *L); +void buffer_init(p_buffer buf, p_io io, p_timeout tm); +int buffer_meth_send(lua_State *L, p_buffer buf); +int buffer_meth_receive(lua_State *L, p_buffer buf); +int buffer_meth_getstats(lua_State *L, p_buffer buf); +int buffer_meth_setstats(lua_State *L, p_buffer buf); +int buffer_isempty(p_buffer buf); + +#endif /* BUF_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/except.c Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,99 @@ +/*=========================================================================*\ +* Simple exception support +* LuaSocket toolkit +* +* RCS ID: $Id: except.c,v 1.8 2005/09/29 06:11:41 diego Exp $ +\*=========================================================================*/ +#include <stdio.h> + +#include "lua.h" +#include "lauxlib.h" + +#include "except.h" + +/*=========================================================================*\ +* Internal function prototypes. +\*=========================================================================*/ +static int global_protect(lua_State *L); +static int global_newtry(lua_State *L); +static int protected_(lua_State *L); +static int finalize(lua_State *L); +static int do_nothing(lua_State *L); + +/* except functions */ +static luaL_reg func[] = { + {"newtry", global_newtry}, + {"protect", global_protect}, + {NULL, NULL} +}; + +/*-------------------------------------------------------------------------*\ +* Try factory +\*-------------------------------------------------------------------------*/ +static void wrap(lua_State *L) { + lua_newtable(L); + lua_pushnumber(L, 1); + lua_pushvalue(L, -3); + lua_settable(L, -3); + lua_insert(L, -2); + lua_pop(L, 1); +} + +static int finalize(lua_State *L) { + if (!lua_toboolean(L, 1)) { + lua_pushvalue(L, lua_upvalueindex(1)); + lua_pcall(L, 0, 0, 0); + lua_settop(L, 2); + wrap(L); + lua_error(L); + return 0; + } else return lua_gettop(L); +} + +static int do_nothing(lua_State *L) { + (void) L; + return 0; +} + +static int global_newtry(lua_State *L) { + lua_settop(L, 1); + if (lua_isnil(L, 1)) lua_pushcfunction(L, do_nothing); + lua_pushcclosure(L, finalize, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Protect factory +\*-------------------------------------------------------------------------*/ +static int unwrap(lua_State *L) { + if (lua_istable(L, -1)) { + lua_pushnumber(L, 1); + lua_gettable(L, -2); + lua_pushnil(L); + lua_insert(L, -2); + return 1; + } else return 0; +} + +static int protected_(lua_State *L) { + lua_pushvalue(L, lua_upvalueindex(1)); + lua_insert(L, 1); + if (lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0) != 0) { + if (unwrap(L)) return 2; + else lua_error(L); + return 0; + } else return lua_gettop(L); +} + +static int global_protect(lua_State *L) { + lua_pushcclosure(L, protected_, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Init module +\*-------------------------------------------------------------------------*/ +int except_open(lua_State *L) { + luaL_openlib(L, NULL, func, 0); + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/except.h Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,35 @@ +#ifndef EXCEPT_H +#define EXCEPT_H +/*=========================================================================*\ +* Exception control +* LuaSocket toolkit (but completely independent from other modules) +* +* This provides support for simple exceptions in Lua. During the +* development of the HTTP/FTP/SMTP support, it became aparent that +* error checking was taking a substantial amount of the coding. These +* function greatly simplify the task of checking errors. +* +* The main idea is that functions should return nil as its first return +* value when it finds an error, and return an error message (or value) +* following nil. In case of success, as long as the first value is not nil, +* the other values don't matter. +* +* The idea is to nest function calls with the "try" function. This function +* checks the first value, and calls "error" on the second if the first is +* nil. Otherwise, it returns all values it received. +* +* The protect function returns a new function that behaves exactly like the +* function it receives, but the new function doesn't throw exceptions: it +* returns nil followed by the error message instead. +* +* With these two function, it's easy to write functions that throw +* exceptions on error, but that don't interrupt the user script. +* +* RCS ID: $Id: except.h,v 1.2 2005/09/29 06:11:41 diego Exp $ +\*=========================================================================*/ + +#include "lua.h" + +int except_open(lua_State *L); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ftp.lua Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,281 @@ +----------------------------------------------------------------------------- +-- FTP support for the Lua language +-- LuaSocket toolkit. +-- Author: Diego Nehab +-- RCS ID: $Id: ftp.lua,v 1.45 2007/07/11 19:25:47 diego Exp $ +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +----------------------------------------------------------------------------- +local base = _G +local table = require("table") +local string = require("string") +local math = require("math") +local socket = require("socket") +local url = require("socket.url") +local tp = require("socket.tp") +local ltn12 = require("ltn12") +module("socket.ftp") + +----------------------------------------------------------------------------- +-- Program constants +----------------------------------------------------------------------------- +-- timeout in seconds before the program gives up on a connection +TIMEOUT = 60 +-- default port for ftp service +PORT = 21 +-- this is the default anonymous password. used when no password is +-- provided in url. should be changed to your e-mail. +USER = "ftp" +PASSWORD = "anonymous@anonymous.org" + +----------------------------------------------------------------------------- +-- Low level FTP API +----------------------------------------------------------------------------- +local metat = { __index = {} } + +function open(server, port, create) + local tp = socket.try(tp.connect(server, port or PORT, TIMEOUT, create)) + local f = base.setmetatable({ tp = tp }, metat) + -- make sure everything gets closed in an exception + f.try = socket.newtry(function() f:close() end) + return f +end + +function metat.__index:portconnect() + self.try(self.server:settimeout(TIMEOUT)) + self.data = self.try(self.server:accept()) + self.try(self.data:settimeout(TIMEOUT)) +end + +function metat.__index:pasvconnect() + self.data = self.try(socket.tcp()) + self.try(self.data:settimeout(TIMEOUT)) + self.try(self.data:connect(self.pasvt.ip, self.pasvt.port)) +end + +function metat.__index:login(user, password) + self.try(self.tp:command("user", user or USER)) + local code, reply = self.try(self.tp:check{"2..", 331}) + if code == 331 then + self.try(self.tp:command("pass", password or PASSWORD)) + self.try(self.tp:check("2..")) + end + return 1 +end + +function metat.__index:pasv() + self.try(self.tp:command("pasv")) + local code, reply = self.try(self.tp:check("2..")) + local pattern = "(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)" + local a, b, c, d, p1, p2 = socket.skip(2, string.find(reply, pattern)) + self.try(a and b and c and d and p1 and p2, reply) + self.pasvt = { + ip = string.format("%d.%d.%d.%d", a, b, c, d), + port = p1*256 + p2 + } + if self.server then + self.server:close() + self.server = nil + end + return self.pasvt.ip, self.pasvt.port +end + +function metat.__index:port(ip, port) + self.pasvt = nil + if not ip then + ip, port = self.try(self.tp:getcontrol():getsockname()) + self.server = self.try(socket.bind(ip, 0)) + ip, port = self.try(self.server:getsockname()) + self.try(self.server:settimeout(TIMEOUT)) + end + local pl = math.mod(port, 256) + local ph = (port - pl)/256 + local arg = string.gsub(string.format("%s,%d,%d", ip, ph, pl), "%.", ",") + self.try(self.tp:command("port", arg)) + self.try(self.tp:check("2..")) + return 1 +end + +function metat.__index:send(sendt) + self.try(self.pasvt or self.server, "need port or pasv first") + -- if there is a pasvt table, we already sent a PASV command + -- we just get the data connection into self.data + if self.pasvt then self:pasvconnect() end + -- get the transfer argument and command + local argument = sendt.argument or + url.unescape(string.gsub(sendt.path or "", "^[/\\]", "")) + if argument == "" then argument = nil end + local command = sendt.command or "stor" + -- send the transfer command and check the reply + self.try(self.tp:command(command, argument)) + local code, reply = self.try(self.tp:check{"2..", "1.."}) + -- if there is not a a pasvt table, then there is a server + -- and we already sent a PORT command + if not self.pasvt then self:portconnect() end + -- get the sink, source and step for the transfer + local step = sendt.step or ltn12.pump.step + local readt = {self.tp.c} + local checkstep = function(src, snk) + -- check status in control connection while downloading + local readyt = socket.select(readt, nil, 0) + if readyt[tp] then code = self.try(self.tp:check("2..")) end + return step(src, snk) + end + local sink = socket.sink("close-when-done", self.data) + -- transfer all data and check error + self.try(ltn12.pump.all(sendt.source, sink, checkstep)) + if string.find(code, "1..") then self.try(self.tp:check("2..")) end + -- done with data connection + self.data:close() + -- find out how many bytes were sent + local sent = socket.skip(1, self.data:getstats()) + self.data = nil + return sent +end + +function metat.__index:receive(recvt) + self.try(self.pasvt or self.server, "need port or pasv first") + if self.pasvt then self:pasvconnect() end + local argument = recvt.argument or + url.unescape(string.gsub(recvt.path or "", "^[/\\]", "")) + if argument == "" then argument = nil end + local command = recvt.command or "retr" + self.try(self.tp:command(command, argument)) + local code = self.try(self.tp:check{"1..", "2.."}) + if not self.pasvt then self:portconnect() end + local source = socket.source("until-closed", self.data) + local step = recvt.step or ltn12.pump.step + self.try(ltn12.pump.all(source, recvt.sink, step)) + if string.find(code, "1..") then self.try(self.tp:check("2..")) end + self.data:close() + self.data = nil + return 1 +end + +function metat.__index:cwd(dir) + self.try(self.tp:command("cwd", dir)) + self.try(self.tp:check(250)) + return 1 +end + +function metat.__index:type(type) + self.try(self.tp:command("type", type)) + self.try(self.tp:check(200)) + return 1 +end + +function metat.__index:greet() + local code = self.try(self.tp:check{"1..", "2.."}) + if string.find(code, "1..") then self.try(self.tp:check("2..")) end + return 1 +end + +function metat.__index:quit() + self.try(self.tp:command("quit")) + self.try(self.tp:check("2..")) + return 1 +end + +function metat.__index:close() + if self.data then self.data:close() end + if self.server then self.server:close() end + return self.tp:close() +end + +----------------------------------------------------------------------------- +-- High level FTP API +----------------------------------------------------------------------------- +local function override(t) + if t.url then + local u = url.parse(t.url) + for i,v in base.pairs(t) do + u[i] = v + end + return u + else return t end +end + +local function tput(putt) + putt = override(putt) + socket.try(putt.host, "missing hostname") + local f = open(putt.host, putt.port, putt.create) + f:greet() + f:login(putt.user, putt.password) + if putt.type then f:type(putt.type) end + f:pasv() + local sent = f:send(putt) + f:quit() + f:close() + return sent +end + +local default = { + path = "/", + scheme = "ftp" +} + +local function parse(u) + local t = socket.try(url.parse(u, default)) + socket.try(t.scheme == "ftp", "wrong scheme '" .. t.scheme .. "'") + socket.try(t.host, "missing hostname") + local pat = "^type=(.)$" + if t.params then + t.type = socket.skip(2, string.find(t.params, pat)) + socket.try(t.type == "a" or t.type == "i", + "invalid type '" .. t.type .. "'") + end + return t +end + +local function sput(u, body) + local putt = parse(u) + putt.source = ltn12.source.string(body) + return tput(putt) +end + +put = socket.protect(function(putt, body) + if base.type(putt) == "string" then return sput(putt, body) + else return tput(putt) end +end) + +local function tget(gett) + gett = override(gett) + socket.try(gett.host, "missing hostname") + local f = open(gett.host, gett.port, gett.create) + f:greet() + f:login(gett.user, gett.password) + if gett.type then f:type(gett.type) end + f:pasv() + f:receive(gett) + f:quit() + return f:close() +end + +local function sget(u) + local gett = parse(u) + local t = {} + gett.sink = ltn12.sink.table(t) + tget(gett) + return table.concat(t) +end + +command = socket.protect(function(cmdt) + cmdt = override(cmdt) + socket.try(cmdt.host, "missing hostname") + socket.try(cmdt.command, "missing command") + local f = open(cmdt.host, cmdt.port, cmdt.create) + f:greet() + f:login(cmdt.user, cmdt.password) + f.try(f.tp:command(cmdt.command, cmdt.argument)) + if cmdt.check then f.try(f.tp:check(cmdt.check)) end + f:quit() + return f:close() +end) + +get = socket.protect(function(gett) + if base.type(gett) == "string" then return sget(gett) + else return tget(gett) end +end) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/http.lua Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,350 @@ +----------------------------------------------------------------------------- +-- HTTP/1.1 client support for the Lua language. +-- LuaSocket toolkit. +-- Author: Diego Nehab +-- RCS ID: $Id: http.lua,v 1.71 2007/10/13 23:55:20 diego Exp $ +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +------------------------------------------------------------------------------- +local socket = require("socket") +local url = require("socket.url") +local ltn12 = require("ltn12") +local mime = require("mime") +local string = require("string") +local base = _G +local table = require("table") +module("socket.http") + +----------------------------------------------------------------------------- +-- Program constants +----------------------------------------------------------------------------- +-- connection timeout in seconds +TIMEOUT = 60 +-- default port for document retrieval +PORT = 80 +-- user agent field sent in request +USERAGENT = socket._VERSION + +----------------------------------------------------------------------------- +-- Reads MIME headers from a connection, unfolding where needed +----------------------------------------------------------------------------- +local function receiveheaders(sock, headers) + local line, name, value, err + headers = headers or {} + -- get first line + line, err = sock:receive() + if err then return nil, err end + -- headers go until a blank line is found + while line ~= "" do + -- get field-name and value + name, value = socket.skip(2, string.find(line, "^(.-):%s*(.*)")) + if not (name and value) then return nil, "malformed reponse headers" end + name = string.lower(name) + -- get next line (value might be folded) + line, err = sock:receive() + if err then return nil, err end + -- unfold any folded values + while string.find(line, "^%s") do + value = value .. line + line = sock:receive() + if err then return nil, err end + end + -- save pair in table + if headers[name] then headers[name] = headers[name] .. ", " .. value + else headers[name] = value end + end + return headers +end + +----------------------------------------------------------------------------- +-- Extra sources and sinks +----------------------------------------------------------------------------- +socket.sourcet["http-chunked"] = function(sock, headers) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function() + -- get chunk size, skip extention + local line, err = sock:receive() + if err then return nil, err end + local size = base.tonumber(string.gsub(line, ";.*", ""), 16) + if not size then return nil, "invalid chunk size" end + -- was it the last chunk? + if size > 0 then + -- if not, get chunk and skip terminating CRLF + local chunk, err, part = sock:receive(size) + if chunk then sock:receive() end + return chunk, err + else + -- if it was, read trailers into headers table + headers, err = receiveheaders(sock, headers) + if not headers then return nil, err end + end + end + }) +end + +socket.sinkt["http-chunked"] = function(sock) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function(self, chunk, err) + if not chunk then return sock:send("0\r\n\r\n") end + local size = string.format("%X\r\n", string.len(chunk)) + return sock:send(size .. chunk .. "\r\n") + end + }) +end + +----------------------------------------------------------------------------- +-- Low level HTTP API +----------------------------------------------------------------------------- +local metat = { __index = {} } + +function open(host, port, create) + -- create socket with user connect function, or with default + local c = socket.try((create or socket.tcp)()) + local h = base.setmetatable({ c = c }, metat) + -- create finalized try + h.try = socket.newtry(function() h:close() end) + -- set timeout before connecting + h.try(c:settimeout(TIMEOUT)) + h.try(c:connect(host, port or PORT)) + -- here everything worked + return h +end + +function metat.__index:sendrequestline(method, uri) + local reqline = string.format("%s %s HTTP/1.1\r\n", method or "GET", uri) + return self.try(self.c:send(reqline)) +end + +function metat.__index:sendheaders(headers) + local h = "\r\n" + for i, v in base.pairs(headers) do + h = i .. ": " .. v .. "\r\n" .. h + end + self.try(self.c:send(h)) + return 1 +end + +function metat.__index:sendbody(headers, source, step) + source = source or ltn12.source.empty() + step = step or ltn12.pump.step + -- if we don't know the size in advance, send chunked and hope for the best + local mode = "http-chunked" + if headers["content-length"] then mode = "keep-open" end + return self.try(ltn12.pump.all(source, socket.sink(mode, self.c), step)) +end + +function metat.__index:receivestatusline() + local status = self.try(self.c:receive(5)) + -- identify HTTP/0.9 responses, which do not contain a status line + -- this is just a heuristic, but is what the RFC recommends + if status ~= "HTTP/" then return nil, status end + -- otherwise proceed reading a status line + status = self.try(self.c:receive("*l", status)) + local code = socket.skip(2, string.find(status, "HTTP/%d*%.%d* (%d%d%d)")) + return self.try(base.tonumber(code), status) +end + +function metat.__index:receiveheaders() + return self.try(receiveheaders(self.c)) +end + +function metat.__index:receivebody(headers, sink, step) + sink = sink or ltn12.sink.null() + step = step or ltn12.pump.step + local length = base.tonumber(headers["content-length"]) + local t = headers["transfer-encoding"] -- shortcut + local mode = "default" -- connection close + if t and t ~= "identity" then mode = "http-chunked" + elseif base.tonumber(headers["content-length"]) then mode = "by-length" end + return self.try(ltn12.pump.all(socket.source(mode, self.c, length), + sink, step)) +end + +function metat.__index:receive09body(status, sink, step) + local source = ltn12.source.rewind(socket.source("until-closed", self.c)) + source(status) + return self.try(ltn12.pump.all(source, sink, step)) +end + +function metat.__index:close() + return self.c:close() +end + +----------------------------------------------------------------------------- +-- High level HTTP API +----------------------------------------------------------------------------- +local function adjusturi(reqt) + local u = reqt + -- if there is a proxy, we need the full url. otherwise, just a part. + if not reqt.proxy and not PROXY then + u = { + path = socket.try(reqt.path, "invalid path 'nil'"), + params = reqt.params, + query = reqt.query, + fragment = reqt.fragment + } + end + return url.build(u) +end + +local function adjustproxy(reqt) + local proxy = reqt.proxy or PROXY + if proxy then + proxy = url.parse(proxy) + return proxy.host, proxy.port or 3128 + else + return reqt.host, reqt.port + end +end + +local function adjustheaders(reqt) + -- default headers + local lower = { + ["user-agent"] = USERAGENT, + ["host"] = reqt.host, + ["connection"] = "close, TE", + ["te"] = "trailers" + } + -- if we have authentication information, pass it along + if reqt.user and reqt.password then + lower["authorization"] = + "Basic " .. (mime.b64(reqt.user .. ":" .. reqt.password)) + end + -- override with user headers + for i,v in base.pairs(reqt.headers or lower) do + lower[string.lower(i)] = v + end + return lower +end + +-- default url parts +local default = { + host = "", + port = PORT, + path ="/", + scheme = "http" +} + +local function adjustrequest(reqt) + -- parse url if provided + local nreqt = reqt.url and url.parse(reqt.url, default) or {} + -- explicit components override url + for i,v in base.pairs(reqt) do nreqt[i] = v end + if nreqt.port == "" then nreqt.port = 80 end + socket.try(nreqt.host and nreqt.host ~= "", + "invalid host '" .. base.tostring(nreqt.host) .. "'") + -- compute uri if user hasn't overriden + nreqt.uri = reqt.uri or adjusturi(nreqt) + -- ajust host and port if there is a proxy + nreqt.host, nreqt.port = adjustproxy(nreqt) + -- adjust headers in request + nreqt.headers = adjustheaders(nreqt) + return nreqt +end + +local function shouldredirect(reqt, code, headers) + return headers.location and + string.gsub(headers.location, "%s", "") ~= "" and + (reqt.redirect ~= false) and + (code == 301 or code == 302) and + (not reqt.method or reqt.method == "GET" or reqt.method == "HEAD") + and (not reqt.nredirects or reqt.nredirects < 5) +end + +local function shouldreceivebody(reqt, code) + if reqt.method == "HEAD" then return nil end + if code == 204 or code == 304 then return nil end + if code >= 100 and code < 200 then return nil end + return 1 +end + +-- forward declarations +local trequest, tredirect + +function tredirect(reqt, location) + local result, code, headers, status = trequest { + -- the RFC says the redirect URL has to be absolute, but some + -- servers do not respect that + url = url.absolute(reqt.url, location), + source = reqt.source, + sink = reqt.sink, + headers = reqt.headers, + proxy = reqt.proxy, + nredirects = (reqt.nredirects or 0) + 1, + create = reqt.create + } + -- pass location header back as a hint we redirected + headers = headers or {} + headers.location = headers.location or location + return result, code, headers, status +end + +function trequest(reqt) + -- we loop until we get what we want, or + -- until we are sure there is no way to get it + local nreqt = adjustrequest(reqt) + local h = open(nreqt.host, nreqt.port, nreqt.create) + -- send request line and headers + h:sendrequestline(nreqt.method, nreqt.uri) + h:sendheaders(nreqt.headers) + -- if there is a body, send it + if nreqt.source then + h:sendbody(nreqt.headers, nreqt.source, nreqt.step) + end + local code, status = h:receivestatusline() + -- if it is an HTTP/0.9 server, simply get the body and we are done + if not code then + h:receive09body(status, nreqt.sink, nreqt.step) + return 1, 200 + end + local headers + -- ignore any 100-continue messages + while code == 100 do + headers = h:receiveheaders() + code, status = h:receivestatusline() + end + headers = h:receiveheaders() + -- at this point we should have a honest reply from the server + -- we can't redirect if we already used the source, so we report the error + if shouldredirect(nreqt, code, headers) and not nreqt.source then + h:close() + return tredirect(reqt, headers.location) + end + -- here we are finally done + if shouldreceivebody(nreqt, code) then + h:receivebody(headers, nreqt.sink, nreqt.step) + end + h:close() + return 1, code, headers, status +end + +local function srequest(u, b) + local t = {} + local reqt = { + url = u, + sink = ltn12.sink.table(t) + } + if b then + reqt.source = ltn12.source.string(b) + reqt.headers = { + ["content-length"] = string.len(b), + ["content-type"] = "application/x-www-form-urlencoded" + } + reqt.method = "POST" + end + local code, headers, status = socket.skip(1, trequest(reqt)) + return table.concat(t), code, headers, status +end + +request = socket.protect(function(reqt, body) + if base.type(reqt) == "string" then return srequest(reqt, body) + else return trequest(reqt) end +end)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/inet.c Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,281 @@ +/*=========================================================================*\ +* Internet domain functions +* LuaSocket toolkit +* +* RCS ID: $Id: inet.c,v 1.28 2005/10/07 04:40:59 diego Exp $ +\*=========================================================================*/ +#include <stdio.h> +#include <string.h> + +#include "lua.h" +#include "lauxlib.h" + +#include "inet.h" + +/*=========================================================================*\ +* Internal function prototypes. +\*=========================================================================*/ +static int inet_global_toip(lua_State *L); +static int inet_global_tohostname(lua_State *L); +static void inet_pushresolved(lua_State *L, struct hostent *hp); +static int inet_global_gethostname(lua_State *L); + +/* DNS functions */ +static luaL_reg func[] = { + { "toip", inet_global_toip }, + { "tohostname", inet_global_tohostname }, + { "gethostname", inet_global_gethostname}, + { NULL, NULL} +}; + +/*=========================================================================*\ +* Exported functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int inet_open(lua_State *L) +{ + lua_pushstring(L, "dns"); + lua_newtable(L); + luaL_openlib(L, NULL, func, 0); + lua_settable(L, -3); + return 0; +} + +/*=========================================================================*\ +* Global Lua functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Returns all information provided by the resolver given a host name +* or ip address +\*-------------------------------------------------------------------------*/ +static int inet_gethost(const char *address, struct hostent **hp) { + struct in_addr addr; + if (inet_aton(address, &addr)) + return socket_gethostbyaddr((char *) &addr, sizeof(addr), hp); + else + return socket_gethostbyname(address, hp); +} + +/*-------------------------------------------------------------------------*\ +* Returns all information provided by the resolver given a host name +* or ip address +\*-------------------------------------------------------------------------*/ +static int inet_global_tohostname(lua_State *L) { + const char *address = luaL_checkstring(L, 1); + struct hostent *hp = NULL; + int err = inet_gethost(address, &hp); + if (err != IO_DONE) { + lua_pushnil(L); + lua_pushstring(L, socket_hoststrerror(err)); + return 2; + } + lua_pushstring(L, hp->h_name); + inet_pushresolved(L, hp); + return 2; +} + +/*-------------------------------------------------------------------------*\ +* Returns all information provided by the resolver given a host name +* or ip address +\*-------------------------------------------------------------------------*/ +static int inet_global_toip(lua_State *L) +{ + const char *address = luaL_checkstring(L, 1); + struct hostent *hp = NULL; + int err = inet_gethost(address, &hp); + if (err != IO_DONE) { + lua_pushnil(L); + lua_pushstring(L, socket_hoststrerror(err)); + return 2; + } + lua_pushstring(L, inet_ntoa(*((struct in_addr *) hp->h_addr))); + inet_pushresolved(L, hp); + return 2; +} + + +/*-------------------------------------------------------------------------*\ +* Gets the host name +\*-------------------------------------------------------------------------*/ +static int inet_global_gethostname(lua_State *L) +{ + char name[257]; + name[256] = '\0'; + if (gethostname(name, 256) < 0) { + lua_pushnil(L); + lua_pushstring(L, "gethostname failed"); + return 2; + } else { + lua_pushstring(L, name); + return 1; + } +} + + + +/*=========================================================================*\ +* Lua methods +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Retrieves socket peer name +\*-------------------------------------------------------------------------*/ +int inet_meth_getpeername(lua_State *L, p_socket ps) +{ + struct sockaddr_in peer; + socklen_t peer_len = sizeof(peer); + if (getpeername(*ps, (SA *) &peer, &peer_len) < 0) { + lua_pushnil(L); + lua_pushstring(L, "getpeername failed"); + } else { + lua_pushstring(L, inet_ntoa(peer.sin_addr)); + lua_pushnumber(L, ntohs(peer.sin_port)); + } + return 2; +} + +/*-------------------------------------------------------------------------*\ +* Retrieves socket local name +\*-------------------------------------------------------------------------*/ +int inet_meth_getsockname(lua_State *L, p_socket ps) +{ + struct sockaddr_in local; + socklen_t local_len = sizeof(local); + if (getsockname(*ps, (SA *) &local, &local_len) < 0) { + lua_pushnil(L); + lua_pushstring(L, "getsockname failed"); + } else { + lua_pushstring(L, inet_ntoa(local.sin_addr)); + lua_pushnumber(L, ntohs(local.sin_port)); + } + return 2; +} + +/*=========================================================================*\ +* Internal functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Passes all resolver information to Lua as a table +\*-------------------------------------------------------------------------*/ +static void inet_pushresolved(lua_State *L, struct hostent *hp) +{ + char **alias; + struct in_addr **addr; + int i, resolved; + lua_newtable(L); resolved = lua_gettop(L); + lua_pushstring(L, "name"); + lua_pushstring(L, hp->h_name); + lua_settable(L, resolved); + lua_pushstring(L, "ip"); + lua_pushstring(L, "alias"); + i = 1; + alias = hp->h_aliases; + lua_newtable(L); + if (alias) { + while (*alias) { + lua_pushnumber(L, i); + lua_pushstring(L, *alias); + lua_settable(L, -3); + i++; alias++; + } + } + lua_settable(L, resolved); + i = 1; + lua_newtable(L); + addr = (struct in_addr **) hp->h_addr_list; + if (addr) { + while (*addr) { + lua_pushnumber(L, i); + lua_pushstring(L, inet_ntoa(**addr)); + lua_settable(L, -3); + i++; addr++; + } + } + lua_settable(L, resolved); +} + +/*-------------------------------------------------------------------------*\ +* Tries to create a new inet socket +\*-------------------------------------------------------------------------*/ +const char *inet_trycreate(p_socket ps, int type) { + return socket_strerror(socket_create(ps, AF_INET, type, 0)); +} + +/*-------------------------------------------------------------------------*\ +* Tries to connect to remote address (address, port) +\*-------------------------------------------------------------------------*/ +const char *inet_tryconnect(p_socket ps, const char *address, + unsigned short port, p_timeout tm) +{ + struct sockaddr_in remote; + int err; + memset(&remote, 0, sizeof(remote)); + remote.sin_family = AF_INET; + remote.sin_port = htons(port); + if (strcmp(address, "*")) { + if (!inet_aton(address, &remote.sin_addr)) { + struct hostent *hp = NULL; + struct in_addr **addr; + err = socket_gethostbyname(address, &hp); + if (err != IO_DONE) return socket_hoststrerror(err); + addr = (struct in_addr **) hp->h_addr_list; + memcpy(&remote.sin_addr, *addr, sizeof(struct in_addr)); + } + } else remote.sin_family = AF_UNSPEC; + err = socket_connect(ps, (SA *) &remote, sizeof(remote), tm); + return socket_strerror(err); +} + +/*-------------------------------------------------------------------------*\ +* Tries to bind socket to (address, port) +\*-------------------------------------------------------------------------*/ +const char *inet_trybind(p_socket ps, const char *address, unsigned short port) +{ + struct sockaddr_in local; + int err; + memset(&local, 0, sizeof(local)); + /* address is either wildcard or a valid ip address */ + local.sin_addr.s_addr = htonl(INADDR_ANY); + local.sin_port = htons(port); + local.sin_family = AF_INET; + if (strcmp(address, "*") && !inet_aton(address, &local.sin_addr)) { + struct hostent *hp = NULL; + struct in_addr **addr; + err = socket_gethostbyname(address, &hp); + if (err != IO_DONE) return socket_hoststrerror(err); + addr = (struct in_addr **) hp->h_addr_list; + memcpy(&local.sin_addr, *addr, sizeof(struct in_addr)); + } + err = socket_bind(ps, (SA *) &local, sizeof(local)); + if (err != IO_DONE) socket_destroy(ps); + return socket_strerror(err); +} + +/*-------------------------------------------------------------------------*\ +* Some systems do not provide this so that we provide our own. It's not +* marvelously fast, but it works just fine. +\*-------------------------------------------------------------------------*/ +#ifdef INET_ATON +int inet_aton(const char *cp, struct in_addr *inp) +{ + unsigned int a = 0, b = 0, c = 0, d = 0; + int n = 0, r; + unsigned long int addr = 0; + r = sscanf(cp, "%u.%u.%u.%u%n", &a, &b, &c, &d, &n); + if (r == 0 || n == 0) return 0; + cp += n; + if (*cp) return 0; + if (a > 255 || b > 255 || c > 255 || d > 255) return 0; + if (inp) { + addr += a; addr <<= 8; + addr += b; addr <<= 8; + addr += c; addr <<= 8; + addr += d; + inp->s_addr = htonl(addr); + } + return 1; +} +#endif + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/inet.h Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,42 @@ +#ifndef INET_H +#define INET_H +/*=========================================================================*\ +* Internet domain functions +* LuaSocket toolkit +* +* This module implements the creation and connection of internet domain +* sockets, on top of the socket.h interface, and the interface of with the +* resolver. +* +* The function inet_aton is provided for the platforms where it is not +* available. The module also implements the interface of the internet +* getpeername and getsockname functions as seen by Lua programs. +* +* The Lua functions toip and tohostname are also implemented here. +* +* RCS ID: $Id: inet.h,v 1.16 2005/10/07 04:40:59 diego Exp $ +\*=========================================================================*/ +#include "lua.h" +#include "socket.h" +#include "timeout.h" + +#ifdef _WIN32 +#define INET_ATON +#endif + +int inet_open(lua_State *L); + +const char *inet_trycreate(p_socket ps, int type); +const char *inet_tryconnect(p_socket ps, const char *address, + unsigned short port, p_timeout tm); +const char *inet_trybind(p_socket ps, const char *address, + unsigned short port); + +int inet_meth_getpeername(lua_State *L, p_socket ps); +int inet_meth_getsockname(lua_State *L, p_socket ps); + +#ifdef INET_ATON +int inet_aton(const char *cp, struct in_addr *inp); +#endif + +#endif /* INET_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/io.c Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,32 @@ +/*=========================================================================*\ +* Input/Output abstraction +* LuaSocket toolkit +* +* RCS ID: $Id: io.c,v 1.6 2005/09/29 06:11:41 diego Exp $ +\*=========================================================================*/ +#include "io.h" + +/*=========================================================================*\ +* Exported functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Initializes C structure +\*-------------------------------------------------------------------------*/ +void io_init(p_io io, p_send send, p_recv recv, p_error error, void *ctx) { + io->send = send; + io->recv = recv; + io->error = error; + io->ctx = ctx; +} + +/*-------------------------------------------------------------------------*\ +* I/O error strings +\*-------------------------------------------------------------------------*/ +const char *io_strerror(int err) { + switch (err) { + case IO_DONE: return NULL; + case IO_CLOSED: return "closed"; + case IO_TIMEOUT: return "timeout"; + default: return "unknown error"; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/io.h Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,67 @@ +#ifndef IO_H +#define IO_H +/*=========================================================================*\ +* Input/Output abstraction +* LuaSocket toolkit +* +* This module defines the interface that LuaSocket expects from the +* transport layer for streamed input/output. The idea is that if any +* transport implements this interface, then the buffer.c functions +* automatically work on it. +* +* The module socket.h implements this interface, and thus the module tcp.h +* is very simple. +* +* RCS ID: $Id: io.h,v 1.11 2005/10/07 04:40:59 diego Exp $ +\*=========================================================================*/ +#include <stdio.h> +#include "lua.h" + +#include "timeout.h" + +/* IO error codes */ +enum { + IO_DONE = 0, /* operation completed successfully */ + IO_TIMEOUT = -1, /* operation timed out */ + IO_CLOSED = -2, /* the connection has been closed */ + IO_UNKNOWN = -3 +}; + +/* interface to error message function */ +typedef const char *(*p_error) ( + void *ctx, /* context needed by send */ + int err /* error code */ +); + +/* interface to send function */ +typedef int (*p_send) ( + void *ctx, /* context needed by send */ + const char *data, /* pointer to buffer with data to send */ + size_t count, /* number of bytes to send from buffer */ + size_t *sent, /* number of bytes sent uppon return */ + p_timeout tm /* timeout control */ +); + +/* interface to recv function */ +typedef int (*p_recv) ( + void *ctx, /* context needed by recv */ + char *data, /* pointer to buffer where data will be writen */ + size_t count, /* number of bytes to receive into buffer */ + size_t *got, /* number of bytes received uppon return */ + p_timeout tm /* timeout control */ +); + +/* IO driver definition */ +typedef struct t_io_ { + void *ctx; /* context needed by send/recv */ + p_send send; /* send function pointer */ + p_recv recv; /* receive function pointer */ + p_error error; /* strerror function */ +} t_io; +typedef t_io *p_io; + +void io_init(p_io io, p_send send, p_recv recv, p_error error, void *ctx); +const char *io_strerror(int err); + +#endif /* IO_H */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ltn12.lua Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,292 @@ +----------------------------------------------------------------------------- +-- LTN12 - Filters, sources, sinks and pumps. +-- LuaSocket toolkit. +-- Author: Diego Nehab +-- RCS ID: $Id: ltn12.lua,v 1.31 2006/04/03 04:45:42 diego Exp $ +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module +----------------------------------------------------------------------------- +local string = require("string") +local table = require("table") +local base = _G +module("ltn12") + +filter = {} +source = {} +sink = {} +pump = {} + +-- 2048 seems to be better in windows... +BLOCKSIZE = 2048 +_VERSION = "LTN12 1.0.1" + +----------------------------------------------------------------------------- +-- Filter stuff +----------------------------------------------------------------------------- +-- returns a high level filter that cycles a low-level filter +function filter.cycle(low, ctx, extra) + base.assert(low) + return function(chunk) + local ret + ret, ctx = low(ctx, chunk, extra) + return ret + end +end + +-- chains a bunch of filters together +-- (thanks to Wim Couwenberg) +function filter.chain(...) + local n = table.getn(arg) + local top, index = 1, 1 + local retry = "" + return function(chunk) + retry = chunk and retry + while true do + if index == top then + chunk = arg[index](chunk) + if chunk == "" or top == n then return chunk + elseif chunk then index = index + 1 + else + top = top+1 + index = top + end + else + chunk = arg[index](chunk or "") + if chunk == "" then + index = index - 1 + chunk = retry + elseif chunk then + if index == n then return chunk + else index = index + 1 end + else base.error("filter returned inappropriate nil") end + end + end + end +end + +----------------------------------------------------------------------------- +-- Source stuff +----------------------------------------------------------------------------- +-- create an empty source +local function empty() + return nil +end + +function source.empty() + return empty +end + +-- returns a source that just outputs an error +function source.error(err) + return function() + return nil, err + end +end + +-- creates a file source +function source.file(handle, io_err) + if handle then + return function() + local chunk = handle:read(BLOCKSIZE) + if not chunk then handle:close() end + return chunk + end + else return source.error(io_err or "unable to open file") end +end + +-- turns a fancy source into a simple source +function source.simplify(src) + base.assert(src) + return function() + local chunk, err_or_new = src() + src = err_or_new or src + if not chunk then return nil, err_or_new + else return chunk end + end +end + +-- creates string source +function source.string(s) + if s then + local i = 1 + return function() + local chunk = string.sub(s, i, i+BLOCKSIZE-1) + i = i + BLOCKSIZE + if chunk ~= "" then return chunk + else return nil end + end + else return source.empty() end +end + +-- creates rewindable source +function source.rewind(src) + base.assert(src) + local t = {} + return function(chunk) + if not chunk then + chunk = table.remove(t) + if not chunk then return src() + else return chunk end + else + table.insert(t, chunk) + end + end +end + +function source.chain(src, f) + base.assert(src and f) + local last_in, last_out = "", "" + local state = "feeding" + local err + return function() + if not last_out then + base.error('source is empty!', 2) + end + while true do + if state == "feeding" then + last_in, err = src() + if err then return nil, err end + last_out = f(last_in) + if not last_out then + if last_in then + base.error('filter returned inappropriate nil') + else + return nil + end + elseif last_out ~= "" then + state = "eating" + if last_in then last_in = "" end + return last_out + end + else + last_out = f(last_in) + if last_out == "" then + if last_in == "" then + state = "feeding" + else + base.error('filter returned ""') + end + elseif not last_out then + if last_in then + base.error('filter returned inappropriate nil') + else + return nil + end + else + return last_out + end + end + end + end +end + +-- creates a source that produces contents of several sources, one after the +-- other, as if they were concatenated +-- (thanks to Wim Couwenberg) +function source.cat(...) + local src = table.remove(arg, 1) + return function() + while src do + local chunk, err = src() + if chunk then return chunk end + if err then return nil, err end + src = table.remove(arg, 1) + end + end +end + +----------------------------------------------------------------------------- +-- Sink stuff +----------------------------------------------------------------------------- +-- creates a sink that stores into a table +function sink.table(t) + t = t or {} + local f = function(chunk, err) + if chunk then table.insert(t, chunk) end + return 1 + end + return f, t +end + +-- turns a fancy sink into a simple sink +function sink.simplify(snk) + base.assert(snk) + return function(chunk, err) + local ret, err_or_new = snk(chunk, err) + if not ret then return nil, err_or_new end + snk = err_or_new or snk + return 1 + end +end + +-- creates a file sink +function sink.file(handle, io_err) + if handle then + return function(chunk, err) + if not chunk then + handle:close() + return 1 + else return handle:write(chunk) end + end + else return sink.error(io_err or "unable to open file") end +end + +-- creates a sink that discards data +local function null() + return 1 +end + +function sink.null() + return null +end + +-- creates a sink that just returns an error +function sink.error(err) + return function() + return nil, err + end +end + +-- chains a sink with a filter +function sink.chain(f, snk) + base.assert(f and snk) + return function(chunk, err) + if chunk ~= "" then + local filtered = f(chunk) + local done = chunk and "" + while true do + local ret, snkerr = snk(filtered, err) + if not ret then return nil, snkerr end + if filtered == done then return 1 end + filtered = f(done) + end + else return 1 end + end +end + +----------------------------------------------------------------------------- +-- Pump stuff +----------------------------------------------------------------------------- +-- pumps one chunk from the source to the sink +function pump.step(src, snk) + local chunk, src_err = src() + local ret, snk_err = snk(chunk, src_err) + if chunk and ret then return 1 + else return nil, src_err or snk_err end +end + +-- pumps all data from a source to a sink, using a step function +function pump.all(src, snk, step) + base.assert(src and snk) + step = step or pump.step + while true do + local ret, err = step(src, snk) + if not ret then + if err then return nil, err + else return 1 end + end + end +end +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luasocket.c Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,118 @@ +/*=========================================================================*\ +* LuaSocket toolkit +* Networking support for the Lua language +* Diego Nehab +* 26/11/1999 +* +* This library is part of an effort to progressively increase the network +* connectivity of the Lua language. The Lua interface to networking +* functions follows the Sockets API closely, trying to simplify all tasks +* involved in setting up both client and server connections. The provided +* IO routines, however, follow the Lua style, being very similar to the +* standard Lua read and write functions. +* +* RCS ID: $Id: luasocket.c,v 1.53 2005/10/07 04:40:59 diego Exp $ +\*=========================================================================*/ + +/*=========================================================================*\ +* Standard include files +\*=========================================================================*/ +#include "lua.h" +#include "lauxlib.h" + +#if !defined(LUA_VERSION_NUM) || (LUA_VERSION_NUM < 501) +#include "compat-5.1.h" +#endif + +/*=========================================================================*\ +* LuaSocket includes +\*=========================================================================*/ +#include "luasocket.h" +#include "auxiliar.h" +#include "except.h" +#include "timeout.h" +#include "buffer.h" +#include "inet.h" +#include "tcp.h" +#include "udp.h" +#include "select.h" + +/*-------------------------------------------------------------------------*\ +* Internal function prototypes +\*-------------------------------------------------------------------------*/ +static int global_skip(lua_State *L); +static int global_unload(lua_State *L); +static int base_open(lua_State *L); + +/*-------------------------------------------------------------------------*\ +* Modules and functions +\*-------------------------------------------------------------------------*/ +static const luaL_reg mod[] = { + {"auxiliar", auxiliar_open}, + {"except", except_open}, + {"timeout", timeout_open}, + {"buffer", buffer_open}, + {"inet", inet_open}, + {"tcp", tcp_open}, + {"udp", udp_open}, + {"select", select_open}, + {NULL, NULL} +}; + +static luaL_reg func[] = { + {"skip", global_skip}, + {"__unload", global_unload}, + {NULL, NULL} +}; + +/*-------------------------------------------------------------------------*\ +* Skip a few arguments +\*-------------------------------------------------------------------------*/ +static int global_skip(lua_State *L) { + int amount = luaL_checkint(L, 1); + int ret = lua_gettop(L) - amount - 1; + return ret >= 0 ? ret : 0; +} + +/*-------------------------------------------------------------------------*\ +* Unloads the library +\*-------------------------------------------------------------------------*/ +static int global_unload(lua_State *L) { + (void) L; + socket_close(); + return 0; +} + +/*-------------------------------------------------------------------------*\ +* Setup basic stuff. +\*-------------------------------------------------------------------------*/ +static int base_open(lua_State *L) { + if (socket_open()) { + /* export functions (and leave namespace table on top of stack) */ + luaL_openlib(L, "socket", func, 0); +#ifdef LUASOCKET_DEBUG + lua_pushstring(L, "_DEBUG"); + lua_pushboolean(L, 1); + lua_rawset(L, -3); +#endif + /* make version string available to scripts */ + lua_pushstring(L, "_VERSION"); + lua_pushstring(L, LUASOCKET_VERSION); + lua_rawset(L, -3); + return 1; + } else { + lua_pushstring(L, "unable to initialize library"); + lua_error(L); + return 0; + } +} + +/*-------------------------------------------------------------------------*\ +* Initializes all library modules. +\*-------------------------------------------------------------------------*/ +LUASOCKET_API int luaopen_socket_core(lua_State *L) { + int i; + base_open(L); + for (i = 0; mod[i].name; i++) mod[i].func(L); + return 1; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luasocket.h Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,32 @@ +#ifndef LUASOCKET_H +#define LUASOCKET_H +/*=========================================================================*\ +* LuaSocket toolkit +* Networking support for the Lua language +* Diego Nehab +* 9/11/1999 +* +* RCS ID: $Id: luasocket.h,v 1.25 2007/06/11 23:44:54 diego Exp $ +\*=========================================================================*/ +#include "lua.h" + +/*-------------------------------------------------------------------------*\ +* Current socket library version +\*-------------------------------------------------------------------------*/ +#define LUASOCKET_VERSION "LuaSocket 2.0.2" +#define LUASOCKET_COPYRIGHT "Copyright (C) 2004-2007 Diego Nehab" +#define LUASOCKET_AUTHORS "Diego Nehab" + +/*-------------------------------------------------------------------------*\ +* This macro prefixes all exported API functions +\*-------------------------------------------------------------------------*/ +#ifndef LUASOCKET_API +#define LUASOCKET_API extern +#endif + +/*-------------------------------------------------------------------------*\ +* Initializes the library. +\*-------------------------------------------------------------------------*/ +LUASOCKET_API int luaopen_socket_core(lua_State *L); + +#endif /* LUASOCKET_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/makefile Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,90 @@ +#------ +# Load configuration +# +include ../config + +#------ +# Hopefully no need to change anything below this line +# + +#------ +# Modules belonging to socket-core +# + +#$(COMPAT)/compat-5.1.o \ + +SOCKET_OBJS:= \ + luasocket.o \ + timeout.o \ + buffer.o \ + io.o \ + auxiliar.o \ + options.o \ + inet.o \ + tcp.o \ + udp.o \ + except.o \ + select.o \ + usocket.o + +#------ +# Modules belonging mime-core +# +#$(COMPAT)/compat-5.1.o \ + +MIME_OBJS:=\ + mime.o + +#------ +# Modules belonging unix (local domain sockets) +# +UNIX_OBJS:=\ + buffer.o \ + auxiliar.o \ + options.o \ + timeout.o \ + io.o \ + usocket.o \ + unix.o + +all: $(SOCKET_SO) $(MIME_SO) + +$(SOCKET_SO): $(SOCKET_OBJS) + $(LD) $(LDFLAGS) -o $@ $(SOCKET_OBJS) + +$(MIME_SO): $(MIME_OBJS) + $(LD) $(LDFLAGS) -o $@ $(MIME_OBJS) + +$(UNIX_SO): $(UNIX_OBJS) + $(LD) $(LDFLAGS) -o $@ $(UNIX_OBJS) + +#------ +# List of dependencies +# +auxiliar.o: auxiliar.c auxiliar.h +buffer.o: buffer.c buffer.h io.h timeout.h +except.o: except.c except.h +inet.o: inet.c inet.h socket.h io.h timeout.h usocket.h +io.o: io.c io.h timeout.h +luasocket.o: luasocket.c luasocket.h auxiliar.h except.h timeout.h \ + buffer.h io.h inet.h socket.h usocket.h tcp.h udp.h select.h +mime.o: mime.c mime.h +options.o: options.c auxiliar.h options.h socket.h io.h timeout.h \ + usocket.h inet.h +select.o: select.c socket.h io.h timeout.h usocket.h select.h +tcp.o: tcp.c auxiliar.h socket.h io.h timeout.h usocket.h inet.h \ + options.h tcp.h buffer.h +timeout.o: timeout.c auxiliar.h timeout.h +udp.o: udp.c auxiliar.h socket.h io.h timeout.h usocket.h inet.h \ + options.h udp.h +unix.o: unix.c auxiliar.h socket.h io.h timeout.h usocket.h options.h \ + unix.h buffer.h +usocket.o: usocket.c socket.h io.h timeout.h usocket.h + +clean: + rm -f $(SOCKET_SO) $(SOCKET_OBJS) + rm -f $(MIME_SO) $(UNIX_SO) $(MIME_OBJS) $(UNIX_OBJS) + +#------ +# End of makefile configuration +#
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mime.c Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,711 @@ +/*=========================================================================*\ +* MIME support functions +* LuaSocket toolkit +* +* RCS ID: $Id: mime.c,v 1.28 2005/11/20 07:20:23 diego Exp $ +\*=========================================================================*/ +#include <string.h> + +#include "lua.h" +#include "lauxlib.h" + +#if !defined(LUA_VERSION_NUM) || (LUA_VERSION_NUM < 501) +#include "compat-5.1.h" +#endif + +#include "mime.h" + +/*=========================================================================*\ +* Don't want to trust escape character constants +\*=========================================================================*/ +typedef unsigned char UC; +static const char CRLF[] = "\r\n"; +static const char EQCRLF[] = "=\r\n"; + +/*=========================================================================*\ +* Internal function prototypes. +\*=========================================================================*/ +static int mime_global_wrp(lua_State *L); +static int mime_global_b64(lua_State *L); +static int mime_global_unb64(lua_State *L); +static int mime_global_qp(lua_State *L); +static int mime_global_unqp(lua_State *L); +static int mime_global_qpwrp(lua_State *L); +static int mime_global_eol(lua_State *L); +static int mime_global_dot(lua_State *L); + +static size_t dot(int c, size_t state, luaL_Buffer *buffer); +static void b64setup(UC *b64unbase); +static size_t b64encode(UC c, UC *input, size_t size, luaL_Buffer *buffer); +static size_t b64pad(const UC *input, size_t size, luaL_Buffer *buffer); +static size_t b64decode(UC c, UC *input, size_t size, luaL_Buffer *buffer); + +static void qpsetup(UC *qpclass, UC *qpunbase); +static void qpquote(UC c, luaL_Buffer *buffer); +static size_t qpdecode(UC c, UC *input, size_t size, luaL_Buffer *buffer); +static size_t qpencode(UC c, UC *input, size_t size, + const char *marker, luaL_Buffer *buffer); +static size_t qppad(UC *input, size_t size, luaL_Buffer *buffer); + +/* code support functions */ +static luaL_reg func[] = { + { "dot", mime_global_dot }, + { "b64", mime_global_b64 }, + { "eol", mime_global_eol }, + { "qp", mime_global_qp }, + { "qpwrp", mime_global_qpwrp }, + { "unb64", mime_global_unb64 }, + { "unqp", mime_global_unqp }, + { "wrp", mime_global_wrp }, + { NULL, NULL } +}; + +/*-------------------------------------------------------------------------*\ +* Quoted-printable globals +\*-------------------------------------------------------------------------*/ +static UC qpclass[256]; +static UC qpbase[] = "0123456789ABCDEF"; +static UC qpunbase[256]; +enum {QP_PLAIN, QP_QUOTED, QP_CR, QP_IF_LAST}; + +/*-------------------------------------------------------------------------*\ +* Base64 globals +\*-------------------------------------------------------------------------*/ +static const UC b64base[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static UC b64unbase[256]; + +/*=========================================================================*\ +* Exported functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +MIME_API int luaopen_mime_core(lua_State *L) +{ + luaL_openlib(L, "mime", func, 0); + /* make version string available to scripts */ + lua_pushstring(L, "_VERSION"); + lua_pushstring(L, MIME_VERSION); + lua_rawset(L, -3); + /* initialize lookup tables */ + qpsetup(qpclass, qpunbase); + b64setup(b64unbase); + return 1; +} + +/*=========================================================================*\ +* Global Lua functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Incrementaly breaks a string into lines. The string can have CRLF breaks. +* A, n = wrp(l, B, length) +* A is a copy of B, broken into lines of at most 'length' bytes. +* 'l' is how many bytes are left for the first line of B. +* 'n' is the number of bytes left in the last line of A. +\*-------------------------------------------------------------------------*/ +static int mime_global_wrp(lua_State *L) +{ + size_t size = 0; + int left = (int) luaL_checknumber(L, 1); + const UC *input = (UC *) luaL_optlstring(L, 2, NULL, &size); + const UC *last = input + size; + int length = (int) luaL_optnumber(L, 3, 76); + luaL_Buffer buffer; + /* end of input black-hole */ + if (!input) { + /* if last line has not been terminated, add a line break */ + if (left < length) lua_pushstring(L, CRLF); + /* otherwise, we are done */ + else lua_pushnil(L); + lua_pushnumber(L, length); + return 2; + } + luaL_buffinit(L, &buffer); + while (input < last) { + switch (*input) { + case '\r': + break; + case '\n': + luaL_addstring(&buffer, CRLF); + left = length; + break; + default: + if (left <= 0) { + left = length; + luaL_addstring(&buffer, CRLF); + } + luaL_putchar(&buffer, *input); + left--; + break; + } + input++; + } + luaL_pushresult(&buffer); + lua_pushnumber(L, left); + return 2; +} + +/*-------------------------------------------------------------------------*\ +* Fill base64 decode map. +\*-------------------------------------------------------------------------*/ +static void b64setup(UC *b64unbase) +{ + int i; + for (i = 0; i <= 255; i++) b64unbase[i] = (UC) 255; + for (i = 0; i < 64; i++) b64unbase[b64base[i]] = (UC) i; + b64unbase['='] = 0; +} + +/*-------------------------------------------------------------------------*\ +* Acumulates bytes in input buffer until 3 bytes are available. +* Translate the 3 bytes into Base64 form and append to buffer. +* Returns new number of bytes in buffer. +\*-------------------------------------------------------------------------*/ +static size_t b64encode(UC c, UC *input, size_t size, + luaL_Buffer *buffer) +{ + input[size++] = c; + if (size == 3) { + UC code[4]; + unsigned long value = 0; + value += input[0]; value <<= 8; + value += input[1]; value <<= 8; + value += input[2]; + code[3] = b64base[value & 0x3f]; value >>= 6; + code[2] = b64base[value & 0x3f]; value >>= 6; + code[1] = b64base[value & 0x3f]; value >>= 6; + code[0] = b64base[value]; + luaL_addlstring(buffer, (char *) code, 4); + size = 0; + } + return size; +} + +/*-------------------------------------------------------------------------*\ +* Encodes the Base64 last 1 or 2 bytes and adds padding '=' +* Result, if any, is appended to buffer. +* Returns 0. +\*-------------------------------------------------------------------------*/ +static size_t b64pad(const UC *input, size_t size, + luaL_Buffer *buffer) +{ + unsigned long value = 0; + UC code[4] = {'=', '=', '=', '='}; + switch (size) { + case 1: + value = input[0] << 4; + code[1] = b64base[value & 0x3f]; value >>= 6; + code[0] = b64base[value]; + luaL_addlstring(buffer, (char *) code, 4); + break; + case 2: + value = input[0]; value <<= 8; + value |= input[1]; value <<= 2; + code[2] = b64base[value & 0x3f]; value >>= 6; + code[1] = b64base[value & 0x3f]; value >>= 6; + code[0] = b64base[value]; + luaL_addlstring(buffer, (char *) code, 4); + break; + default: + break; + } + return 0; +} + +/*-------------------------------------------------------------------------*\ +* Acumulates bytes in input buffer until 4 bytes are available. +* Translate the 4 bytes from Base64 form and append to buffer. +* Returns new number of bytes in buffer. +\*-------------------------------------------------------------------------*/ +static size_t b64decode(UC c, UC *input, size_t size, + luaL_Buffer *buffer) +{ + /* ignore invalid characters */ + if (b64unbase[c] > 64) return size; + input[size++] = c; + /* decode atom */ + if (size == 4) { + UC decoded[3]; + int valid, value = 0; + value = b64unbase[input[0]]; value <<= 6; + value |= b64unbase[input[1]]; value <<= 6; + value |= b64unbase[input[2]]; value <<= 6; + value |= b64unbase[input[3]]; + decoded[2] = (UC) (value & 0xff); value >>= 8; + decoded[1] = (UC) (value & 0xff); value >>= 8; + decoded[0] = (UC) value; + /* take care of paddding */ + valid = (input[2] == '=') ? 1 : (input[3] == '=') ? 2 : 3; + luaL_addlstring(buffer, (char *) decoded, valid); + return 0; + /* need more data */ + } else return size; +} + +/*-------------------------------------------------------------------------*\ +* Incrementally applies the Base64 transfer content encoding to a string +* A, B = b64(C, D) +* A is the encoded version of the largest prefix of C .. D that is +* divisible by 3. B has the remaining bytes of C .. D, *without* encoding. +* The easiest thing would be to concatenate the two strings and +* encode the result, but we can't afford that or Lua would dupplicate +* every chunk we received. +\*-------------------------------------------------------------------------*/ +static int mime_global_b64(lua_State *L) +{ + UC atom[3]; + size_t isize = 0, asize = 0; + const UC *input = (UC *) luaL_optlstring(L, 1, NULL, &isize); + const UC *last = input + isize; + luaL_Buffer buffer; + /* end-of-input blackhole */ + if (!input) { + lua_pushnil(L); + lua_pushnil(L); + return 2; + } + /* process first part of the input */ + luaL_buffinit(L, &buffer); + while (input < last) + asize = b64encode(*input++, atom, asize, &buffer); + input = (UC *) luaL_optlstring(L, 2, NULL, &isize); + /* if second part is nil, we are done */ + if (!input) { + asize = b64pad(atom, asize, &buffer); + luaL_pushresult(&buffer); + if (!(*lua_tostring(L, -1))) lua_pushnil(L); + lua_pushnil(L); + return 2; + } + /* otherwise process the second part */ + last = input + isize; + while (input < last) + asize = b64encode(*input++, atom, asize, &buffer); + luaL_pushresult(&buffer); + lua_pushlstring(L, (char *) atom, asize); + return 2; +} + +/*-------------------------------------------------------------------------*\ +* Incrementally removes the Base64 transfer content encoding from a string +* A, B = b64(C, D) +* A is the encoded version of the largest prefix of C .. D that is +* divisible by 4. B has the remaining bytes of C .. D, *without* encoding. +\*-------------------------------------------------------------------------*/ +static int mime_global_unb64(lua_State *L) +{ + UC atom[4]; + size_t isize = 0, asize = 0; + const UC *input = (UC *) luaL_optlstring(L, 1, NULL, &isize); + const UC *last = input + isize; + luaL_Buffer buffer; + /* end-of-input blackhole */ + if (!input) { + lua_pushnil(L); + lua_pushnil(L); + return 2; + } + /* process first part of the input */ + luaL_buffinit(L, &buffer); + while (input < last) + asize = b64decode(*input++, atom, asize, &buffer); + input = (UC *) luaL_optlstring(L, 2, NULL, &isize); + /* if second is nil, we are done */ + if (!input) { + luaL_pushresult(&buffer); + if (!(*lua_tostring(L, -1))) lua_pushnil(L); + lua_pushnil(L); + return 2; + } + /* otherwise, process the rest of the input */ + last = input + isize; + while (input < last) + asize = b64decode(*input++, atom, asize, &buffer); + luaL_pushresult(&buffer); + lua_pushlstring(L, (char *) atom, asize); + return 2; +} + +/*-------------------------------------------------------------------------*\ +* Quoted-printable encoding scheme +* all (except CRLF in text) can be =XX +* CLRL in not text must be =XX=XX +* 33 through 60 inclusive can be plain +* 62 through 126 inclusive can be plain +* 9 and 32 can be plain, unless in the end of a line, where must be =XX +* encoded lines must be no longer than 76 not counting CRLF +* soft line-break are =CRLF +* To encode one byte, we need to see the next two. +* Worst case is when we see a space, and wonder if a CRLF is comming +\*-------------------------------------------------------------------------*/ +/*-------------------------------------------------------------------------*\ +* Split quoted-printable characters into classes +* Precompute reverse map for encoding +\*-------------------------------------------------------------------------*/ +static void qpsetup(UC *qpclass, UC *qpunbase) +{ + int i; + for (i = 0; i < 256; i++) qpclass[i] = QP_QUOTED; + for (i = 33; i <= 60; i++) qpclass[i] = QP_PLAIN; + for (i = 62; i <= 126; i++) qpclass[i] = QP_PLAIN; + qpclass['\t'] = QP_IF_LAST; + qpclass[' '] = QP_IF_LAST; + qpclass['\r'] = QP_CR; + for (i = 0; i < 256; i++) qpunbase[i] = 255; + qpunbase['0'] = 0; qpunbase['1'] = 1; qpunbase['2'] = 2; + qpunbase['3'] = 3; qpunbase['4'] = 4; qpunbase['5'] = 5; + qpunbase['6'] = 6; qpunbase['7'] = 7; qpunbase['8'] = 8; + qpunbase['9'] = 9; qpunbase['A'] = 10; qpunbase['a'] = 10; + qpunbase['B'] = 11; qpunbase['b'] = 11; qpunbase['C'] = 12; + qpunbase['c'] = 12; qpunbase['D'] = 13; qpunbase['d'] = 13; + qpunbase['E'] = 14; qpunbase['e'] = 14; qpunbase['F'] = 15; + qpunbase['f'] = 15; +} + +/*-------------------------------------------------------------------------*\ +* Output one character in form =XX +\*-------------------------------------------------------------------------*/ +static void qpquote(UC c, luaL_Buffer *buffer) +{ + luaL_putchar(buffer, '='); + luaL_putchar(buffer, qpbase[c >> 4]); + luaL_putchar(buffer, qpbase[c & 0x0F]); +} + +/*-------------------------------------------------------------------------*\ +* Accumulate characters until we are sure about how to deal with them. +* Once we are sure, output to the buffer, in the correct form. +\*-------------------------------------------------------------------------*/ +static size_t qpencode(UC c, UC *input, size_t size, + const char *marker, luaL_Buffer *buffer) +{ + input[size++] = c; + /* deal with all characters we can have */ + while (size > 0) { + switch (qpclass[input[0]]) { + /* might be the CR of a CRLF sequence */ + case QP_CR: + if (size < 2) return size; + if (input[1] == '\n') { + luaL_addstring(buffer, marker); + return 0; + } else qpquote(input[0], buffer); + break; + /* might be a space and that has to be quoted if last in line */ + case QP_IF_LAST: + if (size < 3) return size; + /* if it is the last, quote it and we are done */ + if (input[1] == '\r' && input[2] == '\n') { + qpquote(input[0], buffer); + luaL_addstring(buffer, marker); + return 0; + } else luaL_putchar(buffer, input[0]); + break; + /* might have to be quoted always */ + case QP_QUOTED: + qpquote(input[0], buffer); + break; + /* might never have to be quoted */ + default: + luaL_putchar(buffer, input[0]); + break; + } + input[0] = input[1]; input[1] = input[2]; + size--; + } + return 0; +} + +/*-------------------------------------------------------------------------*\ +* Deal with the final characters +\*-------------------------------------------------------------------------*/ +static size_t qppad(UC *input, size_t size, luaL_Buffer *buffer) +{ + size_t i; + for (i = 0; i < size; i++) { + if (qpclass[input[i]] == QP_PLAIN) luaL_putchar(buffer, input[i]); + else qpquote(input[i], buffer); + } + if (size > 0) luaL_addstring(buffer, EQCRLF); + return 0; +} + +/*-------------------------------------------------------------------------*\ +* Incrementally converts a string to quoted-printable +* A, B = qp(C, D, marker) +* Marker is the text to be used to replace CRLF sequences found in A. +* A is the encoded version of the largest prefix of C .. D that +* can be encoded without doubts. +* B has the remaining bytes of C .. D, *without* encoding. +\*-------------------------------------------------------------------------*/ +static int mime_global_qp(lua_State *L) +{ + + size_t asize = 0, isize = 0; + UC atom[3]; + const UC *input = (UC *) luaL_optlstring(L, 1, NULL, &isize); + const UC *last = input + isize; + const char *marker = luaL_optstring(L, 3, CRLF); + luaL_Buffer buffer; + /* end-of-input blackhole */ + if (!input) { + lua_pushnil(L); + lua_pushnil(L); + return 2; + } + /* process first part of input */ + luaL_buffinit(L, &buffer); + while (input < last) + asize = qpencode(*input++, atom, asize, marker, &buffer); + input = (UC *) luaL_optlstring(L, 2, NULL, &isize); + /* if second part is nil, we are done */ + if (!input) { + asize = qppad(atom, asize, &buffer); + luaL_pushresult(&buffer); + if (!(*lua_tostring(L, -1))) lua_pushnil(L); + lua_pushnil(L); + return 2; + } + /* otherwise process rest of input */ + last = input + isize; + while (input < last) + asize = qpencode(*input++, atom, asize, marker, &buffer); + luaL_pushresult(&buffer); + lua_pushlstring(L, (char *) atom, asize); + return 2; +} + +/*-------------------------------------------------------------------------*\ +* Accumulate characters until we are sure about how to deal with them. +* Once we are sure, output the to the buffer, in the correct form. +\*-------------------------------------------------------------------------*/ +static size_t qpdecode(UC c, UC *input, size_t size, luaL_Buffer *buffer) { + int d; + input[size++] = c; + /* deal with all characters we can deal */ + switch (input[0]) { + /* if we have an escape character */ + case '=': + if (size < 3) return size; + /* eliminate soft line break */ + if (input[1] == '\r' && input[2] == '\n') return 0; + /* decode quoted representation */ + c = qpunbase[input[1]]; d = qpunbase[input[2]]; + /* if it is an invalid, do not decode */ + if (c > 15 || d > 15) luaL_addlstring(buffer, (char *)input, 3); + else luaL_putchar(buffer, (c << 4) + d); + return 0; + case '\r': + if (size < 2) return size; + if (input[1] == '\n') luaL_addlstring(buffer, (char *)input, 2); + return 0; + default: + if (input[0] == '\t' || (input[0] > 31 && input[0] < 127)) + luaL_putchar(buffer, input[0]); + return 0; + } +} + +/*-------------------------------------------------------------------------*\ +* Incrementally decodes a string in quoted-printable +* A, B = qp(C, D) +* A is the decoded version of the largest prefix of C .. D that +* can be decoded without doubts. +* B has the remaining bytes of C .. D, *without* decoding. +\*-------------------------------------------------------------------------*/ +static int mime_global_unqp(lua_State *L) +{ + size_t asize = 0, isize = 0; + UC atom[3]; + const UC *input = (UC *) luaL_optlstring(L, 1, NULL, &isize); + const UC *last = input + isize; + luaL_Buffer buffer; + /* end-of-input blackhole */ + if (!input) { + lua_pushnil(L); + lua_pushnil(L); + return 2; + } + /* process first part of input */ + luaL_buffinit(L, &buffer); + while (input < last) + asize = qpdecode(*input++, atom, asize, &buffer); + input = (UC *) luaL_optlstring(L, 2, NULL, &isize); + /* if second part is nil, we are done */ + if (!input) { + luaL_pushresult(&buffer); + if (!(*lua_tostring(L, -1))) lua_pushnil(L); + lua_pushnil(L); + return 2; + } + /* otherwise process rest of input */ + last = input + isize; + while (input < last) + asize = qpdecode(*input++, atom, asize, &buffer); + luaL_pushresult(&buffer); + lua_pushlstring(L, (char *) atom, asize); + return 2; +} + +/*-------------------------------------------------------------------------*\ +* Incrementally breaks a quoted-printed string into lines +* A, n = qpwrp(l, B, length) +* A is a copy of B, broken into lines of at most 'length' bytes. +* 'l' is how many bytes are left for the first line of B. +* 'n' is the number of bytes left in the last line of A. +* There are two complications: lines can't be broken in the middle +* of an encoded =XX, and there might be line breaks already +\*-------------------------------------------------------------------------*/ +static int mime_global_qpwrp(lua_State *L) +{ + size_t size = 0; + int left = (int) luaL_checknumber(L, 1); + const UC *input = (UC *) luaL_optlstring(L, 2, NULL, &size); + const UC *last = input + size; + int length = (int) luaL_optnumber(L, 3, 76); + luaL_Buffer buffer; + /* end-of-input blackhole */ + if (!input) { + if (left < length) lua_pushstring(L, EQCRLF); + else lua_pushnil(L); + lua_pushnumber(L, length); + return 2; + } + /* process all input */ + luaL_buffinit(L, &buffer); + while (input < last) { + switch (*input) { + case '\r': + break; + case '\n': + left = length; + luaL_addstring(&buffer, CRLF); + break; + case '=': + if (left <= 3) { + left = length; + luaL_addstring(&buffer, EQCRLF); + } + luaL_putchar(&buffer, *input); + left--; + break; + default: + if (left <= 1) { + left = length; + luaL_addstring(&buffer, EQCRLF); + } + luaL_putchar(&buffer, *input); + left--; + break; + } + input++; + } + luaL_pushresult(&buffer); + lua_pushnumber(L, left); + return 2; +} + +/*-------------------------------------------------------------------------*\ +* Here is what we do: \n, and \r are considered candidates for line +* break. We issue *one* new line marker if any of them is seen alone, or +* followed by a different one. That is, \n\n and \r\r will issue two +* end of line markers each, but \r\n, \n\r etc will only issue *one* +* marker. This covers Mac OS, Mac OS X, VMS, Unix and DOS, as well as +* probably other more obscure conventions. +* +* c is the current character being processed +* last is the previous character +\*-------------------------------------------------------------------------*/ +#define eolcandidate(c) (c == '\r' || c == '\n') +static int eolprocess(int c, int last, const char *marker, + luaL_Buffer *buffer) +{ + if (eolcandidate(c)) { + if (eolcandidate(last)) { + if (c == last) luaL_addstring(buffer, marker); + return 0; + } else { + luaL_addstring(buffer, marker); + return c; + } + } else { + luaL_putchar(buffer, c); + return 0; + } +} + +/*-------------------------------------------------------------------------*\ +* Converts a string to uniform EOL convention. +* A, n = eol(o, B, marker) +* A is the converted version of the largest prefix of B that can be +* converted unambiguously. 'o' is the context returned by the previous +* call. 'n' is the new context. +\*-------------------------------------------------------------------------*/ +static int mime_global_eol(lua_State *L) +{ + int ctx = luaL_checkint(L, 1); + size_t isize = 0; + const char *input = luaL_optlstring(L, 2, NULL, &isize); + const char *last = input + isize; + const char *marker = luaL_optstring(L, 3, CRLF); + luaL_Buffer buffer; + luaL_buffinit(L, &buffer); + /* end of input blackhole */ + if (!input) { + lua_pushnil(L); + lua_pushnumber(L, 0); + return 2; + } + /* process all input */ + while (input < last) + ctx = eolprocess(*input++, ctx, marker, &buffer); + luaL_pushresult(&buffer); + lua_pushnumber(L, ctx); + return 2; +} + +/*-------------------------------------------------------------------------*\ +* Takes one byte and stuff it if needed. +\*-------------------------------------------------------------------------*/ +static size_t dot(int c, size_t state, luaL_Buffer *buffer) +{ + luaL_putchar(buffer, c); + switch (c) { + case '\r': + return 1; + case '\n': + return (state == 1)? 2: 0; + case '.': + if (state == 2) + luaL_putchar(buffer, '.'); + default: + return 0; + } +} + +/*-------------------------------------------------------------------------*\ +* Incrementally applies smtp stuffing to a string +* A, n = dot(l, D) +\*-------------------------------------------------------------------------*/ +static int mime_global_dot(lua_State *L) +{ + size_t isize = 0, state = (size_t) luaL_checknumber(L, 1); + const char *input = luaL_optlstring(L, 2, NULL, &isize); + const char *last = input + isize; + luaL_Buffer buffer; + /* end-of-input blackhole */ + if (!input) { + lua_pushnil(L); + lua_pushnumber(L, 2); + return 2; + } + /* process all input */ + luaL_buffinit(L, &buffer); + while (input < last) + state = dot(*input++, state, &buffer); + luaL_pushresult(&buffer); + lua_pushnumber(L, state); + return 2; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mime.h Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,31 @@ +#ifndef MIME_H +#define MIME_H +/*=========================================================================*\ +* Core MIME support +* LuaSocket toolkit +* +* This module provides functions to implement transfer content encodings +* and formatting conforming to RFC 2045. It is used by mime.lua, which +* provide a higher level interface to this functionality. +* +* RCS ID: $Id: mime.h,v 1.15 2007/06/11 23:44:54 diego Exp $ +\*=========================================================================*/ +#include "lua.h" + +/*-------------------------------------------------------------------------*\ +* Current MIME library version +\*-------------------------------------------------------------------------*/ +#define MIME_VERSION "MIME 1.0.2" +#define MIME_COPYRIGHT "Copyright (C) 2004-2007 Diego Nehab" +#define MIME_AUTHORS "Diego Nehab" + +/*-------------------------------------------------------------------------*\ +* This macro prefixes all exported API functions +\*-------------------------------------------------------------------------*/ +#ifndef MIME_API +#define MIME_API extern +#endif + +MIME_API int luaopen_mime_core(lua_State *L); + +#endif /* MIME_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mime.lua Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,87 @@ +----------------------------------------------------------------------------- +-- MIME support for the Lua language. +-- Author: Diego Nehab +-- Conforming to RFCs 2045-2049 +-- RCS ID: $Id: mime.lua,v 1.29 2007/06/11 23:44:54 diego Exp $ +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +----------------------------------------------------------------------------- +local base = _G +local ltn12 = require("ltn12") +local mime = require("mime.core") +local io = require("io") +local string = require("string") +module("mime") + +-- encode, decode and wrap algorithm tables +encodet = {} +decodet = {} +wrapt = {} + +-- creates a function that chooses a filter by name from a given table +local function choose(table) + return function(name, opt1, opt2) + if base.type(name) ~= "string" then + name, opt1, opt2 = "default", name, opt1 + end + local f = table[name or "nil"] + if not f then + base.error("unknown key (" .. base.tostring(name) .. ")", 3) + else return f(opt1, opt2) end + end +end + +-- define the encoding filters +encodet['base64'] = function() + return ltn12.filter.cycle(b64, "") +end + +encodet['quoted-printable'] = function(mode) + return ltn12.filter.cycle(qp, "", + (mode == "binary") and "=0D=0A" or "\r\n") +end + +-- define the decoding filters +decodet['base64'] = function() + return ltn12.filter.cycle(unb64, "") +end + +decodet['quoted-printable'] = function() + return ltn12.filter.cycle(unqp, "") +end + +local function format(chunk) + if chunk then + if chunk == "" then return "''" + else return string.len(chunk) end + else return "nil" end +end + +-- define the line-wrap filters +wrapt['text'] = function(length) + length = length or 76 + return ltn12.filter.cycle(wrp, length, length) +end +wrapt['base64'] = wrapt['text'] +wrapt['default'] = wrapt['text'] + +wrapt['quoted-printable'] = function() + return ltn12.filter.cycle(qpwrp, 76, 76) +end + +-- function that choose the encoding, decoding or wrap algorithm +encode = choose(encodet) +decode = choose(decodet) +wrap = choose(wrapt) + +-- define the end-of-line normalization filter +function normalize(marker) + return ltn12.filter.cycle(eol, 0, marker) +end + +-- high level stuffing filter +function stuff() + return ltn12.filter.cycle(dot, 2) +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mime/CMakeLists.txt Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,45 @@ +# Ugh. I'm very irritated that the Luasocket people rename the modules to "core". (No prefix, .dll/.so suffix). This makes life difficult because I can't build the libraries directly with the name because there are two libraries (socket, mime) so the names conflict with each other. +# Furthermore, CMake doesn't offer good support for renaming TARGETS on install. +# The easiest way to work around this problem was create a new subdirectory just for the mime plugin and build directly to the name 'core'. + + +SET(luasocket_MIME + ${LuaSocket_SOURCE_DIR}/src/mime.h + ${LuaSocket_SOURCE_DIR}/src/mime.c +) +SET(luamime_RESOURCE_FILES +# ${LuaSocket_SOURCE_DIR}/src/mime.lua +) + + +IF(WANTS_BUILD_SHARED_LIBRARY) + ADD_LIBRARY(luamime_library_dynamic MODULE ${luasocket_PUBLIC_HEADERS} ${luasocket_MIME}) + SET_TARGET_PROPERTIES(luamime_library_dynamic PROPERTIES + PREFIX "" + OUTPUT_NAME "core" + ) + SET_TARGET_PROPERTIES(luamime_library_dynamic PROPERTIES COMPILE_FLAGS "${LUASOCKET_C_FLAGS}") + TARGET_LINK_LIBRARIES(luamime_library_dynamic ${LUA_LIBRARIES} ${LUASOCKET_LINK_FLAGS}) +ENDIF(WANTS_BUILD_SHARED_LIBRARY) +#IF(WANTS_BUILD_STATIC_LIBRARY) + ADD_LIBRARY(luamime_library_static STATIC ${luasocket_PUBLIC_HEADERS} ${luasocket_MIME}) + SET_TARGET_PROPERTIES(luamime_library_static PROPERTIES OUTPUT_NAME "mime") + SET_TARGET_PROPERTIES(luamime_library_static PROPERTIES COMPILE_FLAGS "${LUASOCKET_C_FLAGS}") + TARGET_LINK_LIBRARIES(luamime_library_static ${LUA_LIBRARIES} ${LUASOCKET_LINK_FLAGS}) +#ENDIF(WANTS_BUILD_STATIC_LIBRARY) + +INSTALL(TARGETS + luamime_library_static + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib + # ARCHIVE DESTINATION lib/static +) + +IF(WANTS_BUILD_SHARED_LIBRARY) + INSTALL(TARGETS luamime_library_dynamic DESTINATION ${LUAPACKAGE_CDIR}/mime) +ENDIF(WANTS_BUILD_SHARED_LIBRARY) + +INSTALL(FILES ${luamime_RESOURCE_FILES} DESTINATION ${LUAPACKAGE_LDIR}/mime) + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/options.c Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,149 @@ +/*=========================================================================*\ +* Common option interface +* LuaSocket toolkit +* +* RCS ID: $Id: options.c,v 1.6 2005/11/20 07:20:23 diego Exp $ +\*=========================================================================*/ +#include <string.h> + +#include "lauxlib.h" + +#include "auxiliar.h" +#include "options.h" +#include "inet.h" + + +/*=========================================================================*\ +* Internal functions prototypes +\*=========================================================================*/ +static int opt_setmembership(lua_State *L, p_socket ps, int level, int name); +static int opt_setboolean(lua_State *L, p_socket ps, int level, int name); +static int opt_set(lua_State *L, p_socket ps, int level, int name, + void *val, int len); + +/*=========================================================================*\ +* Exported functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Calls appropriate option handler +\*-------------------------------------------------------------------------*/ +int opt_meth_setoption(lua_State *L, p_opt opt, p_socket ps) +{ + const char *name = luaL_checkstring(L, 2); /* obj, name, ... */ + while (opt->name && strcmp(name, opt->name)) + opt++; + if (!opt->func) { + char msg[45]; + sprintf(msg, "unsupported option `%.35s'", name); + luaL_argerror(L, 2, msg); + } + return opt->func(L, ps); +} + +/* enables reuse of local address */ +int opt_reuseaddr(lua_State *L, p_socket ps) +{ + return opt_setboolean(L, ps, SOL_SOCKET, SO_REUSEADDR); +} + +/* disables the Naggle algorithm */ +int opt_tcp_nodelay(lua_State *L, p_socket ps) +{ + return opt_setboolean(L, ps, IPPROTO_TCP, TCP_NODELAY); +} + +int opt_keepalive(lua_State *L, p_socket ps) +{ + return opt_setboolean(L, ps, SOL_SOCKET, SO_KEEPALIVE); +} + +int opt_dontroute(lua_State *L, p_socket ps) +{ + return opt_setboolean(L, ps, SOL_SOCKET, SO_DONTROUTE); +} + +int opt_broadcast(lua_State *L, p_socket ps) +{ + return opt_setboolean(L, ps, SOL_SOCKET, SO_BROADCAST); +} + +int opt_ip_multicast_loop(lua_State *L, p_socket ps) +{ + return opt_setboolean(L, ps, IPPROTO_IP, IP_MULTICAST_LOOP); +} + +int opt_linger(lua_State *L, p_socket ps) +{ + struct linger li; /* obj, name, table */ + if (!lua_istable(L, 3)) luaL_typerror(L, 3, lua_typename(L, LUA_TTABLE)); + lua_pushstring(L, "on"); + lua_gettable(L, 3); + if (!lua_isboolean(L, -1)) + luaL_argerror(L, 3, "boolean 'on' field expected"); + li.l_onoff = (u_short) lua_toboolean(L, -1); + lua_pushstring(L, "timeout"); + lua_gettable(L, 3); + if (!lua_isnumber(L, -1)) + luaL_argerror(L, 3, "number 'timeout' field expected"); + li.l_linger = (u_short) lua_tonumber(L, -1); + return opt_set(L, ps, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof(li)); +} + +int opt_ip_multicast_ttl(lua_State *L, p_socket ps) +{ + int val = (int) luaL_checknumber(L, 3); /* obj, name, int */ + return opt_set(L, ps, SOL_SOCKET, SO_LINGER, (char *) &val, sizeof(val)); +} + +int opt_ip_add_membership(lua_State *L, p_socket ps) +{ + return opt_setmembership(L, ps, IPPROTO_IP, IP_ADD_MEMBERSHIP); +} + +int opt_ip_drop_membersip(lua_State *L, p_socket ps) +{ + return opt_setmembership(L, ps, IPPROTO_IP, IP_DROP_MEMBERSHIP); +} + +/*=========================================================================*\ +* Auxiliar functions +\*=========================================================================*/ +static int opt_setmembership(lua_State *L, p_socket ps, int level, int name) +{ + struct ip_mreq val; /* obj, name, table */ + if (!lua_istable(L, 3)) luaL_typerror(L, 3, lua_typename(L, LUA_TTABLE)); + lua_pushstring(L, "multiaddr"); + lua_gettable(L, 3); + if (!lua_isstring(L, -1)) + luaL_argerror(L, 3, "string 'multiaddr' field expected"); + if (!inet_aton(lua_tostring(L, -1), &val.imr_multiaddr)) + luaL_argerror(L, 3, "invalid 'multiaddr' ip address"); + lua_pushstring(L, "interface"); + lua_gettable(L, 3); + if (!lua_isstring(L, -1)) + luaL_argerror(L, 3, "string 'interface' field expected"); + val.imr_interface.s_addr = htonl(INADDR_ANY); + if (strcmp(lua_tostring(L, -1), "*") && + !inet_aton(lua_tostring(L, -1), &val.imr_interface)) + luaL_argerror(L, 3, "invalid 'interface' ip address"); + return opt_set(L, ps, level, name, (char *) &val, sizeof(val)); +} + +static +int opt_set(lua_State *L, p_socket ps, int level, int name, void *val, int len) +{ + if (setsockopt(*ps, level, name, (char *) val, len) < 0) { + lua_pushnil(L); + lua_pushstring(L, "setsockopt failed"); + return 2; + } + lua_pushnumber(L, 1); + return 1; +} + +static int opt_setboolean(lua_State *L, p_socket ps, int level, int name) +{ + int val = auxiliar_checkboolean(L, 3); /* obj, name, bool */ + return opt_set(L, ps, level, name, (char *) &val, sizeof(val)); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/options.h Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,39 @@ +#ifndef OPTIONS_H +#define OPTIONS_H +/*=========================================================================*\ +* Common option interface +* LuaSocket toolkit +* +* This module provides a common interface to socket options, used mainly by +* modules UDP and TCP. +* +* RCS ID: $Id: options.h,v 1.4 2005/10/07 04:40:59 diego Exp $ +\*=========================================================================*/ + +#include "lua.h" +#include "socket.h" + +/* option registry */ +typedef struct t_opt { + const char *name; + int (*func)(lua_State *L, p_socket ps); +} t_opt; +typedef t_opt *p_opt; + +/* supported options */ +int opt_dontroute(lua_State *L, p_socket ps); +int opt_broadcast(lua_State *L, p_socket ps); +int opt_reuseaddr(lua_State *L, p_socket ps); +int opt_tcp_nodelay(lua_State *L, p_socket ps); +int opt_keepalive(lua_State *L, p_socket ps); +int opt_linger(lua_State *L, p_socket ps); +int opt_reuseaddr(lua_State *L, p_socket ps); +int opt_ip_multicast_ttl(lua_State *L, p_socket ps); +int opt_ip_multicast_loop(lua_State *L, p_socket ps); +int opt_ip_add_membership(lua_State *L, p_socket ps); +int opt_ip_drop_membersip(lua_State *L, p_socket ps); + +/* invokes the appropriate option handler */ +int opt_meth_setoption(lua_State *L, p_opt opt, p_socket ps); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/select.c Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,200 @@ +/*=========================================================================*\ +* Select implementation +* LuaSocket toolkit +* +* RCS ID: $Id: select.c,v 1.22 2005/11/20 07:20:23 diego Exp $ +\*=========================================================================*/ +#include <string.h> + +#include "lua.h" +#include "lauxlib.h" + +#include "socket.h" +#include "timeout.h" +#include "select.h" + +/*=========================================================================*\ +* Internal function prototypes. +\*=========================================================================*/ +static t_socket getfd(lua_State *L); +static int dirty(lua_State *L); +static t_socket collect_fd(lua_State *L, int tab, t_socket max_fd, + int itab, fd_set *set); +static int check_dirty(lua_State *L, int tab, int dtab, fd_set *set); +static void return_fd(lua_State *L, fd_set *set, t_socket max_fd, + int itab, int tab, int start); +static void make_assoc(lua_State *L, int tab); +static int global_select(lua_State *L); + +/* functions in library namespace */ +static luaL_reg func[] = { + {"select", global_select}, + {NULL, NULL} +}; + +/*=========================================================================*\ +* Exported functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int select_open(lua_State *L) { + luaL_openlib(L, NULL, func, 0); + return 0; +} + +/*=========================================================================*\ +* Global Lua functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Waits for a set of sockets until a condition is met or timeout. +\*-------------------------------------------------------------------------*/ +static int global_select(lua_State *L) { + int rtab, wtab, itab, ret, ndirty; + t_socket max_fd; + fd_set rset, wset; + t_timeout tm; + double t = luaL_optnumber(L, 3, -1); + FD_ZERO(&rset); FD_ZERO(&wset); + lua_settop(L, 3); + lua_newtable(L); itab = lua_gettop(L); + lua_newtable(L); rtab = lua_gettop(L); + lua_newtable(L); wtab = lua_gettop(L); + max_fd = collect_fd(L, 1, SOCKET_INVALID, itab, &rset); + ndirty = check_dirty(L, 1, rtab, &rset); + t = ndirty > 0? 0.0: t; + timeout_init(&tm, t, -1); + timeout_markstart(&tm); + max_fd = collect_fd(L, 2, max_fd, itab, &wset); + ret = socket_select(max_fd+1, &rset, &wset, NULL, &tm); + if (ret > 0 || ndirty > 0) { + return_fd(L, &rset, max_fd+1, itab, rtab, ndirty); + return_fd(L, &wset, max_fd+1, itab, wtab, 0); + make_assoc(L, rtab); + make_assoc(L, wtab); + return 2; + } else if (ret == 0) { + lua_pushstring(L, "timeout"); + return 3; + } else { + lua_pushstring(L, "error"); + return 3; + } +} + +/*=========================================================================*\ +* Internal functions +\*=========================================================================*/ +static t_socket getfd(lua_State *L) { + t_socket fd = SOCKET_INVALID; + lua_pushstring(L, "getfd"); + lua_gettable(L, -2); + if (!lua_isnil(L, -1)) { + lua_pushvalue(L, -2); + lua_call(L, 1, 1); + if (lua_isnumber(L, -1)) + fd = (t_socket) lua_tonumber(L, -1); + } + lua_pop(L, 1); + return fd; +} + +static int dirty(lua_State *L) { + int is = 0; + lua_pushstring(L, "dirty"); + lua_gettable(L, -2); + if (!lua_isnil(L, -1)) { + lua_pushvalue(L, -2); + lua_call(L, 1, 1); + is = lua_toboolean(L, -1); + } + lua_pop(L, 1); + return is; +} + +static t_socket collect_fd(lua_State *L, int tab, t_socket max_fd, + int itab, fd_set *set) { + int i = 1; + if (lua_isnil(L, tab)) + return max_fd; + while (1) { + t_socket fd; + lua_pushnumber(L, i); + lua_gettable(L, tab); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + break; + } + fd = getfd(L); + if (fd != SOCKET_INVALID) { + FD_SET(fd, set); + if (max_fd == SOCKET_INVALID || max_fd < fd) + max_fd = fd; + lua_pushnumber(L, fd); + lua_pushvalue(L, -2); + lua_settable(L, itab); + } + lua_pop(L, 1); + i = i + 1; + } + return max_fd; +} + +static int check_dirty(lua_State *L, int tab, int dtab, fd_set *set) { + int ndirty = 0, i = 1; + if (lua_isnil(L, tab)) + return 0; + while (1) { + t_socket fd; + lua_pushnumber(L, i); + lua_gettable(L, tab); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + break; + } + fd = getfd(L); + if (fd != SOCKET_INVALID && dirty(L)) { + lua_pushnumber(L, ++ndirty); + lua_pushvalue(L, -2); + lua_settable(L, dtab); + FD_CLR(fd, set); + } + lua_pop(L, 1); + i = i + 1; + } + return ndirty; +} + +static void return_fd(lua_State *L, fd_set *set, t_socket max_fd, + int itab, int tab, int start) { + t_socket fd; + for (fd = 0; fd < max_fd; fd++) { + if (FD_ISSET(fd, set)) { + lua_pushnumber(L, ++start); + lua_pushnumber(L, fd); + lua_gettable(L, itab); + lua_settable(L, tab); + } + } +} + +static void make_assoc(lua_State *L, int tab) { + int i = 1, atab; + lua_newtable(L); atab = lua_gettop(L); + while (1) { + lua_pushnumber(L, i); + lua_gettable(L, tab); + if (!lua_isnil(L, -1)) { + lua_pushnumber(L, i); + lua_pushvalue(L, -2); + lua_settable(L, atab); + lua_pushnumber(L, i); + lua_settable(L, atab); + } else { + lua_pop(L, 1); + break; + } + i = i+1; + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/select.h Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,17 @@ +#ifndef SELECT_H +#define SELECT_H +/*=========================================================================*\ +* Select implementation +* LuaSocket toolkit +* +* Each object that can be passed to the select function has to export +* method getfd() which returns the descriptor to be passed to the +* underlying select function. Another method, dirty(), should return +* true if there is data ready for reading (required for buffered input). +* +* RCS ID: $Id: select.h,v 1.7 2004/06/16 01:02:07 diego Exp $ +\*=========================================================================*/ + +int select_open(lua_State *L); + +#endif /* SELECT_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/smtp.lua Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,251 @@ +----------------------------------------------------------------------------- +-- SMTP client support for the Lua language. +-- LuaSocket toolkit. +-- Author: Diego Nehab +-- RCS ID: $Id: smtp.lua,v 1.46 2007/03/12 04:08:40 diego Exp $ +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +----------------------------------------------------------------------------- +local base = _G +local coroutine = require("coroutine") +local string = require("string") +local math = require("math") +local os = require("os") +local socket = require("socket") +local tp = require("socket.tp") +local ltn12 = require("ltn12") +local mime = require("mime") +module("socket.smtp") + +----------------------------------------------------------------------------- +-- Program constants +----------------------------------------------------------------------------- +-- timeout for connection +TIMEOUT = 60 +-- default server used to send e-mails +SERVER = "localhost" +-- default port +PORT = 25 +-- domain used in HELO command and default sendmail +-- If we are under a CGI, try to get from environment +DOMAIN = os.getenv("SERVER_NAME") or "localhost" +-- default time zone (means we don't know) +ZONE = "-0000" + +--------------------------------------------------------------------------- +-- Low level SMTP API +----------------------------------------------------------------------------- +local metat = { __index = {} } + +function metat.__index:greet(domain) + self.try(self.tp:check("2..")) + self.try(self.tp:command("EHLO", domain or DOMAIN)) + return socket.skip(1, self.try(self.tp:check("2.."))) +end + +function metat.__index:mail(from) + self.try(self.tp:command("MAIL", "FROM:" .. from)) + return self.try(self.tp:check("2..")) +end + +function metat.__index:rcpt(to) + self.try(self.tp:command("RCPT", "TO:" .. to)) + return self.try(self.tp:check("2..")) +end + +function metat.__index:data(src, step) + self.try(self.tp:command("DATA")) + self.try(self.tp:check("3..")) + self.try(self.tp:source(src, step)) + self.try(self.tp:send("\r\n.\r\n")) + return self.try(self.tp:check("2..")) +end + +function metat.__index:quit() + self.try(self.tp:command("QUIT")) + return self.try(self.tp:check("2..")) +end + +function metat.__index:close() + return self.tp:close() +end + +function metat.__index:login(user, password) + self.try(self.tp:command("AUTH", "LOGIN")) + self.try(self.tp:check("3..")) + self.try(self.tp:command(mime.b64(user))) + self.try(self.tp:check("3..")) + self.try(self.tp:command(mime.b64(password))) + return self.try(self.tp:check("2..")) +end + +function metat.__index:plain(user, password) + local auth = "PLAIN " .. mime.b64("\0" .. user .. "\0" .. password) + self.try(self.tp:command("AUTH", auth)) + return self.try(self.tp:check("2..")) +end + +function metat.__index:auth(user, password, ext) + if not user or not password then return 1 end + if string.find(ext, "AUTH[^\n]+LOGIN") then + return self:login(user, password) + elseif string.find(ext, "AUTH[^\n]+PLAIN") then + return self:plain(user, password) + else + self.try(nil, "authentication not supported") + end +end + +-- send message or throw an exception +function metat.__index:send(mailt) + self:mail(mailt.from) + if base.type(mailt.rcpt) == "table" then + for i,v in base.ipairs(mailt.rcpt) do + self:rcpt(v) + end + else + self:rcpt(mailt.rcpt) + end + self:data(ltn12.source.chain(mailt.source, mime.stuff()), mailt.step) +end + +function open(server, port, create) + local tp = socket.try(tp.connect(server or SERVER, port or PORT, + TIMEOUT, create)) + local s = base.setmetatable({tp = tp}, metat) + -- make sure tp is closed if we get an exception + s.try = socket.newtry(function() + s:close() + end) + return s +end + +-- convert headers to lowercase +local function lower_headers(headers) + local lower = {} + for i,v in base.pairs(headers or lower) do + lower[string.lower(i)] = v + end + return lower +end + +--------------------------------------------------------------------------- +-- Multipart message source +----------------------------------------------------------------------------- +-- returns a hopefully unique mime boundary +local seqno = 0 +local function newboundary() + seqno = seqno + 1 + return string.format('%s%05d==%05u', os.date('%d%m%Y%H%M%S'), + math.random(0, 99999), seqno) +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() + local headers = lower_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) + coroutine.yield("\r\n") + end + -- send each part separated by a boundary + for i, m in base.ipairs(mesgt.body) do + coroutine.yield("\r\n--" .. bd .. "\r\n") + send_message(m) + end + -- send last boundary + coroutine.yield("\r\n--" .. bd .. "--\r\n\r\n") + -- send epilogue + if mesgt.body.epilogue then + coroutine.yield(mesgt.body.epilogue) + coroutine.yield("\r\n") + end +end + +-- yield message body from a source +local function send_source(mesgt) + -- make sure we have a content-type + local headers = lower_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() + if err then coroutine.yield(nil, err) + elseif chunk then coroutine.yield(chunk) + else break end + end +end + +-- yield message body from a string +local function send_string(mesgt) + -- make sure we have a content-type + local headers = lower_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 + +-- message source +function send_message(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 +end + +-- set defaul headers +local function adjust_headers(mesgt) + local lower = lower_headers(mesgt.headers) + lower["date"] = lower["date"] or + os.date("!%a, %d %b %Y %H:%M:%S ") .. (mesgt.zone or ZONE) + lower["x-mailer"] = lower["x-mailer"] or socket._VERSION + -- this can't be overriden + lower["mime-version"] = "1.0" + return lower +end + +function message(mesgt) + mesgt.headers = adjust_headers(mesgt) + -- create and return message source + local co = coroutine.create(function() send_message(mesgt) end) + return function() + local ret, a, b = coroutine.resume(co) + if ret then return a, b + else return nil, a end + end +end + +--------------------------------------------------------------------------- +-- High level SMTP API +----------------------------------------------------------------------------- +send = socket.protect(function(mailt) + local s = open(mailt.server, mailt.port, mailt.create) + local ext = s:greet(mailt.domain) + s:auth(mailt.user, mailt.password, ext) + s:send(mailt) + s:quit() + return s:close() +end)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/socket.h Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,76 @@ +#ifndef SOCKET_H +#define SOCKET_H +/*=========================================================================*\ +* Socket compatibilization module +* LuaSocket toolkit +* +* BSD Sockets and WinSock are similar, but there are a few irritating +* differences. Also, not all *nix platforms behave the same. This module +* (and the associated usocket.h and wsocket.h) factor these differences and +* creates a interface compatible with the io.h module. +* +* RCS ID: $Id: socket.h,v 1.20 2005/11/20 07:20:23 diego Exp $ +\*=========================================================================*/ +#include "io.h" + +/*=========================================================================*\ +* Platform specific compatibilization +\*=========================================================================*/ +#ifdef _WIN32 +#include "wsocket.h" +#else +#include "usocket.h" +#endif + +/*=========================================================================*\ +* The connect and accept functions accept a timeout and their +* implementations are somewhat complicated. We chose to move +* the timeout control into this module for these functions in +* order to simplify the modules that use them. +\*=========================================================================*/ +#include "timeout.h" + +/* we are lazy... */ +typedef struct sockaddr SA; + +/*=========================================================================*\ +* Functions bellow implement a comfortable platform independent +* interface to sockets +\*=========================================================================*/ +int socket_open(void); +int socket_close(void); +void socket_destroy(p_socket ps); +void socket_shutdown(p_socket ps, int how); +int socket_sendto(p_socket ps, const char *data, size_t count, + size_t *sent, SA *addr, socklen_t addr_len, p_timeout tm); +int socket_recvfrom(p_socket ps, char *data, size_t count, + size_t *got, SA *addr, socklen_t *addr_len, p_timeout tm); + +void socket_setnonblocking(p_socket ps); +void socket_setblocking(p_socket ps); + +int socket_waitfd(p_socket ps, int sw, p_timeout tm); +int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds, + p_timeout tm); + +int socket_connect(p_socket ps, SA *addr, socklen_t addr_len, p_timeout tm); +int socket_create(p_socket ps, int domain, int type, int protocol); +int socket_bind(p_socket ps, SA *addr, socklen_t addr_len); +int socket_listen(p_socket ps, int backlog); +int socket_accept(p_socket ps, p_socket pa, SA *addr, + socklen_t *addr_len, p_timeout tm); + +const char *socket_hoststrerror(int err); +const char *socket_strerror(int err); + +/* these are perfect to use with the io abstraction module + and the buffered input module */ +int socket_send(p_socket ps, const char *data, size_t count, + size_t *sent, p_timeout tm); +int socket_recv(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm); +const char *socket_ioerror(p_socket ps, int err); + +int socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp); +int socket_gethostbyname(const char *addr, struct hostent **hp); + +#endif /* SOCKET_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/socket.lua Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,133 @@ +----------------------------------------------------------------------------- +-- LuaSocket helper module +-- Author: Diego Nehab +-- RCS ID: $Id: socket.lua,v 1.22 2005/11/22 08:33:29 diego Exp $ +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +----------------------------------------------------------------------------- +local base = _G +local string = require("string") +local math = require("math") +local socket = require("socket.core") +module("socket") + +----------------------------------------------------------------------------- +-- Exported auxiliar functions +----------------------------------------------------------------------------- +function connect(address, port, laddress, lport) + local sock, err = socket.tcp() + if not sock then return nil, err end + if laddress then + local res, err = sock:bind(laddress, lport, -1) + if not res then return nil, err end + end + local res, err = sock:connect(address, port) + if not res then return nil, err end + return sock +end + +function bind(host, port, backlog) + local sock, err = socket.tcp() + if not sock then return nil, err end + sock:setoption("reuseaddr", true) + local res, err = sock:bind(host, port) + if not res then return nil, err end + res, err = sock:listen(backlog) + if not res then return nil, err end + return sock +end + +try = newtry() + +function choose(table) + return function(name, opt1, opt2) + if base.type(name) ~= "string" then + name, opt1, opt2 = "default", name, opt1 + end + local f = table[name or "nil"] + if not f then base.error("unknown key (".. base.tostring(name) ..")", 3) + else return f(opt1, opt2) end + end +end + +----------------------------------------------------------------------------- +-- Socket sources and sinks, conforming to LTN12 +----------------------------------------------------------------------------- +-- create namespaces inside LuaSocket namespace +sourcet = {} +sinkt = {} + +BLOCKSIZE = 2048 + +sinkt["close-when-done"] = function(sock) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function(self, chunk, err) + if not chunk then + sock:close() + return 1 + else return sock:send(chunk) end + end + }) +end + +sinkt["keep-open"] = function(sock) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function(self, chunk, err) + if chunk then return sock:send(chunk) + else return 1 end + end + }) +end + +sinkt["default"] = sinkt["keep-open"] + +sink = choose(sinkt) + +sourcet["by-length"] = function(sock, length) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function() + if length <= 0 then return nil end + local size = math.min(socket.BLOCKSIZE, length) + local chunk, err = sock:receive(size) + if err then return nil, err end + length = length - string.len(chunk) + return chunk + end + }) +end + +sourcet["until-closed"] = function(sock) + local done + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function() + if done then return nil end + local chunk, err, partial = sock:receive(socket.BLOCKSIZE) + if not err then return chunk + elseif err == "closed" then + sock:close() + done = 1 + return partial + else return nil, err end + end + }) +end + + +sourcet["default"] = sourcet["until-closed"] + +source = choose(sourcet) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/tcp.c Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,339 @@ +/*=========================================================================*\ +* TCP object +* LuaSocket toolkit +* +* RCS ID: $Id: tcp.c,v 1.41 2005/10/07 04:40:59 diego Exp $ +\*=========================================================================*/ +#include <string.h> + +#include "lua.h" +#include "lauxlib.h" + +#include "auxiliar.h" +#include "socket.h" +#include "inet.h" +#include "options.h" +#include "tcp.h" + +/*=========================================================================*\ +* Internal function prototypes +\*=========================================================================*/ +static int global_create(lua_State *L); +static int meth_connect(lua_State *L); +static int meth_listen(lua_State *L); +static int meth_bind(lua_State *L); +static int meth_send(lua_State *L); +static int meth_getstats(lua_State *L); +static int meth_setstats(lua_State *L); +static int meth_getsockname(lua_State *L); +static int meth_getpeername(lua_State *L); +static int meth_shutdown(lua_State *L); +static int meth_receive(lua_State *L); +static int meth_accept(lua_State *L); +static int meth_close(lua_State *L); +static int meth_setoption(lua_State *L); +static int meth_settimeout(lua_State *L); +static int meth_getfd(lua_State *L); +static int meth_setfd(lua_State *L); +static int meth_dirty(lua_State *L); + +/* tcp object methods */ +static luaL_reg tcp[] = { + {"__gc", meth_close}, + {"__tostring", auxiliar_tostring}, + {"accept", meth_accept}, + {"bind", meth_bind}, + {"close", meth_close}, + {"connect", meth_connect}, + {"dirty", meth_dirty}, + {"getfd", meth_getfd}, + {"getpeername", meth_getpeername}, + {"getsockname", meth_getsockname}, + {"getstats", meth_getstats}, + {"setstats", meth_setstats}, + {"listen", meth_listen}, + {"receive", meth_receive}, + {"send", meth_send}, + {"setfd", meth_setfd}, + {"setoption", meth_setoption}, + {"setpeername", meth_connect}, + {"setsockname", meth_bind}, + {"settimeout", meth_settimeout}, + {"shutdown", meth_shutdown}, + {NULL, NULL} +}; + +/* socket option handlers */ +static t_opt opt[] = { + {"keepalive", opt_keepalive}, + {"reuseaddr", opt_reuseaddr}, + {"tcp-nodelay", opt_tcp_nodelay}, + {"linger", opt_linger}, + {NULL, NULL} +}; + +/* functions in library namespace */ +static luaL_reg func[] = { + {"tcp", global_create}, + {NULL, NULL} +}; + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int tcp_open(lua_State *L) +{ + /* create classes */ + auxiliar_newclass(L, "tcp{master}", tcp); + auxiliar_newclass(L, "tcp{client}", tcp); + auxiliar_newclass(L, "tcp{server}", tcp); + /* create class groups */ + auxiliar_add2group(L, "tcp{master}", "tcp{any}"); + auxiliar_add2group(L, "tcp{client}", "tcp{any}"); + auxiliar_add2group(L, "tcp{server}", "tcp{any}"); + /* define library functions */ + luaL_openlib(L, NULL, func, 0); + return 0; +} + +/*=========================================================================*\ +* Lua methods +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Just call buffered IO methods +\*-------------------------------------------------------------------------*/ +static int meth_send(lua_State *L) { + p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{client}", 1); + return buffer_meth_send(L, &tcp->buf); +} + +static int meth_receive(lua_State *L) { + p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{client}", 1); + return buffer_meth_receive(L, &tcp->buf); +} + +static int meth_getstats(lua_State *L) { + p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{client}", 1); + return buffer_meth_getstats(L, &tcp->buf); +} + +static int meth_setstats(lua_State *L) { + p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{client}", 1); + return buffer_meth_setstats(L, &tcp->buf); +} + +/*-------------------------------------------------------------------------*\ +* Just call option handler +\*-------------------------------------------------------------------------*/ +static int meth_setoption(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); + return opt_meth_setoption(L, opt, &tcp->sock); +} + +/*-------------------------------------------------------------------------*\ +* Select support methods +\*-------------------------------------------------------------------------*/ +static int meth_getfd(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); + lua_pushnumber(L, (int) tcp->sock); + return 1; +} + +/* this is very dangerous, but can be handy for those that are brave enough */ +static int meth_setfd(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); + tcp->sock = (t_socket) luaL_checknumber(L, 2); + return 0; +} + +static int meth_dirty(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); + lua_pushboolean(L, !buffer_isempty(&tcp->buf)); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Waits for and returns a client object attempting connection to the +* server object +\*-------------------------------------------------------------------------*/ +static int meth_accept(lua_State *L) +{ + p_tcp server = (p_tcp) auxiliar_checkclass(L, "tcp{server}", 1); + p_timeout tm = timeout_markstart(&server->tm); + t_socket sock; + int err = socket_accept(&server->sock, &sock, NULL, NULL, tm); + /* if successful, push client socket */ + if (err == IO_DONE) { + p_tcp clnt = (p_tcp) lua_newuserdata(L, sizeof(t_tcp)); + auxiliar_setclass(L, "tcp{client}", -1); + /* initialize structure fields */ + socket_setnonblocking(&sock); + clnt->sock = sock; + io_init(&clnt->io, (p_send) socket_send, (p_recv) socket_recv, + (p_error) socket_ioerror, &clnt->sock); + timeout_init(&clnt->tm, -1, -1); + buffer_init(&clnt->buf, &clnt->io, &clnt->tm); + return 1; + } else { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(err)); + return 2; + } +} + +/*-------------------------------------------------------------------------*\ +* Binds an object to an address +\*-------------------------------------------------------------------------*/ +static int meth_bind(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{master}", 1); + const char *address = luaL_checkstring(L, 2); + unsigned short port = (unsigned short) luaL_checknumber(L, 3); + const char *err = inet_trybind(&tcp->sock, address, port); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Turns a master tcp object into a client object. +\*-------------------------------------------------------------------------*/ +static int meth_connect(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); + const char *address = luaL_checkstring(L, 2); + unsigned short port = (unsigned short) luaL_checknumber(L, 3); + p_timeout tm = timeout_markstart(&tcp->tm); + const char *err = inet_tryconnect(&tcp->sock, address, port, tm); + /* have to set the class even if it failed due to non-blocking connects */ + auxiliar_setclass(L, "tcp{client}", 1); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + /* turn master object into a client object */ + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Closes socket used by object +\*-------------------------------------------------------------------------*/ +static int meth_close(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); + socket_destroy(&tcp->sock); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Puts the sockt in listen mode +\*-------------------------------------------------------------------------*/ +static int meth_listen(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{master}", 1); + int backlog = (int) luaL_optnumber(L, 2, 32); + int err = socket_listen(&tcp->sock, backlog); + if (err != IO_DONE) { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(err)); + return 2; + } + /* turn master object into a server object */ + auxiliar_setclass(L, "tcp{server}", 1); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Shuts the connection down partially +\*-------------------------------------------------------------------------*/ +static int meth_shutdown(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{client}", 1); + const char *how = luaL_optstring(L, 2, "both"); + switch (how[0]) { + case 'b': + if (strcmp(how, "both")) goto error; + socket_shutdown(&tcp->sock, 2); + break; + case 's': + if (strcmp(how, "send")) goto error; + socket_shutdown(&tcp->sock, 1); + break; + case 'r': + if (strcmp(how, "receive")) goto error; + socket_shutdown(&tcp->sock, 0); + break; + } + lua_pushnumber(L, 1); + return 1; +error: + luaL_argerror(L, 2, "invalid shutdown method"); + return 0; +} + +/*-------------------------------------------------------------------------*\ +* Just call inet methods +\*-------------------------------------------------------------------------*/ +static int meth_getpeername(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); + return inet_meth_getpeername(L, &tcp->sock); +} + +static int meth_getsockname(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); + return inet_meth_getsockname(L, &tcp->sock); +} + +/*-------------------------------------------------------------------------*\ +* Just call tm methods +\*-------------------------------------------------------------------------*/ +static int meth_settimeout(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); + return timeout_meth_settimeout(L, &tcp->tm); +} + +/*=========================================================================*\ +* Library functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Creates a master tcp object +\*-------------------------------------------------------------------------*/ +static int global_create(lua_State *L) +{ + t_socket sock; + const char *err = inet_trycreate(&sock, SOCK_STREAM); + /* try to allocate a system socket */ + if (!err) { + /* allocate tcp object */ + p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp)); + /* set its type as master object */ + auxiliar_setclass(L, "tcp{master}", -1); + /* initialize remaining structure fields */ + socket_setnonblocking(&sock); + tcp->sock = sock; + io_init(&tcp->io, (p_send) socket_send, (p_recv) socket_recv, + (p_error) socket_ioerror, &tcp->sock); + timeout_init(&tcp->tm, -1, -1); + buffer_init(&tcp->buf, &tcp->io, &tcp->tm); + return 1; + } else { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/tcp.h Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,36 @@ +#ifndef TCP_H +#define TCP_H +/*=========================================================================*\ +* TCP object +* LuaSocket toolkit +* +* The tcp.h module is basicly a glue that puts together modules buffer.h, +* timeout.h socket.h and inet.h to provide the LuaSocket TCP (AF_INET, +* SOCK_STREAM) support. +* +* Three classes are defined: master, client and server. The master class is +* a newly created tcp object, that has not been bound or connected. Server +* objects are tcp objects bound to some local address. Client objects are +* tcp objects either connected to some address or returned by the accept +* method of a server object. +* +* RCS ID: $Id: tcp.h,v 1.7 2005/10/07 04:40:59 diego Exp $ +\*=========================================================================*/ +#include "lua.h" + +#include "buffer.h" +#include "timeout.h" +#include "socket.h" + +typedef struct t_tcp_ { + t_socket sock; + t_io io; + t_buffer buf; + t_timeout tm; +} t_tcp; + +typedef t_tcp *p_tcp; + +int tcp_open(lua_State *L); + +#endif /* TCP_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/timeout.c Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,207 @@ +/*=========================================================================*\ +* Timeout management functions +* LuaSocket toolkit +* +* RCS ID: $Id: timeout.c,v 1.30 2005/10/07 04:40:59 diego Exp $ +\*=========================================================================*/ +#include <stdio.h> + +#include "lua.h" +#include "lauxlib.h" + +#include "auxiliar.h" +#include "timeout.h" + +#ifdef _WIN32 +#include <windows.h> +#else +#include <time.h> +#include <sys/time.h> +#endif + +/* min and max macros */ +#ifndef MIN +#define MIN(x, y) ((x) < (y) ? x : y) +#endif +#ifndef MAX +#define MAX(x, y) ((x) > (y) ? x : y) +#endif + +/*=========================================================================*\ +* Internal function prototypes +\*=========================================================================*/ +static int timeout_lua_gettime(lua_State *L); +static int timeout_lua_sleep(lua_State *L); + +static luaL_reg func[] = { + { "gettime", timeout_lua_gettime }, + { "sleep", timeout_lua_sleep }, + { NULL, NULL } +}; + +/*=========================================================================*\ +* Exported functions. +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Initialize structure +\*-------------------------------------------------------------------------*/ +void timeout_init(p_timeout tm, double block, double total) { + tm->block = block; + tm->total = total; +} + +/*-------------------------------------------------------------------------*\ +* Determines how much time we have left for the next system call, +* if the previous call was successful +* Input +* tm: timeout control structure +* Returns +* the number of ms left or -1 if there is no time limit +\*-------------------------------------------------------------------------*/ +double timeout_get(p_timeout tm) { + if (tm->block < 0.0 && tm->total < 0.0) { + return -1; + } else if (tm->block < 0.0) { + double t = tm->total - timeout_gettime() + tm->start; + return MAX(t, 0.0); + } else if (tm->total < 0.0) { + return tm->block; + } else { + double t = tm->total - timeout_gettime() + tm->start; + return MIN(tm->block, MAX(t, 0.0)); + } +} + +/*-------------------------------------------------------------------------*\ +* Returns time since start of operation +* Input +* tm: timeout control structure +* Returns +* start field of structure +\*-------------------------------------------------------------------------*/ +double timeout_getstart(p_timeout tm) { + return tm->start; +} + +/*-------------------------------------------------------------------------*\ +* Determines how much time we have left for the next system call, +* if the previous call was a failure +* Input +* tm: timeout control structure +* Returns +* the number of ms left or -1 if there is no time limit +\*-------------------------------------------------------------------------*/ +double timeout_getretry(p_timeout tm) { + if (tm->block < 0.0 && tm->total < 0.0) { + return -1; + } else if (tm->block < 0.0) { + double t = tm->total - timeout_gettime() + tm->start; + return MAX(t, 0.0); + } else if (tm->total < 0.0) { + double t = tm->block - timeout_gettime() + tm->start; + return MAX(t, 0.0); + } else { + double t = tm->total - timeout_gettime() + tm->start; + return MIN(tm->block, MAX(t, 0.0)); + } +} + +/*-------------------------------------------------------------------------*\ +* Marks the operation start time in structure +* Input +* tm: timeout control structure +\*-------------------------------------------------------------------------*/ +p_timeout timeout_markstart(p_timeout tm) { + tm->start = timeout_gettime(); + return tm; +} + +/*-------------------------------------------------------------------------*\ +* Gets time in s, relative to January 1, 1970 (UTC) +* Returns +* time in s. +\*-------------------------------------------------------------------------*/ +#ifdef _WIN32 +double timeout_gettime(void) { + FILETIME ft; + double t; + GetSystemTimeAsFileTime(&ft); + /* Windows file time (time since January 1, 1601 (UTC)) */ + t = ft.dwLowDateTime/1.0e7 + ft.dwHighDateTime*(4294967296.0/1.0e7); + /* convert to Unix Epoch time (time since January 1, 1970 (UTC)) */ + return (t - 11644473600.0); +} +#else +double timeout_gettime(void) { + struct timeval v; + gettimeofday(&v, (struct timezone *) NULL); + /* Unix Epoch time (time since January 1, 1970 (UTC)) */ + return v.tv_sec + v.tv_usec/1.0e6; +} +#endif + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int timeout_open(lua_State *L) { + luaL_openlib(L, NULL, func, 0); + return 0; +} + +/*-------------------------------------------------------------------------*\ +* Sets timeout values for IO operations +* Lua Input: base, time [, mode] +* time: time out value in seconds +* mode: "b" for block timeout, "t" for total timeout. (default: b) +\*-------------------------------------------------------------------------*/ +int timeout_meth_settimeout(lua_State *L, p_timeout tm) { + double t = luaL_optnumber(L, 2, -1); + const char *mode = luaL_optstring(L, 3, "b"); + switch (*mode) { + case 'b': + tm->block = t; + break; + case 'r': case 't': + tm->total = t; + break; + default: + luaL_argcheck(L, 0, 3, "invalid timeout mode"); + break; + } + lua_pushnumber(L, 1); + return 1; +} + +/*=========================================================================*\ +* Test support functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Returns the time the system has been up, in secconds. +\*-------------------------------------------------------------------------*/ +static int timeout_lua_gettime(lua_State *L) +{ + lua_pushnumber(L, timeout_gettime()); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Sleep for n seconds. +\*-------------------------------------------------------------------------*/ +int timeout_lua_sleep(lua_State *L) +{ + double n = luaL_checknumber(L, 1); +#ifdef _WIN32 + Sleep((int)(n*1000)); +#else + struct timespec t, r; + t.tv_sec = (int) n; + n -= t.tv_sec; + t.tv_nsec = (int) (n * 1000000000); + if (t.tv_nsec >= 1000000000) t.tv_nsec = 999999999; + while (nanosleep(&t, &r) != 0) { + t.tv_sec = r.tv_sec; + t.tv_nsec = r.tv_nsec; + } +#endif + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/timeout.h Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,30 @@ +#ifndef TIMEOUT_H +#define TIMEOUT_H +/*=========================================================================*\ +* Timeout management functions +* LuaSocket toolkit +* +* RCS ID: $Id: timeout.h,v 1.14 2005/10/07 04:40:59 diego Exp $ +\*=========================================================================*/ +#include "lua.h" + +/* timeout control structure */ +typedef struct t_timeout_ { + double block; /* maximum time for blocking calls */ + double total; /* total number of miliseconds for operation */ + double start; /* time of start of operation */ +} t_timeout; +typedef t_timeout *p_timeout; + +int timeout_open(lua_State *L); +void timeout_init(p_timeout tm, double block, double total); +double timeout_get(p_timeout tm); +double timeout_getretry(p_timeout tm); +p_timeout timeout_markstart(p_timeout tm); +double timeout_getstart(p_timeout tm); +double timeout_gettime(void); +int timeout_meth_settimeout(lua_State *L, p_timeout tm); + +#define timeout_iszero(tm) ((tm)->block == 0.0) + +#endif /* TIMEOUT_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/tp.lua Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,123 @@ +----------------------------------------------------------------------------- +-- Unified SMTP/FTP subsystem +-- LuaSocket toolkit. +-- Author: Diego Nehab +-- RCS ID: $Id: tp.lua,v 1.22 2006/03/14 09:04:15 diego Exp $ +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +----------------------------------------------------------------------------- +local base = _G +local string = require("string") +local socket = require("socket") +local ltn12 = require("ltn12") +module("socket.tp") + +----------------------------------------------------------------------------- +-- Program constants +----------------------------------------------------------------------------- +TIMEOUT = 60 + +----------------------------------------------------------------------------- +-- Implementation +----------------------------------------------------------------------------- +-- gets server reply (works for SMTP and FTP) +local function get_reply(c) + local code, current, sep + local line, err = c:receive() + local reply = line + if err then return nil, err end + code, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)")) + if not code then return nil, "invalid server reply" end + if sep == "-" then -- reply is multiline + repeat + line, err = c:receive() + if err then return nil, err end + current, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)")) + reply = reply .. "\n" .. line + -- reply ends with same code + until code == current and sep == " " + end + return code, reply +end + +-- metatable for sock object +local metat = { __index = {} } + +function metat.__index:check(ok) + local code, reply = get_reply(self.c) + if not code then return nil, reply end + if base.type(ok) ~= "function" then + if base.type(ok) == "table" then + for i, v in base.ipairs(ok) do + if string.find(code, v) then + return base.tonumber(code), reply + end + end + return nil, reply + else + if string.find(code, ok) then return base.tonumber(code), reply + else return nil, reply end + end + else return ok(base.tonumber(code), reply) end +end + +function metat.__index:command(cmd, arg) + if arg then + return self.c:send(cmd .. " " .. arg.. "\r\n") + else + return self.c:send(cmd .. "\r\n") + end +end + +function metat.__index:sink(snk, pat) + local chunk, err = c:receive(pat) + return snk(chunk, err) +end + +function metat.__index:send(data) + return self.c:send(data) +end + +function metat.__index:receive(pat) + return self.c:receive(pat) +end + +function metat.__index:getfd() + return self.c:getfd() +end + +function metat.__index:dirty() + return self.c:dirty() +end + +function metat.__index:getcontrol() + return self.c +end + +function metat.__index:source(source, step) + local sink = socket.sink("keep-open", self.c) + local ret, err = ltn12.pump.all(source, sink, step or ltn12.pump.step) + return ret, err +end + +-- closes the underlying c +function metat.__index:close() + self.c:close() + return 1 +end + +-- connect with server and return c object +function connect(host, port, timeout, create) + local c, e = (create or socket.tcp)() + if not c then return nil, e end + c:settimeout(timeout or TIMEOUT) + local r, e = c:connect(host, port) + if not r then + c:close() + return nil, e + end + return base.setmetatable({c = c}, metat) +end +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/udp.c Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,336 @@ +/*=========================================================================*\ +* UDP object +* LuaSocket toolkit +* +* RCS ID: $Id: udp.c,v 1.29 2005/10/07 04:40:59 diego Exp $ +\*=========================================================================*/ +#include <string.h> + +#include "lua.h" +#include "lauxlib.h" + +#include "auxiliar.h" +#include "socket.h" +#include "inet.h" +#include "options.h" +#include "udp.h" + +/* min and max macros */ +#ifndef MIN +#define MIN(x, y) ((x) < (y) ? x : y) +#endif +#ifndef MAX +#define MAX(x, y) ((x) > (y) ? x : y) +#endif + +/*=========================================================================*\ +* Internal function prototypes +\*=========================================================================*/ +static int global_create(lua_State *L); +static int meth_send(lua_State *L); +static int meth_sendto(lua_State *L); +static int meth_receive(lua_State *L); +static int meth_receivefrom(lua_State *L); +static int meth_getsockname(lua_State *L); +static int meth_getpeername(lua_State *L); +static int meth_setsockname(lua_State *L); +static int meth_setpeername(lua_State *L); +static int meth_close(lua_State *L); +static int meth_setoption(lua_State *L); +static int meth_settimeout(lua_State *L); +static int meth_getfd(lua_State *L); +static int meth_setfd(lua_State *L); +static int meth_dirty(lua_State *L); + +/* udp object methods */ +static luaL_reg udp[] = { + {"__gc", meth_close}, + {"__tostring", auxiliar_tostring}, + {"close", meth_close}, + {"dirty", meth_dirty}, + {"getfd", meth_getfd}, + {"getpeername", meth_getpeername}, + {"getsockname", meth_getsockname}, + {"receive", meth_receive}, + {"receivefrom", meth_receivefrom}, + {"send", meth_send}, + {"sendto", meth_sendto}, + {"setfd", meth_setfd}, + {"setoption", meth_setoption}, + {"setpeername", meth_setpeername}, + {"setsockname", meth_setsockname}, + {"settimeout", meth_settimeout}, + {NULL, NULL} +}; + +/* socket options */ +static t_opt opt[] = { + {"dontroute", opt_dontroute}, + {"broadcast", opt_broadcast}, + {"reuseaddr", opt_reuseaddr}, + {"ip-multicast-ttl", opt_ip_multicast_ttl}, + {"ip-multicast-loop", opt_ip_multicast_loop}, + {"ip-add-membership", opt_ip_add_membership}, + {"ip-drop-membership", opt_ip_drop_membersip}, + {NULL, NULL} +}; + +/* functions in library namespace */ +static luaL_reg func[] = { + {"udp", global_create}, + {NULL, NULL} +}; + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int udp_open(lua_State *L) +{ + /* create classes */ + auxiliar_newclass(L, "udp{connected}", udp); + auxiliar_newclass(L, "udp{unconnected}", udp); + /* create class groups */ + auxiliar_add2group(L, "udp{connected}", "udp{any}"); + auxiliar_add2group(L, "udp{unconnected}", "udp{any}"); + auxiliar_add2group(L, "udp{connected}", "select{able}"); + auxiliar_add2group(L, "udp{unconnected}", "select{able}"); + /* define library functions */ + luaL_openlib(L, NULL, func, 0); + return 0; +} + +/*=========================================================================*\ +* Lua methods +\*=========================================================================*/ +const char *udp_strerror(int err) { + /* a 'closed' error on an unconnected means the target address was not + * accepted by the transport layer */ + if (err == IO_CLOSED) return "refused"; + else return socket_strerror(err); +} + +/*-------------------------------------------------------------------------*\ +* Send data through connected udp socket +\*-------------------------------------------------------------------------*/ +static int meth_send(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{connected}", 1); + p_timeout tm = &udp->tm; + size_t count, sent = 0; + int err; + const char *data = luaL_checklstring(L, 2, &count); + timeout_markstart(tm); + err = socket_send(&udp->sock, data, count, &sent, tm); + if (err != IO_DONE) { + lua_pushnil(L); + lua_pushstring(L, udp_strerror(err)); + return 2; + } + lua_pushnumber(L, sent); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Send data through unconnected udp socket +\*-------------------------------------------------------------------------*/ +static int meth_sendto(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{unconnected}", 1); + size_t count, sent = 0; + const char *data = luaL_checklstring(L, 2, &count); + const char *ip = luaL_checkstring(L, 3); + unsigned short port = (unsigned short) luaL_checknumber(L, 4); + p_timeout tm = &udp->tm; + struct sockaddr_in addr; + int err; + memset(&addr, 0, sizeof(addr)); + if (!inet_aton(ip, &addr.sin_addr)) + luaL_argerror(L, 3, "invalid ip address"); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + timeout_markstart(tm); + err = socket_sendto(&udp->sock, data, count, &sent, + (SA *) &addr, sizeof(addr), tm); + if (err != IO_DONE) { + lua_pushnil(L); + lua_pushstring(L, udp_strerror(err)); + return 2; + } + lua_pushnumber(L, sent); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Receives data from a UDP socket +\*-------------------------------------------------------------------------*/ +static int meth_receive(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); + char buffer[UDP_DATAGRAMSIZE]; + size_t got, count = (size_t) luaL_optnumber(L, 2, sizeof(buffer)); + int err; + p_timeout tm = &udp->tm; + count = MIN(count, sizeof(buffer)); + timeout_markstart(tm); + err = socket_recv(&udp->sock, buffer, count, &got, tm); + if (err != IO_DONE) { + lua_pushnil(L); + lua_pushstring(L, udp_strerror(err)); + return 2; + } + lua_pushlstring(L, buffer, got); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Receives data and sender from a UDP socket +\*-------------------------------------------------------------------------*/ +static int meth_receivefrom(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{unconnected}", 1); + struct sockaddr_in addr; + socklen_t addr_len = sizeof(addr); + char buffer[UDP_DATAGRAMSIZE]; + size_t got, count = (size_t) luaL_optnumber(L, 2, sizeof(buffer)); + int err; + p_timeout tm = &udp->tm; + timeout_markstart(tm); + count = MIN(count, sizeof(buffer)); + err = socket_recvfrom(&udp->sock, buffer, count, &got, + (SA *) &addr, &addr_len, tm); + if (err == IO_DONE) { + lua_pushlstring(L, buffer, got); + lua_pushstring(L, inet_ntoa(addr.sin_addr)); + lua_pushnumber(L, ntohs(addr.sin_port)); + return 3; + } else { + lua_pushnil(L); + lua_pushstring(L, udp_strerror(err)); + return 2; + } +} + +/*-------------------------------------------------------------------------*\ +* Select support methods +\*-------------------------------------------------------------------------*/ +static int meth_getfd(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); + lua_pushnumber(L, (int) udp->sock); + return 1; +} + +/* this is very dangerous, but can be handy for those that are brave enough */ +static int meth_setfd(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); + udp->sock = (t_socket) luaL_checknumber(L, 2); + return 0; +} + +static int meth_dirty(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); + (void) udp; + lua_pushboolean(L, 0); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Just call inet methods +\*-------------------------------------------------------------------------*/ +static int meth_getpeername(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{connected}", 1); + return inet_meth_getpeername(L, &udp->sock); +} + +static int meth_getsockname(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); + return inet_meth_getsockname(L, &udp->sock); +} + +/*-------------------------------------------------------------------------*\ +* Just call option handler +\*-------------------------------------------------------------------------*/ +static int meth_setoption(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); + return opt_meth_setoption(L, opt, &udp->sock); +} + +/*-------------------------------------------------------------------------*\ +* Just call tm methods +\*-------------------------------------------------------------------------*/ +static int meth_settimeout(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); + return timeout_meth_settimeout(L, &udp->tm); +} + +/*-------------------------------------------------------------------------*\ +* Turns a master udp object into a client object. +\*-------------------------------------------------------------------------*/ +static int meth_setpeername(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); + p_timeout tm = &udp->tm; + const char *address = luaL_checkstring(L, 2); + int connecting = strcmp(address, "*"); + unsigned short port = connecting ? + (unsigned short) luaL_checknumber(L, 3) : + (unsigned short) luaL_optnumber(L, 3, 0); + const char *err = inet_tryconnect(&udp->sock, address, port, tm); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + /* change class to connected or unconnected depending on address */ + if (connecting) auxiliar_setclass(L, "udp{connected}", 1); + else auxiliar_setclass(L, "udp{unconnected}", 1); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Closes socket used by object +\*-------------------------------------------------------------------------*/ +static int meth_close(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); + socket_destroy(&udp->sock); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Turns a master object into a server object +\*-------------------------------------------------------------------------*/ +static int meth_setsockname(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{unconnected}", 1); + const char *address = luaL_checkstring(L, 2); + unsigned short port = (unsigned short) luaL_checknumber(L, 3); + const char *err = inet_trybind(&udp->sock, address, port); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + lua_pushnumber(L, 1); + return 1; +} + +/*=========================================================================*\ +* Library functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Creates a master udp object +\*-------------------------------------------------------------------------*/ +static int global_create(lua_State *L) { + t_socket sock; + const char *err = inet_trycreate(&sock, SOCK_DGRAM); + /* try to allocate a system socket */ + if (!err) { + /* allocate tcp object */ + p_udp udp = (p_udp) lua_newuserdata(L, sizeof(t_udp)); + auxiliar_setclass(L, "udp{unconnected}", -1); + /* initialize remaining structure fields */ + socket_setnonblocking(&sock); + udp->sock = sock; + timeout_init(&udp->tm, -1, -1); + return 1; + } else { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/udp.h Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,33 @@ +#ifndef UDP_H +#define UDP_H +/*=========================================================================*\ +* UDP object +* LuaSocket toolkit +* +* The udp.h module provides LuaSocket with support for UDP protocol +* (AF_INET, SOCK_DGRAM). +* +* Two classes are defined: connected and unconnected. UDP objects are +* originally unconnected. They can be "connected" to a given address +* with a call to the setpeername function. The same function can be used to +* break the connection. +* +* RCS ID: $Id: udp.h,v 1.10 2005/10/07 04:40:59 diego Exp $ +\*=========================================================================*/ +#include "lua.h" + +#include "timeout.h" +#include "socket.h" + +/* can't be larger than wsocket.c MAXCHUNK!!! */ +#define UDP_DATAGRAMSIZE 8192 + +typedef struct t_udp_ { + t_socket sock; + t_timeout tm; +} t_udp; +typedef t_udp *p_udp; + +int udp_open(lua_State *L); + +#endif /* UDP_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/unix.c Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,356 @@ +/*=========================================================================*\ +* Unix domain socket +* LuaSocket toolkit +* +* RCS ID: $Id: unix.c,v 1.13 2006/03/13 07:16:39 diego Exp $ +\*=========================================================================*/ +#include <string.h> + +#include "lua.h" +#include "lauxlib.h" + +#include "auxiliar.h" +#include "socket.h" +#include "options.h" +#include "unix.h" +#include <sys/un.h> + +/*=========================================================================*\ +* Internal function prototypes +\*=========================================================================*/ +static int global_create(lua_State *L); +static int meth_connect(lua_State *L); +static int meth_listen(lua_State *L); +static int meth_bind(lua_State *L); +static int meth_send(lua_State *L); +static int meth_shutdown(lua_State *L); +static int meth_receive(lua_State *L); +static int meth_accept(lua_State *L); +static int meth_close(lua_State *L); +static int meth_setoption(lua_State *L); +static int meth_settimeout(lua_State *L); +static int meth_getfd(lua_State *L); +static int meth_setfd(lua_State *L); +static int meth_dirty(lua_State *L); +static int meth_getstats(lua_State *L); +static int meth_setstats(lua_State *L); + +static const char *unix_tryconnect(p_unix un, const char *path); +static const char *unix_trybind(p_unix un, const char *path); + +/* unix object methods */ +static luaL_reg un[] = { + {"__gc", meth_close}, + {"__tostring", auxiliar_tostring}, + {"accept", meth_accept}, + {"bind", meth_bind}, + {"close", meth_close}, + {"connect", meth_connect}, + {"dirty", meth_dirty}, + {"getfd", meth_getfd}, + {"getstats", meth_getstats}, + {"setstats", meth_setstats}, + {"listen", meth_listen}, + {"receive", meth_receive}, + {"send", meth_send}, + {"setfd", meth_setfd}, + {"setoption", meth_setoption}, + {"setpeername", meth_connect}, + {"setsockname", meth_bind}, + {"settimeout", meth_settimeout}, + {"shutdown", meth_shutdown}, + {NULL, NULL} +}; + +/* socket option handlers */ +static t_opt opt[] = { + {"keepalive", opt_keepalive}, + {"reuseaddr", opt_reuseaddr}, + {"linger", opt_linger}, + {NULL, NULL} +}; + +/* our socket creation function */ +static luaL_reg func[] = { + {"unix", global_create}, + {NULL, NULL} +}; + + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int luaopen_socket_unix(lua_State *L) { + /* create classes */ + auxiliar_newclass(L, "unix{master}", un); + auxiliar_newclass(L, "unix{client}", un); + auxiliar_newclass(L, "unix{server}", un); + /* create class groups */ + auxiliar_add2group(L, "unix{master}", "unix{any}"); + auxiliar_add2group(L, "unix{client}", "unix{any}"); + auxiliar_add2group(L, "unix{server}", "unix{any}"); + /* make sure the function ends up in the package table */ + luaL_openlib(L, "socket", func, 0); + /* return the function instead of the 'socket' table */ + lua_pushstring(L, "unix"); + lua_gettable(L, -2); + return 1; +} + +/*=========================================================================*\ +* Lua methods +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Just call buffered IO methods +\*-------------------------------------------------------------------------*/ +static int meth_send(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "unix{client}", 1); + return buffer_meth_send(L, &un->buf); +} + +static int meth_receive(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "unix{client}", 1); + return buffer_meth_receive(L, &un->buf); +} + +static int meth_getstats(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "unix{client}", 1); + return buffer_meth_getstats(L, &un->buf); +} + +static int meth_setstats(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "unix{client}", 1); + return buffer_meth_setstats(L, &un->buf); +} + +/*-------------------------------------------------------------------------*\ +* Just call option handler +\*-------------------------------------------------------------------------*/ +static int meth_setoption(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unix{any}", 1); + return opt_meth_setoption(L, opt, &un->sock); +} + +/*-------------------------------------------------------------------------*\ +* Select support methods +\*-------------------------------------------------------------------------*/ +static int meth_getfd(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unix{any}", 1); + lua_pushnumber(L, (int) un->sock); + return 1; +} + +/* this is very dangerous, but can be handy for those that are brave enough */ +static int meth_setfd(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unix{any}", 1); + un->sock = (t_socket) luaL_checknumber(L, 2); + return 0; +} + +static int meth_dirty(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unix{any}", 1); + lua_pushboolean(L, !buffer_isempty(&un->buf)); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Waits for and returns a client object attempting connection to the +* server object +\*-------------------------------------------------------------------------*/ +static int meth_accept(lua_State *L) { + p_unix server = (p_unix) auxiliar_checkclass(L, "unix{server}", 1); + p_timeout tm = timeout_markstart(&server->tm); + t_socket sock; + int err = socket_accept(&server->sock, &sock, NULL, NULL, tm); + /* if successful, push client socket */ + if (err == IO_DONE) { + p_unix clnt = (p_unix) lua_newuserdata(L, sizeof(t_unix)); + auxiliar_setclass(L, "unix{client}", -1); + /* initialize structure fields */ + socket_setnonblocking(&sock); + clnt->sock = sock; + io_init(&clnt->io, (p_send)socket_send, (p_recv)socket_recv, + (p_error) socket_ioerror, &clnt->sock); + timeout_init(&clnt->tm, -1, -1); + buffer_init(&clnt->buf, &clnt->io, &clnt->tm); + return 1; + } else { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(err)); + return 2; + } +} + +/*-------------------------------------------------------------------------*\ +* Binds an object to an address +\*-------------------------------------------------------------------------*/ +static const char *unix_trybind(p_unix un, const char *path) { + struct sockaddr_un local; + size_t len = strlen(path); + int err; + if (len >= sizeof(local.sun_path)) return "path too long"; + memset(&local, 0, sizeof(local)); + strcpy(local.sun_path, path); + local.sun_family = AF_UNIX; +#ifdef UNIX_HAS_SUN_LEN + local.sun_len = sizeof(local.sun_family) + sizeof(local.sun_len) + + len + 1; + err = socket_bind(&un->sock, (SA *) &local, local.sun_len); + +#else + err = socket_bind(&un->sock, (SA *) &local, + sizeof(local.sun_family) + len); +#endif + if (err != IO_DONE) socket_destroy(&un->sock); + return socket_strerror(err); +} + +static int meth_bind(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "unix{master}", 1); + const char *path = luaL_checkstring(L, 2); + const char *err = unix_trybind(un, path); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Turns a master unix object into a client object. +\*-------------------------------------------------------------------------*/ +static const char *unix_tryconnect(p_unix un, const char *path) +{ + struct sockaddr_un remote; + int err; + size_t len = strlen(path); + if (len >= sizeof(remote.sun_path)) return "path too long"; + memset(&remote, 0, sizeof(remote)); + strcpy(remote.sun_path, path); + remote.sun_family = AF_UNIX; + timeout_markstart(&un->tm); +#ifdef UNIX_HAS_SUN_LEN + remote.sun_len = sizeof(remote.sun_family) + sizeof(remote.sun_len) + + len + 1; + err = socket_connect(&un->sock, (SA *) &remote, remote.sun_len, &un->tm); +#else + err = socket_connect(&un->sock, (SA *) &remote, + sizeof(remote.sun_family) + len, &un->tm); +#endif + if (err != IO_DONE) socket_destroy(&un->sock); + return socket_strerror(err); +} + +static int meth_connect(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkclass(L, "unix{master}", 1); + const char *path = luaL_checkstring(L, 2); + const char *err = unix_tryconnect(un, path); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + /* turn master object into a client object */ + auxiliar_setclass(L, "unix{client}", 1); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Closes socket used by object +\*-------------------------------------------------------------------------*/ +static int meth_close(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkgroup(L, "unix{any}", 1); + socket_destroy(&un->sock); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Puts the sockt in listen mode +\*-------------------------------------------------------------------------*/ +static int meth_listen(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkclass(L, "unix{master}", 1); + int backlog = (int) luaL_optnumber(L, 2, 32); + int err = socket_listen(&un->sock, backlog); + if (err != IO_DONE) { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(err)); + return 2; + } + /* turn master object into a server object */ + auxiliar_setclass(L, "unix{server}", 1); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Shuts the connection down partially +\*-------------------------------------------------------------------------*/ +static int meth_shutdown(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkclass(L, "unix{client}", 1); + const char *how = luaL_optstring(L, 2, "both"); + switch (how[0]) { + case 'b': + if (strcmp(how, "both")) goto error; + socket_shutdown(&un->sock, 2); + break; + case 's': + if (strcmp(how, "send")) goto error; + socket_shutdown(&un->sock, 1); + break; + case 'r': + if (strcmp(how, "receive")) goto error; + socket_shutdown(&un->sock, 0); + break; + } + lua_pushnumber(L, 1); + return 1; +error: + luaL_argerror(L, 2, "invalid shutdown method"); + return 0; +} + +/*-------------------------------------------------------------------------*\ +* Just call tm methods +\*-------------------------------------------------------------------------*/ +static int meth_settimeout(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unix{any}", 1); + return timeout_meth_settimeout(L, &un->tm); +} + +/*=========================================================================*\ +* Library functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Creates a master unix object +\*-------------------------------------------------------------------------*/ +static int global_create(lua_State *L) { + t_socket sock; + int err = socket_create(&sock, AF_UNIX, SOCK_STREAM, 0); + /* try to allocate a system socket */ + if (err == IO_DONE) { + /* allocate unix object */ + p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix)); + /* set its type as master object */ + auxiliar_setclass(L, "unix{master}", -1); + /* initialize remaining structure fields */ + socket_setnonblocking(&sock); + un->sock = sock; + io_init(&un->io, (p_send) socket_send, (p_recv) socket_recv, + (p_error) socket_ioerror, &un->sock); + timeout_init(&un->tm, -1, -1); + buffer_init(&un->buf, &un->io, &un->tm); + return 1; + } else { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(err)); + return 2; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/unix.h Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,28 @@ +#ifndef UNIX_H +#define UNIX_H +/*=========================================================================*\ +* Unix domain object +* LuaSocket toolkit +* +* This module is just an example of how to extend LuaSocket with a new +* domain. +* +* RCS ID: $Id: unix.h,v 1.9 2006/03/13 07:16:39 diego Exp $ +\*=========================================================================*/ +#include "lua.h" + +#include "buffer.h" +#include "timeout.h" +#include "socket.h" + +typedef struct t_unix_ { + t_socket sock; + t_io io; + t_buffer buf; + t_timeout tm; +} t_unix; +typedef t_unix *p_unix; + +int luaopen_socket_unix(lua_State *L); + +#endif /* UNIX_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/url.lua Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,297 @@ +----------------------------------------------------------------------------- +-- URI parsing, composition and relative URL resolution +-- LuaSocket toolkit. +-- Author: Diego Nehab +-- RCS ID: $Id: url.lua,v 1.38 2006/04/03 04:45:42 diego Exp $ +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module +----------------------------------------------------------------------------- +local string = require("string") +local base = _G +local table = require("table") +module("socket.url") + +----------------------------------------------------------------------------- +-- Module version +----------------------------------------------------------------------------- +_VERSION = "URL 1.0.1" + +----------------------------------------------------------------------------- +-- Encodes a string into its escaped hexadecimal representation +-- Input +-- s: binary string to be encoded +-- Returns +-- escaped representation of string binary +----------------------------------------------------------------------------- +function escape(s) + return string.gsub(s, "([^A-Za-z0-9_])", function(c) + return string.format("%%%02x", string.byte(c)) + end) +end + +----------------------------------------------------------------------------- +-- Protects a path segment, to prevent it from interfering with the +-- url parsing. +-- Input +-- s: binary string to be encoded +-- Returns +-- escaped representation of string binary +----------------------------------------------------------------------------- +local function make_set(t) + local s = {} + for i,v in base.ipairs(t) do + s[t[i]] = 1 + end + return s +end + +-- these are allowed withing a path segment, along with alphanum +-- other characters must be escaped +local segment_set = make_set { + "-", "_", ".", "!", "~", "*", "'", "(", + ")", ":", "@", "&", "=", "+", "$", ",", +} + +local function protect_segment(s) + return string.gsub(s, "([^A-Za-z0-9_])", function (c) + if segment_set[c] then return c + else return string.format("%%%02x", string.byte(c)) end + end) +end + +----------------------------------------------------------------------------- +-- Encodes a string into its escaped hexadecimal representation +-- Input +-- s: binary string to be encoded +-- Returns +-- escaped representation of string binary +----------------------------------------------------------------------------- +function unescape(s) + return string.gsub(s, "%%(%x%x)", function(hex) + return string.char(base.tonumber(hex, 16)) + end) +end + +----------------------------------------------------------------------------- +-- Builds a path from a base path and a relative path +-- Input +-- base_path +-- relative_path +-- Returns +-- corresponding absolute path +----------------------------------------------------------------------------- +local function absolute_path(base_path, relative_path) + if string.sub(relative_path, 1, 1) == "/" then return relative_path end + local path = string.gsub(base_path, "[^/]*$", "") + path = path .. relative_path + path = string.gsub(path, "([^/]*%./)", function (s) + if s ~= "./" then return s else return "" end + end) + path = string.gsub(path, "/%.$", "/") + local reduced + while reduced ~= path do + reduced = path + path = string.gsub(reduced, "([^/]*/%.%./)", function (s) + if s ~= "../../" then return "" else return s end + end) + end + path = string.gsub(reduced, "([^/]*/%.%.)$", function (s) + if s ~= "../.." then return "" else return s end + end) + return path +end + +----------------------------------------------------------------------------- +-- Parses a url and returns a table with all its parts according to RFC 2396 +-- The following grammar describes the names given to the URL parts +-- <url> ::= <scheme>://<authority>/<path>;<params>?<query>#<fragment> +-- <authority> ::= <userinfo>@<host>:<port> +-- <userinfo> ::= <user>[:<password>] +-- <path> :: = {<segment>/}<segment> +-- Input +-- url: uniform resource locator of request +-- default: table with default values for each field +-- Returns +-- table with the following fields, where RFC naming conventions have +-- been preserved: +-- scheme, authority, userinfo, user, password, host, port, +-- path, params, query, fragment +-- Obs: +-- the leading '/' in {/<path>} is considered part of <path> +----------------------------------------------------------------------------- +function parse(url, default) + -- initialize default parameters + local parsed = {} + for i,v in base.pairs(default or parsed) do parsed[i] = v end + -- empty url is parsed to nil + if not url or url == "" then return nil, "invalid url" end + -- remove whitespace + -- url = string.gsub(url, "%s", "") + -- get fragment + url = string.gsub(url, "#(.*)$", function(f) + parsed.fragment = f + return "" + end) + -- get scheme + url = string.gsub(url, "^([%w][%w%+%-%.]*)%:", + function(s) parsed.scheme = s; return "" end) + -- get authority + url = string.gsub(url, "^//([^/]*)", function(n) + parsed.authority = n + return "" + end) + -- get query stringing + url = string.gsub(url, "%?(.*)", function(q) + parsed.query = q + return "" + end) + -- get params + url = string.gsub(url, "%;(.*)", function(p) + parsed.params = p + return "" + end) + -- path is whatever was left + if url ~= "" then parsed.path = url end + local authority = parsed.authority + if not authority then return parsed end + authority = string.gsub(authority,"^([^@]*)@", + function(u) parsed.userinfo = u; return "" end) + authority = string.gsub(authority, ":([^:]*)$", + function(p) parsed.port = p; return "" end) + if authority ~= "" then parsed.host = authority end + local userinfo = parsed.userinfo + if not userinfo then return parsed end + userinfo = string.gsub(userinfo, ":([^:]*)$", + function(p) parsed.password = p; return "" end) + parsed.user = userinfo + return parsed +end + +----------------------------------------------------------------------------- +-- Rebuilds a parsed URL from its components. +-- Components are protected if any reserved or unallowed characters are found +-- Input +-- parsed: parsed URL, as returned by parse +-- Returns +-- a stringing with the corresponding URL +----------------------------------------------------------------------------- +function build(parsed) + local ppath = parse_path(parsed.path or "") + local url = build_path(ppath) + if parsed.params then url = url .. ";" .. parsed.params end + if parsed.query then url = url .. "?" .. parsed.query end + local authority = parsed.authority + if parsed.host then + authority = parsed.host + if parsed.port then authority = authority .. ":" .. parsed.port end + local userinfo = parsed.userinfo + if parsed.user then + userinfo = parsed.user + if parsed.password then + userinfo = userinfo .. ":" .. parsed.password + end + end + if userinfo then authority = userinfo .. "@" .. authority end + end + if authority then url = "//" .. authority .. url end + if parsed.scheme then url = parsed.scheme .. ":" .. url end + if parsed.fragment then url = url .. "#" .. parsed.fragment end + -- url = string.gsub(url, "%s", "") + return url +end + +----------------------------------------------------------------------------- +-- Builds a absolute URL from a base and a relative URL according to RFC 2396 +-- Input +-- base_url +-- relative_url +-- Returns +-- corresponding absolute url +----------------------------------------------------------------------------- +function absolute(base_url, relative_url) + if base.type(base_url) == "table" then + base_parsed = base_url + base_url = build(base_parsed) + else + base_parsed = parse(base_url) + end + local relative_parsed = parse(relative_url) + if not base_parsed then return relative_url + elseif not relative_parsed then return base_url + elseif relative_parsed.scheme then return relative_url + else + relative_parsed.scheme = base_parsed.scheme + if not relative_parsed.authority then + relative_parsed.authority = base_parsed.authority + if not relative_parsed.path then + relative_parsed.path = base_parsed.path + if not relative_parsed.params then + relative_parsed.params = base_parsed.params + if not relative_parsed.query then + relative_parsed.query = base_parsed.query + end + end + else + relative_parsed.path = absolute_path(base_parsed.path or "", + relative_parsed.path) + end + end + return build(relative_parsed) + end +end + +----------------------------------------------------------------------------- +-- Breaks a path into its segments, unescaping the segments +-- Input +-- path +-- Returns +-- segment: a table with one entry per segment +----------------------------------------------------------------------------- +function parse_path(path) + local parsed = {} + path = path or "" + --path = string.gsub(path, "%s", "") + string.gsub(path, "([^/]+)", function (s) table.insert(parsed, s) end) + for i = 1, table.getn(parsed) do + parsed[i] = unescape(parsed[i]) + end + if string.sub(path, 1, 1) == "/" then parsed.is_absolute = 1 end + if string.sub(path, -1, -1) == "/" then parsed.is_directory = 1 end + return parsed +end + +----------------------------------------------------------------------------- +-- Builds a path component from its segments, escaping protected characters. +-- Input +-- parsed: path segments +-- unsafe: if true, segments are not protected before path is built +-- Returns +-- path: corresponding path stringing +----------------------------------------------------------------------------- +function build_path(parsed, unsafe) + local path = "" + local n = table.getn(parsed) + if unsafe then + for i = 1, n-1 do + path = path .. parsed[i] + path = path .. "/" + end + if n > 0 then + path = path .. parsed[n] + if parsed.is_directory then path = path .. "/" end + end + else + for i = 1, n-1 do + path = path .. protect_segment(parsed[i]) + path = path .. "/" + end + if n > 0 then + path = path .. protect_segment(parsed[n]) + if parsed.is_directory then path = path .. "/" end + end + end + if parsed.is_absolute then path = "/" .. path end + return path +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/usocket.c Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,370 @@ +/*=========================================================================*\ +* Socket compatibilization module for Unix +* LuaSocket toolkit +* +* The code is now interrupt-safe. +* The penalty of calling select to avoid busy-wait is only paid when +* the I/O call fail in the first place. +* +* RCS ID: $Id: usocket.c,v 1.38 2007/10/13 23:55:20 diego Exp $ +\*=========================================================================*/ +#include <string.h> +#include <signal.h> + +#include "socket.h" + +/*-------------------------------------------------------------------------*\ +* Wait for readable/writable/connected socket with timeout +\*-------------------------------------------------------------------------*/ +#ifdef SOCKET_POLL +#include <sys/poll.h> + +#define WAITFD_R POLLIN +#define WAITFD_W POLLOUT +#define WAITFD_C (POLLIN|POLLOUT) +int socket_waitfd(p_socket ps, int sw, p_timeout tm) { + int ret; + struct pollfd pfd; + pfd.fd = *ps; + pfd.events = sw; + pfd.revents = 0; + if (timeout_iszero(tm)) return IO_TIMEOUT; /* optimize timeout == 0 case */ + do { + int t = (int)(timeout_getretry(tm)*1e3); + ret = poll(&pfd, 1, t >= 0? t: -1); + } while (ret == -1 && errno == EINTR); + if (ret == -1) return errno; + if (ret == 0) return IO_TIMEOUT; + if (sw == WAITFD_C && (pfd.revents & (POLLIN|POLLERR))) return IO_CLOSED; + return IO_DONE; +} +#else + +#define WAITFD_R 1 +#define WAITFD_W 2 +#define WAITFD_C (WAITFD_R|WAITFD_W) + +int socket_waitfd(p_socket ps, int sw, p_timeout tm) { + int ret; + fd_set rfds, wfds, *rp, *wp; + struct timeval tv, *tp; + double t; + if (timeout_iszero(tm)) return IO_TIMEOUT; /* optimize timeout == 0 case */ + do { + /* must set bits within loop, because select may have modifed them */ + rp = wp = NULL; + if (sw & WAITFD_R) { FD_ZERO(&rfds); FD_SET(*ps, &rfds); rp = &rfds; } + if (sw & WAITFD_W) { FD_ZERO(&wfds); FD_SET(*ps, &wfds); wp = &wfds; } + t = timeout_getretry(tm); + tp = NULL; + if (t >= 0.0) { + tv.tv_sec = (int)t; + tv.tv_usec = (int)((t-tv.tv_sec)*1.0e6); + tp = &tv; + } + ret = select(*ps+1, rp, wp, NULL, tp); + } while (ret == -1 && errno == EINTR); + if (ret == -1) return errno; + if (ret == 0) return IO_TIMEOUT; + if (sw == WAITFD_C && FD_ISSET(*ps, &rfds)) return IO_CLOSED; + return IO_DONE; +} +#endif + + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int socket_open(void) { + /* instals a handler to ignore sigpipe or it will crash us */ + signal(SIGPIPE, SIG_IGN); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Close module +\*-------------------------------------------------------------------------*/ +int socket_close(void) { + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Close and inutilize socket +\*-------------------------------------------------------------------------*/ +void socket_destroy(p_socket ps) { + if (*ps != SOCKET_INVALID) { + socket_setblocking(ps); + close(*ps); + *ps = SOCKET_INVALID; + } +} + +/*-------------------------------------------------------------------------*\ +* Select with timeout control +\*-------------------------------------------------------------------------*/ +int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds, + p_timeout tm) { + int ret; + do { + struct timeval tv; + double t = timeout_getretry(tm); + tv.tv_sec = (int) t; + tv.tv_usec = (int) ((t - tv.tv_sec) * 1.0e6); + /* timeout = 0 means no wait */ + ret = select(n, rfds, wfds, efds, t >= 0.0 ? &tv: NULL); + } while (ret < 0 && errno == EINTR); + return ret; +} + +/*-------------------------------------------------------------------------*\ +* Creates and sets up a socket +\*-------------------------------------------------------------------------*/ +int socket_create(p_socket ps, int domain, int type, int protocol) { + *ps = socket(domain, type, protocol); + if (*ps != SOCKET_INVALID) return IO_DONE; + else return errno; +} + +/*-------------------------------------------------------------------------*\ +* Binds or returns error message +\*-------------------------------------------------------------------------*/ +int socket_bind(p_socket ps, SA *addr, socklen_t len) { + int err = IO_DONE; + socket_setblocking(ps); + if (bind(*ps, addr, len) < 0) err = errno; + socket_setnonblocking(ps); + return err; +} + +/*-------------------------------------------------------------------------*\ +* +\*-------------------------------------------------------------------------*/ +int socket_listen(p_socket ps, int backlog) { + int err = IO_DONE; + socket_setblocking(ps); + if (listen(*ps, backlog)) err = errno; + socket_setnonblocking(ps); + return err; +} + +/*-------------------------------------------------------------------------*\ +* +\*-------------------------------------------------------------------------*/ +void socket_shutdown(p_socket ps, int how) { + socket_setblocking(ps); + shutdown(*ps, how); + socket_setnonblocking(ps); +} + +/*-------------------------------------------------------------------------*\ +* Connects or returns error message +\*-------------------------------------------------------------------------*/ +int socket_connect(p_socket ps, SA *addr, socklen_t len, p_timeout tm) { + int err; + /* avoid calling on closed sockets */ + if (*ps == SOCKET_INVALID) return IO_CLOSED; + /* call connect until done or failed without being interrupted */ + do if (connect(*ps, addr, len) == 0) return IO_DONE; + while ((err = errno) == EINTR); + /* if connection failed immediately, return error code */ + if (err != EINPROGRESS && err != EAGAIN) return err; + /* zero timeout case optimization */ + if (timeout_iszero(tm)) return IO_TIMEOUT; + /* wait until we have the result of the connection attempt or timeout */ + err = socket_waitfd(ps, WAITFD_C, tm); + if (err == IO_CLOSED) { + if (recv(*ps, (char *) &err, 0, 0) == 0) return IO_DONE; + else return errno; + } else return err; +} + +/*-------------------------------------------------------------------------*\ +* Accept with timeout +\*-------------------------------------------------------------------------*/ +int socket_accept(p_socket ps, p_socket pa, SA *addr, socklen_t *len, p_timeout tm) { + SA daddr; + socklen_t dlen = sizeof(daddr); + if (*ps == SOCKET_INVALID) return IO_CLOSED; + if (!addr) addr = &daddr; + if (!len) len = &dlen; + for ( ;; ) { + int err; + if ((*pa = accept(*ps, addr, len)) != SOCKET_INVALID) return IO_DONE; + err = errno; + if (err == EINTR) continue; + if (err != EAGAIN && err != ECONNABORTED) return err; + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } + /* can't reach here */ + return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Send with timeout +\*-------------------------------------------------------------------------*/ +int socket_send(p_socket ps, const char *data, size_t count, + size_t *sent, p_timeout tm) +{ + int err; + *sent = 0; + /* avoid making system calls on closed sockets */ + if (*ps == SOCKET_INVALID) return IO_CLOSED; + /* loop until we send something or we give up on error */ + for ( ;; ) { + long put = (long) send(*ps, data, count, 0); + /* if we sent anything, we are done */ + if (put > 0) { + *sent = put; + return IO_DONE; + } + err = errno; + /* send can't really return 0, but EPIPE means the connection was + closed */ + if (put == 0 || err == EPIPE) return IO_CLOSED; + /* we call was interrupted, just try again */ + if (err == EINTR) continue; + /* if failed fatal reason, report error */ + if (err != EAGAIN) return err; + /* wait until we can send something or we timeout */ + if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; + } + /* can't reach here */ + return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Sendto with timeout +\*-------------------------------------------------------------------------*/ +int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent, + SA *addr, socklen_t len, p_timeout tm) +{ + int err; + *sent = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + long put = (long) sendto(*ps, data, count, 0, addr, len); + if (put > 0) { + *sent = put; + return IO_DONE; + } + err = errno; + if (put == 0 || err == EPIPE) return IO_CLOSED; + if (err == EINTR) continue; + if (err != EAGAIN) return err; + if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; + } + return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Receive with timeout +\*-------------------------------------------------------------------------*/ +int socket_recv(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm) { + int err; + *got = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + long taken = (long) recv(*ps, data, count, 0); + if (taken > 0) { + *got = taken; + return IO_DONE; + } + err = errno; + if (taken == 0) return IO_CLOSED; + if (err == EINTR) continue; + if (err != EAGAIN) return err; + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } + return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Recvfrom with timeout +\*-------------------------------------------------------------------------*/ +int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got, + SA *addr, socklen_t *len, p_timeout tm) { + int err; + *got = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + long taken = (long) recvfrom(*ps, data, count, 0, addr, len); + if (taken > 0) { + *got = taken; + return IO_DONE; + } + err = errno; + if (taken == 0) return IO_CLOSED; + if (err == EINTR) continue; + if (err != EAGAIN) return err; + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } + return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Put socket into blocking mode +\*-------------------------------------------------------------------------*/ +void socket_setblocking(p_socket ps) { + int flags = fcntl(*ps, F_GETFL, 0); + flags &= (~(O_NONBLOCK)); + fcntl(*ps, F_SETFL, flags); +} + +/*-------------------------------------------------------------------------*\ +* Put socket into non-blocking mode +\*-------------------------------------------------------------------------*/ +void socket_setnonblocking(p_socket ps) { + int flags = fcntl(*ps, F_GETFL, 0); + flags |= O_NONBLOCK; + fcntl(*ps, F_SETFL, flags); +} + +/*-------------------------------------------------------------------------*\ +* DNS helpers +\*-------------------------------------------------------------------------*/ +int socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp) { + *hp = gethostbyaddr(addr, len, AF_INET); + if (*hp) return IO_DONE; + else if (h_errno) return h_errno; + else if (errno) return errno; + else return IO_UNKNOWN; +} + +int socket_gethostbyname(const char *addr, struct hostent **hp) { + *hp = gethostbyname(addr); + if (*hp) return IO_DONE; + else if (h_errno) return h_errno; + else if (errno) return errno; + else return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Error translation functions +* Make sure important error messages are standard +\*-------------------------------------------------------------------------*/ +const char *socket_hoststrerror(int err) { + if (err <= 0) return io_strerror(err); + switch (err) { + case HOST_NOT_FOUND: return "host not found"; + default: return hstrerror(err); + } +} + +const char *socket_strerror(int err) { + if (err <= 0) return io_strerror(err); + switch (err) { + case EADDRINUSE: return "address already in use"; + case EISCONN: return "already connected"; + case EACCES: return "permission denied"; + case ECONNREFUSED: return "connection refused"; + case ECONNABORTED: return "closed"; + case ECONNRESET: return "closed"; + case ETIMEDOUT: return "timeout"; + default: return strerror(errno); + } +} + +const char *socket_ioerror(p_socket ps, int err) { + (void) ps; + return socket_strerror(err); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/usocket.h Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,40 @@ +#ifndef USOCKET_H +#define USOCKET_H +/*=========================================================================*\ +* Socket compatibilization module for Unix +* LuaSocket toolkit +* +* RCS ID: $Id: usocket.h,v 1.7 2005/10/07 04:40:59 diego Exp $ +\*=========================================================================*/ + +/*=========================================================================*\ +* BSD include files +\*=========================================================================*/ +/* error codes */ +#include <errno.h> +/* close function */ +#include <unistd.h> +/* fnctnl function and associated constants */ +#include <fcntl.h> +/* struct sockaddr */ +#include <sys/types.h> +/* socket function */ +#include <sys/socket.h> +/* struct timeval */ +#include <sys/time.h> +/* gethostbyname and gethostbyaddr functions */ +#include <netdb.h> +/* sigpipe handling */ +#include <signal.h> +/* IP stuff*/ +#include <netinet/in.h> +#include <arpa/inet.h> +/* TCP options (nagle algorithm disable) */ +#include <netinet/tcp.h> + +typedef int t_socket; +typedef t_socket *p_socket; + +#define SOCKET_INVALID (-1) + +#endif /* USOCKET_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/wsocket.c Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,401 @@ +/*=========================================================================*\ +* Socket compatibilization module for Win32 +* LuaSocket toolkit +* +* The penalty of calling select to avoid busy-wait is only paid when +* the I/O call fail in the first place. +* +* RCS ID: $Id: wsocket.c,v 1.36 2007/06/11 23:44:54 diego Exp $ +\*=========================================================================*/ +#include <string.h> + +#include "socket.h" + +/* WinSock doesn't have a strerror... */ +static const char *wstrerror(int err); + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int socket_open(void) { + WSADATA wsaData; + WORD wVersionRequested = MAKEWORD(2, 0); + int err = WSAStartup(wVersionRequested, &wsaData ); + if (err != 0) return 0; + if ((LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 0) && + (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)) { + WSACleanup(); + return 0; + } + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Close module +\*-------------------------------------------------------------------------*/ +int socket_close(void) { + WSACleanup(); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Wait for readable/writable/connected socket with timeout +\*-------------------------------------------------------------------------*/ +#define WAITFD_R 1 +#define WAITFD_W 2 +#define WAITFD_E 4 +#define WAITFD_C (WAITFD_E|WAITFD_W) + +int socket_waitfd(p_socket ps, int sw, p_timeout tm) { + int ret; + fd_set rfds, wfds, efds, *rp = NULL, *wp = NULL, *ep = NULL; + struct timeval tv, *tp = NULL; + double t; + if (timeout_iszero(tm)) return IO_TIMEOUT; /* optimize timeout == 0 case */ + if (sw & WAITFD_R) { + FD_ZERO(&rfds); + FD_SET(*ps, &rfds); + rp = &rfds; + } + if (sw & WAITFD_W) { FD_ZERO(&wfds); FD_SET(*ps, &wfds); wp = &wfds; } + if (sw & WAITFD_C) { FD_ZERO(&efds); FD_SET(*ps, &efds); ep = &efds; } + if ((t = timeout_get(tm)) >= 0.0) { + tv.tv_sec = (int) t; + tv.tv_usec = (int) ((t-tv.tv_sec)*1.0e6); + tp = &tv; + } + ret = select(0, rp, wp, ep, tp); + if (ret == -1) return WSAGetLastError(); + if (ret == 0) return IO_TIMEOUT; + if (sw == WAITFD_C && FD_ISSET(*ps, &efds)) return IO_CLOSED; + return IO_DONE; +} + +/*-------------------------------------------------------------------------*\ +* Select with int timeout in ms +\*-------------------------------------------------------------------------*/ +int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds, + p_timeout tm) { + struct timeval tv; + double t = timeout_get(tm); + tv.tv_sec = (int) t; + tv.tv_usec = (int) ((t - tv.tv_sec) * 1.0e6); + if (n <= 0) { + Sleep((DWORD) (1000*t)); + return 0; + } else return select(0, rfds, wfds, efds, t >= 0.0? &tv: NULL); +} + +/*-------------------------------------------------------------------------*\ +* Close and inutilize socket +\*-------------------------------------------------------------------------*/ +void socket_destroy(p_socket ps) { + if (*ps != SOCKET_INVALID) { + socket_setblocking(ps); /* close can take a long time on WIN32 */ + closesocket(*ps); + *ps = SOCKET_INVALID; + } +} + +/*-------------------------------------------------------------------------*\ +* +\*-------------------------------------------------------------------------*/ +void socket_shutdown(p_socket ps, int how) { + socket_setblocking(ps); + shutdown(*ps, how); + socket_setnonblocking(ps); +} + +/*-------------------------------------------------------------------------*\ +* Creates and sets up a socket +\*-------------------------------------------------------------------------*/ +int socket_create(p_socket ps, int domain, int type, int protocol) { + *ps = socket(domain, type, protocol); + if (*ps != SOCKET_INVALID) return IO_DONE; + else return WSAGetLastError(); +} + +/*-------------------------------------------------------------------------*\ +* Connects or returns error message +\*-------------------------------------------------------------------------*/ +int socket_connect(p_socket ps, SA *addr, socklen_t len, p_timeout tm) { + int err; + /* don't call on closed socket */ + if (*ps == SOCKET_INVALID) return IO_CLOSED; + /* ask system to connect */ + if (connect(*ps, addr, len) == 0) return IO_DONE; + /* make sure the system is trying to connect */ + err = WSAGetLastError(); + if (err != WSAEWOULDBLOCK && err != WSAEINPROGRESS) return err; + /* zero timeout case optimization */ + if (timeout_iszero(tm)) return IO_TIMEOUT; + /* we wait until something happens */ + err = socket_waitfd(ps, WAITFD_C, tm); + if (err == IO_CLOSED) { + int len = sizeof(err); + /* give windows time to set the error (yes, disgusting) */ + Sleep(10); + /* find out why we failed */ + getsockopt(*ps, SOL_SOCKET, SO_ERROR, (char *)&err, &len); + /* we KNOW there was an error. if 'why' is 0, we will return + * "unknown error", but it's not really our fault */ + return err > 0? err: IO_UNKNOWN; + } else return err; + +} + +/*-------------------------------------------------------------------------*\ +* Binds or returns error message +\*-------------------------------------------------------------------------*/ +int socket_bind(p_socket ps, SA *addr, socklen_t len) { + int err = IO_DONE; + socket_setblocking(ps); + if (bind(*ps, addr, len) < 0) err = WSAGetLastError(); + socket_setnonblocking(ps); + return err; +} + +/*-------------------------------------------------------------------------*\ +* +\*-------------------------------------------------------------------------*/ +int socket_listen(p_socket ps, int backlog) { + int err = IO_DONE; + socket_setblocking(ps); + if (listen(*ps, backlog) < 0) err = WSAGetLastError(); + socket_setnonblocking(ps); + return err; +} + +/*-------------------------------------------------------------------------*\ +* Accept with timeout +\*-------------------------------------------------------------------------*/ +int socket_accept(p_socket ps, p_socket pa, SA *addr, socklen_t *len, + p_timeout tm) { + SA daddr; + socklen_t dlen = sizeof(daddr); + if (*ps == SOCKET_INVALID) return IO_CLOSED; + if (!addr) addr = &daddr; + if (!len) len = &dlen; + for ( ;; ) { + int err; + /* try to get client socket */ + if ((*pa = accept(*ps, addr, len)) != SOCKET_INVALID) return IO_DONE; + /* find out why we failed */ + err = WSAGetLastError(); + /* if we failed because there was no connectoin, keep trying */ + if (err != WSAEWOULDBLOCK && err != WSAECONNABORTED) return err; + /* call select to avoid busy wait */ + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } + /* can't reach here */ + return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Send with timeout +* On windows, if you try to send 10MB, the OS will buffer EVERYTHING +* this can take an awful lot of time and we will end up blocked. +* Therefore, whoever calls this function should not pass a huge buffer. +\*-------------------------------------------------------------------------*/ +int socket_send(p_socket ps, const char *data, size_t count, + size_t *sent, p_timeout tm) +{ + int err; + *sent = 0; + /* avoid making system calls on closed sockets */ + if (*ps == SOCKET_INVALID) return IO_CLOSED; + /* loop until we send something or we give up on error */ + for ( ;; ) { + /* try to send something */ + int put = send(*ps, data, (int) count, 0); + /* if we sent something, we are done */ + if (put > 0) { + *sent = put; + return IO_DONE; + } + /* deal with failure */ + err = WSAGetLastError(); + /* we can only proceed if there was no serious error */ + if (err != WSAEWOULDBLOCK) return err; + /* avoid busy wait */ + if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; + } + /* can't reach here */ + return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Sendto with timeout +\*-------------------------------------------------------------------------*/ +int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent, + SA *addr, socklen_t len, p_timeout tm) +{ + int err; + *sent = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + int put = sendto(*ps, data, (int) count, 0, addr, len); + if (put > 0) { + *sent = put; + return IO_DONE; + } + err = WSAGetLastError(); + if (err != WSAEWOULDBLOCK) return err; + if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; + } + return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Receive with timeout +\*-------------------------------------------------------------------------*/ +int socket_recv(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm) { + int err; + *got = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + int taken = recv(*ps, data, (int) count, 0); + if (taken > 0) { + *got = taken; + return IO_DONE; + } + if (taken == 0) return IO_CLOSED; + err = WSAGetLastError(); + if (err != WSAEWOULDBLOCK) return err; + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } + return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Recvfrom with timeout +\*-------------------------------------------------------------------------*/ +int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got, + SA *addr, socklen_t *len, p_timeout tm) { + int err; + *got = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + int taken = recvfrom(*ps, data, (int) count, 0, addr, len); + if (taken > 0) { + *got = taken; + return IO_DONE; + } + if (taken == 0) return IO_CLOSED; + err = WSAGetLastError(); + if (err != WSAEWOULDBLOCK) return err; + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } + return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Put socket into blocking mode +\*-------------------------------------------------------------------------*/ +void socket_setblocking(p_socket ps) { + u_long argp = 0; + ioctlsocket(*ps, FIONBIO, &argp); +} + +/*-------------------------------------------------------------------------*\ +* Put socket into non-blocking mode +\*-------------------------------------------------------------------------*/ +void socket_setnonblocking(p_socket ps) { + u_long argp = 1; + ioctlsocket(*ps, FIONBIO, &argp); +} + +/*-------------------------------------------------------------------------*\ +* DNS helpers +\*-------------------------------------------------------------------------*/ +int socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp) { + *hp = gethostbyaddr(addr, len, AF_INET); + if (*hp) return IO_DONE; + else return WSAGetLastError(); +} + +int socket_gethostbyname(const char *addr, struct hostent **hp) { + *hp = gethostbyname(addr); + if (*hp) return IO_DONE; + else return WSAGetLastError(); +} + +/*-------------------------------------------------------------------------*\ +* Error translation functions +\*-------------------------------------------------------------------------*/ +const char *socket_hoststrerror(int err) { + if (err <= 0) return io_strerror(err); + switch (err) { + case WSAHOST_NOT_FOUND: return "host not found"; + default: return wstrerror(err); + } +} + +const char *socket_strerror(int err) { + if (err <= 0) return io_strerror(err); + switch (err) { + case WSAEADDRINUSE: return "address already in use"; + case WSAECONNREFUSED: return "connection refused"; + case WSAEISCONN: return "already connected"; + case WSAEACCES: return "permission denied"; + case WSAECONNABORTED: return "closed"; + case WSAECONNRESET: return "closed"; + case WSAETIMEDOUT: return "timeout"; + default: return wstrerror(err); + } +} + +const char *socket_ioerror(p_socket ps, int err) { + (void) ps; + return socket_strerror(err); +} + +static const char *wstrerror(int err) { + switch (err) { + case WSAEINTR: return "Interrupted function call"; + case WSAEACCES: return "Permission denied"; + case WSAEFAULT: return "Bad address"; + case WSAEINVAL: return "Invalid argument"; + case WSAEMFILE: return "Too many open files"; + case WSAEWOULDBLOCK: return "Resource temporarily unavailable"; + case WSAEINPROGRESS: return "Operation now in progress"; + case WSAEALREADY: return "Operation already in progress"; + case WSAENOTSOCK: return "Socket operation on nonsocket"; + case WSAEDESTADDRREQ: return "Destination address required"; + case WSAEMSGSIZE: return "Message too long"; + case WSAEPROTOTYPE: return "Protocol wrong type for socket"; + case WSAENOPROTOOPT: return "Bad protocol option"; + case WSAEPROTONOSUPPORT: return "Protocol not supported"; + case WSAESOCKTNOSUPPORT: return "Socket type not supported"; + case WSAEOPNOTSUPP: return "Operation not supported"; + case WSAEPFNOSUPPORT: return "Protocol family not supported"; + case WSAEAFNOSUPPORT: + return "Address family not supported by protocol family"; + case WSAEADDRINUSE: return "Address already in use"; + case WSAEADDRNOTAVAIL: return "Cannot assign requested address"; + case WSAENETDOWN: return "Network is down"; + case WSAENETUNREACH: return "Network is unreachable"; + case WSAENETRESET: return "Network dropped connection on reset"; + case WSAECONNABORTED: return "Software caused connection abort"; + case WSAECONNRESET: return "Connection reset by peer"; + case WSAENOBUFS: return "No buffer space available"; + case WSAEISCONN: return "Socket is already connected"; + case WSAENOTCONN: return "Socket is not connected"; + case WSAESHUTDOWN: return "Cannot send after socket shutdown"; + case WSAETIMEDOUT: return "Connection timed out"; + case WSAECONNREFUSED: return "Connection refused"; + case WSAEHOSTDOWN: return "Host is down"; + case WSAEHOSTUNREACH: return "No route to host"; + case WSAEPROCLIM: return "Too many processes"; + case WSASYSNOTREADY: return "Network subsystem is unavailable"; + case WSAVERNOTSUPPORTED: return "Winsock.dll version out of range"; + case WSANOTINITIALISED: + return "Successful WSAStartup not yet performed"; + case WSAEDISCON: return "Graceful shutdown in progress"; + case WSAHOST_NOT_FOUND: return "Host not found"; + case WSATRY_AGAIN: return "Nonauthoritative host not found"; + case WSANO_RECOVERY: return "Nonrecoverable name lookup error"; + case WSANO_DATA: return "Valid name, no data record of requested type"; + default: return "Unknown error"; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/wsocket.h Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,21 @@ +#ifndef WSOCKET_H +#define WSOCKET_H +/*=========================================================================*\ +* Socket compatibilization module for Win32 +* LuaSocket toolkit +* +* RCS ID: $Id: wsocket.h,v 1.4 2005/10/07 04:40:59 diego Exp $ +\*=========================================================================*/ + +/*=========================================================================*\ +* WinSock include files +\*=========================================================================*/ +#include <winsock.h> + +typedef int socklen_t; +typedef SOCKET t_socket; +typedef t_socket *p_socket; + +#define SOCKET_INVALID (INVALID_SOCKET) + +#endif /* WSOCKET_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/README Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,12 @@ +This provides the automated test scripts used to make sure the library +is working properly. + +The files provided are: + + testsrvr.lua -- test server + testclnt.lua -- test client + +To run these tests, just run lua on the server and then on the client. + +Good luck, +Diego.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/testclnt.lua Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,713 @@ +local socket = require"socket" + +host = host or "localhost" +port = port or "8383" + +function pass(...) + local s = string.format(unpack(arg)) + io.stderr:write(s, "\n") +end + +function fail(...) + local s = string.format(unpack(arg)) + io.stderr:write("ERROR: ", s, "!\n") +socket.sleep(3) + os.exit() +end + +function warn(...) + local s = string.format(unpack(arg)) + io.stderr:write("WARNING: ", s, "\n") +end + +function remote(...) + local s = string.format(unpack(arg)) + s = string.gsub(s, "\n", ";") + s = string.gsub(s, "%s+", " ") + s = string.gsub(s, "^%s*", "") + control:send(s .. "\n") + control:receive() +end + +function test(test) + io.stderr:write("----------------------------------------------\n", + "testing: ", test, "\n", + "----------------------------------------------\n") +end + +function check_timeout(tm, sl, elapsed, err, opp, mode, alldone) + if tm < sl then + if opp == "send" then + if not err then warn("must be buffered") + elseif err == "timeout" then pass("proper timeout") + else fail("unexpected error '%s'", err) end + else + if err ~= "timeout" then fail("should have timed out") + else pass("proper timeout") end + end + else + if mode == "total" then + if elapsed > tm then + if err ~= "timeout" then fail("should have timed out") + else pass("proper timeout") end + elseif elapsed < tm then + if err then fail(err) + else pass("ok") end + else + if alldone then + if err then fail("unexpected error '%s'", err) + else pass("ok") end + else + if err ~= "timeout" then fail(err) + else pass("proper timeoutk") end + end + end + else + if err then fail(err) + else pass("ok") end + end + end +end + +if not socket._DEBUG then + fail("Please define LUASOCKET_DEBUG and recompile LuaSocket") +end + +io.stderr:write("----------------------------------------------\n", +"LuaSocket Test Procedures\n", +"----------------------------------------------\n") + +start = socket.gettime() + +function reconnect() + io.stderr:write("attempting data connection... ") + if data then data:close() end + remote [[ + if data then data:close() data = nil end + data = server:accept() + data:setoption("tcp-nodelay", true) + ]] + data, err = socket.connect(host, port) + if not data then fail(err) + else pass("connected!") end + data:setoption("tcp-nodelay", true) +end + +pass("attempting control connection...") +control, err = socket.connect(host, port) +if err then fail(err) +else pass("connected!") end +control:setoption("tcp-nodelay", true) + +------------------------------------------------------------------------ +function test_methods(sock, methods) + for _, v in pairs(methods) do + if type(sock[v]) ~= "function" then + fail(sock.class .. " method '" .. v .. "' not registered") + end + end + pass(sock.class .. " methods are ok") +end + +------------------------------------------------------------------------ +function test_mixed(len) + reconnect() + local inter = math.ceil(len/4) + local p1 = "unix " .. string.rep("x", inter) .. "line\n" + local p2 = "dos " .. string.rep("y", inter) .. "line\r\n" + local p3 = "raw " .. string.rep("z", inter) .. "bytes" + local p4 = "end" .. string.rep("w", inter) .. "bytes" + local bp1, bp2, bp3, bp4 +remote (string.format("str = data:receive(%d)", + string.len(p1)+string.len(p2)+string.len(p3)+string.len(p4))) + sent, err = data:send(p1..p2..p3..p4) + if err then fail(err) end +remote "data:send(str); data:close()" + bp1, err = data:receive() + if err then fail(err) end + bp2, err = data:receive() + if err then fail(err) end + bp3, err = data:receive(string.len(p3)) + if err then fail(err) end + bp4, err = data:receive("*a") + if err then fail(err) end + if bp1.."\n" == p1 and bp2.."\r\n" == p2 and bp3 == p3 and bp4 == p4 then + pass("patterns match") + else fail("patterns don't match") end +end + +------------------------------------------------------------------------ +function test_asciiline(len) + reconnect() + local str, str10, back, err + str = string.rep("x", math.mod(len, 10)) + str10 = string.rep("aZb.c#dAe?", math.floor(len/10)) + str = str .. str10 +remote "str = data:receive()" + sent, err = data:send(str.."\n") + if err then fail(err) end +remote "data:send(str ..'\\n')" + back, err = data:receive() + if err then fail(err) end + if back == str then pass("lines match") + else fail("lines don't match") end +end + +------------------------------------------------------------------------ +function test_rawline(len) + reconnect() + local str, str10, back, err + str = string.rep(string.char(47), math.mod(len, 10)) + str10 = string.rep(string.char(120,21,77,4,5,0,7,36,44,100), + math.floor(len/10)) + str = str .. str10 +remote "str = data:receive()" + sent, err = data:send(str.."\n") + if err then fail(err) end +remote "data:send(str..'\\n')" + back, err = data:receive() + if err then fail(err) end + if back == str then pass("lines match") + else fail("lines don't match") end +end + +------------------------------------------------------------------------ +function test_raw(len) + reconnect() + local half = math.floor(len/2) + local s1, s2, back, err + s1 = string.rep("x", half) + s2 = string.rep("y", len-half) +remote (string.format("str = data:receive(%d)", len)) + sent, err = data:send(s1) + if err then fail(err) end + sent, err = data:send(s2) + if err then fail(err) end +remote "data:send(str)" + back, err = data:receive(len) + if err then fail(err) end + if back == s1..s2 then pass("blocks match") + else fail("blocks don't match") end +end + +------------------------------------------------------------------------ +function test_totaltimeoutreceive(len, tm, sl) + reconnect() + local str, err, partial + pass("%d bytes, %ds total timeout, %ds pause", len, tm, sl) + remote (string.format ([[ + data:settimeout(%d) + str = string.rep('a', %d) + data:send(str) + print('server: sleeping for %ds') + socket.sleep(%d) + print('server: woke up') + data:send(str) + ]], 2*tm, len, sl, sl)) + data:settimeout(tm, "total") +local t = socket.gettime() + str, err, partial, elapsed = data:receive(2*len) + check_timeout(tm, sl, elapsed, err, "receive", "total", + string.len(str or partial) == 2*len) +end + +------------------------------------------------------------------------ +function test_totaltimeoutsend(len, tm, sl) + reconnect() + local str, err, total + pass("%d bytes, %ds total timeout, %ds pause", len, tm, sl) + remote (string.format ([[ + data:settimeout(%d) + str = data:receive(%d) + print('server: sleeping for %ds') + socket.sleep(%d) + print('server: woke up') + str = data:receive(%d) + ]], 2*tm, len, sl, sl, len)) + data:settimeout(tm, "total") + str = string.rep("a", 2*len) + total, err, partial, elapsed = data:send(str) + check_timeout(tm, sl, elapsed, err, "send", "total", + total == 2*len) +end + +------------------------------------------------------------------------ +function test_blockingtimeoutreceive(len, tm, sl) + reconnect() + local str, err, partial + pass("%d bytes, %ds blocking timeout, %ds pause", len, tm, sl) + remote (string.format ([[ + data:settimeout(%d) + str = string.rep('a', %d) + data:send(str) + print('server: sleeping for %ds') + socket.sleep(%d) + print('server: woke up') + data:send(str) + ]], 2*tm, len, sl, sl)) + data:settimeout(tm) + str, err, partial, elapsed = data:receive(2*len) + check_timeout(tm, sl, elapsed, err, "receive", "blocking", + string.len(str or partial) == 2*len) +end + +------------------------------------------------------------------------ +function test_blockingtimeoutsend(len, tm, sl) + reconnect() + local str, err, total + pass("%d bytes, %ds blocking timeout, %ds pause", len, tm, sl) + remote (string.format ([[ + data:settimeout(%d) + str = data:receive(%d) + print('server: sleeping for %ds') + socket.sleep(%d) + print('server: woke up') + str = data:receive(%d) + ]], 2*tm, len, sl, sl, len)) + data:settimeout(tm) + str = string.rep("a", 2*len) + total, err, partial, elapsed = data:send(str) + check_timeout(tm, sl, elapsed, err, "send", "blocking", + total == 2*len) +end + +------------------------------------------------------------------------ +function empty_connect() + reconnect() + if data then data:close() data = nil end + remote [[ + if data then data:close() data = nil end + data = server:accept() + ]] + data, err = socket.connect("", port) + if not data then + pass("ok") + data = socket.connect(host, port) + else + pass("gethostbyname returns localhost on empty string...") + end +end + +------------------------------------------------------------------------ +function isclosed(c) + return c:getfd() == -1 or c:getfd() == (2^32-1) +end + +function active_close() + reconnect() + if isclosed(data) then fail("should not be closed") end + data:close() + if not isclosed(data) then fail("should be closed") end + data = nil + local udp = socket.udp() + if isclosed(udp) then fail("should not be closed") end + udp:close() + if not isclosed(udp) then fail("should be closed") end + pass("ok") +end + +------------------------------------------------------------------------ +function test_closed() + local back, partial, err + local str = 'little string' + reconnect() + pass("trying read detection") + remote (string.format ([[ + data:send('%s') + data:close() + data = nil + ]], str)) + -- try to get a line + back, err, partial = data:receive() + if not err then fail("should have gotten 'closed'.") + elseif err ~= "closed" then fail("got '"..err.."' instead of 'closed'.") + elseif str ~= partial then fail("didn't receive partial result.") + else pass("graceful 'closed' received") end + reconnect() + pass("trying write detection") + remote [[ + data:close() + data = nil + ]] + total, err, partial = data:send(string.rep("ugauga", 100000)) + if not err then + pass("failed: output buffer is at least %d bytes long!", total) + elseif err ~= "closed" then + fail("got '"..err.."' instead of 'closed'.") + else + pass("graceful 'closed' received after %d bytes were sent", partial) + end +end + +------------------------------------------------------------------------ +function test_selectbugs() + local r, s, e = socket.select(nil, nil, 0.1) + assert(type(r) == "table" and type(s) == "table" and + (e == "timeout" or e == "error")) + pass("both nil: ok") + local udp = socket.udp() + udp:close() + r, s, e = socket.select({ udp }, { udp }, 0.1) + assert(type(r) == "table" and type(s) == "table" and + (e == "timeout" or e == "error")) + pass("closed sockets: ok") + e = pcall(socket.select, "wrong", 1, 0.1) + assert(e == false) + e = pcall(socket.select, {}, 1, 0.1) + assert(e == false) + pass("invalid input: ok") +end + +------------------------------------------------------------------------ +function accept_timeout() + io.stderr:write("accept with timeout (if it hangs, it failed): ") + local s, e = socket.bind("*", 0, 0) + assert(s, e) + local t = socket.gettime() + s:settimeout(1) + local c, e = s:accept() + assert(not c, "should not accept") + assert(e == "timeout", string.format("wrong error message (%s)", e)) + t = socket.gettime() - t + assert(t < 2, string.format("took to long to give up (%gs)", t)) + s:close() + pass("good") +end + +------------------------------------------------------------------------ +function connect_timeout() + io.stderr:write("connect with timeout (if it hangs, it failed!): ") + local t = socket.gettime() + local c, e = socket.tcp() + assert(c, e) + c:settimeout(0.1) + local t = socket.gettime() + local r, e = c:connect("10.0.0.1", 81) +print(r, e) + assert(not r, "should not connect") + assert(socket.gettime() - t < 2, "took too long to give up.") + c:close() + print("ok") +end + +------------------------------------------------------------------------ +function accept_errors() + io.stderr:write("not listening: ") + local d, e = socket.bind("*", 0) + assert(d, e); + local c, e = socket.tcp(); + assert(c, e); + d:setfd(c:getfd()) + d:settimeout(2) + local r, e = d:accept() + assert(not r and e) + print("ok: ", e) + io.stderr:write("not supported: ") + local c, e = socket.udp() + assert(c, e); + d:setfd(c:getfd()) + local r, e = d:accept() + assert(not r and e) + print("ok: ", e) +end + +------------------------------------------------------------------------ +function connect_errors() + io.stderr:write("connection refused: ") + local c, e = socket.connect("localhost", 1); + assert(not c and e) + print("ok: ", e) + io.stderr:write("host not found: ") + local c, e = socket.connect("host.is.invalid", 1); + assert(not c and e, e) + print("ok: ", e) +end + +------------------------------------------------------------------------ +function rebind_test() + local c = socket.bind("localhost", 0) + local i, p = c:getsockname() + local s, e = socket.tcp() + assert(s, e) + s:setoption("reuseaddr", false) + r, e = s:bind("localhost", p) + assert(not r, "managed to rebind!") + assert(e) + print("ok: ", e) +end + +------------------------------------------------------------------------ +function getstats_test() + reconnect() + local t = 0 + for i = 1, 25 do + local c = math.random(1, 100) + remote (string.format ([[ + str = data:receive(%d) + data:send(str) + ]], c)) + data:send(string.rep("a", c)) + data:receive(c) + t = t + c + local r, s, a = data:getstats() + assert(r == t, "received count failed" .. tostring(r) + .. "/" .. tostring(t)) + assert(s == t, "sent count failed" .. tostring(s) + .. "/" .. tostring(t)) + end + print("ok") +end + + +------------------------------------------------------------------------ +function test_nonblocking(size) + reconnect() +print("Testing " .. 2*size .. " bytes") +remote(string.format([[ + data:send(string.rep("a", %d)) + socket.sleep(0.5) + data:send(string.rep("b", %d) .. "\n") +]], size, size)) + local err = "timeout" + local part = "" + local str + data:settimeout(0) + while 1 do + str, err, part = data:receive("*l", part) + if err ~= "timeout" then break end + end + assert(str == (string.rep("a", size) .. string.rep("b", size))) + reconnect() +remote(string.format([[ + str = data:receive(%d) + socket.sleep(0.5) + str = data:receive(2*%d, str) + data:send(str) +]], size, size)) + data:settimeout(0) + local start = 0 + while 1 do + ret, err, start = data:send(str, start+1) + if err ~= "timeout" then break end + end + data:send("\n") + data:settimeout(-1) + local back = data:receive(2*size) + assert(back == str, "'" .. back .. "' vs '" .. str .. "'") + print("ok") +end + +------------------------------------------------------------------------ +function test_readafterclose() + local back, partial, err + local str = 'little string' + reconnect() + pass("trying repeated '*a' pattern") + remote (string.format ([[ + data:send('%s') + data:close() + data = nil + ]], str)) + back, err, partial = data:receive("*a") + assert(back == str, "unexpected data read") + back, err, partial = data:receive("*a") + assert(back == nil and err == "closed", "should have returned 'closed'") + print("ok") + reconnect() + pass("trying active close before '*a'") + remote (string.format ([[ + data:close() + data = nil + ]])) + data:close() + back, err, partial = data:receive("*a") + assert(back == nil and err == "closed", "should have returned 'closed'") + print("ok") + reconnect() + pass("trying active close before '*l'") + remote (string.format ([[ + data:close() + data = nil + ]])) + data:close() + back, err, partial = data:receive() + assert(back == nil and err == "closed", "should have returned 'closed'") + print("ok") + reconnect() + pass("trying active close before raw 1") + remote (string.format ([[ + data:close() + data = nil + ]])) + data:close() + back, err, partial = data:receive(1) + assert(back == nil and err == "closed", "should have returned 'closed'") + print("ok") + reconnect() + pass("trying active close before raw 0") + remote (string.format ([[ + data:close() + data = nil + ]])) + data:close() + back, err, partial = data:receive(0) + assert(back == nil and err == "closed", "should have returned 'closed'") + print("ok") +end + +test("method registration") +test_methods(socket.tcp(), { + "accept", + "bind", + "close", + "connect", + "dirty", + "getfd", + "getpeername", + "getsockname", + "getstats", + "setstats", + "listen", + "receive", + "send", + "setfd", + "setoption", + "setpeername", + "setsockname", + "settimeout", + "shutdown", +}) + +test_methods(socket.udp(), { + "close", + "getpeername", + "dirty", + "getfd", + "getpeername", + "getsockname", + "receive", + "receivefrom", + "send", + "sendto", + "setfd", + "setoption", + "setpeername", + "setsockname", + "settimeout" +}) + +test("testing read after close") +test_readafterclose() + +test("select function") +test_selectbugs() + +test("connect function") +connect_timeout() +empty_connect() +connect_errors() + +test("rebinding: ") +rebind_test() + +test("active close: ") +active_close() + +test("closed connection detection: ") +test_closed() + +test("accept function: ") +accept_timeout() +accept_errors() + +test("getstats test") +getstats_test() + +test("character line") +test_asciiline(1) +test_asciiline(17) +test_asciiline(200) +test_asciiline(4091) +test_asciiline(80199) +test_asciiline(8000000) +test_asciiline(80199) +test_asciiline(4091) +test_asciiline(200) +test_asciiline(17) +test_asciiline(1) + +test("mixed patterns") +test_mixed(1) +test_mixed(17) +test_mixed(200) +test_mixed(4091) +test_mixed(801990) +test_mixed(4091) +test_mixed(200) +test_mixed(17) +test_mixed(1) + +test("binary line") +test_rawline(1) +test_rawline(17) +test_rawline(200) +test_rawline(4091) +test_rawline(80199) +test_rawline(8000000) +test_rawline(80199) +test_rawline(4091) +test_rawline(200) +test_rawline(17) +test_rawline(1) + +test("raw transfer") +test_raw(1) +test_raw(17) +test_raw(200) +test_raw(4091) +test_raw(80199) +test_raw(8000000) +test_raw(80199) +test_raw(4091) +test_raw(200) +test_raw(17) +test_raw(1) + +test("non-blocking transfer") +test_nonblocking(1) +test_nonblocking(17) +test_nonblocking(200) +test_nonblocking(4091) +test_nonblocking(80199) +test_nonblocking(800000) +test_nonblocking(80199) +test_nonblocking(4091) +test_nonblocking(200) +test_nonblocking(17) +test_nonblocking(1) + +test("total timeout on send") +test_totaltimeoutsend(800091, 1, 3) +test_totaltimeoutsend(800091, 2, 3) +test_totaltimeoutsend(800091, 5, 2) +test_totaltimeoutsend(800091, 3, 1) + +test("total timeout on receive") +test_totaltimeoutreceive(800091, 1, 3) +test_totaltimeoutreceive(800091, 2, 3) +test_totaltimeoutreceive(800091, 3, 2) +test_totaltimeoutreceive(800091, 3, 1) + +test("blocking timeout on send") +test_blockingtimeoutsend(800091, 1, 3) +test_blockingtimeoutsend(800091, 2, 3) +test_blockingtimeoutsend(800091, 3, 2) +test_blockingtimeoutsend(800091, 3, 1) + +test("blocking timeout on receive") +test_blockingtimeoutreceive(800091, 1, 3) +test_blockingtimeoutreceive(800091, 2, 3) +test_blockingtimeoutreceive(800091, 3, 2) +test_blockingtimeoutreceive(800091, 3, 1) + +test(string.format("done in %.2fs", socket.gettime() - start))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/testsrvr.lua Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,15 @@ +socket = require("socket"); +host = host or "localhost"; +port = port or "8383"; +server = assert(socket.bind(host, port)); +ack = "\n"; +while 1 do + print("server: waiting for client connection..."); + control = assert(server:accept()); + while 1 do + command = assert(control:receive()); + assert(control:send(ack)); + print(command); + (loadstring(command))(); + end +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/testsupport.lua Tue Aug 26 18:40:01 2008 -0700 @@ -0,0 +1,37 @@ +function readfile(name) + local f = io.open(name, "rb") + if not f then return nil end + local s = f:read("*a") + f:close() + return s +end + +function similar(s1, s2) + return string.lower(string.gsub(s1 or "", "%s", "")) == + string.lower(string.gsub(s2 or "", "%s", "")) +end + +function fail(msg) + msg = msg or "failed" + error(msg, 2) +end + +function compare(input, output) + local original = readfile(input) + local recovered = readfile(output) + if original ~= recovered then fail("comparison failed") + else print("ok") end +end + +local G = _G +local set = rawset +local warn = print + +local setglobal = function(table, key, value) + warn("changed " .. key) + set(table, key, value) +end + +setmetatable(G, { + __newindex = setglobal +})