The http module is experimental.
local h = req.headers
h['content-type'] = 'text/html; charset=UTF-8'
h.via = {'1.1 varnish', '1.1 varnish'}

function print_headers(headers)
    for k, v in pairs(headers) do
        if type(v) == 'table' then
            for _, vi in ipairs(v) do
                print(' ' .. k .. ': ' .. vi)
            print(' ' .. k .. ': ' .. v)

This is a type that should mostly work like a lua table. It models HTTP headers. HTTP headers is a data structure that associates string keys to string values (with restrictions).

The keys are case-insensitive. As a rule to make this work easily, any byte-stream received from the wire will be converted to lower-case, and we expect you’ll only fill values with all lower-case strings. This decision mirrors what NodeJS have done with their data structure.

This data structure is also a multimap and it is allowed to associate several values with a single key. The relative order of values associated with the same key is preserved, but different keys can be traversed in any order (so representing them with a hash table would be okay). This concept is mapped to lua by allowing values to be tables (representing lists) or simple strings.

HTTP headers appear before and after the HTTP body (a chunk of bytes) and they can be empty. The set that arrives after the body is referred to as HTTP trailers, but it is essentially the same data structure. From now on, if the term trailers is not used, you can safely assume we’re referring to the header section that is sent before the HTTP body.

However, the control flow to route HTTP messages to the appropriate handler is quite distinct. Only the HTTP headers (not the trailers) should be taken into account. The routing algorithm doesn’t have the luxury to wait until the trailers arrive to decide which handler will respond to the request as the HTTP body might have an “infinite size” (e.g. live video streams). At the same time, trailers should not be allowed to override/bypass permissions check done at the routing layer. So handlers should always by default only check headers within the header section (i.e. ignore the trailers) and only accept some limited fields within the trailers.

This solution aiming secure systems requires a contract to be followed by two parties. At our side, we only expose the trailers as a separate field in the lua HTTP message object as to not pollute the primary header section. And you’re expected to use each field carefully. This solution is also followed by NodeJS.

Element keys must also be strings encoded with the ISO-8859-1 charset whose contents are limited to the chars listed below (case-sensitive):

  • A digit (i.e. '0', '1', '2', '3', '4', '5', '6', '7', '8' or '9').

  • A lowercase alphabetic (i.e. 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y' or 'z').

  • A few special characters: '!', '#', '$', '%', '&', '\'', '*', '+', '-', '.', '^', '_', backtick (i.e. '\x60'), '|' or '~'.

And element string values must be encoded with the ISO-8859-1 charset whose contents are limited to the chars listed below (whether they’re case-sensitive or not is defined on a header-by-header basis and, as such, they all are considered case-sensitive in this layer of abstraction):

  • Any visible USASCII character.

  • Any character in the closed interval (i.e. both ends are inclusive) between '\x80' and '\xFF'. The use of these characters within the HTTP field value is obsolete and should be avoided.

  • Space (i.e. '\x20') and horizontal tab (i.e. '\t'). These characters are not allowed in the beginning or in the end of the HTTP field value.


  • __index()

  • __newindex()

  • __pairs()