Lua/APR binding documentation

The Lua/APR binding aims to bring most of the functionality in the Apache Portable Runtime (APR) to the small and flexible programming language Lua. This document contains the documentation for the Lua/APR binding. Some notes about this documentation:

This document was generated from the Lua/APR 0.23.1 source code.

# Table of contents

Base64 encoding

Cryptography routines

Date parsing

Relational database drivers

DBM routines

Environment manipulation

File path manipulation

Filename matching

Directory manipulation

File I/O handling

Network I/O handling

Pipe I/O handling

LDAP connection handling

Memcached client

Command argument parsing

HTTP request parsing

Pollset

Process handling

Shared memory

Signal handling

String routines

Multi threading

Thread queues

Time routines

Uniform resource identifier parsing

User/group identification

Universally unique identifiers

Character encoding translation

XML parsing

Serialization

Miscellaneous functions

Miscellaneous sections

Base64 encoding

The functions in this module can be used to encode strings in base64 and to decode base64 encoded strings. The base64 format uses the printable characters A-Z, a-z, 0-9, + and / to encode binary data. This can be useful when your data is used in a context that isn’t 8-bit clean, for example in e-mail attachments and data: URLs. You can read more about base64 encoding in this Wikipedia article.

test coverage: 91%
apr.base64_encode(plain) → coded

Encode the string plain using base64 encoding. On success the coded string is returned, otherwise a nil followed by an error message is returned. As an example, here is how to convert an image file into a data: URL:

> image = io.open 'lua-logo.png'
> encoded_data = apr.base64_encode(image:read '*a')
> data_url = 'data:image/png;base64,' .. encoded_data
> = '<img src="//peterodding.com/code/lua/apr/docs/'</span> <span style="color:#2239A8;font-weight:bold">.. url .. '" width=16 height=16 alt="Lua logo">'
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAm5JREFUOMt9k01LW0EUhp+Z5CaxatSbljRioaQgmCIKoUo3XXRRF241tUuhIEgWIgRSKzhtNLZbKf0BulL6C1x234WgoS0otIoWP4Ji48ed3Dtd9CoSW18YBs45z8uZjyOokVKqGQgBzZZlHQshHM/zGqrVaiCRSGyOjOwmhXDngZgxjAUvwLm5uXC5XI5Ho9G98fHxQ2AXVNS3/QFQLBZjdXXuu7MzegCE4IO4CiulfoK6LSUTxvAcaPX9t4Vg0fMoSskbYxj14ysBgN7e3oRSahPepoUwn4FnQONFd11d8cZstvexbUderq0dKCk5iUTEL63lqCgWi3eklE4+fxYWghXg7tU7aWmJsLExRlNTGIC+voWD5eWtDqUcY9v2sXRdtyGfzx9JyataGKCtLXoJA7S3x2JSOhNKqf1yuXxPuq57DGAML/iHVld3WVpaA6BU2mNxce2yNhgMnkrLsgIw2wLEC4Wn1wyMgaGhT9j2ezo7P7K/fwIQB2VXq9VT6XleFIRXC05OPrncM5mHDAykGB19dLXEC4VCASml/A35I2CL/+jkRHN6qkkm7YvQFqhDx3GapNZa+59iIRAQZLM9DA93U6k4DA6miMVukU4n0NrDtusIhQIIwfyFt1BKtaVSqZ1MptQoBF+AJDdrwxjSs7NhEQwGHamU2iqVSg9AHRpDP7B+A7xuDP3GTB2dn5/X53K5ivQH6HuhUOgA9dUYuo3hNbAKaH+tGiMm/uamvk1PT99PpVI7AKJmEpPAtlLqzH9EPy8MwMzMTEJrHfh75Ix7zcA3abUsy9VaG8AGDgEHiNbX1+/lcrnK1fo/txYAMvuVJrYAAAAASUVORK5CYII=" width=16 height=16 alt="Lua logo">

This is what the result looks like (might not work in older web browsers): Lua logo

This function is binary safe.

test coverage: 83%
apr.base64_decode(coded) → plain

Decode the base64 encoded string coded. On success the decoded string is returned, otherwise a nil followed by an error message is returned.

This function is binary safe.

Cryptography routines

These functions support the MD5 and SHA1 cryptographic hash functions. You can also use them to encrypt plain text passwords using a salt, validate plain text passwords against their encrypted, salted digest and read passwords from standard input while masking the characters typed by the user.

The MD5 and SHA1 functions can be used to hash binary data. This is useful because the hash is only 16 or 32 bytes long, yet it still changes significantly when the binary data changes by just one byte.

If that doesn’t look useful consider the following scenario: The Lua authors have just finished a new release of Lua and are about to publish the source code on http://lua.org. Before they publish the tarball they first calculate its MD5 and SHA1 hashes. They then publish the archive and hashes on the downloads page. When a user downloads the tarball they can verify whether it was corrupted or manipulated since it was published on http://lua.org by comparing the published hash against the hash of the tarball they just downloaded:

> handle = io.open('lua-5.1.4.tar.gz', 'rb')
> data = handle:read('*a'); handle:close()
> = apr.md5(data) == 'd0870f2de55d59c1c8419f36e8fac150'
true
> = apr.sha1(data) == '2b11c8e60306efb7f0734b747588f57995493db7'
true

apr.md5(input [, binary]) → digest

Calculate the MD5 message digest of the string input. On success the digest is returned as a string of 32 hexadecimal characters, or a string of 16 bytes if binary evaluates to true. Otherwise a nil followed by an error message is returned.

This function is binary safe.

test coverage: 90%
apr.md5_encode(password, salt) → digest

Encode the string password using the MD5 algorithm and a salt string. On success the digest is returned, otherwise a nil followed by an error message is returned.

This function is not binary safe.

test coverage: 100%
apr.password_validate(password, digest) → valid

Validate the string password against a digest created by one of the APR-supported algorithms (MD5 and SHA1). On success true is returned, otherwise a nil followed by an error message is returned.

Hashes created by crypt are supported only on platforms that provide crypt(3), so don’t rely on that function unless you know that your application will be run only on platforms that support it. On platforms that don’t support crypt(3), this falls back to a clear text string comparison.

This function is not binary safe.

test coverage: none
apr.password_get(prompt) → password

Display the string prompt on the command-line prompt and read in a password from standard input. If your platform allows it, the typed password will be masked by a placeholder like *. On success the password is returned, otherwise a nil followed by an error message is returned.

This function is not binary safe.

test coverage: 75%
apr.md5_init() → md5_context

Create and return an object that can be used to calculate MD5 message digests in steps. If an error occurs a nil followed by an error message is returned. This can be useful when you want to calculate message digests of large inputs, for example files like ISO images and backups:

> function md5_file(path, binary)
>>  local handle = assert(io.open(path, 'rb'))
>>  local context = assert(apr.md5_init())
>>  while true do
>>    local block = handle:read(1024 * 1024)
>>    if not block then break end
>>    assert(context:update(block))
>>  end
>>  return context:digest(binary)
>> end
>
> md5_file 'ubuntu-10.04-desktop-i386.iso'
'd044a2a0c8103fc3e5b7e18b0f7de1c8'

md5_context:update(input) → status

Continue an MD5 message digest operation by processing another message block and updating the context. On success true is returned, otherwise a nil followed by an error message is returned.

This function is binary safe.

md5_context:digest([binary]) → digest

End an MD5 message digest operation. On success the digest is returned as a string of 32 hexadecimal characters, or a string of 16 bytes if binary evaluates to true. Otherwise a nil followed by an error message is returned.

If you want to re-use the context object after calling this method see md5_context:reset().

md5_context:reset() → status

Use this method to reset the context after calling md5_context:digest(). This enables you to re-use the same context to perform another message digest calculation. On success true is returned, otherwise a nil followed by an error message is returned.

apr.sha1(input [, binary]) → digest

Calculate the SHA1 message digest of the string input. On success the digest is returned as a string of 40 hexadecimal characters, or a string of 20 bytes if binary evaluates to true. Otherwise a nil followed by an error message is returned.

This function is binary safe.

test coverage: 83%
apr.sha1_init() → sha1_context

Create and return an object that can be used to calculate SHA1 message digests in steps. See also the example for apr.md5_init().

sha1_context:update(input) → status

Continue an SHA1 message digest operation by processing another message block and updating the context.

This function is binary safe.

sha1_context:digest([binary]) → digest

End an SHA1 message digest operation. On success the digest is returned as a string of 40 hexadecimal characters, or a string of 20 bytes if binary evaluates to true. Otherwise a nil followed by an error message is returned.

If you want to re-use the context object after calling this method see sha1_context:reset().

sha1_context:reset() → status

Use this method to reset the context after calling sha1_context:digest(). This enables you to re-use the same context to perform another message digest calculation.

Date parsing

test coverage: 85%
apr.date_parse_http(string) → time

Parses an HTTP date in one of three standard forms:

On success the date is returned as a number like documented under time routines. If the date string is out of range or invalid nil is returned.

This function is not binary safe.

test coverage: 85%
apr.date_parse_rfc(string) → time

Parses a string resembling an RFC 822 date. This is meant to be lenient in its parsing of dates and hence will parse a wider range of dates than apr.date_parse_http().

The prominent mailer (or poster, if mailer is unknown) that has been seen in the wild is included for the unknown formats:

On success the date is returned as a number like documented under time routines. If the date string is out of range or invalid nil is returned.

This function is not binary safe.

Relational database drivers

The APR DBD module makes it possible to query relational database engines like SQLite, MySQL, PostgreSQL, Oracle, ODBC and FreeTDS. This module currently has some drawbacks which appear to be unavoidable given the design of the Apache Portable Runtime DBD framework:

Note that if you control the data going into the database you can overcome the second limitation by using APR’s built in support for Base64 encoding.

# Installing drivers

On Debian/Ubuntu Linux you can install one or more of the following packages to enable support for the corresponding database driver (dependencies will be installed automatically by the package management system):

# Debugging “DSO load failed” errors

In my initial tests on Ubuntu I installed libaprutil1-dbd-sqlite3 but kept getting an error when trying to load the driver:

$ lua -e "print(require('apr').dbd('sqlite3'))"
nil  DSO load failed  EDSOOPEN

After a while I found the problem using LD_DEBUG:

$ LD_DEBUG=libs lua -e "require('apr').dbd('sqlite3')" 2>&1 | grep undefined
/usr/lib/apr-util-1/apr_dbd_sqlite3-1.so: error: symbol lookup error: undefined symbol: apr_pool_cleanup_null (fatal)

Having identified the problem, finding a workaround was easy:

$ export LD_PRELOAD='/usr/lib/libapr-1.so.0:/usr/lib/libaprutil-1.so.0'
$ lua -e "print(require('apr').dbd('sqlite3'))"
database driver (0x853bdfc)

test coverage: 77%
apr.dbd(name) → driver

Create a database driver object. The string name decides which database engine to use. On success the driver object is returned, otherwise a nil followed by an error message is returned. Currently supported engines include:

Note that in its default configuration the Apache Portable Runtime uses dynamic loading to load database drivers which means apr.dbd() can fail because a driver can’t be loaded.

test coverage: 47%
driver:open(params) → status

Open a connection to a backend. The string params contains the arguments to the driver (implementation-dependent). On success true is returned, otherwise a nil followed by an error message is returned. The syntax of params is as follows:

This function is not binary safe.

test coverage: 100%
driver:dbname(name) → status

Select the database name. On succes true is returned, otherwise a nil followed by an error message is returned. Not supported for all drivers (e.g. SQLite by definition only knows a single database).

This function is not binary safe.

test coverage: 100%
driver:driver() → name

Get the name of the database driver. Returns one of the strings listed for apr.dbd().

driver:check() → status

Check the status of the database connection. When the connection is still alive true is returned, otherwise a nil followed by an error message is returned.

test coverage: 100%
driver:query(sql) → status, affected_rows

Execute an SQL query that doesn’t return a result set. On success true followed by the number of affected rows is returned, otherwise a nil followed by an error message is returned.

This function is not binary safe.

test coverage: 88%
driver:select(sql [, random_access]) → result_set

Execute an SQL query that returns a result set. On success a result set object is returned, otherwise a nil followed by an error message is returned. To enable support for random access you can pass the optional argument random_access as true.

This function is not binary safe.

test coverage: 100%
driver:transaction_start() → status

Start a transaction. May be a no-op. On success true is returned, otherwise a nil followed by an error message is returned.

Note that transaction modes, set by calling driver:transaction_mode(), will affect all query/select calls within a transaction. By default, any error in query/select during a transaction will cause the transaction to inherit the error code and any further query/select calls will fail immediately. Put transaction in 'ignore-errors' mode to avoid that. Use 'rollback' mode to do explicit rollback.

TODO Support for transaction objects that have query(), select(), prepare() methods?

test coverage: 100%
driver:transaction_end() → status

End a transaction (commit on success, rollback on error). May be a no-op. On success true is returned, otherwise a nil followed by an error message is returned.

test coverage: 84%
driver:transaction_mode([mode]) → mode

Get or set the transaction mode, one of:

On success the new transaction mode is returned, otherwise a nil followed by an error message is returned.

