Manual is almost done. HTTP is missing.

Implemented new distribution scheme.
Select is now purely C.
HTTP reimplemented seems faster dunno why.
LTN12 functions that coroutines fail gracefully.
This commit is contained in:
Diego Nehab 2004-06-15 06:24:00 +00:00
parent 9ed7f955e5
commit 58096449c6
40 changed files with 2035 additions and 815 deletions

11
TODO
View File

@ -2,6 +2,15 @@ ajeitar os README.*
ajeitar select. upvalue nao tem nada a ver... ajeitar select. upvalue nao tem nada a ver...
make sure filter.chain fails gracefully. make sure filter.chain fails gracefully.
ajeitar o manual sobre select, mais liberal agora ajeitar o manual sobre select, mais liberal agora
falar sobre o novo esquema de namespace
tirar socket.url socket.ftp etc do manual. agora os namespaces estao
liberados.
ajeitar as referencias a RFCS e LTNS em todos os arquivos.
proxy no ftp
ajeitar < e-mail > no smtp?
ajeitar referencias a LTN12 nos manuais
make sure sockets are closed when exceptions are raised
falar sobre encodet/wrapt/decodet no manual sobre mime falar sobre encodet/wrapt/decodet no manual sobre mime
@ -14,8 +23,6 @@ expose encode/decode tables to provide extensibility for mime module
use coroutines instead of fancy filters use coroutines instead of fancy filters
check garbage collection in test*.lua check garbage collection in test*.lua
pop3???
add socket.TIMEOUT to be default timeout? add socket.TIMEOUT to be default timeout?

View File

