Fun with Lua

Introduction

Lua is available as a plain interpreter with very limited features, or through richer alternatives:

The reason for module managers is that the Lua interpreter only offers very basic features, and rely on modules for the rest. Modules can be written in pure Lua (plain text) and compiled by the interpreter on the fly, but can also be written in C, and thus require a C compiler in addition to the module manager. Here's an example at how to write, compile, and call a Lua module in C.

An easy way to distribute an application is to zip the Lua interpreter along with modules (pure Lua, and compiled C modules as DLLs).

IDEs: ZeroBrane Studio (up-to-date, cross-platform), Decoda (Last update 2013?), and LuaEdit (Last update December 18, 2011).

The lua-users wiki lists many user-contributed addons for Lua, including tools, libraries, full distributions, and binaries for several platforms. You can get support through the Lua mailing-list.

The lua interpreter

The -e option allows us to enter code directly into the command line:

% lua -e "print(math.sin(12))"

The -i switch enters interactive mode after running the other argument.

The -l option loads a library:

% lua -i -llib -e "x = 10"

A script can retrieve its arguments through the predefined global variable arg. In a call like % lua script a b c, the interpreter creates the table arg with all the command-line arguments, before running any code. The script name goes into index 0; its first argument ("a" in the example) goes to index 1, and so on:

arg[0] = "script"
arg[1] = "a"
arg[2] = "b"

Compiling

The simplest way to produce a precompiled file —also called a binary chunk in Lua jargon— is with the luac program that comes in the standard distribution. For instance, the next call creates a new file prog.lc with a precompiled version of a file prog.lua:

$ luac -o prog.lc prog.lua

The Lua interpreter can execute this new file just like normal Lua code, performing exactly as it would with the original source:

$ lua prog.lc

Lua accepts precompiled code mostly anywhere it accepts source code. In particular, both loadfile and load accept precompiled code.

Installing LuaRocks

Note: LuaRocks is not available as a Windows installer, and requires the user to edit Windows' system and  user environment variables. Also, as of March 2020, it ships with Lua 5.1 so you are required to compile Lua yourself if you want/need a more recent release.

  1. Install the MinGW C compiler in the default directory so that the LuaRocks can find it
  2. If you need the latest release of Lua, download and unzip its source code in eg. c:\temp\Lua.5.3.5.src
  3. Launch a Mingw shell: C:\MinGW\msys\1.0\msys.bat
  4. cd /c/temp/Lua.5.3.5.src
  5. make PLAT=mingw
  6. make install INSTALL_TOP=c:/lua/5.3 TO_BIN="lua.exe luac.exe lua53.dll"
  7. Exit the Mingw shell, and open a DOS (cmd) box
  8. Download and unzip the LuaRocks source code in eg. c:\temp\luarocks-3.3.1-win32
  9. cd c:\temp\luarocks-3.3.1-win32
  10. install.bat /F /MW /LV 5.3 /LUA c:\lua\5.3
  11. Update Windows' environment variables as shown. (bug?) You might need to copy the system LUA_PATH into the user LUA_PATH for Lua to find modules downloaded by LuaRocks
  12. As a test, install a new module through LuaRocks: Open a DOS box, and run "luarocks install dkjson"
  13. Launch Lua, and run: local json = require ("dkjson")

Note: "luarocks config" and "luarocks path" display LuaRocks' settings.

Error Handling and Exceptions

error()

assert()

pcall(), xpcall()

debug.debug, debug.traceback

Hello, world!

Here's a simple Lua script that you can use to check that the interpreter works:

-- This is a comment
print ("Hello World!")

Note: The shebang line is not needed in Windows, provided the OS can locate the interpreter (eg. lua5.1.exe).

To run the script, simply call the interpreter with the script name as parameter:

lua5.1.exe hello.lua

On Linux, alternatively, add the shebang line and make the script executable, respectively:

#!/usr/bin/lua
 
chmod +x hello.lua

Commenting a whole block

--[[
whole
block
commented
out
--]]

Variables

By default, variables in Lua are global. All local variables must be declared as such. Unlike global variables, a local variable has its scope limited to the block where it is declared.

The Lua distribution comes with a module strict.lua for global-variable checks; it raises an error if we try to assign to a non-existent global inside a function or to use a non-existent global. It is a good habit to use it when developing Lua code.

Lua is a dynamically-typed language. There are no type definitions in the language; each value carries its own type. There are eight basic types in Lua: nil, Boolean, number, string, userdata, function, thread, and table. The function type gives the type name of any given value.

Lua uses two alternative representations for numbers: 64-bit integer numbers, called simply integers, and double-precision floating-point numbers, called simply floats.

The standard string library that comes with Lua assumes one-byte characters, but it can handle UTF-8 strings quite reasonably.

Strings in Lua are immutable values.