test coverage: 88%
driver:prepare(sql [, random_access]) → prepared_statement

Prepare an SQL statement. On success a prepared statement object is returned, otherwise a nil followed by an error message is returned. The string sql gives the query to prepare. If the optional argument random_access is true, result sets created by the prepared statement will support random access.

To specify parameters of the prepared query, use %s, %d etc. (see below for full list) in place of database specific parameter syntax (e.g. for PostgreSQL, this would be $1, $2, for SQLite3 this would be ?, etc.). For instance: SELECT name FROM customers WHERE name = %s would be a query that this function understands. Here is the list of supported format specifiers and what they map to in SQL (generally you’ll only need the types marked in bold text):

This function is not binary safe.

test coverage: 100%
prepared_statement:query(...) → status

Using a prepared statement, execute an SQL query that doesn’t return a result set. On success true followed by the number of affected rows is returned, otherwise a nil followed by an error message is returned.

If you pass a list then the values in the list become query parameters, otherwise all function arguments become query parameters.

This function is not binary safe.

test coverage: 90%
prepared_statement:select(...) → result_set

Using a prepared statement, execute an SQL query that returns a result set. On success a result set object is returned, otherwise a nil followed by an error message is returned. To enable support for random access pass random_access as true, otherwise pass it as false.

If you pass a list then the values in the list become query parameters, otherwise all function arguments after random_access become query parameters.

This function is not binary safe.

test coverage: 100%
result_set:columns([num]) → name [, ...]

If no arguments are given return the names of all columns in the result set, otherwise return the name of the column with index num (counting from one). For example:

> driver = assert(apr.dbd 'sqlite3')
> assert(driver:open ':memory:')
> results = assert(driver:select [[ SELECT 1 AS col1, 2 AS col2 ]])
> = assert(results:columns())
{ 'col1', 'col2' }

This function is not binary safe.

test coverage: 100%
result_set:row(num) → row

Return a table with named fields for the next row in the result set or the row with index rownum if given. When there are no more rows nothing is returned, in case of an error a nil followed by an error message is returned.

This function is not binary safe.

test coverage: 100%
result_set:rows() → iterator

Return an iterator that produces a table with named fields for each (remaining) row in the result set.

In Lua 5.2 you can also use pairs(result_set).

This function is not binary safe.

test coverage: 100%
result_set:tuple([rownum]) → value [, ...]

Return a tuple for the next row in the result set or the row with index rownum if given. If more than one value is returned, the return values will be in the same order as the column list in the SQL query. When there are no more rows nothing is returned, in case of an error a nil followed by an error message is returned.

This function is not binary safe.

test coverage: 100%
result_set:tuples() → iterator

Return an iterator that produces a tuple for each (remaining) row in the result set. The tuples produced by the iterator are in the same order as the column list in the SQL query, for example:

> driver = assert(apr.dbd 'sqlite3')
> assert(driver:open 'quotes.sqlite3')
> results = assert(driver:select [[ SELECT author, quote FROM quotes ]])
> for author, quote in results:tuples() do
>>  print(author, 'wrote:')
>>  print(quote)
>>  print()
>> end

This function is not binary safe.

test coverage: 100%
result_set:pairs() → iterator

Return an iterator that produces a row number and a table with named fields for each (remaining) row in the result set.

In Lua 5.2 you can also use ipairs(result_set).

This function is not binary safe.

test coverage: 100%
#result_set → num_tuples

Get the number of rows in a result set of a synchronous select. If the results are asynchronous -1 is returned.

test coverage: 100%
driver:close() → status

Close a connection to a backend.

DBM routines

This module enables the creation and manipulation of dbm databases. If you’ve never heard of dbm before you can think of it as a Lua table that only supports string keys and values but is backed by a file, so that you can stop and restart your application and find the exact same contents. This module supports the following libraries/implementations:

The SDBM format is the default database format for Lua/APR because APR has built-in support for SDBM while the other libraries need to be separately installed. This is why not all types may be available at run time.

test coverage: 87%
apr.dbm_open(path [, mode [, permissions [, type ]]]) → dbm object

Open a dbm file by path. On success a database object is returned, otherwise a nil followed by an error message is returned. The following mode strings are supported:

The permissions string is documented elsewhere. Valid values for type are listed in the introductory text for this module. Also note that the path string may not be a real file name, as many dbm packages append suffixes for separate data and index files (see also apr.dbm_getnames()).

This function is not binary safe.

test coverage: 84%
apr.dbm_getnames(path [, type]) → used1 [, used2]

If the specified path were passed to apr.dbm_open(), return the actual pathnames which would be (created and) used. At most, two files may be used; used2 is nil if only one file is used. The dbm file(s) don’t need to exist because this function only manipulates the pathnames. Valid values for type are listed in the introductory text for this module.

This function is not binary safe.

test coverage: 100%
dbm:exists(key) → status

Check whether the dbm record with the given string key exists.

This function is binary safe.

test coverage: 90%
dbm:fetch(key) → value

Fetch the dbm record with the given string key. On success the fetched value is returned as a string, if the key doesn’t exist nothing is returned and on error a nil followed by an error message is returned.

This function is binary safe.

test coverage: 100%
dbm:store(key, value) → status

Store the dbm record value (a string) by the given string key. On success true is returned, otherwise a nil followed by an error message is returned.

This function is binary safe.

test coverage: 100%
dbm:delete(key) → status

Delete the dbm record with the given string key. On success true is returned, otherwise a nil followed by an error message is returned.

This function is binary safe.

test coverage: 88%
dbm:firstkey() → key

Retrieve the first record key from a dbm. On success the first key is returned as a string, when there are no keys nothing is returned. In case of error a nil followed by an error message is returned.

This function is binary safe.

test coverage: 70%
dbm:nextkey(key1) → key2

Retrieve the next record key from a dbm. This function works just like Lua’s next() function: On success the next key is returned as a string, when there are no more keys nothing is returned. In case of error a nil followed by an error message is returned.

This function is binary safe.

test coverage: 100%
dbm:close() → status

Close a dbm database handle and return true (this can’t fail).

Environment manipulation

Operating systems organize tasks into processes. One of the simplest means of communication between processes is the use of environment variables. If you’re not familiar with environment variables, picture every process on your computer having an associated Lua table with string key/value pairs. When a process creates or overwrites a key/value pair only the table of that process changes. When the process spawns a child process the child gets a copy of the table which from that point onwards is no longer associated with the parent process.

test coverage: 90%
apr.env_get(name) → value

Get the value of the environment variable name. On success the string value is returned. If the variable does not exist nothing is returned. Otherwise a nil followed by an error message is returned.

This function is not binary safe.

test coverage: 100%
apr.env_set(name, value) → status

Set the value of the environment variable name to the string value. On success true is returned, otherwise a nil followed by an error message is returned.

This function is not binary safe.

test coverage: 100%
apr.env_delete(name) → status

Delete the environment variable name. On success true is returned, otherwise a nil followed by an error message is returned.

This function is not binary safe.

File path manipulation

test coverage: 90%
apr.filepath_root(path [, option, ...]) → root, path

Extract the root from the file path path. On success the extracted root and the relative path following the root are returned, otherwise a nil followed by an error message is returned. Either or both of the following options may be given after path:

These options only influence the resulting root. The path after the root is returned as is. If you want to convert a whole file path to its true case and/or native format use apr.filepath_merge() instead.

This function is not binary safe.

test coverage: 80%
apr.filepath_parent(path [, option, ...]) → parent, filename

Split the file path path into its parent path and filename. This function supports the same options as apr.filepath_root(). If any options are given they’re applied to path before it is split, so the options influence both of the resulting values. If path is a filename and the 'true-name' option isn’t given then the returned parent path will be an empty string.

This function is not binary safe.

test coverage: 100%
apr.filepath_name(path [, split]) → filename [, extension]

Extract the filename (the final element) from the file path path. If split evaluates true then the extension will be split from the filename and returned separately. Some examples of what is considered a filename or an extension:

> -- regular file path
> = apr.filepath_name('/usr/bin/lua', true)
'lua', ''

> -- hidden file on UNIX
> = apr.filepath_name('/home/xolox/.vimrc', true)
'.vimrc', ''

> -- multiple extensions
> = apr.filepath_name('index.html.en', true)
'index.html', '.en'

This function is not binary safe.

test coverage: 86%
apr.filepath_merge(root, path [, option, ...]) → merged

Merge the file paths root and path. On success the merged file path is returned, otherwise a nil followed by an error message is returned. Any combination of one or more of the following options may be given:

This function can be used to generate absolute file paths as follows:

apr.filepath_merge('.', 'filepath.c', 'not-relative')
-- the above is equivalent to the below:
apr.filepath_merge(apr.filepath_get(), 'filepath.c', 'not-relative')

This function is not binary safe.

test coverage: 90%
apr.filepath_list_split(searchpath) → components

Split a search path string into a table of separate components. On success the table of components is returned, otherwise a nil followed by an error message is returned. Empty elements do not become part of the returned table.

An example of a search path is the $PATH environment variable available in UNIX and Windows operating systems which controls the way program names are resolved to absolute pathnames (see apr.filepath_which()).

This function is not binary safe.

test coverage: 77%
apr.filepath_list_merge(components) → searchpath

Merge a table of search path components into a single search path string. On success the table of components is returned, otherwise a nil followed by an error message is returned.

This function is not binary safe.

test coverage: 87%
apr.filepath_get([native]) → path

Get the default filepath for relative filenames. If native evaluates true the file system’s native path format is used. On success the filepath is returned, otherwise a nil followed by an error message is returned.

On at least Windows and UNIX the default file path for relative file names is the current working directory. Because some operating systems supported by APR don’t use this convention Lua/APR uses the term ‘default filepath’ instead.

This function is not binary safe.

test coverage: 100%
apr.filepath_set(path) → status

Set the default file path for relative file names to the string path. On success true is returned, otherwise a nil followed by an error message is returned. Also see the notes for apr.filepath_get().

This function is not binary safe.

apr.filepath_which(program [, find_all]) → pathname

Find the full pathname of program by searching the directories in the $PATH environment variable and return the pathname of the first program that’s found. If find_all is true then a list with the pathnames of all matching programs is returned instead.

apr.filepath_executable(path) → is_executable

Check whether the file pointed to by path is executable. Returns true when the file is executable, false otherwise.

Filename matching

test coverage: 100%
apr.fnmatch(pattern, input [, ignorecase]) → status

Try to match a string against a filename pattern. When the string matches the pattern true is returned, otherwise false. The supported pattern items are the following subset of shell wild cards:

If the optional argument ignorecase is true, characters are compared case-insensitively.

This function is not binary safe.

test coverage: 100%
apr.fnmatch_test(pattern) → status

Determine if a file path pattern contains one or more of the wild cards that are supported by apr.fnmatch(). On success true is returned, otherwise false.

This function is not binary safe.

apr.glob(pattern [, ignorecase]) → iterator

Split pattern into a directory path and a filename pattern and return an iterator which returns all filenames in the directory that match the extracted filename pattern. The apr.fnmatch() function is used for filename matching so the documentation there applies.

This function is not binary safe.

Directory manipulation

test coverage: 85%
apr.temp_dir_get() → path

Find an existing directory suitable as a temporary storage location. On success the directory file path is returned, otherwise a nil followed by an error message is returned.

This function is not binary safe.

test coverage: 100%
apr.dir_make(path [, permissions]) → status

Create the directory path on the file system. On success true is returned, otherwise a nil followed by an error message is returned. See the documentation on permissions for the optional second argument.

This function is not binary safe.

test coverage: 100%
apr.dir_make_recursive(path [, permissions]) → status

Create the directory path on the file system, creating intermediate directories as required. On success true is returned, otherwise a nil followed by an error message is returned. See the documentation on permissions for the optional second argument.

This function is not binary safe.

test coverage: 100%
apr.dir_remove(path) → status

Remove the empty directory path from the file system. On success true is returned, otherwise a nil followed by an error message is returned.

This function is not binary safe.

test coverage: 78%
apr.dir_remove_recursive(path) → status

Remove the directory path and all its contents from the file system. On success true is returned, otherwise a nil followed by an error message is returned.

Note: This function isn’t part of the Apache Portable Runtime but has been implemented on top of it by the author of the Lua/APR binding. It also hasn’t been properly tested yet.

This function is not binary safe.

test coverage: 66%
apr.dir_open(path) → directory handle

Open the directory path for reading. On success a directory object is returned, otherwise a nil followed by an error message is returned.

This function is not binary safe.

test coverage: 61%
directory:read([property, ...]) → value, ...

Return the requested properties for the next directory entry. On success the requested properties are returned, otherwise a nil followed by an error message is returned. This function implements the same interface as apr.stat().

test coverage: 100%
directory:rewind() → status

Rewind the directory handle to start from the first entry. On success true is returned, otherwise a nil followed by an error message is returned.

test coverage: 100%
directory:entries([property, ...]) → iterator, directory handle

Return a function that iterates over the (remaining) directory entries and returns the requested properties for each entry. If you don’t request any properties, a table with the available properties will be returned for each directory entry:

