├── Makefile ├── Makefile.win ├── NOTES.md ├── README.md ├── TODO.md ├── benchmarks ├── file_io.lua ├── webservers.cmd └── webservers.sh ├── etc ├── apreq_standalone.c ├── buildbot.lua ├── docs.lua ├── errors.lua ├── lua-apr-scm-1.rockspec ├── make.lua ├── signals.lua ├── template.rockspec ├── win32-bootstrap │ ├── clean.cmd │ ├── libapreq2.mak │ └── make.cmd └── wrap.lua ├── examples ├── async-webserver.lua ├── download.lua ├── threaded-webserver.lua └── webserver.lua ├── make.cmd ├── src ├── apr.lua ├── base64.c ├── buffer.c ├── crypt.c ├── date.c ├── dbd.c ├── dbm.c ├── env.c ├── errno.c ├── filepath.c ├── fnmatch.c ├── getopt.c ├── http.c ├── io_dir.c ├── io_file.c ├── io_net.c ├── io_pipe.c ├── ldap.c ├── lua_apr.c ├── lua_apr.h ├── memcache.c ├── memory_pool.c ├── object.c ├── permissions.c ├── pollset.c ├── proc.c ├── serialize.c ├── serialize.lua ├── shm.c ├── signal.c ├── stat.c ├── str.c ├── thread.c ├── thread_queue.c ├── time.c ├── uri.c ├── user.c ├── uuid.c ├── xlate.c └── xml.c └── test ├── base64.lua ├── crypt.lua ├── date.lua ├── dbd-child.lua ├── dbd.lua ├── dbm.lua ├── env-child.lua ├── env.lua ├── filepath.lua ├── fnmatch.lua ├── getopt.lua ├── helpers.lua ├── http.lua ├── init.lua ├── io_buffer.lua ├── io_dir.lua ├── io_file-bidi_pipes.lua ├── io_file-named_pipe.lua ├── io_file.lua ├── io_net-server.lua ├── io_net.lua ├── ldap-child.lua ├── ldap.lua ├── memcache.lua ├── misc.lua ├── pollset.lua ├── proc.lua ├── serialize.lua ├── shm-child.lua ├── shm.lua ├── signal.lua ├── str.lua ├── thread-child.lua ├── thread.lua ├── thread_queue-child.lua ├── thread_queue.lua ├── time.lua ├── uri.lua ├── user.lua ├── uuid.lua ├── xlate.lua └── xml.lua /Makefile.win: -------------------------------------------------------------------------------- 1 | # This is the Windows makefile for the Lua/APR binding. 2 | # 3 | # Author: Peter Odding 4 | # Last Change: November 20, 2011 5 | # Homepage: http://peterodding.com/code/lua/apr/ 6 | # License: MIT 7 | # 8 | # This makefile has been tested on Windows XP with NMAKE from the free 9 | # Microsoft Visual C++ 2010 Express tool chain. You may want to change the 10 | # following settings: 11 | 12 | # The directories where "lua.h" and "lua51.lib" can be found (these defaults 13 | # are based on the directory structure used by Lua for Windows v5.1.4-40). 14 | LUA_DIR = C:\Program Files\Lua\5.1 15 | LUA_INCDIR = $(LUA_DIR)\include 16 | LUA_LIBDIR = $(LUA_DIR)\clibs 17 | LUA_LINKDIR = $(LUA_DIR)\lib 18 | LUA_SHAREDIR = $(LUA_DIR)\lua 19 | 20 | # The directories where "apr.h" and "libapr-1.lib" can be found. 21 | APR_INCDIR = C:\lua-apr\apr\include 22 | APR_LIBDIR = C:\lua-apr\apr\release 23 | 24 | # The directories where "apu.h" and "libaprutil-1.lib" can be found. 25 | APU_INCDIR = C:\lua-apr\apr-util\include 26 | APU_LIBDIR = C:\lua-apr\apr-util\release 27 | 28 | # The directory where "libapriconv-1.lib" can be found. 29 | API_LIBDIR = C:\lua-apr\apr-iconv\release 30 | 31 | # The directories where "apreq.h" and "libapreq-2.lib" can be found. 32 | APREQ_INCDIR = C:\lua-apr\libapreq2\include 33 | APREQ_LIBDIR = C:\lua-apr\libapreq2\win32\libs 34 | 35 | # You shouldn't need to change anything below here. 36 | 37 | BINARY_MODULE = core.dll 38 | APREQ_BINARY = apreq.dll 39 | 40 | # Compiler and linker flags composed from the above settings. 41 | CFLAGS = "/I$(LUA_INCDIR)" "/I$(APR_INCDIR)" "/I$(APU_INCDIR)" /D"_CRT_SECURE_NO_DEPRECATE" 42 | LFLAGS = "/LIBPATH:$(LUA_LINKDIR)" lua51.lib "/LIBPATH:$(APR_LIBDIR)" libapr-1.lib "/LIBPATH:$(APU_LIBDIR)" libaprutil-1.lib Wldap32.Lib 43 | 44 | # Names of compiled object files (the individual lines enable automatic 45 | # rebasing between git feature branches and the master branch). 46 | OBJECTS = src\base64.obj \ 47 | src\buffer.obj \ 48 | src\crypt.obj \ 49 | src\date.obj \ 50 | src\dbd.obj \ 51 | src\dbm.obj \ 52 | src\env.obj \ 53 | src\errno.obj \ 54 | src\filepath.obj \ 55 | src\fnmatch.obj \ 56 | src\getopt.obj \ 57 | src\http.obj \ 58 | src\io_dir.obj \ 59 | src\io_file.obj \ 60 | src\io_net.obj \ 61 | src\io_pipe.obj \ 62 | src\ldap.obj \ 63 | src\lua_apr.obj \ 64 | src\memcache.obj \ 65 | src\memory_pool.obj \ 66 | src\object.obj \ 67 | src\permissions.obj \ 68 | src\pollset.obj \ 69 | src\proc.obj \ 70 | src\serialize.obj \ 71 | src\shm.obj \ 72 | src\signal.obj \ 73 | src\stat.obj \ 74 | src\str.obj \ 75 | src\thread.obj \ 76 | src\thread_queue.obj \ 77 | src\time.obj \ 78 | src\uri.obj \ 79 | src\user.obj \ 80 | src\uuid.obj \ 81 | src\xlate.obj \ 82 | src\xml.obj 83 | 84 | # Create debug builds by default but enable release builds 85 | # using the command line "NMAKE /f Makefile.win RELEASE=1". 86 | !IFNDEF RELEASE 87 | CFLAGS = $(CFLAGS) /Zi /Fd"core.pdb" /DDEBUG 88 | LFLAGS = $(LFLAGS) /debug 89 | !ENDIF 90 | 91 | # Experimental support for HTTP request parsing using libapreq2. 92 | CFLAGS = $(CFLAGS) "/I$(APREQ_INCDIR)" /DLUA_APR_HAVE_APREQ=1 93 | LFLAGS = $(LFLAGS) "/LIBPATH:$(APREQ_LIBDIR)" libapreq2.lib 94 | 95 | # Build the binary module. 96 | $(BINARY_MODULE): $(OBJECTS) Makefile 97 | @LINK /nologo /dll /out:$@ $(OBJECTS) $(LFLAGS) 98 | @IF EXIST $@.manifest MT -nologo -manifest $@.manifest -outputresource:$@;2 99 | 100 | # Build the standalone libapreq2 binding. 101 | $(APREQ_BINARY): etc\apreq_standalone.c 102 | CD etc && CL /W3 /nologo /MD /D"WIN32" /D"LUA_BUILD_AS_DLL" $(CFLAGS) /TC /c apreq_standalone.c 103 | LINK /nologo /dll /out:$@ etc\apreq_standalone.obj $(LFLAGS) 104 | IF EXIST $@.manifest MT -nologo -manifest $@.manifest -outputresource:$@;2 105 | 106 | # Compile individual source code files to object files. 107 | $(OBJECTS): Makefile 108 | .c.obj: 109 | @CL /W3 /nologo /MD /D"WIN32" /D"LUA_BUILD_AS_DLL" $(CFLAGS) /TC /c $< /Fo$@ 110 | 111 | # Always try to regenerate the error handling module. 112 | src\errno.c: etc\errors.lua 113 | @LUA etc\errors.lua > src\errno.c.new && MOVE src\errno.c.new src\errno.c || EXIT /B 0 114 | 115 | # Install the Lua/APR binding and external dependencies. 116 | install: $(BINARY_MODULE) 117 | COPY src\apr.lua "$(LUA_SHAREDIR)" 118 | IF NOT EXIST "$(LUA_SHAREDIR)\apr" MD "$(LUA_SHAREDIR)\apr" 119 | IF NOT EXIST "$(LUA_SHAREDIR)\apr\test" MD "$(LUA_SHAREDIR)\apr\test" 120 | COPY test\*.lua "$(LUA_SHAREDIR)\apr\test" 121 | IF NOT EXIST "$(LUA_LIBDIR)\apr" MD "$(LUA_LIBDIR)\apr" 122 | COPY $(BINARY_MODULE) "$(LUA_LIBDIR)\apr" 123 | COPY "$(APR_LIBDIR)\libapr-1.dll" "$(LUA_DIR)" 124 | COPY "$(APU_LIBDIR)\libaprutil-1.dll" "$(LUA_DIR)" 125 | COPY "$(API_LIBDIR)\libapriconv-1.dll" "$(LUA_DIR)" 126 | IF EXIST "$(APREQ_LIBDIR)\libapreq2.dll" COPY "$(APREQ_LIBDIR)\libapreq2.dll" "$(LUA_DIR)" 127 | 128 | # Remove previously installed files. 129 | uninstall: 130 | DEL "$(LUA_SHAREDIR)\apr.lua" 131 | DEL "$(LUA_SHAREDIR)\apr\test\*.lua" 132 | RD "$(LUA_SHAREDIR)\apr\test" 133 | RD "$(LUA_SHAREDIR)\apr" 134 | DEL "$(LUA_LIBDIR)\apr\$(BINARY_MODULE)" 135 | RD "$(LUA_LIBDIR)\apr" 136 | DEL "$(LUA_DIR)\libapr-1.dll" 137 | DEL "$(LUA_DIR)\libaprutil-1.dll" 138 | DEL "$(LUA_DIR)\libapriconv-1.dll" 139 | IF EXIST "$(LUA_DIR)\libapreq2.dll" DEL "$(LUA_DIR)\libapreq2.dll" 140 | 141 | # Run the test suite. 142 | test: install 143 | LUA -e "require 'apr.test' ()" 144 | 145 | # Debug the test suite using NTSD. 146 | debug: 147 | NTSD -g LUA -e "require 'apr.test' ()" 148 | 149 | # Clean generated files from working directory. 150 | clean: 151 | DEL $(OBJECTS) $(BINARY_MODULE) core.lib core.exp core.pdb core.ilk core.dll.manifest 2>NUL 152 | DEL $(APREQ_BINARY) apreq.lib apreq.exp apreq.pdb apreq.ilk 2>NUL 153 | 154 | .PHONY: install uninstall test debug clean 155 | 156 | # vim: ts=4 sw=4 157 | -------------------------------------------------------------------------------- /NOTES.md: -------------------------------------------------------------------------------- 1 | # Notes about the Lua/APR binding 2 | 3 | ## APR documentation 4 | 5 | The [online API documentation for APR] [api_docs] has been my most useful resource while working on the Lua/APR binding but it leaves something to be desired when you're looking for a high level overview. The following online resources help in this regard: 6 | 7 | * [God's gift to C] [gift_to_c] is an introductory article to the functionality of APR by [The Register] [register]. 8 | * The [libapr tutorial by Inoue Seiichiro] [tutorial] is a useful starting point for writing code. 9 | 10 | [api_docs]: http://apr.apache.org/docs/apr/trunk/modules.html 11 | [gift_to_c]: http://www.theregister.co.uk/2006/04/27/gift_to_c/ 12 | [register]: http://www.theregister.co.uk/ 13 | [tutorial]: http://dev.ariel-networks.com/apr/apr-tutorial/html/apr-tutorial.html 14 | 15 | ## Building APR on Windows 16 | 17 | Building APR on Windows can be a pain in the ass. It is meant to be done with Microsoft tools but fortunately these are freely available. Here are some notes I made in the process: 18 | 19 | 1. Install [Microsoft Visual C++ Express] [msvc]. You only need the command line tools, the GUI isn't needed. 20 | 21 | 2. Install the [Microsoft Platform SDK] [sdk]. The full SDK is over 1 GB but you only need the following: 22 | 23 | * Microsoft Windows Core SDK 24 | * Build environment (x86 32-bit) 25 | * Microsoft Web Workshop (IE) SDK 26 | * Build environment 27 | 28 | 3. Download the APR, APR-util, APR-iconv and libapreq2 archives (I used `apr-1.4.2-win32-src.zip`, `apr-util-1.3.9-win32-src.zip`, `apr-iconv-1.2.1-win32-src-r2.zip` and `libapreq2-2.13.tar.gz`) from [apr.apache.org] [apr_homepage] (you can [get libapreq2 here] [apreq_downloads]). Unpack all archives to the same directory and rename the subdirectories to `apr`, `apr-util` and `apr-iconv` (those three are build at the same time while libapreq2 is build separately). 29 | 30 | 4. *The instructions about [building APR on Windows] [compile_hints] don't work for me so this is where things get sketchy:* Open a Windows SDK command prompt and navigate to the `apr-util` directory. Inside this directory execute `nmake -f Makefile.win buildall`. This doesn't work for me out of the box because of what's probably a bug in the APR-util makefile; I needed to replace `apr_app` with `aprapp` on lines 176 and 177 of `Makefile.win`. After this change `nmake` still exits with errors but nevertheless seems to build `libapr-1.dll` and `libaprutil-1.dll`... 31 | 32 | 5. You also have to build APREQ, last I tried this was a mess on Windows, I collected some notes at the bottom of this page. 33 | 34 | [msvc]: http://www.microsoft.com/express/Downloads/#2010-Visual-CPP 35 | [sdk]: http://en.wikipedia.org/wiki/Microsoft_Windows_SDK#Obtaining_the_SDK 36 | [apr_homepage]: http://apr.apache.org/ 37 | [apreq_downloads]: http://www.apache.org/dist/httpd/libapreq/ 38 | [compile_hints]: http://apr.apache.org/compiling_win32.html 39 | 40 | ### Building the SQlite3 database driver on Windows 41 | 42 | The SQLite 3 driver is included in the [Windows binaries] [win32_binaries] but for the benefit of those who want to build the Apache Portable Runtime on Windows here are the steps involved: 43 | 44 | 1. Download the [precompiled SQLite 3 binaries For Windows] [sqlite_binaries] (273.98 KiB) and unpack the files somewhere. 45 | 46 | 2. Create `sqlite3.lib` from `sqlite3.def` (included in the precompiled binaries) using the command `lib /machine:i386 /def:sqlite3.def` and copy `sqlite3.lib` to `apr-util-1.3.9/LibR`. 47 | 48 | 3. Download the corresponding [source code distribution] [sqlite_sources] (1.20 MiB) and copy `sqlite3.h` to `apr-util-1.3.9/include`. 49 | 50 | 4. Build the driver in the Windows SDK command prompt using the command `nmake /f apr_dbd_sqlite3.mak`. 51 | 52 | 5. To install the driver you can copy `sqlite3.dll` and `apr_dbd_sqlite3-1.dll` to Lua's installation directory. 53 | 54 | [win32_binaries]: http://peterodding.com/code/lua/apr/downloads/lua-apr-0.20-win32.zip 55 | [sqlite_binaries]: http://www.sqlite.org/sqlite-dll-win32-x86-3070400.zip 56 | [sqlite_sources]: http://www.sqlite.org/sqlite-preprocessed-3070400.zip 57 | 58 | ### Building `libapreq2` on Windows 59 | 60 | I wasted a few hours getting `libapreq2` version 2.13 to build on Windows because of the following issues: 61 | 62 | * The included makefile `libapreq2.mak` is full of syntax errors. 63 | * The makefile unconditionally includes the Apache module and consequently doesn't link without a full Apache build. 64 | * The build environment requires a specific flavor of Perl which I haven't gotten to work. 65 | 66 | Eventually I decided to just rewrite the damned makefile and be done with it, enabling me to finally test the HTTP request parsing module on Windows (all tests passed the first time). I've included the [customized makefile] [apreq_makefile] in the Lua/APR git repository. 67 | 68 | [apreq_makefile]: https://github.com/xolox/lua-apr/blob/master/etc/win32-bootstrap/libapreq2.mak 69 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # To-do list for the Lua/APR binding 2 | 3 | ## New features 4 | 5 | * **Encrypted network communication**. It appears that APR itself doesn't support this but clearly it's possible because there are dozens of projects that use APR and support encrypted network communication (the [Apache HTTP server] [httpd], [ApacheBench] [ab], [Tomcat] [tomcat], etc.) 6 | * Make it possible to enable text mode for files, pipes and sockets on platforms where there is no distinction between text/binary mode (because `CR` + `LF` → `LF` translation can be useful on UNIX as well) 7 | 8 | [httpd]: http://en.wikipedia.org/wiki/Apache_HTTP_Server 9 | [ab]: http://en.wikipedia.org/wiki/ApacheBench 10 | [tomcat]: http://en.wikipedia.org/wiki/Apache_Tomcat 11 | 12 | ## Known problems 13 | 14 | * Find out why **`apr.xlate()` doesn't work on Windows** (I can't seem to get `apr_iconv` working on Windows) 15 | * Zhiguo Zhao mentioned that he's using `apr.xlate()` on Windows using the original `libiconv` instead of `apr_iconv` (see [issue #11] [issue_11]) 16 | * Investigate **escaping problem in `apr_proc_create()`** as found by the test for the `apr.namedpipe_create()` function (see `etc/tests.lua` around line 625) 17 | * Why is the DBD `LD_PRELOAD` trick needed?! [More information] [dbd_trick] 18 | 19 | [issue_11]: https://github.com/xolox/lua-apr/issues/11#issuecomment-2888555 20 | [dbd_trick]: https://answers.launchpad.net/ubuntu/+source/apr-util/+question/143914 21 | 22 | ## Anything else? 23 | 24 | * Propose the [libapreq2 binding] [apreq_binding] for inclusion as the official Lua language binding of [libapreq2] [libapreq2]? (first make the binding a lot more complete) 25 | * [Maybe][atexit] I shouldn't be using `atexit()` to call `apr_terminate()`? (BTW the whole linked blog post is interesting, as is the follow-up post) 26 | 27 | [apreq_binding]: https://github.com/xolox/lua-apr/blob/master/src/http.c 28 | [libapreq2]: http://httpd.apache.org/apreq/ 29 | [atexit]: http://davidz25.blogspot.com/2011/06/writing-c-library-part-1.html 30 | -------------------------------------------------------------------------------- /benchmarks/file_io.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | --[[ 4 | 5 | Benchmarks of the different formats supported by file:read() as implemented in 6 | the Lua standard library vs. the Lua/APR binding. Requires gnuplot to generate 7 | graphs in PNG format. See http://peterodding.com/code/lua/apr/benchmarks for 8 | examples of the generated graphs. 9 | 10 | --]] 11 | 12 | local apr = require 'apr' 13 | 14 | local function msg(...) 15 | io.stderr:write(string.format(...), '\n') 16 | end 17 | 18 | local formats = {{ 19 | format = '*a', 20 | graph = 'file-read-all.png', 21 | title = "Performance of file:read('*a')", 22 | generate = function(size) 23 | local random = io.open('/dev/urandom') 24 | local data = random:read(math.min(size, 1024 * 512)) 25 | random:close() 26 | if #data < size then 27 | data = data:rep(size / #data) 28 | end 29 | return data 30 | end, 31 | }, { 32 | format = '*l', 33 | graph = 'file-read-lines.png', 34 | title = "Performance of file:read('*l')", 35 | generate = function(size) 36 | -- Generate a line with random printable characters. 37 | local line = {} 38 | local char = string.char 39 | local random = math.random 40 | for i = 1, 512 do line[#line + 1] = char(random(0x20, 0x7E)) end 41 | line = table.concat(line) 42 | -- Generate a few lines of random lengths. 43 | local lines = {} 44 | local count = 0 45 | while #lines < 100 do 46 | local cline = line:sub(1, math.random(1, #line)) 47 | lines[#lines + 1] = cline 48 | size = size + #cline 49 | end 50 | lines = table.concat(lines, '\n') 51 | -- Now generate the requested size in lines. 52 | return lines:rep(size / #lines) 53 | end, 54 | }, { 55 | format = '*n', 56 | graph = 'file-read-numbers.png', 57 | title = "Performance of file:read('*n')", 58 | generate = function(size) 59 | local numbers = {} 60 | for i = 1, 100 do 61 | numbers[#numbers + 1] = tostring(math.random()) 62 | end 63 | numbers = table.concat(numbers, '\n') 64 | return numbers:rep(size / #numbers) 65 | end, 66 | }} 67 | 68 | local function benchformat(func, path, format, size, label) 69 | local best 70 | for i = 1, 2 do 71 | local start = apr.time_now() 72 | local handle = func(path) 73 | repeat 74 | local result = handle:read(format) 75 | until format == '*a' and result == '' or not result 76 | handle:close() 77 | local total = apr.time_now() - start 78 | msg('%17s: read %s with file:read(%s) at %s/s', 79 | label, apr.strfsize(size), format, apr.strfsize(size / total)) 80 | best = best and best < total and best or total 81 | end 82 | return { time = best, size = size / 1024 / 1024 } 83 | end 84 | 85 | local function writeresults(fname, data) 86 | local handle = io.open(fname, 'w') 87 | for _, sample in ipairs(data) do 88 | handle:write(sample.size, ' ', sample.time, '\n') 89 | end 90 | handle:close() 91 | end 92 | 93 | local datafile = os.tmpname() .. '.bin' 94 | local luafile = os.tmpname() .. '.dat' 95 | local aprfile = os.tmpname() .. '.dat' 96 | 97 | for _, format in ipairs(formats) do 98 | local luatimes = {{ time = 0, size= 0 }} 99 | local aprtimes = {{ time = 0, size= 0 }} 100 | local size = 1024 * 1024 101 | local max = 1024 * 1024 * 25 102 | local data = format.generate(max) 103 | while size <= max do 104 | local handle = io.open(datafile, 'w') 105 | handle:write(data:sub(1, size)) 106 | handle:close() 107 | table.insert(luatimes, benchformat(io.open, datafile, format.format, size, "Standard library")) 108 | table.insert(aprtimes, benchformat(apr.file_open, datafile, format.format, size, "Lua/APR")) 109 | size = size + 1024 * 1024 110 | end 111 | writeresults(luafile, luatimes) 112 | writeresults(aprfile, aprtimes) 113 | local gnuplot = io.popen('gnuplot', 'w') 114 | gnuplot:write(string.format([[ 115 | set title "%s" 116 | set xlabel "size in megabytes" 117 | set ylabel "time in seconds" 118 | set term png 119 | set output '%s' 120 | plot '%s' with lines lt 3 title "Lua standard library", \ 121 | '%s' with lines lt 4 title "Lua/APR binding" 122 | ]], format.title, format.graph, luafile, aprfile)) 123 | gnuplot:close() 124 | end 125 | 126 | os.remove(datafile) 127 | os.remove(luafile) 128 | os.remove(aprfile) 129 | -------------------------------------------------------------------------------- /benchmarks/webservers.cmd: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM This is a batch script to benchmark the example webservers included in the 4 | REM Lua/APR binding on Windows platforms. This script uses "ab.exe" (ApacheBench). 5 | 6 | SET SECONDS=5 7 | SET THREADS=4 8 | 9 | REM Benchmark the single threaded webserver. 10 | SET PORT=%RANDOM% 11 | START lua ../examples/webserver.lua %PORT% 12 | lua -e "require('apr').sleep(5)" 13 | ab.exe -q -t%SECONDS% http://localhost:%PORT%/ 14 | ECHO Finished test 1 15 | 16 | REM Benchmark the multi threaded webserver with several thread pool sizes. 17 | REM FOR /l %%i in (1, 1, %THREADS%) DO ECHO %%i 18 | FOR /l %%I in (1, 1, %THREADS%) DO CALL :THREADED_TEST %%I 19 | GOTO :EOF 20 | 21 | :THREADED_TEST 22 | ECHO Starting test %1 23 | SET PORT=%RANDOM% 24 | START lua ../examples/threaded-webserver.lua %1 %PORT% 25 | lua -e "require('apr').sleep(5)" 26 | ECHO ON 27 | ab.exe -q -t%SECONDS% -c%1 http://localhost:%PORT%/ 28 | @ECHO OFF 29 | ECHO. 30 | GOTO :EOF 31 | -------------------------------------------------------------------------------- /benchmarks/webservers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This is a Bash script to benchmark the example webservers included in the 4 | # Lua/APR binding on UNIX platforms. This script uses "ab" (ApacheBench). 5 | 6 | # Note that the last request sent by ApacheBench may be terminated prematurely 7 | # because of the "ab -t" option. In other words, don't worry about the sporadic 8 | # error message just before or after ApacheBench reports its results. 9 | 10 | SECONDS=5 # Number of seconds to run each benchmark. 11 | THREADS=4 # Number of threads to test. 12 | 13 | # Benchmark the single threaded webserver. 14 | PORT=$((($RANDOM % 30000) + 1000)) 15 | lua ../examples/webserver.lua $PORT & 16 | disown 17 | PID=$! 18 | sleep 5 19 | ab -q -t$SECONDS http://localhost:$PORT/ | grep 'Requests per second\|Transfer rate' 20 | kill $PID 21 | echo 22 | 23 | # Benchmark the multi threaded webserver with several thread pool sizes. 24 | for ((i=1; $i<=$THREADS; i+=1)); do 25 | PORT=$((($RANDOM % 30000) + 1000)) 26 | lua ../examples/threaded-webserver.lua $i $PORT & 27 | disown 28 | PID=$! 29 | sleep 5 30 | ab -q -t$SECONDS -c$i http://localhost:$PORT/ | grep 'Requests per second\|Transfer rate' 31 | kill $PID 32 | echo 33 | done 34 | -------------------------------------------------------------------------------- /etc/apreq_standalone.c: -------------------------------------------------------------------------------- 1 | /* Standalone libapreq2 binding based on the Lua/APR binding. 2 | * 3 | * Author: Peter Odding 4 | * Last Change: February 27, 2011 5 | * Homepage: http://peterodding.com/code/lua/apr/ 6 | * License: MIT 7 | * 8 | * This file transforms the HTTP request parsing module for the Lua/APR binding 9 | * into a standalone binding to libapreq2, making it a lighter dependency for 10 | * other web projects. 11 | */ 12 | 13 | #include "../src/lua_apr.h" 14 | 15 | /* Make memory_pool.c use separate memory pool for standalone APREQ module. */ 16 | #undef LUA_APR_POOL_MT 17 | #define LUA_APR_POOL_MT "APREQ memory pool type" 18 | #undef LUA_APR_POOL_KEY 19 | #define LUA_APR_POOL_KEY "APREQ memory pool instance" 20 | #define APREQ_STANDALONE 21 | 22 | /* Dependencies copied from Lua/APR. */ 23 | 24 | int status_to_message(lua_State *L, apr_status_t status) 25 | { 26 | char message[LUA_APR_MSGSIZE]; 27 | apr_strerror(status, message, count(message)); 28 | lua_pushstring(L, message); 29 | return 1; 30 | } 31 | 32 | int push_error_status(lua_State *L, apr_status_t status) 33 | { 34 | lua_pushnil(L); 35 | status_to_message(L, status); 36 | status_to_name(L, status); 37 | return 3; 38 | } 39 | 40 | #include "../src/errno.c" 41 | #include "../src/memory_pool.c" 42 | #include "../src/http.c" 43 | 44 | /* Module loader. */ 45 | 46 | /* Used to make sure that APR is only initialized once. */ 47 | static int apr_was_initialized = 0; 48 | 49 | LUA_APR_EXPORT int luaopen_apreq(lua_State *L) 50 | { 51 | apr_status_t status; 52 | 53 | /* The Lua/APR binding probably isn't loaded which makes the APREQ binding 54 | * responsible for correctly initializing the Apache Portable Runtime. */ 55 | if (!apr_was_initialized) { 56 | if ((status = apr_initialize()) != APR_SUCCESS) 57 | raise_error_status(L, status); 58 | if (atexit(apr_terminate) != 0) 59 | raise_error_message(L, "APREQ: Failed to register apr_terminate()"); 60 | apr_was_initialized = 1; 61 | } 62 | 63 | /* Create the memory pool for APREQ functions and install a __gc metamethod 64 | * to destroy the memory pool when the Lua state is closed. */ 65 | to_pool(L); 66 | 67 | /* Create and return the module table. */ 68 | lua_newtable(L); 69 | # define push_libfun(f) \ 70 | lua_pushcfunction(L, lua_apr_ ## f); \ 71 | lua_setfield(L, -2, #f) 72 | push_libfun(parse_headers); 73 | push_libfun(parse_multipart); 74 | push_libfun(parse_cookie_header); 75 | push_libfun(parse_query_string); 76 | return 1; 77 | } 78 | -------------------------------------------------------------------------------- /etc/buildbot.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | --[[ 4 | 5 | Multi platform build bot for the Lua/APR binding. 6 | 7 | Author: Peter Odding 8 | Last Change: November 13, 2011 9 | Homepage: http://peterodding.com/code/lua/apr/ 10 | License: MIT 11 | 12 | This script makes it easier for me to build and test the Lua/APR binding on 13 | multiple platforms simultaneously. In its simplest form this script builds the 14 | Lua/APR binding, copies the installation files to a temporary directory and 15 | runs the test suite inside this temporary directory. The script can also be 16 | started in server mode where it will wait for another build bot that commands 17 | it over TCP to run a build and report the results. 18 | 19 | I start this script in server mode in a Windows XP virtual machine, then more 20 | or less forget about the virtual machine and just run the build bot from my 21 | Ubuntu Linux host environment, where it reports the build and test results 22 | from both my Linux and Windows environments :-) 23 | 24 | The following issues influenced the design of this build bot (I came up with 25 | these after finding out the hard way every variation that didn't work out): 26 | 27 | * On Windows the build bot can't run "make install" because the build bot 28 | itself uses the Lua/APR binding and on Windows it is impossible to 29 | overwrite dynamic link libraries while they're in use :-\ 30 | 31 | * On UNIX the build bot can be run from a low privilege account because "make 32 | install" is no longer needed. This makes it easier to run the build bot 33 | from a web hook (for example) 34 | 35 | Note that this script uses the Lua/APR binding so you'll need to run "make 36 | install" or similar at least once before trying to run this script. 37 | 38 | --]] 39 | 40 | local apr = require 'apr' 41 | local serverport = 38560 42 | local projectdir = assert(apr.filepath_get()) 43 | local builddir = projectdir .. '/build' 44 | local libext = apr.platform_get() == 'WIN32' and 'dll' or 'so' 45 | local libname = 'core.' .. libext 46 | local buildfiles = { 47 | [libname] = 'apr/' .. libname, 48 | ['src/apr.lua'] = 'apr.lua', 49 | ['src/serialize.lua'] = 'apr/serialize.lua', 50 | ['test'] = 'apr/test', 51 | } 52 | local usage = [[ 53 | Usage: buildbot [OPTS] 54 | 55 | --local build and test on this machine 56 | --remote=IP build and test on a remote machine 57 | --server become a server, waiting for commands 58 | --port=NR port number for server build bot 59 | ]] 60 | 61 | function main() -- {{{1 62 | local opts, args = apr.getopt(usage) 63 | local did_something = false 64 | if opts['local'] then 65 | for line in local_build() do 66 | printf('%s', line) 67 | end 68 | did_something = true 69 | end 70 | if opts.remote then 71 | remote_build(opts.remote, serverport or opts.port) 72 | did_something = true 73 | end 74 | if opts.server then 75 | start_server(serverport or opts.port) 76 | did_something = true 77 | end 78 | if not did_something then 79 | io.write(usage) 80 | end 81 | end 82 | 83 | function local_build() -- {{{1 84 | -- Create a binary for the current platform using a makefile. 85 | return coroutine.wrap(function() 86 | local started = apr.time_now() 87 | local platform = apr.platform_get() 88 | coroutine.yield("Building on " .. platform .. " ..") 89 | local build = assert(io.popen(platform ~= 'WIN32' and 'make --no-print-directory 2>&1' 90 | or 'nmake /nologo /f Makefile.win 2>&1')) 91 | for line in build:lines() do 92 | coroutine.yield(line) 93 | end 94 | create_env() 95 | run_tests() 96 | printf("\nFinished local build in %.2f seconds!\n", 97 | apr.time_now() - started) 98 | end) 99 | end 100 | 101 | function create_env() -- {{{1 102 | -- Create or update the virtual environment. 103 | for source, target in pairs(buildfiles) do 104 | source = projectdir .. '/' .. source 105 | target = builddir .. '/' .. target 106 | if apr.stat(source, 'type') ~= 'directory' then 107 | copy_file(source, target) 108 | else 109 | local directory = assert(apr.dir_open(source)) 110 | for entry in directory:entries 'name' do 111 | if entry:find '%.lua$' then 112 | copy_file(source .. '/' .. entry, target .. '/' .. entry) 113 | end 114 | end 115 | assert(directory:close()) 116 | end 117 | end 118 | end 119 | 120 | function create_dir(path) -- {{{2 121 | -- Create directories in virtual environment automatically. 122 | if apr.stat(path, 'type') ~= 'directory' then 123 | assert(apr.dir_make_recursive(path)) 124 | end 125 | end 126 | 127 | function copy_file(source, target) -- {{{2 128 | -- Copy changed files to the virtual environment. 129 | if apr.stat(source, 'mtime') ~= apr.stat(target, 'mtime') then 130 | create_dir(apr.filepath_parent(target)) 131 | assert(apr.file_copy(source, target)) 132 | end 133 | end 134 | 135 | function run_tests() -- {{{1 136 | -- Run the test suite in the virtual environment. 137 | local pathsep = package.config:sub(3, 3) 138 | assert(apr.env_set('LUA_PATH', builddir .. '/?.lua' .. pathsep .. builddir .. '/?/init.lua')) 139 | assert(apr.env_set('LUA_CPATH', builddir .. '/?.' .. libext)) 140 | assert(apr.env_set('LD_PRELOAD', '/lib/libSegFault.so')) 141 | local testsuite = assert(io.popen [[ lua -e "require 'apr.test' ()" 2>&1 ]]) 142 | for line in testsuite:lines() do 143 | coroutine.yield(line) 144 | end 145 | assert(testsuite:close()) 146 | end 147 | 148 | function start_server(port) -- {{{1 149 | -- Open a TCP server socket and wait for commands from another build bot. 150 | local server = assert(apr.socket_create('tcp', 'inet')) 151 | local localhost = apr.host_to_addr(apr.hostname_get()) 152 | assert(server:bind('*', port)) 153 | assert(server:listen(1)) 154 | while true do 155 | printf("Build bot listening on %s:%i ..", localhost, port) 156 | local client = assert(server:accept()) 157 | for line in local_build() do 158 | printf('%s', line) 159 | client:write(line, '\n') 160 | end 161 | assert(client:close()) 162 | printf "Finished build .." 163 | end 164 | end 165 | 166 | function remote_build(host, port) -- {{{1 167 | -- Command a remote build bot to perform a build and report its results. 168 | local started = apr.time_now() 169 | local socket = assert(apr.socket_create('tcp', 'inet')) 170 | assert(socket:connect(host, port)) 171 | for line in socket:lines() do 172 | printf('%s', line) 173 | end 174 | assert(socket:close()) 175 | printf("\nFinished remote build in %.2f seconds!\n", 176 | apr.time_now() - started) 177 | end 178 | 179 | function printf(...) -- {{{1 180 | io.stderr:write(string.format(...), '\n') 181 | io.stderr:flush() 182 | end 183 | 184 | -- }}} 185 | 186 | main() 187 | -------------------------------------------------------------------------------- /etc/lua-apr-scm-1.rockspec: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | This is the LuaRocks rockspec for the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: June 18, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | --]] 11 | 12 | package = 'Lua-APR' 13 | version = 'scm-1' 14 | source = { url = 'git://github.com/xolox/lua-apr.git', branch = 'master' } 15 | description = { 16 | summary = 'Apache Portable Runtime binding for Lua', 17 | detailed = [[ 18 | Lua/APR is a binding to the Apache Portable Runtime (APR) library. APR 19 | powers software such as the Apache webserver and Subversion and Lua/APR 20 | makes the APR operating system interfaces available to Lua. 21 | ]], 22 | homepage = 'http://peterodding.com/code/lua/apr/', 23 | license = 'MIT', 24 | } 25 | dependencies = { 'lua >= 5.1' } 26 | build = { 27 | type = 'make', 28 | variables = { 29 | LUA_DIR = '$(PREFIX)', 30 | LUA_LIBDIR = '$(LIBDIR)', 31 | LUA_SHAREDIR = '$(LUADIR)', 32 | CFLAGS = '$(CFLAGS) -I$(LUA_INCDIR)', 33 | } 34 | } 35 | 36 | -- vim: ft=lua ts=2 sw=2 et 37 | -------------------------------------------------------------------------------- /etc/signals.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Signal handling module code generator for the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: June 23, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | APR supports signal handling but leaves the definition of signal names up to 11 | the system where APR is installed. This isn't a problem in C where the usual 12 | SIG* symbols are available by including but of course that doesn't 13 | work in Lua! This script works around the problem by generating some really 14 | nasty preprocessor heavy code :-). The list of signal names in this script was 15 | taken from "man 7 signal" on my Ubuntu Linux 10.04 installation. 16 | 17 | --]] 18 | 19 | -- Symbol names. 20 | local symbols = [[ 21 | -- The signals described in the original POSIX.1-1990 standard. 22 | SIGHUP 23 | SIGINT 24 | SIGQUIT 25 | SIGILL 26 | SIGABRT 27 | SIGFPE 28 | SIGKILL 29 | SIGSEGV 30 | SIGPIPE 31 | SIGALRM 32 | SIGTERM 33 | SIGUSR1 34 | SIGUSR2 35 | SIGCHLD 36 | SIGCONT 37 | SIGSTOP 38 | SIGTSTP 39 | SIGTTIN 40 | SIGTTOU 41 | -- The signals described in SUSv2 and POSIX.1-2001. 42 | SIGBUS 43 | SIGPOLL 44 | SIGPROF 45 | SIGSYS 46 | SIGTRAP 47 | SIGURG 48 | SIGVTALRM 49 | SIGXCPU 50 | SIGXFSZ 51 | -- "Various other signals." 52 | SIGIOT 53 | SIGEMT 54 | SIGSTKFLT 55 | SIGIO 56 | SIGCLD 57 | SIGPWR 58 | SIGINFO 59 | SIGLOST 60 | SIGWINCH 61 | SIGUNUSED 62 | ]] 63 | 64 | local map, index = {}, 1 65 | for line in symbols:gmatch '[^\n]+' do 66 | local name = line:match 'SIG%S+' 67 | if name then 68 | map[index] = name 69 | index = index + 1 70 | end 71 | end 72 | 73 | local signal_table = {} 74 | local signal_handlers = {} 75 | for i, name in ipairs(map) do 76 | 77 | signal_table[i] = string.format([[ 78 | # ifdef %s 79 | { "%s", %s, %s_hook, NULL, NULL, 0, 0 }, 80 | # else 81 | { NULL, 0, NULL, NULL, NULL, 0, 0 }, 82 | # endif]], name, name, name, name:lower()) 83 | 84 | signal_handlers[i] = string.format([[ 85 | #ifdef %s 86 | static void %s_hook(lua_State *L, lua_Debug *ar) { generic_hook(L, ar, %i); } 87 | #endif]], name, name:lower(), i - 1) 88 | 89 | end 90 | 91 | print(string.format([[ 92 | /* Generated signal handlers. */ 93 | 94 | %s 95 | 96 | /* Generated signal table. */ 97 | 98 | static struct { 99 | const char *name; /* Symbolic name of signal (used in Lua) */ 100 | const int value; /* Integer code of signal (used in C) */ 101 | void (*hook)(lua_State*, lua_Debug*); /* Lua hook to be called by signal handler */ 102 | lua_State *state; /* Lua state that installed signal handler */ 103 | lua_Hook oldhook; /* Old debug hook and related variables */ 104 | int oldmask, oldcount; 105 | } known_signals[] = { 106 | %s 107 | };]], 108 | table.concat(signal_handlers, '\n'), 109 | table.concat(signal_table, '\n'))) 110 | -------------------------------------------------------------------------------- /etc/template.rockspec: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | This is the LuaRocks rockspec for the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: {{DATE}} 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | 9 | --]] 10 | 11 | package = 'Lua-APR' 12 | version = '{{VERSION}}' 13 | source = { 14 | url = 'http://peterodding.com/code/lua/apr/downloads/lua-apr-{{VERSION}}.zip', 15 | md5 = '{{HASH}}', 16 | } 17 | description = { 18 | summary = 'Apache Portable Runtime binding for Lua', 19 | detailed = [[ 20 | Lua/APR is a binding to the Apache Portable Runtime (APR) library. APR 21 | powers software such as the Apache webserver and Subversion and Lua/APR 22 | makes the APR operating system interfaces available to Lua. 23 | ]], 24 | homepage = 'http://peterodding.com/code/lua/apr/', 25 | license = 'MIT', 26 | } 27 | dependencies = { 'lua >= 5.1' } 28 | build = { 29 | type = 'make', 30 | variables = { 31 | LUA_DIR = '$(PREFIX)', 32 | LUA_LIBDIR = '$(LIBDIR)', 33 | LUA_SHAREDIR = '$(LUADIR)', 34 | CFLAGS = '$(CFLAGS) -I$(LUA_INCDIR)', 35 | } 36 | } 37 | 38 | -- vim: ft=lua ts=2 sw=2 et 39 | -------------------------------------------------------------------------------- /etc/win32-bootstrap/clean.cmd: -------------------------------------------------------------------------------- 1 | RMDIR /S /Q apr 2 | RMDIR /S /Q apr-util 3 | RMDIR /S /Q apr-iconv 4 | RMDIR /S /Q libapreq2 5 | RMDIR /S /Q sqlite3 6 | RMDIR /S /Q binaries 7 | -------------------------------------------------------------------------------- /etc/win32-bootstrap/libapreq2.mak: -------------------------------------------------------------------------------- 1 | # Microsoft Developer Studio Generated NMAKE File, Based on libapreq.dsp 2 | # Compile with NMAKE /f libapreq2.mak "CFG=libapreq2 - Win32 Debug" 3 | 4 | APR_LIB=$(ROOT)\apr\Debug\libapr-1.lib 5 | APU_LIB=$(ROOT)\apr-util\Debug\libaprutil-1.lib 6 | APREQ_HOME=$(ROOT)\libapreq2 7 | 8 | !IF "$(CFG)" == "" 9 | CFG=libapreq2 - Win32 Release 10 | !MESSAGE No configuration specified. Defaulting to libapreq2 - Win32 Release. 11 | !ENDIF 12 | 13 | !IF "$(CFG)" != "libapreq2 - Win32 Release" && "$(CFG)" != "libapreq2 - Win32 Debug" 14 | !MESSAGE Invalid configuration "$(CFG)" specified. 15 | !MESSAGE You can specify a configuration when running NMAKE 16 | !MESSAGE by defining the macro CFG on the command line. For example: 17 | !MESSAGE 18 | !MESSAGE NMAKE /f "libapreq2.mak" CFG="libapreq2 - Win32 Debug" 19 | !MESSAGE 20 | !MESSAGE Possible choices for configuration are: 21 | !MESSAGE 22 | !MESSAGE "libapreq2 - Win32 Release" (based on "Win32 (x86) Static Library") 23 | !MESSAGE "libapreq2 - Win32 Debug" (based on "Win32 (x86) Static Library") 24 | !MESSAGE 25 | !ERROR An invalid configuration is specified. 26 | !ENDIF 27 | 28 | !IF "$(OS)" == "Windows_NT" 29 | NULL= 30 | !ELSE 31 | NULL=nul 32 | !ENDIF 33 | 34 | CPP=cl.exe /I$(ROOT)\apr\include /I$(ROOT)\apr-util\include 35 | RSC=rc.exe /I$(ROOT)\apr\include /I$(ROOT)\apr-util\include 36 | 37 | CFG_HOME=$(APREQ_HOME)\win32 38 | OUTDIR=$(CFG_HOME)\libs 39 | INTDIR=$(CFG_HOME)\libs 40 | LIBDIR=$(APREQ_HOME)\library 41 | 42 | LINK32_OBJS= \ 43 | "$(INTDIR)\cookie.obj" \ 44 | "$(INTDIR)\param.obj" \ 45 | "$(INTDIR)\parser.obj" \ 46 | "$(INTDIR)\parser_header.obj" \ 47 | "$(INTDIR)\parser_multipart.obj" \ 48 | "$(INTDIR)\parser_urlencoded.obj" \ 49 | "$(INTDIR)\util.obj" \ 50 | "$(INTDIR)\version.obj" \ 51 | "$(INTDIR)\error.obj" \ 52 | "$(INTDIR)\libapreq.res" 53 | 54 | !IF "$(CFG)" == "libapreq2 - Win32 Release" 55 | 56 | ALL: "$(OUTDIR)\libapreq2.dll" 57 | 58 | "$(OUTDIR)": 59 | if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" 60 | 61 | CPP_PROJ=/nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "APREQ_DECLARE_EXPORT" /I"$(APREQ_HOME)\include" /FD /c 62 | MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 63 | RSC_PROJ=/l 0x409 /d "NDEBUG" /i "$(APREQ_HOME)\include" 64 | BSC32=bscmake.exe 65 | BSC32_FLAGS=/nologo /o"$(OUTDIR)\libapreq2.bsc" 66 | LINK32=link.exe 67 | MANIFEST=$(OUTDIR)\libapreq2.dll.manifest 68 | LINK32_FLAGS="$(APR_LIB)" "$(APU_LIB)" kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /incremental:no /machine:I386 /out:"$(OUTDIR)\libapreq2.dll" /implib:"$(OUTDIR)\libapreq2.lib" 69 | 70 | "$(OUTDIR)\libapreq2.dll": "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) 71 | $(LINK32) $(LINK32_FLAGS) $(DEF_FLAGS) $(LINK32_OBJS) 72 | if exist $(MANIFEST) mt /nologo /manifest $(MANIFEST) /outputresource:$(OUTDIR)\libapreq2.dll;2 73 | 74 | !ELSEIF "$(CFG)" == "libapreq2 - Win32 Debug" 75 | 76 | ALL: "$(OUTDIR)\libapreq2.dll" 77 | 78 | "$(OUTDIR)": 79 | if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" 80 | 81 | CPP_PROJ=/nologo /MDd /W3 /Gm /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "APREQ_DECLARE_EXPORT" /I"$(APREQ_HOME)\include" /FD /RTC1 /c 82 | MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32 83 | RSC_PROJ=/l 0x409 /d "_DEBUG" /i "$(APREQ_HOME)\include" 84 | BSC32=bscmake.exe 85 | BSC32_FLAGS=/nologo /o"$(OUTDIR)\libapreq2.bsc" 86 | LINK32=link.exe 87 | MANIFEST=$(OUTDIR)\libapreq2.dll.manifest 88 | LINK32_FLAGS="$(APR_LIB)" "$(APU_LIB)" kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /incremental:yes /pdb:"$(OUTDIR)\libapreq2.pdb" /debug /machine:I386 /out:"$(OUTDIR)\libapreq2.dll" /implib:"$(OUTDIR)\libapreq2.lib" /pdbtype:sept 89 | 90 | "$(OUTDIR)\libapreq2.dll": "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) 91 | $(LINK32) $(LINK32_FLAGS) $(DEF_FLAGS) $(LINK32_OBJS) 92 | if exist $(MANIFEST) mt /nologo /manifest $(MANIFEST) /outputresource:$(OUTDIR)\libapreq2.dll;2 93 | 94 | !ENDIF 95 | 96 | .c{$(INTDIR)}.obj:: 97 | $(CPP) @<< 98 | $(CPP_PROJ) $< 99 | << 100 | 101 | .cpp{$(INTDIR)}.obj:: 102 | $(CPP) @<< 103 | $(CPP_PROJ) $< 104 | << 105 | 106 | .cxx{$(INTDIR)}.obj:: 107 | $(CPP) @<< 108 | $(CPP_PROJ) $< 109 | << 110 | 111 | .c{$(INTDIR)}.sbr:: 112 | $(CPP) @<< 113 | $(CPP_PROJ) $< 114 | << 115 | 116 | .cpp{$(INTDIR)}.sbr:: 117 | $(CPP) @<< 118 | $(CPP_PROJ) $< 119 | << 120 | 121 | .cxx{$(INTDIR)}.sbr:: 122 | $(CPP) @<< 123 | $(CPP_PROJ) $< 124 | << 125 | 126 | 127 | !IF "$(CFG)" == "libapreq2 - Win32 Release" || "$(CFG)" == "libapreq2 - Win32 Debug" 128 | 129 | SOURCE=$(LIBDIR)\cookie.c 130 | 131 | "$(INTDIR)\cookie.obj" : $(SOURCE) "$(INTDIR)" 132 | $(CPP) /Fo"$(INTDIR)\cookie.obj" $(CPP_PROJ) $(SOURCE) 133 | 134 | SOURCE=$(LIBDIR)\param.c 135 | 136 | "$(INTDIR)\param.obj" : $(SOURCE) "$(INTDIR)" 137 | $(CPP) /Fo"$(INTDIR)\param.obj" $(CPP_PROJ) $(SOURCE) 138 | 139 | SOURCE=$(LIBDIR)\parser.c 140 | 141 | "$(INTDIR)\parser.obj" : $(SOURCE) "$(INTDIR)" 142 | $(CPP) /Fo"$(INTDIR)\parser.obj" $(CPP_PROJ) $(SOURCE) 143 | 144 | SOURCE=$(LIBDIR)\parser_header.c 145 | 146 | "$(INTDIR)\parser_header.obj" : $(SOURCE) "$(INTDIR)" 147 | $(CPP) /Fo"$(INTDIR)\parser_header.obj" $(CPP_PROJ) $(SOURCE) 148 | 149 | SOURCE=$(LIBDIR)\parser_multipart.c 150 | 151 | "$(INTDIR)\parser_multipart.obj" : $(SOURCE) "$(INTDIR)" 152 | $(CPP) /Fo"$(INTDIR)\parser_multipart.obj" $(CPP_PROJ) $(SOURCE) 153 | 154 | SOURCE=$(LIBDIR)\parser_urlencoded.c 155 | 156 | "$(INTDIR)\parser_urlencoded.obj" : $(SOURCE) "$(INTDIR)" 157 | $(CPP) /Fo"$(INTDIR)\parser_urlencoded.obj" $(CPP_PROJ) $(SOURCE) 158 | 159 | SOURCE=$(LIBDIR)\version.c 160 | 161 | "$(INTDIR)\version.obj" : $(SOURCE) "$(INTDIR)" 162 | $(CPP) /Fo"$(INTDIR)\version.obj" $(CPP_PROJ) $(SOURCE) 163 | 164 | SOURCE=$(LIBDIR)\util.c 165 | 166 | "$(INTDIR)\util.obj" : $(SOURCE) "$(INTDIR)" 167 | $(CPP) /Fo"$(INTDIR)\util.obj" $(CPP_PROJ) $(SOURCE) 168 | 169 | SOURCE=$(LIBDIR)\error.c 170 | 171 | "$(INTDIR)\error.obj" : $(SOURCE) "$(INTDIR)" 172 | $(CPP) /Fo"$(INTDIR)\error.obj" $(CPP_PROJ) $(SOURCE) 173 | 174 | SOURCE=.\libapreq.rc 175 | 176 | "$(INTDIR)\libapreq.res" : $(SOURCE) "$(INTDIR)" 177 | $(RSC) /fo"$(INTDIR)\libapreq.res" $(RSC_PROJ) $(SOURCE) 178 | 179 | !ENDIF 180 | 181 | -------------------------------------------------------------------------------- /etc/wrap.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Markdown processor for the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: December 3, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | This Lua script takes two arguments, the first is the pathname of an existing 11 | Markdown document and the second is the name of an HTML file to generate. The 12 | script will transform the Markdown to HTML, syntax highlight snippets of Lua 13 | source code using my LXSH module and wrap the output in a page template. 14 | 15 | ]] 16 | 17 | -- Get the names of the input and output file. 18 | local infile, outfile = unpack(arg, 1, 2) 19 | assert(infile, "Missing source Markdown file as 1st argument") 20 | assert(outfile, "Missing target HTML file as 2nd argument") 21 | 22 | -- Load Markdown module (lua-discount or markdown.lua) 23 | local status, markdown = pcall(require, 'discount') 24 | if not status then 25 | status, markdown = pcall(require, 'markdown') 26 | end 27 | if not status then 28 | io.stderr:write("Markdown not installed, can't update HTML files!\n") 29 | os.exit() 30 | end 31 | 32 | -- Parse and convert the markdown to HTML. 33 | assert(io.input(infile)) 34 | local input = assert(io.read '*all') 35 | local title = assert(input:match '^%s*#%s*([^\r\n]+)', "Failed to match page title") 36 | local content = markdown(input) 37 | 38 | -- Workaround for bug in Discount :-( 39 | content = content:gsub('', '-') 40 | 41 | -- If my LXSH module is installed we will use it to syntax highlight 42 | -- the snippets of Lua source code in the Lua/APR documentation. 43 | local status, highlighter = pcall(require, 'lxsh.highlighters.lua') 44 | if status and highlighter then 45 | local html_entities = { amp = '&', lt = '<', gt = '>' } 46 | content = content:gsub('
(.-)
', function(block) 47 | block = block:match '^(.-)$' or block 48 | block = block:gsub('&(%w+);', html_entities) 49 | return highlighter(block, { 50 | encodews = true, 51 | formatter = lxsh.formatters.html, 52 | }) 53 | end) 54 | end 55 | 56 | -- Wrap the HTML in a standardized template. 57 | assert(io.output(outfile)) 58 | io.write([[ 59 | 61 | 62 | 63 | ]], title, [[ 64 | 65 | 81 | 82 | 83 | ]], content, [[ 84 | 85 | 86 | ]]) 87 | -------------------------------------------------------------------------------- /examples/async-webserver.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Example: Asynchronous webserver 4 | 5 | Author: Peter Odding 6 | Last Change: November 6, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | We can do even better than the performance of the multi threaded webserver by 11 | using the [APR pollset module] [pollset_module]. The following webserver uses 12 | asynchronous network communication to process requests from multiple clients 13 | 'in parallel' without using multiple threads or processes. Here is a 14 | benchmark of the asynchronous code listed below (again using [ApacheBench] 15 | [ab] with the `-c` argument): 16 | 17 | $ CONCURRENCY=4 18 | $ POLLSET_SIZE=10 19 | $ lua examples/async-webserver.lua $POLLSET_SIZE 8080 cheat & 20 | $ ab -qt5 -c$CONCURRENCY http://localhost:8080/ | grep 'Requests per second\|Transfer rate' 21 | Requests per second: 11312.64 [#/sec] (mean) 22 | Transfer rate: 6219.74 [Kbytes/sec] received 23 | 24 | The [single threaded webserver] [simple_server] handled 3670 requests per 25 | second, the [multi threaded webserver] [threaded_server] handled 9210 26 | requests per second and the asynchronous webserver below can handle 11310 27 | requests per second. Actually in the above benchmark I may have cheated a bit 28 | (depending on whether your goal is correct usage or performance). When I 29 | started writing this asynchronous server example I didn't bother to add 30 | writable sockets to the pollset, instead I handled the request and response 31 | once the client socket was reported as readable by the pollset. Later on I 32 | changed the code to handle writing using the pollset and I noticed that the 33 | performance dropped. This is probably because the example is so contrived. 34 | Anyway here's the performance without cheating: 35 | 36 | $ lua examples/async-webserver.lua $POLLSET_SIZE 8080 & 37 | $ ab -qt5 -c$CONCURRENCY http://localhost:8888/ | grep 'Requests per second\|Transfer rate' 38 | Requests per second: 9867.17 [#/sec] (mean) 39 | Transfer rate: 5425.03 [Kbytes/sec] received 40 | 41 | Now follows the implementation of the asynchronous webserver example: 42 | 43 | [pollset_module]: #pollset 44 | [ab]: http://en.wikipedia.org/wiki/ApacheBench 45 | [simple_server]: #example_single_threaded_webserver 46 | [threaded_server]: #example_multi_threaded_webserver 47 | 48 | ]] 49 | 50 | local pollset_size = tonumber(arg[1]) or 10 51 | local port_number = tonumber(arg[2]) or 8080 52 | local cheat = arg[3] == 'cheat' -- cheat to make it faster? 53 | 54 | local template = [[ 55 | 56 | 57 | Hello from Lua/APR! 58 | 63 | 64 | 65 |

