=head1 NAME
copas - dispatcher based on coroutines for TCP/IP servers (Copas)
=head1 OVERVIEW
Copas is a dispatcher based on coroutines that can be used by TCP/IP
servers. It uses LuaSocket
(L) as the
interface with the TCP/IP stack.
A server registered with Copas should provide a handler for requests
and use Copas socket functions to send the response. Copas loops
through requests and invokes the corresponding handlers. For a full
implementation of a Copas HTTP server you can refer to Xavante
(L) as an example.
=head1 DOWNLOAD
Copas can be downloaded from its LuaForge
(L) page.
CVS access is also available from LuaForge.
=head1 DEPENDENCIES
Copas 1.1 depends only on LuaSocket 2.0
(L) and
Compat-5.1 Release 5 (L) (if you
are using Lua 5.0).
=head1 INSTALLING
Copas follows the package model
(L) for Lua
5.1, therefore it should be "installed". Refer to Compat-5.1
configuration
(L)
section about how to install the modules properly in a Lua 5.0
environment.
If you are using Kepler Copas is already installed by it, but you may
want to upgrade manually your Copas version. Be aware that this may
cause problems with other Kepler modules such as Xavante that depends
on Copas.
=head1 INTRODUCTION
Copas is a dispatcher that can help a lot in the creation of servers
based on LuaSocket
(L). Here
we present a quick introduction to Copas and how to implement a server
with it.
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:
server = socket.bind(host, port)
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 C and
C on that socket to exchange data with the client.
For example, a simple echo handler would be:
function echoHandler(skt)
while true do
local data = copas.receive(skt)
if data == "quit" then
break
end
copas.send(skt, data)
end
end
If all you will do with the socket is send and receive data, you may
alternatively use C to let your code more close to a
standard LuaSocket use:
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
To register the server socket with Copas and associate it with the
corresponding handler we do:
copas.addserver(server, echoHandler)
Finally, to start Copas and all the registered servers we just call:
copas.loop()
As long as every handler uses Copas's C and C,
simultaneous connections will be handled transparently by Copas for
every registered server.
Since Copas is coroutine based, using it within a Lua C or
C context does not work with Lua 5 yielding. If you need to
use any of those functions in your handler we strongly suggest using
Xavante's coxpcall (L), a
coroutine safe version of the Lua 5 protected calls. For an example of
this usage please check Xavante.
=head2 Why use Copas?
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:
server = socket.bind(host, port)
while true
skt = server:accept()
handle(skt)
end
Here C is a function that implements the server protocol using
LuaSocket's socket functions:
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
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 C, like in:
server = socket.bind(host, port)
reading = {server}
while true
input = socket.select(reading)
skt = input:accept()
handle(skt)
end
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.
The dispatcher loop then becomes something like:
server = socket.bind(host, port)
reading = {server}
while true
input = socket.select(reading)
skt = input:accept()
newthread(handle(skt))
end
where C is able to create a new thread that executes
independently the handler function.
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.
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.
=head2 Using Copas with an existing server
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.
First each server socket and its corresponding handler function have
to be registered with Copas:
server = socket.bind(host, port)
copas.addserver(server, handle)
Secondly the server handler has to be adapted to use Copas. One
solution is to use Copas C and C
functions to receive and send data to the client:
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
The other alternative is to wrap the socket in a Copas socket. This
allows your handler code to remain basically the same:
function handle(skt)
-- this line may suffice for your handler to work with Copas
skt = copas.wrap(skt)
-- 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
Finally, to run the dispatcher infinite loop you just call:
copas.loop()
During the loop Copas' dispatcher accepts connections from clients and
automatically calls the corresponding handler functions.
=head2 Controlling Copas
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
C at each iteration of the loop:
while condition do
copas.step()
-- processing for other events from your system here
end
=head1 REFERENCE
Copas functions are separated in two groups.
The first group is relative to the use of the dispatcher itself and
are used to register servers and to execute the main loop of Copas:
=head2 C
copas.addserver(server, handler[, timeout])
Adds a new C and its C to the dispatcher using an
optional C.
=over 4
=item C
is a LuaSocket server socket created using C.
=item C
is a function that receives a LuaSocket client socket and handles the
communication with that client.
=item C
is the timeout for blocking I/O in seconds.
=back
The handler will be executed in parallel with other threads and the
registered handlers as long as it uses the Copas socket functions.
=head2 C
copas.addthread(thrd[, ...])
Adds a new thread to the dispatcher using optional parameters.
The thread will be executed in parallel with other threads and the
registered handlers as long as it uses the Copas socket functions.
=head2 C
copas.loop(timeout)
Starts the Copas infinite loop accepting client connections for the
registered servers and handling those connections with the
corresponding handlers. Every time a server accepts a connection,
Copas calls the associated handler passing the client socket returned
by C. The C parameter is optional.
=head2 C
copas.step(timeout)
Executes one copas iteration accepting client connections for the
registered servers and handling those connections with the
corresponding handlers. When a server accepts a connection, Copas
calls the associated handler passing the client socket returned by
C. The C parameter is optional.
=head2 Data Exchange Functions
The second group is used by the handler functions to exchange data
with the clients, and by threads registered with C to
exchange data with other services.
=head2 C
copas.flush(skt)
Flushes a client write buffer. C is called from time to
time by C but it may be necessary to call it from the
handler function or one of the threads.
=head2 C
copas.receive(skt, pattern)
Reads data from a client socket according to a pattern just like
LuaSocket C. The Copas version does not block and
allows the multitasking of the other handlers and threads.
=head2 C
copas.send(skt, data)
Sends data to a client socket just like C. The Copas
version is buffered and does not block, allowing the multitasking of
the other handlers and threads.
=head2 C
copas.wrap(skt)
Wraps a LuaSocket socket and returns a Copas socket that implements
LuaSocket's API but use Copas' methods C and
C automatically.
=head1 VERSION
Current version is 1.1. It was developed for Lua 5.0.
=head1 CREDITS
Copas was designed and implemented by André Carregal and Javier
Guerra as part of the Kepler Project (L)
which holds its copyright. Copas development had significative
contributions from Diego Nehab, Mike Pall, David Burgess and Leonardo
Godinho.
=head1 HISTORY
=over 4
=item "1.1" [20/Sep/2006]
C added
=item "1.0" [17/May/2005] (L)
C added
=item "1.0" Beta [17/Feb/2005]
First public version
=back
=head1 CONTACT
For more information please contact us
(info-NO-SPAM-THANKS@keplerproject.org) Comments are welcome!
You can also reach other Kepler developers and users on the Kepler Project
mailing list (L).
=head1 LICENSE
Copas is free software and uses the same license as Lua 5.
Copas is free software: it can be used for both academic and
commercial purposes at absolutely no cost. There are no royalties or
GNU-like "copyleft" restrictions. Copas qualifies as Open Source
(L) software. Its
licenses are compatible with GPL
(L). Copas is not in the public
domain and the Kepler Project (L) keep
its copyright. The legal details are below.
The spirit of the license is that you are free to use Copas for any
purpose at no cost without having to ask us. The only requirement is
that if you do use Copas, then you should give us credit by including
the appropriate copyright notice somewhere in your product or its
documentation.
Copas was designed and implemented by AndrE Carregal and
Javier Guerra. The implementation is not derived from licensed
software.
~~~~~
Copyright E 2006 The Kepler Project.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
~~~~~