> directory = apr.dir_open 'examples'
> for info in directory:entries() do print(info) end
{
  type = 'file', name = 'webserver.lua',
  user = 'peter', group = 'peter', protection = 'rw-r--r--',
  size = 2789, csize = 4096, inode = 12455648, dev = 64514, nlink = 1,
  path = '/home/peter/Development/Lua/APR/examples/webserver.lua',
  mtime = 1293753884.3382, atime = 1293994993.3855, ctime = 1293753884.3382,
}
{
 type = 'file', name = 'download.lua',
 user = 'peter' group = 'peter', protection = 'rw-r--r--',
 size = 2580, csize = 4096, inode = 12455598, dev = 64514, nlink = 1,
 path = '/home/peter/Development/Lua/APR/examples/download.lua',
 mtime = 1293753884.3382, atime = 1293994993.3855, ctime = 1293753884.3382,
}

This function implements the same interface as apr.stat() with one exception: If you pass property names to directory:entries() that are not available they will be returned as false instead of nil because of a technical limitation in the Lua iterator protocol:

“On each iteration, the iterator function is called to produce a new value, stopping when this new value is nil.”

test coverage: 100%
directory:close() → status

Close the directory handle. On success true is returned, otherwise a nil followed by an error message is returned.

File I/O handling

apr.file_link(source, target) → status

Create a hard link to the specified file. On success true is returned, otherwise a nil followed by an error message is returned. Both files must reside on the same device.

Please note that this function will only be available when the Lua/APR binding is compiled against APR 1.4 or newer because the apr_file_link() function wasn’t available in earlier releases.

This function is not binary safe.

test coverage: 100%
apr.file_copy(source, target [, permissions]) → status

Copy the file source to target. On success true is returned, otherwise a nil followed by an error message is returned. The permissions argument is documented elsewhere. The new file does not need to exist, it will be created if required. If the new file already exists, its contents will be overwritten.

This function is not binary safe.

test coverage: 100%
apr.file_append(source, target [, permissions]) → status

Append the file source to target. On success true is returned, otherwise a nil followed by an error message is returned. The permissions argument is documented elsewhere. The new file does not need to exist, it will be created if required.

This function is not binary safe.

test coverage: 100%
apr.file_rename(source, target) → status

Rename the file source to target. On success true is returned, otherwise a nil followed by an error message is returned. If a file exists at the new location, then it will be overwritten. Moving files or directories across devices may not be possible.

This function is not binary safe.

test coverage: 100%
apr.file_remove(path) → status

Delete the file pointed to by path. On success true is returned, otherwise a nil followed by an error message is returned. If the file is open, it won’t be removed until all instances of the file are closed.

This function is not binary safe.

apr.file_truncate(path [, offset]) → status

Truncate the file’s length to the specified offset (defaults to 0). On success true is returned, otherwise a nil followed by an error message is returned.

test coverage: 100%
apr.file_mtime_set(path, mtime) → status

Set the last modified time of the file pointed to by path to mtime. On success true is returned, otherwise a nil followed by an error message is returned.

This function is not binary safe.

test coverage: 60%
apr.file_attrs_set(path, attributes) → status

Set the attributes of the file pointed to by path. On success true is returned, otherwise a nil followed by an error message is returned.

The table attributes should consist of string keys and boolean values. The supported attributes are readonly, hidden and executable.

This function should be used in preference to explicit manipulation of the file permissions, because the operations to provide these attributes are platform specific and may involve more than simply setting permission bits.

This function is not binary safe.

test coverage: 100%
apr.file_perms_set(path, permissions) → status

Set the permissions of the file specified by path. On success true is returned, otherwise a nil followed by an error message is returned.

Warning: Some platforms may not be able to apply all of the available permission bits, in this case a third value 'INCOMPLETE' is returned (see error handling).

This function is not binary safe.

test coverage: 100%
apr.stat(path [, property, ...]) → value, ...

Get the status of the file pointed to by path. On success, if no properties are given a table of property name/value pairs is returned, otherwise the named properties are returned in the same order as the arguments. On failure a nil followed by an error message is returned.

The following fields are supported:

Here are some examples:

> -- Here's an example of a table with all properties:
> = apr.stat('lua-5.1.4.tar.gz')
{
 name = 'lua-5.1.4.tar.gz',
 path = 'lua-5.1.4.tar.gz',
 type = 'file',
 user = 'peter',
 group = 'peter',
 size = 216679,
 csize = 217088,
 ctime = 1284159662.7264,
 atime = 1287954158.6019,
 mtime = 1279317348.194,
 nlink = 1,
 inode = 1838576,
 dev  = 64514,
 protection = 'rw-r--r--',
}
> -- To check whether a directory exists:
> function isdir(p) return apr.stat(p, 'type') == 'directory' end
> = isdir('.')
true
> -- To get a file's size in bytes:
> function filesize(p) return apr.stat(p, 'size') end
> = filesize('lua-5.1.4.tar.gz')
216679

This function is not binary safe.

test coverage: 93%
apr.file_open(path [, mode [, permissions]]) → file

This function imitates Lua’s io.open() function with one exception: On UNIX you can pass a file descriptor (number) instead of a path string (see also file:fd_get()). Now follows the documentation for io.open():

This function opens a file, in the mode specified in the string mode. It returns a new file handle, or, in case of errors, nil plus an error message. The mode string can be any of the following:

The mode string may also have a b at the end, which is needed in some systems to open the file in binary mode. This string is exactly what is used in the standard C function fopen(). The permissions argument is documented elsewhere.

This function is not binary safe.

test coverage: 90%
file:stat([field, ...]) → value, ...

This method works like apr.stat() except that it uses a file handle instead of a filepath to access the file.

test coverage: 100%
file:lines() → iterator

This function implements the interface of the file:lines() function described in the Lua 5.1 reference manual. Here is the description from the reference manual:

Return an iterator function that, each time it is called, returns a new line read from the file. Therefore, the construction

for line in file:lines() do body end

will iterate over all lines. This function does not close the file when the loop ends.

test coverage: 100%
file:truncate([offset]) → status

Truncate the file’s length to the specified offset (defaults to 0). On success true is returned, otherwise a nil followed by an error message is returned. Note that the read/write file offset is repositioned to the selected offset.

test coverage: 100%
file:read([format, ...]) → mixed value, ...

This function implements the interface of the file:read() function described in the Lua 5.1 reference manual. Here is the description from the reference manual:

Read from file, according to the given formats, which specify what to read. For each format, the function returns a string (or a number) with the characters read, or nil if it cannot read data with the specified format. When called without formats, it uses a default format that reads the entire next line (see below).

The available formats are:

test coverage: 100%
file:write(value [, ...]) → status

This function implements the interface of the file:write() function described in the Lua 5.1 reference manual. Here is the description from the reference manual:

Write the value of each argument to file. The arguments must be strings or numbers. To write other values, use tostring() or string.format() before this function.

test coverage: 70%
file:seek([whence [, offset]]) → offset

This function implements the interface of the file:seek() function described in the Lua 5.1 reference manual. Here is the description from the reference manual:

Sets and gets the file position, measured from the beginning of the file, to the position given by offset plus a base specified by the string whence, as follows:

In case of success, function seek returns the final file position, measured in bytes from the beginning of the file. If this function fails, it returns nil, plus a string describing the error.

The default value for whence is 'cur', and for offset is 0. Therefore, the call file:seek() returns the current file position, without changing it; the call file:seek('set') sets the position to the beginning of the file (and returns 0); and the call file:seek('end') sets the position to the end of the file, and returns its size.

test coverage: none
file:flush() → status

Saves any written data to file. On success true is returned, otherwise a nil followed by an error message is returned.

test coverage: none
file:lock(type [, nonblocking ]) → status

Establish a lock on the open file file. On success true is returned, otherwise a nil followed by an error message is returned. The type must be one of:

If nonblocking is true the call will not block while acquiring the file lock.

The lock may be advisory or mandatory, at the discretion of the platform. The lock applies to the file as a whole, rather than a specific range. Locks are established on a per-thread/process basis; a second lock by the same thread will not block.

test coverage: none
file:unlock() → status

Remove any outstanding locks on the file. On success true is returned, otherwise a nil followed by an error message is returned.

test coverage: none
pipe:timeout_get() → timeout

Get the timeout value or blocking state of pipe. On success the timeout value is returned, otherwise a nil followed by an error message is returned.

The timeout true means wait forever, false means don’t wait at all and a number is the microseconds to wait.

test coverage: none
pipe:timeout_set(timeout) → status

Set the timeout value or blocking state of pipe. On success true is returned, otherwise a nil followed by an error message is returned.

The timeout true means wait forever, false means don’t wait at all and a number is the microseconds to wait. For example:

-- Configure read end of pipe to block for a maximum of 5 seconds.
pipe:timeout_set(1000000 * 5)
for line in pipe:lines() do
  print(line)
end

test coverage: 85%
file:fd_get() → fd

Get the underlying file descriptor for this file. On success a number is returned, otherwise a nil followed by an error message is returned.

To convert a file descriptor into a Lua/APR file object you can pass the file descriptor (number) to apr.file_open() instead of the pathname.

Note that this function is only available on platforms where file descriptors are numbers (this includes UNIX and excludes Windows).

test coverage: none
file:inherit_set() → status

Set a file to be inherited by child processes. By default, file descriptors will not be inherited by child processes created by apr.proc_create().

At the time of writing this seems to only apply to UNIX where APR will close all open file handles after performing a fork() unless you explicitly set your files to be inheritable.

test coverage: none
file:inherit_unset() → status

Unset a file from being inherited by child processes.

test coverage: 100%
file:close() → status

Close file. On success true is returned, otherwise a nil followed by an error message is returned.

Network I/O handling

To get started with network programming using the Lua/APR binding, have a look at any of the following examples:

test coverage: 93%
apr.socket_create([protocol [, family]]) → socket

Create a network socket. On success the new socket object is returned, otherwise a nil followed by an error message is returned. Valid values for the protocol argument are:

These are the valid values for the family argument:

Note that 'inet6' is only supported when apr.socket_supports_ipv6 is true.

test coverage: 85%
apr.hostname_get() → name

Get the name of the current machine. On success the host name string is returned, otherwise a nil followed by an error message is returned.

This function is not binary safe.

test coverage: 90%
apr.host_to_addr(hostname [, family]) → ip_address

Resolve a host name to an IP-address. On success the IP-address is returned as a string, otherwise a nil followed by an error message is returned. The optional family argument is documented under apr.socket_create().

> = apr.host_to_addr 'www.lua.org'
'89.238.129.35'

This function is not binary safe.

test coverage: 90%
apr.addr_to_host(ip_address [, family]) → hostname

Look up the host name from an IP-address (also known as a reverse DNS lookup). On success the host name is returned as a string, otherwise a nil followed by an error message is returned. The optional family argument is documented under apr.socket_create().

> = apr.addr_to_host '89.238.129.35'
'flounder.pepperfish.net'

This function is not binary safe.

test coverage: 100%
socket:connect(host, port) → status

Issue a connection request to a socket either on the same machine or a different one, as indicated by the host string and port number. On success true is returned, otherwise a nil followed by an error message is returned.

This function is not binary safe.

test coverage: 100%
socket:bind(host, port) → status

Bind the socket to the given host string and port number. On success true is returned, otherwise a nil followed by an error message is returned. The special host value '*' can be used to select the default ‘any’ address. For example if you want to create a web server you can start with the following:

-- Basic single threaded server
server = assert(apr.socket_create())
assert(server:bind('*', 80))
assert(server:listen(10))
while true do
  local client = assert(server:accept())
  -- Here you can receive data from the client by calling client:read()
  -- and send data to the client by calling client:write()
end

This function can fail if you try to bind a port below 1000 without superuser privileges or if another process is already bound to the given port number.

This function is not binary safe.

test coverage: 100%
socket:listen(backlog) → status

To listen for incoming network connections three steps must be performed:

  1. First a socket is created with apr.socket_create()
  2. Next a willingness to accept incoming connections and a queue limit for incoming connections are specified with socket:listen() (this call doesn’t block)
  3. Finally socket:accept() is called to wait for incoming connections

On success true is returned, otherwise a nil followed by an error message is returned. The backlog argument indicates the number of outstanding connections allowed in the socket’s listen queue. If this value is less than zero, the listen queue size is set to zero. As a special case, if you pass the string 'max' as backlog then a platform specific maximum value is chosen based on the compile time constant SOMAXCONN.

test coverage: 95%
socket:recvfrom([bufsize]) → address, data

Read data from an UDP socket that has been bound to an interface and/or port. On success two values are returned: A table with the address of the peer from which the data was sent and a string with the received data. If the call fails it returns nil followed by an error message.

By default bufsize is 1024, this means the resulting data string will be truncated to a maximum of 1024 bytes. If you want to receive larger messages, pass a larger bufsize.

The returned address table contains the following fields:

This function is binary safe.

test coverage: 91%
socket:accept() → client_socket

Accept a connection request on a server socket. On success a socket is returned which forms the connection to the client, otherwise a nil followed by an error message is returned. This function blocks until a client connects.

test coverage: 100%
socket:read([format, ...]) → mixed value, ...

