Added Lua modules: Copas, binaryheap, and timerwheel.

This commit is contained in:
Scott Duensing 2023-12-12 20:31:05 -06:00
parent 4ea35bed7d
commit b1bc8aa063
140 changed files with 16071 additions and 50 deletions

View file

@ -38,6 +38,9 @@ New Features
- LuaSocket
- LuaSec
- LuaRS232
- timerwheel
- binaryheap
- Copas
- New command line option: -p (or --program). This is similar to the
existing -t (or --trace) option. Where "trace" displays and logs script
@ -68,6 +71,8 @@ Fixes
- PNG files no longer generate warnings on the console.
- Non-VLDP videos now honor command line volume settings.
- Menu system now performs "natural sorting" of game titles instead of brain
dead "computer sorting". It should make a lot more sense paging through
games now.

View file

@ -4,7 +4,9 @@ these tools, Singe would not exist.
arg_parser BSD-2-Clause http://savannah.nongnu.org/projects/arg-parser
binaryheap.lua MIT http://tieske.github.io/binaryheap.lua
bzip2 bzip2-1.0.6 https://sourceware.org/bzip2
copas MIT https://lunarmodules.github.io/copas
ffmpeg LGPL-2.1 https://ffmpeg.org
ffms2 GPL-3.0-only https://github.com/FFMS/ffms2
jbigkit GPL-2.0 https://www.cl.cam.ac.uk/~mgk25/jbigkit
@ -21,6 +23,7 @@ SDL2_gfx LGPL-2.1 https://github.com/ferzkopp/SDL_gfx
SDL2_image Zlib https://www.libsdl.org
SDL2_mixer Zlib https://www.libsdl.org
SDL2_ttf Zlib https://www.libsdl.org
timerwheel MIT https://tieske.github.io/timerwheel.lua
uthash BSD-1-Clause https://troydhanson.github.io/uthash
vlc GPL-2.0 https://www.videolan.org/vlc
xz Public-Domain https://github.com/tukaani-project/xz

View file

@ -48,9 +48,12 @@ function loadGameAssets()
SPRITE_CABINET = spriteLoad(GAME_LIST[GAME_SELECTED].CABINET)
SPRITE_MARQUEE = spriteLoad(GAME_LIST[GAME_SELECTED].MARQUEE)
VIDEO_ATTRACT = videoLoad(GAME_LIST[GAME_SELECTED].ATTRACT)
if GAME_LIST[GAME_SELECTED].AUDIO_TRACK then
videoSetAudioTrack(VIDEO_ATTRACT, GAME_LIST[GAME_SELECTED].AUDIO_TRACK)
end
videoPlay(VIDEO_ATTRACT)
videoSeek(VIDEO_ATTRACT, GAME_LIST[GAME_SELECTED].ATTRACT_START)
videoSetVolume(VIDEO_ATTRACT, 0, 0)
-- videoSetVolume(VIDEO_ATTRACT, 0, 0)
-- Build text sprites
local textBox = GAME_LIST[GAME_SELECTED].DESCRIPTION .. WRAP_BREAK .. WRAP_BREAK ..

View file

