=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. ~~~~~