This function implements the interface of the file:read() function described in the Lua 5.1 reference manual. Here is the description from the reference manual:

Read from socket, according to the given formats, which specify what to read. For each format, the function returns a string (or a number) with the characters read, or nil if it cannot read data with the specified format. When called without formats, it uses a default format that reads the entire next line (see below).

The available formats are:

test coverage: 85%
socket:write(value [, ...]) → status

This function implements the interface of the file:write() function described in the Lua 5.1 reference manual. Here is the description from the reference manual:

Write the value of each argument to socket. The arguments must be strings or numbers. To write other values, use tostring() or string.format() before this function.

test coverage: 100%
socket:lines() → iterator

This function implements the interface of the file:lines() function described in the Lua 5.1 reference manual. Here is the description from the reference manual:

Return an iterator function that, each time it is called, returns a new line read from the socket. Therefore, the construction

for line in socket:lines() do body end

will iterate over all lines. This function does not close the socket when the loop ends.

test coverage: none
socket:timeout_get() → timeout

Get the timeout value or blocking state of socket. On success the timeout value is returned, otherwise a nil followed by an error message is returned.

The timeout true means wait forever, false means don’t wait at all and a number is the microseconds to wait.

test coverage: none
socket:timeout_set(timeout) → status

Set the timeout value or blocking state of socket. On success true is returned, otherwise a nil followed by an error message is returned.

The timeout true means wait forever, false means don’t wait at all and a number is the microseconds to wait.

test coverage: none
socket:opt_get(name) → value

Query socket options for the specified socket. Valid values for name are:

The 'sndbuf' and 'rcvbuf' options have integer values, all other options have a boolean value.

test coverage: none
socket:opt_set(name, value) → status

Setup socket options for the specified socket. Valid values for name are documented under socket:opt_get(), value should be a boolean or integer value. On success true is returned, otherwise a nil followed by an error message is returned.

test coverage: none
socket:addr_get([type]) → ip_address, port [, hostname]

Get one of the IP-address / port pairs associated with socket, according to type:

On success the local or remote IP-address (a string) and the port (a number) are returned, otherwise a nil followed by an error message is returned. If a host name is available that will be returned as the third value.

This function is not binary safe.

test coverage: 85%
socket:fd_get() → fd

Get the underlying file descriptor for this socket. On success a number is returned, otherwise a nil followed by an error message is returned.

test coverage: 100%
socket:fd_set(fd) → status

Set the underlying file descriptor (a number) of an existing socket. On success true is returned, otherwise a nil followed by an error message is returned.

test coverage: none
socket:shutdown(mode) → status

Shutdown either reading, writing, or both sides of a socket. On success true is returned, otherwise a nil followed by an error message is returned. Valid values for mode are:

This does not actually close the socket descriptor, it just controls which calls are still valid on the socket. To close sockets see socket:close().

test coverage: 100%
socket:close() → status

Close socket. On success true is returned, otherwise a nil followed by an error message is returned.

Pipe I/O handling

Lua/APR represents pipes as files just like Lua’s standard library function io.popen() does because it works fairly well, however there are some differences between files and pipes which impact the API:

One of the reasons that file/pipe support is so interwoven in APR and thus Lua/APR is that you can create a named pipe with apr.namedpipe_create() and access it using apr.file_open() and APR won’t know or even care that you’re reading/writing a pipe instead of a file.

test coverage: 100%
apr.pipe_open_stdin() → pipe

Open standard input as a pipe. On success the pipe is returned, otherwise a nil followed by an error message is returned.

test coverage: 100%
apr.pipe_open_stdout() → pipe

Open standard output as a pipe. On success the pipe is returned, otherwise a nil followed by an error message is returned.

test coverage: 100%
apr.pipe_open_stderr() → pipe

Open standard error as a pipe. On success the pipe is returned, otherwise a nil followed by an error message is returned.

test coverage: 100%
apr.namedpipe_create(name [, permissions]) → status

Create a named pipe. On success true is returned, otherwise a nil followed by an error message is returned. The permissions argument is documented elsewhere.

Named pipes can be used for interprocess communication:

  1. Check if the named pipe already exists, if it doesn’t then create it
  2. Have each process access the named pipe using apr.file_open()
  3. Communicate between the two processes over the read/write ends of the named pipe and close it when the communication is finished.

Note that APR supports named pipes on UNIX but not on Windows. If you try anyhow the error message “This function has not been implemented on this platform” is returned.

This function is not binary safe.

test coverage: none
apr.pipe_create() → input, output

Create an anonymous pipe. On success the write and read ends of the pipe are returned, otherwise a nil followed by an error message is returned. There’s an example use of this function in the documentation for process:in_set().

LDAP connection handling

The Lightweight Directory Access Protocol (LDAP) enables querying and modifying data hosted on directory servers. LDAP databases are similar to relational databases in the sense that both types of databases store records with attributes and allow clients to search records based on those attributes. Notable differences between LDAP and relational databases are that LDAP stores all records in a hierarchy and records can have an arbitrary number of attributes. LDAP is frequently used by (large) organizations to provide a centralized address book for all employees and to store system account information like user names and passwords in a central place (one piece of the puzzle towards roaming profiles).

This module is based on LuaLDAP by Roberto Ierusalimschy, André Carregal and Tomás Guisasola.

test coverage: 59%
apr.ldap([url [, secure ]]) → ldap_conn

Create an LDAP connection. The url argument is a URL string with the following components:

If secure is true the connection will use STARTTLS even if the URL scheme is ldap://. On success an LDAP connection object is returned, otherwise a nil followed by an error message is returned.

test coverage: 85%
apr.ldap_info() → string

This function returns a string describing the LDAP SDK (library) currently in use. On success a string is returned, otherwise a nil followed by an error message is returned. The resulting string is intended to be displayed to the user, it’s not meant to be parsed (although you can of course decide to do this :-). According to apr_ldap.h the following LDAP SDKs can be used:

test coverage: 46%
apr.ldap_url_parse(string) → table

Parse an LDAP URL into a table of URL components. On success a table is returned, otherwise a nil followed by an error message and one of the following strings is returned:

LDAP URLs look like this:

ldap[is]://host:port[/[dn[?[attributes][?[scope][?[filter][?exts]]]]]]

Where:

For example:

> = apr.ldap_url_parse 'ldap://root.openldap.org/dc=openldap,dc=org'
{
  scheme = 'ldap',
  host = 'root.openldap.org',
  port = 389,
  scope = 'sub',
  dn = 'dc=openldap,dc=org',
  crit_exts = 0,
}

test coverage: 100%
apr.ldap_url_check(url) → type

Checks whether the given URL is an LDAP URL. On success one of the strings below is returned, otherwise nil is returned:

test coverage: 90%
ldap_conn:bind([who [, passwd]]) → status

Bind to the LDAP directory. If no arguments are given an anonymous bind is attempted, otherwise who should be a string with the relative distinguished name (RDN) of the user in the form 'cn=admin,dc=example,dc=com'. On success true is returned, otherwise a nil followed by an error message is returned.

test coverage: 100%
ldap_conn:unbind() → status

Unbind from the directory. On success true is returned, otherwise a nil followed by an error message is returned.

test coverage: 92%
ldap_conn:option_get(name) → value

Get an LDAP option by its name (one of the strings documented below). On success the option value is returned, otherwise a nil followed by an error message is returned. These are the supported LDAP options:

test coverage: 90%
ldap_conn:option_set(name, value) → status

Set the LDAP option name (one of the strings documented for ldap_conn:option_get()) to value. On success true is returned, otherwise a nil followed by an error message is returned.

test coverage: none
ldap_conn:rebind_add([who [, password]]) → status

LDAP servers can return referrals to other servers for requests the server itself will not/can not serve. This function creates a cross reference entry for the specified LDAP connection. The rebind callback function will look up this LDAP connection so it can retrieve the who and password fields for use in any binds while referrals are being chased.

On success true is returned, otherwise a nil followed by an error message is returned.

When the LDAP connection is garbage collected the cross reference entry is automatically removed, alternatively ldap_conn:rebind_remove() can be called to explicitly remove the entry.

test coverage: none
ldap_conn:rebind_remove() → status

Explicitly remove an LDAP cross reference entry (also done automatically when the LDAP connection is garbage collected). On success true is returned, otherwise a nil followed by an error message is returned.

test coverage: 83%
ldap_conn:search(parameters) → iterator

The implementation of this method is based on LuaLDAP and the following documentation was based on the LuaLDAP manual:

Performs a search operation on the directory. The parameters are described below. The search method will return a search iterator which is a function that requires no arguments. The search iterator is used to get the search result and will return a string representing the distinguished name and a table of attributes as returned by the search request.

Supported parameters:

test coverage: 100%
ldap_conn:add(dn, attrs) → future

Add a new entry to the directory. The string dn is the distinguished name of the new entry. The table attrs contains the attributes and values. Returns a function to process the LDAP result.

This function is not binary safe.

test coverage: 100%
ldap_conn:compare(dn, attr, value) → future

Compare a value against an entry. The string dn contains the distinguished name of the entry, the string attr is the name of the attribute to compare and the string value is the value to compare against. Returns a function to process the LDAP result.

This function is not binary safe.

test coverage: 100%
ldap_conn:delete(dn) → future

Delete an entry. The string dn is the distinguished name of the entry to delete. Returns a function to process the LDAP result.

This function is not binary safe.

test coverage: 93%
ldap_conn:modify(dn, mods [, ...]) → future

Modify an entry. The string dn is the distinguished name of the entry to modify. The table mods contains modifications to apply. You can pass any number of additional tables with modifications to apply. On success true is returned, otherwise a nil followed by an error message is returned.

This function is not binary safe.

test coverage: 100%
ldap_conn:rename(dn, new_rdn [, new_parent [, delete]]) → future

Change the distinguished name of an entry. The string dn is the distinguished name of the entry to rename. The string new_rdn gives the new root distinguished name. The optional string new_parent gives the distinguished name of the new parent for the entry. If the optional argument delete is true the entry is removed from it’s old parent. Returns a function to process the LDAP result.

This function is not binary safe.

Memcached client

Memcached is a “distributed memory object caching system”. It’s designed as an in-memory key-value store for small chunks of arbitrary data (strings, objects) from results of database calls, API calls, or page rendering. The memcached client module makes it possible to read from and write to one or more memcached servers over a network socket in Lua.

To-do: Find out if “flags” can be any 32 bits value and how useful it is to Lua.

test coverage: 72%
apr.memcache([max_servers]) → mc_client

Create a memcached client. The optional argument max_servers determines the maximum number of memcached servers supported by the client (defaults to 10). On success the client object is returned, otherwise nil followed by an error message is returned.

test coverage: 100%
mc_client:hash(str) → hash

Create a CRC32 hash used to split keys between servers. The hash is not compatible with old memcached clients.

This function is binary safe.

test coverage: 88%
mc_client:find_server_hash(hash) → mc_server

Picks a server based on a hash. Returns the info of the server that controls the specified hash.

test coverage: 89%
mc_client:add_server(host, port [, min [, smax [, max [, ttl]]]]) → mc_server

Create a new server object and add it to the client object. On success the server object is returned, otherwise a nil followed by an error message is returned. The parameters have the following meaning:

Note that min, smax and max are only used when APR was compiled with threads. Also a word of caution: Changing servers after startup may cause keys to go to different servers.

test coverage: 90%
mc_client:find_server(host, port) → mc_server

Finds a server object based on a (hostname, port) pair. On success the server with matching host name and port is returned, otherwise nothing is returned.

test coverage: 100%
mc_client:enable_server(mc_server) → status

Enable a server for use again after disabling the server with mc_client:disable_server(). On success true is returned, otherwise nil followed by an error message is returned.

test coverage: 100%
mc_client:disable_server(mc_server) → status

Disable a server. On success true is returned, otherwise nil followed by an error message is returned.

test coverage: 85%
mc_client:get(key [, ...]) → status, value [, ...]

Get one or more values from the server. On success true is returned followed by the retrieved value(s), otherwise nil followed by an error message is returned. The keys are null terminated strings and the return values are binary safe strings. Keys that don’t have an associated value result in nil.

test coverage: 100%
mc_client:set(key, value [, timeout]) → status

Sets a value by key on the server. If the key already exists the old value will be overwritten. The key is a null terminated string, the value is a binary safe string and timeout is the time in seconds for the data to live on the server (a number, defaults to 60 seconds). On success true is returned, otherwise nil followed by an error message is returned.

test coverage: 100%
mc_client:add(key, value [, timeout]) → status

Adds a value by key on the server. If the key already exists this call will fail and the old value will be preserved. The arguments and return values are documented under mc_client:set().

test coverage: 100%
mc_client:replace(key, value [, timeout]) → status

Replace a value by key on the server. If the key doesn’t exist no value will be set. The arguments and return values are documented under mc_client:set().

test coverage: 100%
mc_client:delete(key [, timeout]) → status

Delete a key from the server. The key is a null terminated string and timeout is the time in seconds for the delete to stop other clients from adding (a number, defaults to 10 seconds). On success true is returned, otherwise nil followed by an error message is returned.

test coverage: 88%
mc_client:incr(key [, number]) → value