@ -35,9 +35,9 @@
<p> <p>
FTP (File Transfer Protocol) is a protocol used to transfer files FTP (File Transfer Protocol) is a protocol used to transfer files
between hosts. The module <tt>ftp.lua</tt> offers simple FTP support, between hosts. The module <tt>ftp.lua</tt> offers simple FTP support.
allowing applications to download and upload files, and list directory Applications can easily download and upload files.
contents. The implementation conforms to The implementation conforms to
<a href="http://www.cs.princeton.edu/~diego/rfc/rfc0959.txt">RFC 959</a>. <a href="http://www.cs.princeton.edu/~diego/rfc/rfc0959.txt">RFC 959</a>.
</p> </p>
@ -49,175 +49,191 @@ URLs MUST conform to
<blockquote> <blockquote>
<tt> <tt>
[ftp://][&lt;user&gt;[:&lt;password&gt;]@]&lt;host&gt;[:&lt;port&gt;][/&lt;path&gt;][<i>type</i>=a|i|d]</tt> [ftp://][&lt;user&gt;[:&lt;password&gt;]@]&lt;host&gt;[:&lt;port&gt;][/&lt;path&gt;][<i>type</i>=a|i]</tt>
</blockquote> </blockquote>
<p>
High level functions are provided supporting the most common operations.
These high level functions are implemented on top of a lower level
interface. By using the low-level interface, users can easily create their
own functions to access <em>any</em> operation supported by the FTP
protocol. For that, check the implementation.
</p>
<p>
To use some of the functions in this module, a good understanding of
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">
LTN012, Filters sources and sinks</a> is necessary.
</p>
<p>
The following constants can be set to control the default behaviour of
the FTP module:
</p>
<ul>
<li> <tt>PASSWORD</tt>: default anonymous password.
<li> <tt>PORT</tt>: default port used for the control connection;
<li> <tt>TIMEOUT</tt>: sets the timeout for all I/O operations;
<li> <tt>USER</tt>: default anonymous user;
</ul>
<!-- ftp.get ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- ftp.get ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=get> <p class=name id=get>
socket.ftp.<b>get(</b>url<b>)</b><br> ftp.<b>get(</b>url<b>)</b><br>
socket.ftp.<b>get{</b><br> ftp.<b>get{</b><br>
&nbsp;&nbsp;url = <i>string</i>,<br> &nbsp;&nbsp;host = <i>string</i>,<br>
&nbsp;&nbsp;type = <i>string</i>,<br> &nbsp;&nbsp;sink = <i>LTN12 sink</i>,<br>
&nbsp;&nbsp;user = <i>string</i>,<br> &nbsp;&nbsp;argument <i>or</i> path = <i>string</i>,<br>
&nbsp;&nbsp;password = <i>string</i><br> &nbsp;&nbsp;[user = <i>string</i>,]<br>
&nbsp;&nbsp;[password = <i>string</i>]<br>
&nbsp;&nbsp;[command = <i>string</i>,]<br>
&nbsp;&nbsp;[port = <i>number</i>,]<br>
&nbsp;&nbsp;[type = <i>string</i>,]<br>
&nbsp;&nbsp;[step = <i>LTN12 pump step</i>],<br>
<b>}</b> <b>}</b>
</p> </p>
<p class=description> <p class=description>
Downloads an URL from a FTP server. The <tt>get</tt> function has two forms. The simple form has fixed
functionality: it downloads the contents of a URL and returns it as a
string. The generic form allows a <em>lot</em> more control, as explained
below.
</p> </p>
<p class=parameters> <p class=parameters>
The function can be called either directly with a <tt>url</tt> If the argument of the <tt>get</tt> function is a table, the function
or with a <em>request table</em>. expects at least the fields <tt>host</tt>, <tt>sink</tt>, and one of
Fields passed explicitly in the request table override those <tt>argument</tt> or <tt>path</tt> (<tt>argument</tt> takes
present in the <tt>url</tt>. precedence). <tt>Host</tt> is the server to connect to. <tt>Sink</tt> is
</p> the LTN12 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
<p class=parameters> optional arguments are the following:
The parameter <tt>type</tt> accepts values '<tt>a</tt>' (ASCII, the
default), '<tt>i</tt>' (binary) or '<tt>d</tt>' (directory listing) and
determines the transfer type. If <tt>&lt;path&gt;</tt> ends with a
'<tt>/</tt>' or <tt>type</tt> is '<tt>d</tt>', a directory listing of
<tt>&lt;path&gt;</tt> is returned. If no <tt>user</tt> is provided in the
<tt>url</tt> or explicitly, the function tries to log in as user
'<tt>anonymous</tt>'.
</p> </p>
<ul>
<li><tt>user</tt>, <tt>password</tt>: User name and password used for
authentication. Defaults to "<tt>ftp:anonymous@anonymous.org</tt>";
<li><tt>command</tt>: The FTP command used to obtain data. Defaults to
"<tt>retr</tt>", but see example below;
<li><tt>port</tt>: The port to contacct the server at. Defaults to 21;
<li><tt>type</tt>: The transfer mode. Can take values "<tt>i</tt>" or
"<tt>a</tt>". Defaults to whatever is the server default;
<li><tt>step</tt>: LTN12 pump step function used to pass data from the
server to the sink. Defaults to the LTN12 <tt>pump.step</tt> function.
</ul>
<p class=return> <p class=return>
If successful, the function returns If successful, the simple version returns the URL contents as a
the file content as a string. In case of error, the function returns string, and the generic function returns 1. In case of error, both
<b><tt>nil</tt></b> and an error message describing the error. functions return <b><tt>nil</tt></b> and an error message describing the
error.
</p> </p>
<pre class=example> <pre class=example>
-- Log as user "anonymous" on server "ftp.tecgraf.puc-rio.br", -- load the ftp support
-- go to directory "pub/lua" and get file "lua.tar.gz" as binary. local ftp = require("ftp")
f, e = socket.ftp.get("ftp://ftp.tecgraf.puc-rio.br/pub/lua/lua.tar.gz;type=i")
-- Log as user "anonymous" on server "ftp.tecgraf.puc-rio.br", -- Log as user "anonymous" on server "ftp.tecgraf.puc-rio.br",
-- go to director "pub" and retrieve directory listing of directory "lua" -- and get file "lua.tar.gz" from directory "pub/lua" as binary.
f, e = socket.ftp.get("ftp://ftp.tecgraf.puc-rio.br/pub/lua;type=d") f, e = ftp.get("ftp://ftp.tecgraf.puc-rio.br/pub/lua/lua.tar.gz;type=i")
-- Log as user "diego", password "nehab", on server "ftp.tecgraf.puc-rio.br",
-- go to directory "tec/luasocket/bin" and retrieve file "luasocket.exe"
-- (actually, fails because of wrong password, of course)
f, e = socket.ftp.get{
url = "ftp://ftp.tecgraf.puc-rio.br/tec/luasocket/bin/luasocket.exe",
user = "diego",
password = "nehab",
type = "i"
}
-- f returns nil, and e returns an appropriate error message
</pre> </pre>
<!-- get_cb +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <pre class=example>
-- load needed modules
local ftp = require("ftp")
local ltn12 = require("ltn12")
local url = require("url")
<p class=name id=get_cb> -- a function that returns a directory listing
socket.ftp.<b>get_cb{</b><br> function ls(u)
&nbsp;&nbsp;url = <i>string</i>,<br> local t = {}
&nbsp;&nbsp;type = <i>string</i>,<br> local p = url.parse(u)
&nbsp;&nbsp;content_cb = <i>receive-callback</i>,<br> p.command = "nlst"
&nbsp;&nbsp;user = <i>string</i>,<br> p.sink = ltn12.sink.table(t)
&nbsp;&nbsp;password = <i>string</i><br> local r, e = ftp.get(p)
<b>}</b> return r and table.concat(t), e
</p> end
</pre>
<p class=description>
Same as <a href="#get"><tt>get</tt></a>, but the library returns
the content of the downloaded file to the receive callback
<tt>content_cb</tt>.
</p>
<p class=note>
Note: for more information on callbacks, refer to
<a href="stream.html#stream">Streaming with callbacks</a>.
</p>
<!-- put ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- put ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=put> <p class=name id=put>
socket.ftp.<b>put(</b>url, content<b>)</b><br> ftp.<b>put(</b>url, content<b>)</b><br>
socket.ftp.<b>put{</b><br> ftp.<b>put{</b><br>
&nbsp;&nbsp;url = <i>string</i>,<br> &nbsp;&nbsp;host = <i>string</i>,<br>
&nbsp;&nbsp;content = <i>string</i>,<br> &nbsp;&nbsp;source = <i>LTN12 sink</i>,<br>
&nbsp;&nbsp;type = <i>string</i>,<br> &nbsp;&nbsp;argument <i>or</i> path = <i>string</i>,<br>
&nbsp;&nbsp;user = <i>string</i>,<br> &nbsp;&nbsp;[user = <i>string</i>,]<br>
&nbsp;&nbsp;password = <i>string</i><br> &nbsp;&nbsp;[password = <i>string</i>]<br>
&nbsp;&nbsp;[command = <i>string</i>,]<br>
&nbsp;&nbsp;[port = <i>number</i>,]<br>
&nbsp;&nbsp;[type = <i>string</i>,]<br>
&nbsp;&nbsp;[step = <i>LTN12 pump step</i>],<br>
<b>}</b> <b>}</b>
</p> </p>
<p class=description> <p class=description>
Upload a file to a FTP server. The <tt>put</tt> function has two forms. The simple form has fixed
functionality: it uploads a string of content into a URL. The generic form
allows a <em>lot</em> more control, as explained below.
</p> </p>
<p class=parameters> <p class=parameters>
The function can be called directly with a If the argument of the <tt>put</tt> function is a table, the function
<tt>url</tt> and <tt>content</tt> parameters, or with a expects at least the fields <tt>host</tt>, <tt>source</tt>, and one of
<em>request table</em>. <tt>argument</tt> or <tt>path</tt> (<tt>argument</tt> takes
Values passed explicitly in the request table override those present in precedence). <tt>Host</tt> is the server to connect to. <tt>Source</tt> is
the <tt>url</tt>. The parameter <tt>type</tt> accept values the LTN12 source that will provide the contents to be uploaded.
'<tt>a</tt>' (ASCII, the default) or '<tt>i</tt>' (binary) and <tt>Argument</tt> or
determines the transfer type. If no <tt>user</tt> is provided, the <tt>path</tt> give the target path to the resource in the server. The
function tries to log in as '<tt>anonymous</tt>'. optional arguments are the following:
</p> </p>
<ul>
<li><tt>user</tt>, <tt>password</tt>: User name and password used for
authentication. Defaults to "<tt>ftp:anonymous@anonymous.org</tt>";
<li><tt>command</tt>: The FTP command used to obtain data. Defaults to
"<tt>retr</tt>", but see example below;
<li><tt>port</tt>: The port to contacct the server at. Defaults to 21;
<li><tt>type</tt>: The transfer mode. Can take values "<tt>i</tt>" or
"<tt>a</tt>". Defaults to whatever is the server default;
<li><tt>step</tt>: LTN12 pump step function used to pass data from the
server to the sink. Defaults to the LTN12 <tt>pump.step</tt> function.
</ul>
<p class=return> <p class=return>
If successful, the function returns 1. In case of error, the Both functions return 1 if successful, or <b><tt>nil</tt></b> and an error
function returns <b><tt>nil</tt></b> followed by a string describing the error. message describing the reason for failure.
</p> </p>
<pre class=example> <pre class=example>
-- Log as user "anonymous" on server "ftp.free.org" and store file -- load the ftp support
-- "hello" with contents "hello world!", using binary mode for the transfer local ftp = require("ftp")
r, e = socket.ftp.put("ftp://ftp.free.org/hello;type=i", "hello world!\n")
-- Does exactly the same, but logging in as diego -- Log as user "diego" on server "ftp.tecgraf.puc-rio.br",
r, e = socket.ftp.put{ -- using password "nehab", and store a file "README" with contents
url = "ftp://ftp.free.org/hello", -- "wrong password, of course"
type = "i", f, e = ftp.put("ftp://diego:nehab@ftp.tecgraf.puc-rio.br/README", "wrong password, of course")
</pre>
<pre class=example>
-- load the ftp support
local ftp = require("ftp")
local ltn12 = require("ltn12")
-- Log as user "diego" on server "ftp.tecgraf.puc-rio.br",
-- using password "nehab", and append to the file "LOG", sending the
-- contents of a local file
f, e = ftp.put{
host = "ftp.tecgraf.puc-rio.br",
user = "diego", user = "diego",
password = "nehab", password = "nehab",
content = "hello world\n" command = "appe",
argument = "LOG",
source = ltn12.source.file(io.open("LOCAL-LOG", "r"))
} }
</pre> </pre>
</blockquote>
<!-- put_cb +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=put_cb>
socket.ftp.<b>put_cb{</b><br>
&nbsp;&nbsp;url = <i>string</i>,<br>
&nbsp;&nbsp;type = <i>string</i>,<br>
&nbsp;&nbsp;content_cb = <i>send-callback</i>,<br>
&nbsp;&nbsp;user = <i>string</i>,<br>
&nbsp;&nbsp;password = <i>string</i><br>
<b>}</b>
</p>
<p class=description>
Same as <a href="#put"><tt>put</tt></a>, but the
library obtains the contents of the file to be uploaded using the send
callback <tt>content_cb</tt>.
</p>
<p class=note>
Note: for more information on callbacks, refer to
<a href="stream.html#stream">Streaming with callbacks</a>.
</p>
<pre class=example>
-- Log as user "anonymous" on server "ftp.free.org" and store file
-- "hello" with contents of the same file in the current directory,
-- using binary mode for the transfer
r, e = socket.ftp.put_cb{
url = "ftp://ftp.free.org/hello",
type = "i",
content_cb = socket.callback.send_file(io.open("hello", "r"))
}
</pre>
</blockquote>
<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->

421
doc/ltn12.html Normal file
View File

@ -0,0 +1,421 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>LuaSocket: Network support for the Lua language</title>
<link rel="stylesheet" href="reference.css" type="text/css">
</head>
<body>
<!-- header +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=header>
<hr>
<center>
<table summary="LuaSocket logo">
<tr><td align=center><a href="http://www.lua.org">
<img border=0 alt="LuaSocket" src="luasocket.png">
</a></td></tr>
<tr><td align=center valign=top>Network support for the Lua language
</td></tr>
</table>
<p class=bar>
<a href="home.html">home</a> &middot;
<a href="home.html#download">download</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a>
</p>
</center>
<hr>
</div>
<!-- ltn12 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h2 id=ltn12>LTN12</h2>
<p> The LTN12 module implements the ideas described in
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">
LTN012, Filters sources and sinks</a>. This manual simply describe the
functions. Please refer to the LTN for a deeper explanation of the
functionality provided by this module.
</p>
<!-- filters ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h3 id="filter">Filters</h3>
<!-- chain ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="filter.chain">
ltn12.filter.<b>chain(</b>filter<sub>1</sub>, filter<sub>2</sub>
[, ... filter<sub>N</sub>]<b>)</b>
</p>
<p class=description>
Returns a filter that passes all data it receives through each of a
series of given filters.
</p>
<p class=parameters>
<tt>Filter<sub>1</sub></tt> to <tt>filter<sub>N</sub></tt> are simple
filters.
</p>
<p class=return>
The function returns the chained filter.
</p>
<p class=note>
The nesting of filters can be arbritrary. For instance, the useless filter
below doesn't do anything but return the data that was passed to it,
unaltered.
</p>
<pre class=example>
-- load required modules
ltn12 = require("ltn12")
mime = require("mime")
-- create a silly identity filter
id = ltn12.filter.chain(
mime.encode("quoted-printable"),
mime.encode("base64"),
mime.decode("base64"),
mime.decode("quoted-printable")
)
</pre>
<!-- cycle ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="filter.cycle">
ltn12.filter.<b>cycle(</b>low [, ctx, extra]<b>)</b>
</p>
<p class=description>
Returns a high-level filter that cycles though a low-level filter by
passing it each chunk and updating a context between calls.
</p>
<p class=parameters>
<tt>Low</tt> is the low-level filter to be cycled,
<tt>ctx</tt> is the initial context and <tt>extra</tt> is any extra
argument the low-level filter might take.
</p>
<p class=return>
The function returns the high-level filter.
</p>
<pre class=example>
-- load the ltn12 module
local ltn12 = require("ltn12")
-- the base64 mime filter factory
encodet['base64'] = function()
return ltn12.filter.cycle(b64, "")
end
</pre>
<!-- pumps ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h3 id="pump">Pumps</h3>
<!-- all ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="pump.all">
ltn12.pump.<b>all(</b>source, sink<b>)</b>
</p>
<p class=description>
Pumps <em>all</em> data from a <tt>source</tt> to a <tt>sink</tt>.
</p>
<p class=return>
If successful, the function returns a value that evaluates to
<b><tt>true</tt></b>. In case
of error, the function returns a <b><tt>false</tt></b> value, followed by an error message.
</p>
<!-- step +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="pump.step">
ltn12.pump.<b>step(</b>source, sink<b>)</b>
</p>
<p class=description>
Pumps <em>one</em> chunk of data from a <tt>source</tt> to a <tt>sink</tt>.
</p>
<p class=return>
If successful, the function returns a value that evaluates to
<b><tt>true</tt></b>. In case
of error, the function returns a <b><tt>false</tt></b> value, followed by an error message.
</p>
<!-- sinks ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h3 id="sink">Sinks</h3>
<!-- chain ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="sink.chain">
ltn12.sink.<b>chain(</b>filter, sink<b>)</b>
</p>
<p class=description>
Creates a new sink that passes data through a <tt>filter</tt> before sending
it to a given <tt>sink</tt>.
</p>
<p class=return>
The function returns the new sink.
</p>
<!-- error ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="sink.error">
ltn12.sink.<b>error(</b>message<b>)</b>
</p>
<p class=description>
Creates and returns a sink that aborts transmission with an error
<tt>message</tt>.
</p>
<!-- file +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="sink.file">
ltn12.sink.<b>file(</b>handle, message<b>)</b>
</p>
<p class=description>
Creates a sink that sends data to a file.
</p>
<p class=parameters>
<tt>Handle</tt> is a file handle. If <tt>handle</tt> is <tt><b>nil</b></tt>,
<tt>message</tt> should give the reason for failure.
</p>
<p class=return>
The function returns a sink that sends all data to the given <tt>handle</tt>
and closes the file when done, or a sink that aborts the transmission with
an error <tt>message</tt>
</p>
<p class=note>
In the following example, notice how the prototype is designed to
fit nicely with the <tt>io.open</tt> function.
</p>
<pre class=example>
-- load the ltn12 module
local ltn12 = require("ltn12")
-- copy a file
ltn12.pump.all(
ltn12.source.file(io.open("original.png")),
ltn12.sink.file(io.open("copy.png"))
)
</pre>
<!-- null +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="sink.null">
ltn12.sink.<b>null()</b>
</p>
<p class=description>
Returns a sink that ignores all data it receives.
</p>
<!-- simplify +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="sink.simplify">
ltn12.sink.<b>simplify(</b>sink<b>)</b>
</p>
<p class=description>
Creates and returns a simple sink given a fancy <tt>sink</tt>.
</p>
<!-- table ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="sink.table">
ltn12.sink.<b>table(</b>[table]<b>)</b>
</p>
<p class=description>
Creates a sink that stores all chunks in a table. The chunks can later be
efficiently concatenated into a single string.
</p>
<p class=parameters>
<tt>Table</tt> is used to hold the chunks. If
<tt><b>nil</b></tt>, the function creates its own table.
</p>
<p class=return>
The function returns the sink and the table.
</p>
<pre class=example>
-- load needed modules
local http = require("http")
local ltn12 = require("ltn12")
-- the http.get function
function get(u)
local t = {}
local respt = request{
url = u,
sink = ltn12.sink.table(t)
}
return table.concat(t), respt.headers, respt.code, respt.error
end
</pre>
<!-- sinks ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h3 id="source">Sources</h3>
<!-- cat ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="source.cat">
ltn12.source.<b>cat(</b>source<sub>1</sub> [, source<sub>2</sub>, ...,
source<sub>N</sub>]<b>)</b>
</p>
<p class=description>
Creates a new source that produces the concatenation of the data produced
by a number of sources.
</p>
<p class=parameters>
<tt>Source<sub>1</sub></tt> to <tt>source<sub>N</sub></tt> are the original
sources.
</p>
<p class=return>
The function returns the new source.
</p>
<!-- chain ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="source.chain">
ltn12.source.<b>chain(</b>source, filter<b>)</b>
</p>
<p class=description>
Creates a new <tt>source</tt> that passes data through a <tt>filter</tt>
before returning it.
</p>
<p class=return>
The function returns the new source.
</p>
<!-- empty ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="source.empty">
ltn12.source.<b>empty()</b>
</p>
<p class=description>
Creates and returns an empty source.
</p>
<!-- error ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="source.error">
ltn12.source.<b>error(</b>message<b>)</b>
</p>
<p class=description>
Creates and returns a source that aborts transmission with an error
<tt>message</tt>.
</p>
<!-- file +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="source.file">
ltn12.source.<b>file(</b>handle, message<b>)</b>
</p>
<p class=description>
Creates a source that produces the contents of a file.
</p>
<p class=parameters>
<tt>Handle</tt> is a file handle. If <tt>handle</tt> is <tt><b>nil</b></tt>,
<tt>message</tt> should give the reason for failure.
</p>
<p class=return>
The function returns a source that reads chunks of data from
given <tt>handle</tt> and returns it to the user,
closing the file when done, or a source that aborts the transmission with
an error <tt>message</tt>
</p>
<p class=note>
In the following example, notice how the prototype is designed to
fit nicely with the <tt>io.open</tt> function.
</p>
<pre class=example>
-- load the ltn12 module
local ltn12 = require("ltn12")
-- copy a file
ltn12.pump.all(
ltn12.source.file(io.open("original.png")),
ltn12.sink.file(io.open("copy.png"))
)
</pre>
<!-- simplify +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="source.simplify">
ltn12.source.<b>simplify(</b>source<b>)</b>
</p>
<p class=description>
Creates and returns a simple source given a fancy <tt>source</tt>.
</p>
<!-- string +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="source.string">
ltn12.source.<b>string(</b>string<b>)</b>
</p>
<p class=description>
Creates and returns a source that produces the contents of a
<tt>string</tt>, chunk by chunk.
</p>
<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=footer>
<hr>
<center>
<p class=bar>
<a href="home.html">home</a> &middot;
<a href="home.html#down">download</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a>
</p>
<p>
<small>
Last modified by Diego Nehab on <br>
Sat Aug 9 01:00:41 PDT 2003
</small>
</p>
</center>
</div>
</body>
</html>

428
doc/mime.html Normal file
View File

@ -0,0 +1,428 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>LuaSocket: Network support for the Lua language</title>
<link rel="stylesheet" href="reference.css" type="text/css">
</head>
<body>
<!-- header +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=header>
<hr>
<center>
<table summary="LuaSocket logo">
<tr><td align=center><a href="http://www.lua.org">
<img border=0 alt="LuaSocket" src="luasocket.png">
</a></td></tr>
<tr><td align=center valign=top>Network support for the Lua language
</td></tr>
</table>
<p class=bar>
<a href="home.html">home</a> &middot;
<a href="home.html#download">download</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a>
</p>
</center>
<hr>
</div>
<!-- mime +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h2 id=mime>MIME</h2>
<p>
The MIME module offers filters that apply and remove common
content transfer encodings, such as Base64 and Quoted-Printable.
It also provides functions to break text into lines and change
the end-of-line convention.
MIME is described mainly in
<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2045.txt">RFC 2045</a>,
<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2046.txt">2046</a>,
<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2047.txt">2047</a>,
<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2047.txt">2048</a> and
<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2048.txt">2049</a>.
</p>
<p>
All functionality provided by the MIME module
follows the ideas presented in
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">
LTN012, Filters sources and sinks</a>.
</p>
<!-- High-level +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h3 id=high>High-level filters</h3>
<!-- normalize ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="normalize">
mime.<b>normalize(</b>[marker]<b>)</b>
</p>
<p class=description>
Converts most common end-of-line markers to a specific given marker.
</p>
<p class=parameters>
<tt>Marker</tt> is the new marker. It defaults to CRLF, the canonic
end-of-line marker defined by the MIME standard.
</p>
<p class=return>
The function returns a filter that performs the conversion.
</p>
<p class=note>
Note: There is no perfect solution to this problem. Different end-of-line
markers are an evil that will probably plague developers forever.
This function, however, will work perfectly for text created with any of
the most common end-of-line markers, i.e. the MacOS (CR), the Unix (LF),
or the DOS (CRLF) conventions. Even if the data has mixed end-of-line
markers, the function will still work well, although it doesn't
guarantee that the number of empty lines will be correct.
</p>
<!-- decode +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="decode">
mime.<b>decode(</b>"base64"<b>)</b><br>
mime.<b>decode(</b>"quoted-printable"<b>)</b>
</p>
<p class=description>
Returns a filter that decodes data from a given transfer content
encoding.
</p>
<p class=return>
The function returns the created filter.
</p>
<!-- encode +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="encode">
mime.<b>encode(</b>"base64"<b>)</b><br>
mime.<b>encode(</b>"quoted-printable" [, mode])</b>
</p>
<p class=description>
Returns a filter that encodes data according to a given transfer content
encoding.
</p>
<p class=parameters>
In the Quoted-Printable case, the user can specify whether the data is
textual or binary, by passing the <tt>mode</tt> strings "<tt>text</tt>" or
"<tt>binary</tt>". <tt>Mode</tt> defaults to "<tt>text</tt>".
</p>
<p class=return>
The function returns the created filter.
</p>
<p class=note>
Although both transfer content encodings specify a limit for the line
length, the encoding filters do <em>not</em> break text into lines (for
added flexibility).
Below is a filter that converts binary data to the Base64 transfer content
encoding and breaks it into lines of the correct size.
</p>
<pre class=example>
base64 = ltn12.filter.chain(
mime.encode("base64"),
mime.wrap("base64")
)
</pre>
<p class=note>
Note: Text data <em>has</em> to be converted to canonic form
<em>before</em> being encoded.
</p>
<pre class=example>
base64 = ltn12.filter.chain(
mime.normalize(),
mime.encode("base64"),
mime.wrap("base64")
)
</pre>
<!-- wrap +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="wrap">
mime.<b>wrap(</b>"text" [, length]<b>)</b><br>
mime.<b>wrap(</b>"base64"<b>)</b><br>
mime.<b>wrap(</b>"quoted-printable"<b>)</b>
</p>
<p class=description>
Returns a filter that breaks data into lines.
</p>
<p class=parameters>
The "<tt>text</tt>" line-wrap filter simply breaks text into lines by
inserting CRLF end-of-line markers at appropriate positions.
<tt>Length</tt> defaults 76.
The "<tt>base64</tt>" line-wrap filter works just like the default
"<tt>text</tt>" line-wrap filter with default length.
The function can also wrap "<tt>quoted-printable</tt>" lines, taking care
not to break lines in the middle of an escaped character. In that case, the
line length is fixed at 76.
</p>
<p class=return>
The function returns the created filter.
</p>
<p class=note>
For example, to create an encoding filter for the Quoted-Printable transfer content encoding of text data, do the following:
</p>
<pre class=example>
qp = ltn12.filter.chain(
mime.normalize(),
mime.encode("quoted-printable"),
mime.wrap("quoted-printable")
)
</pre>
<p class=note>
Note: To break into lines with a different end-of-line convention, apply
a normalization filter after the line break filter.
</p>
<!-- Low-level ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h3 id=low>Low-level filters</h3>
<!-- b64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="b64">
A, B = mime.<b>b64(</b>C [, D]<b>)</b>
</p>
<p class=description>
Low-level filter to perform Base64 encoding.
</p>
<p class=description>
<tt>A</tt> is the encoded version of the largest prefix of
<tt>C..D</tt>
that can be encoded unambiguously. <tt>B</tt> has the remaining bytes of
<tt>C..D</tt>, <em>before</em> encoding.
If <tt>D</tt> is <tt><b>nil</b></tt>, <tt>A</tt> is padded with
the encoding of the remaining bytes of <tt>C</tt>.
</p>
<p class=note>
Note: The simplest use of this function is to encode a string into it's
Base64 transfer content encoding. Notice the extra parenthesis around the
call to <tt>mime.b64</tt>, to discard the second return value.
</p>
<pre class=example>
print((mime.b64("diego:password")))
--&gt; ZGllZ286cGFzc3dvcmQ=
</pre>
<!-- eol ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="eol">
A, B = mime.<b>eol(</b>C [, D, marker]<b>)</b>
</p>
<p class=description>
Low-level filter to perform end-of-line marker translation.
For each chunk, the function needs to know if the last character of the
previous chunk could be part of an end-of-line marker or not. This is the
context the function receives besides the chunk. An updated version of
the context is returned after each new chunk.
</p>
<p class=description>
<tt>A</tt> is the translated version of <tt>D</tt>. <tt>C</tt> is the
ASCII value of the last character of the previous chunk, if it was a
candidate for line break, or 0 otherwise.
<tt>B</tt> is the same as <tt>C</tt>, but for the current
chunk. If <tt>D</tt> is <tt><b>nil</b></tt>, <tt>A</tt> includes a
new end-of-line marker, depending on <tt>C</tt>.
<tt>Marker</tt> gives the new end-of-line marker and defaults to CRLF.
</p>
<pre class=example>
-- translates the end-of-line marker to UNIX
unix = mime.eol(0, dos, "\n")
</pre>
<!-- qp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="qp">
A, B = mime.<b>qp(</b>C [, D, marker]<b>)</b>
</p>
<p class=description>
Low-level filter to perform Quoted-Printable encoding.
</p>
<p class=description>
<tt>A</tt> is the encoded version of the largest prefix of
<tt>C..D</tt>
that can be encoded unambiguously. <tt>B</tt> has the remaining bytes of
<tt>C..D</tt>, <em>before</em> encoding.
If <tt>D</tt> is <tt><b>nil</b></tt>, <tt>A</tt> is padded with
the encoding of the remaining bytes of <tt>C</tt>.
Throughout encoding, occurences of CRLF are replaced by the
<tt>marker</tt>, which itself defaults to CRLF.
</p>
<p class=note>
Note: The simplest use of this function is to encode a string into it's
Quoted-Printable transfer content encoding.
Notice the extra parenthesis around the call to <tt>mime.qp</tt>, to discard the second return value.
</p>
<pre class=example>
print((mime.qp("maçã")))
--&gt; ma=E7=E3=
</pre>
<!-- qpwrp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="qpwrp">
A, m = mime.<b>qpwrp(</b>n [, B, length]<b>)</b>
</p>
<p class=description>
Low-level filter to break Quoted-Printable text into lines.
</p>
<p class=description>
<tt>A</tt> is a copy of <tt>B</tt>, broken into lines of at most
<tt>length</tt> bytes (defaults to 76).
'<tt>n</tt>' should tell how many bytes are left for the first
line of <tt>B</tt> and '<tt>m</tt>' returns the number of bytes
left in the last line of <tt>A</tt>.
</p>
<p class=note>
Note: Besides breaking text into lines, this function makes sure the line
breaks don't fall in the middle of an escaped character combination. Also,
this function only breaks lines that are bigger than <tt>length</tt> bytes.
</p>
<!-- unb64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="unb64">
A, B = mime.<b>unb64(</b>C [, D]<b>)</b>
</p>
<p class=description>
Low-level filter to perform Base64 decoding.
</p>
<p class=description>
<tt>A</tt> is the decoded version of the largest prefix of
<tt>C..D</tt>
that can be decoded unambiguously. <tt>B</tt> has the remaining bytes of
<tt>C..D</tt>, <em>before</em> decoding.
If <tt>D</tt> is <tt><b>nil</b></tt>, <tt>A</tt> is the empty string
and <tt>B</tt> returns whatever couldn't be decoded.
</p>
<p class=note>
Note: The simplest use of this function is to decode a string from it's
Base64 transfer content encoding.
Notice the extra parenthesis around the call to <tt>mime.unqp</tt>, to discard the second return value.
</p>
<pre class=example>
print((mime.unb64("ZGllZ286cGFzc3dvcmQ=")))
--&gt; diego:password
</pre>
<!-- unqp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="unqp">
A, B = mime.<b>unqp(</b>C [, D]<b>)</b>
</p>
<p class=description>
Low-level filter to remove the Quoted-Printable transfer content encoding
from data.
</p>
<p class=description>
<tt>A</tt> is the decoded version of the largest prefix of
<tt>C..D</tt>
that can be decoded unambiguously. <tt>B</tt> has the remaining bytes of
<tt>C..D</tt>, <em>before</em> decoding.
If <tt>D</tt> is <tt><b>nil</b></tt>, <tt>A</tt> is augmented with
the encoding of the remaining bytes of <tt>C</tt>.
</p>
<p class=note>
Note: The simplest use of this function is to decode a string from it's
Quoted-Printable transfer content encoding.
Notice the extra parenthesis around the call to <tt>mime.unqp</tt>, to discard the second return value.
</p>
<pre class=example>
print((mime.qp("ma=E7=E3=")))
--&gt; maçã
</pre>
<!-- wrp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="wrp">
A, m = mime.<b>wrp(</b>n [, B, length]<b>)</b>
</p>
<p class=description>
Low-level filter to break text into lines with CRLF marker.
Text is assumed to be in the <a href=#normalize><tt>normalize</tt></a> form.
</p>
<p class=description>
<tt>A</tt> is a copy of <tt>B</tt>, broken into lines of at most
<tt>length</tt> bytes (defaults to 76).
'<tt>n</tt>' should tell how many bytes are left for the first
line of <tt>B</tt> and '<tt>m</tt>' returns the number of bytes
left in the last line of <tt>A</tt>.
</p>
<p class=note>
Note: This function only breaks lines that are bigger than
<tt>length</tt> bytes. The resulting line length does not include the CRLF
marker.
</p>
<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=footer>
<hr>
<center>
<p class=bar>
<a href="home.html">home</a> &middot;
<a href="home.html#down">download</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a>
</p>
<p>
<small>
Last modified by Diego Nehab on <br>
Sat Aug 9 01:00:41 PDT 2003
</small>
</p>
</center>
</div>
</body>
</html>

View File

@ -10,6 +10,9 @@ tt {
h1, h2, h3, h4 { margin-left: 0em; } h1, h2, h3, h4 { margin-left: 0em; }
h3 { padding-top: 1em; }
p { margin-left: 1em; } p { margin-left: 1em; }
p.name { p.name {

View File

@ -36,7 +36,7 @@
<h2>Reference</h2> <h2>Reference</h2>
<blockquote> <blockquote>
<a href="dns.html">DNS services (socket.dns)</a> <a href="dns.html">DNS (in socket)</a>
<blockquote> <blockquote>
<a href="dns.html#toip">toip</a>, <a href="dns.html#toip">toip</a>,
<a href="dns.html#tohostname">tohostname</a>, <a href="dns.html#tohostname">tohostname</a>,
@ -47,31 +47,17 @@
<!-- ftp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- ftp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<blockquote> <blockquote>
<a href="ftp.html">FTP (socket.ftp)</a> <a href="ftp.html">FTP</a>
<blockquote> <blockquote>
<a href="ftp.html#get">get</a>, <a href="ftp.html#get">get</a>,
<a href="ftp.html#put">put</a>, <a href="ftp.html#put">put</a>
<a href="ftp.html#open">open</a>.
</blockquote> </blockquote>
</blockquote> </blockquote>
<!-- global +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<blockquote>
<a href="global.html">Global symbols</a>
<blockquote>
<a href="global.html#LUASOCKET_LIBNAME">LUASOCKET_LIBNAME</a>,
<a href="global.html#mime">mime</a>,
<a href="global.html#ltn12">ltn12</a>,
<a href="global.html#socket">socket</a>.
</blockquote>
</blockquote>
</table>
<!-- http +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- http +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<blockquote> <blockquote>
<a href="http.html">HTTP (socket.http)</a> <a href="http.html">HTTP</a>
<blockquote> <blockquote>
<a href="http.html#get">get</a>, <a href="http.html#get">get</a>,
<a href="http.html#post">post</a>, <a href="http.html#post">post</a>,
@ -82,46 +68,45 @@
<!-- ltn12 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- ltn12 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<blockquote> <blockquote>
<a href="ltn12.html">LTN012 (ltn12)</a> <a href="ltn12.html">LTN12</a>
<blockquote> <blockquote>
<a href="ltn12.html#filter">filter</a>: <a href="ltn12.html#filter">filter</a>:
<a href="ltn12.html#chain">chain</a>, <a href="ltn12.html#filter.chain">chain</a>,
<a href="ltn12.html#cycle">cycle</a>. <a href="ltn12.html#filter.cycle">cycle</a>.
</blockquote> </blockquote>
<blockquote> <blockquote>
<a href="ltn12.html#pump">pump</a>: <a href="ltn12.html#pump">pump</a>:
<a href="ltn12.html#all">all</a>, <a href="ltn12.html#pump.all">all</a>,
<a href="ltn12.html#step">step</a>. <a href="ltn12.html#pump.step">step</a>.
</blockquote> </blockquote>
<blockquote> <blockquote>
<a href="ltn12.html#sink">sink</a>: <a href="ltn12.html#sink">sink</a>:
<a href="ltn12.html#chain">chain</a>, <a href="ltn12.html#sink.chain">chain</a>,
<a href="ltn12.html#error">error</a>, <a href="ltn12.html#sink.error">error</a>,
<a href="ltn12.html#chain">file</a>, <a href="ltn12.html#sink.file">file</a>,
<a href="ltn12.html#file">null</a>, <a href="ltn12.html#sink.null">null</a>,
<a href="ltn12.html#simplify">simplify</a>, <a href="ltn12.html#sink.simplify">simplify</a>,
<a href="ltn12.html#table">table</a>. <a href="ltn12.html#sink.table">table</a>.
</blockquote> </blockquote>
<blockquote> <blockquote>
<a href="ltn12.html#source">source</a>: <a href="ltn12.html#source">source</a>:
<a href="ltn12.html#cat">cat</a>, <a href="ltn12.html#source.cat">cat</a>,
<a href="ltn12.html#chain">chain</a>, <a href="ltn12.html#source.chain">chain</a>,
<a href="ltn12.html#empty">empty</a>, <a href="ltn12.html#source.empty">empty</a>,
<a href="ltn12.html#file">file</a>, <a href="ltn12.html#source.error">error</a>,
<a href="ltn12.html#simplify">simplify</a>, <a href="ltn12.html#source.file">file</a>,
<a href="ltn12.html#simplify">rewind</a>, <a href="ltn12.html#source.simplify">simplify</a>,
<a href="ltn12.html#simplify">string</a>. <a href="ltn12.html#source.string">string</a>.
</blockquote> </blockquote>
</blockquote> </blockquote>
<!-- mime +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- mime +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<blockquote> <blockquote>
<a href="mime.html">MIME (mime) </a> <a href="mime.html">MIME</a>
<blockquote> <blockquote>
<a href="mime.html#high">high-level</a>: <a href="mime.html#high">high-level</a>:
<a href="mime.html#normalize">normalize</a>, <a href="mime.html#normalize">normalize</a>,
<a href="mime.html#chain">chain</a>,
<a href="mime.html#decode">decode</a>, <a href="mime.html#decode">decode</a>,
<a href="mime.html#encode">encode</a>, <a href="mime.html#encode">encode</a>,
<a href="mime.html#wrap">wrap</a>. <a href="mime.html#wrap">wrap</a>.
@ -141,9 +126,8 @@
<!-- smtp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- smtp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<blockquote> <blockquote>
<a href="smtp.html">SMTP (socket.smtp)</a> <a href="smtp.html">SMTP</a>
<blockquote> <blockquote>
<a href="smtp.html#mail">open</a>,
<a href="smtp.html#message">message</a>, <a href="smtp.html#message">message</a>,
<a href="smtp.html#send">send</a>. <a href="smtp.html#send">send</a>.
</blockquote> </blockquote>
@ -152,26 +136,20 @@
<!-- socket +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- socket +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<blockquote> <blockquote>
<a href="socket.html">The socket namespace (socket)</a> <a href="socket.html">Socket</a>
<blockquote> <blockquote>
<a href="tcp.html#bind">bind</a>, <a href="socket.html#debug">DEBUG</a>,
<a href="tcp.html#connect">connect</a>,
<a href="socket.html#debug">debug</a>,
<a href="dns.html#dns">dns</a>, <a href="dns.html#dns">dns</a>,
<a href="ftp.html#ftp">ftp</a>,
<a href="http.html#http">http</a>,
<a href="socket.html#protect">protect</a>, <a href="socket.html#protect">protect</a>,
<a href="socket.html#select">select</a>, <a href="socket.html#select">select</a>,
<a href="socket.html#sink">sink</a>, <a href="socket.html#sink">sink</a>,
<a href="socket.html#source">source</a>, <a href="socket.html#source">source</a>,
<a href="socket.html#sleep">sleep</a>, <a href="socket.html#sleep">sleep</a>,
<a href="smtp.html#smtp">smtp</a>,
<a href="socket.html#time">time</a>, <a href="socket.html#time">time</a>,
<a href="tcp.html#tcp">tcp</a>, <a href="tcp.html#tcp">tcp</a>,
<a href="socket.html#try">try</a>, <a href="socket.html#try">try</a>,
<a href="udp.html#udp">udp</a>, <a href="udp.html#udp">udp</a>,
<a href="url.html#url">url</a>, <a href="socket.html#version">VERSION</a>.
<a href="socket.html#version">version</a>.
</blockquote> </blockquote>
</blockquote> </blockquote>
</table> </table>
@ -179,7 +157,7 @@
<!-- tcp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- tcp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<blockquote> <blockquote>
<a href="tcp.html">TCP (socket.tcp)</a> <a href="tcp.html">TCP (in socket)</a>
<blockquote> <blockquote>
<a href="tcp.html#accept">accept</a>, <a href="tcp.html#accept">accept</a>,
<a href="tcp.html#bind">bind</a>, <a href="tcp.html#bind">bind</a>,
@ -198,7 +176,7 @@
<!-- udp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- udp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<blockquote> <blockquote>
<a href="udp.html">UDP (socket.udp)</a> <a href="udp.html">UDP (in socket)</a>
<blockquote> <blockquote>
<a href="udp.html#close">close</a>, <a href="udp.html#close">close</a>,
<a href="udp.html#getpeername">getpeername</a>, <a href="udp.html#getpeername">getpeername</a>,
@ -210,23 +188,22 @@
<a href="udp.html#setpeername">setpeername</a>, <a href="udp.html#setpeername">setpeername</a>,
<a href="udp.html#setsockname">setsockname</a>, <a href="udp.html#setsockname">setsockname</a>,
<a href="udp.html#setoption">setoption</a>, <a href="udp.html#setoption">setoption</a>,
<a href="udp.html#settimeout">settimeout</a>, <a href="udp.html#settimeout">settimeout</a>.
<a href="udp.html#settimeout">shutdown</a>.
</blockquote> </blockquote>
</blockquote> </blockquote>
<!-- url ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- url ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<blockquote> <blockquote>
<a href="url.html">URL (socket.url) </a> <a href="url.html">URL</a>
<blockquote> <blockquote>
<a href="url.html#absolute">absolute</a>, <a href="url.html#absolute">absolute</a>,
<a href="url.html#build">build</a>, <a href="url.html#build">build</a>,
<a href="url.html#build_path">build_path</a>, <a href="url.html#build_path">build_path</a>,
<a href="url.html#quote">quote</a>, <a href="url.html#escape">escape</a>,
<a href="url.html#parse">parse</a>, <a href="url.html#parse">parse</a>,
<a href="url.html#parse_path">parse_path</a>, <a href="url.html#parse_path">parse_path</a>,
<a href="url.html#quote">unquote</a>. <a href="url.html#unescape">unescape</a>.
</blockquote> </blockquote>
</blockquote> </blockquote>

View File

@ -35,15 +35,22 @@
<h2 id=smtp>SMTP</h2> <h2 id=smtp>SMTP</h2>
<p> <p> The <tt>smtp.lua</tt> module provides functionality to send e-mail
The <tt>smtp.lua</tt> module provides functionality to send e-mail
messages. The implementation conforms to the Simple Mail Transfer Protocol, messages. The implementation conforms to the Simple Mail Transfer Protocol,
<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2821.txt">RFC 2821</a>. <a href="http://www.cs.princeton.edu/~diego/rfc/rfc2821.txt">RFC 2821</a>.
The other RFC of interest in this implementation is Another RFC of interest is <a
<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2822.txt">RFC 2822</a>, href="http://www.cs.princeton.edu/~diego/rfc/rfc2822.txt">RFC 2822</a>,
which governs the Internet Message Format. which governs the Internet Message Format.
Multipart messages (those that contain attatchments) are part
of the MIME standard, but described mainly
in <a href="http://www.cs.princeton.edu/~diego/rfc/rfc2046.txt">RFC
2046</a>
</p> <p> In the description below, good understanding of <a
href="http://lua-users.org/wiki/FiltersSourcesAndSinks"> LTN012, Filters
sources and sinks</a> and the <a href=mime.html>MIME</a> module is
assumed. In fact, the SMTP module was the main reason for their
creation. </p>
<p> <p>
MIME headers are represented as a Lua table in the form: MIME headers are represented as a Lua table in the form:
@ -78,29 +85,56 @@ Note: MIME headers are independent of order. Therefore, there is no problem
in representing them in a Lua table. in representing them in a Lua table.
</p> </p>
<!-- mail +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <p>
The following constants can be set to control the default behaviour of
the SMTP module:
</p>
<p class=name id=mail> <ul>
socket.smtp.<b>mail{</b><br> <li> <tt>DOMAIN</tt>: domain used to greet the server;
<li> <tt>PORT</tt>: default port used for the connection;
<li> <tt>SERVER</tt>: default server used for the connection;
<li> <tt>TIMEOUT</tt>: default timeout for all I/O operations;
<li> <tt>ZONE</tt>: default time zone.
</ul>
<!-- send +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=send>
smtp.<b>send{</b><br>
&nbsp;&nbsp;from = <i>string</i>,<br> &nbsp;&nbsp;from = <i>string</i>,<br>
&nbsp;&nbsp;rcpt = <i>string</i> or <i>string-table</i>,<br> &nbsp;&nbsp;rcpt = <i>string</i> or <i>string-table</i>,<br>
&nbsp;&nbsp;body = <i>string</i>,<br> &nbsp;&nbsp;source = <i>LTN12 source</i>,<br>
&nbsp;&nbsp;headers = <i>headers-table</i>,<br> &nbsp;&nbsp;[server = <i>string</i>],<br>
&nbsp;&nbsp;server = <i>string</i><br> &nbsp;&nbsp;[port = <i>string</i>]<br>
&nbsp;&nbsp;[domain = <i>string</i>],<br>
&nbsp;&nbsp;[step = <i>LTN12 pump step</i>],<br>
<b>}</b> <b>}</b>
</p> </p>
<p class=description> <p class=description>
Sends a message to a recipient list. Sends a message to a recipient list. Since sending messages is not as
simple as downloading an URL from a FTP or HTTP server, this function
doesn't have a simple interface. However, see the
<a href=#message><tt>message</tt></a> source factory for
a very powerful way to define the message contents.
</p> </p>
<p class=parameters> <p class=parameters>
<tt>Rcpt</tt> is a Lua table with one entry for each recipient, or a string The sender is given by the e-mail address in the <tt>from</tt> field.
<tt>Rcpt</tt> is a Lua table with one entry for each recipient e-mail
address, or a string
in case there is just one recipient. in case there is just one recipient.
The sender is given by the e-mail address <tt>from</tt>. The contents of the message are given by a LTN12 <tt>source</tt>. Several
The message is composed by the optional MIME Headers <tt>headers</tt> arguments are optional:
and text <tt>body</tt>. The message is sent using the server <ul>
<tt>server</tt>. <li> <tt>server</tt>: Server to connect to. Defaults to "localhost";
<li> <tt>port</tt>: Port to connect to. Defaults to 25;
<li> <tt>domain</tt>: Domain name used to greet the server; Defaults to the
local machine host name;
<li> <tt>step</tt>: LTN12 pump step function used to pass data from the
source to the server. Defaults to the LTN12 <tt>pump.step</tt> function.
</ul>
</p> </p>
<p class=return> <p class=return>
@ -108,6 +142,13 @@ If successful, the function returns 1. Otherwise, the function returns
<b><tt>nil</tt></b> followed by an error message. <b><tt>nil</tt></b> followed by an error message.
</p> </p>
<p class=note>
Note: SMTP servers are can be very picky with the format of e-mail
addresses. To be safe, use only addresses of the form
"<tt>&lt;fulano@tecgraf.puc-rio.br&gt;</tt>" in the <tt>from</tt> and
<tt>rcpt</tt> arguments to the <tt>send</tt> function. In headers, e-mail
addresses can take whatever form you like. </p>
<p class=note> <p class=note>
Big note: There is a good deal of misconception with the use of the Big note: There is a good deal of misconception with the use of the
destination address field headers, i.e., the '<tt>To</tt>', '<tt>Cc</tt>', destination address field headers, i.e., the '<tt>To</tt>', '<tt>Cc</tt>',
@ -117,11 +158,12 @@ exact opposite of what you expect.
</p> </p>
<p class=note> <p class=note>
Only recipients specified in the recipient list will receive a copy of the 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. Each recipient of an SMTP mail message receives a copy of the
message body along with the headers, and nothing more. The headers are message body along with the headers, and nothing more. The headers
considered as part of the message. The list of recipients is <em>not</em> <em>are</em> part of the message and should be produced by the LTN12
part of the message. <tt>source</tt> function. The <tt>rcpt</tt> list is <em>not</em>
part of the message and will not be sent to anyone.
</p> </p>
<p class=note> <p class=note>
@ -143,9 +185,9 @@ Copy") contains addresses of recipients of the message whose addresses are not t
</ul> </ul>
<p class=note> <p class=note>
The LuaSocket <tt>mail</tt> function does not interpret the headers you The LuaSocket <tt>send</tt> function does not care or interpret the
pass to, but it gives you full control over what is sent and to whom headers you send, but it gives you full control over what is sent and
it is sent: to whom it is sent:
</p> </p>
<ul> <ul>
<li> If someone is to receive the message, the e-mail address <em>has</em> <li> If someone is to receive the message, the e-mail address <em>has</em>
@ -171,36 +213,147 @@ and
</p> </p>
<pre class=example> <pre class=example>
-- load the smtp support
local smtp = require("smtp")
-- Connects to server "localhost" and sends a message to users -- Connects to server "localhost" and sends a message to users
-- "fulano@tecgraf.puc-rio.br", "beltrano@tecgraf.puc-rio.br", -- "fulano@tecgraf.puc-rio.br", "beltrano@tecgraf.puc-rio.br",
-- and "sicrano@tecgraf.puc-rio.br". -- and "sicrano@tecgraf.puc-rio.br".
-- Note that "fulano" is the primary recipient, "beltrano" receives a -- Note that "fulano" is the primary recipient, "beltrano" receives a
-- carbon copy and neither of them knows that "sicrano" received a blind -- carbon copy and neither of them knows that "sicrano" received a blind
-- carbon copy of the message. -- carbon copy of the message.
headers = { from = "&lt;luasocket@tecgraf.puc-rio.br&gt;"
to = "fulano@tecgraf.puc-rio.br",
cc = "beltrano@tecgraf.puc-rio.br",
subject = "LuaSocket test message"
}
from = "luasocket@tecgraf.puc-rio.br"
rcpt = { rcpt = {
"fulano@tecgraf.puc-rio.br", "&lt;fulano@tecgraf.puc-rio.br&gt;",
"beltrano@tecgraf.puc-rio.br", "&lt;beltrano@tecgraf.puc-rio.br&gt;",
"sicrano@tecgraf.puc-rio.br" "&lt;sicrano@tecgraf.puc-rio.br&gt;"
} }
body = "This is a test message. Please ignore." mesgt = {
headers = {
to = "Fulano da Silva &lt;fulano@tecgraf.puc-rio.br&gt;",
cc = '"Beltrano F. Nunes" &lt;beltrano@tecgraf.puc-rio.br&gt;',
subject = "My first message"
}
body = "I hope this works. If it does, I can send you another 1000 copies."
}
server = "localhost" r, e = smtp.send{
r, e = socket.smtp.mail{
from = from, from = from,
rcpt = rcpt, rcpt = rcpt,
headers = headers, source = smtp.message(mesgt)
body = body, }
server = server </pre>
<!-- message ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=message>
smtp.<b>message(</b>mesgt<b>)</b>
</p>
<p class=description>
Returns a LTN12 source that sends an SMTP message body, possibly multipart
(arbitrarily deep).
</p>
<p class=parameters>
The only parameter of the function is a table describing the message.
<tt>Mesgt</tt> has the following form (notice the recursive structure):
</p>
<blockquote>
<table summary="Mesgt table structure">
<tr><td><tt>
mesgt = {<br>
&nbsp;&nbsp;headers = <i>header-table</i>,<br>
&nbsp;&nbsp;body = <i>LTN12 source</i> or <i>string</i> or
<i>multipart-mesgt</i><br>
}<br>
&nbsp;<br>
multipart-mesgt = {<br>
&nbsp;&nbsp;preamble = <i>string</i><br>
&nbsp;&nbsp;[1] = <i>mesgt</i>,<br>
&nbsp;&nbsp;[2] = <i>mesgt</i>,<br>
&nbsp;&nbsp;...<br>
&nbsp;&nbsp;[<i>n</i>] = <i>mesgt</i>,<br>
&nbsp;&nbsp;epilogue = <i>string</i>,<br>
}<br>
</tt></td></tr>
</table>
</blockquote>
<p class=parameters>
For a simple message, all that is needed is a set of <tt>headers</tt>
and the <tt>body</tt>. The message <tt>body</tt> can be given as a string
or as a LTN12 source. For multipart messages, the body is a table that
recursively defines each part as an independent message, plus a preamble
and an epilogue.
</p>
<p class=return>
The function returns an LTN12 source that produces the message contents as
defined by <tt>mesgt</tt>. Hopefuly, the following example will make
things clear. When in doubt, refer to the appropriate RFC as listed in the
introduction. </p>
<pre class=example>
-- load the smtp support and its friends
local smtp = require("smtp")
local mime = require("mime")
local ltn12 = require("ltn12")
-- creates a source to send a message with two parts. The first part is
-- plain text, the second part is a PNG image, encoded as base64.
source = smtp.message{
headers = {
-- Remember that headers are *ignored* by smtp.send.
from = "Sicrano de Oliveira &lt;sicrano@tecgraf.puc-rio.br&gt;",
to = "Fulano da Silva &lt;fulano@tecgraf.puc-rio.br&gt;",
subject = "Here is a message with attachments"
},
body = {
preamble = "If your client doesn't understand attachments, \r\n" ..
"it will still display the preamble and the epilogue.\r\n",
"Preamble might show up even in a MIME enabled client.",
-- first part: no headers means plain text, us-ascii.
-- The mime.eol low-level filter normalizes end-of-line markers.
[1] = {
body = mime.eol(0, [[
Lines in a message body should always end with CRLF.
The smtp module will *NOT* perform translation. It will
perform necessary stuffing or '.' characters, though.
]])
},
-- second part: headers describe content to be a png image,
-- sent under the base64 transfer content encoding.
-- notice that nothing happens until the message is actually sent.
-- small chunks are loaded into memory right before transmission and
-- translation happens on the fly.
[2] = {
headers = {
["content-type"] = 'image/png; name="image.png"',
["content-disposition"] = 'attachment; filename="image.png"',
["content-description"] = 'a beautiful image',
["content-transfer-encoding"] = "BASE64"
},
body = ltn12.source.chain(
ltn12.source.file(io.open("image.png", "rb")),
ltn12.filter.chain(
mime.encode("base64"),
mime.wrap()
)
)
},
epilogue = "This might also show up, but after the attachments"
}
}
-- finally send it
r, e = smtp.send{
from = "&lt;sicrano@tecgraf.puc-rio.br&gt;",
rcpt = "&lt;fulano@tecgraf.puc-rio.br&gt;",
source = source,
} }
</pre> </pre>

View File

@ -36,16 +36,13 @@
<h2 id=socket>The socket namespace</h2> <h2 id=socket>The socket namespace</h2>
<p> <p>
The <tt>socket</tt> namespace contains the namespace tables for all The <tt>socket</tt> namespace contains the core functionality of LuaSocket.
LuaSocket modules as well as function that didn't belong in any specific
module, functions that are so commonly used that deserve a shortcut and a
few constants.
</p> </p>
<!-- debug ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- debug ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=debug> <p class=name id=debug>
socket.<b>debug</b> socket.<b>DEBUG</b>
</p> </p>
<p class=description> <p class=description>
@ -57,7 +54,7 @@ with debug support.
<!-- protect +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- protect +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=protect> <p class=name id=protect>
socket.<b>protect(</b>function<b>)</b> socket.<b>protect(</b>func<b>)</b>
</p> </p>
<p class=description> <p class=description>
@ -65,12 +62,12 @@ Converts a function that throws exceptions into a safe function.
</p> </p>
<p class=parameters> <p class=parameters>
<tt>Function</tt> is a function that calls <tt>Funct</tt> is a function that calls
<a href=#try><tt>try</tt></a> to throw exceptions. <a href=#try><tt>try</tt></a> to throw exceptions.
</p> </p>
<p class=return> <p class=return>
The function an equivalent function that instead of throwing exceptoins, Returns an equivalent function that instead of throwing exceptions,
returns <tt><b>nil</b></tt> followed by an error message. returns <tt><b>nil</b></tt> followed by an error message.
</p> </p>
@ -103,16 +100,16 @@ simplify the test if a specific socket has changed status.
</p> </p>
<p class=note> <p class=note>
<b>Important Note</b>: a known bug in WinSock causes <tt>select</tt> to fail <b>Important note</b>: a known bug in WinSock causes <tt>select</tt> to fail
on non-blocking TCP sockets. The function may return a socket as on non-blocking TCP sockets. The function may return a socket as
writable even though the socket is <em>not</em> ready for sending. writable even though the socket is <em>not</em> ready for sending.
</p> </p>
<p class=note> <p class=note>
<b>Important note</b>: calling select with a server socket in the receive <b>Another important note</b>: calling select with a server socket in the receive
parameter before a call to accept does <em>not</em> guarantee parameter before a call to accept does <em>not</em> guarantee
<a href=tcp.html#accept><tt>accept</tt></a> will return immediately. <a href=tcp.html#accept><tt>accept</tt></a> will return immediately.
Use the <a href=tcp.html#timeout><tt>timeout</tt></a> Use the <a href=tcp.html#settimeout><tt>settimeout</tt></a>
method or <tt>accept</tt> might block forever. method or <tt>accept</tt> might block forever.
</p> </p>
@ -131,7 +128,7 @@ socket.<b>sink(</b>mode, socket<b>)</b>
<p class=description> <p class=description>
Creates an Creates an
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN012</a> <a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a>
sink from a stream socket object. sink from a stream socket object.
</p> </p>
@ -163,7 +160,7 @@ socket.<b>source(</b>mode, socket [, length]<b>)</b>
<p class=description> <p class=description>
Creates an Creates an
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN012</a> <a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a>
source from a stream socket object. source from a stream socket object.
</p> </p>
@ -217,7 +214,7 @@ c = socket.try(socket.connect("localhost", 80))
<!-- version ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- version ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=version> <p class=name id=version>
socket.<b>version</b> socket.<b>VERSION</b>
</p> </p>
<p class=description> <p class=description>

View File

@ -241,18 +241,16 @@ method returns <b><tt>nil</tt></b> followed by an error message.
<!-- receive ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- receive ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=receive> <p class=name id=receive>
client:<b>receive(</b>[pattern<sub>1</sub>, pattern<sub>2</sub>, client:<b>receive(</b>[pattern]<b>)</b>
... pattern<sub>N</sub>]<b>)</b>
</p> </p>
<p class=description> <p class=description>
Reads data from a client object, according to the specified <em>read Reads data from a client object, according to the specified <em>read
patterns</em>. Patterns follow the Lua file I/O format, and the difference in performance between all patterns is negligible. pattern</em>. Patterns follow the Lua file I/O format, and the difference in performance between all patterns is negligible.
</p> </p>
<p class=parameters> <p class=parameters>
The parameters <tt>pattern</tt><sub>1</sub>, <tt>pattern</tt><sub>2</sub>, ... <tt>Pattern</tt> can be any of the following:
<tt>pattern</tt><sub>N</sub> can be any of the following:
</p> </p>
<ul> <ul>
@ -267,16 +265,21 @@ of bytes from the socket.
</ul> </ul>
<p class=return> <p class=return>
The method returns one value for each pattern, followed by a single If successful, the method returns the received pattern. In case of error,
error code that can be <b><tt>nil</tt></b> in case of success, the string the method returns <tt><b>nil</b></tt> followed by an error message which
'<tt>closed</tt>' in case the connection was closed before the can be the string '<tt>closed</tt>' in case the connection was
transmission was completed or the string '<tt>timeout</tt>' in case closed before the transmission was completed or the string
there was a timeout during the operation. '<tt>timeout</tt>' in case there was a timeout during the operation.
Also, after the error message, the function returns the partial result of
the transmission.
</p> </p>
<p class=note> <p class=note>
Note: In case of error, the method always return everything it managed <b>Important note</b>: This function was changed <em>severely</em>. It used
to download before the error condition was met. to support multiple patterns (but I have never seen this feature used) and
partial results used to be returned in the same way as successful results.
This last feature violated the idea that all functions should return
<tt><b>nil</b></tt> on error. Thus the change.
</p> </p>
<!-- send +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- send +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
@ -428,7 +431,7 @@ client:<b>shutdown(</b>mode<b>)</b><br>
</p> </p>
<p class=description> <p class=description>
Shuts down part of a full duplex connection. Shuts down part of a full-duplex connection.
</p> </p>
<p class=parameters> <p class=parameters>

View File

@ -29,6 +29,11 @@
<hr> <hr>
</div> </div>
<!-- udp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h2 id=udp>UDP</h2>
<!-- socket.udp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- socket.udp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class="name" id="socket.udp"> <p class="name" id="socket.udp">

View File

@ -59,7 +59,7 @@ An URL is defined by the following grammar:
<!-- absolute +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- absolute +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=absolute> <p class=name id=absolute>
socket.url.<b>absolute(</b>base, relative<b>)</b> url.<b>absolute(</b>base, relative<b>)</b>
</p> </p>
<p class=description> <p class=description>
@ -79,7 +79,7 @@ The function returns a string with the absolute URL.
Note: The rules that Note: The rules that
govern the composition are fairly complex, and are described in detail in govern the composition are fairly complex, and are described in detail in
<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2396.txt">RFC 2396</a>. <a href="http://www.cs.princeton.edu/~diego/rfc/rfc2396.txt">RFC 2396</a>.
The example bellow should give an idea of what are the rules. The example bellow should give an idea of what the rules are.
</p> </p>
<pre class=example> <pre class=example>
@ -114,7 +114,7 @@ g;x?y#s = http://a/b/c/g;x?y#s
<!-- build ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- build ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=build> <p class=name id=build>
socket.url.<b>build(</b>parsed_url<b>)</b> url.<b>build(</b>parsed_url<b>)</b>
</p> </p>
<p class=description> <p class=description>
@ -135,7 +135,7 @@ The function returns a string with the built URL.
<!-- build_path +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- build_path +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=build_path> <p class=name id=build_path>
socket.url.<b>build_path(</b>segments, unsafe<b>)</b> url.<b>build_path(</b>segments, unsafe<b>)</b>
</p> </p>
<p class=description> <p class=description>
@ -157,10 +157,39 @@ The function returns a string with the
built <tt>&lt;path&gt;</tt> component. built <tt>&lt;path&gt;</tt> component.
</p> </p>
<!-- escape +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="escape">
url.<b>escape(</b>content<b>)</b>
</p>
<p class=description>
Applies the URL escaping content coding to a string
Each byte is encoded as a percent character followed
by the two byte hexadecimal representation of its integer
value.
</p>
<p class=parameters>
<tt>Content</tt> is the string to be encoded.
</p>
<p class=result>
The function returns the encoded string.
</p>
<pre class=example>
-- load url module
url = require("url")
code = url.escape("/#?;")
-- code = "%2f%23%3f%3b"
</pre>
<!-- parse ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- parse ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=parse> <p class=name id=parse>
socket.url.<b>parse(</b>url, default<b>)</b> url.<b>parse(</b>url, default<b>)</b>
</p> </p>
<p class=description> <p class=description>
@ -196,7 +225,10 @@ parsed_url = {<br>
</tt></blockquote> </tt></blockquote>
<pre class=example> <pre class=example>
parsed_url = socket.url.parse("http://www.puc-rio.br/~diego/index.lua?a=2#there") -- load url module
url = require("url")
parsed_url = url.parse("http://www.puc-rio.br/~diego/index.lua?a=2#there")
-- parsed_url = { -- parsed_url = {
-- scheme = "http", -- scheme = "http",
-- authority = "www.puc-rio.br", -- authority = "www.puc-rio.br",
@ -206,7 +238,7 @@ parsed_url = socket.url.parse("http://www.puc-rio.br/~diego/index.lua?a=2#there"
-- host = "www.puc-rio.br", -- host = "www.puc-rio.br",
-- } -- }
parsed_url = socket.url.parse("ftp://root:passwd@unsafe.org/pub/virus.exe;type=i") parsed_url = url.parse("ftp://root:passwd@unsafe.org/pub/virus.exe;type=i")
-- parsed_url = { -- parsed_url = {
-- scheme = "ftp", -- scheme = "ftp",
-- authority = "root:passwd@unsafe.org", -- authority = "root:passwd@unsafe.org",
@ -222,7 +254,7 @@ parsed_url = socket.url.parse("ftp://root:passwd@unsafe.org/pub/virus.exe;type=i
<!-- parse_path +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- parse_path +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=parse_path> <p class=name id=parse_path>
socket.url.<b>parse_path(</b>path<b>)</b> url.<b>parse_path(</b>path<b>)</b>
</p> </p>
<p class=description> <p class=description>
@ -241,36 +273,10 @@ returning a list with all the parsed segments, the function unescapes all
of them. of them.
</p> </p>
<!-- escape +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="escape">
socket.url.<b>escape(</b>content<b>)</b>
</p>
<p class=description>
Applies the URL escaping content coding to a string
Each byte is encoded as a percent character followed
by the two byte hexadecimal representation of its integer
value.
</p>
<p class=parameters>
<tt>Content</tt> is the string to be encoded.
</p>
<p class=result>
The function returns the encoded string.
</p>
<pre class=example>
code = socket.url.escape("/#?;")
-- code = "%2f%23%3f%3b"
</pre>
<!-- unescape +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- unescape +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="unescape"> <p class=name id="unescape">
socket.url.<b>unescape(</b>content<b>)</b> url.<b>unescape(</b>content<b>)</b>
</p> </p>
<p class=description> <p class=description>

17
etc/check-memory.lua Normal file
View File

@ -0,0 +1,17 @@
function load(s)
collectgarbage()
local a = gcinfo()
_G[s] = require(s)
collectgarbage()
local b = gcinfo()
print(s .. ":\t " .. (b-a) .. "k")
end
load("socket")
load("url")
load("ltn12")
load("mime")
load("tp")
load("smtp")
load("http")
load("ftp")

View File

@ -8,6 +8,7 @@ socket = require("socket")
http = require("http") http = require("http")
ftp = require("ftp") ftp = require("ftp")
url = require("url") url = require("url")
ltn12 = require("ltn12")
-- formats a number of seconds into human readable form -- formats a number of seconds into human readable form
function nicetime(s) function nicetime(s)

View File

@ -4,7 +4,7 @@
-- Author: Diego Nehab -- Author: Diego Nehab
-- RCS ID: $Id$ -- RCS ID: $Id$
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
require("socket") socket = require("socket")
host = host or "*" host = host or "*"
port1 = port1 or 8080 port1 = port1 or 8080
port2 = port2 or 8181 port2 = port2 or 8181

View File

@ -7,7 +7,6 @@
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include "luasocket.h"
#include "auxiliar.h" #include "auxiliar.h"
/*=========================================================================*\ /*=========================================================================*\
@ -16,16 +15,15 @@
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Initializes the module * Initializes the module
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
int aux_open(lua_State *L) int aux_open(lua_State *L) {
{
return 0; return 0;
} }
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Creates a new class with given methods * Creates a new class with given methods
* Methods whose names start with __ are passed directly to the metatable.
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
void aux_newclass(lua_State *L, const char *classname, luaL_reg *func) void aux_newclass(lua_State *L, const char *classname, luaL_reg *func) {
{
luaL_newmetatable(L, classname); /* mt */ luaL_newmetatable(L, classname); /* mt */
/* create __index table to place methods */ /* create __index table to place methods */
lua_pushstring(L, "__index"); /* mt,"__index" */ lua_pushstring(L, "__index"); /* mt,"__index" */
@ -45,11 +43,31 @@ void aux_newclass(lua_State *L, const char *classname, luaL_reg *func)
lua_pop(L, 1); lua_pop(L, 1);
} }
/*-------------------------------------------------------------------------*\
* Prints the value of a class in a nice way
\*-------------------------------------------------------------------------*/
int aux_tostring(lua_State *L) {
char buf[32];
if (!lua_getmetatable(L, 1)) goto error;
lua_pushstring(L, "__index");
lua_gettable(L, -2);
if (!lua_istable(L, -1)) goto error;
lua_pushstring(L, "class");
lua_gettable(L, -2);
if (!lua_isstring(L, -1)) goto error;
sprintf(buf, "%p", lua_touserdata(L, 1));
lua_pushfstring(L, "%s: %s", lua_tostring(L, -1), buf);
return 1;
error:
lua_pushstring(L, "invalid object passed to 'auxiliar.c:__tostring'");
lua_error(L);
return 1;
}
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Insert class into group * Insert class into group
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
void aux_add2group(lua_State *L, const char *classname, const char *groupname) void aux_add2group(lua_State *L, const char *classname, const char *groupname) {
{
luaL_getmetatable(L, classname); luaL_getmetatable(L, classname);
lua_pushstring(L, groupname); lua_pushstring(L, groupname);
lua_pushboolean(L, 1); lua_pushboolean(L, 1);
@ -60,8 +78,7 @@ void aux_add2group(lua_State *L, const char *classname, const char *groupname)
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Make sure argument is a boolean * Make sure argument is a boolean
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
int aux_checkboolean(lua_State *L, int objidx) int aux_checkboolean(lua_State *L, int objidx) {
{
if (!lua_isboolean(L, objidx)) if (!lua_isboolean(L, objidx))
luaL_typerror(L, objidx, lua_typename(L, LUA_TBOOLEAN)); luaL_typerror(L, objidx, lua_typename(L, LUA_TBOOLEAN));
return lua_toboolean(L, objidx); return lua_toboolean(L, objidx);
@ -71,8 +88,7 @@ int aux_checkboolean(lua_State *L, int objidx)
* Return userdata pointer if object belongs to a given class, abort with * Return userdata pointer if object belongs to a given class, abort with
* error otherwise * error otherwise
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
void *aux_checkclass(lua_State *L, const char *classname, int objidx) void *aux_checkclass(lua_State *L, const char *classname, int objidx) {
{
void *data = aux_getclassudata(L, classname, objidx); void *data = aux_getclassudata(L, classname, objidx);
if (!data) { if (!data) {
char msg[45]; char msg[45];
@ -86,8 +102,7 @@ void *aux_checkclass(lua_State *L, const char *classname, int objidx)
* Return userdata pointer if object belongs to a given group, abort with * Return userdata pointer if object belongs to a given group, abort with
* error otherwise * error otherwise
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
void *aux_checkgroup(lua_State *L, const char *groupname, int objidx) void *aux_checkgroup(lua_State *L, const char *groupname, int objidx) {
{
void *data = aux_getgroupudata(L, groupname, objidx); void *data = aux_getgroupudata(L, groupname, objidx);
if (!data) { if (!data) {
char msg[45]; char msg[45];
@ -100,8 +115,7 @@ void *aux_checkgroup(lua_State *L, const char *groupname, int objidx)
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Set object class * Set object class
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
void aux_setclass(lua_State *L, const char *classname, int objidx) void aux_setclass(lua_State *L, const char *classname, int objidx) {
{
luaL_getmetatable(L, classname); luaL_getmetatable(L, classname);
if (objidx < 0) objidx--; if (objidx < 0) objidx--;
lua_setmetatable(L, objidx); lua_setmetatable(L, objidx);
@ -111,8 +125,7 @@ void aux_setclass(lua_State *L, const char *classname, int objidx)
* Get a userdata pointer if object belongs to a given group. Return NULL * Get a userdata pointer if object belongs to a given group. Return NULL
* otherwise * otherwise
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
void *aux_getgroupudata(lua_State *L, const char *groupname, int objidx) void *aux_getgroupudata(lua_State *L, const char *groupname, int objidx) {
{
if (!lua_getmetatable(L, objidx)) if (!lua_getmetatable(L, objidx))
return NULL; return NULL;
lua_pushstring(L, groupname); lua_pushstring(L, groupname);
@ -130,7 +143,6 @@ void *aux_getgroupudata(lua_State *L, const char *groupname, int objidx)
* Get a userdata pointer if object belongs to a given class. Return NULL * Get a userdata pointer if object belongs to a given class. Return NULL
* otherwise * otherwise
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
void *aux_getclassudata(lua_State *L, const char *classname, int objidx) void *aux_getclassudata(lua_State *L, const char *classname, int objidx) {
{
return luaL_checkudata(L, objidx, classname); return luaL_checkudata(L, objidx, classname);
} }

View File

@ -2,26 +2,28 @@
#define AUX_H #define AUX_H
/*=========================================================================*\ /*=========================================================================*\
* Auxiliar routines for class hierarchy manipulation * Auxiliar routines for class hierarchy manipulation
* LuaSocket toolkit * LuaSocket toolkit (but completely independent of other LuaSocket modules)
* *
* A LuaSocket class is a name associated with Lua metatables. A LuaSocket * A LuaSocket class is a name associated with Lua metatables. A LuaSocket
* group is a name associated to a class. A class can belong to any number * group is a name associated with a class. A class can belong to any number
* of groups. This module provides the functionality to: * of groups. This module provides the functionality to:
* *
* - create new classes * - create new classes
* - add classes to groups * - add classes to groups
* - set the class of object * - set the class of objects
* - check if an object belongs to a given class or group * - check if an object belongs to a given class or group
* - get the userdata associated to objects
* - print objects in a pretty way
* *
* LuaSocket class names follow the convention <module>{<class>}. Modules * LuaSocket class names follow the convention <module>{<class>}. Modules
* can define any number of classes and groups. The module tcp.c, for * can define any number of classes and groups. The module tcp.c, for
* example, defines the classes tcp{master}, tcp{client} and tcp{server} and * example, defines the classes tcp{master}, tcp{client} and tcp{server} and
* the groups tcp{client, server} and tcp{any}. Module functions can then * the groups tcp{client,server} and tcp{any}. Module functions can then
* perform type-checking on it's arguments by either class or group. * perform type-checking on their arguments by either class or group.
* *
* LuaSocket metatables define the __index metamethod as being a table. This * LuaSocket metatables define the __index metamethod as being a table. This
* table has one field for each method supported by the class. In DEBUG * table has one field for each method supported by the class, and a field
* mode, it also has one field with the class name. * "class" with the class name.
* *
* The mapping from class name to the corresponding metatable and the * The mapping from class name to the corresponding metatable and the
* reverse mapping are done using lauxlib. * reverse mapping are done using lauxlib.
@ -32,14 +34,6 @@
#include <lua.h> #include <lua.h>
#include <lauxlib.h> #include <lauxlib.h>
/* min and max macros */
#ifndef MIN
#define MIN(x, y) ((x) < (y) ? x : y)
#endif
#ifndef MAX
#define MAX(x, y) ((x) > (y) ? x : y)
#endif
int aux_open(lua_State *L); int aux_open(lua_State *L);
void aux_newclass(lua_State *L, const char *classname, luaL_reg *func); void aux_newclass(lua_State *L, const char *classname, luaL_reg *func);
void aux_add2group(lua_State *L, const char *classname, const char *group); void aux_add2group(lua_State *L, const char *classname, const char *group);
@ -49,5 +43,6 @@ void *aux_checkgroup(lua_State *L, const char *groupname, int objidx);
void *aux_getclassudata(lua_State *L, const char *groupname, int objidx); void *aux_getclassudata(lua_State *L, const char *groupname, int objidx);
void *aux_getgroupudata(lua_State *L, const char *groupname, int objidx); void *aux_getgroupudata(lua_State *L, const char *groupname, int objidx);
int aux_checkboolean(lua_State *L, int objidx); int aux_checkboolean(lua_State *L, int objidx);
int aux_tostring(lua_State *L);
#endif /* AUX_H */ #endif /* AUX_H */

View File

@ -7,7 +7,6 @@
#include <lua.h> #include <lua.h>
#include <lauxlib.h> #include <lauxlib.h>
#include "auxiliar.h"
#include "buffer.h" #include "buffer.h"
/*=========================================================================*\ /*=========================================================================*\
@ -20,6 +19,14 @@ static int buf_get(p_buf buf, const char **data, size_t *count);
static void buf_skip(p_buf buf, size_t count); static void buf_skip(p_buf buf, size_t count);
static int sendraw(p_buf buf, const char *data, size_t count, size_t *sent); static int sendraw(p_buf buf, const char *data, size_t count, size_t *sent);
/* min and max macros */
#ifndef MIN
#define MIN(x, y) ((x) < (y) ? x : y)
#endif
#ifndef MAX
#define MAX(x, y) ((x) > (y) ? x : y)
#endif
/*=========================================================================*\ /*=========================================================================*\
* Exported functions * Exported functions
\*=========================================================================*/ \*=========================================================================*/

52
src/except.c Normal file
View File

@ -0,0 +1,52 @@
#include <lauxlib.h>
#include <stdio.h>
#include "except.h"
static int global_try(lua_State *L);
static int global_protect(lua_State *L);
static int protected(lua_State *L);
static luaL_reg func[] = {
{"try", global_try},
{"protect", global_protect},
{NULL, NULL}
};
/*-------------------------------------------------------------------------*\
* Exception handling: try method
\*-------------------------------------------------------------------------*/
static int global_try(lua_State *L) {
if (lua_isnil(L, 1) || (lua_isboolean(L, 1) && !lua_toboolean(L, 1))) {
lua_settop(L, 2);
lua_error(L);
return 0;
} else return lua_gettop(L);
}
/*-------------------------------------------------------------------------*\
* Exception handling: protect factory
\*-------------------------------------------------------------------------*/
static int protected(lua_State *L) {
lua_pushvalue(L, lua_upvalueindex(1));
lua_insert(L, 1);
if (lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0) != 0) {
lua_pushnil(L);
lua_insert(L, 1);
return 2;
} else return lua_gettop(L);
}
static int global_protect(lua_State *L) {
lua_insert(L, 1);
lua_pushcclosure(L, protected, 1);
return 1;
}
/*-------------------------------------------------------------------------*\
* Init module
\*-------------------------------------------------------------------------*/
int except_open(lua_State *L) {
luaL_openlib(L, NULL, func, 0);
return 0;
}

35
src/except.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef EXCEPT_H
#define EXCEPT_H
/*=========================================================================*\
* Exception control
* LuaSocket toolkit (but completely independent from other modules)
*
* This provides support for simple exceptions in Lua. During the
* development of the HTTP/FTP/SMTP support, it became aparent that
* error checking was taking a substantial amount of the coding. These
* function greatly simplify the task of checking errors.
*
* The main idea is that functions should return nil as its first return
* value when it finds an error, and return an error message (or value)
* following nil. In case of success, as long as the first value is not nil,
* the other values don't matter.
*
* The idea is to nest function calls with the "try" function. This function
* checks the first value, and calls "error" on the second if the first is
* nil. Otherwise, it returns all values it received.
*
* The protect function returns a new function that behaves exactly like the
* function it receives, but the new function doesn't throw exceptions: it
* returns nil followed by the error message instead.
*
* With these two function, it's easy to write functions that throw
* exceptions on error, but that don't interrupt the user script.
*
* RCS ID: $Id$
\*=========================================================================*/
#include <lua.h>
int except_open(lua_State *L);
#endif

View File

@ -7,7 +7,7 @@
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Load other required modules -- Load required modules
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
local socket = require("socket") local socket = require("socket")
local ltn12 = require("ltn12") local ltn12 = require("ltn12")
@ -17,10 +17,7 @@ local tp = require("tp")
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Setup namespace -- Setup namespace
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
local ftp = {} _LOADED["ftp"] = getfenv(1)
-- make all module globals fall into namespace
setmetatable(ftp, { __index = _G })
setfenv(1, ftp)
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Program constants -- Program constants
@ -32,9 +29,7 @@ PORT = 21
-- this is the default anonymous password. used when no password is -- this is the default anonymous password. used when no password is
-- provided in url. should be changed to your e-mail. -- provided in url. should be changed to your e-mail.
USER = "ftp" USER = "ftp"
EMAIL = "anonymous@anonymous.org" PASSWORD = "anonymous@anonymous.org"
-- block size used in transfers
BLOCKSIZE = 2048
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Low level FTP API -- Low level FTP API
@ -42,7 +37,7 @@ BLOCKSIZE = 2048
local metat = { __index = {} } local metat = { __index = {} }
function open(server, port) function open(server, port)
local tp = socket.try(socket.tp.connect(server, port or PORT)) local tp = socket.try(tp.connect(server, port or PORT, TIMEOUT))
return setmetatable({tp = tp}, metat) return setmetatable({tp = tp}, metat)
end end
@ -51,14 +46,17 @@ local function port(portt)
end end
local function pasv(pasvt) local function pasv(pasvt)
return socket.connect(pasvt.ip, pasvt.port) local data = socket.try(socket.tcp())
socket.try(data:settimeout(TIMEOUT))
socket.try(data:connect(pasvt.ip, pasvt.port))
return data
end end
function metat.__index:login(user, password) function metat.__index:login(user, password)
socket.try(self.tp:command("user", user or USER)) socket.try(self.tp:command("user", user or USER))
local code, reply = socket.try(self.tp:check{"2..", 331}) local code, reply = socket.try(self.tp:check{"2..", 331})
if code == 331 then if code == 331 then
socket.try(self.tp:command("pass", password or EMAIL)) socket.try(self.tp:command("pass", password or PASSWORD))
socket.try(self.tp:check("2..")) socket.try(self.tp:check("2.."))
end end
return 1 return 1
@ -104,6 +102,7 @@ function metat.__index:send(sendt)
socket.try(self.pasvt or self.portt, "need port or pasv first") socket.try(self.pasvt or self.portt, "need port or pasv first")
if self.pasvt then data = socket.try(pasv(self.pasvt)) end if self.pasvt then data = socket.try(pasv(self.pasvt)) end
local argument = sendt.argument or string.gsub(sendt.path, "^/", "") local argument = sendt.argument or string.gsub(sendt.path, "^/", "")
if argument == "" then argument = nil end
local command = sendt.command or "stor" local command = sendt.command or "stor"
socket.try(self.tp:command(command, argument)) socket.try(self.tp:command(command, argument))
local code, reply = socket.try(self.tp:check{"2..", "1.."}) local code, reply = socket.try(self.tp:check{"2..", "1.."})
@ -133,6 +132,7 @@ function metat.__index:receive(recvt)
socket.try(self.pasvt or self.portt, "need port or pasv first") socket.try(self.pasvt or self.portt, "need port or pasv first")
if self.pasvt then data = socket.try(pasv(self.pasvt)) end if self.pasvt then data = socket.try(pasv(self.pasvt)) end
local argument = recvt.argument or string.gsub(recvt.path, "^/", "") local argument = recvt.argument or string.gsub(recvt.path, "^/", "")
if argument == "" then argument = nil end
local command = recvt.command or "retr" local command = recvt.command or "retr"
socket.try(self.tp:command(command, argument)) socket.try(self.tp:command(command, argument))
local code = socket.try(self.tp:check{"1..", "2.."}) local code = socket.try(self.tp:check{"1..", "2.."})
@ -182,14 +182,14 @@ end
-- High level FTP API -- High level FTP API
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
local function tput(putt) local function tput(putt)
local ftp = socket.ftp.open(putt.host, putt.port) local con = ftp.open(putt.host, putt.port)
ftp:greet() con:greet()
ftp:login(putt.user, putt.password) con:login(putt.user, putt.password)
if putt.type then ftp:type(putt.type) end if putt.type then con:type(putt.type) end
ftp:pasv() con:pasv()
ftp:send(putt) con:send(putt)
ftp:quit() con:quit()
return ftp:close() return con:close()
end end
local default = { local default = {
@ -198,15 +198,16 @@ local default = {
} }
local function parse(u) local function parse(u)
local putt = socket.try(url.parse(u, default)) local t = socket.try(url.parse(u, default))
socket.try(putt.scheme == "ftp", "invalid scheme '" .. putt.scheme .. "'") socket.try(t.scheme == "ftp", "invalid scheme '" .. t.scheme .. "'")
socket.try(putt.host, "invalid host") socket.try(t.host, "invalid host")
local pat = "^type=(.)$" local pat = "^type=(.)$"
if putt.params then if t.params then
putt.type = socket.skip(2, string.find(putt.params, pat)) t.type = socket.skip(2, string.find(t.params, pat))
socket.try(putt.type == "a" or putt.type == "i") socket.try(t.type == "a" or t.type == "i",
"invalid type '" .. t.type .. "'")
end end
return putt return t
end end
local function sput(u, body) local function sput(u, body)
@ -221,17 +222,17 @@ put = socket.protect(function(putt, body)
end) end)
local function tget(gett) local function tget(gett)
local ftp = socket.ftp.open(gett.host, gett.port) local con = ftp.open(gett.host, gett.port)
ftp:greet() con:greet()
ftp:login(gett.user, gett.password) con:login(gett.user, gett.password)
if gett.type then ftp:type(gett.type) end if gett.type then con:type(gett.type) end
ftp:pasv() con:pasv()
ftp:receive(gett) con:receive(gett)
ftp:quit() con:quit()
return ftp:close() return con:close()
end end
local function sget(u, body) local function sget(u)
local gett = parse(u) local gett = parse(u)
local t = {} local t = {}
gett.sink = ltn12.sink.table(t) gett.sink = ltn12.sink.table(t)
@ -240,7 +241,7 @@ local function sget(u, body)
end end
get = socket.protect(function(gett) get = socket.protect(function(gett)
if type(gett) == "string" then return sget(gett, body) if type(gett) == "string" then return sget(gett)
else return tget(gett) end else return tget(gett) end
end) end)

View File

@ -7,7 +7,7 @@
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Load other required modules -- Load required modules
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
local socket = require("socket") local socket = require("socket")
local ltn12 = require("ltn12") local ltn12 = require("ltn12")
@ -17,42 +17,68 @@ local url = require("url")
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Setup namespace -- Setup namespace
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
http = {} _LOADED["http"] = getfenv(1)
-- make all module globals fall into namespace
setmetatable(http, { __index = _G })
setfenv(1, http)
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Program constants -- Program constants
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- connection timeout in seconds -- connection timeout in seconds
TIMEOUT = 60 TIMEOUT = 4
-- default port for document retrieval -- default port for document retrieval
PORT = 80 PORT = 80
-- user agent field sent in request -- user agent field sent in request
USERAGENT = socket.version USERAGENT = socket.VERSION
-- block size used in transfers -- block size used in transfers
BLOCKSIZE = 2048 BLOCKSIZE = 2048
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Function return value selectors -- Low level HTTP API
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
local function second(a, b) local metat = { __index = {} }
return b
function open(host, port)
local con = socket.try(socket.tcp())
socket.try(con:settimeout(TIMEOUT))
port = port or PORT
socket.try(con:connect(host, port))
return setmetatable({ con = con }, metat)
end end
local function third(a, b, c) function metat.__index:sendrequestline(method, uri)
return c local reqline = string.format("%s %s HTTP/1.1\r\n", method or "GET", uri)
return socket.try(self.con:send(reqline))
end end
local function receive_headers(reqt, respt, tmp) function metat.__index:sendheaders(headers)
local sock = tmp.sock for i, v in pairs(headers) do
socket.try(self.con:send(i .. ": " .. v .. "\r\n"))
end
-- mark end of request headers
socket.try(self.con:send("\r\n"))
return 1
end
function metat.__index:sendbody(headers, source, step)
source = source or ltn12.source.empty()
step = step or ltn12.pump.step
-- if we don't know the size in advance, send chunked and hope for the best
local mode
if headers["content-length"] then mode = "keep-open"
else mode = "http-chunked" end
return socket.try(ltn12.pump.all(source, socket.sink(mode, self.con), step))
end
function metat.__index:receivestatusline()
local status = socket.try(self.con:receive())
local code = socket.skip(2, string.find(status, "HTTP/%d*%.%d* (%d%d%d)"))
return socket.try(tonumber(code), status)
end
function metat.__index:receiveheaders()
local line, name, value local line, name, value
local headers = {} local headers = {}
-- store results
respt.headers = headers
-- get first line -- get first line
line = socket.try(sock:receive()) line = socket.try(self.con:receive())
-- headers go until a blank line is found -- headers go until a blank line is found
while line ~= "" do while line ~= "" do
-- get field-name and value -- get field-name and value
@ -60,127 +86,65 @@ local function receive_headers(reqt, respt, tmp)
socket.try(name and value, "malformed reponse headers") socket.try(name and value, "malformed reponse headers")
name = string.lower(name) name = string.lower(name)
-- get next line (value might be folded) -- get next line (value might be folded)
line = socket.try(sock:receive()) line = socket.try(self.con:receive())
-- unfold any folded values -- unfold any folded values
while string.find(line, "^%s") do while string.find(line, "^%s") do
value = value .. line value = value .. line
line = socket.try(sock:receive()) line = socket.try(self.con:receive())
end end
-- save pair in table -- save pair in table
if headers[name] then headers[name] = headers[name] .. ", " .. value if headers[name] then headers[name] = headers[name] .. ", " .. value
else headers[name] = value end else headers[name] = value end
end end
return headers
end end
local function receive_body(reqt, respt, tmp) function metat.__index:receivebody(headers, sink, step)
local sink = reqt.sink or ltn12.sink.null() sink = sink or ltn12.sink.null()
local step = reqt.step or ltn12.pump.step step = step or ltn12.pump.step
local source local length = tonumber(headers["content-length"])
local te = respt.headers["transfer-encoding"] local TE = headers["transfer-encoding"]
if te and te ~= "identity" then local mode
-- get by chunked transfer-coding of message body if TE and TE ~= "identity" then mode = "http-chunked"
source = socket.source("http-chunked", tmp.sock) elseif tonumber(headers["content-length"]) then mode = "by-length"
elseif tonumber(respt.headers["content-length"]) then else mode = "default" end
-- get by content-length return socket.try(ltn12.pump.all(socket.source(mode, self.con, length),
local length = tonumber(respt.headers["content-length"]) sink, step))
source = socket.source("by-length", tmp.sock, length)
else
-- get it all until connection closes
source = socket.source(tmp.sock)
end
socket.try(ltn12.pump.all(source, sink, step))
end end
local function send_headers(sock, headers) function metat.__index:close()
-- send request headers return self.con:close()
for i, v in pairs(headers) do
socket.try(sock:send(i .. ": " .. v .. "\r\n"))
end
-- mark end of request headers
socket.try(sock:send("\r\n"))
end end
local function should_receive_body(reqt, respt, tmp) -----------------------------------------------------------------------------
if reqt.method == "HEAD" then return nil end -- High level HTTP API
if respt.code == 204 or respt.code == 304 then return nil end -----------------------------------------------------------------------------
if respt.code >= 100 and respt.code < 200 then return nil end local function uri(reqt)
return 1 local u = reqt
end if not reqt.proxy and not PROXY then
local function receive_status(reqt, respt, tmp)
local status = socket.try(tmp.sock:receive())
local code = third(string.find(status, "HTTP/%d*%.%d* (%d%d%d)"))
-- store results
respt.code, respt.status = tonumber(code), status
end
local function request_uri(reqt, respt, tmp)
local u = tmp.parsed
if not reqt.proxy then
local parsed = tmp.parsed
u = { u = {
path = parsed.path, path = reqt.path,
params = parsed.params, params = reqt.params,
query = parsed.query, query = reqt.query,
fragment = parsed.fragment fragment = reqt.fragment
} }
end end
return url.build(u) return url.build(u)
end end
local function send_request(reqt, respt, tmp) local function adjustheaders(headers, host)
local uri = request_uri(reqt, respt, tmp)
local headers = tmp.headers
local step = reqt.step or ltn12.pump.step
-- send request line
socket.try(tmp.sock:send((reqt.method or "GET")
.. " " .. uri .. " HTTP/1.1\r\n"))
if reqt.source and not headers["content-length"] then
headers["transfer-encoding"] = "chunked"
end
send_headers(tmp.sock, headers)
-- send request message body, if any
if not reqt.source then return end
if headers["content-length"] then
socket.try(ltn12.pump.all(reqt.source,
socket.sink(tmp.sock), step))
else
socket.try(ltn12.pump.all(reqt.source,
socket.sink("http-chunked", tmp.sock), step))
end
end
local function open(reqt, respt, tmp)
local proxy = reqt.proxy or PROXY
local host, port
if proxy then
local pproxy = url.parse(proxy)
socket.try(pproxy.port and pproxy.host, "invalid proxy")
host, port = pproxy.host, pproxy.port
else
host, port = tmp.parsed.host, tmp.parsed.port
end
-- store results
tmp.sock = socket.try(socket.tcp())
socket.try(tmp.sock:settimeout(reqt.timeout or TIMEOUT))
socket.try(tmp.sock:connect(host, port))
end
local function adjust_headers(reqt, respt, tmp)
local lower = {} local lower = {}
-- override with user values -- override with user values
for i,v in (reqt.headers or lower) do for i,v in (headers or lower) do
lower[string.lower(i)] = v lower[string.lower(i)] = v
end end
lower["user-agent"] = lower["user-agent"] or USERAGENT lower["user-agent"] = lower["user-agent"] or USERAGENT
-- these cannot be overriden -- these cannot be overriden
lower["host"] = tmp.parsed.host lower["host"] = host
lower["connection"] = "close" return lower
-- store results
tmp.headers = lower
end end
local function parse_url(reqt, respt, tmp) local function adjustrequest(reqt)
-- parse url with default fields -- parse url with default fields
local parsed = url.parse(reqt.url, { local parsed = url.parse(reqt.url, {
host = "", host = "",
@ -188,61 +152,71 @@ local function parse_url(reqt, respt, tmp)
path ="/", path ="/",
scheme = "http" scheme = "http"
}) })
-- scheme has to be http -- explicit info in reqt overrides that given by the URL
socket.try(parsed.scheme == "http", for i,v in reqt do parsed[i] = v end
string.format("unknown scheme '%s'", parsed.scheme)) -- compute uri if user hasn't overriden
-- explicit authentication info overrides that given by the URL parsed.uri = parsed.uri or uri(parsed)
parsed.user = reqt.user or parsed.user -- adjust headers in request
parsed.password = reqt.password or parsed.password parsed.headers = adjustheaders(parsed.headers, parsed.host)
-- store results return parsed
tmp.parsed = parsed
end end
-- forward declaration local function shouldredirect(reqt, respt)
local request_p return (reqt.redirect ~= false) and
(respt.code == 301 or respt.code == 302) and
(not reqt.method or reqt.method == "GET" or reqt.method == "HEAD")
and (not reqt.nredirects or reqt.nredirects < 5)
end
local function should_authorize(reqt, respt, tmp) local function shouldauthorize(reqt, respt)
-- if there has been an authorization attempt, it must have failed -- if there has been an authorization attempt, it must have failed
if reqt.headers and reqt.headers["authorization"] then return nil end if reqt.headers and reqt.headers["authorization"] then return nil end
-- if last attempt didn't fail due to lack of authentication, -- if last attempt didn't fail due to lack of authentication,
-- or we don't have authorization information, we can't retry -- or we don't have authorization information, we can't retry
return respt.code == 401 and tmp.parsed.user and tmp.parsed.password return respt.code == 401 and reqt.user and reqt.password
end end
local function clone(headers) local function shouldreceivebody(reqt, respt)
if not headers then return nil end if reqt.method == "HEAD" then return nil end
local copy = {} local code = respt.code
for i,v in pairs(headers) do if code == 204 or code == 304 then return nil end
copy[i] = v if code >= 100 and code < 200 then return nil end
return 1
end
local requestp, authorizep, redirectp
function requestp(reqt)
local reqt = adjustrequest(reqt)
local respt = {}
local con = open(reqt.host, reqt.port)
con:sendrequestline(reqt.method, reqt.uri)
con:sendheaders(reqt.headers)
con:sendbody(reqt.headers, reqt.source, reqt.step)
respt.code, respt.status = con:receivestatusline()
respt.headers = con:receiveheaders()
if shouldredirect(reqt, respt) then
con:close()
return redirectp(reqt, respt)
elseif shouldauthorize(reqt, respt) then
con:close()
return authorizep(reqt, respt)
elseif shouldreceivebody(reqt, respt) then
con:receivebody(respt.headers, reqt.sink, reqt.step)
end end
return copy con:close()
return respt
end end
local function authorize(reqt, respt, tmp) function authorizep(reqt, respt)
local headers = clone(reqt.headers) or {} local auth = "Basic " .. (mime.b64(reqt.user .. ":" .. reqt.password))
headers["authorization"] = "Basic " .. reqt.headers["authorization"] = auth
(mime.b64(tmp.parsed.user .. ":" .. tmp.parsed.password)) return requestp(reqt)
local autht = {
method = reqt.method,
url = reqt.url,
source = reqt.source,
sink = reqt.sink,
headers = headers,
timeout = reqt.timeout,
proxy = reqt.proxy,
}
request_p(autht, respt, tmp)
end end
local function should_redirect(reqt, respt, tmp) function redirectp(reqt, respt)
return (reqt.redirect ~= false) and -- we create a new table to get rid of anything we don't
(respt.code == 301 or respt.code == 302) and -- absolutely need, including authentication info
(not reqt.method or reqt.method == "GET" or reqt.method == "HEAD")
and (not tmp.nredirects or tmp.nredirects < 5)
end
local function redirect(reqt, respt, tmp)
tmp.nredirects = (tmp.nredirects or 0) + 1
local redirt = { local redirt = {
method = reqt.method, method = reqt.method,
-- the RFC says the redirect URL has to be absolute, but some -- the RFC says the redirect URL has to be absolute, but some
@ -251,61 +225,30 @@ local function redirect(reqt, respt, tmp)
source = reqt.source, source = reqt.source,
sink = reqt.sink, sink = reqt.sink,
headers = reqt.headers, headers = reqt.headers,
timeout = reqt.timeout, proxy = reqt.proxy,
proxy = reqt.proxy nredirects = (reqt.nredirects or 0) + 1
} }
request_p(redirt, respt, tmp) respt = requestp(redirt)
-- we pass the location header as a clue we redirected -- we pass the location header as a clue we redirected
if respt.headers then respt.headers.location = redirt.url end if respt.headers then respt.headers.location = redirt.url end
end
local function skip_continue(reqt, respt, tmp)
if respt.code == 100 then
receive_status(reqt, respt, tmp)
end
end
-- execute a request of through an exception
function request_p(reqt, respt, tmp)
parse_url(reqt, respt, tmp)
adjust_headers(reqt, respt, tmp)
open(reqt, respt, tmp)
send_request(reqt, respt, tmp)
receive_status(reqt, respt, tmp)
skip_continue(reqt, respt, tmp)
receive_headers(reqt, respt, tmp)
if should_redirect(reqt, respt, tmp) then
tmp.sock:close()
redirect(reqt, respt, tmp)
elseif should_authorize(reqt, respt, tmp) then
tmp.sock:close()
authorize(reqt, respt, tmp)
elseif should_receive_body(reqt, respt, tmp) then
receive_body(reqt, respt, tmp)
end
end
function request(reqt)
local respt, tmp = {}, {}
local s, e = pcall(request_p, reqt, respt, tmp)
if not s then respt.error = e end
if tmp.sock then tmp.sock:close() end
return respt return respt
end end
function get(u) request = socket.protect(requestp)
get = socket.protect(function(u)
local t = {} local t = {}
respt = request { local respt = requestp {
url = u, url = u,
sink = ltn12.sink.table(t) sink = ltn12.sink.table(t)
} }
return (table.getn(t) > 0 or nil) and table.concat(t), respt.headers, return (table.getn(t) > 0 or nil) and table.concat(t), respt.headers,
respt.code, respt.error respt.code
end end)
function post(u, body) post = socket.protect(function(u, body)
local t = {} local t = {}
respt = request { local respt = requestp {
url = u, url = u,
method = "POST", method = "POST",
source = ltn12.source.string(body), source = ltn12.source.string(body),
@ -313,7 +256,7 @@ function post(u, body)
headers = { ["content-length"] = string.len(body) } headers = { ["content-length"] = string.len(body) }
} }
return (table.getn(t) > 0 or nil) and table.concat(t), return (table.getn(t) > 0 or nil) and table.concat(t),
respt.headers, respt.code, respt.error respt.headers, respt.code
end end)
return http return http

View File

@ -10,7 +10,6 @@
#include <lua.h> #include <lua.h>
#include <lauxlib.h> #include <lauxlib.h>
#include "luasocket.h"
#include "inet.h" #include "inet.h"
/*=========================================================================*\ /*=========================================================================*\

View File

@ -8,9 +8,8 @@
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Setup namespace -- Setup namespace
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
local ltn12 = {} _LOADED["ltn12"] = getfenv(1)
setmetatable(ltn12, { __index = _G })
setfenv(1, ltn12)
filter = {} filter = {}
source = {} source = {}
sink = {} sink = {}
@ -19,10 +18,6 @@ pump = {}
-- 2048 seems to be better in windows... -- 2048 seems to be better in windows...
BLOCKSIZE = 2048 BLOCKSIZE = 2048
local function shift(a, b, c)
return b, c
end
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Filter stuff -- Filter stuff
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
@ -53,7 +48,9 @@ local function chain2(f1, f2)
end end
end) end)
return function(chunk) return function(chunk)
return shift(coroutine.resume(co, chunk)) local ret, a, b = coroutine.resume(co, chunk)
if ret then return a, b
else return nil, a end
end end
end end
@ -149,7 +146,9 @@ function source.chain(src, f)
end end
end) end)
return function() return function()
return shift(coroutine.resume(co)) local ret, a, b = coroutine.resume(co)
if ret then return a, b
else return nil, a end
end end
end end
@ -166,7 +165,9 @@ function source.cat(...)
end end
end) end)
return function() return function()
return shift(coroutine.resume(co)) local ret, a, b = coroutine.resume(co)
if ret then return a, b
else return nil, a end
end end
end end

View File

@ -26,7 +26,7 @@
#include "luasocket.h" #include "luasocket.h"
#include "auxiliar.h" #include "auxiliar.h"
#include "base.h" #include "except.h"
#include "timeout.h" #include "timeout.h"
#include "buffer.h" #include "buffer.h"
#include "inet.h" #include "inet.h"
@ -35,11 +35,18 @@
#include "select.h" #include "select.h"
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Modules * Internal function prototypes
\*-------------------------------------------------------------------------*/
static int global_skip(lua_State *L);
static int global_unload(lua_State *L);
static int base_open(lua_State *L);
/*-------------------------------------------------------------------------*\
* Modules and functions
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
static const luaL_reg mod[] = { static const luaL_reg mod[] = {
{"auxiliar", aux_open}, {"auxiliar", aux_open},
{"base", base_open}, {"except", except_open},
{"timeout", tm_open}, {"timeout", tm_open},
{"buffer", buf_open}, {"buffer", buf_open},
{"inet", inet_open}, {"inet", inet_open},
@ -49,11 +56,69 @@ static const luaL_reg mod[] = {
{NULL, NULL} {NULL, NULL}
}; };
static luaL_reg func[] = {
{"skip", global_skip},
{"__unload", global_unload},
{NULL, NULL}
};
/*-------------------------------------------------------------------------*\
* Skip a few arguments
\*-------------------------------------------------------------------------*/
static int global_skip(lua_State *L) {
int amount = luaL_checkint(L, 1);
int ret = lua_gettop(L) - amount - 1;
return ret >= 0 ? ret : 0;
}
/*-------------------------------------------------------------------------*\
* Unloads the library
\*-------------------------------------------------------------------------*/
static int global_unload(lua_State *L) {
sock_close();
return 0;
}
/*-------------------------------------------------------------------------*\
* Setup basic stuff.
\*-------------------------------------------------------------------------*/
static int base_open(lua_State *L) {
if (sock_open()) {
/* whoever is loading the library replaced the global environment
* with the namespace table */
lua_pushvalue(L, LUA_GLOBALSINDEX);
/* make sure library is still "requirable" if initialized staticaly */
lua_pushstring(L, "_LOADEDLIB");
lua_gettable(L, -2);
lua_pushstring(L, LUASOCKET_LIBNAME);
lua_pushcfunction(L, (lua_CFunction) luaopen_socket);
lua_settable(L, -3);
lua_pop(L, 1);
#ifdef LUASOCKET_DEBUG
lua_pushstring(L, "DEBUG");
lua_pushboolean(L, 1);
lua_rawset(L, -3);
#endif
/* make version string available to scripts */
lua_pushstring(L, "VERSION");
lua_pushstring(L, LUASOCKET_VERSION);
lua_rawset(L, -3);
/* export other functions */
luaL_openlib(L, NULL, func, 0);
return 1;
} else {
lua_pushstring(L, "unable to initialize library");
lua_error(L);
return 0;
}
}
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Initializes all library modules. * Initializes all library modules.
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
LUASOCKET_API int luaopen_socket(lua_State *L) { LUASOCKET_API int luaopen_socket(lua_State *L) {
int i; int i;
base_open(L);
for (i = 0; mod[i].name; i++) mod[i].func(L); for (i = 0; mod[i].name; i++) mod[i].func(L);
return 1; return 1;
} }

View File

@ -25,6 +25,7 @@
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Initializes the library. * Initializes the library.
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
#define LUASOCKET_LIBNAME "socket"
LUASOCKET_API int luaopen_socket(lua_State *L); LUASOCKET_API int luaopen_socket(lua_State *L);
#endif /* LUASOCKET_H */ #endif /* LUASOCKET_H */

View File

@ -76,7 +76,17 @@ static UC b64unbase[256];
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
MIME_API int luaopen_mime(lua_State *L) MIME_API int luaopen_mime(lua_State *L)
{ {
lua_newtable(L); /* whoever is loading the library replaced the global environment
* with the namespace table */
lua_pushvalue(L, LUA_GLOBALSINDEX);
/* make sure library is still "requirable" if initialized staticaly */
lua_pushstring(L, "_LOADEDLIB");
lua_gettable(L, -2);
lua_pushstring(L, MIME_LIBNAME);
lua_pushcfunction(L, (lua_CFunction) luaopen_mime);
lua_settable(L, -3);
lua_pop(L, 1);
/* export functions */
luaL_openlib(L, NULL, func, 0); luaL_openlib(L, NULL, func, 0);
/* initialize lookup tables */ /* initialize lookup tables */
qpsetup(qpclass, qpunbase); qpsetup(qpclass, qpunbase);

View File

@ -19,6 +19,7 @@
#define MIME_API extern #define MIME_API extern
#endif #endif
#define MIME_LIBNAME "mime"
MIME_API int luaopen_mime(lua_State *L); MIME_API int luaopen_mime(lua_State *L);
#endif /* MIME_H */ #endif /* MIME_H */

View File

@ -5,24 +5,16 @@
-- RCS ID: $Id$ -- RCS ID: $Id$
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Load MIME from dynamic library
-- Comment these lines if you are loading static
-----------------------------------------------------------------------------
local open = assert(loadlib("mime", "luaopen_mime"))
local mime = assert(open())
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Load other required modules -- Load other required modules
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
local mime = requirelib("mime", "luaopen_mime", getfenv(1))
local ltn12 = require("ltn12") local ltn12 = require("ltn12")
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Setup namespace -- Setup namespace
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- make all module globals fall into mime namespace _LOADED["mime"] = mime
setmetatable(mime, { __index = _G })
setfenv(1, mime)
-- encode, decode and wrap algorithm tables -- encode, decode and wrap algorithm tables
encodet = {} encodet = {}
@ -48,7 +40,7 @@ end
encodet['quoted-printable'] = function(mode) encodet['quoted-printable'] = function(mode)
return ltn12.filter.cycle(qp, "", return ltn12.filter.cycle(qp, "",
(mode == "binary") and "=0D=0A" or "\13\10") (mode == "binary") and "=0D=0A" or "\r\n")
end end
-- define the decoding filters -- define the decoding filters

View File

@ -9,26 +9,21 @@
#include <lua.h> #include <lua.h>
#include <lauxlib.h> #include <lauxlib.h>
#include "luasocket.h"
#include "socket.h" #include "socket.h"
#include "auxiliar.h"
#include "select.h" #include "select.h"
/*=========================================================================*\ /*=========================================================================*\
* Internal function prototypes. * Internal function prototypes.
\*=========================================================================*/ \*=========================================================================*/
static int meth_set(lua_State *L); static int getfd(lua_State *L);
static int meth_isset(lua_State *L); static int dirty(lua_State *L);
static int c_select(lua_State *L); static int collect_fd(lua_State *L, int tab, int max_fd, int itab, fd_set *set);
static int check_dirty(lua_State *L, int tab, int dtab, fd_set *set);
static void return_fd(lua_State *L, fd_set *set, int max_fd,
int itab, int tab, int start);
static void make_assoc(lua_State *L, int tab);
static int global_select(lua_State *L); static int global_select(lua_State *L);
/* fd_set object methods */
static luaL_reg set[] = {
{"set", meth_set},
{"isset", meth_isset},
{NULL, NULL}
};
/* functions in library namespace */ /* functions in library namespace */
static luaL_reg func[] = { static luaL_reg func[] = {
{"select", global_select}, {"select", global_select},
@ -36,22 +31,13 @@ static luaL_reg func[] = {
}; };
/*=========================================================================*\ /*=========================================================================*\
* Internal function prototypes. * Exported functions
\*=========================================================================*/ \*=========================================================================*/
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Initializes module * Initializes module
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
int select_open(lua_State *L) int select_open(lua_State *L) {
{ luaL_openlib(L, NULL, func, 0);
/* get select auxiliar lua function from lua code and register
* pass it as an upvalue to global_select */
#ifdef LUASOCKET_COMPILED
#include "select.lch"
#else
lua_dofile(L, "select.lua");
#endif
luaL_openlib(L, NULL, func, 1);
aux_newclass(L, "select{fd_set}", set);
return 0; return 0;
} }
@ -61,64 +47,149 @@ int select_open(lua_State *L)
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Waits for a set of sockets until a condition is met or timeout. * Waits for a set of sockets until a condition is met or timeout.
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
static int global_select(lua_State *L) static int global_select(lua_State *L) {
{ int timeout, rtab, wtab, itab, max_fd, ret, ndirty;
fd_set *read_fd_set, *write_fd_set; fd_set rset, wset;
/* make sure we have enough arguments (nil is the default) */ FD_ZERO(&rset); FD_ZERO(&wset);
lua_settop(L, 3); lua_settop(L, 3);
/* check timeout */ timeout = lua_isnil(L, 3) ? -1 : (int)(luaL_checknumber(L, 3) * 1000);
if (!lua_isnil(L, 3) && !lua_isnumber(L, 3)) lua_newtable(L); itab = lua_gettop(L);
luaL_argerror(L, 3, "number or nil expected"); lua_newtable(L); rtab = lua_gettop(L);
/* select auxiliar lua function to be called comes first */ lua_newtable(L); wtab = lua_gettop(L);
lua_pushvalue(L, lua_upvalueindex(1)); max_fd = collect_fd(L, 1, -1, itab, &rset);
lua_insert(L, 1); ndirty = check_dirty(L, 1, rtab, &rset);
/* pass fd_set objects */ timeout = ndirty > 0? 0: timeout;
read_fd_set = (fd_set *) lua_newuserdata(L, sizeof(fd_set)); max_fd = collect_fd(L, 2, max_fd, itab, &wset);
FD_ZERO(read_fd_set); ret = sock_select(max_fd+1, &rset, &wset, NULL, timeout);
aux_setclass(L, "select{fd_set}", -1); if (ret > 0 || (ret == 0 && ndirty > 0)) {
write_fd_set = (fd_set *) lua_newuserdata(L, sizeof(fd_set)); return_fd(L, &rset, max_fd+1, itab, rtab, ndirty);
FD_ZERO(write_fd_set); return_fd(L, &wset, max_fd+1, itab, wtab, 0);
aux_setclass(L, "select{fd_set}", -1); make_assoc(L, rtab);
/* pass select auxiliar C function */ make_assoc(L, wtab);
lua_pushcfunction(L, c_select); return 2;
/* call select auxiliar lua function */ } else if (ret == 0) {
lua_call(L, 6, 3); lua_pushstring(L, "timeout");
return 3; return 3;
} } else {
lua_pushnil(L);
/*=========================================================================*\ lua_pushnil(L);
* Lua methods lua_pushstring(L, "error");
\*=========================================================================*/ return 3;
static int meth_set(lua_State *L) }
{
fd_set *set = (fd_set *) aux_checkclass(L, "select{fd_set}", 1);
t_sock fd = (t_sock) lua_tonumber(L, 2);
if (fd >= 0) FD_SET(fd, set);
return 0;
}
static int meth_isset(lua_State *L)
{
fd_set *set = (fd_set *) aux_checkclass(L, "select{fd_set}", 1);
t_sock fd = (t_sock) lua_tonumber(L, 2);
if (fd >= 0 && FD_ISSET(fd, set)) lua_pushnumber(L, 1);
else lua_pushnil(L);
return 1;
} }
/*=========================================================================*\ /*=========================================================================*\
* Internal functions * Internal functions
\*=========================================================================*/ \*=========================================================================*/
static int c_select(lua_State *L) static int getfd(lua_State *L) {
{ int fd = -1;
int max_fd = (int) lua_tonumber(L, 1); lua_pushstring(L, "getfd");
fd_set *read_fd_set = (fd_set *) aux_checkclass(L, "select{fd_set}", 2); lua_gettable(L, -2);
fd_set *write_fd_set = (fd_set *) aux_checkclass(L, "select{fd_set}", 3); if (!lua_isnil(L, -1)) {
int timeout = lua_isnil(L, 4) ? -1 : (int)(lua_tonumber(L, 4) * 1000); lua_pushvalue(L, -2);
struct timeval tv; lua_call(L, 1, 1);
tv.tv_sec = timeout / 1000; if (lua_isnumber(L, -1))
tv.tv_usec = (timeout % 1000) * 1000; fd = lua_tonumber(L, -1);
lua_pushnumber(L, select(max_fd, read_fd_set, write_fd_set, NULL, }
timeout < 0 ? NULL : &tv)); lua_pop(L, 1);
return 1; return fd;
} }
static int dirty(lua_State *L) {
int is = 0;
lua_pushstring(L, "dirty");
lua_gettable(L, -2);
if (!lua_isnil(L, -1)) {
lua_pushvalue(L, -2);
lua_call(L, 1, 1);
is = lua_toboolean(L, -1);
}
lua_pop(L, 1);
return is;
}
static int collect_fd(lua_State *L, int tab, int max_fd,
int itab, fd_set *set) {
int i = 1;
if (lua_isnil(L, tab))
return max_fd;
while (1) {
int fd;
lua_pushnumber(L, i);
lua_gettable(L, tab);
if (lua_isnil(L, -1)) {
lua_pop(L, 1);
break;
}
fd = getfd(L);
if (fd > 0) {
FD_SET(fd, set);
if (max_fd < fd) max_fd = fd;
lua_pushnumber(L, fd);
lua_pushvalue(L, -2);
lua_settable(L, itab);
}
lua_pop(L, 1);
i = i + 1;
}
return max_fd;
}
static int check_dirty(lua_State *L, int tab, int dtab, fd_set *set) {
int ndirty = 0, i = 1;
if (lua_isnil(L, tab))
return 0;
while (1) {
int fd;
lua_pushnumber(L, i);
lua_gettable(L, tab);
if (lua_isnil(L, -1)) {
lua_pop(L, 1);
break;
}
fd = getfd(L);
if (fd > 0 && dirty(L)) {
lua_pushnumber(L, ++ndirty);
lua_pushvalue(L, -2);
lua_settable(L, dtab);
FD_CLR(fd, set);
}
lua_pop(L, 1);
i = i + 1;
}
return ndirty;
}
static void return_fd(lua_State *L, fd_set *set, int max_fd,
int itab, int tab, int start) {
int fd;
for (fd = 0; fd < max_fd; fd++) {
if (FD_ISSET(fd, set)) {
lua_pushnumber(L, ++start);
lua_pushnumber(L, fd);
lua_gettable(L, itab);
lua_settable(L, tab);
}
}
}
static void make_assoc(lua_State *L, int tab) {
int i = 1, atab;
lua_newtable(L); atab = lua_gettop(L);
while (1) {
lua_pushnumber(L, i);
lua_gettable(L, tab);
if (!lua_isnil(L, -1)) {
lua_pushnumber(L, i);
lua_pushvalue(L, -2);
lua_settable(L, atab);
lua_pushnumber(L, i);
lua_settable(L, atab);
} else {
lua_pop(L, 1);
break;
}
i = i+1;
}
}

View File

@ -7,15 +7,9 @@
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Load SMTP from dynamic library -- Load required modules
-- Comment these lines if you are loading static
-----------------------------------------------------------------------------
local open = assert(loadlib("smtp", "luaopen_smtp"))
local smtp = assert(open())
-----------------------------------------------------------------------------
-- Load other required modules
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
local smtp = requirelib("smtp")
local socket = require("socket") local socket = require("socket")
local ltn12 = require("ltn12") local ltn12 = require("ltn12")
local tp = require("tp") local tp = require("tp")
@ -23,10 +17,10 @@ local tp = require("tp")
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Setup namespace -- Setup namespace
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- make all module globals fall into smtp namespace _LOADED["smtp"] = smtp
setmetatable(smtp, { __index = _G })
setfenv(1, smtp)
-- timeout for connection
TIMEOUT = 60
-- default server used to send e-mails -- default server used to send e-mails
SERVER = "localhost" SERVER = "localhost"
-- default port -- default port
@ -94,9 +88,7 @@ function metat.__index:send(mailt)
end end
function open(server, port) function open(server, port)
print(server or SERVER, port or PORT) local tp = socket.try(tp.connect(server or SERVER, port or PORT, TIMEOUT))
local tp, error = tp.connect(server or SERVER, port or PORT)
if not tp then return nil, error end
return setmetatable({tp = tp}, metat) return setmetatable({tp = tp}, metat)
end end
@ -121,7 +113,10 @@ local function send_multipart(mesgt)
coroutine.yield('content-type: multipart/mixed; boundary="' .. coroutine.yield('content-type: multipart/mixed; boundary="' ..
bd .. '"\r\n\r\n') bd .. '"\r\n\r\n')
-- send preamble -- send preamble
if mesgt.body.preamble then coroutine.yield(mesgt.body.preamble) end if mesgt.body.preamble then
coroutine.yield(mesgt.body.preamble)
coroutine.yield("\r\n")
end
-- send each part separated by a boundary -- send each part separated by a boundary
for i, m in ipairs(mesgt.body) do for i, m in ipairs(mesgt.body) do
coroutine.yield("\r\n--" .. bd .. "\r\n") coroutine.yield("\r\n--" .. bd .. "\r\n")
@ -130,7 +125,10 @@ local function send_multipart(mesgt)
-- send last boundary -- send last boundary
coroutine.yield("\r\n--" .. bd .. "--\r\n\r\n") coroutine.yield("\r\n--" .. bd .. "--\r\n\r\n")
-- send epilogue -- send epilogue
if mesgt.body.epilogue then coroutine.yield(mesgt.body.epilogue) end if mesgt.body.epilogue then
coroutine.yield(mesgt.body.epilogue)
coroutine.yield("\r\n")
end
end end
-- yield message body from a source -- yield message body from a source
@ -183,12 +181,12 @@ end
-- set defaul headers -- set defaul headers
local function adjust_headers(mesgt) local function adjust_headers(mesgt)
local lower = {} local lower = {}
for i,v in (mesgt or lower) do for i,v in (mesgt.headers or lower) do
lower[string.lower(i)] = v lower[string.lower(i)] = v
end end
lower["date"] = lower["date"] or lower["date"] = lower["date"] or
os.date("!%a, %d %b %Y %H:%M:%S ") .. (mesgt.zone or ZONE) os.date("!%a, %d %b %Y %H:%M:%S ") .. (mesgt.zone or ZONE)
lower["x-mailer"] = lower["x-mailer"] or socket.version lower["x-mailer"] = lower["x-mailer"] or socket.VERSION
-- this can't be overriden -- this can't be overriden
lower["mime-version"] = "1.0" lower["mime-version"] = "1.0"
mesgt.headers = lower mesgt.headers = lower
@ -198,18 +196,22 @@ function message(mesgt)
adjust_headers(mesgt) adjust_headers(mesgt)
-- create and return message source -- create and return message source
local co = coroutine.create(function() send_message(mesgt) end) local co = coroutine.create(function() send_message(mesgt) end)
return function() return socket.skip(1, coroutine.resume(co)) end return function()
local ret, a, b = coroutine.resume(co)
if ret then return a, b
else return nil, a end
end
end end
--------------------------------------------------------------------------- ---------------------------------------------------------------------------
-- High level SMTP API -- High level SMTP API
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
send = socket.protect(function(mailt) send = socket.protect(function(mailt)
local smtp = socket.try(open(mailt.server, mailt.port)) local con = open(mailt.server, mailt.port)
smtp:greet(mailt.domain) con:greet(mailt.domain)
smtp:send(mailt) con:send(mailt)
smtp:quit() con:quit()
return smtp:close() return con:close()
end) end)
return smtp return smtp

View File

@ -7,8 +7,8 @@
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Load LuaSocket from dynamic library -- Load LuaSocket from dynamic library
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
local open = assert(loadlib("luasocket", "luaopen_socket")) local socket = requirelib("luasocket", "luaopen_socket", getfenv(1))
local socket = assert(open()) _LOADED["socket"] = socket
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Auxiliar functions -- Auxiliar functions
@ -116,18 +116,21 @@ socket.sourcet["by-length"] = function(sock, length)
end end
socket.sourcet["until-closed"] = function(sock) socket.sourcet["until-closed"] = function(sock)
local done
return setmetatable({ return setmetatable({
getfd = function() return sock:getfd() end, getfd = function() return sock:getfd() end,
dirty = function() return sock:dirty() end dirty = function() return sock:dirty() end
}, { }, {
__call = ltn12.source.simplify(function() __call = function()
if done then return nil end
local chunk, err, partial = sock:receive(socket.BLOCKSIZE) local chunk, err, partial = sock:receive(socket.BLOCKSIZE)
if not err then return chunk if not err then return chunk
elseif err == "closed" then elseif err == "closed" then
sock:close() sock:close()
return partial, ltn12.source.empty() done = 1
return partial
else return nil, err end else return nil, err end
end) end
}) })
end end

View File

@ -9,13 +9,10 @@
#include <lua.h> #include <lua.h>
#include <lauxlib.h> #include <lauxlib.h>
#include "luasocket.h"
#include "auxiliar.h" #include "auxiliar.h"
#include "socket.h" #include "socket.h"
#include "inet.h" #include "inet.h"
#include "options.h" #include "options.h"
#include "base.h"
#include "tcp.h" #include "tcp.h"
/*=========================================================================*\ /*=========================================================================*\
@ -41,7 +38,7 @@ static int meth_dirty(lua_State *L);
/* tcp object methods */ /* tcp object methods */
static luaL_reg tcp[] = { static luaL_reg tcp[] = {
{"__gc", meth_close}, {"__gc", meth_close},
{"__tostring", base_meth_tostring}, {"__tostring", aux_tostring},
{"accept", meth_accept}, {"accept", meth_accept},
{"bind", meth_bind}, {"bind", meth_bind},
{"close", meth_close}, {"close", meth_close},

View File

@ -26,6 +26,14 @@
#endif #endif
#endif #endif
/* min and max macros */
#ifndef MIN
#define MIN(x, y) ((x) < (y) ? x : y)
#endif
#ifndef MAX
#define MAX(x, y) ((x) > (y) ? x : y)
#endif
/*=========================================================================*\ /*=========================================================================*\
* Internal function prototypes * Internal function prototypes
\*=========================================================================*/ \*=========================================================================*/

View File

@ -2,24 +2,28 @@
-- Unified SMTP/FTP subsystem -- Unified SMTP/FTP subsystem
-- LuaSocket toolkit. -- LuaSocket toolkit.
-- Author: Diego Nehab -- Author: Diego Nehab
-- Conforming to: RFC 2616, LTN7
-- RCS ID: $Id$ -- RCS ID: $Id$
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Load other required modules -- Load required modules
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
local socket = require("socket") local socket = require("socket")
local ltn12 = require("ltn12")
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Setup namespace -- Setup namespace
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
tp = {} _LOADED["tp"] = getfenv(1)
setmetatable(tp, { __index = _G })
setfenv(1, tp)
-----------------------------------------------------------------------------
-- Program constants
-----------------------------------------------------------------------------
TIMEOUT = 60 TIMEOUT = 60
-----------------------------------------------------------------------------
-- Implementation
-----------------------------------------------------------------------------
-- gets server reply (works for SMTP and FTP) -- gets server reply (works for SMTP and FTP)
local function get_reply(control) local function get_reply(control)
local code, current, sep local code, current, sep
@ -37,7 +41,6 @@ local function get_reply(control)
-- reply ends with same code -- reply ends with same code
until code == current and sep == " " until code == current and sep == " "
end end
print(reply)
return code, reply return code, reply
end end
@ -46,6 +49,7 @@ local metat = { __index = {} }
function metat.__index:check(ok) function metat.__index:check(ok)
local code, reply = get_reply(self.control) local code, reply = get_reply(self.control)
print(reply)
if not code then return nil, reply end if not code then return nil, reply end
if type(ok) ~= "function" then if type(ok) ~= "function" then
if type(ok) == "table" then if type(ok) == "table" then
@ -103,11 +107,11 @@ function metat.__index:close()
end end
-- connect with server and return control object -- connect with server and return control object
function connect(host, port) connect = socket.protect(function(host, port, timeout)
local control, err = socket.connect(host, port) local control = socket.try(socket.tcp())
if not control then return nil, err end socket.try(control:settimeout(timeout or TIMEOUT))
control:settimeout(TIMEOUT) socket.try(control:connect(host, port))
return setmetatable({control = control}, metat) return setmetatable({control = control}, metat)
end end)
return tp return tp

View File

@ -9,13 +9,10 @@
#include <lua.h> #include <lua.h>
#include <lauxlib.h> #include <lauxlib.h>
#include "luasocket.h"
#include "auxiliar.h" #include "auxiliar.h"
#include "socket.h" #include "socket.h"
#include "inet.h" #include "inet.h"
#include "options.h" #include "options.h"
#include "base.h"
#include "udp.h" #include "udp.h"
/*=========================================================================*\ /*=========================================================================*\
@ -51,7 +48,7 @@ static luaL_reg udp[] = {
{"close", meth_close}, {"close", meth_close},
{"setoption", meth_setoption}, {"setoption", meth_setoption},
{"__gc", meth_close}, {"__gc", meth_close},
{"__tostring", base_meth_tostring}, {"__tostring", aux_tostring},
{"getfd", meth_getfd}, {"getfd", meth_getfd},
{"setfd", meth_setfd}, {"setfd", meth_setfd},
{"dirty", meth_dirty}, {"dirty", meth_dirty},

View File

@ -9,9 +9,7 @@
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Setup namespace -- Setup namespace
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
local url = {} _LOADED["url"] = getfenv(1)
setmetatable(url, { __index = _G })
setfenv(1, url)
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Encodes a string into its escaped hexadecimal representation -- Encodes a string into its escaped hexadecimal representation

View File

@ -4,14 +4,18 @@
-- to "/luasocket-test-cgi" and "/luasocket-test-cgi/" -- to "/luasocket-test-cgi" and "/luasocket-test-cgi/"
-- needs "AllowOverride AuthConfig" on /home/c/diego/tec/luasocket/test/auth -- needs "AllowOverride AuthConfig" on /home/c/diego/tec/luasocket/test/auth
require("http") socket = require("socket")
http = require("http")
mime = require("mime")
url = require("url")
ltn12 = require("ltn12")
dofile("testsupport.lua") dofile("testsupport.lua")
local host, proxy, request, response, index_file local host, proxy, request, response, index_file
local ignore, expect, index, prefix, cgiprefix, index_crlf local ignore, expect, index, prefix, cgiprefix, index_crlf
socket.http.TIMEOUT = 10 http.TIMEOUT = 10
local t = socket.time() local t = socket.time()
@ -56,7 +60,7 @@ local check_request = function(request, expect, ignore)
end end
request.source = request.source or request.source = request.source or
(request.body and ltn12.source.string(request.body)) (request.body and ltn12.source.string(request.body))
local response = socket.http.request(request) local response = http.request(request)
if t and table.getn(t) > 0 then response.body = table.concat(t) end if t and table.getn(t) > 0 then response.body = table.concat(t) end
check_result(response, expect, ignore) check_result(response, expect, ignore)
end end
@ -64,16 +68,16 @@ end
------------------------------------------------------------------------ ------------------------------------------------------------------------
io.write("testing request uri correctness: ") io.write("testing request uri correctness: ")
local forth = cgiprefix .. "/request-uri?" .. "this+is+the+query+string" local forth = cgiprefix .. "/request-uri?" .. "this+is+the+query+string"
local back, h, c, e = socket.http.get("http://" .. host .. forth) local back, h, c, e = http.get("http://" .. host .. forth)
if not back then fail(e) end if not back then fail(e) end
back = socket.url.parse(back) back = url.parse(back)
if similar(back.query, "this+is+the+query+string") then print("ok") if similar(back.query, "this+is+the+query+string") then print("ok")
else fail(back.query) end else fail(back.query) end
------------------------------------------------------------------------ ------------------------------------------------------------------------
io.write("testing query string correctness: ") io.write("testing query string correctness: ")
forth = "this+is+the+query+string" forth = "this+is+the+query+string"
back = socket.http.get("http://" .. host .. cgiprefix .. back = http.get("http://" .. host .. cgiprefix ..
"/query-string?" .. forth) "/query-string?" .. forth)
if similar(back, forth) then print("ok") if similar(back, forth) then print("ok")
else fail("failed!") end else fail("failed!") end
@ -149,7 +153,7 @@ check_request(request, expect, ignore)
------------------------------------------------------------------------ ------------------------------------------------------------------------
io.write("testing simple post function: ") io.write("testing simple post function: ")
back = socket.http.post("http://" .. host .. cgiprefix .. "/cat", index) back = http.post("http://" .. host .. cgiprefix .. "/cat", index)
assert(back == index) assert(back == index)
------------------------------------------------------------------------ ------------------------------------------------------------------------
@ -277,30 +281,6 @@ ignore = {
} }
check_request(request, expect, ignore) check_request(request, expect, ignore)
------------------------------------------------------------------------
io.write("testing host not found: ")
request = {
url = "http://wronghost/does/not/exist"
}
local c, e = socket.connect("wronghost", 80)
expect = {
error = e
}
ignore = {}
check_request(request, expect, ignore)
------------------------------------------------------------------------
io.write("testing invalid url: ")
request = {
url = host .. prefix
}
local c, e = socket.connect("", 80)
expect = {
error = e
}
ignore = {}
check_request(request, expect, ignore)
------------------------------------------------------------------------ ------------------------------------------------------------------------
io.write("testing document not found: ") io.write("testing document not found: ")
request = { request = {
@ -396,37 +376,43 @@ ignore = {
} }
check_request(request, expect, ignore) check_request(request, expect, ignore)
------------------------------------------------------------------------
io.write("testing wrong scheme: ")
request = {
url = "wrong://" .. host .. cgiprefix .. "/cat",
method = "GET"
}
expect = {
error = "unknown scheme 'wrong'"
}
ignore = {
}
check_request(request, expect, ignore)
------------------------------------------------------------------------ ------------------------------------------------------------------------
local body local body
io.write("testing simple get function: ") io.write("testing simple get function: ")
body = socket.http.get("http://" .. host .. prefix .. "/index.html") body = http.get("http://" .. host .. prefix .. "/index.html")
assert(body == index) assert(body == index)
print("ok") print("ok")
------------------------------------------------------------------------ ------------------------------------------------------------------------
io.write("testing HEAD method: ") io.write("testing HEAD method: ")
socket.http.TIMEOUT = 1 http.TIMEOUT = 1
response = socket.http.request { response = http.request {
method = "HEAD", method = "HEAD",
url = "http://www.cs.princeton.edu/~diego/" url = "http://www.cs.princeton.edu/~diego/"
} }
assert(response and response.headers) assert(response and response.headers)
print("ok") print("ok")
------------------------------------------------------------------------
io.write("testing host not found: ")
local c, e = socket.connect("wronghost", 80)
local r, re = http.request{url = "http://wronghost/does/not/exist"}
assert(r == nil and e == re)
r, re = http.get("http://wronghost/does/not/exist")
assert(r == nil and e == re)
print("ok")
------------------------------------------------------------------------
io.write("testing invalid url: ")
local c, e = socket.connect("", 80)
local r, re = http.request{url = host .. prefix}
assert(r == nil and e == re)
r, re = http.get(host .. prefix)
assert(r == nil and e == re)
print("ok")
------------------------------------------------------------------------ ------------------------------------------------------------------------
print("passed all tests") print("passed all tests")
os.remove("err")
print(string.format("done in %.2fs", socket.time() - t)) print(string.format("done in %.2fs", socket.time() - t))

View File

@ -69,7 +69,7 @@ function check_timeout(tm, sl, elapsed, err, opp, mode, alldone)
end end
end end
if not socket.debug then if not socket.DEBUG then
fail("Please define LUASOCKET_DEBUG and recompile LuaSocket") fail("Please define LUASOCKET_DEBUG and recompile LuaSocket")
end end

View File

@ -1,52 +1,57 @@
require("smtp") -- load the smtp support and its friends
require("mime") local smtp = require("smtp")
local mime = require("mime")
local ltn12 = require("ltn12")
mesgt = { -- creates a source to send a message with two parts. The first part is
-- plain text, the second part is a PNG image, encoded as base64.
source = smtp.message{
headers = { headers = {
to = "D Burgess <db@werx4.com>", -- Remember that headers are *ignored* by smtp.send.
subject = "Looking good! (please check headers)" from = "Sicrano <sicrano@tecgraf.puc-rio.br>",
to = "Fulano <fulano@tecgraf.puc-rio.br>",
subject = "Here is a message with attachments"
}, },
body = { body = {
preamble = "Some attatched stuff", preamble = "If your client doesn't understand attachments, \r\n" ..
"it will still display the preamble and the epilogue.\r\n",
"Preamble might show up even in a MIME enabled client.",
-- first part: No headers means plain text, us-ascii.
-- The mime.eol low-level filter normalizes end-of-line markers.
[1] = { [1] = {
body = mime.eol(0, "Testing stuffing.\n.\nGot you.\n.Hehehe.\n") body = mime.eol(0, [[
Lines in a message body should always end with CRLF.
The smtp module will *NOT* perform translation. It will
perform necessary stuffing, though.
]])
}, },
-- second part: Headers describe content the to be an image,
-- sent under the base64 transfer content encoding.
-- Notice that nothing happens until the message is sent. Small
-- chunks are loaded into memory and translation happens on the fly.
[2] = { [2] = {
headers = { headers = {
["content-type"] = 'application/octet-stream; name="testmesg.lua"', ["content-type"] = 'image/png; name="image.png"',
["content-disposition"] = 'attachment; filename="testmesg.lua"', ["content-disposition"] = 'attachment; filename="image.png"',
["content-description"] = 'a beautiful image',
["content-transfer-encoding"] = "BASE64" ["content-transfer-encoding"] = "BASE64"
}, },
body = ltn12.source.chain( body = ltn12.source.chain(
ltn12.source.file(io.open("testmesg.lua", "rb")), ltn12.source.file(io.open("image.png", "rb")),
ltn12.filter.chain( ltn12.filter.chain(
mime.encode("base64"), mime.encode("base64"),
mime.wrap() mime.wrap()
) )
) )
}, },
[3] = { epilogue = "This might also show up, but after the attachments"
headers = {
["content-type"] = 'text/plain; name="testmesg.lua"',
["content-disposition"] = 'attachment; filename="testmesg.lua"',
["content-transfer-encoding"] = "QUOTED-PRINTABLE"
},
body = ltn12.source.chain(
ltn12.source.file(io.open("testmesg.lua", "rb")),
ltn12.filter.chain(
mime.normalize(),
mime.encode("quoted-printable"),
mime.wrap("quoted-printable")
)
)
},
epilogue = "Done attaching stuff",
} }
} }
print(socket.smtp.send { -- finally send it
r, e = smtp.send{
rcpt = "<diego@cs.princeton.edu>", rcpt = "<diego@cs.princeton.edu>",
from = "<diego@cs.princeton.edu>", from = "<diego@cs.princeton.edu>",
source = socket.smtp.message(mesgt), source = source,
server = "mail.cs.princeton.edu" server = "mail.cs.princeton.edu"
}) }

View File

@ -1,4 +1,5 @@
require"url" socket = require("socket")
socket.url = require("url")
dofile("testsupport.lua") dofile("testsupport.lua")
local check_build_url = function(parsed) local check_build_url = function(parsed)