127 Commits

Author SHA1 Message Date
a24294d331 Revert "url.lua:absolute_path(): fix issue #254" 2018-08-22 14:19:48 -03:00
648d81281f Merge pull request #256 from ewestbrook/luasocket254
url.lua:absolute_path(): fix issue #254
2018-08-22 14:18:43 -03:00
043e997713 url.lua:remove_dot_components(): avoid ambiguous numeric representation as empty-path-segment marker 2018-08-21 12:43:30 -06:00
ca5398be09 url.lua:remove_dot_components(): use temporary NUL marker to reduce empty-segment special-case code 2018-08-21 12:17:12 -06:00
38d936ec0e url.lua:remove_dot_components(): empty path component double-dot corner case 2018-08-21 11:27:42 -06:00
5b862e6a3c url.lua:absolute_path(): ensure a separator between base_path and relative_path 2018-08-21 10:43:04 -06:00
7ccea58776 url.lua:remove_dot_components(): avoid overconsuming dot segments 2018-08-21 09:59:45 -06:00
c570a32c21 url.lua:remove_dot_components(): limit beginning-of-string double-dot corner case to prevent triple-dot activation and authority collision 2018-08-21 09:07:42 -06:00
c905b5d44f url.lua: separate remove_dot_components() from absolute_path(); also use in _M.absolute() even when not merging 2018-08-21 08:03:51 -06:00
17a95c126a url.lua:absolute_path(): fix issue #254, simplify, add more test cases 2018-08-19 11:32:42 -06:00
5813cd0505 Merge pull request #253 from flyingdutchman23/master
Fix gcc-8.1.0 warning.
2018-08-06 13:15:58 -03:00
9b82c7871d Fix gcc-8.1.0 warning. 2018-08-06 15:30:13 +02:00
4df569e9f8 Merge pull request #250 from ewestbrook/testudata-compat
Update auxiliar.c to use luaL_testudata (#249), now with Lua 5.1 compatibility
2018-06-05 10:54:55 -03:00
cc42bcbf80 Merge pull request #251 from ewestbrook/ltn12tblsrc
Add ltn12.source.table()
2018-06-05 09:56:59 -03:00
8fee636309 Add ltn12.source.table() 2018-06-05 00:00:39 -06:00
5848de4851 src/compat.c: provide luaL_testudata() for use by auxiliar.c under Lua 5.1 2018-06-04 20:14:13 -06:00
e1e41be948 Update auxiliar.c 2018-06-03 20:08:02 +08:00
6529598909 Merge pull request #239 from ewestbrook/namebuf57
Fix buffer size error in src/options.c detected by GCC 7
2017-12-23 01:24:33 -02:00
72fb9dcb49 src/options.c: increase buffer from 45 to 57, to accommodate string sizes specified (detected by gcc7's -Wstringpop-overflow) 2017-12-22 16:57:20 -07:00
88b13a825b Merge pull request #238 from hleuwer/master
Fix URL parsing of userinfo containing # character.
2017-11-23 13:28:25 -02:00
2d6a0f7bda fixed url parsing; postpone fragment parsing after authority parsing; added test cases to test/urltest.lua
fixed reference patterns in check_protect() to upper case hex letters
2017-11-22 09:30:12 +01:00
3ee89515a0 fixed URL parsing in url.lua: parse fragment after parsing username and password. 2017-11-19 19:48:37 +01:00
5a17f79b03 Merge pull request #213 from elliptica/master
Fix typo
2017-05-15 15:29:49 -03:00
aef7bcf288 Fix typo 2017-04-17 18:47:48 +03:00
ff1a447b4d Merge pull request #210 from Lord-Helmchen/patch-1
make protect_segment in url.lua rfc compliant
2017-03-20 17:01:30 -03:00
47e644031f Preserve path when parsing urls. 2017-03-20 16:56:15 -03:00
1b4debffee Fix issue #196 2017-03-20 15:50:47 -03:00
865356f6eb Merge pull request #211 from fperrad/patch-1
fix for Lua 5.3 built without number / string conversion
2017-03-20 13:58:33 -03:00
44fb9e9112 correct typo 2017-03-16 17:53:02 +01:00
16bb548746 fix for Lua 5.3 built without number / string conversion
This kind of Lua could be built with this command:
```
    hererocks --lua 5.3 --cflags="-DLUA_NOCVTN2S -DLUA_NOCVTS2N"
```
2017-03-16 16:57:17 +01:00
860da0f4b4 make protect_segment in url.lua rfc compliant
percent-encode uppercase
see https://tools.ietf.org/html/rfc3986#section-6.2.2
2017-03-16 16:33:28 +01:00
29e5ad610a Merge pull request #207 from cbane/unix-sockets
Add backwards compatibility wrappers for socket.unix
2017-01-25 23:25:55 -02:00
ea0064625b Add backwards compatibility wrappers for socket.unix
Add backwards compatibility aliases "tcp" and "udp" for the recently renamed
"stream" and "dgram" functions, as well as a wrapper function and metatable
setup so that socket.unix() calls socket.unix.stream().
2017-01-25 18:04:35 -06:00
843fe9b65f Merge pull request #206 from ncopa/create-socket-on-first-sendto
Create socket on first sendto if family agnostic udp() was used
2017-01-25 13:15:51 -02:00
3041a808c3 Create socket on first sendto if family agnostic udp() was used
Create socket and set family on first sendto() if udp() was created
without address family.

Signed-off-by: Natanael Copa <ncopa@alpinelinux.org>
2017-01-25 12:52:56 +01:00
a0baab5f3c Merge pull request #199 from enginix/master
Rename unix.tcp to unix.stream, unix.udp to unix.dgram
2017-01-04 18:41:31 -02:00
3a33c37b9c rename unix.tcp to unix.stream, unix.udp to unix.dgram 2016-12-25 23:33:10 +08:00
ac3201d620 Merge pull request #192 from mmaxs/master
Allow DESTDIR to be set from the environment
2016-11-11 02:28:07 -05:00
cd1e52eb7a allow DESTDIR to be set from the environment 2016-11-08 22:07:20 +03:00
316a9455b9 Merge pull request #181 from enginix/master
Add support for datagram unix domain sockets
2016-07-22 14:06:30 -03:00
2205c2053c add getsockname api for unix {udp,tcp} socket 2016-07-22 22:52:13 +08:00
c87f953d81 fix unixudp object checking issue 2016-07-04 16:38:37 +08:00
9f77f8b24f unix socket: compat lua 5.1 2016-06-30 15:40:51 +08:00
aa1b8cc9bc support datagram unix domain sockets 2016-06-24 21:33:19 +08:00
30a64c585a Merge pull request #178 from pdxmeshnet/master
Add rockspec to current development version.
2016-06-17 11:17:26 -03:00
3abd1f2dd0 Added future release rockspec file 2016-06-15 15:27:07 -07:00
6aa4f2bc33 Merge pull request #167 from xspager/add_haiku_plat
Add the lib network to the linked libs if the platform is Haiku
2016-04-12 09:29:46 -03:00
e2acf378ea Merge pull request #171 from JonasKunze/feature/overwriteFlags
Enabled overwriting of MYCF/MYLDFlAGS
2016-04-12 09:29:14 -03:00
6a0506ca44 Merge pull request #170 from bytefire/master
URL-decode user password before adding to authorization header.
2016-04-12 09:28:27 -03:00
624924a77b Enabled overwriting of MYCF/MYLDFlAGS 2016-04-12 13:06:47 +02:00
b9f6fd215a URL-decode user password before adding to authorization header. 2016-04-12 00:04:21 +01:00
9984741d94 Update comments for url.unescape() function. 2016-04-12 00:01:51 +01:00
6fa6462636 Given LuaRocks support Haiku as a valid platform, link the network library 2016-03-11 16:05:21 -03:00
23ce5aeaa2 Hide ftp.PORT "constant" as a local 2016-03-07 01:33:08 -03:00
916b548240 Family agostic FTP and expose HTTP/FTP url parsing 2016-03-07 01:30:30 -03:00
5b4b915879 Remove global PORT. Fix https redirect. 2016-03-04 16:16:41 -03:00
944305dc21 Added gettimeout for completeness.
Also documented.
Rordered manuals so order is alphabetical.
2016-03-04 15:36:32 -03:00
cdce73b226 Added support for FTP command lists 2016-03-04 14:38:56 -03:00
fe7b37aced Merge pull request #166 from siffiejoe/exception-tweaks
Exception tweaks
2016-02-24 13:23:20 -03:00
0341516a29 Clarify documentation for try/protect. 2016-02-24 06:59:37 +01:00
4392bdcdd4 Always put metatable in first upvalue. 2016-02-24 00:57:42 +01:00
9fe38c654f Don't swallow errors in finalizers. 2016-02-24 00:48:43 +01:00
9ffd96724d Merge pull request #162 from siffiejoe/exception-meta
Support table errors in LuaSockets LTN13 C implementation.
2016-02-23 14:30:35 -03:00
7cab8a5006 Update comment in except.h 2016-02-21 12:28:13 +01:00
7c1df8a7cd Update HTML docs for try/protect 2016-02-21 12:10:25 +01:00
fb713cdedb Add more tests for socket.try/protect 2016-02-21 12:09:58 +01:00
d075e7322f Support table errors.
LuaSocket wraps error messages raised by newtry() in a table and unpacks
them later so that (string) errors raised by 3rd-party code can be
passed through as-is. This obviously didn't work when the 3rd-party code
raised a table as an error message. This change sets a private metatable
on all wrapped LuaSocket exceptions to distinguish them from 3rd-party
table errors.
2016-02-21 11:59:44 +01:00
bf13ec7fd4 Merge pull request #155 from JonasKunze/feature/solaris
Added Solaris platform
2016-02-11 11:02:58 -02:00
280e1d68a6 Merge pull request #157 from mpeterv/ltn12-lua53
Fix error in ltn12 under Lua 5.3
2016-02-11 11:01:57 -02:00
9192881346 Don't break global mbox table in mbox.split_mbox 2016-02-11 15:57:56 +03:00
52bb99af35 Fix sink method in tp module 2016-02-11 15:56:57 +03:00
700ece0721 Fix base_parsed global in url module 2016-02-11 15:54:59 +03:00
3c3a5d0011 Use base.select instead of just select 2016-02-11 15:53:53 +03:00
a7f21e8ec4 Fix error in ltn12 under Lua 5.3 2016-02-11 15:51:54 +03:00
5d52ffedf4 Added solaris platform
To compile on solaris some libs had to be linked. So far I was only able
to test it on OmniOS r151006
2016-01-15 18:48:57 +01:00
83880dbed7 When zero-timeout, only try first address in connect. 2015-12-03 12:56:18 -02:00
be67f63f4e Changed buffer-per-socket to buffer-per-operation.
This is a difficult tradeoff to measure. I think large
datagrams won't be used very frequently. So it is better to
not lock a large buffer to each socket object and instead
allocate and deallocate for each operation receiving a
datagram larger than UDP_DATAGRAMSIZE.
2015-10-06 11:33:50 +08:00
fd729b32a8 Added support for arbitrary datagram sizes.
The maximum size is still constant per UDP object, but the
size can be speficied at creation time.
2015-10-05 11:47:51 +08:00
d1ec29be7f Merge branch 'KateAdams-tcp_reuseport' 2015-10-05 10:28:54 +08:00
f4b4720073 Merge branch 'tcp_reuseport' of https://github.com/KateAdams/luasocket into KateAdams-tcp_reuseport 2015-10-05 10:28:29 +08:00
4110e4125d Merge branch 'agnostic'
Seems safe to move to master.
2015-08-25 15:43:48 -03:00
77bba625d7 Fixes suggested by @Florob in #147. 2015-08-25 15:41:40 -03:00
46d7e75f3e Merge pull request #96 from chastabor/master
Generate headers before proxy changes host and port
2015-08-24 16:41:24 -03:00
b6a10ccb68 Merge pull request #84 from PixelToast/patch-2
the universe has only existed for 43.8 years
2015-08-24 16:31:42 -03:00
96965b179c New agnostic IPv4 IPv6 functions.
Also dealing with EPROTOTYPE Yosemite seems to be throwing
at us for no reason.
2015-08-22 19:52:01 -03:00
b211838648 Fix rockspec and serial.c module. 2015-08-21 22:31:29 -03:00
9322eacefd Merge pull request #141 from cjtallman/master
Added missing options for UDP getoption.
2015-08-21 15:51:52 -03:00
e75444ccd1 New compat.h module implements luaL_setfuncs.
Makes initialization code simpler everywhere.
2015-08-21 15:39:34 -03:00
67abaf89eb Added missing options for UDP getoption.
Documentation says "dontroute", "broadcast", "reuseaddr", and
"reuseport" are supported as arguments to getoption, however their
implementations were missing.
2015-06-11 09:02:20 -07:00
321c0c9b1f Merge branch 'Tieske-errormsg' 2015-03-03 19:20:44 -03:00
863a54cbaf Fix cut-paste typo on PIE_HOST_NOT_FOUND. 2015-03-03 19:20:13 -03:00
892ea1791a Merge branch 'errormsg' of https://github.com/Tieske/luasocket into Tieske-errormsg 2015-03-03 19:17:53 -03:00
8066a90e87 Merge pull request #115 from tst2005/patch-1
white background
2015-03-03 19:11:15 -03:00
bbcbbf845e Merge pull request #119 from gatzka/master
Make casts const correct.
2015-03-03 19:01:00 -03:00
d3ed08616b updated error messages, less error prone, platform independent 2015-03-03 21:17:06 +01:00
8396a0291b Better solution to luaL_checkint... 2015-03-01 22:49:04 -03:00
9d49cdcf05 Merge pull request #128 from Tieske/accidental_global
fixed accidental global in `ftp.lua`
2015-03-01 22:01:15 -03:00
59869b8bf6 fixed accidental global in ftp.lua 2015-03-01 20:34:53 +01:00
76ed24fe8a Fix inet_global_getaddrinfo leak of getnameinfo
Issue #127 closed.
2015-02-18 20:54:27 -02:00
ddf4292824 Out of the box support for Lua 5.3. 2015-02-18 20:51:37 -02:00
d8f77cca64 Remove unused function luaL_typerror(). 2014-12-21 07:45:36 +01:00
9178451ef9 Add missing prototype for opt_get_reuseport(). 2014-12-21 07:45:17 +01:00
c6f136c7f5 Make local function udp_strerror() static. 2014-12-21 07:44:11 +01:00
41692dfb4b Make casts const correct. 2014-12-21 06:57:10 +01:00
TsT
8a24ddaf49 white background
white background (successfully tested on https://tst2005.github.io/luasocket/ in https://tst2005.github.io/luasocket/reference.css )
2014-11-28 12:09:44 +01:00
5edf093643 Merge pull request #114 from siffiejoe/yieldable_protect52
make socket.protect yieldable on Lua 5.2/5.3
2014-11-10 15:56:25 -02:00
0b03eec16b make socket.protect yieldable on Lua 5.2/5.3 2014-11-10 18:49:40 +01:00
583257c28c Merge pull request #113 from siffiejoe/yieldable_protect51
fixed yieldable socket.protect in etc/dispatch.lua
2014-11-10 15:39:34 -02:00
7006ae120d fixed yieldable socket.protect in etc/dispatch.lua 2014-11-10 18:17:10 +01:00
6dcecd8f45 Merge pull request #108 from daurnimator/master
src/usocket: Do not setblocking on destroy, shutdown, and listen.
2014-10-27 16:30:48 -02:00
4f122e60b1 src/usocket: Don't unset/set O_NONBLOCK around listen() or shutdown() calls.
It doesn't effect them.
Not true on windows
2014-10-27 14:07:38 -04:00
e602c2b271 src/usocket: Do not setblocking on destroy;
This results in unexpected behaviour if the socket has been `dup()`d, as O_NONBLOCK is shared.
Close is always 'blocking' anyway

See https://github.com/wahern/cqueues/issues/13 for an example use case
2014-10-27 13:35:36 -04:00
d80bb0d82b Fix Host: header according to RFC7230 2014-07-15 14:49:20 -03:00
2314235b3a Add proxy authentication headers if present. 2014-03-27 14:40:04 -05:00
36aa87e031 Generate headers before proxy changes host and port 2014-03-21 14:25:44 -05:00
534dfed859 the universe has only existed for 43.8 years 2013-11-07 19:13:36 -05:00
833333e131 Added ability to set the option reuseport of a tcp socket. 2013-09-12 00:46:32 +01:00
6d5e40c324 Add MYCFLAGS and MYLDFLAGS go allow for customization 2013-09-09 14:55:20 -03:00
d0b1f5b4c1 Add support for FreeBSD in makefile
Thanks to Leryan. See issue #78.
2013-09-09 14:29:14 -03:00
396e9e5ee6 Fixed timeout bug introduced by commit e81a6ff 2013-09-09 14:23:00 -03:00
244e5d34a0 Merge pull request #74 from catwell/pull-hostport
Include port in default Host header
2013-09-09 09:54:08 -07:00
c715993fb8 Merge pull request #65 from fab13n/480a818bf0ef6de32527ba14fc2bb27e754d0612
Support for several filters in ltn12.{sink,source}.chain
2013-09-09 09:48:46 -07:00
87d72dce4e include port in default Host header
See RFC 2616 section 14.23.
2013-08-14 14:41:57 +02:00
6bdb00e24c Merge pull request #69 from catwell/pull-globals
fixes related to constants in the HTTP module
2013-07-05 12:40:49 -07:00
1f9ccb2b58 http: look for PROXY in _M, not as a global 2013-07-05 18:00:29 +02:00
ddf88aca09 http: do not set global TIMEOUT 2013-07-05 17:59:52 +02:00
480a818bf0 support multiple filters in ltn12.{sink,source}.chain() 2013-06-18 11:01:46 +02:00
68 changed files with 3119 additions and 1884 deletions

View File

@ -86,7 +86,6 @@ the FTP module:
<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>

View File

@ -112,12 +112,15 @@ 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>
<p class=note id="post">
Note: These constants are global. Changing them will also
change the behavior other code that might be using LuaSocket.
</p>
<!-- http.request ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->

View File

@ -405,6 +405,16 @@ Creates and returns a source that produces the contents of a
<tt>string</tt>, chunk by chunk.
</p>
<!-- table +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="source.table">
ltn12.source.<b>table(</b>table<b>)</b>
</p>
<p class=description>
Creates and returns a source that produces the numerically-indexed values of a <tt>table</tt> successively beginning at 1. The source returns nil (end-of-stream) whenever a nil value is produced by the current index, which proceeds forward regardless.
</p>
<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=footer>

View File

@ -72,34 +72,6 @@ local mime = require("mime")
<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 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
@ -159,6 +131,35 @@ base64 = ltn12.filter.chain(
)
</pre>
<!-- 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>
<!-- stuff +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="stuff">
@ -466,7 +467,7 @@ marker.
<p>
<small>
Last modified by Diego Nehab on <br>
Thu Apr 20 00:25:44 EDT 2006
Fri Mar 4 15:19:17 BRT 2016
</small>
</p>
</center>

View File

@ -2,6 +2,7 @@ body {
margin-left: 1em;
margin-right: 1em;
font-family: "Verdana", sans-serif;
background: #ffffff;
}
tt {

View File

@ -99,7 +99,8 @@ Support, Manual">
<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>.
<a href="ltn12.html#source.string">string</a>,
<a href="ltn12.html#source.table">table</a>.
</blockquote>
</blockquote>
@ -147,6 +148,7 @@ Support, Manual">
<a href="socket.html#connect">connect</a>,
<a href="socket.html#connect">connect4</a>,
<a href="socket.html#connect">connect6</a>,
<a href="socket.html#datagramsize">_DATAGRAMSIZE</a>,
<a href="socket.html#debug">_DEBUG</a>,
<a href="dns.html#dns">dns</a>,
<a href="socket.html#gettime">gettime</a>,
@ -158,11 +160,14 @@ Support, Manual">
<a href="socket.html#skip">skip</a>,
<a href="socket.html#sleep">sleep</a>,
<a href="socket.html#setsize">_SETSIZE</a>,
<a href="socket.html#socketinvalid">_SOCKETINVALID</a>,
<a href="socket.html#source">source</a>,
<a href="tcp.html#socket.tcp">tcp</a>,
<a href="tcp.html#socket.tcp4">tcp4</a>,
<a href="tcp.html#socket.tcp6">tcp6</a>,
<a href="socket.html#try">try</a>,
<a href="udp.html#socket.udp">udp</a>,
<a href="udp.html#socket.udp4">udp4</a>,
<a href="udp.html#socket.udp6">udp6</a>,
<a href="socket.html#version">_VERSION</a>.
</blockquote>
@ -183,6 +188,7 @@ Support, Manual">
<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#gettimeout">gettimeout</a>,
<a href="tcp.html#listen">listen</a>,
<a href="tcp.html#receive">receive</a>,
<a href="tcp.html#send">send</a>,
@ -203,6 +209,7 @@ Support, Manual">
<a href="udp.html#getoption">getoption</a>,
<a href="udp.html#getpeername">getpeername</a>,
<a href="udp.html#getsockname">getsockname</a>,
<a href="udp.html#gettimeout">gettimeout</a>,
<a href="udp.html#receive">receive</a>,
<a href="udp.html#receivefrom">receivefrom</a>,
<a href="udp.html#send">send</a>,

View File

@ -114,6 +114,124 @@ the SMTP module:
<li> <tt>ZONE</tt>: default time zone.
</ul>
<!-- 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>
&nbsp;&nbsp;headers = <i>header-table</i>,<br>
&nbsp;&nbsp;body = <i>LTN12 source</i> or <i>string</i> or
<i>multipart-mesgt</i><br>
}<br>
&nbsp;<br>
multipart-mesgt = {<br>
&nbsp;&nbsp;[preamble = <i>string</i>,]<br>
&nbsp;&nbsp;[1] = <i>mesgt</i>,<br>
&nbsp;&nbsp;[2] = <i>mesgt</i>,<br>
&nbsp;&nbsp;...<br>
&nbsp;&nbsp;[<i>n</i>] = <i>mesgt</i>,<br>
&nbsp;&nbsp;[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 &lt;sicrano@example.com&gt;",
to = "Fulano da Silva &lt;fulano@example.com&gt;",
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 = "&lt;sicrano@example.com&gt;",
rcpt = "&lt;fulano@example.com&gt;",
source = source,
}
</pre>
<!-- send +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=send>
@ -275,123 +393,6 @@ r, e = smtp.send{
}
</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>
&nbsp;&nbsp;headers = <i>header-table</i>,<br>
&nbsp;&nbsp;body = <i>LTN12 source</i> or <i>string</i> or
<i>multipart-mesgt</i><br>
}<br>
&nbsp;<br>
multipart-mesgt = {<br>
&nbsp;&nbsp;[preamble = <i>string</i>,]<br>
&nbsp;&nbsp;[1] = <i>mesgt</i>,<br>
&nbsp;&nbsp;[2] = <i>mesgt</i>,<br>
&nbsp;&nbsp;...<br>
&nbsp;&nbsp;[<i>n</i>] = <i>mesgt</i>,<br>
&nbsp;&nbsp;[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 &lt;sicrano@example.com&gt;",
to = "Fulano da Silva &lt;fulano@example.com&gt;",
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 = "&lt;sicrano@example.com&gt;",
rcpt = "&lt;fulano@example.com&gt;",
source = source,
}
</pre>
<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=footer>

View File

@ -51,6 +51,30 @@ To obtain the <tt>socket</tt> namespace, run:
local socket = require("socket")
</pre>
<!-- headers.canonic ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="headers.canonic">
socket.headers.<b>canonic</b></p>
<p> The <tt>socket.headers.canonic</tt> table
is used by the HTTP and SMTP modules to translate from
lowercase field names back into their canonic
capitalization. When a lowercase field name exists as a key
in this table, the associated value is substituted in
whenever the field name is sent out.
</p>
<p>
You can obtain the <tt>headers</tt> namespace if case run-time
modifications are required by running:
</p>
<pre class=example>
-- loads the headers module
local headers = require("headers")
</pre>
<!-- bind ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=bind>
@ -99,6 +123,19 @@ This constant is set to <tt><b>true</b></tt> if the library was compiled
with debug support.
</p>
<!-- datagramsize +++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=debug>
socket.<b>_DATAGRAMSIZE</b>
</p>
<p class=description>
Default datagram size used by calls to
<a href="udp.html#receive"<tt>receive</tt></a> and
<a href="udp.html#receivefrom"><tt>receivefrom</tt></a>.
(Unless changed in compile time, the value is 8192.)
</p>
<!-- get time +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=gettime>
@ -106,8 +143,7 @@ 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
Returns the UNIX time in seconds. You should subtract the values returned by this function
to get meaningful values.
</p>
@ -117,29 +153,6 @@ t = socket.gettime()
print(socket.gettime() - t .. " seconds elapsed")
</pre>
<!-- socket.headers ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="headers.canonic">
socket.headers.<b>canonic</b></p>
<p> The <tt>socket.headers.canonic</tt> table
is used by the HTTP and SMTP modules to translate from
lowercase field names back into their canonic
capitalization. When a lowercase field name exists as a key
in this table, the associated value is substituted in
whenever the field name is sent out.
</p>
<p>
You can obtain the <tt>headers</tt> namespace if case run-time
modifications are required by running:
</p>
<pre class=example>
-- loads the headers module
local headers = require("headers")
</pre>
<!-- newtry +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=newtry>
@ -155,8 +168,7 @@ is raised.
<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.
<tt>try</tt> throws the exception.
</p>
<p class=return>
@ -204,15 +216,9 @@ 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.
Returns an equivalent function that instead of throwing exceptions in case of
a failed <a href=#try><tt>try</tt></a> call, returns <tt><b>nil</b></tt>
followed by an error message.
</p>
<!-- select +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
@ -238,7 +244,9 @@ non-numeric indices) in the arrays will be silently ignored.
<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
The error message is "<tt>timeout</tt>" if a timeout
condition was met, "<tt>select failed</tt>" if the call
to <tt>select</tt> failed, 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
@ -246,7 +254,7 @@ changed status.
</p>
<p class=note>
<b>Note: </b>: <tt>select</tt> can monitor a limited number
<b>Note:</b> <tt>select</tt> can monitor a limited number
of sockets, as defined by the constant <tt>socket._SETSIZE</tt>. This
number may be as high as 1024 or as low as 64 by default,
depending on the system. It is usually possible to change this
@ -276,6 +284,18 @@ it to <tt>select</tt>, it will be ignored.
<b>Using select with non-socket objects</b>: Any object that implements <tt>getfd</tt> and <tt>dirty</tt> can be used with <tt>select</tt>, allowing objects from other libraries to be used within a <tt>socket.select</tt> driven loop.
</p>
<!-- setsize ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=setsize>
socket.<b>_SETSIZE</b>
</p>
<p class=description>
The maximum number of sockets that the <a
href=#select><tt>select</tt></a> function can handle.
</p>
<!-- sink ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=sink>
@ -383,15 +403,14 @@ side closes the connection.
The function returns a source with the appropriate behavior.
</p>
<!-- setsize ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<!-- socketinvalid ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=setsize>
socket.<b>_SETSIZE</b>
<p class=name id=socketinvalid>
socket.<b>_SOCKETINVALID</b>
</p>
<p class=description>
The maximum number of sockets that the <a
href=#select><tt>select</tt></a> function can handle.
The OS value for an invalid socket.
</p>
<!-- try ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
@ -401,9 +420,9 @@ 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.
Throws an exception in case <tt>ret<sub>1</sub></tt> is falsy, using
<tt>ret<sub>2</sub></tt> as the error message. The exception is supposed to be caught
by a <a href=#protect><tt>protect</tt></a>ed function only.
</p>
<p class=parameters>
@ -414,7 +433,10 @@ nested with <tt>try</tt>.
<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>.
<tt>ret</tt><sub>1</sub> is not <tt><b>nil</b></tt> or <tt><b>false</b></tt>.
Otherwise, it calls <tt>error</tt> passing <tt>ret</tt><sub>2</sub> wrapped
in a table with metatable used by <a href=#protect><tt>protect</tt></a> to
distinguish exceptions from runtime errors.
</p>
<pre class=example>

View File

@ -38,51 +38,6 @@
<h2 id="tcp">TCP</h2>
<!-- socket.tcp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="socket.tcp">
socket.<b>tcp()</b>
</p>
<p class=description>
Creates and returns an IPv4 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>
<!-- socket.tcp6 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="socket.tcp6">
socket.<b>tcp6()</b>
</p>
<p class=description>
Creates and returns an IPv6 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>
<p class=note>
Note: The TCP object returned will have the option
"<tt>ipv6-v6only</tt>" set to <tt><b>true</b></tt>.
</p>
<!-- accept +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="accept">
@ -209,13 +164,88 @@ established.
<p class=note>
Note: Starting with LuaSocket 3.0, the host name resolution
depends on whether the socket was created by <a
href=#socket.tcp><tt>socket.tcp</tt></a> or <a
href=#socket.tcp6><tt>socket.tcp6</tt></a>. Addresses from
the appropriate family are tried in succession until the
first success or until the last failure.
depends on whether the socket was created by
<a href=#socket.tcp><tt>socket.tcp</tt></a>,
<a href=#socket.tcp4><tt>socket.tcp4</tt></a> or
<a href=#socket.tcp6><tt>socket.tcp6</tt></a>. Addresses from
the appropriate family (or both) are tried in the order
returned by the resolver until the
first success or until the last failure. If the timeout was
set to zero, only the first address is tried.
</p>
<!-- dirty +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="dirty">
master:<b>dirty()</b><br>
client:<b>dirty()</b><br>
server:<b>dirty()</b>
</p>
<p class=description>
Check the read buffer status.
</p>
<p class=return>
Returns <tt>true</tt> if there is any data in the read buffer, <tt>false</tt> otherwise.
</p>
<p class=note>
Note: <b>This is an internal method, use at your own risk.</b>
</p>
<!-- getfd +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="getfd">
master:<b>getfd()</b><br>
client:<b>getfd()</b><br>
server:<b>getfd()</b>
</p>
<p class=description>
Returns the underling socket descriptor or handle associated to the object.
</p>
<p class=return>
The descriptor or handle. In case the object has been closed, the return will be -1.
</p>
<p class=note>
Note: <b>This is an internal method. Unlikely to be
portable. Use at your own risk. </b>
</p>
<!-- getoption ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="getoption">
client:<b>getoption(</b>option)</b><br>
server:<b>getoption(</b>option)</b>
</p>
<p class=description>
Gets options for the TCP object.
See <a href=#setoption><tt>setoption</tt></a> for description of the
option names and values.
</p>
<p class=parameters>
<tt>Option</tt> is a string with the option name.
<ul>
<li> '<tt>keepalive</tt>'
<li> '<tt>linger</tt>'
<li> '<tt>reuseaddr</tt>'
<li> '<tt>tcp-nodelay</tt>'
</ul>
<p class=return>
The method returns the option <tt>value</tt> in case of success, or
<b><tt>nil</tt></b> followed by an error message otherwise.
</p>
<!-- getpeername ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="getpeername">
@ -274,6 +304,20 @@ The method returns the number of bytes received, the number of bytes sent,
and the age of the socket object in seconds.
</p>
<!-- gettimeout +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="gettimeout">
master:<b>gettimeout()</b><br>
client:<b>gettimeout()</b><br>
server:<b>gettimeout()</b>
</p>
<p class=description>
Returns the current block timeout followed by the curent
total timeout.
</p>
<!-- listen ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="listen">
@ -447,34 +491,6 @@ followed by an error message otherwise.
Note: The descriptions above come from the man pages.
</p>
<!-- getoption ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="getoption">
client:<b>getoption(</b>option)</b><br>
server:<b>getoption(</b>option)</b>
</p>
<p class=description>
Gets options for the TCP object.
See <a href=#setoption><tt>setoption</tt></a> for description of the
option names and values.
</p>
<p class=parameters>
<tt>Option</tt> is a string with the option name.
<ul>
<li> '<tt>keepalive</tt>'
<li> '<tt>linger</tt>'
<li> '<tt>reuseaddr</tt>'
<li> '<tt>tcp-nodelay</tt>'
</ul>
<p class=return>
The method returns the option <tt>value</tt> in case of success, or
<b><tt>nil</tt></b> followed by an error message otherwise.
</p>
<!-- setstats +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="setstats">
@ -579,46 +595,6 @@ This is the default mode;
This function returns 1.
</p>
<!-- dirty +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="dirty">
master:<b>dirty()</b><br>
client:<b>dirty()</b><br>
server:<b>dirty()</b>
</p>
<p class=description>
Check the read buffer status.
</p>
<p class=return>
Returns <tt>true</tt> if there is any data in the read buffer, <tt>false</tt> otherwise.
</p>
<p class=note>
Note: <b>This is an internal method, any use is unlikely to be portable.</b>
</p>
<!-- getfd +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="getfd">
master:<b>getfd()</b><br>
client:<b>getfd()</b><br>
server:<b>getfd()</b>
</p>
<p class=description>
Returns the underling socket descriptor or handle associated to the object.
</p>
<p class=return>
The descriptor or handle. In case the object has been closed, the return will be -1.
</p>
<p class=note>
Note: <b>This is an internal method, any use is unlikely to be portable.</b>
</p>
<!-- setfd +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="setfd">
@ -636,9 +612,90 @@ No return value.
</p>
<p class=note>
Note: <b>This is an internal method, any use is unlikely to be portable.</b>
Note: <b>This is an internal method. Unlikely to be
portable. Use at your own risk. </b>
</p>
<!-- socket.tcp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="socket.tcp">
socket.<b>tcp()</b>
</p>
<p class=description>
Creates and returns an 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>
<p class=note>
Note: The choice between IPv4 and IPv6 happens during a call to
<a href=#bind><tt>bind</tt></a> or <a
href=#bind><tt>connect</tt></a>, depending on the address
family obtained from the resolver.
</p>
<p class=note>
Note: Before the choice between IPv4 and IPv6 happens,
the internal socket object is invalid and therefore <a
href=#setoption><tt>setoption</tt></a> will fail.
</p>
<!-- socket.tcp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="socket.tcp4">
socket.<b>tcp4()</b>
</p>
<p class=description>
Creates and returns an IPv4 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>
<!-- socket.tcp6 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="socket.tcp6">
socket.<b>tcp6()</b>
</p>
<p class=description>
Creates and returns an IPv6 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>
<p class=note>
Note: The TCP object returned will have the option
"<tt>ipv6-v6only</tt>" set to <tt><b>true</b></tt>.
</p>
<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=footer>

View File

@ -39,73 +39,6 @@
<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 IPv4 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="#getoption"><tt>getoption</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>
<!-- socket.udp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class="name" id="socket.udp6">
socket.<b>udp6()</b>
</p>
<p class="description">
Creates and returns an unconnected IPv6 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="#getoption"><tt>getoption</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>
<p class=note>
Note: The TCP object returned will have the option
"<tt>ipv6-v6only</tt>" set to <tt><b>true</b></tt>.
</p>
<!-- close +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<!-- close +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class="name" id="close">
@ -129,6 +62,40 @@ Garbage-collected objects are automatically closed before
destruction, though.
</p>
<!-- getoption +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class="name" id="getoption">
connected:<b>getoption()</b><br>
unconnected:<b>getoption()</b>
</p>
<p class="description">
Gets an option value from the UDP object.
See <a href=#setoption><tt>setoption</tt></a> for
description of the option names and values.
</p>
<p class="parameters"><tt>Option</tt> is a string with the option name.
<ul>
<li> '<tt>dontroute</tt>'
<li> '<tt>broadcast</tt>'
<li> '<tt>reuseaddr</tt>'
<li> '<tt>reuseport</tt>'
<li> '<tt>ip-multicast-loop</tt>'
<li> '<tt>ipv6-v6only</tt>'
<li> '<tt>ip-multicast-if</tt>'
<li> '<tt>ip-multicast-ttl</tt>'
<li> '<tt>ip-add-membership</tt>'
<li> '<tt>ip-drop-membership</tt>'
</ul>
</p>
<p class=return>
The method returns the option <tt>value</tt> in case of
success, or
<b><tt>nil</tt></b> followed by an error message otherwise.
</p>
<!-- getpeername +++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class="name" id="getpeername">
@ -179,6 +146,18 @@ first time (in which case it is bound to an ephemeral port and the
wild-card address).
</p>
<!-- gettimeout +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="gettimeout">
connected:<b>settimeout(</b>value<b>)</b><br>
unconnected:<b>settimeout(</b>value<b>)</b>
</p>
<p class=description>
Returns the current timeout value.
</p>
<!-- receive +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class="name" id="receive">
@ -199,9 +178,12 @@ 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).
available bytes are returned.
If <tt>size</tt> is omitted, the
compile-time constant <a
href=socket.html#datagramsize><tt>socket._DATAGRAMSIZE</tt></a> is used
(it defaults to 8192 bytes). Larger sizes will cause a
temporary buffer to be allocated for the operation.
</p>
<p class="return">
@ -223,40 +205,6 @@ address and port as extra return values (and is therefore slightly less
efficient).
</p>
<!-- getoption +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class="name" id="getoption">
connected:<b>getoption()</b><br>
unconnected:<b>getoption()</b>
</p>
<p class="description">
Gets an option value from the UDP object.
See <a href=#setoption><tt>setoption</tt></a> for
description of the option names and values.
</p>
<p class="parameters"><tt>Option</tt> is a string with the option name.
<ul>
<li> '<tt>dontroute</tt>'
<li> '<tt>broadcast</tt>'
<li> '<tt>reuseaddr</tt>'
<li> '<tt>reuseport</tt>'
<li> '<tt>ip-multicast-loop</tt>'
<li> '<tt>ipv6-v6only</tt>'
<li> '<tt>ip-multicast-if</tt>'
<li> '<tt>ip-multicast-ttl</tt>'
<li> '<tt>ip-add-membership</tt>'
<li> '<tt>ip-drop-membership</tt>'
</ul>
</p>
<p class=return>
The method returns the option <tt>value</tt> in case of
success, or
<b><tt>nil</tt></b> followed by an error message otherwise.
</p>
<!-- send ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class="name" id="send">
@ -320,6 +268,75 @@ refuses to send a message to the specified address (i.e. no
interface accepts the address).
</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>': Indicates that outgoing
messages should bypass the standard routing facilities.
Receives a boolean value;
<li> '<tt>broadcast</tt>': Requests permission to send
broadcast datagrams on the socket.
Receives a boolean value;
<li> '<tt>reuseaddr</tt>': Indicates that the rules used in
validating addresses supplied in a <tt>bind()</tt> call
should allow reuse of local addresses.
Receives a boolean value;
<li> '<tt>reuseport</tt>': Allows completely duplicate
bindings by multiple processes if they all set
'<tt>reuseport</tt>' before binding the port.
Receives a boolean value;
<li> '<tt>ip-multicast-loop</tt>':
Specifies whether or not a copy of an outgoing multicast
datagram is delivered to the sending host as long as it is a
member of the multicast group.
Receives a boolean value;
<li> '<tt>ipv6-v6only</tt>':
Specifies whether to restrict <tt>inet6</tt> sockets to
sending and receiving only IPv6 packets.
Receive a boolean value;
<li> '<tt>ip-multicast-if</tt>':
Sets the interface over which outgoing multicast datagrams
are sent.
Receives an IP address;
<li> '<tt>ip-multicast-ttl</tt>':
Sets the Time To Live in the IP header for outgoing
multicast datagrams.
Receives a number;
<li> '<tt>ip-add-membership</tt>':
Joins the multicast group specified.
Receives a table with fields
<tt>multiaddr</tt> and <tt>interface</tt>, each containing an
IP address;
<li> '<tt>ip-drop-membership</tt>': Leaves the multicast
group specified.
Receives a table with fields
<tt>multiaddr</tt> and <tt>interface</tt>, each containing an
IP address.
</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>
<!-- setpeername +++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class="name" id="setpeername">
@ -406,74 +423,6 @@ 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>': Indicates that outgoing
messages should bypass the standard routing facilities.
Receives a boolean value;
<li> '<tt>broadcast</tt>': Requests permission to send
broadcast datagrams on the socket.
Receives a boolean value;
<li> '<tt>reuseaddr</tt>': Indicates that the rules used in
validating addresses supplied in a <tt>bind()</tt> call
should allow reuse of local addresses.
Receives a boolean value;
<li> '<tt>reuseport</tt>': Allows completely duplicate
bindings by multiple processes if they all set
'<tt>reuseport</tt>' before binding the port.
Receives a boolean value;
<li> '<tt>ip-multicast-loop</tt>':
Specifies whether or not a copy of an outgoing multicast
datagram is delivered to the sending host as long as it is a
member of the multicast group.
Receives a boolean value;
<li> '<tt>ipv6-v6only</tt>':
Specifies whether to restrict <tt>inet6</tt> sockets to
sending and receiving only IPv6 packets.
Receive a boolean value;
<li> '<tt>ip-multicast-if</tt>':
Sets the interface over which outgoing multicast datagrams
are sent.
Receives an IP address;
<li> '<tt>ip-multicast-ttl</tt>':
Sets the Time To Live in the IP header for outgoing
multicast datagrams.
Receives a number;
<li> '<tt>ip-add-membership</tt>':
Joins the multicast group specified.
Receives a table with fields
<tt>multiaddr</tt> and <tt>interface</tt>, each containing an
IP address;
<li> '<tt>ip-drop-membership</tt>': Leaves the multicast
group specified.
Receives a table with fields
<tt>multiaddr</tt> and <tt>interface</tt>, each containing an
IP address.
</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">
@ -514,6 +463,114 @@ all other method names already contained verbs making their
imperative nature obvious.
</p>
<!-- 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="#getoption"><tt>getoption</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>
<p class=note>
Note: The choice between IPv4 and IPv6 happens during a call to
<a href=#sendto><tt>sendto</tt></a>, <a
href=#setpeername><tt>setpeername</tt></a>, or <a
href=#setsockname><tt>sockname</tt></a>, depending on the address
family obtained from the resolver.
</p>
<p class=note>
Note: Before the choice between IPv4 and IPv6 happens,
the internal socket object is invalid and therefore <a
href=#setoption><tt>setoption</tt></a> will fail.
</p>
<!-- socket.udp4 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class="name" id="socket.udp">
socket.<b>udp4()</b>
</p>
<p class="description">
Creates and returns an unconnected IPv4 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="#getoption"><tt>getoption</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>
<!-- socket.udp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class="name" id="socket.udp6">
socket.<b>udp6()</b>
</p>
<p class="description">
Creates and returns an unconnected IPv6 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="#getoption"><tt>getoption</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>
<p class=note>
Note: The TCP object returned will have the option
"<tt>ipv6-v6only</tt>" set to <tt><b>true</b></tt>.
</p>
<!-- footer ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=footer>

View File

@ -5,6 +5,7 @@
-----------------------------------------------------------------------------
local base = _G
local table = require("table")
local string = require("string")
local socket = require("socket")
local coroutine = require("coroutine")
module("dispatch")
@ -43,26 +44,32 @@ 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
-- we can't yield across calls to protect on Lua 5.1, so we rewrite it with
-- coroutines
-- 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, ...)}
local status = table.remove(results, 1)
if not status then
if base.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))}
if string.sub(base._VERSION, -3) == "5.1" then
local function _protect(co, status, ...)
if not status then
local msg = ...
if base.type(msg) == 'table' then
return nil, msg[1]
else
return base.unpack(results)
base.error(msg, 0)
end
end
if coroutine.status(co) == "suspended" then
return _protect(co, coroutine.resume(co, coroutine.yield(...)))
else
return ...
end
end
function socket.protect(f)
return function(...)
local co = coroutine.create(f)
return _protect(co, coroutine.resume(co, ...))
end
end
end