Increment a value. The key is a null terminated string and the optional argument number is the number to increment by (defaults to 1). On success the new value after incrementing is returned, otherwise nil followed by an error message is returned.

test coverage: 88%
mc_client:decr(key [, number]) → value

Decrement a value. The key is a null terminated string and the optional argument number is the number to decrement by (defaults to 1). On success the new value after decrementing is returned, otherwise nil followed by an error message is returned.

test coverage: 90%
mc_client:version(mc_server) → version

Query a server’s version. On success the version string is returned, otherwise nil followed by an error message is returned.

test coverage: 96%
mc_client:stats() → statistics

Query a server for statistics. On success a table with information is returned, otherwise nil followed by an error message is returned. The following fields are supported:

Command argument parsing

test coverage: 92%
apr.getopt(usage [, config ]) → options, arguments

Parse the command line arguments according to the option letters and/or long options defined in the string usage (see the example below) and return a table with the matched options and a table with any remaining positional arguments. When an option is matched multiple times, the resulting value in options depends on the following context:

The optional config table can be used to change the following defaults:

Here is a short example of a valid Lua script that doesn’t really do anything useful but demonstrates the use of apr.getopt():

apr = require 'apr'
opts, args = apr.getopt [[
Usage: echo.lua [OPTIONS] ARG...
  -h, --help     show this message and exit
  -v, --verbose  make more noise
      --version  print version and exit
]]
if opts.version then
  print "This is version 0.1"
else
  if opts.verbose then
    print("Got", #args, "arguments")
  end
  if opts.verbose >= 2 then
    print "Here they are:"
  end
  for i = 1, #args do print(args[i]) end
end

The apr.getopt() function is very similar to Lapp by Steve Donovan although Lapp is more full featured, for example it validates and converts argument types.

HTTP request parsing

This module is an experimental binding to the apreq2 library which enables HTTP request parsing of query strings, headers and multipart messages. Some general notes about the functions in this module:

The functions in this module return three values on error: a nil followed by an error message and an error code. This module defines the following error codes (in addition to the generic Lua/APR error codes):

test coverage: 90%
apr.parse_headers(request) → headers, body

Parse the headers in a HTTP request string according to RFC 822. On success a table of header name/value pairs and the request body string are returned, otherwise nil followed by an error message is returned.

There are some gotchas in using this function:

test coverage: 90%
apr.parse_multipart(request, enctype [, limit [, tempdir]]) → parts

Parse a multipart/form-data or multipart/related HTTP request body according to RFC 2388 and the boundary string in enctype. On success the table with parameter name/value pairs is returned, otherwise a nil followed by an error message is returned.

The optional number limit gives the maximum in-memory bytes that the data structure used to parse the request may use (it defaults to 1024 KB), but be aware that because of internal copying apr.parse_multipart() can use more than double this amount of memory while parsing a request.

The optional string tempdir is the directory used for temporary storage of large uploads.

test coverage: 100%
apr.parse_cookie_header(header) → cookies

Parse a cookie header and store the cookies in a Lua table. On success the table with cookie name/value pairs is returned, otherwise nil followed by an error message and error code is returned.

test coverage: 90%
apr.parse_query_string(query_string) → parameters

Parse a URL encoded string into a Lua table. On success the table with parameter name/value pairs is returned, otherwise nil followed by an error message and error code is returned.

This function uses & and ; as the set of tokens to delineate words, and will treat a word without = as a name/value pair with the value true.

test coverage: 100%
apr.header_attribute(header, name) → value

Search a header string for the value of a particular named attribute. On success the matched value is returned, otherwise nil followed by an error message is returned.

This function is not binary safe.

test coverage: 100%
apr.uri_encode(string) → encoded

Encode unsafe bytes in string using percent-encoding so that the string can be embedded in a URI query string.

This function is not binary safe.

test coverage: none
apr.uri_decode(encoded) → string

Decode all percent-encoded bytes in the string encoded.

This function is binary safe.

Pollset

The pollset module enables asynchronous I/O which can improve throughput, latency and/or responsiveness. It works as follows:

  1. Create a pollset object by calling apr.pollset()
  2. Add one or more sockets to the pollset (e.g. a server socket listening for connections or a bunch of sockets receiving data)
  3. Call pollset:poll() in a loop to process readable/writable sockets

You can keep adding and removing sockets from the pollset at runtime, just keep in mind that the size given to apr.pollset() is a hard limit. There is an example of a simple asynchronous webserver that uses a pollset.

test coverage: 85%
apr.pollset(size) → pollset

Create a pollset object. The number size is the maximum number of sockets that the pollset can hold. On success a pollset object is returned, otherwise a nil followed by an error message is returned.

test coverage: 75%
pollset:add(socket, flag [, ...]) → status

Add a network socket to the pollset. On success true is returned, otherwise a nil followed by an error message is returned. One or two of the following flags should be provided:

If the socket is already in the pollset the flags of the existing entry in the pollset will be combined with the new flags. If you want to change a socket from readable to writable or the other way around, you have to first remove the socket from the pollset and then add it back with the new flag.

test coverage: 100%
pollset:remove(socket) → status

Remove a socket from the pollset. On success true is returned, otherwise a nil followed by an error message is returned. It is not an error if the socket is not contained in the pollset.

test coverage: 90%
pollset:poll(timeout) → readable, writable

Block for activity on the descriptor(s) in a pollset. The timeout argument gives the amount of time in microseconds to wait. This is a maximum, not a minimum. If a descriptor is signaled, we will wake up before this time. A negative number means wait until a descriptor is signaled. On success a table with sockets waiting to be read followed by a table with sockets waiting to be written is returned, otherwise a nil followed by an error message is returned.

test coverage: 100%
pollset:destroy() → status

Destroy a pollset. On success true is returned, otherwise a nil followed by an error message is returned. Note that pollset objects are automatically destroyed when they are garbage collected.

Process handling

test coverage: 100%
apr.proc_create(program) → process

Create a child process that will execute the given program when started. Once you’ve called this function you still need to execute the process using the process:exec() function. Here’s a simple example that emulates Lua’s os.execute() function:

function execute(command)
  local arguments = apr.tokenize_to_argv(command)
  local progname = table.remove(arguments, 1)
  local process = apr.proc_create(progname)
  process:cmdtype_set('shellcmd/env')
  process:exec(arguments)
  local done, code, why = process:wait(true)
  return code
end

execute 'echo This can be any process...'

This function is not binary safe.

test coverage: none
apr.proc_detach(daemonize) → status

Detach the current process from the controlling terminal. If daemonize evaluates to true the process will daemonize and become a background process, otherwise it will stay in the foreground. On success true is returned, otherwise a nil followed by an error message is returned.

test coverage: 85%
apr.proc_fork() → process, context

This is currently the only non-portable function in APR and by extension Lua/APR. It performs a standard UNIX fork. If the fork succeeds a process object and context string ('parent' or 'child') are returned, otherwise a nil followed by an error message is returned. The parent process can use the returned process object to wait for the child process to die:

if apr.proc_fork then -- forking supported?
  process, context = assert(apr.proc_fork())
  if context == 'parent' then
    print "Parent waiting for child.."
    process:wait(true)
    print "Parent is done!"
  else -- context == 'child'
    print "Child simulating activity.."
    apr.sleep(10)
    print "Child is done!"
  end
end

As the above example implies the apr.proc_fork() function will only be defined when forking is supported on the current platform.

test coverage: none
process:addrspace_set(separate) → status

If separate evaluates to true the child process will start in its own address space, otherwise the child process executes in the current address space from its parent. On success true is returned, otherwise a nil followed by an error message is returned. The default is no on NetWare and yes on other platforms.

test coverage: none
process:user_set(username [, password]) → status

Set the user under which the child process will run. On success true is returned, otherwise a nil followed by an error message is returned.

On Windows and other platforms where apr.user_set_requires_password is true this method requires a password.

This function is not binary safe.

test coverage: none
process:group_set(groupname) → status

Set the group under which the child process will run. On success true is returned, otherwise a nil followed by an error message is returned.

This function is not binary safe.

test coverage: 100%
process:cmdtype_set(type) → status

Set what type of command the child process will execute. On success true is returned, otherwise a nil followed by an error message is returned. The argument type must be one of:

test coverage: 73%
process:env_set(environment) → status

Set the environment variables of the child process to the key/value pairs in the table environment. On success true is returned, otherwise a nil followed by an error message is returned.

Please note that the environment table is ignored for the command types 'shellcmd/env', 'program/env' and 'program/env/path' (set using the process:cmdtype_set() method).

This function is not binary safe.

test coverage: none
process:dir_set(path) → status

Set which directory the child process should start executing in. On success true is returned, otherwise a nil followed by an error message is returned.

By default child processes inherit this directory from their parent process at the moment when the process:exec() call is made. To find out the current directory see the apr.filepath_get() function.

This function is not binary safe.

test coverage: none
process:detach_set(detach) → status

Determine if the child should start in detached state. On success true is returned, otherwise a nil followed by an error message is returned. Default is no.

test coverage: none
process:error_check_set(enabled) → nothing

Specify that process:exec() should do whatever it can to report failures directly, rather than find out in the child that something is wrong. This leads to extra overhead in the calling process, but it may help you handle these errors more gracefully.

Note that this option is only useful on platforms where fork() is used.

test coverage: 100%
process:io_set(stdin, stdout, stderr) → status

Determine if the child process should be linked to its parent through one or more pipes. On success true is returned, otherwise a nil followed by an error message is returned.

Each argument gives the blocking mode of a pipe, which can be one of the following strings:

Once the child process has been started with process:exec(), the pipes can be accessed with the methods process:in_get(), process:out_get() and process:err_get().

Here’s an example that executes the external command tr a-z A-Z to translate some characters to uppercase:

> p = apr.proc_create 'tr'
> p:cmdtype_set('shellcmd/env')
> p:io_set('child-block', 'parent-block', 'none')
> p:exec{'a-z', 'A-Z'}
> input = p:in_get()
> output = p:out_get()
> input:write('Testing, 1, 2, 3\n')
> input:close()
> print(output:read())
TESTING, 1, 2, 3
> output:close()
> p:wait(true)

test coverage: none
process:in_set(child_in [, parent_in]) → status

Initialize the standard input pipe of the child process to an existing pipe or a pair of pipes. This can be useful if you have already opened a pipe (or multiple files) that you wish to use, perhaps persistently across multiple process invocations - such as a log file. On success true is returned, otherwise a nil followed by an error message is returned. Here’s a basic example that connects two processes using an anonymous pipe:

-- Create a gzip process to decompress the Lua source code archive.
gzip = apr.proc_create 'gunzip'
gzip:cmdtype_set 'shellcmd/env'
gzip:in_set(apr.file_open('lua-5.1.4.tar.gz', 'rb'))

-- Create a tar process to list the files in the decompressed archive.
tar = apr.proc_create 'tar'
tar:cmdtype_set 'shellcmd/env'
tar:out_set(apr.pipe_open_stdout())

-- Connect the two processes using an anonymous pipe.
input, output = assert(apr.pipe_create())
gzip:out_set(output)
tar:in_set(input)

-- Start the pipeline by executing both processes.
gzip:exec()
tar:exec{'-t'}

test coverage: none
process:out_set(child_out [, parent_out]) → status

Initialize the standard output pipe of the child process to an existing pipe or a pair of pipes. This can be useful if you have already opened a pipe (or multiple files) that you wish to use, perhaps persistently across multiple process invocations - such as a log file. On success true is returned, otherwise a nil followed by an error message is returned.

test coverage: none
process:err_set(child_err [, parent_err]) → status

Initialize the standard error pipe of the child process to an existing pipe or a pair of pipes. This can be useful if you have already opened a pipe (or multiple files) that you wish to use, perhaps persistently across multiple process invocations - such as a log file. On success true is returned, otherwise a nil followed by an error message is returned.

test coverage: 100%
process:in_get() → pipe

Get the parent end of the standard input pipe (a writable pipe).

test coverage: 100%
process:out_get() → pipe

Get the parent end of the standard output pipe (a readable pipe).

test coverage: 100%
process:err_get() → pipe

Get the parent end of the standard error pipe (a readable pipe).

test coverage: 91%
process:exec([args]) → status

Create the child process and execute a program or shell command inside it. On success true is returned, otherwise a nil followed by an error message is returned. If the args array is given the contained strings become the command line arguments to the child process. The program name for the child process defaults to the name passed into apr.proc_create(), but you can change it by setting args[0].

This function is not binary safe.

test coverage: 73%
process:wait(how) → done [, why, code]

Wait for the child process to die. If how is true the call blocks until the process dies, otherwise the call returns immediately regardless of if the process is dead or not. The first return value is false if the process isn’t dead yet. If it’s true the process died and two more return values are available. The second return value is the reason the process died, which is one of:

The third return value is the exit code of the process. If an error occurs a nil followed by an error message is returned.

test coverage: none
process:kill(how) → status

Terminate a running child process. On success true is returned, otherwise a nil followed by an error message is returned. The parameter how must be one of:

Shared memory

Shared memory is memory that may be simultaneously accessed by multiple programs with an intent to provide communication among them. The Lua/APR binding represents shared memory as file objects through the shm:read(), shm:write() and shm:seek() methods.

test coverage: 78%
apr.shm_create(filename, size) → shm object

Create and make accessible a shared memory segment. The filename argument is the file to use for shared memory on platforms that require it. The size argument is the desired size of the segment.

A note about anonymous vs. named shared memory segments: Not all platforms support anonymous shared memory segments, but in some cases it is preferred over other types of shared memory implementations. Passing a nil filename parameter to this function will cause the subsystem to use anonymous shared memory segments. If such a system is not available, the error 'ENOTIMPL' is returned as the third return value (the first and second being nil and an error message string).

This function is not binary safe.

test coverage: 90%
apr.shm_attach(filename) → shm object

Attach to a shared memory segment that was created by another process. The filename argument is the file used to create the original segment (this must match the original filename). On success a shared memory object is returned, otherwise a nil followed by an error message is returned.

This function is not binary safe.

test coverage: 100%
apr.shm_remove(filename) → status

Remove the named resource associated with a shared memory segment, preventing attachments to the resource, but not destroying it. On success true is returned, otherwise a nil followed by an error message is returned.

This function is only supported on platforms which support name-based shared memory segments, and will return the error code 'ENOTIMPL' on platforms without such support. Removing the file while the shared memory is in use is not entirely portable, caller may use this to enhance obscurity of the resource, but be prepared for the the call to fail, and for concurrent attempts to create a resource of the same name to also fail.

Note that the named resource is also removed when a shared memory object created by apr.shm_create() is garbage collected.

This function is not binary safe.

test coverage: 100%
shm:read([format, ...]) → mixed value, ...

This function implements the interface of the file:read() function described in the Lua 5.1 reference manual. Here is the description from the reference manual:

Read from shared memory, according to the given formats, which specify what to read. For each format, the function returns a string (or a number) with the characters read, or nil if it cannot read data with the specified format. When called without formats, it uses a default format that reads the entire next line (see below).

The available formats are:

test coverage: 100%
shm:write(value [, ...]) → status

This function implements the interface of the file:write() function described in the Lua 5.1 reference manual. Here is the description from the reference manual:

Write the value of each argument to shared memory. The arguments must be strings or numbers. To write other values, use tostring() or string.format() before this function.

test coverage: 85%
shm:seek([whence [, offset]]) → offset

This function implements the interface of the file:seek() function described in the Lua 5.1 reference manual. Here is the description from the reference manual:

Sets and gets the shared memory position, measured from the beginning of the shared memory, to the position given by offset plus a base specified by the string whence, as follows:

In case of success, function seek returns the final shared memory position, measured in bytes from the beginning of the shared memory. If this function fails, it returns nil, plus a string describing the error.

The default value for whence is 'cur', and for offset is 0. Therefore, the call shm:seek() returns the current shared memory position, without changing it; the call shm:seek('set') sets the position to the beginning of the shared memory (and returns 0); and the call shm:seek('end') sets the position to the end of the shared memory, and returns its size.

test coverage: 100%
shm:detach() → status

Detach from a shared memory segment without destroying it. On success true is returned, otherwise a nil followed by an error message is returned.

test coverage: 100%
shm:destroy() → status

Destroy a shared memory segment and associated memory. On success true is returned, otherwise a nil followed by an error message is returned. Note that this will be done automatically when the shared memory object is garbage collected and has not already been destroyed.

Signal handling

Signals provide a limited form of inter-process communication. On UNIX they are for example used to communicate to daemons that they should reload their configuration or stop gracefully. This module works on Linux and most if not all UNIX systems but it’s not very useful on Windows, because Windows has poor support for signals:

SIGINT is not supported for any Win32 application, including Windows 98/Me and Windows NT/2000/XP. When a CTRL+C interrupt occurs, Win32 operating systems generate a new thread to specifically handle that interrupt. This can cause a single-thread application such as UNIX, to become multithreaded, resulting in unexpected behavior.

The SIGILL, SIGSEGV, and SIGTERM signals are not generated under Windows NT. They are included for ANSI compatibility. Thus you can set signal handlers for these signals with signal(), and you can also explicitly generate these signals by calling raise().

The following signal related functionality is not exposed by the Lua/APR binding because the Apache Portable Runtime doesn’t wrap the required functions:

test coverage: 100%
apr.signal(name [, handler]) → status

Set the signal handler function for a given signal. The argument name must be a string with the name of the signal to handle (apr.signal_names() returns a table of available names on your platform). The argument handler must be of type function, unless it is nil in which case default handling is restored for the given signal. On success true is returned, otherwise a nil followed by an error message is returned.

test coverage: 100%
apr.signal_raise(name) → status

Send a signal to the current process. The result is true when the call succeeded, false otherwise. This function is useful to test your own signal handlers:

> = apr.signal('SIGSEGV', function() print 'almost crashed :-)' end)
true
> = apr.signal_raise('SIGSEGV')
almost crashed :-)
true
> = apr.signal('SIGSEGV', nil)
true
> = apr.signal_raise('SIGSEGV')
zsh: segmentation fault  lua

