### README Lwan Web Server =============== Lwan is a **high-performance** & **scalable** web server. The [project web site](https://lwan.ws/) contains more details. Build status ------------ | OS | Arch | Build | Static Analysis | Tests | |-------------|--------|---------|-----------------|-------| | Linux | x86_64 | | | [](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:lwan) | | FreeBSD 15 | x86_64 | | | | OpenBSD 7.9 | x86_64 | | | | Installing ---------- You can either [build Lwan yourself](#Building), use a [container image](#container-images), or grab a package from [your favorite distribution](#lwan-in-the-wild). Building -------- Before installing Lwan, ensure all dependencies are installed. All of them are common dependencies found in any GNU/Linux distribution; package names will be different, but it shouldn't be difficult to search using whatever package management tool that's used by your distribution. ### Required dependencies - [CMake](https://cmake.org/), at least version 2.8 - [libdeflate](https://github.com/ebiggers/libdeflate), [zlib-ng](https://github.com/zlib-ng/zlib-ng) or [ZLib](http://zlib.net) ### Optional dependencies The build system will look for these libraries and enable/link if available. - [Lua 5.4](http://www.lua.org) - [Valgrind](http://valgrind.org) - [Brotli](https://github.com/google/brotli) - Can be disabled by passing `-DENABLE_BROTLI=NO` - [ZSTD](https://github.com/facebook/zstd) - Can be disabled by passing `-DENABLE_ZSTD=NO` - On Linux builds, if `-DENABLE_TLS=ON` (default) is passed: - [mbedTLS](https://github.com/ARMmbed/mbedtls) - Alternative memory allocators can be used by passing `-DUSE_ALTERNATIVE_MALLOC` to CMake with the following values: - ["mimalloc"](https://github.com/microsoft/mimalloc) - ["jemalloc"](http://jemalloc.net/) - ["tcmalloc"](https://github.com/gperftools/gperftools) - "auto": Autodetect from the list above, falling back to libc malloc if none found - To run test suite: - [Python](https://www.python.org/) (2.6+) with Requests - [Lua 5.1](http://www.lua.org) - To run benchmark: - [Weighttp](https://github.com/lpereira/weighttp) -- bundled and built alongside Lwan for convenience - [Matplotlib](https://github.com/matplotlib/matplotlib) - To build TechEmpower benchmark suite: - Client libraries for [MariaDB](https://mariadb.org) - [SQLite 3](http://sqlite.org) - [shared-mime-info](https://www.freedesktop.org/wiki/Software/shared-mime-info/) may be used during build-time to augment the built-in extention/MIME type database. ### Common operating system package names #### Minimum to build - ArchLinux: `pacman -S cmake zlib-ng pkgconf` - FreeBSD: `pkg install cmake pkgconf` - Debian and Ubuntu 14+: `apt-get update && apt-get install git cmake zlib1g-dev pkg-config` - macOS: `brew install cmake` #### Build with all optional features - ArchLinux: `pacman -S cmake zlib-ng pkgconf sqlite lua55 mariadb-libs gperftools valgrind mbedtls` - FreeBSD: `pkg install cmake pkgconf sqlite3 lua55` - Debian and Ubuntu 14+: `apt-get update && apt-get install git cmake zlib1g-dev pkg-config lua5.5-dev libsqlite3-dev libmariadb-dev libmbedtls-dev` - macOS: `brew install cmake mariadb-connector-c sqlite lua@5.5 pkg-config` ### Build commands #### Clone the repository ~$ git clone git://github.com/lpereira/lwan ~$ cd lwan #### Create the build directory ~/lwan$ mkdir build ~/lwan$ cd build #### Select build type Selecting a *release* version (no debugging symbols, messages, enable some optimizations, etc): ~/lwan/build$ cmake .. -DCMAKE_BUILD_TYPE=Release If you'd like to enable optimizations but still use a debugger, use this instead: ~/lwan/build$ cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo To disable optimizations and build a more debugging-friendly version: ~/lwan/build$ cmake .. -DCMAKE_BUILD_TYPE=Debug #### Build Lwan ~/lwan/build$ make This will generate a few binaries: - `src/bin/lwan/lwan`: The main Lwan executable. May be executed with `--help` for guidance. - `src/bin/testrunner/testrunner`: Contains code to execute the test suite (`src/scripts/testsuite.py`). - `src/samples/freegeoip/freegeoip`: [FreeGeoIP sample implementation](https://freegeoip.lwan.ws). Requires SQLite. - `src/samples/techempower/techempower`: Code for the TechEmpower Web Framework benchmark. Requires SQLite and MariaDB libraries. - `src/samples/clock/clock`: [Clock sample](https://time.lwan.ws). Generates a never-ending animated GIF file that always shows the local time. - `src/samples/forthsalon/forthsalon`: Generates a never-ending animated GIF from a program written in the [Forth Salon](https://forthsalon.appspot.com/) dialect of the [Forth](https://en.wikipedia.org/wiki/Forth_(programming_language)) programming language. *In construction!* - `src/samples/forthsalon/forth`: Test harness for the Forth dialect used in the `forthsalon` sample. - `src/bin/tools/mimegen`: Builds the extension-MIME type table. Used during the build process. - `src/bin/tools/bin2hex`: Generates a C file from a binary file, suitable for use with #include. Used during the build process. - `src/bin/tools/configdump`: Dumps a configuration file using the configuration reader API. Used for testing. - `src/bin/tools/weighttp`: Rewrite of the `weighttp` HTTP benchmarking tool. - `src/bin/tools/statuslookupgen`: Generates a perfect hash table for HTTP status codes and their descriptions. Used during the build process. - `src/lib/lwan-template-test`: Benchmark for the template engine #### Remarks Passing `-DCMAKE_BUILD_TYPE=Release` will enable some compiler optimizations (such as [LTO](http://gcc.gnu.org/wiki/LinkTimeOptimization)) and tune the code for current architecture. > [!IMPORTANT] > > *Please use the release build when benchmarking*. > The default is the Debug build, which not only logs all requests to the > standard output, but does so while holding a lock, severely holding down > the server. The default build (i.e. not passing `-DCMAKE_BUILD_TYPE=Release`) will build a version suitable for debugging purposes. This version can be used under Valgrind *(if its headers are present)* and includes debugging messages that are stripped in the release version. Debugging messages are printed for each and every request. On these builds, sanitizers can be enabled. To select which one to build Lwan with, specify one of the following options to the CMake invocation line: - `-DSANITIZER=ubsan` selects the Undefined Behavior Sanitizer. - `-DSANITIZER=address` selects the Address Sanitizer. - `-DSANITIZER=thread` selects the Thread Sanitizer. Alternative memory allocators can be selected as well. Lwan currently supports [TCMalloc](https://github.com/google/tcmalloc), [mimalloc](https://github.com/microsoft/mimalloc), and [jemalloc](http://jemalloc.net/) out of the box. To use either one of them, pass `-DALTERNATIVE_MALLOC=name` to the CMake invocation line, using the names provided in the "Optional dependencies" section. The `-DUSE_SYSLOG=ON` option can be passed to CMake to also log to the system log in addition to the standard output. If you're building Lwan for a distribution, it might be wise to use the `-DMTUNE_NATIVE=OFF` option, otherwise the generated binary may fail to run on some computers. TLS support is enabled automatically in the presence of a suitable mbedTLS installation on Linux systems with headers new enough to support kTLS, but can be disabled by passing `-DENABLE_TLS=NO` to CMake. ### Tests ~/lwan/build$ make testsuite This will compile the `testrunner` program and execute regression test suite in `src/scripts/testsuite.py`. ### Benchmark ~/lwan/build$ make benchmark This will compile `testrunner` and execute benchmark script `src/scripts/benchmark.py`. ### Coverage Lwan can also be built with the Coverage build type by specifying `-DCMAKE_BUILD_TYPE=Coverage`. This enables the `generate-coverage` make target, which will run `testrunner` to prepare a test coverage report with [lcov](http://ltp.sourceforge.net/coverage/lcov.php). Every commit in this repository triggers the generation of this report, and results are [publicly available](https://buildbot.lwan.ws/lcov/). ### CMake options These options are available to configure a build. Even if an option is set, it'll be checked if it can be used or not (e.g. third-party libraries need to be installed). | Option | Default | Description | |--------|---------|-------------| |`ENABLE_BROTLI`|`ON`|Enables Brotli compression support| |`ENABLE_IA32_CRC32`|`ON`|Enable Intel CRC32c instructions for hashing| |`ENABLE_TLS`|`OFF`|Enables kTLS (with mbedTLS) support| |`ENABLE_ZSTD`|`ON`|Enables Zstd compression support| |`MTUNE_NATIVE`|`ON`|Tune the binary for the current architecture| |`SANITIZER`|`none`|Select which sanitizer to use (see above)| |`USE_ALTERNATIVE_MALLOC`|`OFF`|Select a different `malloc()` implementation; default is to use the one from libc| |`USE_SYSLOG`|`OFF`|Log to syslog in addition to standard output| Running ------- Set up the server by editing the provided `lwan.conf`; the format is explained in details below. > [!NOTE] > > Lwan will try to find a configuration file based in the > executable name in the current directory; `testrunner.conf` will be used > for the `testrunner` binary, `lwan.conf` for the `lwan` binary, and so on. Configuration files are loaded from the current directory. If no changes are made to this file, running Lwan will serve static files located in the `./wwwroot` directory. Lwan will listen on port 8080 on all interfaces. Lwan will detect the number of CPUs, will increase the maximum number of open file descriptors and generally try its best to autodetect reasonable settings for the environment it's running on. Many of these settings can be tweaked in the configuration file, but it's usually a good idea to not mess with them. > [!TIP] > > Optionally, the `lwan` binary can be used for one-shot > static file serving without any configuration file. Run it with `--help` > for help on that. Configuration File ------------------ ### Format Lwan uses a familiar `key = value` configuration file syntax. Comments are supported with the `#` character (similar to e.g. shell scripts, Python, and Perl). Nested sections can be created with curly brackets. Sections can be empty; in this case, curly brackets are optional. `some_key_name` is equivalent to `some key name` in configuration files (as an implementation detail, code reading configuration options will only be given the version with underscores). > [!TIP] > > Values can contain environment variables. Use the > syntax `${VARIABLE_NAME}`. Default values can be specified with a colon > (e.g. `${VARIABLE_NAME:foo}`, which evaluates to `${VARIABLE_NAME}` if > it's set, or `foo` otherwise). ``` sound volume = 11 # This one is 1 louder playlist metal { files = ''' /multi/line/strings/are/supported.mp3 /anything/inside/these/are/stored/verbatim.mp3 ''' } playlist chiptune { files = """ /if/it/starts/with/single/quotes/it/ends/with/single/quotes.mod /but/it/can/use/double/quotes.s3m """ } ``` Some examples can be found in `lwan.conf` and `techempower.conf`. #### Constants Constants can be defined and reused throughout the configuration file by specifying them in a `constants` section anywhere in the configuration file. A constant will be available only after that section defines a particular constant. Constants can be re-defined. If a constant isn't defined, its value will be obtained from an environment variable. If it's not defined in either one `constants` section, or in the environment, Lwan will abort with an appropriate error message. ``` constants { user_name = ${USER} home_directory = ${HOME} buffer_size = 1000000 } ``` The same syntax for default values specified above is valid here (e.g. specifying `user_name` to be `${USER:nobody}` will set `${user_name}` to `nobody` if `${USER}` isn't set in the environment variable or isn't another constant.) #### Value types | Type | Description | |--------|-------------| | `str` | Any kind of free-form text, usually application specific | | `int` | Integer number. Range is application specific | | `time` | Time interval. See table below for units | | `bool` | Boolean value. See table below for valid values | #### Time Intervals Time fields can be specified using multipliers. Multiple can be specified, they're just added together; for instance, "1M 1w" specifies "1 month and 1 week" (37 days). The following table lists all known multipliers: | Multiplier | Description | |------------|-------------| | `s` | Seconds | | `m` | Minutes | | `h` | Hours | | `d` | Days | | `w` | 7-day Weeks | | `M` | 30-day Months | | `y` | 365-day Years | > [!NOTE] > > No spaces must exist between the number and its multiplier. Multipliers outside > the ones in the table above will cause parsing errors. #### Boolean Values | True Values | False Values | |-------------|--------------| | Any integer number different than 0 | 0 | | `on` | `off` | | `true` | `false` | | `yes` | `no` | ### Global Settings It's generally a good idea to let Lwan decide the best settings for your environment. However, not every environment is the same, and not all uses can be decided automatically, so some configuration options are provided. | Option | Type | Default | Description | |--------|------|---------|-------------| | `keep_alive_timeout` | `time` | `15` | Timeout to keep a connection alive | | `quiet` | `bool` | `false` | Set to true to not print any debugging messages. Only effective in release builds. | | `expires` | `time` | `1M 1w` | Value of the "Expires" header. Default is 1 month and 1 week | | `threads` | `int` | `0` | Number of I/O threads. Default (0) is the number of online CPUs | | `proxy_protocol` | `bool` | `false` | Enables the [PROXY protocol](https://www.haproxy.com/blog/haproxy/proxy-protocol/). Versions 1 and 2 are supported. Only enable this setting if using Lwan behind a proxy, and the proxy supports this protocol; otherwise, this allows anybody to spoof origin IP addresses | | `max_post_data_size` | `int` | `40960` | Sets the maximum number of data size for POST requests, in bytes | | `max_put_data_size` | `int` | `40960` | Sets the maximum number of data size for PUT requests, in bytes | | `max_file_descriptors` | `int` | `524288` | Maximum number of file descriptors. Needs to be at least 10x `threads` | | `request_buffer_size` | `int` | `4096` | Request buffer size length. If larger than the default of `4096`, it'll be dynamically allocated. | | `allow_temp_files` | `str` | `""` | Use temporary files; set to `post` for POST requests, `put` for PUT requests, or `all` (equivalent to setting to `post put`) for both.| | `error_template` | `str` | Default error template | Template for error codes. See variables below. | #### Variables for `error_template` | Variable | Type | Description | |----------|------|-------------| | `short_message` | `str` | Short error message (e.g. `Not found`) | | `long_message` | `str` | Long error message (e.g. `The requested resource could not be found on this server`) | ### Straitjacket Lwan can drop its privileges to a user in the system, and limit its filesystem view with a chroot. While not bulletproof, this provides a first layer of security in the case there's a bug in Lwan. In order to use this feature, declare a `straitjacket` (or `straightjacket`) section, and set some options. This requires Lwan to be executed as `root`. Although this section can be written anywhere in the file (as long as it is a top level declaration), if any directories are open, due to e.g. instantiating the `serve_files` module, Lwan will refuse to start. (This check is only performed on Linux as a safeguard for malconfiguration.) > [!TIP] > > Declare a Straitjacket right before a `site` section > in such a way that configuration files and private data (e.g. TLS keys) > are out of reach of the server after initialization has taken place. | Option | Type | Default | Description | |--------|------|---------|-------------| | `user` | `str` | `NULL` | Drop privileges to this user name | | `chroot` | `str` | `NULL` | Path to `chroot()` | | `drop_capabilities` | `bool` | `true` | Drop all capabilities with capset(2) (under Linux), or pledge(2) (under OpenBSD). | ### Headers If there's a need to specify custom headers for each response, one can declare a `headers` section in the global scope. The order which this section appears isn't important. For example, this declaration: ``` headers { Server = Apache/1.0.0 or nginx/1.0.0 (at your option) Some-Custom-Header = ${WITH_THIS_ENVIRONMENT_VARIABLE} } ``` Will both override the `Server` header (`Server: lwan` won't be sent), and set `Some-Custom-Header` with the value obtained from the environment variable `$WITH_THIS_ENVIRONMENT_VARIABLE`. Some headers can't be overridden, as that would cause issues when sending their actual values while servicing requests. These include but is not limited to: - `Date` - `Expires` - `WWW-Authenticate` - `Connection` - `Content-Type` - `Transfer-Encoding` - All `Access-Control-Allow-` headers > [!NOTE] > > Header names are also case-insensitive (and case-preserving). Overriding > `SeRVeR` will override the `Server` header, but send it the way it was > written in the configuration file. ### Listeners Only two listeners are supported per Lwan process: the HTTP listener (`listener` section), and the HTTPS listener (`tls_listener` section). Only one listener of each type is allowed. > [!WARNING] > > TLS support is experimental. Although it is stable > during initial testing, your mileage may vary. Only TLSv1.2 is supported > at this point, but TLSv1.3 is planned. > [!NOTE] > > TLS support requires :penguin: Linux with the `tls.ko` > module built-in or loaded. Support for other operating systems may be > added in the future. FreeBSD seems possible, other operating systems > do not seem to offer similar feature. For unsupported operating systems, > using a TLS terminator proxy such as [Hitch](https://hitch-tls.org/) is a good > option. For both `listener` and `tls_listener` sections, the only parameter is the the interface address and port to listen on. The listener syntax is `${ADDRESS}:${PORT}`, where `${ADDRESS}` can either be `*` (binding to all interfaces), an IPv6 address (if surrounded by square brackets), an IPv4 address, or a hostname. For instance, `listener localhost:9876` would listen only in the `lo` interface, port `9876`. While a `listener` section takes no keys, a `tls_listener` section requires two: `cert` and `key` (each pointing, respectively, to the location on disk where the TLS certificate and private key files are located) and takes an optional boolean `hsts` key, which controls if `Strict-Transport-Security` headers will be sent on HTTPS responses. > [!TIP] > > To generate these keys for testing purposes, the > OpenSSL command-line tool can be used like the following: > `openssl req -nodes -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 7` > [!NOTE] > > It's recommended that a [Straitjacket](#Straitjacket) with a `chroot` option is declared > right after a `tls_listener` section, in such a way that the paths to the > certificate and key are out of reach from that point on. If systemd socket activation is used, `systemd` can be specified as a parameter. (If multiple listeners from systemd are specified, `systemd:FileDescriptorName` can be specified, where `FileDescriptorName` follows the [conventions set in the `systemd.socket` documentation](https://www.freedesktop.org/software/systemd/man/systemd.socket.html).) Examples: ``` listener *:8080 # Listen on all interfaces, port 8080, HTTP tls_listener *:8081 { # Listen on all interfaces, port 8081, HTTPS cert = /path/to/cert.pem key = /path/to/key.pem } # Use named systemd socket activation for HTTP listener listener systemd:my-service-http.socket # Use named systemd socket activation for HTTPS listener tls_listener systemd:my-service-https.socket { ... } ``` ### Site A `site` section groups instances of modules and handlers that will respond to requests to a given URL prefix. #### Routing URLs Using Modules or Handlers In order to route URLs, Lwan matches the largest common prefix from the request URI with a set of prefixes specified in the listener section. How a request to a particular prefix will be handled depends on which handler or module has been declared in the listener section. Handlers and modules are similar internally; handlers are merely functions and hold no state, and modules holds state ("instance"). Multiple instances of a module can appear in a listener section. There is no special syntax to attach a prefix to a handler or module; all the configuration parser rules apply here. Use `${NAME} ${PREFIX}` to link the `${PREFIX}` prefix path to either a handler named `${NAME}` (if `${NAME}` begins with `&`, as with C's "address of" operator), or a module named `${NAME}`. Empty sections can be used here. Each module will have its specific set of options, and they're listed in the next sections. In addition to configuration options, a special `authorization` section can be present in the declaration of a module instance. Handlers do not take any configuration options, but may include the `authorization` section. > [!TIP] > > Executing Lwan with the `--version` command-line > argument will show a list of built-in modules and handlers. The following is some basic documentation for the modules shipped with Lwan. #### File Serving The `serve_files` module will serve static files, and automatically create directory indices or serve pre-compressed files. It'll generally try its best to serve files in the fastest way possible according to some heuristics. | Option | Type | Default | Description | |--------|------|---------|-------------| | `path` | `str` | `NULL` | Path to a directory containing files to be served | | `index_path` | `str` | `index.html` | File name to serve as an index for a directory | | `serve_precompressed_path` | `bool` | `true` | If $FILE.gz exists, is smaller and newer than $FILE, and the client accepts `gzip` encoding, transfer it | | `auto_index` | `bool` | `true` | Generate a directory list automatically if no `index_path` file present. Otherwise, yields 404 | | `auto_index_readme` | `bool` | `true` | Includes the contents of README files as part of the automatically generated directory index | | `directory_list_template` | `str` | `NULL` | Path to a Mustache template for the directory list; by default, use an internal template | | `read_ahead` | `int` | `131702` | Maximum amount of bytes to read ahead when caching open files. A value of `0` disables readahead. Readahead is performed by a low priority thread to not block the I/O threads while file extents are being read from the filesystem. | | `cache_for` | `time` | `5s` | Time to keep file metadata (size, compressed contents, open file descriptor, etc.) in cache | > [!NOTE] > > Files smaller than 16KiB will be compressed in RAM for > the duration specified in the `cache_for` setting. Lwan will always try > to compress with deflate, and will optionally compress with Brotli and > zstd (if Lwan has been built with proper support). > > In cases where compression wouldn't be worth the effort (e.g. adding the > `Content-Encoding` header would result in a larger response than sending > the uncompressed file, usually the case for very small files), Lwan won't > spend time compressing a file. > > For files larger than 16KiB, Lwan will not attempt to compress them. In > future versions, it might do this and send responses using > chunked-encoding while the file is being compressed (up to a certain > limit, of course), but for now, only precompressed files (see > `serve_precompressed_path` setting in the table above) are considered. > > For all cases, Lwan might try using the gzipped version if that's found in > the filesystem and the client requested this encoding. ##### Variables for `directory_list_template` | Variable | Type | Description | |----------|------|-------------| | `rel_path` | `str` | Path relative to the root directory real path | | `readme` | `str` | Contents of first readme file found (`readme`, `readme.txt`, `read.me`, `README.TXT`, `README`) | | `file_list` | iterator | Iterates on file list | | `file_list.zebra_class` | `str` | `odd` for odd items, or `even` or even items | | `file_list.icon` | `str` | Path to the icon for the file type | | `file_list.name` | `str` | File name (escaped) | | `file_list.type` | `str` | File type (directory or regular file) | | `file_list.size` | `int` | File size | | `file_list.unit` | `str` | Unit for `file_size` | #### Lua The `lua` module will allow requests to be serviced by scripts written in the [Lua](https://www.lua.org/) programming language. Although the functionality provided by this module is quite spartan, it's able to run frameworks such as [Sailor](https://github.com/lpereira/sailor-hello-lwan). Scripts can be served from files or embedded in the configuration file, and the results of loading them and the standard Lua modules will be cached for a while (see `cache_period` below). | Option | Type | Default | Description | |--------|------|---------|-------------| | `default_type` | `str` | `text/plain` | Default MIME-Type for responses | | `script_file` | `str` | `NULL` | 1️⃣ Path to Lua script| | `script` | `str` | `NULL` | 1️⃣ Inline lua script | | `server_pages` | `str` | `NULL` | 2️⃣ Path to directory containing Lua Server Pages | | `cache_period` | `time` | `15s` | Time to keep Lua state loaded in memory | > [!NOTE] > > Only one of 1️⃣ or 2️⃣ can be specified at a time. ##### Writing request handlers (1️⃣) > [!NOTE] > > Lua scripts can't use global variables, as they may be not > only serviced by different threads, but the state will be available only > for the amount of time specified in the `cache_period` configuration > option. This is because each I/O thread in Lwan will create an instance > of a Lua VM (i.e. one `lua_State` struct for every I/O thread), and each > Lwan coroutine will spawn a Lua thread (with `lua_newthread()`) per > request. There's no need to have one instance of the Lua module for each endpoint; a single script, embedded in the configuration file or otherwise, can service many different endpoints. Scripts are supposed to implement functions with the following signature: `handle_${METHOD}_${ENDPOINT}(req)`, where `${METHOD}` can be a HTTP method (i.e. `get`, `post`, `head`, etc.), and `${ENDPOINT}` is the desired endpoint to be handled by that function. A generic `handle(req)` function will be called if the specific version doesn't exist. > [!TIP] > > Use the `root` endpoint for a catchall. For example, > the handler function `handle_get_root()` will be called if no other handler > could be found for that request. If no catchall is specified, the server > will return a `404 Not Found` error. The `req` parameter points to a metatable that contains methods to obtain information from the request, or to set the response, as seen below: - `req:query_param(param)` returns the query parameter (from the query string) with the key `param`, or `nil` if not found - `req:post_param(param)` returns the post parameter (only for `${POST}` handlers) with the key `param`, or `nil` if not found - `req:set_response(str)` sets the response to the string `str` - `req:say(str)` sends a response chunk (using chunked encoding in HTTP) - `req:send_event(event, str)` sends an event (using server-sent events) - `req:cookie(param)` returns the cookie named `param`, or `nil` is not found - `req:set_headers(tbl)` sets the response headers from the table `tbl`; a header may be specified multiple times by using a table, rather than a string, in the table value (`{'foo'={'bar', 'baz'}}`); must be called before sending any response with `say()` or `send_event()` - `req:header(name)` obtains the header from the request with the given name or `nil` if not found - `req:sleep(ms)` pauses the current handler for the specified amount of milliseconds - `req:ws_upgrade()` returns `1` if the connection could be upgraded to a WebSocket; `0` otherwise - `req:ws_write_text(str)` sends `str` through the WebSocket-upgraded connection as text frame - `req:ws_write_binary(str)` sends `str` through the WebSocket-upgraded connection as binary frame - `req:ws_write(str)` sends `str` through the WebSocket-upgraded connection as text or binary frame, depending on content containing only ASCII characters or not - `req:ws_read()` returns a string with the contents of the last WebSocket frame, or a number indicating an status (ENOTCONN/107 on Linux if it has been disconnected; EAGAIN/11 on Linux if nothing was available; ENOMSG/42 on Linux otherwise). The return value here might change in the future for something more Lua-like. - `req:remote_address()` returns a string with the remote IP address. - `req:path()` returns a string with the request path. - `req:query_string()` returns a string with the query string (empty string if no query string present). - `req:body()` returns the request body (POST/PUT requests). - `req:request_id()` returns a string containing the request ID. - `req:request_date()` returns the date as it'll be written in the `Date` response header. - `req:is_https()` returns `true` if this request is serviced through HTTPS, `false` otherwise. - `req:host()` returns the value of the `Host` header if present, otherwise `nil`. - `req:http_version()` returns `HTTP/1.0` or `HTTP/1.1` depending on the request version. - `req:http_method()` returns a string, in uppercase, with the HTTP method (e.g. `"GET"`). - `req:http_headers()` returns a table with all headers and their values. - `req:num_http_headers()` returns the number of HTTP headers. - `req:random_double()` returns a double in the interval `[0, 1)`. - `req:version()` returns the Lwan version. - `req:operating_system()` returns which operating system Lwan is running on. - `req:get_mime_type(path)` returns the MIME type for `path`. - `req:base64_encode(str)` base 64-encodes `str`. - `req:base64_decode(str)` base 64-decodes `str`. Handler functions may return either `nil` (in which case, a `200 OK` response is generated), or a number matching an HTTP status code. Attempting to return an invalid HTTP status code or anything other than a number or `nil` will result in a `500 Internal Server Error` response being thrown. ##### Lua Server Pages (2️⃣) Lwan is somewhat compatible with [CivetWeb](https://github.com/civetweb/civetweb/blob/588860e30721bf5453b0440c390865a8e85dcae5/docs/UserManual.md#lua-scripts-and-lua-server-pages)'s implementation of Lua Server Pages (LSP). Not all entries in the `mg` table is implemented due to API differences between Lwan and CivetWeb. A LSP file is converted into a Lua script and is executed as if it were a regular Lwan Lua script (1️⃣). The `__request` metatable is available with the same methods described above; the entries in the `mg` table are a thin wrapper around this metatable, and may not behave exactly like their original counterparts. Of note, elements in the table are lazily created (using a `__index` function in its metatable), so looping over, for instance, `mg.request_info` won't work unless the keys you're interested in were previously accessed. Because of this, the same caveat about using global variables applies to Lua Server Pages. Lua script elements must be enclosed between `` and `?>` blocks; other ways of enclosing Lua scripts (e.g. with `<%` and `%>`) aren't supported at the moment. So, for instance, one could write this code into `headers.lp` to dump all the request headers: ```