Compare commits

...

52 Commits

Author SHA1 Message Date
Sharad Singh
47e5bd71a9
docs: Improve installation instructions, focus on LuaRocks installation (#454) 2025-03-31 13:36:12 +03:00
Caleb Maclennan
58c76080a0
Merge pull request #450 from luau-project/update-ci
Update CI
2025-03-01 00:59:17 +03:00
luau-project
250e4d48f7
fix: Use a macos version supported by GitHub Actions 2025-03-01 00:58:23 +03:00
luau-project
f139105c98
ci: Use GitHub Actions from luarocks namespace 2025-03-01 00:57:51 +03:00
Caleb Maclennan
66cdeca663
Merge pull request #447 from masakk1/patch-1 2025-02-23 11:22:32 +03:00
Masak1
676e5f3501
docs: Remove unnecessary value argument from gettimeout 2025-02-23 11:12:41 +03:00
Masak1
4dad084cfd
docs: Fix udp documentation gettimeout typo 2025-02-23 11:12:41 +03:00
Tianjia Zhang
1fad162690
chore: Remove the executable permission bit from some source files (#434) 2024-06-29 14:33:47 +03:00
Kim Alvefur
e3ca4a767a
fix(unix): Pass correct path length for abstract sockets (#430) 2024-05-24 13:28:51 +03:00
Thijs Schreijer
93eef5015e
chore(docs): update LTN12 links (#428) 2024-03-15 12:55:02 +01:00
Thijs Schreijer
23241717bf
fix(docs): link to included LTN documents (#426) 2024-03-05 23:18:24 +03:00
Alexandre Detiste
98be8d9fc1
chore(http): Correct typos in error message and code comments (#423) 2024-02-12 11:28:01 +03:00
Max1Truc
fa69770e52
fix(http): Use the right protocol for proxies (#386) 2023-11-23 08:01:29 +03:00
_AMD_
13f2b3c663
fix(http): Correct receiveheaders() handling of folded values (#420) 2023-11-13 23:33:26 +03:00
Caleb Maclennan
453a5207ed
style(docs): Trim trailing whitespace in HTML docs
Many editors remove these automatically anyway which makes opening and
editng the docs cause a bunch of noise. This is just to get the noise
out of the way in a style commit so it doesn't leak into other PRs
2023-11-11 08:07:38 +03:00
Caleb Maclennan
de359ea408
chore(core): Update version markers to last released version
Closes #401
2023-11-11 00:48:02 +03:00
Caleb Maclennan
c93f9154e1
feat(rockspec): Ship mbox parser with LuaRocks, already packaged in some distros
Closes #324
2023-11-11 00:44:22 +03:00
Caleb Maclennan
8a5368b659
Merge pull request #418 from alerque/url-empty-path 2023-11-09 14:11:33 +03:00
Paul Kulchenko
22b8202d70
fix(wsocket): Properly report CONNRESET (#81) 2023-11-09 14:00:09 +03:00
Caleb Maclennan
3a817a56eb
fix(url): Avoid fragment being part of authority, allows parsing empty paths 2023-11-08 23:27:58 +03:00
Caleb Maclennan
7eaf648056
fix(url): Avoid query string being part of authority, allows parsing empty paths 2023-11-08 14:50:43 +03:00
georgeto
bef62aeb50
fix(inet): Return port as number in getsockname (#392) 2023-11-08 14:29:06 +03:00
Caleb Maclennan
43a97b7f00
ci: Update Luacheck to v1+, use newer Ubuntu runner images 2023-11-08 13:14:04 +03:00
Caleb Maclennan
64c9d531df
ci: Identify CI jobs better and skip irrelevant platform specific steps 2023-11-08 13:08:10 +03:00
Caleb Maclennan
0f37af645c
Merge pull request #415 from leso-kn/feature/actions-windows 2023-10-30 22:04:00 +03:00
leso-kn
9c6195ea62
ci: Enable windows build 2023-10-27 19:31:18 +02:00
leso-kn
708e50f8e6
refactor(core): Replace check for SO_BINDTODEVICE support with platform independent version 2023-10-27 11:34:39 +02:00
leso-kn
0bc8c56043
fix(core): Disable SO_BINDTODEVICE on windows
Co-authored-by: Sewbacca <sebastian.kalus@kolabnow.com>
2023-10-27 09:59:26 +03:00
Leso_KN
f741a88b80
feat(tcp): Add 'bindtodevice' option (#408) 2023-10-23 21:27:01 +03:00
Caleb Maclennan
9100f7e3ab
docs: Fixup badge URLs with current GH API 2023-10-17 09:07:25 +03:00
Henri D
8c2ff7217e
fix(http): Allow relative redirect on https (#395)
Location header can now be relative: https://httpwg.org/specs/rfc9110.html#field.location
2022-10-08 09:42:36 +03:00
Caleb Maclennan
26b524e1d7
Revert "fix(usocket): Fix typo in return value function name"
This reverts commit 88c72acd771ac6c96ea7f4e57293307e282ad49f.

See discussion on #390
2022-08-31 00:34:39 +03:00
Caleb Maclennan
48164b572e
Merge branch 'zhaozg/master' 2022-08-30 13:17:21 +03:00
zhaozg
159890c366
style(usocket): Remove trailing space at EOL 2022-08-30 13:16:38 +03:00
zhaozg
88c72acd77
fix(usocket): Fix typo in return value function name 2022-08-30 13:15:53 +03:00
George Zhao
30ddf0eaea
style(core) Remove lingering c-style comment (#388) 2022-08-27 21:31:54 +03:00
Thijs Schreijer
87c48f3e4d
Merge pull request #364 from lunarmodules/cleanup 2022-08-24 12:31:18 +02:00
Caleb Maclennan
95b7efa9da
chore: Release v3.1.0 2022-07-27 10:07:00 +03:00
Caleb Maclennan
80503077db
ci: Echo correct platform in job title 2022-07-27 09:55:54 +03:00
rpatters1
5a7e3f0888
fix(build): Use gai_strerrorA not gai_strerror on Windows
* Explicitly call gai_strerrorA (for Windows builds), so that the code work correctly in 32bit or 64bit builds.

* Implement GAI_STRERROR macro to deal with Windows vs. Non-Windows compiles for 64-bit.

* make usocket.c consistent with other modules that call macro GAI_STRERROR

* Use different name not just different case for macro wrapping function

Co-authored-by: Caleb Maclennan <caleb@alerque.com>
2022-07-27 09:51:35 +03:00
Kim Alvefur
d1ad8160cb
feat(tcp): Add support for TCP Defer Accept
This makes it so that a listening socket does not become readable for
accept() until a connection has been fully established *and* started
sending something, thus the program doesn't have to wait for the first
data. This only makes sense for client-speaks-first protocols.

Co-authored-by: Caleb Maclennan <caleb@alerque.com>
2022-07-27 09:40:18 +03:00
Kim Alvefur
0c7df119c2
feat(tcp): Add support for TCP Fast Open 2022-07-27 09:16:43 +03:00
Caleb Maclennan
cff09ffb32
chore(rockspec): Move recent PR change to proper rockspec (#384) 2022-07-26 23:39:17 +03:00
goldenstein64
38c7b5161b
fix(rockspec): Fixup Windows (mingw32) builds (#383) 2022-07-26 23:24:25 +03:00
Thijs Schreijer
97d5194f30 chore(ltn) rename files to markdown 2022-03-29 14:10:30 +02:00
Thijs Schreijer
db2f1c9598 chore(ltn) update file contents from wiki to markdown 2022-03-29 14:09:10 +02:00
Thijs Schreijer
3adf252b45 cleanup; move FIX, TODO, WISH into TODO.md 2022-03-29 13:33:00 +02:00
Thijs Schreijer
86de838eb5 cleanup; move ./etc into ./samples and mark 'unsupported' 2022-03-29 13:33:00 +02:00
Thijs Schreijer
7187be8b76 cleanup; delete the ./gem folder 2022-03-29 13:33:00 +02:00
Thijs Schreijer
0fc0122df8 cleanup; move logo file into docs, more appropriate 2022-03-29 13:33:00 +02:00
Thijs Schreijer
ec7d7a2b8f cleanup; delete unreferenced powerpoint file from docs 2022-03-29 13:33:00 +02:00
Robert Scheck
1d61853ab8
chore: Update internal version references to match release (#370) 2022-03-28 11:53:32 +00:00
99 changed files with 1291 additions and 2478 deletions

View File

@ -8,27 +8,32 @@ on:
jobs:
build:
name: Test build on Linux
name: Test ${{ matrix.luaVersion }} on ${{ matrix.platform }}
strategy:
fail-fast: false
matrix:
luaVersion: [ "5.4", "5.3", "5.2", "5.1", "luajit", "luajit-openresty"]
platform: [ "ubuntu-20.04", "macos-11" ] # "windows-2022" not supported by gh-actions-lua
luaVersion: [ "5.4", "5.3", "5.2", "5.1", "luajit", "luajit-openresty" ]
platform: [ "ubuntu-22.04", "macos-14", "windows-2022" ]
runs-on: ${{ matrix.platform }}
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup msvc
if: ${{ startsWith(matrix.platform, 'windows') && !startsWith(matrix.luaVersion, 'luajit') }}
uses: ilammy/msvc-dev-cmd@v1
- name: Setup lua
uses: leafo/gh-actions-lua@v9
uses: luarocks/gh-actions-lua@v10
with:
luaVersion: ${{ matrix.luaVersion }}
- name: Setup luarocks
uses: leafo/gh-actions-luarocks@v4
uses: luarocks/gh-actions-luarocks@v5
- name: Make and install
run: |
export DEBUG=DEBUG
luarocks make -- luasocket-scm-3.rockspec
env:
DEBUG: DEBUG
- name: Run regression tests
shell: bash
run: |
cd test
lua hello.lua

View File

@ -5,9 +5,9 @@ on: [push, pull_request]
jobs:
luacheck:
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Luacheck
uses: lunarmodules/luacheck@v0
uses: lunarmodules/luacheck@v1

View File

@ -15,8 +15,6 @@ include_files = {
}
exclude_files = {
"etc/*.lua",
"etc/**/*.lua",
"test/*.lua",
"test/**/*.lua",
"samples/*.lua",

View File

@ -1,5 +1,12 @@
# Changelog
## [v3.1.0](https://github.com/lunarmodules/luasocket/releases/v3.1.0) — 2022-07-27
* Add support for TCP Defer Accept @Zash
* Add support for TCP Fast Open @Zash
* Fix Windows (mingw32) builds @goldenstein64
* Avoid build warnings on 64-bit Windows @rpatters1
## [v3.0.0](https://github.com/lunarmodules/luasocket/releases/v3.0.0) — 2022-03-25
The last time LuaSocket had a stable release tag was 14 years ago when 2.0.2 was tagged.

28
FIX
View File

@ -1,28 +0,0 @@
http was preserving old host header during redirects
fix smtp.send hang on source error
add create field to FTP and SMTP and fix HTTP ugliness
clean timeout argument to open functions in SMTP, HTTP and FTP
eliminate globals from namespaces created by module().
url.absolute was not working when base_url was already parsed
http.request was redirecting even when the location header was empty
tcp{client}:shutdown() was checking for group instead of class.
tcp{client}:send() now returns i+sent-1...
get rid of a = socket.try() in the manual, except for protected cases. replace it with assert.
get rid of "base." kludge in package.loaded
check all "require("http")" etc in the manual.
make sure sock_gethostname.* only return success if the hp is not null!
change 'l' prefix in C libraries to 'c' to avoid clash with LHF libraries
don't forget the declarations in luasocket.h and mime.h!!!
setpeername was using udp{unconnected}
fixed a bug in http.lua that caused some requests to fail (Florian Berger)
fixed a bug in select.c that prevented sockets with descriptor 0 from working (Renato Maia)
fixed a "bug" that caused dns.toip to crash under uLinux
fixed a "bug" that caused a crash in gethostbyname under VMS
DEBUG and VERSION became _DEBUG and _VERSION
send returns the right value if input is "". Alexander Marinov

0
Lua.props Executable file → Normal file
View File

View File

@ -1,8 +1,8 @@
# LuaSocket
[![Build](https://img.shields.io/github/workflow/status/lunarmodules/luasocket/Build?label=Build=Lua)](https://github.com/lunarmodules/luasocket/actions?workflow=Build)
[![Luacheck](https://img.shields.io/github/workflow/status/lunarmodules/luasocket/Luacheck?label=Luacheck&logo=Lua)](https://github.com/lunarmodules/luasocket/actions?workflow=Luacheck)
[![Build](https://img.shields.io/github/actions/workflow/status/lunarmodules/luasocket/build.yml?branch=master&label=Build&logo=Lua)](https://github.com/lunarmodules/luasocket/actions?workflow=Build)
[![Luacheck](https://img.shields.io/github/actions/workflow/status/lunarmodules/luasocket/luacheck.yml?branch=master&label=Luacheck&logo=Lua)](https://github.com/lunarmodules/luasocket/actions?workflow=Luacheck)
[![GitHub tag (latest SemVer)](https://img.shields.io/github/v/tag/lunarmodules/luasocket?label=Tag&logo=GitHub)](https://github.com/lunarmodules/luasocket/releases)
[![Luarocks](https://img.shields.io/luarocks/v/lunarmodules/luasocket?label=Luarocks&logo=Lua)](https://luarocks.org/modules/lunarmodules/luasocket)

81
TODO
View File

@ -1,81 +0,0 @@
- bizarre default values for getnameinfo should throw error instead!
> It's just too bad it can't talk to gmail -
> reason 1: they absolutely want TLS
> reason 2: unlike all the other SMTP implementations, they
> don't
> tolerate missing < > around adresses
- document the new bind and connect behavior.
- shouldn't we instead make the code compatible to Lua 5.2
without any compat stuff, and use a compatibility layer to
make it work on 5.1?
- add what's new to manual
- should there be an equivalent to tohostname for IPv6?
- should we add service name resolution as well to getaddrinfo?
- Maybe the sockaddr to presentation conversion should be done with getnameinfo()?
- add http POST sample to manual
people keep asking stupid questions
- documentation of dirty/getfd/setfd is problematic because of portability
same for unix and serial.
what to do about this? add a stronger disclaimer?
- fix makefile with decent defaults?
Done:
- added IPv6 support to getsockname
- simplified getpeername implementation
- added family to return of getsockname and getpeername
and added modification to the manual to describe
- connect and bind try all adresses returned by getaddrinfo
- document headers.lua?
- update copyright date everywhere?
- remove RCSID from files?
- move version to 2.1 rather than 2.1.1?
- fixed url package to support ipv6 hosts
- changed domain to family
- implement getfamily methods.
- remove references to Lua 5.0 from documentation, add 5.2?
- update lua and luasocket version in samples in documentation
- document ipv5_v6only default option being set?
- document tcp6 and udp6
- document dns.getaddrinfo
- documented zero-sized datagram change?
no.
- document unix socket and serial socket? add raw support?
no.
- document getoption
- merge luaL_typeerror into auxiliar to avoid using luaL prefix?
replace \r\n with \0xD\0xA in everything
New mime support
ftp send should return server replies?
make sure there are no object files in the distribution tarball
http handling of 100-continue, see DB patch
DB ftp.lua bug.
test unix.c to return just a function and works with require"unix"
get rid of setmetatable(, nil) since packages don't need this anymore in 5.1
compat-5.1 novo
ajeitar pra lua-5.1
adicionar exemplos de expansão: pipe, local, named pipe
testar os options!
- Thread-unsafe functions to protect
gethostbyname(), gethostbyaddr(), gethostent(),
inet_ntoa(), strerror(),

135
TODO.md Normal file
View File

@ -0,0 +1,135 @@
## FIX
http was preserving old host header during redirects
fix smtp.send hang on source error
add create field to FTP and SMTP and fix HTTP ugliness
clean timeout argument to open functions in SMTP, HTTP and FTP
eliminate globals from namespaces created by module().
url.absolute was not working when base_url was already parsed
http.request was redirecting even when the location header was empty
tcp{client}:shutdown() was checking for group instead of class.
tcp{client}:send() now returns i+sent-1...
get rid of a = socket.try() in the manual, except for protected cases. replace it with assert.
get rid of "base." kludge in package.loaded
check all "require("http")" etc in the manual.
make sure sock_gethostname.* only return success if the hp is not null!
change 'l' prefix in C libraries to 'c' to avoid clash with LHF libraries
don't forget the declarations in luasocket.h and mime.h!!!
setpeername was using udp{unconnected}
fixed a bug in http.lua that caused some requests to fail (Florian Berger)
fixed a bug in select.c that prevented sockets with descriptor 0 from working (Renato Maia)
fixed a "bug" that caused dns.toip to crash under uLinux
fixed a "bug" that caused a crash in gethostbyname under VMS
DEBUG and VERSION became _DEBUG and _VERSION
send returns the right value if input is "". Alexander Marinov
## WISH
... as an l-value to get all results of a function call?
at least ...[i] and #...
extend to full tuples?
__and __or __not metamethods
lua_tostring, lua_tonumber, lua_touseradta etc push values in stack
__tostring,__tonumber, __touserdata metamethods are checked
and expected to push an object of correct type on stack
lua_rawtostring, lua_rawtonumber, lua_rawtouserdata don't
push anything on stack, return data of appropriate type,
skip metamethods and throw error if object not of exact type
package.findfile exported
module not polluting the global namespace
coxpcall with a coroutine pool for efficiency (reusing coroutines)
exception mechanism formalized? just like the package system was.
a nice bitlib in the core
## TODO
- bizarre default values for getnameinfo should throw error instead!
> It's just too bad it can't talk to gmail -
> reason 1: they absolutely want TLS
> reason 2: unlike all the other SMTP implementations, they
> don't
> tolerate missing < > around adresses
- document the new bind and connect behavior.
- shouldn't we instead make the code compatible to Lua 5.2
without any compat stuff, and use a compatibility layer to
make it work on 5.1?
- add what's new to manual
- should there be an equivalent to tohostname for IPv6?
- should we add service name resolution as well to getaddrinfo?
- Maybe the sockaddr to presentation conversion should be done with getnameinfo()?
- add http POST sample to manual
people keep asking stupid questions
- documentation of dirty/getfd/setfd is problematic because of portability
same for unix and serial.
what to do about this? add a stronger disclaimer?
- fix makefile with decent defaults?
## Done:
- added IPv6 support to getsockname
- simplified getpeername implementation
- added family to return of getsockname and getpeername
and added modification to the manual to describe
- connect and bind try all adresses returned by getaddrinfo
- document headers.lua?
- update copyright date everywhere?
- remove RCSID from files?
- move version to 2.1 rather than 2.1.1?
- fixed url package to support ipv6 hosts
- changed domain to family
- implement getfamily methods.
- remove references to Lua 5.0 from documentation, add 5.2?
- update lua and luasocket version in samples in documentation
- document ipv5_v6only default option being set?
- document tcp6 and udp6
- document dns.getaddrinfo
- documented zero-sized datagram change?
no.
- document unix socket and serial socket? add raw support?
no.
- document getoption
- merge luaL_typeerror into auxiliar to avoid using luaL prefix?
replace \r\n with \0xD\0xA in everything
New mime support
ftp send should return server replies?
make sure there are no object files in the distribution tarball
http handling of 100-continue, see DB patch
DB ftp.lua bug.
test unix.c to return just a function and works with require"unix"
get rid of setmetatable(, nil) since packages don't need this anymore in 5.1
compat-5.1 novo
ajeitar pra lua-5.1
adicionar exemplos de expans<6E>o: pipe, local, named pipe
testar os options!
- Thread-unsafe functions to protect
gethostbyname(), gethostbyaddr(), gethostent(),
inet_ntoa(), strerror(),

22
WISH
View File

@ -1,22 +0,0 @@
... as an l-value to get all results of a function call?
at least ...[i] and #...
extend to full tuples?
__and __or __not metamethods
lua_tostring, lua_tonumber, lua_touseradta etc push values in stack
__tostring,__tonumber, __touserdata metamethods are checked
and expected to push an object of correct type on stack
lua_rawtostring, lua_rawtonumber, lua_rawtouserdata don't
push anything on stack, return data of appropriate type,
skip metamethods and throw error if object not of exact type
package.findfile exported
module not polluting the global namespace
coxpcall with a coroutine pool for efficiency (reusing coroutines)
exception mechanism formalized? just like the package system was.
a nice bitlib in the core

View File

@ -55,7 +55,7 @@ protocol. For that, check the implementation.
<p>
To really benefit from this module, a good understanding of
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">
<a href="https://github.com/lunarmodules/luasocket/blob/master/ltn012.md">
LTN012, Filters sources and sinks</a> is necessary.
</p>
@ -122,7 +122,7 @@ expects at least the fields <tt>host</tt>, <tt>sink</tt>, and one of
<tt>argument</tt> or <tt>path</tt> (<tt>argument</tt> takes
precedence). <tt>Host</tt> is the server to connect to. <tt>Sink</tt> is
the <em>simple</em>
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a>
<a href="https://github.com/lunarmodules/luasocket/blob/master/ltn012.md">LTN12</a>
sink that will receive the downloaded data. <tt>Argument</tt> or
<tt>path</tt> give the target path to the resource in the server. The
optional arguments are the following:
@ -136,7 +136,7 @@ authentication. Defaults to "<tt>ftp:anonymous@anonymous.org</tt>";</li>
<li><tt>type</tt>: The transfer mode. Can take values "<tt>i</tt>" or
"<tt>a</tt>". Defaults to whatever is the server default;</li>
<li><tt>step</tt>:
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a>
<a href="https://github.com/lunarmodules/luasocket/blob/master/ltn012.md">LTN12</a>
pump step function used to pass data from the
server to the sink. Defaults to the LTN12 <tt>pump.step</tt> function;</li>
<li><tt>create</tt>: An optional function to be used instead of
@ -206,7 +206,7 @@ expects at least the fields <tt>host</tt>, <tt>source</tt>, and one of
<tt>argument</tt> or <tt>path</tt> (<tt>argument</tt> takes
precedence). <tt>Host</tt> is the server to connect to. <tt>Source</tt> is
the <em>simple</em>
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a>
<a href="https://github.com/lunarmodules/luasocket/blob/master/ltn012.md">LTN12</a>
source that will provide the contents to be uploaded.
<tt>Argument</tt> or
<tt>path</tt> give the target path to the resource in the server. The
@ -221,7 +221,7 @@ authentication. Defaults to "<tt>ftp:anonymous@anonymous.org</tt>";</li>
<li><tt>type</tt>: The transfer mode. Can take values "<tt>i</tt>" or
"<tt>a</tt>". Defaults to whatever is the server default;</li>
<li><tt>step</tt>:
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a>
<a href="https://github.com/lunarmodules/luasocket/blob/master/ltn012.md">LTN12</a>
pump step function used to pass data from the
server to the sink. Defaults to the LTN12 <tt>pump.step</tt> function;</li>
<li><tt>create</tt>: An optional function to be used instead of

View File

@ -52,7 +52,7 @@ implementation conforms to the HTTP/1.1 standard,
The module exports functions that provide HTTP functionality in different
levels of abstraction. From the simple
string oriented requests, through generic
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a> based, down to even lower-level if you bother to look through the source code.
<a href="https://github.com/lunarmodules/luasocket/blob/master/ltn012.md">LTN12</a> based, down to even lower-level if you bother to look through the source code.
</p>
<p>
@ -144,7 +144,7 @@ http.<b>request{</b><br>
The request function has two forms. The simple form downloads
a URL using the <tt>GET</tt> or <tt>POST</tt> method and is based
on strings. The generic form performs any HTTP method and is
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a> based.
<a href="https://github.com/lunarmodules/luasocket/blob/master/ltn012.md">LTN12</a> based.
</p>
<p class="parameters">
@ -158,7 +158,7 @@ in the <tt>url</tt>. Otherwise, it performs a <tt>GET</tt> in the
<p class="parameters">
If the first argument is instead a table, the most important fields are
the <tt>url</tt> and the <em>simple</em>
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a>
<a href="https://github.com/lunarmodules/luasocket/blob/master/ltn012.md">LTN12</a>
<tt>sink</tt> that will receive the downloaded content.
Any part of the <tt>url</tt> can be overridden by including
the appropriate field in the request table.
@ -172,13 +172,13 @@ following:
<li><tt>method</tt>: The HTTP request method. Defaults to "GET";</li>
<li><tt>headers</tt>: Any additional HTTP headers to send with the request;</li>
<li><tt>source</tt>: <em>simple</em>
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a>
<a href="https://github.com/lunarmodules/luasocket/blob/master/ltn012.md">LTN12</a>
source to provide the request body. If there
is a body, you need to provide an appropriate "<tt>content-length</tt>"
request header field, or the function will attempt to send the body as
"<tt>chunked</tt>" (something few servers support). Defaults to the empty source;</li>
<li><tt>step</tt>:
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a>
<a href="https://github.com/lunarmodules/luasocket/blob/master/ltn012.md">LTN12</a>
pump step function used to move data.
Defaults to the LTN12 <tt>pump.step</tt> function.</li>
<li><tt>proxy</tt>: The URL of a proxy server to use. Defaults to no proxy;</li>

View File

@ -87,7 +87,7 @@ Author: <a href="http://www.impa.br/~diego">Diego Nehab</a>
<h2 id="download">Download</h2>
<p>
LuaSocket version 3.0.0 is now available for download!
LuaSocket version 3.1.0 is now available for download!
It is compatible with Lua&nbsp;5.1 through 5.4.
Chances are it works well on most UNIX distributions and Windows flavors.
</p>

View File

@ -1,11 +1,11 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta name="description" content="LuaSocket: Introduction to the core">
<meta name="keywords" content="Lua, LuaSocket, TCP, UDP, Network, Support,
Installation">
Installation">
<title>LuaSocket: Installation</title>
<link rel="stylesheet" href="reference.css" type="text/css">
</head>
@ -29,7 +29,7 @@ Installation">
<a href="index.html#download">download</a> &middot;
<a href="installation.html">installation</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a>
<a href="reference.html">reference</a>
</p>
</center>
<hr>
@ -37,70 +37,30 @@ Installation">
<!-- installation ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h2>Installation</h2>
<h2>Installation via luarocks</h2>
<p> Here we describe the standard distribution. If the
standard doesn't meet your needs, we refer you to the Lua
discussion list, where any question about the package scheme
will likely already have been answered. </p>
<h3>Directory structure</h3>
<p> On Unix systems, the standard distribution uses two base
directories, one for system dependent files, and another for system
independent files. Let's call these directories <tt>&lt;CDIR&gt;</tt>
and <tt>&lt;LDIR&gt;</tt>, respectively.
For example, in my laptp, Lua&nbsp;5.1 is configured to
use '<tt>/usr/local/lib/lua/5.1</tt>' for
<tt>&lt;CDIR&gt;</tt> and '<tt>/usr/local/share/lua/5.1</tt>' for
<tt>&lt;LDIR&gt;</tt>. On Windows, <tt>&lt;CDIR&gt;</tt>
usually points to the directory where the Lua executable is
found, and <tt>&lt;LDIR&gt;</tt> points to a
<tt>lua/</tt> directory inside <tt>&lt;CDIR&gt;</tt>. (These
settings can be overridden by environment variables
<tt>LUA_PATH</tt> and <tt>LUA_CPATH</tt>. See the Lua
documentation for details.) Here is the standard LuaSocket
distribution directory structure:</p>
<p>LuaSocket can be easily installed using <a href="https://luarocks.org/" target="_blank">LuaRocks</a>, the Lua package manager.</p>
<h3>Installing via LuaRocks</h3>
<p>Run the following command in your terminal:</p>
<pre class=example>
&lt;LDIR&gt;/ltn12.lua
&lt;LDIR&gt;/socket.lua
&lt;CDIR&gt;/socket/core.dll
&lt;LDIR&gt;/socket/http.lua
&lt;LDIR&gt;/socket/tp.lua
&lt;LDIR&gt;/socket/ftp.lua
&lt;LDIR&gt;/socket/smtp.lua
&lt;LDIR&gt;/socket/url.lua
&lt;LDIR&gt;/mime.lua
&lt;CDIR&gt;/mime/core.dll
luarocks install luasocket
</pre>
<p> Naturally, on Unix systems, <tt>core.dll</tt>
would be replaced by <tt>core.so</tt>.
</p>
<h3>Verification</h3>
<p>To verify that LuaSocket is installed correctly, open Lua and run:</p>
<pre class=example><code>
local socket = require("socket")
print(socket._VERSION)
</code></pre>
<h3>Using LuaSocket</h3>
<p>If you see output like <strong>LuaSocket 3.0</strong>, the installation was successful.</p>
<p> With the above setup, and an interpreter with shared library support,
it should be easy to use LuaSocket. Just fire the interpreter and use the
<tt>require</tt> function to gain access to whatever module you need:</p>
<pre class=example>
Lua 5.2.2 Copyright (C) 1994-2013 Lua.org, PUC-Rio
&gt; socket = require("socket")
&gt; print(socket._VERSION)
--&gt; LuaSocket 3.0-rc1
</pre>
<p> Each module loads their dependencies automatically, so you only need to
load the modules you directly depend upon: </p>
<pre class=example>
Lua 5.2.2 Copyright (C) 1994-2013 Lua.org, PUC-Rio
&gt; http = require("socket.http")
&gt; print(http.request("http://www.impa.br/~diego/software/luasocket"))
--&gt; homepage gets dumped to terminal
</pre>
<h3>More Information</h3>
<p>For more details, visit the <a href="https://github.com/lunarmodules/luasocket" target="_blank">LuaSocket GitHub repository</a>.</p>
<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->

View File

@ -1,11 +1,11 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta name="description" content="LuaSocket: Introduction to the core">
<meta name="keywords" content="Lua, LuaSocket, TCP, UDP, Network,
Library, Support">
Library, Support">
<title>LuaSocket: Introduction to the core</title>
<link rel="stylesheet" href="reference.css" type="text/css">
</head>
@ -29,7 +29,7 @@ Library, Support">
<a href="index.html#download">download</a> &middot;
<a href="installation.html">installation</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a>
<a href="reference.html">reference</a>
</p>
</center>
<hr>
@ -61,23 +61,23 @@ interface to I/O across different domains and operating systems.
The API design had two goals in mind. First, users
experienced with the C API to sockets should feel comfortable using LuaSocket.
Second, the simplicity and the feel of the Lua language should be
preserved. To achieve these goals, the LuaSocket API keeps the function names and semantics the C API whenever possible, but their usage in Lua has been greatly simplified.
preserved. To achieve these goals, the LuaSocket API keeps the function names and semantics the C API whenever possible, but their usage in Lua has been greatly simplified.
</p>
<p>
One of the simplifications is the receive pattern capability.
Applications can read data from stream domains (such as TCP)
Applications can read data from stream domains (such as TCP)
line by line, block by block, or until the connection is closed.
All I/O reads are buffered and the performance differences between
different receive patterns are negligible.
different receive patterns are negligible.
</p>
<p>
Another advantage is the flexible timeout control
mechanism. As in C, all I/O operations are blocking by default. For
example, the <a href=tcp.html#send><tt>send</tt></a>,
<a href=tcp.html#receive><tt>receive</tt></a> and
example, the <a href=tcp.html#send><tt>send</tt></a>,
<a href=tcp.html#receive><tt>receive</tt></a> and
<a href=tcp.html#accept><tt>accept</tt></a> methods
of the TCP domain will block the caller application until
the operation is completed (if ever!). However, with a call to the
@ -87,7 +87,7 @@ the time it can be blocked by LuaSocket (the "<tt>total</tt>" timeout), on
the time LuaSocket can internally be blocked by any OS call (the
"<tt>block</tt>" timeout) or a combination of the two. Each LuaSocket
call might perform several OS calls, so that the two timeout values are
<em>not</em> equivalent.
<em>not</em> equivalent.
</p>
<p>
@ -95,10 +95,10 @@ Finally, the host name resolution is transparent, meaning that most
functions and methods accept both IP addresses and host names. In case a
host name is given, the library queries the system's resolver and
tries the main IP address returned. Note that direct use of IP addresses
is more efficient, of course. The
<a href=dns.html#toip><tt>toip</tt></a>
and <a href=dns.html#tohostname><tt>tohostname</tt></a>
functions from the DNS module are provided to convert between host names and IP addresses.
is more efficient, of course. The
<a href=dns.html#toip><tt>toip</tt></a>
and <a href=dns.html#tohostname><tt>tohostname</tt></a>
functions from the DNS module are provided to convert between host names and IP addresses.
</p>
<p>
@ -118,47 +118,47 @@ reassembled transparently on the other end. There are no boundaries in
the data transfers. The library allows users to read data from the
sockets in several different granularities: patterns are available for
lines, arbitrary sized blocks or "read up to connection closed", all with
good performance.
good performance.
</p>
<p>
The library distinguishes three types of TCP sockets: <em>master</em>,
<em>client</em> and <em>server</em> sockets.
The library distinguishes three types of TCP sockets: <em>master</em>,
<em>client</em> and <em>server</em> sockets.
</p>
<p>
Master sockets are newly created TCP sockets returned by the function
<a href=tcp.html#tcp><tt>socket.tcp</tt></a>. A master socket is
<a href=tcp.html#tcp><tt>socket.tcp</tt></a>. A master socket is
transformed into a server socket
after it is associated with a <em>local</em> address by a call to the
<a href=tcp.html#bind><tt>bind</tt></a> method followed by a call to the
<a href=tcp.html#listen><tt>listen</tt></a>. Conversely, a master socket
can be changed into a client socket with the method
<a href=tcp.html#connect><tt>connect</tt></a>,
which associates it with a <em>remote</em> address.
<a href=tcp.html#bind><tt>bind</tt></a> method followed by a call to the
<a href=tcp.html#listen><tt>listen</tt></a>. Conversely, a master socket
can be changed into a client socket with the method
<a href=tcp.html#connect><tt>connect</tt></a>,
which associates it with a <em>remote</em> address.
</p>
<p>
On server sockets, applications can use the
On server sockets, applications can use the
<a href=tcp.html#accept><tt>accept</tt></a> method
to wait for a client connection. Once a connection is established, a
client socket object is returned representing this connection. The
other methods available for server socket objects are
<a href=tcp.html#getsockname><tt>getsockname</tt></a>,
<a href=tcp.html#getsockname><tt>getsockname</tt></a>,
<a href=tcp.html#setoption><tt>setoption</tt></a>,
<a href=tcp.html#settimeout><tt>settimeout</tt></a>, and
<a href=tcp.html#close><tt>close</tt></a>.
<a href=tcp.html#settimeout><tt>settimeout</tt></a>, and
<a href=tcp.html#close><tt>close</tt></a>.
</p>
<p>
Client sockets are used to exchange data between two applications over
the Internet. Applications can call the methods
the Internet. Applications can call the methods
<a href=tcp.html#send><tt>send</tt></a> and
<a href=tcp.html#receive><tt>receive</tt></a>
<a href=tcp.html#receive><tt>receive</tt></a>
to send and receive data. The other methods
available for client socket objects are
available for client socket objects are
<a href=tcp.html#getsockname><tt>getsockname</tt></a>,
<a href=tcp.html#getpeername><tt>getpeername</tt></a>,
<a href=tcp.html#getpeername><tt>getpeername</tt></a>,
<a href=tcp.html#setoption><tt>setoption</tt></a>,
<a href=tcp.html#settimeout><tt>settimeout</tt></a>,
<a href=tcp.html#shutdown><tt>shutdown</tt></a>, and
@ -175,7 +175,7 @@ port (one that is chosen by the operating system) on the local host and
awaits client connections on that port. When a connection is established,
the program reads a line from the remote end and sends it back, closing
the connection immediately. You can test it using the telnet
program.
program.
</p>
<pre class=example>
@ -217,68 +217,68 @@ error free. Data transfers are atomic, one datagram at a time. Reading
only part of a datagram discards the rest, so that the following read
operation will act on the next datagram. The advantages are in
simplicity (no connection setup) and performance (no error checking or
error correction).
error correction).
</p>
<p>
Note that although no guarantees are made, these days
networks are so good that, under normal circumstances, few errors
networks are so good that, under normal circumstances, few errors
happen in practice.
</p>
<p>
An UDP socket object is created by the
An UDP socket object is created by the
<a href=udp.html#udp><tt>socket.udp</tt></a> function. UDP
sockets do not need to be connected before use. The method
<a href=udp.html#sendto><tt>sendto</tt></a>
<a href=udp.html#sendto><tt>sendto</tt></a>
can be used immediately after creation to
send a datagram to IP address and port. Host names are not allowed
because performing name resolution for each packet would be forbiddingly
slow. Methods
<a href=udp.html#receive><tt>receive</tt></a> and
slow. Methods
<a href=udp.html#receive><tt>receive</tt></a> and
<a href=udp.html#receivefrom><tt>receivefrom</tt></a>
can be used to retrieve datagrams, the latter returning the IP and port of
the sender as extra return values (thus being slightly less
efficient).
efficient).
</p>
<p>
When communication is performed repeatedly with a single peer, an
application should call the
application should call the
<a href=udp.html#setpeername><tt>setpeername</tt></a> method to specify a
permanent partner. Methods
permanent partner. Methods
<a href=udp.html#sendto><tt>sendto</tt></a> and
<a href=udp.html#receivefrom><tt>receivefrom</tt></a>
<a href=udp.html#receivefrom><tt>receivefrom</tt></a>
can no longer be used, but the method
<a href=udp.html#send><tt>send</tt></a> can be used to send data
directly to the peer, and the method
<a href=udp.html#receive><tt>receive</tt></a>
<a href=udp.html#send><tt>send</tt></a> can be used to send data
directly to the peer, and the method
<a href=udp.html#receive><tt>receive</tt></a>
will only return datagrams originating
from that peer. There is about 30% performance gain due to this practice.
</p>
<p>
To associate an UDP socket with a local address, an application calls the
<a href=udp.html#setsockname><tt>setsockname</tt></a>
To associate an UDP socket with a local address, an application calls the
<a href=udp.html#setsockname><tt>setsockname</tt></a>
method <em>before</em> sending any datagrams. Otherwise, the socket is
automatically bound to an ephemeral address before the first data
transmission and once bound the local address cannot be changed.
transmission and once bound the local address cannot be changed.
The other methods available for UDP sockets are
<a href=udp.html#getpeername><tt>getpeername</tt></a>,
<a href=udp.html#getsockname><tt>getsockname</tt></a>,
<a href=udp.html#getpeername><tt>getpeername</tt></a>,
<a href=udp.html#getsockname><tt>getsockname</tt></a>,
<a href=udp.html#settimeout><tt>settimeout</tt></a>,
<a href=udp.html#setoption><tt>setoption</tt></a> and
<a href=udp.html#close><tt>close</tt></a>.
<a href=udp.html#setoption><tt>setoption</tt></a> and
<a href=udp.html#close><tt>close</tt></a>.
</p>
<p>
Example:
Example:
</p>
<blockquote>
<p>
A simple daytime client, using LuaSocket. The program connects to a remote
server and tries to retrieve the daytime, printing the answer it got or an
error message.
error message.
</p>
<pre class=example>
@ -301,11 +301,11 @@ io.write(assert(udp:receive()))
<h3 id=more>Support modules</h3>
<p> Although not covered in the introduction, LuaSocket offers
<p> Although not covered in the introduction, LuaSocket offers
much more than TCP and UDP functionality. As the library
evolved, support for <a href=http.html>HTTP</a>, <a href=ftp.html>FTP</a>,
and <a href=smtp.html>SMTP</a> were built on top of these. These modules
and many others are covered by the <a href=reference.html>reference manual</a>.
and many others are covered by the <a href=reference.html>reference manual</a>.
</p>
<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->

View File

@ -40,7 +40,7 @@ Pump, Support, Library">
<h2 id="ltn12">LTN12</h2>
<p> The <tt>ltn12</tt> namespace implements the ideas described in
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">
<a href="https://github.com/lunarmodules/luasocket/blob/master/ltn012.md">
LTN012, Filters sources and sinks</a>. This manual simply describes the
functions. Please refer to the LTN for a deeper explanation of the
functionality provided by this module.

Binary file not shown.

View File

@ -54,7 +54,7 @@ MIME is described mainly in
<p>
All functionality provided by the MIME module
follows the ideas presented in
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">
<a href="https://github.com/lunarmodules/luasocket/blob/master/ltn012.md">
LTN012, Filters sources and sinks</a>.
</p>

View File

@ -57,7 +57,7 @@ of the MIME standard, but described mainly
in <a href="http://www.ietf.org/rfc/rfc2046.txt">RFC 2046</a>.</p>
<p> In the description below, good understanding of <a
href="http://lua-users.org/wiki/FiltersSourcesAndSinks"> LTN012, Filters
href="https://github.com/lunarmodules/luasocket/blob/master/ltn012.md"> LTN012, Filters
sources and sinks</a> and the <a href="mime.html">MIME</a> module is
assumed. In fact, the SMTP module was the main reason for their
creation. </p>
@ -122,7 +122,7 @@ smtp.<b>message(</b>mesgt<b>)</b>
<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).
<a href="https://github.com/lunarmodules/luasocket/blob/master/ltn012.md">LTN12</a> source that sends an SMTP message body, possibly multipart (arbitrarily deep).
</p>
<p class="parameters">
@ -155,7 +155,7 @@ multipart-mesgt = {<br>
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>
<a href="https://github.com/lunarmodules/luasocket/blob/master/ltn012.md">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>.
@ -163,7 +163,7 @@ recursively defines each part as an independent message, plus an optional
<p class="return">
The function returns a <em>simple</em>
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a>
<a href="https://github.com/lunarmodules/luasocket/blob/master/ltn012.md">LTN12</a>
source that produces the
message contents as defined by <tt>mesgt</tt>, chunk by chunk.
Hopefully, the following
@ -264,7 +264,7 @@ The sender is given by the e-mail address in the <tt>from</tt> field.
address, or a string
in case there is just one recipient.
The contents of the message are given by a <em>simple</em>
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a>
<a href="https://github.com/lunarmodules/luasocket/blob/master/ltn012.md">LTN12</a>
<tt>source</tt>. Several arguments are optional:
</p>
<ul>
@ -276,7 +276,7 @@ methods if supported by the server (both are unsafe);</li>
<li> <tt>domain</tt>: Domain name used to greet the server; Defaults to the
local machine host name;</li>
<li> <tt>step</tt>:
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a>
<a href="https://github.com/lunarmodules/luasocket/blob/master/ltn012.md">LTN12</a>
pump step function used to pass data from the
source to the server. Defaults to the LTN12 <tt>pump.step</tt> function;</li>
<li><tt>create</tt>: An optional function to be used instead of
@ -308,7 +308,7 @@ Only recipients specified in the <tt>rcpt</tt> list will receive a copy of the
message. Each recipient of an SMTP mail message receives a copy of the
message body along with the headers, and nothing more. The headers
<em>are</em> part of the message and should be produced by the
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a>
<a href="https://github.com/lunarmodules/luasocket/blob/master/ltn012.md">LTN12</a>
<tt>source</tt> function. The <tt>rcpt</tt> list is <em>not</em>
part of the message and will not be sent to anyone.
</p>

View File

@ -164,6 +164,9 @@ Creates and returns a <em>clean</em>
<a href="#try"><tt>try</tt></a>
function that allows for cleanup before the exception
is raised.
This implements the ideas described in
<a href="https://github.com/lunarmodules/luasocket/blob/master/ltn013.md">
LTN012, Using finalized exceptions</a>.
</p>
<p class="parameters">
@ -207,6 +210,9 @@ Converts a function that throws exceptions into a safe function. This
function only catches exceptions thrown by the <a href="#try"><tt>try</tt></a>
and <a href="#newtry"><tt>newtry</tt></a> functions. It does not catch normal
Lua errors.
This implements the ideas described in
<a href="https://github.com/lunarmodules/luasocket/blob/master/ltn013.md">
LTN012, Using finalized exceptions</a>.
</p>
<p class="parameters">
@ -305,7 +311,7 @@ socket.<b>sink(</b>mode, socket<b>)</b>
<p class="description">
Creates an
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a>
<a href="https://github.com/lunarmodules/luasocket/blob/master/ltn012.md">LTN12</a>
sink from a stream socket object.
</p>
@ -380,7 +386,7 @@ socket.<b>source(</b>mode, socket [, length]<b>)</b>
<p class="description">
Creates an
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a>
<a href="https://github.com/lunarmodules/luasocket/blob/master/ltn012.md">LTN12</a>
source from a stream socket object.
</p>
@ -425,6 +431,9 @@ socket.<b>try(</b>ret<sub>1</sub> [, ret<sub>2</sub> ... ret<sub>N</sub>]<b>)</b
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.
This implements the ideas described in
<a href="https://github.com/lunarmodules/luasocket/blob/master/ltn013.md">
LTN012, Using finalized exceptions</a>.
</p>
<p class="parameters">

View File

@ -485,6 +485,12 @@ disables the Nagle's algorithm for the connection;</li>
<li> '<tt>tcp-keepintvl</tt>': value for <tt>TCP_KEEPINTVL</tt> Linux only!!</li>
<li> '<tt>tcp-defer-accept</tt>': value for <tt>TCP_DEFER_ACCEPT</tt> Linux only!!</li>
<li> '<tt>tcp-fastopen</tt>': value for <tt>TCP_FASTOPEN</tt> Linux only!!</li>
<li> '<tt>tcp-fastopen-connect</tt>': value for <tt>TCP_FASTOPEN_CONNECT</tt> Linux only!!</li>
<li> '<tt>ipv6-v6only</tt>':
Setting this option to <tt>true</tt> restricts an <tt>inet6</tt> socket to
sending and receiving only IPv6 packets.</li>

View File

@ -149,8 +149,8 @@ wild-card address).
<!-- gettimeout +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class="name" id="gettimeout">
connected:<b>settimeout(</b>value<b>)</b><br>
unconnected:<b>settimeout(</b>value<b>)</b>
connected:<b>gettimeout()</b><br>
unconnected:<b>gettimeout()</b>
</p>
<p class="description">

View File

@ -1,10 +1,10 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta name="description" content="LuaSocket: URL manipulation">
<meta name="keywords" content="Lua, LuaSocket, URL, Library, Link, Network, Support">
<meta name="keywords" content="Lua, LuaSocket, URL, Library, Link, Network, Support">
<title>LuaSocket: URL support</title>
<link rel="stylesheet" href="reference.css" type="text/css">
</head>
@ -28,7 +28,7 @@
<a href="index.html#download">download</a> &middot;
<a href="installation.html">installation</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a>
<a href="reference.html">reference</a>
</p>
</center>
<hr>
@ -36,21 +36,21 @@
<!-- url ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h2 id="url">URL</h2>
<h2 id="url">URL</h2>
<p>
The <tt>url</tt> namespace provides functions to parse, protect,
and build URLs, as well as functions to compose absolute URLs
from base and relative URLs, according to
from base and relative URLs, according to
<a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>.
</p>
<p>
<p>
To obtain the <tt>url</tt> namespace, run:
</p>
<pre class=example>
-- loads the URL module
-- loads the URL module
local url = require("socket.url")
</pre>
@ -74,7 +74,7 @@ url.<b>absolute(</b>base, relative<b>)</b>
</p>
<p class=description>
Builds an absolute URL from a base URL and a relative URL.
Builds an absolute URL from a base URL and a relative URL.
</p>
<p class=parameters>
@ -130,14 +130,14 @@ url.<b>build(</b>parsed_url<b>)</b>
</p>
<p class=description>
Rebuilds an URL from its parts.
Rebuilds an URL from its parts.
</p>
<p class=parameters>
<tt>Parsed_url</tt> is a table with same components returned by
<a href="#parse"><tt>parse</tt></a>.
Lower level components, if specified,
take precedence over high level components of the URL grammar.
take precedence over high level components of the URL grammar.
</p>
<p class=return>
@ -152,10 +152,10 @@ url.<b>build_path(</b>segments, unsafe<b>)</b>
<p class=description>
Builds a <tt>&lt;path&gt;</tt> component from a list of
<tt>&lt;segment&gt;</tt> parts.
<tt>&lt;segment&gt;</tt> parts.
Before composition, any reserved characters found in a segment are escaped into
their protected form, so that the resulting path is a valid URL path
component.
component.
</p>
<p class=parameters>
@ -165,8 +165,8 @@ characters are left untouched.
</p>
<p class=return>
The function returns a string with the
built <tt>&lt;path&gt;</tt> component.
The function returns a string with the
built <tt>&lt;path&gt;</tt> component.
</p>
<!-- escape +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
@ -178,7 +178,7 @@ url.<b>escape(</b>content<b>)</b>
<p class=description>
Applies the URL escaping content coding to a string
Each byte is encoded as a percent character followed
by the two byte hexadecimal representation of its integer
by the two byte hexadecimal representation of its integer
value.
</p>
@ -270,8 +270,8 @@ url.<b>parse_path(</b>path<b>)</b>
</p>
<p class=description>
Breaks a <tt>&lt;path&gt;</tt> URL component into all its
<tt>&lt;segment&gt;</tt> parts.
Breaks a <tt>&lt;path&gt;</tt> URL component into all its
<tt>&lt;segment&gt;</tt> parts.
</p>
<p class=description>
@ -282,7 +282,7 @@ Breaks a <tt>&lt;path&gt;</tt> URL component into all its
Since some characters are reserved in URLs, they must be escaped
whenever present in a <tt>&lt;path&gt;</tt> component. Therefore, before
returning a list with all the parsed segments, the function removes
escaping from all of them.
escaping from all of them.
</p>
<!-- unescape +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
@ -300,7 +300,7 @@ Removes the URL escaping content coding from a string.
</p>
<p class=return>
The function returns the decoded string.
The function returns the decoded string.
</p>
<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->

View File

@ -1,89 +0,0 @@
This directory contains code that is more useful than the
samples. This code *is* supported.
tftp.lua -- Trivial FTP client
This module implements file retrieval by the TFTP protocol.
Its main use was to test the UDP code, but since someone
found it usefull, I turned it into a module that is almost
official (no uploads, yet).
dict.lua -- Dict client
The dict.lua module started with a cool simple client
for the DICT protocol, written by Luiz Henrique Figueiredo.
This new version has been converted into a library, similar
to the HTTP and FTP libraries, that can be used from within
any luasocket application. Take a look on the source code
and you will be able to figure out how to use it.
lp.lua -- LPD client library
The lp.lua module implements the client part of the Line
Printer Daemon protocol, used to print files on Unix
machines. It is courtesy of David Burgess! See the source
code and the lpr.lua in the examples directory.
b64.lua
qp.lua
eol.lua
These are tiny programs that perform Base64,
Quoted-Printable and end-of-line marker conversions.
get.lua -- file retriever
This little program is a client that uses the FTP and
HTTP code to implement a command line file graber. Just
run
lua get.lua <remote-file> [<local-file>]
to download a remote file (either ftp:// or http://) to
the specified local file. The program also prints the
download throughput, elapsed time, bytes already downloaded
etc during download.
check-memory.lua -- checks memory consumption
This is just to see how much memory each module uses.
dispatch.lua -- coroutine based dispatcher
This is a first try at a coroutine based non-blocking
dispatcher for LuaSocket. Take a look at 'check-links.lua'
and at 'forward.lua' to see how to use it.
check-links.lua -- HTML link checker program
This little program scans a HTML file and checks for broken
links. It is similar to check-links.pl by Jamie Zawinski,
but uses all facilities of the LuaSocket library and the Lua
language. It has not been thoroughly tested, but it should
work. Just run
lua check-links.lua [-n] {<url>} > output
and open the result to see a list of broken links. Make sure
you check the '-n' switch. It runs in non-blocking mode,
using coroutines, and is MUCH faster!
forward.lua -- coroutine based forward server
This is a forward server that can accept several connections
and transfers simultaneously using non-blocking I/O and the
coroutine-based dispatcher. You can run, for example
lua forward.lua 8080:proxy.com:3128
to redirect all local conections to port 8080 to the host
'proxy.com' at port 3128.
unix.c and unix.h
This is an implementation of Unix local domain sockets and
demonstrates how to extend LuaSocket with a new type of
transport. It has been tested on Linux and on Mac OS X.
Good luck,
Diego.

View File

@ -1,4 +0,0 @@
local CRLF = "\013\010"
local input = source.chain(source.file(io.stdin), normalize(CRLF))
local output = sink.file(io.stdout)
pump.all(input, output)

View File

@ -1,17 +0,0 @@
function pump.step(src, snk)
local chunk, src_err = src()
local ret, snk_err = snk(chunk, src_err)
if chunk and ret then return 1
else return nil, src_err or snk_err end
end
function pump.all(src, snk, step)
step = step or pump.step
while true do
local ret, err = step(src, snk)
if not ret then
if err then return nil, err
else return 1 end
end
end
end

View File

@ -1,7 +0,0 @@
local input = source.chain(
source.file(io.open("input.bin", "rb")),
encode("base64"))
local output = sink.chain(
wrap(76),
sink.file(io.open("output.b64", "w")))
pump.all(input, output)

View File

@ -1,34 +0,0 @@
local smtp = require"socket.smtp"
local mime = require"mime"
local ltn12 = require"ltn12"
CRLF = "\013\010"
local message = smtp.message{
headers = {
from = "Sicrano <sicrano@example.com>",
to = "Fulano <fulano@example.com>",
subject = "A message with an attachment"},
body = {
preamble = "Hope you can see the attachment" .. CRLF,
[1] = {
body = "Here is our logo" .. CRLF},
[2] = {
headers = {
["content-type"] = 'image/png; name="luasocket.png"',
["content-disposition"] =
'attachment; filename="luasocket.png"',
["content-description"] = 'LuaSocket logo',
["content-transfer-encoding"] = "BASE64"},
body = ltn12.source.chain(
ltn12.source.file(io.open("luasocket.png", "rb")),
ltn12.filter.chain(
mime.encode("base64"),
mime.wrap()))}}}
assert(smtp.send{
rcpt = "<diego@cs.princeton.edu>",
from = "<diego@cs.princeton.edu>",
server = "localhost",
port = 2525,
source = message})

View File

@ -1,11 +0,0 @@
function filter.cycle(lowlevel, context, extra)
return function(chunk)
local ret
ret, context = lowlevel(context, chunk, extra)
return ret
end
end
function normalize(marker)
return filter.cycle(eol, 0, marker)
end

View File

@ -1,15 +0,0 @@
local function chainpair(f1, f2)
return function(chunk)
local ret = f2(f1(chunk))
if chunk then return ret
else return (ret or "") .. (f2() or "") end
end
end
function filter.chain(...)
local f = select(1, ...)
for i = 2, select('#', ...) do
f = chainpair(f, select(i, ...))
end
return f
end

View File

@ -1,5 +0,0 @@
local qp = filter.chain(normalize(CRLF), encode("quoted-printable"),
wrap("quoted-printable"))
local input = source.chain(source.file(io.stdin), qp)
local output = sink.file(io.stdout)
pump.all(input, output)

View File

@ -1,15 +0,0 @@
function source.empty(err)
return function()
return nil, err
end
end
function source.file(handle, io_err)
if handle then
return function()
local chunk = handle:read(20)
if not chunk then handle:close() end
return chunk
end
else return source.empty(io_err or "unable to open file") end
end

View File

@ -1,14 +0,0 @@
function source.chain(src, f)
return function()
if not src then
return nil
end
local chunk, err = src()
if not chunk then
src = nil
return f(nil)
else
return f(chunk)
end
end
end

View File

@ -1,16 +0,0 @@
function sink.table(t)
t = t or {}
local f = function(chunk, err)
if chunk then table.insert(t, chunk) end
return 1
end
return f, t
end
local function null()
return 1
end
function sink.null()
return null
end

View File

@ -1,5 +0,0 @@
local input = source.file(io.stdin)
local output, t = sink.table()
output = sink.chain(normalize(CRLF), output)
pump.all(input, output)
io.write(table.concat(t))

View File

@ -1,3 +0,0 @@
for chunk in source.file(io.stdin) do
io.write(chunk)
end

View File

@ -1,54 +0,0 @@
#include "lua.h"
#include "lauxlib.h"
#define CR '\xD'
#define LF '\xA'
#define CRLF "\xD\xA"
#define candidate(c) (c == CR || c == LF)
static int pushchar(int c, int last, const char *marker,
luaL_Buffer *buffer) {
if (candidate(c)) {
if (candidate(last)) {
if (c == last)
luaL_addstring(buffer, marker);
return 0;
} else {
luaL_addstring(buffer, marker);
return c;
}
} else {
luaL_putchar(buffer, c);
return 0;
}
}
static int eol(lua_State *L) {
int context = luaL_checkint(L, 1);
size_t isize = 0;
const char *input = luaL_optlstring(L, 2, NULL, &isize);
const char *last = input + isize;
const char *marker = luaL_optstring(L, 3, CRLF);
luaL_Buffer buffer;
luaL_buffinit(L, &buffer);
if (!input) {
lua_pushnil(L);
lua_pushnumber(L, 0);
return 2;
}
while (input < last)
context = pushchar(*input++, context, marker, &buffer);
luaL_pushresult(&buffer);
lua_pushnumber(L, context);
return 2;
}
static luaL_reg func[] = {
{ "eol", eol },
{ NULL, NULL }
};
int luaopen_gem(lua_State *L) {
luaL_openlib(L, "gem", func, 0);
return 0;
}

View File

@ -1,206 +0,0 @@
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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1,695 +0,0 @@
\documentclass[10pt]{article}
\usepackage{fancyvrb}
\usepackage{url}
\DefineVerbatimEnvironment{lua}{Verbatim}{fontsize=\small,commandchars=\@\#\%}
\DefineVerbatimEnvironment{C}{Verbatim}{fontsize=\small,commandchars=\@\#\%}
\DefineVerbatimEnvironment{mime}{Verbatim}{fontsize=\small,commandchars=\$\#\%}
\newcommand{\stick}[1]{\vbox{\setlength{\parskip}{0pt}#1}}
\newcommand{\bl}{\ensuremath{\mathtt{\backslash}}}
\newcommand{\CR}{\texttt{CR}}
\newcommand{\LF}{\texttt{LF}}
\newcommand{\CRLF}{\texttt{CR~LF}}
\newcommand{\nil}{\texttt{nil}}
\title{Filters, sources, sinks, and pumps\\
{\large or Functional programming for the rest of us}}
\author{Diego Nehab}
\begin{document}
\maketitle
\begin{abstract}
Certain data processing operations can be implemented in the
form of filters. A filter is a function that can process
data received in consecutive invocations, returning partial
results each time it is called. Examples of operations that
can be implemented as filters include the end-of-line
normalization for text, Base64 and Quoted-Printable transfer
content encodings, the breaking of text into lines, SMTP
dot-stuffing, and there are many others. Filters become
even more powerful when we allow them to be chained together
to create composite filters. In this context, filters can be
seen as the internal links in a chain of data transformations.
Sources and sinks are the corresponding end points in these
chains. A source is a function that produces data, chunk by
chunk, and a sink is a function that takes data, chunk by
chunk. Finally, pumps are procedures that actively drive
data from a source to a sink, and indirectly through all
intervening filters. In this article, we describe the design of an
elegant interface for filters, sources, sinks, chains, and
pumps, and we illustrate each step with concrete examples.
\end{abstract}
\section{Introduction}
Within the realm of networking applications, we are often
required to apply transformations to streams of data. Examples
include the end-of-line normalization for text, Base64 and
Quoted-Printable transfer content encodings, breaking text
into lines with a maximum number of columns, SMTP
dot-stuffing, \texttt{gzip} compression, HTTP chunked
transfer coding, and the list goes on.
Many complex tasks require a combination of two or more such
transformations, and therefore a general mechanism for
promoting reuse is desirable. In the process of designing
\texttt{LuaSocket~2.0}, we repeatedly faced this problem.
The solution we reached proved to be very general and
convenient. It is based on the concepts of filters, sources,
sinks, and pumps, which we introduce below.
\emph{Filters} are functions that can be repeatedly invoked
with chunks of input, successively returning processed
chunks of output. Naturally, the result of
concatenating all the output chunks must be the same as the
result of applying the filter to the concatenation of all
input chunks. In fancier language, filters \emph{commute}
with the concatenation operator. More importantly, filters
must handle input data correctly no matter how the stream
has been split into chunks.
A \emph{chain} is a function that transparently combines the
effect of one or more filters. The interface of a chain is
indistinguishable from the interface of its component
filters. This allows a chained filter to be used wherever
an atomic filter is accepted. In particular, chains can be
themselves chained to create arbitrarily complex operations.
Filters can be seen as internal nodes in a network through
which data will flow, potentially being transformed many
times along the way. Chains connect these nodes together.
The initial and final nodes of the network are
\emph{sources} and \emph{sinks}, respectively. Less
abstractly, a source is a function that produces new chunks
of data every time it is invoked. Conversely, sinks are
functions that give a final destination to the chunks of
data they receive in sucessive calls. Naturally, sources
and sinks can also be chained with filters to produce
filtered sources and sinks.
Finally, filters, chains, sources, and sinks are all passive
entities: they must be repeatedly invoked in order for
anything to happen. \emph{Pumps} provide the driving force
that pushes data through the network, from a source to a
sink, and indirectly through all intervening filters.
In the following sections, we start with a simplified
interface, which we later refine. The evolution we present
is not contrived: it recreates the steps we ourselves
followed as we consolidated our understanding of these
concepts within our application domain.
\subsection{A simple example}
The end-of-line normalization of text is a good
example to motivate our initial filter interface.
Assume we are given text in an unknown end-of-line
convention (including possibly mixed conventions) out of the
commonly found Unix (\LF), Mac OS (\CR), and
DOS (\CRLF) conventions. We would like to be able to
use the folowing code to normalize the end-of-line markers:
\begin{quote}
\begin{lua}
@stick#
local CRLF = "\013\010"
local input = source.chain(source.file(io.stdin), normalize(CRLF))
local output = sink.file(io.stdout)
pump.all(input, output)
%
\end{lua}
\end{quote}
This program should read data from the standard input stream
and normalize the end-of-line markers to the canonic
\CRLF\ marker, as defined by the MIME standard.
Finally, the normalized text should be sent to the standard output
stream. We use a \emph{file source} that produces data from
standard input, and chain it with a filter that normalizes
the data. The pump then repeatedly obtains data from the
source, and passes it to the \emph{file sink}, which sends
it to the standard output.
In the code above, the \texttt{normalize} \emph{factory} is a
function that creates our normalization filter, which
replaces any end-of-line marker with the canonic marker.
The initial filter interface is
trivial: a filter function receives a chunk of input data,
and returns a chunk of processed data. When there are no
more input data left, the caller notifies the filter by invoking
it with a \nil\ chunk. The filter responds by returning
the final chunk of processed data (which could of course be
the empty string).
Although the interface is extremely simple, the
implementation is not so obvious. A normalization filter
respecting this interface needs to keep some kind of context
between calls. This is because a chunk boundary may lie between
the \CR\ and \LF\ characters marking the end of a single line. This
need for contextual storage motivates the use of
factories: each time the factory is invoked, it returns a
filter with its own context so that we can have several
independent filters being used at the same time. For
efficiency reasons, we must avoid the obvious solution of
concatenating all the input into the context before
producing any output chunks.
To that end, we break the implementation into two parts:
a low-level filter, and a factory of high-level filters. The
low-level filter is implemented in C and does not maintain
any context between function calls. The high-level filter
factory, implemented in Lua, creates and returns a
high-level filter that maintains whatever context the low-level
filter needs, but isolates the user from its internal
details. That way, we take advantage of C's efficiency to
perform the hard work, and take advantage of Lua's
simplicity for the bookkeeping.
\subsection{The Lua part of the filter}
Below is the complete implementation of the factory of high-level
end-of-line normalization filters:
\begin{quote}
\begin{lua}
@stick#
function filter.cycle(lowlevel, context, extra)
return function(chunk)
local ret
ret, context = lowlevel(context, chunk, extra)
return ret
end
end
%
@stick#
function normalize(marker)
return filter.cycle(eol, 0, marker)
end
%
\end{lua}
\end{quote}
The \texttt{normalize} factory simply calls a more generic
factory, the \texttt{cycle}~factory, passing the low-level
filter~\texttt{eol}. The \texttt{cycle}~factory receives a
low-level filter, an initial context, and an extra
parameter, and returns a new high-level filter. Each time
the high-level filer is passed a new chunk, it invokes the
low-level filter with the previous context, the new chunk,
and the extra argument. It is the low-level filter that
does all the work, producing the chunk of processed data and
a new context. The high-level filter then replaces its
internal context, and returns the processed chunk of data to
the user. Notice that we take advantage of Lua's lexical
scoping to store the context in a closure between function
calls.
\subsection{The C part of the filter}
As for the low-level filter, we must first accept
that there is no perfect solution to the end-of-line marker
normalization problem. The difficulty comes from an
inherent ambiguity in the definition of empty lines within
mixed input. However, the following solution works well for
any consistent input, as well as for non-empty lines in
mixed input. It also does a reasonable job with empty lines
and serves as a good example of how to implement a low-level
filter.
The idea is to consider both \CR\ and~\LF\ as end-of-line
\emph{candidates}. We issue a single break if any candidate
is seen alone, or if it is followed by a different
candidate. In other words, \CR~\CR~and \LF~\LF\ each issue
two end-of-line markers, whereas \CR~\LF~and \LF~\CR\ issue
only one marker each. It is easy to see that this method
correctly handles the most common end-of-line conventions.
With this in mind, we divide the low-level filter into two
simple functions. The inner function~\texttt{pushchar} performs the
normalization itself. It takes each input character in turn,
deciding what to output and how to modify the context. The
context tells if the last processed character was an
end-of-line candidate, and if so, which candidate it was.
For efficiency, we use Lua's auxiliary library's buffer
interface:
\begin{quote}
\begin{C}
@stick#
@#define candidate(c) (c == CR || c == LF)
static int pushchar(int c, int last, const char *marker,
luaL_Buffer *buffer) {
if (candidate(c)) {
if (candidate(last)) {
if (c == last)
luaL_addstring(buffer, marker);
return 0;
} else {
luaL_addstring(buffer, marker);
return c;
}
} else {
luaL_pushchar(buffer, c);
return 0;
}
}
%
\end{C}
\end{quote}
The outer function~\texttt{eol} simply interfaces with Lua.
It receives the context and input chunk (as well as an
optional custom end-of-line marker), and returns the
transformed output chunk and the new context.
Notice that if the input chunk is \nil, the operation
is considered to be finished. In that case, the loop will
not execute a single time and the context is reset to the
initial state. This allows the filter to be reused many
times:
\begin{quote}
\begin{C}
@stick#
static int eol(lua_State *L) {
int context = luaL_checkint(L, 1);
size_t isize = 0;
const char *input = luaL_optlstring(L, 2, NULL, &isize);
const char *last = input + isize;
const char *marker = luaL_optstring(L, 3, CRLF);
luaL_Buffer buffer;
luaL_buffinit(L, &buffer);
if (!input) {
lua_pushnil(L);
lua_pushnumber(L, 0);
return 2;
}
while (input < last)
context = pushchar(*input++, context, marker, &buffer);
luaL_pushresult(&buffer);
lua_pushnumber(L, context);
return 2;
}
%
\end{C}
\end{quote}
When designing filters, the challenging part is usually
deciding what to store in the context. For line breaking, for
instance, it could be the number of bytes that still fit in the
current line. For Base64 encoding, it could be a string
with the bytes that remain after the division of the input
into 3-byte atoms. The MIME module in the \texttt{LuaSocket}
distribution has many other examples.
\section{Filter chains}
Chains greatly increase the power of filters. For example,
according to the standard for Quoted-Printable encoding,
text should be normalized to a canonic end-of-line marker
prior to encoding. After encoding, the resulting text must
be broken into lines of no more than 76 characters, with the
use of soft line breaks (a line terminated by the \texttt{=}
sign). To help specifying complex transformations like
this, we define a chain factory that creates a composite
filter from one or more filters. A chained filter passes
data through all its components, and can be used wherever a
primitive filter is accepted.
The chaining factory is very simple. The auxiliary
function~\texttt{chainpair} chains two filters together,
taking special care if the chunk is the last. This is
because the final \nil\ chunk notification has to be
pushed through both filters in turn:
\begin{quote}
\begin{lua}
@stick#
local function chainpair(f1, f2)
return function(chunk)
local ret = f2(f1(chunk))
if chunk then return ret
else return ret .. f2() end
end
end
%
@stick#
function filter.chain(...)
local f = select(1, ...)
for i = 2, select('@#', ...) do
f = chainpair(f, select(i, ...))
end
return f
end
%
\end{lua}
\end{quote}
Thanks to the chain factory, we can
define the Quoted-Printable conversion as such:
\begin{quote}
\begin{lua}
@stick#
local qp = filter.chain(normalize(CRLF), encode("quoted-printable"),
wrap("quoted-printable"))
local input = source.chain(source.file(io.stdin), qp)
local output = sink.file(io.stdout)
pump.all(input, output)
%
\end{lua}
\end{quote}
\section{Sources, sinks, and pumps}
The filters we introduced so far act as the internal nodes
in a network of transformations. Information flows from node
to node (or rather from one filter to the next) and is
transformed along the way. Chaining filters together is our
way to connect nodes in this network. As the starting point
for the network, we need a source node that produces the
data. In the end of the network, we need a sink node that
gives a final destination to the data.
\subsection{Sources}
A source returns the next chunk of data each time it is
invoked. When there is no more data, it simply returns~\nil.
In the event of an error, the source can inform the
caller by returning \nil\ followed by the error message.
Below are two simple source factories. The \texttt{empty} source
returns no data, possibly returning an associated error
message. The \texttt{file} source yields the contents of a file
in a chunk by chunk fashion:
\begin{quote}
\begin{lua}
@stick#
function source.empty(err)
return function()
return nil, err
end
end
%
@stick#
function source.file(handle, io_err)
if handle then
return function()
local chunk = handle:read(2048)
if not chunk then handle:close() end
return chunk
end
else return source.empty(io_err or "unable to open file") end
end
%
\end{lua}
\end{quote}
\subsection{Filtered sources}
A filtered source passes its data through the
associated filter before returning it to the caller.
Filtered sources are useful when working with
functions that get their input data from a source (such as
the pumps in our examples). By chaining a source with one or
more filters, such functions can be transparently provided
with filtered data, with no need to change their interfaces.
Here is a factory that does the job:
\begin{quote}
\begin{lua}
@stick#
function source.chain(src, f)
return function()
if not src then
return nil
end
local chunk, err = src()
if not chunk then
src = nil
return f(nil)
else
return f(chunk)
end
end
end
%
\end{lua}
\end{quote}
\subsection{Sinks}
Just as we defined an interface for a source of data, we can
also define an interface for a data destination. We call
any function respecting this interface a sink. In our first
example, we used a file sink connected to the standard
output.
Sinks receive consecutive chunks of data, until the end of
data is signaled by a \nil\ input chunk. A sink can be
notified of an error with an optional extra argument that
contains the error message, following a \nil\ chunk.
If a sink detects an error itself, and
wishes not to be called again, it can return \nil,
followed by an error message. A return value that
is not \nil\ means the sink will accept more data.
Below are two useful sink factories.
The table factory creates a sink that stores
individual chunks into an array. The data can later be
efficiently concatenated into a single string with Lua's
\texttt{table.concat} library function. The \texttt{null} sink
simply discards the chunks it receives:
\begin{quote}
\begin{lua}
@stick#
function sink.table(t)
t = t or {}
local f = function(chunk, err)
if chunk then table.insert(t, chunk) end
return 1
end
return f, t
end
%
@stick#
local function null()
return 1
end
function sink.null()
return null
end
%
\end{lua}
\end{quote}
Naturally, filtered sinks are just as useful as filtered
sources. A filtered sink passes each chunk it receives
through the associated filter before handing it down to the
original sink. In the following example, we use a source
that reads from the standard input. The input chunks are
sent to a table sink, which has been coupled with a
normalization filter. The filtered chunks are then
concatenated from the output array, and finally sent to
standard out:
\begin{quote}
\begin{lua}
@stick#
local input = source.file(io.stdin)
local output, t = sink.table()
output = sink.chain(normalize(CRLF), output)
pump.all(input, output)
io.write(table.concat(t))
%
\end{lua}
\end{quote}
\subsection{Pumps}
Although not on purpose, our interface for sources is
compatible with Lua iterators. That is, a source can be
neatly used in conjunction with \texttt{for} loops. Using
our file source as an iterator, we can write the following
code:
\begin{quote}
\begin{lua}
@stick#
for chunk in source.file(io.stdin) do
io.write(chunk)
end
%
\end{lua}
\end{quote}
Loops like this will always be present because everything
we designed so far is passive. Sources, sinks, filters: none
of them can do anything on their own. The operation of
pumping all data a source can provide into a sink is so
common that it deserves its own function:
\begin{quote}
\begin{lua}
@stick#
function pump.step(src, snk)
local chunk, src_err = src()
local ret, snk_err = snk(chunk, src_err)
if chunk and ret then return 1
else return nil, src_err or snk_err end
end
%
@stick#
function pump.all(src, snk, step)
step = step or pump.step
while true do
local ret, err = step(src, snk)
if not ret then
if err then return nil, err
else return 1 end
end
end
end
%
\end{lua}
\end{quote}
The \texttt{pump.step} function moves one chunk of data from
the source to the sink. The \texttt{pump.all} function takes
an optional \texttt{step} function and uses it to pump all the
data from the source to the sink.
Here is an example that uses the Base64 and the
line wrapping filters from the \texttt{LuaSocket}
distribution. The program reads a binary file from
disk and stores it in another file, after encoding it to the
Base64 transfer content encoding:
\begin{quote}
\begin{lua}
@stick#
local input = source.chain(
source.file(io.open("input.bin", "rb")),
encode("base64"))
local output = sink.chain(
wrap(76),
sink.file(io.open("output.b64", "w")))
pump.all(input, output)
%
\end{lua}
\end{quote}
The way we split the filters here is not intuitive, on
purpose. Alternatively, we could have chained the Base64
encode filter and the line-wrap filter together, and then
chain the resulting filter with either the file source or
the file sink. It doesn't really matter.
\section{Exploding filters}
Our current filter interface has one serious shortcoming.
Consider for example a \texttt{gzip} decompression filter.
During decompression, a small input chunk can be exploded
into a huge amount of data. To address this problem, we
decided to change the filter interface and allow exploding
filters to return large quantities of output data in a chunk
by chunk manner.
More specifically, after passing each chunk of input to
a filter, and collecting the first chunk of output, the
user must now loop to receive other chunks from the filter until no
filtered data is left. Within these secondary calls, the
caller passes an empty string to the filter. The filter
responds with an empty string when it is ready for the next
input chunk. In the end, after the user passes a
\nil\ chunk notifying the filter that there is no
more input data, the filter might still have to produce too
much output data to return in a single chunk. The user has
to loop again, now passing \nil\ to the filter each time,
until the filter itself returns \nil\ to notify the
user it is finally done.
Fortunately, it is very easy to modify a filter to respect
the new interface. In fact, the end-of-line translation
filter we presented earlier already conforms to it. The
complexity is encapsulated within the chaining functions,
which must now include a loop. Since these functions only
have to be written once, the user is rarely affected.
Interestingly, the modifications do not have a measurable
negative impact in the performance of filters that do
not need the added flexibility. On the other hand, for a
small price in complexity, the changes make exploding
filters practical.
\section{A complex example}
The LTN12 module in the \texttt{LuaSocket} distribution
implements all the ideas we have described. The MIME
and SMTP modules are tightly integrated with LTN12,
and can be used to showcase the expressive power of filters,
sources, sinks, and pumps. Below is an example
of how a user would proceed to define and send a
multipart message, with attachments, using \texttt{LuaSocket}:
\begin{quote}
\begin{mime}
local smtp = require"socket.smtp"
local mime = require"mime"
local ltn12 = require"ltn12"
local message = smtp.message{
headers = {
from = "Sicrano <sicrano@example.com>",
to = "Fulano <fulano@example.com>",
subject = "A message with an attachment"},
body = {
preamble = "Hope you can see the attachment" .. CRLF,
[1] = {
body = "Here is our logo" .. CRLF},
[2] = {
headers = {
["content-type"] = 'image/png; name="luasocket.png"',
["content-disposition"] =
'attachment; filename="luasocket.png"',
["content-description"] = 'LuaSocket logo',
["content-transfer-encoding"] = "BASE64"},
body = ltn12.source.chain(
ltn12.source.file(io.open("luasocket.png", "rb")),
ltn12.filter.chain(
mime.encode("base64"),
mime.wrap()))}}}
assert(smtp.send{
rcpt = "<fulano@example.com>",
from = "<sicrano@example.com>",
source = message})
\end{mime}
\end{quote}
The \texttt{smtp.message} function receives a table
describing the message, and returns a source. The
\texttt{smtp.send} function takes this source, chains it with the
SMTP dot-stuffing filter, connects a socket sink
with the server, and simply pumps the data. The message is never
assembled in memory. Everything is produced on demand,
transformed in small pieces, and sent to the server in chunks,
including the file attachment which is loaded from disk and
encoded on the fly. It just works.
\section{Conclusions}
In this article, we introduced the concepts of filters,
sources, sinks, and pumps to the Lua language. These are
useful tools for stream processing in general. Sources provide
a simple abstraction for data acquisition. Sinks provide an
abstraction for final data destinations. Filters define an
interface for data transformations. The chaining of
filters, sources and sinks provides an elegant way to create
arbitrarily complex data transformations from simpler
components. Pumps simply push the data through.
\section{Acknowledgements}
The concepts described in this text are the result of long
discussions with David Burgess. A version of this text has
been released on-line as the Lua Technical Note 012, hence
the name of the corresponding LuaSocket module, LTN12. Wim
Couwenberg contributed to the implementation of the module,
and Adrian Sietsma was the first to notice the
correspondence between sources and Lua iterators.
\end{document}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1,14 +0,0 @@
ltn012.pdf: ltn012.ps
./myps2pdf ltn012.ps
ltn012.ps: ltn012.dvi
dvips -G0 -t letter -o ltn012.ps ltn012.dvi
ltn012.dvi: ltn012.tex
latex ltn012
clean:
rm -f *~ *.log *.aux *.bbl *.blg ltn012.pdf ltn012.ps ltn012.dvi ltn012.lof ltn012.toc ltn012.lot
pdf: ltn012.pdf
open ltn012.pdf

View File

@ -1,113 +0,0 @@
#!/bin/sh -
do_opt=1
best=0
rot=0
a4=0
eps=0
usage="Usage: $0 [-no_opt] [-best] [-rot] [-a4] [-eps] in.ps [out.pdf]"
case "x$1" in
"x-no_opt") do_opt=0 ; shift ;;
esac
case "x$1" in
"x-best") best=1 ; shift ;;
esac
case "x$1" in
"x-rot") rot=1 ; shift ;;
esac
case "x$1" in
"x-a4") a4=1 ; shift ;;
esac
case "x$1" in
"x-eps") eps=1 ; shift ;;
esac
case $# in
2) ifilename=$1 ; ofilename=$2 ;;
1) ifilename=$1
if `echo $1 | grep -i '\.e*ps$' > /dev/null`
then
ofilename=`echo $1 | sed 's/\..*$/.pdf/'`
else
echo "$usage" 1>&2
exit 1
fi ;;
*) echo "$usage" 1>&2 ; exit 1 ;;
esac
if [ $best == 1 ]
then
options="-dPDFSETTINGS=/prepress \
-r1200 \
-dMonoImageResolution=1200 \
-dGrayImageResolution=1200 \
-dColorImageResolution=1200 \
-dDownsampleMonoImages=false \
-dDownsampleGrayImages=false \
-dDownsampleColorImages=false \
-dAutoFilterMonoImages=false \
-dAutoFilterGrayImages=false \
-dAutoFilterColorImages=false \
-dMonoImageFilter=/FlateEncode \
-dGrayImageFilter=/FlateEncode \
-dColorImageFilter=/FlateEncode"
else
options="-dPDFSETTINGS=/prepress \
-r600 \
-dDownsampleMonoImages=true \
-dDownsampleGrayImages=true \
-dDownsampleColorImages=true \
-dMonoImageDownsampleThreshold=2.0 \
-dGrayImageDownsampleThreshold=1.5 \
-dColorImageDownsampleThreshold=1.5 \
-dMonoImageResolution=600 \
-dGrayImageResolution=600 \
-dColorImageResolution=600 \
-dAutoFilterMonoImages=false \
-dMonoImageFilter=/FlateEncode \
-dAutoFilterGrayImages=true \
-dAutoFilterColorImages=true"
fi
if [ $rot == 1 ]
then
options="$options -dAutoRotatePages=/PageByPage"
fi
if [ $eps == 1 ]
then
options="$options -dEPSCrop"
fi
set -x
if [ $a4 == 1 ]
then
# Resize from A4 to letter size
psresize -Pa4 -pletter "$ifilename" myps2pdf.temp.ps
ifilename=myps2pdf.temp.ps
fi
gs -q -dSAFER -dNOPAUSE -dBATCH \
-sDEVICE=pdfwrite -sPAPERSIZE=letter -sOutputFile=myps2pdf.temp.pdf \
-dCompatibilityLevel=1.3 \
$options \
-dMaxSubsetPct=100 \
-dSubsetFonts=true \
-dEmbedAllFonts=true \
-dColorConversionStrategy=/LeaveColorUnchanged \
-dDoThumbnails=true \
-dPreserveEPSInfo=true \
-c .setpdfwrite -f "$ifilename"
if [ $do_opt == 1 ]
then
pdfopt myps2pdf.temp.pdf $ofilename
else
mv myps2pdf.temp.pdf $ofilename
fi
rm -f myps2pdf.temp.pdf myps2pdf.temp.ps

View File

@ -1,25 +0,0 @@
source = {}
sink = {}
pump = {}
filter = {}
-- source.chain
dofile("ex6.lua")
-- source.file
dofile("ex5.lua")
-- normalize
require"gem"
eol = gem.eol
dofile("ex2.lua")
-- sink.file
require"ltn12"
sink.file = ltn12.sink.file
-- pump.all
dofile("ex10.lua")
-- run test
dofile("ex1.lua")

View File

@ -1,5 +0,0 @@
this is a test file
it should have been saved as lf eol
but t1.lua will convert it to crlf eol
otherwise it is broken!

View File

@ -1,36 +0,0 @@
source = {}
sink = {}
pump = {}
filter = {}
-- filter.chain
dofile("ex3.lua")
-- normalize
require"gem"
eol = gem.eol
dofile("ex2.lua")
-- encode
require"mime"
encode = mime.encode
-- wrap
wrap = mime.wrap
-- source.chain
dofile("ex6.lua")
-- source.file
dofile("ex5.lua")
-- sink.file
require"ltn12"
sink.file = ltn12.sink.file
-- pump.all
dofile("ex10.lua")
-- run test
CRLF = "\013\010"
dofile("ex4.lua")

View File

@ -1,4 +0,0 @@
esse é um texto com acentos
quoted-printable tem que quebrar linhas longas, com mais que 76 linhas de texto
fora que as quebras de linhas têm que ser normalizadas
vamos ver o que dá isso aqui

View File

@ -1,5 +0,0 @@
esse =E9 um texto com acentos
quoted-printable tem que quebrar linhas longas, com mais que 76 linhas de t=
exto
fora que as quebras de linhas t=EAm que ser normalizadas
vamos ver o que d=E1 isso aqui

View File

@ -1,25 +0,0 @@
source = {}
sink = {}
pump = {}
filter = {}
-- source.file
dofile("ex5.lua")
-- sink.table
dofile("ex7.lua")
-- sink.chain
require"ltn12"
sink.chain = ltn12.sink.chain
-- normalize
require"gem"
eol = gem.eol
dofile("ex2.lua")
-- pump.all
dofile("ex10.lua")
-- run test
dofile("ex8.lua")

View File

@ -1,10 +0,0 @@
source = {}
sink = {}
pump = {}
filter = {}
-- source.file
dofile("ex5.lua")
-- run test
dofile("ex9.lua")

View File

@ -1,30 +0,0 @@
source = {}
sink = {}
pump = {}
filter = {}
-- source.chain
dofile("ex6.lua")
-- source.file
dofile("ex5.lua")
-- encode
require"mime"
encode = mime.encode
-- sink.chain
require"ltn12"
sink.chain = ltn12.sink.chain
-- wrap
wrap = mime.wrap
-- sink.file
sink.file = ltn12.sink.file
-- pump.all
dofile("ex10.lua")
-- run test
dofile("ex11.lua")

View File

@ -1,46 +0,0 @@
function readfile(n)
local f = io.open(n, "rb")
local s = f:read("*a")
f:close()
return s
end
lf = readfile("t1lf.txt")
os.remove("t1crlf.txt")
os.execute("lua t1.lua < t1lf.txt > t1crlf.txt")
crlf = readfile("t1crlf.txt")
assert(crlf == string.gsub(lf, "\010", "\013\010"), "broken")
gt = readfile("t2gt.qp")
os.remove("t2.qp")
os.execute("lua t2.lua < t2.txt > t2.qp")
t2 = readfile("t2.qp")
assert(gt == t2, "broken")
os.remove("t1crlf.txt")
os.execute("lua t3.lua < t1lf.txt > t1crlf.txt")
crlf = readfile("t1crlf.txt")
assert(crlf == string.gsub(lf, "\010", "\013\010"), "broken")
t = readfile("test.lua")
os.execute("lua t4.lua < test.lua > t")
t2 = readfile("t")
assert(t == t2, "broken")
os.remove("output.b64")
gt = readfile("gt.b64")
os.execute("lua t5.lua")
t5 = readfile("output.b64")
assert(gt == t5, "failed")
print("1 2 5 6 10 passed")
print("2 3 4 5 6 10 passed")
print("2 5 6 7 8 10 passed")
print("5 9 passed")
print("5 6 10 11 passed")
os.remove("t")
os.remove("t2.qp")
os.remove("t1crlf.txt")
os.remove("t11.b64")
os.remove("output.b64")

390
ltn012.md Normal file
View File

@ -0,0 +1,390 @@
# Filters, sources and sinks: design, motivation and examples
### or Functional programming for the rest of us
by DiegoNehab
## Abstract
Certain operations can be implemented in the form of filters. A filter is a function that processes data received in consecutive function calls, returning partial results chunk by chunk. Examples of operations that can be implemented as filters include the end-of-line normalization for text, Base64 and Quoted-Printable transfer content encodings, the breaking of text into lines, SMTP byte stuffing, and there are many others. Filters become even more powerful when we allow them to be chained together to create composite filters. Filters can be seen as middle nodes in a chain of data transformations. Sources an sinks are the corresponding end points of these chains. A source is a function that produces data, chunk by chunk, and a sink is a function that takes data, chunk by chunk. In this technical note, we define an elegant interface for filters, sources, sinks and chaining. We evolve our interface progressively, until we reach a high degree of generality. We discuss difficulties that arise during the implementation of this interface and we provide solutions and examples.
## Introduction
Applications sometimes have too much information to process to fit in memory and are thus forced to process data in smaller parts. Even when there is enough memory, processing all the data atomically may take long enough to frustrate a user that wants to interact with the application. Furthermore, complex transformations can often be defined as series of simpler operations. Several different complex transformations might share the same simpler operations, so that an uniform interface to combine them is desirable. The following concepts constitute our solution to these problems.
"Filters" are functions that accept successive chunks of input, and produce successive chunks of output. Furthermore, the result of concatenating all the output data is the same as the result of applying the filter over the concatenation of the input data. As a consequence, boundaries are irrelevant: filters have to handle input data split arbitrarily by the user.
A "chain" is a function that combines the effect of two (or more) other functions, but whose interface is indistinguishable from the interface of one of its components. Thus, a chained filter can be used wherever an atomic filter can be used. However, its effect on data is the combined effect of its component filters. Note that, as a consequence, chains can be chained themselves to create arbitrarily complex operations that can be used just like atomic operations.
Filters can be seen as internal nodes in a network through which data flows, potentially being transformed along its way. Chains connect these nodes together. To complete the picture, we need "sources" and "sinks" as initial and final nodes of the network, respectively. Less abstractly, a source is a function that produces new data every time it is called. On the other hand, sinks are functions that give a final destination to the data they receive. Naturally, sources and sinks can be chained with filters.
Finally, filters, chains, sources, and sinks are all passive entities: they need to be repeatedly called in order for something to happen. "Pumps" provide the driving force that pushes data through the network, from a source to a sink.
Hopefully, these concepts will become clear with examples. In the following sections, we start with simplified interfaces, which we improve several times until we can find no obvious shortcomings. The evolution we present is not contrived: it follows the steps we followed ourselves as we consolidated our understanding of these concepts.
### A concrete example
Some data transformations are easier to implement as filters than others. Examples of operations that can be implemented as filters include the end-of-line normalization for text, the Base64 and Quoted-Printable transfer content encodings, the breaking of text into lines, SMTP byte stuffing, and many others. Let's use the end-of-line normalization as an example to define our initial filter interface. We later discuss why the implementation might not be trivial.
Assume we are given text in an unknown end-of-line convention (including possibly mixed conventions) out of the commonly found Unix (LF), Mac OS (CR), and DOS (CRLF) conventions. We would like to be able to write code like the following:
```lua
input = source.chain(source.file(io.stdin), normalize("\r\n"))
output = sink.file(io.stdout)
pump(input, output)
```
This program should read data from the standard input stream and normalize the end-of-line markers to the canonic CRLF marker defined by the MIME standard, finally sending the results to the standard output stream. For that, we use a "file source" to produce data from standard input, and chain it with a filter that normalizes the data. The pump then repeatedly gets data from the source, and moves it to the "file sink" that sends it to standard output.
To make the discussion even more concrete, we start by discussing the implementation of the normalization filter. The `normalize` "factory" is a function that creates such a filter. Our initial filter interface is as follows: the filter receives a chunk of input data, and returns a chunk of processed data. When there is no more input data, the user notifies the filter by invoking it with a `nil` chunk. The filter then returns the final chunk of processed data.
Although the interface is extremely simple, the implementation doesn't seem so obvious. Any filter respecting this interface needs to keep some kind of context between calls. This is because chunks can be broken between the CR and LF characters marking the end of a line. This need for context storage is what motivates the use of factories: each time the factory is called, it returns a filter with its own context so that we can have several independent filters being used at the same time. For the normalization filter, we know that the obvious solution (i.e. concatenating all the input into the context before producing any output) is not good enough, so we will have to find another way.
We will break the implementation in two parts: a low-level filter, and a factory of high-level filters. The low-level filter will be implemented in C and will not carry any context between function calls. The high-level filter factory, implemented in Lua, will create and return a high-level filter that keeps whatever context the low-level filter needs, but isolates the user from its internal details. That way, we take advantage of C's efficiency to perform the dirty work, and take advantage of Lua's simplicity for the bookkeeping.
### The Lua part of the implementation
Below is the implementation of the factory of high-level end-of-line normalization filters:
```lua
function filter.cycle(low, ctx, extra)
return function(chunk)
local ret
ret, ctx = low(ctx, chunk, extra)
return ret
end
end
function normalize(marker)
return cycle(eol, 0, marker)
end
```
The `normalize` factory simply calls a more generic factory, the `cycle` factory. This factory receives a low-level filter, an initial context and some extra value and returns the corresponding high-level filter. Each time the high level filer is called with a new chunk, it calls the low-level filter passing the previous context, the new chunk and the extra argument. The low-level filter produces the chunk of processed data and a new context. Finally, the high-level filter updates its internal context and returns the processed chunk of data to the user. It is the low-level filter that does all the work. Notice that this implementation takes advantage of the Lua 5.0 lexical scoping rules to store the context locally, between function calls.
Moving to the low-level filter, we notice there is no perfect solution to the end-of-line marker normalization problem itself. The difficulty comes from an inherent ambiguity on the definition of empty lines within mixed input. However, the following solution works well for any consistent input, as well as for non-empty lines in mixed input. It also does a reasonable job with empty lines and serves as a good example of how to implement a low-level filter.
Here is what we do: CR and LF are considered candidates for line break. We issue "one" end-of-line line marker if one of the candidates is seen alone, or followed by a "different" candidate. That is, CR&nbsp;CR and LF&nbsp;LF issue two end of line markers each, but CR&nbsp;LF and LF&nbsp;CR issue only one marker. This idea takes care of Mac OS, Mac OS X, VMS and Unix, DOS and MIME, as well as probably other more obscure conventions.
### The C part of the implementation
The low-level filter is divided into two simple functions. The inner function actually does the conversion. It takes each input character in turn, deciding what to output and how to modify the context. The context tells if the last character seen was a candidate and, if so, which candidate it was.
```c
#define candidate(c) (c == CR || c == LF)
static int process(int c, int last, const char *marker, luaL_Buffer *buffer) {
if (candidate(c)) {
if (candidate(last)) {
if (c == last) luaL_addstring(buffer, marker);
return 0;
} else {
luaL_addstring(buffer, marker);
return c;
}
} else {
luaL_putchar(buffer, c);
return 0;
}
}
```
The inner function makes use of Lua's auxiliary library's buffer interface for its efficiency and ease of use. The outer function simply interfaces with Lua. It receives the context and the input chunk (as well as an optional end-of-line marker), and returns the transformed output and the new context.
```c
static int eol(lua_State *L) {
int ctx = luaL_checkint(L, 1);
size_t isize = 0;
const char *input = luaL_optlstring(L, 2, NULL, &isize);
const char *last = input + isize;
const char *marker = luaL_optstring(L, 3, CRLF);
luaL_Buffer buffer;
luaL_buffinit(L, &amp;buffer);
if (!input) {
lua_pushnil(L);
lua_pushnumber(L, 0);
return 2;
}
while (input &lt; last)
ctx = process(*input++, ctx, marker, &amp;buffer);
luaL_pushresult(&amp;buffer);
lua_pushnumber(L, ctx);
return 2;
}
```
Notice that if the input chunk is `nil`, the operation is considered to be finished. In that case, the loop will not execute a single time and the context is reset to the initial state. This allows the filter to be reused indefinitely. It is a good idea to write filters like this, when possible.
Besides the end-of-line normalization filter shown above, many other filters can be implemented with the same ideas. Examples include Base64 and Quoted-Printable transfer content encodings, the breaking of text into lines, SMTP byte stuffing etc. The challenging part is to decide what will be the context. For line breaking, for instance, it could be the number of bytes left in the current line. For Base64 encoding, it could be the bytes that remain in the division of the input into 3-byte atoms.
## Chaining
Filters become more powerful when the concept of chaining is introduced. Suppose you have a filter for Quoted-Printable encoding and you want to encode some text. According to the standard, the text has to be normalized into its canonic form prior to encoding. A nice interface that simplifies this task is a factory that creates a composite filter that passes data through multiple filters, but that can be used wherever a primitive filter is used.
```lua
local function chain2(f1, f2)
return function(chunk)
local ret = f2(f1(chunk))
if chunk then return ret
else return ret .. f2() end
end
end
function filter.chain(...)
local arg = {...}
local f = arg[1]
for i = 2, #arg do
f = chain2(f, arg[i])
end
return f
end
local chain = filter.chain(normalize("\r\n"), encode("quoted-printable"))
while 1 do
local chunk = io.read(2048)
io.write(chain(chunk))
if not chunk then break end
end
```
The chaining factory is very simple. All it does is return a function that passes data through all filters and returns the result to the user. It uses the simpler auxiliary function that knows how to chain two filters together. In the auxiliary function, special care must be taken if the chunk is final. This is because the final chunk notification has to be pushed through both filters in turn. Thanks to the chain factory, it is easy to perform the Quoted-Printable conversion, as the above example shows.
## Sources, sinks, and pumps
As we noted in the introduction, the filters we introduced so far act as the internal nodes in a network of transformations. Information flows from node to node (or rather from one filter to the next) and is transformed on its way out. Chaining filters together is the way we found to connect nodes in the network. But what about the end nodes? In the beginning of the network, we need a node that provides the data, a source. In the end of the network, we need a node that takes in the data, a sink.
### Sources
We start with two simple sources. The first is the `empty` source: It simply returns no data, possibly returning an error message. The second is the `file` source, which produces the contents of a file in a chunk by chunk fashion, closing the file handle when done.
```lua
function source.empty(err)
return function()
return nil, err
end
end
function source.file(handle, io_err)
if handle then
return function()
local chunk = handle:read(2048)
if not chunk then handle:close() end
return chunk
end
else return source.empty(io_err or "unable to open file") end
end
```
A source returns the next chunk of data each time it is called. When there is no more data, it just returns `nil`. If there is an error, the source can inform the caller by returning `nil` followed by an error message. Adrian Sietsma noticed that, although not on purpose, the interface for sources is compatible with the idea of iterators in Lua 5.0. That is, a data source can be nicely used in conjunction with `for` loops. Using our file source as an iterator, we can rewrite our first example:
```lua
local process = normalize("\r\n")
for chunk in source.file(io.stdin) do
io.write(process(chunk))
end
io.write(process(nil))
```
Notice that the last call to the filter obtains the last chunk of processed data. The loop terminates when the source returns `nil` and therefore we need that final call outside of the loop.
### Maintaining state between calls
It is often the case that a source needs to change its behavior after some event. One simple example would be a file source that wants to make sure it returns `nil` regardless of how many times it is called after the end of file, avoiding attempts to read past the end of the file. Another example would be a source that returns the contents of several files, as if they were concatenated, moving from one file to the next until the end of the last file is reached.
One way to implement this kind of source is to have the factory declare extra state variables that the source can use via lexical scoping. Our file source could set the file handle itself to `nil` when it detects the end-of-file. Then, every time the source is called, it could check if the handle is still valid and act accordingly:
```lua
function source.file(handle, io_err)
if handle then
return function()
if not handle then return nil end
local chunk = handle:read(2048)
if not chunk then
handle:close()
handle = nil
end
return chunk
end
else return source.empty(io_err or "unable to open file") end
end
```
Another way to implement this behavior involves a change in the source interface to makes it more flexible. Let's allow a source to return a second value, besides the next chunk of data. If the returned chunk is `nil`, the extra return value tells us what happened. A second `nil` means that there is just no more data and the source is empty. Any other value is considered to be an error message. On the other hand, if the chunk was "not" `nil`, the second return value tells us whether the source wants to be replaced. If it is `nil`, we should proceed using the same source. Otherwise it has to be another source, which we have to use from then on, to get the remaining data.
This extra freedom is good for someone writing a source function, but it is a pain for those that have to use it. Fortunately, given one of these "fancy" sources, we can transform it into a simple source that never needs to be replaced, using the following factory.
```lua
function source.simplify(src)
return function()
local chunk, err_or_new = src()
src = err_or_new or src
if not chunk then return nil, err_or_new
else return chunk end
end
end
```
The simplification factory allows us to write fancy sources and use them as if they were simple. Therefore, our next functions will only produce simple sources, and functions that take sources will assume they are simple.
Going back to our file source, the extended interface allows for a more elegant implementation. The new source just asks to be replaced by an empty source as soon as there is no more data. There is no repeated checking of the handle. To make things simpler to the user, the factory itself simplifies the the fancy file source before returning it to the user:
```lua
function source.file(handle, io_err)
if handle then
return source.simplify(function()
local chunk = handle:read(2048)
if not chunk then
handle:close()
return "", source.empty()
end
return chunk
end)
else return source.empty(io_err or "unable to open file") end
end
```
We can make these ideas even more powerful if we use a new feature of Lua 5.0: coroutines. Coroutines suffer from a great lack of advertisement, and I am going to play my part here. Just like lexical scoping, coroutines taste odd at first, but once you get used with the concept, it can save your day. I have to admit that using coroutines to implement our file source would be overkill, so let's implement a concatenated source factory instead.
```lua
function source.cat(...)
local arg = {...}
local co = coroutine.create(function()
local i = 1
while i <= #arg do
local chunk, err = arg[i]()
if chunk then coroutine.yield(chunk)
elseif err then return nil, err
else i = i + 1 end
end
end)
return function()
return shift(coroutine.resume(co))
end
end
```
The factory creates two functions. The first is an auxiliary that does all the work, in the form of a coroutine. It reads a chunk from one of the sources. If the chunk is `nil`, it moves to the next source, otherwise it just yields returning the chunk. When it is resumed, it continues from where it stopped and tries to read the next chunk. The second function is the source itself, and just resumes the execution of the auxiliary coroutine, returning to the user whatever chunks it returns (skipping the first result that tells us if the coroutine terminated). Imagine writing the same function without coroutines and you will notice the simplicity of this implementation. We will use coroutines again when we make the filter interface more powerful.
### Chaining Sources
What does it mean to chain a source with a filter? The most useful interpretation is that the combined source-filter is a new source that produces data and passes it through the filter before returning it. Here is a factory that does it:
```lua
function source.chain(src, f)
return source.simplify(function()
local chunk, err = src()
if not chunk then return f(nil), source.empty(err)
else return f(chunk) end
end)
end
```
Our motivating example in the introduction chains a source with a filter. The idea of chaining a source with a filter is useful when one thinks about functions that might get their input data from a source. By chaining a simple source with one or more filters, the same function can be provided with filtered data even though it is unaware of the filtering that is happening behind its back.
### Sinks
Just as we defined an interface for an initial source of data, we can also define an interface for a final destination of data. We call any function respecting that interface a "sink". Below are two simple factories that return sinks. The table factory creates a sink that stores all obtained data into a table. The data can later be efficiently concatenated into a single string with the `table.concat` library function. As another example, we introduce the `null` sink: A sink that simply discards the data it receives.
```lua
function sink.table(t)
t = t or {}
local f = function(chunk, err)
if chunk then table.insert(t, chunk) end
return 1
end
return f, t
end
local function null()
return 1
end
function sink.null()
return null
end
```
Sinks receive consecutive chunks of data, until the end of data is notified with a `nil` chunk. An error is notified by an extra argument giving an error message after the `nil` chunk. If a sink detects an error itself and wishes not to be called again, it should return `nil`, optionally followed by an error message. A return value that is not `nil` means the source will accept more data. Finally, just as sources can choose to be replaced, so can sinks, following the same interface. Once again, it is easy to implement a `sink.simplify` factory that transforms a fancy sink into a simple sink.
As an example, let's create a source that reads from the standard input, then chain it with a filter that normalizes the end-of-line convention and let's use a sink to place all data into a table, printing the result in the end.
```lua
local load = source.chain(source.file(io.stdin), normalize("\r\n"))
local store, t = sink.table()
while 1 do
local chunk = load()
store(chunk)
if not chunk then break end
end
print(table.concat(t))
```
Again, just as we created a factory that produces a chained source-filter from a source and a filter, it is easy to create a factory that produces a new sink given a sink and a filter. The new sink passes all data it receives through the filter before handing it in to the original sink. Here is the implementation:
```lua
function sink.chain(f, snk)
return function(chunk, err)
local r, e = snk(f(chunk))
if not r then return nil, e end
if not chunk then return snk(nil, err) end
return 1
end
end
```
### Pumps
There is a while loop that has been around for too long in our examples. It's always there because everything that we designed so far is passive. Sources, sinks, filters: None of them will do anything on their own. The operation of pumping all data a source can provide into a sink is so common that we will provide a couple helper functions to do that for us.
```lua
function pump.step(src, snk)
local chunk, src_err = src()
local ret, snk_err = snk(chunk, src_err)
return chunk and ret and not src_err and not snk_err, src_err or snk_err
end
function pump.all(src, snk, step)
step = step or pump.step
while true do
local ret, err = step(src, snk)
if not ret then return not err, err end
end
end
```
The `pump.step` function moves one chunk of data from the source to the sink. The `pump.all` function takes an optional `step` function and uses it to pump all the data from the source to the sink. We can now use everything we have to write a program that reads a binary file from disk and stores it in another file, after encoding it to the Base64 transfer content encoding:
```lua
local load = source.chain(
source.file(io.open("input.bin", "rb")),
encode("base64")
)
local store = sink.chain(
wrap(76),
sink.file(io.open("output.b64", "w")),
)
pump.all(load, store)
```
The way we split the filters here is not intuitive, on purpose. Alternatively, we could have chained the Base64 encode filter and the line-wrap filter together, and then chain the resulting filter with either the file source or the file sink. It doesn't really matter.
## One last important change
Turns out we still have a problem. When David Burgess was writing his gzip filter, he noticed that the decompression filter can explode a small input chunk into a huge amount of data. Although we wished we could ignore this problem, we soon agreed we couldn't. The only solution is to allow filters to return partial results, and that is what we chose to do. After invoking the filter to pass input data, the user now has to loop invoking the filter to find out if it has more output data to return. Note that these extra calls can't pass more data to the filter.
More specifically, after passing a chunk of input data to a filter and collecting the first chunk of output data, the user invokes the filter repeatedly, passing the empty string, to get extra output chunks. When the filter itself returns an empty string, the user knows there is no more output data, and can proceed to pass the next input chunk. In the end, after the user passes a `nil` notifying the filter that there is no more input data, the filter might still have produced too much output data to return in a single chunk. The user has to loop again, this time passing `nil` each time, until the filter itself returns `nil` to notify the user it is finally done.
Most filters won't need this extra freedom. Fortunately, the new filter interface is easy to implement. In fact, the end-of-line translation filter we created in the introduction already conforms to it. On the other hand, the chaining function becomes much more complicated. If it wasn't for coroutines, I wouldn't be happy to implement it. Let me know if you can find a simpler implementation that does not use coroutines!
```lua
local function chain2(f1, f2)
local co = coroutine.create(function(chunk)
while true do
local filtered1 = f1(chunk)
local filtered2 = f2(filtered1)
local done2 = filtered1 and ""
while true do
if filtered2 == "" or filtered2 == nil then break end
coroutine.yield(filtered2)
filtered2 = f2(done2)
end
if filtered1 == "" then chunk = coroutine.yield(filtered1)
elseif filtered1 == nil then return nil
else chunk = chunk and "" end
end
end)
return function(chunk)
local _, res = coroutine.resume(co, chunk)
return res
end
end
```
Chaining sources also becomes more complicated, but a similar solution is possible with coroutines. Chaining sinks is just as simple as it has always been. Interestingly, these modifications do not have a measurable negative impact in the the performance of filters that didn't need the added flexibility. They do severely improve the efficiency of filters like the gzip filter, though, and that is why we are keeping them.
## Final considerations
These ideas were created during the development of [LuaSocket](https://github.com/lunarmodules/luasocket) 2.0, and are available as the LTN12 module. As a result, [LuaSocket](https://github.com/lunarmodules/luasocket) implementation was greatly simplified and became much more powerful. The MIME module is especially integrated to LTN12 and provides many other filters. We felt these concepts deserved to be made public even to those that don't care about [LuaSocket](https://github.com/lunarmodules/luasocket), hence the LTN.
One extra application that deserves mentioning makes use of an identity filter. Suppose you want to provide some feedback to the user while a file is being downloaded into a sink. Chaining the sink with an identity filter (a filter that simply returns the received data unaltered), you can update a progress counter on the fly. The original sink doesn't have to be modified. Another interesting idea is that of a T sink: A sink that sends data to two other sinks. In summary, there appears to be enough room for many other interesting ideas.
In this technical note we introduced filters, sources, sinks, and pumps. These are useful tools for data processing in general. Sources provide a simple abstraction for data acquisition. Sinks provide an abstraction for final data destinations. Filters define an interface for data transformations. The chaining of filters, sources and sinks provides an elegant way to create arbitrarily complex data transformation from simpler transformations. Pumps just put the machinery to work.

View File

@ -1,393 +0,0 @@
===Filters, sources and sinks: design, motivation and examples===
==or Functional programming for the rest of us==
by DiegoNehab
{{{
}}}
===Abstract===
Certain operations can be implemented in the form of filters. A filter is a function that processes data received in consecutive function calls, returning partial results chunk by chunk. Examples of operations that can be implemented as filters include the end-of-line normalization for text, Base64 and Quoted-Printable transfer content encodings, the breaking of text into lines, SMTP byte stuffing, and there are many others. Filters become even more powerful when we allow them to be chained together to create composite filters. Filters can be seen as middle nodes in a chain of data transformations. Sources an sinks are the corresponding end points of these chains. A source is a function that produces data, chunk by chunk, and a sink is a function that takes data, chunk by chunk. In this technical note, we define an elegant interface for filters, sources, sinks and chaining. We evolve our interface progressively, until we reach a high degree of generality. We discuss difficulties that arise during the implementation of this interface and we provide solutions and examples.
===Introduction===
Applications sometimes have too much information to process to fit in memory and are thus forced to process data in smaller parts. Even when there is enough memory, processing all the data atomically may take long enough to frustrate a user that wants to interact with the application. Furthermore, complex transformations can often be defined as series of simpler operations. Several different complex transformations might share the same simpler operations, so that an uniform interface to combine them is desirable. The following concepts constitute our solution to these problems.
''Filters'' are functions that accept successive chunks of input, and produce successive chunks of output. Furthermore, the result of concatenating all the output data is the same as the result of applying the filter over the concatenation of the input data. As a consequence, boundaries are irrelevant: filters have to handle input data split arbitrarily by the user.
A ''chain'' is a function that combines the effect of two (or more) other functions, but whose interface is indistinguishable from the interface of one of its components. Thus, a chained filter can be used wherever an atomic filter can be used. However, its effect on data is the combined effect of its component filters. Note that, as a consequence, chains can be chained themselves to create arbitrarily complex operations that can be used just like atomic operations.
Filters can be seen as internal nodes in a network through which data flows, potentially being transformed along its way. Chains connect these nodes together. To complete the picture, we need ''sources'' and ''sinks'' as initial and final nodes of the network, respectively. Less abstractly, a source is a function that produces new data every time it is called. On the other hand, sinks are functions that give a final destination to the data they receive. Naturally, sources and sinks can be chained with filters.
Finally, filters, chains, sources, and sinks are all passive entities: they need to be repeatedly called in order for something to happen. ''Pumps'' provide the driving force that pushes data through the network, from a source to a sink.
Hopefully, these concepts will become clear with examples. In the following sections, we start with simplified interfaces, which we improve several times until we can find no obvious shortcomings. The evolution we present is not contrived: it follows the steps we followed ourselves as we consolidated our understanding of these concepts.
== A concrete example ==
Some data transformations are easier to implement as filters than others. Examples of operations that can be implemented as filters include the end-of-line normalization for text, the Base64 and Quoted-Printable transfer content encodings, the breaking of text into lines, SMTP byte stuffing, and many others. Let's use the end-of-line normalization as an example to define our initial filter interface. We later discuss why the implementation might not be trivial.
Assume we are given text in an unknown end-of-line convention (including possibly mixed conventions) out of the commonly found Unix (LF), Mac OS (CR), and DOS (CRLF) conventions. We would like to be able to write code like the following:
{{{
input = source.chain(source.file(io.stdin), normalize("\r\n"))
output = sink.file(io.stdout)
pump(input, output)
}}}
This program should read data from the standard input stream and normalize the end-of-line markers to the canonic CRLF marker defined by the MIME standard, finally sending the results to the standard output stream. For that, we use a ''file source'' to produce data from standard input, and chain it with a filter that normalizes the data. The pump then repeatedly gets data from the source, and moves it to the ''file sink'' that sends it to standard output.
To make the discussion even more concrete, we start by discussing the implementation of the normalization filter. The {{normalize}} ''factory'' is a function that creates such a filter. Our initial filter interface is as follows: the filter receives a chunk of input data, and returns a chunk of processed data. When there is no more input data, the user notifies the filter by invoking it with a {{nil}} chunk. The filter then returns the final chunk of processed data.
Although the interface is extremely simple, the implementation doesn't seem so obvious. Any filter respecting this interface needs to keep some kind of context between calls. This is because chunks can be broken between the CR and LF characters marking the end of a line. This need for context storage is what motivates the use of factories: each time the factory is called, it returns a filter with its own context so that we can have several independent filters being used at the same time. For the normalization filter, we know that the obvious solution (i.e. concatenating all the input into the context before producing any output) is not good enough, so we will have to find another way.
We will break the implementation in two parts: a low-level filter, and a factory of high-level filters. The low-level filter will be implemented in C and will not carry any context between function calls. The high-level filter factory, implemented in Lua, will create and return a high-level filter that keeps whatever context the low-level filter needs, but isolates the user from its internal details. That way, we take advantage of C's efficiency to perform the dirty work, and take advantage of Lua's simplicity for the bookkeeping.
==The Lua part of the implementation==
Below is the implementation of the factory of high-level end-of-line normalization filters:
{{{
function filter.cycle(low, ctx, extra)
return function(chunk)
local ret
ret, ctx = low(ctx, chunk, extra)
return ret
end
end
function normalize(marker)
return cycle(eol, 0, marker)
end
}}}
The {{normalize}} factory simply calls a more generic factory, the {{cycle}} factory. This factory receives a low-level filter, an initial context and some extra value and returns the corresponding high-level filter. Each time the high level filer is called with a new chunk, it calls the low-level filter passing the previous context, the new chunk and the extra argument. The low-level filter produces the chunk of processed data and a new context. Finally, the high-level filter updates its internal context and returns the processed chunk of data to the user. It is the low-level filter that does all the work. Notice that this implementation takes advantage of the Lua 5.0 lexical scoping rules to store the context locally, between function calls.
Moving to the low-level filter, we notice there is no perfect solution to the end-of-line marker normalization problem itself. The difficulty comes from an inherent ambiguity on the definition of empty lines within mixed input. However, the following solution works well for any consistent input, as well as for non-empty lines in mixed input. It also does a reasonable job with empty lines and serves as a good example of how to implement a low-level filter.
Here is what we do: CR and LF are considered candidates for line break. We issue ''one'' end-of-line line marker if one of the candidates is seen alone, or followed by a ''different'' candidate. That is, CR&nbsp;CR and LF&nbsp;LF issue two end of line markers each, but CR&nbsp;LF and LF&nbsp;CR issue only one marker. This idea takes care of Mac OS, Mac OS X, VMS and Unix, DOS and MIME, as well as probably other more obscure conventions.
==The C part of the implementation==
The low-level filter is divided into two simple functions. The inner function actually does the conversion. It takes each input character in turn, deciding what to output and how to modify the context. The context tells if the last character seen was a candidate and, if so, which candidate it was.
{{{
#define candidate(c) (c == CR || c == LF)
static int process(int c, int last, const char *marker, luaL_Buffer *buffer) {
if (candidate(c)) {
if (candidate(last)) {
if (c == last) luaL_addstring(buffer, marker);
return 0;
} else {
luaL_addstring(buffer, marker);
return c;
}
} else {
luaL_putchar(buffer, c);
return 0;
}
}
}}}
The inner function makes use of Lua's auxiliary library's buffer interface for its efficiency and ease of use. The outer function simply interfaces with Lua. It receives the context and the input chunk (as well as an optional end-of-line marker), and returns the transformed output and the new context.
{{{
static int eol(lua_State *L) {
int ctx = luaL_checkint(L, 1);
size_t isize = 0;
const char *input = luaL_optlstring(L, 2, NULL, &isize);
const char *last = input + isize;
const char *marker = luaL_optstring(L, 3, CRLF);
luaL_Buffer buffer;
luaL_buffinit(L, &amp;buffer);
if (!input) {
lua_pushnil(L);
lua_pushnumber(L, 0);
return 2;
}
while (input &lt; last)
ctx = process(*input++, ctx, marker, &amp;buffer);
luaL_pushresult(&amp;buffer);
lua_pushnumber(L, ctx);
return 2;
}
}}}
Notice that if the input chunk is {{nil}}, the operation is considered to be finished. In that case, the loop will not execute a single time and the context is reset to the initial state. This allows the filter to be reused indefinitely. It is a good idea to write filters like this, when possible.
Besides the end-of-line normalization filter shown above, many other filters can be implemented with the same ideas. Examples include Base64 and Quoted-Printable transfer content encodings, the breaking of text into lines, SMTP byte stuffing etc. The challenging part is to decide what will be the context. For line breaking, for instance, it could be the number of bytes left in the current line. For Base64 encoding, it could be the bytes that remain in the division of the input into 3-byte atoms.
===Chaining===
Filters become more powerful when the concept of chaining is introduced. Suppose you have a filter for Quoted-Printable encoding and you want to encode some text. According to the standard, the text has to be normalized into its canonic form prior to encoding. A nice interface that simplifies this task is a factory that creates a composite filter that passes data through multiple filters, but that can be used wherever a primitive filter is used.
{{{
local function chain2(f1, f2)
return function(chunk)
local ret = f2(f1(chunk))
if chunk then return ret
else return ret .. f2() end
end
end
function filter.chain(...)
local arg = {...}
local f = arg[1]
for i = 2, #arg do
f = chain2(f, arg[i])
end
return f
end
local chain = filter.chain(normalize("\r\n"), encode("quoted-printable"))
while 1 do
local chunk = io.read(2048)
io.write(chain(chunk))
if not chunk then break end
end
}}}
The chaining factory is very simple. All it does is return a function that passes data through all filters and returns the result to the user. It uses the simpler auxiliary function that knows how to chain two filters together. In the auxiliary function, special care must be taken if the chunk is final. This is because the final chunk notification has to be pushed through both filters in turn. Thanks to the chain factory, it is easy to perform the Quoted-Printable conversion, as the above example shows.
===Sources, sinks, and pumps===
As we noted in the introduction, the filters we introduced so far act as the internal nodes in a network of transformations. Information flows from node to node (or rather from one filter to the next) and is transformed on its way out. Chaining filters together is the way we found to connect nodes in the network. But what about the end nodes? In the beginning of the network, we need a node that provides the data, a source. In the end of the network, we need a node that takes in the data, a sink.
==Sources==
We start with two simple sources. The first is the {{empty}} source: It simply returns no data, possibly returning an error message. The second is the {{file}} source, which produces the contents of a file in a chunk by chunk fashion, closing the file handle when done.
{{{
function source.empty(err)
return function()
return nil, err
end
end
function source.file(handle, io_err)
if handle then
return function()
local chunk = handle:read(2048)
if not chunk then handle:close() end
return chunk
end
else return source.empty(io_err or "unable to open file") end
end
}}}
A source returns the next chunk of data each time it is called. When there is no more data, it just returns {{nil}}. If there is an error, the source can inform the caller by returning {{nil}} followed by an error message. Adrian Sietsma noticed that, although not on purpose, the interface for sources is compatible with the idea of iterators in Lua 5.0. That is, a data source can be nicely used in conjunction with {{for}} loops. Using our file source as an iterator, we can rewrite our first example:
{{{
local process = normalize("\r\n")
for chunk in source.file(io.stdin) do
io.write(process(chunk))
end
io.write(process(nil))
}}}
Notice that the last call to the filter obtains the last chunk of processed data. The loop terminates when the source returns {{nil}} and therefore we need that final call outside of the loop.
==Maintaining state between calls==
It is often the case that a source needs to change its behavior after some event. One simple example would be a file source that wants to make sure it returns {{nil}} regardless of how many times it is called after the end of file, avoiding attempts to read past the end of the file. Another example would be a source that returns the contents of several files, as if they were concatenated, moving from one file to the next until the end of the last file is reached.
One way to implement this kind of source is to have the factory declare extra state variables that the source can use via lexical scoping. Our file source could set the file handle itself to {{nil}} when it detects the end-of-file. Then, every time the source is called, it could check if the handle is still valid and act accordingly:
{{{
function source.file(handle, io_err)
if handle then
return function()
if not handle then return nil end
local chunk = handle:read(2048)
if not chunk then
handle:close()
handle = nil
end
return chunk
end
else return source.empty(io_err or "unable to open file") end
end
}}}
Another way to implement this behavior involves a change in the source interface to makes it more flexible. Let's allow a source to return a second value, besides the next chunk of data. If the returned chunk is {{nil}}, the extra return value tells us what happened. A second {{nil}} means that there is just no more data and the source is empty. Any other value is considered to be an error message. On the other hand, if the chunk was ''not'' {{nil}}, the second return value tells us whether the source wants to be replaced. If it is {{nil}}, we should proceed using the same source. Otherwise it has to be another source, which we have to use from then on, to get the remaining data.
This extra freedom is good for someone writing a source function, but it is a pain for those that have to use it. Fortunately, given one of these ''fancy'' sources, we can transform it into a simple source that never needs to be replaced, using the following factory.
{{{
function source.simplify(src)
return function()
local chunk, err_or_new = src()
src = err_or_new or src
if not chunk then return nil, err_or_new
else return chunk end
end
end
}}}
The simplification factory allows us to write fancy sources and use them as if they were simple. Therefore, our next functions will only produce simple sources, and functions that take sources will assume they are simple.
Going back to our file source, the extended interface allows for a more elegant implementation. The new source just asks to be replaced by an empty source as soon as there is no more data. There is no repeated checking of the handle. To make things simpler to the user, the factory itself simplifies the the fancy file source before returning it to the user:
{{{
function source.file(handle, io_err)
if handle then
return source.simplify(function()
local chunk = handle:read(2048)
if not chunk then
handle:close()
return "", source.empty()
end
return chunk
end)
else return source.empty(io_err or "unable to open file") end
end
}}}
We can make these ideas even more powerful if we use a new feature of Lua 5.0: coroutines. Coroutines suffer from a great lack of advertisement, and I am going to play my part here. Just like lexical scoping, coroutines taste odd at first, but once you get used with the concept, it can save your day. I have to admit that using coroutines to implement our file source would be overkill, so let's implement a concatenated source factory instead.
{{{
function source.cat(...)
local arg = {...}
local co = coroutine.create(function()
local i = 1
while i <= #arg do
local chunk, err = arg[i]()
if chunk then coroutine.yield(chunk)
elseif err then return nil, err
else i = i + 1 end
end
end)
return function()
return shift(coroutine.resume(co))
end
end
}}}
The factory creates two functions. The first is an auxiliary that does all the work, in the form of a coroutine. It reads a chunk from one of the sources. If the chunk is {{nil}}, it moves to the next source, otherwise it just yields returning the chunk. When it is resumed, it continues from where it stopped and tries to read the next chunk. The second function is the source itself, and just resumes the execution of the auxiliary coroutine, returning to the user whatever chunks it returns (skipping the first result that tells us if the coroutine terminated). Imagine writing the same function without coroutines and you will notice the simplicity of this implementation. We will use coroutines again when we make the filter interface more powerful.
==Chaining Sources==
What does it mean to chain a source with a filter? The most useful interpretation is that the combined source-filter is a new source that produces data and passes it through the filter before returning it. Here is a factory that does it:
{{{
function source.chain(src, f)
return source.simplify(function()
local chunk, err = src()
if not chunk then return f(nil), source.empty(err)
else return f(chunk) end
end)
end
}}}
Our motivating example in the introduction chains a source with a filter. The idea of chaining a source with a filter is useful when one thinks about functions that might get their input data from a source. By chaining a simple source with one or more filters, the same function can be provided with filtered data even though it is unaware of the filtering that is happening behind its back.
==Sinks==
Just as we defined an interface for an initial source of data, we can also define an interface for a final destination of data. We call any function respecting that interface a ''sink''. Below are two simple factories that return sinks. The table factory creates a sink that stores all obtained data into a table. The data can later be efficiently concatenated into a single string with the {{table.concat}} library function. As another example, we introduce the {{null}} sink: A sink that simply discards the data it receives.
{{{
function sink.table(t)
t = t or {}
local f = function(chunk, err)
if chunk then table.insert(t, chunk) end
return 1
end
return f, t
end
local function null()
return 1
end
function sink.null()
return null
end
}}}
Sinks receive consecutive chunks of data, until the end of data is notified with a {{nil}} chunk. An error is notified by an extra argument giving an error message after the {{nil}} chunk. If a sink detects an error itself and wishes not to be called again, it should return {{nil}}, optionally followed by an error message. A return value that is not {{nil}} means the source will accept more data. Finally, just as sources can choose to be replaced, so can sinks, following the same interface. Once again, it is easy to implement a {{sink.simplify}} factory that transforms a fancy sink into a simple sink.
As an example, let's create a source that reads from the standard input, then chain it with a filter that normalizes the end-of-line convention and let's use a sink to place all data into a table, printing the result in the end.
{{{
local load = source.chain(source.file(io.stdin), normalize("\r\n"))
local store, t = sink.table()
while 1 do
local chunk = load()
store(chunk)
if not chunk then break end
end
print(table.concat(t))
}}}
Again, just as we created a factory that produces a chained source-filter from a source and a filter, it is easy to create a factory that produces a new sink given a sink and a filter. The new sink passes all data it receives through the filter before handing it in to the original sink. Here is the implementation:
{{{
function sink.chain(f, snk)
return function(chunk, err)
local r, e = snk(f(chunk))
if not r then return nil, e end
if not chunk then return snk(nil, err) end
return 1
end
end
}}}
==Pumps==
There is a while loop that has been around for too long in our examples. It's always there because everything that we designed so far is passive. Sources, sinks, filters: None of them will do anything on their own. The operation of pumping all data a source can provide into a sink is so common that we will provide a couple helper functions to do that for us.
{{{
function pump.step(src, snk)
local chunk, src_err = src()
local ret, snk_err = snk(chunk, src_err)
return chunk and ret and not src_err and not snk_err, src_err or snk_err
end
function pump.all(src, snk, step)
step = step or pump.step
while true do
local ret, err = step(src, snk)
if not ret then return not err, err end
end
end
}}}
The {{pump.step}} function moves one chunk of data from the source to the sink. The {{pump.all}} function takes an optional {{step}} function and uses it to pump all the data from the source to the sink. We can now use everything we have to write a program that reads a binary file from disk and stores it in another file, after encoding it to the Base64 transfer content encoding:
{{{
local load = source.chain(
source.file(io.open("input.bin", "rb")),
encode("base64")
)
local store = sink.chain(
wrap(76),
sink.file(io.open("output.b64", "w")),
)
pump.all(load, store)
}}}
The way we split the filters here is not intuitive, on purpose. Alternatively, we could have chained the Base64 encode filter and the line-wrap filter together, and then chain the resulting filter with either the file source or the file sink. It doesn't really matter.
===One last important change===
Turns out we still have a problem. When David Burgess was writing his gzip filter, he noticed that the decompression filter can explode a small input chunk into a huge amount of data. Although we wished we could ignore this problem, we soon agreed we couldn't. The only solution is to allow filters to return partial results, and that is what we chose to do. After invoking the filter to pass input data, the user now has to loop invoking the filter to find out if it has more output data to return. Note that these extra calls can't pass more data to the filter.
More specifically, after passing a chunk of input data to a filter and collecting the first chunk of output data, the user invokes the filter repeatedly, passing the empty string, to get extra output chunks. When the filter itself returns an empty string, the user knows there is no more output data, and can proceed to pass the next input chunk. In the end, after the user passes a {{nil}} notifying the filter that there is no more input data, the filter might still have produced too much output data to return in a single chunk. The user has to loop again, this time passing {{nil}} each time, until the filter itself returns {{nil}} to notify the user it is finally done.
Most filters won't need this extra freedom. Fortunately, the new filter interface is easy to implement. In fact, the end-of-line translation filter we created in the introduction already conforms to it. On the other hand, the chaining function becomes much more complicated. If it wasn't for coroutines, I wouldn't be happy to implement it. Let me know if you can find a simpler implementation that does not use coroutines!
{{{
local function chain2(f1, f2)
local co = coroutine.create(function(chunk)
while true do
local filtered1 = f1(chunk)
local filtered2 = f2(filtered1)
local done2 = filtered1 and ""
while true do
if filtered2 == "" or filtered2 == nil then break end
coroutine.yield(filtered2)
filtered2 = f2(done2)
end
if filtered1 == "" then chunk = coroutine.yield(filtered1)
elseif filtered1 == nil then return nil
else chunk = chunk and "" end
end
end)
return function(chunk)
local _, res = coroutine.resume(co, chunk)
return res
end
end
}}}
Chaining sources also becomes more complicated, but a similar solution is possible with coroutines. Chaining sinks is just as simple as it has always been. Interestingly, these modifications do not have a measurable negative impact in the the performance of filters that didn't need the added flexibility. They do severely improve the efficiency of filters like the gzip filter, though, and that is why we are keeping them.
===Final considerations===
These ideas were created during the development of {{LuaSocket}}[http://www.tecgraf.puc-rio.br/luasocket] 2.0, and are available as the LTN12 module. As a result, {{LuaSocket}}[http://www.tecgraf.puc-rio.br/luasocket] implementation was greatly simplified and became much more powerful. The MIME module is especially integrated to LTN12 and provides many other filters. We felt these concepts deserved to be made public even to those that don't care about {{LuaSocket}}[http://www.tecgraf.puc-rio.br/luasocket], hence the LTN.
One extra application that deserves mentioning makes use of an identity filter. Suppose you want to provide some feedback to the user while a file is being downloaded into a sink. Chaining the sink with an identity filter (a filter that simply returns the received data unaltered), you can update a progress counter on the fly. The original sink doesn't have to be modified. Another interesting idea is that of a T sink: A sink that sends data to two other sinks. In summary, there appears to be enough room for many other interesting ideas.
In this technical note we introduced filters, sources, sinks, and pumps. These are useful tools for data processing in general. Sources provide a simple abstraction for data acquisition. Sinks provide an abstraction for final data destinations. Filters define an interface for data transformations. The chaining of filters, sources and sinks provides an elegant way to create arbitrarily complex data transformation from simpler transformations. Pumps just put the machinery to work.

191
ltn013.md Normal file
View File

@ -0,0 +1,191 @@
# Using finalized exceptions
### or How to get rid of all those if statements
by DiegoNehab
## Abstract
This little LTN describes a simple exception scheme that greatly simplifies error checking in Lua programs. All the needed functionality ships standard with Lua, but is hidden between the `assert` and `pcall` functions. To make it more evident, we stick to a convenient standard (you probably already use anyways) for Lua function return values, and define two very simple helper functions (either in C or in Lua itself).
## Introduction
Most Lua functions return `nil` in case of error, followed by a message describing the error. If you don't use this convention, you probably have good reasons. Hopefully, after reading on, you will realize your reasons are not good enough.
If you are like me, you hate error checking. Most nice little code snippets that look beautiful when you first write them lose some of their charm when you add all that error checking code. Yet, error checking is as important as the rest of the code. How sad.
Even if you stick to a return convention, any complex task involving several function calls makes error checking both boring and error-prone (do you see the "error" below?)
```lua
function task(arg1, arg2, ...)
local ret1, err = task1(arg1)
if not ret1 then
cleanup1()
return nil, error
end
local ret2, err = task2(arg2)
if not ret then
cleanup2()
return nil, error
end
...
end
```
The standard `assert` function provides an interesting alternative. To use it, simply nest every function call to be error checked with a call to `assert`. The `assert` function checks the value of its first argument. If it is `nil`, `assert` throws the second argument as an error message. Otherwise, `assert` lets all arguments through as if had not been there. The idea greatly simplifies error checking:
```lua
function task(arg1, arg2, ...)
local ret1 = assert(task1(arg1))
local ret2 = assert(task2(arg2))
...
end
```
If any task fails, the execution is aborted by `assert` and the error message is displayed to the user as the cause of the problem. If no error happens, the task completes as before. There isn't a single `if` statement and this is great. However, there are some problems with the idea.
First, the topmost `task` function doesn't respect the protocol followed by the lower-level tasks: It raises an error instead of returning `nil` followed by the error messages. Here is where the standard `pcall` comes in handy.
```lua
function xtask(arg1, arg2, ...)
local ret1 = assert(task1(arg1))
local ret2 = assert(task2(arg2))
...
end
function task(arg1, arg2, ...)
local ok, ret_or_err = pcall(xtask, arg1, arg2, ...)
if ok then return ret_or_err
else return nil, ret_or_err end
end
```
Our new `task` function is well behaved. `Pcall` catches any error raised by the calls to `assert` and returns it after the status code. That way, errors don't get propagated to the user of the high level `task` function.
These are the main ideas for our exception scheme, but there are still a few glitches to fix:
* Directly using `pcall` ruined the simplicity of the code;
* What happened to the cleanup function calls? What if we have to, say, close a file?
* `Assert` messes with the error message before raising the error (it adds line number information).
Fortunately, all these problems are very easy to solve and that's what we do in the following sections.
## Introducing the `protect` factory
We used the `pcall` function to shield the user from errors that could be raised by the underlying implementation. Instead of directly using `pcall` (and thus duplicating code) every time we prefer a factory that does the same job:
```lua
local function pack(ok, ...)
return ok, {...}
end
function protect(f)
return function(...)
local ok, ret = pack(pcall(f, ...))
if ok then return unpack(ret)
else return nil, ret[1] end
end
end
```
The `protect` factory receives a function that might raise exceptions and returns a function that respects our return value convention. Now we can rewrite the top-level `task` function in a much cleaner way:
```lua
task = protect(function(arg1, arg2, ...)
local ret1 = assert(task1(arg1))
local ret2 = assert(task2(arg2))
...
end)
```
The Lua implementation of the `protect` factory suffers with the creation of tables to hold multiple arguments and return values. It is possible (and easy) to implement the same function in C, without any table creation.
```c
static int safecall(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) {
lua_pushnil(L);
lua_insert(L, 1);
return 2;
} else return lua_gettop(L);
}
static int protect(lua_State *L) {
lua_pushcclosure(L, safecall, 1);
return 1;
}
```
## The `newtry` factory
Let's solve the two remaining issues with a single shot and use a concrete example to illustrate the proposed solution. Suppose you want to write a function to download an HTTP document. You have to connect, send the request and read the reply. Each of these tasks can fail, but if something goes wrong after you connected, you have to close the connection before returning the error message.
```lua
get = protect(function(host, path)
local c
-- create a try function with a finalizer to close the socket
local try = newtry(function()
if c then c:close() end
end)
-- connect and send request
c = try(connect(host, 80))
try(c:send("GET " .. path .. " HTTP/1.0\r\n\r\n"))
-- get headers
local h = {}
while 1 do
l = try(c:receive())
if l == "" then break end
table.insert(h, l)
end
-- get body
local b = try(c:receive("*a"))
c:close()
return b, h
end)
```
The `newtry` factory returns a function that works just like `assert`. The differences are that the `try` function doesn't mess with the error message and it calls an optional "finalizer" before raising the error. In our example, the finalizer simply closes the socket.
Even with a simple example like this, we see that the finalized exceptions simplified our life. Let's see what we gain in general, not just in this example:
* We don't need to declare dummy variables to hold error messages in case any ever shows up;
* We avoid using a variable to hold something that could either be a return value or an error message;
* We didn't have to use several "if" statements to check for errors;
* If an error happens, we know our finalizer is going to be invoked automatically;
* Exceptions get propagated, so we don't repeat these "if" statements until the error reaches the user.
Try writing the same function without the tricks we used above and you will see that the code gets ugly. Longer sequences of operations with error checking would get even uglier. So let's implement the `newtry` function in Lua:
```lua
function newtry(f)
return function(...)
if not arg[1] then
if f then f() end
error(arg[2], 0)
else
return ...
end
end
end
```
Again, the implementation suffers from the creation of tables at each function call, so we prefer the C version:
```lua
static int finalize(lua_State *L) {
if (!lua_toboolean(L, 1)) {
lua_pushvalue(L, lua_upvalueindex(1));
lua_pcall(L, 0, 0, 0);
lua_settop(L, 2);
lua_error(L);
return 0;
} else return lua_gettop(L);
}
static int do_nothing(lua_State *L) {
(void) L;
return 0;
}
static int newtry(lua_State *L) {
lua_settop(L, 1);
if (lua_isnil(L, 1))
lua_pushcfunction(L, do_nothing);
lua_pushcclosure(L, finalize, 1);
return 1;
}
```
## Final considerations
The `protect` and `newtry` functions saved a "lot" of work in the implementation of [LuaSocket](https://github.com/lunarmodules/luasocket). The size of some modules was cut in half by the these ideas. It's true the scheme is not as generic as the exception mechanism of programming languages like C++ or Java, but the power/simplicity ratio is favorable and I hope it serves you as well as it served [LuaSocket](https://github.com/lunarmodules/luasocket).

View File

@ -1,194 +0,0 @@
===Using finalized exceptions===
==or How to get rid of all those if statements==
by DiegoNehab
{{{
}}}
===Abstract===
This little LTN describes a simple exception scheme that greatly simplifies error checking in Lua programs. All the needed functionality ships standard with Lua, but is hidden between the {{assert}} and {{pcall}} functions. To make it more evident, we stick to a convenient standard (you probably already use anyways) for Lua function return values, and define two very simple helper functions (either in C or in Lua itself).
===Introduction===
Most Lua functions return {{nil}} in case of error, followed by a message describing the error. If you don't use this convention, you probably have good reasons. Hopefully, after reading on, you will realize your reasons are not good enough.
If you are like me, you hate error checking. Most nice little code snippets that look beautiful when you first write them lose some of their charm when you add all that error checking code. Yet, error checking is as important as the rest of the code. How sad.
Even if you stick to a return convention, any complex task involving several function calls makes error checking both boring and error-prone (do you see the ''error'' below?)
{{{
function task(arg1, arg2, ...)
local ret1, err = task1(arg1)
if not ret1 then
cleanup1()
return nil, error
end
local ret2, err = task2(arg2)
if not ret then
cleanup2()
return nil, error
end
...
end
}}}
The standard {{assert}} function provides an interesting alternative. To use it, simply nest every function call to be error checked with a call to {{assert}}. The {{assert}} function checks the value of its first argument. If it is {{nil}}, {{assert}} throws the second argument as an error message. Otherwise, {{assert}} lets all arguments through as if had not been there. The idea greatly simplifies error checking:
{{{
function task(arg1, arg2, ...)
local ret1 = assert(task1(arg1))
local ret2 = assert(task2(arg2))
...
end
}}}
If any task fails, the execution is aborted by {{assert}} and the error message is displayed to the user as the cause of the problem. If no error happens, the task completes as before. There isn't a single {{if}} statement and this is great. However, there are some problems with the idea.
First, the topmost {{task}} function doesn't respect the protocol followed by the lower-level tasks: It raises an error instead of returning {{nil}} followed by the error messages. Here is where the standard {{pcall}} comes in handy.
{{{
function xtask(arg1, arg2, ...)
local ret1 = assert(task1(arg1))
local ret2 = assert(task2(arg2))
...
end
function task(arg1, arg2, ...)
local ok, ret_or_err = pcall(xtask, arg1, arg2, ...)
if ok then return ret_or_err
else return nil, ret_or_err end
end
}}}
Our new {{task}} function is well behaved. {{Pcall}} catches any error raised by the calls to {{assert}} and returns it after the status code. That way, errors don't get propagated to the user of the high level {{task}} function.
These are the main ideas for our exception scheme, but there are still a few glitches to fix:
* Directly using {{pcall}} ruined the simplicity of the code;
* What happened to the cleanup function calls? What if we have to, say, close a file?
* {{Assert}} messes with the error message before raising the error (it adds line number information).
Fortunately, all these problems are very easy to solve and that's what we do in the following sections.
== Introducing the {{protect}} factory ==
We used the {{pcall}} function to shield the user from errors that could be raised by the underlying implementation. Instead of directly using {{pcall}} (and thus duplicating code) every time we prefer a factory that does the same job:
{{{
local function pack(ok, ...)
return ok, {...}
end
function protect(f)
return function(...)
local ok, ret = pack(pcall(f, ...))
if ok then return unpack(ret)
else return nil, ret[1] end
end
end
}}}
The {{protect}} factory receives a function that might raise exceptions and returns a function that respects our return value convention. Now we can rewrite the top-level {{task}} function in a much cleaner way:
{{{
task = protect(function(arg1, arg2, ...)
local ret1 = assert(task1(arg1))
local ret2 = assert(task2(arg2))
...
end)
}}}
The Lua implementation of the {{protect}} factory suffers with the creation of tables to hold multiple arguments and return values. It is possible (and easy) to implement the same function in C, without any table creation.
{{{
static int safecall(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) {
lua_pushnil(L);
lua_insert(L, 1);
return 2;
} else return lua_gettop(L);
}
static int protect(lua_State *L) {
lua_pushcclosure(L, safecall, 1);
return 1;
}
}}}
===The {{newtry}} factory===
Let's solve the two remaining issues with a single shot and use a concrete example to illustrate the proposed solution. Suppose you want to write a function to download an HTTP document. You have to connect, send the request and read the reply. Each of these tasks can fail, but if something goes wrong after you connected, you have to close the connection before returning the error message.
{{{
get = protect(function(host, path)
local c
-- create a try function with a finalizer to close the socket
local try = newtry(function()
if c then c:close() end
end)
-- connect and send request
c = try(connect(host, 80))
try(c:send("GET " .. path .. " HTTP/1.0\r\n\r\n"))
-- get headers
local h = {}
while 1 do
l = try(c:receive())
if l == "" then break end
table.insert(h, l)
end
-- get body
local b = try(c:receive("*a"))
c:close()
return b, h
end)
}}}
The {{newtry}} factory returns a function that works just like {{assert}}. The differences are that the {{try}} function doesn't mess with the error message and it calls an optional ''finalizer'' before raising the error. In our example, the finalizer simply closes the socket.
Even with a simple example like this, we see that the finalized exceptions simplified our life. Let's see what we gain in general, not just in this example:
* We don't need to declare dummy variables to hold error messages in case any ever shows up;
* We avoid using a variable to hold something that could either be a return value or an error message;
* We didn't have to use several ''if'' statements to check for errors;
* If an error happens, we know our finalizer is going to be invoked automatically;
* Exceptions get propagated, so we don't repeat these ''if'' statements until the error reaches the user.
Try writing the same function without the tricks we used above and you will see that the code gets ugly. Longer sequences of operations with error checking would get even uglier. So let's implement the {{newtry}} function in Lua:
{{{
function newtry(f)
return function(...)
if not arg[1] then
if f then f() end
error(arg[2], 0)
else
return ...
end
end
end
}}}
Again, the implementation suffers from the creation of tables at each function call, so we prefer the C version:
{{{
static int finalize(lua_State *L) {
if (!lua_toboolean(L, 1)) {
lua_pushvalue(L, lua_upvalueindex(1));
lua_pcall(L, 0, 0, 0);
lua_settop(L, 2);
lua_error(L);
return 0;
} else return lua_gettop(L);
}
static int do_nothing(lua_State *L) {
(void) L;
return 0;
}
static int newtry(lua_State *L) {
lua_settop(L, 1);
if (lua_isnil(L, 1))
lua_pushcfunction(L, do_nothing);
lua_pushcclosure(L, finalize, 1);
return 1;
}
}}}
===Final considerations===
The {{protect}} and {{newtry}} functions saved a ''lot'' of work in the implementation of {{LuaSocket}}[http://www.tecgraf.puc-rio.br/luasocket]. The size of some modules was cut in half by the these ideas. It's true the scheme is not as generic as the exception mechanism of programming languages like C++ or Java, but the power/simplicity ratio is favorable and I hope it serves you as well as it served {{LuaSocket}}.

View File

@ -34,7 +34,7 @@ local function make_plat(plat)
},
mingw32 = {
"LUASOCKET_DEBUG",
"LUASOCKET_INET_PTON",
-- "LUASOCKET_INET_PTON",
"WINVER=0x0501"
}
}
@ -69,6 +69,7 @@ local function make_plat(plat)
["socket.smtp"] = "src/smtp.lua",
ltn12 = "src/ltn12.lua",
socket = "src/socket.lua",
mbox = "src/mbox.lua",
mime = "src/mime.lua"
}
if plat == "unix"
@ -113,6 +114,7 @@ local function make_plat(plat)
then
modules["socket.core"].sources[#modules["socket.core"].sources+1] = "src/wsocket.c"
modules["socket.core"].libraries = { "ws2_32" }
modules["socket.core"].libdirs = {}
end
return { modules = modules }
end
@ -129,6 +131,5 @@ build = {
copy_directories = {
"docs"
, "samples"
, "etc"
, "test" }
}

0
makefile Executable file → Normal file
View File

View File

@ -1,7 +1,7 @@
#--------------------------------------------------------------------------
# Distribution makefile
#--------------------------------------------------------------------------
DIST = luasocket-3.0-rc1
DIST = luasocket-3.1.0
TEST = \
test/README \
@ -22,20 +22,17 @@ SAMPLES = \
samples/lpr.lua \
samples/talker.lua \
samples/tinyirc.lua
ETC = \
etc/README \
etc/b64.lua \
etc/check-links.lua \
etc/check-memory.lua \
etc/dict.lua \
etc/dispatch.lua \
etc/eol.lua \
etc/forward.lua \
etc/get.lua \
etc/lp.lua \
etc/qp.lua \
etc/tftp.lua
samples/b64.lua \
samples/check-links.lua \
samples/check-memory.lua \
samples/dict.lua \
samples/dispatch.lua \
samples/eol.lua \
samples/forward.lua \
samples/get.lua \
samples/lp.lua \
samples/qp.lua \
samples/tftp.lua
SRC = \
src/makefile \
@ -117,9 +114,6 @@ dist:
cp -vf README.md $(DIST)
cp -vf $(MAKE) $(DIST)
mkdir -p $(DIST)/etc
cp -vf $(ETC) $(DIST)/etc
mkdir -p $(DIST)/src
cp -vf $(SRC) $(DIST)/src

0
mime.vcxproj Executable file → Normal file
View File

View File

@ -0,0 +1,135 @@
package = "LuaSocket"
version = "3.1.0-1"
source = {
url = "git+https://github.com/lunarmodules/luasocket.git",
tag = "v3.1.0"
}
description = {
summary = "Network support for the Lua language",
detailed = [[
LuaSocket is a Lua extension library composed of two parts: a set of C
modules that provide support for the TCP and UDP transport layers, and a
set of Lua modules that provide functions commonly needed by applications
that deal with the Internet.
]],
homepage = "https://github.com/lunarmodules/luasocket",
license = "MIT"
}
dependencies = {
"lua >= 5.1"
}
local function make_plat(plat)
local defines = {
unix = {
"LUASOCKET_DEBUG"
},
macosx = {
"LUASOCKET_DEBUG",
"UNIX_HAS_SUN_LEN"
},
win32 = {
"LUASOCKET_DEBUG",
"NDEBUG"
},
mingw32 = {
"LUASOCKET_DEBUG",
-- "LUASOCKET_INET_PTON",
"WINVER=0x0501"
}
}
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/compat.c"
, "src/auxiliar.c"
, "src/options.c"
, "src/timeout.c"
, "src/io.c"
, "src/usocket.c"
, "src/unix.c"
, "src/unixdgram.c"
, "src/unixstream.c" },
defines = defines[plat],
incdir = "/src"
}
modules["socket.serial"] = {
sources = {
"src/buffer.c"
, "src/compat.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" }
modules["socket.core"].libdirs = {}
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 = {
"docs"
, "samples"
, "etc"
, "test" }
}

View File

@ -1,11 +1,95 @@
This directory contains some sample programs using
LuaSocket. This code is not supported.
tftp.lua -- Trivial FTP client
This module implements file retrieval by the TFTP protocol.
Its main use was to test the UDP code, but since someone
found it usefull, I turned it into a module that is almost
official (no uploads, yet).
dict.lua -- Dict client
The dict.lua module started with a cool simple client
for the DICT protocol, written by Luiz Henrique Figueiredo.
This new version has been converted into a library, similar
to the HTTP and FTP libraries, that can be used from within
any luasocket application. Take a look on the source code
and you will be able to figure out how to use it.
lp.lua -- LPD client library
The lp.lua module implements the client part of the Line
Printer Daemon protocol, used to print files on Unix
machines. It is courtesy of David Burgess! See the source
code and the lpr.lua in the examples directory.
b64.lua
qp.lua
eol.lua
These are tiny programs that perform Base64,
Quoted-Printable and end-of-line marker conversions.
get.lua -- file retriever
This little program is a client that uses the FTP and
HTTP code to implement a command line file graber. Just
run
lua get.lua <remote-file> [<local-file>]
to download a remote file (either ftp:// or http://) to
the specified local file. The program also prints the
download throughput, elapsed time, bytes already downloaded
etc during download.
check-memory.lua -- checks memory consumption
This is just to see how much memory each module uses.
dispatch.lua -- coroutine based dispatcher
This is a first try at a coroutine based non-blocking
dispatcher for LuaSocket. Take a look at 'check-links.lua'
and at 'forward.lua' to see how to use it.
check-links.lua -- HTML link checker program
This little program scans a HTML file and checks for broken
links. It is similar to check-links.pl by Jamie Zawinski,
but uses all facilities of the LuaSocket library and the Lua
language. It has not been thoroughly tested, but it should
work. Just run
lua check-links.lua [-n] {<url>} > output
and open the result to see a list of broken links. Make sure
you check the '-n' switch. It runs in non-blocking mode,
using coroutines, and is MUCH faster!
forward.lua -- coroutine based forward server
This is a forward server that can accept several connections
and transfers simultaneously using non-blocking I/O and the
coroutine-based dispatcher. You can run, for example
lua forward.lua 8080:proxy.com:3128
to redirect all local conections to port 8080 to the host
'proxy.com' at port 3128.
unix.c and unix.h
This is an implementation of Unix local domain sockets and
demonstrates how to extend LuaSocket with a new type of
transport. It has been tested on Linux and on Mac OS X.
listener.lua -- socket to stdout
talker.lua -- stdin to socket
listener.lua and talker.lua are about the simplest
applications you can write using LuaSocket. Run
applications you can write using LuaSocket. Run
'lua listener.lua' and 'lua talker.lua'
@ -17,13 +101,13 @@ be printed by listen.lua.
This is a cool program written by David Burgess to print
files using the Line Printer Daemon protocol, widely used in
Unix machines. It uses the lp.lua implementation, in the
etc directory. Just run 'lua lpr.lua <filename>
samples directory. Just run 'lua lpr.lua <filename>
queue=<printername>' and the file will print!
cddb.lua -- CDDB client
This is the first try on a simple CDDB client. Not really
useful, but one day it might become a module.
useful, but one day it might become a module.
daytimeclnt.lua -- day time client

0
socket.vcxproj Executable file → Normal file
View File

View File

@ -54,7 +54,7 @@ local function receiveheaders(sock, headers)
while line ~= "" do
-- get field-name and value
name, value = socket.skip(2, string.find(line, "^(.-):%s*(.*)"))
if not (name and value) then return nil, "malformed reponse headers" end
if not (name and value) then return nil, "malformed response headers" end
name = string.lower(name)
-- get next line (value might be folded)
line, err = sock:receive()
@ -62,7 +62,7 @@ local function receiveheaders(sock, headers)
-- unfold any folded values
while string.find(line, "^%s") do
value = value .. line
line = sock:receive()
line, err = sock:receive()
if err then return nil, err end
end
-- save pair in table
@ -81,7 +81,7 @@ socket.sourcet["http-chunked"] = function(sock, headers)
dirty = function() return sock:dirty() end
}, {
__call = function()
-- get chunk size, skip extention
-- get chunk size, skip extension
local line, err = sock:receive()
if err then return nil, err end
local size = base.tonumber(string.gsub(line, ";.*", ""), 16)
@ -219,9 +219,11 @@ local function adjustproxy(reqt)
local proxy = reqt.proxy or _M.PROXY
if proxy then
proxy = url.parse(proxy)
return proxy.host, proxy.port or 3128
proxy.port = proxy.port or 3128
proxy.create = SCHEMES[proxy.scheme].create(reqt)
return proxy.host, proxy.port, proxy.create
else
return reqt.host, reqt.port
return reqt.host, reqt.port, reqt.create
end
end
@ -279,7 +281,7 @@ local function adjustrequest(reqt)
if not (host and host ~= "") then
socket.try(nil, "invalid host '" .. base.tostring(nreqt.host) .. "'")
end
-- compute uri if user hasn't overriden
-- compute uri if user hasn't overridden
nreqt.uri = reqt.uri or adjusturi(nreqt)
-- adjust headers in request
nreqt.headers = adjustheaders(nreqt)
@ -291,7 +293,10 @@ local function adjustrequest(reqt)
end
-- ajust host and port if there is a proxy
nreqt.host, nreqt.port = adjustproxy(nreqt)
local proxy_create
nreqt.host, nreqt.port, proxy_create = adjustproxy(nreqt)
if not reqt.create then nreqt.create = proxy_create end
return nreqt
end
@ -300,6 +305,8 @@ local function shouldredirect(reqt, code, headers)
if not location then return false end
location = string.gsub(location, "%s", "")
if location == "" then return false end
-- the RFC says the redirect URL may be relative
location = url.absolute(reqt.url, location)
local scheme = url.parse(location).scheme
if scheme and (not SCHEMES[scheme]) then return false end
-- avoid https downgrades
@ -323,8 +330,7 @@ end
local trequest, tredirect
--[[local]] function tredirect(reqt, location)
-- the RFC says the redirect URL has to be absolute, but some
-- servers do not respect that
-- the RFC says the redirect URL may be relative
local newurl = url.absolute(reqt.url, location)
-- if switching schemes, reset port and create function
if url.parse(newurl).scheme ~= reqt.scheme then

View File

@ -253,7 +253,7 @@ int inet_meth_getpeername(lua_State *L, p_socket ps, int family)
port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV);
if (err) {
lua_pushnil(L);
lua_pushstring(L, gai_strerror(err));
lua_pushstring(L, LUA_GAI_STRERROR(err));
return 2;
}
lua_pushstring(L, name);
@ -286,11 +286,11 @@ int inet_meth_getsockname(lua_State *L, p_socket ps, int family)
name, INET6_ADDRSTRLEN, port, 6, NI_NUMERICHOST | NI_NUMERICSERV);
if (err) {
lua_pushnil(L);
lua_pushstring(L, gai_strerror(err));
lua_pushstring(L, LUA_GAI_STRERROR(err));
return 2;
}
lua_pushstring(L, name);
lua_pushstring(L, port);
lua_pushinteger(L, (int) strtol(port, (char **) NULL, 10));
switch (family) {
case AF_INET: lua_pushliteral(L, "inet"); break;
case AF_INET6: lua_pushliteral(L, "inet6"); break;

0
src/luasocket.c Executable file → Normal file
View File

View File

@ -10,7 +10,7 @@
/*-------------------------------------------------------------------------* \
* Current socket library version
\*-------------------------------------------------------------------------*/
#define LUASOCKET_VERSION "LuaSocket 3.0-rc1"
#define LUASOCKET_VERSION "LuaSocket 3.1.0"
#define LUASOCKET_COPYRIGHT "Copyright (C) 1999-2013 Diego Nehab"
/*-------------------------------------------------------------------------*\

4
src/makefile Executable file → Normal file
View File

@ -1,6 +1,6 @@
# luasocket src/makefile
#
# Definitions in this section can be overriden on the command line or in the
# Definitions in this section can be overridden on the command line or in the
# environment.
#
# These are equivalent:
@ -272,7 +272,7 @@ SOCKET_win64=wsocket.obj
#
SO=$(SO_$(PLAT))
O=$(O_$(PLAT))
SOCKET_V=3.0-rc1
SOCKET_V=3.1.0
MIME_V=1.0.3
SOCKET_SO=socket-$(SOCKET_V).$(SO)
MIME_SO=mime-$(MIME_V).$(SO)

0
src/mime.c Executable file → Normal file
View File

View File

@ -54,6 +54,33 @@ int opt_meth_getoption(lua_State *L, p_opt opt, p_socket ps)
return opt->func(L, ps);
}
/*------------------------------------------------------*/
/* binds socket to network interface */
int opt_set_bindtodevice(lua_State *L, p_socket ps)
{
#ifndef SO_BINDTODEVICE
return luaL_error(L, "SO_BINDTODEVICE is not supported on this operating system");
#else
const char *dev = luaL_checkstring(L, 3);
return opt_set(L, ps, SOL_SOCKET, SO_BINDTODEVICE, (char*)dev, strlen(dev)+1);
#endif
}
int opt_get_bindtodevice(lua_State *L, p_socket ps)
{
#ifndef SO_BINDTODEVICE
return luaL_error(L, "SO_BINDTODEVICE is not supported on this operating system");
#else
char dev[IFNAMSIZ];
int len = sizeof(dev);
int err = opt_get(L, ps, SOL_SOCKET, SO_BINDTODEVICE, &dev, &len);
if (err)
return err;
lua_pushstring(L, dev);
return 1;
#endif
}
/*------------------------------------------------------*/
/* enables reuse of local address */
int opt_set_reuseaddr(lua_State *L, p_socket ps)
@ -190,6 +217,31 @@ int opt_set_send_buf_size(lua_State *L, p_socket ps)
return opt_setint(L, ps, SOL_SOCKET, SO_SNDBUF);
}
/*------------------------------------------------------*/
#ifdef TCP_FASTOPEN
int opt_set_tcp_fastopen(lua_State *L, p_socket ps)
{
return opt_setint(L, ps, IPPROTO_TCP, TCP_FASTOPEN);
}
#endif
#ifdef TCP_FASTOPEN_CONNECT
int opt_set_tcp_fastopen_connect(lua_State *L, p_socket ps)
{
return opt_setint(L, ps, IPPROTO_TCP, TCP_FASTOPEN_CONNECT);
}
#endif
/*------------------------------------------------------*/
#ifdef TCP_DEFER_ACCEPT
int opt_set_tcp_defer_accept(lua_State *L, p_socket ps)
{
return opt_setint(L, ps, IPPROTO_TCP, TCP_DEFER_ACCEPT);
}
#endif
/*------------------------------------------------------*/
int opt_set_ip6_unicast_hops(lua_State *L, p_socket ps)
{

View File

@ -49,6 +49,13 @@ int opt_set_tcp_keepintvl(lua_State *L, p_socket ps);
int opt_get_tcp_keepintvl(lua_State *L, p_socket ps);
#endif
#ifdef TCP_DEFER_ACCEPT
int opt_set_tcp_defer_accept(lua_State *L, p_socket ps);
#endif
int opt_set_bindtodevice(lua_State *L, p_socket ps);
int opt_get_bindtodevice(lua_State *L, p_socket ps);
int opt_set_keepalive(lua_State *L, p_socket ps);
int opt_get_keepalive(lua_State *L, p_socket ps);
@ -64,6 +71,13 @@ int opt_get_recv_buf_size(lua_State *L, p_socket ps);
int opt_set_send_buf_size(lua_State *L, p_socket ps);
int opt_get_send_buf_size(lua_State *L, p_socket ps);
#ifdef TCP_FASTOPEN
int opt_set_tcp_fastopen(lua_State *L, p_socket ps);
#endif
#ifdef TCP_FASTOPEN_CONNECT
int opt_set_tcp_fastopen_connect(lua_State *L, p_socket ps);
#endif
int opt_set_ip6_unicast_hops(lua_State *L, p_socket ps);
int opt_get_ip6_unicast_hops(lua_State *L, p_socket ps);

View File

@ -225,7 +225,7 @@ local function adjust_headers(mesgt)
lower["date"] = lower["date"] or
os.date("!%a, %d %b %Y %H:%M:%S ") .. (mesgt.zone or _M.ZONE)
lower["x-mailer"] = lower["x-mailer"] or socket._VERSION
-- this can't be overriden
-- this can't be overridden
lower["mime-version"] = "1.0"
return lower
end
@ -253,4 +253,4 @@ _M.send = socket.protect(function(mailt)
return s:close()
end)
return _M
return _M

View File

@ -16,8 +16,10 @@
\*=========================================================================*/
#ifdef _WIN32
#include "wsocket.h"
#define LUA_GAI_STRERROR gai_strerrorA
#else
#include "usocket.h"
#define LUA_GAI_STRERROR gai_strerror
#endif
/*=========================================================================*\

View File

@ -71,6 +71,7 @@ static luaL_Reg tcp_methods[] = {
/* socket option handlers */
static t_opt optget[] = {
{"bindtodevice", opt_get_bindtodevice},
{"keepalive", opt_get_keepalive},
{"reuseaddr", opt_get_reuseaddr},
{"reuseport", opt_get_reuseport},
@ -92,6 +93,7 @@ static t_opt optget[] = {
};
static t_opt optset[] = {
{"bindtodevice", opt_set_bindtodevice},
{"keepalive", opt_set_keepalive},
{"reuseaddr", opt_set_reuseaddr},
{"reuseport", opt_set_reuseport},
@ -109,6 +111,15 @@ static t_opt optset[] = {
{"linger", opt_set_linger},
{"recv-buffer-size", opt_set_recv_buf_size},
{"send-buffer-size", opt_set_send_buf_size},
#ifdef TCP_DEFER_ACCEPT
{"tcp-defer-accept", opt_set_tcp_defer_accept},
#endif
#ifdef TCP_FASTOPEN
{"tcp-fastopen", opt_set_tcp_fastopen},
#endif
#ifdef TCP_FASTOPEN_CONNECT
{"tcp-fastopen-connect", opt_set_tcp_fastopen_connect},
#endif
{NULL, NULL}
};

View File

@ -191,7 +191,7 @@ static int meth_sendto(lua_State *L) {
err = getaddrinfo(ip, port, &aihint, &ai);
if (err) {
lua_pushnil(L);
lua_pushstring(L, gai_strerror(err));
lua_pushstring(L, LUA_GAI_STRERROR(err));
return 2;
}
@ -290,7 +290,7 @@ static int meth_receivefrom(lua_State *L) {
INET6_ADDRSTRLEN, portstr, 6, NI_NUMERICHOST | NI_NUMERICSERV);
if (err) {
lua_pushnil(L);
lua_pushstring(L, gai_strerror(err));
lua_pushstring(L, LUA_GAI_STRERROR(err));
if (wanted > sizeof(buf)) free(dgram);
return 2;
}

View File

@ -16,13 +16,6 @@
#define UNIXDGRAM_DATAGRAMSIZE 8192
/* provide a SUN_LEN macro if sys/un.h doesn't (e.g. Android) */
#ifndef SUN_LEN
#define SUN_LEN(ptr) \
((size_t) (((struct sockaddr_un *) 0)->sun_path) \
+ strlen ((ptr)->sun_path))
#endif
/*=========================================================================*\
* Internal function prototypes
\*=========================================================================*/
@ -42,8 +35,8 @@ 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);
static const char *unixdgram_tryconnect(p_unix un, const char *path, size_t len);
static const char *unixdgram_trybind(p_unix un, const char *path, size_t len);
/* unixdgram object methods */
static luaL_Reg unixdgram_methods[] = {
@ -133,13 +126,12 @@ static int meth_send(lua_State *L)
static int meth_sendto(lua_State *L)
{
p_unix un = (p_unix) auxiliar_checkclass(L, "unixdgram{unconnected}", 1);
size_t count, sent = 0;
size_t count, sent, len = 0;
const char *data = luaL_checklstring(L, 2, &count);
const char *path = luaL_checkstring(L, 3);
const char *path = luaL_checklstring(L, 3, &len);
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);
@ -148,7 +140,7 @@ static int meth_sendto(lua_State *L)
}
memset(&remote, 0, sizeof(remote));
strcpy(remote.sun_path, path);
memcpy(remote.sun_path, path, len);
remote.sun_family = AF_UNIX;
timeout_markstart(tm);
#ifdef UNIX_HAS_SUN_LEN
@ -264,18 +256,22 @@ static int meth_dirty(lua_State *L) {
/*-------------------------------------------------------------------------*\
* Binds an object to an address
\*-------------------------------------------------------------------------*/
static const char *unixdgram_trybind(p_unix un, const char *path) {
static const char *unixdgram_trybind(p_unix un, const char *path, size_t len) {
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);
memcpy(local.sun_path, path, len);
local.sun_family = AF_UNIX;
size_t addrlen = SUN_LEN(&local);
#ifdef UNIX_HAS_SUN_LEN
local.sun_len = addrlen + 1;
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
int err = socket_bind(&un->sock, (SA *) &local, addrlen);
if (err != IO_DONE) socket_destroy(&un->sock);
return socket_strerror(err);
}
@ -283,8 +279,9 @@ static const char *unixdgram_trybind(p_unix un, const char *path) {
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);
size_t len;
const char *path = luaL_checklstring(L, 2, &len);
const char *err = unixdgram_trybind(un, path, len);
if (err) {
lua_pushnil(L);
lua_pushstring(L, err);
@ -313,20 +310,23 @@ static int meth_getsockname(lua_State *L)
/*-------------------------------------------------------------------------*\
* Turns a master unixdgram object into a client object.
\*-------------------------------------------------------------------------*/
static const char *unixdgram_tryconnect(p_unix un, const char *path)
static const char *unixdgram_tryconnect(p_unix un, const char *path, size_t len)
{
struct sockaddr_un remote;
size_t len = strlen(path);
int err;
if (len >= sizeof(remote.sun_path)) return "path too long";
memset(&remote, 0, sizeof(remote));
strcpy(remote.sun_path, path);
memcpy(remote.sun_path, path, len);
remote.sun_family = AF_UNIX;
timeout_markstart(&un->tm);
size_t addrlen = SUN_LEN(&remote);
#ifdef UNIX_HAS_SUN_LEN
remote.sun_len = addrlen + 1;
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
int err = socket_connect(&un->sock, (SA *) &remote, addrlen, &un->tm);
if (err != IO_DONE) socket_destroy(&un->sock);
return socket_strerror(err);
}
@ -334,8 +334,9 @@ static const char *unixdgram_tryconnect(p_unix un, const char *path)
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);
size_t len;
const char *path = luaL_checklstring(L, 2, &len);
const char *err = unixdgram_tryconnect(un, path, len);
if (err) {
lua_pushnil(L);
lua_pushstring(L, err);

View File

@ -33,8 +33,8 @@ 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);
static const char *unixstream_tryconnect(p_unix un, const char *path, size_t len);
static const char *unixstream_trybind(p_unix un, const char *path, size_t len);
/* unixstream object methods */
static luaL_Reg unixstream_methods[] = {
@ -181,13 +181,12 @@ static int meth_accept(lua_State *L) {
/*-------------------------------------------------------------------------*\
* Binds an object to an address
\*-------------------------------------------------------------------------*/
static const char *unixstream_trybind(p_unix un, const char *path) {
static const char *unixstream_trybind(p_unix un, const char *path, size_t len) {
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);
memcpy(local.sun_path, path, len);
local.sun_family = AF_UNIX;
#ifdef UNIX_HAS_SUN_LEN
local.sun_len = sizeof(local.sun_family) + sizeof(local.sun_len)
@ -204,8 +203,9 @@ static const char *unixstream_trybind(p_unix un, const char *path) {
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);
size_t len;
const char *path = luaL_checklstring(L, 2, &len);
const char *err = unixstream_trybind(un, path, len);
if (err) {
lua_pushnil(L);
lua_pushstring(L, err);
@ -234,14 +234,13 @@ static int meth_getsockname(lua_State *L)
/*-------------------------------------------------------------------------*\
* Turns a master unixstream object into a client object.
\*-------------------------------------------------------------------------*/
static const char *unixstream_tryconnect(p_unix un, const char *path)
static const char *unixstream_tryconnect(p_unix un, const char *path, size_t len)
{
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);
memcpy(remote.sun_path, path, len);
remote.sun_family = AF_UNIX;
timeout_markstart(&un->tm);
#ifdef UNIX_HAS_SUN_LEN
@ -259,8 +258,9 @@ static const char *unixstream_tryconnect(p_unix un, const char *path)
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);
size_t len;
const char *path = luaL_checklstring(L, 2, &len);
const char *err = unixstream_tryconnect(un, path, len);
if (err) {
lua_pushnil(L);
lua_pushstring(L, err);

View File

@ -152,7 +152,7 @@ function _M.parse(url, default)
url = string.gsub(url, "^([%w][%w%+%-%.]*)%:",
function(s) parsed.scheme = s; return "" end)
-- get authority
url = string.gsub(url, "^//([^/]*)", function(n)
url = string.gsub(url, "^//([^/%?#]*)", function(n)
parsed.authority = n
return ""
end)

View File

@ -236,7 +236,7 @@ int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent,
*sent = 0;
if (*ps == SOCKET_INVALID) return IO_CLOSED;
for ( ;; ) {
long put = (long) sendto(*ps, data, count, 0, addr, len);
long put = (long) sendto(*ps, data, count, 0, addr, len);
if (put >= 0) {
*sent = put;
return IO_DONE;
@ -449,6 +449,6 @@ const char *socket_gaistrerror(int err) {
case EAI_SERVICE: return PIE_SERVICE;
case EAI_SOCKTYPE: return PIE_SOCKTYPE;
case EAI_SYSTEM: return strerror(errno);
default: return gai_strerror(err);
default: return LUA_GAI_STRERROR(err);
}
}

4
src/wsocket.c Executable file → Normal file
View File

@ -262,6 +262,7 @@ int socket_recv(p_socket ps, char *data, size_t count, size_t *got,
if (err != WSAEWOULDBLOCK) {
if (err != WSAECONNRESET || prev == WSAECONNRESET) return err;
prev = err;
continue;
}
if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err;
}
@ -291,6 +292,7 @@ int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got,
if (err != WSAEWOULDBLOCK) {
if (err != WSAECONNRESET || prev == WSAECONNRESET) return err;
prev = err;
continue;
}
if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err;
}
@ -429,6 +431,6 @@ const char *socket_gaistrerror(int err) {
#ifdef EAI_SYSTEM
case EAI_SYSTEM: return strerror(errno);
#endif
default: return gai_strerror(err);
default: return LUA_GAI_STRERROR(err);
}
}

View File

@ -265,6 +265,37 @@ ignore = {
}
check_request(request, expect, ignore)
-- Use https://httpbin.org/#/Dynamic_data/get_base64__value_ for testing
-----------------------------------------------------
io.write("testing absolute https redirection: ")
request = {
url = "https://httpbin.org/redirect-to?url=https://httpbin.org/base64/THVhIFNvY2tldA=="
}
expect = {
code = 200,
body = "Lua Socket"
}
ignore = {
status = 1,
headers = 1
}
check_request(request, expect, ignore)
-----------------------------------------------------
io.write("testing relative https redirection: ")
request = {
url = "https://httpbin.org/redirect-to?url=/base64/THVhIFNvY2tldA=="
}
expect = {
code = 200,
body = "Lua Socket"
}
ignore = {
status = 1,
headers = 1
}
check_request(request, expect, ignore)
------------------------------------------------------------------------
--[[
io.write("testing proxy with redirection: ")

0
vc32.bat Executable file → Normal file
View File

0
vc64.bat Executable file → Normal file
View File

0
win32.cmd Executable file → Normal file
View File

0
win64.cmd Executable file → Normal file
View File