test coverage: 100%
apr.signal_block(name) → status

Block the delivery of a particular signal. On success true is returned, otherwise a nil followed by an error message is returned.

test coverage: 100%
apr.signal_unblock(name) → status

Enable the delivery of a particular signal. On success true is returned, otherwise a nil followed by an error message is returned.

test coverage: 100%
apr.signal_names() → names

Return a table with available signal names on your platform. The keys of the table are the names of the signals and the values are the integer codes associated with the signals.

As the previous paragraph implies the result of this function can differ depending on your operating system and processor architecture. For example on my Ubuntu Linux 10.04 installation I get these results:

> signals = {}
> for k, v in pairs(apr.signal_names()) do
>>  table.insert(signals, { name = k, value = v })
>> end
> table.sort(signals, function(a, b)
>>  return a.value < b.value
>> end)
> for _, s in ipairs(signals) do
>>  print(string.format('% 2i: %s', s.value, s.name))
>> end
 1: SIGHUP
 2: SIGINT
 3: SIGQUIT
 4: SIGILL
 5: SIGTRAP
 6: SIGIOT
 6: SIGABRT
 7: SIGBUS
 8: SIGFPE
 9: SIGKILL
10: SIGUSR1
11: SIGSEGV
12: SIGUSR2
13: SIGPIPE
14: SIGALRM
15: SIGTERM
16: SIGSTKFLT
17: SIGCHLD
17: SIGCLD
18: SIGCONT
19: SIGSTOP
20: SIGTSTP
21: SIGTTIN
22: SIGTTOU
23: SIGURG
24: SIGXCPU
25: SIGXFSZ
26: SIGVTALRM
27: SIGPROF
28: SIGWINCH
29: SIGPOLL
29: SIGIO
30: SIGPWR
31: SIGSYS

After creating the above table I was surprised to see several numbers which have two names in the above output, but after looking up the names it turns out that these are just synonyms.

Note that just because a signal is included in this table doesn’t necessarily mean the signal is usable from Lua! For example SIGALRM is only useful when you can call the alarm() function defined by POSIX but that function isn’t exposed by the Apache Portable Runtime (you can use lalarm instead in this case).

String routines

test coverage: 100%
apr.strnatcmp(left, right) → status

Do a natural order comparison of two strings. Returns true when the left string is less than the right string, false otherwise. This function can be used as a callback for Lua’s standard library function table.sort().

> -- the canonical example:
> list = { 'rfc1.txt', 'rfc2086.txt', 'rfc822.txt' }
> -- collate order:
> table.sort(list)
> for _, name in ipairs(list) do print(name) end
rfc1.txt
rfc2086.txt
rfc822.txt
> -- natural order:
> table.sort(list, apr.strnatcmp)
> for _, name in ipairs(list) do print(name) end
rfc1.txt
rfc822.txt
rfc2086.txt

This function is not binary safe.

test coverage: 100%
apr.strnatcasecmp(left, right) → status

Like apr.strnatcmp(), but ignores the case of the strings.

This function is not binary safe.

test coverage: 100%
apr.strfsize(number [, padding]) → readable

Format a binary size positive number to a compacted human readable string. If the optional padding argument evaluates to true the resulting string will be padded with spaces to make it four characters wide, otherwise no padding will be applied.

> = apr.strfsize(1024)
'1.0K'
> = apr.strfsize(1024 ^ 2)
'1.0M'
> = apr.strfsize(1024 ^ 3)
'1.0G'

Here’s a simplified implementation of the UNIX command ls -l --human-readable which makes use of the padding argument to nicely line up the fields following the size:

function ls(dirpath)
  local directory = assert(apr.dir_open(dirpath))
  for info in directory:entries() do
    io.write(info.protection, ' ')
    io.write(info.user, ' ')
    io.write(info.group, ' ')
    io.write(apr.strfsize(info.size, true), ' ')
    io.write(apr.time_format('%Y-%m-%d %H:%I', info.ctime), ' ')
    io.write(info.name, '\n')
  end
  assert(directory:close())
end

This is what the result looks like for the source code directory of the Lua/APR project:

> ls 'lua-apr/src'
rw-r--r-- peter peter 5.4K 2011-01-02 22:10 apr.lua
rw-r--r-- peter peter 4.7K 2011-01-02 06:06 base64.c
rw-r--r-- peter peter  11K 2010-10-27 13:01 buffer.c
rw-r--r-- peter peter  13K 2011-01-02 21:09 crypt.c
rw-r--r-- peter peter 2.8K 2010-12-31 01:01 date.c
rw-r--r-- peter peter 9.4K 2011-01-01 16:04 dbm.c
rw-r--r-- peter peter 2.5K 2010-09-25 23:11 env.c
rw-r--r-- peter peter  17K 2011-01-02 22:10 errno.c
rw-r--r-- peter peter  10K 2011-01-02 22:10 filepath.c
rw-r--r-- peter peter 1.9K 2011-01-02 04:04 fnmatch.c
rw-r--r-- peter peter  12K 2010-12-31 01:01 io_dir.c
rw-r--r-- peter peter  25K 2011-01-02 04:04 io_file.c
rw-r--r-- peter peter  17K 2010-12-31 01:01 io_net.c
rw-r--r-- peter peter 4.6K 2011-01-02 22:10 io_pipe.c
rw-r--r-- peter peter  11K 2011-01-02 11:11 lua_apr.c
rw-r--r-- peter peter 9.0K 2011-01-02 11:11 lua_apr.h
rw-r--r-- peter peter 6.9K 2010-12-29 14:02 permissions.c
rw-r--r-- peter peter  26K 2011-01-02 22:10 proc.c
rw-r--r-- peter peter 866  2010-10-23 00:12 refpool.c
rw-r--r-- peter peter 4.8K 2010-12-29 14:02 stat.c
rw-r--r-- peter peter 3.5K 2011-01-02 22:10 str.c
rw-r--r-- peter peter 9.8K 2010-12-31 01:01 time.c
rw-r--r-- peter peter 4.7K 2010-09-25 23:11 uri.c
rw-r--r-- peter peter 2.5K 2010-09-25 23:11 user.c
rw-r--r-- peter peter 2.9K 2010-10-22 19:07 uuid.c
rw-r--r-- peter peter 3.8K 2011-01-02 04:04 xlate.c

Note: It seems that apr.strfsize() doesn’t support terabyte range sizes.

test coverage: 90%
apr.tokenize_to_argv(cmdline) → arguments

Convert the string cmdline to a table of arguments. On success the table of arguments is returned, otherwise a nil followed by an error message is returned.

This function is not binary safe.

Multi threading

This is an experimental multi threading module that makes it possible to execute Lua functions in dedicated Lua states and operating system threads. When you create a thread you can pass it any number of arguments and when a thread exits it can return any number of return values. For details about supported Lua values see the documentation of the serialization module.

Please consider the following issues when using this module:

test coverage: 78%
apr.thread(f [, ...]) → thread

Execute the Lua function f in a dedicated Lua state and operating system thread. Any extra arguments are passed onto the function. On success a thread object is returned, otherwise a nil followed by an error message is returned. You can use thread:join() to wait for the thread to finish and get the return values of the thread function.

This function is binary safe.

test coverage: 100%
apr.thread_yield() → nothing

Force the current thread to yield the processor. This causes the currently executing thread to temporarily pause and allow other threads to execute.

test coverage: 100%
thread:status() → status

Returns a string describing the state of the thread:

test coverage: 80%
thread:join() → status [, result, ...]

Block until a thread stops executing and return its result. If the thread terminated with an error a nil followed by an error message is returned, otherwise true is returned, followed by any return values of the thread function.

This function is binary safe.

Thread queues

The valid types that can be transported through thread queues are documented under the serialization module. The example of a multi threaded webserver uses a thread queue to pass sockets between the main server thread and several worker threads.

test coverage: 90%
apr.thread_queue([capacity]) → queue

Create a [FIFO] [fifo] queue. The optional argument capacity controls the maximum size of the queue and defaults to 1. On success the queue object is returned, otherwise a nil followed by an error message is returned.

The capacity of a thread queue cannot be changed after construction.

test coverage: 100%
queue:push(value [, ...]) → status