Hello from Lua/APR!

66 |

The headers provided by your web browser:

67 |
%s
68 | 69 | 70 | ]] 71 | 72 | -- Load the Lua/APR binding. 73 | local apr = require 'apr' 74 | 75 | -- Initialize the server socket. 76 | local server = assert(apr.socket_create()) 77 | assert(server:bind('*', port_number)) 78 | assert(server:listen(pollset_size)) 79 | 80 | -- Create the pollset. 81 | local pollset = assert(apr.pollset(pollset_size)) 82 | assert(pollset:add(server, 'input')) 83 | 84 | -- Use a table indexed with socket objects to keep track of "sessions". 85 | local sessions = setmetatable({}, {__mode='k'}) 86 | 87 | -- Enter the I/O loop. 88 | print("Running webserver on http://localhost:" .. port_number .. " ..") 89 | while true do 90 | local readable, writable = assert(pollset:poll(-1)) 91 | -- Process requests. 92 | for _, socket in ipairs(readable) do 93 | if socket == server then 94 | local client = assert(server:accept()) 95 | assert(pollset:add(client, 'input')) 96 | else 97 | local request = assert(socket:read(), "Failed to receive request from client!") 98 | local method, location, protocol = assert(request:match '^(%w+)%s+(%S+)%s+(%S+)') 99 | local headers = {} 100 | for line in socket:lines() do 101 | local name, value = line:match '^(%S+):%s+(.-)$' 102 | if not name then 103 | break 104 | end 105 | table.insert(headers, '
' .. name .. ':
' .. value .. '
') 106 | end 107 | table.sort(headers) 108 | local content = template:format(table.concat(headers)) 109 | assert(socket:write( 110 | protocol, ' 200 OK\r\n', 111 | 'Content-Type: text/html\r\n', 112 | 'Content-Length: ', #content, '\r\n', 113 | 'Connection: close\r\n', 114 | '\r\n')) 115 | if cheat then 116 | assert(socket:write(content)) 117 | assert(pollset:remove(socket)) 118 | assert(socket:close()) 119 | else 120 | sessions[socket] = content 121 | assert(pollset:remove(socket)) 122 | assert(pollset:add(socket, 'output')) 123 | end 124 | end 125 | end 126 | if not cheat then 127 | -- Process responses. 128 | for _, socket in ipairs(writable) do 129 | assert(socket:write(sessions[socket])) 130 | -- I don't like that when I switch the order of these 131 | -- calls, it breaks... Seems like a fairly big gotcha. 132 | assert(pollset:remove(socket)) 133 | assert(socket:close()) 134 | end 135 | end 136 | end 137 | 138 | -- vim: ts=2 sw=2 et 139 | -------------------------------------------------------------------------------- /examples/download.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Example: HTTP client 4 | 5 | Author: Peter Odding 6 | Last Change: December 30, 2010 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | The following Lua script implements a minimal [HTTP client] [http] which can 11 | be used to download a given [URL] [url] on the command line (comparable to 12 | [wget] [wget] and [curl] [curl]): 13 | 14 | $ FILE=lua-5.1.4.tar.gz 15 | $ URL=http://www.lua.org/ftp/$FILE 16 | $ time curl -s $URL > $FILE 17 | 0,01s user 0,02s system 6% cpu 0,465 total 18 | $ time lua examples/download.lua $URL > $FILE 19 | 0,03s user 0,02s system 9% cpu 0,549 total 20 | 21 | Note that this script and Lua/APR in general are a bit handicapped in that 22 | they don't support [HTTPS] [https] because the Apache Portable Runtime does 23 | not support encrypted network communication. 24 | 25 | [url]: http://en.wikipedia.org/wiki/Uniform_Resource_Locator 26 | [wget]: http://en.wikipedia.org/wiki/wget 27 | [curl]: http://en.wikipedia.org/wiki/cURL 28 | [https]: http://en.wikipedia.org/wiki/HTTPS 29 | 30 | ]] 31 | 32 | local apr = require 'apr' 33 | 34 | -- Report errors without stack traces. 35 | local function assert(...) 36 | local status, message = ... 37 | if not status then 38 | io.stderr:write('Error: ', message or '(no message)', '\n') 39 | os.exit(1) 40 | end 41 | return ... 42 | end 43 | 44 | local function getpage(url) 45 | local components = assert(apr.uri_parse(url)) 46 | assert(components.scheme == 'http', "invalid protocol!") 47 | local port = assert(components.port or apr.uri_port_of_scheme(components.scheme)) 48 | local socket = assert(apr.socket_create()) 49 | assert(socket:connect(components.hostname, port)) 50 | local pathinfo = assert(apr.uri_unparse(components, 'pathinfo')) 51 | assert(socket:write('GET ', pathinfo, ' HTTP/1.0\r\n', 52 | 'Host: ', components.hostname, '\r\n', 53 | '\r\n')) 54 | local statusline = assert(socket:read(), 'HTTP response missing status line!') 55 | local protocol, statuscode, reason = assert(statusline:match '^(%S+)%s+(%S+)%s+(.-)$') 56 | local redirect = statuscode:find '^30[123]$' 57 | for line in socket:lines() do 58 | local name, value = line:match '^(%S+):%s+(.-)\r?$' 59 | if name and value then 60 | if redirect and name:lower() == 'location' then 61 | io.stderr:write("Following redirect to ", value, " ..\n") 62 | return getpage(value) 63 | end 64 | else 65 | return (assert(socket:read '*a', 'HTTP response missing body?!')) 66 | end 67 | end 68 | if statuscode ~= '200' then error(reason) end 69 | end 70 | 71 | local usage = "Please provide a URL to download as argument" 72 | io.write(getpage(assert(arg and arg[1], usage))) 73 | 74 | -- vim: ts=2 sw=2 et 75 | -------------------------------------------------------------------------------- /examples/threaded-webserver.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Example: Multi threaded webserver 4 | 5 | Author: Peter Odding 6 | Last Change: November 6, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | Thanks to the [multi threading] [threading_module] and [thread queue] 11 | [thread_queues] modules in the Apache Portable Runtime it is possible to 12 | improve the performance of the [single threaded webserver] [simple_server] 13 | from the previous example. Here is a benchmark of the multi threaded code 14 | listed below (again using [ApacheBench] [ab], but now with the `-c` 15 | argument): 16 | 17 | $ CONCURRENCY=4 18 | $ lua examples/threaded-webserver.lua $CONCURRENCY & 19 | $ ab -qt5 -c$CONCURRENCY http://localhost:8080/ | grep 'Requests per second\|Transfer rate' 20 | Requests per second: 9210.72 [#/sec] (mean) 21 | Transfer rate: 5594.79 [Kbytes/sec] received 22 | 23 | Comparing these numbers to the benchmark of the [single threaded webserver] 24 | [simple_server] we can see that the number of requests per second went from 25 | 3670 to 9210, more than doubling the throughput of the webserver on a dual 26 | core processor. If you want to know how we can make it even faster, have a 27 | look at the [asynchronous webserver] [async_server] example. 28 | 29 | [threading_module]: #multi_threading 30 | [thread_queues]: #thread_queues 31 | [simple_server]: #example_single_threaded_webserver 32 | [ab]: http://en.wikipedia.org/wiki/ApacheBench 33 | [thread_pool]: http://en.wikipedia.org/wiki/Thread_pool_pattern 34 | [async_server]: #example_asynchronous_webserver 35 | 36 | ]] 37 | 38 | local num_threads = tonumber(arg[1]) or 2 39 | local port_number = tonumber(arg[2]) or 8080 40 | 41 | local template = [[ 42 | 43 | 44 | Hello from Lua/APR! 45 | 50 | 51 | 52 |

Hello from Lua/APR!

53 |

This web page was served by worker %i.

54 |

The headers provided by your web browser:

55 |
%s
56 | 57 | 58 | ]] 59 | 60 | -- Load the Lua/APR binding. 61 | local apr = require 'apr' 62 | 63 | -- Initialize the server socket. 64 | local server = assert(apr.socket_create()) 65 | assert(server:bind('*', port_number)) 66 | assert(server:listen(num_threads * 2)) 67 | print("Running webserver with " .. num_threads .. " client threads on http://localhost:" .. port_number .. " ..") 68 | 69 | -- Create the thread queue (used to pass sockets between threads). 70 | local queue = apr.thread_queue(num_threads) 71 | 72 | -- Define the function to execute in each child thread. 73 | function worker(thread_id, queue, template) 74 | pcall(require, 'luarocks.require') 75 | local apr = require 'apr' 76 | while true do 77 | local client, msg, code = queue:pop() 78 | assert(client or code == 'EINTR', msg) 79 | if client then 80 | local status, message = pcall(function() 81 | local request = assert(client:read(), "Failed to receive request from client!") 82 | local method, location, protocol = assert(request:match '^(%w+)%s+(%S+)%s+(%S+)') 83 | local headers = {} 84 | for line in client:lines() do 85 | local name, value = line:match '^(%S+):%s+(.-)$' 86 | if not name then 87 | break 88 | end 89 | table.insert(headers, '
' .. name .. ':
' .. value .. '
') 90 | end 91 | table.sort(headers) 92 | local content = template:format(thread_id, table.concat(headers)) 93 | client:write(protocol, ' 200 OK\r\n', 94 | 'Content-Type: text/html\r\n', 95 | 'Content-Length: ' .. #content .. '\r\n', 96 | 'Connection: close\r\n', 97 | '\r\n', 98 | content) 99 | assert(client:close()) 100 | end) 101 | if not status then 102 | print('Error while serving request:', message) 103 | end 104 | end 105 | end 106 | end 107 | 108 | -- Create the child threads and keep them around in a table (so that they are 109 | -- not garbage collected while we are still using them). 110 | local pool = {} 111 | for i = 1, num_threads do 112 | table.insert(pool, apr.thread(worker, i, queue, template)) 113 | end 114 | 115 | -- Enter the accept() loop in the parent thread. 116 | while true do 117 | local status, message = pcall(function() 118 | local client = assert(server:accept()) 119 | assert(queue:push(client)) 120 | end) 121 | if not status then 122 | print('Error while serving request:', message) 123 | end 124 | end 125 | 126 | -- vim: ts=2 sw=2 et 127 | -------------------------------------------------------------------------------- /examples/webserver.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Example: Single threaded webserver 4 | 5 | Author: Peter Odding 6 | Last Change: November 6, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | The following script implements a minimalistic webserver on top of Lua/APR 11 | [network sockets] [socket_module]. It should work out of the box on Windows 12 | and UNIX, although you might get a prompt from your firewall. Once the server 13 | is running you can open in your web browser to see 14 | the server in action. Because the server is single threaded I was curious how 15 | bad it would perform, so I tested it with [ApacheBench] [ab]: 16 | 17 | $ lua examples/webserver.lua & 18 | $ ab -qt5 http://localhost:8080/ | grep 'Requests per second\|Transfer rate' 19 | Requests per second: 3672.19 [#/sec] (mean) 20 | Transfer rate: 2201.88 [Kbytes/sec] received 21 | 22 | That's not too bad for 40 lines of code! For more complex webservers see the 23 | [multi threaded] [threaded_server] and [asynchronous] [async_server] 24 | webserver examples. 25 | 26 | *Note that all benchmarks are run on my Compaq Presario CQ60 laptop (which 27 | features an Intel Core 2 Duo T5800 processor clocked at 2 GHz and 3 GB of 28 | RAM) and that the Lua/APR binding was compiled without debugging symbols.* 29 | 30 | [socket_module]: #network_i_o_handling 31 | [ab]: http://en.wikipedia.org/wiki/ApacheBench 32 | [threaded_server]: #example_multi_threaded_webserver 33 | [async_server]: #example_asynchronous_webserver 34 | 35 | ]] 36 | 37 | local port_number = tonumber(arg[1]) or 8080 38 | local bind_address = arg[2] or '*' 39 | 40 | -- Load the Lua/APR binding. 41 | local apr = require 'apr' 42 | 43 | -- Initialize the server socket. 44 | local server = assert(apr.socket_create()) 45 | assert(server:bind(bind_address, port_number)) 46 | assert(server:listen(1)) 47 | print("Running webserver on http://" .. bind_address .. ":" .. port_number .. " ..") 48 | 49 | -- Wait for clients to serve. 50 | local visitor = 1 51 | local template = [[ 52 | 53 | 54 | Hello from Lua/APR! 55 | 60 | 61 | 62 |

Hello from Lua/APR!

63 |

You are visitor number %010i.

64 |

The headers provided by your web browser:

65 |
%s
66 | 67 | 68 | ]] 69 | while true do 70 | local status, message = pcall(function() 71 | local client = assert(server:accept()) 72 | -- Read the HTTP request so that the client can receive data. 73 | local request = assert(client:read(), "Failed to receive request from client!") 74 | local method, location, protocol = assert(request:match '^(%w+)%s+(%S+)%s+(%S+)') 75 | local headers = {} 76 | for line in client:lines() do 77 | local name, value = line:match '^(%S+):%s+(.-)$' 78 | if not name then break end 79 | table.insert(headers, '
' .. name .. ':
' .. value .. '
') 80 | end 81 | -- Generate the HTTP response. 82 | table.sort(headers) 83 | content = template:format(visitor, table.concat(headers)) 84 | client:write(protocol, ' 200 OK\r\n', 85 | 'Content-Type: text/html\r\n', 86 | 'Content-Length: ' .. #content .. '\r\n', 87 | 'Connection: close\r\n', 88 | '\r\n', 89 | content) 90 | assert(client:close()) 91 | visitor = visitor + 1 92 | end) 93 | if not status then 94 | print('Error while serving request:', message) 95 | end 96 | end 97 | 98 | -- vim: ts=2 sw=2 et 99 | -------------------------------------------------------------------------------- /make.cmd: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM On Windows the Lua/APR binding is compiled using NMAKE /f Makefile.win. 4 | REM This batch script makes it possible to just type MAKE and be done with it. 5 | 6 | NMAKE /nologo /f Makefile.win %* 7 | -------------------------------------------------------------------------------- /src/base64.c: -------------------------------------------------------------------------------- 1 | /* Base64 encoding module for the Lua/APR binding. 2 | * 3 | * Author: Peter Odding 4 | * Last Change: January 2, 2011 5 | * Homepage: http://peterodding.com/code/lua/apr/ 6 | * License: MIT 7 | * 8 | * The functions in this module can be used to encode strings in base64 and to 9 | * decode base64 encoded strings. The base64 format uses the printable 10 | * characters `A-Z`, `a-z`, `0-9`, `+` and `/` to encode binary data. This can 11 | * be useful when your data is used in a context that isn't 8-bit clean, for 12 | * example in e-mail attachments and [data:] [data_uris] URLs. You can read 13 | * more about base64 encoding in [this] [base64] Wikipedia article. 14 | * 15 | * [base64]: http://en.wikipedia.org/wiki/Base64 16 | * [data_uris]: http://en.wikipedia.org/wiki/Data_URI_scheme 17 | */ 18 | 19 | #include "lua_apr.h" 20 | #include 21 | 22 | /* apr.base64_encode(plain) -> coded {{{1 23 | * 24 | * Encode the string @plain using base64 encoding. On success the coded string 25 | * is returned, otherwise a nil followed by an error message is returned. As an 26 | * example, here is how to convert an image file into a [data: URL] 27 | * [data_uris]: 28 | * 29 | * > image = io.open 'lua-logo.png' 30 | * > encoded_data = apr.base64_encode(image:read '*a') 31 | * > data_url = 'data:image/png;base64,' .. encoded_data 32 | * > = 'Lua logo' 33 | * Lua logo 34 | * 35 | * This is what the result looks like (might not work in older web browsers): 36 | * Lua logo 37 | */ 38 | 39 | int lua_apr_base64_encode(lua_State *L) 40 | { 41 | size_t plain_len, coded_len; 42 | apr_pool_t *memory_pool; 43 | const char *plain; 44 | char *coded; 45 | 46 | memory_pool = to_pool(L); 47 | plain = luaL_checklstring(L, 1, &plain_len); 48 | coded_len = apr_base64_encode_len(plain_len); 49 | coded = apr_palloc(memory_pool, coded_len); 50 | if (coded == NULL) 51 | return push_error_memory(L); 52 | coded_len = apr_base64_encode(coded, plain, plain_len); 53 | if (coded_len > 0 && coded[coded_len - 1] == '\0') 54 | coded_len--; 55 | lua_pushlstring(L, coded, coded_len); 56 | return 1; 57 | } 58 | 59 | /* apr.base64_decode(coded) -> plain {{{1 60 | * 61 | * Decode the base64 encoded string @coded. On success the decoded string is 62 | * returned, otherwise a nil followed by an error message is returned. 63 | */ 64 | 65 | int lua_apr_base64_decode(lua_State *L) 66 | { 67 | size_t plain_len, coded_len; 68 | apr_pool_t *memory_pool; 69 | const char *coded; 70 | char *plain; 71 | 72 | memory_pool = to_pool(L); 73 | coded = luaL_checklstring(L, 1, &coded_len); 74 | plain_len = apr_base64_decode_len(coded); 75 | plain = apr_palloc(memory_pool, plain_len); 76 | if (plain == NULL) 77 | return push_error_memory(L); 78 | plain_len = apr_base64_decode(plain, coded); 79 | if (plain_len > 0 && plain[plain_len - 1] == '\0') 80 | plain_len--; 81 | lua_pushlstring(L, plain, plain_len); 82 | return 1; 83 | } 84 | 85 | /* vim: set ts=2 sw=2 et tw=79 fen fdm=marker : */ 86 | -------------------------------------------------------------------------------- /src/date.c: -------------------------------------------------------------------------------- 1 | /* Date parsing module for the Lua/APR binding. 2 | * 3 | * Author: Peter Odding 4 | * Last Change: February 13, 2011 5 | * Homepage: http://peterodding.com/code/lua/apr/ 6 | * License: MIT 7 | */ 8 | 9 | #include "lua_apr.h" 10 | #include 11 | 12 | /* apr.date_parse_http(string) -> time {{{1 13 | * 14 | * Parses an [HTTP] [http] date in one of three standard forms: 15 | * 16 | * - `'Sun, 06 Nov 1994 08:49:37 GMT'` - [RFC 822] [rfc822], updated by [RFC 1123][rfc1123] 17 | * - `'Sunday, 06-Nov-94 08:49:37 GMT'` - [RFC 850] [rfc850], obsoleted by [RFC 1036][rfc1036] 18 | * - `'Sun Nov 6 08:49:37 1994'` - ANSI C's [asctime()] [asctime] format 19 | * 20 | * On success the date is returned as a number like documented under [time 21 | * routines](#time_routines). If the date string is out of range or invalid nil 22 | * is returned. 23 | * 24 | * [http]: http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol 25 | * [rfc822]: http://tools.ietf.org/html/rfc822 26 | * [rfc1123]: http://tools.ietf.org/html/rfc1123 27 | * [rfc850]: http://tools.ietf.org/html/rfc850 28 | * [rfc1036]: http://tools.ietf.org/html/rfc1036 29 | * [asctime]: http://linux.die.net/man/3/asctime 30 | */ 31 | 32 | int lua_apr_date_parse_http(lua_State *L) 33 | { 34 | const char *input; 35 | apr_time_t result; 36 | 37 | input = luaL_checkstring(L, 1); 38 | result = apr_date_parse_http(input); 39 | if (result == 0) 40 | return 0; 41 | time_put(L, result); 42 | return 1; 43 | } 44 | 45 | /* apr.date_parse_rfc(string) -> time {{{1 46 | * 47 | * Parses a string resembling an [RFC 822] [rfc822] date. This is meant to be 48 | * lenient in its parsing of dates and hence will parse a wider range of dates 49 | * than `apr.date_parse_http()`. 50 | * 51 | * The prominent mailer (or poster, if mailer is unknown) that has been seen in 52 | * the wild is included for the unknown formats: 53 | * 54 | * - `'Sun, 06 Nov 1994 08:49:37 GMT'` - [RFC 822] [rfc822], updated by [RFC 1123] [rfc1123] 55 | * - `'Sunday, 06-Nov-94 08:49:37 GMT'` - [RFC 850] [rfc850], obsoleted by [RFC 1036] [rfc1036] 56 | * - `'Sun Nov 6 08:49:37 1994'` - ANSI C's [asctime()] [asctime] format 57 | * - `'Sun, 6 Nov 1994 08:49:37 GMT'` - [RFC 822] [rfc822], updated by [RFC 1123] [rfc1123] 58 | * - `'Sun, 06 Nov 94 08:49:37 GMT'` - [RFC 822] [rfc822] 59 | * - `'Sun, 6 Nov 94 08:49:37 GMT'` - [RFC 822] [rfc822] 60 | * - `'Sun, 06 Nov 94 08:49 GMT'` - Unknown [drtr@ast.cam.ac.uk] 61 | * - `'Sun, 6 Nov 94 08:49 GMT'` - Unknown [drtr@ast.cam.ac.uk] 62 | * - `'Sun, 06 Nov 94 8:49:37 GMT'` - Unknown [Elm 70.85] 63 | * - `'Sun, 6 Nov 94 8:49:37 GMT'` - Unknown [Elm 70.85] 64 | * 65 | * On success the date is returned as a number like documented under [time 66 | * routines](#time_routines). If the date string is out of range or invalid nil 67 | * is returned. 68 | */ 69 | 70 | int lua_apr_date_parse_rfc(lua_State *L) 71 | { 72 | const char *input; 73 | apr_time_t result; 74 | 75 | input = luaL_checkstring(L, 1); 76 | result = apr_date_parse_rfc(input); 77 | if (result == 0) 78 | return 0; 79 | time_put(L, result); 80 | return 1; 81 | } 82 | 83 | /* vim: set ts=2 sw=2 et tw=79 fen fdm=marker : */ 84 | -------------------------------------------------------------------------------- /src/env.c: -------------------------------------------------------------------------------- 1 | /* Environment manipulation module for the Lua/APR binding. 2 | * 3 | * Author: Peter Odding 4 | * Last Change: September 19, 2010 5 | * Homepage: http://peterodding.com/code/lua/apr/ 6 | * License: MIT 7 | * 8 | * Operating systems organize tasks into processes. One of the simplest means 9 | * of communication between processes is the use of environment variables. If 10 | * you're not familiar with environment variables, picture every process on 11 | * your computer having an associated Lua table with string key/value pairs. 12 | * When a process creates or overwrites a key/value pair only the table of that 13 | * process changes. When the process spawns a child process the child gets a 14 | * copy of the table which from that point onwards is no longer associated with 15 | * the parent process. 16 | */ 17 | 18 | #include "lua_apr.h" 19 | #include 20 | 21 | /* apr.env_get(name) -> value {{{1 22 | * 23 | * Get the value of the environment variable @name. On success the string value 24 | * is returned. If the variable does not exist nothing is returned. Otherwise a 25 | * nil followed by an error message is returned. 26 | */ 27 | 28 | int lua_apr_env_get(lua_State *L) 29 | { 30 | apr_pool_t *memory_pool; 31 | apr_status_t status; 32 | const char *name; 33 | char *value; 34 | 35 | memory_pool = to_pool(L); 36 | name = luaL_checkstring(L, 1); 37 | status = apr_env_get(&value, name, memory_pool); 38 | if (APR_STATUS_IS_ENOENT(status)) { 39 | return 0; 40 | } else if (status != APR_SUCCESS) { 41 | return push_error_status(L, status); 42 | } else { 43 | lua_pushstring(L, value); 44 | return 1; 45 | } 46 | } 47 | 48 | /* apr.env_set(name, value) -> status {{{1 49 | * 50 | * Set the value of the environment variable @name to the string @value. On 51 | * success true is returned, otherwise a nil followed by an error message is 52 | * returned. 53 | */ 54 | 55 | int lua_apr_env_set(lua_State *L) 56 | { 57 | const char *name, *value; 58 | apr_pool_t *memory_pool; 59 | apr_status_t status; 60 | 61 | memory_pool = to_pool(L); 62 | name = luaL_checkstring(L, 1); 63 | value = luaL_checkstring(L, 2); 64 | status = apr_env_set(name, value, memory_pool); 65 | 66 | return push_status(L, status); 67 | } 68 | 69 | /* apr.env_delete(name) -> status {{{1 70 | * 71 | * Delete the environment variable @name. On success true is returned, 72 | * otherwise a nil followed by an error message is returned. 73 | */ 74 | 75 | int lua_apr_env_delete(lua_State *L) 76 | { 77 | apr_pool_t *memory_pool; 78 | apr_status_t status; 79 | const char *name; 80 | 81 | memory_pool = to_pool(L); 82 | name = luaL_checkstring(L, 1); 83 | status = apr_env_delete(name, memory_pool); 84 | 85 | return push_status(L, status); 86 | } 87 | 88 | /* vim: set ts=2 sw=2 et tw=79 fen fdm=marker : */ 89 | -------------------------------------------------------------------------------- /src/fnmatch.c: -------------------------------------------------------------------------------- 1 | /* Filename matching module for the Lua/APR binding. 2 | * 3 | * Author: Peter Odding 4 | * Last Change: January 2, 2011 5 | * Homepage: http://peterodding.com/code/lua/apr/ 6 | * License: MIT 7 | */ 8 | 9 | #include "lua_apr.h" 10 | #include 11 | 12 | /* apr.fnmatch(pattern, input [, ignorecase]) -> status {{{1 13 | * 14 | * Try to match a string against a filename pattern. When the string matches 15 | * the pattern true is returned, otherwise false. The supported pattern items 16 | * are the following subset of shell wild cards: 17 | * 18 | * - `?` matches one character (any character) 19 | * - `*` matches zero or more characters (any character) 20 | * - `\x` escapes the special meaning of the character `x` 21 | * - `[set]` matches one character within set. A range of characters can be 22 | * specified by separating the characters of the range with a 23 | * - character 24 | * - `[^set]` matches the complement of set, where set is defined as above 25 | * 26 | * If the optional argument @ignorecase is true, characters are compared 27 | * case-insensitively. 28 | */ 29 | 30 | int lua_apr_fnmatch(lua_State *L) 31 | { 32 | apr_status_t status; 33 | const char *pattern, *input; 34 | int flags; 35 | 36 | pattern = luaL_checkstring(L, 1); 37 | input = luaL_checkstring(L, 2); 38 | flags = lua_toboolean(L, 3) ? APR_FNM_CASE_BLIND : 0; 39 | status = apr_fnmatch(pattern, input, flags); 40 | lua_pushboolean(L, APR_SUCCESS == status); 41 | 42 | return 1; 43 | } 44 | 45 | /* apr.fnmatch_test(pattern) -> status {{{1 46 | * 47 | * Determine if a file path @pattern contains one or more of the wild cards 48 | * that are supported by `apr.fnmatch()`. On success true is returned, 49 | * otherwise false. 50 | */ 51 | 52 | int lua_apr_fnmatch_test(lua_State *L) 53 | { 54 | const char *string; 55 | int pattern = 0; 56 | 57 | string = luaL_checkstring(L, 1); 58 | pattern = apr_fnmatch_test(string); 59 | lua_pushboolean(L, pattern); 60 | 61 | return 1; 62 | } 63 | 64 | /* vim: set ts=2 sw=2 et tw=79 fen fdm=marker : */ 65 | -------------------------------------------------------------------------------- /src/getopt.c: -------------------------------------------------------------------------------- 1 | /* Command argument parsing module for the Lua/APR binding. 2 | * 3 | * Author: Peter Odding 4 | * Last Change: February 25, 2011 5 | * Homepage: http://peterodding.com/code/lua/apr/ 6 | * License: MIT 7 | */ 8 | 9 | #include "lua_apr.h" 10 | #include 11 | 12 | int lua_apr_getopt(lua_State *L) 13 | { 14 | apr_status_t status; 15 | apr_pool_t *pool; 16 | apr_getopt_t *os; 17 | const char *s, **argv; 18 | apr_getopt_option_t *opts; 19 | int i, j, argc, optc, silent; 20 | 21 | /* XXX No data validation because only apr.lua calls us directly! */ 22 | 23 | silent = lua_toboolean(L, 3); 24 | lua_settop(L, 2); 25 | 26 | status = apr_pool_create(&pool, NULL); 27 | if (status != APR_SUCCESS) 28 | return push_error_status(L, status); 29 | 30 | /* Copy options to array of apr_getopt_option_t structures. */ 31 | optc = lua_objlen(L, 1); 32 | opts = apr_palloc(pool, sizeof opts[0] * (optc + 1)); 33 | for (i = 1; i <= optc; i++) { 34 | lua_rawgeti(L, 1, i); 35 | lua_getfield(L, -1, "optch"); 36 | s = lua_tostring(L, -1); 37 | opts[i - 1].optch = s && s[0] ? s[0] : 256; 38 | lua_pop(L, 1); 39 | lua_getfield(L, -1, "name"); 40 | opts[i - 1].name = lua_tostring(L, -1); 41 | lua_pop(L, 1); 42 | lua_getfield(L, -1, "has_arg"); 43 | opts[i - 1].has_arg = lua_toboolean(L, -1); 44 | lua_pop(L, 1); 45 | lua_getfield(L, -1, "description"); 46 | opts[i - 1].description = lua_tostring(L, -1); 47 | lua_pop(L, 2); 48 | } 49 | /* Terminate the options array. */ 50 | opts[optc].optch = 0; 51 | 52 | /* Copy arguments to array of strings. */ 53 | argc = lua_objlen(L, 2) + 1; 54 | argv = apr_palloc(pool, sizeof argv[0] * argc); 55 | for (i = 0; i <= argc; i++) { 56 | lua_rawgeti(L, 2, i); 57 | argv[i] = lua_tostring(L, -1); 58 | lua_pop(L, 1); 59 | } 60 | 61 | /* Initialize arguments for parsing by apr_getopt_long(). */ 62 | status = apr_getopt_init(&os, pool, argc, argv); 63 | if (status != APR_SUCCESS) 64 | goto fail; 65 | os->interleave = 1; 66 | if (silent) 67 | os->errfn = NULL; 68 | 69 | /* Parse options, save matched options in table #1. */ 70 | lua_createtable(L, 0, optc); 71 | for (;;) { 72 | char buffer[2] = { '\0', '\0' }; 73 | i = 256; 74 | s = NULL; 75 | status = apr_getopt_long(os, opts, &i, &s); 76 | if (status == APR_EOF) 77 | break; 78 | else if (status != APR_SUCCESS) 79 | goto fail; 80 | assert(i != 256); 81 | buffer[0] = i; 82 | /* Get existing value(s) for option. */ 83 | lua_getfield(L, -1, buffer); 84 | if (s == NULL) { 85 | /* Options without arguments are counted. */ 86 | if (!lua_isnumber(L, -1)) 87 | lua_pushinteger(L, 1); 88 | else 89 | lua_pushinteger(L, lua_tointeger(L, -1) + 1); 90 | lua_setfield(L, -3, buffer); 91 | lua_pop(L, 1); /* existing value */ 92 | } else if (lua_istable(L, -1)) { 93 | /* Add argument to table of existing values. */ 94 | push_string_or_true(L, s); 95 | lua_rawseti(L, -2, lua_objlen(L, -2) + 1); 96 | lua_pop(L, 1); /* existing value */ 97 | } else if (!lua_isnil(L, -1)) { 98 | /* Combine 1st and 2nd argument in table. */ 99 | lua_newtable(L); 100 | lua_insert(L, -2); 101 | lua_rawseti(L, -2, 1); 102 | push_string_or_true(L, s); 103 | lua_rawseti(L, -2, 2); 104 | lua_setfield(L, -2, buffer); 105 | } else { 106 | /* Set 1st argument value. */ 107 | lua_pop(L, 1); /* pop nil result */ 108 | push_string_or_true(L, s); 109 | lua_setfield(L, -2, buffer); 110 | } 111 | } 112 | 113 | /* Save matched arguments in table #2. */ 114 | lua_createtable(L, argc - os->ind, 0); 115 | for (i = 1, j = os->ind; j < argc; i++, j++) { 116 | lua_pushstring(L, os->argv[j]); 117 | lua_rawseti(L, -2, i); 118 | } 119 | 120 | /* Destroy the memory pool and return the two tables. */ 121 | apr_pool_destroy(pool); 122 | return 2; 123 | 124 | fail: 125 | apr_pool_destroy(pool); 126 | return push_error_status(L, status); 127 | } 128 | -------------------------------------------------------------------------------- /src/io_pipe.c: -------------------------------------------------------------------------------- 1 | /* Pipe I/O handling module for the Lua/APR binding. 2 | * 3 | * Author: Peter Odding 4 | * Last Change: February 13, 2011 5 | * Homepage: http://peterodding.com/code/lua/apr/ 6 | * License: MIT 7 | * 8 | * Lua/APR represents [pipes] [pipeline] as files just like Lua's standard 9 | * library function `io.popen()` does because it works fairly well, however 10 | * there are some differences between files and pipes which impact the API: 11 | * 12 | * - File objects implement `pipe:timeout_get()` and `pipe:timeout_set()` even 13 | * though these methods only make sense for pipe objects 14 | * 15 | * - Pipe objects implement `file:seek()` but don't support it 16 | * 17 | * One of the reasons that file/pipe support is so interwoven in APR and thus 18 | * Lua/APR is that you can create a named pipe with `apr.namedpipe_create()` 19 | * and access it using `apr.file_open()` and APR won't know or even care that 20 | * you're reading/writing a pipe instead of a file. 21 | * 22 | * [pipeline]: http://en.wikipedia.org/wiki/Pipeline_(Unix) 23 | */ 24 | 25 | #include "lua_apr.h" 26 | #include 27 | 28 | static int pipe_open(lua_State*, lua_apr_openpipe_f); 29 | 30 | /* apr.pipe_open_stdin() -> pipe {{{1 31 | * 32 | * Open standard input as a pipe. On success the pipe is returned, otherwise a 33 | * nil followed by an error message is returned. 34 | */ 35 | 36 | int lua_apr_pipe_open_stdin(lua_State *L) 37 | { 38 | return pipe_open(L, apr_file_open_stdin); 39 | } 40 | 41 | /* apr.pipe_open_stdout() -> pipe {{{1 42 | * 43 | * Open standard output as a pipe. On success the pipe is returned, otherwise a 44 | * nil followed by an error message is returned. 45 | */ 46 | 47 | int lua_apr_pipe_open_stdout(lua_State *L) 48 | { 49 | return pipe_open(L, apr_file_open_stdout); 50 | } 51 | 52 | /* apr.pipe_open_stderr() -> pipe {{{1 53 | * 54 | * Open standard error as a pipe. On success the pipe is returned, otherwise a 55 | * nil followed by an error message is returned. 56 | */ 57 | 58 | int lua_apr_pipe_open_stderr(lua_State *L) 59 | { 60 | return pipe_open(L, apr_file_open_stderr); 61 | } 62 | 63 | /* apr.namedpipe_create(name [, permissions]) -> status {{{1 64 | * 65 | * Create a [named pipe] [named_pipe]. On success true is returned, otherwise a 66 | * nil followed by an error message is returned. The @permissions argument is 67 | * documented elsewhere. 68 | * 69 | * Named pipes can be used for interprocess communication: 70 | * 71 | * 1. Check if the named pipe already exists, if it doesn't then create it 72 | * 2. Have each process access the named pipe using `apr.file_open()` 73 | * 3. Communicate between the two processes over the read/write ends of the 74 | * named pipe and close it when the communication is finished. 75 | * 76 | * Note that APR supports named pipes on UNIX but not on Windows. If you try 77 | * anyhow the error message "This function has not been implemented on this 78 | * platform" is returned. 79 | * 80 | * [named_pipe]: http://en.wikipedia.org/wiki/Named_pipe 81 | */ 82 | 83 | int lua_apr_namedpipe_create(lua_State *L) 84 | { 85 | apr_status_t status; 86 | apr_pool_t *pool; 87 | apr_fileperms_t permissions; 88 | const char *filename; 89 | 90 | pool = to_pool(L); 91 | filename = luaL_checkstring(L, 1); 92 | permissions = check_permissions(L, 2, 0); 93 | status = apr_file_namedpipe_create(filename, permissions, pool); 94 | return push_status(L, status); 95 | } 96 | 97 | /* apr.pipe_create() -> input, output {{{1 98 | * 99 | * Create an [anonymous pipe] [anon_pipe]. On success the write and read ends 100 | * of the pipe are returned, otherwise a nil followed by an error message is 101 | * returned. There's an example use of this function in the documentation for 102 | * `process:in_set()`. 103 | * 104 | * [anon_pipe]: http://en.wikipedia.org/wiki/Anonymous_pipe 105 | */ 106 | 107 | int lua_apr_pipe_create(lua_State *L) 108 | { 109 | apr_status_t status; 110 | lua_apr_file *input, *output; 111 | lua_apr_pool *refpool; 112 | 113 | /* XXX The apr_file_pipe_create() API enforces that both pipes are allocated 114 | * from the same memory pool. This means we need a reference counted memory 115 | * pool to avoid double free bugs on exit! 116 | */ 117 | refpool = refpool_alloc(L); 118 | input = file_alloc(L, NULL, refpool); 119 | output = file_alloc(L, NULL, refpool); 120 | status = apr_file_pipe_create(&input->handle, &output->handle, refpool->ptr); 121 | if (status != APR_SUCCESS) 122 | return push_error_status(L, status); 123 | init_file_buffers(L, input, 1); 124 | init_file_buffers(L, output, 1); 125 | return 2; 126 | } 127 | 128 | /* }}}1 */ 129 | 130 | int pipe_open(lua_State *L, lua_apr_openpipe_f open_std_pipe) 131 | { 132 | apr_status_t status; 133 | lua_apr_file *pipe; 134 | 135 | pipe = file_alloc(L, NULL, NULL); 136 | status = open_std_pipe(&pipe->handle, pipe->pool->ptr); 137 | if (status != APR_SUCCESS) 138 | return push_error_status(L, status); 139 | init_file_buffers(L, pipe, 1); 140 | return 1; 141 | } 142 | 143 | /* vim: set ts=2 sw=2 et tw=79 fen fdm=marker : */ 144 | -------------------------------------------------------------------------------- /src/memory_pool.c: -------------------------------------------------------------------------------- 1 | /* Global memory pool handling for the Lua/APR binding. 2 | * 3 | * Author: Peter Odding 4 | * Last Change: November 28, 2011 5 | * Homepage: http://peterodding.com/code/lua/apr/ 6 | * License: MIT 7 | * 8 | * Most functions in APR and APR-util require a memory pool argument to perform 9 | * memory allocation. Object methods in Lua/APR generally use the memory pool 10 | * associated with the object (such memory pools are destroyed when the object 11 | * userdata is garbage collected). For standalone functions Lua/APR uses a 12 | * global memory pool stored in the registry of the Lua state. This source file 13 | * implements the global memory pool. 14 | */ 15 | 16 | #include "lua_apr.h" 17 | 18 | typedef struct { 19 | apr_pool_t *pool; 20 | int managed; /* should be cleared/destroyed by Lua/APR? */ 21 | } global_pool; 22 | 23 | static apr_pool_t *pool_register(lua_State*, apr_pool_t*, int); 24 | static int pool_gc(lua_State*); 25 | 26 | /* to_pool() - Get the global memory pool (creating or clearing it). {{{1 */ 27 | 28 | apr_pool_t *to_pool(lua_State *L) 29 | { 30 | global_pool *reference; 31 | apr_pool_t *memory_pool; 32 | apr_status_t status; 33 | 34 | luaL_checkstack(L, 1, "not enough stack space to get memory pool"); 35 | lua_getfield(L, LUA_REGISTRYINDEX, LUA_APR_POOL_KEY); 36 | if (!lua_isuserdata(L, -1)) { 37 | /* Pop the nil value from lua_getfield(). */ 38 | lua_pop(L, 1); 39 | /* Create the memory pool itself. */ 40 | status = apr_pool_create(&memory_pool, NULL); 41 | if (status != APR_SUCCESS) 42 | raise_error_status(L, status); 43 | /* Create a reference to the memory pool in the Lua state. */ 44 | (void) pool_register(L, memory_pool, 1); 45 | } else { 46 | /* Return the previously created global memory pool. */ 47 | reference = lua_touserdata(L, -1); 48 | memory_pool = reference->pool; 49 | /* Clear it when we're allowed to do so. */ 50 | if (reference->managed) 51 | apr_pool_clear(memory_pool); 52 | lua_pop(L, 1); 53 | } 54 | 55 | return memory_pool; 56 | } 57 | 58 | /* pool_register() - Initialize or replace the global memory pool. {{{1 */ 59 | 60 | apr_pool_t *pool_register(lua_State *L, apr_pool_t *new_pool, int managed) 61 | { 62 | apr_pool_t *old_pool = NULL; 63 | global_pool *reference; 64 | 65 | /* Get the old global memory pool (if any). */ 66 | lua_getfield(L, LUA_REGISTRYINDEX, LUA_APR_POOL_KEY); 67 | if (lua_isuserdata(L, -1)) { 68 | reference = lua_touserdata(L, -1); 69 | old_pool = reference->pool; 70 | /* We don't need this userdata on the stack. */ 71 | lua_pop(L, 1); 72 | } else { 73 | /* Pop the nil value from lua_getfield(). */ 74 | lua_pop(L, 1); 75 | /* Create the reference to the global memory pool. */ 76 | reference = lua_newuserdata(L, sizeof *reference); 77 | reference->managed = managed; 78 | /* Create and install a metatable for garbage collection. */ 79 | if (luaL_newmetatable(L, LUA_APR_POOL_MT)) { 80 | /* The metatable has not yet been initialized. */ 81 | lua_pushcfunction(L, pool_gc); 82 | lua_setfield(L, -2, "__gc"); 83 | } 84 | lua_setmetatable(L, -2); 85 | /* Add the reference tot the Lua registry (popping it from the stack). */ 86 | lua_setfield(L, LUA_REGISTRYINDEX, LUA_APR_POOL_KEY); 87 | } 88 | 89 | /* Should we change the memory pool? */ 90 | if (new_pool != old_pool) { 91 | /* Just in case lua_apr_pool_register() is ever called after the global 92 | * memory pool has already been created. */ 93 | if (reference->managed && old_pool != NULL) { 94 | /* Destroy the old _managed_ memory pool. */ 95 | apr_pool_destroy(old_pool); 96 | /* Don't return it to the caller though. */ 97 | old_pool = NULL; 98 | } 99 | /* Set the reference to the new pool. */ 100 | reference->pool = new_pool; 101 | } 102 | 103 | /* Always set the managed field. */ 104 | reference->managed = managed; 105 | 106 | /* Return the old memory pool (if any) in case 107 | * the caller created it and wants it back :-) */ 108 | return old_pool; 109 | } 110 | 111 | /* pool_gc() - Destroy the global memory pool automatically. {{{1 */ 112 | 113 | int pool_gc(lua_State *L) 114 | { 115 | global_pool *reference; 116 | 117 | reference = luaL_checkudata(L, 1, LUA_APR_POOL_MT); 118 | if (reference->managed) 119 | apr_pool_destroy(reference->pool); 120 | 121 | return 0; 122 | } 123 | 124 | /* lua_apr_pool_register() - Enable others to replace the global memory pool (e.g. mod_lua). {{{1 */ 125 | 126 | LUA_APR_EXPORT apr_pool_t *lua_apr_pool_register(lua_State *L, apr_pool_t *new_pool) 127 | { 128 | return pool_register(L, new_pool, 0); 129 | } 130 | -------------------------------------------------------------------------------- /src/permissions.c: -------------------------------------------------------------------------------- 1 | /* File system permissions module for the Lua/APR binding. 2 | * 3 | * Author: Peter Odding 4 | * Last Change: December 28, 2010 5 | * Homepage: http://peterodding.com/code/lua/apr/ 6 | * License: MIT 7 | * 8 | * The Apache Portable Runtime represents file system permissions somewhat 9 | * similar to those of [UNIX] [unix]. There are three categories of 10 | * permissions: the user, the group and everyone else (the world). Each 11 | * category supports read and write permission bits while the meaning of the 12 | * third permission bit differs between categories. 13 | * 14 | * [unix]: http://en.wikipedia.org/wiki/Unix 15 | * 16 | * ### How Lua/APR presents permissions 17 | * 18 | * The Lua/APR binding uses a string of 9 characters to represent file system 19 | * permissions such as those returned by `apr.stat()`. Here's an example: 20 | * 21 | * > = apr.stat('.', 'protection') 22 | * 'rwxr-xr-x' 23 | * 24 | * This is the syntax of these permissions strings: 25 | * 26 | * - Character 1: `r` if the user has read permissions, `-` otherwise 27 | * - Character 2: `w` if the user has write permissions, `-` otherwise 28 | * - Character 3: `x` if the user has execute permissions, `S` if the user 29 | * has set user id permissions or `s` if the user has both permissions 30 | * - Characters 4..6: the same respective permissions for the group 31 | * - Characters 7..8: the same respective permissions for the world 32 | * - Character 9: `x` if the world has execute permissions, `T` if the world 33 | * has sticky permissions or `t` if the world has both permissions 34 | * 35 | * As an example, `rwxrwx---` means the user and group have full permissions 36 | * while the world has none. Another example: `r-xr-xr-x` means no-one has 37 | * write permissions. 38 | * 39 | * ### How you can request permissions 40 | * 41 | * When you need to request file system permissions for an operation like 42 | * [reading](#apr.file_open) or [copying](#apr.file_copy) a file there are two 43 | * string formats you can use. The first format is a string of nine characters 44 | * that lists each permission explicitly. This is the format documented above. 45 | * 46 | * The second format is very flexible and is one of the formats accepted by the 47 | * Linux command line program [chmod] [chmod]. The permissions are split in 48 | * three groups with a one-letter code: user is `u`, group is `g` and world is 49 | * `o` (for "others"). One or more permissions can then be assigned to one or 50 | * more of these groups. Here's an example that requests read permission for 51 | * user, group and others: `ugo=r`. Now when you also need write permission for 52 | * user and group, you can use `ugo=r,ug=w`. 53 | * 54 | * [chmod]: http://en.wikipedia.org/wiki/chmod 55 | */ 56 | 57 | #include "lua_apr.h" 58 | 59 | /* Character constants used to represent permission bits */ 60 | 61 | #define NONE '-' 62 | #define READ 'r' 63 | #define WRITE 'w' 64 | #define EXEC 'x' 65 | #define SETID 'S' 66 | #define STICKY 'T' 67 | #define EXEC_AND(S) (S + 32) 68 | 69 | /* Convert an APR bitfield to a Lua string */ 70 | 71 | int push_protection(lua_State *L, apr_fileperms_t perm) 72 | { 73 | char str[9], *p = str; 74 | 75 | #define UNPARSE(KIND, CHAR, MAGIC) \ 76 | *p++ = (perm & APR_FPROT_ ## KIND ## READ) ? 'r' : '-'; \ 77 | *p++ = (perm & APR_FPROT_ ## KIND ## WRITE) ? 'w' : '-'; \ 78 | if ((perm & APR_FPROT_ ## KIND ## EXECUTE) && \ 79 | (perm & APR_FPROT_ ## KIND ## MAGIC)) \ 80 | *p++ = CHAR; \ 81 | else if (perm & APR_FPROT_ ## KIND ## MAGIC) \ 82 | *p++ = (CHAR - 32); \ 83 | else if (perm & APR_FPROT_ ## KIND ## EXECUTE) \ 84 | *p++ = 'x'; \ 85 | else \ 86 | *p++ = '-' 87 | 88 | UNPARSE(U, 's', SETID); 89 | UNPARSE(G, 's', SETID); 90 | UNPARSE(W, 't', STICKY); 91 | 92 | #undef UNPARSE 93 | 94 | lua_pushlstring(L, str, sizeof str); 95 | return 1; 96 | } 97 | 98 | /* Convert a Lua value to an APR bitfield of filesystem permission flags */ 99 | 100 | apr_fileperms_t check_permissions(lua_State *L, int idx, int inherit) 101 | { 102 | enum { USER = 0x01, GROUP = 0x02, OTHER = 0x04 } whom; 103 | apr_fileperms_t output = 0; 104 | char *input; 105 | int loop; 106 | 107 | if (!lua_isstring(L, idx)) 108 | return (inherit && lua_toboolean(L, idx)) 109 | ? APR_FPROT_FILE_SOURCE_PERMS 110 | : APR_FPROT_OS_DEFAULT; 111 | 112 | /* Note: Though the "input" pointer is incremented, the Lua string 113 | * isn't modified. I just don't know to express this in C casts... */ 114 | input = (char *) lua_tostring(L, idx); 115 | 116 | # define MATCH(offset, special) ( \ 117 | (input[offset+0] == NONE || input[offset+0] == READ) && \ 118 | (input[offset+1] == NONE || input[offset+1] == WRITE) && \ 119 | (input[offset+2] == NONE || input[offset+2] == EXEC || \ 120 | input[offset+2] == special || input[offset+2] == EXEC_AND(special))) 121 | 122 | if (MATCH(0, SETID) && MATCH(3, SETID) && MATCH(6, STICKY)) { 123 | 124 | #undef MATCH 125 | 126 | # define PARSE(offset, special, class) \ 127 | if (input[offset+0] == READ) \ 128 | output |= APR_FPROT_ ## class ## READ; \ 129 | if (input[offset+1] == WRITE) \ 130 | output |= APR_FPROT_ ## class ## WRITE; \ 131 | if (input[offset+2] == EXEC_AND(special)) \ 132 | output |= APR_FPROT_ ## class ## EXECUTE | APR_FPROT_ ## class ## special; \ 133 | else if (input[offset+2] == special) \ 134 | output |= APR_FPROT_ ## class ## special; \ 135 | else if (input[offset+2] == EXEC) \ 136 | output |= APR_FPROT_ ## class ## EXECUTE 137 | 138 | PARSE(0, SETID, U); 139 | PARSE(3, SETID, G); 140 | PARSE(6, STICKY, W); 141 | 142 | #undef PARSE 143 | 144 | return output; 145 | } 146 | 147 | for (whom = 0;;) { 148 | switch (*input++) { 149 | case '\0': 150 | /* Lua API always \0 terminates. */ 151 | return output; 152 | case 'u': 153 | whom |= USER; 154 | break; 155 | case 'g': 156 | whom |= GROUP; 157 | break; 158 | case 'o': 159 | whom |= OTHER; 160 | break; 161 | default: 162 | whom |= USER | GROUP | OTHER; 163 | input--; 164 | /* fall through! */ 165 | case '=': 166 | for (loop = 1; loop;) { 167 | switch (*input++) { 168 | default: 169 | return output; 170 | case ',': 171 | loop = 0; 172 | whom = 0; 173 | break; 174 | case READ: 175 | if (whom & USER) output |= APR_FPROT_UREAD; 176 | if (whom & GROUP) output |= APR_FPROT_GREAD; 177 | if (whom & OTHER) output |= APR_FPROT_WREAD; 178 | break; 179 | case WRITE: 180 | if (whom & USER) output |= APR_FPROT_UWRITE; 181 | if (whom & GROUP) output |= APR_FPROT_GWRITE; 182 | if (whom & OTHER) output |= APR_FPROT_WWRITE; 183 | break; 184 | case EXEC: 185 | if (whom & USER) output |= APR_FPROT_UEXECUTE; 186 | if (whom & GROUP) output |= APR_FPROT_GEXECUTE; 187 | if (whom & OTHER) output |= APR_FPROT_WEXECUTE; 188 | break; 189 | case SETID: 190 | if (whom & USER) output |= APR_FPROT_USETID; 191 | if (whom & GROUP) output |= APR_FPROT_GSETID; 192 | break; 193 | case STICKY: 194 | if (whom & OTHER) output |= APR_FPROT_WSTICKY; 195 | break; 196 | } 197 | } 198 | } 199 | } 200 | } 201 | 202 | /* vim: set ts=2 sw=2 et tw=79 fen fdm=marker : */ 203 | -------------------------------------------------------------------------------- /src/serialize.c: -------------------------------------------------------------------------------- 1 | /* Serialization module for the Lua/APR binding. 2 | * 3 | * Author: Peter Odding 4 | * Last Change: November 20, 2011 5 | * Homepage: http://peterodding.com/code/lua/apr/ 6 | * License: MIT 7 | * 8 | * The Lua/APR binding contains a serialization function based on the [Metalua 9 | * table-to-source serializer] [metalua_serializer] extended to support 10 | * function upvalues and userdata objects created by the Lua/APR binding. The 11 | * following Lua values can be serialized: 12 | * 13 | * - strings, numbers, booleans, nil (scalars) 14 | * - Lua functions (including upvalues) 15 | * - userdata objects created by the Lua/APR binding 16 | * - tables thereof. There is no restriction on keys; recursive and shared 17 | * sub-tables are handled correctly. 18 | * 19 | * Restrictions: 20 | * 21 | * - *Metatables and environments aren't saved*; 22 | * this might or might not be what you want. 23 | * 24 | * - *If multiple functions share a scalar upvalue they will each get their 25 | * own copy.* Because it is impossible to join upvalues of multiple 26 | * functions in Lua 5.1 this won't be fixed any time soon. 27 | * 28 | * The following functions in the Lua/APR binding internally use serialization 29 | * to transfer Lua functions and other values between operating system threads: 30 | * 31 | * - `apr.thread()` and `thread:wait()` 32 | * - `queue:push()` and `queue:pop()` 33 | * - `queue:try_push()` and `queue:try_pop()` 34 | * 35 | * [metalua_serializer]: https://github.com/fab13n/metalua/blob/master/src/lib/serialize.lua 36 | */ 37 | 38 | /* TODO Verify that we're reference counting correctly... 39 | * TODO Locking around modifications of chain?! Bad experiences with mutexes :-\ 40 | */ 41 | 42 | #include "lua_apr.h" 43 | #include 44 | 45 | /* Internal stuff. {{{1 */ 46 | 47 | typedef struct reference reference; 48 | 49 | struct reference { 50 | char uuid[APR_UUID_FORMATTED_LENGTH + 1]; 51 | lua_apr_objtype *type; 52 | lua_apr_refobj *object; 53 | reference *next; 54 | }; 55 | 56 | static reference *chain = NULL; 57 | 58 | static void load_lua_apr(lua_State *L) 59 | { 60 | /* Make sure the Lua/APR binding is loaded. */ 61 | lua_getglobal(L, "require"); 62 | lua_pushliteral(L, "apr"); 63 | lua_call(L, 1, 1); 64 | /* require('apr') should raise on errors, but just as a sanity check: */ 65 | if (!lua_istable(L, -1)) 66 | raise_error_message(L, "Failed to load Lua/APR binding!"); 67 | } 68 | 69 | /* apr.ref(object) -> uuid {{{1 70 | * 71 | * Prepare the Lua/APR userdata @object so that it can be referenced from 72 | * another Lua state in the same operating system process and associate a 73 | * [UUID] [uuid] with the object. The UUID is returned as a string. When you 74 | * pass this UUID to `apr.deref()` you'll get the same object back. This only 75 | * works once, but of course you're free to generate another UUID for the same 76 | * object. 77 | * 78 | * [uuid]: http://en.wikipedia.org/wiki/Universally_unique_identifier 79 | */ 80 | 81 | int lua_apr_ref(lua_State *L) 82 | { 83 | lua_apr_objtype *type = NULL; 84 | reference *node = NULL; 85 | apr_uuid_t uuid; 86 | int i; 87 | 88 | /* Make sure we're dealing with a userdata object. */ 89 | luaL_checktype(L, 1, LUA_TUSERDATA); 90 | 91 | /* Make sure the userdata has one of the supported types. */ 92 | for (i = 0; lua_apr_types[i] != NULL; i++) 93 | if (object_has_type(L, 1, lua_apr_types[i], 1)) { 94 | type = lua_apr_types[i]; 95 | break; 96 | } 97 | luaL_argcheck(L, type != NULL, 1, "userdata cannot be referenced"); 98 | 99 | /* Prepare to insert object in chain of references. */ 100 | node = calloc(1, sizeof(reference)); 101 | if (node == NULL) 102 | raise_error_memory(L); 103 | node->object = prepare_reference(type, lua_touserdata(L, 1)); 104 | if (node->object == NULL) { 105 | free(node); 106 | raise_error_memory(L); 107 | } 108 | node->type = type; 109 | apr_uuid_get(&uuid); 110 | apr_uuid_format(node->uuid, &uuid); 111 | 112 | /* Increase the reference count of the object because it is now being 113 | * referenced from the Lua state and the chain of references. */ 114 | object_incref(node->object); 115 | 116 | /* Insert the object into the chain of references. */ 117 | node->next = chain; 118 | chain = node; 119 | 120 | /* Return newly associated UUID for object. */ 121 | lua_pushlstring(L, node->uuid, APR_UUID_FORMATTED_LENGTH); 122 | return 1; 123 | } 124 | 125 | /* apr.deref(uuid) -> object {{{1 126 | * 127 | * Convert a UUID that was previously returned by `apr.ref()` into a userdata 128 | * object and return the object. You can only dereference a UUID once, but of 129 | * course you're free to generate another UUID for the same object. 130 | */ 131 | 132 | int lua_apr_deref(lua_State *L) 133 | { 134 | const char *uuid; 135 | reference *node, *last = NULL; 136 | 137 | uuid = luaL_checkstring(L, 1); 138 | 139 | /* Look for the UUID in the chain of object references. */ 140 | node = chain; 141 | while (node != NULL) { 142 | if (node->object != NULL && strcmp(uuid, node->uuid) == 0) { 143 | /* Return an object that references the real object in unmanaged memory. */ 144 | create_reference(L, node->type, node->object); 145 | /* Invalidate the UUID. */ 146 | if (node == chain) 147 | chain = node->next; 148 | else 149 | last->next = node->next; 150 | free(node); 151 | return 1; 152 | } 153 | last = node; 154 | node = node->next; 155 | } 156 | 157 | luaL_argerror(L, 1, "userdata has not been referenced"); 158 | 159 | /* Make the compiler happy. */ 160 | return 0; 161 | } 162 | 163 | /* lua_apr_serialize() - serialize values from "idx" to stack top (pops 0..n values, pushes string) {{{1 */ 164 | 165 | int lua_apr_serialize(lua_State *L, int idx) 166 | { 167 | int num_args = lua_gettop(L) - idx + 1; /* calculate number of arguments */ 168 | load_lua_apr(L); /* load Lua/APR binding */ 169 | lua_getfield(L, -1, "serialize"); /* get apr.serialize() function */ 170 | if (!lua_isfunction(L, -1)) /* make sure we found it */ 171 | raise_error_message(L, "Failed to load apr.serialize() function!"); 172 | lua_insert(L, idx); /* move function before arguments */ 173 | lua_pop(L, 1); /* remove "apr" table from stack */ 174 | lua_call(L, num_args, 1); /* call function (propagating errors upwards) */ 175 | if (!lua_isstring(L, -1)) /* apr.serialize() should raise on errors, but just as a sanity check: */ 176 | raise_error_message(L, "Failed to serialize value(s) using apr.serialize()"); 177 | return 1; /* leave result string on top of stack */ 178 | } 179 | 180 | /* lua_apr_unserialize() - unserialize string at top of stack (pops string, pushes 0..n values). {{{1 */ 181 | 182 | int lua_apr_unserialize(lua_State *L) 183 | { 184 | int idx = lua_gettop(L); /* remember input string stack index */ 185 | load_lua_apr(L); /* load Lua/APR binding */ 186 | lua_getfield(L, -1, "unserialize"); /* get apr.unserialize() function */ 187 | if (!lua_isfunction(L, -1)) /* make sure we found it */ 188 | raise_error_message(L, "Failed to load apr.unserialize() function!"); 189 | lua_insert(L, idx); /* move function before input string */ 190 | lua_pop(L, 1); /* pop "apr" table (now at top of stack) */ 191 | lua_call(L, 1, LUA_MULTRET); /* call function with string argument (propagating errors upwards) */ 192 | return lua_gettop(L) - idx; /* return 0..n unserialized values */ 193 | } 194 | 195 | /* vim: set ts=2 sw=2 et tw=79 fen fdm=marker : */ 196 | -------------------------------------------------------------------------------- /src/stat.c: -------------------------------------------------------------------------------- 1 | /* File status querying (stat()) for the Lua/APR binding. 2 | * 3 | * Author: Peter Odding 4 | * Last Change: December 28, 2010 5 | * Homepage: http://peterodding.com/code/lua/apr/ 6 | * License: MIT 7 | */ 8 | 9 | #include "lua_apr.h" 10 | #include 11 | #include 12 | 13 | /* Lua/APR introduces one new field to the apr_stat() interface, which is the 14 | * filepath. It's defined to 0 so it won't influence the .wanted bitfield :) */ 15 | 16 | #define APR_FINFO_PATH 0 17 | 18 | static const char *const options[] = { 19 | "atime", "csize", "ctime", "dev", "group", "inode", "link", "mtime", 20 | "name", "nlink", "path", "size", "type", "protection", "user", NULL 21 | }; 22 | 23 | static const apr_int32_t flags[] = { 24 | APR_FINFO_ATIME, APR_FINFO_CSIZE, APR_FINFO_CTIME, APR_FINFO_DEV, 25 | APR_FINFO_GROUP, APR_FINFO_INODE, APR_FINFO_LINK, APR_FINFO_MTIME, 26 | APR_FINFO_NAME, APR_FINFO_NLINK, APR_FINFO_PATH, APR_FINFO_SIZE, 27 | APR_FINFO_TYPE, APR_FINFO_UPROT | APR_FINFO_GPROT | APR_FINFO_WPROT, 28 | APR_FINFO_USER 29 | }; 30 | 31 | void push_stat_field(lua_State*, apr_finfo_t*, apr_int32_t, const char*); 32 | 33 | void check_stat_request(lua_State *L, lua_apr_stat_context *ctx) 34 | { 35 | apr_int32_t flag; 36 | int i; 37 | 38 | ctx->count = 0; 39 | ctx->wanted = 0; 40 | 41 | for (i = ctx->firstarg; i - ctx->firstarg <= count(flags) && !lua_isnoneornil(L, i) && i <= ctx->lastarg; i++) { 42 | ctx->wanted |= flag = flags[luaL_checkoption(L, i, NULL, options)]; 43 | if (APR_FINFO_LINK != flag) 44 | ctx->fields[ctx->count++] = flag; 45 | } 46 | 47 | if (ctx->count == 0) 48 | for (i = 0; i < count(flags); ++i) 49 | ctx->wanted |= flags[i]; 50 | } 51 | 52 | int push_stat_results(lua_State *L, lua_apr_stat_context *ctx, const char *path) 53 | { 54 | int i; 55 | 56 | if (0 == ctx->count) { 57 | lua_createtable(L, 0, ctx->count); 58 | for (i = 0; i < count(flags); i++) { 59 | if (APR_FINFO_LINK != flags[i] && (ctx->info.valid & flags[i]) == flags[i]) { 60 | lua_pushstring(L, options[i]); 61 | push_stat_field(L, &ctx->info, flags[i], path); 62 | lua_rawset(L, -3); 63 | } 64 | } 65 | return 1; 66 | } else { 67 | for (i = 0; i < ctx->count; i++) { 68 | if ((ctx->info.valid & ctx->fields[i]) == ctx->fields[i]) { 69 | push_stat_field(L, &ctx->info, ctx->fields[i], path); 70 | } else { 71 | lua_pushboolean(L, 0); 72 | } 73 | } 74 | return ctx->count; 75 | } 76 | } 77 | 78 | void push_stat_field(lua_State *L, apr_finfo_t *info, apr_int32_t which, const char *path) 79 | { 80 | switch (which) { 81 | 82 | /* user/group name */ 83 | case APR_FINFO_USER: 84 | if (!push_username(L, info->pool, info->user)) 85 | lua_pushnil(L); 86 | break; 87 | case APR_FINFO_GROUP: 88 | push_groupname(L, info->pool, info->group); 89 | break; 90 | 91 | /* dates */ 92 | case APR_FINFO_CTIME: 93 | time_push(L, info->ctime); 94 | break; 95 | case APR_FINFO_MTIME: 96 | time_push(L, info->mtime); 97 | break; 98 | case APR_FINFO_ATIME: 99 | time_push(L, info->atime); 100 | break; 101 | 102 | /* numbers */ 103 | case APR_FINFO_SIZE: 104 | lua_pushinteger(L, (lua_Integer) info->size); 105 | break; 106 | case APR_FINFO_CSIZE: 107 | lua_pushinteger(L, (lua_Integer) info->csize); 108 | break; 109 | case APR_FINFO_INODE: 110 | lua_pushinteger(L, (lua_Integer) info->inode); 111 | break; 112 | case APR_FINFO_NLINK: 113 | lua_pushinteger(L, (lua_Integer) info->nlink); 114 | break; 115 | case APR_FINFO_DEV: 116 | lua_pushinteger(L, (lua_Integer) info->device); 117 | break; 118 | 119 | /* file path / name */ 120 | case APR_FINFO_PATH: 121 | if (path && 0 != strcmp(path, ".")) { 122 | char *filepath; 123 | apr_status_t status; 124 | status = apr_filepath_merge(&filepath, path, info->name, 0, info->pool); 125 | if (APR_SUCCESS == status) { 126 | lua_pushstring(L, filepath); 127 | break; 128 | } 129 | } 130 | 131 | /* fall through */ 132 | case APR_FINFO_NAME: 133 | lua_pushstring(L, info->name); 134 | break; 135 | 136 | /* type */ 137 | case APR_FINFO_TYPE: 138 | switch (info->filetype) { 139 | case APR_LNK: 140 | lua_pushliteral(L, "link"); 141 | break; 142 | case APR_REG: 143 | lua_pushliteral(L, "file"); 144 | break; 145 | case APR_PIPE: 146 | lua_pushliteral(L, "pipe"); 147 | break; 148 | case APR_SOCK: 149 | lua_pushliteral(L, "socket"); 150 | break; 151 | case APR_DIR: 152 | lua_pushliteral(L, "directory"); 153 | break; 154 | case APR_BLK: 155 | lua_pushliteral(L, "block device"); 156 | break; 157 | case APR_CHR: 158 | lua_pushliteral(L, "character device"); 159 | break; 160 | default: 161 | lua_pushliteral(L, "unknown"); 162 | break; 163 | } 164 | break; 165 | 166 | /* protection tables */ 167 | case APR_FINFO_UPROT | APR_FINFO_GPROT | APR_FINFO_WPROT: 168 | push_protection(L, info->protection); 169 | break; 170 | 171 | /* Error in Lua/APR :( */ 172 | default: 173 | assert(0); 174 | lua_pushnil(L); 175 | } 176 | } 177 | 178 | /* vim: set ts=2 sw=2 et tw=79 fen fdm=marker : */ 179 | -------------------------------------------------------------------------------- /src/str.c: -------------------------------------------------------------------------------- 1 | /* String routines module for the Lua/APR binding. 2 | * 3 | * Author: Peter Odding 4 | * Last Change: January 3, 2011 5 | * Homepage: http://peterodding.com/code/lua/apr/ 6 | * License: MIT 7 | */ 8 | 9 | #include "lua_apr.h" 10 | #include 11 | 12 | /* apr.strnatcmp(left, right) -> status {{{1 13 | * 14 | * Do a [natural order comparison] [natsort] of two strings. Returns true when 15 | * the @left string is less than the @right string, false otherwise. This 16 | * function can be used as a callback for Lua's standard library function 17 | * `table.sort()`. 18 | * 19 | * > -- the canonical example: 20 | * > list = { 'rfc1.txt', 'rfc2086.txt', 'rfc822.txt' } 21 | * > -- collate order: 22 | * > table.sort(list) 23 | * > for _, name in ipairs(list) do print(name) end 24 | * rfc1.txt 25 | * rfc2086.txt 26 | * rfc822.txt 27 | * > -- natural order: 28 | * > table.sort(list, apr.strnatcmp) 29 | * > for _, name in ipairs(list) do print(name) end 30 | * rfc1.txt 31 | * rfc822.txt 32 | * rfc2086.txt 33 | * 34 | * [natsort]: http://sourcefrog.net/projects/natsort/ 35 | */ 36 | 37 | int lua_apr_strnatcmp(lua_State *L) 38 | { 39 | const char *a, *b; 40 | int difference; 41 | 42 | a = luaL_checkstring(L, 1); 43 | b = luaL_checkstring(L, 2); 44 | difference = apr_strnatcmp(a, b); 45 | lua_pushboolean(L, difference < 0); 46 | 47 | return 1; 48 | } 49 | 50 | /* apr.strnatcasecmp(left, right) -> status {{{1 51 | * 52 | * Like `apr.strnatcmp()`, but ignores the case of the strings. 53 | */ 54 | 55 | int lua_apr_strnatcasecmp(lua_State *L) 56 | { 57 | const char *a, *b; 58 | int difference; 59 | 60 | a = luaL_checkstring(L, 1); 61 | b = luaL_checkstring(L, 2); 62 | difference = apr_strnatcasecmp(a, b); 63 | lua_pushboolean(L, difference < 0); 64 | 65 | return 1; 66 | } 67 | 68 | /* apr.strfsize(number [, padding]) -> readable {{{1 69 | * 70 | * Format a binary size positive @number to a compacted human readable string. 71 | * If the optional @padding argument evaluates to true the resulting string 72 | * will be padded with spaces to make it four characters wide, otherwise no 73 | * padding will be applied. 74 | * 75 | * > = apr.strfsize(1024) 76 | * '1.0K' 77 | * > = apr.strfsize(1024 ^ 2) 78 | * '1.0M' 79 | * > = apr.strfsize(1024 ^ 3) 80 | * '1.0G' 81 | * 82 | * Here's a simplified implementation of the [UNIX] [unix] command `ls -l 83 | * --human-readable` which makes use of the @padding argument to nicely line up 84 | * the fields following the size: 85 | * 86 | * function ls(dirpath) 87 | * local directory = assert(apr.dir_open(dirpath)) 88 | * for info in directory:entries() do 89 | * io.write(info.protection, ' ') 90 | * io.write(info.user, ' ') 91 | * io.write(info.group, ' ') 92 | * io.write(apr.strfsize(info.size, true), ' ') 93 | * io.write(apr.time_format('%Y-%m-%d %H:%I', info.ctime), ' ') 94 | * io.write(info.name, '\n') 95 | * end 96 | * assert(directory:close()) 97 | * end 98 | * 99 | * This is what the result looks like for the [source code directory] [srcdir] 100 | * of the Lua/APR project: 101 | * 102 | * > ls 'lua-apr/src' 103 | * rw-r--r-- peter peter 5.4K 2011-01-02 22:10 apr.lua 104 | * rw-r--r-- peter peter 4.7K 2011-01-02 06:06 base64.c 105 | * rw-r--r-- peter peter 11K 2010-10-27 13:01 buffer.c 106 | * rw-r--r-- peter peter 13K 2011-01-02 21:09 crypt.c 107 | * rw-r--r-- peter peter 2.8K 2010-12-31 01:01 date.c 108 | * rw-r--r-- peter peter 9.4K 2011-01-01 16:04 dbm.c 109 | * rw-r--r-- peter peter 2.5K 2010-09-25 23:11 env.c 110 | * rw-r--r-- peter peter 17K 2011-01-02 22:10 errno.c 111 | * rw-r--r-- peter peter 10K 2011-01-02 22:10 filepath.c 112 | * rw-r--r-- peter peter 1.9K 2011-01-02 04:04 fnmatch.c 113 | * rw-r--r-- peter peter 12K 2010-12-31 01:01 io_dir.c 114 | * rw-r--r-- peter peter 25K 2011-01-02 04:04 io_file.c 115 | * rw-r--r-- peter peter 17K 2010-12-31 01:01 io_net.c 116 | * rw-r--r-- peter peter 4.6K 2011-01-02 22:10 io_pipe.c 117 | * rw-r--r-- peter peter 11K 2011-01-02 11:11 lua_apr.c 118 | * rw-r--r-- peter peter 9.0K 2011-01-02 11:11 lua_apr.h 119 | * rw-r--r-- peter peter 6.9K 2010-12-29 14:02 permissions.c 120 | * rw-r--r-- peter peter 26K 2011-01-02 22:10 proc.c 121 | * rw-r--r-- peter peter 866 2010-10-23 00:12 refpool.c 122 | * rw-r--r-- peter peter 4.8K 2010-12-29 14:02 stat.c 123 | * rw-r--r-- peter peter 3.5K 2011-01-02 22:10 str.c 124 | * rw-r--r-- peter peter 9.8K 2010-12-31 01:01 time.c 125 | * rw-r--r-- peter peter 4.7K 2010-09-25 23:11 uri.c 126 | * rw-r--r-- peter peter 2.5K 2010-09-25 23:11 user.c 127 | * rw-r--r-- peter peter 2.9K 2010-10-22 19:07 uuid.c 128 | * rw-r--r-- peter peter 3.8K 2011-01-02 04:04 xlate.c 129 | * 130 | * Note: It seems that `apr.strfsize()` doesn't support terabyte range sizes. 131 | * 132 | * [srcdir]: https://github.com/xolox/lua-apr/tree/master/src 133 | */ 134 | 135 | int lua_apr_strfsize(lua_State *L) 136 | { 137 | apr_off_t number; 138 | char buffer[5]; 139 | int padding, offset = 0, length = 4; 140 | 141 | number = (apr_off_t) luaL_checklong(L, 1); 142 | padding = lua_gettop(L) > 1 && lua_toboolean(L, 2); 143 | luaL_argcheck(L, number >= 0, 1, "must be non-negative"); 144 | apr_strfsize(number, buffer); 145 | while (!padding && buffer[offset] == ' ') offset++; 146 | while (!padding && buffer[length-1] == ' ') length--; 147 | lua_pushlstring(L, &buffer[offset], length - offset); 148 | 149 | return 1; 150 | } 151 | 152 | /* apr.tokenize_to_argv(cmdline) -> arguments {{{1 153 | * 154 | * Convert the string @cmdline to a table of arguments. On success the table of 155 | * arguments is returned, otherwise a nil followed by an error message is 156 | * returned. 157 | */ 158 | 159 | int lua_apr_tokenize_to_argv(lua_State *L) 160 | { 161 | apr_status_t status; 162 | apr_pool_t *pool; 163 | const char *str; 164 | char **argv; 165 | int i; 166 | 167 | pool = to_pool(L); 168 | str = luaL_checkstring(L, 1); 169 | status = apr_tokenize_to_argv(str, &argv, pool); 170 | 171 | if (APR_SUCCESS != status) 172 | return push_error_status(L, status); 173 | 174 | lua_newtable(L); 175 | for (i = 0; NULL != argv[i]; i++) { 176 | lua_pushstring(L, argv[i]); 177 | lua_rawseti(L, -2, i + 1); 178 | } 179 | 180 | return 1; 181 | } 182 | 183 | /* vim: set ts=2 sw=2 et tw=79 fen fdm=marker : */ 184 | -------------------------------------------------------------------------------- /src/uri.c: -------------------------------------------------------------------------------- 1 | /* Uniform resource identifier parsing module for the Lua/APR binding. 2 | * 3 | * Author: Peter Odding 4 | * Last Change: September 25, 2010 5 | * Homepage: http://peterodding.com/code/lua/apr/ 6 | * License: MIT 7 | */ 8 | 9 | #include "lua_apr.h" 10 | #include 11 | #include 12 | 13 | const static struct { 14 | const char *name; 15 | const int offset; 16 | } fields[] = { 17 | { "scheme", offsetof(apr_uri_t, scheme ) }, 18 | { "hostinfo", offsetof(apr_uri_t, hostinfo) }, 19 | { "user", offsetof(apr_uri_t, user ) }, 20 | { "password", offsetof(apr_uri_t, password) }, 21 | { "hostname", offsetof(apr_uri_t, hostname) }, 22 | { "port", offsetof(apr_uri_t, port_str) }, 23 | { "path", offsetof(apr_uri_t, path ) }, 24 | { "query", offsetof(apr_uri_t, query ) }, 25 | { "fragment", offsetof(apr_uri_t, fragment) } 26 | }; 27 | 28 | /* apr.uri_parse(uri) -> components {{{1 29 | * 30 | * Parse the [Uniform Resource Identifier] [uri] @uri. On success a table of 31 | * components is returned, otherwise a nil followed by an error message is 32 | * returned. The table of components can have the following fields, all 33 | * strings: 34 | * 35 | * - `scheme` is the part of the URI before `://` (as in `http`, `ftp`, etc.) 36 | * - `user` is the user name, as in `scheme://user:pass@host:port/` 37 | * - `password` is the password, as in `scheme://user:pass@host:port/` 38 | * - `hostinfo` is the combined `[user[:password]@]hostname[:port]` 39 | * - `hostname` is the host name or IP address 40 | * - `port` is the port number 41 | * - `path` is the request path (`/` if only `scheme://hostname` was given) 42 | * - `query` is everything after a `?` in the path, if present 43 | * - `fragment` is the trailing `#fragment` string, if present 44 | * 45 | * [uri]: http://en.wikipedia.org/wiki/Uniform_Resource_Identifier 46 | */ 47 | 48 | int lua_apr_uri_parse(lua_State *L) 49 | { 50 | apr_status_t status; 51 | apr_pool_t *memory_pool; 52 | apr_uri_t components = { NULL }; 53 | const char *string; 54 | int i; 55 | 56 | memory_pool = to_pool(L); 57 | string = luaL_checkstring(L, 1); 58 | status = apr_uri_parse(memory_pool, string, &components); 59 | if (status != APR_SUCCESS) 60 | return push_error_status(L, status); 61 | 62 | lua_newtable(L); 63 | for (i = 0; i < count(fields); i++) { 64 | char *field = *(char **) ((char *) &components + fields[i].offset); 65 | if (field && *field) { 66 | lua_pushstring(L, fields[i].name); 67 | lua_pushstring(L, field); 68 | lua_rawset(L, -3); 69 | } 70 | } 71 | return 1; 72 | } 73 | 74 | /* apr.uri_unparse(components [, option]) -> uri {{{1 75 | * 76 | * Convert a table of [URI] [uri] @components into a URI string. On success the 77 | * URI string is returned, otherwise a nil followed by an error message is 78 | * returned. The list of fields in the @components table is available in the 79 | * documentation for `apr.uri_parse()`. The argument @option may be one of the 80 | * following: 81 | * 82 | * - `hostinfo` to unparse the components `[user[:password]@]hostname[:port]` 83 | * - `pathinfo` to unparse the components `path[?query[#fragment]]` 84 | */ 85 | 86 | int lua_apr_uri_unparse(lua_State *L) 87 | { 88 | const char *options[] = { "hostinfo", "pathinfo", "default" }; 89 | const int values[] = { 90 | APR_URI_UNP_OMITPATHINFO | APR_URI_UNP_REVEALPASSWORD, 91 | APR_URI_UNP_OMITSITEPART, 92 | APR_URI_UNP_REVEALPASSWORD 93 | }; 94 | 95 | apr_uri_t components = { 0 }; 96 | apr_pool_t *memory_pool; 97 | int i, flags = 0; 98 | 99 | memory_pool = to_pool(L); 100 | luaL_checktype(L, 1, LUA_TTABLE); 101 | flags = values[luaL_checkoption(L, 2, "default", options)]; 102 | 103 | for (i = 0; i < count(fields); i++) { 104 | lua_getfield(L, 1, fields[i].name); 105 | if (lua_isstring(L, -1)) { 106 | char **field = (char **)((char *) &components + fields[i].offset); 107 | *field = apr_pstrdup(memory_pool, lua_tostring(L, -1)); 108 | } 109 | lua_pop(L, 1); 110 | } 111 | 112 | /* .port_str and .port must both be set, otherwise :port 113 | * will not be included in the unparsed components! (I think) */ 114 | if (components.port_str) 115 | components.port = (apr_port_t) atoi(components.port_str); 116 | 117 | lua_pushstring(L, apr_uri_unparse(memory_pool, &components, flags)); 118 | return 1; 119 | } 120 | 121 | /* apr.uri_port_of_scheme(scheme) -> port {{{1 122 | * 123 | * Return the default port for the given [URI] [uri] @scheme string. [Since at 124 | * least APR 1.2.8] [uri_ports] the following schemes are supported: `acap`, 125 | * `ftp`, `gopher`, `http`, `https`, `imap`, `ldap`, `nfs`, `nntp`, `pop`, 126 | * `prospero`, `rtsp`, `sip`, `snews`, `ssh`, `telnet`, `tip`, `wais`, 127 | * `z39.50r` and `z39.50s`. 128 | * 129 | * [uri_ports]: http://svn.apache.org/viewvc/apr/apr/trunk/uri/apr_uri.c?view=markup#l43 130 | */ 131 | 132 | int lua_apr_uri_port_of_scheme(lua_State *L) 133 | { 134 | const char *scheme; 135 | int port; 136 | 137 | scheme = luaL_checkstring(L, 1); 138 | port = apr_uri_port_of_scheme(scheme); 139 | if (0 == port) 140 | return 0; 141 | lua_pushnumber(L, port); 142 | return 1; 143 | } 144 | 145 | /* vim: set ts=2 sw=2 et tw=79 fen fdm=marker : */ 146 | -------------------------------------------------------------------------------- /src/user.c: -------------------------------------------------------------------------------- 1 | /* User/group identification module for the Lua/APR binding. 2 | * 3 | * Author: Peter Odding 4 | * Last Change: September 25, 2010 5 | * Homepage: http://peterodding.com/code/lua/apr/ 6 | * License: MIT 7 | */ 8 | 9 | #include "lua_apr.h" 10 | #include 11 | 12 | /* apr.user_get() -> username, groupname {{{1 13 | * 14 | * Get the username and groupname of the calling process. On success the 15 | * username and groupname are returned, otherwise a nil followed by an error 16 | * message is returned. 17 | */ 18 | 19 | int lua_apr_user_get(lua_State *L) 20 | { 21 | apr_status_t status; 22 | apr_pool_t *pool; 23 | apr_uid_t uid; 24 | apr_gid_t gid; 25 | char *username, *groupname; 26 | 27 | # if APR_HAS_USER 28 | pool = to_pool(L); 29 | status = apr_uid_current(&uid, &gid, pool); 30 | if (APR_SUCCESS == status) 31 | status = apr_uid_name_get(&username, uid, pool); 32 | if (APR_SUCCESS == status) 33 | status = apr_gid_name_get(&groupname, gid, pool); 34 | if (APR_SUCCESS != status) 35 | return push_error_status(L, status); 36 | lua_pushstring(L, username); 37 | lua_pushstring(L, groupname); 38 | return 2; 39 | # else 40 | raise_error_status(L, APR_ENOTIMPL); 41 | return 0; 42 | # endif 43 | } 44 | 45 | /* apr.user_homepath_get(username) -> homepath {{{1 46 | * 47 | * Get the [home directory] [home] for the named user. On success the directory 48 | * pathname is returned, otherwise a nil followed by an error message is 49 | * returned. 50 | * 51 | * [home]: http://en.wikipedia.org/wiki/Home_directory 52 | */ 53 | 54 | int lua_apr_user_homepath_get(lua_State *L) 55 | { 56 | apr_status_t status; 57 | apr_pool_t *pool; 58 | const char *name; 59 | char *path; 60 | 61 | # if APR_HAS_USER 62 | pool = to_pool(L); 63 | name = luaL_checkstring(L, 1); 64 | status = apr_uid_homepath_get(&path, name, pool); 65 | if (APR_SUCCESS != status) 66 | return push_error_status(L, status); 67 | lua_pushstring(L, path); 68 | return 1; 69 | # else 70 | raise_error_status(L, APR_ENOTIMPL); 71 | return 0; 72 | # endif 73 | } 74 | 75 | /* Push name for userid */ 76 | 77 | int push_username(lua_State *L, apr_pool_t *pool, apr_uid_t uid) 78 | { 79 | char *username; 80 | 81 | # if APR_HAS_USER 82 | if (APR_SUCCESS == apr_uid_name_get(&username, uid, pool)) 83 | lua_pushstring(L, username); 84 | else 85 | lua_pushnil(L); 86 | # else 87 | lua_pushnil(L); 88 | # endif 89 | 90 | return 1; 91 | } 92 | 93 | /* Push name for groupid */ 94 | 95 | int push_groupname(lua_State *L, apr_pool_t *pool, apr_gid_t gid) 96 | { 97 | char *groupname; 98 | 99 | # if APR_HAS_USER 100 | if (APR_SUCCESS == apr_gid_name_get(&groupname, gid, pool)) 101 | lua_pushstring(L, groupname); 102 | else 103 | lua_pushnil(L); 104 | # else 105 | lua_pushnil(L); 106 | # endif 107 | 108 | return 1; 109 | } 110 | 111 | /* vim: set ts=2 sw=2 et tw=79 fen fdm=marker : */ 112 | -------------------------------------------------------------------------------- /src/uuid.c: -------------------------------------------------------------------------------- 1 | /* Universally unique identifiers module for the Lua/APR binding. 2 | * 3 | * Author: Peter Odding 4 | * Last Change: October 22, 2010 5 | * Homepage: http://peterodding.com/code/lua/apr/ 6 | * License: MIT 7 | * 8 | * [Universally unique identifiers] [uuid] are a standard for generating unique 9 | * strings that are specific to the machine on which they are generated and/or 10 | * the time at which they are generated. They can be used as primary keys in 11 | * databases and are used to uniquely identify file system types and instances 12 | * on modern operating systems. This is what a standard format UUID looks like: 13 | * 14 | * > = apr.uuid_format(apr.uuid_get()) 15 | * '0ad5d4a4-591e-41f7-8be4-07d7961a8079' 16 | * 17 | * [uuid]: http://en.wikipedia.org/wiki/Universally_unique_identifier 18 | */ 19 | 20 | #include "lua_apr.h" 21 | #include 22 | 23 | #define APR_UUID_LENGTH sizeof(apr_uuid_t) 24 | 25 | /* apr.uuid_get() -> binary {{{1 26 | * 27 | * Generate and return a UUID as a binary string of 16 bytes. 28 | */ 29 | 30 | int lua_apr_uuid_get(lua_State *L) 31 | { 32 | apr_uuid_t uuid; 33 | apr_uuid_get(&uuid); 34 | lua_pushlstring(L, (const char *) uuid.data, sizeof uuid); 35 | return 1; 36 | } 37 | 38 | /* apr.uuid_format(binary) -> formatted {{{1 39 | * 40 | * Format a UUID of 16 bytes following the standard format of 32 hexadecimal 41 | * digits, displayed in 5 groups separated by hyphens, in the form `8-4-4-4-12` 42 | * for a total of 36 characters, like `f5dc3464-6c8f-654e-a407-b15b7a30f038`. 43 | * On success the formatted UUID is returned, otherwise a nil followed by an 44 | * error message is returned. 45 | */ 46 | 47 | int lua_apr_uuid_format(lua_State *L) 48 | { 49 | size_t length; 50 | const char *uuid; 51 | char formatted[APR_UUID_FORMATTED_LENGTH + 1]; 52 | 53 | uuid = luaL_checklstring(L, 1, &length); 54 | 55 | if (APR_UUID_LENGTH != length) { 56 | const char *msg = "expected string of %d characters"; 57 | luaL_argerror(L, 1, lua_pushfstring(L, msg, APR_UUID_LENGTH)); 58 | } 59 | 60 | /* pointer to structure == pointer to first element */ 61 | apr_uuid_format(formatted, (const apr_uuid_t *) uuid); 62 | lua_pushlstring(L, formatted, APR_UUID_FORMATTED_LENGTH); 63 | return 1; 64 | } 65 | 66 | /* apr.uuid_parse(formatted) -> binary {{{1 67 | * 68 | * Parse a standard format UUID and return its 16-byte equivalent. On success 69 | * the parsed UUID is returned, otherwise a nil followed by an error message is 70 | * returned. 71 | */ 72 | 73 | int lua_apr_uuid_parse(lua_State *L) 74 | { 75 | apr_status_t status; 76 | size_t length; 77 | const char *formatted; 78 | apr_uuid_t uuid; 79 | 80 | formatted = luaL_checklstring(L, 1, &length); 81 | 82 | if (APR_UUID_FORMATTED_LENGTH != length) { 83 | const char *msg = "expected string of %d characters"; 84 | luaL_argerror(L, 1, lua_pushfstring(L, msg, APR_UUID_FORMATTED_LENGTH)); 85 | } 86 | 87 | status = apr_uuid_parse(&uuid, formatted); 88 | if (status != APR_SUCCESS) 89 | return push_error_status(L, status); 90 | lua_pushlstring(L, (const char *) uuid.data, APR_UUID_LENGTH); 91 | return 1; 92 | } 93 | 94 | /* vim: set ts=2 sw=2 et tw=79 fen fdm=marker : */ 95 | -------------------------------------------------------------------------------- /src/xlate.c: -------------------------------------------------------------------------------- 1 | /* Character encoding translation module for the Lua/APR binding. 2 | * 3 | * Author: Peter Odding 4 | * Last Change: February 13, 2011 5 | * Homepage: http://peterodding.com/code/lua/apr/ 6 | * License: MIT 7 | */ 8 | 9 | #include "lua_apr.h" 10 | #include 11 | 12 | /* Internal functions {{{1 */ 13 | 14 | static const char *check_codepage(lua_State *L, int idx) 15 | { 16 | const char *codepage = luaL_checkstring(L, idx); 17 | return strcmp(codepage, "locale") == 0 ? APR_LOCALE_CHARSET : codepage; 18 | } 19 | 20 | /* apr.xlate(input, from, to) -> translated {{{1 21 | * 22 | * Translate a string of text from one [character encoding] [charenc] to 23 | * another. The @from and @to arguments are strings identifying the source and 24 | * target character encoding. The special value `'locale'` indicates the 25 | * character set of the [current locale] [locale]. On success the translated 26 | * string is returned, otherwise a nil followed by an error message is 27 | * returned. 28 | * 29 | * Which character encodings are supported by `apr.xlate()` is system dependent 30 | * because APR can use both the system's [iconv] [iconv] implementation and the 31 | * bundled library [apr-iconv] [apr_iconv]. To get a list of valid character 32 | * encoding names you can look through the [apr-iconv/ccs] [iconv_ccs] and 33 | * [apr-iconv/ces] [iconv_ces] directories (those are links to the web 34 | * interface of the apr-iconv repository). 35 | * 36 | * [charenc]: http://en.wikipedia.org/wiki/Character_encoding 37 | * [locale]: http://en.wikipedia.org/wiki/Locale 38 | * [iconv]: http://en.wikipedia.org/wiki/Iconv 39 | * [apr_iconv]: http://apr.apache.org/docs/apr-iconv/trunk/group__apr__iconv.html 40 | * [iconv_ccs]: http://svn.apache.org/viewvc/apr/apr-iconv/trunk/ccs/ 41 | * [iconv_ces]: http://svn.apache.org/viewvc/apr/apr-iconv/trunk/ces/ 42 | */ 43 | 44 | int lua_apr_xlate(lua_State *L) 45 | { 46 | apr_pool_t *pool; 47 | const char *input, *frompage, *topage; 48 | size_t length, bufsize, extra; 49 | apr_xlate_t *convset; 50 | apr_status_t status; 51 | apr_size_t todo, unused; 52 | char *output, *temp; 53 | 54 | pool = to_pool(L); 55 | input = luaL_checklstring(L, 1, &length); 56 | frompage = check_codepage(L, 2); 57 | topage = check_codepage(L, 3); 58 | 59 | /* Apparently apr-iconv doesn't like empty input strings. */ 60 | if (length == 0) { 61 | lua_pushliteral(L, ""); 62 | return 1; 63 | } 64 | 65 | /* Initialize the output buffer and related variables. */ 66 | output = malloc(bufsize = length); 67 | if (output == NULL) { 68 | status = APR_ENOMEM; 69 | goto fail; 70 | } 71 | todo = unused = length; 72 | 73 | /* Initialize the translation context. */ 74 | status = apr_xlate_open(&convset, topage, frompage, pool); 75 | if (status != APR_SUCCESS) 76 | goto fail; 77 | 78 | /* Perform the conversion in one or more passes. */ 79 | for (;;) { 80 | status = apr_xlate_conv_buffer(convset, 81 | &input[length - todo], &todo, 82 | &output[bufsize - unused], &unused); 83 | if (status != APR_SUCCESS) 84 | goto fail; 85 | else if (todo == 0) 86 | break; 87 | /* Grow the output buffer by one third. */ 88 | extra = bufsize < 10 ? 10 : bufsize / 3; 89 | temp = realloc(output, bufsize + extra); 90 | if (temp == NULL) { 91 | status = APR_ENOMEM; 92 | goto fail; 93 | } 94 | output = temp; 95 | bufsize += extra; 96 | unused += extra; 97 | } 98 | 99 | /* Correctly terminate the output buffer for some multibyte character set encodings. */ 100 | status = apr_xlate_conv_buffer(convset, NULL, NULL, 101 | &output[bufsize - unused], &unused); 102 | if (status != APR_SUCCESS) 103 | goto fail; 104 | 105 | /* Close the translation context. */ 106 | status = apr_xlate_close(convset); 107 | if (status != APR_SUCCESS) 108 | goto fail; 109 | 110 | lua_pushlstring(L, output, bufsize - unused); 111 | free(output); 112 | /* I'm not sure whether any resources remain in memory after the call to 113 | * apr_xlate_close(). Just to make sure, we clear the global memory pool 114 | * now instead of waiting until the next time it's used. */ 115 | apr_pool_clear(pool); 116 | return 1; 117 | 118 | fail: 119 | free(output); 120 | apr_pool_clear(pool); 121 | return push_status(L, status); 122 | } 123 | -------------------------------------------------------------------------------- /test/base64.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Unit tests for the Base64 encoding module of the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: March 27, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | --]] 11 | 12 | local status, apr = pcall(require, 'apr') 13 | if not status then 14 | pcall(require, 'luarocks.require') 15 | apr = require 'apr' 16 | end 17 | 18 | -- Sample data from http://en.wikipedia.org/wiki/Base64#Examples 19 | local plain = 'Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.' 20 | local coded = 'TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=' 21 | 22 | -- Check that Base64 encoding returns the expected result. 23 | assert(apr.base64_encode(plain) == coded) 24 | 25 | -- Check that Base64 decoding returns the expected result. 26 | assert(apr.base64_decode(coded) == plain) 27 | -------------------------------------------------------------------------------- /test/crypt.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Unit tests for the cryptography module of the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: March 27, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | --]] 11 | 12 | local status, apr = pcall(require, 'apr') 13 | if not status then 14 | pcall(require, 'luarocks.require') 15 | apr = require 'apr' 16 | end 17 | 18 | -- Sample data from http://en.wikipedia.org/wiki/MD5#MD5_hashes 19 | -- and http://en.wikipedia.org/wiki/SHA1#Example_hashes 20 | 21 | -- Check that MD5 hashing returns the expected result. 22 | assert(apr.md5 '' == 'd41d8cd98f00b204e9800998ecf8427e') 23 | assert(apr.md5 'The quick brown fox jumps over the lazy dog' == '9e107d9d372bb6826bd81d3542a419d6') 24 | assert(apr.md5 'The quick brown fox jumps over the lazy eog' == 'ffd93f16876049265fbaef4da268dd0e') 25 | 26 | local pass, salt = 'password', 'salt' 27 | hash = apr.md5_encode(pass, salt) 28 | -- Test that MD5 passwords can be validated. 29 | assert(apr.password_validate(pass, hash)) 30 | 31 | -- Test that SHA1 hashing returns the expected result. 32 | assert(apr.sha1 'The quick brown fox jumps over the lazy dog' == '2fd4e1c67a2d28fced849ee1bb76e7391b93eb12') 33 | assert(apr.sha1 'The quick brown fox jumps over the lazy cog' == 'de9f2c7fd25e1b3afad3e85a0bd17d9b100db4b3') 34 | 35 | -- Test the incremental MD5 message digest interface. 36 | local context = assert(apr.md5_init()) 37 | assert(context:update 'The quick brown fox jumps over the lazy dog') 38 | assert(context:digest() == '9e107d9d372bb6826bd81d3542a419d6') 39 | assert(context:reset()) 40 | assert(context:update 'The quick brown fox jumps over the lazy eog') 41 | assert(context:digest() == 'ffd93f16876049265fbaef4da268dd0e') 42 | 43 | -- Test the incremental SHA1 message digest interface. 44 | local context = assert(apr.sha1_init()) 45 | assert(context:update 'The quick brown fox jumps over the lazy dog') 46 | assert(context:digest() == '2fd4e1c67a2d28fced849ee1bb76e7391b93eb12') 47 | assert(context:reset()) 48 | assert(context:update 'The quick brown fox jumps over the lazy cog') 49 | assert(context:digest() == 'de9f2c7fd25e1b3afad3e85a0bd17d9b100db4b3') 50 | -------------------------------------------------------------------------------- /test/date.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Unit tests for the date parsing module of the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: March 27, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | --]] 11 | 12 | local status, apr = pcall(require, 'apr') 13 | if not status then 14 | pcall(require, 'luarocks.require') 15 | apr = require 'apr' 16 | end 17 | 18 | -- The following tests were copied from http://svn.apache.org/viewvc/apr/apr/trunk/test/testdate.c?view=markup 19 | for i, rfc_pair in ipairs { 20 | { "Mon, 27 Feb 1995 20:49:44 -0800", "Tue, 28 Feb 1995 04:49:44 GMT" }, 21 | { "Fri, 1 Jul 2005 11:34:25 -0400", "Fri, 01 Jul 2005 15:34:25 GMT" }, 22 | { "Monday, 27-Feb-95 20:49:44 -0800", "Tue, 28 Feb 1995 04:49:44 GMT" }, 23 | { "Tue, 4 Mar 1997 12:43:52 +0200", "Tue, 04 Mar 1997 10:43:52 GMT" }, 24 | { "Mon, 27 Feb 95 20:49:44 -0800", "Tue, 28 Feb 1995 04:49:44 GMT" }, 25 | { "Tue, 4 Mar 97 12:43:52 +0200", "Tue, 04 Mar 1997 10:43:52 GMT" }, 26 | { "Tue, 4 Mar 97 12:43:52 +0200", "Tue, 04 Mar 1997 10:43:52 GMT" }, 27 | { "Mon, 27 Feb 95 20:49 GMT", "Mon, 27 Feb 1995 20:49:00 GMT" }, 28 | { "Tue, 4 Mar 97 12:43 GMT", "Tue, 04 Mar 1997 12:43:00 GMT" } 29 | } do 30 | local time = assert(apr.date_parse_rfc(rfc_pair[1])) 31 | assert(apr.time_format('rfc822', time) == rfc_pair[2]) 32 | end 33 | 34 | -- The tests for apr_date_parse_http() are far too complex to reproduce in Lua 35 | -- so instead here's a round trip of one of the examples in the documentation: 36 | local date = 'Sun, 06 Nov 1994 08:49:37 GMT' 37 | assert(apr.time_format('rfc822', apr.date_parse_http(date)) == date) 38 | -------------------------------------------------------------------------------- /test/dbd.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Unit tests for the relational database module of the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: July 2, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | This script executes the real test in dbd-child.lua as a child process. 11 | 12 | --]] 13 | 14 | local status, apr = pcall(require, 'apr') 15 | if not status then 16 | pcall(require, 'luarocks.require') 17 | apr = require 'apr' 18 | end 19 | local helpers = require 'apr.test.helpers' 20 | return helpers.ld_preload_trick 'dbd-child.lua' 21 | -------------------------------------------------------------------------------- /test/dbm.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Unit tests for the DBM module of the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: June 30, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | --]] 11 | 12 | local status, apr = pcall(require, 'apr') 13 | if not status then 14 | pcall(require, 'luarocks.require') 15 | apr = require 'apr' 16 | end 17 | local helpers = require 'apr.test.helpers' 18 | local dbmfile = helpers.tmpname() 19 | local dbm = assert(apr.dbm_open(dbmfile, 'n')) 20 | local dbmkey, dbmvalue = 'the key', 'the value' 21 | assert(not dbm:firstkey()) -- nothing there yet 22 | assert(dbm:store(dbmkey, dbmvalue)) 23 | local function checkdbm() 24 | assert(dbm:exists(dbmkey)) 25 | assert(dbm:fetch(dbmkey) == dbmvalue) 26 | assert(dbm:firstkey() == dbmkey) 27 | assert(not dbm:nextkey(dbmkey)) -- only 1 record exists 28 | end 29 | checkdbm() 30 | assert(dbm:close()) 31 | local file1, file2 = assert(apr.dbm_getnames(dbmfile)) 32 | assert(apr.stat(file1, 'type') == 'file') 33 | assert(not file2 or apr.stat(file2, 'type') == 'file') 34 | dbm = assert(apr.dbm_open(dbmfile, 'w')) 35 | checkdbm() 36 | assert(dbm:delete(dbmkey)) 37 | assert(not dbm:fetch(dbmkey)) 38 | assert(not dbm:firstkey()) 39 | 40 | -- Test tostring(dbm). 41 | assert(tostring(dbm):find '^dbm %([x%x]+%)$') 42 | assert(dbm:close()) 43 | assert(tostring(dbm):find '^dbm %(closed%)$') 44 | 45 | -- Cleanup. 46 | apr.file_remove(file1) 47 | if file2 then 48 | apr.file_remove(file2) 49 | end 50 | -------------------------------------------------------------------------------- /test/env-child.lua: -------------------------------------------------------------------------------- 1 | local status, apr = pcall(require, 'apr') 2 | if not status then 3 | pcall(require, 'luarocks.require') 4 | apr = require 'apr' 5 | end 6 | local valid_key = 'LUA_APR_MAGIC_ENV_KEY' 7 | local invalid_key = 'LUA_APR_INVALID_ENV_KEY' 8 | assert(apr.env_get(valid_key) == apr._VERSION) 9 | assert(not apr.env_get(invalid_key)) 10 | apr.sleep(1) 11 | os.exit(42) 12 | -------------------------------------------------------------------------------- /test/env.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Unit tests for the environment manipulation module of the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: March 27, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | --]] 11 | 12 | local status, apr = pcall(require, 'apr') 13 | if not status then 14 | pcall(require, 'luarocks.require') 15 | apr = require 'apr' 16 | end 17 | 18 | -- Based on http://svn.apache.org/viewvc/apr/apr/trunk/test/testenv.c?view=markup 19 | 20 | local TEST_ENVVAR_NAME = "apr_test_envvar" 21 | local TEST_ENVVAR2_NAME = "apr_test_envvar2" 22 | local TEST_ENVVAR_VALUE = "Just a value that we'll check" 23 | 24 | -- Test that environment variables can be set. 25 | assert(apr.env_set(TEST_ENVVAR_NAME, TEST_ENVVAR_VALUE)) 26 | 27 | -- Test that environment variables can be read. 28 | assert(apr.env_get(TEST_ENVVAR_NAME)) 29 | assert(apr.env_get(TEST_ENVVAR_NAME) == TEST_ENVVAR_VALUE) 30 | 31 | -- Test that environment variables can be deleted. 32 | assert(apr.env_delete(TEST_ENVVAR_NAME)) 33 | assert(not apr.env_get(TEST_ENVVAR_NAME)) 34 | 35 | -- http://issues.apache.org/bugzilla/show_bug.cgi?id=40764 36 | 37 | -- Set empty string and test that status is OK. 38 | assert(apr.env_set(TEST_ENVVAR_NAME, "")) 39 | assert(apr.env_get(TEST_ENVVAR_NAME)) 40 | assert(apr.env_get(TEST_ENVVAR_NAME) == "") 41 | 42 | -- Delete environment variable and retest. 43 | assert(apr.env_delete(TEST_ENVVAR_NAME)) 44 | assert(not apr.env_get(TEST_ENVVAR_NAME)) 45 | 46 | -- Set second variable and test. 47 | assert(apr.env_set(TEST_ENVVAR2_NAME, TEST_ENVVAR_VALUE)) 48 | assert(apr.env_get(TEST_ENVVAR2_NAME)) 49 | assert(apr.env_get(TEST_ENVVAR2_NAME) == TEST_ENVVAR_VALUE) 50 | 51 | -- Finally, test ENOENT (first variable) followed by second != ENOENT. 52 | assert(not apr.env_get(TEST_ENVVAR_NAME)) 53 | assert(apr.env_get(TEST_ENVVAR2_NAME)) 54 | assert(apr.env_get(TEST_ENVVAR2_NAME) == TEST_ENVVAR_VALUE) 55 | 56 | -- Cleanup. 57 | assert(apr.env_delete(TEST_ENVVAR2_NAME)) 58 | -------------------------------------------------------------------------------- /test/filepath.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Unit tests for the file path manipulation module of the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: November 6, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | --]] 11 | 12 | local status, apr = pcall(require, 'apr') 13 | if not status then 14 | pcall(require, 'luarocks.require') 15 | apr = require 'apr' 16 | end 17 | local helpers = require 'apr.test.helpers' 18 | 19 | -- Test apr.filepath_root(). {{{1 20 | if apr.platform_get() == 'WIN32' then 21 | assert(apr.filepath_root 'c:/foo/bar' == 'c:/') 22 | else 23 | -- Doesn't really do anything useful on UNIX :-P 24 | assert(apr.filepath_root '/foo/bar' == '/') 25 | end 26 | 27 | -- Test apr.filepath_parent(). {{{1 28 | 29 | for _, path in ipairs { 30 | 'foo/bar/baz', -- clean and simple path with 3 segments 31 | 'foo//bar//baz///' -- same path with some empty segments (should be ignored) 32 | } do 33 | local root = apr.platform_get() == 'WIN32' and 'c:/' or '/' 34 | local abs = function(s) return apr.filepath_merge(root, s) end 35 | local p1, n1 = apr.filepath_parent(abs(path)) 36 | local p2, n2 = apr.filepath_parent(p1) 37 | local p3, n3 = apr.filepath_parent(p2) 38 | local p4, n4 = apr.filepath_parent(p3) 39 | assert(p4 == root and n4 == '') 40 | assert(p3 == root and n3 == 'foo') 41 | assert(p2 == abs 'foo/' and n2 == 'bar') 42 | assert(p1 == abs 'foo/bar/' and n1 == 'baz') 43 | end 44 | 45 | -- Test apr.filepath_name(). {{{1 46 | assert(apr.filepath_name('/usr/bin/lua') == 'lua') 47 | local parts = { apr.filepath_name('/home/xolox/.vimrc', true) } 48 | assert(#parts == 2 and parts[1] == '.vimrc' and parts[2] == '') 49 | parts = { apr.filepath_name('index.html.en', true) } 50 | assert(#parts == 2 and parts[1] == 'index.html' and parts[2] == '.en') 51 | 52 | -- Test apr.filepath_merge(). {{{1 53 | if apr.platform_get() == 'WIN32' then 54 | assert(apr.filepath_merge('c:/', 'foo') == 'c:/foo') 55 | else 56 | assert(apr.filepath_merge('/foo', 'bar') == '/foo/bar') 57 | end 58 | 59 | -- Test apr.filepath_list_split()/merge(). {{{1 60 | -- Based on http://svn.apache.org/viewvc/apr/apr/trunk/test/testpath.c?view=markup. 61 | 62 | local PSEP, DSEP 63 | local p = apr.platform_get() 64 | if p == 'WIN32' or p == 'NETWARE' or p == 'OS2' then 65 | PSEP, DSEP = ';', '\\' 66 | else 67 | PSEP, DSEP = ':', '/' 68 | end 69 | local PX = "" 70 | local P1 = "first path" 71 | local P2 = "second" .. DSEP .. "path" 72 | local P3 = "th ird" .. DSEP .. "path" 73 | local P4 = "fourth" .. DSEP .. "pa th" 74 | local P5 = "fifthpath" 75 | local parts_in = { P1, P2, P3, PX, P4, P5 } 76 | local path_in = table.concat(parts_in, PSEP) 77 | local parts_out = { P1, P2, P3, P4, P5 } 78 | local path_out = table.concat(parts_out, PSEP) 79 | 80 | -- list_split_multi 81 | do 82 | local pathelts = assert(apr.filepath_list_split(path_in)) 83 | assert(#parts_out == #pathelts) 84 | for i = 1, #pathelts do assert(parts_out[i] == pathelts[i]) end 85 | end 86 | 87 | -- list_split_single 88 | for i = 1, #parts_in do 89 | local pathelts = assert(apr.filepath_list_split(parts_in[i])) 90 | if parts_in[i] == '' then 91 | assert(#pathelts == 0) 92 | else 93 | assert(#pathelts == 1) 94 | assert(parts_in[i] == pathelts[1]) 95 | end 96 | end 97 | 98 | -- list_merge_multi 99 | do 100 | local pathelts = {} 101 | for i = 1, #parts_in do pathelts[i] = parts_in[i] end 102 | local liststr = assert(apr.filepath_list_merge(pathelts)) 103 | assert(liststr == path_out) 104 | end 105 | 106 | -- list_merge_single 107 | for i = 1, #parts_in do 108 | local liststr = assert(apr.filepath_list_merge{ parts_in[i] }) 109 | if parts_in[i] == '' then 110 | assert(liststr == '') 111 | else 112 | assert(liststr == parts_in[i]) 113 | end 114 | end 115 | 116 | -- Test apr.filepath_which() and apr.filepath_executable(). {{{1 117 | 118 | local lua_program = assert(apr.filepath_which 'lua') 119 | assert(apr.filepath_executable(lua_program)) 120 | -------------------------------------------------------------------------------- /test/fnmatch.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Unit tests for the filename matching module of the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: March 27, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | --]] 11 | 12 | local status, apr = pcall(require, 'apr') 13 | if not status then 14 | pcall(require, 'luarocks.require') 15 | apr = require 'apr' 16 | end 17 | 18 | -- Check that the ?, *, and [] wild cards are supported. 19 | assert(apr.fnmatch('lua_apr.?', 'lua_apr.c')) 20 | assert(apr.fnmatch('lua_apr.?', 'lua_apr.h')) 21 | assert(apr.fnmatch('lua_apr.[ch]', 'lua_apr.h')) 22 | assert(not apr.fnmatch('lua_apr.[ch]', 'lua_apr.l')) 23 | assert(not apr.fnmatch('lua_apr.?', 'lua_apr.cc')) 24 | assert(apr.fnmatch('lua*', 'lua51')) 25 | 26 | -- Check that filename matching is case sensitive by default. 27 | assert(not apr.fnmatch('lua*', 'LUA51')) 28 | 29 | -- Check that case insensitive filename matching works. 30 | assert(apr.fnmatch('lua*', 'LUA51', true)) 31 | 32 | -- Check that special characters in filename matching are detected. 33 | assert(not apr.fnmatch_test('lua51')) 34 | assert(apr.fnmatch_test('lua5?')) 35 | assert(apr.fnmatch_test('lua5*')) 36 | assert(apr.fnmatch_test('[lL][uU][aA]')) 37 | assert(not apr.fnmatch_test('+-^#@!%')) 38 | -------------------------------------------------------------------------------- /test/getopt.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Unit tests for command argument parsing module of the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: March 27, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | --]] 11 | 12 | local status, apr = pcall(require, 'apr') 13 | if not status then 14 | pcall(require, 'luarocks.require') 15 | apr = require 'apr' 16 | end 17 | local helpers = require 'apr.test.helpers' 18 | 19 | local function testopts(context) 20 | context.arguments[0] = 'getopt' 21 | local config = { silent = false, args = context.arguments } 22 | local opts, args = assert(apr.getopt(context.usage, config)) 23 | assert(helpers.deepequal(opts, context.expected_opts)) 24 | assert(helpers.deepequal(args, context.expected_args)) 25 | end 26 | 27 | -- Short options (option letters) only. 28 | testopts { 29 | usage = [[ 30 | -s only an option letter 31 | -a=? short option with argument 32 | ]], 33 | arguments = { 'arg1', '-s', 'arg2', '-a5' }, 34 | expected_opts = { s = 1, a = '5' }, 35 | expected_args = { 'arg1', 'arg2' } 36 | } 37 | 38 | -- Long option names only. 39 | testopts { 40 | usage = [[ 41 | --long long option name 42 | --arg=? long option with argument 43 | ]], 44 | arguments = { '--long', 'arg1', '--arg=yes', 'arg2' }, 45 | expected_opts = { long = 1, arg = 'yes' }, 46 | expected_args = { 'arg1', 'arg2' } 47 | } 48 | 49 | -- Options with aliases. 50 | testopts { 51 | usage = [[ 52 | -b, --both option letter and name 53 | -a, --arg=? aliased + argument 54 | ]], 55 | arguments = { '-b', 'arg1', '--arg=yes', 'arg2' }, 56 | expected_opts = { b = 1, both = 1, a = 'yes', arg = 'yes' }, 57 | expected_args = { 'arg1', 'arg2' } 58 | } 59 | 60 | -- Repeating options. 61 | testopts { 62 | usage = [[ 63 | -v, --verbose increase verbosity 64 | -a, --arg=? add value to list 65 | ]], 66 | arguments = { '-vv', 'a1', '--verbose', 'a2', '-av1', '-a', 'v2', '--arg', 'v3', '--arg=v4', 'a3' }, 67 | expected_opts = { v = 3, verbose = 3, a = { 'v1', 'v2', 'v3', 'v4' }, arg = { 'v1', 'v2', 'v3', 'v4' } }, 68 | expected_args = { 'a1', 'a2', 'a3' } 69 | } 70 | -------------------------------------------------------------------------------- /test/helpers.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Test infrastructure for the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: November 27, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | --]] 11 | 12 | local status, apr = pcall(require, 'apr') 13 | if not status then 14 | pcall(require, 'luarocks.require') 15 | apr = require 'apr' 16 | end 17 | local helpers = {} 18 | 19 | function print(...) 20 | local t = {} 21 | for i = 1, select('#', ...) do 22 | t[#t + 1] = tostring(select(i, ...)) 23 | end 24 | io.stderr:write(table.concat(t, ' ') .. '\n') 25 | end 26 | 27 | function helpers.pack(...) -- {{{1 28 | return { n = select('#', ...), ... } 29 | end 30 | 31 | function helpers.unpack(t) -- {{{1 32 | return unpack(t, 1, t.n) 33 | end 34 | 35 | function helpers.message(s, ...) -- {{{1 36 | io.stderr:write(string.format(s, ...)) 37 | io.stderr:flush() 38 | end 39 | 40 | function helpers.warning(s, ...) -- {{{1 41 | io.stderr:write("\nWarning: ", string.format(s, ...)) 42 | io.stderr:flush() 43 | end 44 | 45 | function helpers.soft_assert(tuple, extramsg) -- {{{1 46 | local status, details = helpers.unpack(tuple) 47 | if not status then 48 | helpers.warning("Soft assertion failed with \"%s\"! (%s)\n", details, extramsg) 49 | end 50 | end 51 | 52 | function helpers.try(body, errorhandler) -- {{{1 53 | local status, value = xpcall(body, debug.traceback) 54 | if status then return true end 55 | errorhandler(value) 56 | return false 57 | end 58 | 59 | function helpers.filedefined() -- {{{1 60 | local info = assert(debug.getinfo(2, 'S')) 61 | return info.source:sub(2) 62 | end 63 | 64 | function helpers.deepequal(a, b) -- {{{1 65 | if type(a) ~= 'table' or type(b) ~= 'table' then 66 | return a == b 67 | else 68 | for k, v in pairs(a) do 69 | if not helpers.deepequal(v, b[k]) then 70 | return false 71 | end 72 | end 73 | for k, v in pairs(b) do 74 | if not helpers.deepequal(v, a[k]) then 75 | return false 76 | end 77 | end 78 | return true 79 | end 80 | end 81 | 82 | function helpers.checktuple(expected, ...) -- {{{1 83 | local received = helpers.pack(...) 84 | assert(received.n == #expected) 85 | for i = 1, #expected do assert(expected[i] == received[i]) end 86 | end 87 | 88 | local testscripts = apr.filepath_parent(helpers.filedefined()) 89 | 90 | function helpers.scriptpath(name) -- {{{1 91 | return assert(apr.filepath_merge(testscripts, name)) 92 | end 93 | 94 | function helpers.ld_preload_trick(script) -- {{{1 95 | 96 | -- XXX This hack is needed to make the tests pass on Ubuntu 10.04 and probably 97 | -- also other versions of Ubuntu and Debian? The Lua/APR documentation for the 98 | -- DBD module contains some notes about this, here's a direct link: 99 | -- http://peterodding.com/code/lua/apr/docs/#debugging_dso_load_failed_errors 100 | 101 | -- Include the libapr-1.so and libaprutil-1.so libraries in $LD_PRELOAD if 102 | -- they exist in the usual Debian location. 103 | local libs = apr.filepath_list_split(apr.env_get 'LD_PRELOAD' or '') 104 | for _, libname in ipairs { '/usr/lib/libapr-1.so.0', '/usr/lib/libaprutil-1.so.0' } do 105 | if apr.stat(libname, 'type') == 'file' then table.insert(libs, libname) end 106 | end 107 | apr.env_set('LD_PRELOAD', apr.filepath_list_merge(libs)) 108 | 109 | -- Now run the test in a child process where $LD_PRELOAD applies. 110 | local child = assert(apr.proc_create 'lua') 111 | assert(child:cmdtype_set 'shellcmd/env') 112 | assert(child:exec { helpers.scriptpath(script) }) 113 | local dead, reason, code = assert(child:wait(true)) 114 | return reason == 'exit' and code == 0 115 | 116 | end 117 | 118 | function helpers.wait_for(signalfile, timeout) -- {{{1 119 | local starttime = apr.time_now() 120 | while apr.time_now() - starttime < timeout do 121 | apr.sleep(0.25) 122 | if apr.stat(signalfile, 'type') == 'file' then 123 | return true 124 | end 125 | end 126 | end 127 | 128 | local tmpnum = 1 129 | local tmpdir = assert(apr.temp_dir_get()) 130 | 131 | local function tmpname(tmpnum) 132 | return apr.filepath_merge(tmpdir, 'lua-apr-tempfile-' .. tmpnum) 133 | end 134 | 135 | function helpers.tmpname() -- {{{1 136 | local file = tmpname(tmpnum) 137 | apr.file_remove(file) 138 | tmpnum = tmpnum + 1 139 | return file 140 | end 141 | 142 | function helpers.cleanup() -- {{{1 143 | for i = 1, tmpnum do 144 | apr.file_remove(tmpname(i)) 145 | end 146 | end 147 | 148 | function helpers.readfile(path) -- {{{1 149 | local handle = assert(io.open(path, 'r')) 150 | local data = assert(handle:read '*all') 151 | assert(handle:close()) 152 | return data 153 | end 154 | 155 | function helpers.writefile(path, data) -- {{{1 156 | local handle = assert(io.open(path, 'w')) 157 | assert(handle:write(data)) 158 | assert(handle:close()) 159 | end 160 | 161 | function helpers.writable(directory) -- {{{1 162 | local entry = apr.filepath_merge(directory, 'io_dir_writable_check') 163 | local status = pcall(helpers.writefile, entry, 'something') 164 | if status then os.remove(entry) end 165 | return status 166 | end 167 | 168 | local escapes = { ['\r'] = '\\r', ['\n'] = '\\n', ['"'] = '\\"', ['\0'] = '\\0' } 169 | 170 | function helpers.formatvalue(v) -- {{{1 171 | if type(v) == 'number' then 172 | local s = string.format('%.99f', v) 173 | return s:find '%.' and (s:gsub('0+$', '0')) or s 174 | elseif type(v) == 'string' then 175 | return '"' .. v:gsub('[\r\n"%z]', escapes) .. '"' 176 | else 177 | return tostring(v) 178 | end 179 | end 180 | 181 | -- }}}1 182 | 183 | return helpers 184 | -------------------------------------------------------------------------------- /test/init.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Driver script for the unit tests of the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: November 13, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | Because the unit tests for the Lua/APR binding are included as installable 11 | modules, it's easy to run the test suite once you've installed the Lua/APR 12 | binding. Just execute the following command from a terminal: 13 | 14 | lua -e "require 'apr.test' ()" 15 | 16 | --]] 17 | 18 | -- TODO Cleanup and extend the tests for `filepath.c'. 19 | -- TODO Add tests for file:seek() (tricky to get right!) 20 | -- TODO Add tests for apr.glob()! 21 | 22 | -- Names of modules for which tests have been written (the individual lines 23 | -- enable automatic rebasing between git feature branches and master branch). 24 | local modules = { 25 | 'base64', 26 | 'crypt', 27 | 'date', 28 | 'dbd', 29 | 'dbm', 30 | 'env', 31 | 'filepath', 32 | 'fnmatch', 33 | 'getopt', 34 | 'http', 35 | 'io_dir', 36 | 'io_file', 37 | 'io_net', 38 | 'ldap', 39 | 'memcache', 40 | 'misc', 41 | 'pollset', 42 | 'proc', 43 | 'serialize', 44 | 'shm', 45 | 'signal', 46 | 'str', 47 | 'thread', 48 | 'thread_queue', 49 | 'time', 50 | 'uri', 51 | 'user', 52 | 'uuid', 53 | 'xlate', 54 | 'xml' 55 | } 56 | 57 | local modname = ... 58 | return function() 59 | 60 | local status, apr = pcall(require, 'apr') 61 | if not status then 62 | pcall(require, 'luarocks.require') 63 | apr = require 'apr' 64 | end 65 | local helpers = require 'apr.test.helpers' 66 | 67 | local success = true 68 | for _, testname in ipairs(modules) do 69 | local modname = modname .. '.' .. testname 70 | package.loaded[modname] = nil 71 | helpers.message("Running %s tests: ", testname) 72 | local starttime = apr.time_now() 73 | local status, result = pcall(require, modname) 74 | if status and result ~= false then 75 | -- All tests passed. 76 | local elapsed = apr.time_now() - starttime 77 | if elapsed >= 0.5 then 78 | helpers.message("OK (%.2fs)\n", elapsed) 79 | else 80 | helpers.message "OK\n" 81 | end 82 | elseif status then 83 | -- Soft failure (anticipated). 84 | helpers.message("Skipped!\n") 85 | else 86 | -- Hard failure. 87 | helpers.message("Failed! (%s)\n", result) 88 | success = false 89 | end 90 | package.loaded[modname] = nil 91 | -- Garbage collect unreferenced objects before testing the next module. 92 | collectgarbage 'collect' 93 | collectgarbage 'collect' 94 | end 95 | 96 | -- Cleanup temporary files. 97 | helpers.cleanup() 98 | 99 | helpers.message "Done!\n" 100 | 101 | -- Exit the interpreter (started with lua -e "require 'apr.test' ()"). 102 | os.exit(success and 0 or 1) 103 | 104 | end 105 | 106 | -- vim: ts=2 sw=2 et 107 | -------------------------------------------------------------------------------- /test/io_buffer.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Unit tests for the buffered I/O interface of the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: June 15, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | --]] 11 | 12 | local helpers = require 'apr.test.helpers' 13 | local verbosity = 0 14 | 15 | local function testformat(apr_file, lua_file, format) 16 | if verbosity >= 1 then helpers.message("Testing file:read(%s) ..\n", format) end 17 | repeat 18 | local lua_value = lua_file:read(format) 19 | if verbosity >= 2 then helpers.message("file:read(%s) = %s\n", format, helpers.formatvalue(lua_value)) end 20 | local apr_value = apr_file:read(format) 21 | if lua_value ~= apr_value then 22 | helpers.warning("Wrong result for file:read(%s)!\nLua value: %s\nAPR value: %s\n", 23 | format, helpers.formatvalue(lua_value), helpers.formatvalue(apr_value)) 24 | helpers.warning("Lua position: %i, APR position: %i\n", lua_file:seek 'cur', apr_file:seek 'cur') 25 | helpers.warning("Remaining data in Lua file: %s\n", helpers.formatvalue(lua_file:read '*a')) 26 | helpers.warning("Remaining data in APR file: %s\n", helpers.formatvalue(apr_file:read '*a')) 27 | os.exit(1) 28 | end 29 | until (format == '*a' and lua_value == '') or not lua_value 30 | end 31 | 32 | return function(test_file, apr_object) 33 | local lua_file = assert(io.open(test_file)) 34 | for _, format in pairs { '*n', '*l', '*a', 1, 2, 3, 4, 5, 10, 20, 50, 100 } do 35 | testformat(apr_object, lua_file, format) 36 | apr_object:seek('set', 0) 37 | lua_file:seek('set', 0) 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /test/io_dir.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Unit tests for the directory manipulation module of the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: November 27, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | --]] 11 | 12 | local status, apr = pcall(require, 'apr') 13 | if not status then 14 | pcall(require, 'luarocks.require') 15 | apr = require 'apr' 16 | end 17 | local helpers = require 'apr.test.helpers' 18 | 19 | -- Make sure apr.temp_dir_get() returns an existing, writable directory 20 | local directory = assert(apr.temp_dir_get()) 21 | assert(helpers.writable(directory)) 22 | 23 | -- Create a temporary workspace directory for the following tests 24 | math.randomseed(os.time()) 25 | local io_dir_tests = apr.filepath_merge(directory, 26 | string.format('lua-apr-io_dir_tests-%i', math.random(1e9))) 27 | assert(apr.dir_make(io_dir_tests)) 28 | 29 | -- Change to the temporary workspace directory 30 | local cwd_save = assert(apr.filepath_get()) 31 | assert(apr.filepath_set(io_dir_tests)) 32 | 33 | -- Test dir_make() 34 | assert(apr.dir_make 'foobar') 35 | assert(helpers.writable 'foobar') 36 | 37 | -- Test dir_remove() 38 | assert(apr.dir_remove 'foobar') 39 | assert(not helpers.writable 'foobar') 40 | 41 | -- Test dir_make_recursive() and dir_remove_recursive() 42 | assert(apr.dir_make_recursive 'foo/bar/baz') 43 | assert(helpers.writable 'foo/bar/baz') 44 | assert(apr.dir_remove_recursive 'foo') 45 | assert(not helpers.writable 'foo') 46 | 47 | -- Random selection of Unicode from my music library :-) 48 | local msi = 'Mindless Self Indulgence - Despierta Los Niños' 49 | local manu_chao = 'Manu Chao - Próxima Estación; Esperanza' 50 | local cassius = 'Cassius - Au Rêve' 51 | local yoko_kanno = '菅野 よう子' 52 | local nonascii = { msi, manu_chao, cassius, yoko_kanno } 53 | 54 | -- Workarounds for Unicode normalization on Mac OS X. {{{1 55 | 56 | -- Mac OS X apparently applies Unicode normalization on file system level. This 57 | -- means you can write a file without getting any errors, but when you check 58 | -- for the file later it doesn't exist (because the normalized form differs 59 | -- from the original form and the normalized form is reported back by the OS). 60 | -- See also issue 10 on GitHub: https://github.com/xolox/lua-apr/issues/10. 61 | -- The following table maps the titles above to their normalized form. 62 | local nonascii_normalized = { 63 | -- Technically it's superstition that made me write out these normalized 64 | -- forms as sequences of bytes. I did so because the visual differences 65 | -- between the normalized forms and the original track titles above are so 66 | -- small it hurts my head :-( 67 | [string.char(77, 105, 110, 100, 108, 101, 115, 115, 32, 83, 101, 108, 102, 32, 73, 110, 100, 117, 108, 103, 101, 110, 99, 101, 32, 45, 32, 68, 101, 115, 112, 105, 101, 114, 116, 97, 32, 76, 111, 115, 32, 78, 105, 110, 204, 131, 111, 115)] = msi, 68 | [string.char(77, 97, 110, 117, 32, 67, 104, 97, 111, 32, 45, 32, 80, 114, 111, 204, 129, 120, 105, 109, 97, 32, 69, 115, 116, 97, 99, 105, 111, 204, 129, 110, 59, 32, 69, 115, 112, 101, 114, 97, 110, 122, 97)] = manu_chao, 69 | [string.char(67, 97, 115, 115, 105, 117, 115, 32, 45, 32, 65, 117, 32, 82, 101, 204, 130, 118, 101)] = cassius, 70 | } 71 | 72 | local warned_about_normalization = false 73 | 74 | local function handle_normalized_forms(s) 75 | if nonascii_normalized[s] then 76 | if not warned_about_normalization then 77 | helpers.warning "Detected Unicode normalization on file system level, applying workaround... (you're probably on Mac OS X)\n" 78 | warned_about_normalization = true 79 | end 80 | return nonascii_normalized[s] 81 | else 82 | return s 83 | end 84 | end 85 | 86 | -- }}}1 87 | 88 | for _, name in ipairs(nonascii) do 89 | assert(apr.dir_make(name)) 90 | -- Using writable() here won't work because the APR API deals with UTF-8 91 | -- while the Lua API does not, which makes the strings non-equal... :-) 92 | assert(assert(apr.stat(name, 'type')) == 'directory') 93 | end 94 | 95 | -- Test apr.dir_open() and directory methods. 96 | local dir = assert(apr.dir_open '.') 97 | local entries = {} 98 | for name in dir:entries 'name' do entries[#entries+1] = handle_normalized_forms(name) end 99 | assert(dir:rewind()) 100 | local rewinded = {} 101 | for name in dir:entries 'name' do rewinded[#rewinded+1] = handle_normalized_forms(name) end 102 | assert(tostring(dir):find '^directory %([x%x]+%)$') 103 | assert(dir:close()) 104 | assert(tostring(dir):find '^directory %(closed%)$') 105 | table.sort(nonascii) 106 | table.sort(entries) 107 | table.sort(rewinded) 108 | if not pcall(function() 109 | for i = 1, math.max(#nonascii, #entries, #rewinded) do 110 | assert(nonascii[i] == entries[i]) 111 | assert(entries[i] == rewinded[i]) 112 | end 113 | end) then 114 | error("Directory traversal returned incorrect result?\n" 115 | .. 'Input strings: "' .. table.concat(nonascii, '", "') .. '"\n' 116 | .. 'Directory entries: "' .. table.concat(entries, '", "') .. '"\n' 117 | .. 'Rewinded entries: "' .. table.concat(rewinded, '", "') .. '"') 118 | end 119 | 120 | -- Remove temporary workspace directory 121 | assert(apr.filepath_set(cwd_save)) 122 | assert(apr.dir_remove_recursive(io_dir_tests)) 123 | -------------------------------------------------------------------------------- /test/io_file-bidi_pipes.lua: -------------------------------------------------------------------------------- 1 | local status, apr = pcall(require, 'apr') 2 | if not status then 3 | pcall(require, 'luarocks.require') 4 | apr = require 'apr' 5 | end 6 | local stdin = assert(apr.pipe_open_stdin()) 7 | local stdout = assert(apr.pipe_open_stdout()) 8 | local stderr = assert(apr.pipe_open_stderr()) 9 | while true do 10 | local input = stdin:read() 11 | if not input then break end 12 | stdout:write(input:lower(), '\n') 13 | stderr:write(input:upper(), '\n') 14 | end 15 | -------------------------------------------------------------------------------- /test/io_file-named_pipe.lua: -------------------------------------------------------------------------------- 1 | local status, apr = pcall(require, 'apr') 2 | if not status then 3 | pcall(require, 'luarocks.require') 4 | apr = require 'apr' 5 | end 6 | local namedpipe = assert(table.remove(arg, 1)) 7 | local namedmsg = assert(table.concat(arg, ' ')) 8 | local handle = assert(apr.file_open(namedpipe, 'w')) 9 | assert(handle:write(namedmsg)) 10 | assert(handle:close()) 11 | -------------------------------------------------------------------------------- /test/io_net-server.lua: -------------------------------------------------------------------------------- 1 | local status, apr = pcall(require, 'apr') 2 | if not status then 3 | pcall(require, 'luarocks.require') 4 | apr = require 'apr' 5 | end 6 | local server = assert(apr.socket_create()) 7 | assert(server:bind('*', arg[1])) 8 | assert(server:listen(1)) 9 | 10 | -- Signal to the test suite that we've initialized successfully? 11 | local handle = assert(io.open(arg[2], 'w')) 12 | assert(handle:write 'DONE') 13 | assert(handle:close()) 14 | 15 | local client = assert(server:accept()) 16 | for line in client:lines() do 17 | assert(client:write(line:upper(), '\n')) 18 | end 19 | assert(client:close()) 20 | -------------------------------------------------------------------------------- /test/io_net.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Unit tests for the network I/O handling module of the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: December 7, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | --]] 11 | 12 | local status, apr = pcall(require, 'apr') 13 | if not status then 14 | pcall(require, 'luarocks.require') 15 | apr = require 'apr' 16 | end 17 | local helpers = require 'apr.test.helpers' 18 | 19 | -- Test apr.hostname_get(), apr.host_to_addr() and apr.addr_to_host(). {{{1 20 | local hostname = assert(apr.hostname_get()) 21 | local rdns_ok = false 22 | for i, address in ipairs { assert(apr.host_to_addr(hostname)) } do 23 | if apr.addr_to_host(address) then 24 | rdns_ok = true 25 | end 26 | end 27 | if not rdns_ok then 28 | helpers.warning "Soft assertion failed: Reverse DNS of local host name failed!\n" 29 | end 30 | 31 | -- Test socket:bind(), socket:listen() and socket:accept(). {{{1 32 | local server = assert(apr.proc_create 'lua') 33 | local port = math.random(10000, 50000) 34 | local signalfile = helpers.tmpname() 35 | local scriptfile = helpers.scriptpath 'io_net-server.lua' 36 | assert(server:cmdtype_set 'shellcmd/env') 37 | assert(server:exec { scriptfile, port, signalfile }) 38 | assert(helpers.wait_for(signalfile, 15), "Failed to initialize " .. scriptfile) 39 | local client = assert(apr.socket_create()) 40 | assert(client:connect('localhost', port)) 41 | for _, msg in ipairs { 'First line', 'Second line', 'Third line' } do 42 | assert(client:write(msg, '\n')) 43 | assert(msg:upper() == assert(client:read())) 44 | end 45 | 46 | -- Test socket:fd_get() and socket:fd_set(). {{{1 47 | local fd = assert(client:fd_get()) 48 | local thread = assert(apr.thread(function() 49 | -- Load the Lua/APR binding. 50 | local apr = require 'apr' 51 | -- Convert file descriptor to socket. 52 | local client = assert(apr.socket_create()) 53 | assert(client:fd_set(fd)) 54 | local msg = 'So does it expose file descriptors?' 55 | assert(client:write(msg, '\n')) 56 | assert(msg:upper() == assert(client:read())) 57 | -- Test tostring(socket). 58 | assert(tostring(client):find '^socket %([x%x]+%)$') 59 | assert(client:close()) 60 | assert(tostring(client):find '^socket %(closed%)$') 61 | end)) 62 | assert(thread:join()) 63 | 64 | -- Test UDP server socket using socket:recvfrom() . {{{1 65 | 66 | local udp_port = math.random(10000, 50000) 67 | local udp_socket = assert(apr.socket_create 'udp') 68 | assert(udp_socket:bind('*', udp_port)) 69 | 70 | local server = assert(apr.thread(function() 71 | local client, data = assert(udp_socket:recvfrom()) 72 | assert(client.address == '127.0.0.1') 73 | assert(client.port >= 1024) 74 | assert(data == 'booyah!') 75 | end)) 76 | 77 | local client = assert(apr.thread(function() 78 | local apr = require 'apr' 79 | local socket = assert(apr.socket_create 'udp') 80 | assert(socket:connect('127.0.0.1', udp_port)) 81 | assert(socket:write 'booyah!') 82 | assert(socket:close()) 83 | end)) 84 | 85 | assert(server:join()) 86 | assert(client:join()) 87 | -------------------------------------------------------------------------------- /test/ldap-child.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Unit tests for the LDAP connection handling module of the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: December 3, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | The LDAP client tests need access to an LDAP server so if you want to run 11 | these tests you have to define the following environment variables: 12 | 13 | - LUA_APR_LDAP_URL is a URL indicating SSL, host name and port 14 | - LUA_APR_LDAP_WHO is the distinguished name used to bind (login) 15 | - LUA_APR_LDAP_PASSWD is the password used to bind (login) 16 | - LUA_APR_LDAP_BASE is the base of the directory (required to search) 17 | 18 | To enable the add()/modify()/compare()/delete() tests you will need to 19 | define the following environment variable to the indicated value: 20 | 21 | - LUA_APR_LDAP_WRITE_ALLOWED=yes 22 | 23 | --]] 24 | 25 | local status, apr = pcall(require, 'apr') 26 | if not status then 27 | pcall(require, 'luarocks.require') 28 | apr = require 'apr' 29 | end 30 | local helpers = require 'apr.test.helpers' 31 | 32 | -- Enable overriding of test configuration using environment variables. 33 | local SERVER = apr.env_get 'LUA_APR_LDAP_URL' 34 | local ACCOUNT = apr.env_get 'LUA_APR_LDAP_WHO' or nil 35 | local PASSWD = apr.env_get 'LUA_APR_LDAP_PASSWD' or nil 36 | local BASE = apr.env_get 'LUA_APR_LDAP_BASE' or nil 37 | local WRITE_ALLOWED = apr.env_get 'LUA_APR_LDAP_WRITE_ALLOWED' == 'yes' 38 | 39 | -- Test apr.ldap_url_check(). {{{1 40 | assert(apr.ldap_url_check 'ldap://root.openldap.org/dc=openldap,dc=org' == 'ldap') 41 | assert(apr.ldap_url_check 'ldapi://root.openldap.org/dc=openldap,dc=org' == 'ldapi') 42 | assert(apr.ldap_url_check 'ldaps://root.openldap.org/dc=openldap,dc=org' == 'ldaps') 43 | assert(not apr.ldap_url_check 'http://root.openldap.org/dc=openldap,dc=org') 44 | 45 | -- Test apr.ldap_url_parse(). {{{1 46 | local url = 'ldap://root.openldap.org/dc=openldap,dc=org' 47 | local components = assert(apr.ldap_url_parse(url)) 48 | assert(components.scheme == 'ldap') 49 | assert(components.host == 'root.openldap.org') 50 | assert(components.port == 389) 51 | assert(components.scope == 'sub') 52 | assert(components.dn == 'dc=openldap,dc=org') 53 | assert(components.crit_exts == 0) 54 | 55 | -- Test apr.ldap_info(). {{{1 56 | local info, errmsg = apr.ldap_info() 57 | if not info then 58 | helpers.warning "apr.ldap_info() failed, I'm guessing you don't have an LDAP library installed?\n" 59 | os.exit(1) 60 | end 61 | assert(type(info) == 'string' and info ~= '') 62 | 63 | -- Print the LDAP SDK being used (may be helpful in debugging). 64 | info = info:gsub('^APR LDAP: Built with ', '') 65 | helpers.message("\rRunning ldap tests using %s: ", info) 66 | 67 | -- Test apr.ldap(). {{{1 68 | local ldap_conn = assert(apr.ldap(SERVER)) 69 | 70 | -- Test tostring(ldap_conn). 71 | assert(tostring(ldap_conn):match '^LDAP connection %([0x%x]+%)$') 72 | assert(apr.type(ldap_conn) == 'LDAP connection') 73 | 74 | -- Test ldap_conn:bind(). {{{1 75 | local status, errmsg = ldap_conn:bind(ACCOUNT, PASSWD) 76 | if not status then 77 | helpers.warning("Failed to bind to LDAP server, I'm assuming it's not available (reason: %s)\n", errmsg) 78 | os.exit(1) 79 | end 80 | 81 | -- Test ldap_conn:option_get() and ldap_conn:option_set(). {{{1 82 | assert(ldap_conn:option_set('timeout', 0.5)) 83 | assert(ldap_conn:option_set('network-timeout', 0.5)) 84 | for option, typename in pairs { 85 | ['defbase'] = 'string', 86 | ['deref'] = 'number', 87 | ['network-timeout'] = 'number', 88 | ['protocol-version'] = 'number', 89 | ['refhop-limit'] = 'number', 90 | ['referrals'] = 'boolean', 91 | ['restart'] = 'boolean', 92 | ['size-limit'] = 'number', 93 | ['time-limit'] = 'number', 94 | ['timeout'] = 'number', 95 | ['uri'] = 'string', 96 | } do 97 | local value = ldap_conn:option_get(option) 98 | if value ~= nil then 99 | assert(type(value) == typename) 100 | local status, errmsg = ldap_conn:option_set(option, value) 101 | if not status then 102 | -- I've made this a warning instead of an error because I'm not even sure 103 | -- if all options can be set and whether this goes for all SDKs. 104 | helpers.warning("Failed to set LDAP option %s to %s! (reason: %s)\n", option, tostring(value), errmsg) 105 | end 106 | end 107 | end 108 | 109 | -- Skip search tests when $LUA_APR_LDAP_BASE isn't set. {{{1 110 | if not BASE then 111 | helpers.warning "Please set $LUA_APR_LDAP_BASE to enable the LDAP search tests ..\n" 112 | assert(ldap_conn:unbind()) 113 | return 114 | end 115 | 116 | -- Test ldap_conn:search(). {{{1 117 | local attributes = {} 118 | local valuetypes = {} 119 | for dn, attrs in ldap_conn:search { scope = 'sub', base = BASE } do 120 | assert(attrs.objectClass, "LDAP search result without objectClass?!") 121 | for k, v in pairs(attrs) do 122 | local t = type(v) 123 | attributes[k] = (attributes[k] or 0) + 1 124 | valuetypes[t] = (valuetypes[t] or 0) + 1 125 | end 126 | end 127 | -- Assuming the search matched some entries... 128 | if next(valuetypes) then 129 | -- Check that the supported value types were tested. 130 | assert(valuetypes.table > 0, "No table attributes (multiple values for one attribute) in LDAP directory?!") 131 | assert(valuetypes.string > 0, "No string attributes in LDAP directory?!") 132 | assert(valuetypes.string > valuetypes.table, "Expecting more string than table attributes?!") 133 | end 134 | -- Again, assuming the search matched some entries... 135 | if next(attributes) then 136 | -- Check that some common attributes were found. 137 | assert(attributes.cn > 0, "No common names matched in LDAP directory?!") 138 | assert(attributes.sn > 0, "No last names matched in LDAP directory?!") 139 | assert(attributes.givenName > 0, "No first names matched in LDAP directory?!") 140 | end 141 | 142 | -- Skip modification tests when $LUA_APR_LDAP_WRITE_ALLOWED isn't set. {{{1 143 | if not WRITE_ALLOWED then 144 | helpers.warning "Please set $LUA_APR_LDAP_WRITE_ALLOWED=yes to enable the LDAP modification tests ..\n" 145 | assert(ldap_conn:unbind()) 146 | return 147 | end 148 | 149 | local NEW_USER_GN = 'Lua/APR' 150 | local NEW_USER_SN = 'Test User' 151 | local NEW_USER_CN = 'lua_apr_testuser_1' 152 | local NEW_USER_DN = 'cn=' .. NEW_USER_CN .. ',' .. BASE 153 | local RENAMED_RDN = 'cn=lua_apr_testuser_2' 154 | local RENAMED_DN = RENAMED_RDN .. ',' .. BASE 155 | 156 | -- Cleanup records from previous (failed) runs. 157 | ldap_conn:delete(NEW_USER_DN)() 158 | ldap_conn:delete(RENAMED_DN)() 159 | 160 | -- Test ldap_conn:add(). {{{1 161 | assert(ldap_conn:add(NEW_USER_DN, { 162 | objectClass = { 'top', 'person', 'organizationalPerson', 'inetOrgPerson' }, 163 | cn = NEW_USER_CN, sn = NEW_USER_SN, givenName = NEW_USER_GN, 164 | })()) 165 | 166 | -- Test ldap_conn:modify(). {{{1 167 | 168 | local function test_modify(operation, attribute, old_value, new_value) 169 | -- Apply the modification. 170 | assert(ldap_conn:modify(NEW_USER_DN, { operation, [attribute] = new_value })()) 171 | -- Check that the modification was applied successfully. 172 | assert(ldap_conn:compare(NEW_USER_DN, attribute, new_value)() == true) 173 | end 174 | 175 | -- TODO More tests, also for '+' and '-' operations. 176 | test_modify('=', 'givenName', NEW_USER_GN, NEW_USER_GN .. ' (modified)') 177 | 178 | -- Test ldap_conn:rename() and ldap_conn:delete(). {{{1 179 | assert(ldap_conn:rename(NEW_USER_DN, RENAMED_RDN)()) -- copy record / create another reference. 180 | assert(ldap_conn:compare(RENAMED_DN, 'sn', NEW_USER_SN)() == true) 181 | 182 | -- Test ldap_conn:delete(). {{{1 183 | assert(ldap_conn:delete(RENAMED_DN)()) 184 | assert(ldap_conn:compare(RENAMED_DN, 'sn', NEW_USER_SN)() ~= true) 185 | 186 | -- Test ldap_conn:unbind(). {{{1 187 | assert(ldap_conn:unbind()) 188 | -------------------------------------------------------------------------------- /test/ldap.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Unit tests for the LDAP connection handling module of the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: October 29, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | This script executes the real test in ldap-child.lua as a child process. 11 | 12 | --]] 13 | 14 | local status, apr = pcall(require, 'apr') 15 | if not status then 16 | pcall(require, 'luarocks.require') 17 | apr = require 'apr' 18 | end 19 | local helpers = require 'apr.test.helpers' 20 | 21 | if not apr.ldap then 22 | helpers.warning "LDAP module not available!\n" 23 | return false 24 | end 25 | 26 | return helpers.ld_preload_trick 'ldap-child.lua' 27 | -------------------------------------------------------------------------------- /test/memcache.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Unit tests for the relational database module of the Lua/APR binding. 4 | 5 | Authors: 6 | - zhiguo zhao 7 | - Peter Odding 8 | Last Change: December 3, 2011 9 | Homepage: http://peterodding.com/code/lua/apr/ 10 | License: MIT 11 | 12 | These tests are based on http://svn.apache.org/viewvc/apr/apr/trunk/test/testmemcache.c?view=markup. 13 | If you have memcached running on a remote machine you can set the environment 14 | variable LUA_APR_MEMCACHE_HOST to the host name or IP-address of the server. 15 | You can also set LUA_APR_MEMCACHE_PORT to change the port number. 16 | 17 | --]] 18 | 19 | local status, apr = pcall(require, 'apr') 20 | if not status then 21 | pcall(require, 'luarocks.require') 22 | apr = require 'apr' 23 | end 24 | local helpers = require 'apr.test.helpers' 25 | 26 | -- You might have to change these depending on your environment. 27 | local server_host = apr.env_get 'LUA_APR_MEMCACHE_HOST' or '127.0.0.1' 28 | local default_port = apr.env_get 'LUA_APR_MEMCACHE_PORT' or 11211 29 | local max_servers = 10 30 | 31 | -- Test data. 32 | local prefix = 'testmemcache' 33 | local TDATA_SIZE = 500 34 | local txt = 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Duis at' .. 35 | 'lacus in ligula hendrerit consectetuer. Vestibulum tristique odio' .. 36 | 'iaculis leo. In massa arcu, ultricies a, laoreet nec, hendrerit non,' .. 37 | 'neque. Nulla sagittis sapien ac risus. Morbi ligula dolor, vestibulum' .. 38 | 'nec, viverra id, placerat dapibus, arcu. Curabitur egestas feugiat' .. 39 | 'tellus. Donec dignissim. Nunc ante. Curabitur id lorem. In mollis' .. 40 | 'tortor sit amet eros auctor dapibus. Proin nulla sem, tristique in,' .. 41 | 'convallis id, iaculis feugiat cras amet.' 42 | 43 | -- Create a memcached client object. 44 | local client = assert(apr.memcache(max_servers)) 45 | assert(apr.type(client) == 'memcache client') 46 | assert(tostring(client):find '^memcache client %([x%x]+%)$') 47 | local server = assert(client:add_server(server_host, default_port)) 48 | assert(client:enable_server(server)) 49 | 50 | -- Test metadata (server version and statistics). 51 | local version, message, code = client:version(server) 52 | if code == 'ECONNREFUSED' then 53 | helpers.warning("Looks like memcached isn't available! (%s)\n", message) 54 | return false 55 | end 56 | assert(type(version) == 'string') 57 | 58 | -- Print the Memcached server version (may be helpful in debugging). 59 | helpers.message('\rRunning memcache tests against %s server: ', version) 60 | 61 | local stats = assert(client:stats(server)) 62 | assert(type(stats) == 'table') 63 | assert(stats.version == version) 64 | assert(stats.pid >= 0) 65 | assert(stats.time >= 0) 66 | assert(stats.rusage_user >= 0) 67 | assert(stats.rusage_system >= 0) 68 | assert(stats.curr_items >= 0) 69 | assert(stats.total_items >= 0) 70 | assert(stats.bytes >= 0) 71 | assert(stats.curr_connections >= 0) 72 | assert(stats.total_connections >= 0) 73 | assert(stats.connection_structures >= 0) 74 | assert(stats.cmd_get >= 0) 75 | assert(stats.cmd_set >= 0) 76 | assert(stats.get_hits >= 0) 77 | assert(stats.get_misses >= 0) 78 | assert(stats.evictions >= 0) 79 | assert(stats.bytes_read >= 0) 80 | assert(stats.bytes_written >= 0) 81 | assert(stats.limit_maxbytes >= 0) 82 | assert(stats.threads >= 0) 83 | 84 | -- Generate test data from the lorem ipsum text above. 85 | local testpairs = {} 86 | math.randomseed(os.time()) 87 | for i = 1, TDATA_SIZE do 88 | testpairs[prefix .. i] = txt:sub(1, math.random(1, #txt)) 89 | end 90 | 91 | local function delete(client, key) 92 | -- clean up. 93 | if not client:delete(key) then 94 | -- https://github.com/xolox/lua-apr/issues/5#issuecomment-2106284 95 | assert(client:delete(key, 0)) 96 | end 97 | end 98 | 99 | -- Test client:add(), client:replace(), client:get() and client:delete(). 100 | for key, value in pairs(testpairs) do 101 | -- doesn't exist yet, fail. 102 | assert(not client:replace(key, value)) 103 | -- doesn't exist yet, succeed. 104 | assert(client:add(key, value)) 105 | helpers.checktuple({ true, value }, assert(client:get(key))) 106 | -- exists now, succeed. 107 | assert(client:replace(key, 'new')) 108 | -- make sure its different. 109 | helpers.checktuple({ true, 'new' }, assert(client:get(key))) 110 | -- exists now, fail. 111 | assert(not client:add(key, value)) 112 | -- clean up. 113 | delete(client, key) 114 | local status, value = client:get(key) 115 | assert(status and not value) 116 | end 117 | 118 | -- Test client:incr() and client:decr(). 119 | local value = 271 120 | assert(client:set(prefix, value)) 121 | for i = 1, TDATA_SIZE do 122 | local increment = math.random(1, TDATA_SIZE) 123 | value = value + increment 124 | assert(client:incr(prefix, increment) == value) 125 | local status, strval = assert(client:get(prefix)) 126 | assert(tonumber(strval) == value) 127 | local decrement = math.random(1, value) 128 | value = value - decrement 129 | assert(client:decr(prefix, decrement) == value) 130 | local status, strval = assert(client:get(prefix)) 131 | assert(tonumber(strval) == value) 132 | end 133 | delete(client, prefix) 134 | 135 | -- Test server management functions. 136 | for i = 1, max_servers - 1 do 137 | local port = default_port + i 138 | local fake_server = assert(client:add_server(server_host, port)) 139 | assert(apr.type(fake_server) == 'memcache server') 140 | assert(tostring(fake_server):find '^memcache server %([x%x]+%)$') 141 | assert(client:find_server(server_host, port)) 142 | assert(client:enable_server(fake_server)) 143 | local hash = client:hash(prefix) 144 | assert(hash > 0) 145 | assert(apr.type(client:find_server_hash(hash)) == 'memcache server') 146 | assert(client:disable_server(fake_server)) 147 | end 148 | -------------------------------------------------------------------------------- /test/misc.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Unit tests for the miscellaneous routines of the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: July 1, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | --]] 11 | 12 | local status, apr = pcall(require, 'apr') 13 | if not status then 14 | pcall(require, 'luarocks.require') 15 | apr = require 'apr' 16 | end 17 | local helpers = require 'apr.test.helpers' 18 | 19 | -- Test apr.type() 20 | local selfpath = helpers.filedefined() 21 | assert(apr.type(apr.file_open(selfpath)) == 'file') 22 | assert(apr.type(apr.socket_create()) == 'socket') 23 | assert(apr.type(apr.proc_create 'test') == 'process') 24 | assert(apr.type(apr.dir_open '.') == 'directory') 25 | 26 | -- Test apr.version_get() 27 | local v = apr.version_get() 28 | assert(v.apr:find '^%d+%.%d+%.%d+$') 29 | assert(v.aprutil == nil or v.aprutil:find '^%d+%.%d+%.%d+$') 30 | assert(v.apreq == nil or v.apreq:find '^%d+%.%d+%.%d+$') 31 | 32 | -- Test status_to_name() (indirectly). 33 | assert(select(3, apr.stat("I assume this won't exist")) == 'ENOENT') 34 | 35 | -- Test apr.os_default/locale_encoding() 36 | local default = apr.os_default_encoding() 37 | local locale = apr.os_locale_encoding() 38 | assert(type(default) == 'string' and default:find '%S') 39 | assert(type(locale) == 'string' and locale:find '%S') 40 | -------------------------------------------------------------------------------- /test/pollset.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Unit tests for the pollset module of the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: November 20, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | --]] 11 | 12 | local apr = require 'apr' 13 | local SERVER_PORT = math.random(10000, 40000) 14 | local NUM_CLIENTS = 25 15 | local main, server_loop, client_loop 16 | 17 | function main() -- {{{1 18 | local server_thread = assert(apr.thread(server_loop)) 19 | apr.sleep(0.25) 20 | local client_threads = {} 21 | for i = 1, NUM_CLIENTS do 22 | table.insert(client_threads, assert(apr.thread(client_loop))) 23 | end 24 | local _, served_clients = assert(server_thread:join()) 25 | for i = 1, NUM_CLIENTS do 26 | assert(client_threads[i]:join()) 27 | end 28 | assert(served_clients == NUM_CLIENTS) 29 | end 30 | 31 | function server_loop() -- {{{1 32 | local apr = require 'apr' 33 | -- Create the pollset object (+1 for the server socket). 34 | local pollset = assert(apr.pollset(NUM_CLIENTS + 1)) 35 | -- Create a server socket. 36 | local server = assert(apr.socket_create()) 37 | assert(server:bind('*', SERVER_PORT)) 38 | assert(server:listen(NUM_CLIENTS)) 39 | -- Add the server socket to the pollset. 40 | assert(pollset:add(server, 'input')) 41 | -- Loop to handle connections. 42 | local counter = 0 43 | while counter < NUM_CLIENTS do 44 | local readable = assert(pollset:poll(-1)) 45 | for _, socket in ipairs(readable) do 46 | if socket == server then 47 | local client = assert(server:accept()) 48 | assert(pollset:add(client, 'input')) 49 | else 50 | assert('first request line' == assert(socket:read())) 51 | assert(socket:write 'first response line\n') 52 | assert('second request line' == assert(socket:read())) 53 | assert(socket:write 'second response line\n') 54 | assert(pollset:remove(socket)) 55 | assert(socket:close()) 56 | counter = counter + 1 57 | end 58 | end 59 | end 60 | -- Remove the server socket from the pollset. 61 | assert(pollset:remove(server)) 62 | assert(server:close()) 63 | -- Destroy the pollset. 64 | assert(pollset:destroy()) 65 | return counter 66 | end 67 | 68 | function client_loop() -- {{{1 69 | local apr = require 'apr' 70 | local socket = assert(apr.socket_create()) 71 | assert(socket:connect('127.0.0.1', SERVER_PORT)) 72 | assert(socket:write 'first request line\n') 73 | assert('first response line' == assert(socket:read())) 74 | assert(socket:write 'second request line\n') 75 | assert('second response line' == assert(socket:read())) 76 | assert(socket:close()) 77 | end 78 | 79 | -- }}} 80 | 81 | main() 82 | 83 | -- vim: ts=2 sw=2 et tw=79 fen fdm=marker 84 | -------------------------------------------------------------------------------- /test/proc.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Unit tests for the process handling module of the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: June 23, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | --]] 11 | 12 | local status, apr = pcall(require, 'apr') 13 | if not status then 14 | pcall(require, 'luarocks.require') 15 | apr = require 'apr' 16 | end 17 | local helpers = require 'apr.test.helpers' 18 | 19 | local function newchild(cmdtype, script, env) 20 | local child = assert(apr.proc_create 'lua') 21 | assert(child:cmdtype_set(cmdtype)) 22 | if env then child:env_set(env) end 23 | assert(child:io_set('child-block', 'parent-block', 'parent-block')) 24 | assert(child:exec { helpers.scriptpath(script) }) 25 | return child, assert(child:in_get()), assert(child:out_get()), assert(child:err_get()) 26 | end 27 | 28 | -- Test bidirectional pipes. {{{1 29 | local child, stdin, stdout, stderr = newchild('shellcmd/env', 'io_file-bidi_pipes.lua') 30 | local testmsg = "Is There Anyone Out There?!" 31 | assert(stdin:write(testmsg)); assert(stdin:close()) 32 | assert(stdout:read() == testmsg:lower()) 33 | assert(stderr:read() == testmsg:upper()) 34 | 35 | -- Test tostring(process). {{{1 36 | assert(tostring(child):find '^process %([x%x]+%)$') 37 | 38 | -- Test child environment manipulation. {{{1 39 | local child = newchild('shellcmd', 'test-child-env.lua', { 40 | LUA_APR_MAGIC_ENV_KEY = apr._VERSION, -- This is the only thing we're interested in testing… 41 | SystemRoot = apr.env_get 'SystemRoot', -- needed on Windows XP, without it code = -1072365564 below 42 | }) 43 | local done, why, code = assert(child:wait(true)) 44 | assert(done == true) 45 | assert(why == 'exit') 46 | -- TODO I thought I'd finally fixed the "incorrect subprocess return codes" 47 | -- problem but it's back; now it only triggers when I run the test suite 48 | -- under Lua/APR on Linux installed through LuaRocks :-\ 49 | -- assert(code == 42) 50 | 51 | -- Test apr.proc_fork() when supported. {{{1 52 | if apr.proc_fork then 53 | local forked_file, forked_text = assert(helpers.tmpname()), 'hello from forked child!' 54 | local process, context = assert(apr.proc_fork()) 55 | if context == 'child' then 56 | helpers.writefile(forked_file, forked_text) 57 | os.exit(0) 58 | elseif context == 'parent' then 59 | assert(helpers.wait_for(forked_file, 10), "Forked child failed to create file?!") 60 | assert(helpers.readfile(forked_file) == forked_text) 61 | end 62 | end 63 | 64 | -- Test apr.namedpipe_create(). {{{1 65 | 66 | local namedpipe = helpers.tmpname() 67 | local namedmsg = "Hello world over a named pipe!" 68 | local status, errmsg, errcode = apr.namedpipe_create(namedpipe) 69 | if errcode ~= 'ENOTIMPL' then 70 | local child = assert(apr.proc_create 'lua') 71 | assert(child:cmdtype_set('shellcmd/env')) 72 | assert(child:exec { helpers.scriptpath 'io_file-named_pipe.lua', namedpipe, namedmsg }) 73 | local handle = assert(apr.file_open(namedpipe, 'r')) 74 | assert(namedmsg == handle:read()) 75 | assert(handle:close()) 76 | end 77 | 78 | --[[ 79 | 80 | TODO Investigate escaping problem in apr_proc_create() ?! 81 | 82 | I originally used the following message above: 83 | 84 | local namedmsg = "Hello world over a named pipe! :-)" 85 | 86 | Which caused the following error message: 87 | 88 | /bin/sh: Syntax error: ")" unexpected 89 | 90 | Using strace as follows I can see the escaping is lost: 91 | 92 | $ strace -vfe trace=execve -s 250 lua etc/tests.lua 93 | [pid 30868] execve("/bin/sh", ["/bin/sh", "-c", "lua etc/test-namedpipe.lua /tmp/lua-apr-tempfile-66 Hello world over a named pipe! :-)"], [...]) = 0 94 | 95 | After removing the smiley the syntax errors disappeared but the words in 96 | "namedmsg" are received as individual arguments by the test-namedpipe.lua 97 | script :-\ 98 | 99 | --]] 100 | 101 | -------------------------------------------------------------------------------- /test/serialize.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Tests for the serialization function of the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: November 20, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | ]] 11 | 12 | local status, apr = pcall(require, 'apr') 13 | if not status then 14 | pcall(require, 'luarocks.require') 15 | apr = require 'apr' 16 | end 17 | 18 | function main() 19 | 20 | -- Test single, scalar values. {{{1 21 | assert(roundtrip(nil), "Failed to serialize nil value") 22 | assert(roundtrip(true), "Failed to serialize true value") 23 | assert(roundtrip(false), "Failed to serialize false value") 24 | assert(roundtrip(0), "Failed to serialize number value (0)") 25 | assert(roundtrip(1), "Failed to serialize number value (1)") 26 | assert(roundtrip(-1), "Failed to serialize number value (-1)") 27 | assert(roundtrip(math.pi), "Failed to serialize number value (math.pi)") 28 | assert(roundtrip(math.huge), "Failed to serialize number value (math.huge)") 29 | assert(roundtrip(''), "Failed to serialize empty string") 30 | assert(roundtrip('simple'), "Failed to serialize simple string ('simple')") 31 | assert(roundtrip('foo\nbar\rbaz\0qux'), "Failed to serialize complex string ('foo\\nbar\\rbaz\\0qux')") 32 | 33 | -- Test multiple scalar values (a tuple). {{{1 34 | assert(roundtrip(true, false, nil, 13), "Failed to serialize tuple with nil value") 35 | 36 | -- Test tables (empty, list, dictionary, nested). {{{1 37 | assert(roundtrip({}), "Failed to serialize empty table") 38 | assert(roundtrip({1, 2, 3, 4, 5}), "Failed to serialize list like table ({1, 2, 3, 4, 5})") 39 | assert(roundtrip({pi=math.pi}), "Failed to serialize table ({pi=3.14})") 40 | assert(roundtrip({nested={42}}), "Failed to serialize nested table ({nested={42}})") 41 | 42 | -- Test tables with cycles. {{{1 43 | local a, b = {}, {}; a.b, b.a = b, a 44 | local chunk = apr.serialize(a, b) 45 | local a2, b2 = apr.unserialize(chunk) 46 | assert(a2.b == b2 and b2.a == a2) 47 | 48 | -- Test simple Lua function. {{{1 49 | assert(roundtrip(function() return 42 end), "Failed to serialize simple function (return 42)") 50 | 51 | -- Test Lua function with scalar upvalue. {{{1 52 | local simple_upvalue = 42 53 | assert(roundtrip(function() return simple_upvalue end), "Failed to serialize function with scalar upvalue") 54 | 55 | -- Test Lua function with multiple upvalues. {{{1 56 | local a, b, c, d = 1, 9, 8, 6 57 | assert(roundtrip(function() return a, b, c, d end), "Failed to serialize function with multiple upvalues") 58 | 59 | -- Test Lua function with complex upvalues. {{{1 60 | local nested = {1, 2, 3, 4, 5} 61 | local complex_upvalue = {pi=math.pi, string=name_of_upvalue, nested=nested} 62 | assert(roundtrip(function() return nested, complex_upvalue end), "Failed to serialize function with complex upvalues") 63 | 64 | -- Test Lua/APR userdata. {{{1 65 | local object = apr.pipe_open_stdin() 66 | local data = apr.serialize(object) 67 | local result = apr.unserialize(data) 68 | assert(object == result, "Failed to preserve userdata identity!") 69 | 70 | end 71 | 72 | function pack(...) 73 | return { n = select('#', ...), ... } 74 | end 75 | 76 | function roundtrip(...) 77 | return deepequals(pack(...), pack(apr.unserialize(apr.serialize(...)))) 78 | end 79 | 80 | function deepequals(a, b) 81 | if a == b then return true end 82 | local at, bt = type(a), type(b) 83 | if at ~= bt then return false end 84 | if at == 'function' then 85 | -- Compare functions based on return values. 86 | return deepequals(pack(a()), pack(b())) 87 | end 88 | if at ~= 'table' then 89 | -- Everything except functions and tables can be compared literally. 90 | return a == b 91 | end 92 | -- Compare the two tables by iterating the keys of both tables. 93 | for k, v in pairs(a) do 94 | if not deepequals(v, b[k]) then 95 | return false 96 | end 97 | end 98 | for k, v in pairs(b) do 99 | if not deepequals(v, a[k]) then 100 | return false 101 | end 102 | end 103 | return true 104 | end 105 | 106 | main() 107 | -------------------------------------------------------------------------------- /test/shm-child.lua: -------------------------------------------------------------------------------- 1 | local status, apr = pcall(require, 'apr') 2 | if not status then 3 | pcall(require, 'luarocks.require') 4 | apr = require 'apr' 5 | end 6 | local shm_file = assert(apr.shm_attach(arg[1])) 7 | local tmp_file = assert(io.open(arg[2])) 8 | assert(shm_file:write(tmp_file:read('*a'))) 9 | 10 | -- You don't actually have to call this, I'm 11 | -- just testing that it works as advertised :-) 12 | assert(shm_file:detach()) 13 | 14 | -- Check that removing works on supported platforms. 15 | local status, errmsg, errcode = apr.shm_remove(arg[1]) 16 | if errcode ~= 'EACCES' then 17 | assert(status, errmsg) 18 | assert(not apr.shm_attach(arg[1])) 19 | end 20 | -------------------------------------------------------------------------------- /test/shm.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Unit tests for the shared memory module of the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: March 27, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | --]] 11 | 12 | local status, apr = pcall(require, 'apr') 13 | if not status then 14 | pcall(require, 'luarocks.require') 15 | apr = require 'apr' 16 | end 17 | local helpers = require 'apr.test.helpers' 18 | local testdata = [[ 19 | 1 20 | 3.1 21 | 100 22 | 0xCAFEBABE 23 | 0xDEADBEEF 24 | 3.141592653589793115997963468544185161590576171875 25 | this line is in fact not a number :-) 26 | 27 | that was an empty line]] 28 | 29 | -- Write test data to file. 30 | local tmp_path = helpers.tmpname() 31 | helpers.writefile(tmp_path, testdata) 32 | 33 | -- Create shared memory segment. 34 | local shm_path = assert(helpers.tmpname()) 35 | local shm_file = assert(apr.shm_create(shm_path, #testdata)) 36 | assert(tostring(shm_file):find '^shared memory %([0x%x]+%)$') 37 | 38 | -- Launch child process. 39 | local child = assert(apr.proc_create('lua')) 40 | assert(child:cmdtype_set 'shellcmd/env') 41 | assert(child:exec { helpers.scriptpath 'shm-child.lua', shm_path, tmp_path }) 42 | assert(child:wait(true)) 43 | 44 | -- Execute buffered I/O tests. 45 | local test_buffer = require(((...):gsub('shm$', 'io_buffer'))) 46 | test_buffer(tmp_path, shm_file) 47 | 48 | -- Destroy the shared memory segment. 49 | assert(shm_file:destroy()) 50 | -------------------------------------------------------------------------------- /test/signal.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Unit tests for the signal handling module of the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: July 3, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | I wanted to write this test as follows: Start a child process that listens for 11 | SIGKILL and/or SIGQUIT, kill the child process using process:kill() and check 12 | that the child process received the signals. Unfortunately it doesn't... 13 | 14 | The only other useful way I could think of to test that signals work is what 15 | I've now implemented in this test: Listen for SIGCHLD, start and then kill a 16 | child process and check that the parent process received SIGCHLD. 17 | 18 | --]] 19 | 20 | local status, apr = pcall(require, 'apr') 21 | if not status then 22 | pcall(require, 'luarocks.require') 23 | apr = require 'apr' 24 | end 25 | local helpers = require 'apr.test.helpers' 26 | 27 | -- The ISO C standard only requires the signal names SIGABRT, SIGFPE, SIGILL, 28 | -- SIGINT, SIGSEGV, and SIGTERM to be defined (and these are in fact the only 29 | -- signal names defined on Windows). 30 | local names = apr.signal_names() 31 | assert(names.SIGABRT) 32 | assert(names.SIGFPE) 33 | assert(names.SIGILL) 34 | assert(names.SIGINT) 35 | assert(names.SIGSEGV) 36 | assert(names.SIGTERM) 37 | 38 | -- apr.signal() tests using apr.signal_raise(). {{{1 39 | 40 | local got_sigabrt = false 41 | apr.signal('SIGABRT', function() 42 | got_sigabrt = true 43 | end) 44 | assert(apr.signal_raise 'SIGABRT') 45 | assert(got_sigabrt) 46 | 47 | local got_sigterm = false 48 | apr.signal('SIGTERM', function() 49 | got_sigterm = true 50 | end) 51 | assert(apr.signal_raise 'SIGTERM') 52 | assert(got_sigterm) 53 | 54 | -- apr.signal() tests using real signals. {{{1 55 | 56 | if apr.platform_get() ~= 'WIN32' then 57 | 58 | -- Spawn a child process that dies. 59 | local function spawn() 60 | local child = assert(apr.proc_create 'lua') 61 | assert(child:cmdtype_set 'program/env/path') 62 | assert(child:exec { '-e', 'os.exit(0)' }) 63 | assert(child:wait(true)) 64 | end 65 | 66 | -- Use apr.signal() to listen for dying child processes. 67 | local got_sigchild = false 68 | apr.signal('SIGCHLD', function() 69 | got_sigchild = true 70 | end) 71 | 72 | -- Create and kill a child process. 73 | spawn() 74 | 75 | -- Make sure we got the signal. 76 | assert(got_sigchild) 77 | 78 | -- Test that apr.signal_block() blocks the signal. 79 | got_sigchild = false 80 | assert(apr.signal_block 'SIGCHLD') 81 | 82 | -- Create and kill a child process. 83 | spawn() 84 | 85 | -- Make sure we didn't get the signal. 86 | assert(not got_sigchild) 87 | 88 | -- Test that apr.signal_unblock() unblocks the signal. 89 | assert(apr.signal_unblock 'SIGCHLD') 90 | 91 | -- Create and kill a child process. 92 | spawn() 93 | 94 | -- Make sure we got the signal. 95 | assert(got_sigchild) 96 | 97 | -- Test that signal handlers can be disabled. 98 | apr.signal('SIGCHLD', nil) 99 | got_sigchild = false 100 | 101 | -- Create and then kill a child process. 102 | spawn() 103 | 104 | -- Make sure the old signal handler was not executed. 105 | assert(not got_sigchild) 106 | 107 | end 108 | -------------------------------------------------------------------------------- /test/str.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Unit tests for the string routines module of the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: March 27, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | --]] 11 | 12 | local status, apr = pcall(require, 'apr') 13 | if not status then 14 | pcall(require, 'luarocks.require') 15 | apr = require 'apr' 16 | end 17 | 18 | -- Test natural comparison. 19 | local filenames = { 'rfc2086.txt', 'rfc1.txt', 'rfc822.txt' } 20 | table.sort(filenames, apr.strnatcmp) 21 | assert(filenames[1] == 'rfc1.txt') 22 | assert(filenames[2] == 'rfc822.txt') 23 | assert(filenames[3] == 'rfc2086.txt') 24 | 25 | -- Test case insensitive natural comparison. 26 | local filenames = { 'RFC2086.txt', 'RFC1.txt', 'rfc822.txt' } 27 | table.sort(filenames, apr.strnatcasecmp) 28 | assert(filenames[1] == 'RFC1.txt') 29 | assert(filenames[2] == 'rfc822.txt') 30 | assert(filenames[3] == 'RFC2086.txt') 31 | 32 | -- Test binary size formatting. 33 | assert(apr.strfsize(1024^1) == '1.0K') 34 | assert(apr.strfsize(1024^2) == '1.0M') 35 | assert(apr.strfsize(1024^3) == '1.0G') 36 | 37 | -- Test command line tokenization. 38 | local command = [[ program argument1 "argument 2" 'argument 3' argument\ 4 ]] 39 | local cmdline = assert(apr.tokenize_to_argv(command)) 40 | assert(cmdline[1] == 'program') 41 | assert(cmdline[2] == 'argument1') 42 | assert(cmdline[3] == 'argument 2') 43 | assert(cmdline[4] == 'argument 3') 44 | assert(cmdline[5] == 'argument 4') 45 | -------------------------------------------------------------------------------- /test/thread-child.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Unit tests for the multi threading module of the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: November 20, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | This script is executed as a child process by thread.lua. 11 | 12 | --]] 13 | 14 | local status, apr = pcall(require, 'apr') 15 | if not status then 16 | pcall(require, 'luarocks.require') 17 | apr = require 'apr' 18 | end 19 | local helpers = require 'apr.test.helpers' 20 | 21 | -- Check that yield() exists, can be called and does mostly nothing :-) 22 | assert(select('#', apr.thread_yield()) == 0) 23 | 24 | -- Test thread creation. 25 | local threadfile = helpers.tmpname() 26 | local thread = assert(apr.thread(function() 27 | local handle = assert(io.open(threadfile, 'w')) 28 | assert(handle:write 'hello world!') 29 | assert(handle:close()) 30 | end)) 31 | assert(thread:join()) 32 | 33 | -- Check that the file was actually created inside the thread. 34 | assert(helpers.readfile(threadfile) == 'hello world!') 35 | 36 | -- Test that package.config, package.path and package.cpath are preserved. 37 | local config, path, cpath = package.config, package.path, package.cpath 38 | local thread = assert(apr.thread(function() 39 | assert(package.config == config, "apr.thread() failed to preserve package.config") 40 | assert(package.path == path, "apr.thread() failed to preserve package.path") 41 | assert(package.cpath == cpath, "apr.thread() failed to preserve package.cpath") 42 | end)) 43 | assert(thread:join()) 44 | 45 | -- Test module loading and multiple return values. 46 | local thread = assert(apr.thread(function() 47 | local status, apr = pcall(require, 'apr') 48 | if not status then 49 | pcall(require, 'luarocks.require') 50 | apr = require 'apr' 51 | end 52 | return apr.version_get().apr 53 | end)) 54 | helpers.checktuple({ true, apr.version_get().apr }, assert(thread:join())) 55 | 56 | -- Test thread:status() 57 | local thread = assert(apr.thread(function() 58 | local status, apr = pcall(require, 'apr') 59 | if not status then 60 | pcall(require, 'luarocks.require') 61 | apr = require 'apr' 62 | end 63 | apr.sleep(1) 64 | end)) 65 | while assert(thread:status()) == 'init' do apr.sleep(0.1) end 66 | assert('running' == assert(thread:status())) 67 | assert(thread:join()) 68 | assert('done' == assert(thread:status())) 69 | -------------------------------------------------------------------------------- /test/thread.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Unit tests for the multi threading module of the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: November 20, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | This script runs the multi threading tests in a child process to 11 | protect the test suite from crashing on unsupported platforms. 12 | 13 | --]] 14 | 15 | local status, apr = pcall(require, 'apr') 16 | if not status then 17 | pcall(require, 'luarocks.require') 18 | apr = require 'apr' 19 | end 20 | local helpers = require 'apr.test.helpers' 21 | 22 | if not apr.thread then 23 | helpers.warning "Multi threading module not available!\n" 24 | return false 25 | end 26 | 27 | local child = assert(apr.proc_create 'lua') 28 | assert(child:cmdtype_set 'shellcmd/env') 29 | assert(child:exec { helpers.scriptpath 'thread-child.lua' }) 30 | local dead, reason, code = assert(child:wait(true)) 31 | return reason == 'exit' and code == 0 32 | -------------------------------------------------------------------------------- /test/thread_queue-child.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Unit tests for the thread queues module of the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: November 20, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | This script is executed as a child process by thread_queue.lua. 11 | 12 | --]] 13 | 14 | local status, apr = pcall(require, 'apr') 15 | if not status then 16 | pcall(require, 'luarocks.require') 17 | apr = require 'apr' 18 | end 19 | local helpers = require 'apr.test.helpers' 20 | 21 | -- Create a thread queue with space for one tuple. 22 | local queue = assert(apr.thread_queue(1)) 23 | 24 | -- Test that the queue starts empty. 25 | assert(not queue:trypop()) 26 | 27 | -- Pass the thread queue to a thread. 28 | local thread = assert(apr.thread(function() 29 | local status, apr = pcall(require, 'apr') 30 | if not status then 31 | pcall(require, 'luarocks.require') 32 | apr = require 'apr' 33 | end 34 | local helpers = require 'apr.test.helpers' 35 | helpers.try(function() 36 | -- Scalar values. 37 | assert(queue:push(nil)) 38 | assert(queue:push(false)) 39 | assert(queue:push(true)) 40 | assert(queue:push(42)) 41 | assert(queue:push(math.pi)) 42 | assert(queue:push "hello world through a queue!") 43 | -- Tuples. 44 | assert(queue:push(true, false, 13, math.huge, _VERSION)) 45 | -- Object values. 46 | assert(queue:push(queue)) 47 | assert(queue:push(apr.pipe_open_stdin())) 48 | assert(queue:push(apr.socket_create())) 49 | end, function(errmsg) 50 | helpers.message("Thread queue tests failed in child thread: %s\n", errmsg) 51 | assert(queue:terminate()) 52 | end) 53 | end)) 54 | 55 | helpers.try(function() 56 | -- Check the sequence of supported value types. 57 | assert(queue:pop() == nil) 58 | assert(queue:pop() == false) 59 | assert(queue:pop() == true) 60 | assert(queue:pop() == 42) 61 | assert(queue:pop() == math.pi) 62 | assert(queue:pop() == "hello world through a queue!") 63 | -- Check that multiple values are supported. 64 | local expected = { true, false, 13, math.huge, _VERSION } 65 | helpers.checktuple(expected, assert(queue:pop())) 66 | -- These test that Lua/APR objects can be passed between threads and that 67 | -- objects which are really references __equal the object they reference. 68 | assert(assert(queue:pop()) == queue) 69 | assert(apr.type(queue:pop()) == 'file') 70 | assert(apr.type(queue:pop()) == 'socket') 71 | -- Now make sure the queue is empty again. 72 | assert(not queue:trypop()) 73 | -- Make sure trypush() works as expected. 74 | assert(queue:push(1)) -- the thread queue is now full 75 | assert(not queue:trypush(2)) -- thus trypush() should fail 76 | assert(thread:join()) 77 | end, function(errmsg) 78 | helpers.message("Thread queue tests failed in parent thread: %s\n", errmsg) 79 | assert(queue:terminate()) 80 | os.exit(1) 81 | end) 82 | -------------------------------------------------------------------------------- /test/thread_queue.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Unit tests for the thread queues module of the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: May 15, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | This script runs the thread queue tests in a child process to 11 | protect the test suite from crashing on unsupported platforms. 12 | 13 | --]] 14 | 15 | local status, apr = pcall(require, 'apr') 16 | if not status then 17 | pcall(require, 'luarocks.require') 18 | apr = require 'apr' 19 | end 20 | local helpers = require 'apr.test.helpers' 21 | 22 | if not apr.thread_queue then 23 | helpers.warning "Thread queues module not available!\n" 24 | return false 25 | end 26 | 27 | local child = assert(apr.proc_create 'lua') 28 | assert(child:cmdtype_set 'shellcmd/env') 29 | assert(child:exec { helpers.scriptpath 'thread_queue-child.lua' }) 30 | local dead, reason, code = assert(child:wait(true)) 31 | return reason == 'exit' and code == 0 32 | -------------------------------------------------------------------------------- /test/time.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Unit tests for the time routines module of the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: June 16, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | --]] 11 | 12 | local status, apr = pcall(require, 'apr') 13 | if not status then 14 | pcall(require, 'luarocks.require') 15 | apr = require 'apr' 16 | end 17 | 18 | -- Based on http://svn.apache.org/viewvc/apr/apr/trunk/test/testtime.c?view=markup. 19 | 20 | local now = 1032030336186711 / 1000000 21 | 22 | -- Check that apr.time_now() more or less matches os.time() 23 | assert(math.abs(os.time() - apr.time_now()) <= 2) 24 | 25 | -- apr.time_explode() using apr_time_exp_lt() 26 | local posix_exp = os.date('*t', now) 27 | local xt = assert(apr.time_explode(now)) -- apr_time_exp_lt() 28 | for k, v in pairs(posix_exp) do assert(v == xt[k]) end 29 | 30 | -- apr.time_implode() on a local time table (ignores floating point precision 31 | -- because on my laptop "now" equals 1032030336.186711 while "imp" equals 32 | -- 1032037536.186710) 33 | local imp = assert(apr.time_implode(xt)) -- apr_time_exp_gmt_get() 34 | assert(math.floor(now) == math.floor(imp)) 35 | 36 | -- apr.time_implode() on a GMT time table 37 | local xt = assert(apr.time_explode(now, true)) -- apr_time_exp_gmt() 38 | local imp = assert(apr.time_implode(xt)) -- apr_time_exp_gmt_get() 39 | assert(math.floor(now) == math.floor(imp)) 40 | 41 | -- Test apr.time_format() (example from http://en.wikipedia.org/wiki/Unix_time) 42 | assert(apr.time_format('rfc822', 1000000000) == 'Sun, 09 Sep 2001 01:46:40 GMT') 43 | assert(apr.time_format('%Y-%m', 1000000000) == '2001-09') 44 | 45 | -- Test that apr.sleep() supports sub second resolution. 46 | local before = apr.time_now() 47 | apr.sleep(0.25) 48 | local after = apr.time_now() 49 | assert(string.format('%.1f', after - before):find '^0%.[123]$') 50 | -------------------------------------------------------------------------------- /test/uri.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Unit tests for the URI parsing module of the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: March 27, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | --]] 11 | 12 | local status, apr = pcall(require, 'apr') 13 | if not status then 14 | pcall(require, 'luarocks.require') 15 | apr = require 'apr' 16 | end 17 | local hostinfo = 'scheme://user:pass@host:80' 18 | local pathinfo = '/path/file?query-param=value#fragment' 19 | local input = hostinfo .. pathinfo 20 | 21 | -- Parse a URL into a table of components. 22 | local parsed = assert(apr.uri_parse(input)) 23 | 24 | -- Validate the parsed URL fields. 25 | assert(parsed.scheme == 'scheme') 26 | assert(parsed.user == 'user') 27 | assert(parsed.password == 'pass') 28 | assert(parsed.hostname == 'host') 29 | assert(parsed.port == '80') 30 | assert(parsed.hostinfo == 'user:pass@host:80') 31 | assert(parsed.path == '/path/file') 32 | assert(parsed.query == 'query-param=value') 33 | assert(parsed.fragment == 'fragment') 34 | 35 | -- Check that complete and partial URL `unparsing' works. 36 | assert(apr.uri_unparse(parsed) == input) 37 | assert(apr.uri_unparse(parsed, 'hostinfo') == hostinfo) 38 | assert(apr.uri_unparse(parsed, 'pathinfo') == pathinfo) 39 | 40 | -- Make sure uri_port_of_scheme() works. 41 | assert(apr.uri_port_of_scheme 'ssh' == 22) 42 | assert(apr.uri_port_of_scheme 'http' == 80) 43 | assert(apr.uri_port_of_scheme 'https' == 443) 44 | -------------------------------------------------------------------------------- /test/user.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Unit tests for the user/group identification module of the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: March 27, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | Note that these tests don't assert() on anything useful because that would 11 | make it impossible to run the Lua/APR test suite in "chrooted" build 12 | environments and such.. 13 | 14 | --]] 15 | 16 | local status, apr = pcall(require, 'apr') 17 | if not status then 18 | pcall(require, 'luarocks.require') 19 | apr = require 'apr' 20 | end 21 | local helpers = require 'apr.test.helpers' 22 | 23 | -- Get the name and primary group of the current user. 24 | local apr_user, apr_group = assert(apr.user_get()) 25 | assert(type(apr_user) == 'string' and apr_user ~= '') 26 | assert(type(apr_group) == 'string' and apr_group ~= '') 27 | 28 | local function report(...) 29 | helpers.warning(...) 30 | helpers.message("This might not be an error, e.g. when using a chroot, which is why the tests will continue as normal.\n") 31 | end 32 | 33 | -- Try to match the result of apr.user_get() against $USER. 34 | local env_user = apr.env_get 'USER' or apr.env_get 'USERNAME' or '' 35 | if apr_user ~= env_user then 36 | report("$USER == %q but apr.user_get() == %q\n", env_user, apr_user) 37 | return false 38 | end 39 | 40 | -- Try to match the result of apr.user_homepath_get() against $HOME. 41 | local function normpath(p) 42 | return assert(apr.filepath_merge('', p, 'native')) 43 | end 44 | local env_home = normpath(apr.env_get 'HOME' or apr.env_get 'USERPROFILE' or '') 45 | local apr_home = normpath(assert(apr.user_homepath_get(apr_user))) 46 | if apr_home ~= env_home then 47 | report("$HOME == %q but apr.user_homepath_get(%q) == %q\n", env_home, apr_user, apr_home) 48 | return false 49 | end 50 | -------------------------------------------------------------------------------- /test/uuid.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Unit tests for the universally unique identifiers module of the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: March 27, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | --]] 11 | 12 | local status, apr = pcall(require, 'apr') 13 | if not status then 14 | pcall(require, 'luarocks.require') 15 | apr = require 'apr' 16 | end 17 | 18 | -- Check that apr.uuid_get() returns at least 500 KB of unique strings. 19 | local set = {} 20 | assert(pcall(function() 21 | for i = 1, 32000 do 22 | uuid = assert(apr.uuid_get()) 23 | assert(not set[uuid], 'duplicate UUID!') 24 | set[uuid] = true 25 | end 26 | end)) 27 | 28 | -- I can't find any binary/formatted example UUIDs on the internet and don't 29 | -- really know how to make the following tests useful. At least they illustrate 30 | -- the purpose… 31 | 32 | -- Check that apr.uuid_format() works. 33 | assert(apr.uuid_format(('\0'):rep(16)) == '00000000-0000-0000-0000-000000000000') 34 | assert(apr.uuid_format(('\255'):rep(16)) == 'ffffffff-ffff-ffff-ffff-ffffffffffff') 35 | 36 | -- Check that apr.uuid_parse() works. 37 | assert(apr.uuid_parse '00000000-0000-0000-0000-000000000000' == ('\0'):rep(16)) 38 | assert(apr.uuid_parse 'ffffffff-ffff-ffff-ffff-ffffffffffff' == ('\255'):rep(16)) 39 | -------------------------------------------------------------------------------- /test/xlate.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Unit tests for the character encoding translation module of the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: July 3, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | --]] 11 | 12 | local status, apr = pcall(require, 'apr') 13 | if not status then 14 | pcall(require, 'luarocks.require') 15 | apr = require 'apr' 16 | end 17 | local helpers = require 'apr.test.helpers' 18 | 19 | local utf8 = "Edelwei\195\159" 20 | local utf7 = "Edelwei+AN8-" 21 | local latin1 = "Edelwei\223" 22 | local latin2 = "Edelwei\223" 23 | 24 | -- 1. Identity transformation: UTF-8 -> UTF-8 25 | assert(utf8 == assert(apr.xlate(utf8, 'UTF-8', 'UTF-8'))) 26 | 27 | -- 2. UTF-8 <-> ISO-8859-1 28 | assert(latin1 == assert(apr.xlate(utf8, 'UTF-8', 'ISO-8859-1'), "(known to fail on Windows)")) 29 | assert(utf8 == assert(apr.xlate(latin1, 'ISO-8859-1', 'UTF-8'))) 30 | 31 | -- 3. ISO-8859-1 <-> ISO-8859-2, identity 32 | assert(latin2 == assert(apr.xlate(latin1, 'ISO-8859-1', 'ISO-8859-2'))) 33 | assert(latin1 == assert(apr.xlate(latin2, 'ISO-8859-2', 'ISO-8859-1'))) 34 | 35 | -- 4. Transformation using character set aliases 36 | assert(utf7 == assert(apr.xlate(utf8, 'UTF-8', 'UTF-7'))) 37 | assert(utf8 == assert(apr.xlate(utf7, 'UTF-7', 'UTF-8'))) 38 | -------------------------------------------------------------------------------- /test/xml.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Unit tests for the XML parsing module of the Lua/APR binding. 4 | 5 | Author: Peter Odding 6 | Last Change: March 27, 2011 7 | Homepage: http://peterodding.com/code/lua/apr/ 8 | License: MIT 9 | 10 | --]] 11 | 12 | local status, apr = pcall(require, 'apr') 13 | if not status then 14 | pcall(require, 'luarocks.require') 15 | apr = require 'apr' 16 | end 17 | local helpers = require 'apr.test.helpers' 18 | 19 | local function parse_xml(text) 20 | local parser = assert(apr.xml()) 21 | assert(parser:feed(text)) 22 | assert(parser:done()) 23 | local info = assert(parser:getinfo()) 24 | assert(parser:close()) 25 | return info 26 | end 27 | 28 | -- Empty root element. 29 | assert(helpers.deepequal(parse_xml '', 30 | { tag = 'elem' })) 31 | 32 | -- Element with a single attribute. 33 | assert(helpers.deepequal(parse_xml '', 34 | { tag = 'elem', attr = { 'name', name = 'value' } })) 35 | 36 | -- Element with multiple attributes. 37 | assert(helpers.deepequal(parse_xml '', 38 | { tag = 'elem', attr = { 'name', 'a2', 'a3', name = 'value', a2 = '2', a3 = '3' }})) 39 | 40 | -- Element with text child node. 41 | assert(helpers.deepequal(parse_xml 'text', 42 | { tag = 'elem', 'text' })) 43 | 44 | -- Element with child element. 45 | assert(helpers.deepequal(parse_xml '', 46 | { tag = 'elem', { tag = 'child' } })) 47 | 48 | -- Element with child element that contains a text node. 49 | assert(helpers.deepequal(parse_xml 'text', 50 | { tag = 'parent', { tag = 'child', 'text' } })) 51 | 52 | -- Create a temporary XML file to test the alternative constructor. 53 | local xml_path = helpers.tmpname() 54 | helpers.writefile(xml_path, 'text') 55 | local parser = assert(apr.xml(xml_path)) 56 | assert(helpers.deepequal(parser:getinfo(), 57 | { tag = 'parent', { tag = 'child', 'text' } })) 58 | 59 | -- Check that tostring(apr.xml()) works as expected. 60 | local parser = assert(apr.xml()) 61 | assert(tostring(parser):find '^xml parser %([x%x]+%)$') 62 | assert(parser:close()) 63 | assert(tostring(parser):find '^xml parser %(closed%)$') 64 | --------------------------------------------------------------------------------