pax_global_header00006660000000000000000000000064121003136130014500gustar00rootroot0000000000000052 comment=b76f6ceba7f21331c30fddd99a483a1b91e5f9fc lua-copas-1.2.0/000077500000000000000000000000001210031361300133645ustar00rootroot00000000000000lua-copas-1.2.0/Makefile000066400000000000000000000004301210031361300150210ustar00rootroot00000000000000# $Id: Makefile,v 1.3 2007/10/29 22:50:16 carregal Exp $ # Default prefix PREFIX = /usr/local # System's lua directory (where Lua libraries are installed) LUA_DIR= $(PREFIX)/share/lua/5.1 install: mkdir -p $(LUA_DIR)/copas cp src/copas/copas.lua $(LUA_DIR)/copas.lua clean: lua-copas-1.2.0/Makefile.win000066400000000000000000000002271210031361300156210ustar00rootroot00000000000000# $Id: Makefile.win,v 1.5 2008/01/16 18:07:17 mascarenhas Exp $ LUA_DIR= c:\lua5.1\lua build clean: install: copy src\copas\copas.lua "$(LUA_DIR)" lua-copas-1.2.0/README000066400000000000000000000030431210031361300142440ustar00rootroot00000000000000Copas 1.2.0 (http://www.keplerproject.org/copas) Copas is a dispatcher based on coroutines that can be used by TCP/IP servers. It uses LuaSocket 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 as an example. Copas is free software and uses the same license as Lua 5.1 Copas can be downloaded from its LuaForge page. You can also install Copas using LuaRocks: luarocks install copas Dependencies Copas depends on LuaSocket 2.1 and Coxpcall 1.14. History Copas 1.2.0 [30/Jan/2013] * Support for Lua 5.2 Copas 1.1.6 [18/Mar/2010] * Now checks to see if socket.http was required before copas Copas 1.1.5 [07/Apr/2009] * Fixed bug reported by Sam Roberts on the Kepler list Copas 1.1.4 [10/Dec/2008] * Fixed bug [#5372] - copas.connect is semi-broken (found by Gary ng) Copas 1.1.3 [19/May/2008] * Using copcall instead of pcall in socket.protect (feature request [#5274] by Gary NG) Copas 1.1.2 [15/May/2008] * Fixed Bug [#4249] - bugs in copas.receive (found by Gary NG) Copas 1.1.1 [13/Aug/2007] * Compatible with Lua 5.1 * Refactored by Thomas Harning Jr. (for more details check Bug 766) * Patch by Gary NG concerning the handling of stopped sockets Copas 1.1 [20/Sep/2006] * copas.addthread() added Copas 1.0 [17/May/2005] * copas.step() added Copas 1.0 Beta[17/Feb/2005] * First public version lua-copas-1.2.0/doc/000077500000000000000000000000001210031361300141315ustar00rootroot00000000000000lua-copas-1.2.0/doc/us/000077500000000000000000000000001210031361300145605ustar00rootroot00000000000000lua-copas-1.2.0/doc/us/copas.png000066400000000000000000000266501210031361300164040ustar00rootroot00000000000000PNG  IHDRL\ pHYsHHFk>-IDATx=ixTEno $@UdQAQdQ\A@ATaI%aIHb!KޏJ_nӝN 9_|Mnݪs:[BswzC?az-#Z,E_B== Ts1M/D@]r??pF4S]cLSWQkf:m-auZL `۷ԩSnWAu7J9B@.;-ջ!D?2!>qҥKN:k ,ڷo_yy9nte˖L&Ƹ!333 W^!!!~g0B@ժTT@CXNG1N)#Akyן?b.]4dȐxبQӵ"1f41FQRftflfG?… D'^R9$h׿.~q^NNEee,S0tGG-Z2cF^O(eƞnJ#G:twsvZC}iii2t `6Zu{߿cbbF7=y#@ p A^yeى2t- 8ԩSvZtiRRcOçMf۳^2 W^}衇O`~jB־K:쬬*PBBM&U t0nkjoׁcŸccvڰpPjBcfYQ & qƍ7NkTѭڷSu0iii#FX,yyyj bsBpyywIR \ rco~sIvԵY^~I$뮻&NǏ6rHAؽ{.._\UU5p@pHU\ic*wȑ-2 S%%%=i{55+$EsWkS~ev-mYt hyRk׮N{MOOOIIA-[c233njsK.@MM͔)SRSS @9wÇG9cƌ޽{qB'txD,+ŬY3ZObPE*999˖-ܿEƌs>쩧۷/99yF1777(((111((ȝ1|G !TPPw޲ٳg D1HQ%Fz6=hddcV|'9@+BEEEnjjZpaDDtƌ:n׮]Æ SE$^.\|E.^h0)0,UpQ8籱k׮=}tEE[ws;!m}o"Խ矟,ɹA=֬noذ۷/T_:ti^z%TQQ|'+FsO^O<8#"ف$ɘZl6m /~ 4͛7766Bl6L&411q̘1W\q"!% lͧ 8{={Z !0c\Q 9X }{7%eÆ eI‚ NNG1tiia !msO666+ӦM&X,[l\tvWcqzJ?q뢢r!^5~g/Q=rtt~7~ᒄ~c_$zgC_-aJ׭zu;yIJtmƄ Q6ƨ#= 8Ƙ1966̙GVW"HeHMBVLJJ*..>vN>|Y.(ܬ<辌ME"v 1)I=[2q[+W~f 瞛 ФulT+ !РR1JKJJ6lؙ3ga:V#FGalts{c@)@~oI”raO>pHY6t`!aJO<1ydJT+DB"J)z_˗$q:䓟F!$B;|ADZ0W_]3Y-g?C-..V9Õ'OV žDs2k֎͛$?JQ.|);qN9c5pH߻1cl's_= SEiʹ"\YQ1Fs}ݣ?>ZhV<SC)P__{]|999yРA$]pʕ+K.2djOΚ+( TLqnz__D)7mlP -$ +Jczz}K{6)$]]̼V\\&fOBm7E~>?~ӧ_M}l1ckff$*J'cߡ&4tɓM5*"22GG)ꫢ>+((GHBHI"b5jD@"d67+#Ȳ`^4cVJEQ@ԐSN,3}e$u;1¹ӟ'&&bD_|/ +f ,3Z{n$Q)HX0vku.#-.x۶ݏ}1c Ӗ- M!UªijW;DN4.Z4UfbDj =!5ʐ!f;9ի͛7Ϟ=;z6#ZhfVQѸrnyG]( #k1"n"e3"$8B0cWgdSڌ1vcG>))SԩS+++ y+RVV(k0˩w͚jsޭ>Ƙ1Ƀx`L'W;"sFvϜ >}1BH!3]~ww#ħS.$o~s:$@Qؘ1KO?=ufٳ,Xt@}  u8\p]gH]İSc[4P/Y2AQȢwƍ&HF׫Zm gdd (?-*vᣢO4iNGTr']wE0F/-:Lf~;C8GwIIOs֯0Rc>δy\@qLYđL7ϯ$ v%&&.\гe!VĺǘHFiHtt1]Є׿fKaBxΝo[<UT_} \(x{R}W&JL`ؾlqq`"^ӧO}`u:B8v ]Q1x`HfsÖ- .\o Y?yJgt=\T TdawI֋H~ǎs"*&.^.'Z8l6SEb>\xb%BZ;Sq;]jj n,JXwjArr,uuum-Tu…7 ~x]vp 9R6c,?u׃DEE=cm6k]If=+1*b2m-Vͣ:u&B.)&I(ѨLٳ} `%9`,UUee#g{ΙAWVVĀ{ZtxP!fKb#dڳ\ 'rhVƽGM YJJ E$`dÆSMm Jyy7-‰;wI=N0AmVXxFG[T(>]BƆ+Wǎ|hɨ9".e_Z c(quу'^]] .yʒ87 p ɽFhpߒ%c>>ѣ%ǎvBz䑱<3.,ɗP #.z≃99jD]ucD|⍱c=O$88MSmh*&՛9c,,f2/=v2!4: 㜿w͙;7yBC ol}wB ėRoĔ^ 9@#d^QR_&?y9) gҀs9"lmۖm ΧO_Ibjklc#8Bx@u򖺂V F/ !XZ4 F)嵎5$͠"7 ӿ^@QP[kuX@*D1؉ejkrN:EN \zx F{&wYn%;!,zϋdX< wq^e?ԌoC(Sq"Nr{,3Ƹ;<&>>>K,۷/؞J3X$PaCP>oh֬g6B*J#6Qh29s AO7o|I 5 Dsq\;!Ε?LQXmm믟tu 8 Z(EQXyyæM5m81_ {0F@Anْ]YycwU=#Bb9sfԩzhGC#zҥ1J||ώ N:gE!CByf=[i) Buʌ?yiR ?]9o ,8E, Xh5)3ٺuS Qaڴ8??CsZ_fSZ,keFsyM4@BBʕ+u:5xC]Qӥ >>.ڿ?W&`|D QƽqM&8[;@t׷o_9=$& Ue3XKbѣha\MM,$&CJmi -UCAAA"ŽA!z0]>̇t8BDx8g+r0 xj=!&cճm5 hW\ Q3QLiX0c\\Cݽalo{D@ khhht$%%9m`+,X]~(,[}|:4tРBCBB|zMÆ]־+V7x BytEL4=Gs.xYUe $ox H]UUURU@ c9 !& @'*8#"?5 2siiCQQ+5GSNX^(eI@8rg'AqqqHHo힯t4k"554--,;ƆU~{^jj_U1cJ6n|xo W˗E4F jBs,BϞ8Lҵp:J3`ƌx_r<#c?y,StHQzm$g$N*O\ұDvS}ɘ% E:wŒt=: zR^nH䋐$Qp!QqLøp`p Tt'?ILL.ł'cJ w9SRB{ݛEV<|&-0%)!r:i߾& l|~[qMYXZjI"pˀ`&N쟖Z|[ޫ.((eك[sbQS}8o9#'BCz}KI"`o;.$GTv.s.IH:ևra*n9ouтԔO>ʕ+ 9IW`w 1/9&f L&Ϯ\T 23L0EjsX'P hϝb78Ę1$bzCzzܶmzL`FmJ0cA, ^whGo]' 00pҥF/-tx1>BgTEa'2%փuS_\tM|/_:vd6ȺuYbc{-]:lXرܔ)հ?tjAAM\\oYG?ܪFϙ8sf 6ш G3^{-b[scJ5kƈS!Va}LI&i B]Zg|6v;5js Zh UО܉[z?y课^S$BM_|ˏr BbΜ9xϞ_,vW/sUףq ӧo}r<.oܰW_b3t:dpN{nv#Gagt#G׌ *:0Ƴfmۻ7WSCMb} $I0!nĘ ܸtL޽Wn`lb d1;8R )S32B', 3f3'eDI*+3NŋI!ƚLIŅTіz/rbby-o "ڵk!݂/^>jFEqiJv Y"sι @MSЫ[\{|"s(BN щ\V5@hDYY۱c… <b]رC\K>fXzfX,r;:s"iE(?zbwDa M16[vt)S.\.F_z̙"rn3bSrE ˮ<>@TS~z]8|J3BCC vd\ Նܼyntt$w`A^̄kG6Cs޶m[~~?ޫW;@6iVVU57m:i61!w:ʹ90&}}Vc%!y;ݱcGnnի]3Z#xb^^lg+XA Mo]^~"3%: "}Z  ,,~xddd.Yo>nܸ8E=L7w}ތI]nZ,X,{nȑ&3$G9ww{J]Em2ٲ;HTiƇ]UryiaN@]6l_d7[+# /̼VZZ nJiձ#va#"Ǝ7cFqQQ-IѭQ\2q[]]݆ f̘5ʽɄP-BTfs򮗖6TW[f,L:7** )ϐ!#FD$%L:q!lȑZuwN4E&ԫ+VBAd[mVU*t:l4JƀCPii0ƪhqj'Nll>9 vwgwu9noBNG対?y-EΚjNJQ?~ի˗/ljڜ̋8PTTԷoQF R"+>sj0XVBNScGة̼rʕ+BzmM[3imphת7oYnI&BT[8''')))""" @EtJ:O>9v옿>h q97^.܁ddpiۋΝ;9tsIMMURss3Xӹ겊644Օgر*NㅨFoGu=(YMMMx7ʄYb`7xCxz}ZZzDK Y+B- vj,)),#Fh~ ݋N 0IhMU|{pAkRͱ!0zl mf-h/%Ҫ{?p[ۇ9=L4 Copas - Coroutine Oriented Portable Asynchronous Services for Lua
Copas
Coroutine Oriented Portable Asynchronous Services for Lua