Add a tuple of one or more Lua values to the queue. This call will block if the queue is full. On success true is returned, otherwise a nil followed by an error message and error code is returned:

This function is binary safe.

test coverage: 100%
queue:pop() → value [, ...]

Get one or more Lua values from the queue. This call will block if the queue is empty. On success true is returned, otherwise a nil followed by an error message and error code is returned:

This function is binary safe.

test coverage: 100%
queue:trypush(value [, ...]) → status

Add a tuple of one or more Lua values to the queue. This call doesn’t block if the queue is full. On success true is returned, otherwise a nil followed by an error message and error code is returned:

This function is binary safe.

test coverage: 100%
queue:trypop() → value [, ...]

Get a tuple of Lua values from the queue. This call doesn’t block if the queue is empty. On success true is returned, otherwise a nil followed by an error message and error code is returned:

This function is binary safe.

test coverage: none
queue:interrupt() → status

Interrupt all the threads blocking on this queue. On success true is returned, otherwise a nil followed by an error message is returned.

test coverage: none
queue:terminate() → status

Terminate the queue, sending an interrupt to all the blocking threads. On success true is returned, otherwise a nil followed by an error message is returned.

test coverage: none
queue:close() → status

Close the handle queue and (if no other threads are using the queue) destroy the queue and release the associated memory. This function always returns true (it cannot fail).

This will be done automatically when the queue object is garbage collected which means you don’t need to call this unless you want to reclaim memory as soon as possible.

Time routines

Lua represents dates as numbers though the meaning of these numbers is not specified. The manual does state (in the documentation for os.time()) that on POSIX, Windows and some other systems these numbers count the number of seconds since some given start time called the epoch. This epoch is 00:00:00 January 1, 1970 UTC. The Apache Portable Runtime represents dates as the number of microseconds since that same epoch. As a compromise between the two units Lua/APR uses seconds but supports sub-second resolution in the decimal part of floating point numbers (see this thread on lua-l for discussion about the API).

test coverage: 100%
apr.sleep(seconds) → nothing

Sleep for the specified number of seconds. Sub-second resolution is supported so you can for example give 0.5 to sleep for half a second. This function may sleep for longer than the specified time because of platform limitations.

test coverage: 100%
apr.time_now() → time

Get the current time as the number of seconds since 00:00:00 January 1, 1970 UTC. If Lua is compiled with floating point support then more precision will be available in the decimal part of the returned number.

test coverage: 87%
apr.time_explode([time [, timezone]]) → components

Convert the numeric value time (current time if none given) to its human readable components. If timezone isn’t given or evaluates to false the local timezone is used. If its a number then this number is used as the offset in seconds from GMT. The value true is treated the same as 0, i.e. GMT. On success the table of components is returned, otherwise a nil followed by an error message is returned. The resulting table contains the following fields:

All of these fields are numbers except for isdst which is a boolean. Here’s an example of the output returned by apr.time_explode():

> -- Note that numeric dates are always in UTC while tables with
> -- date components are in the local timezone by default.
> components = apr.time_explode(1032030336.18671)
> = components
{
 usec = 186710,
 sec = 36,
 min = 5,
 hour = 21,
 day = 14,
 month = 9,
 year = 2002,
 wday = 7,
 yday = 257,
 gmtoff = 7200, -- my local timezone
 isdst = true,
}
> -- To convert a table of date components back into a number
> -- you can use the apr.time_implode() function as follows:
> = apr.time_implode(components)
1032030336.18671

test coverage: 85%
apr.time_implode(components) → time

Convert a table of time components to its numeric value. On success the time is returned, otherwise a nil followed by an error message is returned. See apr.time_explode() for a list of supported components.

test coverage: 69%
apr.time_format(format [, time]) → formatted

Format time (current time if none given) according to string format. On success the formatted time is returned, otherwise a nil followed by an error message is returned. The two special formats 'ctime' and 'rfc822' result in a fixed length string of 24 or 29 characters in length. The time argument may be either a number or a table with components like those returned by apr.time_explode().

> = apr.time_format('%Y-%m-%d %H:%I:%S', apr.time_now())
'2010-09-25 17:05:08'
> = apr.time_format('ctime', apr.time_now())
'Sat Sep 25 17:26:22 2010'
> = apr.time_format('rfc822', apr.time_now())
'Sat, 25 Sep 2010 15:26:36 GMT'

This function is not binary safe.

Uniform resource identifier parsing

test coverage: 93%
apr.uri_parse(uri) → components

Parse the Uniform Resource Identifier uri. On success a table of components is returned, otherwise a nil followed by an error message is returned. The table of components can have the following fields, all strings:

This function is not binary safe.

test coverage: 100%
apr.uri_unparse(components [, option]) → uri

Convert a table of URI components into a URI string. On success the URI string is returned, otherwise a nil followed by an error message is returned. The list of fields in the components table is available in the documentation for apr.uri_parse(). The argument option may be one of the following:

This function is not binary safe.

test coverage: 85%
apr.uri_port_of_scheme(scheme) → port

Return the default port for the given URI scheme string. Since at least APR 1.2.8 the following schemes are supported: acap, ftp, gopher, http, https, imap, ldap, nfs, nntp, pop, prospero, rtsp, sip, snews, ssh, telnet, tip, wais, z39.50r and z39.50s.

User/group identification

test coverage: 91%
apr.user_get() → username, groupname

Get the username and groupname of the calling process. On success the username and groupname are returned, otherwise a nil followed by an error message is returned.

This function is not binary safe.

test coverage: 87%
apr.user_homepath_get(username) → homepath

Get the home directory for the named user. On success the directory pathname is returned, otherwise a nil followed by an error message is returned.

This function is not binary safe.

Universally unique identifiers

Universally unique identifiers are a standard for generating unique strings that are specific to the machine on which they are generated and/or the time at which they are generated. They can be used as primary keys in databases and are used to uniquely identify file system types and instances on modern operating systems. This is what a standard format UUID looks like:

> = apr.uuid_format(apr.uuid_get())
'0ad5d4a4-591e-41f7-8be4-07d7961a8079'

test coverage: 100%
apr.uuid_get() → binary

Generate and return a UUID as a binary string of 16 bytes.

test coverage: 75%
apr.uuid_format(binary) → formatted

Format a UUID of 16 bytes following the standard format of 32 hexadecimal digits, displayed in 5 groups separated by hyphens, in the form 8-4-4-4-12 for a total of 36 characters, like f5dc3464-6c8f-654e-a407-b15b7a30f038. On success the formatted UUID is returned, otherwise a nil followed by an error message is returned.

test coverage: 70%
apr.uuid_parse(formatted) → binary

Parse a standard format UUID and return its 16-byte equivalent. On success the parsed UUID is returned, otherwise a nil followed by an error message is returned.

Character encoding translation

test coverage: 71%
apr.xlate(input, from, to) → translated

Translate a string of text from one character encoding to another. The from and to arguments are strings identifying the source and target character encoding. The special value 'locale' indicates the character set of the current locale. On success the translated string is returned, otherwise a nil followed by an error message is returned.

Which character encodings are supported by apr.xlate() is system dependent because APR can use both the system’s iconv implementation and the bundled library apr-iconv. To get a list of valid character encoding names you can look through the apr-iconv/ccs and apr-iconv/ces directories (those are links to the web interface of the apr-iconv repository).

This function is binary safe.

XML parsing

This module enables parsing of XML documents. Unlike LuaExpat the parsers returned by apr.xml() don’t use callbacks, instead they parse XML into a document object model which is then exposed to Lua. Because of this you can’t use apr.xml() for incremental parsing. To parse an XML document you follow these steps:

  1. Create an XML parser object by calling apr.xml()
  2. Parse the document by calling xml_parser:feed() repeatedly until the full document has been parsed
  3. Signal to the parser that the full document has been parsed by calling xml_parser:done()
  4. Get the parse information by calling xml_parser:getinfo()

Right now the only way to get the parse information is by calling xml_parser:getinfo() which converts the information to a Lua table following the Lua object model defined by LuaExpat. The Lua object model is a mapping of XML to Lua tables that’s not 100% complete (e.g. it doesn’t include namespaces) but makes it a lot easier to deal with XML in Lua.

In the future this module might expose the full XML parse tree to Lua as userdata objects, so that Lua has access to all parse information. This would also make it possible to expose the apr_xml_to_text() function.

test coverage: 70%
apr.xml([filename]) → xml_parser

Create an XML parser. If the optional string filename is given, the file pointed to by filename will be parsed. On success the parser object is returned, otherwise a nil followed by an error message is returned.

This function is not binary safe.

test coverage: 100%
xml_parser:feed(input) → status

Feed the string input into the XML parser. On success true is returned, otherwise a nil followed by an error message is returned.

This function is binary safe.

test coverage: 100%
xml_parser:done() → status

Terminate the parsing and save the resulting parse information. On success true is returned, otherwise a nil followed by an error message is returned.

test coverage: none
xml_parser:geterror() → message

Fetch additional error information from the parser after one of its methods has failed.

test coverage: 100%
xml_parser:getinfo() → table

Convert the parse information to a Lua table following the Lua Object Model defined by LuaExpat.

test coverage: 100%
xml_parser:close() → status

Close the XML parser and destroy any parse information. This will be done automatically when the xml_parser object is garbage collected which means you don’t need to call this unless you want to reclaim memory as soon as possible (e.g. because you just parsed a large XML document).

Serialization

The Lua/APR binding contains a serialization function based on the Metalua table-to-source serializer extended to support function upvalues and userdata objects created by the Lua/APR binding. The following Lua values can be serialized:

Restrictions:

The following functions in the Lua/APR binding internally use serialization to transfer Lua functions and other values between operating system threads:

test coverage: 83%
apr.serialize(...) → string

Serialize any number of Lua values (a tuple) into a source code string. When passed to apr.unserialize() this string results in a tuple of values that is structurally identical to the original tuple.

test coverage: 90%
apr.unserialize(string) → ...

Unserialize a source code string into one or more Lua values.

test coverage: 87%
apr.ref(object) → uuid

Prepare the Lua/APR userdata object so that it can be referenced from another Lua state in the same operating system process and associate a UUID with the object. The UUID is returned as a string. When you pass this UUID to apr.deref() you’ll get the same object back. This only works once, but of course you’re free to generate another UUID for the same object.

test coverage: 87%
apr.deref(uuid) → object

Convert a UUID that was previously returned by apr.ref() into a userdata object and return the object. You can only dereference a UUID once, but of course you’re free to generate another UUID for the same object.

Miscellaneous functions

test coverage: 100%
apr.platform_get() → name

Get the name of the platform for which the Lua/APR binding was compiled. Returns one of the following strings:

Please note that the labels returned by apr.platform_get() don’t imply that these platforms are fully supported; the author of the Lua/APR binding doesn’t have NETWARE and OS2 environments available for testing.

test coverage: 100%
apr.version_get() → versions_table

Get the versions of the libraries used by the Lua/APR binding. Returns a table with one or more of the following fields:

Each field is a string containing three numbers separated by dots. These numbers have the following meaning:

  1. Major API changes that can cause compatibility problems between the Lua/APR binding and APR library

  2. Minor API changes that shouldn’t impact existing functionality in the Lua/APR binding

  3. Used exclusively for bug fixes

This function can be useful when you want to know whether a certain bug fix has been applied to APR and/or APR-util or if you want to report a bug in APR, APR-util or the Lua/APR binding.

If you’re looking for the version of the Lua/APR binding you can use the apr._VERSION string, but note that Lua/APR currently does not adhere to the above versioning rules.

test coverage: 100%
apr.os_default_encoding() → name

Get the name of the system default character set as a string.

test coverage: 100%
apr.os_locale_encoding() → name

Get the name of the current locale character set as a string. If the current locale’s data cannot be retrieved on this system, the name of the system default character set is returned instead.

test coverage: 88%
apr.type(object) → name

Return the type of a userdata object created by the Lua/APR binding. If object is of a known type one of the following strings will be returned, otherwise nothing is returned:

File system permissions

The Apache Portable Runtime represents file system permissions somewhat similar to those of UNIX. There are three categories of permissions: the user, the group and everyone else (the world). Each category supports read and write permission bits while the meaning of the third permission bit differs between categories.

# How Lua/APR presents permissions

The Lua/APR binding uses a string of 9 characters to represent file system permissions such as those returned by apr.stat(). Here’s an example:

> = apr.stat('.', 'protection')
'rwxr-xr-x'

This is the syntax of these permissions strings:

As an example, rwxrwx--- means the user and group have full permissions while the world has none. Another example: r-xr-xr-x means no-one has write permissions.

# How you can request permissions

When you need to request file system permissions for an operation like reading or copying a file there are two string formats you can use. The first format is a string of nine characters that lists each permission explicitly. This is the format documented above.

The second format is very flexible and is one of the formats accepted by the Linux command line program chmod. The permissions are split in three groups with a one-letter code: user is u, group is g and world is o (for “others”). One or more permissions can then be assigned to one or more of these groups. Here’s an example that requests read permission for user, group and others: ugo=r. Now when you also need write permission for user and group, you can use ugo=r,ug=w.

Error handling