We can get the length of a string (in bytes) using the length operator (denoted by #):

a = "hello"
print(#a) --> 5

Like long comments, multi-line strings can be built using double square brackets, as we do with long comments:

page = [[
<html>
</html>
]]

Operators

The == operator tests for equality; the ~= operator is the negation of equality.

Control Structures

To exit a script before the end

if true then return end

Alternatively, the function os.exit terminates the execution of a program. Its optional first argument is the return status of the program. It can be a number (zero means a successful execution) or a Boolean (true means a successful execution).

Reading Environment Variables

The function os.getenv gets the value of an environment variable. It takes the name of the variable and returns a string with its value:

print(os.getenv("HOME"))

Running External Commands

Declaring and filling a table

t = {}
t["foo"] = 123
print t["foo"]

Alternatively:

t = {foo = 123, bar = "456"}
t.foo = 123
print t.foo

Looping through a table

for key,value in pairs(t) do
    print(key,value)
end

Alternatively, this can be done in a single line:

for key,value in pairs(t) do print(key, value) end

Finding/replacing text

To provide very compact binaries, Lua supports neither POSIX regex nor Perl regular expressions pattern matching. Nevertheless, pattern matching in Lua is a powerful tool, and includes some features that are difficult to match with standard POSIX implementations.

The string library offers four functions based on patterns: find, match, and sub.

find

s = "hello world"
i, j = string.find(s, "hello")
print(string.sub(s, i, j))
 
if string.find(s, "^[+-]?%d+$") then ...

(g)match

date = "Today is 17/7/1990"
d = string.match(date, "%d+/%d+/%d+")
print(d)

Find all occurences in the string

s = "some string"
words = {}
for w in string.gmatch(s, "%a+") do
    words[#words + 1] = w
end

Capture bits:

pair = "name = Anna"
key, value = string.match(pair, "(%a+)%s*=%s*(%a+)")

%n can be used to refer to the n capture:

s = [[then he said: "it's all right"!]]
q, quotedPart = string.match(s, "([\"'])(.-)%1")

Note: %0 is the whole match

print((string.gsub("hello Lua!", "%a", "%0-%0"))) --> h-he-el-ll-lo-o L-Lu-ua-a!
print((string.gsub("hello Lua", "(.)(.)", "%2%1"))) --> ehll ouLa

(g)sub

s = string.gsub("Lua is cute", "cute", "great")

An optional fourth parameter limits the number of substitutions to be made:

s = string.gsub("all lii", "l", "x", 1)

The third argument can also be a function or a table (_G is a predefined table containing all global variables):

function expand (s)
    return (string.gsub(s, "$(%w+)", _G))
end
 
name = "Lua"; status = "great"
print(expand("$name is $status, isn't it?"))

Patterns

Patterns in Lua use the percent sign as an escape. In general, any escaped alphanumeric character has some special meaning (e.g., '%a' matches any letter), while any escaped non-alphanumeric character represents itself (e.g., '%.' matches a dot).

Predefined character classes:

An upper-case version of any of these classes represents the complement of the class. For instance, '%A' represents all non-letter characters:

print((string.gsub("hello, up-down!", "%A", ".")))

Note: Extra parentheses are used to discard the second result, which is the number of substitutions.

Unlike ? used by POSIX/Perl regular expressions, Lua uses "-" to specify non-greedy regular expressions:

output = string.gsub(bigstring, "<name>.-</name>", "REMOVED")

'%bxy' is used to match balanced strings, where x and y are any two distinct characters; the x acts as an opening character and the y as the closing one. Typically, we use this pattern as '%b()', '%b[]', '%b{}', or '%b<>', but we can use any two distinct characters as delimiters. Again, it's greedy and will take in all the characters until the last occurence of the y character:

s = "a (enclosed (in) parentheses) line"
print((string.gsub(s, "%b()", "")))

'%f[char-set]' represents a frontier pattern. It matches an empty string only if the next character is in char-set but the previous one is not:

s = "the anthem is the theme"
print((string.gsub(s, "%f[%w]the%f[%W]", "one"))) --> one anthem is one theme

An empty capture like '()' has a special meaning in Lua. This pattern captures its position in the subject string, as a number:

print(string.match("hello", "()ll()")) --> 3 5

(Note that the result of this example is not the same as what we get from string.find, because the position of the second empty capture is after the match.)

 

String.format

The function string.format is a powerful tool for formatting strings and converting numbers to strings :

string.format("x = %d y = %d", 10, 20)

Extracting first token in space-separated items

exec = line:sub(1,string.find(line,"%s")-1)

Extracting space- and comma-separated tokens

for token in string.gmatch(line, "[^%s,]+") do
    print(token)
end

Extracting filename from path

filename=string.gsub(fullfilename, "(.*/)(.*)", "%2")

Extracting multiple tokens

page = '<span>item1</span><br>item2<br>item3<br><i>'

for a,b in string.gmatch(page, '<span>.-</span><br>(.-)<br>(.-)<br><i>') do

        stuff = a .. b .. "\n========\n"

        print(stuff)

end

Date and Time

The standard libraries offer few functions to manipulate date and time in Lua. As usual, all it offers is what is available in the standard C libraries: os.time and os.date.

Working with files

To maximize portability and embeddability, Lua offers only the functionalities that the ISO C standard offers —namely, basic file manipulation plus some extras.

The I/O library offers two different models for file manipulation: Simple and and complete.

Simple I/O Model

The simple model assumes a current input stream and a current output stream, and its I/O operations operate on these streams. The simple model assumes a current input stream and a current output stream, and its I/O operations operate on these streams. The library initializes the current input stream to the process's standard input (stdin) and the current output stream to the process's standard output (stdout).

print() vs. io.write(): As a rule, you should use print only for quick-and-dirty programs or debugging; always use io.write when you need full control over your output. Unlike print, write adds no extra characters to the output, such as tabs or newlines. Moreover, io.write allows you to redirect your output, whereas print always uses the standard output. Finally, print automatically applies tostring to its arguments; this is handy for debugging, but it also can hide subtle bugs.

The function io.read reads strings from the current input stream. It supports different arguments:

Here's an example:

t = io.read("a")
t = string.gsub(t, "bad", "good")
io.write(t)

To iterate line by line:

local count = 0
for line in io.lines() do
    count = count + 1
    io.write(string.format("%6d ", count), line, "\n")
end

Complete I/O Model

Here's how to open and read from a file:

local f = assert(io.open(filename, "r"))
local t = f:read("a")
f:close()

Renaming/removing Files

os.rename changes the name of a file and os.remove removes (deletes) a file. Note that these functions come from the os library, not the io library, because they manipulate real files, not streams.

Files

Lua's operating-system library and I/O library are mainly interfaces to the underlying system, so their support for UTF-8 strings depends on that underlying system. On Linux, for instance, we can use UTF-8 for file names, but Windows uses UTF-16. Therefore, to manipulate Unicode file names on Windows, we need either extra libraries or changes to the standard Lua libraries. If needed, check the new utf8 library.

Check the size of a file

function fsize (file)
        local current = file:seek()      -- get current position
        local size = file:seek("end")    -- get file size
        file:seek("set", current)        -- restore position
        return size
end
 
fh = assert(io.open("/full/path/to/file","r"))
print(fsize(fh))
fh:close()

Read from a text file

local file = assert(io.open ("myfile.txt","r"))
data = file:read("*a")
print(data)
file:close()

Alternatively, use io.lines:

for line in io.lines('myfile.txt') do
        print(line)
end

Write to a text file

local file = assert(io.open ("myfile.txt","w"))
file:write("Blah\n")
file:close()

Read file and extract tokens

libs = io.open("myfile.txt","r")
for line in libs:lines() do
    -- finds all occurences of "some text," at the beginning of a line
    for token in string.gmatch(line, "[^%s,]+") do
        print(token)
    end
end
libs:close()

Here's an example that extract all URL's from a text file:

for line in data:lines() do
        for w in string.gmatch(line, '<a href="(/somedir/[%w-]+%.php)">') do
                print(w)
        end
end

Tables

Tables are the only data structuring mechanism in Lua. They are used to represent arrays, sets, records, etc. A table in Lua is essentially an associative array. A table is an array that accepts not only numbers as indices, but also strings or any other value of the language (except nil):

a = {}
a[20] = "great"
a["x"]=10 --OR a.x = 10

Although we can index a table both with the number 0 and with the string "0", these two values are different and therefore denote different entries in a table.

Constructors are expressions that create and initialize tables. The simplest constructor is the empty constructor, {}, as we have seen. Constructors also initialize lists.

days = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}
a = {x = 10, y = 20}

Note: The first element of the constructor has index 1, not 0.

To remove an item, set its value to nil:

w.x = nil

Arrays, Lists, and Sequences

To represent a conventional array or a list, we simply use a table with integer keys:

a = {}
for i = 1, 10 do
    a[i] = io.read()
end

It is customary in Lua to start arrays with one (and not with zero, as in C) and many facilities in Lua stick to this convention.

On tables, the length operator (#) gives the length of the sequence represented by the table:

for i = 1, #a do
    print(a[i])
end

Append an item to the table:

a[#a + 1] = v

Note: The length operator only works reliably for sequences, ie. lists without holes. If you really need to handle lists with holes, you should store the length explicitly somewhere.

To traverse key/value pairs:

t = {10, print, x = 12, k = "hi"}
for k, v in pairs(t) do
    print(k, v)
end

Due to the way that Lua implements tables, the order that elements appear in a traversal is undefined.

For lists, use the ipairs iterator and Lua ensures the order:

t = {10, print, 12, "hi"}
for k, v in ipairs(t) do
    print(k, v)
end

The table library offers several useful functions to operate over lists and sequences

t = {}
for line in io.lines() do
    table.insert(t, line)
end
print(#t)

The function table.remove removes and returns an element from the given position in a sequence, moving subsequent elements down to fill the gap. With these two functions (insert/remove), it is straightforward to implement stacks, queues, and double queues.

Use table.pack/unpack:

a,b = table.unpack{10,20}

Functions

An unconventional but quite convenient feature of Lua is that functions can return multiple results. This is the case for several predefined functions, such as string.find().

A function in Lua can be variadic, that is, it can take a variable number of arguments:

function add (...)
    local s = 0
    for _, v in ipairs{...} do
        s = s + v
    end
    return s
end

The three-dot expression is called a vararg expression:

function foo (...)
    local a, b, c = ...

Modules

A package is a collection of modules. A module is some code that can be loaded through the function require and that creates and returns a table. Everything that the module exports, such as functions and constants, it defines inside this table, which works as a kind of namespace.

The stand-alone interpreter preloads all standard libraries with code equivalent to this:

string = require "string"

To import modules:

local m = require "mod"
local f = require "mod".foo -- (require("mod")).foo

The path that require uses to search for Lua files is always the current value of the variable package.path. The path used to search for a C library works exactly in the same way, but its value comes from the variable package.cpath, instead of package.path. The previous example uses .so for all templates; in Windows, a typical path would be more like this one:

.\?.dll;C:\Program Files\Lua502\dll\?.dll

The function package.searchpath encodes all those rules for searching libraries.

The array package.searchers lists the searchers that require uses. When looking for a module, require calls each searcher in the list passing the module name, until one of them finds a loader for the module. If the list ends without a positive response, require raises an error.

Installing binary modules

Here's how to install the LuaRocks package manager on Debian/Ubuntu, and install ready-to-use modules:

  1. apt-get install luarocks
  2. luarocks install mypackage

Note: LuaRocks has a lot of dependencies:

The following extra packages will be installed:
  autotools-dev binutils cpp cpp-4.6 gcc gcc-4.6 gcc-4.6-base libc-dev-bin libc6-dev libgmp10 libgomp1 libltdl-dev libltdl7
  liblua5.1-0 liblua5.1-0-dev libmpc2 libmpfr4 libreadline-dev libreadline6-dev libtinfo-dev libtool linux-libc-dev lua5.1
  manpages-dev pkg-config zip
 
Suggested packages:
  binutils-doc cpp-doc gcc-4.6-locales gcc-multilib make autoconf automake1.9 flex bison gdb gcc-doc libmudflap0-4.6-dev
  gcc-4.6-doc libgcc1-dbg libgomp1-dbg libquadmath-dbg libmudflap0-dbg binutils-gold glibc-doc libtool-doc automaken
  gfortran fortran95-compiler gcj
 
The following NEW packages will be installed:
  autotools-dev binutils cpp cpp-4.6 gcc gcc-4.6 gcc-4.6-base libc-dev-bin libc6-dev libgmp10 libgomp1 libltdl-dev libltdl7
  liblua5.1-0 liblua5.1-0-dev libmpc2 libmpfr4 libreadline-dev libreadline6-dev libtinfo-dev libtool linux-libc-dev lua5.1
  luarocks manpages-dev pkg-config zip
 
0 upgraded, 27 newly installed, 0 to remove and 0 not upgraded.
Need to get 22.2 MB of archives.
After this operation, 55.2 MB of additional disk space will be used.

Testing

Here's a simply module that you can use to check that you can successfully compile and load a module:

/* dummy.c */
#include <stdio.h>
#include "lua.h"
 
int luaopen_dummy (lua_State *L) {
    puts("Hello from dummy");
    return 0;
}

Here's how to compile it. You must first provide the Lua source files:

gcc -fpic -I/usr/src/lua-5.1.4/src -shared -Wl,-E,-soname,libdummy.so -o libdummy.so dummy.c

And here's how to load it in Lua:

lua -l libdummy

LuaSocket

After compiling LuaSocket, here's how to install and load it:

  1. mkdir -p /usr/local/lib/lua/5.1.4/mime
    mkdir -p /usr/local/lib/lua/5.1.4/socket
    wget http://srv/socket.2.0.2.so
    wget http://srv/mime.1.0.2.so
    mv socket.2.0.2.so /usr/local/lib/lua/5.1.4/socket/core.so
    mv mime.1.0.2.so /usr/local/lib/lua/5.1.4/mime/core.so
  2. mkdir -p /usr/local/share/lua/5.1.4/socket
    wget http://srv/luasocket.scripts.tar
    tar xvf luasocket.scripts.tar
    mv ltn12.lua mime.lua socket.lua /usr/local/share/lua/5.1.4/
    mv http.lua tp.lua ftp.lua smtp.lua url.lua /usr/local/share/lua/5.1.4/socket
  3. vi /etc/profile
    export LUA_PATH='/usr/local/share/lua/5.1.4/?.lua;?.lua'
    export LUA_CPATH='/usr/local/lib/lua/5.1.4/?.so;?.so'
  4. cd /var/tmp/
    ./lua
    > socket = require("socket")
    > print(socket._VERSION)

Here's how to send a datagram to NetCID to display CallerID information on the user's monitor:

http://www.voip-info.org/wiki/view/Asterisk+NetCID

LuaSQL

This is a front-end to different database servers. If using SQLite3, you must first compile SQLite3 from www.sqlite.org, then compile LuaSQL for SQLite3. The output file sqlite3.so combines SQLite3 and LuaSQL in a single binary file.

Here's the config file and Makefile to compile LuaSQL and include libsqlite3.o.

/usr/src/luasql-2.1.1# cat config
 
T=sqlite3
LIBNAME= $T.so
 
PREFIX = /usr/local
LUA_INC= /usr/src/lua-5.1.4
LUA_VERSION_NUM= 501
 
INCS= -I$(LUA_INC)
 
LIB_OPTION = -shared -Wl,-soname,$(LIBNAME)
 
#DRIVER_LIBS=-L/usr/src/sqlite-3.7.3/preprocessed -lsqlite3
DRIVER_INCS = -I/usr/src/sqlite-3.7.3/preprocessed
 
CFLAGS= -O2 -Wall -ansi
 
/usr/src/luasql-2.1.1# cat Makefile
V= 2.1.1
CONFIG= ./config
 
include $(CONFIG)
 
OBJS=src/luasql.o src/ls_$T.o
 
src/$(LIBNAME): $(OBJS)
        $(CC) $(CFLAGS) $(LIB_OPTION) -o $@ $(OBJS) /usr/src/sqlite-3.7.3/preprocessed/sqlite3.o
 
$(OBJS):
        $(CC) $(CFLAGS) $(INCS) $(DRIVER_INCS) -c $*.c -o $@

Put sqlite3.so in its ./luasql/ directory where Lua can find it. Next, here's how to use it:

require "luasql.sqlite3"
 
env = luasql.sqlite3()
conn = env:connect("testsqlite.db3")
 
assert(conn:execute("create table if not exists tbl1(one, two)"))
assert(conn:execute("insert into tbl1 values('hello!',10)"))
assert(conn:execute("insert into tbl1 values('goodbye',20)"))
 
conn:close()
env:close()

SQLite

Here's how to install SQLite and the Lua package required to use it from Lua:

  1. apt-get update
  2. Check that the SQLite library is installed: dpkg -l | grep sqlite ; If not installed: apt-get install libsqlite3-0
  3. Install the shared library : apt-get install libsqlite3-dev
  4. Install the command-line interface: apt-get install sqlite3
  5. apt-get install luarocks (Rocks are packages containing Lua modules, and Luarocks is a package manager; Luarocks will install other packages such as the liblua5.1-0 interpreter)
  6. Test that Lua is installed by simply typing "lua" at the command prompt, and hitting CTRL+C to exit
  7. luarocks install lsqlite3 SQLITE_INCDIR=/tmp/sqlite3/sqlite-amalgamation-3080200
  8. Test with the following script:

    #!/usr/bin/env lua
    sqlite3 = require('lsqlite3')

    local db = sqlite3.open('requests.db')
    db:close()
     
  9. Chmod +x test.lua ; ./test.lua

Writing a Windows GUI app with Lua

 

http://lua-users.org/wiki/GraphicalUserInterfaceToolkits

Networking

Downloading web pages

http = require("socket.http")

mypage = http.request("http://www.google.com")

HTTP client

There are different ways to have a Lua script connect to an HTTP server:

LuaCURL (easy interface; luacurl 1.2.1 is single file with no makefile: luacurl.c, and requires downloading Libcurl; written by Alexander Marinov and Enrico Tassi)

lua-curl (richer; written by Jürgen Hötzel)

Lua-httpd (very basic)

webGet (Windows only)

HTTP Front-end

Here's how to run a server that will open a TCP socket, wait for HTTP queries, use a regex to extract bits from the query, forward the SQL query to the SQLite back-end, and return results to the client:

Here's how to install this as a Windows Service:

How to prevent the server from duplicating the socket, ie. only one client must be able to connect and work with the socket at any time?

In , luasocket-2.0.2-lua-5.1.2-Win32-vc8.zip, no trace of \lib\:

 

http://w3.impa.br/~diego/software/luasocket/

"it's actually quite difficult to write network code without multithreading. given that networking is largely unreliable speed-wise (to the tune of several milliseconds in bad cases) you really don't want to tie up the cpu waiting for a packet to arrive. normally the networking layer runs continuously in the background, doing all the painful waiting around for packets for you, and then you query it for useful information. however, this requires multithreading." http://www.devmaster.net/forums/showthread.php?t=5890

Web scripting with Lua

There are multiple ways to run Lua scripts:

Through CGI

For security reason, Nginx doesn't recommend running scripts through CGI but it can be done.

Alternative : thttpd or mini-httpd + Haserl, or uwsgi with CGI support.

"While nginx is a fine piece of machinery, do you really need such a tight integration and its added complexity? CGI is looked down at, but its simplicity and portability cannot be beaten. A combination of, say, Acme Labs' thttpd and Lua scripts could be a winner, at one tenth of nginx footprint."

http://lua-users.org/lists/lua-l/2012-09/msg00438.html

"And if you really think [parsing] is going to make any practical difference, package the entire enchilada into one tight little executable with srclua [1] or such."

http://lua-users.org/lists/lua-l/2012-09/msg00453.html

mini-httpd + Haserl

Why use Haserl instead of straight CGI?

Let's go:

  1. apt-get update
  2. apt-get install mini-http
  3. apt-get install haserl
  4. mkdir -p /tmp/mini-httpd/www
  5. mkdir /tmp/mini-httpd/cgi-bin
  6. vi /etc/mini-httpd.conf

    host=0.0.0.0
    port=9999
    user=nobody
    cgipat=**.lua|**.cgi
    charset=iso-8859-1
    data_dir=/tmp/mini-httpd
    logfile=/tmp/mini-httpd/mini-httpd.log
    pidfile=/var/run/mini-httpd.pid

    #dir
    #chroot/nochroot
     
  7. vi /etc/default/mini-httpd.conf: "START=1" if launching the server through its init.d script
  8. vi hello.lua

    #!/usr/bin/haserl --shell=lua
    content-type: text/plain

    Both work:
    <!-- % io.write ("Hello World" ) %-->
    <%= "Hello World" %>
     
  9. chown nobody.nogroup /tmp/mini-httpd/cgi-bin/hello.lua
  10. chmod +x /tmp/mini-httpd/cgi-bin/hello.lua
  11. /etc/init.d/mini-httpd start
  12. Aim your browser at http://srv:9999/cgi-bin/hello.lua

/usr/share/doc/mini-httpd

mini_httpd  [-C configfile] [-p port] [-d dir] [-dd data_dir] [-c cgipat] [-u user] [-h hostname] [-r] [-v] [-l logfile] [-i pidfile] [-T charset] [-P P3P] [-M maxage] [-S] [-E certfile] [-Y cipher] [-D] [-V]

"/usr/sbin/mini-httpd: started as root without requesting chroot(), warning only"

Can CGI scripts be restricted to ./cgi-bin?
How to disable directory listing?

Remove the read permission bit for the user under which mini_httpd is running, eg:

chmod o-r directory

(In this example, assuming that mini_httpd isn't running as a user or group member of that directory.)

How to run chroot?

Make sure any shells, utilities, and config files used by your CGI programs and scripts are available.  [...] However, one thing you should do is tell syslogd about the chroot tree, so that mini_httpd can still  generate  syslog  messages.   Check  your  system's  syslodg  man  page  for  how  to  do  this.  In FreeBSD you would put something like this in /etc/rc.conf:

           syslogd_flags="-l /usr/local/www/data/dev/log"

Substitute in your own chroot tree's pathname, of course.  Don't worry about creating the log socket, syslogd  wants  to  do that  itself.   (You  may  need to create the dev directory.)  In Linux the flag is -a instead of -l, and there may be other differences.

How to password-protect directory?

Basic Authentication uses a password file called ".htpasswd", in the directory to be protected.  This file is  formatted  as the familiar colon-separated username/encrypted-password pair, records delimited by newlines.  The protection does not carry over to subdirectories. The utility program htpasswd(1) is included to help create and modify .htpasswd files.

/usr/bin/htpasswd

How to handle errors?

mini_httpd  lets you define your own custom error pages for the various HTTP errors.  There's a separate file for each error number, all stored in one special directory.  The directory name is "errors", at the top of the  web  directory  tree.   The error  files  should be named "errNNN.html", where NNN is the error number.  So for example, to make a custom error page for the authentication failure error, which is number 401, you would put your HTML into the file  "errors/err401.html".   If  no custom error file is found for a given error number, then the usual built-in error page is generated.

mini-httpd + WSAPI

Based on Using lua wsapi with mini-httpd

  1. apt-get install liblua5.1-wsapi1
  2. apt-get install mini-httpd
  3. vi /etc/mini-httpd.conf
  4. vi /etc/default/mini-httpd.conf
  5. mkdir /tmp/mini-httpd
  6. touch /tmp/mini-httpd/mini-httpd.log
  7. vi index.cgi
  8. chown nobody.nogroup ./index.cgi
  9. chmod +x index.cgi
  10. /etc/init.d/mini-httpd start
  11. Aim your browser at http://srv:9999/index.cgi

Through FastCGI

WSAPI and luafcgid? Note: WSAPI is to Lua what WSGI is to Python: "WSAPI is an API that abstracts the web server from Lua web applications. By coding against WSAPI your application can run on any of the supported servers and interfaces" (source)

https://github.com/mbalmer/luafcgi

Through uWSGI

More information here.

Through the Nginx ngx_lua module (a.k.a. HttpLuaModule)

Important: "unlike most other SQL databases, SQLite is serverless, which means that using SQLite directly in Nginx will block the Nginx worker processes and ruin the performance. " (source)

So it looks like if your script relies on SQLite, ngx_lua isn't a good idea and you should check the FastCGI/uWSGI options instead.

OpenResty

OpenResty is a tool that will build a Lua-capable Nginx server with additional modules. Note that, as of Jan 2013, SQLite seems unavailable as a Nginx module, so the Lua scripts require installing SQLite and the Lua module independently.
  1. apt-get install libreadline-dev libncurses5-dev libpcre3-dev libssl-dev perl make
  2. wget -c http://openresty.org/download/ngx_openresty-1.4.3.6.tar.gz
  3. tar xzvf ngx_openresty-1.4.3.6.tar.gz
  4. cd ngx_openresty-1.4.3.6/
  5. To see which options are available: ./configure --help
  6. ./configure --with-luajit --prefix=/tmp/ngx_openresty-1.4.3.6/install
  7. make
  8. make install (default: /usr/local/openresty/)

If you want to find where the binaries will be installed, use "make -n install"; Use "make install DESTDIR=TMP && tree TMP" to install it to a given directory.

Note: OpenResty doesn't include support for SQLite, because "using SQLite directly in Nginx will block the Nginx worker processes and ruin the performance." (source)

Manually

If you already have Nginx installed, run "nginx -V" to check if it was compiled with the the ngx_lua module.

"You can install it from source by compiling the lua-nginx-module with your existing Nginx. If you chose that path you will also need a Lua interpreter. LuaJIT-2.0.0 is recommended."

http://blog.cloudflare.com/pushing-nginx-to-its-limit-with-lua

"The ngx_lua module is for running Lua code directly in the nginx webserver. It is possible to run entire Lua applications in this way but this is not the specific target of that module. Actually, some of the module directives specifically should not be used with long running or complex routines. You will need to recompile Nginx with this module as you cannot just download an Nginx module and use it like that."

"BTW openresty is just regular Nginx with some 3rd party modules bundled in including ngx_lua and the people behind openresty are the same behind ngx_lua"

http://stackoverflow.com/questions/9434448/running-lua-under-nginx-writing-a-website-with-lua

As an alternative to the run-of-the-mill Lua compiler, you can try LuaJIT.

Here's how to compile Nginx to include the ngx_lua module:

  1. Download and install the following parts:
    1. Either Lua or LuaJIT and its include/lib
    2. ngx_devel_kit: wget -c https://github.com/simpl/ngx_devel_kit/archive/v0.2.19.tar.gz
    3. ngx_lua: https://github.com/chaoslawful/lua-nginx-module/tags
    4. The Nginx source: wget -c http://nginx.org/download/nginx-1.5.8.tar.gz
  2. Compile and install thusly:

    export LUA_LIB=/usr/lib/arm-linux-gnueabi
    export LUA_INC=/usr/include/lua5.1

    ./configure --prefix=/opt/nginx --add-module=../ngx_devel_kit-0.2.19 --add-module=../lua-nginx-module-0.9.4
    make -j2
    make install
  3.  

Mongoose

Mongoose is a light cross-platform web server that can be compiled to support Lua and SQLite:

  1. apt-get install git
  2. cd /tmp
  3. git clone https://github.com/cesanta/mongoose.git
  4. cd mongoose/build
  5. make mongoose-lua-sqlite
  6. Run the Mongoose binary: ./mongoose-lua-sqlite
  7. Save this sample as test.mg.lua in the same directory:

    mg.write('HTTP/1.0 200 OK\r\n', 'Content-Type: text/plain\r\n', '\r\n')
    mg.write(os.date("%A"))
     
  8. Hit the server: http://srv/test.mg.lua
  9. Next, add this to check that SQLite works:

    local db = sqlite3.open('requests.db')
    db:close()

April 2014: The build process changed

  1. Download and untar the Mongoose 5.4 source code
  2. Within the Mongoose directory, download and untar the Lua source code (eg. /tmp/Mongoose-5.4/lua-5.2.3/src)
  3. Edit examples/Makefile to enable Lua
  4. cd examples
  5. make server

Note:

Mongoose.conf example

enable_directory_listing no
hide_files_patterns .htaccess
#DEPRECATED? access_log_file mongoose.access.log
#DEPRECATED error_log_file mongoose.error.log
run_as_user nobody
cgi_pattern /var/local/mongoose/cgi-bin/*.cgi
#Per-directory .htpasswd is alternative to global_auth_file
#listening_port 80
 
#If using .htpasswd
auth_domain acme.com
 
document_root /var/local/mongoose/www.

Mongoose init.d script

#! /bin/sh
### BEGIN INIT INFO
# Provides:          mongoose
# Required-Start:    $syslog $time $remote_fs
# Required-Stop:     $syslog $time $remote_fs
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Mongoose web server
# Description:       Debian init script for the Mongoose web server
### END INIT INFO
 
PATH=/bin:/usr/bin:/sbin:/usr/sbin
DAEMON=/usr/sbin/mongoose-lua-sqlite
PIDFILE=/var/run/mongoose.pid
 
test -x $DAEMON || exit 0
 
. /lib/lsb/init-functions
 
case "$1" in
  start)
        log_daemon_msg "Starting Mongoose web server" "mongoose"
        start_daemon -p $PIDFILE $DAEMON &
        log_end_msg $?
    ;;
  stop)
        log_daemon_msg "Stopping Mongoose web server" "Mongoose"
        killproc -p $PIDFILE $DAEMON
        log_end_msg $?
    ;;
  force-reload|restart)
    $0 stop
    $0 start
    ;;
  status)
    status_of_proc -p $PIDFILE $DAEMON atd && exit 0 || exit $?
    ;;
  *)
    echo "Usage: /etc/init.d/mongoose {start|stop|restart|force-reload|status}"
    exit 1
    ;;
esac
 
exit 0

Running Lua scripts

https://github.com/cesanta/mongoose/tree/master/examples/lua

Here's hello.lp:

HTTP/1.0 200 OK
Content-Type: text/plain
 
<?
mg.write("Hello, world!", "\n")
?>

Here's how to play with SQLite:

HTTP/1.0 200 OK
Content-Type: text/plain
 
<?
local db = sqlite3.open('test.db')
 
-- Can be slow if writing to an SD card formated in ext4
db:exec[[
  BEGIN TRANSACTION;
  CREATE TABLE IF NOT EXISTS test (id INTEGER PRIMARY KEY, content);
  INSERT INTO test VALUES (NULL, 'Hello World');
  INSERT INTO test VALUES (NULL, 'Hello Lua');
  INSERT INTO test VALUES (NULL, 'Hello Sqlite3')
  COMMIT;
]]
 
for row in db:nrows("SELECT * FROM test") do
  print(row.id, row.content)
end
db:close()
?>

Note: If SQLite is noticeably slower than other systems, check if the filesystem is ext4.

Examples are available in the lsqlite LuaRocks package (eg. /usr/local/lib/luarocks/rocks/lsqlite3/0.9.1-2/examples/).

Questions

http://cesanta.com/#docs,Mongoose.md

DONE mongoose.conf : Format can be either "A B" or ""A=B"

NO Are Lua scripts run through CGI when compiled with "make mongoose-lua"?
NO Is it safe to locate and run cgi-bin scripts from within the docroot?
What should file access rights be for CGI scripts? Is 500 OK?

YES Is it possible to mix Lua with HTML?

docs/LuaSqlite.md doesn't mention Lua scripts with .mg.lua extension: Are HTML with embedded Lua code (.lp) still available in addition to Lua scripts with .mg.lua extension? Which way do you recommend?

Is using LuaRocks OK to install Lua modules and access them from Lua scripts in Mongoose?

What is the difference between lua and luac?

Can Mongoose be launched at boot time as a service? "Mongoose does not detach from terminal." says the docs

Any tips about how to handle HTML forms safely, and generally, about running Mongoose safely, including Lua scripting?

Infos about running Mongoose safely, including Lua scripting

.htaccess: What is a realm? This doesn't work: ./mongoose-lua-sqlite -A .htpasswd 192.168.0.10 joe test -> joe:192.168.0.10:c8b1228323d9839b089a4ebd57c131cf

How to handle 404 instead of just displaying "404 Not Found"?

How to log access/error? Even with "error_log_file mongoose.error.log" in mongoose.conf, no file is created when a 404 occurs

Xavante

Here's how to install the light-weight Xavante server which is written in Lua so you have everything you need to write a low-scale web server to run Lua scripts:

  1. Download and install Lua for Windows
  2. Download the LuaRocks for Lua for Windows ZIP file
  3. Unzip the LuaRocks package directly at the root of the Lua for Windows installation directory (C:\Program Files\Lua\5.1\).
  4. Next, install Xavante using LuaRocks: luarocks install xavante
  5. The "xavante" package installs Xavante as a library. To run Xavanteas an application, install WSAPI: luarocks install wsapi-xavante
  6. cd "C:\Program Files\Lua\5.1\rocks\wsapi\X.X.X\samples\", and run

    wsapi (If 8080 isn't available, run eg. wsapi -p9999)
     
  7. Next, aim your browser at http://localhost:8080/hello.lua

Troubleshooting

If you get an error message like "Unable to resolve symbol" when loading a module, try compiling and loading this simple module:

/* dummy.c -- a minimal Lua library for testing dynamic loading */
#include <stdio.h>
#include "lua.h"
int luaopen_dummy (lua_State *L) {
    puts("Hello from dummy");
    return 0;
}

Compile this as a shared library: cc -shared -fPIC -o dummy.so dummy.c

Load in in Lua thusly: lua -ldummy OR lua > print(require"dummy")

Note: The line require "dummy" looks for the function name luaopen_dummy within the shared library.

If that works, try this next:

#include <stdio.h>
#include "lua.h"
int luaopen_dummy (lua_State *L) {
 puts("Hello from dummy");
 lua_pushnil(L);
 puts("Bye from dummy");
 return 0;
}

Reading Notes: "Beginning Lua Programming" - Wrox 2007

The Lua interpreter lua51.exe just calls the Lua DLL where most of the logic live. lua51.exe can be put anywhere as long as it can find the DLL through the %PATH% env't variable.

wlua5.1.exe acts like lua51.exe, but with no console output (so a GUI is required to interact with user)

lua5.1.dll and lua51.dll: There was a disagreement about what the DLL should be called, so lua51.dll is a 'proxy' DLL referencing lua5.1.dll. Some extensions link against lua5.1.dll, some against lua51.dll.

luac5.1.exe turns Lua source files into bytecode. Since the interpreter is very fast anyway, the compiler is apparently not often used.

bin2c5.1.exe: Turns Lua scripts into C source.

All binary extensions must be linked against the same C runtime (The Lua for Windows depends on the Microsoft C runtime library MSVCRT.DLL).

Kepler = Lua-based web server

Two ways to build Lua so you can run Unix tools:

Unlike other languages, a function in Lua can return more than one value:

function ReturnArgs(Arg1, Arg2, Arg3)
    return Arg1, Arg2, Arg3
end
 
A, B, C = ReturnArgs(alpha", "bravo", "charlie")
print(A, B, C)
> alpha bravo charlie

The Lua for Windows executable looks up the DLL extensions in the \clibs subdirectory (in general, Lua uses the LUA_CPATH environment variable)

LuaRocks: For some common rocks, Windows binaries already exist. If a binary rock exists, Lua Rocks will download that rather than the source rock.

Q&A

I need "sleep"

Lua is based on ANSI C, which doesn't provide a function to pause a script. Here's a C program that you can compile as a shared library:

www.troubleshooters.com/codecorn/lua/lua_lua_calls_c.htm#_Make_an_msleep_Function

Can Xavante be installed as a Windows Service?

http://luaforge.net/projects/luaservice/

What files/directories do I need to pack a self-contained Xavante server?

WSAPI vs. WSAPI-Xavante vs. Xavante?

Xavante is a module that handles HTTP requests. WSAPI is a protocol to generate HTTP responses and isn't tied to a specific, underlying web server.

wsapi.exe runs WSAPI-Xavante which binds the handler (Xavante) and the protocol (WSAPI).

What do the files do?

Downloaded the basic Windows package from luabinaries.luaforge.net lua5_1_4_Win32:

Here's the Lua for Windows package (C:\Program Files\Lua\5.1):

What's the difference between the binaries on lua.org vs. others?

What toolkit to use to write Windows GUI apps?

What's the difference between lua.exe and wlua.exe ?

Do I need to distribut lua.DLL with (w)lua.exe?

Do I need to provide an installer, or can I just copy (w)lua.exe and lua.dll?

Resources