Overview

Copas is a dispatcher based on coroutines that can be used by TCP/IP servers. It uses LuaSocket 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 as an example.

Copas is free software and uses the same license as Lua 5.1

Status

Current version is 1.2.0 and supports both Lua 5.1 and Lua 5.2.

Download

Copas can be downloaded from its LuaForge page, in the "Downloads" tab.

You can also install Copas using LuaRocks:

luarocks install copas

Dependencies

Copas depends on LuaSocket 2.1 and Coxpcall 1.14.

History

Copas 1.2.0 [30/Jan/2013]
  • Support for Lua 5.2
Copas 1.1.6 [18/Mar/2010]
  • Now checks to see if socket.http was required before copas
Copas 1.1.5 [07/Apr/2009]
  • Fixed bug reported by Sam Roberts on the Kepler list (found due to Xavante locking up on some POST requests)
Copas 1.1.4 [10/Dec/2008]
  • Fixed bug [#5372] - copas.connect is semi-broken (found by Gary NG)
Copas 1.1.3 [19/May/2008]
  • Using copcall instead of pcall in socket.protect (feature request [#5274] by Gary NG)
Copas 1.1.2 [15/May/2008]
  • Fixed Bug [#4249] - bugs in copas.receive (found by Gary NG)
Copas 1.1.1 [13/Aug/2007]
  • Compatible with Lua 5.1
  • Refactored by Thomas Harning Jr. (for more details check Bug 766)
  • Patch by Gary NG concerning the handling of stopped sockets
Copas 1.1 [20/Sep/2006]
Copas 1.0 [17/May/2005]
Copas 1.0 Beta[17/Feb/2005]
  • First public version

Credits

Copas was designed and implemented by André Carregal and Javier Guerra as part of the Kepler Project which holds its copyright. Copas development had significative contributions from Diego Nehab, Mike Pall, David Burgess, Leonardo Godinho, Thomas Harning Jr. and Gary NG.

Contact us

For more information please contact us. Comments are welcome!

You can also reach other Kepler developers and users on the Kepler Project mailing list.

Valid XHTML 1.0!

$Id: index.html,v 1.38 2009/04/07 22:33:34 carregal Exp $

lua-copas-1.2.0/doc/us/license.html000066400000000000000000000110331210031361300170660ustar00rootroot00000000000000 Copas License
Copas
Coroutine Oriented Portable Asynchronous Services for Lua

License

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 software. Its licenses are compatible with GPL. Copas is not in the public domain and the Kepler Project 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 André Carregal and Javier Guerra. The implementation is not derived from licensed software.


Copyright © 2005-2010 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.

Valid XHTML 1.0!

$Id: license.html,v 1.17 2009/03/24 22:04:26 carregal Exp $

lua-copas-1.2.0/doc/us/manual.html000066400000000000000000000256731210031361300167400ustar00rootroot00000000000000 Copas - Coroutine Oriented Portable Asynchronous Services for Lua
Copas
Coroutine Oriented Portable Asynchronous Services for Lua

Installing

You can install Copas using LuaRocks:

luarocks install copas

Introduction to Copas

Copas is a dispatcher that can help a lot in the creation of servers based on LuaSocket. 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 copas.send() and copas.receive() 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 copas.wrap() 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 send and receive, simultaneous connections will be handled transparently by Copas for every registered server.

Since Copas is coroutine based, using it within a Lua pcall or xpcall 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 coxpcall, a coroutine safe version of the Lua 5.1 protected calls. For an example of this usage please check Xavante.

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 do
  skt = server:accept()
  handle(skt)
end

Here handle 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 socket.select(), like in:

server = socket.bind(host, port)
reading = {server}
while true do
  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 do
  input = socket.select(reading)
  skt = input:accept()
  newthread(handle(skt))
end

where newthread 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.

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 send and receive 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.

Using UDP servers

Copas may also be used for UDP servers. Here is an 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.loop()

For UDP sockets the receivefrom() and sendto() methods are available, both for copas and when the socket is wrapped. These methods cannot be used on TCP sockets.

NOTE: When using the copas.receive([size]) method on a UDP socket, the size 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).

Adding threads

Additional threads may be added to the scheduler, as long as they use the Copas send and receive methods. Below an example of a thread being added to create an outgoing TCP connection using Copas;

local socket = require("socket")
local copas = require("copas.timer")

local host = "127.0.0.1"
local port = 10000

local skt = socket.connect(host, port)

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.loop()

The example connects, echoes whatever it receives and exists upon receiving 'quit'.

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 copas.step() at each iteration of the loop:

while condition do
  copas.step()
  -- processing for other events from your system here
end

Valid XHTML 1.0!

$Id: manual.html,v 1.19 2009/03/24 22:04:26 carregal Exp $

lua-copas-1.2.0/doc/us/reference.html000066400000000000000000000141451210031361300174110ustar00rootroot00000000000000 Copas - Coroutine Oriented Portable Asynchronous Services for Lua
Copas
Coroutine Oriented Portable Asynchronous Services for Lua

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:

copas.addserver(server, handler[, timeout])
Adds a new server and its handler to the dispatcher using an optional timeout.
server is a LuaSocket server socket created using socket.bind().
handler is a function that receives a LuaSocket client socket and handles the communication with that client.
timeout is the timeout for blocking I/O in seconds. The handler will be executed in parallel with other threads and the registered handlers as long as it uses the Copas socket functions.
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.
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 socket.accept(). The timeout parameter is optional.
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 socket.accept(). The timeout parameter is optional. It returns false when no data was handled (timeout) or true if there was data handled (or alternatively nil + error message in case of errors).

The second group is used by the handler functions to exchange data with the clients, and by threads registered with addthread to exchange data with other services.

copas.flush(skt)
Flushes a client write buffer. copas.flush() is called from time to time by copas.loop() but it may be necessary to call it from the handler function or one of the threads.
copas.receive(skt, pattern)
Reads data from a client socket according to a pattern just like LuaSocket socket:receive(). The Copas version does not block and allows the multitasking of the other handlers and threads.
copas.send(skt, data)
Sends data to a client socket just like socket:send(). The Copas version is buffered and does not block, allowing the multitasking of the other handlers and threads.
copas.wrap(skt)
Wraps a LuaSocket socket and returns a Copas socket that implements LuaSocket's API but use Copas' methods copas.send() and copas.receive() automatically.

Valid XHTML 1.0!

$Id: reference.html,v 1.16 2009/04/07 21:34:52 carregal Exp $

lua-copas-1.2.0/rockspec/000077500000000000000000000000001210031361300151755ustar00rootroot00000000000000lua-copas-1.2.0/rockspec/copas-1.1.2-1.rockspec000066400000000000000000000016501210031361300206320ustar00rootroot00000000000000package = "Copas" version = "1.1.2-1" source = { url = "http://luaforge.net/frs/download.php/3367/copas-1.1.2.tar.gz", } description = { summary = "Coroutine Oriented Portable Asynchronous Services", detailed = [[ Copas is a dispatcher based on coroutines that can be used by TCP/IP servers. It uses LuaSocket 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 as an example. ]], license = "MIT/X11", homepage = "http://www.keplerproject.org/copas/" } dependencies = { "lua >= 5.1", "luasocket >= 2.0" } build = { type = "make", build_pass = false, install_variables = { LUA_DIR = "$(LUADIR)" } } lua-copas-1.2.0/rockspec/copas-1.1.3-1.rockspec000066400000000000000000000017001210031361300206270ustar00rootroot00000000000000package = "Copas" version = "1.1.3-1" source = { url = "http://luaforge.net/frs/download.php/3409/copas-1.1.3.tar.gz", } description = { summary = "Coroutine Oriented Portable Asynchronous Services", detailed = [[ Copas is a dispatcher based on coroutines that can be used by TCP/IP servers. It uses LuaSocket 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 as an example. ]], license = "MIT/X11", homepage = "http://www.keplerproject.org/copas/" } dependencies = { "lua >= 5.1", "luasocket >= 2.0", "coxpcall >= 1.13", } build = { type = "make", build_pass = false, install_variables = { LUA_DIR = "$(LUADIR)" } } lua-copas-1.2.0/rockspec/copas-1.1.4-1.rockspec000066400000000000000000000016411210031361300206340ustar00rootroot00000000000000package = "Copas" version = "1.1.4-1" source = { url = "http://luaforge.net/frs/download.php/3896/copas-1.1.4.tar.gz", } description = { summary = "Coroutine Oriented Portable Asynchronous Services", detailed = [[ Copas is a dispatcher based on coroutines that can be used by TCP/IP servers. It uses LuaSocket 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 as an example. ]], license = "MIT/X11", homepage = "http://www.keplerproject.org/copas/" } dependencies = { "lua >= 5.1", "luasocket >= 2.0", "coxpcall >= 1.13", } build = { type = "module", modules = { copas = "src/copas/copas.lua" } } lua-copas-1.2.0/rockspec/copas-1.1.5-1.rockspec000066400000000000000000000016411210031361300206350ustar00rootroot00000000000000package = "Copas" version = "1.1.5-1" source = { url = "http://luaforge.net/frs/download.php/4027/copas-1.1.5.tar.gz", } description = { summary = "Coroutine Oriented Portable Asynchronous Services", detailed = [[ Copas is a dispatcher based on coroutines that can be used by TCP/IP servers. It uses LuaSocket 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 as an example. ]], license = "MIT/X11", homepage = "http://www.keplerproject.org/copas/" } dependencies = { "lua >= 5.1", "luasocket >= 2.0", "coxpcall >= 1.13", } build = { type = "module", modules = { copas = "src/copas/copas.lua" } } lua-copas-1.2.0/rockspec/copas-1.1.6-1.rockspec000066400000000000000000000016501210031361300206360ustar00rootroot00000000000000package = "Copas" version = "1.1.6-1" source = { url = "http://github.com/downloads/keplerproject/copas/copas-1.1.6.tar.gz", } description = { summary = "Coroutine Oriented Portable Asynchronous Services", detailed = [[ Copas is a dispatcher based on coroutines that can be used by TCP/IP servers. It uses LuaSocket 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 as an example. ]], license = "MIT/X11", homepage = "http://www.keplerproject.org/copas/" } dependencies = { "lua >= 5.1", "luasocket >= 2.0", "coxpcall >= 1.13", } build = { type = "builtin", modules = { copas = "src/copas/copas.lua" } } lua-copas-1.2.0/rockspec/copas-1.2.0-1.rockspec000066400000000000000000000016331210031361300206320ustar00rootroot00000000000000package = "Copas" version = "1.2.0-1" source = { url = "http://www.keplerproject.org/files/copas-1.2.0.tar.gz", } description = { summary = "Coroutine Oriented Portable Asynchronous Services", detailed = [[ Copas is a dispatcher based on coroutines that can be used by TCP/IP servers. It uses LuaSocket 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 as an example. ]], license = "MIT/X11", homepage = "http://www.keplerproject.org/copas/" } dependencies = { "lua >= 5.1", "luasocket >= 2.1", "coxpcall >= 1.14", } build = { type = "builtin", modules = { copas = "src/copas/copas.lua" } } lua-copas-1.2.0/rockspec/copas-cvs-2.rockspec000066400000000000000000000016101210031361300207630ustar00rootroot00000000000000package = "Copas" version = "cvs-2" source = { url = "git://github.com/keplerproject/copas.git" } description = { summary = "Coroutine Oriented Portable Asynchronous Services", detailed = [[ Copas is a dispatcher based on coroutines that can be used by TCP/IP servers. It uses LuaSocket 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 as an example. ]], license = "MIT/X11", homepage = "http://www.keplerproject.org/copas/" } dependencies = { "lua >= 5.1", "luasocket >= 2.0", "coxpcall >= 1.13", } build = { type = "module", modules = { copas = "src/copas/copas.lua" } } lua-copas-1.2.0/src/000077500000000000000000000000001210031361300141535ustar00rootroot00000000000000lua-copas-1.2.0/src/copas/000077500000000000000000000000001210031361300152605ustar00rootroot00000000000000lua-copas-1.2.0/src/copas/copas.lua000066400000000000000000000404721210031361300170770ustar00rootroot00000000000000------------------------------------------------------------------------------- -- Copas - Coroutine Oriented Portable Asynchronous Services -- -- A dispatcher based on coroutines that can be used by TCP/IP servers. -- Uses LuaSocket as the interface with the TCP/IP stack. -- -- Authors: Andre Carregal, Javier Guerra, and Fabio Mascarenhas -- Contributors: Diego Nehab, Mike Pall, David Burgess, Leonardo Godinho, -- Thomas Harning Jr., and Gary NG -- -- Copyright 2005 - Kepler Project (www.keplerproject.org) -- -- $Id: copas.lua,v 1.37 2009/04/07 22:09:52 carregal Exp $ ------------------------------------------------------------------------------- if package.loaded["socket.http"] then error("you must require copas before require'ing socket.http") end local socket = require "socket" local coxpcall = require "coxpcall" local WATCH_DOG_TIMEOUT = 120 local UDP_DATAGRAM_MAX = 8192 -- Redefines LuaSocket functions with coroutine safe versions -- (this allows the use of socket.http from within copas) local function statusHandler(status, ...) if status then return ... end local err = (...) if type(err) == "table" then return nil, err[1] else error(err) end end function socket.protect(func) return function (...) return statusHandler(coxpcall.pcall(func, ...)) end end function socket.newtry(finalizer) return function (...) local status = (...) if not status then coxpcall.pcall(finalizer, select(2, ...)) error({ (select(2, ...)) }, 0) end return ... end end -- end of LuaSocket redefinitions local copas = {} -- Meta information is public even if beginning with an "_" copas._COPYRIGHT = "Copyright (C) 2005-2010 Kepler Project" copas._DESCRIPTION = "Coroutine Oriented Portable Asynchronous Services" copas._VERSION = "Copas 1.1.7" -- Close the socket associated with the current connection after the handler finishes copas.autoclose = true ------------------------------------------------------------------------------- -- Simple set implementation based on LuaSocket's tinyirc.lua example -- adds a FIFO queue for each value in the set ------------------------------------------------------------------------------- local function newset() local reverse = {} local set = {} local q = {} setmetatable(set, { __index = { insert = function(set, value) if not reverse[value] then set[#set + 1] = value reverse[value] = #set end end, remove = function(set, value) local index = reverse[value] if index then reverse[value] = nil local top = set[#set] set[#set] = nil if top ~= value then reverse[top] = index set[index] = top end end end, push = function (set, key, itm) local qKey = q[key] if qKey == nil then q[key] = {itm} else qKey[#qKey + 1] = itm end end, pop = function (set, key) local t = q[key] if t ~= nil then local ret = table.remove (t, 1) if t[1] == nil then q[key] = nil end return ret end end }}) return set end local _servers = newset() -- servers being handled local _reading_log = {} local _writing_log = {} local _reading = newset() -- sockets currently being read local _writing = newset() -- sockets currently being written ------------------------------------------------------------------------------- -- Coroutine based socket I/O functions. ------------------------------------------------------------------------------- -- reads a pattern from a client and yields to the reading set on timeouts -- UDP: a UDP socket expects a second argument to be a number, so it MUST -- be provided as the 'pattern' below defaults to a string. Will throw a -- 'bad argument' error if omitted. function copas.receive(client, pattern, part) local s, err pattern = pattern or "*l" repeat s, err, part = client:receive(pattern, part) if s or err ~= "timeout" then _reading_log[client] = nil return s, err, part end _reading_log[client] = os.time() coroutine.yield(client, _reading) until false end -- receives data from a client over UDP. Not available for TCP. -- (this is a copy of receive() method, adapted for receivefrom() use) function copas.receivefrom(client, size) local s, err, port size = size or UDP_DATAGRAM_MAX repeat s, err, port = client:receivefrom(size) -- upon success err holds ip address if s or err ~= "timeout" then _reading_log[client] = nil return s, err, port end _reading_log[client] = os.time() coroutine.yield(client, _reading) until false end -- same as above but with special treatment when reading chunks, -- unblocks on any data received. function copas.receivePartial(client, pattern) local s, err, part pattern = pattern or "*l" repeat s, err, part = client:receive(pattern) if s or ( (type(pattern)=="number") and part~="" and part ~=nil ) or err ~= "timeout" then _reading_log[client] = nil return s, err, part end _reading_log[client] = os.time() coroutine.yield(client, _reading) until false end -- sends data to a client. The operation is buffered and -- yields to the writing set on timeouts -- Note: from and to parameters will be ignored by/for UDP sockets function copas.send(client, data, from, to) local s, err,sent from = from or 1 local lastIndex = from - 1 repeat s, err, lastIndex = client:send(data, lastIndex + 1, to) -- adds extra corrotine swap -- garantees that high throuput dont take other threads to starvation if (math.random(100) > 90) then _writing_log[client] = os.time() coroutine.yield(client, _writing) end if s or err ~= "timeout" then _writing_log[client] = nil return s, err,lastIndex end _writing_log[client] = os.time() coroutine.yield(client, _writing) until false end -- sends data to a client over UDP. Not available for TCP. -- (this is a copy of send() method, adapted for sendto() use) function copas.sendto(client, data, ip, port) local s, err,sent repeat s, err = client:sendto(data, ip, port) -- adds extra corrotine swap -- garantees that high throuput dont take other threads to starvation if (math.random(100) > 90) then _writing_log[client] = os.time() coroutine.yield(client, _writing) end if s or err ~= "timeout" then _writing_log[client] = nil return s, err end _writing_log[client] = os.time() coroutine.yield(client, _writing) until false end -- waits until connection is completed function copas.connect(skt, host, port) skt:settimeout(0) local ret, err repeat ret, err = skt:connect (host, port) if ret or err ~= "timeout" then _writing_log[skt] = nil return ret, err end _writing_log[skt] = os.time() coroutine.yield(skt, _writing) until false return ret, err end -- flushes a client write buffer (deprecated) function copas.flush(client) end -- wraps a TCP socket to use Copas methods (send, receive, flush and settimeout) local _skt_mt = {__index = { send = function (self, data, from, to) return copas.send (self.socket, data, from, to) end, receive = function (self, pattern) if (self.timeout==0) then return copas.receivePartial(self.socket, pattern) end return copas.receive(self.socket, pattern) end, flush = function (self) return copas.flush(self.socket) end, settimeout = function (self,time) self.timeout=time return end, }} -- wraps a UDP socket, copy of TCP one adapted for UDP. -- Mainly adds sendto() and receivefrom() local _skt_mt_udp = {__index = { send = function (self, data) return copas.send (self.socket, data) end, sendto = function (self, data, ip, port) return copas.sendto (self.socket, data, ip, port) end, receive = function (self, size) return copas.receive (self.socket, (size or UDP_DATAGRAM_MAX)) end, receivefrom = function (self, size) return copas.receivefrom (self.socket, (size or UDP_DATAGRAM_MAX)) end, flush = function (self) return copas.flush (self.socket) end, settimeout = function (self,time) self.timeout=time return end, }} function copas.wrap (skt) if string.sub(tostring(skt),1,3) == "udp" then return setmetatable ({socket = skt}, _skt_mt_udp) else return setmetatable ({socket = skt}, _skt_mt) end end -------------------------------------------------- -- Error handling -------------------------------------------------- local _errhandlers = {} -- error handler per coroutine function copas.setErrorHandler (err) local co = coroutine.running() if co then _errhandlers [co] = err end end local function _deferror (msg, co, skt) print (msg, co, skt) end ------------------------------------------------------------------------------- -- Thread handling ------------------------------------------------------------------------------- local function _doTick (co, skt, ...) if not co then return end local ok, res, new_q = coroutine.resume(co, skt, ...) if ok and res and new_q then new_q:insert (res) new_q:push (res, co) else if not ok then coxpcall.pcall (_errhandlers [co] or _deferror, res, co, skt) end if skt and copas.autoclose then skt:close() end _errhandlers [co] = nil end end -- accepts a connection on socket input local function _accept(input, handler) local client = input:accept() if client then client:settimeout(0) local co = coroutine.create(handler) _doTick (co, client) --_reading:insert(client) end return client end -- handle threads on a queue local function _tickRead (skt) _doTick (_reading:pop (skt), skt) end local function _tickWrite (skt) _doTick (_writing:pop (skt), skt) end ------------------------------------------------------------------------------- -- Adds a server/handler pair to Copas dispatcher ------------------------------------------------------------------------------- local function addTCPserver(server, handler, timeout) server:settimeout(timeout or 0.1) _servers[server] = handler _reading:insert(server) end local function addUDPserver(server, handler, timeout) server:settimeout(timeout or 0) local co = coroutine.create(handler) _reading:insert(server) _doTick (co, server) end function copas.addserver(server, handler, timeout) if string.sub(tostring(server),1,3) == "udp" then addUDPserver(server, handler, timeout) else addTCPserver(server, handler, timeout) end end ------------------------------------------------------------------------------- -- Adds an new courotine thread to Copas dispatcher ------------------------------------------------------------------------------- function copas.addthread(thread, ...) if type(thread) ~= "thread" then thread = coroutine.create(thread) end _doTick (thread, nil, ...) return thread end ------------------------------------------------------------------------------- -- tasks registering ------------------------------------------------------------------------------- local _tasks = {} local function addtaskRead (tsk) -- lets tasks call the default _tick() tsk.def_tick = _tickRead _tasks [tsk] = true end local function addtaskWrite (tsk) -- lets tasks call the default _tick() tsk.def_tick = _tickWrite _tasks [tsk] = true end local function tasks () return next, _tasks end ------------------------------------------------------------------------------- -- main tasks: manage readable and writable socket sets ------------------------------------------------------------------------------- -- a task to check ready to read events local _readable_t = { events = function(self) local i = 0 return function () i = i + 1 return self._evs [i] end end, tick = function (self, input) local handler = _servers[input] if handler then input = _accept(input, handler) else _reading:remove (input) self.def_tick (input) end end } addtaskRead (_readable_t) -- a task to check ready to write events local _writable_t = { events = function (self) local i = 0 return function () i = i + 1 return self._evs [i] end end, tick = function (self, output) _writing:remove (output) self.def_tick (output) end } addtaskWrite (_writable_t) local last_cleansing = 0 ------------------------------------------------------------------------------- -- Checks for reads and writes on sockets ------------------------------------------------------------------------------- local function _select (timeout) local err local now = os.time() local duration = os.difftime _readable_t._evs, _writable_t._evs, err = socket.select(_reading, _writing, timeout) local r_evs, w_evs = _readable_t._evs, _writable_t._evs if duration(now, last_cleansing) > WATCH_DOG_TIMEOUT then last_cleansing = now for k,v in pairs(_reading_log) do if not r_evs[k] and duration(now, v) > WATCH_DOG_TIMEOUT then _reading_log[k] = nil r_evs[#r_evs + 1] = k r_evs[k] = #r_evs end end for k,v in pairs(_writing_log) do if not w_evs[k] and duration(now, v) > WATCH_DOG_TIMEOUT then _writing_log[k] = nil w_evs[#w_evs + 1] = k w_evs[k] = #w_evs end end end if err == "timeout" and #r_evs + #w_evs > 0 then return nil else return err end end ------------------------------------------------------------------------------- -- Dispatcher loop step. -- Listen to client requests and handles them -- Returns false if no data was handled (timeout), or true if there was data -- handled (or nil + error message) ------------------------------------------------------------------------------- function copas.step(timeout) local err = _select (timeout) if err == "timeout" then return false end if err then error(err) end for tsk in tasks() do for ev in tsk:events() do tsk:tick (ev) end end return true end ------------------------------------------------------------------------------- -- Dispatcher endless loop. -- Listen to client requests and handles them forever ------------------------------------------------------------------------------- function copas.loop(timeout) while true do copas.step(timeout) end end return copas lua-copas-1.2.0/tests/000077500000000000000000000000001210031361300145265ustar00rootroot00000000000000lua-copas-1.2.0/tests/cosocket.lua000066400000000000000000000024701210031361300170460ustar00rootroot00000000000000------------------------------------------------------------------------------- -- Copas - Coroutine Oriented Portable Asynchronous Services -- -- Copas Wrapper for socket.http module -- -- Written by Leonardo Godinho da Cunha ------------------------------------------------------------------------------- local copas = require "copas" local cosocket = {} -- Meta information is public even begining with an "_" cosocket._COPYRIGHT = "Copyright (C) 2004-2006 Kepler Project" cosocket._DESCRIPTION = "Coroutine Oriented Portable Asynchronous Services Wrapper for socket module" cosocket._NAME = "Copas.cosocket" cosocket._VERSION = "0.1" function cosocket.tcp () local skt = socket.tcp() local w_skt_mt = { __index = skt } local ret_skt = setmetatable ({ socket = skt }, w_skt_mt) ret_skt.settimeout = function (self,val) return self.socket:settimeout (val) end ret_skt.connect = function (self,host, port) local ret,err = copas.connect (self.socket,host, port) local d = copas.wrap(self.socket) self.send= function(client, data) local ret,val=d.send(client, data) return ret,val end self.receive=d.receive self.close = function (w_socket) ret=w_socket.socket:close() return ret end return ret,err end return ret_skt end lua-copas-1.2.0/tests/test.lua000066400000000000000000000010361210031361300162100ustar00rootroot00000000000000-- Tests Copas with a simple Echo server -- -- Run the test file and the connect to the server using telnet on the used port. -- The server should be able to echo any input, to stop the test just send the command "quit" local copas = require"copas" local function echoHandler(skt) skt = copas.wrap(skt) while true do local data = skt:receive() if not data or data == "quit" then break end skt:send(data) end end local server = socket.bind("localhost", 20000) copas.addserver(server, echoHandler) copas.loop()