Most functions in the Lua/APR binding follow the Lua idiom of returning nil followed by an error message string. These functions also return a third argument which is the symbolic name of the error (or the error code in case a symbolic name cannot be determined). The following symbolic names are currently defined (there’s actually a lot more but they shouldn’t be relevant when working in Lua):

Note that the error descriptions above were copied verbatim from apr_errno.h.

Example: HTTP client

The following Lua script implements a minimal HTTP client which can be used to download a given URL on the command line (comparable to wget and curl):

$ FILE=lua-5.1.4.tar.gz
$ URL=http://www.lua.org/ftp/$FILE
$ time curl -s $URL > $FILE
0,01s user 0,02s system 6% cpu 0,465 total
$ time lua examples/download.lua $URL > $FILE
0,03s user 0,02s system 9% cpu 0,549 total

Note that this script and Lua/APR in general are a bit handicapped in that they don’t support HTTPS because the Apache Portable Runtime does not support encrypted network communication.

local apr = require 'apr'

-- Report errors without stack traces.
local function assert(...)
  local status, message = ...
  if not status then
    io.stderr:write('Error: ', message or '(no message)', '\n')
    os.exit(1)
  end
  return ...
end

local function getpage(url)
  local components = assert(apr.uri_parse(url))
  assert(components.scheme == 'http', "invalid protocol!")
  local port = assert(components.port or apr.uri_port_of_scheme(components.scheme))
  local socket = assert(apr.socket_create())
  assert(socket:connect(components.hostname, port))
  local pathinfo = assert(apr.uri_unparse(components, 'pathinfo'))
  assert(socket:write('GET ', pathinfo, ' HTTP/1.0\r\n',
                      'Host: ', components.hostname, '\r\n',
                      '\r\n'))
  local statusline = assert(socket:read(), 'HTTP response missing status line!')
  local protocol, statuscode, reason = assert(statusline:match '^(%S+)%s+(%S+)%s+(.-)$')
  local redirect = statuscode:find '^30[123]$'
  for line in socket:lines() do
    local name, value = line:match '^(%S+):%s+(.-)\r?$'
    if name and value then
      if redirect and name:lower() == 'location' then
        io.stderr:write("Following redirect to ", value, " ..\n")
        return getpage(value)
      end
    else
      return (assert(socket:read '*a', 'HTTP response missing body?!'))
    end
  end
  if statuscode ~= '200' then error(reason) end
end

local usage = "Please provide a URL to download as argument"
io.write(getpage(assert(arg and arg[1], usage)))

-- vim: ts=2 sw=2 et

Example: Single threaded webserver

The following script implements a minimalistic webserver on top of Lua/APR network sockets. It should work out of the box on Windows and UNIX, although you might get a prompt from your firewall. Once the server is running you can open http://localhost:8080 in your web browser to see the server in action. Because the server is single threaded I was curious how bad it would perform, so I tested it with ApacheBench:

$ lua examples/webserver.lua &
$ ab -qt5 http://localhost:8080/ | grep 'Requests per second\|Transfer rate'
Requests per second:    3672.19 [#/sec] (mean)
Transfer rate:          2201.88 [Kbytes/sec] received

That’s not too bad for 40 lines of code! For more complex webservers see the multi threaded and asynchronous webserver examples.

Note that all benchmarks are run on my Compaq Presario CQ60 laptop (which features an Intel Core 2 Duo T5800 processor clocked at 2 GHz and 3 GB of RAM) and that the Lua/APR binding was compiled without debugging symbols.

local port_number = tonumber(arg[1]) or 8080
local bind_address = arg[2] or '*'

-- Load the Lua/APR binding.
local apr = require 'apr'

-- Initialize the server socket.
local server = assert(apr.socket_create())
assert(server:bind(bind_address, port_number))
assert(server:listen(1))
print("Running webserver on http://" .. bind_address .. ":" .. port_number .. " ..")

-- Wait for clients to serve.
local visitor = 1
local template = [[
<html>
  <head>
    <title>Hello from Lua/APR!</title>
    <style type="text/css">
      body { font-family: sans-serif; }
      dt { font-weight: bold; }
      dd { font-family: monospace; margin: -1.4em 0 0 14em; }
    </style>
  </head>
  <body>
    <h1>Hello from Lua/APR!</h1>
    <p><em>You are visitor number %010i.</em></p>
    <p>The headers provided by your web browser:</p>
    <dl>%s</dl>
  </body>
</html>
]]
while true do
  local status, message = pcall(function()
    local client = assert(server:accept())
    -- Read the HTTP request so that the client can receive data.
    local request = assert(client:read(), "Failed to receive request from client!")
    local method, location, protocol = assert(request:match '^(%w+)%s+(%S+)%s+(%S+)')
    local headers = {}
    for line in client:lines() do
      local name, value = line:match '^(%S+):%s+(.-)$'
      if not name then break end
      table.insert(headers, '<dt>' .. name .. ':</dt><dd>' .. value .. '</dd>')
    end
    -- Generate the HTTP response.
    table.sort(headers)
    content = template:format(visitor, table.concat(headers))
    client:write(protocol, ' 200 OK\r\n',
                 'Content-Type: text/html\r\n',
                 'Content-Length: ' .. #content .. '\r\n',
                 'Connection: close\r\n',
                 '\r\n',
                 content)
    assert(client:close())
    visitor = visitor + 1
  end)
  if not status then
    print('Error while serving request:', message)
  end
end

-- vim: ts=2 sw=2 et

Example: Multi threaded webserver

Thanks to the multi threading and thread queue modules in the Apache Portable Runtime it is possible to improve the performance of the single threaded webserver from the previous example. Here is a benchmark of the multi threaded code listed below (again using ApacheBench, but now with the -c argument):

$ CONCURRENCY=4
$ lua examples/threaded-webserver.lua $CONCURRENCY &
$ ab -qt5 -c$CONCURRENCY http://localhost:8080/ | grep 'Requests per second\|Transfer rate'
Requests per second:    9210.72 [#/sec] (mean)
Transfer rate:          5594.79 [Kbytes/sec] received

Comparing these numbers to the benchmark of the single threaded webserver we can see that the number of requests per second went from 3670 to 9210, more than doubling the throughput of the webserver on a dual core processor. If you want to know how we can make it even faster, have a look at the asynchronous webserver example.

local num_threads = tonumber(arg[1]) or 2
local port_number = tonumber(arg[2]) or 8080

local template = [[
<html>
  <head>
    <title>Hello from Lua/APR!</title>
    <style type="text/css">
      body { font-family: sans-serif; }
      dt { font-weight: bold; }
      dd { font-family: monospace; margin: -1.4em 0 0 14em; }
    </style>
  </head>
  <body>
    <h1>Hello from Lua/APR!</h1>
    <p><em>This web page was served by worker %i.</em></p>
    <p>The headers provided by your web browser:</p>
    <dl>%s</dl>
  </body>
</html>
]]

-- Load the Lua/APR binding.
local apr = require 'apr'

-- Initialize the server socket.
local server = assert(apr.socket_create())
assert(server:bind('*', port_number))
assert(server:listen(num_threads * 2))
print("Running webserver with " .. num_threads .. " client threads on http://localhost:" .. port_number .. " ..")

-- Create the thread queue (used to pass sockets between threads).
local queue = apr.thread_queue(num_threads)

-- Define the function to execute in each child thread.
function worker(thread_id, queue, template)
  pcall(require, 'luarocks.require')
  local apr = require 'apr'
  while true do
    local client, msg, code = queue:pop()
    assert(client or code == 'EINTR', msg)
    if client then
      local status, message = pcall(function()
        local request = assert(client:read(), "Failed to receive request from client!")
        local method, location, protocol = assert(request:match '^(%w+)%s+(%S+)%s+(%S+)')
        local headers = {}
        for line in client:lines() do
          local name, value = line:match '^(%S+):%s+(.-)$'
          if not name then
            break
          end
          table.insert(headers, '<dt>' .. name .. ':</dt><dd>' .. value .. '</dd>')
        end
        table.sort(headers)
        local content = template:format(thread_id, table.concat(headers))
        client:write(protocol, ' 200 OK\r\n',
                     'Content-Type: text/html\r\n',
                     'Content-Length: ' .. #content .. '\r\n',
                     'Connection: close\r\n',
                     '\r\n',
                     content)
        assert(client:close())
      end)
      if not status then
        print('Error while serving request:', message)
      end
    end
  end
end

-- Create the child threads and keep them around in a table (so that they are
-- not garbage collected while we are still using them).
local pool = {}
for i = 1, num_threads do
  table.insert(pool, apr.thread(worker, i, queue, template))
end

-- Enter the accept() loop in the parent thread.
while true do
  local status, message = pcall(function()
    local client = assert(server:accept())
    assert(queue:push(client))
  end)
  if not status then
    print('Error while serving request:', message)
  end
end

-- vim: ts=2 sw=2 et

Example: Asynchronous webserver

We can do even better than the performance of the multi threaded webserver by using the APR pollset module. The following webserver uses asynchronous network communication to process requests from multiple clients ‘in parallel’ without using multiple threads or processes. Here is a benchmark of the asynchronous code listed below (again using ApacheBench with the -c argument):

$ CONCURRENCY=4
$ POLLSET_SIZE=10
$ lua examples/async-webserver.lua $POLLSET_SIZE 8080 cheat &
$ ab -qt5 -c$CONCURRENCY http://localhost:8080/ | grep 'Requests per second\|Transfer rate'
Requests per second:    11312.64 [#/sec] (mean)
Transfer rate:          6219.74 [Kbytes/sec] received

The single threaded webserver handled 3670 requests per second, the multi threaded webserver handled 9210 requests per second and the asynchronous webserver below can handle 11310 requests per second. Actually in the above benchmark I may have cheated a bit (depending on whether your goal is correct usage or performance). When I started writing this asynchronous server example I didn’t bother to add writable sockets to the pollset, instead I handled the request and response once the client socket was reported as readable by the pollset. Later on I changed the code to handle writing using the pollset and I noticed that the performance dropped. This is probably because the example is so contrived. Anyway here’s the performance without cheating:

$ lua examples/async-webserver.lua $POLLSET_SIZE 8080 &
$ ab -qt5 -c$CONCURRENCY http://localhost:8888/ | grep 'Requests per second\|Transfer rate'
Requests per second:    9867.17 [#/sec] (mean)
Transfer rate:          5425.03 [Kbytes/sec] received

Now follows the implementation of the asynchronous webserver example:

local pollset_size = tonumber(arg[1]) or 10
local port_number = tonumber(arg[2]) or 8080
local cheat = arg[3] == 'cheat' -- cheat to make it faster?

local template = [[
<html>
  <head>
    <title>Hello from Lua/APR!</title>
    <style type="text/css">
      body { font-family: sans-serif; }
      dt { font-weight: bold; }
      dd { font-family: monospace; margin: -1.4em 0 0 14em; }
    </style>
  </head>
  <body>
    <h1>Hello from Lua/APR!</h1>
    <p>The headers provided by your web browser:</p>
    <dl>%s</dl>
  </body>
</html>
]]

-- Load the Lua/APR binding.
local apr = require 'apr'

-- Initialize the server socket.
local server = assert(apr.socket_create())
assert(server:bind('*', port_number))
assert(server:listen(pollset_size))

-- Create the pollset.
local pollset = assert(apr.pollset(pollset_size))
assert(pollset:add(server, 'input'))

-- Use a table indexed with socket objects to keep track of "sessions".
local sessions = setmetatable({}, {__mode='k'})

-- Enter the I/O loop.
print("Running webserver on http://localhost:" .. port_number .. " ..")
while true do
  local readable, writable = assert(pollset:poll(-1))
  -- Process requests.
  for _, socket in ipairs(readable) do
    if socket == server then
      local client = assert(server:accept())
      assert(pollset:add(client, 'input'))
    else
      local request = assert(socket:read(), "Failed to receive request from client!")
      local method, location, protocol = assert(request:match '^(%w+)%s+(%S+)%s+(%S+)')
      local headers = {}
      for line in socket:lines() do
        local name, value = line:match '^(%S+):%s+(.-)$'
        if not name then
          break
        end
        table.insert(headers, '<dt>' .. name .. ':</dt><dd>' .. value .. '</dd>')
      end
      table.sort(headers)
      local content = template:format(table.concat(headers))
      assert(socket:write(
        protocol, ' 200 OK\r\n',
        'Content-Type: text/html\r\n',
        'Content-Length: ', #content, '\r\n',
        'Connection: close\r\n',
        '\r\n'))
      if cheat then
        assert(socket:write(content))
        assert(pollset:remove(socket))
        assert(socket:close())
      else
        sessions[socket] = content
        assert(pollset:remove(socket))
        assert(pollset:add(socket, 'output'))
      end
    end
  end
  if not cheat then
    -- Process responses.
    for _, socket in ipairs(writable) do
      assert(socket:write(sessions[socket]))
      -- I don't like that when I switch the order of these
      -- calls, it breaks... Seems like a fairly big gotcha.
      assert(pollset:remove(socket))
      assert(socket:close())
    end
  end
end

-- vim: ts=2 sw=2 et

Last updated Tue Dec 06 20:08:32 UTC 2011.