View File

@ -1 +1 @@
make PLAT=linux DEBUG=DEBUG LUAINC_linux_base=/home/diego/build/linux/include LUAPREFIX_linux=/home/diego/build/linux
make PLAT=linux DEBUG=DEBUG LUAINC_linux_base=/home/diego/build/ubuntu/include LUAPREFIX_linux=/home/diego/build/ubuntu

View File

@ -50,13 +50,12 @@ local function make_plat(plat)
}
local modules = {
["socket.core"] = {
sources = { "src/luasocket.c", "src/timeout.c", "src/buffer.c", "src/io.c", "src/auxiliar.c",
"src/options.c", "src/inet.c", "src/except.c", "src/select.c", "src/tcp.c", "src/udp.c" },
sources = { "src/luasocket.c", "src/timeout.c", "src/buffer.c", "src/io.c", "src/auxiliar.c", "src/options.c", "src/inet.c", "src/except.c", "src/select.c", "src/tcp.c", "src/udp.c", "src/compat.c" },
defines = defines[plat],
incdir = "/src"
},
["mime.core"] = {
sources = { "src/mime.c" },
sources = { "src/mime.c", "src/compat.c" },
defines = defines[plat],
incdir = "/src"
},
@ -70,17 +69,18 @@ local function make_plat(plat)
socket = "src/socket.lua",
mime = "src/mime.lua"
}
if plat == "unix" or plat == "macosx" then
if plat == "unix" or plat == "macosx" or plat == "haiku" then
modules["socket.core"].sources[#modules["socket.core"].sources+1] = "src/usocket.c"
if plat == "haiku" then
modules["socket.core"].libraries = {"network"}
end
modules["socket.unix"] = {
sources = { "src/buffer.c", "src/auxiliar.c", "src/options.c", "src/timeout.c", "src/io.c",
"src/usocket.c", "src/unix.c" },
sources = { "src/buffer.c", "src/auxiliar.c", "src/options.c", "src/timeout.c", "src/io.c", "src/usocket.c", "src/unix.c" },
defines = defines[plat],
incdir = "/src"
}
modules["socket.serial"] = {
sources = { "src/buffer.c", "src/auxiliar.c", "src/options.c", "src/timeout.c",
"src/io.c", "src/usocket.c", "src/serial.c" },
sources = { "src/buffer.c", "src/auxiliar.c", "src/options.c", "src/timeout.c", "src/io.c", "src/usocket.c", "src/serial.c" },
defines = defines[plat],
incdir = "/src"
}
@ -97,6 +97,7 @@ build = {
platforms = {
unix = make_plat("unix"),
macosx = make_plat("macosx"),
haiku = make_plat("haiku"),
win32 = make_plat("win32"),
mingw32 = make_plat("mingw32")
},

View File

@ -5,12 +5,12 @@
# Targets:
# install install system independent support
# install-unix also install unix-only support
# install-both install for both lua5.1 and lua5.2
# install-both install for lua51 lua52 lua53
# install-both-unix also install unix-only
# print print the build settings
PLAT?= linux
PLATS= macosx linux win32 mingw
PLATS= macosx linux win32 mingw freebsd solaris
all: $(PLAT)
@ -30,6 +30,9 @@ install-both:
$(MAKE) clean
@cd src; $(MAKE) $(PLAT) LUAV=5.2
@cd src; $(MAKE) install LUAV=5.2
$(MAKE) clean
@cd src; $(MAKE) $(PLAT) LUAV=5.3
@cd src; $(MAKE) install LUAV=5.3
install-both-unix:
$(MAKE) clean
@ -38,6 +41,9 @@ install-both-unix:
$(MAKE) clean
@cd src; $(MAKE) $(PLAT) LUAV=5.2
@cd src; $(MAKE) install-unix LUAV=5.2
$(MAKE) clean
@cd src; $(MAKE) $(PLAT) LUAV=5.3
@cd src; $(MAKE) install-unix LUAV=5.3
.PHONY: test

View File

@ -0,0 +1,105 @@
package = "LuaSocket"
version = "3.0rc2-1"
source = {
url = "git://github.com/diegonehab/luasocket.git",
tag = "v3.0-rc2",
}
description = {
summary = "Network support for the Lua language",
detailed = [[
LuaSocket is a Lua 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.
]],
homepage = "http://luaforge.net/projects/luasocket/",
license = "MIT"
}
dependencies = {
"lua >= 5.1"
}
local function make_plat(plat)
local defines = {
unix = {
"LUASOCKET_DEBUG",
"LUASOCKET_API=__attribute__((visibility(\"default\")))",
"UNIX_API=__attribute__((visibility(\"default\")))",
"MIME_API=__attribute__((visibility(\"default\")))"
},
macosx = {
"LUASOCKET_DEBUG",
"UNIX_HAS_SUN_LEN",
"LUASOCKET_API=__attribute__((visibility(\"default\")))",
"UNIX_API=__attribute__((visibility(\"default\")))",
"MIME_API=__attribute__((visibility(\"default\")))"
},
win32 = {
"LUASOCKET_DEBUG",
"NDEBUG",
"LUASOCKET_API=__declspec(dllexport)",
"MIME_API=__declspec(dllexport)"
},
mingw32 = {
"LUASOCKET_DEBUG",
"LUASOCKET_INET_PTON",
"WINVER=0x0501",
"LUASOCKET_API=__declspec(dllexport)",
"MIME_API=__declspec(dllexport)"
}
}
local modules = {
["socket.core"] = {
sources = { "src/luasocket.c", "src/timeout.c", "src/buffer.c", "src/io.c", "src/auxiliar.c", "src/options.c", "src/inet.c", "src/except.c", "src/select.c", "src/tcp.c", "src/udp.c", "src/compat.c" },
defines = defines[plat],
incdir = "/src"
},
["mime.core"] = {
sources = { "src/mime.c", "src/compat.c" },
defines = defines[plat],
incdir = "/src"
},
["socket.http"] = "src/http.lua",
["socket.url"] = "src/url.lua",
["socket.tp"] = "src/tp.lua",
["socket.ftp"] = "src/ftp.lua",
["socket.headers"] = "src/headers.lua",
["socket.smtp"] = "src/smtp.lua",
ltn12 = "src/ltn12.lua",
socket = "src/socket.lua",
mime = "src/mime.lua"
}
if plat == "unix" or plat == "macosx" or plat == "haiku" then
modules["socket.core"].sources[#modules["socket.core"].sources+1] = "src/usocket.c"
if plat == "haiku" then
modules["socket.core"].libraries = {"network"}
end
modules["socket.unix"] = {
sources = { "src/buffer.c", "src/auxiliar.c", "src/options.c", "src/timeout.c", "src/io.c", "src/usocket.c", "src/unix.c" },
defines = defines[plat],
incdir = "/src"
}
modules["socket.serial"] = {
sources = { "src/buffer.c", "src/auxiliar.c", "src/options.c", "src/timeout.c", "src/io.c", "src/usocket.c", "src/serial.c" },
defines = defines[plat],
incdir = "/src"
}
end
if plat == "win32" or plat == "mingw32" then
modules["socket.core"].sources[#modules["socket.core"].sources+1] = "src/wsocket.c"
modules["socket.core"].libraries = { "ws2_32" }
end
return { modules = modules }
end
build = {
type = "builtin",
platforms = {
unix = make_plat("unix"),
macosx = make_plat("macosx"),
haiku = make_plat("haiku"),
win32 = make_plat("win32"),
mingw32 = make_plat("mingw32")
},
copy_directories = { "doc", "samples", "etc", "test" }
}

View File

@ -143,7 +143,7 @@ void *auxiliar_getgroupudata(lua_State *L, const char *groupname, int objidx) {
* otherwise
\*-------------------------------------------------------------------------*/
void *auxiliar_getclassudata(lua_State *L, const char *classname, int objidx) {
return luaL_checkudata(L, objidx, classname);
return luaL_testudata(L, objidx, classname);
}
/*-------------------------------------------------------------------------*\

View File

@ -31,6 +31,7 @@
#include "lua.h"
#include "lauxlib.h"
#include "compat.h"
int auxiliar_open(lua_State *L);
void auxiliar_newclass(lua_State *L, const char *classname, luaL_Reg *func);

View File

@ -4,6 +4,7 @@
\*=========================================================================*/
#include "lua.h"
#include "lauxlib.h"
#include "compat.h"
#include "buffer.h"
@ -78,9 +79,7 @@ int buffer_meth_send(lua_State *L, p_buffer buf) {
const char *data = luaL_checklstring(L, 2, &size);
long start = (long) luaL_optnumber(L, 3, 1);
long end = (long) luaL_optnumber(L, 4, -1);
#ifdef LUASOCKET_DEBUG
p_timeout tm = timeout_markstart(buf->tm);
#endif
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;
@ -98,7 +97,7 @@ int buffer_meth_send(lua_State *L, p_buffer buf) {
}
#ifdef LUASOCKET_DEBUG
/* push time elapsed during operation as the last return value */
lua_pushnumber(L, timeout_gettime() - timeout_getstart(tm));
lua_pushnumber(L, timeout_gettime() - timeout_getstart(buf->tm));
#endif
return lua_gettop(L) - top;
}
@ -111,9 +110,7 @@ int buffer_meth_receive(lua_State *L, p_buffer buf) {
luaL_Buffer b;
size_t size;
const char *part = luaL_optlstring(L, 3, "", &size);
#ifdef LUASOCKET_DEBUG
p_timeout tm = timeout_markstart(buf->tm);
#endif
timeout_markstart(buf->tm);
/* initialize buffer with optional extra prefix
* (useful for concatenating previous partial results) */
luaL_buffinit(L, &b);
@ -149,7 +146,7 @@ int buffer_meth_receive(lua_State *L, p_buffer buf) {
}
#ifdef LUASOCKET_DEBUG
/* push time elapsed during operation as the last return value */
lua_pushnumber(L, timeout_gettime() - timeout_getstart(tm));
lua_pushnumber(L, timeout_gettime() - timeout_getstart(buf->tm));
#endif
return lua_gettop(L) - top;
}

37
src/compat.c Normal file
View File

@ -0,0 +1,37 @@
#include "compat.h"
#if LUA_VERSION_NUM==501
/*
** Adapted from Lua 5.2
*/
void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) {
luaL_checkstack(L, nup+1, "too many upvalues");
for (; l->name != NULL; l++) { /* fill the table with given functions */
int i;
lua_pushstring(L, l->name);
for (i = 0; i < nup; i++) /* copy upvalues to the top */
lua_pushvalue(L, -(nup+1));
lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */
lua_settable(L, -(nup + 3));
}
lua_pop(L, nup); /* remove upvalues */
}
/*
** Duplicated from Lua 5.2
*/
void *luaL_testudata (lua_State *L, int ud, const char *tname) {
void *p = lua_touserdata(L, ud);
if (p != NULL) { /* value is a userdata? */
if (lua_getmetatable(L, ud)) { /* does it have a metatable? */
luaL_getmetatable(L, tname); /* get correct metatable */
if (!lua_rawequal(L, -1, -2)) /* not the same? */
p = NULL; /* value is a userdata with wrong metatable */
lua_pop(L, 2); /* remove both metatables */
return p;
}
}
return NULL; /* value is not a userdata with a metatable */
}
#endif

12
src/compat.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef COMPAT_H
#define COMPAT_H
#include "lua.h"
#include "lauxlib.h"
#if LUA_VERSION_NUM==501
void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup);
void *luaL_testudata ( lua_State *L, int arg, const char *tname);
#endif
#endif

View File

@ -6,9 +6,19 @@
#include "lua.h"
#include "lauxlib.h"
#include "compat.h"
#include "except.h"
#if LUA_VERSION_NUM < 502
#define lua_pcallk(L, na, nr, err, ctx, cont) \
(((void)ctx),((void)cont),lua_pcall(L, na, nr, err))
#endif
#if LUA_VERSION_NUM < 503
typedef int lua_KContext;
#endif
/*=========================================================================*\
* Internal function prototypes.
\*=========================================================================*/
@ -29,18 +39,17 @@ static luaL_Reg func[] = {
* 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);
lua_createtable(L, 1, 0);
lua_pushvalue(L, -2);
lua_rawseti(L, -2, 1);
lua_pushvalue(L, lua_upvalueindex(1));
lua_setmetatable(L, -2);
}
static int finalize(lua_State *L) {
if (!lua_toboolean(L, 1)) {
lua_pushvalue(L, lua_upvalueindex(1));
lua_pcall(L, 0, 0, 0);
lua_pushvalue(L, lua_upvalueindex(2));
lua_call(L, 0, 0);
lua_settop(L, 2);
wrap(L);
lua_error(L);
@ -56,7 +65,9 @@ static int do_nothing(lua_State *L) {
static int global_newtry(lua_State *L) {
lua_settop(L, 1);
if (lua_isnil(L, 1)) lua_pushcfunction(L, do_nothing);
lua_pushcclosure(L, finalize, 1);
lua_pushvalue(L, lua_upvalueindex(1));
lua_insert(L, -2);
lua_pushcclosure(L, finalize, 2);
return 1;
}
@ -64,27 +75,49 @@ static int global_newtry(lua_State *L) {
* 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;
if (lua_istable(L, -1) && lua_getmetatable(L, -1)) {
int r = lua_rawequal(L, -1, lua_upvalueindex(1));
lua_pop(L, 1);
if (r) {
lua_pushnil(L);
lua_rawgeti(L, -2, 1);
return 1;
}
}
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) {
static int protected_finish(lua_State *L, int status, lua_KContext ctx) {
(void)ctx;
if (status != 0 && status != LUA_YIELD) {
if (unwrap(L)) return 2;
else lua_error(L);
return 0;
else return lua_error(L);
} else return lua_gettop(L);
}
#if LUA_VERSION_NUM == 502
static int protected_cont(lua_State *L) {
int ctx = 0;
int status = lua_getctx(L, &ctx);
return protected_finish(L, status, ctx);
}
#else
#define protected_cont protected_finish
#endif
static int protected_(lua_State *L) {
int status;
lua_pushvalue(L, lua_upvalueindex(2));
lua_insert(L, 1);
status = lua_pcallk(L, lua_gettop(L) - 1, LUA_MULTRET, 0, 0, protected_cont);
return protected_finish(L, status, 0);
}
static int global_protect(lua_State *L) {
lua_pushcclosure(L, protected_, 1);
lua_settop(L, 1);
lua_pushvalue(L, lua_upvalueindex(1));
lua_insert(L, 1);
lua_pushcclosure(L, protected_, 2);
return 1;
}
@ -92,10 +125,9 @@ static int global_protect(lua_State *L) {
* Init module
\*-------------------------------------------------------------------------*/
int except_open(lua_State *L) {
#if LUA_VERSION_NUM > 501 && !defined(LUA_COMPAT_MODULE)
luaL_setfuncs(L, func, 0);
#else
luaL_openlib(L, NULL, func, 0);
#endif
lua_newtable(L); /* metatable for wrapped exceptions */
lua_pushboolean(L, 0);
lua_setfield(L, -2, "__metatable");
luaL_setfuncs(L, func, 1);
return 0;
}

View File

@ -9,20 +9,25 @@
* 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)
* The main idea is that functions should return nil as their first return
* values when they find 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.
* checks the first value, and, if it's falsy, wraps the second value in a
* table with metatable and calls "error" on it. Otherwise, it returns all
* values it received. Basically, it works like the Lua "assert" function,
* but it creates errors targeted specifically at "protect".
*
* 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.
* The "newtry" function is a factory for "try" functions that call a
* finalizer in protected mode before calling "error".
*
* With these two function, it's easy to write functions that throw
* The "protect" function returns a new function that behaves exactly like
* the function it receives, but the new function catches exceptions thrown
* by "try" functions and returns nil followed by the error message instead.
*
* With these three functions, it's easy to write functions that throw
* exceptions on error, but that don't interrupt the user script.
\*=========================================================================*/

View File

@ -23,7 +23,7 @@ local _M = socket.ftp
-- timeout in seconds before the program gives up on a connection
_M.TIMEOUT = 60
-- default port for ftp service
_M.PORT = 21
local PORT = 21
-- this is the default anonymous password. used when no password is
-- provided in url. should be changed to your e-mail.
_M.USER = "ftp"
@ -35,7 +35,7 @@ _M.PASSWORD = "anonymous@anonymous.org"
local metat = { __index = {} }
function _M.open(server, port, create)
local tp = socket.try(tp.connect(server, port or _M.PORT, _M.TIMEOUT, create))
local tp = socket.try(tp.connect(server, port or PORT, _M.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)
@ -51,7 +51,7 @@ end
function metat.__index:pasvconnect()
self.data = self.try(socket.tcp())
self.try(self.data:settimeout(_M.TIMEOUT))
self.try(self.data:connect(self.pasvt.ip, self.pasvt.port))
self.try(self.data:connect(self.pasvt.address, self.pasvt.port))
end
function metat.__index:login(user, password)
@ -71,32 +71,65 @@ function metat.__index:pasv()
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),
address = 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
return self.pasvt.address, self.pasvt.port
end
function metat.__index:port(ip, port)
function metat.__index:epsv()
self.try(self.tp:command("epsv"))
local code, reply = self.try(self.tp:check("229"))
local pattern = "%((.)(.-)%1(.-)%1(.-)%1%)"
local d, prt, address, port = string.match(reply, pattern)
self.try(port, "invalid epsv response")
self.pasvt = {
address = self.tp:getpeername(),
port = port
}
if self.server then
self.server:close()
self.server = nil
end
return self.pasvt.address, self.pasvt.port
end
function metat.__index:port(address, 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())
if not address then
address, port = self.try(self.tp:getsockname())
self.server = self.try(socket.bind(address, 0))
address, port = self.try(self.server:getsockname())
self.try(self.server:settimeout(_M.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), "%.", ",")
local arg = string.gsub(string.format("%s,%d,%d", address, ph, pl), "%.", ",")
self.try(self.tp:command("port", arg))
self.try(self.tp:check("2.."))
return 1
end
function metat.__index:eprt(family, address, port)
self.pasvt = nil
if not address then
address, port = self.try(self.tp:getsockname())
self.server = self.try(socket.bind(address, 0))
address, port = self.try(self.server:getsockname())
self.try(self.server:settimeout(_M.TIMEOUT))
end
local arg = string.format("|%s|%s|%d|", family, address, port)
self.try(self.tp:command("eprt", 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
@ -110,12 +143,12 @@ function metat.__index:send(sendt)
-- 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
-- if there is not 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 readt = { self.tp }
local checkstep = function(src, snk)
-- check status in control connection while downloading
local readyt = socket.select(readt, nil, 0)
@ -207,7 +240,7 @@ local function tput(putt)
f:greet()
f:login(putt.user, putt.password)
if putt.type then f:type(putt.type) end
f:pasv()
f:epsv()
local sent = f:send(putt)
f:quit()
f:close()
@ -219,7 +252,7 @@ local default = {
scheme = "ftp"
}
local function parse(u)
local function genericform(u)
local t = socket.try(url.parse(u, default))
socket.try(t.scheme == "ftp", "wrong scheme '" .. t.scheme .. "'")
socket.try(t.host, "missing hostname")
@ -232,8 +265,10 @@ local function parse(u)
return t
end
_M.genericform = genericform
local function sput(u, body)
local putt = parse(u)
local putt = genericform(u)
putt.source = ltn12.source.string(body)
return tput(putt)
end
@ -250,14 +285,14 @@ local function tget(gett)
f:greet()
f:login(gett.user, gett.password)
if gett.type then f:type(gett.type) end
f:pasv()
f:epsv()
f:receive(gett)
f:quit()
return f:close()
end
local function sget(u)
local gett = parse(u)
local gett = genericform(u)
local t = {}
gett.sink = ltn12.sink.table(t)
tget(gett)
@ -268,11 +303,20 @@ _M.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)
local f = _M.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
if type(cmdt.command) == "table" then
local argument = cmdt.argument or {}
local check = cmdt.check or {}
for i,cmd in ipairs(cmdt.command) do
f.try(f.tp:command(cmd, argument[i]))
if check[i] then f.try(f.tp:check(check[i])) end
end
else
f.try(f.tp:command(cmdt.command, cmdt.argument))
if cmdt.check then f.try(f.tp:check(cmdt.check)) end
end
f:quit()
return f:close()
end)

View File

@ -22,12 +22,15 @@ local _M = socket.http
-- Program constants
-----------------------------------------------------------------------------
-- connection timeout in seconds
TIMEOUT = 60
-- default port for document retrieval
_M.PORT = 80
_M.TIMEOUT = 60
-- user agent field sent in request
_M.USERAGENT = socket._VERSION
-- supported schemes
local SCHEMES = { ["http"] = true }
-- default port for document retrieval
local PORT = 80
-----------------------------------------------------------------------------
-- Reads MIME headers from a connection, unfolding where needed
-----------------------------------------------------------------------------
@ -114,7 +117,7 @@ function _M.open(host, port, create)
h.try = socket.newtry(function() h:close() end)
-- set timeout before connecting
h.try(c:settimeout(_M.TIMEOUT))
h.try(c:connect(host, port or _M.PORT))
h.try(c:connect(host, port or PORT))
-- here everything worked
return h
end
@ -186,7 +189,7 @@ end
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
if not reqt.proxy and not _M.PROXY then
u = {
path = socket.try(reqt.path, "invalid path 'nil'"),
params = reqt.params,
@ -198,7 +201,7 @@ local function adjusturi(reqt)
end
local function adjustproxy(reqt)
local proxy = reqt.proxy or PROXY
local proxy = reqt.proxy or _M.PROXY
if proxy then
proxy = url.parse(proxy)
return proxy.host, proxy.port or 3128
@ -209,16 +212,27 @@ end
local function adjustheaders(reqt)
-- default headers
local host = string.gsub(reqt.authority, "^.-@", "")
local lower = {
["user-agent"] = _M.USERAGENT,
["host"] = reqt.host,
["host"] = 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))
"Basic " .. (mime.b64(reqt.user .. ":" ..
url.unescape(reqt.password)))
end
-- if we have proxy authentication information, pass it along
local proxy = reqt.proxy or _M.PROXY
if proxy then
proxy = url.parse(proxy)
if proxy.user and proxy.password then
lower["proxy-authorization"] =
"Basic " .. (mime.b64(proxy.user .. ":" .. proxy.password))
end
end
-- override with user headers
for i,v in base.pairs(reqt.headers or lower) do
@ -230,7 +244,7 @@ end
-- default url parts
local default = {
host = "",
port = _M.PORT,
port = PORT,
path ="/",
scheme = "http"
}
@ -240,22 +254,27 @@ local function adjustrequest(reqt)
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) .. "'")
if nreqt.port == "" then nreqt.port = PORT end
if not (nreqt.host and nreqt.host ~= "") then
socket.try(nil, "invalid host '" .. base.tostring(nreqt.host) .. "'")
end
-- 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)
-- ajust host and port if there is a proxy
nreqt.host, nreqt.port = adjustproxy(nreqt)
return nreqt
end
local function shouldredirect(reqt, code, headers)
return headers.location and
string.gsub(headers.location, "%s", "") ~= "" and
(reqt.redirect ~= false) and
local location = headers.location
if not location then return false end
location = string.gsub(location, "%s", "")
if location == "" then return false end
local scheme = string.match(location, "^([%w][%w%+%-%.]*)%:")
if scheme and not SCHEMES[scheme] then return false end
return (reqt.redirect ~= false) and
(code == 301 or code == 302 or code == 303 or code == 307) and
(not reqt.method or reqt.method == "GET" or reqt.method == "HEAD")
and (not reqt.nredirects or reqt.nredirects < 5)
@ -328,11 +347,13 @@ end
return 1, code, headers, status
end
local function srequest(u, b)
-- turns an url and a body into a generic request
local function genericform(u, b)
local t = {}
local reqt = {
url = u,
sink = ltn12.sink.table(t)
sink = ltn12.sink.table(t),
target = t
}
if b then
reqt.source = ltn12.source.string(b)
@ -342,8 +363,15 @@ local function srequest(u, b)
}
reqt.method = "POST"
end
local code, headers, status = socket.skip(1, trequest(reqt))
return table.concat(t), code, headers, status
return reqt
end
_M.genericform = genericform
local function srequest(u, b)
local reqt = genericform(u, b)
local _, code, headers, status = trequest(reqt)
return table.concat(reqt.target), code, headers, status
end
_M.request = socket.protect(function(reqt, body)

View File

@ -8,6 +8,7 @@
#include "lua.h"
#include "lauxlib.h"
#include "compat.h"
#include "inet.h"
@ -41,11 +42,7 @@ int inet_open(lua_State *L)
{
lua_pushstring(L, "dns");
lua_newtable(L);
#if LUA_VERSION_NUM > 501 && !defined(LUA_COMPAT_MODULE)
luaL_setfuncs(L, func, 0);
#else
luaL_openlib(L, NULL, func, 0);
#endif
lua_settable(L, -3);
return 0;
}
@ -97,7 +94,7 @@ static int inet_global_getnameinfo(lua_State *L) {
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = PF_UNSPEC;
hints.ai_family = AF_UNSPEC;
ret = getaddrinfo(host, serv, &hints, &resolved);
if (ret != 0) {
@ -149,7 +146,7 @@ static int inet_global_toip(lua_State *L)
int inet_optfamily(lua_State* L, int narg, const char* def)
{
static const char* optname[] = { "unspec", "inet", "inet6", NULL };
static int optvalue[] = { PF_UNSPEC, PF_INET, PF_INET6, 0 };
static int optvalue[] = { AF_UNSPEC, AF_INET, AF_INET6, 0 };
return optvalue[luaL_checkoption(L, narg, def, optname)];
}
@ -170,7 +167,7 @@ static int inet_global_getaddrinfo(lua_State *L)
int i = 1, ret = 0;
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = PF_UNSPEC;
hints.ai_family = AF_UNSPEC;
ret = getaddrinfo(hostname, NULL, &hints, &resolved);
if (ret != 0) {
lua_pushnil(L);
@ -183,6 +180,7 @@ static int inet_global_getaddrinfo(lua_State *L)
ret = getnameinfo(iterator->ai_addr, (socklen_t) iterator->ai_addrlen,
hbuf, (socklen_t) sizeof(hbuf), NULL, 0, NI_NUMERICHOST);
if (ret){
freeaddrinfo(resolved);
lua_pushnil(L);
lua_pushstring(L, socket_gaistrerror(ret));
return 2;
@ -200,6 +198,16 @@ static int inet_global_getaddrinfo(lua_State *L)
lua_pushliteral(L, "inet6");
lua_settable(L, -3);
break;
case AF_UNSPEC:
lua_pushliteral(L, "family");
lua_pushliteral(L, "unspec");
lua_settable(L, -3);
break;
default:
lua_pushliteral(L, "family");
lua_pushliteral(L, "unknown");
lua_settable(L, -3);
break;
}
lua_pushliteral(L, "addr");
lua_pushstring(L, hbuf);
@ -256,12 +264,11 @@ int inet_meth_getpeername(lua_State *L, p_socket ps, int family)
}
lua_pushstring(L, name);
lua_pushinteger(L, (int) strtol(port, (char **) NULL, 10));
if (family == PF_INET) {
lua_pushliteral(L, "inet");
} else if (family == PF_INET6) {
lua_pushliteral(L, "inet6");
} else {
lua_pushliteral(L, "uknown family");
switch (family) {
case AF_INET: lua_pushliteral(L, "inet"); break;
case AF_INET6: lua_pushliteral(L, "inet6"); break;
case AF_UNSPEC: lua_pushliteral(L, "unspec"); break;
default: lua_pushliteral(L, "unknown"); break;
}
return 3;
}
@ -290,12 +297,11 @@ int inet_meth_getsockname(lua_State *L, p_socket ps, int family)
}
lua_pushstring(L, name);
lua_pushstring(L, port);
if (family == PF_INET) {
lua_pushliteral(L, "inet");
} else if (family == PF_INET6) {
lua_pushliteral(L, "inet6");
} else {
lua_pushliteral(L, "uknown family");
switch (family) {
case AF_INET: lua_pushliteral(L, "inet"); break;
case AF_INET6: lua_pushliteral(L, "inet6"); break;
case AF_UNSPEC: lua_pushliteral(L, "unspec"); break;
default: lua_pushliteral(L, "unknown"); break;
}
return 3;
}
@ -346,8 +352,13 @@ static void inet_pushresolved(lua_State *L, struct hostent *hp)
/*-------------------------------------------------------------------------*\
* Tries to create a new inet socket
\*-------------------------------------------------------------------------*/
const char *inet_trycreate(p_socket ps, int family, int type) {
return socket_strerror(socket_create(ps, family, type, 0));
const char *inet_trycreate(p_socket ps, int family, int type, int protocol) {
const char *err = socket_strerror(socket_create(ps, family, type, protocol));
if (err == NULL && family == AF_INET6) {
int yes = 1;
setsockopt(*ps, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&yes, sizeof(yes));
}
return err;
}
/*-------------------------------------------------------------------------*\
@ -356,7 +367,7 @@ const char *inet_trycreate(p_socket ps, int family, int type) {
const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm)
{
switch (family) {
case PF_INET: {
case AF_INET: {
struct sockaddr_in sin;
memset((char *) &sin, 0, sizeof(sin));
sin.sin_family = AF_UNSPEC;
@ -364,7 +375,7 @@ const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm)
return socket_strerror(socket_connect(ps, (SA *) &sin,
sizeof(sin), tm));
}
case PF_INET6: {
case AF_INET6: {
struct sockaddr_in6 sin6;
struct in6_addr addrany = IN6ADDR_ANY_INIT;
memset((char *) &sin6, 0, sizeof(sin6));
@ -385,6 +396,7 @@ const char *inet_tryconnect(p_socket ps, int *family, const char *address,
{
struct addrinfo *iterator = NULL, *resolved = NULL;
const char *err = NULL;
int current_family = *family;
/* try resolving */
err = socket_gaistrerror(getaddrinfo(address, serv,
connecthints, &resolved));
@ -399,23 +411,23 @@ const char *inet_tryconnect(p_socket ps, int *family, const char *address,
* that shows up while iterating. if there was a
* bind, all families will be the same and we will
* not enter this branch. */
if (*family != iterator->ai_family) {
if (current_family != iterator->ai_family || *ps == SOCKET_INVALID) {
socket_destroy(ps);
err = socket_strerror(socket_create(ps, iterator->ai_family,
iterator->ai_socktype, iterator->ai_protocol));
if (err != NULL) {
freeaddrinfo(resolved);
return err;
}
*family = iterator->ai_family;
/* all sockets initially non-blocking */
err = inet_trycreate(ps, iterator->ai_family,
iterator->ai_socktype, iterator->ai_protocol);
if (err) continue;
current_family = iterator->ai_family;
/* set non-blocking before connect */
socket_setnonblocking(ps);
}
/* try connecting to remote address */
err = socket_strerror(socket_connect(ps, (SA *) iterator->ai_addr,
(socklen_t) iterator->ai_addrlen, tm));
/* if success, break out of loop */
if (err == NULL) break;
/* if success or timeout is zero, break out of loop */
if (err == NULL || timeout_iszero(tm)) {
*family = current_family;
break;
}
}
freeaddrinfo(resolved);
/* here, if err is set, we failed */
@ -426,15 +438,14 @@ const char *inet_tryconnect(p_socket ps, int *family, const char *address,
* Tries to accept a socket
\*-------------------------------------------------------------------------*/
const char *inet_tryaccept(p_socket server, int family, p_socket client,
p_timeout tm)
{
p_timeout tm) {
socklen_t len;
t_sockaddr_storage addr;
if (family == PF_INET6) {
len = sizeof(struct sockaddr_in6);
} else {
len = sizeof(struct sockaddr_in);
}
switch (family) {
case AF_INET6: len = sizeof(struct sockaddr_in6); break;
case AF_INET: len = sizeof(struct sockaddr_in); break;
default: len = sizeof(addr); break;
}
return socket_strerror(socket_accept(server, client, (SA *) &addr,
&len, tm));
}
@ -442,12 +453,11 @@ const char *inet_tryaccept(p_socket server, int family, p_socket client,
/*-------------------------------------------------------------------------*\
* Tries to bind socket to (address, port)
\*-------------------------------------------------------------------------*/
const char *inet_trybind(p_socket ps, const char *address, const char *serv,
struct addrinfo *bindhints)
{
const char *inet_trybind(p_socket ps, int *family, const char *address,
const char *serv, struct addrinfo *bindhints) {
struct addrinfo *iterator = NULL, *resolved = NULL;
const char *err = NULL;
t_socket sock = *ps;
int current_family = *family;
/* translate luasocket special values to C */
if (strcmp(address, "*") == 0) address = NULL;
if (!serv) serv = "0";
@ -459,30 +469,27 @@ const char *inet_trybind(p_socket ps, const char *address, const char *serv,
}
/* iterate over resolved addresses until one is good */
for (iterator = resolved; iterator; iterator = iterator->ai_next) {
if(sock == SOCKET_INVALID) {
err = socket_strerror(socket_create(&sock, iterator->ai_family,
iterator->ai_socktype, iterator->ai_protocol));
if(err)
continue;
if (current_family != iterator->ai_family || *ps == SOCKET_INVALID) {
socket_destroy(ps);
err = inet_trycreate(ps, iterator->ai_family,
iterator->ai_socktype, iterator->ai_protocol);
if (err) continue;
current_family = iterator->ai_family;
}
/* try binding to local address */
err = socket_strerror(socket_bind(&sock,
(SA *) iterator->ai_addr,
err = socket_strerror(socket_bind(ps, (SA *) iterator->ai_addr,
(socklen_t) iterator->ai_addrlen));
/* keep trying unless bind succeeded */
if (err) {
if(sock != *ps)
socket_destroy(&sock);
} else {
/* remember what we connected to, particularly the family */
*bindhints = *iterator;
if (err == NULL) {
*family = current_family;
/* set to non-blocking after bind */
socket_setnonblocking(ps);
break;
}
}
/* cleanup and return error */
freeaddrinfo(resolved);
*ps = sock;
/* here, if err is set, we failed */
return err;
}

View File

@ -24,11 +24,11 @@
int inet_open(lua_State *L);
const char *inet_trycreate(p_socket ps, int family, int type);
const char *inet_trycreate(p_socket ps, int family, int type, int protocol);
const char *inet_tryconnect(p_socket ps, int *family, const char *address,
const char *serv, p_timeout tm, struct addrinfo *connecthints);
const char *inet_trybind(p_socket ps, const char *address, const char *serv,
struct addrinfo *bindhints);
const char *inet_trybind(p_socket ps, int *family, const char *address,
const char *serv, struct addrinfo *bindhints);
const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm);
const char *inet_tryaccept(p_socket server, int family, p_socket client, p_timeout tm);

View File

@ -9,6 +9,7 @@
-----------------------------------------------------------------------------
local string = require("string")
local table = require("table")
local unpack = unpack or table.unpack
local base = _G
local _M = {}
if module then -- heuristic for exporting a global package table
@ -21,6 +22,9 @@ _M.source = source
_M.sink = sink
_M.pump = pump
local unpack = unpack or table.unpack
local select = base.select
-- 2048 seems to be better in windows...
_M.BLOCKSIZE = 2048
_M._VERSION = "LTN12 1.0.3"
@ -42,7 +46,7 @@ end
-- (thanks to Wim Couwenberg)
function filter.chain(...)
local arg = {...}
local n = select('#',...)
local n = base.select('#',...)
local top, index = 1, 1
local retry = ""
return function(chunk)
@ -124,6 +128,16 @@ function source.string(s)
else return source.empty() end
end
-- creates table source
function source.table(t)
base.assert('table' == type(t))
local i = 0
return function()
i = i + 1
return t[i]
end
end
-- creates rewindable source
function source.rewind(src)
base.assert(src)
@ -139,7 +153,9 @@ function source.rewind(src)
end
end
function source.chain(src, f)
-- chains a source with one or several filter(s)
function source.chain(src, f, ...)
if ... then f=filter.chain(f, ...) end
base.assert(src and f)
local last_in, last_out = "", ""
local state = "feeding"
@ -254,8 +270,13 @@ function sink.error(err)
end
end
-- chains a sink with a filter
function sink.chain(f, snk)
-- chains a sink with one or several filter(s)
function sink.chain(f, snk, ...)
if ... then
local args = { f, snk, ... }
snk = table.remove(args, #args)
f = filter.chain(unpack(args))
end
base.assert(f and snk)
return function(chunk, err)
if chunk ~= "" then

View File

@ -17,7 +17,7 @@
\*=========================================================================*/
#include "lua.h"
#include "lauxlib.h"
#include "compat.h"
/*=========================================================================*\
* LuaSocket includes
@ -64,7 +64,7 @@ static luaL_Reg func[] = {
* Skip a few arguments
\*-------------------------------------------------------------------------*/
static int global_skip(lua_State *L) {
int amount = luaL_checkint(L, 1);
int amount = luaL_checkinteger(L, 1);
int ret = lua_gettop(L) - amount - 1;
return ret >= 0 ? ret : 0;
}
@ -78,26 +78,14 @@ static int global_unload(lua_State *L) {
return 0;
}
#if LUA_VERSION_NUM > 501
int luaL_typerror (lua_State *L, int narg, const char *tname) {
const char *msg = lua_pushfstring(L, "%s expected, got %s",
tname, luaL_typename(L, narg));
return luaL_argerror(L, narg, msg);
}
#endif
/*-------------------------------------------------------------------------*\
* Setup basic stuff.
\*-------------------------------------------------------------------------*/
static int base_open(lua_State *L) {
if (socket_open()) {
/* export functions (and leave namespace table on top of stack) */
#if LUA_VERSION_NUM > 501 && !defined(LUA_COMPAT_MODULE)
lua_newtable(L);
luaL_setfuncs(L, func, 0);
#else
luaL_openlib(L, "socket", func, 0);
#endif
#ifdef LUASOCKET_DEBUG
lua_pushstring(L, "_DEBUG");
lua_pushboolean(L, 1);

View File

@ -20,15 +20,17 @@ PLAT?=linux
# lua version to build against
LUAV?=5.1
# MYCFLAGS: to be set by user if needed
MYCFLAGS?=
# MYLDFLAGS: to be set by user if needed
MYLDFLAGS?=
# DEBUG: NODEBUG DEBUG
# debug mode causes luasocket to collect and returns timing information useful
# for testing and debugging luasocket itself
DEBUG?=NODEBUG
# COMPAT: COMPAT NOCOMPAT
# when compiling for 5.2, use LUA_COMPAT_MODULE
COMPAT?=NOCOMPAT
# where lua headers are found for macosx builds
# LUAINC_macosx:
# /opt/local/include
@ -40,7 +42,6 @@ LUAPREFIX_macosx?=/opt/local
CDIR_macosx?=lib/lua/$(LUAV)
LDIR_macosx?=share/lua/$(LUAV)
# LUAINC_linux:
# /usr/include/lua$(LUAV)
# /usr/local/include
@ -52,6 +53,15 @@ LUAPREFIX_linux?=/usr/local
CDIR_linux?=lib/lua/$(LUAV)
LDIR_linux?=share/lua/$(LUAV)
# LUAINC_freebsd:
# /usr/local/include/lua$(LUAV)
# where lua headers are found for freebsd builds
LUAINC_freebsd_base?=/usr/local/include/
LUAINC_freebsd?=$(LUAINC_freebsd_base)/lua$(LUAV)
LUAPREFIX_freebsd?=/usr/local/
CDIR_freebsd?=lib/lua/$(LUAV)
LDIR_freebsd?=share/lua/$(LUAV)
# where lua headers are found for mingw builds
# LUAINC_mingw:
# /opt/local/include
@ -67,13 +77,21 @@ LDIR_mingw?=lua/$(LUAV)/lua
# LUAINC_win32:
# LUALIB_win32:
# where lua headers and libraries are found for win32 builds
LUAINC_win32_base?=
LUAINC_win32?=$(LUAINC_win32_base)/lua/$(LUAV)
PLATFORM_win32?=Release
LUAPREFIX_win32?=
CDIR_win32?=lua/$(LUAV)/$(PLATFORM_win32)
LDIR_win32?=lua/$(LUAV)/$(PLATFORM_win32)/lua
LUALIB_win32?=$(LUAPREFIX_win32)/lua/$(LUAV)/$(PLATFORM_win32)
LUAINC_win32?=$(LUAPREFIX_win32)/include/lua/$(LUAV)
PLATFORM_win32?=Release
CDIR_win32?=bin/lua/$(LUAV)/$(PLATFORM_win32)
LDIR_win32?=bin/lua/$(LUAV)/$(PLATFORM_win32)/lua
LUALIB_win32?=$(LUAPREFIX_win32)/lib/lua/$(LUAV)/$(PLATFORM_win32)
LUALIBNAME_win32?=lua$(subst .,,$(LUAV)).lib
# LUAINC_solaris:
LUAINC_solaris_base?=/usr/include
LUAINC_solaris?=$(LUAINC_solaris_base)/lua/$(LUAV)
LUAPREFIX_solaris?=/usr/local
CDIR_solaris?=lib/lua/$(LUAV)
LDIR_solaris?=share/lua/$(LUAV)
# prefix: /usr/local /usr /opt/local /sw
# the top of the default install tree
@ -84,7 +102,7 @@ LDIR?=$(LDIR_$(PLAT))
# DESTDIR: (no default)
# used by package managers to install into a temporary destination
DESTDIR=
DESTDIR?=
#------
# Definitions below can be overridden on the make command line, but
@ -117,11 +135,13 @@ print:
@echo LUALIB_$(PLAT)=$(LUALIB_$(PLAT))
@echo INSTALL_TOP_CDIR=$(INSTALL_TOP_CDIR)
@echo INSTALL_TOP_LDIR=$(INSTALL_TOP_LDIR)
@echo CFLAGS=$(CFLAGS)
@echo LDFLAGS=$(LDFLAGS)
#------
# Supported platforms
#
PLATS= macosx linux win32 mingw
PLATS= macosx linux win32 mingw solaris
#------
# Compiler and linker settings
@ -129,11 +149,11 @@ PLATS= macosx linux win32 mingw
SO_macosx=so
O_macosx=o
CC_macosx=gcc
DEF_macosx= -DLUASOCKET_$(DEBUG) -DUNIX_HAS_SUN_LEN -DLUA_$(COMPAT)_MODULE \
DEF_macosx= -DLUASOCKET_$(DEBUG) -DUNIX_HAS_SUN_LEN \
-DLUASOCKET_API='__attribute__((visibility("default")))' \
-DUNIX_API='__attribute__((visibility("default")))' \
-DMIME_API='__attribute__((visibility("default")))'
CFLAGS_macosx= -I$(LUAINC) $(DEF) -pedantic -Wall -O2 -fno-common \
CFLAGS_macosx= -I$(LUAINC) $(DEF) -Wall -O2 -fno-common \
-fvisibility=hidden
LDFLAGS_macosx= -bundle -undefined dynamic_lookup -o
LD_macosx= export MACOSX_DEPLOYMENT_TARGET="10.3"; gcc
@ -145,26 +165,58 @@ SOCKET_macosx=usocket.o
SO_linux=so
O_linux=o
CC_linux=gcc
DEF_linux=-DLUASOCKET_$(DEBUG) -DLUA_$(COMPAT)_MODULE \
DEF_linux=-DLUASOCKET_$(DEBUG) \
-DLUASOCKET_API='__attribute__((visibility("default")))' \
-DUNIX_API='__attribute__((visibility("default")))' \
-DMIME_API='__attribute__((visibility("default")))'
CFLAGS_linux= -I$(LUAINC) $(DEF) -pedantic -Wall -Wshadow -Wextra \
CFLAGS_linux= -I$(LUAINC) $(DEF) -Wall -Wshadow -Wextra \
-Wimplicit -O2 -ggdb3 -fpic -fvisibility=hidden
LDFLAGS_linux=-O -shared -fpic -o
LD_linux=gcc
SOCKET_linux=usocket.o
#------
# Compiler and linker settings
# for FreeBSD
SO_freebsd=so
O_freebsd=o
CC_freebsd=gcc
DEF_freebsd=-DLUASOCKET_$(DEBUG) \
-DLUASOCKET_API='__attribute__((visibility("default")))' \
-DUNIX_API='__attribute__((visibility("default")))' \
-DMIME_API='__attribute__((visibility("default")))'
CFLAGS_freebsd= -I$(LUAINC) $(DEF) -Wall -Wshadow -Wextra \
-Wimplicit -O2 -ggdb3 -fpic -fvisibility=hidden
LDFLAGS_freebsd=-O -shared -fpic -o
LD_freebsd=gcc
SOCKET_freebsd=usocket.o
#------
# Compiler and linker settings
# for Solaris
SO_solaris=so
O_solaris=o
CC_solaris=gcc
DEF_solaris=-DLUASOCKET_$(DEBUG) \
-DLUASOCKET_API='__attribute__((visibility("default")))' \
-DUNIX_API='__attribute__((visibility("default")))' \
-DMIME_API='__attribute__((visibility("default")))'
CFLAGS_solaris=-I$(LUAINC) $(DEF) -Wall -Wshadow -Wextra \
-Wimplicit -O2 -ggdb3 -fpic -fvisibility=hidden
LDFLAGS_solaris=-lnsl -lsocket -lresolv -O -shared -fpic -o
LD_solaris=gcc
SOCKET_solaris=usocket.o
#------
# Compiler and linker settings
# for MingW
SO_mingw=dll
O_mingw=o
CC_mingw=gcc
DEF_mingw= -DLUASOCKET_INET_PTON -DLUASOCKET_$(DEBUG) -DLUA_$(COMPAT)_MODULE \
DEF_mingw= -DLUASOCKET_INET_PTON -DLUASOCKET_$(DEBUG) \
-DWINVER=0x0501 -DLUASOCKET_API='__declspec(dllexport)' \
-DMIME_API='__declspec(dllexport)'
CFLAGS_mingw= -I$(LUAINC) $(DEF) -pedantic -Wall -O2 -fno-common \
CFLAGS_mingw= -I$(LUAINC) $(DEF) -Wall -O2 -fno-common \
-fvisibility=hidden
LDFLAGS_mingw= $(LUALIB) -shared -Wl,-s -lws2_32 -o
LD_mingw=gcc
@ -179,8 +231,7 @@ O_win32=obj
CC_win32=cl
DEF_win32= //D "WIN32" //D "NDEBUG" //D "_WINDOWS" //D "_USRDLL" \
//D "LUASOCKET_API=__declspec(dllexport)" //D "_CRT_SECURE_NO_WARNINGS" \
//D "_WINDLL" //D "LUA_$(COMPAT)_MODULE" \
//D "MIME_API=__declspec(dllexport)" \
//D "_WINDLL" //D "MIME_API=__declspec(dllexport)" \
//D "LUASOCKET_$(DEBUG)"
CFLAGS_win32=//I "$(LUAINC)" $(DEF) //O2 //Ot //MD //W3 //nologo
LDFLAGS_win32= //nologo //link //NOLOGO //DLL //INCREMENTAL:NO \
@ -188,7 +239,7 @@ LDFLAGS_win32= //nologo //link //NOLOGO //DLL //INCREMENTAL:NO \
//MANIFESTUAC:"level='asInvoker' uiAccess='false'" \
//SUBSYSTEM:WINDOWS //OPT:REF //OPT:ICF //DYNAMICBASE:NO \
//MACHINE:X86 /LIBPATH:"$(shell cmd //c echo $(LUALIB))" \
lua$(subst .,,$(LUAV)).lib ws2_32.lib //OUT:
$(LUALIBNAME_win32) ws2_32.lib //OUT:
LD_win32=cl
SOCKET_win32=wsocket.obj
@ -204,8 +255,8 @@ SO=$(SO_$(PLAT))
O=$(O_$(PLAT))
SOCKET_V=3.0-rc1
MIME_V=1.0.3
SOCKET_SO=socket.$(SO).$(SOCKET_V)
MIME_SO=mime.$(SO).$(MIME_V)
SOCKET_SO=socket-$(SOCKET_V).$(SO)
MIME_SO=mime-$(MIME_V).$(SO)
UNIX_SO=unix.$(SO)
SERIAL_SO=serial.$(SO)
SOCKET=$(SOCKET_$(PLAT))
@ -215,8 +266,8 @@ SOCKET=$(SOCKET_$(PLAT))
#
CC=$(CC_$(PLAT))
DEF=$(DEF_$(PLAT))
CFLAGS=$(CFLAGS_$(PLAT))
LDFLAGS=$(LDFLAGS_$(PLAT))
CFLAGS=$(MYCFLAGS) $(CFLAGS_$(PLAT))
LDFLAGS=$(MYLDFLAGS) $(LDFLAGS_$(PLAT))
LD=$(LD_$(PLAT))
LUAINC= $(LUAINC_$(PLAT))
LUALIB= $(LUALIB_$(PLAT))
@ -230,6 +281,7 @@ SOCKET_OBJS= \
buffer.$(O) \
io.$(O) \
auxiliar.$(O) \
compat.$(O) \
options.$(O) \
inet.$(O) \
$(SOCKET) \
@ -242,7 +294,8 @@ SOCKET_OBJS= \
# Modules belonging mime-core
#
MIME_OBJS= \
mime.$(O)
mime.$(O) \
compat.$(O)
#------
# Modules belonging unix (local domain sockets)
@ -254,12 +307,15 @@ UNIX_OBJS=\
timeout.$(O) \
io.$(O) \
usocket.$(O) \
unixstream.$(O) \
unixdgram.$(O) \
compat.$(O) \
unix.$(O)
#------
# Modules belonging to serial (device streams)
#
SERIAL_OBJS:=\
SERIAL_OBJS=\
buffer.$(O) \
auxiliar.$(O) \
options.$(O) \
@ -289,6 +345,10 @@ TO_TOP_LDIR= \
#
default: $(PLAT)
freebsd:
$(MAKE) all-unix PLAT=freebsd
macosx:
$(MAKE) all-unix PLAT=macosx
@ -301,6 +361,9 @@ linux:
mingw:
$(MAKE) all PLAT=mingw
solaris:
$(MAKE) all-unix PLAT=solaris
none:
@echo "Please run"
@echo " make PLATFORM"
@ -349,6 +412,7 @@ clean:
#------
# List of dependencies
#
compat.$(O): compat.c compat.h
auxiliar.$(O): auxiliar.c auxiliar.h
buffer.$(O): buffer.c buffer.h io.h timeout.h
except.$(O): except.c except.h

View File

@ -61,7 +61,7 @@ function _M.parse_from(from)
end
function _M.split_mbox(mbox_s)
mbox = {}
local mbox = {}
mbox_s = string.gsub(mbox_s, "\r\n", "\n") .."\n\nFrom \n"
local nj, i, j = 1, 1, 1
while 1 do

View File

@ -6,10 +6,7 @@
#include "lua.h"
#include "lauxlib.h"
#if !defined(LUA_VERSION_NUM) || (LUA_VERSION_NUM < 501)
#include "compat-5.1.h"
#endif
#include "compat.h"
#include "mime.h"
@ -81,12 +78,8 @@ static UC b64unbase[256];
\*-------------------------------------------------------------------------*/
MIME_API int luaopen_mime_core(lua_State *L)
{
#if LUA_VERSION_NUM > 501 && !defined(LUA_COMPAT_MODULE)
lua_newtable(L);
luaL_setfuncs(L, func, 0);
#else
luaL_openlib(L, "mime", func, 0);
#endif
/* make version string available to scripts */
lua_pushstring(L, "_VERSION");
lua_pushstring(L, MIME_VERSION);
@ -111,7 +104,7 @@ 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 *input = (const UC *) luaL_optlstring(L, 2, NULL, &size);
const UC *last = input + size;
int length = (int) luaL_optnumber(L, 3, 76);
luaL_Buffer buffer;
@ -259,7 +252,7 @@ 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 *input = (const UC *) luaL_optlstring(L, 1, NULL, &isize);
const UC *last = input + isize;
luaL_Buffer buffer;
/* end-of-input blackhole */
@ -274,7 +267,7 @@ static int mime_global_b64(lua_State *L)
luaL_buffinit(L, &buffer);
while (input < last)
asize = b64encode(*input++, atom, asize, &buffer);
input = (UC *) luaL_optlstring(L, 2, NULL, &isize);
input = (const UC *) luaL_optlstring(L, 2, NULL, &isize);
/* if second part is nil, we are done */
if (!input) {
size_t osize = 0;
@ -305,7 +298,7 @@ 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 *input = (const UC *) luaL_optlstring(L, 1, NULL, &isize);
const UC *last = input + isize;
luaL_Buffer buffer;
/* end-of-input blackhole */
@ -320,7 +313,7 @@ static int mime_global_unb64(lua_State *L)
luaL_buffinit(L, &buffer);
while (input < last)
asize = b64decode(*input++, atom, asize, &buffer);
input = (UC *) luaL_optlstring(L, 2, NULL, &isize);
input = (const UC *) luaL_optlstring(L, 2, NULL, &isize);
/* if second is nil, we are done */
if (!input) {
size_t osize = 0;
@ -457,7 +450,7 @@ 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 *input = (const UC *) luaL_optlstring(L, 1, NULL, &isize);
const UC *last = input + isize;
const char *marker = luaL_optstring(L, 3, CRLF);
luaL_Buffer buffer;
@ -473,7 +466,7 @@ static int mime_global_qp(lua_State *L)
luaL_buffinit(L, &buffer);
while (input < last)
asize = qpencode(*input++, atom, asize, marker, &buffer);
input = (UC *) luaL_optlstring(L, 2, NULL, &isize);
input = (const UC *) luaL_optlstring(L, 2, NULL, &isize);
/* if second part is nil, we are done */
if (!input) {
asize = qppad(atom, asize, &buffer);
@ -533,7 +526,7 @@ 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 *input = (const UC *) luaL_optlstring(L, 1, NULL, &isize);
const UC *last = input + isize;
luaL_Buffer buffer;
/* end-of-input blackhole */
@ -548,7 +541,7 @@ static int mime_global_unqp(lua_State *L)
luaL_buffinit(L, &buffer);
while (input < last)
asize = qpdecode(*input++, atom, asize, &buffer);
input = (UC *) luaL_optlstring(L, 2, NULL, &isize);
input = (const UC *) luaL_optlstring(L, 2, NULL, &isize);
/* if second part is nil, we are done */
if (!input) {
luaL_pushresult(&buffer);
@ -578,7 +571,7 @@ 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 *input = (const UC *) luaL_optlstring(L, 2, NULL, &size);
const UC *last = input + size;
int length = (int) luaL_optnumber(L, 3, 76);
luaL_Buffer buffer;
@ -661,7 +654,7 @@ static int eolprocess(int c, int last, const char *marker,
\*-------------------------------------------------------------------------*/
static int mime_global_eol(lua_State *L)
{
int ctx = luaL_checkint(L, 1);
int ctx = luaL_checkinteger(L, 1);
size_t isize = 0;
const char *input = luaL_optlstring(L, 2, NULL, &isize);
const char *last = input + isize;
@ -696,6 +689,7 @@ static size_t dot(int c, size_t state, luaL_Buffer *buffer)
case '.':
if (state == 2)
luaL_addchar(buffer, '.');
/* Falls through. */
default:
return 0;
}

View File

@ -37,7 +37,7 @@ int opt_meth_setoption(lua_State *L, p_opt opt, p_socket ps)
while (opt->name && strcmp(name, opt->name))
opt++;
if (!opt->func) {
char msg[45];
char msg[57];
sprintf(msg, "unsupported option `%.35s'", name);
luaL_argerror(L, 2, msg);
}
@ -50,7 +50,7 @@ int opt_meth_getoption(lua_State *L, p_opt opt, p_socket ps)
while (opt->name && strcmp(name, opt->name))
opt++;
if (!opt->func) {
char msg[45];
char msg[57];
sprintf(msg, "unsupported option `%.35s'", name);
luaL_argerror(L, 2, msg);
}
@ -105,11 +105,21 @@ int opt_set_dontroute(lua_State *L, p_socket ps)
return opt_setboolean(L, ps, SOL_SOCKET, SO_DONTROUTE);
}
int opt_get_dontroute(lua_State *L, p_socket ps)
{
return opt_getboolean(L, ps, SOL_SOCKET, SO_DONTROUTE);
}
int opt_set_broadcast(lua_State *L, p_socket ps)
{
return opt_setboolean(L, ps, SOL_SOCKET, SO_BROADCAST);
}
int opt_get_broadcast(lua_State *L, p_socket ps)
{
return opt_getboolean(L, ps, SOL_SOCKET, SO_BROADCAST);
}
int opt_set_ip6_unicast_hops(lua_State *L, p_socket ps)
{
return opt_setint(L, ps, IPPROTO_IPV6, IPV6_UNICAST_HOPS);

View File

@ -21,7 +21,6 @@ typedef t_opt *p_opt;
/* supported options for setoption */
int opt_set_dontroute(lua_State *L, p_socket ps);
int opt_set_broadcast(lua_State *L, p_socket ps);
int opt_set_reuseaddr(lua_State *L, p_socket ps);
int opt_set_tcp_nodelay(lua_State *L, p_socket ps);
int opt_set_keepalive(lua_State *L, p_socket ps);
int opt_set_linger(lua_State *L, p_socket ps);
@ -40,11 +39,13 @@ int opt_set_ip6_drop_membersip(lua_State *L, p_socket ps);
int opt_set_ip6_v6only(lua_State *L, p_socket ps);
/* supported options for getoption */
int opt_get_dontroute(lua_State *L, p_socket ps);
int opt_get_broadcast(lua_State *L, p_socket ps);
int opt_get_reuseaddr(lua_State *L, p_socket ps);
int opt_get_reuseport(lua_State *L, p_socket ps);
int opt_get_tcp_nodelay(lua_State *L, p_socket ps);
int opt_get_keepalive(lua_State *L, p_socket ps);
int opt_get_linger(lua_State *L, p_socket ps);
int opt_get_reuseaddr(lua_State *L, p_socket ps);
int opt_get_ip_multicast_loop(lua_State *L, p_socket ps);
int opt_get_ip_multicast_if(lua_State *L, p_socket ps);
int opt_get_error(lua_State *L, p_socket ps);
@ -52,6 +53,7 @@ int opt_get_ip6_multicast_loop(lua_State *L, p_socket ps);
int opt_get_ip6_multicast_hops(lua_State *L, p_socket ps);
int opt_get_ip6_unicast_hops(lua_State *L, p_socket ps);
int opt_get_ip6_v6only(lua_State *L, p_socket ps);
int opt_get_reuseport(lua_State *L, p_socket ps);
/* invokes the appropriate option handler */
int opt_meth_setoption(lua_State *L, p_opt opt, p_socket ps);

28
src/pierror.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef PIERROR_H
#define PIERROR_H
/*=========================================================================*\
* Error messages
* Defines platform independent error messages
\*=========================================================================*/
#define PIE_HOST_NOT_FOUND "host not found"
#define PIE_ADDRINUSE "address already in use"
#define PIE_ISCONN "already connected"
#define PIE_ACCESS "permission denied"
#define PIE_CONNREFUSED "connection refused"
#define PIE_CONNABORTED "closed"
#define PIE_CONNRESET "closed"
#define PIE_TIMEDOUT "timeout"
#define PIE_AGAIN "temporary failure in name resolution"
#define PIE_BADFLAGS "invalid value for ai_flags"
#define PIE_BADHINTS "invalid value for hints"
#define PIE_FAIL "non-recoverable failure in name resolution"
#define PIE_FAMILY "ai_family not supported"
#define PIE_MEMORY "memory allocation failure"
#define PIE_NONAME "host or service not provided, or not known"
#define PIE_OVERFLOW "argument buffer overflow"
#define PIE_PROTOCOL "resolved protocol is unknown"
#define PIE_SERVICE "service not supported for socket type"
#define PIE_SOCKTYPE "ai_socktype not supported"
#endif

View File

@ -6,6 +6,7 @@
#include "lua.h"
#include "lauxlib.h"
#include "compat.h"
#include "socket.h"
#include "timeout.h"
@ -38,13 +39,12 @@ static luaL_Reg func[] = {
\*-------------------------------------------------------------------------*/
int select_open(lua_State *L) {
lua_pushstring(L, "_SETSIZE");
lua_pushnumber(L, FD_SETSIZE);
lua_pushinteger(L, FD_SETSIZE);
lua_rawset(L, -3);
lua_pushstring(L, "_SOCKETINVALID");
lua_pushinteger(L, SOCKET_INVALID);
lua_rawset(L, -3);
#if LUA_VERSION_NUM > 501 && !defined(LUA_COMPAT_MODULE)
luaL_setfuncs(L, func, 0);
#else
luaL_openlib(L, NULL, func, 0);
#endif
return 0;
}

View File

@ -54,15 +54,6 @@ static luaL_Reg serial_methods[] = {
{NULL, NULL}
};
/* our socket creation function */
/* this is an ad-hoc module that returns a single function
* as such, do not include other functions in this array. */
static luaL_Reg func[] = {
{"serial", global_create},
{NULL, NULL}
};
/*-------------------------------------------------------------------------*\
* Initializes module
\*-------------------------------------------------------------------------*/
@ -71,14 +62,7 @@ LUASOCKET_API int luaopen_socket_serial(lua_State *L) {
auxiliar_newclass(L, "serial{client}", serial_methods);
/* create class groups */
auxiliar_add2group(L, "serial{client}", "serial{any}");
#if LUA_VERSION_NUM > 501 && !defined(LUA_COMPAT_MODULE)
lua_pushcfunction(L, global_create);
(void) func;
#else
/* set function into socket namespace */
luaL_openlib(L, "socket", func, 0);
lua_pushcfunction(L, global_create);
#endif
return 1;
}

View File

@ -32,7 +32,7 @@ function _M.bind(host, port, backlog)
err = "no info on address"
for i, alt in base.ipairs(addrinfo) do
if alt.family == "inet" then
sock, err = socket.tcp()
sock, err = socket.tcp4()
else
sock, err = socket.tcp6()
end

136
src/tcp.c
View File

@ -6,6 +6,7 @@
#include "lua.h"
#include "lauxlib.h"
#include "compat.h"
#include "auxiliar.h"
#include "socket.h"
@ -17,6 +18,7 @@
* Internal function prototypes
\*=========================================================================*/
static int global_create(lua_State *L);
static int global_create4(lua_State *L);
static int global_create6(lua_State *L);
static int global_connect(lua_State *L);
static int meth_connect(lua_State *L);
@ -34,6 +36,7 @@ static int meth_accept(lua_State *L);
static int meth_close(lua_State *L);
static int meth_getoption(lua_State *L);
static int meth_setoption(lua_State *L);
static int meth_gettimeout(lua_State *L);
static int meth_settimeout(lua_State *L);
static int meth_getfd(lua_State *L);
static int meth_setfd(lua_State *L);
@ -63,6 +66,7 @@ static luaL_Reg tcp_methods[] = {
{"setpeername", meth_connect},
{"setsockname", meth_bind},
{"settimeout", meth_settimeout},
{"gettimeout", meth_gettimeout},
{"shutdown", meth_shutdown},
{NULL, NULL}
};
@ -71,6 +75,7 @@ static luaL_Reg tcp_methods[] = {
static t_opt optget[] = {
{"keepalive", opt_get_keepalive},
{"reuseaddr", opt_get_reuseaddr},
{"reuseport", opt_get_reuseport},
{"tcp-nodelay", opt_get_tcp_nodelay},
{"linger", opt_get_linger},
{"error", opt_get_error},
@ -80,6 +85,7 @@ static t_opt optget[] = {
static t_opt optset[] = {
{"keepalive", opt_set_keepalive},
{"reuseaddr", opt_set_reuseaddr},
{"reuseport", opt_set_reuseport},
{"tcp-nodelay", opt_set_tcp_nodelay},
{"ipv6-v6only", opt_set_ip6_v6only},
{"linger", opt_set_linger},
@ -89,6 +95,7 @@ static t_opt optset[] = {
/* functions in library namespace */
static luaL_Reg func[] = {
{"tcp", global_create},
{"tcp4", global_create4},
{"tcp6", global_create6},
{"connect", global_connect},
{NULL, NULL}
@ -108,11 +115,7 @@ int tcp_open(lua_State *L)
auxiliar_add2group(L, "tcp{client}", "tcp{any}");
auxiliar_add2group(L, "tcp{server}", "tcp{any}");
/* define library functions */
#if LUA_VERSION_NUM > 501 && !defined(LUA_COMPAT_MODULE)
luaL_setfuncs(L, func, 0);
#else
luaL_openlib(L, NULL, func, 0);
#endif
return 0;
}
@ -216,8 +219,7 @@ static int meth_accept(lua_State *L)
/*-------------------------------------------------------------------------*\
* Binds an object to an address
\*-------------------------------------------------------------------------*/
static int meth_bind(lua_State *L)
{
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);
const char *port = luaL_checkstring(L, 3);
@ -227,7 +229,7 @@ static int meth_bind(lua_State *L)
bindhints.ai_socktype = SOCK_STREAM;
bindhints.ai_family = tcp->family;
bindhints.ai_flags = AI_PASSIVE;
err = inet_trybind(&tcp->sock, address, port, &bindhints);
err = inet_trybind(&tcp->sock, &tcp->family, address, port, &bindhints);
if (err) {
lua_pushnil(L);
lua_pushstring(L, err);
@ -240,8 +242,7 @@ static int meth_bind(lua_State *L)
/*-------------------------------------------------------------------------*\
* Turns a master tcp object into a client object.
\*-------------------------------------------------------------------------*/
static int meth_connect(lua_State *L)
{
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);
const char *port = luaL_checkstring(L, 3);
@ -282,9 +283,12 @@ static int meth_close(lua_State *L)
static int meth_getfamily(lua_State *L)
{
p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1);
if (tcp->family == PF_INET6) {
if (tcp->family == AF_INET6) {
lua_pushliteral(L, "inet6");
return 1;
} else if (tcp->family == AF_INET) {
lua_pushliteral(L, "inet4");
return 1;
} else {
lua_pushliteral(L, "inet4");
return 1;
@ -348,6 +352,12 @@ static int meth_settimeout(lua_State *L)
return timeout_meth_settimeout(L, &tcp->tm);
}
static int meth_gettimeout(lua_State *L)
{
p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1);
return timeout_meth_gettimeout(L, &tcp->tm);
}
/*=========================================================================*\
* Library functions
\*=========================================================================*/
@ -355,37 +365,36 @@ static int meth_settimeout(lua_State *L)
* Creates a master tcp object
\*-------------------------------------------------------------------------*/
static int tcp_create(lua_State *L, int family) {
t_socket sock;
const char *err = inet_trycreate(&sock, family, SOCK_STREAM);
/* try to allocate a system socket */
if (!err) {
/* allocate tcp object */
p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp));
memset(tcp, 0, sizeof(t_tcp));
/* set its type as master object */
auxiliar_setclass(L, "tcp{master}", -1);
/* initialize remaining structure fields */
socket_setnonblocking(&sock);
if (family == PF_INET6) {
int yes = 1;
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
(void *)&yes, sizeof(yes));
p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp));
memset(tcp, 0, sizeof(t_tcp));
/* set its type as master object */
auxiliar_setclass(L, "tcp{master}", -1);
/* if family is AF_UNSPEC, we leave the socket invalid and
* store AF_UNSPEC into family. This will allow it to later be
* replaced with an AF_INET6 or AF_INET socket upon first use. */
tcp->sock = SOCKET_INVALID;
tcp->family = family;
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);
if (family != AF_UNSPEC) {
const char *err = inet_trycreate(&tcp->sock, family, SOCK_STREAM, 0);
if (err != NULL) {
lua_pushnil(L);
lua_pushstring(L, err);
return 2;
}
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);
tcp->family = family;
return 1;
} else {
lua_pushnil(L);
lua_pushstring(L, err);
return 2;
socket_setnonblocking(&tcp->sock);
}
return 1;
}
static int global_create(lua_State *L) {
return tcp_create(L, AF_UNSPEC);
}
static int global_create4(lua_State *L) {
return tcp_create(L, AF_INET);
}
@ -393,53 +402,6 @@ static int global_create6(lua_State *L) {
return tcp_create(L, AF_INET6);
}
#if 0
static const char *tryconnect6(const char *remoteaddr, const char *remoteserv,
struct addrinfo *connecthints, p_tcp tcp) {
struct addrinfo *iterator = NULL, *resolved = NULL;
const char *err = NULL;
/* try resolving */
err = socket_gaistrerror(getaddrinfo(remoteaddr, remoteserv,
connecthints, &resolved));
if (err != NULL) {
if (resolved) freeaddrinfo(resolved);
return err;
}
/* iterate over all returned addresses trying to connect */
for (iterator = resolved; iterator; iterator = iterator->ai_next) {
p_timeout tm = timeout_markstart(&tcp->tm);
/* create new socket if necessary. if there was no
* bind, we need to create one for every new family
* that shows up while iterating. if there was a
* bind, all families will be the same and we will
* not enter this branch. */
if (tcp->family != iterator->ai_family) {
socket_destroy(&tcp->sock);
err = socket_strerror(socket_create(&tcp->sock,
iterator->ai_family, iterator->ai_socktype,
iterator->ai_protocol));
if (err != NULL) {
freeaddrinfo(resolved);
return err;
}
tcp->family = iterator->ai_family;
/* all sockets initially non-blocking */
socket_setnonblocking(&tcp->sock);
}
/* finally try connecting to remote address */
err = socket_strerror(socket_connect(&tcp->sock,
(SA *) iterator->ai_addr,
(socklen_t) iterator->ai_addrlen, tm));
/* if success, break out of loop */
if (err == NULL) break;
}
freeaddrinfo(resolved);
/* here, if err is set, we failed */
return err;
}
#endif
static int global_connect(lua_State *L) {
const char *remoteaddr = luaL_checkstring(L, 1);
const char *remoteserv = luaL_checkstring(L, 2);
@ -456,26 +418,26 @@ static int global_connect(lua_State *L) {
timeout_init(&tcp->tm, -1, -1);
buffer_init(&tcp->buf, &tcp->io, &tcp->tm);
tcp->sock = SOCKET_INVALID;
tcp->family = PF_UNSPEC;
tcp->family = AF_UNSPEC;
/* allow user to pick local address and port */
memset(&bindhints, 0, sizeof(bindhints));
bindhints.ai_socktype = SOCK_STREAM;
bindhints.ai_family = family;
bindhints.ai_flags = AI_PASSIVE;
if (localaddr) {
err = inet_trybind(&tcp->sock, localaddr, localserv, &bindhints);
err = inet_trybind(&tcp->sock, &tcp->family, localaddr,
localserv, &bindhints);
if (err) {
lua_pushnil(L);
lua_pushstring(L, err);
return 2;
}
tcp->family = bindhints.ai_family;
}
/* try to connect to remote address and port */
memset(&connecthints, 0, sizeof(connecthints));
connecthints.ai_socktype = SOCK_STREAM;
/* make sure we try to connect only to the same family */
connecthints.ai_family = bindhints.ai_family;
connecthints.ai_family = tcp->family;
err = inet_tryconnect(&tcp->sock, &tcp->family, remoteaddr, remoteserv,
&tcp->tm, &connecthints);
if (err) {

View File

@ -8,6 +8,7 @@
#include "lua.h"
#include "lauxlib.h"
#include "compat.h"
#include "auxiliar.h"
#include "timeout.h"
@ -144,11 +145,7 @@ double timeout_gettime(void) {
* Initializes module
\*-------------------------------------------------------------------------*/
int timeout_open(lua_State *L) {
#if LUA_VERSION_NUM > 501 && !defined(LUA_COMPAT_MODULE)
luaL_setfuncs(L, func, 0);
#else
luaL_openlib(L, NULL, func, 0);
#endif
return 0;
}
@ -176,6 +173,16 @@ int timeout_meth_settimeout(lua_State *L, p_timeout tm) {
return 1;
}
/*-------------------------------------------------------------------------*\
* Gets timeout values for IO operations
* Lua Output: block, total
\*-------------------------------------------------------------------------*/
int timeout_meth_gettimeout(lua_State *L, p_timeout tm) {
lua_pushnumber(L, tm->block);
lua_pushnumber(L, tm->total);
return 2;
}
/*=========================================================================*\
* Test support functions
\*=========================================================================*/

View File

@ -22,6 +22,7 @@ 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);
int timeout_meth_gettimeout(lua_State *L, p_timeout tm);
#define timeout_iszero(tm) ((tm)->block == 0.0)

View File

@ -46,6 +46,14 @@ end
-- metatable for sock object
local metat = { __index = {} }
function metat.__index:getpeername()
return self.c:getpeername()
end
function metat.__index:getsockname()
return self.c:getpeername()
end
function metat.__index:check(ok)
local code, reply = get_reply(self.c)
if not code then return nil, reply end
@ -74,7 +82,7 @@ function metat.__index:command(cmd, arg)
end
function metat.__index:sink(snk, pat)
local chunk, err = c:receive(pat)
local chunk, err = self.c:receive(pat)
return snk(chunk, err)
end

152
src/udp.c
View File

@ -7,6 +7,7 @@
#include "lua.h"
#include "lauxlib.h"
#include "compat.h"
#include "auxiliar.h"
#include "socket.h"
@ -26,6 +27,7 @@
* Internal function prototypes
\*=========================================================================*/
static int global_create(lua_State *L);
static int global_create4(lua_State *L);
static int global_create6(lua_State *L);
static int meth_send(lua_State *L);
static int meth_sendto(lua_State *L);
@ -34,6 +36,7 @@ static int meth_receivefrom(lua_State *L);
static int meth_getfamily(lua_State *L);
static int meth_getsockname(lua_State *L);
static int meth_getpeername(lua_State *L);
static int meth_gettimeout(lua_State *L);
static int meth_setsockname(lua_State *L);
static int meth_setpeername(lua_State *L);
static int meth_close(lua_State *L);
@ -64,6 +67,7 @@ static luaL_Reg udp_methods[] = {
{"setpeername", meth_setpeername},
{"setsockname", meth_setsockname},
{"settimeout", meth_settimeout},
{"gettimeout", meth_gettimeout},
{NULL, NULL}
};
@ -89,6 +93,10 @@ static t_opt optset[] = {
/* socket options for getoption */
static t_opt optget[] = {
{"dontroute", opt_get_dontroute},
{"broadcast", opt_get_broadcast},
{"reuseaddr", opt_get_reuseaddr},
{"reuseport", opt_get_reuseport},
{"ip-multicast-if", opt_get_ip_multicast_if},
{"ip-multicast-loop", opt_get_ip_multicast_loop},
{"error", opt_get_error},
@ -102,6 +110,7 @@ static t_opt optget[] = {
/* functions in library namespace */
static luaL_Reg func[] = {
{"udp", global_create},
{"udp4", global_create4},
{"udp6", global_create6},
{NULL, NULL}
};
@ -109,8 +118,7 @@ static luaL_Reg func[] = {
/*-------------------------------------------------------------------------*\
* Initializes module
\*-------------------------------------------------------------------------*/
int udp_open(lua_State *L)
{
int udp_open(lua_State *L) {
/* create classes */
auxiliar_newclass(L, "udp{connected}", udp_methods);
auxiliar_newclass(L, "udp{unconnected}", udp_methods);
@ -120,18 +128,18 @@ int udp_open(lua_State *L)
auxiliar_add2group(L, "udp{connected}", "select{able}");
auxiliar_add2group(L, "udp{unconnected}", "select{able}");
/* define library functions */
#if LUA_VERSION_NUM > 501 && !defined(LUA_COMPAT_MODULE)
luaL_setfuncs(L, func, 0);
#else
luaL_openlib(L, NULL, func, 0);
#endif
/* export default UDP size */
lua_pushliteral(L, "_DATAGRAMSIZE");
lua_pushinteger(L, UDP_DATAGRAMSIZE);
lua_rawset(L, -3);
return 0;
}
/*=========================================================================*\
* Lua methods
\*=========================================================================*/
const char *udp_strerror(int err) {
static 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";
@ -181,6 +189,27 @@ static int meth_sendto(lua_State *L) {
lua_pushstring(L, gai_strerror(err));
return 2;
}
/* create socket if on first sendto if AF_UNSPEC was set */
if (udp->family == AF_UNSPEC && udp->sock == SOCKET_INVALID) {
struct addrinfo *ap;
const char *errstr = NULL;
for (ap = ai; ap != NULL; ap = ap->ai_next) {
errstr = inet_trycreate(&udp->sock, ap->ai_family, SOCK_DGRAM, 0);
if (errstr == NULL) {
socket_setnonblocking(&udp->sock);
udp->family = ap->ai_family;
break;
}
}
if (errstr != NULL) {
lua_pushnil(L);
lua_pushstring(L, errstr);
freeaddrinfo(ai);
return 2;
}
}
timeout_markstart(tm);
err = socket_sendto(&udp->sock, data, count, &sent, ai->ai_addr,
(socklen_t) ai->ai_addrlen, tm);
@ -199,49 +228,57 @@ static int meth_sendto(lua_State *L) {
\*-------------------------------------------------------------------------*/
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));
char buf[UDP_DATAGRAMSIZE];
size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf));
char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf;
int err;
p_timeout tm = &udp->tm;
count = MIN(count, sizeof(buffer));
timeout_markstart(tm);
err = socket_recv(&udp->sock, buffer, count, &got, tm);
/* Unlike TCP, recv() of zero is not closed, but a zero-length packet. */
if (err == IO_CLOSED)
err = IO_DONE;
if (err != IO_DONE) {
if (!dgram) {
lua_pushnil(L);
lua_pushstring(L, udp_strerror(err));
lua_pushliteral(L, "out of memory");
return 2;
}
lua_pushlstring(L, buffer, got);
err = socket_recv(&udp->sock, dgram, wanted, &got, tm);
/* Unlike TCP, recv() of zero is not closed, but a zero-length packet. */
if (err != IO_DONE && err != IO_CLOSED) {
lua_pushnil(L);
lua_pushstring(L, udp_strerror(err));
if (wanted > sizeof(buf)) free(dgram);
return 2;
}
lua_pushlstring(L, dgram, got);
if (wanted > sizeof(buf)) free(dgram);
return 1;
}
/*-------------------------------------------------------------------------*\
* Receives data and sender from a UDP socket
\*-------------------------------------------------------------------------*/
static int meth_receivefrom(lua_State *L)
{
static int meth_receivefrom(lua_State *L) {
p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{unconnected}", 1);
char buffer[UDP_DATAGRAMSIZE];
size_t got, count = (size_t) luaL_optnumber(L, 2, sizeof(buffer));
int err;
p_timeout tm = &udp->tm;
char buf[UDP_DATAGRAMSIZE];
size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf));
char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf;
struct sockaddr_storage addr;
socklen_t addr_len = sizeof(addr);
char addrstr[INET6_ADDRSTRLEN];
char portstr[6];
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,
if (!dgram) {
lua_pushnil(L);
lua_pushliteral(L, "out of memory");
return 2;
}
err = socket_recvfrom(&udp->sock, dgram, wanted, &got, (SA *) &addr,
&addr_len, tm);
/* Unlike TCP, recv() of zero is not closed, but a zero-length packet. */
if (err == IO_CLOSED)
err = IO_DONE;
if (err != IO_DONE) {
if (err != IO_DONE && err != IO_CLOSED) {
lua_pushnil(L);
lua_pushstring(L, udp_strerror(err));
if (wanted > sizeof(buf)) free(dgram);
return 2;
}
err = getnameinfo((struct sockaddr *)&addr, addr_len, addrstr,
@ -249,21 +286,22 @@ static int meth_receivefrom(lua_State *L)
if (err) {
lua_pushnil(L);
lua_pushstring(L, gai_strerror(err));
if (wanted > sizeof(buf)) free(dgram);
return 2;
}
lua_pushlstring(L, buffer, got);
lua_pushlstring(L, dgram, got);
lua_pushstring(L, addrstr);
lua_pushinteger(L, (int) strtol(portstr, (char **) NULL, 10));
if (wanted > sizeof(buf)) free(dgram);
return 3;
}
/*-------------------------------------------------------------------------*\
* Returns family as string
\*-------------------------------------------------------------------------*/
static int meth_getfamily(lua_State *L)
{
static int meth_getfamily(lua_State *L) {
p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1);
if (udp->family == PF_INET6) {
if (udp->family == AF_INET6) {
lua_pushliteral(L, "inet6");
return 1;
} else {
@ -332,6 +370,11 @@ static int meth_settimeout(lua_State *L) {
return timeout_meth_settimeout(L, &udp->tm);
}
static int meth_gettimeout(lua_State *L) {
p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1);
return timeout_meth_gettimeout(L, &udp->tm);
}
/*-------------------------------------------------------------------------*\
* Turns a master udp object into a client object.
\*-------------------------------------------------------------------------*/
@ -362,7 +405,6 @@ static int meth_setpeername(lua_State *L) {
inet_trydisconnect(&udp->sock, udp->family, tm);
auxiliar_setclass(L, "udp{unconnected}", 1);
}
/* change class to connected or unconnected depending on address */
lua_pushnumber(L, 1);
return 1;
}
@ -390,7 +432,7 @@ static int meth_setsockname(lua_State *L) {
bindhints.ai_socktype = SOCK_DGRAM;
bindhints.ai_family = udp->family;
bindhints.ai_flags = AI_PASSIVE;
err = inet_trybind(&udp->sock, address, port, &bindhints);
err = inet_trybind(&udp->sock, &udp->family, address, port, &bindhints);
if (err) {
lua_pushnil(L);
lua_pushstring(L, err);
@ -407,32 +449,32 @@ static int meth_setsockname(lua_State *L) {
* Creates a master udp object
\*-------------------------------------------------------------------------*/
static int udp_create(lua_State *L, int family) {
t_socket sock;
const char *err = inet_trycreate(&sock, family, SOCK_DGRAM);
/* try to allocate a system socket */
if (!err) {
/* allocate udp 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);
if (family == PF_INET6) {
int yes = 1;
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
(void *)&yes, sizeof(yes));
/* allocate udp object */
p_udp udp = (p_udp) lua_newuserdata(L, sizeof(t_udp));
auxiliar_setclass(L, "udp{unconnected}", -1);
/* if family is AF_UNSPEC, we leave the socket invalid and
* store AF_UNSPEC into family. This will allow it to later be
* replaced with an AF_INET6 or AF_INET socket upon first use. */
udp->sock = SOCKET_INVALID;
timeout_init(&udp->tm, -1, -1);
udp->family = family;
if (family != AF_UNSPEC) {
const char *err = inet_trycreate(&udp->sock, family, SOCK_DGRAM, 0);
if (err != NULL) {
lua_pushnil(L);
lua_pushstring(L, err);
return 2;
}
udp->sock = sock;
timeout_init(&udp->tm, -1, -1);
udp->family = family;
return 1;
} else {
lua_pushnil(L);
lua_pushstring(L, err);
return 2;
socket_setnonblocking(&udp->sock);
}
return 1;
}
static int global_create(lua_State *L) {
return udp_create(L, AF_UNSPEC);
}
static int global_create4(lua_State *L) {
return udp_create(L, AF_INET);
}

View File

@ -17,7 +17,6 @@
#include "timeout.h"
#include "socket.h"
/* can't be larger than wsocket.c MAXCHUNK!!! */
#define UDP_DATAGRAMSIZE 8192
typedef struct t_udp_ {

View File

@ -2,345 +2,69 @@
* Unix domain socket
* LuaSocket toolkit
\*=========================================================================*/
#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>
#include "unixstream.h"
#include "unixdgram.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 unix_methods[] = {
{"__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}
/*-------------------------------------------------------------------------*\
* Modules and functions
\*-------------------------------------------------------------------------*/
static const luaL_Reg mod[] = {
{"stream", unixstream_open},
{"dgram", unixdgram_open},
{NULL, NULL}
};
/* socket option handlers */
static t_opt optset[] = {
{"keepalive", opt_set_keepalive},
{"reuseaddr", opt_set_reuseaddr},
{"linger", opt_set_linger},
{NULL, NULL}
};
static void add_alias(lua_State *L, int index, const char *name, const char *target)
{
lua_getfield(L, index, target);
lua_setfield(L, index, name);
}
/* our socket creation function */
/* this is an ad-hoc module that returns a single function
* as such, do not include other functions in this array. */
static luaL_Reg func[] = {
{"unix", global_create},
{NULL, NULL}
};
static int compat_socket_unix_call(lua_State *L)
{
/* Look up socket.unix.stream in the socket.unix table (which is the first
* argument). */
lua_getfield(L, 1, "stream");
/* Replace the stack entry for the socket.unix table with the
* socket.unix.stream function. */
lua_replace(L, 1);
/* Call socket.unix.stream, passing along any arguments. */
int n = lua_gettop(L);
lua_call(L, n-1, LUA_MULTRET);
/* Pass along the return values from socket.unix.stream. */
n = lua_gettop(L);
return n;
}
/*-------------------------------------------------------------------------*\
* Initializes module
\*-------------------------------------------------------------------------*/
int luaopen_socket_unix(lua_State *L) {
/* create classes */
auxiliar_newclass(L, "unix{master}", unix_methods);
auxiliar_newclass(L, "unix{client}", unix_methods);
auxiliar_newclass(L, "unix{server}", unix_methods);
/* create class groups */
auxiliar_add2group(L, "unix{master}", "unix{any}");
auxiliar_add2group(L, "unix{client}", "unix{any}");
auxiliar_add2group(L, "unix{server}", "unix{any}");
#if LUA_VERSION_NUM > 501 && !defined(LUA_COMPAT_MODULE)
lua_pushcfunction(L, global_create);
(void) func;
#else
/* set function into socket namespace */
luaL_openlib(L, "socket", func, 0);
lua_pushcfunction(L, global_create);
#endif
/* return the function instead of the 'socket' table */
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, optset, &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)
int luaopen_socket_unix(lua_State *L)
{
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);
}
int i;
lua_newtable(L);
int socket_unix_table = lua_gettop(L);
for (i = 0; mod[i].name; i++)
mod[i].func(L);
/* Add backwards compatibility aliases "tcp" and "udp" for the "stream" and
* "dgram" functions. */
add_alias(L, socket_unix_table, "tcp", "stream");
add_alias(L, socket_unix_table, "udp", "dgram");
/* Add a backwards compatibility function and a metatable setup to call it
* for the old socket.unix() interface. */
lua_pushcfunction(L, compat_socket_unix_call);
lua_setfield(L, socket_unix_table, "__call");
lua_pushvalue(L, socket_unix_table);
lua_setmetatable(L, socket_unix_table);
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)
{
/* SHUT_RD, SHUT_WR, SHUT_RDWR have the value 0, 1, 2, so we can use method index directly */
static const char* methods[] = { "receive", "send", "both", NULL };
p_unix tcp = (p_unix) auxiliar_checkclass(L, "unix{client}", 1);
int how = luaL_checkoption(L, 2, "both", methods);
socket_shutdown(&tcp->sock, how);
lua_pushnumber(L, 1);
return 1;
}
/*-------------------------------------------------------------------------*\
* 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;
}
}

408
src/unixdgram.c Normal file
View File

@ -0,0 +1,408 @@
/*=========================================================================*\
* Unix domain socket dgram submodule
* LuaSocket toolkit
\*=========================================================================*/
#include <string.h>
#include <stdlib.h>
#include "lua.h"
#include "lauxlib.h"
#include "compat.h"
#include "auxiliar.h"
#include "socket.h"
#include "options.h"
#include "unix.h"
#include <sys/un.h>
#define UNIXDGRAM_DATAGRAMSIZE 8192
/*=========================================================================*\
* Internal function prototypes
\*=========================================================================*/
static int global_create(lua_State *L);
static int meth_connect(lua_State *L);
static int meth_bind(lua_State *L);
static int meth_send(lua_State *L);
static int meth_receive(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_gettimeout(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_receivefrom(lua_State *L);
static int meth_sendto(lua_State *L);
static int meth_getsockname(lua_State *L);
static const char *unixdgram_tryconnect(p_unix un, const char *path);
static const char *unixdgram_trybind(p_unix un, const char *path);
/* unixdgram object methods */
static luaL_Reg unixdgram_methods[] = {
{"__gc", meth_close},
{"__tostring", auxiliar_tostring},
{"bind", meth_bind},
{"close", meth_close},
{"connect", meth_connect},
{"dirty", meth_dirty},
{"getfd", meth_getfd},
{"send", meth_send},
{"sendto", meth_sendto},
{"receive", meth_receive},
{"receivefrom", meth_receivefrom},
{"setfd", meth_setfd},
{"setoption", meth_setoption},
{"setpeername", meth_connect},
{"setsockname", meth_bind},
{"getsockname", meth_getsockname},
{"settimeout", meth_settimeout},
{"gettimeout", meth_gettimeout},
{NULL, NULL}
};
/* socket option handlers */
static t_opt optset[] = {
{"reuseaddr", opt_set_reuseaddr},
{NULL, NULL}
};
/* functions in library namespace */
static luaL_Reg func[] = {
{"dgram", global_create},
{NULL, NULL}
};
/*-------------------------------------------------------------------------*\
* Initializes module
\*-------------------------------------------------------------------------*/
int unixdgram_open(lua_State *L)
{
/* create classes */
auxiliar_newclass(L, "unixdgram{connected}", unixdgram_methods);
auxiliar_newclass(L, "unixdgram{unconnected}", unixdgram_methods);
/* create class groups */
auxiliar_add2group(L, "unixdgram{connected}", "unixdgram{any}");
auxiliar_add2group(L, "unixdgram{unconnected}", "unixdgram{any}");
auxiliar_add2group(L, "unixdgram{connected}", "select{able}");
auxiliar_add2group(L, "unixdgram{unconnected}", "select{able}");
luaL_setfuncs(L, func, 0);
return 0;
}
/*=========================================================================*\
* Lua methods
\*=========================================================================*/
static const char *unixdgram_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);
}
static int meth_send(lua_State *L)
{
p_unix un = (p_unix) auxiliar_checkclass(L, "unixdgram{connected}", 1);
p_timeout tm = &un->tm;
size_t count, sent = 0;
int err;
const char *data = luaL_checklstring(L, 2, &count);
timeout_markstart(tm);
err = socket_send(&un->sock, data, count, &sent, tm);
if (err != IO_DONE) {
lua_pushnil(L);
lua_pushstring(L, unixdgram_strerror(err));
return 2;
}
lua_pushnumber(L, (lua_Number) sent);
return 1;
}
/*-------------------------------------------------------------------------*\
* Send data through unconnected unixdgram socket
\*-------------------------------------------------------------------------*/
static int meth_sendto(lua_State *L)
{
p_unix un = (p_unix) auxiliar_checkclass(L, "unixdgram{unconnected}", 1);
size_t count, sent = 0;
const char *data = luaL_checklstring(L, 2, &count);
const char *path = luaL_checkstring(L, 3);
p_timeout tm = &un->tm;
int err;
struct sockaddr_un remote;
size_t len = strlen(path);
if (len >= sizeof(remote.sun_path)) {
lua_pushnil(L);
lua_pushstring(L, "path too long");
return 2;
}
memset(&remote, 0, sizeof(remote));
strcpy(remote.sun_path, path);
remote.sun_family = AF_UNIX;
timeout_markstart(tm);
#ifdef UNIX_HAS_SUN_LEN
remote.sun_len = sizeof(remote.sun_family) + sizeof(remote.sun_len)
+ len + 1;
err = socket_sendto(&un->sock, data, count, &sent, (SA *) &remote, remote.sun_len, tm);
#else
err = socket_sendto(&un->sock, data, count, &sent, (SA *) &remote,
sizeof(remote.sun_family) + len, tm);
#endif
if (err != IO_DONE) {
lua_pushnil(L);
lua_pushstring(L, unixdgram_strerror(err));
return 2;
}
lua_pushnumber(L, (lua_Number) sent);
return 1;
}
static int meth_receive(lua_State *L) {
p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1);
char buf[UNIXDGRAM_DATAGRAMSIZE];
size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf));
char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf;
int err;
p_timeout tm = &un->tm;
timeout_markstart(tm);
if (!dgram) {
lua_pushnil(L);
lua_pushliteral(L, "out of memory");
return 2;
}
err = socket_recv(&un->sock, dgram, wanted, &got, tm);
/* Unlike STREAM, recv() of zero is not closed, but a zero-length packet. */
if (err != IO_DONE && err != IO_CLOSED) {
lua_pushnil(L);
lua_pushstring(L, unixdgram_strerror(err));
if (wanted > sizeof(buf)) free(dgram);
return 2;
}
lua_pushlstring(L, dgram, got);
if (wanted > sizeof(buf)) free(dgram);
return 1;
}
/*-------------------------------------------------------------------------*\
* Receives data and sender from a DGRAM socket
\*-------------------------------------------------------------------------*/
static int meth_receivefrom(lua_State *L) {
p_unix un = (p_unix) auxiliar_checkclass(L, "unixdgram{unconnected}", 1);
char buf[UNIXDGRAM_DATAGRAMSIZE];
size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf));
char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf;
struct sockaddr_un addr;
socklen_t addr_len = sizeof(addr);
int err;
p_timeout tm = &un->tm;
timeout_markstart(tm);
if (!dgram) {
lua_pushnil(L);
lua_pushliteral(L, "out of memory");
return 2;
}
addr.sun_path[0] = '\0';
err = socket_recvfrom(&un->sock, dgram, wanted, &got, (SA *) &addr,
&addr_len, tm);
/* Unlike STREAM, recv() of zero is not closed, but a zero-length packet. */
if (err != IO_DONE && err != IO_CLOSED) {
lua_pushnil(L);
lua_pushstring(L, unixdgram_strerror(err));
if (wanted > sizeof(buf)) free(dgram);
return 2;
}
lua_pushlstring(L, dgram, got);
/* the path may be empty, when client send without bind */
lua_pushstring(L, addr.sun_path);
if (wanted > sizeof(buf)) free(dgram);
return 2;
}
/*-------------------------------------------------------------------------*\
* Just call option handler
\*-------------------------------------------------------------------------*/
static int meth_setoption(lua_State *L) {
p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1);
return opt_meth_setoption(L, optset, &un->sock);
}
/*-------------------------------------------------------------------------*\
* Select support methods
\*-------------------------------------------------------------------------*/
static int meth_getfd(lua_State *L) {
p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{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, "unixdgram{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, "unixdgram{any}", 1);
(void) un;
lua_pushboolean(L, 0);
return 1;
}
/*-------------------------------------------------------------------------*\
* Binds an object to an address
\*-------------------------------------------------------------------------*/
static const char *unixdgram_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, "unixdgram{unconnected}", 1);
const char *path = luaL_checkstring(L, 2);
const char *err = unixdgram_trybind(un, path);
if (err) {
lua_pushnil(L);
lua_pushstring(L, err);
return 2;
}
lua_pushnumber(L, 1);
return 1;
}
static int meth_getsockname(lua_State *L)
{
p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1);
struct sockaddr_un peer = {0};
socklen_t peer_len = sizeof(peer);
if (getsockname(un->sock, (SA *) &peer, &peer_len) < 0) {
lua_pushnil(L);
lua_pushstring(L, socket_strerror(errno));
return 2;
}
lua_pushstring(L, peer.sun_path);
return 1;
}
/*-------------------------------------------------------------------------*\
* Turns a master unixdgram object into a client object.
\*-------------------------------------------------------------------------*/
static const char *unixdgram_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_checkgroup(L, "unixdgram{any}", 1);
const char *path = luaL_checkstring(L, 2);
const char *err = unixdgram_tryconnect(un, path);
if (err) {
lua_pushnil(L);
lua_pushstring(L, err);
return 2;
}
/* turn unconnected object into a connected object */
auxiliar_setclass(L, "unixdgram{connected}", 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, "unixdgram{any}", 1);
socket_destroy(&un->sock);
lua_pushnumber(L, 1);
return 1;
}
/*-------------------------------------------------------------------------*\
* Just call tm methods
\*-------------------------------------------------------------------------*/
static int meth_settimeout(lua_State *L)
{
p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1);
return timeout_meth_settimeout(L, &un->tm);
}
static int meth_gettimeout(lua_State *L)
{
p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1);
return timeout_meth_gettimeout(L, &un->tm);
}
/*=========================================================================*\
* Library functions
\*=========================================================================*/
/*-------------------------------------------------------------------------*\
* Creates a master unixdgram object
\*-------------------------------------------------------------------------*/
static int global_create(lua_State *L)
{
t_socket sock;
int err = socket_create(&sock, AF_UNIX, SOCK_DGRAM, 0);
/* try to allocate a system socket */
if (err == IO_DONE) {
/* allocate unixdgram object */
p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix));
/* set its type as master object */
auxiliar_setclass(L, "unixdgram{unconnected}", -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;
}
}

20
src/unixdgram.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef UNIXDGRAM_H
#define UNIXDGRAM_H
/*=========================================================================*\
* DGRAM object
* LuaSocket toolkit
*
* The dgram.h module provides LuaSocket with support for DGRAM protocol
* (AF_INET, SOCK_DGRAM).
*
* Two classes are defined: connected and unconnected. DGRAM 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.
\*=========================================================================*/
#include "unix.h"
int unixdgram_open(lua_State *L);
#endif /* UNIXDGRAM_H */

357
src/unixstream.c Normal file
View File

@ -0,0 +1,357 @@
/*=========================================================================*\
* Unix domain socket stream sub module
* LuaSocket toolkit
\*=========================================================================*/
#include <string.h>
#include "lua.h"
#include "lauxlib.h"
#include "compat.h"
#include "auxiliar.h"
#include "socket.h"
#include "options.h"
#include "unixstream.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 int meth_getsockname(lua_State *L);
static const char *unixstream_tryconnect(p_unix un, const char *path);
static const char *unixstream_trybind(p_unix un, const char *path);
/* unixstream object methods */
static luaL_Reg unixstream_methods[] = {
{"__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},
{"getsockname", meth_getsockname},
{"settimeout", meth_settimeout},
{"shutdown", meth_shutdown},
{NULL, NULL}
};
/* socket option handlers */
static t_opt optset[] = {
{"keepalive", opt_set_keepalive},
{"reuseaddr", opt_set_reuseaddr},
{"linger", opt_set_linger},
{NULL, NULL}
};
/* functions in library namespace */
static luaL_Reg func[] = {
{"stream", global_create},
{NULL, NULL}
};
/*-------------------------------------------------------------------------*\
* Initializes module
\*-------------------------------------------------------------------------*/
int unixstream_open(lua_State *L)
{
/* create classes */
auxiliar_newclass(L, "unixstream{master}", unixstream_methods);
auxiliar_newclass(L, "unixstream{client}", unixstream_methods);
auxiliar_newclass(L, "unixstream{server}", unixstream_methods);
/* create class groups */
auxiliar_add2group(L, "unixstream{master}", "unixstream{any}");
auxiliar_add2group(L, "unixstream{client}", "unixstream{any}");
auxiliar_add2group(L, "unixstream{server}", "unixstream{any}");
luaL_setfuncs(L, func, 0);
return 0;
}
/*=========================================================================*\
* Lua methods
\*=========================================================================*/
/*-------------------------------------------------------------------------*\
* Just call buffered IO methods
\*-------------------------------------------------------------------------*/
static int meth_send(lua_State *L) {
p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1);
return buffer_meth_send(L, &un->buf);
}
static int meth_receive(lua_State *L) {
p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1);
return buffer_meth_receive(L, &un->buf);
}
static int meth_getstats(lua_State *L) {
p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1);
return buffer_meth_getstats(L, &un->buf);
}
static int meth_setstats(lua_State *L) {
p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{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, "unixstream{any}", 1);
return opt_meth_setoption(L, optset, &un->sock);
}
/*-------------------------------------------------------------------------*\
* Select support methods
\*-------------------------------------------------------------------------*/
static int meth_getfd(lua_State *L) {
p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{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, "unixstream{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, "unixstream{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, "unixstream{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, "unixstream{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 *unixstream_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, "unixstream{master}", 1);
const char *path = luaL_checkstring(L, 2);
const char *err = unixstream_trybind(un, path);
if (err) {
lua_pushnil(L);
lua_pushstring(L, err);
return 2;
}
lua_pushnumber(L, 1);
return 1;
}
static int meth_getsockname(lua_State *L)
{
p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1);
struct sockaddr_un peer = {0};
socklen_t peer_len = sizeof(peer);
if (getsockname(un->sock, (SA *) &peer, &peer_len) < 0) {
lua_pushnil(L);
lua_pushstring(L, socket_strerror(errno));
return 2;
}
lua_pushstring(L, peer.sun_path);
return 1;
}
/*-------------------------------------------------------------------------*\
* Turns a master unixstream object into a client object.
\*-------------------------------------------------------------------------*/
static const char *unixstream_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, "unixstream{master}", 1);
const char *path = luaL_checkstring(L, 2);
const char *err = unixstream_tryconnect(un, path);
if (err) {
lua_pushnil(L);
lua_pushstring(L, err);
return 2;
}
/* turn master object into a client object */
auxiliar_setclass(L, "unixstream{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, "unixstream{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, "unixstream{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, "unixstream{server}", 1);
lua_pushnumber(L, 1);
return 1;
}
/*-------------------------------------------------------------------------*\
* Shuts the connection down partially
\*-------------------------------------------------------------------------*/
static int meth_shutdown(lua_State *L)
{
/* SHUT_RD, SHUT_WR, SHUT_RDWR have the value 0, 1, 2, so we can use method index directly */
static const char* methods[] = { "receive", "send", "both", NULL };
p_unix stream = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1);
int how = luaL_checkoption(L, 2, "both", methods);
socket_shutdown(&stream->sock, how);
lua_pushnumber(L, 1);
return 1;
}
/*-------------------------------------------------------------------------*\
* Just call tm methods
\*-------------------------------------------------------------------------*/
static int meth_settimeout(lua_State *L) {
p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1);
return timeout_meth_settimeout(L, &un->tm);
}
/*=========================================================================*\
* Library functions
\*=========================================================================*/
/*-------------------------------------------------------------------------*\
* Creates a master unixstream 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 unixstream object */
p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix));
/* set its type as master object */
auxiliar_setclass(L, "unixstream{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;
}
}

21
src/unixstream.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef UNIXSTREAM_H
#define UNIXSTREAM_H
/*=========================================================================*\
* UNIX STREAM object
* LuaSocket toolkit
*
* The unixstream.h module is basicly a glue that puts together modules buffer.h,
* timeout.h socket.h and inet.h to provide the LuaSocket UNIX STREAM (AF_UNIX,
* SOCK_STREAM) support.
*
* Three classes are defined: master, client and server. The master class is
* a newly created unixstream object, that has not been bound or connected. Server
* objects are unixstream objects bound to some local address. Client objects are
* unixstream objects either connected to some address or returned by the accept
* method of a server object.
\*=========================================================================*/
#include "unix.h"
int unixstream_open(lua_State *L);
#endif /* UNIXSTREAM_H */

View File

@ -49,7 +49,7 @@ local function make_set(t)
return s
end
-- these are allowed withing a path segment, along with alphanum
-- these are allowed within a path segment, along with alphanum
-- other characters must be escaped
local segment_set = make_set {
"-", "_", ".", "!", "~", "*", "'", "(",
@ -59,16 +59,16 @@ 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
else return string.format("%%%02X", string.byte(c)) end
end)
end
-----------------------------------------------------------------------------
-- Encodes a string into its escaped hexadecimal representation
-- Unencodes a escaped hexadecimal string into its binary representation
-- Input
-- s: binary string to be encoded
-- s: escaped hexadecimal string to be unencoded
-- Returns
-- escaped representation of string binary
-- unescaped binary representation of escaped hexadecimal binary
-----------------------------------------------------------------------------
function _M.unescape(s)
return (string.gsub(s, "%%(%x%x)", function(hex)
@ -131,11 +131,6 @@ function _M.parse(url, default)
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)
@ -144,6 +139,11 @@ function _M.parse(url, default)
parsed.authority = n
return ""
end)
-- get fragment
url = string.gsub(url, "#(.*)$", function(f)
parsed.fragment = f
return ""
end)
-- get query string
url = string.gsub(url, "%?(.*)", function(q)
parsed.query = q
@ -183,8 +183,9 @@ end
-- a stringing with the corresponding URL
-----------------------------------------------------------------------------
function _M.build(parsed)
local ppath = _M.parse_path(parsed.path or "")
local url = _M.build_path(ppath)
--local ppath = _M.parse_path(parsed.path or "")
--local url = _M.build_path(ppath)
local url = parsed.path or ""
if parsed.params then url = url .. ";" .. parsed.params end
if parsed.query then url = url .. "?" .. parsed.query end
local authority = parsed.authority
@ -193,7 +194,7 @@ function _M.build(parsed)
if string.find(authority, ":") then -- IPv6?
authority = "[" .. authority .. "]"
end
if parsed.port then authority = authority .. ":" .. parsed.port end
if parsed.port then authority = authority .. ":" .. base.tostring(parsed.port) end
local userinfo = parsed.userinfo
if parsed.user then
userinfo = parsed.user
@ -219,6 +220,7 @@ end
-- corresponding absolute url
-----------------------------------------------------------------------------
function _M.absolute(base_url, relative_url)
local base_parsed
if base.type(base_url) == "table" then
base_parsed = base_url
base_url = _M.build(base_parsed)

View File

@ -10,6 +10,7 @@
#include <signal.h>
#include "socket.h"
#include "pierror.h"
/*-------------------------------------------------------------------------*\
* Wait for readable/writable/connected socket with timeout
@ -75,7 +76,7 @@ int socket_waitfd(p_socket ps, int sw, p_timeout tm) {
* Initializes module
\*-------------------------------------------------------------------------*/
int socket_open(void) {
/* instals a handler to ignore sigpipe or it will crash us */
/* installs a handler to ignore sigpipe or it will crash us */
signal(SIGPIPE, SIG_IGN);
return 1;
}
@ -92,7 +93,6 @@ int socket_close(void) {
\*-------------------------------------------------------------------------*/
void socket_destroy(p_socket ps) {
if (*ps != SOCKET_INVALID) {
socket_setblocking(ps);
close(*ps);
*ps = SOCKET_INVALID;
}
@ -140,9 +140,7 @@ int socket_bind(p_socket ps, SA *addr, socklen_t len) {
\*-------------------------------------------------------------------------*/
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;
}
@ -150,9 +148,7 @@ int socket_listen(p_socket ps, int backlog) {
*
\*-------------------------------------------------------------------------*/
void socket_shutdown(p_socket ps, int how) {
socket_setblocking(ps);
shutdown(*ps, how);
socket_setnonblocking(ps);
}
/*-------------------------------------------------------------------------*\
@ -215,6 +211,8 @@ int socket_send(p_socket ps, const char *data, size_t count,
err = errno;
/* EPIPE means the connection was closed */
if (err == EPIPE) return IO_CLOSED;
/* EPROTOTYPE means the connection is being closed (on Yosemite!)*/
if (err == EPROTOTYPE) continue;
/* we call was interrupted, just try again */
if (err == EINTR) continue;
/* if failed fatal reason, report error */
@ -243,6 +241,7 @@ int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent,
}
err = errno;
if (err == EPIPE) return IO_CLOSED;
if (err == EPROTOTYPE) continue;
if (err == EINTR) continue;
if (err != EAGAIN) return err;
if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err;
@ -321,6 +320,8 @@ int socket_write(p_socket ps, const char *data, size_t count,
err = errno;
/* EPIPE means the connection was closed */
if (err == EPIPE) return IO_CLOSED;
/* EPROTOTYPE means the connection is being closed (on Yosemite!)*/
if (err == EPROTOTYPE) continue;
/* we call was interrupted, just try again */
if (err == EINTR) continue;
/* if failed fatal reason, report error */
@ -399,7 +400,7 @@ int socket_gethostbyname(const char *addr, struct hostent **hp) {
const char *socket_hoststrerror(int err) {
if (err <= 0) return io_strerror(err);
switch (err) {
case HOST_NOT_FOUND: return "host not found";
case HOST_NOT_FOUND: return PIE_HOST_NOT_FOUND;
default: return hstrerror(err);
}
}
@ -407,14 +408,16 @@ const char *socket_hoststrerror(int 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(err);
case EADDRINUSE: return PIE_ADDRINUSE;
case EISCONN: return PIE_ISCONN;
case EACCES: return PIE_ACCESS;
case ECONNREFUSED: return PIE_CONNREFUSED;
case ECONNABORTED: return PIE_CONNABORTED;
case ECONNRESET: return PIE_CONNRESET;
case ETIMEDOUT: return PIE_TIMEDOUT;
default: {
return strerror(err);
}
}
}
@ -426,22 +429,21 @@ const char *socket_ioerror(p_socket ps, int err) {
const char *socket_gaistrerror(int err) {
if (err == 0) return NULL;
switch (err) {
case EAI_AGAIN: return "temporary failure in name resolution";
case EAI_BADFLAGS: return "invalid value for ai_flags";
case EAI_AGAIN: return PIE_AGAIN;
case EAI_BADFLAGS: return PIE_BADFLAGS;
#ifdef EAI_BADHINTS
case EAI_BADHINTS: return "invalid value for hints";
case EAI_BADHINTS: return PIE_BADHINTS;
#endif
case EAI_FAIL: return "non-recoverable failure in name resolution";
case EAI_FAMILY: return "ai_family not supported";
case EAI_MEMORY: return "memory allocation failure";
case EAI_NONAME:
return "host or service not provided, or not known";
case EAI_OVERFLOW: return "argument buffer overflow";
case EAI_FAIL: return PIE_FAIL;
case EAI_FAMILY: return PIE_FAMILY;
case EAI_MEMORY: return PIE_MEMORY;
case EAI_NONAME: return PIE_NONAME;
case EAI_OVERFLOW: return PIE_OVERFLOW;
#ifdef EAI_PROTOCOL
case EAI_PROTOCOL: return "resolved protocol is unknown";
case EAI_PROTOCOL: return PIE_PROTOCOL;
#endif
case EAI_SERVICE: return "service not supported for socket type";
case EAI_SOCKTYPE: return "ai_socktype not supported";
case EAI_SERVICE: return PIE_SERVICE;
case EAI_SOCKTYPE: return PIE_SOCKTYPE;
case EAI_SYSTEM: return strerror(errno);
default: return gai_strerror(err);
}

View File

@ -8,6 +8,7 @@
#include <string.h>
#include "socket.h"
#include "pierror.h"
/* WinSock doesn't have a strerror... */
static const char *wstrerror(int err);
@ -330,7 +331,7 @@ int socket_gethostbyname(const char *addr, struct hostent **hp) {
const char *socket_hoststrerror(int err) {
if (err <= 0) return io_strerror(err);
switch (err) {
case WSAHOST_NOT_FOUND: return "host not found";
case WSAHOST_NOT_FOUND: return PIE_HOST_NOT_FOUND;
default: return wstrerror(err);
}
}
@ -338,13 +339,13 @@ const char *socket_hoststrerror(int 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";
case WSAEADDRINUSE: return PIE_ADDRINUSE;
case WSAECONNREFUSED : return PIE_CONNREFUSED;
case WSAEISCONN: return PIE_ISCONN;
case WSAEACCES: return PIE_ACCESS;
case WSAECONNABORTED: return PIE_CONNABORTED;
case WSAECONNRESET: return PIE_CONNRESET;
case WSAETIMEDOUT: return PIE_TIMEDOUT;
default: return wstrerror(err);
}
}
@ -357,7 +358,7 @@ const char *socket_ioerror(p_socket ps, int err) {
static const char *wstrerror(int err) {
switch (err) {
case WSAEINTR: return "Interrupted function call";
case WSAEACCES: return "Permission denied";
case WSAEACCES: return PIE_ACCESS; // "Permission denied";
case WSAEFAULT: return "Bad address";
case WSAEINVAL: return "Invalid argument";
case WSAEMFILE: return "Too many open files";
@ -370,24 +371,23 @@ static const char *wstrerror(int err) {
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 WSAESOCKTNOSUPPORT: return PIE_SOCKTYPE; // "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 WSAEAFNOSUPPORT: return PIE_FAMILY; // "Address family not supported by protocol family";
case WSAEADDRINUSE: return PIE_ADDRINUSE; // "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 WSAECONNRESET: return PIE_CONNRESET; // "Connection reset by peer";
case WSAENOBUFS: return "No buffer space available";
case WSAEISCONN: return "Socket is already connected";
case WSAEISCONN: return PIE_ISCONN; // "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 WSAETIMEDOUT: return PIE_TIMEDOUT; // "Connection timed out";
case WSAECONNREFUSED: return PIE_CONNREFUSED; // "Connection refused";
case WSAEHOSTDOWN: return "Host is down";
case WSAEHOSTUNREACH: return "No route to host";
case WSAEPROCLIM: return "Too many processes";
@ -396,9 +396,9 @@ static const char *wstrerror(int err) {
case WSANOTINITIALISED:
return "Successful WSAStartup not yet performed";
case WSAEDISCON: return "Graceful shutdown in progress";
case WSAHOST_NOT_FOUND: return "Host not found";
case WSAHOST_NOT_FOUND: return PIE_HOST_NOT_FOUND; // "Host not found";
case WSATRY_AGAIN: return "Nonauthoritative host not found";
case WSANO_RECOVERY: return "Nonrecoverable name lookup error";
case WSANO_RECOVERY: return PIE_FAIL; // "Nonrecoverable name lookup error";
case WSANO_DATA: return "Valid name, no data record of requested type";
default: return "Unknown error";
}
@ -407,24 +407,23 @@ static const char *wstrerror(int err) {
const char *socket_gaistrerror(int err) {
if (err == 0) return NULL;
switch (err) {
case EAI_AGAIN: return "temporary failure in name resolution";
case EAI_BADFLAGS: return "invalid value for ai_flags";
case EAI_AGAIN: return PIE_AGAIN;
case EAI_BADFLAGS: return PIE_BADFLAGS;
#ifdef EAI_BADHINTS
case EAI_BADHINTS: return "invalid value for hints";
case EAI_BADHINTS: return PIE_BADHINTS;
#endif
case EAI_FAIL: return "non-recoverable failure in name resolution";
case EAI_FAMILY: return "ai_family not supported";
case EAI_MEMORY: return "memory allocation failure";
case EAI_NONAME:
return "host or service not provided, or not known";
case EAI_FAIL: return PIE_FAIL;
case EAI_FAMILY: return PIE_FAMILY;
case EAI_MEMORY: return PIE_MEMORY;
case EAI_NONAME: return PIE_NONAME;
#ifdef EAI_OVERFLOW
case EAI_OVERFLOW: return "argument buffer overflow";
case EAI_OVERFLOW: return PIE_OVERFLOW;
#endif
#ifdef EAI_PROTOCOL
case EAI_PROTOCOL: return "resolved protocol is unknown";
case EAI_PROTOCOL: return PIE_PROTOCOL;
#endif
case EAI_SERVICE: return "service not supported for socket type";
case EAI_SOCKTYPE: return "ai_socktype not supported";
case EAI_SERVICE: return PIE_SERVICE;
case EAI_SOCKTYPE: return PIE_SOCKTYPE;
#ifdef EAI_SYSTEM
case EAI_SYSTEM: return strerror(errno);
#endif

4
test/auth/.htaccess Normal file
View File

@ -0,0 +1,4 @@
AuthName "test-auth"
AuthType Basic
AuthUserFile /home/diego/impa/luasocket/test/auth/.htpasswd
Require valid-user

View File

@ -1 +1 @@
luasocket:l8n2npozPB.sQ
luasocket:$apr1$47u2O.Me$.m/5BWAtt7GVoxsouIPBR1

View File

@ -1,6 +1,30 @@
local socket = require("socket")
try = socket.newtry(function()
print("finalized!!!")
local finalizer_called
local func = socket.protect(function(err, ...)
local try = socket.newtry(function()
finalizer_called = true
end)
if err then
return error(err, 0)
else
return try(...)
end
end)
try = socket.protect(try)
print(try(nil, "it works"))
local ret1, ret2, ret3 = func(false, 1, 2, 3)
assert(not finalizer_called, "unexpected finalizer call")
assert(ret1 == 1 and ret2 == 2 and ret3 == 3, "incorrect return values")
ret1, ret2, ret3 = func(false, false, "error message")
assert(finalizer_called, "finalizer not called")
assert(ret1 == nil and ret2 == "error message" and ret3 == nil, "incorrect return values")
local err = {key = "value"}
ret1, ret2 = pcall(func, err)
assert(not ret1, "error not rethrown")
assert(ret2 == err, "incorrect error rethrown")
print("OK")

View File

@ -3,19 +3,31 @@ local ftp = require("socket.ftp")
local url = require("socket.url")
local ltn12 = require("ltn12")
-- use dscl to create user "luasocket" with password "password"
-- with home in /Users/diego/luasocket/test/ftp
-- with group com.apple.access_ftp
-- with shell set to /sbin/nologin
-- set /etc/ftpchroot to chroot luasocket
-- must set group com.apple.access_ftp on user _ftp (for anonymous access)
-- copy index.html to /var/empty/pub (home of user ftp)
-- start ftp server with
-- sudo -s launchctl load -w /System/Library/LaunchDaemons/ftp.plist
-- copy index.html to /Users/diego/luasocket/test/ftp
-- stop with
-- sudo -s launchctl unload -w /System/Library/LaunchDaemons/ftp.plist
-- override protection to make sure we see all errors
--socket.protect = function(s) return s end
dofile("testsupport.lua")
local host, port, index_file, index, back, err, ret
local host = host or "localhost"
local port, index_file, index, back, err, ret
local t = socket.gettime()
host = host or "localhost"
index_file = "index.html"
-- a function that returns a directory listing
local function nlst(u)
local t = {}
@ -55,27 +67,27 @@ assert(not err and back == index, err)
print("ok")
io.write("erasing before upload: ")
ret, err = dele("ftp://luasocket:pedrovian@" .. host .. "/index.up.html")
ret, err = dele("ftp://luasocket:password@" .. host .. "/index.up.html")
if not ret then print(err)
else print("ok") end
io.write("testing upload: ")
ret, err = ftp.put("ftp://luasocket:pedrovian@" .. host .. "/index.up.html;type=i", index)
ret, err = ftp.put("ftp://luasocket:password@" .. host .. "/index.up.html;type=i", index)
assert(ret and not err, err)
print("ok")
io.write("downloading uploaded file: ")
back, err = ftp.get("ftp://luasocket:pedrovian@" .. host .. "/index.up.html;type=i")
back, err = ftp.get("ftp://luasocket:password@" .. host .. "/index.up.html;type=i")
assert(ret and not err and index == back, err)
print("ok")
io.write("erasing after upload/download: ")
ret, err = dele("ftp://luasocket:pedrovian@" .. host .. "/index.up.html")
ret, err = dele("ftp://luasocket:password@" .. host .. "/index.up.html")
assert(ret and not err, err)
print("ok")
io.write("testing weird-character translation: ")
back, err = ftp.get("ftp://luasocket:pedrovian@" .. host .. "/%23%3f;type=i")
back, err = ftp.get("ftp://luasocket:password@" .. host .. "/%23%3f;type=i")
assert(not err and back == index, err)
print("ok")
@ -84,7 +96,7 @@ local back = {}
ret, err = ftp.get{
url = "//stupid:mistake@" .. host .. "/index.html",
user = "luasocket",
password = "pedrovian",
password = "password",
type = "i",
sink = ltn12.sink.table(back)
}

View File

@ -180,6 +180,15 @@ assert(ltn12.pump.all(source, sink), "returned error")
assert(table.concat(t) == s, "mismatch")
print("ok")
--------------------------------
io.write("testing source.table: ")
local inp = {'a','b','c','d','e'}
local source = ltn12.source.table(inp)
sink, t = ltn12.sink.table()
assert(ltn12.pump.all(source, sink), "returned error")
for i = 1, #inp do assert(t[i] == inp[i], "mismatch") end
print("ok")
--------------------------------
io.write("testing source.chain (with split): ")
source = ltn12.source.string(s)
@ -191,6 +200,21 @@ assert(table.concat(t) == s, "mismatch")
assert(filter(nil, 1), "filter not empty")
print("ok")
--------------------------------
io.write("testing source.chain (with several filters): ")
local function double(x) -- filter turning "ABC" into "AABBCC"
if not x then return end
local b={}
for k in x:gmatch'.' do table.insert(b, k..k) end
return table.concat(b)
end
source = ltn12.source.string(s)
source = ltn12.source.chain(source, double, double, double)
sink, t = ltn12.sink.table()
assert(ltn12.pump.all(source, sink), "returned error")
assert(table.concat(t) == double(double(double(s))), "mismatch")
print("ok")
--------------------------------
io.write("testing source.chain (with split) and sink.chain (with merge): ")
source = ltn12.source.string(s)
@ -205,6 +229,15 @@ assert(filter(nil, 1), "filter not empty")
assert(filter2(nil, 1), "filter2 not empty")
print("ok")
--------------------------------
io.write("testing sink.chain (with several filters): ")
source = ltn12.source.string(s)
sink, t = ltn12.sink.table()
sink = ltn12.sink.chain(double, double, double, sink)
assert(ltn12.pump.all(source, sink), "returned error")
assert(table.concat(t) == double(double(double(s))), "mismatch")
print("ok")
--------------------------------
io.write("testing filter.chain (and sink.chain, with split, merge): ")
source = ltn12.source.string(s)
@ -272,3 +305,4 @@ assert(filter3(nil, 1), "filter3 not empty")
assert(filter4(nil, 1), "filter4 not empty")
assert(filter5(nil, 1), "filter5 not empty")
print("ok")

View File

@ -304,15 +304,20 @@ function isclosed(c)
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()
local tcp = socket.tcp4()
if isclosed(tcp) then fail("should not be closed") end
tcp:close()
if not isclosed(tcp) then fail("should be closed") end
tcp = socket.tcp()
if not isclosed(tcp) then fail("should be closed") end
tcp = nil
local udp = socket.udp4()
if isclosed(udp) then fail("should not be closed") end
udp:close()
if not isclosed(udp) then fail("should be closed") end
udp = socket.udp()
if not isclosed(udp) then fail("should be closed") end
udp = nil
pass("ok")
end
@ -368,7 +373,7 @@ function test_selectbugs()
pass("invalid input: ok")
local toomany = {}
for i = 1, socket._SETSIZE+1 do
toomany[#toomany+1] = socket.udp()
toomany[#toomany+1] = socket.udp4()
end
if #toomany > socket._SETSIZE then
local e = pcall(socket.select, toomany, nil, 0.1)
@ -447,16 +452,14 @@ end
------------------------------------------------------------------------
function rebind_test()
--local c ,c1 = socket.bind("localhost", 0)
local c ,c1 = socket.bind("127.0.0.1", 0)
if not c then pass ("failed to bind! " .. tostring(c) .. ' ' .. tostring(c1)) return end
assert(c,c1)
local i, p = c:getsockname()
local s, e = socket.tcp()
assert(s, e)
s:setoption("reuseaddr", false)
r, e = s:bind("localhost", p)
r, e = s:bind(i, p)
assert(not r, "managed to rebind!")
assert(e)
pass("ok")
@ -593,7 +596,7 @@ function test_writeafterclose()
while not err do
sent, err, errsent, time = data:send(str)
end
assert(err == "closed", "should have returned 'closed'")
assert(err == "closed", "got " .. err .. " instead of 'closed'")
pass("ok")
end
@ -666,7 +669,6 @@ local udp_methods = {
"settimeout"
}
------------------------------------------------------------------------
test_methods(socket.udp(), udp_methods)
do local sock = socket.tcp6()
@ -674,6 +676,9 @@ if sock then test_methods(socket.udp6(), udp_methods)
else io.stderr:write("Warning! IPv6 does not support!\n") end
end
test("closed connection detection: ")
test_closed()
test("partial receive")
test_partialrecv()
@ -697,9 +702,6 @@ rebind_test()
test("active close: ")
active_close()
test("closed connection detection: ")
test_closed()
test("accept function: ")
accept_timeout()
accept_errors()

View File

@ -1,7 +1,7 @@
local socket = require"socket"
local udp = socket.udp
local localhost = "127.0.0.1"
local port = arg[1]
local port = assert(arg[1], "missing port argument")
se = udp(); se:setoption("reuseaddr", true)
se:setsockname(localhost, 5062)

9
test/unixdgramclnt.lua Normal file
View File

@ -0,0 +1,9 @@
socket = require"socket"
socket.unix = require"socket.unix"
c = assert(socket.unix.dgram())
print(c:bind("/tmp/bar"))
while 1 do
local l = io.read("*l")
assert(c:sendto(l, "/tmp/foo"))
print(assert(c:receivefrom()))
end

9
test/unixdgramsrvr.lua Normal file
View File

@ -0,0 +1,9 @@
socket = require"socket"
socket.unix = require"socket.unix"
u = assert(socket.unix.dgram())
assert(u:bind("/tmp/foo"))
while 1 do
x, r = assert(u:receivefrom())
print(x, r)
assert(u:sendto(">" .. x, r))
end

View File

@ -1,6 +1,6 @@
socket = require"socket"
socket.unix = require"socket.unix"
c = assert(socket.unix())
c = assert(socket.unix.stream())
assert(c:connect("/tmp/foo"))
while 1 do
local l = io.read()

View File

@ -1,6 +1,6 @@
socket = require"socket"
socket.unix = require"socket.unix"
u = assert(socket.unix())
u = assert(socket.unix.stream())
assert(u:bind("/tmp/foo"))
assert(u:listen())
c = assert(u:accept())

View File

@ -90,6 +90,75 @@ local check_parse_url = function(gaba)
end
print("testing URL parsing")
check_parse_url{
url = "scheme://user:pass$%?#wd@host:port/path;params?query#fragment",
scheme = "scheme",
authority = "user:pass$%?#wd@host:port",
host = "host",
port = "port",
userinfo = "user:pass$%?#wd",
password = "pass$%?#wd",
user = "user",
path = "/path",
params = "params",
query = "query",
fragment = "fragment"
}
check_parse_url{
url = "scheme://user:pass?#wd@host:port/path;params?query#fragment",
scheme = "scheme",
authority = "user:pass?#wd@host:port",
host = "host",
port = "port",
userinfo = "user:pass?#wd",
password = "pass?#wd",
user = "user",
path = "/path",
params = "params",
query = "query",
fragment = "fragment"
}
check_parse_url{
url = "scheme://user:pass-wd@host:port/path;params?query#fragment",
scheme = "scheme",
authority = "user:pass-wd@host:port",
host = "host",
port = "port",
userinfo = "user:pass-wd",
password = "pass-wd",
user = "user",
path = "/path",
params = "params",
query = "query",
fragment = "fragment"
}
check_parse_url{
url = "scheme://user:pass#wd@host:port/path;params?query#fragment",
scheme = "scheme",
authority = "user:pass#wd@host:port",
host = "host",
port = "port",
userinfo = "user:pass#wd",
password = "pass#wd",
user = "user",
path = "/path",
params = "params",
query = "query",
fragment = "fragment"
}
check_parse_url{
url = "scheme://user:pass#wd@host:port/path;params?query",
scheme = "scheme",
authority = "user:pass#wd@host:port",
host = "host",
port = "port",
userinfo = "user:pass#wd",
password = "pass#wd",
user = "user",
path = "/path",
params = "params",
query = "query",
}
check_parse_url{
url = "scheme://userinfo@host:port/path;params?query#fragment",
scheme = "scheme",
@ -608,9 +677,9 @@ check_parse_path("eu/tu", { "eu", "tu" })
print("testing path protection")
check_protect({ "eu", "-_.!~*'():@&=+$,", "tu" }, "eu/-_.!~*'():@&=+$,/tu")
check_protect({ "eu ", "~diego" }, "eu%20/~diego")
check_protect({ "/eu>", "<diego?" }, "%2feu%3e/%3cdiego%3f")
check_protect({ "\\eu]", "[diego`" }, "%5ceu%5d/%5bdiego%60")
check_protect({ "{eu}", "|diego\127" }, "%7beu%7d/%7cdiego%7f")
check_protect({ "/eu>", "<diego?" }, "%2Feu%3E/%3Cdiego%3F")
check_protect({ "\\eu]", "[diego`" }, "%5Ceu%5D/%5Bdiego%60")
check_protect({ "{eu}", "|diego\127" }, "%7Beu%7D/%7Cdiego%7F")
check_protect({ "eu ", "~diego" }, "eu /~diego", 1)
check_protect({ "/eu>", "<diego?" }, "/eu>/<diego?", 1)
check_protect({ "\\eu]", "[diego`" }, "\\eu]/[diego`", 1)

View File

@ -1,12 +1 @@
make PLAT=win32 LUAV=5.2 LUAINC_win32='c:\cygwin\home\diego\build\include' LUALIB_win32='c:\cygwin\home\diego\build\bin\release'
#!/bin/sh
for p in Release Debug x64/Release x64/Debug; do
for el in mime socket; do
for e in dll lib; do
cp $p/$el/core.$e ../bin/$p/$el/
done;
done;
cp src/ltn12.lua src/socket.lua src/mime.lua ../bin/$p/
cp src/http.lua src/url.lua src/tp.lua src/ftp.lua src/headers.lua src/smtp.lua ../bin/$p/socket/
done;
make LUAPREFIX_win32='c:\cygwin\home\diego\vc12' LUAV=5.1 PLAT=win32 LUALIBNAME_win32=lualib.lib PLATFORM_win32=Debug install-both