@ -304,15 +304,6 @@ if [[ 0 == 1 ]]; then
# === "Indexing" Text ===
createEmbeddedImage indexing
# === Singe Framework ===
createEmbeddedBinary assets/Framework.singe ${G_GENERATED}/Framework_singe.h FRAMEWORK_SINGE_H
# === Default Config ===
createEmbeddedBinary assets/controls.cfg ${G_GENERATED}/controls_cfg.h CONTROLS_CFG_H
# === Singe Menu App ===
createEmbeddedBinary assets/Menu.singe ${G_GENERATED}/Menu_singe.h MENU_SINGE_H
# === Singe Menu Font ===
createEmbeddedBinary assets/FreeSansBold.ttf ${G_GENERATED}/FreeSansBold_ttf.h FREESANSBOLD_TTF_H
@ -342,6 +333,32 @@ if [[ 0 == 1 ]]; then
# === LuaRS232 ===
createEmbeddedBinary thirdparty/librs232/bindings/lua/rs232.lua ${G_GENERATED}/rs232_lua.h RS232_LUA_H
# === Copas ===
createEmbeddedBinary thirdparty/copas/src/copas.lua ${G_GENERATED}/copas_lua.h COPAS_LUA_H
createEmbeddedBinary thirdparty/copas/src/copas/ftp.lua ${G_GENERATED}/copas_ftp_lua.h COPAS_FTP_LUA_H copas
createEmbeddedBinary thirdparty/copas/src/copas/http.lua ${G_GENERATED}/copas_http_lua.h COPAS_HTTP_LUA_H copas
createEmbeddedBinary thirdparty/copas/src/copas/smtp.lua ${G_GENERATED}/copas_smtp_lua.h COPAS_SMTP_LUA_H copas
createEmbeddedBinary thirdparty/copas/src/copas/lock.lua ${G_GENERATED}/copas_lock_lua.h COPAS_LOCK_LUA_H copas
createEmbeddedBinary thirdparty/copas/src/copas/queue.lua ${G_GENERATED}/copas_queue_lua.h COPAS_QUEUE_LUA_H copas
createEmbeddedBinary thirdparty/copas/src/copas/semaphore.lua ${G_GENERATED}/copas_semaphore_lua.h COPAS_SEMAPHORE_LUA_H copas
createEmbeddedBinary thirdparty/copas/src/copas/timer.lua ${G_GENERATED}/copas_timer_lua.h COPAS_TIMER_LUA_H copas
# === binaryheap.lua ===
createEmbeddedBinary thirdparty/binaryheap.lua/src/binaryheap.lua ${G_GENERATED}/binaryheap_lua.h BINARYHEAP_LUA_H
# === binaryheap.lua ===
createEmbeddedBinary thirdparty/timerwheel.lua/src/timerwheel/timerwheel.lua ${G_GENERATED}/timerwheel_lua.h TIMERWHEEL_LUA_H
fi
# === Singe Framework ===
createEmbeddedBinary assets/Framework.singe ${G_GENERATED}/Framework_singe.h FRAMEWORK_SINGE_H
# === Default Config ===
createEmbeddedBinary assets/controls.cfg ${G_GENERATED}/controls_cfg.h CONTROLS_CFG_H
# === Singe Menu App ===
createEmbeddedBinary assets/Menu.singe ${G_GENERATED}/Menu_singe.h MENU_SINGE_H
:<<UNUSED
# === Singe Manual ===
#libreoffice --headless "-env:UserInstallation=file:///tmp/LibreOffice_Conversion_${USER}" --convert-to pdf:writer_pdf_Export Manual.odt
@ -350,7 +367,6 @@ if [[ 0 == 1 ]]; then
createEmbeddedBinary ${G_TARGET}/Manual.pdf ${G_GENERATED}/Manual_pdf.h MANUAL_H
rm ${G_TARGET}/Manual.pdf
UNUSED
fi
pushd ${G_TARGET}
clearAndEnterBuild
@ -373,6 +389,7 @@ function createEmbeddedBinary() {
local BINFILE=$1
local SOURCEFILE=$2 # This is assumed to be an absolute path
local BLOCKER=$3
local PREFIX=$4
local FILENAME=$(basename ${BINFILE})
local DIRNAME=$(dirname ${BINFILE})
@ -382,9 +399,14 @@ function createEmbeddedBinary() {
pushd ${DIRNAME}
xxd -i ${FILENAME} >> ${SOURCEFILE}
popd
if [[ ! -z ${PREFIX} ]]; then
PREFIX=${PREFIX}_
sed -i "s/unsigned char /unsigned char ${PREFIX}/" ${SOURCEFILE}
sed -i "s/unsigned int /unsigned int ${PREFIX}/" ${SOURCEFILE}
fi
printf "\n#else // EMBED_HERE\n\n" >> ${SOURCEFILE}
printf "extern unsigned char ${FILENAME/\./_}[];\n" >> ${SOURCEFILE}
printf "extern unsigned int ${FILENAME/\./_}_len;\n" >> ${SOURCEFILE}
printf "extern unsigned char ${PREFIX}${FILENAME/\./_}[];\n" >> ${SOURCEFILE}
printf "extern unsigned int ${PREFIX}${FILENAME/\./_}_len;\n" >> ${SOURCEFILE}
printf "\n#endif // EMBED_HERE\n\n" >> ${SOURCEFILE}
outputFooter ${BLOCKER} >> ${SOURCEFILE}
}
@ -469,7 +491,7 @@ function outputLicense() {
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
@ -479,7 +501,8 @@ function outputLicense() {
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
*/
LICENSE

View file

@ -58,5 +58,21 @@
// LuaRS232
#include "generated/rs232_lua.h"
// binaryheap
#include "generated/binaryheap_lua.h"
// timerwheel
#include "generated/timerwheel_lua.h"
// Copas
#include "generated/copas_lua.h"
#include "generated/copas_ftp_lua.h"
#include "generated/copas_http_lua.h"
#include "generated/copas_lock_lua.h"
#include "generated/copas_queue_lua.h"
#include "generated/copas_semaphore_lua.h"
#include "generated/copas_smtp_lua.h"
#include "generated/copas_timer_lua.h"
#endif // EMBEDDED_H

View file

@ -1012,7 +1012,7 @@ void unpackGames(void) {
// Unpack it!
if (ok) {
utilSay(">>> Unpacking %s: %s", types[packageType], de->d_name);
utilSay(">>> Installing %s: %s", types[packageType], de->d_name);
// https://github.com/libarchive/libarchive/wiki/Examples#user-content-A_Complete_Extractor
flags = ARCHIVE_EXTRACT_TIME;
flags |= ARCHIVE_EXTRACT_PERM;

View file

@ -307,6 +307,19 @@ static const luaModuleT luaModules[] = {
// LuaRS232
MODC("rs232.core", luaopen_luars232),
MODL("rs232", rs232_lua),
// binaryheap
MODL("binaryheap", binaryheap_lua),
// timerwheel
MODL("timerwheel", timerwheel_lua),
// Copas
MODL("copas", copas_lua),
MODL("copas.ftp", copas_ftp_lua),
MODL("copas.http", copas_http_lua),
MODL("copas.lock", copas_lock_lua),
MODL("copas.queue", copas_queue_lua),
MODL("copas.semaphore", copas_semaphore_lua),
MODL("copas.smtp", copas_smtp_lua),
MODL("copas.timer", copas_timer_lua),
};
@ -2808,7 +2821,6 @@ int32_t apiVideoGetLanguageDescription(lua_State *L) {
if (lua_isstring(L, 1)) {
c = (char *)lua_tostring(L, 1);
r = videoGetLanguageDescription(c);
// utilSay("[%s] [%s]", c, r);
}
}

View file

@ -292,7 +292,6 @@ int32_t _loadVideoAndAudio(char *vFilename, char *aFilename, char *indexPath, bo
while ((tag = (AVDictionaryEntry *)av_dict_iterate(fmt_ctx->streams[x]->metadata, tag))) {
if (utilStricmp("language", tag->key) == 0) {
v->audio[count++].language = strdup(tag->value);
utilSay("Track %d is %s", x, tag->value);
break;
}
}
@ -502,15 +501,6 @@ char *videoGetLanguageDescription(char *languageCode) {
int32_t i = 0;
while (p_languages[i].psz_eng_name != NULL) {
/*
utilSay("[%s] [%s] [%s] [%s] [%s]",
languageCode,
p_languages[i].psz_eng_name,
p_languages[i].psz_iso639_1,
p_languages[i].psz_iso639_2T,
p_languages[i].psz_iso639_2B
);
*/
if ((utilStricmp(languageCode, (char *)p_languages[i].psz_iso639_1) == 0) ||
(utilStricmp(languageCode, (char *)p_languages[i].psz_iso639_2T) == 0) ||
(utilStricmp(languageCode, (char *)p_languages[i].psz_iso639_2B) == 0)) {

7
thirdparty/binaryheap.lua/.busted vendored Normal file
View file

@ -0,0 +1,7 @@
return {
default = {
verbose = true,
coverage = true,
output = "gtest",
},
}

13
thirdparty/binaryheap.lua/.editorconfig vendored Normal file
View file

@ -0,0 +1,13 @@
root = true
[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
[*.lua]
indent_style = space
indent_size = 2
[Makefile]
indent_style = tab

2
thirdparty/binaryheap.lua/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
.DS_Store
luacov.*

31
thirdparty/binaryheap.lua/.luacheckrc vendored Normal file
View file

@ -0,0 +1,31 @@
std = "ngx_lua+busted"
unused_args = false
redefined = false
max_line_length = false
globals = {
--"_KONG",
--"kong",
--"ngx.IS_CLI",
}
not_globals = {
"string.len",
"table.getn",
}
ignore = {
--"6.", -- ignore whitespace warnings
"211/_ENV", -- unused variable
}
exclude_files = {
"here/**"
--"spec/fixtures/invalid-module.lua",
--"spec-old-api/fixtures/invalid-module.lua",
}

6
thirdparty/binaryheap.lua/.luacov vendored Normal file
View file

@ -0,0 +1,6 @@
modules = {
["binaryheap"] = "src/binaryheap.lua",
["binaryheap.*"] = "src"
}
runreport = true
deletestats = false -- file still needed to push to coveralls

29
thirdparty/binaryheap.lua/.travis.yml vendored Normal file
View file

@ -0,0 +1,29 @@
language: python
sudo: false
env:
- LUA="lua 5.1"
- LUA="lua 5.2"
- LUA="lua 5.3"
- LUA="luajit 2.0"
- LUA="luajit 2.0 --compat 5.2"
- LUA="luajit 2.1"
- LUA="luajit 2.1 --compat 5.2"
before_install:
- pip install hererocks
- hererocks here -r^ --$LUA
- source here/bin/activate
- luarocks install luacheck
- luarocks install busted
- luarocks install luacov-coveralls
install:
- luarocks make
script:
- luacheck .
- busted
after_success:
- luacov-coveralls

21
thirdparty/binaryheap.lua/LICENSE vendored Normal file
View file

@ -0,0 +1,21 @@
Copyright © 2015-2019 Thijs Schreijer.
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.

53
thirdparty/binaryheap.lua/README.md vendored Normal file
View file

@ -0,0 +1,53 @@
[![Build Status](https://travis-ci.com/Tieske/binaryheap.lua.svg?branch=master)](https://travis-ci.com/Tieske/binaryheap.lua)
[![Coverage Status](https://coveralls.io/repos/github/Tieske/binaryheap.lua/badge.svg?branch=master)](https://coveralls.io/github/Tieske/binaryheap.lua?branch=master)
binaryheap.lua
==============
[Binary heap](http://en.wikipedia.org/wiki/Binary_heap) implementation
Both the [source code](https://github.com/Tieske/binaryheap.lua) as well as the
[documentation](http://tieske.github.io/binaryheap.lua) are on github
Based on [original code](http://lua-users.org/lists/lua-l/2015-04/msg00137.html)
by Oliver Kroth, with
[extras](http://lua-users.org/lists/lua-l/2015-04/msg00133.html)
as proposed by Sean Conner.
Contributions
=============
This library was create by contributions from Oliver Kroth,
Thijs Schreijer, Boris Nagaev
History
=======
Version 0.4, 7-Nov-2018
- [breaking] added additional tests, mostly on returning errors, minor behaviour changes
- added `size` method
- fixed a lot of linter issues
Version 0.3, 15-Jul-2018
- bugfix `unique:pop` returning wrong order results (by Daurnimator)
- change `unique:peek` returning same order as `pop`
- added `unique:peekValue` returning just the value
Version 0.2, 21-Apr-2015
- bugfix `remove` function (by Boris Nagaev)
- configurable comparison function for the tree
Version 0.1, 20-Apr-2015
- Initial release
Copyright
=========
Copyright 2015-2019 Thijs Schreijer
License
=======
MIT/X11

View file

@ -0,0 +1,26 @@
package = "binaryheap"
version = "0.4-1"
source = {
url = "https://github.com/Tieske/binaryheap.lua/archive/version_0v4.tar.gz",
dir = "binaryheap.lua-version_0v4"
}
description = {
summary = "Binary heap implementation in pure Lua",
detailed = [[
Binary heaps are an efficient sorting algorithm. This module
implements a plain binary heap (without reverse lookup) and a
'unique' binary heap (with unique payloads and reverse lookup).
]],
license = "MIT/X11",
homepage = "https://github.com/Tieske/binaryheap.lua"
}
dependencies = {
"lua >= 5.1",
}
build = {
type = "builtin",
modules = { binaryheap = "src/binaryheap.lua" }
}

11
thirdparty/binaryheap.lua/config.ld vendored Normal file
View file

@ -0,0 +1,11 @@
project='binaryheap.lua'
title='binaryheap'
description='Module to create binary heaps'
format='markdown'
file='./src/'
dir='docs'
readme='readme.md'
sort=true
sort_modules=true
all=false
style='./docs/'

View file

@ -0,0 +1,703 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head>
<title>binaryheap</title>
<link rel="stylesheet" href="ldoc.css" type="text/css" />
</head>
<body>
<div id="container">
<div id="product">
<div id="product_logo"></div>
<div id="product_name"><big><b></b></big></div>
<div id="product_description"></div>
</div> <!-- id="product" -->
<div id="main">
<!-- Menu -->
<div id="navigation">
<br/>
<h1>binaryheap.lua</h1>
<h2>Contents</h2>
<ul>
<li><a href="#Basic_heap">Basic heap </a></li>
<li><a href="#Plain_heap">Plain heap </a></li>
<li><a href="#Unique_heap">Unique heap </a></li>
</ul>
<h2>Modules</h2>
<ul class="nowrap">
<li><strong>binaryheap</strong></li>
</ul>
<h2>Topics</h2>
<ul class="">
<li><a href="topics/readme.md.html">readme</a></li>
</ul>
</div>
<div id="content">
<h1>Module <code>binaryheap</code></h1>
<p>Binary heap implementation</p>
<p> A binary heap (or binary tree) is a <a href="http://en.wikipedia.org/wiki/Binary_heap">sorting algorithm</a>.</p>
<p>
<p> The 'plain binary heap' is managed by positions. Which are hard to get once
an element is inserted. It can be anywhere in the list because it is re-sorted
upon insertion/deletion of items. The array with values is stored in field
<code>values</code>:</p>
<pre>
<span class="backtick"><code>peek = heap.values[1]</code></span>
</pre>
<p> A 'unique binary heap' is where the payload is unique and the payload itself
also stored (as key) in the heap with the position as value, as in;</p>
<pre>
<span class="backtick"><code>heap.reverse[payload] = [pos]</code></span>
</pre>
<p> Due to this setup the reverse search, based on payload, is now a
much faster operation because instead of traversing the list/heap,
you can do;</p>
<pre>
<span class="backtick"><code>pos = heap.reverse[payload]</code></span>
</pre>
<p> This means that deleting elements from a 'unique binary heap' is
faster than from a plain heap.</p>
<p> All management functions in the 'unique binary heap' take <code>payload</code>
instead of <code>pos</code> as argument.
Note that the value of the payload must be unique!</p>
<p> Fields of heap object:</p>
<ul>
<li>values - array of values</li>
<li>payloads - array of payloads (unique binary heap only)</li>
<li>reverse - map from payloads to indices (unique binary heap only)</li>
</ul>
</p>
<h2><a href="#Basic_heap">Basic heap </a></h2>
<table class="function_list">
<tr>
<td class="name" nowrap><a href="#binaryHeap">binaryHeap (swap, erase, lt)</a></td>
<td class="summary">Creates a new binary heap.</td>
</tr>
</table>
<h2><a href="#Plain_heap">Plain heap </a></h2>
<table class="function_list">
<tr>
<td class="name" nowrap><a href="#heap:insert">heap:insert (value)</a></td>
<td class="summary">Inserts an element in the heap.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#heap:peek">heap:peek ()</a></td>
<td class="summary">Returns the element at the top of the heap, without removing it.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#heap:pop">heap:pop ()</a></td>
<td class="summary">Removes the top of the heap and returns it.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#heap:remove">heap:remove (pos)</a></td>
<td class="summary">Removes an element from the heap.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#heap:size">heap:size ()</a></td>
<td class="summary">Returns the number of elements in the heap.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#heap:update">heap:update (pos, newValue)</a></td>
<td class="summary">Updates the value of an element in the heap.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#maxHeap">maxHeap (gt)</a></td>
<td class="summary">Creates a new max-heap, where the largest value is at the top.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#minHeap">minHeap (lt)</a></td>
<td class="summary">Creates a new min-heap, where the smallest value is at the top.</td>
</tr>
</table>
<h2><a href="#Unique_heap">Unique heap </a></h2>
<table class="function_list">
<tr>
<td class="name" nowrap><a href="#heap:size">heap:size ()</a></td>
<td class="summary">Returns the number of elements in the heap.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#maxUnique">maxUnique (gt)</a></td>
<td class="summary">Creates a new max-heap with unique payloads.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#minUnique">minUnique (lt)</a></td>
<td class="summary">Creates a new min-heap with unique payloads.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#unique:insert">unique:insert (value, payload)</a></td>
<td class="summary">Inserts an element in the heap.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#unique:peek">unique:peek ()</a></td>
<td class="summary">Returns the element at the top of the heap, without removing it.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#unique:peekValue">unique:peekValue ()</a></td>
<td class="summary">Returns the element at the top of the heap, without removing it.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#unique:pop">unique:pop ()</a></td>
<td class="summary">Removes the top of the heap and returns it.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#unique:remove">unique:remove (payload)</a></td>
<td class="summary">Removes an element from the heap.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#unique:update">unique:update (payload, newValue)</a></td>
<td class="summary">Updates the value of an element in the heap.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#unique:valueByPayload">unique:valueByPayload (payload)</a></td>
<td class="summary">Returns the value associated with the payload</td>
</tr>
</table>
<br/>
<br/>
<h2 class="section-header has-description"><a name="Basic_heap"></a>Basic heap </h2>
<div class="section-description">
This is the base implementation of the heap. Under regular circumstances
this should not be used, instead use a <em>Plain heap</em> or <em>Unique heap</em>.
</div>
<dl class="function">
<dt>
<a name = "binaryHeap"></a>
<strong>binaryHeap (swap, erase, lt)</strong>
</dt>
<dd>
Creates a new binary heap.
This is the core of all heaps, the others
are built upon these sorting functions.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">swap</span>
(function) <code>swap(heap, idx1, idx2)</code> swaps values at
<code>idx1</code> and <code>idx2</code> in the heaps <code>heap.values</code> and <code>heap.payloads</code> lists (see
return value below).
</li>
<li><span class="parameter">erase</span>
(function) <code>swap(heap, position)</code> raw removal
</li>
<li><span class="parameter">lt</span>
(function) in <code>lt(a, b)</code> returns <code>true</code> when <code>a &lt; b</code> (for a min-heap)
</li>
</ul>
<h3>Returns:</h3>
<ol>
table with two methods; <code>heap:bubbleUp(pos)</code> and <code>heap:sinkDown(pos)</code>
that implement the sorting algorithm and two fields; <code>heap.values</code> and
<code>heap.payloads</code> being lists, holding the values and payloads respectively.
</ol>
</dd>
</dl>
<h2 class="section-header has-description"><a name="Plain_heap"></a>Plain heap </h2>
<div class="section-description">
A plain heap carries a single piece of information per entry. This can be
any type (except <code>nil</code>), as long as the comparison function used to create
the heap can handle it.
</div>
<dl class="function">
<dt>
<a name = "heap:insert"></a>
<strong>heap:insert (value)</strong>
</dt>
<dd>
Inserts an element in the heap.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">value</span>
the value used for sorting this element
</li>
</ul>
<h3>Returns:</h3>
<ol>
nothing, or throws an error on bad input
</ol>
</dd>
<dt>
<a name = "heap:peek"></a>
<strong>heap:peek ()</strong>
</dt>
<dd>
Returns the element at the top of the heap, without removing it.
<h3>Returns:</h3>
<ol>
value at the top, or <code>nil</code> if there is none
</ol>
</dd>
<dt>
<a name = "heap:pop"></a>
<strong>heap:pop ()</strong>
</dt>
<dd>
Removes the top of the heap and returns it.
<h3>Returns:</h3>
<ol>
value at the top, or <code>nil</code> if there is none
</ol>
</dd>
<dt>
<a name = "heap:remove"></a>
<strong>heap:remove (pos)</strong>
</dt>
<dd>
Removes an element from the heap.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">pos</span>
the position to remove
</li>
</ul>
<h3>Returns:</h3>
<ol>
value, or nil if a bad <code>pos</code> value was provided
</ol>
</dd>
<dt>
<a name = "heap:size"></a>
<strong>heap:size ()</strong>
</dt>
<dd>
Returns the number of elements in the heap.
<h3>Returns:</h3>
<ol>
number of elements
</ol>
</dd>
<dt>
<a name = "heap:update"></a>
<strong>heap:update (pos, newValue)</strong>
</dt>
<dd>
Updates the value of an element in the heap.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">pos</span>
the position which value to update
</li>
<li><span class="parameter">newValue</span>
the new value to use for this payload
</li>
</ul>
</dd>
<dt>
<a name = "maxHeap"></a>
<strong>maxHeap (gt)</strong>
</dt>
<dd>
Creates a new max-heap, where the largest value is at the top.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">gt</span>
(optional) comparison function (greater-than), see <a href="index.html#binaryHeap">binaryHeap</a>.
</li>
</ul>
<h3>Returns:</h3>
<ol>
the new heap
</ol>
</dd>
<dt>
<a name = "minHeap"></a>
<strong>minHeap (lt)</strong>
</dt>
<dd>
Creates a new min-heap, where the smallest value is at the top.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">lt</span>
(optional) comparison function (less-than), see <a href="index.html#binaryHeap">binaryHeap</a>.
</li>
</ul>
<h3>Returns:</h3>
<ol>
the new heap
</ol>
</dd>
</dl>
<h2 class="section-header has-description"><a name="Unique_heap"></a>Unique heap </h2>
<div class="section-description">
A unique heap carries 2 pieces of information per entry.</p>
<ol>
<li>The <code>value</code>, this is used for ordering the heap. It can be any type (except
<code>nil</code>), as long as the comparison function used to create the heap can
handle it.</li>
<li>The <code>payload</code>, this can be any type (except <code>nil</code>), but it MUST be unique.</li>
</ol>
<p> With the 'unique heap' it is easier to remove elements from the heap.
</div>
<dl class="function">
<dt>
<a name = "heap:size"></a>
<strong>heap:size ()</strong>
</dt>
<dd>
Returns the number of elements in the heap.
<h3>Returns:</h3>
<ol>
number of elements
</ol>
</dd>
<dt>
<a name = "maxUnique"></a>
<strong>maxUnique (gt)</strong>
</dt>
<dd>
Creates a new max-heap with unique payloads.
A max-heap is where the largest value is at the top.</p>
<p> <em>NOTE</em>: All management functions in the 'unique binary heap'
take <code>payload</code> instead of <code>pos</code> as argument.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">gt</span>
(optional) comparison function (greater-than), see <a href="index.html#binaryHeap">binaryHeap</a>.
</li>
</ul>
<h3>Returns:</h3>
<ol>
the new heap
</ol>
</dd>
<dt>
<a name = "minUnique"></a>
<strong>minUnique (lt)</strong>
</dt>
<dd>
Creates a new min-heap with unique payloads.
A min-heap is where the smallest value is at the top.</p>
<p> <em>NOTE</em>: All management functions in the 'unique binary heap'
take <code>payload</code> instead of <code>pos</code> as argument.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">lt</span>
(optional) comparison function (less-than), see <a href="index.html#binaryHeap">binaryHeap</a>.
</li>
</ul>
<h3>Returns:</h3>
<ol>
the new heap
</ol>
</dd>
<dt>
<a name = "unique:insert"></a>
<strong>unique:insert (value, payload)</strong>
</dt>
<dd>
Inserts an element in the heap.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">value</span>
the value used for sorting this element
</li>
<li><span class="parameter">payload</span>
the payload attached to this element
</li>
</ul>
<h3>Returns:</h3>
<ol>
nothing, or throws an error on bad input
</ol>
</dd>
<dt>
<a name = "unique:peek"></a>
<strong>unique:peek ()</strong>
</dt>
<dd>
Returns the element at the top of the heap, without removing it.
<h3>Returns:</h3>
<ol>
payload, value, or <code>nil</code> if there is none
</ol>
</dd>
<dt>
<a name = "unique:peekValue"></a>
<strong>unique:peekValue ()</strong>
</dt>
<dd>
Returns the element at the top of the heap, without removing it.
<h3>Returns:</h3>
<ol>
value at the top, or <code>nil</code> if there is none
</ol>
<h3>Usage:</h3>
<ul>
<pre class="example"><span class="comment">-- simple timer based heap example
</span><span class="keyword">while</span> <span class="keyword">true</span> <span class="keyword">do</span>
sleep(heap:peekValue() - gettime()) <span class="comment">-- assume LuaSocket gettime function
</span> <span class="global">coroutine</span>.resume((heap:pop())) <span class="comment">-- assumes payload to be a coroutine,
</span> <span class="comment">-- double parens to drop extra return value
</span><span class="keyword">end</span></pre>
</ul>
</dd>
<dt>
<a name = "unique:pop"></a>
<strong>unique:pop ()</strong>
</dt>
<dd>
Removes the top of the heap and returns it.
When used with timers, <a href="index.html#unique:pop">pop</a> will return the payload that is due.</p>
<p> Note: this function returns <code>payload</code> as the first result to prevent
extra locals when retrieving the <code>payload</code>.
<h3>Returns:</h3>
<ol>
payload, value, or <code>nil</code> if there is none
</ol>
</dd>
<dt>
<a name = "unique:remove"></a>
<strong>unique:remove (payload)</strong>
</dt>
<dd>
Removes an element from the heap.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">payload</span>
the payload to remove
</li>
</ul>
<h3>Returns:</h3>
<ol>
value, payload or nil if not found
</ol>
</dd>
<dt>
<a name = "unique:update"></a>
<strong>unique:update (payload, newValue)</strong>
</dt>
<dd>
Updates the value of an element in the heap.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">payload</span>
the payoad whose value to update
</li>
<li><span class="parameter">newValue</span>
the new value to use for this payload
</li>
</ul>
<h3>Returns:</h3>
<ol>
nothing, or throws an error on bad input
</ol>
</dd>
<dt>
<a name = "unique:valueByPayload"></a>
<strong>unique:valueByPayload (payload)</strong>
</dt>
<dd>
Returns the value associated with the payload
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">payload</span>
the payload to lookup
</li>
</ul>
<h3>Returns:</h3>
<ol>
value or nil if no such payload exists
</ol>
</dd>
</dl>
</div> <!-- id="content" -->
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i>
<i style="float:right;">Last updated 2018-11-07 17:56:33 </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>
</html>

291
thirdparty/binaryheap.lua/docs/ldoc.css vendored Normal file
View file

@ -0,0 +1,291 @@
body {
color: #47555c;
font-size: 16px;
font-family: "Open Sans", sans-serif;
margin: 0;
background: #eff4ff;
}
a:link { color: #008fee; }
a:visited { color: #008fee; }
a:hover { color: #22a7ff; }
h1 { font-size:26px; font-weight: normal; }
h2 { font-size:22px; font-weight: normal; }
h3 { font-size:18px; font-weight: normal; }
h4 { font-size:16px; font-weight: bold; }
hr {
height: 1px;
background: #c1cce4;
border: 0px;
margin: 15px 0;
}
code, tt {
font-family: monospace;
}
span.parameter {
font-family: monospace;
font-weight: bold;
color: rgb(99, 115, 131);
}
span.parameter:after {
content:":";
}
span.types:before {
content:"(";
}
span.types:after {
content:")";
}
.type {
font-weight: bold; font-style:italic
}
p.name {
font-family: "Andale Mono", monospace;
}
#navigation {
float: left;
background-color: white;
border-right: 1px solid #d3dbec;
border-bottom: 1px solid #d3dbec;
width: 14em;
vertical-align: top;
overflow: visible;
}
#navigation br {
display: none;
}
#navigation h1 {
background-color: white;
border-bottom: 1px solid #d3dbec;
padding: 15px;
margin-top: 0px;
margin-bottom: 0px;
}
#navigation h2 {
font-size: 18px;
background-color: white;
border-bottom: 1px solid #d3dbec;
padding-left: 15px;
padding-right: 15px;
padding-top: 10px;
padding-bottom: 10px;
margin-top: 30px;
margin-bottom: 0px;
}
#content h1 {
background-color: #2c3e67;
color: white;
padding: 15px;
margin: 0px;
}
#content h2 {
background-color: #6c7ea7;
color: white;
padding: 15px;
padding-top: 15px;
padding-bottom: 15px;
margin-top: 0px;
}
#content h2 a {
background-color: #6c7ea7;
color: white;
text-decoration: none;
}
#content h2 a:hover {
text-decoration: underline;
}
#content h3 {
font-style: italic;
padding-top: 15px;
padding-bottom: 4px;
margin-right: 15px;
margin-left: 15px;
margin-bottom: 5px;
border-bottom: solid 1px #bcd;
}
#content h4 {
margin-right: 15px;
margin-left: 15px;
border-bottom: solid 1px #bcd;
}
#content pre {
margin: 15px;
}
pre {
background-color: rgb(50, 55, 68);
color: white;
border-radius: 3px;
/* border: 1px solid #C0C0C0; /* silver */
padding: 15px;
overflow: auto;
font-family: "Andale Mono", monospace;
}
#content ul pre.example {
margin-left: 0px;
}
table.index {
/* border: 1px #00007f; */
}
table.index td { text-align: left; vertical-align: top; }
#navigation ul
{
font-size:1em;
list-style-type: none;
margin: 1px 1px 10px 1px;
padding-left: 20px;
}
#navigation li {
text-indent: -1em;
display: block;
margin: 3px 0px 0px 22px;
}
#navigation li li a {
margin: 0px 3px 0px -1em;
}
#content {
margin-left: 14em;
}
#content p {
padding-left: 15px;
padding-right: 15px;
}
#content table {
padding-left: 15px;
padding-right: 15px;
background-color: white;
}
#content p, #content table, #content ol, #content ul, #content dl {
max-width: 900px;
}
#about {
padding: 15px;
padding-left: 16em;
background-color: white;
border-top: 1px solid #d3dbec;
border-bottom: 1px solid #d3dbec;
}
table.module_list, table.function_list {
border-width: 1px;
border-style: solid;
border-color: #cccccc;
border-collapse: collapse;
margin: 15px;
}
table.module_list td, table.function_list td {
border-width: 1px;
padding-left: 10px;
padding-right: 10px;
padding-top: 5px;
padding-bottom: 5px;
border: solid 1px rgb(193, 204, 228);
}
table.module_list td.name, table.function_list td.name {
background-color: white; min-width: 200px; border-right-width: 0px;
}
table.module_list td.summary, table.function_list td.summary {
background-color: white; width: 100%; border-left-width: 0px;
}
dl.function {
margin-right: 15px;
margin-left: 15px;
border-bottom: solid 1px rgb(193, 204, 228);
border-left: solid 1px rgb(193, 204, 228);
border-right: solid 1px rgb(193, 204, 228);
background-color: white;
}
dl.function dt {
color: rgb(99, 123, 188);
font-family: monospace;
border-top: solid 1px rgb(193, 204, 228);
padding: 15px;
}
dl.function dd {
margin-left: 15px;
margin-right: 15px;
margin-top: 5px;
margin-bottom: 15px;
}
#content dl.function dd h3 {
margin-top: 0px;
margin-left: 0px;
padding-left: 0px;
font-size: 16px;
color: rgb(128, 128, 128);
border-bottom: solid 1px #def;
}
#content dl.function dd ul, #content dl.function dd ol {
padding: 0px;
padding-left: 15px;
list-style-type: none;
}
ul.nowrap {
overflow:auto;
white-space:nowrap;
}
.section-description {
padding-left: 15px;
padding-right: 15px;
}
/* stop sublists from having initial vertical space */
ul ul { margin-top: 0px; }
ol ul { margin-top: 0px; }
ol ol { margin-top: 0px; }
ul ol { margin-top: 0px; }
/* make the target distinct; helps when we're navigating to a function */
a:target + * {
background-color: #FF9;
}
/* styles for prettification of source */
pre .comment { color: #bbccaa; }
pre .constant { color: #a8660d; }
pre .escape { color: #844631; }
pre .keyword { color: #ffc090; font-weight: bold; }
pre .library { color: #0e7c6b; }
pre .marker { color: #512b1e; background: #fedc56; font-weight: bold; }
pre .string { color: #8080ff; }
pre .number { color: #f8660d; }
pre .operator { color: #2239a8; font-weight: bold; }
pre .preprocessor, pre .prepro { color: #a33243; }
pre .global { color: #c040c0; }
pre .user-keyword { color: #800080; }
pre .prompt { color: #558817; }
pre .url { color: #272fc2; text-decoration: underline; }

View file

@ -0,0 +1,109 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head>
<title>binaryheap</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head>
<body>
<div id="container">
<div id="product">
<div id="product_logo"></div>
<div id="product_name"><big><b></b></big></div>
<div id="product_description"></div>
</div> <!-- id="product" -->
<div id="main">
<!-- Menu -->
<div id="navigation">
<br/>
<h1>binaryheap.lua</h1>
<h2>Topics</h2>
<ul class="">
<li><strong>readme</strong></li>
</ul>
<h2>Modules</h2>
<ul class="nowrap">
<li><a href="../index.html">binaryheap</a></li>
</ul>
</div>
<div id="content">
<h1>binaryheap.lua</h1>
<p><a href="http://en.wikipedia.org/wiki/Binary_heap">Binary heap</a> implementation</p>
<p>Both the <a href="https://github.com/Tieske/binaryheap.lua">source code</a> as well as the
<a href="http://tieske.github.io/binaryheap.lua">documentation</a> are on github</p>
<p>Based on <a href="http://lua-users.org/lists/lua-l/2015-04/msg00137.html">original code</a>
by Oliver Kroth, with
<a href="http://lua-users.org/lists/lua-l/2015-04/msg00133.html">extras</a>
as proposed by Sean Conner.</p>
<h1>Contributions</h1>
<p>This library was create by contributions from Oliver Kroth,
Thijs Schreijer, Boris Nagaev</p>
<h1>History</h1>
<p>Version 0.4, 7-Nov-2018</p>
<ul>
<li>[breaking] added additional tests, mostly on returning errors, minor behaviour changes</li>
<li>added <a href="../index.html#heap:size">size</a> method</li>
<li>fixed a lot of linter issues</li>
</ul>
<p>Version 0.3, 15-Jul-2018</p>
<ul>
<li>bugfix <a href="../index.html#unique:pop">unique:pop</a> returning wrong order results (by Daurnimator)</li>
<li>change <a href="../index.html#unique:peek">unique:peek</a> returning same order as <a href="../index.html#unique:pop">pop</a></li>
<li>added <a href="../index.html#unique:peekValue">unique:peekValue</a> returning just the value</li>
</ul>
<p>Version 0.2, 21-Apr-2015</p>
<ul>
<li>bugfix <a href="../index.html#heap:remove">remove</a> function (by Boris Nagaev)</li>
<li>configurable comparison function for the tree</li>
</ul>
<p>Version 0.1, 20-Apr-2015</p>
<ul>
<li>Initial release</li>
</ul>
<h1>Copyright</h1>
<p>Copyright 2015-2018 Thijs Schreijer</p>
<h1>License</h1>
<p>MIT/X11</p>
</div> <!-- id="content" -->
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i>
<i style="float:right;">Last updated 2018-11-07 17:56:33 </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>
</html>

View file

@ -0,0 +1,85 @@
-- Calculates shortest path of Knight from (1, 1) to (x, y).
-- Prints matrix of shortest paths for all cells.
-- Knight can't leave the rectangle from (1, 1) to (x, y).
-- See https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
-- See http://stackoverflow.com/questions/2339101
-- Usage:
-- $ lua knight_dijkstra.lua X Y
local binaryheap = require 'binaryheap'
local ROWS = tonumber(arg[2]) or 8
local COLS = tonumber(arg[1]) or 8
local unvisited = binaryheap.minUnique()
local function Cell(x, y)
return x .. '_' .. y
end
local function Coordinates(cell)
local x, y = cell:match('(%d+)_(%d+)')
return x, y
end
local function neighbours(cell)
local x, y = Coordinates(cell)
local gen = coroutine.wrap(function()
coroutine.yield(x - 1, y - 2)
coroutine.yield(x - 1, y + 2)
coroutine.yield(x + 1, y - 2)
coroutine.yield(x + 1, y + 2)
coroutine.yield(x - 2, y - 1)
coroutine.yield(x - 2, y + 1)
coroutine.yield(x + 2, y - 1)
coroutine.yield(x + 2, y + 1)
end)
return coroutine.wrap(function()
for xx, yy in gen do
if 1 <= xx and xx <= COLS and
1 <= yy and yy <= ROWS then
coroutine.yield(Cell(xx, yy))
end
end
end)
end
for y = 1, ROWS do
for x = 1, COLS do
local cell = Cell(x, y)
unvisited:insert(math.huge, cell)
end
end
unvisited:update('1_1', 0)
local final_distance = {}
while unvisited:peek() do
local current_distance, current = unvisited:peek()
assert(not final_distance[current])
final_distance[current] = current_distance
unvisited:remove(current)
-- update neighbours
local new_distance = current_distance + 1
for neighbour in neighbours(current) do
if unvisited.reverse[neighbour] then
local pos = unvisited.reverse[neighbour]
local distance = unvisited.value[pos]
if distance > new_distance then
unvisited:update(neighbour, new_distance)
end
end
end
end
for y = 1, ROWS do
local row = {}
for x = 1, COLS do
local cell = Cell(x, y)
local distance = final_distance[cell]
table.insert(row, distance)
end
print(table.concat(row, ' '))
end

View file

@ -0,0 +1,835 @@
local bh = require('binaryheap')
local data = {
{ value = 98, payload = "pos08" }, -- 1
{ value = 28, payload = "pos05" }, -- 2
{ value = 36, payload = "pos06" }, -- 3
{ value = 48, payload = "pos09" }, -- 4
{ value = 68, payload = "pos10" }, -- 5
{ value = 58, payload = "pos13" }, -- 6
{ value = 80, payload = "pos15" }, -- 7
{ value = 46, payload = "pos04" }, -- 8
{ value = 19, payload = "pos03" }, -- 9
{ value = 66, payload = "pos11" }, -- 10
{ value = 22, payload = "pos02" }, -- 11
{ value = 60, payload = "pos12" }, -- 12
{ value = 15, payload = "pos01" }, -- 13
{ value = 83, payload = "pos14" }, -- 14
{ value = 59, payload = "pos07" }, -- 15
}
local sort = function(t)
table.sort(t, function(a,b) return (a.value < b.value) end)
return t
end
local function check(heap)
for pos = 2, #heap.values do
local parent = math.floor(pos / 2)
assert(not heap.lt(heap.values[pos], heap.values[parent]))
end
if heap.payloads then
for pos in ipairs(heap.values) do
local payload = heap.payloads[pos]
assert(heap.reverse[payload] == pos)
end
end
end
local function newheap()
-- create a heap with data
local heap = bh.minUnique()
for _, node in ipairs(data) do
heap:insert(node.value,node.payload)
check(heap)
end
-- create a sorted list with data, sorted by 'value'
local sorted = {}
for k,v in pairs(data) do sorted[k] = v end
sort(sorted)
-- create a reverse list of the sorted table; returns sorted-index, based on 'value'
local sreverse = {}
for i,v in ipairs(sorted) do
sreverse[v.value] = i
end
return heap, sorted, sreverse
end
local function testheap(heap, sorted)
while sorted[1] do
local value1, payload1
if heap.reverse then
-- it is a unique heap
payload1, value1 = heap:pop()
else
-- it is a plain heap
value1, payload1 = heap:pop()
end
local value2, payload2 = sorted[1].value, sorted[1].payload
table.remove(sorted, 1)
assert.are.equal(payload1, payload2)
assert.are.equal(value1, value2)
end
end
describe("[minUnique]", function()
it("validates order of insertion", function()
local h = newheap()
assert.are.equal(h.payloads[1], data[13].payload)
assert.are.equal(h.payloads[2], data[11].payload)
assert.are.equal(h.payloads[3], data[9].payload)
assert.are.equal(h.payloads[4], data[8].payload)
assert.are.equal(h.payloads[5], data[2].payload)
assert.are.equal(h.payloads[6], data[3].payload)
assert.are.equal(h.payloads[7], data[15].payload)
assert.are.equal(h.payloads[8], data[1].payload)
assert.are.equal(h.payloads[9], data[4].payload)
assert.are.equal(h.payloads[10], data[5].payload)
assert.are.equal(h.payloads[11], data[10].payload)
assert.are.equal(h.payloads[12], data[12].payload)
assert.are.equal(h.payloads[13], data[6].payload)
assert.are.equal(h.payloads[14], data[14].payload)
assert.are.equal(h.payloads[15], data[7].payload)
end)
it("validates order of popping", function()
testheap(newheap())
end)
it("peek()", function()
local heap, sorted = newheap()
local payload, value = heap:peek()
-- correct values?
assert.are.equal(value, sorted[1].value)
assert.are.equal(payload, sorted[1].payload)
-- are they still on the heap?
assert.are.equal(value, heap.values[1])
assert.are.equal(payload, heap.payloads[1])
end)
it("peekValue()", function()
local h = bh.minUnique()
h:insert(1, 11)
assert.equal(1, h:peekValue(11))
-- try again empty
h:pop()
assert.is_nil(h:peekValue(11))
end)
it("pop()", function()
local h = bh.minUnique()
h:insert(3, 13)
h:insert(2, 12)
h:insert(1, 11)
-- try again empty
local pl, v
pl, v = h:pop()
assert.equal(v, 1)
assert.equal(pl, 11)
pl, v = h:pop()
assert.equal(v, 2)
assert.equal(pl, 12)
pl, v = h:pop()
assert.equal(v, 3)
assert.equal(pl, 13)
pl, v = h:pop()
assert.is_nil(v)
assert.is_nil(pl)
end)
describe("remove()", function()
it("a middle item", function()
local heap, sorted = newheap()
local idx = 4
local value = sorted[idx].value
local payload = sorted[idx].payload
local v, pl = heap:remove(payload)
check(heap)
-- did we get the right ones?
assert.are.equal(value, v)
assert.are.equal(payload, pl)
assert.is.Nil(heap[payload])
-- remove from test data and compare
table.remove(sorted, idx)
testheap(heap, sorted)
end)
it("the last item (of the array)", function()
local heap, sorted = newheap()
local idx = #heap.values
local value = sorted[idx].value
local payload = sorted[idx].payload
local v, pl = heap:remove(payload)
check(heap)
-- did we get the right ones?
assert.are.equal(value, v)
assert.are.equal(payload, pl)
assert.is.Nil(heap.reverse[payload])
-- remove from test data and compare
table.remove(sorted, idx)
testheap(heap, sorted)
end)
it("non existing payload returns nil", function()
local heap = newheap()
local v, pl = heap:remove({})
assert.is_nil(v)
assert.is_nil(pl)
end)
it("nil payload returns nil", function()
local heap = newheap()
local v, pl = heap:remove(nil)
assert.is_nil(v)
assert.is_nil(pl)
end)
it("with repeated values", function()
local h = bh.minUnique()
h:insert(1, 11)
check(h)
h:insert(1, 12)
check(h)
local value, payload
value, payload = h:remove(11)
check(h)
assert.equal(1, value)
assert.equal(11, payload)
payload, value = h:peek()
assert.equal(1, value)
assert.equal(12, payload)
assert.same({1}, h.values)
assert.same({12}, h.payloads)
assert.same({[12]=1}, h.reverse)
end)
end)
describe("insert()", function()
it("a top item", function()
local heap, sorted = newheap()
local nvalue = sorted[1].value - 10
local npayload = {}
table.insert(sorted, 1, {})
sorted[1].value = nvalue
sorted[1].payload = npayload
heap:insert(nvalue, npayload)
check(heap)
testheap(heap, sorted)
end)
it("a middle item", function()
local heap, sorted = newheap()
local nvalue = 57
local npayload = {}
table.insert(sorted, { value = nvalue, payload = npayload })
sort(sorted)
heap:insert(nvalue, npayload)
check(heap)
testheap(heap, sorted)
end)
it("a last item", function()
local heap, sorted = newheap()
local nvalue = sorted[#sorted].value + 10
local npayload = {}
table.insert(sorted, {})
sorted[#sorted].value = nvalue
sorted[#sorted].payload = npayload
heap:insert(nvalue, npayload)
check(heap)
testheap(heap, sorted)
end)
it("a nil value throws an error", function()
local heap = newheap()
assert.has.error(function()
heap:insert(nil, "something")
end)
end)
it("a nil payload throws an error", function()
local heap = newheap()
assert.has.error(function()
heap:insert(15, nil)
end)
end)
it("a duplicate payload throws an error", function()
local heap = newheap()
local value = {}
heap:insert(1, value)
assert.has.error(function()
heap:insert(2, value)
end)
end)
end)
describe("update()", function()
it("a top item", function()
local heap, sorted = newheap()
local idx = 1
local payload = sorted[idx].payload
local nvalue = sorted[#sorted].value + 1 -- move to end with new value
sorted[idx].value = nvalue
sort(sorted)
heap:update(payload, nvalue)
check(heap)
testheap(heap, sorted)
end)
it("a middle item", function()
local heap, sorted = newheap()
local idx = 4
local payload = sorted[idx].payload
local nvalue = sorted[idx].value * 2
sorted[idx].value = nvalue
sort(sorted)
heap:update(payload, nvalue)
check(heap)
testheap(heap, sorted)
end)
it("a last item", function()
local heap, sorted = newheap()
local idx = #sorted
local payload = sorted[idx].payload
local nvalue = sorted[1].value - 1 -- move to top with new value
sorted[idx].value = nvalue
sort(sorted)
heap:update(payload, nvalue)
check(heap)
testheap(heap, sorted)
end)
it("a nil value throws an error", function()
local heap, sorted = newheap()
local idx = #sorted
local payload = sorted[idx].payload
assert.has.error(function()
heap:update(payload, nil)
end)
end)
it("an unknown payload throws an error", function()
local heap = newheap()
assert.has.error(function()
heap:update({}, 10)
end)
end)
end)
describe("size()", function()
it("returns number of elements", function()
local h = bh.minUnique()
assert.equal(0, h:size())
h:insert(1, -1)
assert.equal(1, h:size())
h:insert(2, -2)
assert.equal(2, h:size())
h:insert(3, -3)
assert.equal(3, h:size())
h:insert(4, -4)
assert.equal(4, h:size())
h:insert(5, -5)
assert.equal(5, h:size())
h:pop()
assert.equal(4, h:size())
h:pop()
assert.equal(3, h:size())
h:pop()
assert.equal(2, h:size())
h:pop()
assert.equal(1, h:size())
h:pop()
assert.equal(0, h:size())
end)
end)
describe("valueByPayload()", function()
it("gets value by payload", function()
local h = bh.minUnique()
h:insert(1, -1)
h:insert(2, -2)
h:insert(3, -3)
h:insert(4, -4)
h:insert(5, -5)
assert.equal(1, h:valueByPayload((-1)))
assert.equal(2, h:valueByPayload((-2)))
assert.equal(3, h:valueByPayload((-3)))
assert.equal(4, h:valueByPayload((-4)))
assert.equal(5, h:valueByPayload((-5)))
h:remove(-1)
assert.falsy(h:valueByPayload((-1)))
end)
it("non existing payload returns nil", function()
local h = bh.minUnique()
h:insert(1, -1)
h:insert(2, -2)
h:insert(3, -3)
h:insert(4, -4)
h:insert(5, -5)
assert.is_nil(h:valueByPayload({}))
end)
it("nil payload returns nil", function()
local h = bh.minUnique()
h:insert(1, -1)
h:insert(2, -2)
h:insert(3, -3)
h:insert(4, -4)
h:insert(5, -5)
assert.is_nil(h:valueByPayload(nil))
end)
end)
it("creates minUnique with custom less-than function", function()
local h = bh.minUnique(function (a, b)
return math.abs(a) < math.abs(b)
end)
h:insert(1, -1)
check(h)
h:insert(-2, 2)
check(h)
h:insert(3, -3)
check(h)
h:insert(-4, 4)
check(h)
h:insert(5, -5)
check(h)
local value, payload
payload, value = h:peek()
assert.equal(1, value)
assert.equal(-1, payload)
h:pop()
check(h)
payload, value = h:peek()
assert.equal(-2, value)
assert.equal(2, payload)
end)
end)
describe("[maxUnique]", function()
it("creates maxUnique with custom less-than function", function()
local h = bh.maxUnique(function (a, b)
return math.abs(a) > math.abs(b)
end)
h:insert(1, -1)
check(h)
h:insert(-2, 2)
check(h)
h:insert(3, -3)
check(h)
h:insert(-4, 4)
check(h)
h:insert(5, -5)
check(h)
local value, payload
payload, value = h:peek()
assert.equal(5, value)
assert.equal(-5, payload)
h:pop()
check(h)
payload, value = h:peek()
assert.equal(-4, value)
assert.equal(4, payload)
end)
end)
describe("[minHeap]", function()
it("creates minHeap", function()
local h = bh.minHeap()
check(h)
end)
describe("insert()", function()
it("a number into minHeap", function()
local h = bh.minHeap()
h:insert(42)
check(h)
end)
it("nil throws an error", function()
local h = bh.minHeap()
assert.has.error(function()
h:insert(nil)
end)
end)
end)
describe("remove()", function()
it("a position", function()
local h = bh.minHeap()
h:insert(42)
h:insert(43)
assert.equal(43, h:remove(2))
check(h)
end)
it("a bad position returns nil", function()
local h = bh.minHeap()
h:insert(42)
h:insert(43)
assert.is_nil(h:remove(0))
assert.is_nil(h:remove(3))
check(h)
end)
end)
describe("size()", function()
it("returns number of elements", function()
local h = bh.minHeap()
assert.equal(0, h:size())
h:insert(1)
assert.equal(1, h:size())
h:insert(2)
assert.equal(2, h:size())
h:insert(3)
assert.equal(3, h:size())
h:insert(4)
assert.equal(4, h:size())
h:insert(5)
assert.equal(5, h:size())
h:pop()
assert.equal(4, h:size())
h:pop()
assert.equal(3, h:size())
h:pop()
assert.equal(2, h:size())
h:pop()
assert.equal(1, h:size())
h:pop()
assert.equal(0, h:size())
end)
end)
describe("peek()", function()
it("return nil in empty minHeap", function()
local h = bh.minHeap()
assert.is_nil(h:peek())
check(h)
end)
it("minHeap of one element", function()
local h = bh.minHeap()
h:insert(42)
check(h)
local value, payload = h:peek()
assert.equal(42, value)
assert.falsy(payload)
end)
it("minHeap of two elements", function()
local h = bh.minHeap()
h:insert(42)
check(h)
h:insert(1)
check(h)
local value, payload = h:peek()
assert.equal(1, value)
assert.falsy(payload)
end)
it("minHeap of 10 elements", function()
local h = bh.minHeap()
h:insert(10)
h:insert(7)
h:insert(1)
h:insert(5)
h:insert(6)
h:insert(9)
h:insert(8)
h:insert(4)
h:insert(2)
h:insert(3)
check(h)
local value, payload = h:peek()
assert.equal(1, value)
assert.falsy(payload)
end)
it("removes peek in minHeap of 5 elements", function()
local h = bh.minHeap()
h:insert(1)
h:insert(2)
h:insert(3)
h:insert(4)
h:insert(5)
local value
value = h:pop()
check(h)
assert.equal(1, value)
value = h:peek()
assert.equal(2, value)
end)
end)
describe("update()", function()
it("in minHeap of 5 elements (pos 2 -> pos 1)", function()
local h = bh.minHeap()
h:insert(1)
h:insert(2)
h:insert(3)
h:insert(4)
h:insert(5)
check(h)
h:update(2, -100)
check(h)
local value = h:peek()
assert.equal(-100, value)
end)
it("in minHeap of 5 elements (pos 1 -> pos 2)", function()
local h = bh.minHeap()
h:insert(1)
h:insert(2)
h:insert(3)
h:insert(4)
h:insert(5)
check(h)
h:update(1, 100)
check(h)
local value = h:peek()
assert.equal(2, value)
end)
it("nil throws an error", function()
local h = bh.minHeap()
h:insert(10)
assert.has.error(function()
h:update(nil)
end)
end)
it("bad position throws an error", function()
local h = bh.minHeap()
h:insert(10)
assert.has.error(function()
h:update(0)
end)
assert.has.error(function()
h:update(2)
end)
end)
end)
it("creates minHeap with custom less-than function", function()
local h = bh.minHeap(function (a, b)
return math.abs(a) < math.abs(b)
end)
h:insert(1)
check(h)
h:insert(-2)
check(h)
h:insert(3)
check(h)
h:insert(-4)
check(h)
h:insert(5)
check(h)
assert.equal(1, h:peek())
h:pop()
check(h)
assert.equal(-2, h:peek())
end)
end)
describe("[maxHeap]", function()
it("creates maxHeap", function()
local h = bh.maxHeap()
check(h)
end)
describe("insert()", function()
it("inserts a number into maxHeap", function()
local h = bh.maxHeap()
h:insert(42)
check(h)
end)
it("nil throws an error", function()
local h = bh.maxHeap()
assert.has.error(function()
h:insert(nil)
end)
end)
end)
describe("remove()", function()
it("a position", function()
local h = bh.maxHeap()
h:insert(42)
h:insert(43)
assert.equal(42, h:remove(2))
check(h)
end)
it("a bad position returns nil", function()
local h = bh.maxHeap()
h:insert(42)
h:insert(43)
assert.is_nil(h:remove(0))
assert.is_nil(h:remove(3))
check(h)
end)
end)
describe("size()", function()
it("returns number of elements", function()
local h = bh.minHeap()
assert.equal(0, h:size())
h:insert(1)
assert.equal(1, h:size())
h:insert(2)
assert.equal(2, h:size())
h:insert(3)
assert.equal(3, h:size())
h:insert(4)
assert.equal(4, h:size())
h:insert(5)
assert.equal(5, h:size())
h:pop()
assert.equal(4, h:size())
h:pop()
assert.equal(3, h:size())
h:pop()
assert.equal(2, h:size())
h:pop()
assert.equal(1, h:size())
h:pop()
assert.equal(0, h:size())
end)
end)
describe("peek()", function()
it("return nil in empty maxHeap", function()
local h = bh.maxHeap()
assert.is_nil(h:peek())
check(h)
end)
it("maxHeap of one element", function()
local h = bh.maxHeap()
h:insert(42)
check(h)
local value = h:peek()
assert.equal(42, value)
end)
it("maxHeap of two elements", function()
local h = bh.maxHeap()
h:insert(42)
check(h)
h:insert(1)
check(h)
local value = h:peek()
assert.equal(42, value)
end)
it("maxHeap of 10 elements", function()
local h = bh.maxHeap()
h:insert(10)
h:insert(7)
h:insert(1)
h:insert(5)
h:insert(6)
h:insert(9)
h:insert(8)
h:insert(4)
h:insert(2)
h:insert(3)
check(h)
local value = h:peek()
assert.equal(10, value)
end)
it("removes peek in maxHeap of 5 elements", function()
local h = bh.maxHeap()
h:insert(1)
h:insert(2)
h:insert(3)
h:insert(4)
h:insert(5)
check(h)
local value
value = h:pop()
check(h)
assert.equal(5, value)
value = h:peek()
assert.equal(4, value)
end)
end)
describe("update()", function()
it("in maxHeap of 5 elements (pos 2 -> pos 1)", function()
local h = bh.maxHeap()
h:insert(1)
h:insert(2)
h:insert(3)
h:insert(4)
h:insert(5)
check(h)
h:update(2, 100)
check(h)
local value = h:peek()
assert.equal(100, value)
end)
it("in maxHeap of 5 elements (pos 1 -> pos 2)", function()
local h = bh.maxHeap()
h:insert(1)
h:insert(2)
h:insert(3)
h:insert(4)
h:insert(5)
check(h)
h:update(1, -100)
check(h)
local value = h:peek()
assert.equal(4, value)
end)
it("nil throws an error", function()
local h = bh.maxHeap()
h:insert(10)
assert.has.error(function()
h:update(nil)
end)
end)
it("bad position throws an error", function()
local h = bh.maxHeap()
h:insert(10)
assert.has.error(function()
h:update(0)
end)
assert.has.error(function()
h:update(2)
end)
end)
end)
it("creates maxHeap with custom greater-than function", function()
local h = bh.maxHeap(function (a, b)
return math.abs(a) > math.abs(b)
end)
h:insert(1)
check(h)
h:insert(-2)
check(h)
h:insert(3)
check(h)
h:insert(-4)
check(h)
h:insert(5)
check(h)
assert.equal(5, (h:peek()))
h:pop()
check(h)
assert.equal(-4, (h:peek()))
end)
end)

View file

@ -0,0 +1,95 @@
describe("dijkstras algorithm with binaryheap", function()
-- See https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
it("calculates knight's shortest path", function()
-- See http://stackoverflow.com/questions/2339101
local binaryheap = require 'binaryheap'
local ROWS = 8
local COLS = 8
local unvisited = binaryheap.minUnique()
local function Cell(x, y)
return x .. '_' .. y
end
local function Coordinates(cell)
local x, y = cell:match('(%d+)_(%d+)')
return x, y
end
local function neighbours(cell)
local x, y = Coordinates(cell)
local gen = coroutine.wrap(function()
coroutine.yield(x - 1, y - 2)
coroutine.yield(x - 1, y + 2)
coroutine.yield(x + 1, y - 2)
coroutine.yield(x + 1, y + 2)
coroutine.yield(x - 2, y - 1)
coroutine.yield(x - 2, y + 1)
coroutine.yield(x + 2, y - 1)
coroutine.yield(x + 2, y + 1)
end)
return coroutine.wrap(function()
for xx, yy in gen do
if 1 <= xx and xx <= COLS and
1 <= yy and yy <= ROWS then
coroutine.yield(Cell(xx, yy))
end
end
end)
end
for y = 1, ROWS do
for x = 1, COLS do
local cell = Cell(x, y)
unvisited:insert(math.huge, cell)
end
end
unvisited:update('1_1', 0)
local final_distance = {}
while unvisited:peekValue() do
local current, current_distance = unvisited:peek()
assert(not final_distance[current])
final_distance[current] = current_distance
unvisited:remove(current)
-- update neighbours
local new_distance = current_distance + 1
for neighbour in neighbours(current) do
if unvisited.reverse[neighbour] then
local pos = unvisited.reverse[neighbour]
local distance = unvisited.values[pos]
if distance > new_distance then
unvisited:update(neighbour, new_distance)
end
end
end
end
local rows = {}
for y = 1, ROWS do
local row = {}
for x = 1, COLS do
local cell = Cell(x, y)
local distance = final_distance[cell]
table.insert(row, distance)
end
table.insert(rows, table.concat(row, ' '))
end
assert.equal([[
0 3 2 3 2 3 4 5
3 4 1 2 3 4 3 4
2 1 4 3 2 3 4 5
3 2 3 2 3 4 3 4
2 3 2 3 4 3 4 5
3 4 3 4 3 4 5 4
4 3 4 3 4 5 4 5
5 4 5 4 5 4 5 6]],
table.concat(rows, '\n'))
end)
end)

View file

@ -0,0 +1,406 @@
-------------------------------------------------------------------
-- Binary heap implementation
--
-- A binary heap (or binary tree) is a [sorting algorithm](http://en.wikipedia.org/wiki/Binary_heap).
--
-- The 'plain binary heap' is managed by positions. Which are hard to get once
-- an element is inserted. It can be anywhere in the list because it is re-sorted
-- upon insertion/deletion of items. The array with values is stored in field
-- `values`:
--
-- `peek = heap.values[1]`
--
-- A 'unique binary heap' is where the payload is unique and the payload itself
-- also stored (as key) in the heap with the position as value, as in;
-- `heap.reverse[payload] = [pos]`
--
-- Due to this setup the reverse search, based on payload, is now a
-- much faster operation because instead of traversing the list/heap,
-- you can do;
-- `pos = heap.reverse[payload]`
--
-- This means that deleting elements from a 'unique binary heap' is
-- faster than from a plain heap.
--
-- All management functions in the 'unique binary heap' take `payload`
-- instead of `pos` as argument.
-- Note that the value of the payload must be unique!
--
-- Fields of heap object:
--
-- * values - array of values
-- * payloads - array of payloads (unique binary heap only)
-- * reverse - map from payloads to indices (unique binary heap only)
local assert = assert
local floor = math.floor
local _ENV = nil
local M = {}
--================================================================
-- basic heap sorting algorithm
--================================================================
--- Basic heap.
-- This is the base implementation of the heap. Under regular circumstances
-- this should not be used, instead use a _Plain heap_ or _Unique heap_.
-- @section baseheap
--- Creates a new binary heap.
-- This is the core of all heaps, the others
-- are built upon these sorting functions.
-- @param swap (function) `swap(heap, idx1, idx2)` swaps values at
-- `idx1` and `idx2` in the heaps `heap.values` and `heap.payloads` lists (see
-- return value below).
-- @param erase (function) `swap(heap, position)` raw removal
-- @param lt (function) in `lt(a, b)` returns `true` when `a < b` (for a min-heap)
-- @return table with two methods; `heap:bubbleUp(pos)` and `heap:sinkDown(pos)`
-- that implement the sorting algorithm and two fields; `heap.values` and
-- `heap.payloads` being lists, holding the values and payloads respectively.
M.binaryHeap = function(swap, erase, lt)
local heap = {
values = {}, -- list containing values
erase = erase,
swap = swap,
lt = lt,
}
function heap:bubbleUp(pos)
local values = self.values
while pos>1 do
local parent = floor(pos/2)
if not lt(values[pos], values[parent]) then
break
end
swap(self, parent, pos)
pos = parent
end
end
function heap:sinkDown(pos)
local values = self.values
local last = #values
while true do
local min = pos
local child = 2 * pos
for c = child, child + 1 do
if c <= last and lt(values[c], values[min]) then min = c end
end
if min == pos then break end
swap(self, pos, min)
pos = min
end
end
return heap
end
--================================================================
-- plain heap management functions
--================================================================
--- Plain heap.
-- A plain heap carries a single piece of information per entry. This can be
-- any type (except `nil`), as long as the comparison function used to create
-- the heap can handle it.
-- @section plainheap
do end -- luacheck: ignore
-- the above is to trick ldoc (otherwise `update` below disappears)
local update
--- Updates the value of an element in the heap.
-- @function heap:update
-- @param pos the position which value to update
-- @param newValue the new value to use for this payload
update = function(self, pos, newValue)
assert(newValue ~= nil, "cannot add 'nil' as value")
assert(pos >= 1 and pos <= #self.values, "illegal position")
self.values[pos] = newValue
if pos > 1 then self:bubbleUp(pos) end
if pos < #self.values then self:sinkDown(pos) end
end
local remove
--- Removes an element from the heap.
-- @function heap:remove
-- @param pos the position to remove
-- @return value, or nil if a bad `pos` value was provided
remove = function(self, pos)
local last = #self.values
if pos < 1 then
return -- bad pos
elseif pos < last then
local v = self.values[pos]
self:swap(pos, last)
self:erase(last)
self:bubbleUp(pos)
self:sinkDown(pos)
return v
elseif pos == last then
local v = self.values[pos]
self:erase(last)
return v
else
return -- bad pos: pos > last
end
end
local insert
--- Inserts an element in the heap.
-- @function heap:insert
-- @param value the value used for sorting this element
-- @return nothing, or throws an error on bad input
insert = function(self, value)
assert(value ~= nil, "cannot add 'nil' as value")
local pos = #self.values + 1
self.values[pos] = value
self:bubbleUp(pos)
end
local pop
--- Removes the top of the heap and returns it.
-- @function heap:pop
-- @return value at the top, or `nil` if there is none
pop = function(self)
if self.values[1] ~= nil then
return remove(self, 1)
end
end
local peek
--- Returns the element at the top of the heap, without removing it.
-- @function heap:peek
-- @return value at the top, or `nil` if there is none
peek = function(self)
return self.values[1]
end
local size
--- Returns the number of elements in the heap.
-- @function heap:size
-- @return number of elements
size = function(self)
return #self.values
end
local function swap(heap, a, b)
heap.values[a], heap.values[b] = heap.values[b], heap.values[a]
end
local function erase(heap, pos)
heap.values[pos] = nil
end
--================================================================
-- plain heap creation
--================================================================
local function plainHeap(lt)
local h = M.binaryHeap(swap, erase, lt)
h.peek = peek
h.pop = pop
h.size = size
h.remove = remove
h.insert = insert
h.update = update
return h
end
--- Creates a new min-heap, where the smallest value is at the top.
-- @param lt (optional) comparison function (less-than), see `binaryHeap`.
-- @return the new heap
M.minHeap = function(lt)
if not lt then
lt = function(a,b) return (a < b) end
end
return plainHeap(lt)
end
--- Creates a new max-heap, where the largest value is at the top.
-- @param gt (optional) comparison function (greater-than), see `binaryHeap`.
-- @return the new heap
M.maxHeap = function(gt)
if not gt then
gt = function(a,b) return (a > b) end
end
return plainHeap(gt)
end
--================================================================
-- unique heap management functions
--================================================================
--- Unique heap.
-- A unique heap carries 2 pieces of information per entry.
--
-- 1. The `value`, this is used for ordering the heap. It can be any type (except
-- `nil`), as long as the comparison function used to create the heap can
-- handle it.
-- 2. The `payload`, this can be any type (except `nil`), but it MUST be unique.
--
-- With the 'unique heap' it is easier to remove elements from the heap.
-- @section uniqueheap
do end -- luacheck: ignore
-- the above is to trick ldoc (otherwise `update` below disappears)
local updateU
--- Updates the value of an element in the heap.
-- @function unique:update
-- @param payload the payoad whose value to update
-- @param newValue the new value to use for this payload
-- @return nothing, or throws an error on bad input
function updateU(self, payload, newValue)
return update(self, self.reverse[payload], newValue)
end
local insertU
--- Inserts an element in the heap.
-- @function unique:insert
-- @param value the value used for sorting this element
-- @param payload the payload attached to this element
-- @return nothing, or throws an error on bad input
function insertU(self, value, payload)
assert(self.reverse[payload] == nil, "duplicate payload")
local pos = #self.values + 1
self.reverse[payload] = pos
self.payloads[pos] = payload
return insert(self, value)
end
local removeU
--- Removes an element from the heap.
-- @function unique:remove
-- @param payload the payload to remove
-- @return value, payload or nil if not found
function removeU(self, payload)
local pos = self.reverse[payload]
if pos ~= nil then
return remove(self, pos), payload
end
end
local popU
--- Removes the top of the heap and returns it.
-- When used with timers, `pop` will return the payload that is due.
--
-- Note: this function returns `payload` as the first result to prevent
-- extra locals when retrieving the `payload`.
-- @function unique:pop
-- @return payload, value, or `nil` if there is none
function popU(self)
if self.values[1] then
local payload = self.payloads[1]
local value = remove(self, 1)
return payload, value
end
end
local peekU
--- Returns the element at the top of the heap, without removing it.
-- @function unique:peek
-- @return payload, value, or `nil` if there is none
peekU = function(self)
return self.payloads[1], self.values[1]
end
local peekValueU
--- Returns the element at the top of the heap, without removing it.
-- @function unique:peekValue
-- @return value at the top, or `nil` if there is none
-- @usage -- simple timer based heap example
-- while true do
-- sleep(heap:peekValue() - gettime()) -- assume LuaSocket gettime function
-- coroutine.resume((heap:pop())) -- assumes payload to be a coroutine,
-- -- double parens to drop extra return value
-- end
peekValueU = function(self)
return self.values[1]
end
local valueByPayload
--- Returns the value associated with the payload
-- @function unique:valueByPayload
-- @param payload the payload to lookup
-- @return value or nil if no such payload exists
valueByPayload = function(self, payload)
return self.values[self.reverse[payload]]
end
local sizeU
--- Returns the number of elements in the heap.
-- @function heap:size
-- @return number of elements
sizeU = function(self)
return #self.values
end
local function swapU(heap, a, b)
local pla, plb = heap.payloads[a], heap.payloads[b]
heap.reverse[pla], heap.reverse[plb] = b, a
heap.payloads[a], heap.payloads[b] = plb, pla
swap(heap, a, b)
end
local function eraseU(heap, pos)
local payload = heap.payloads[pos]
heap.reverse[payload] = nil
heap.payloads[pos] = nil
erase(heap, pos)
end
--================================================================
-- unique heap creation
--================================================================
local function uniqueHeap(lt)
local h = M.binaryHeap(swapU, eraseU, lt)
h.payloads = {} -- list contains payloads
h.reverse = {} -- reverse of the payloads list
h.peek = peekU
h.peekValue = peekValueU
h.valueByPayload = valueByPayload
h.pop = popU
h.size = sizeU
h.remove = removeU
h.insert = insertU
h.update = updateU
return h
end
--- Creates a new min-heap with unique payloads.
-- A min-heap is where the smallest value is at the top.
--
-- *NOTE*: All management functions in the 'unique binary heap'
-- take `payload` instead of `pos` as argument.
-- @param lt (optional) comparison function (less-than), see `binaryHeap`.
-- @return the new heap
M.minUnique = function(lt)
if not lt then
lt = function(a,b) return (a < b) end
end
return uniqueHeap(lt)
end
--- Creates a new max-heap with unique payloads.
-- A max-heap is where the largest value is at the top.
--
-- *NOTE*: All management functions in the 'unique binary heap'
-- take `payload` instead of `pos` as argument.
-- @param gt (optional) comparison function (greater-than), see `binaryHeap`.
-- @return the new heap
M.maxUnique = function(gt)
if not gt then
gt = function(a,b) return (a > b) end
end
return uniqueHeap(gt)
end
return M

14
thirdparty/copas/.editorconfig vendored Normal file
View file

@ -0,0 +1,14 @@
root = true
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8
[*.lua]
indent_style = space
indent_size = 2
[Makefile]
indent_style = tab

6
thirdparty/copas/.gitignore vendored Normal file
View file

@ -0,0 +1,6 @@
.DS_Store
**/*.srl
**/*.pem
*.rock
luacov.report.out
luacov.stats.out

31
thirdparty/copas/.luacheckrc vendored Normal file
View file

@ -0,0 +1,31 @@
--std = "ngx_lua+busted"
unused_args = false
redefined = false
max_line_length = false
globals = {
--"_KONG",
--"kong",
--"ngx.IS_CLI",
}
not_globals = {
"string.len",
"table.getn",
}
ignore = {
--"6.", -- ignore whitespace warnings
}
exclude_files = {
".install/**",
".luarocks/**",
--"spec/fixtures/invalid-module.lua",
--"spec-old-api/fixtures/invalid-module.lua",
}

4
thirdparty/copas/.luacov vendored Normal file
View file

@ -0,0 +1,4 @@
modules = {
["copas"] = "src/copas.lua",
["copas.*"] = "src"
}

21
thirdparty/copas/LICENSE vendored Normal file
View file

@ -0,0 +1,21 @@
Copyright © 2005-2013 Kepler Project, 2015-2023 Thijs Schreijer.
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.

75
thirdparty/copas/Makefile vendored Normal file
View file

@ -0,0 +1,75 @@
# $Id: Makefile,v 1.3 2007/10/29 22:50:16 carregal Exp $
DESTDIR ?=
# Default prefix
PREFIX ?= /usr/local
# System's lua directory (where Lua libraries are installed)
LUA_DIR ?= $(PREFIX)/share/lua/5.1
DELIM=-e "print(([[=]]):rep(70))"
PKGPATH=-e "package.path='src/?.lua;'..package.path"
# Lua interpreter
LUA=lua
.PHONY: certs
install:
mkdir -p $(DESTDIR)$(LUA_DIR)/copas
cp src/copas.lua $(DESTDIR)$(LUA_DIR)/copas.lua
cp src/copas/ftp.lua $(DESTDIR)$(LUA_DIR)/copas/ftp.lua
cp src/copas/smtp.lua $(DESTDIR)$(LUA_DIR)/copas/smtp.lua
cp src/copas/http.lua $(DESTDIR)$(LUA_DIR)/copas/http.lua
cp src/copas/timer.lua $(DESTDIR)$(LUA_DIR)/copas/timer.lua
cp src/copas/lock.lua $(DESTDIR)$(LUA_DIR)/copas/lock.lua
cp src/copas/semaphore.lua $(DESTDIR)$(LUA_DIR)/copas/semaphore.lua
cp src/copas/queue.lua $(DESTDIR)$(LUA_DIR)/copas/queue.lua
tests/certs/clientA.pem:
cd ./tests/certs && \
./rootA.sh && \
./rootB.sh && \
./serverA.sh && \
./serverB.sh && \
./clientA.sh && \
./clientB.sh && \
cd ../..
certs: tests/certs/clientA.pem
test: certs
$(LUA) $(DELIM) $(PKGPATH) tests/close.lua
$(LUA) $(DELIM) $(PKGPATH) tests/connecttwice.lua
$(LUA) $(DELIM) $(PKGPATH) tests/errhandlers.lua
$(LUA) $(DELIM) $(PKGPATH) tests/exit.lua
$(LUA) $(DELIM) $(PKGPATH) tests/exittest.lua
$(LUA) $(DELIM) $(PKGPATH) tests/http-timeout.lua
$(LUA) $(DELIM) $(PKGPATH) tests/httpredirect.lua
$(LUA) $(DELIM) $(PKGPATH) tests/largetransfer.lua
$(LUA) $(DELIM) $(PKGPATH) tests/lock.lua
$(LUA) $(DELIM) $(PKGPATH) tests/loop_starter.lua
$(LUA) $(DELIM) $(PKGPATH) tests/pause.lua
$(LUA) $(DELIM) $(PKGPATH) tests/queue.lua
$(LUA) $(DELIM) $(PKGPATH) tests/removeserver.lua
$(LUA) $(DELIM) $(PKGPATH) tests/removethread.lua
$(LUA) $(DELIM) $(PKGPATH) tests/request.lua 'http://www.google.com'
$(LUA) $(DELIM) $(PKGPATH) tests/request.lua 'https://www.google.nl' true
$(LUA) $(DELIM) $(PKGPATH) tests/semaphore.lua
$(LUA) $(DELIM) $(PKGPATH) tests/starve.lua
$(LUA) $(DELIM) $(PKGPATH) tests/tcptimeout.lua
$(LUA) $(DELIM) $(PKGPATH) tests/timer.lua
$(LUA) $(DELIM) $(PKGPATH) tests/timeout_errors.lua
$(LUA) $(DELIM) $(PKGPATH) tests/tls-sni.lua
$(LUA) $(DELIM) $(PKGPATH) tests/udptimeout.lua
$(LUA) $(DELIM)
coverage:
$(RM) luacov.stats.out
$(MAKE) test LUA="$(LUA) -lluacov"
luacov
clean:
$(RM) luacov.stats.out luacov.report.out
$(RM) tests/certs/*.pem tests/certs/*.srl

16
thirdparty/copas/Makefile.win vendored Normal file
View file

@ -0,0 +1,16 @@
# $Id: Makefile.win,v 1.5 2008/01/16 18:07:17 mascarenhas Exp $
LUA_DIR= c:\lua5.1\lua
build clean:
install:
mkdir "$(LUA_DIR)\copas"
copy src\copas.lua "$(LUA_DIR)\copas.lua"
copy src\copas\ftp.lua "$(LUA_DIR)\copas\ftp.lua"
copy src\copas\http.lua "$(LUA_DIR)\copas\http.lua"
copy src\copas\lock.lua "$(LUA_DIR)\copas\lock.lua"
copy src\copas\queue.lua "$(LUA_DIR)\copas\queue.lua"
copy src\copas\semaphore.lua "$(LUA_DIR)\copas\semaphore.lua"
copy src\copas\smtp.lua "$(LUA_DIR)\copas\smtp.lua"
copy src\copas\timer.lua "$(LUA_DIR)\copas\timer.lua"

36
thirdparty/copas/README.md vendored Normal file
View file

@ -0,0 +1,36 @@
# Copas 4.7
[![Unix build](https://img.shields.io/github/actions/workflow/status/lunarmodules/copas/unix_build.yml?branch=master&label=Unix%20build&logo=linux)](https://github.com/lunarmodules/copas/actions)
[![Coveralls code coverage](https://img.shields.io/coveralls/github/lunarmodules/copas?logo=coveralls)](https://coveralls.io/github/lunarmodules/copas)
[![Luacheck](https://github.com/lunarmodules/copas/workflows/Luacheck/badge.svg)](https://github.com/lunarmodules/copas/actions)
[![SemVer](https://img.shields.io/github/v/tag/lunarmodules/copas?color=brightgreen&label=SemVer&logo=semver&sort=semver)](CHANGELOG.md)
[![Licence](http://img.shields.io/badge/Licence-MIT-brightgreen.svg)](LICENSE)
Copas is a dispatcher based on coroutines that can be used for asynchronous networking. For example TCP or UDP based servers. But it also features timers and client support for http(s), ftp and smtp requests.
It uses [LuaSocket](https://github.com/diegonehab/luasocket) as the interface with the TCP/IP stack and [LuaSec](https://github.com/brunoos/luasec) for ssl support.
A server or thread 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](http://keplerproject.github.io/xavante/) as an example.
Copas is free software and uses the same license as Lua (MIT), and can be downloaded from [its GitHub page](https://github.com/lunarmodules/copas).
The easiest way to install Copas is through [LuaRocks](https://luarocks.org/):
```
luarocks install copas
```
For more details see [the documentation](http://lunarmodules.github.io/copas/).
### Releasing a new version
- update changelog in docs (`index.html`, update `history` and `status` sections)
- update version in `copas.lua`
- update version at the top of this README,
- update copyright years if needed
- update rockspec
- commit as `release X.Y.Z`
- tag as `vX_Y_Z` and as `X.Y.Z`
- push commit and tag
- upload to luarocks
- test luarocks installation

85
thirdparty/copas/bin/copas.lua vendored Executable file
View file

@ -0,0 +1,85 @@
#!/usr/bin/env lua
-- luacheck: globals copas
copas = require("copas")
-- Error handler that forces an application exit
local function errorhandler(err, co, skt)
io.stderr:write(copas.gettraceback(err, co, skt).."\n")
os.exit(1)
end
local function version_info()
print(copas._VERSION, copas._COPYRIGHT)
print(copas._DESCRIPTION)
print("Lua VM:", _G._VERSION)
end
local function load_lib(lib_name)
require(lib_name)
end
local function run_code(code)
if loadstring then -- deprecated in Lua 5.2
assert(loadstring(code, "command line"))()
else
assert(load(code, "command line"))()
end
end
local function run_stdin()
assert(loadfile())()
end
local function run_file(filename, i)
-- shift arguments, such that the Lua file being executed is at index 0. The
-- first argument following the name is at index 1.
local last = #arg
local first = #arg
for idx, v in pairs(arg) do
if idx < first then first = idx end
end
for n = first - i, last do
arg[n] = arg[n+i] -- luacheck: ignore
end
assert(loadfile(filename))()
end
local function show_usage()
print([[
usage: copas [options]... [script [args]...].
Available options are:
-e chunk Execute string 'chunk'.
-l name Require library 'name'.
-v Show version information.
-- Stop handling options.
- Execute stdin and stop handling options.]])
os.exit(1)
end
copas(function()
copas.seterrorhandler(errorhandler)
local i = 0
while i < math.max(#arg, 1) do -- if no args, use 1 being 'nil'
i = i + 1
local handled = false
local opt = arg[i] or "-" -- set default action if no args
-- options to continue handling
if opt == "-v" then version_info() handled = true end
if opt == "-l" then i = i + 1 load_lib(arg[i]) handled = true end
if opt == "-e" then i = i + 1 run_code(arg[i]) handled = true end
-- options that terminate handling
if opt == "--" then return end
if opt == "-" then return run_stdin() end
if opt:sub(1,1) == "-" and not handled then return show_usage() end
if not handled then return run_file(opt, i) end
end
end)

56
thirdparty/copas/copas-cvs-6.rockspec vendored Normal file
View file

@ -0,0 +1,56 @@
local package_name = "copas"
local package_version = "cvs"
local rockspec_revision = "6"
local github_account_name = "lunarmodules"
local github_repo_name = package_name
package = package_name
version = package_version.."-"..rockspec_revision
source = {
url = "git+https://github.com/"..github_account_name.."/"..github_repo_name..".git",
branch = (package_version == "cvs") and "master" or nil,
tag = (package_version ~= "cvs") and package_version or nil,
}
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 = "https://github.com/"..github_account_name.."/"..github_repo_name,
}
dependencies = {
"lua >= 5.1, < 5.5",
"luasocket >= 2.1, <= 3.0rc1-2",
"coxpcall >= 1.14",
"binaryheap >= 0.4",
"timerwheel ~> 1",
}
build = {
type = "builtin",
install = {
bin = {
copas = "bin/copas.lua",
}
},
modules = {
["copas"] = "src/copas.lua",
["copas.http"] = "src/copas/http.lua",
["copas.ftp"] = "src/copas/ftp.lua",
["copas.smtp"] = "src/copas/smtp.lua",
["copas.timer"] = "src/copas/timer.lua",
["copas.lock"] = "src/copas/lock.lua",
["copas.semaphore"] = "src/copas/semaphore.lua",
["copas.queue"] = "src/copas/queue.lua",
},
copy_directories = {
"docs",
},
}

BIN
thirdparty/copas/docs/copas.png (Stored with Git LFS) vendored Normal file

Binary file not shown.

209
thirdparty/copas/docs/doc.css vendored Normal file
View file

@ -0,0 +1,209 @@
body {
color: #47555c;
font-size: 16px;
font-family: "Open Sans", sans-serif;
margin: 0;
padding: 0;
background: #eff4ff;
}
a:link { color: #008fee; }
a:visited { color: #008fee; }
a:hover { color: #22a7ff; }
h1 { font-size:26px; }
h2 { font-size:24px; }
h3 { font-size:18px; }
h4 { font-size:16px; }
hr {
height: 1px;
background: #c1cce4;
border: 0px;
margin: 20px 0;
}
code {
font-family: "Open Sans Mono", "Andale Mono", monospace;
}
tt {
font-family: "Open Sans Mono", "Andale Mono", monospace;
}
body, td, th {
}
textarea, pre, tt {
font-family: "Open Sans Mono", "Andale Mono", monospace;
}
img {
border-width: 0px;
}
.example {
background-color: #323744;
color: white;
font-size: 16px;
padding: 16px 24px;
border-radius: 2px;
}
div.header, div.footer {
}
#container {
}
#product {
background-color: white;
padding: 10px;
height: 130px;
border-bottom: solid #d3dbec 1px;
}
#product big {
font-size: 42px;
}
#product strong {
font-weight: normal;
}
#product_logo {
float: right;
}
#product_name {
padding-top: 15px;
padding-left: 30px;
font-size: 42px;
font-weight: normal;
}
#product_description {
padding-left: 30px;
color: #757779;
}
#main {
background: #eff4ff;
margin: 0;
}
#navigation {
width: 100%;
background-color: rgb(44,62,103);
padding: 10px;
margin: 0;
}
#navigation h1 {
display: none;
}
#navigation a:hover {
text-decoration: underline;
}
#navigation ul li a {
color: rgb(136, 208, 255);
font-weight: bold;
text-decoration: none;
}
#navigation ul li li a {
color: rgb(136, 208, 255);
font-weight: normal;
text-decoration: none;
}
#navigation ul {
display: inline;
color: white;
padding: 0px;
padding-top: 10px;
padding-bottom: 10px;
}
#navigation li {
display: inline;
list-style-type: none;
padding-left: 5px;
padding-right: 5px;
}
#navigation li {
padding: 10px;
padding: 10px;
}
#navigation li li {
}
#navigation li:hover a {
color: rgb(166, 238, 255);
}
#content {
padding: 20px;
width: 800px;
margin-left: auto;
margin-right: auto;
}
#about {
display: none;
}
dl.reference {
background-color: white;
padding: 20px;
border: solid #d3dbec 1px;
}
dl.reference dt {
padding: 5px;
padding-top: 25px;
color: #637bbc;
}
dl.reference dl dt {
padding-top: 5px;
color: #637383;
}
dl.reference dd {
}
@media print {
body {
font: 10pt "Times New Roman", "TimeNR", Times, serif;
}
a {
font-weight:bold; color: #004080; text-decoration: underline;
}
#main {
background-color: #ffffff; border-left: 0px;
}
#container {
margin-left: 2%; margin-right: 2%; background-color: #ffffff;
}
#content {
margin-left: 0px; padding: 1em; border-left: 0px; border-right: 0px; background-color: #ffffff;
}
#navigation {
display: none;
}
#product_logo {
display: none;
}
#about img {
display: none;
}
.example {
font-family: "Andale Mono", monospace;
font-size: 8pt;
page-break-inside: avoid;
}
}

395
thirdparty/copas/docs/index.html vendored Normal file
View file

@ -0,0 +1,395 @@
<!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><strong>Home</strong>
<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><a href="manual.html">Manual</a>
</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="over"></a>Overview</h2>
<p>
Copas is a dispatcher based on coroutines that can be used for asynchroneous
networking. For example TCP or UDP based servers. But it also features timers
and client support for http(s), ftp and smtp requests.
</p>
<p>
It uses <a href="http://www.cs.princeton.edu/~diego/professional/luasocket/">LuaSocket</a>
as the interface with the TCP/IP stack and <a href="https://github.com/brunoos/luasec">LuaSec</a> for ssl
support.
</p>
<p>
A server or thread 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
<a href="http://keplerproject.github.io/xavante">Xavante</a> as an example.
</p>
<p>Copas is free software and uses the same <a href="license.html">license</a>
as Lua 5.1 to 5.4</p>
<h2><a name="status"></a>Status</h2>
<p>Current version is 4.7.0 and was developed for Lua 5.1 to 5.4.</p>
<h2><a name="download"></a>Download</h2>
<p>
Copas can be downloaded from its
<a href="http://github.com/lunarmodules/copas">Github</a> page, in
the "Downloads" tab.
</p>
<p>You can also install Copas using <a href="http://www.luarocks.org">LuaRocks</a>:</p>
<pre class="example">
luarocks install copas
</pre>
<h2><a name="dependencies"></a>Dependencies</h2>
<p>Copas depends on
LuaSocket, <a href="http://keplerproject.github.io/coxpcall/">Coxpcall</a> (only when using Lua 5.1), and (optionally) LuaSec.
</p>
<h2><a name="history"></a>History</h2>
<dl class="history">
<dt><strong>Copas 4.7.x</strong> [unreleased]</dt>
<dd><ul>
<li>Fix: <code>copas.removethread</code> would not remove a sleeping thread immediately (it would not execute, but
would prevent the Copas loop from exiting until the timer expired).</li>
</ul></dd>
<dt><strong>Copas 4.7.0</strong> [15/Jan/2023]</dt>
<dd><ul>
<li>Fix: windows makefile didn't include all submodules.</li>
<li>Fix: creating a new timer with a bad delay setting would throw a bad error message.</li>
<li>Refactor: submodules are now part of copas (lazily loaded) and do not need to be required anymore</li>
<li>Feat: runtime script added to directly run copas based code</li>
<li>Fix: improved socket wrapper for lacking LuaSec methods (<code>setoption, getoption, getpeername, getsockname</code>)</li>
<li>Feat: added LuaSec methods to wrapper (<code>getalpn, getsniname</code>)</li>
</ul></dd>
<dt><strong>Copas 4.6.0</strong> [30/Dec/2022]</dt>
<dd><ul>
<li>Added: for timeouts in copas, lock, semaphore, and queue, allow <code>math.huge</code>
to specify no timeout/wait forever. Using <code>math.huge</code> over long timeouts will
reduce pressure on the timer-wheel.</li>
<li>Refactor: increase ringsize (timer-wheel) from 1 minute to 1 day to reduce timer-wheel pressure.</li>
</ul></dd>
<dt><strong>Copas 4.5.0</strong> [18/Dec/2022]</dt>
<dd><ul>
<li>Added: <code>copas.status</code> got an extra parameter to track more detailed stats.</li>
<li>Fix: queue workers would not properly handle falsy items in the queue. The worker would
exit instead of handle the item.</li>
<li>Fix: a non-reentrant lock should block instead of returning an error when entered again.</li>
<li>Fix: finishing a queue would not honour the timeout.</li>
<li>Refactor: more manual cleanup instead of relying on weak-tables.</li>
</ul></dd>
<dt><strong>Copas 4.4.0</strong> [23/Oct/2022]</dt>
<dd><ul>
<li>Fix: an error in the timer callback would kill the timer.</li>
<li>Added: <code>copas.geterrorhandler</code> to retrieve the active errorhandler.</li>
<li>Added: option <code>errorhandler</code> for timer objects.</li>
<li>Added: <code>copas.pause</code> and <code>copas.pauseforever</code> to replace <code>copas.sleep</code>. The latter
method can accidentally sleep-forever if time arithmetic returns a negative result.</li>
<li>Added: <code>copas.status</code> which returns an object with number of tasks/timers/sockets.</li>
<li>Change: renamed <code>copas.setErrorHandler</code> to <code>copas.seterrorhandler</code>.</li>
<li>Change: renamed <code>copas.useSocketTimeoutErrors</code> to <code>copas.usesockettimeouterrors</code>.</li>
</ul></dd>
<dt><strong>Copas 4.3.2</strong> [03/Oct/2022]</dt>
<dd><ul>
<li>Fix: error handler for timeouts. Underlying <a href="https://github.com/keplerproject/coxpcall/issues/18">
bug is in coxpcall</a>, and hence this only applies to PuC Lua 5.1.</li>
</ul></dd>
<dt><strong>Copas 4.3.1</strong> [21/Sep/2022]</dt>
<dd><ul>
<li>Fix: with Lua 5.1 the timeouts would resume the wrapped (by coxpcall)
coroutines, instead of the original ones. Causing errors to bubble up one
level too many.
</li>
</ul></dd>
<dt><strong>Copas 4.3.0</strong> [19/Sep/2022]</dt>
<dd><ul>
<li>Fix: when the loop is idle, do an occasional GC to clean out any lingering
non-closed sockets. This could prevent the loop from exiting.</li>
<li>Fix: in debug mode very large data is now truncated when displayed.</li>
<li>Fix: the receive methods could starve other threads on high-throughput.</li>
<li>Change: order of <code>copas.addnamedthread</code> args.</li>
<li>Fix: <code>copas.receivepartial</code> could return early with no data received
if the `prefix` parameter was specified.</li>
<li>Change: renamed <code>copas.receivePartial</code> to <code>copas.receivepartial</code>.</li>
<li>Added: <code>sock:receivepartial</code> to better process streaming TCP data.</li>
<li>fix: <code>copas.receivepartial</code> is now documented.</li>
<li>fix: <code>copas.http</code> was missing some error handling.</li>
<li>fix: Copas timeouts when receiving/sending would not return partial results,
or last bytes sent.</li>
</ul></dd>
<dt><strong>Copas 4.2.0</strong> [06/Sep/2022]</dt>
<dd><ul>
<li>Change: pick up datagram size from newer LuaSocket versions.</li>
<li>Fix: non-recurring timer can now be armed again from its own handler.</li>
<li>Added: calling on the module table now invokes the <code>copas.loop</code> method.</li>
</ul></dd>
<dt><strong>Copas 4.1.0</strong> [25/Aug/2022]</dt>
<dd><ul>
<li>Fix: handle errors thrown by the error handlers themselves.</li>
<li>Deps: Bump timerwheel to 1.0 (no changes, just a small fix)</li>
<li>Added: <code>copas.gettraceback</code>, previously internal to the default error handler,
now exposed to make it easier to write proper error handlers</li>
<li>Added: http-request now takes a timeout setting, previously it would always use the default value
of 30 seconds.</li>
<li>Added: the previously internal function for generating a TCP socket in the http-request module,
is now exported as <code>http.getcreatefunc()</code>. This allows to capture the socket used by the
request. When using streaming responses, for example with server-sent events, this can be used to modify
the timeouts, or for closing the stream.</li>
<li>Fix: empty queues were not destroyed properly and could prevent Copas from exiting</li>
</ul></dd>
<dt><strong>Copas 4.0.0</strong> [29/Jul/2022]</dt>
<dd><ul>
<li>[breaking] Change: removed the "limitset". Its functionality can easily be recreated with
the new "queue" class, which is a better abstraction.</li>
<li>[breaking] Change: threads added through <code>copas.addthread</code> or
<code>copas.addnamedthread</code> will now be "scheduled", instead of immediately started.</li>
<li>Fixed: yielding to the Copas scheduler from user-code now throws a proper error and
no longer breaks the loop. Breaking the loop could also happen if a thread returned with
at least 2 return values.</li>
<li>Fixed: wrongly auto-closing sockets. Upon exiting a coroutine, sockets would be automatically
closed. This should only be the case for accepted TCP connections on a TCP server socket. This caused issues
for sockets shared between threads.<br />
[breaking]: this changes behavior, auto-close is now determined when accepting the connection, and no longer when
terminating the handler thread. This will only affect users that dynamically change <code>copas.autoclose</code>
at runtime.</li>
<li>Fixed: http requests would not set SNI defaults. Setting fields <code>protocol</code>,
<code>options</code>, and <code>verify</code> directly on the http options table is now deprecated. Instead
specify <code>sslparams</code>, similar to other SSL/TLS functions.</li>
<li>Added: added <code>sempahore:destroy()</code></li>
<li>Added: <code>copas.settimeouts</code>, to set separate timeouts for connect, send, receive</li>
<li>Added: queue class, see module "copas.queue"</li>
<li>Added: names for sockets and coroutines:
<ul>
<li><code>copas.addserver()</code> has a 4th argument; name, to name the server socket</li>
<li><code>copas.addnamedthread()</code> is new and identical to <code>copas.addthread()</code>,
but also accepts a name</li>
<li><code>copas.setsocketname()</code>, <code>copas.getsocketname()</code>,
<code>copas.setthreadname()</code>, <code>copas.getthreadname()</code> added to manage names</li>
<li><code>copas.debug.start()</code> and <code>copas.debug.end()</code> to enable debug
logging for the scheduler itself.</li>
<li><code>copas.debug.socket()</code> to enable debug logging for socket methods (experimental).</li>
</ul>
</li>
</ul></dd>
<dt><strong>Copas 3.0.0</strong> [12/Nov/2021]</dt>
<dd><ul>
<li>[breaking] Change: <code>copas.addserver()</code> now uses the timeout value as a copas timeout,
instead of a luasocket timeout. The client sockets for incoming connections will
inherit the timeout from the server socket.</li>
<li>Added: support for SNI on TLS connections #81 (@amyspark)</li>
<li>Added: <code>copas.settimeout()</code> so Copas can manage its own timeouts instead of spinning forever (Patrick Barrett )</li>
<li>Added: timer class, see module "copas.timer"</li>
<li>Added: lock class, see module "copas.lock"</li>
<li>Added: semaphore class, see module "copas.semaphore"</li>
<li>Added: timeout interface <code>copas.timeout()</code></li>
<li>Added: option to override the default errorhandler, and fixes to the handler</li>
<li>Added: <code>copas.removethread()</code> added to be able to forcefully remove a previously added thread</li>
<li>Added: <code>copas.loop()</code> now takes an optional initialization function</li>
<li>Fixed: closing sockets from another thread would make the read/write ops hang #104</li>
<li>Fixed: coxpcall dependency in limit.lua #63 (Francois Perrad)</li>
<li>Fixed: CI now generates the certificates for testing, on unix make can be used, on Windows generate them manually</li>
<li>Fixed: type in wrapped <code>udp:setpeername</code> was actually calling <code>udp:getpeername</code></li>
<li>Fixed: default error handler didn't print the stacktrace</li>
<li>Fixed: small memory leak when sleeping until woken</li>
<li>Fixed: do not wrap <code>udp:sendto()</code> method, since udp send doesn't block</li>
<li>Change: performance improvement in big limit-sets (Francisco Castro)</li>
<li>Change: update deprecated tls default to tls 1.2 in (copas.http)</li>
</ul></dd>
<dt><strong>Copas 2.0.2</strong> [2017]</dt>
<dd><ul>
<li>Added: <code>copas.running</code> flag</li>
<li>Fixed: fix for http request #53 (Peter Melnichenko)</li>
<li>Added: extra parameter <code>keep_open</code> for the <code>removeserver()</code> method (Hisham Muhammad)</li>
<li>Change: tweaked makefile with a <code>DESTDIR</code> variable (Richard Leitner)</li>
</ul></dd>
<dt><strong>Copas 2.0.1</strong> [2016]</dt>
<dd><ul>
<li>Added: support for Lua 5.3 (no code changes, just rockspec update)</li>
<li>Fixed: yield across c boundary error (by Peter Melnichenko)</li>
<li>Fixed: bug in wrappers for <code>setoption()</code> and <code>shutdown()</code> (reported by Rob Probin)</li>
</ul></dd>
<dt><strong>Copas 2.0.0</strong> [2015]</dt>
<dd><ul>
<li>Added: <code>removeserver()</code> function to remove servers from the scheduler (by Paul Kulchenko)</li>
<li>Added: client requests for http(s), ftp, and smtp (like LuaSocket/LuaSec, but async)</li>
<li>Added: transparent async support (handshake, and send/receive) for ssl using LuaSec</li>
<li>Added: <code>handler()</code> as a convenience for full copas and ssl wrapping</li>
<li>[breaking] Change: the loop now exits when there is nothing more to do</li>
<li>[breaking] Change: dummy first argument to new tasks removed</li>
<li>Fixed: completed the socket wrappers, missing functions were added</li>
<li>Fixed: connect issue, <code>step()</code> errorring out instead of returning <code>nil + error</code></li>
<li>Fixed: UDP sockets being auto closed</li>
<li>Fixed: the <code>receivePartial</code> function for http request support (by Paul Kulchenko)</li>
</ul></dd>
<dt><strong>Copas 1.2.1</strong> [2013]</dt>
<dd><ul>
<li>Fixed bad version constant</li>
<li>Fixed timer issue</li>
<li>updated documentation</li>
</ul></dd>
<dt><strong>Copas 1.2.0</strong> [2013]</dt>
<dd><ul>
<li>Support for Lua 5.2</li>
<li>UDP support</li>
<li>suspending threads</li>
<li>other minor updates</li>
</ul></dd>
<dt><strong>Copas 1.1.6</strong> [18/Mar/2010]</dt>
<dd><ul>
<li>Now checks to see if socket.http was required before copas
</li>
</ul></dd>
<dt><strong>Copas 1.1.5</strong> [07/Apr/2009]</dt>
<dd><ul>
<li>Fixed bug reported by Sam Roberts on the
Kepler list
(found due to Xavante locking up on some POST requests)
</li>
</ul></dd>
<dt><strong>Copas 1.1.4</strong> [10/Dec/2008]</dt>
<dd><ul>
<li>Fixed bug [#5372]
- copas.connect is semi-broken (found by Gary NG)</li>
</ul></dd>
<dt><strong>Copas 1.1.3</strong> [19/May/2008]</dt>
<dd><ul>
<li>Using <code>copcall</code> instead of <code>pcall</code> in <code>socket.protect</code>
(feature request [#5274] by Gary NG)</li>
</ul></dd>
<dt><strong>Copas 1.1.2</strong> [15/May/2008]</dt>
<dd><ul>
<li>Fixed Bug [#4249]
- bugs in copas.receive (found by Gary NG)</li>
</ul></dd>
<dt><strong>Copas 1.1.1</strong> [13/Aug/2007]</dt>
<dd>
<ul>
<li>Compatible with Lua 5.1</li>
<li>Refactored by Thomas Harning Jr. (for more details check
Bug 766)</li>
<li>Patch by Gary NG concerning the handling of stopped sockets</li>
</ul>
</dd>
<dt><strong>Copas 1.1</strong> [20/Sep/2006]</dt>
<dd><ul>
<li><a href="reference.html">copas.addthread()</a> added</li>
</ul></dd>
<dt><strong><a href="http://www.keplerproject.org/copas/1.0">Copas 1.0</a></strong> [17/May/2005]</dt>
<dd><ul>
<li><a href="reference.html">copas.step()</a> added</li>
</ul></dd>
<dt><strong>Copas 1.0 Beta</strong>[17/Feb/2005]</dt>
<dd><ul>
<li>First public version</li>
</ul></dd>
</dl>
<h2><a name="credits"></a>Credits</h2>
<p>Copas was designed and implemented by Andr&eacute; Carregal and
Javier Guerra as part of the
<a href="http://www.keplerproject.org">Kepler Project</a> which
holds its copyright. Copas development had significative contributions from Diego Nehab,
Mike Pall, David Burgess, Leonardo Godinho, Thomas Harning Jr. and Gary NG.</p>
<h2><a name="contact"></a>Contact us</h2>
<p>For more information please
<a href="mailto:info-NO-SPAM-THANKS@keplerproject.org">contact us</a>.
Comments are welcome!</p>
<p>
You can also reach other Kepler developers and users on the Kepler Project
<a href="https://groups.google.com/forum/#!forum/kepler-project">mailing list</a>.
</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>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>
</html>

100
thirdparty/copas/docs/license.html vendored Normal file
View file

@ -0,0 +1,100 @@
<!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 License</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><a href="manual.html">Manual</a>
</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><strong>License</strong></li>
</ul>
</div> <!-- id="navigation" -->
<div id="content">
<h1>License</h1>
<p>
Copas is free software: it can be used for both academic and
commercial purposes at absolutely no cost.</p>
<p>
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.</p>
<p>
Copas was designed and implemented by Andr&eacute; Carregal and
Javier Guerra. The implementation is not derived from
licensed software.</p>
<p>
<!-- ===================================================================== -->
</p>
<hr />
<p>Copyright &copy; 2005-2013 Kepler Project, 2015-2023 Thijs Schreijer.</p>
<p>
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:</p>
<p>
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.</p>
<p>
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.</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: license.html,v 1.17 2009/03/24 22:04:26 carregal Exp $</small></p>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>
</html>

527
thirdparty/copas/docs/manual.html vendored Normal file
View file

@ -0,0 +1,527 @@
<!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 &gt;= 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>

1003
thirdparty/copas/docs/reference.html vendored Normal file

File diff suppressed because it is too large Load diff

44
thirdparty/copas/misc/cosocket.lua vendored Normal file
View file

@ -0,0 +1,44 @@
-------------------------------------------------------------------------------
-- Copas - Coroutine Oriented Portable Asynchronous Services
--
-- Copas Wrapper for socket.http module
--
-- Written by Leonardo Godinho da Cunha
-------------------------------------------------------------------------------
local copas = require("copas")
local socket = require("socket")
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
return cosocket

24
thirdparty/copas/misc/echoserver.lua vendored Normal file
View file

@ -0,0 +1,24 @@
-- 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 socket = require("socket")
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()

View file

@ -0,0 +1,58 @@
local copas = require("copas")
local synchttp = require("socket.http").request
local asynchttp = copas.http.request
local gettime = require("socket").gettime
local targets = {
"http://www.google.com",
"http://www.microsoft.com",
"http://www.apple.com",
"http://www.facebook.com",
"http://www.yahoo.com",
}
local function sync(list)
for _, host in ipairs(list) do
local res = synchttp(host)
if not res then
print("Error sync: "..host.." failed, rerun test!")
else
print("Sync host done: "..host)
end
end
end
local handler = function(host)
local res = asynchttp(host)
if not res then
print("Error async: "..host.." failed, rerun test!")
else
print("Async host done: "..host)
end
end
local function async(list)
for _, host in ipairs(list) do copas.addthread(handler, host) end
copas.loop()
end
-- three times to remove caching differences
async(targets)
async(targets)
async(targets)
print("\nNow starting the real test...\n")
local t1 = gettime()
print("Sync:")
sync(targets)
local t2 = gettime()
print("Async:")
async(targets)
local t3 = gettime()
print("\nResults:")
print("========")
print(" Sync : ", t2-t1)
print(" Async: ", t3-t2)

View file

@ -0,0 +1,30 @@
package = "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)"
}
}

View file

@ -0,0 +1,31 @@
package = "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)"
}
}

View file

@ -0,0 +1,28 @@
package = "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" }
}

View file

@ -0,0 +1,28 @@
package = "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" }
}

View file

@ -0,0 +1,28 @@
package = "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" }
}

View file

@ -0,0 +1,29 @@
package = "Copas"
version = "1.2.0-1"
source = {
url = "https://github.com/keplerproject/copas/archive/v1_2_0.tar.gz",
dir = "copas-1_2_0",
}
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, < 5.3",
"luasocket >= 2.1",
"coxpcall >= 1.14",
}
build = {
type = "builtin",
modules = { copas = "src/copas/copas.lua" }
}

View file

@ -0,0 +1,29 @@
package = "Copas"
version = "1.2.1-1"
source = {
url = "https://github.com/keplerproject/copas/archive/v1_2_1.tar.gz",
dir = "copas-1_2_1",
}
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, < 5.3",
"luasocket >= 2.1",
"coxpcall >= 1.14",
}
build = {
type = "builtin",
modules = { copas = "src/copas/copas.lua" }
}

View file

@ -0,0 +1,35 @@
package = "Copas"
version = "2.0.0-1"
source = {
url = "https://github.com/keplerproject/copas/archive/v2_0_0.tar.gz",
dir = "copas-2_0_0",
}
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, < 5.3",
"luasocket >= 2.1",
"coxpcall >= 1.14",
}
build = {
type = "builtin",
modules = {
["copas"] = "src/copas.lua",
["copas.http"] = "src/copas/http.lua",
["copas.ftp"] = "src/copas/ftp.lua",
["copas.smtp"] = "src/copas/smtp.lua",
["copas.limit"] = "src/copas/limit.lua",
}
}

View file

@ -0,0 +1,35 @@
package = "Copas"
version = "2.0.0-2"
source = {
url = "https://github.com/keplerproject/copas/archive/v2_0_0.tar.gz",
dir = "copas-2_0_0",
}
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, < 5.4",
"luasocket >= 2.1",
"coxpcall >= 1.14",
}
build = {
type = "builtin",
modules = {
["copas"] = "src/copas.lua",
["copas.http"] = "src/copas/http.lua",
["copas.ftp"] = "src/copas/ftp.lua",
["copas.smtp"] = "src/copas/smtp.lua",
["copas.limit"] = "src/copas/limit.lua",
}
}

View file

@ -0,0 +1,35 @@
package = "Copas"
version = "2.0.1-1"
source = {
url = "https://github.com/keplerproject/copas/archive/v2_0_1.tar.gz",
dir = "copas-2_0_1",
}
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, < 5.4",
"luasocket >= 2.1",
"coxpcall >= 1.14",
}
build = {
type = "builtin",
modules = {
["copas"] = "src/copas.lua",
["copas.http"] = "src/copas/http.lua",
["copas.ftp"] = "src/copas/ftp.lua",
["copas.smtp"] = "src/copas/smtp.lua",
["copas.limit"] = "src/copas/limit.lua",
}
}

View file

@ -0,0 +1,35 @@
package = "Copas"
version = "2.0.2-1"
source = {
url = "https://github.com/keplerproject/copas/archive/v2_0_2.tar.gz",
dir = "copas-2_0_2",
}
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, < 5.4",
"luasocket >= 2.1",
"coxpcall >= 1.14",
}
build = {
type = "builtin",
modules = {
["copas"] = "src/copas.lua",
["copas.http"] = "src/copas/http.lua",
["copas.ftp"] = "src/copas/ftp.lua",
["copas.smtp"] = "src/copas/smtp.lua",
["copas.limit"] = "src/copas/limit.lua",
}
}

View file

@ -0,0 +1,51 @@
local package_name = "copas"
local package_version = "3.0.0"
local rockspec_revision = "1"
local github_account_name = "keplerproject"
local github_repo_name = package_name
package = package_name
version = package_version.."-"..rockspec_revision
source = {
url = "git://github.com/"..github_account_name.."/"..github_repo_name..".git",
branch = (package_version == "cvs") and "master" or nil,
tag = (package_version ~= "cvs") and package_version or nil,
}
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 = "https://github.com/"..github_account_name.."/"..github_repo_name,
}
dependencies = {
"lua >= 5.1, < 5.4",
"luasocket >= 2.1",
"coxpcall >= 1.14",
"binaryheap >= 0.4",
"timerwheel >= 0.2",
}
build = {
type = "builtin",
modules = {
["copas"] = "src/copas.lua",
["copas.http"] = "src/copas/http.lua",
["copas.ftp"] = "src/copas/ftp.lua",
["copas.smtp"] = "src/copas/smtp.lua",
["copas.limit"] = "src/copas/limit.lua",
["copas.timer"] = "src/copas/timer.lua",
["copas.lock"] = "src/copas/lock.lua",
["copas.semaphore"] = "src/copas/semaphore.lua",
},
copy_directories = {
"docs",
},
}

View file

@ -0,0 +1,51 @@
local package_name = "copas"
local package_version = "3.0.0"
local rockspec_revision = "2"
local github_account_name = "lunarmodules"
local github_repo_name = package_name
package = package_name
version = package_version.."-"..rockspec_revision
source = {
url = "git+https://github.com/"..github_account_name.."/"..github_repo_name..".git",
branch = (package_version == "cvs") and "master" or nil,
tag = (package_version ~= "cvs") and package_version or nil,
}
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 = "https://github.com/"..github_account_name.."/"..github_repo_name,
}
dependencies = {
"lua >= 5.1, < 5.5",
"luasocket >= 2.1",
"coxpcall >= 1.14",
"binaryheap >= 0.4",
"timerwheel >= 0.2",
}
build = {
type = "builtin",
modules = {
["copas"] = "src/copas.lua",
["copas.http"] = "src/copas/http.lua",
["copas.ftp"] = "src/copas/ftp.lua",
["copas.smtp"] = "src/copas/smtp.lua",
["copas.limit"] = "src/copas/limit.lua",
["copas.timer"] = "src/copas/timer.lua",
["copas.lock"] = "src/copas/lock.lua",
["copas.semaphore"] = "src/copas/semaphore.lua",
},
copy_directories = {
"docs",
},
}

View file

@ -0,0 +1,51 @@
local package_name = "copas"
local package_version = "3.0.0"
local rockspec_revision = "3"
local github_account_name = "lunarmodules"
local github_repo_name = package_name
package = package_name
version = package_version.."-"..rockspec_revision
source = {
url = "git+https://github.com/"..github_account_name.."/"..github_repo_name..".git",
branch = (package_version == "cvs") and "master" or nil,
tag = (package_version ~= "cvs") and package_version or nil,
}
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 = "https://github.com/"..github_account_name.."/"..github_repo_name,
}
dependencies = {
"lua >= 5.1, < 5.5",
"luasocket >= 2.1, <= 3.0rc1-2",
"coxpcall >= 1.14",
"binaryheap >= 0.4",
"timerwheel >= 0.2",
}
build = {
type = "builtin",
modules = {
["copas"] = "src/copas.lua",
["copas.http"] = "src/copas/http.lua",
["copas.ftp"] = "src/copas/ftp.lua",
["copas.smtp"] = "src/copas/smtp.lua",
["copas.limit"] = "src/copas/limit.lua",
["copas.timer"] = "src/copas/timer.lua",
["copas.lock"] = "src/copas/lock.lua",
["copas.semaphore"] = "src/copas/semaphore.lua",
},
copy_directories = {
"docs",
},
}

View file

@ -0,0 +1,51 @@
local package_name = "copas"
local package_version = "4.0.0"
local rockspec_revision = "1"
local github_account_name = "lunarmodules"
local github_repo_name = package_name
package = package_name
version = package_version.."-"..rockspec_revision
source = {
url = "git+https://github.com/"..github_account_name.."/"..github_repo_name..".git",
branch = (package_version == "cvs") and "master" or nil,
tag = (package_version ~= "cvs") and package_version or nil,
}
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 = "https://github.com/"..github_account_name.."/"..github_repo_name,
}
dependencies = {
"lua >= 5.1, < 5.5",
"luasocket >= 2.1, <= 3.0rc1-2",
"coxpcall >= 1.14",
"binaryheap >= 0.4",
"timerwheel >= 0.2",
}
build = {
type = "builtin",
modules = {
["copas"] = "src/copas.lua",
["copas.http"] = "src/copas/http.lua",
["copas.ftp"] = "src/copas/ftp.lua",
["copas.smtp"] = "src/copas/smtp.lua",
["copas.timer"] = "src/copas/timer.lua",
["copas.lock"] = "src/copas/lock.lua",
["copas.semaphore"] = "src/copas/semaphore.lua",
["copas.queue"] = "src/copas/queue.lua",
},
copy_directories = {
"docs",
},
}

View file

@ -0,0 +1,51 @@
local package_name = "copas"
local package_version = "4.1.0"
local rockspec_revision = "1"
local github_account_name = "lunarmodules"
local github_repo_name = package_name
package = package_name
version = package_version.."-"..rockspec_revision
source = {
url = "git+https://github.com/"..github_account_name.."/"..github_repo_name..".git",
branch = (package_version == "cvs") and "master" or nil,
tag = (package_version ~= "cvs") and package_version or nil,
}
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 = "https://github.com/"..github_account_name.."/"..github_repo_name,
}
dependencies = {
"lua >= 5.1, < 5.5",
"luasocket >= 2.1, <= 3.0rc1-2",
"coxpcall >= 1.14",
"binaryheap >= 0.4",
"timerwheel ~> 1",
}
build = {
type = "builtin",
modules = {
["copas"] = "src/copas.lua",
["copas.http"] = "src/copas/http.lua",
["copas.ftp"] = "src/copas/ftp.lua",
["copas.smtp"] = "src/copas/smtp.lua",
["copas.timer"] = "src/copas/timer.lua",
["copas.lock"] = "src/copas/lock.lua",
["copas.semaphore"] = "src/copas/semaphore.lua",
["copas.queue"] = "src/copas/queue.lua",
},
copy_directories = {
"docs",
},
}

View file

@ -0,0 +1,51 @@
local package_name = "copas"
local package_version = "4.2.0"
local rockspec_revision = "1"
local github_account_name = "lunarmodules"
local github_repo_name = package_name
package = package_name
version = package_version.."-"..rockspec_revision
source = {
url = "git+https://github.com/"..github_account_name.."/"..github_repo_name..".git",
branch = (package_version == "cvs") and "master" or nil,
tag = (package_version ~= "cvs") and package_version or nil,
}
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 = "https://github.com/"..github_account_name.."/"..github_repo_name,
}
dependencies = {
"lua >= 5.1, < 5.5",
"luasocket >= 2.1, <= 3.0rc1-2",
"coxpcall >= 1.14",
"binaryheap >= 0.4",
"timerwheel ~> 1",
}
build = {
type = "builtin",
modules = {
["copas"] = "src/copas.lua",
["copas.http"] = "src/copas/http.lua",
["copas.ftp"] = "src/copas/ftp.lua",
["copas.smtp"] = "src/copas/smtp.lua",
["copas.timer"] = "src/copas/timer.lua",
["copas.lock"] = "src/copas/lock.lua",
["copas.semaphore"] = "src/copas/semaphore.lua",
["copas.queue"] = "src/copas/queue.lua",
},
copy_directories = {
"docs",
},
}

View file

@ -0,0 +1,51 @@
local package_name = "copas"
local package_version = "4.3.0"
local rockspec_revision = "1"
local github_account_name = "lunarmodules"
local github_repo_name = package_name
package = package_name
version = package_version.."-"..rockspec_revision
source = {
url = "git+https://github.com/"..github_account_name.."/"..github_repo_name..".git",
branch = (package_version == "cvs") and "master" or nil,
tag = (package_version ~= "cvs") and package_version or nil,
}
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 = "https://github.com/"..github_account_name.."/"..github_repo_name,
}
dependencies = {
"lua >= 5.1, < 5.5",
"luasocket >= 2.1, <= 3.0rc1-2",
"coxpcall >= 1.14",
"binaryheap >= 0.4",
"timerwheel ~> 1",
}
build = {
type = "builtin",
modules = {
["copas"] = "src/copas.lua",
["copas.http"] = "src/copas/http.lua",
["copas.ftp"] = "src/copas/ftp.lua",
["copas.smtp"] = "src/copas/smtp.lua",
["copas.timer"] = "src/copas/timer.lua",
["copas.lock"] = "src/copas/lock.lua",
["copas.semaphore"] = "src/copas/semaphore.lua",
["copas.queue"] = "src/copas/queue.lua",
},
copy_directories = {
"docs",
},
}

View file

@ -0,0 +1,51 @@
local package_name = "copas"
local package_version = "4.3.1"
local rockspec_revision = "1"
local github_account_name = "lunarmodules"
local github_repo_name = package_name
package = package_name
version = package_version.."-"..rockspec_revision
source = {
url = "git+https://github.com/"..github_account_name.."/"..github_repo_name..".git",
branch = (package_version == "cvs") and "master" or nil,
tag = (package_version ~= "cvs") and package_version or nil,
}
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 = "https://github.com/"..github_account_name.."/"..github_repo_name,
}
dependencies = {
"lua >= 5.1, < 5.5",
"luasocket >= 2.1, <= 3.0rc1-2",
"coxpcall >= 1.14",
"binaryheap >= 0.4",
"timerwheel ~> 1",
}
build = {
type = "builtin",
modules = {
["copas"] = "src/copas.lua",
["copas.http"] = "src/copas/http.lua",
["copas.ftp"] = "src/copas/ftp.lua",
["copas.smtp"] = "src/copas/smtp.lua",
["copas.timer"] = "src/copas/timer.lua",
["copas.lock"] = "src/copas/lock.lua",
["copas.semaphore"] = "src/copas/semaphore.lua",
["copas.queue"] = "src/copas/queue.lua",
},
copy_directories = {
"docs",
},
}

View file

@ -0,0 +1,51 @@
local package_name = "copas"
local package_version = "4.3.2"
local rockspec_revision = "1"
local github_account_name = "lunarmodules"
local github_repo_name = package_name
package = package_name
version = package_version.."-"..rockspec_revision
source = {
url = "git+https://github.com/"..github_account_name.."/"..github_repo_name..".git",
branch = (package_version == "cvs") and "master" or nil,
tag = (package_version ~= "cvs") and package_version or nil,
}
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 = "https://github.com/"..github_account_name.."/"..github_repo_name,
}
dependencies = {
"lua >= 5.1, < 5.5",
"luasocket >= 2.1, <= 3.0rc1-2",
"coxpcall >= 1.14",
"binaryheap >= 0.4",
"timerwheel ~> 1",
}
build = {
type = "builtin",
modules = {
["copas"] = "src/copas.lua",
["copas.http"] = "src/copas/http.lua",
["copas.ftp"] = "src/copas/ftp.lua",
["copas.smtp"] = "src/copas/smtp.lua",
["copas.timer"] = "src/copas/timer.lua",
["copas.lock"] = "src/copas/lock.lua",
["copas.semaphore"] = "src/copas/semaphore.lua",
["copas.queue"] = "src/copas/queue.lua",
},
copy_directories = {
"docs",
},
}

View file

@ -0,0 +1,51 @@
local package_name = "copas"
local package_version = "4.4.0"
local rockspec_revision = "1"
local github_account_name = "lunarmodules"
local github_repo_name = package_name
package = package_name
version = package_version.."-"..rockspec_revision
source = {
url = "git+https://github.com/"..github_account_name.."/"..github_repo_name..".git",
branch = (package_version == "cvs") and "master" or nil,
tag = (package_version ~= "cvs") and package_version or nil,
}
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 = "https://github.com/"..github_account_name.."/"..github_repo_name,
}
dependencies = {
"lua >= 5.1, < 5.5",
"luasocket >= 2.1, <= 3.0rc1-2",
"coxpcall >= 1.14",
"binaryheap >= 0.4",
"timerwheel ~> 1",
}
build = {
type = "builtin",
modules = {
["copas"] = "src/copas.lua",
["copas.http"] = "src/copas/http.lua",
["copas.ftp"] = "src/copas/ftp.lua",
["copas.smtp"] = "src/copas/smtp.lua",
["copas.timer"] = "src/copas/timer.lua",
["copas.lock"] = "src/copas/lock.lua",
["copas.semaphore"] = "src/copas/semaphore.lua",
["copas.queue"] = "src/copas/queue.lua",
},
copy_directories = {
"docs",
},
}

View file

@ -0,0 +1,51 @@
local package_name = "copas"
local package_version = "4.5.0"
local rockspec_revision = "1"
local github_account_name = "lunarmodules"
local github_repo_name = package_name
package = package_name
version = package_version.."-"..rockspec_revision
source = {
url = "git+https://github.com/"..github_account_name.."/"..github_repo_name..".git",
branch = (package_version == "cvs") and "master" or nil,
tag = (package_version ~= "cvs") and package_version or nil,
}
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 = "https://github.com/"..github_account_name.."/"..github_repo_name,
}
dependencies = {
"lua >= 5.1, < 5.5",
"luasocket >= 2.1, <= 3.0rc1-2",
"coxpcall >= 1.14",
"binaryheap >= 0.4",
"timerwheel ~> 1",
}
build = {
type = "builtin",
modules = {
["copas"] = "src/copas.lua",
["copas.http"] = "src/copas/http.lua",
["copas.ftp"] = "src/copas/ftp.lua",
["copas.smtp"] = "src/copas/smtp.lua",
["copas.timer"] = "src/copas/timer.lua",
["copas.lock"] = "src/copas/lock.lua",
["copas.semaphore"] = "src/copas/semaphore.lua",
["copas.queue"] = "src/copas/queue.lua",
},
copy_directories = {
"docs",
},
}

View file

@ -0,0 +1,51 @@
local package_name = "copas"
local package_version = "4.6.0"
local rockspec_revision = "1"
local github_account_name = "lunarmodules"
local github_repo_name = package_name
package = package_name
version = package_version.."-"..rockspec_revision
source = {
url = "git+https://github.com/"..github_account_name.."/"..github_repo_name..".git",
branch = (package_version == "cvs") and "master" or nil,
tag = (package_version ~= "cvs") and package_version or nil,
}
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 = "https://github.com/"..github_account_name.."/"..github_repo_name,
}
dependencies = {
"lua >= 5.1, < 5.5",
"luasocket >= 2.1, <= 3.0rc1-2",
"coxpcall >= 1.14",
"binaryheap >= 0.4",
"timerwheel ~> 1",
}
build = {
type = "builtin",
modules = {
["copas"] = "src/copas.lua",
["copas.http"] = "src/copas/http.lua",
["copas.ftp"] = "src/copas/ftp.lua",
["copas.smtp"] = "src/copas/smtp.lua",
["copas.timer"] = "src/copas/timer.lua",
["copas.lock"] = "src/copas/lock.lua",
["copas.semaphore"] = "src/copas/semaphore.lua",
["copas.queue"] = "src/copas/queue.lua",
},
copy_directories = {
"docs",
},
}

View file

@ -0,0 +1,56 @@
local package_name = "copas"
local package_version = "4.7.0"
local rockspec_revision = "1"
local github_account_name = "lunarmodules"
local github_repo_name = package_name
package = package_name
version = package_version.."-"..rockspec_revision
source = {
url = "git+https://github.com/"..github_account_name.."/"..github_repo_name..".git",
branch = (package_version == "cvs") and "master" or nil,
tag = (package_version ~= "cvs") and package_version or nil,
}
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 = "https://github.com/"..github_account_name.."/"..github_repo_name,
}
dependencies = {
"lua >= 5.1, < 5.5",
"luasocket >= 2.1, <= 3.0rc1-2",
"coxpcall >= 1.14",
"binaryheap >= 0.4",
"timerwheel ~> 1",
}
build = {
type = "builtin",
install = {
bin = {
copas = "bin/copas.lua",
}
},
modules = {
["copas"] = "src/copas.lua",
["copas.http"] = "src/copas/http.lua",
["copas.ftp"] = "src/copas/ftp.lua",
["copas.smtp"] = "src/copas/smtp.lua",
["copas.timer"] = "src/copas/timer.lua",
["copas.lock"] = "src/copas/lock.lua",
["copas.semaphore"] = "src/copas/semaphore.lua",
["copas.queue"] = "src/copas/queue.lua",
},
copy_directories = {
"docs",
},
}

2008
thirdparty/copas/src/copas.lua vendored Normal file

File diff suppressed because it is too large Load diff

95
thirdparty/copas/src/copas/ftp.lua vendored Normal file
View file

@ -0,0 +1,95 @@
-------------------------------------------------------------------
-- identical to the socket.ftp module except that it uses
-- async wrapped Copas sockets
local copas = require("copas")
local socket = require("socket")
local ftp = require("socket.ftp")
local ltn12 = require("ltn12")
local url = require("socket.url")
local create = function() return copas.wrap(socket.tcp()) end
local forwards = { -- setting these will be forwarded to the original smtp module
PORT = true,
TIMEOUT = true,
PASSWORD = true,
USER = true
}
copas.ftp = setmetatable({}, {
-- use original module as metatable, to lookup constants like socket.TIMEOUT, etc.
__index = ftp,
-- Setting constants is forwarded to the luasocket.ftp module.
__newindex = function(self, key, value)
if forwards[key] then ftp[key] = value return end
return rawset(self, key, value)
end,
})
local _M = copas.ftp
---[[ copy of Luasocket stuff here untile PR #133 is accepted
-- a copy of the version in LuaSockets' ftp.lua
-- no 'create' can be passed in the string form, hence a local copy here
local default = {
path = "/",
scheme = "ftp"
}
-- a copy of the version in LuaSockets' ftp.lua
-- no 'create' can be passed in the string form, hence a local copy here
local function parse(u)
local t = socket.try(url.parse(u, default))
socket.try(t.scheme == "ftp", "wrong scheme '" .. t.scheme .. "'")
socket.try(t.host, "missing hostname")
local pat = "^type=(.)$"
if t.params then
t.type = socket.skip(2, string.find(t.params, pat))
socket.try(t.type == "a" or t.type == "i",
"invalid type '" .. t.type .. "'")
end
return t
end
-- parses a simple form into the advanced form
-- if `body` is provided, a PUT, otherwise a GET.
-- If GET, then a field `target` is added to store the results
_M.parseRequest = function(u, body)
local t = parse(u)
if body then
t.source = ltn12.source.string(body)
else
t.target = {}
t.sink = ltn12.sink.table(t.target)
end
end
--]]
_M.put = socket.protect(function(putt, body)
if type(putt) == "string" then
putt = _M.parseRequest(putt, body)
_M.put(putt)
return table.concat(putt.target)
else
putt.create = putt.create or create
return ftp.put(putt)
end
end)
_M.get = socket.protect(function(gett)
if type(gett) == "string" then
gett = _M.parseRequest(gett)
_M.get(gett)
return table.concat(gett.target)
else
gett.create = gett.create or create
return ftp.get(gett)
end
end)
_M.command = function(cmdt)
cmdt.create = cmdt.create or create
return ftp.command(cmdt)
end
return _M

459
thirdparty/copas/src/copas/http.lua vendored Normal file
View file

@ -0,0 +1,459 @@
-----------------------------------------------------------------------------
-- Full copy of the LuaSocket code, modified to include
-- https and http/https redirects, and Copas async enabled.
-----------------------------------------------------------------------------
-- HTTP/1.1 client support for the Lua language.
-- LuaSocket toolkit.
-- Author: Diego Nehab
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Declare module and import dependencies
-------------------------------------------------------------------------------
local socket = require("socket")
local url = require("socket.url")
local ltn12 = require("ltn12")
local mime = require("mime")
local string = require("string")
local headers = require("socket.headers")
local base = _G
local table = require("table")
local copas = require("copas")
copas.http = {}
local _M = copas.http
-----------------------------------------------------------------------------
-- Program constants
-----------------------------------------------------------------------------
-- connection timeout in seconds
_M.TIMEOUT = 60
-- default port for document retrieval
_M.PORT = 80
-- user agent field sent in request
_M.USERAGENT = socket._VERSION
-- Default settings for SSL
_M.SSLPORT = 443
_M.SSLPROTOCOL = "tlsv1_2"
_M.SSLOPTIONS = "all"
_M.SSLVERIFY = "none"
_M.SSLSNISTRICT = false
-----------------------------------------------------------------------------
-- Reads MIME headers from a connection, unfolding where needed
-----------------------------------------------------------------------------
local function receiveheaders(sock, headers)
local line, name, value, err
headers = headers or {}
-- get first line
line, err = sock:receive()
if err then return nil, err end
-- headers go until a blank line is found
while line ~= "" do
-- get field-name and value
name, value = socket.skip(2, string.find(line, "^(.-):%s*(.*)"))
if not (name and value) then return nil, "malformed reponse headers" end
name = string.lower(name)
-- get next line (value might be folded)
line, err = sock:receive()
if err then return nil, err end
-- unfold any folded values
while string.find(line, "^%s") do
value = value .. line
line, err = sock:receive()
if err then return nil, err end
end
-- save pair in table
if headers[name] then headers[name] = headers[name] .. ", " .. value
else headers[name] = value end
end
return headers
end
-----------------------------------------------------------------------------
-- Extra sources and sinks
-----------------------------------------------------------------------------
socket.sourcet["http-chunked"] = function(sock, headers)
return base.setmetatable({
getfd = function() return sock:getfd() end,
dirty = function() return sock:dirty() end
}, {
__call = function()
-- get chunk size, skip extention
local line, err = sock:receive()
if err then return nil, err end
local size = base.tonumber(string.gsub(line, ";.*", ""), 16)
if not size then return nil, "invalid chunk size" end
-- was it the last chunk?
if size > 0 then
-- if not, get chunk and skip terminating CRLF
local chunk, err = sock:receive(size)
if chunk then sock:receive() end
return chunk, err
else
-- if it was, read trailers into headers table
headers, err = receiveheaders(sock, headers)
if not headers then return nil, err end
end
end
})
end
socket.sinkt["http-chunked"] = function(sock)
return base.setmetatable({
getfd = function() return sock:getfd() end,
dirty = function() return sock:dirty() end
}, {
__call = function(self, chunk, err)
if not chunk then return sock:send("0\r\n\r\n") end
local size = string.format("%X\r\n", string.len(chunk))
return sock:send(size .. chunk .. "\r\n")
end
})
end
-----------------------------------------------------------------------------
-- Low level HTTP API
-----------------------------------------------------------------------------
local metat = { __index = {} }
function _M.open(reqt)
-- create socket with user connect function
local c = socket.try(reqt:create()) -- method call, passing reqt table as self!
local h = base.setmetatable({ c = c }, metat)
-- create finalized try
h.try = socket.newtry(function() h:close() end)
-- set timeout before connecting
local to = reqt.timeout or _M.TIMEOUT
if type(to) == "table" then
h.try(c:settimeouts(
to.connect or _M.TIMEOUT,
to.send or _M.TIMEOUT,
to.receive or _M.TIMEOUT))
else
h.try(c:settimeout(to))
end
h.try(c:connect(reqt.host, reqt.port or _M.PORT))
-- here everything worked
return h
end
function metat.__index:sendrequestline(method, uri)
local reqline = string.format("%s %s HTTP/1.1\r\n", method or "GET", uri)
return self.try(self.c:send(reqline))
end
function metat.__index:sendheaders(tosend)
local canonic = headers.canonic
local h = "\r\n"
for f, v in base.pairs(tosend) do
h = (canonic[f] or f) .. ": " .. v .. "\r\n" .. h
end
self.try(self.c:send(h))
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 = "http-chunked"
if headers["content-length"] then mode = "keep-open" end
return self.try(ltn12.pump.all(source, socket.sink(mode, self.c), step))
end
function metat.__index:receivestatusline()
local status = self.try(self.c:receive(5))
-- identify HTTP/0.9 responses, which do not contain a status line
-- this is just a heuristic, but is what the RFC recommends
if status ~= "HTTP/" then return nil, status end
-- otherwise proceed reading a status line
status = self.try(self.c:receive("*l", status))
local code = socket.skip(2, string.find(status, "HTTP/%d*%.%d* (%d%d%d)"))
return self.try(base.tonumber(code), status)
end
function metat.__index:receiveheaders()
return self.try(receiveheaders(self.c))
end
function metat.__index:receivebody(headers, sink, step)
sink = sink or ltn12.sink.null()
step = step or ltn12.pump.step
local length = base.tonumber(headers["content-length"])
local t = headers["transfer-encoding"] -- shortcut
local mode = "default" -- connection close
if t and t ~= "identity" then mode = "http-chunked"
elseif base.tonumber(headers["content-length"]) then mode = "by-length" end
return self.try(ltn12.pump.all(socket.source(mode, self.c, length),
sink, step))
end
function metat.__index:receive09body(status, sink, step)
local source = ltn12.source.rewind(socket.source("until-closed", self.c))
source(status)
return self.try(ltn12.pump.all(source, sink, step))
end
function metat.__index:close()
return self.c:close()
end
-----------------------------------------------------------------------------
-- High level HTTP API
-----------------------------------------------------------------------------
local function adjusturi(reqt)
local u = reqt
-- if there is a proxy, we need the full url. otherwise, just a part.
if not reqt.proxy and not _M.PROXY then
u = {
path = socket.try(reqt.path, "invalid path 'nil'"),
params = reqt.params,
query = reqt.query,
fragment = reqt.fragment
}
end
return url.build(u)
end
local function adjustproxy(reqt)
local proxy = reqt.proxy or _M.PROXY
if proxy then
proxy = url.parse(proxy)
return proxy.host, proxy.port or 3128
else
return reqt.host, reqt.port
end
end
local function adjustheaders(reqt)
-- default headers
local host = string.gsub(reqt.authority, "^.-@", "")
local lower = {
["user-agent"] = _M.USERAGENT,
["host"] = host,
["connection"] = "close, TE",
["te"] = "trailers"
}
-- if we have authentication information, pass it along
if reqt.user and reqt.password then
lower["authorization"] =
"Basic " .. (mime.b64(reqt.user .. ":" .. reqt.password))
end
-- override with user headers
for i,v in base.pairs(reqt.headers or lower) do
lower[string.lower(i)] = v
end
return lower
end
-- default url parts
local default = {
host = "",
port = _M.PORT,
path ="/",
scheme = "http"
}
local function adjustrequest(reqt)
-- parse url if provided
local nreqt = reqt.url and url.parse(reqt.url, default) or {}
-- explicit components override url
for i,v in base.pairs(reqt) do nreqt[i] = v end
if nreqt.port == "" then nreqt.port = 80 end
socket.try(nreqt.host and nreqt.host ~= "",
"invalid host '" .. base.tostring(nreqt.host) .. "'")
-- compute uri if user hasn't overriden
nreqt.uri = reqt.uri or adjusturi(nreqt)
-- ajust host and port if there is a proxy
nreqt.host, nreqt.port = adjustproxy(nreqt)
-- adjust headers in request
nreqt.headers = adjustheaders(nreqt)
return nreqt
end
local function shouldredirect(reqt, code, headers)
return headers.location and
string.gsub(headers.location, "%s", "") ~= "" and
(reqt.redirect ~= false) and
(code == 301 or code == 302 or code == 303 or code == 307) and
(not reqt.method or reqt.method == "GET" or reqt.method == "HEAD")
and (not reqt.nredirects or reqt.nredirects < 5)
end
local function shouldreceivebody(reqt, code)
if reqt.method == "HEAD" then return nil end
if code == 204 or code == 304 then return nil end
if code >= 100 and code < 200 then return nil end
return 1
end
-- forward declarations
local trequest, tredirect
--[[local]] function tredirect(reqt, location)
local result, code, headers, status = trequest {
-- the RFC says the redirect URL has to be absolute, but some
-- servers do not respect that
url = url.absolute(reqt.url, location),
source = reqt.source,
sink = reqt.sink,
headers = reqt.headers,
proxy = reqt.proxy,
nredirects = (reqt.nredirects or 0) + 1,
create = reqt.create,
timeout = reqt.timeout,
}
-- pass location header back as a hint we redirected
headers = headers or {}
headers.location = headers.location or location
return result, code, headers, status
end
--[[local]] function trequest(reqt)
-- we loop until we get what we want, or
-- until we are sure there is no way to get it
local nreqt = adjustrequest(reqt)
local h = _M.open(nreqt)
-- send request line and headers
h:sendrequestline(nreqt.method, nreqt.uri)
h:sendheaders(nreqt.headers)
-- if there is a body, send it
if nreqt.source then
h:sendbody(nreqt.headers, nreqt.source, nreqt.step)
end
local code, status = h:receivestatusline()
-- if it is an HTTP/0.9 server, simply get the body and we are done
if not code then
h:receive09body(status, nreqt.sink, nreqt.step)
return 1, 200
end
local headers
-- ignore any 100-continue messages
while code == 100 do
h:receiveheaders()
code, status = h:receivestatusline()
end
headers = h:receiveheaders()
-- at this point we should have a honest reply from the server
-- we can't redirect if we already used the source, so we report the error
if shouldredirect(nreqt, code, headers) and not nreqt.source then
h:close()
return tredirect(reqt, headers.location)
end
-- here we are finally done
if shouldreceivebody(nreqt, code) then
h:receivebody(headers, nreqt.sink, nreqt.step)
end
h:close()
return 1, code, headers, status
end
-- Return a function which creates a tcp socket that will
-- include the optional SSL/TLS connection, and unsafe redirect checks
function _M.getcreatefunc(params)
params = params or {}
local ssl_params = params.sslparams or {}
ssl_params.wrap = ssl_params.wrap or {
-- backward compatibility
protocol = params.protocol,
options = params.options,
verify = params.verify,
}
ssl_params.sni = ssl_params.sni or {
strict = _M.SSLSNISTRICT
}
-- Default settings
ssl_params.wrap.protocol = ssl_params.wrap.protocol or _M.SSLPROTOCOL
ssl_params.wrap.options = ssl_params.wrap.options or _M.SSLOPTIONS
if ssl_params.wrap.verify == nil then
ssl_params.wrap.verify = _M.SSLVERIFY
end
ssl_params.wrap.mode = "client" -- Force client mode
if not ssl_params.sni.names then
-- names haven't been set, and hence will be set below. Since this alters
-- the table, we must make a copy. Otherwise the altered table might be
-- reused if a redirect is encountered.
local old_params = ssl_params
ssl_params = {}
for k,v in pairs(old_params) do
ssl_params[k] = v
end
ssl_params.sni = { strict = old_params.sni.strict }
end
-- upvalue to track https -> http redirection
local washttps = false
-- 'create' function for LuaSocket
return function (reqt)
local u = url.parse(reqt.url)
if (reqt.scheme or u.scheme) == "https" then
-- set SNI name to host if not given
ssl_params.sni.names = ssl_params.sni.names or u.host
-- https, provide an ssl wrapped socket
local conn = copas.wrap(socket.tcp(), ssl_params)
-- insert https default port, overriding http port inserted by LuaSocket
if not u.port then
u.port = _M.SSLPORT
reqt.url = url.build(u)
reqt.port = _M.SSLPORT
end
washttps = true
return conn
else
-- regular http, needs just a socket...
if washttps and params.redirect ~= "all" then
socket.try(nil, "Unallowed insecure redirect https to http")
end
return copas.wrap(socket.tcp())
end
end
end
-- parses a shorthand form into the advanced table form.
-- adds field `target` to the table. This will hold the return values.
_M.parseRequest = function(u, b)
local reqt = {
url = u,
target = {},
}
reqt.sink = ltn12.sink.table(reqt.target)
if b then
reqt.source = ltn12.source.string(b)
reqt.headers = {
["content-length"] = string.len(b),
["content-type"] = "application/x-www-form-urlencoded"
}
reqt.method = "POST"
end
return reqt
end
_M.request = socket.protect(function(reqt, body)
if base.type(reqt) == "string" then
reqt = _M.parseRequest(reqt, body)
local ok, code, headers, status = _M.request(reqt)
if ok then
return table.concat(reqt.target), code, headers, status
else
return nil, code
end
else
-- strict check on timeout table to prevent typo's from going unnoticed
if type(reqt.timeout) == "table" then
local allowed = { connect = true, send = true, receive = true }
for k in pairs(reqt.timeout) do
assert(allowed[k], "'"..tostring(k).."' is not a valid timeout option. Valid: 'connect', 'send', 'receive'")
end
end
reqt.create = reqt.create or _M.getcreatefunc(reqt)
return trequest(reqt)
end
end)
return _M

191
thirdparty/copas/src/copas/lock.lua vendored Normal file
View file

@ -0,0 +1,191 @@
local copas = require("copas")
local gettime = require("socket").gettime
local DEFAULT_TIMEOUT = 10
local lock = {}
lock.__index = lock
-- registry, locks indexed by the coroutines using them.
local registry = setmetatable({}, { __mode="kv" })
--- Creates a new lock.
-- @param seconds (optional) default timeout in seconds when acquiring the lock (defaults to 10),
-- set to `math.huge` to have no timeout.
-- @param not_reentrant (optional) if truthy the lock will not allow a coroutine to grab the same lock multiple times
-- @return the lock object
function lock.new(seconds, not_reentrant)
local timeout = tonumber(seconds or DEFAULT_TIMEOUT) or -1
if timeout < 0 then
error("expected timeout (1st argument) to be a number greater than or equal to 0, got: " .. tostring(seconds), 2)
end
return setmetatable({
timeout = timeout,
not_reentrant = not_reentrant,
queue = {},
q_tip = 0, -- index of the first in line waiting
q_tail = 0, -- index where the next one will be inserted
owner = nil, -- coroutine holding lock currently
call_count = nil, -- recursion call count
errors = setmetatable({}, { __mode = "k" }), -- error indexed by coroutine
}, lock)
end
do
local destroyed_func = function()
return nil, "destroyed"
end
local destroyed_lock_mt = {
__index = function()
return destroyed_func
end
}
--- destroy a lock.
-- Releases all waiting threads with `nil+"destroyed"`
function lock:destroy()
--print("destroying ",self)
for i = self.q_tip, self.q_tail do
local co = self.queue[i]
self.queue[i] = nil
if co then
self.errors[co] = "destroyed"
--print("marked destroyed ", co)
copas.wakeup(co)
end
end
if self.owner then
self.errors[self.owner] = "destroyed"
--print("marked destroyed ", co)
end
self.queue = {}
self.q_tip = 0
self.q_tail = 0
self.destroyed = true
setmetatable(self, destroyed_lock_mt)
return true
end
end
local function timeout_handler(co)
local self = registry[co]
if not self then
return
end
for i = self.q_tip, self.q_tail do
if co == self.queue[i] then
self.queue[i] = nil
self.errors[co] = "timeout"
--print("marked timeout ", co)
copas.wakeup(co)
return
end
end
-- if we get here, we own it currently, or we finished it by now, or
-- the lock was destroyed. Anyway, nothing to do here...
end
--- Acquires the lock.
-- If the lock is owned by another thread, this will yield control, until the
-- lock becomes available, or it times out.
-- If `timeout == 0` then it will immediately return (without yielding).
-- @param timeout (optional) timeout in seconds, defaults to the timeout passed to `new` (use `math.huge` to have no timeout).
-- @return wait-time on success, or nil+error+wait_time on failure. Errors can be "timeout", "destroyed", or "lock is not re-entrant"
function lock:get(timeout)
local co = coroutine.running()
local start_time
-- is the lock already taken?
if self.owner then
-- are we re-entering?
if co == self.owner and not self.not_reentrant then
self.call_count = self.call_count + 1
return 0
end
self.queue[self.q_tail] = co
self.q_tail = self.q_tail + 1
timeout = timeout or self.timeout
if timeout == 0 then
return nil, "timeout", 0
end
-- set up timeout
registry[co] = self
copas.timeout(timeout, timeout_handler)
start_time = gettime()
copas.pauseforever()
local err = self.errors[co]
self.errors[co] = nil
registry[co] = nil
--print("released ", co, err)
if err ~= "timeout" then
copas.timeout(0)
end
if err then
return nil, err, gettime() - start_time
end
end
-- it's ours to have
self.owner = co
self.call_count = 1
return start_time and (gettime() - start_time) or 0
end
--- Releases the lock currently held.
-- Releasing a lock that is not owned by the current co-routine will return
-- an error.
-- returns true, or nil+err on an error
function lock:release()
local co = coroutine.running()
if co ~= self.owner then
return nil, "cannot release a lock not owned"
end
self.call_count = self.call_count - 1
if self.call_count > 0 then
-- same coro is still holding it
return true
end
-- need a loop, since individual coroutines might have been removed
-- so there might be holes
while self.q_tip < self.q_tail do
local next_up = self.queue[self.q_tip]
if next_up then
self.owner = next_up
self.queue[self.q_tip] = nil
self.q_tip = self.q_tip + 1
copas.wakeup(next_up)
return true
end
self.q_tip = self.q_tip + 1
end
-- queue is empty, reset pointers
self.owner = nil
self.q_tip = 0
self.q_tail = 0
return true
end
return lock

191
thirdparty/copas/src/copas/queue.lua vendored Normal file
View file

@ -0,0 +1,191 @@
local copas = require "copas"
local Sema = copas.semaphore
local Lock = copas.lock
local Queue = {}
Queue.__index = Queue
local new_name do
local count = 0
function new_name()
count = count + 1
return "copas_queue_" .. count
end
end
-- Creates a new Queue instance
function Queue.new(opts)
opts = opts or {}
local self = {}
setmetatable(self, Queue)
self.name = opts.name or new_name()
self.sema = Sema.new(10^9)
self.head = 1
self.tail = 1
self.list = {}
self.workers = setmetatable({}, { __mode = "k" })
self.stopping = false
self.worker_id = 0
return self
end
-- Pushes an item in the queue (can be 'nil')
-- returns true, or nil+err ("stopping", or "destroyed")
function Queue:push(item)
if self.stopping then
return nil, "stopping"
end
self.list[self.head] = item
self.head = self.head + 1
self.sema:give()
return true
end
-- Pops and item from the queue. If there are no items in the queue it will yield
-- until there are or a timeout happens (exception is when `timeout == 0`, then it will
-- not yield but return immediately). If the timeout is `math.huge` it will wait forever.
-- Returns item, or nil+err ("timeout", or "destroyed")
function Queue:pop(timeout)
local ok, err = self.sema:take(1, timeout)
if not ok then
return ok, err
end
local item = self.list[self.tail]
self.list[self.tail] = nil
self.tail = self.tail + 1
if self.tail == self.head then
-- reset queue
self.list = {}
self.tail = 1
self.head = 1
if self.stopping then
-- we're stopping and last item being returned, so we're done
self:destroy()
end
end
return item
end
-- return the number of items left in the queue
function Queue:get_size()
return self.head - self.tail
end
-- instructs the queue to stop. Will not accept any more 'push' calls.
-- will autocall 'destroy' when the queue is empty.
-- returns immediately. See `finish`
function Queue:stop()
if not self.stopping then
self.stopping = true
self.lock = Lock.new(nil, true)
self.lock:get() -- close the lock
if self:get_size() == 0 then
-- queue is already empty, so "pop" function cannot call destroy on next
-- pop, so destroy now.
self:destroy()
end
end
return true
end
-- Finishes a queue. Calls stop and then waits for the queue to run empty (and be
-- destroyed) before returning. returns true or nil+err ("timeout", or "destroyed")
-- Parameter no_destroy_on_timeout indicates if the queue is not to be forcefully
-- destroyed on a timeout.
function Queue:finish(timeout, no_destroy_on_timeout)
self:stop()
local _, err = self.lock:get(timeout)
-- the lock never gets released, only destroyed, so we have to check the error string
if err == "timeout" then
if not no_destroy_on_timeout then
self:destroy()
end
return nil, err
end
return true
end
do
local destroyed_func = function()
return nil, "destroyed"
end
local destroyed_queue_mt = {
__index = function()
return destroyed_func
end
}
-- destroys a queue immediately. Abandons what is left in the queue.
-- Releases all waiting threads with `nil+"destroyed"`
function Queue:destroy()
if self.lock then
self.lock:destroy()
end
self.sema:destroy()
setmetatable(self, destroyed_queue_mt)
-- clear anything left in the queue
for key in pairs(self.list) do
self.list[key] = nil
end
return true
end
end
-- adds a worker that will handle whatever is passed into the queue. Can be called
-- multiple times to add more workers.
-- The threads automatically exit when the queue is destroyed.
-- worker function signature: `function(item)` (Note: worker functions run
-- unprotected, so wrap code in an (x)pcall if errors are expected, otherwise the
-- worker will exit on an error, and queue handling will stop)
-- Returns the coroutine added.
function Queue:add_worker(worker)
assert(type(worker) == "function", "expected worker to be a function")
local coro
self.worker_id = self.worker_id + 1
local worker_name = self.name .. ":worker_" .. self.worker_id
coro = copas.addnamedthread(worker_name, function()
while true do
local item, err = self:pop(math.huge) -- wait forever
if err then
break -- queue destroyed, exit
end
worker(item) -- TODO: wrap in errorhandling
end
self.workers[coro] = nil
end)
self.workers[coro] = true
return coro
end
-- returns a list/array of current workers (coroutines) handling the queue.
-- (only the workers added by `add_worker`, and still active, will be in this list)
function Queue:get_workers()
local lst = {}
for coro in pairs(self.workers) do
if coroutine.status(coro) ~= "dead" then
lst[#lst+1] = coro
end
end
return lst
end
return Queue

202
thirdparty/copas/src/copas/semaphore.lua vendored Normal file
View file

@ -0,0 +1,202 @@
local copas = require("copas")
local DEFAULT_TIMEOUT = 10
local semaphore = {}
semaphore.__index = semaphore
-- registry, semaphore indexed by the coroutines using them.
local registry = setmetatable({}, { __mode="kv" })
-- create a new semaphore
-- @param max maximum number of resources the semaphore can hold (this maximum does NOT include resources that have been given but not yet returned).
-- @param start (optional, default 0) the initial resources available
-- @param seconds (optional, default 10) default semaphore timeout in seconds, or `math.huge` to have no timeout.
function semaphore.new(max, start, seconds)
local timeout = tonumber(seconds or DEFAULT_TIMEOUT) or -1
if timeout < 0 then
error("expected timeout (2nd argument) to be a number greater than or equal to 0, got: " .. tostring(seconds), 2)
end
if type(max) ~= "number" or max < 1 then
error("expected max resources (1st argument) to be a number greater than 0, got: " .. tostring(max), 2)
end
local self = setmetatable({
count = start or 0,
max = max,
timeout = timeout,
q_tip = 1, -- position of next entry waiting
q_tail = 1, -- position where next one will be inserted
queue = {},
to_flags = setmetatable({}, { __mode = "k" }), -- timeout flags indexed by coroutine
}, semaphore)
return self
end
do
local destroyed_func = function()
return nil, "destroyed"
end
local destroyed_semaphore_mt = {
__index = function()
return destroyed_func
end
}
-- destroy a semaphore.
-- Releases all waiting threads with `nil+"destroyed"`
function semaphore:destroy()
self:give(math.huge)
self.destroyed = true
setmetatable(self, destroyed_semaphore_mt)
return true
end
end
-- Gives resources.
-- @param given (optional, default 1) number of resources to return. If more
-- than the maximum are returned then it will be capped at the maximum and
-- error "too many" will be returned.
function semaphore:give(given)
local err
given = given or 1
local count = self.count + given
--print("now at",count, ", after +"..given)
if count > self.max then
count = self.max
err = "too many"
end
while self.q_tip < self.q_tail do
local i = self.q_tip
local nxt = self.queue[i] -- there can be holes, so nxt might be nil
if not nxt then
self.q_tip = i + 1
else
if count >= nxt.requested then
-- release it
self.queue[i] = nil
self.to_flags[nxt.co] = nil
count = count - nxt.requested
self.q_tip = i + 1
copas.wakeup(nxt.co)
nxt.co = nil
else
break -- we ran out of resources
end
end
end
if self.q_tip == self.q_tail then -- reset queue
self.queue = {}
self.q_tip = 1
self.q_tail = 1
end
self.count = count
if err then
return nil, err
end
return true
end
local function timeout_handler(co)
local self = registry[co]
--print("checking timeout ", co)
if not self then
return
end
for i = self.q_tip, self.q_tail do
local item = self.queue[i]
if item and co == item.co then
self.queue[i] = nil
self.to_flags[co] = true
--print("marked timeout ", co)
copas.wakeup(co)
return
end
end
-- nothing to do here...
end
-- Requests resources from the semaphore.
-- Waits if there are not enough resources available before returning.
-- @param requested (optional, default 1) the number of resources requested
-- @param timeout (optional, defaults to semaphore timeout) timeout in
-- seconds. If 0 it will either succeed or return immediately with error "timeout".
-- If `math.huge` it will wait forever.
-- @return true, or nil+"destroyed"
function semaphore:take(requested, timeout)
requested = requested or 1
if self.q_tail == 1 and self.count >= requested then
-- nobody is waiting before us, and there is enough in store
self.count = self.count - requested
return true
end
if requested > self.max then
return nil, "too many"
end
local to = timeout or self.timeout
if to == 0 then
return nil, "timeout"
end
-- get in line
local co = coroutine.running()
self.to_flags[co] = nil
registry[co] = self
copas.timeout(to, timeout_handler)
self.queue[self.q_tail] = {
co = co,
requested = requested,
--timeout = nil, -- flag indicating timeout
}
self.q_tail = self.q_tail + 1
copas.pauseforever() -- block until woken
registry[co] = nil
if self.to_flags[co] then
-- a timeout happened
self.to_flags[co] = nil
return nil, "timeout"
end
copas.timeout(0)
if self.destroyed then
return nil, "destroyed"
end
return true
end
-- returns current available resources
function semaphore:get_count()
return self.count
end
-- returns total shortage for requested resources
function semaphore:get_wait()
local wait = 0
for i = self.q_tip, self.q_tail - 1 do
wait = wait + ((self.queue[i] or {}).requested or 0)
end
return wait - self.count
end
return semaphore

34
thirdparty/copas/src/copas/smtp.lua vendored Normal file
View file

@ -0,0 +1,34 @@
-------------------------------------------------------------------
-- identical to the socket.smtp module except that it uses
-- async wrapped Copas sockets
local copas = require("copas")
local smtp = require("socket.smtp")
local socket = require("socket")
local create = function() return copas.wrap(socket.tcp()) end
local forwards = { -- setting these will be forwarded to the original smtp module
PORT = true,
SERVER = true,
TIMEOUT = true,
DOMAIN = true,
TIMEZONE = true
}
copas.smtp = setmetatable({}, {
-- use original module as metatable, to lookup constants like socket.SERVER, etc.
__index = smtp,
-- Setting constants is forwarded to the luasocket.smtp module.
__newindex = function(self, key, value)
if forwards[key] then smtp[key] = value return end
return rawset(self, key, value)
end,
})
local _M = copas.smtp
_M.send = function(mailt)
mailt.create = mailt.create or create
return smtp.send(mailt)
end
return _M

130
thirdparty/copas/src/copas/timer.lua vendored Normal file
View file

@ -0,0 +1,130 @@
local copas = require("copas")
local xpcall = xpcall
local coroutine_running = coroutine.running
if _VERSION=="Lua 5.1" and not jit then -- obsolete: only for Lua 5.1 compatibility
xpcall = require("coxpcall").xpcall
coroutine_running = require("coxpcall").running
end
local timer = {}
timer.__index = timer
local new_name do
local count = 0
function new_name()
count = count + 1
return "copas_timer_" .. count
end
end
do
local function expire_func(self, initial_delay)
if self.errorhandler then
copas.seterrorhandler(self.errorhandler)
end
copas.pause(initial_delay)
while true do
if not self.cancelled then
if not self.recurring then
-- non-recurring timer
self.cancelled = true
self.co = nil
self:callback(self.params)
return
else
-- recurring timer
self:callback(self.params)
end
end
if self.cancelled then
-- clean up and exit the thread
self.co = nil
self.cancelled = true
return
end
copas.pause(self.delay)
end
end
--- Arms the timer object.
-- @param initial_delay (optional) the first delay to use, if not provided uses the timer delay
-- @return timer object, nil+error, or throws an error on bad input
function timer:arm(initial_delay)
assert(initial_delay == nil or initial_delay >= 0, "delay must be greater than or equal to 0")
if self.co then
return nil, "already armed"
end
self.cancelled = false
self.co = copas.addnamedthread(self.name, expire_func, self, initial_delay or self.delay)
return self
end
end
--- Cancels a running timer.
-- @return timer object, or nil+error
function timer:cancel()
if not self.co then
return nil, "not armed"
end
if self.cancelled then
return nil, "already cancelled"
end
self.cancelled = true
copas.wakeup(self.co) -- resume asap
copas.removethread(self.co) -- will immediately drop the thread upon resuming
self.co = nil
return self
end
do
-- xpcall error handler that forwards to the copas errorhandler
local ehandler = function(err_obj)
return copas.geterrorhandler()(err_obj, coroutine_running(), nil)
end
--- Creates a new timer object.
-- Note: the callback signature is: `function(timer_obj, params)`.
-- @param opts (table) `opts.delay` timer delay in seconds, `opts.callback` function to execute, `opts.recurring` boolean
-- `opts.params` (optional) this value will be passed to the timer callback, `opts.initial_delay` (optional) the first delay to use, defaults to `delay`.
-- @return timer object, or throws an error on bad input
function timer.new(opts)
assert(opts.delay or -1 >= 0, "delay must be greater than or equal to 0")
assert(type(opts.callback) == "function", "expected callback to be a function")
local callback = function(timer_obj, params)
xpcall(opts.callback, ehandler, timer_obj, params)
end
return setmetatable({
name = opts.name or new_name(),
delay = opts.delay,
callback = callback,
recurring = not not opts.recurring,
params = opts.params,
cancelled = false,
errorhandler = opts.errorhandler,
}, timer):arm(opts.initial_delay)
end
end
return timer

View file

@ -0,0 +1,3 @@
The certificate generation scripts here are copied from LuaSec

14
thirdparty/copas/tests/certs/all.bat vendored Normal file
View file

@ -0,0 +1,14 @@
REM make sure the 'openssl.exe' commandline tool is in your path before starting!
REM set the path below;
set opensslpath=c:\program files (x86)\openssl-win32\bin
setlocal
set path=%opensslpath%;%path%
call roota.bat
call rootb.bat
call servera.bat
call serverb.bat
call clienta.bat
call clientb.bat

13
thirdparty/copas/tests/certs/all.sh vendored Executable file
View file

@ -0,0 +1,13 @@
#!/bin/sh
CWD=$(PWD)
cd $( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
./rootA.sh
./rootB.sh
./serverA.sh
./serverB.sh
./clientA.sh
./clientB.sh
cd $CWD

View file

@ -0,0 +1,9 @@
rem #!/bin/sh
openssl req -newkey rsa:1024 -sha1 -keyout clientAkey.pem -out clientAreq.pem -nodes -config ./clientA.cnf -days 365 -batch
openssl x509 -req -in clientAreq.pem -sha1 -extfile ./clientA.cnf -extensions usr_cert -CA rootA.pem -CAkey rootAkey.pem -CAcreateserial -out clientAcert.pem -days 365
copy clientAcert.pem + rootA.pem clientA.pem
openssl x509 -subject -issuer -noout -in clientA.pem

316
thirdparty/copas/tests/certs/clientA.cnf vendored Normal file
View file

@ -0,0 +1,316 @@
#
# OpenSSL example configuration file.
# This is mostly being used for generation of certificate requests.
#
# This definition stops the following lines choking if HOME isn't
# defined.
HOME = .
RANDFILE = $ENV::HOME/.rnd
# Extra OBJECT IDENTIFIER info:
#oid_file = $ENV::HOME/.oid
oid_section = new_oids
# To use this configuration file with the "-extfile" option of the
# "openssl x509" utility, name here the section containing the
# X.509v3 extensions to use:
# extensions =
# (Alternatively, use a configuration file that has only
# X.509v3 extensions in its main [= default] section.)
[ new_oids ]
# We can add new OIDs in here for use by 'ca' and 'req'.
# Add a simple OID like this:
# testoid1=1.2.3.4
# Or use config file substitution like this:
# testoid2=${testoid1}.5.6
####################################################################
[ ca ]
default_ca = CA_default # The default ca section
####################################################################
[ CA_default ]
dir = ./demoCA # Where everything is kept
certs = $dir/certs # Where the issued certs are kept
crl_dir = $dir/crl # Where the issued crl are kept
database = $dir/index.txt # database index file.
#unique_subject = no # Set to 'no' to allow creation of
# several ctificates with same subject.
new_certs_dir = $dir/newcerts # default place for new certs.
certificate = $dir/cacert.pem # The CA certificate
serial = $dir/serial # The current serial number
crlnumber = $dir/crlnumber # the current crl number
# must be commented out to leave a V1 CRL
crl = $dir/crl.pem # The current CRL
private_key = $dir/private/cakey.pem # The private key
RANDFILE = $dir/private/.rand # private random number file
x509_extensions = usr_cert # The extensions to add to the cert
# Comment out the following two lines for the "traditional"
# (and highly broken) format.
name_opt = ca_default # Subject Name options
cert_opt = ca_default # Certificate field options
# Extension copying option: use with caution.
# copy_extensions = copy
# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
# so this is commented out by default to leave a V1 CRL.
# crlnumber must also be commented out to leave a V1 CRL.
# crl_extensions = crl_ext
default_days = 365 # how long to certify for
default_crl_days= 30 # how long before next CRL
default_md = sha1 # which md to use.
preserve = no # keep passed DN ordering
# A few difference way of specifying how similar the request should look
# For type CA, the listed attributes must be the same, and the optional
# and supplied fields are just that :-)
policy = policy_match
# For the CA policy
[ policy_match ]
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
# For the 'anything' policy
# At this point in time, you must list all acceptable 'object'
# types.
[ policy_anything ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
####################################################################
[ req ]
default_bits = 1024
default_keyfile = privkey.pem
distinguished_name = req_distinguished_name
attributes = req_attributes
x509_extensions = v3_ca # The extensions to add to the self signed cert
# Passwords for private keys if not present they will be prompted for
# input_password = secret
# output_password = secret
# This sets a mask for permitted string types. There are several options.
# default: PrintableString, T61String, BMPString.
# pkix : PrintableString, BMPString.
# utf8only: only UTF8Strings.
# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
# MASK:XXXX a literal mask value.
# WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings
# so use this option with caution!
string_mask = nombstr
# req_extensions = v3_req # The extensions to add to a certificate request
[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = BR
countryName_min = 2
countryName_max = 2
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = Some-State
stateOrProvinceName_default = Espirito Santo
localityName = Locality Name (eg, city)
localityName_default = Santo Antonio do Canaa
0.organizationName = Organization Name (eg, company)
0.organizationName_default = Sao Tonico Ltda
# we can do this but it is not needed normally :-)
#1.organizationName = Second Organization Name (eg, company)
#1.organizationName_default = World Wide Web Pty Ltd
organizationalUnitName = Organizational Unit Name (eg, section)
organizationalUnitName_default = Department of Computer Science
commonName = Common Name (eg, YOUR name)
commonName_default = Client A
commonName_max = 64
emailAddress = Email Address
emailAddress_max = 64
# SET-ex3 = SET extension number 3
[ req_attributes ]
challengePassword = A challenge password
challengePassword_min = 4
challengePassword_max = 20
unstructuredName = An optional company name
[ usr_cert ]
# These extensions are added when 'ca' signs a request.
# This goes against PKIX guidelines but some CAs do it and some software
# requires this to avoid interpreting an end user certificate as a CA.
basicConstraints=CA:FALSE
# Here are some examples of the usage of nsCertType. If it is omitted
# the certificate can be used for anything *except* object signing.
# This is OK for an SSL server.
# nsCertType = server
# For an object signing certificate this would be used.
# nsCertType = objsign
# For normal client use this is typical
# nsCertType = client, email
# and for everything including object signing:
# nsCertType = client, email, objsign
# This is typical in keyUsage for a client certificate.
# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
# This will be displayed in Netscape's comment listbox.
nsComment = "OpenSSL Generated Certificate"
# PKIX recommendations harmless if included in all certificates.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
# This stuff is for subjectAltName and issuerAltname.
# Import the email address.
# subjectAltName=email:copy
# An alternative to produce certificates that aren't
# deprecated according to PKIX.
# subjectAltName=email:move
# Copy subject details
# issuerAltName=issuer:copy
#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
#nsBaseUrl
#nsRevocationUrl
#nsRenewalUrl
#nsCaPolicyUrl
#nsSslServerName
[ v3_req ]
# Extensions to add to a certificate request
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
[ v3_ca ]
# Extensions for a typical CA
# PKIX recommendation.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer:always
# This is what PKIX recommends but some broken software chokes on critical
# extensions.
#basicConstraints = critical,CA:true
# So we do this instead.
basicConstraints = CA:true
# Key usage: this is typical for a CA certificate. However since it will
# prevent it being used as an test self-signed certificate it is best
# left out by default.
# keyUsage = cRLSign, keyCertSign
# Some might want this also
# nsCertType = sslCA, emailCA
# Include email address in subject alt name: another PKIX recommendation
# subjectAltName=email:copy
# Copy issuer details
# issuerAltName=issuer:copy
# DER hex encoding of an extension: beware experts only!
# obj=DER:02:03
# Where 'obj' is a standard or added object
# You can even override a supported extension:
# basicConstraints= critical, DER:30:03:01:01:FF
[ crl_ext ]
# CRL extensions.
# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
# issuerAltName=issuer:copy
authorityKeyIdentifier=keyid:always,issuer:always
[ proxy_cert_ext ]
# These extensions should be added when creating a proxy certificate
# This goes against PKIX guidelines but some CAs do it and some software
# requires this to avoid interpreting an end user certificate as a CA.
basicConstraints=CA:FALSE
# Here are some examples of the usage of nsCertType. If it is omitted
# the certificate can be used for anything *except* object signing.
# This is OK for an SSL server.
# nsCertType = server
# For an object signing certificate this would be used.
# nsCertType = objsign
# For normal client use this is typical
# nsCertType = client, email
# and for everything including object signing:
# nsCertType = client, email, objsign
# This is typical in keyUsage for a client certificate.
# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
# This will be displayed in Netscape's comment listbox.
nsComment = "OpenSSL Generated Certificate"
# PKIX recommendations harmless if included in all certificates.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer:always
# This stuff is for subjectAltName and issuerAltname.
# Import the email address.
# subjectAltName=email:copy
# An alternative to produce certificates that aren't
# deprecated according to PKIX.
# subjectAltName=email:move
# Copy subject details
# issuerAltName=issuer:copy
#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
#nsBaseUrl
#nsRevocationUrl
#nsRenewalUrl
#nsCaPolicyUrl
#nsSslServerName
# This really needs to be in place for it to be a proxy certificate.
proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo

12
thirdparty/copas/tests/certs/clientA.sh vendored Executable file
View file

@ -0,0 +1,12 @@
#!/bin/sh
openssl req -newkey rsa:1024 -sha1 -keyout clientAkey.pem -out clientAreq.pem \
-nodes -config ./clientA.cnf -days 365 -batch
openssl x509 -req -in clientAreq.pem -sha1 -extfile ./clientA.cnf \
-extensions usr_cert -CA rootA.pem -CAkey rootAkey.pem -CAcreateserial \
-out clientAcert.pem -days 365
cat clientAcert.pem rootA.pem > clientA.pem
openssl x509 -subject -issuer -noout -in clientA.pem

View file

@ -0,0 +1,9 @@
rem #!/bin/sh
openssl req -newkey rsa:1024 -sha1 -keyout clientBkey.pem -out clientBreq.pem -nodes -config ./clientB.cnf -days 365 -batch
openssl x509 -req -in clientBreq.pem -sha1 -extfile ./clientB.cnf -extensions usr_cert -CA rootB.pem -CAkey rootBkey.pem -CAcreateserial -out clientBcert.pem -days 365
copy clientBcert.pem + rootB.pem clientB.pem
openssl x509 -subject -issuer -noout -in clientB.pem

316
thirdparty/copas/tests/certs/clientB.cnf vendored Normal file
View file

@ -0,0 +1,316 @@
#
# OpenSSL example configuration file.
# This is mostly being used for generation of certificate requests.
#
# This definition stops the following lines choking if HOME isn't
# defined.
HOME = .
RANDFILE = $ENV::HOME/.rnd
# Extra OBJECT IDENTIFIER info:
#oid_file = $ENV::HOME/.oid
oid_section = new_oids
# To use this configuration file with the "-extfile" option of the
# "openssl x509" utility, name here the section containing the
# X.509v3 extensions to use:
# extensions =
# (Alternatively, use a configuration file that has only
# X.509v3 extensions in its main [= default] section.)
[ new_oids ]
# We can add new OIDs in here for use by 'ca' and 'req'.
# Add a simple OID like this:
# testoid1=1.2.3.4
# Or use config file substitution like this:
# testoid2=${testoid1}.5.6
####################################################################
[ ca ]
default_ca = CA_default # The default ca section
####################################################################
[ CA_default ]
dir = ./demoCA # Where everything is kept
certs = $dir/certs # Where the issued certs are kept
crl_dir = $dir/crl # Where the issued crl are kept
database = $dir/index.txt # database index file.
#unique_subject = no # Set to 'no' to allow creation of
# several ctificates with same subject.
new_certs_dir = $dir/newcerts # default place for new certs.
certificate = $dir/cacert.pem # The CA certificate
serial = $dir/serial # The current serial number
crlnumber = $dir/crlnumber # the current crl number
# must be commented out to leave a V1 CRL
crl = $dir/crl.pem # The current CRL
private_key = $dir/private/cakey.pem # The private key
RANDFILE = $dir/private/.rand # private random number file
x509_extensions = usr_cert # The extensions to add to the cert
# Comment out the following two lines for the "traditional"
# (and highly broken) format.
name_opt = ca_default # Subject Name options
cert_opt = ca_default # Certificate field options
# Extension copying option: use with caution.
# copy_extensions = copy
# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
# so this is commented out by default to leave a V1 CRL.
# crlnumber must also be commented out to leave a V1 CRL.
# crl_extensions = crl_ext
default_days = 365 # how long to certify for
default_crl_days= 30 # how long before next CRL
default_md = sha1 # which md to use.
preserve = no # keep passed DN ordering
# A few difference way of specifying how similar the request should look
# For type CA, the listed attributes must be the same, and the optional
# and supplied fields are just that :-)
policy = policy_match
# For the CA policy
[ policy_match ]
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
# For the 'anything' policy
# At this point in time, you must list all acceptable 'object'
# types.
[ policy_anything ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
####################################################################
[ req ]
default_bits = 1024
default_keyfile = privkey.pem
distinguished_name = req_distinguished_name
attributes = req_attributes
x509_extensions = v3_ca # The extensions to add to the self signed cert
# Passwords for private keys if not present they will be prompted for
# input_password = secret
# output_password = secret
# This sets a mask for permitted string types. There are several options.
# default: PrintableString, T61String, BMPString.
# pkix : PrintableString, BMPString.
# utf8only: only UTF8Strings.
# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
# MASK:XXXX a literal mask value.
# WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings
# so use this option with caution!
string_mask = nombstr
# req_extensions = v3_req # The extensions to add to a certificate request
[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = BR
countryName_min = 2
countryName_max = 2
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = Some-State
stateOrProvinceName_default = Espirito Santo
localityName = Locality Name (eg, city)
localityName_default = Santo Antonio do Canaa
0.organizationName = Organization Name (eg, company)
0.organizationName_default = Sao Tonico Ltda
# we can do this but it is not needed normally :-)
#1.organizationName = Second Organization Name (eg, company)
#1.organizationName_default = World Wide Web Pty Ltd
organizationalUnitName = Organizational Unit Name (eg, section)
organizationalUnitName_default = Department of Computer Science
commonName = Common Name (eg, YOUR name)
commonName_default = Client B
commonName_max = 64
emailAddress = Email Address
emailAddress_max = 64
# SET-ex3 = SET extension number 3
[ req_attributes ]
challengePassword = A challenge password
challengePassword_min = 4
challengePassword_max = 20
unstructuredName = An optional company name
[ usr_cert ]
# These extensions are added when 'ca' signs a request.
# This goes against PKIX guidelines but some CAs do it and some software
# requires this to avoid interpreting an end user certificate as a CA.
basicConstraints=CA:FALSE
# Here are some examples of the usage of nsCertType. If it is omitted
# the certificate can be used for anything *except* object signing.
# This is OK for an SSL server.
# nsCertType = server
# For an object signing certificate this would be used.
# nsCertType = objsign
# For normal client use this is typical
# nsCertType = client, email
# and for everything including object signing:
# nsCertType = client, email, objsign
# This is typical in keyUsage for a client certificate.
# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
# This will be displayed in Netscape's comment listbox.
nsComment = "OpenSSL Generated Certificate"
# PKIX recommendations harmless if included in all certificates.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
# This stuff is for subjectAltName and issuerAltname.
# Import the email address.
# subjectAltName=email:copy
# An alternative to produce certificates that aren't
# deprecated according to PKIX.
# subjectAltName=email:move
# Copy subject details
# issuerAltName=issuer:copy
#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
#nsBaseUrl
#nsRevocationUrl
#nsRenewalUrl
#nsCaPolicyUrl
#nsSslServerName
[ v3_req ]
# Extensions to add to a certificate request
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
[ v3_ca ]
# Extensions for a typical CA
# PKIX recommendation.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer:always
# This is what PKIX recommends but some broken software chokes on critical
# extensions.
#basicConstraints = critical,CA:true
# So we do this instead.
basicConstraints = CA:true
# Key usage: this is typical for a CA certificate. However since it will
# prevent it being used as an test self-signed certificate it is best
# left out by default.
# keyUsage = cRLSign, keyCertSign
# Some might want this also
# nsCertType = sslCA, emailCA
# Include email address in subject alt name: another PKIX recommendation
# subjectAltName=email:copy
# Copy issuer details
# issuerAltName=issuer:copy
# DER hex encoding of an extension: beware experts only!
# obj=DER:02:03
# Where 'obj' is a standard or added object
# You can even override a supported extension:
# basicConstraints= critical, DER:30:03:01:01:FF
[ crl_ext ]
# CRL extensions.
# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
# issuerAltName=issuer:copy
authorityKeyIdentifier=keyid:always,issuer:always
[ proxy_cert_ext ]
# These extensions should be added when creating a proxy certificate
# This goes against PKIX guidelines but some CAs do it and some software
# requires this to avoid interpreting an end user certificate as a CA.
basicConstraints=CA:FALSE
# Here are some examples of the usage of nsCertType. If it is omitted
# the certificate can be used for anything *except* object signing.
# This is OK for an SSL server.
# nsCertType = server
# For an object signing certificate this would be used.
# nsCertType = objsign
# For normal client use this is typical
# nsCertType = client, email
# and for everything including object signing:
# nsCertType = client, email, objsign
# This is typical in keyUsage for a client certificate.
# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
# This will be displayed in Netscape's comment listbox.
nsComment = "OpenSSL Generated Certificate"
# PKIX recommendations harmless if included in all certificates.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer:always
# This stuff is for subjectAltName and issuerAltname.
# Import the email address.
# subjectAltName=email:copy
# An alternative to produce certificates that aren't
# deprecated according to PKIX.
# subjectAltName=email:move
# Copy subject details
# issuerAltName=issuer:copy
#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
#nsBaseUrl
#nsRevocationUrl
#nsRenewalUrl
#nsCaPolicyUrl
#nsSslServerName
# This really needs to be in place for it to be a proxy certificate.
proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo

12
thirdparty/copas/tests/certs/clientB.sh vendored Executable file
View file

@ -0,0 +1,12 @@
#!/bin/sh
openssl req -newkey rsa:1024 -sha1 -keyout clientBkey.pem -out clientBreq.pem \
-nodes -config ./clientB.cnf -days 365 -batch
openssl x509 -req -in clientBreq.pem -sha1 -extfile ./clientB.cnf \
-extensions usr_cert -CA rootB.pem -CAkey rootBkey.pem -CAcreateserial \
-out clientBcert.pem -days 365
cat clientBcert.pem rootB.pem > clientB.pem
openssl x509 -subject -issuer -noout -in clientB.pem

View file

@ -0,0 +1,7 @@
REM #!/bin/sh
openssl req -newkey rsa:1024 -sha1 -keyout rootAkey.pem -out rootAreq.pem -nodes -config ./rootA.cnf -days 365 -batch
openssl x509 -req -in rootAreq.pem -sha1 -extfile ./rootA.cnf -extensions v3_ca -signkey rootAkey.pem -out rootA.pem -days 365
openssl x509 -subject -issuer -noout -in rootA.pem

315
thirdparty/copas/tests/certs/rootA.cnf vendored Normal file
View file

@ -0,0 +1,315 @@
#
# OpenSSL example configuration file.
# This is mostly being used for generation of certificate requests.
#
# This definition stops the following lines choking if HOME isn't
# defined.
HOME = .
RANDFILE = $ENV::HOME/.rnd
# Extra OBJECT IDENTIFIER info:
#oid_file = $ENV::HOME/.oid
oid_section = new_oids
# To use this configuration file with the "-extfile" option of the
# "openssl x509" utility, name here the section containing the
# X.509v3 extensions to use:
# extensions =
# (Alternatively, use a configuration file that has only
# X.509v3 extensions in its main [= default] section.)
[ new_oids ]
# We can add new OIDs in here for use by 'ca' and 'req'.
# Add a simple OID like this:
# testoid1=1.2.3.4
# Or use config file substitution like this:
# testoid2=${testoid1}.5.6
####################################################################
[ ca ]
default_ca = CA_default # The default ca section
####################################################################
[ CA_default ]
dir = ./demoCA # Where everything is kept
certs = $dir/certs # Where the issued certs are kept
crl_dir = $dir/crl # Where the issued crl are kept
database = $dir/index.txt # database index file.
#unique_subject = no # Set to 'no' to allow creation of
# several ctificates with same subject.
new_certs_dir = $dir/newcerts # default place for new certs.
certificate = $dir/cacert.pem # The CA certificate
serial = $dir/serial # The current serial number
crlnumber = $dir/crlnumber # the current crl number
# must be commented out to leave a V1 CRL
crl = $dir/crl.pem # The current CRL
private_key = $dir/private/cakey.pem # The private key
RANDFILE = $dir/private/.rand # private random number file
x509_extensions = usr_cert # The extensions to add to the cert
# Comment out the following two lines for the "traditional"
# (and highly broken) format.
name_opt = ca_default # Subject Name options
cert_opt = ca_default # Certificate field options
# Extension copying option: use with caution.
# copy_extensions = copy
# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
# so this is commented out by default to leave a V1 CRL.
# crlnumber must also be commented out to leave a V1 CRL.
# crl_extensions = crl_ext
default_days = 365 # how long to certify for
default_crl_days= 30 # how long before next CRL
default_md = sha1 # which md to use.
preserve = no # keep passed DN ordering
# A few difference way of specifying how similar the request should look
# For type CA, the listed attributes must be the same, and the optional
# and supplied fields are just that :-)
policy = policy_match
# For the CA policy
[ policy_match ]
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
# For the 'anything' policy
# At this point in time, you must list all acceptable 'object'
# types.
[ policy_anything ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
####################################################################
[ req ]
default_bits = 1024
default_keyfile = privkey.pem
distinguished_name = req_distinguished_name
attributes = req_attributes
x509_extensions = v3_ca # The extensions to add to the self signed cert
# Passwords for private keys if not present they will be prompted for
# input_password = secret
# output_password = secret
# This sets a mask for permitted string types. There are several options.
# default: PrintableString, T61String, BMPString.
# pkix : PrintableString, BMPString.
# utf8only: only UTF8Strings.
# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
# MASK:XXXX a literal mask value.
# WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings
# so use this option with caution!
string_mask = nombstr
# req_extensions = v3_req # The extensions to add to a certificate request
[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = BR
countryName_min = 2
countryName_max = 2
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = Espirito Santo
localityName = Locality Name (eg, city)
localityName_default = Santo Antonio do Canaa
0.organizationName = Organization Name (eg, company)
0.organizationName_default = Santo Tonico Ltda
# we can do this but it is not needed normally :-)
#1.organizationName = Second Organization Name (eg, company)
#1.organizationName_default = World Wide Web Pty Ltd
organizationalUnitName = Organizational Unit Name (eg, section)
organizationalUnitName_default = Department of Computer Science
commonName = Common Name (eg, YOUR name)
commonName_max = 64
commonName_default = Root A
emailAddress = Email Address
emailAddress_max = 64
# SET-ex3 = SET extension number 3
[ req_attributes ]
challengePassword = A challenge password
challengePassword_min = 4
challengePassword_max = 20
unstructuredName = An optional company name
[ usr_cert ]
# These extensions are added when 'ca' signs a request.
# This goes against PKIX guidelines but some CAs do it and some software
# requires this to avoid interpreting an end user certificate as a CA.
basicConstraints=CA:FALSE
# Here are some examples of the usage of nsCertType. If it is omitted
# the certificate can be used for anything *except* object signing.
# This is OK for an SSL server.
# nsCertType = server
# For an object signing certificate this would be used.
# nsCertType = objsign
# For normal client use this is typical
# nsCertType = client, email
# and for everything including object signing:
# nsCertType = client, email, objsign
# This is typical in keyUsage for a client certificate.
# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
# This will be displayed in Netscape's comment listbox.
nsComment = "OpenSSL Generated Certificate"
# PKIX recommendations harmless if included in all certificates.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
# This stuff is for subjectAltName and issuerAltname.
# Import the email address.
# subjectAltName=email:copy
# An alternative to produce certificates that aren't
# deprecated according to PKIX.
# subjectAltName=email:move
# Copy subject details
# issuerAltName=issuer:copy
#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
#nsBaseUrl
#nsRevocationUrl
#nsRenewalUrl
#nsCaPolicyUrl
#nsSslServerName
[ v3_req ]
# Extensions to add to a certificate request
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
[ v3_ca ]
# Extensions for a typical CA
# PKIX recommendation.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer:always
# This is what PKIX recommends but some broken software chokes on critical
# extensions.
#basicConstraints = critical,CA:true
# So we do this instead.
basicConstraints = CA:true
# Key usage: this is typical for a CA certificate. However since it will
# prevent it being used as an test self-signed certificate it is best
# left out by default.
# keyUsage = cRLSign, keyCertSign
# Some might want this also
# nsCertType = sslCA, emailCA
# Include email address in subject alt name: another PKIX recommendation
# subjectAltName=email:copy
# Copy issuer details
# issuerAltName=issuer:copy
# DER hex encoding of an extension: beware experts only!
# obj=DER:02:03
# Where 'obj' is a standard or added object
# You can even override a supported extension:
# basicConstraints= critical, DER:30:03:01:01:FF
[ crl_ext ]
# CRL extensions.
# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
# issuerAltName=issuer:copy
authorityKeyIdentifier=keyid:always,issuer:always
[ proxy_cert_ext ]
# These extensions should be added when creating a proxy certificate
# This goes against PKIX guidelines but some CAs do it and some software
# requires this to avoid interpreting an end user certificate as a CA.
basicConstraints=CA:FALSE
# Here are some examples of the usage of nsCertType. If it is omitted
# the certificate can be used for anything *except* object signing.
# This is OK for an SSL server.
# nsCertType = server
# For an object signing certificate this would be used.
# nsCertType = objsign
# For normal client use this is typical
# nsCertType = client, email
# and for everything including object signing:
# nsCertType = client, email, objsign
# This is typical in keyUsage for a client certificate.
# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
# This will be displayed in Netscape's comment listbox.
nsComment = "OpenSSL Generated Certificate"
# PKIX recommendations harmless if included in all certificates.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer:always
# This stuff is for subjectAltName and issuerAltname.
# Import the email address.
# subjectAltName=email:copy
# An alternative to produce certificates that aren't
# deprecated according to PKIX.
# subjectAltName=email:move
# Copy subject details
# issuerAltName=issuer:copy
#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
#nsBaseUrl
#nsRevocationUrl
#nsRenewalUrl
#nsCaPolicyUrl
#nsSslServerName
# This really needs to be in place for it to be a proxy certificate.
proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo

7
thirdparty/copas/tests/certs/rootA.sh vendored Executable file
View file

@ -0,0 +1,7 @@
#!/bin/sh
openssl req -newkey rsa:1024 -sha1 -keyout rootAkey.pem -out rootAreq.pem -nodes -config ./rootA.cnf -days 365 -batch
openssl x509 -req -in rootAreq.pem -sha1 -extfile ./rootA.cnf -extensions v3_ca -signkey rootAkey.pem -out rootA.pem -days 365
openssl x509 -subject -issuer -noout -in rootA.pem

View file

@ -0,0 +1,7 @@
rem #!/bin/sh
openssl req -newkey rsa:1024 -sha1 -keyout rootBkey.pem -out rootBreq.pem -nodes -config ./rootB.cnf -days 365 -batch
openssl x509 -req -in rootBreq.pem -sha1 -extfile ./rootB.cnf -extensions v3_ca -signkey rootBkey.pem -out rootB.pem -days 365
openssl x509 -subject -issuer -noout -in rootB.pem

315
thirdparty/copas/tests/certs/rootB.cnf vendored Normal file
View file

@ -0,0 +1,315 @@
#
# OpenSSL example configuration file.
# This is mostly being used for generation of certificate requests.
#
# This definition stops the following lines choking if HOME isn't
# defined.
HOME = .
RANDFILE = $ENV::HOME/.rnd
# Extra OBJECT IDENTIFIER info:
#oid_file = $ENV::HOME/.oid
oid_section = new_oids
# To use this configuration file with the "-extfile" option of the
# "openssl x509" utility, name here the section containing the
# X.509v3 extensions to use:
# extensions =
# (Alternatively, use a configuration file that has only
# X.509v3 extensions in its main [= default] section.)
[ new_oids ]
# We can add new OIDs in here for use by 'ca' and 'req'.
# Add a simple OID like this:
# testoid1=1.2.3.4
# Or use config file substitution like this:
# testoid2=${testoid1}.5.6
####################################################################
[ ca ]
default_ca = CA_default # The default ca section
####################################################################
[ CA_default ]
dir = ./demoCA # Where everything is kept
certs = $dir/certs # Where the issued certs are kept
crl_dir = $dir/crl # Where the issued crl are kept
database = $dir/index.txt # database index file.
#unique_subject = no # Set to 'no' to allow creation of
# several ctificates with same subject.
new_certs_dir = $dir/newcerts # default place for new certs.
certificate = $dir/cacert.pem # The CA certificate
serial = $dir/serial # The current serial number
crlnumber = $dir/crlnumber # the current crl number
# must be commented out to leave a V1 CRL
crl = $dir/crl.pem # The current CRL
private_key = $dir/private/cakey.pem # The private key
RANDFILE = $dir/private/.rand # private random number file
x509_extensions = usr_cert # The extensions to add to the cert
# Comment out the following two lines for the "traditional"
# (and highly broken) format.
name_opt = ca_default # Subject Name options
cert_opt = ca_default # Certificate field options
# Extension copying option: use with caution.
# copy_extensions = copy
# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
# so this is commented out by default to leave a V1 CRL.
# crlnumber must also be commented out to leave a V1 CRL.
# crl_extensions = crl_ext
default_days = 365 # how long to certify for
default_crl_days= 30 # how long before next CRL
default_md = sha1 # which md to use.
preserve = no # keep passed DN ordering
# A few difference way of specifying how similar the request should look
# For type CA, the listed attributes must be the same, and the optional
# and supplied fields are just that :-)
policy = policy_match
# For the CA policy
[ policy_match ]
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
# For the 'anything' policy
# At this point in time, you must list all acceptable 'object'
# types.
[ policy_anything ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
####################################################################
[ req ]
default_bits = 1024
default_keyfile = privkey.pem
distinguished_name = req_distinguished_name
attributes = req_attributes
x509_extensions = v3_ca # The extensions to add to the self signed cert
# Passwords for private keys if not present they will be prompted for
# input_password = secret
# output_password = secret
# This sets a mask for permitted string types. There are several options.
# default: PrintableString, T61String, BMPString.
# pkix : PrintableString, BMPString.
# utf8only: only UTF8Strings.
# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
# MASK:XXXX a literal mask value.
# WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings
# so use this option with caution!
string_mask = nombstr
# req_extensions = v3_req # The extensions to add to a certificate request
[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = BR
countryName_min = 2
countryName_max = 2
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = Espirito Santo
localityName = Locality Name (eg, city)
localityName_default = Santo Antonio do Canaa
0.organizationName = Organization Name (eg, company)
0.organizationName_default = Sao Tonico Ltda
# we can do this but it is not needed normally :-)
#1.organizationName = Second Organization Name (eg, company)
#1.organizationName_default = World Wide Web Pty Ltd
organizationalUnitName = Organizational Unit Name (eg, section)
organizationalUnitName_default = Department of Computer Science
commonName = Common Name (eg, YOUR name)
commonName_default = Root B
commonName_max = 64
emailAddress = Email Address
emailAddress_max = 64
# SET-ex3 = SET extension number 3
[ req_attributes ]
challengePassword = A challenge password
challengePassword_min = 4
challengePassword_max = 20
unstructuredName = An optional company name
[ usr_cert ]
# These extensions are added when 'ca' signs a request.
# This goes against PKIX guidelines but some CAs do it and some software
# requires this to avoid interpreting an end user certificate as a CA.
basicConstraints=CA:FALSE
# Here are some examples of the usage of nsCertType. If it is omitted
# the certificate can be used for anything *except* object signing.
# This is OK for an SSL server.
# nsCertType = server
# For an object signing certificate this would be used.
# nsCertType = objsign
# For normal client use this is typical
# nsCertType = client, email
# and for everything including object signing:
# nsCertType = client, email, objsign
# This is typical in keyUsage for a client certificate.
# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
# This will be displayed in Netscape's comment listbox.
nsComment = "OpenSSL Generated Certificate"
# PKIX recommendations harmless if included in all certificates.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
# This stuff is for subjectAltName and issuerAltname.
# Import the email address.
# subjectAltName=email:copy
# An alternative to produce certificates that aren't
# deprecated according to PKIX.
# subjectAltName=email:move
# Copy subject details
# issuerAltName=issuer:copy
#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
#nsBaseUrl
#nsRevocationUrl
#nsRenewalUrl
#nsCaPolicyUrl
#nsSslServerName
[ v3_req ]
# Extensions to add to a certificate request
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
[ v3_ca ]
# Extensions for a typical CA
# PKIX recommendation.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer:always
# This is what PKIX recommends but some broken software chokes on critical
# extensions.
#basicConstraints = critical,CA:true
# So we do this instead.
basicConstraints = CA:true
# Key usage: this is typical for a CA certificate. However since it will
# prevent it being used as an test self-signed certificate it is best
# left out by default.
# keyUsage = cRLSign, keyCertSign
# Some might want this also
# nsCertType = sslCA, emailCA
# Include email address in subject alt name: another PKIX recommendation
# subjectAltName=email:copy
# Copy issuer details
# issuerAltName=issuer:copy
# DER hex encoding of an extension: beware experts only!
# obj=DER:02:03
# Where 'obj' is a standard or added object
# You can even override a supported extension:
# basicConstraints= critical, DER:30:03:01:01:FF
[ crl_ext ]
# CRL extensions.
# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
# issuerAltName=issuer:copy
authorityKeyIdentifier=keyid:always,issuer:always
[ proxy_cert_ext ]
# These extensions should be added when creating a proxy certificate
# This goes against PKIX guidelines but some CAs do it and some software
# requires this to avoid interpreting an end user certificate as a CA.
basicConstraints=CA:FALSE
# Here are some examples of the usage of nsCertType. If it is omitted
# the certificate can be used for anything *except* object signing.
# This is OK for an SSL server.
# nsCertType = server
# For an object signing certificate this would be used.
# nsCertType = objsign
# For normal client use this is typical
# nsCertType = client, email
# and for everything including object signing:
# nsCertType = client, email, objsign
# This is typical in keyUsage for a client certificate.
# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
# This will be displayed in Netscape's comment listbox.
nsComment = "OpenSSL Generated Certificate"
# PKIX recommendations harmless if included in all certificates.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer:always
# This stuff is for subjectAltName and issuerAltname.
# Import the email address.
# subjectAltName=email:copy
# An alternative to produce certificates that aren't
# deprecated according to PKIX.
# subjectAltName=email:move
# Copy subject details
# issuerAltName=issuer:copy
#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
#nsBaseUrl
#nsRevocationUrl
#nsRenewalUrl
#nsCaPolicyUrl
#nsSslServerName
# This really needs to be in place for it to be a proxy certificate.
proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo

7
thirdparty/copas/tests/certs/rootB.sh vendored Executable file
View file

@ -0,0 +1,7 @@
#!/bin/sh
openssl req -newkey rsa:1024 -sha1 -keyout rootBkey.pem -out rootBreq.pem -nodes -config ./rootB.cnf -days 365 -batch
openssl x509 -req -in rootBreq.pem -sha1 -extfile ./rootB.cnf -extensions v3_ca -signkey rootBkey.pem -out rootB.pem -days 365
openssl x509 -subject -issuer -noout -in rootB.pem

View file

@ -0,0 +1,9 @@
rem #!/bin/sh
openssl req -newkey rsa:1024 -keyout serverAkey.pem -out serverAreq.pem -config ./serverA.cnf -nodes -days 365 -batch
openssl x509 -req -in serverAreq.pem -sha1 -extfile ./serverA.cnf -extensions usr_cert -CA rootA.pem -CAkey rootAkey.pem -CAcreateserial -out serverAcert.pem -days 365
copy serverAcert.pem + rootA.pem serverA.pem
openssl x509 -subject -issuer -noout -in serverA.pem

316
thirdparty/copas/tests/certs/serverA.cnf vendored Normal file
View file

@ -0,0 +1,316 @@
#
# OpenSSL example configuration file.
# This is mostly being used for generation of certificate requests.
#
# This definition stops the following lines choking if HOME isn't
# defined.
HOME = .
RANDFILE = $ENV::HOME/.rnd
# Extra OBJECT IDENTIFIER info:
#oid_file = $ENV::HOME/.oid
oid_section = new_oids
# To use this configuration file with the "-extfile" option of the
# "openssl x509" utility, name here the section containing the
# X.509v3 extensions to use:
# extensions =
# (Alternatively, use a configuration file that has only
# X.509v3 extensions in its main [= default] section.)
[ new_oids ]
# We can add new OIDs in here for use by 'ca' and 'req'.
# Add a simple OID like this:
# testoid1=1.2.3.4
# Or use config file substitution like this:
# testoid2=${testoid1}.5.6
####################################################################
[ ca ]
default_ca = CA_default # The default ca section
####################################################################
[ CA_default ]
dir = ./demoCA # Where everything is kept
certs = $dir/certs # Where the issued certs are kept
crl_dir = $dir/crl # Where the issued crl are kept
database = $dir/index.txt # database index file.
#unique_subject = no # Set to 'no' to allow creation of
# several ctificates with same subject.
new_certs_dir = $dir/newcerts # default place for new certs.
certificate = $dir/cacert.pem # The CA certificate
serial = $dir/serial # The current serial number
crlnumber = $dir/crlnumber # the current crl number
# must be commented out to leave a V1 CRL
crl = $dir/crl.pem # The current CRL
private_key = $dir/private/cakey.pem # The private key
RANDFILE = $dir/private/.rand # private random number file
x509_extensions = usr_cert # The extensions to add to the cert
# Comment out the following two lines for the "traditional"
# (and highly broken) format.
name_opt = ca_default # Subject Name options
cert_opt = ca_default # Certificate field options
# Extension copying option: use with caution.
# copy_extensions = copy
# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
# so this is commented out by default to leave a V1 CRL.
# crlnumber must also be commented out to leave a V1 CRL.
# crl_extensions = crl_ext
default_days = 365 # how long to certify for
default_crl_days= 30 # how long before next CRL
default_md = sha1 # which md to use.
preserve = no # keep passed DN ordering
# A few difference way of specifying how similar the request should look
# For type CA, the listed attributes must be the same, and the optional
# and supplied fields are just that :-)
policy = policy_match
# For the CA policy
[ policy_match ]
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
# For the 'anything' policy
# At this point in time, you must list all acceptable 'object'
# types.
[ policy_anything ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
####################################################################
[ req ]
default_bits = 1024
default_keyfile = privkey.pem
distinguished_name = req_distinguished_name
attributes = req_attributes
x509_extensions = v3_ca # The extensions to add to the self signed cert
# Passwords for private keys if not present they will be prompted for
# input_password = secret
# output_password = secret
# This sets a mask for permitted string types. There are several options.
# default: PrintableString, T61String, BMPString.
# pkix : PrintableString, BMPString.
# utf8only: only UTF8Strings.
# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
# MASK:XXXX a literal mask value.
# WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings
# so use this option with caution!
string_mask = nombstr
# req_extensions = v3_req # The extensions to add to a certificate request
[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = BR
countryName_min = 2
countryName_max = 2
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = Some-State
stateOrProvinceName_default = Espirito Santo
localityName = Locality Name (eg, city)
localityName_default = Santo Antonio do Canaa
0.organizationName = Organization Name (eg, company)
0.organizationName_default = Sao Tonico Ltda
# we can do this but it is not needed normally :-)
#1.organizationName = Second Organization Name (eg, company)
#1.organizationName_default = World Wide Web Pty Ltd
organizationalUnitName = Organizational Unit Name (eg, section)
organizationalUnitName_default = Department of Computer Science
commonName = Common Name (eg, YOUR name)
commonName_default = Server A
commonName_max = 64
emailAddress = Email Address
emailAddress_max = 64
# SET-ex3 = SET extension number 3
[ req_attributes ]
challengePassword = A challenge password
challengePassword_min = 4
challengePassword_max = 20
unstructuredName = An optional company name
[ usr_cert ]
# These extensions are added when 'ca' signs a request.
# This goes against PKIX guidelines but some CAs do it and some software
# requires this to avoid interpreting an end user certificate as a CA.
basicConstraints=CA:FALSE
# Here are some examples of the usage of nsCertType. If it is omitted
# the certificate can be used for anything *except* object signing.
# This is OK for an SSL server.
nsCertType = server
# For an object signing certificate this would be used.
# nsCertType = objsign
# For normal client use this is typical
# nsCertType = client, email
# and for everything including object signing:
# nsCertType = client, email, objsign
# This is typical in keyUsage for a client certificate.
# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
# This will be displayed in Netscape's comment listbox.
nsComment = "OpenSSL Generated Certificate"
# PKIX recommendations harmless if included in all certificates.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
# This stuff is for subjectAltName and issuerAltname.
# Import the email address.
# subjectAltName=email:copy
# An alternative to produce certificates that aren't
# deprecated according to PKIX.
# subjectAltName=email:move
# Copy subject details
# issuerAltName=issuer:copy
#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
#nsBaseUrl
#nsRevocationUrl
#nsRenewalUrl
#nsCaPolicyUrl
#nsSslServerName
[ v3_req ]
# Extensions to add to a certificate request
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
[ v3_ca ]
# Extensions for a typical CA
# PKIX recommendation.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer:always
# This is what PKIX recommends but some broken software chokes on critical
# extensions.
#basicConstraints = critical,CA:true
# So we do this instead.
basicConstraints = CA:true
# Key usage: this is typical for a CA certificate. However since it will
# prevent it being used as an test self-signed certificate it is best
# left out by default.
# keyUsage = cRLSign, keyCertSign
# Some might want this also
# nsCertType = sslCA, emailCA
# Include email address in subject alt name: another PKIX recommendation
# subjectAltName=email:copy
# Copy issuer details
# issuerAltName=issuer:copy
# DER hex encoding of an extension: beware experts only!
# obj=DER:02:03
# Where 'obj' is a standard or added object
# You can even override a supported extension:
# basicConstraints= critical, DER:30:03:01:01:FF
[ crl_ext ]
# CRL extensions.
# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
# issuerAltName=issuer:copy
authorityKeyIdentifier=keyid:always,issuer:always
[ proxy_cert_ext ]
# These extensions should be added when creating a proxy certificate
# This goes against PKIX guidelines but some CAs do it and some software
# requires this to avoid interpreting an end user certificate as a CA.
basicConstraints=CA:FALSE
# Here are some examples of the usage of nsCertType. If it is omitted
# the certificate can be used for anything *except* object signing.
# This is OK for an SSL server.
# nsCertType = server
# For an object signing certificate this would be used.
# nsCertType = objsign
# For normal client use this is typical
# nsCertType = client, email
# and for everything including object signing:
# nsCertType = client, email, objsign
# This is typical in keyUsage for a client certificate.
# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
# This will be displayed in Netscape's comment listbox.
nsComment = "OpenSSL Generated Certificate"
# PKIX recommendations harmless if included in all certificates.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer:always
# This stuff is for subjectAltName and issuerAltname.
# Import the email address.
# subjectAltName=email:copy
# An alternative to produce certificates that aren't
# deprecated according to PKIX.
# subjectAltName=email:move
# Copy subject details
# issuerAltName=issuer:copy
#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
#nsBaseUrl
#nsRevocationUrl
#nsRenewalUrl
#nsCaPolicyUrl
#nsSslServerName
# This really needs to be in place for it to be a proxy certificate.
proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo

12
thirdparty/copas/tests/certs/serverA.sh vendored Executable file
View file

@ -0,0 +1,12 @@
#!/bin/sh
openssl req -newkey rsa:1024 -keyout serverAkey.pem -out serverAreq.pem \
-config ./serverA.cnf -nodes -days 365 -batch
openssl x509 -req -in serverAreq.pem -sha1 -extfile ./serverA.cnf \
-extensions usr_cert -CA rootA.pem -CAkey rootAkey.pem -CAcreateserial \
-out serverAcert.pem -days 365
cat serverAcert.pem rootA.pem > serverA.pem
openssl x509 -subject -issuer -noout -in serverA.pem

View file

@ -0,0 +1,9 @@
rem #!/bin/sh
openssl req -newkey rsa:1024 -keyout serverBkey.pem -out serverBreq.pem -config ./serverB.cnf -nodes -days 365 -batch
openssl x509 -req -in serverBreq.pem -sha1 -extfile ./serverB.cnf -extensions usr_cert -CA rootB.pem -CAkey rootBkey.pem -CAcreateserial -out serverBcert.pem -days 365
copy serverBcert.pem + rootB.pem serverB.pem
openssl x509 -subject -issuer -noout -in serverB.pem

316
thirdparty/copas/tests/certs/serverB.cnf vendored Normal file
View file

@ -0,0 +1,316 @@
#
# OpenSSL example configuration file.
# This is mostly being used for generation of certificate requests.
#
# This definition stops the following lines choking if HOME isn't
# defined.
HOME = .
RANDFILE = $ENV::HOME/.rnd
# Extra OBJECT IDENTIFIER info:
#oid_file = $ENV::HOME/.oid
oid_section = new_oids
# To use this configuration file with the "-extfile" option of the
# "openssl x509" utility, name here the section containing the
# X.509v3 extensions to use:
# extensions =
# (Alternatively, use a configuration file that has only
# X.509v3 extensions in its main [= default] section.)
[ new_oids ]
# We can add new OIDs in here for use by 'ca' and 'req'.
# Add a simple OID like this:
# testoid1=1.2.3.4
# Or use config file substitution like this:
# testoid2=${testoid1}.5.6
####################################################################
[ ca ]
default_ca = CA_default # The default ca section
####################################################################
[ CA_default ]
dir = ./demoCA # Where everything is kept
certs = $dir/certs # Where the issued certs are kept
crl_dir = $dir/crl # Where the issued crl are kept
database = $dir/index.txt # database index file.
#unique_subject = no # Set to 'no' to allow creation of
# several ctificates with same subject.
new_certs_dir = $dir/newcerts # default place for new certs.
certificate = $dir/cacert.pem # The CA certificate
serial = $dir/serial # The current serial number
crlnumber = $dir/crlnumber # the current crl number
# must be commented out to leave a V1 CRL
crl = $dir/crl.pem # The current CRL
private_key = $dir/private/cakey.pem # The private key
RANDFILE = $dir/private/.rand # private random number file
x509_extensions = usr_cert # The extensions to add to the cert
# Comment out the following two lines for the "traditional"
# (and highly broken) format.
name_opt = ca_default # Subject Name options
cert_opt = ca_default # Certificate field options
# Extension copying option: use with caution.
# copy_extensions = copy
# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
# so this is commented out by default to leave a V1 CRL.
# crlnumber must also be commented out to leave a V1 CRL.
# crl_extensions = crl_ext
default_days = 365 # how long to certify for
default_crl_days= 30 # how long before next CRL
default_md = sha1 # which md to use.
preserve = no # keep passed DN ordering
# A few difference way of specifying how similar the request should look
# For type CA, the listed attributes must be the same, and the optional
# and supplied fields are just that :-)
policy = policy_match
# For the CA policy
[ policy_match ]
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
# For the 'anything' policy
# At this point in time, you must list all acceptable 'object'
# types.
[ policy_anything ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
####################################################################
[ req ]
default_bits = 1024
default_keyfile = privkey.pem
distinguished_name = req_distinguished_name
attributes = req_attributes
x509_extensions = v3_ca # The extensions to add to the self signed cert
# Passwords for private keys if not present they will be prompted for
# input_password = secret
# output_password = secret
# This sets a mask for permitted string types. There are several options.
# default: PrintableString, T61String, BMPString.
# pkix : PrintableString, BMPString.
# utf8only: only UTF8Strings.
# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
# MASK:XXXX a literal mask value.
# WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings
# so use this option with caution!
string_mask = nombstr
# req_extensions = v3_req # The extensions to add to a certificate request
[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = BR
countryName_min = 2
countryName_max = 2
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = Some-State
stateOrProvinceName_default = Espirito Santo
localityName = Locality Name (eg, city)
localityName_default = Santo Antonio do Canaa
0.organizationName = Organization Name (eg, company)
0.organizationName_default = Sao Tonico Ltda
# we can do this but it is not needed normally :-)
#1.organizationName = Second Organization Name (eg, company)
#1.organizationName_default = World Wide Web Pty Ltd
organizationalUnitName = Organizational Unit Name (eg, section)
organizationalUnitName_default = Department of Computer Science
commonName = Common Name (eg, YOUR name)
commonName_default = Server B
commonName_max = 64
emailAddress = Email Address
emailAddress_max = 64
# SET-ex3 = SET extension number 3
[ req_attributes ]
challengePassword = A challenge password
challengePassword_min = 4
challengePassword_max = 20
unstructuredName = An optional company name
[ usr_cert ]
# These extensions are added when 'ca' signs a request.
# This goes against PKIX guidelines but some CAs do it and some software
# requires this to avoid interpreting an end user certificate as a CA.
basicConstraints=CA:FALSE
# Here are some examples of the usage of nsCertType. If it is omitted
# the certificate can be used for anything *except* object signing.
# This is OK for an SSL server.
nsCertType = server
# For an object signing certificate this would be used.
# nsCertType = objsign
# For normal client use this is typical
# nsCertType = client, email
# and for everything including object signing:
# nsCertType = client, email, objsign
# This is typical in keyUsage for a client certificate.
# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
# This will be displayed in Netscape's comment listbox.
nsComment = "OpenSSL Generated Certificate"
# PKIX recommendations harmless if included in all certificates.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
# This stuff is for subjectAltName and issuerAltname.
# Import the email address.
# subjectAltName=email:copy
# An alternative to produce certificates that aren't
# deprecated according to PKIX.
# subjectAltName=email:move
# Copy subject details
# issuerAltName=issuer:copy
#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
#nsBaseUrl
#nsRevocationUrl
#nsRenewalUrl
#nsCaPolicyUrl
#nsSslServerName
[ v3_req ]
# Extensions to add to a certificate request
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
[ v3_ca ]
# Extensions for a typical CA
# PKIX recommendation.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer:always
# This is what PKIX recommends but some broken software chokes on critical
# extensions.
#basicConstraints = critical,CA:true
# So we do this instead.
basicConstraints = CA:true
# Key usage: this is typical for a CA certificate. However since it will
# prevent it being used as an test self-signed certificate it is best
# left out by default.
# keyUsage = cRLSign, keyCertSign
# Some might want this also
# nsCertType = sslCA, emailCA
# Include email address in subject alt name: another PKIX recommendation
# subjectAltName=email:copy
# Copy issuer details
# issuerAltName=issuer:copy
# DER hex encoding of an extension: beware experts only!
# obj=DER:02:03
# Where 'obj' is a standard or added object
# You can even override a supported extension:
# basicConstraints= critical, DER:30:03:01:01:FF
[ crl_ext ]
# CRL extensions.
# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
# issuerAltName=issuer:copy
authorityKeyIdentifier=keyid:always,issuer:always
[ proxy_cert_ext ]
# These extensions should be added when creating a proxy certificate
# This goes against PKIX guidelines but some CAs do it and some software
# requires this to avoid interpreting an end user certificate as a CA.
basicConstraints=CA:FALSE
# Here are some examples of the usage of nsCertType. If it is omitted
# the certificate can be used for anything *except* object signing.
# This is OK for an SSL server.
# nsCertType = server
# For an object signing certificate this would be used.
# nsCertType = objsign
# For normal client use this is typical
# nsCertType = client, email
# and for everything including object signing:
# nsCertType = client, email, objsign
# This is typical in keyUsage for a client certificate.
# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
# This will be displayed in Netscape's comment listbox.
nsComment = "OpenSSL Generated Certificate"
# PKIX recommendations harmless if included in all certificates.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer:always
# This stuff is for subjectAltName and issuerAltname.
# Import the email address.
# subjectAltName=email:copy
# An alternative to produce certificates that aren't
# deprecated according to PKIX.
# subjectAltName=email:move
# Copy subject details
# issuerAltName=issuer:copy
#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
#nsBaseUrl
#nsRevocationUrl
#nsRenewalUrl
#nsCaPolicyUrl
#nsSslServerName
# This really needs to be in place for it to be a proxy certificate.
proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo

12
thirdparty/copas/tests/certs/serverB.sh vendored Executable file
View file

@ -0,0 +1,12 @@
#!/bin/sh
openssl req -newkey rsa:1024 -keyout serverBkey.pem -out serverBreq.pem \
-config ./serverB.cnf -nodes -days 365 -batch
openssl x509 -req -in serverBreq.pem -sha1 -extfile ./serverB.cnf \
-extensions usr_cert -CA rootB.pem -CAkey rootBkey.pem -CAcreateserial \
-out serverBcert.pem -days 365
cat serverBcert.pem rootB.pem > serverB.pem
openssl x509 -subject -issuer -noout -in serverB.pem

75
thirdparty/copas/tests/close.lua vendored Normal file
View file

@ -0,0 +1,75 @@
-- when a socket is being closed, while another coroutine
-- is reading on it, the `select` call does not return an event on that
-- socket. Hence the loop keeps running until the watch-dog kicks-in, reads
-- on the socket, and that returns the final "closed" error to the reading
-- coroutine.
--
-- when a socket is closed, any read/write operation should return immediately
-- with a "closed" error.
local copas = require "copas"
local socket = require "socket"
copas.loop(function()
local client_socket
local close_time
local send_end_time
local receive_end_time
print "------------- starting close test ---------------"
local function check_exit()
if receive_end_time and send_end_time then
-- both set, so we're done
print "success!"
os.exit(0)
end
end
do -- set up a server that accepts but doesn't read or write anything
local server = socket.bind("localhost", 20000)
copas.addserver(server, copas.handler(function(conn_skt)
-- client connected, we're not doing anything, let the client
-- wait in the read/write queues
copas.pause(2)
-- now we're closing the connecting_socket
close_time = socket.gettime()
print("closing client socket now, client receive and send operation should immediately error out now")
client_socket:close()
copas.pause(10)
conn_skt:close()
copas.removeserver(server)
print "timeout, test failed"
os.exit(1)
end))
end
do -- create a client that connect to the server
client_socket = copas.wrap(socket.connect("localhost", 20000))
copas.addthread(function()
local data, err = client_socket:receive(1)
print("receive result: ", tostring(data), tostring(err))
receive_end_time = socket.gettime()
print("receive took: ", receive_end_time - close_time)
check_exit()
end)
copas.addthread(function()
local ok, err = true, nil
while ok do -- loop to fill any buffers, until we get stuck
ok, err = client_socket:send(("hello world"):rep(100))
end
print("send result: ", tostring(ok), tostring(err))
send_end_time = socket.gettime()
print("send took: ", send_end_time - close_time)
check_exit()
end)
end
end)

36
thirdparty/copas/tests/connecttwice.lua vendored Normal file
View file

@ -0,0 +1,36 @@
-- test reconnecting a socket, should return an error "already connected"
-- test based on Windows behaviour, see comments in `copas.connect()` function
local copas = require("copas")
local socket = require("socket")
local skt = copas.wrap(socket.tcp())
local done = false
copas.addthread(function()
print("First try... (should succeed)")
local ok, err = skt:connect("google.com", 80)
if ok then
print("Success")
else
print("Failed: "..err)
os.exit(1)
end
print("\nSecond try... (should error as already connected)")
ok, err = skt:connect("thijsschreijer.nl", 80)
if ok then
print("Unexpected success")
os.exit(1)
else
print("Failed: "..err)
end
done = true
end)
copas.loop()
if not done then
print("Loop completed with test not finished")
os.exit(1)
end

160
thirdparty/copas/tests/errhandlers.lua vendored Normal file
View file

@ -0,0 +1,160 @@
-- Tests Copas socket timeouts
--
-- Run the test file, it should exit successfully without hanging.
-- make sure we are pointing to the local copas first
package.path = string.format("../src/?.lua;%s", package.path)
--local platform = "unix"
--if package.config:sub(1,1) == "\\" then
-- platform = "windows"
--elseif io.popen("uname", "r"):read("*a"):find("Darwin") then
-- platform = "mac"
--end
--print("Testing platform: " .. platform)
local copas = require("copas")
local tests = {}
if _VERSION ~= "Lua 5.1" then
-- do not run these for Lua 5.1 since it has a different stacktrace
tests.default_properly_formats_coro_errors = function()
local old_print = print
local msg
print = function(errmsg) --luacheck: ignore
msg = errmsg
--old_print(msg)
end
copas.loop(function()
local f = function()
error("hi there!")
end
f()
end)
print = old_print --luacheck: ignore
assert(msg:find("errhandlers%.lua:%d-: hi there! %(coroutine: copas_initializer, socket: nil%)"), "got:\n"..msg)
assert(msg:find("stack traceback:.+errhandlers%.lua"), "got:\n"..msg)
end
tests.default_properly_formats_timerwheel_errors = function()
local old_print = print
local msg
print = function(errmsg) --luacheck: ignore
msg = errmsg
--old_print(msg)
end
copas.loop(function()
copas.timeout(0.01, function(co)
local f = function()
error("hi there!")
end
f()
end)
copas.pause(1)
end)
print = old_print --luacheck: ignore
assert(msg:find("errhandlers%.lua:%d-: hi there! %(coroutine: copas_core_timer, socket: nil%)"), "got:\n"..msg)
assert(msg:find("stack traceback:.+errhandlers%.lua"), "got:\n"..msg)
end
end
tests.yielding_from_user_code_fails = function()
local old_print = print
local msg
print = function(errmsg) --luacheck: ignore
msg = errmsg
--old_print(msg)
end
copas.loop(function()
copas.pause(1)
coroutine.yield() -- directly yield to Copas
end)
print = old_print --luacheck: ignore
assert(msg:find("coroutine.yield was called without a resume first, user-code cannot yield to Copas", 1, true), "got:\n"..msg)
end
tests.handler_gets_called_if_set = function()
local call_count = 0
copas.loop(function()
copas.setErrorHandler(function() call_count = call_count + 1 end)
error("end of the world!")
end)
assert(call_count == 1, "expected callcount 1, got: " .. tostring(call_count))
end
tests.default_handler_gets_called_if_set = function()
local call_count = 0
copas.setErrorHandler(function() call_count = call_count + 10 end, true)
copas.loop(function()
error("end of the world!")
end)
assert(call_count == 10, "expected callcount 10, got: " .. tostring(call_count))
end
tests.default_handler_doesnt_get_called_if_overridden = function()
local call_count = 0
copas.setErrorHandler(function() call_count = call_count + 10 end, true)
copas.loop(function()
copas.setErrorHandler(function() call_count = call_count + 1 end)
error("end of the world!")
end)
assert(call_count == 1, "expected callcount 1, got: " .. tostring(call_count))
end
tests.timerwheel_callbacks_call_the_default_error_handler = function()
local call_count = 0
copas.setErrorHandler(function() call_count = call_count - 10 end, true)
copas.loop(function()
copas.timeout(0.01, function(co) error("hi there!") end)
copas.pause(1)
end)
assert(call_count == -10, "expected callcount -10, got: " .. tostring(call_count))
end
-- test "framework"
for name, test in pairs(tests) do
-- reload copas, to clear default err handlers
package.loaded.copas = nil
copas = require "copas"
print("testing: "..tostring(name))
local status, err = pcall(test)
if not status then
error(err)
end
end
print("[✓] all tests completed successuly")

Some files were not shown because too many files have changed in this diff Show more