527 lines
16 KiB
HTML
527 lines
16 KiB
HTML
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
|
<head>
|
|
<title>Copas - Coroutine Oriented Portable Asynchronous Services for Lua</title>
|
|
<link rel="stylesheet" href="doc.css" type="text/css"/>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
|
</head>
|
|
<body>
|
|
<div id="container">
|
|
|
|
<div id="product">
|
|
<div id="product_logo"><a href="http://www.keplerproject.org">
|
|
<img alt="Copas logo" src="copas.png"/>
|
|
</a></div>
|
|
<div id="product_name"><big><strong>Copas</strong></big></div>
|
|
<div id="product_description">Coroutine Oriented Portable Asynchronous Services for Lua</div>
|
|
</div> <!-- id="product" -->
|
|
|
|
|
|
<div id="main">
|
|
|
|
<div id="navigation">
|
|
<h1>Copas</h1>
|
|
<ul>
|
|
<li><a href="index.html">Home</a>
|
|
<ul>
|
|
<li><a href="index.html#status">Status</a></li>
|
|
<li><a href="index.html#download">Download</a></li>
|
|
<li><a href="index.html#dependencies">Dependencies</a></li>
|
|
<li><a href="index.html#history">History</a></li>
|
|
<li><a href="index.html#credits">Credits</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><strong>Manual</strong></li>
|
|
<li><a href="reference.html">Reference</a></li>
|
|
<li><a href="http://github.com/lunarmodules/copas/">Project</a>
|
|
<ul>
|
|
<li><a href="http://github.com/lunarmodules/copas/issues">Bug Tracker</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="license.html">License</a></li>
|
|
</ul>
|
|
</div> <!-- id="navigation" -->
|
|
|
|
<div id="content">
|
|
|
|
<h2><a name="install"></a>Installing</h2>
|
|
|
|
<p>You can install Copas using <a href="http://www.luarocks.org">LuaRocks</a>:</p>
|
|
|
|
<pre class="example">
|
|
luarocks install copas
|
|
</pre>
|
|
<p>Note: LuaSec is not automatically installed as a dependency. If you want to use ssl with Copas,
|
|
you need to manually install LuaSec as well.</p>
|
|
|
|
<h2><a name="cli"></a>Runtime</h2>
|
|
|
|
Copas can either be used as a regular Lua library, or as a runtime. A command line script that
|
|
acts as a runtime engine is included.
|
|
|
|
When using the runtime, the library is available as a global (<code>copas</code>), and the
|
|
scheduler will automatically be started. For example:
|
|
|
|
<pre class="example">
|
|
#!/usr/bin/env copas
|
|
|
|
local count = 0
|
|
copas.timer.new {
|
|
delay = 1,
|
|
recurring = true,
|
|
callback = function(self)
|
|
count = count + 1
|
|
print('hello world ' .. count)
|
|
if count >= 5 then
|
|
self:cancel()
|
|
end
|
|
end
|
|
}
|
|
</pre>
|
|
|
|
<h2><a name="introduction"></a>Introduction to Copas</h2>
|
|
|
|
<p>
|
|
Copas is a dispatcher that can help a lot in the creation of servers based on
|
|
<a href="http://www.cs.princeton.edu/~diego/professional/luasocket/">LuaSocket</a>.
|
|
Here we present a quick introduction to Copas and how to implement a server with it.
|
|
</p>
|
|
|
|
<p>
|
|
Assuming you know how to implement the desired server protocol, the first thing you have
|
|
to do in order to create a Copas based server is create a server socket to receive the
|
|
client connections. To do this you have to bind a host and a port using LuaSocket:
|
|
</p>
|
|
|
|
<pre class="example">
|
|
server = socket.bind(host, port)
|
|
</pre>
|
|
|
|
<p>Then you have to create a handler function that implements the server protocol.
|
|
The handler function will be called with a socket for each client connection
|
|
and you can use <code>copas.send()</code> and <code>copas.receive()</code> on that socket to
|
|
exchange data with the client.</p>
|
|
|
|
<p>For example, a simple echo handler would be:</p>
|
|
|
|
<pre class="example">
|
|
function echoHandler(skt)
|
|
while true do
|
|
local data = copas.receive(skt)
|
|
if data == "quit" then
|
|
break
|
|
end
|
|
copas.send(skt, data)
|
|
end
|
|
end
|
|
</pre>
|
|
|
|
<p>You may alternatively use <code>copas.wrap()</code> to let your code more close to a standard
|
|
LuaSocket use:</p>
|
|
|
|
<pre class="example">
|
|
function echoHandler(skt)
|
|
skt = copas.wrap(skt)
|
|
while true do
|
|
local data = skt:receive()
|
|
if data == "quit" then
|
|
break
|
|
end
|
|
skt:send(data)
|
|
end
|
|
end
|
|
</pre>
|
|
|
|
<p>
|
|
To register the server socket with Copas and associate it with the corresponding
|
|
handler we do:
|
|
</p>
|
|
|
|
<pre class="example">
|
|
copas.addserver(server, echoHandler)
|
|
</pre>
|
|
|
|
<p>Finally, to start Copas and all the registered servers we just call:</p>
|
|
|
|
<pre class="example">
|
|
copas()
|
|
</pre>
|
|
|
|
<p>As long as every handler uses Copas's <code>send</code> and <code>receive</code>,
|
|
simultaneous connections will be handled transparently by Copas for every registered
|
|
server.</p>
|
|
|
|
<p>
|
|
Since Copas is coroutine based, using it within a Lua <code>pcall</code> or
|
|
<code>xpcall</code> context does not work with Lua 5.1 yielding. If you need to use
|
|
any of those functions in your handler we strongly suggest using
|
|
<a href="http://keplerproject.github.com/coxpcall/">coxpcall</a>, a coroutine safe
|
|
version of the Lua 5.1 protected calls. For an example of this usage please check Xavante.
|
|
</p>
|
|
|
|
<h2><a name="why"></a>Why use Copas?</h2>
|
|
|
|
<p>
|
|
For those who already have a server implemented, here is an explanation of why and
|
|
how to migrate to Copas. In a typical LuaSocket server usually there is a dispatcher
|
|
loop like the one below:
|
|
</p>
|
|
|
|
<pre class="example">
|
|
server = socket.bind(host, port)
|
|
while true do
|
|
skt = server:accept()
|
|
handle(skt)
|
|
end
|
|
</pre>
|
|
|
|
<p>Here <code>handle</code> is a function that implements the server protocol using LuaSocket's
|
|
socket functions:</p>
|
|
|
|
<pre class="example">
|
|
function handle(skt)
|
|
...
|
|
-- gets some data from the client - "the request"
|
|
reqdata = skt:receive(pattern)
|
|
...
|
|
-- sends some data to the client - "the response"
|
|
skt:send(respdata)
|
|
...
|
|
end
|
|
</pre>
|
|
|
|
<p>
|
|
The problem with that approach is that the dispatcher loop is doing a busy wait
|
|
and can handle just one connection at a time. To solve the busy waiting we can
|
|
use LuaSocket's <code>socket.select()</code>, like in:</p>
|
|
|
|
<pre class="example">
|
|
server = socket.bind(host, port)
|
|
reading = {server}
|
|
while true do
|
|
input = socket.select(reading)
|
|
skt = input:accept()
|
|
handle(skt)
|
|
end
|
|
</pre>
|
|
|
|
<p>
|
|
While this helps our CPU usage, the server is still accepting only one client
|
|
connection at a time. To handle more than one client the server must be able to
|
|
multitask, and the solution usually involves some kind of threads.</p>
|
|
<p>The dispatcher loop then becomes something like:</p>
|
|
|
|
<pre class="example">
|
|
server = socket.bind(host, port)
|
|
reading = {server}
|
|
while true do
|
|
input = socket.select(reading)
|
|
skt = input:accept()
|
|
newthread(handle(skt))
|
|
end
|
|
</pre>
|
|
|
|
<p>
|
|
where <code>newthread</code> is able to create a new thread that executes
|
|
independently the handler function.</p>
|
|
|
|
<p>
|
|
The use of threads in the new loop solves the multitasking problem but may
|
|
create another. Some platforms does not offer multithreading or maybe you
|
|
don't want to use threads at all.
|
|
</p>
|
|
|
|
<p>
|
|
If that is the case, using Lua's coroutines may help a lot, and that's
|
|
exactly what Copas does. Copas implements the dispatcher loop using coroutines
|
|
so the handlers can multitask without the use of threads.</p>
|
|
|
|
<h2><a name="using"></a>Using Copas with an existing server</h2>
|
|
|
|
<p>
|
|
If you already have a running server using some dispatcher like the previous
|
|
example, migrating to Copas is quite simple, usually consisting of just three
|
|
steps.
|
|
</p>
|
|
|
|
<p>
|
|
First each server socket and its corresponding handler function have to be registered
|
|
with Copas:</p>
|
|
|
|
<pre class="example">
|
|
server = socket.bind(host, port)
|
|
copas.addserver(server, handle)
|
|
</pre>
|
|
|
|
<p>Secondly the server handler has to be adapted to use Copas. One solution
|
|
is to use Copas <code>send</code> and <code>receive</code> functions to receive
|
|
and send data to the client:</p>
|
|
|
|
<pre class="example">
|
|
function handle(skt)
|
|
...
|
|
-- gets some data from the client - "the request"
|
|
reqdata = copas.receive(skt, pattern)
|
|
...
|
|
-- sends some data to the client - "the response"
|
|
copas.send(skt, respdata)
|
|
...
|
|
end
|
|
</pre>
|
|
|
|
<p>The other alternative is to wrap the socket in a Copas socket. This
|
|
allows your handler code to remain basically the same:</p>
|
|
|
|
<pre class="example">
|
|
function handle(skt)
|
|
-- this line may suffice for your handler to work with Copas
|
|
skt = copas.wrap(skt) -- or... skip this line and wrap `handle` using copas.handler()
|
|
-- now skt behaves like a LuaSocket socket but uses Copas'
|
|
...
|
|
-- gets some data from the client - "the request"
|
|
reqdata = skt:receive(pattern)
|
|
...
|
|
-- sends some data to the client - "the response"
|
|
skt:send(respdata)
|
|
...
|
|
end
|
|
</pre>
|
|
|
|
<p>Note that by default Copas might return different timeout errors than the
|
|
traditional Lua libraries. Checkout <code>copas.useSocketTimeoutErrors()</code>
|
|
for more information.</p>
|
|
|
|
<p>Finally, to run the dispatcher loop you just call:</p>
|
|
|
|
<pre class="example">
|
|
copas()
|
|
</pre>
|
|
|
|
<p>During the loop Copas' dispatcher accepts connections from clients and
|
|
automatically calls the corresponding handler functions.</p>
|
|
|
|
<h2><a name="udp"></a>Using UDP servers</h2>
|
|
<p>Copas may also be used for UDP servers. Here is an example;</p>
|
|
<pre class="example">
|
|
local port = 51034
|
|
local server = socket.udp()
|
|
server:setsockname("*",port)
|
|
|
|
function handler(skt)
|
|
skt = copas.wrap(skt)
|
|
print("UDP connection handler")
|
|
|
|
while true do
|
|
local s, err
|
|
print("receiving...")
|
|
s, err = skt:receive(2048)
|
|
if not s then
|
|
print("Receive error: ", err)
|
|
return
|
|
end
|
|
print("Received data, bytes:" , #s)
|
|
end
|
|
end
|
|
|
|
copas.addserver(server, handler, 1)
|
|
copas()
|
|
</pre>
|
|
<p>For UDP sockets the <code>receivefrom()</code> and <code>sendto()</code>
|
|
methods are available, both for copas and when the socket is wrapped. These
|
|
methods cannot be used on TCP sockets.</p>
|
|
<p><strong>IMPORTANT:</strong> UDP sockets do not have the notion of master and client sockets, so where a handler function can close the client socket for a TCP connection, a handler should never close a UDP socket, because the socket is the same as the server socket, hence closing it destroys the server.</p>
|
|
<p><strong>NOTE:</strong> When using the <code>copas.receive([size])</code> method
|
|
on a UDP socket, the <code>size</code> parameter is NOT optional as with regular
|
|
luasocket UDP sockets. This limitation is removed when the socket is wrapped
|
|
(it then defaults to 8192, the max UDP datagram size luasocket supports).</p>
|
|
|
|
<h2><a name="tasks"></a>Adding tasks</h2>
|
|
<p>Additional threads may be added to the scheduler, as long as they use the Copas <code>send</code>, <code>receive</code> or <code>sleep</code> methods. Below an example of a thread being added to create an outgoing TCP connection using Copas;</p>
|
|
<pre class="example">
|
|
local socket = require("socket")
|
|
local copas = require("copas")
|
|
|
|
local host = "127.0.0.1"
|
|
local port = 10000
|
|
|
|
local skt = socket.connect(host, port)
|
|
skt:settimeout(0) -- important: make it non-blocking
|
|
|
|
copas.addthread(function()
|
|
while true do
|
|
print("receiving...")
|
|
local resp = copas.receive(skt, 6)
|
|
print("received:", resp or "nil")
|
|
if resp and resp:sub(1,4) == "quit" then
|
|
skt:close()
|
|
break
|
|
end
|
|
end
|
|
end)
|
|
|
|
copas()
|
|
</pre>
|
|
<p>The example connects, echoes whatever it receives and exits upon receiving 'quit'. For an
|
|
example passing arguments to a task, see the async http example below.</p>
|
|
|
|
<h2><a name="timers"></a>Creating timers</h2>
|
|
<p>Timers can be created using the <code>copas.timer</code> module.
|
|
Below an example of a timer;</p>
|
|
<pre class="example">
|
|
local copas = require("copas")
|
|
|
|
copas(function()
|
|
copas.timer.new({
|
|
delay = 1, -- delay in seconds
|
|
recurring = true, -- make the timer repeat
|
|
params = "hello world",
|
|
callback = function(timer_obj, params)
|
|
print(params) -- prints "hello world"
|
|
timer_obj:cancel() -- cancel the timer after 1 occurence
|
|
end
|
|
})
|
|
end)
|
|
</pre>
|
|
<p>The example simply prints a message once every second, but gets cancelled right after the first one.</p>
|
|
|
|
<h2><a name="synchronization"></a>Synchronization primitives</h2>
|
|
<p>Since Copas allows to asynchroneously schedule tasks, synchronization might be required
|
|
to protect resources from concurrent access. In this case the <code>copas.lock</code> and
|
|
<code>copas.semaphore</code> classes
|
|
can be used. The lock/semaphore will ensure that the coroutine running will be yielded until
|
|
the protected resource becomes available, without blocking other threads.
|
|
</p>
|
|
<pre class="example">
|
|
local copas = require("copas")
|
|
|
|
local lock = copas.lock.new()
|
|
|
|
local function some_func()
|
|
local ok, err, wait = lock:get()
|
|
if not ok then
|
|
return nil, "we got error '" .. err .. "' after " .. wait .. " seconds"
|
|
end
|
|
|
|
print("doing something on my own")
|
|
copas.pause() -- allow to yield, while inside the lock
|
|
print("after " .. ok .. " seconds waiting")
|
|
|
|
lock:release()
|
|
end
|
|
</pre>
|
|
<p>The <code>some_func</code> function may now be called and the 2 lines will
|
|
be printed together because of the lock.</p>
|
|
|
|
<h2><a name="ssl"></a>Ssl support</h2>
|
|
<p>LuaSec is transparently integrated in the Copas scheduler (though must be installed separately when using LuaRocks).</p>
|
|
<p>
|
|
Here's an example for an incoming connection in a server scenario;
|
|
</p>
|
|
<pre class="example">
|
|
function handler(skt)
|
|
skt = copas.wrap(skt):dohandshake(sslparams)
|
|
-- skt = copas.wrap(skt, sslparams):dohandshake() -- would be identical
|
|
|
|
while true do
|
|
-- perform the regular reading/writing ops on skt
|
|
end
|
|
end
|
|
</pre>
|
|
<p>
|
|
A simpler handler would wrap the handler function to do the wrapping and
|
|
handshake before the handler gets called;
|
|
</p>
|
|
<pre class="example">
|
|
function handle(skt)
|
|
-- by now `skt` is copas wrapped, and has its handshake already completed
|
|
|
|
while true do
|
|
-- perform the regular reading/writing ops on skt
|
|
end
|
|
end
|
|
handle = copas.handler(handle, sslparams) -- wraps the handler to auto wrap and handshake
|
|
</pre>
|
|
<p>
|
|
Here's an example for an outgoing request;
|
|
</p>
|
|
<pre class="example">
|
|
copas.addthread(function()
|
|
local skt = copas.wrap(socket.tcp(), sslparams)
|
|
skt:connect(host, port) -- connecting will also perform the handshake on a wrapped socket
|
|
|
|
while true do
|
|
|
|
-- perform the regular reading/writing ops on skt
|
|
|
|
end
|
|
end
|
|
</pre>
|
|
|
|
<h2><a name="highlevel"></a>High level requests</h2>
|
|
|
|
<p>
|
|
For creating high level requests; http(s), ftp or smtp versions of the
|
|
methods are available that handle them async. As opposed to the original
|
|
LuaSocket and LuaSec implementations.
|
|
</p>
|
|
<p>
|
|
Below an example that schedules a number of http requests, then starts the Copas
|
|
loop to execute them. The loop exits when it's done.
|
|
</p>
|
|
|
|
<pre class="example">
|
|
local copas = require("copas")
|
|
local asynchttp = require("copas.http").request
|
|
|
|
local list = {
|
|
"http://www.google.com",
|
|
"http://www.microsoft.com",
|
|
"http://www.apple.com",
|
|
"http://www.facebook.com",
|
|
"http://www.yahoo.com",
|
|
}
|
|
|
|
local handler = function(host)
|
|
res, err = asynchttp(host)
|
|
print("Host done: "..host)
|
|
end
|
|
|
|
for _, host in ipairs(list) do copas.addthread(handler, host) end
|
|
copas()
|
|
</pre>
|
|
|
|
<h2><a name="control"></a>Controlling Copas</h2>
|
|
|
|
<p>
|
|
If you do not want copas to simply enter an infinite loop (maybe you have to
|
|
respond to events from other sources, such as an user interface), you should
|
|
have your own loop and just call <code>copas.step()</code> at each iteration of
|
|
the loop:
|
|
</p>
|
|
|
|
<pre class="example">
|
|
while condition do
|
|
copas.step()
|
|
-- processing for other events from your system here
|
|
end
|
|
</pre>
|
|
<p>
|
|
When using your own main loop, you should consider manually setting the
|
|
<code>copas.running</code> flag.
|
|
</p>
|
|
|
|
|
|
|
|
</div> <!-- id="content" -->
|
|
|
|
</div> <!-- id="main" -->
|
|
|
|
<div id="about">
|
|
<p><a href="http://validator.w3.org/check?uri=referer">Valid XHTML 1.0!</a></p>
|
|
<p><small>$Id: manual.html,v 1.19 2009/03/24 22:04:26 carregal Exp $</small></p>
|
|
</div> <!-- id="about" -->
|
|
|
|
</div> <!-- id="container" -->
|
|
</body>
|
|
</html>
|