[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-6486":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":16,"stars7d":17,"stars30d":18,"stars90d":16,"forks30d":16,"starsTrendScore":16,"compositeScore":19,"rankGlobal":10,"rankLanguage":10,"license":20,"archived":21,"fork":21,"defaultBranch":22,"hasWiki":23,"hasPages":21,"topics":24,"createdAt":10,"pushedAt":10,"updatedAt":29,"readmeContent":30,"aiSummary":31,"trendingCount":16,"starSnapshotCount":16,"syncStatus":32,"lastSyncTime":33,"discoverSource":34},6486,"lwan","lpereira\u002Flwan","lpereira","Experimental, scalable, high performance HTTP server","https:\u002F\u002Flwan.ws",null,"C",6030,551,274,48,0,1,8,65.53,"GNU General Public License v2.0",false,"master",true,[25,26,27,28,5],"c","experimental","http","library","2026-06-12 04:00:28","Lwan Web Server\n===============\n\nLwan is a **high-performance** & **scalable** web server.\n\nThe [project web site](https:\u002F\u002Flwan.ws\u002F) contains more details.\n\nBuild status\n------------\n\n| OS          | Arch   | Release | Debug | Static Analysis | Tests |\n|-------------|--------|---------|-------|-----------------|------------|\n| Linux       | x86_64 | ![release](https:\u002F\u002Fshield.lwan.ws\u002Fimg\u002FgycKbr\u002Frelease \"Release\")  | ![debug](https:\u002F\u002Fshield.lwan.ws\u002Fimg\u002FgycKbr\u002Fdebug \"Debug\")     | ![static-analysis](https:\u002F\u002Fshield.lwan.ws\u002Fimg\u002FgycKbr\u002Fclang-analyze \"Static Analysis\") ![coverity](https:\u002F\u002Fscan.coverity.com\u002Fprojects\u002F375\u002Fbadge.svg) [Report history](https:\u002F\u002Fbuildbot.lwan.ws\u002Fsa\u002F) | ![tests](https:\u002F\u002Fshield.lwan.ws\u002Fimg\u002FgycKbr\u002Funit-tests \"Test\") [![Fuzzing Status](https:\u002F\u002Foss-fuzz-build-logs.storage.googleapis.com\u002Fbadges\u002Flwan.svg)](https:\u002F\u002Fbugs.chromium.org\u002Fp\u002Foss-fuzz\u002Fissues\u002Flist?sort=-opened&can=1&q=proj:lwan)           |\n| FreeBSD 14  | x86_64 | ![freebsd-release](https:\u002F\u002Fshield.lwan.ws\u002Fimg\u002FgycKbr\u002Frelease-freebsd \"Release FreeBSD\") | ![freebsd-debug](https:\u002F\u002Fshield.lwan.ws\u002Fimg\u002FgycKbr\u002Fdebug-freebsd \"Debug FreeBSD\")     |                |           |\n| OpenBSD 7.4 | x86_64 | ![openbsd-release](https:\u002F\u002Fshield.lwan.ws\u002Fimg\u002FgycKbr\u002Frelease-openbsd \"Release OpenBSD\")       | ![openbsd-debug](https:\u002F\u002Fshield.lwan.ws\u002Fimg\u002FgycKbr\u002Fdebug-openbsd \"Debug OpenBSD\")     |               | ![openbsd-tests](https:\u002F\u002Fshield.lwan.ws\u002Fimg\u002FgycKbr\u002Fopenbsd-unit-tests \"OpenBSD Tests\")         |\n\nInstalling\n----------\n\nYou can either [build Lwan yourself](#Building), use a [container\nimage](#container-images), or grab a package from [your favorite\ndistribution](#lwan-in-the-wild).\n\nBuilding\n--------\n\nBefore installing Lwan, ensure all dependencies are installed. All of them\nare common dependencies found in any GNU\u002FLinux distribution; package names\nwill be different, but it shouldn't be difficult to search using whatever\npackage management tool that's used by your distribution.\n\n### Required dependencies\n\n - [CMake](https:\u002F\u002Fcmake.org\u002F), at least version 2.8\n - [libdeflate](https:\u002F\u002Fgithub.com\u002Febiggers\u002Flibdeflate), [zlib-ng](https:\u002F\u002Fgithub.com\u002Fzlib-ng\u002Fzlib-ng) or [ZLib](http:\u002F\u002Fzlib.net)\n\n### Optional dependencies\n\nThe build system will look for these libraries and enable\u002Flink if available.\n\n - [Lua 5.1](http:\u002F\u002Fwww.lua.org) or [LuaJIT 2.0](http:\u002F\u002Fluajit.org)\n - [Valgrind](http:\u002F\u002Fvalgrind.org)\n - [Brotli](https:\u002F\u002Fgithub.com\u002Fgoogle\u002Fbrotli)\n    - Can be disabled by passing `-DENABLE_BROTLI=NO`\n - [ZSTD](https:\u002F\u002Fgithub.com\u002Ffacebook\u002Fzstd)\n    - Can be disabled by passing `-DENABLE_ZSTD=NO`\n - On Linux builds, if `-DENABLE_TLS=ON` (default) is passed:\n    - [mbedTLS](https:\u002F\u002Fgithub.com\u002FARMmbed\u002Fmbedtls)\n - Alternative memory allocators can be used by passing `-DUSE_ALTERNATIVE_MALLOC` to CMake with the following values:\n    - [\"mimalloc\"](https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fmimalloc)\n    - [\"jemalloc\"](http:\u002F\u002Fjemalloc.net\u002F)\n    - [\"tcmalloc\"](https:\u002F\u002Fgithub.com\u002Fgperftools\u002Fgperftools)\n    - \"auto\": Autodetect from the list above, falling back to libc malloc if none found\n - To run test suite:\n    - [Python](https:\u002F\u002Fwww.python.org\u002F) (2.6+) with Requests\n    - [Lua 5.1](http:\u002F\u002Fwww.lua.org)\n - To run benchmark:\n    - [Weighttp](https:\u002F\u002Fgithub.com\u002Flpereira\u002Fweighttp) -- bundled and built alongside Lwan for convenience\n    - [Matplotlib](https:\u002F\u002Fgithub.com\u002Fmatplotlib\u002Fmatplotlib)\n - To build TechEmpower benchmark suite:\n    - Client libraries for [MariaDB](https:\u002F\u002Fmariadb.org)\n    - [SQLite 3](http:\u002F\u002Fsqlite.org)\n\n### Common operating system package names\n\n#### Minimum to build\n - ArchLinux: `pacman -S cmake zlib-ng pkgconf`\n - FreeBSD: `pkg install cmake pkgconf`\n - Debian and Ubuntu 14+: `apt-get update && apt-get install git cmake zlib1g-dev pkg-config`\n - macOS: `brew install cmake`\n\n#### Build with all optional features\n - ArchLinux: `pacman -S cmake zlib-ng pkgconf sqlite luajit mariadb-libs gperftools valgrind mbedtls`\n - FreeBSD: `pkg install cmake pkgconf sqlite3 lua51`\n - Debian and Ubuntu 14+: `apt-get update && apt-get install git cmake zlib1g-dev pkg-config lua5.1-dev libsqlite3-dev libmariadb-dev libmbedtls-dev`\n - macOS: `brew install cmake mariadb-connector-c sqlite lua@5.1 pkg-config`\n\n### Build commands\n\n#### Clone the repository\n\n    ~$ git clone git:\u002F\u002Fgithub.com\u002Flpereira\u002Flwan\n    ~$ cd lwan\n\n#### Create the build directory\n\n    ~\u002Flwan$ mkdir build\n    ~\u002Flwan$ cd build\n\n#### Select build type\n\nSelecting a *release* version (no debugging symbols, messages, enable some\noptimizations, etc):\n\n    ~\u002Flwan\u002Fbuild$ cmake .. -DCMAKE_BUILD_TYPE=Release\n\nIf you'd like to enable optimizations but still use a debugger, use this instead:\n\n    ~\u002Flwan\u002Fbuild$ cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo\n\nTo disable optimizations and build a more debugging-friendly version:\n\n    ~\u002Flwan\u002Fbuild$ cmake .. -DCMAKE_BUILD_TYPE=Debug\n\n#### Build Lwan\n\n    ~\u002Flwan\u002Fbuild$ make\n\nThis will generate a few binaries:\n\n - `src\u002Fbin\u002Flwan\u002Flwan`: The main Lwan executable. May be executed with `--help` for guidance.\n - `src\u002Fbin\u002Ftestrunner\u002Ftestrunner`: Contains code to execute the test suite (`src\u002Fscripts\u002Ftestsuite.py`).\n - `src\u002Fsamples\u002Ffreegeoip\u002Ffreegeoip`: [FreeGeoIP sample implementation](https:\u002F\u002Ffreegeoip.lwan.ws). Requires SQLite.\n - `src\u002Fsamples\u002Ftechempower\u002Ftechempower`: Code for the TechEmpower Web Framework benchmark. Requires SQLite and MariaDB libraries.\n - `src\u002Fsamples\u002Fclock\u002Fclock`: [Clock sample](https:\u002F\u002Ftime.lwan.ws). Generates a never-ending animated GIF file that always shows the local time.\n - `src\u002Fsamples\u002Fforthsalon\u002Fforthsalon`: Generates a never-ending animated GIF from a program written in the [Forth Salon](https:\u002F\u002Fforthsalon.appspot.com\u002F) dialect of the [Forth](https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FForth_(programming_language)) programming language. *In construction!*\n - `src\u002Fsamples\u002Fforthsalon\u002Fforth`: Test harness for the Forth dialect used in the `forthsalon` sample.\n - `src\u002Fbin\u002Ftools\u002Fmimegen`: Builds the extension-MIME type table. Used during the build process.\n - `src\u002Fbin\u002Ftools\u002Fbin2hex`: Generates a C file from a binary file, suitable for use with #include. Used during the build process.\n - `src\u002Fbin\u002Ftools\u002Fconfigdump`: Dumps a configuration file using the configuration reader API. Used for testing.\n - `src\u002Fbin\u002Ftools\u002Fweighttp`: Rewrite of the `weighttp` HTTP benchmarking tool.\n - `src\u002Fbin\u002Ftools\u002Fstatuslookupgen`: Generates a perfect hash table for HTTP status codes and their descriptions. Used during the build process.\n - `src\u002Flib\u002Flwan-template-test`: Benchmark for the template engine\n\n#### Remarks\n\nPassing `-DCMAKE_BUILD_TYPE=Release` will enable some compiler\noptimizations (such as [LTO](http:\u002F\u002Fgcc.gnu.org\u002Fwiki\u002FLinkTimeOptimization))\nand tune the code for current architecture.\n\n> [!IMPORTANT]\n>\n> *Please use the release build when benchmarking*.\n> The default is the Debug build, which not only logs all requests to the\n> standard output, but does so while holding a lock, severely holding down\n> the server.\n\nThe default build (i.e. not passing `-DCMAKE_BUILD_TYPE=Release`) will build\na version suitable for debugging purposes.  This version can be used under\nValgrind *(if its headers are present)* and includes debugging messages that\nare stripped in the release version.  Debugging messages are printed for\neach and every request.\n\nOn these builds, sanitizers can be enabled.  To select which one to build Lwan\nwith, specify one of the following options to the CMake invocation line:\n\n - `-DSANITIZER=ubsan` selects the Undefined Behavior Sanitizer.\n - `-DSANITIZER=address` selects the Address Sanitizer.\n - `-DSANITIZER=thread` selects the Thread Sanitizer.\n\nAlternative memory allocators can be selected as well.  Lwan currently\nsupports [TCMalloc](https:\u002F\u002Fgithub.com\u002Fgoogle\u002Ftcmalloc),\n[mimalloc](https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fmimalloc), and\n[jemalloc](http:\u002F\u002Fjemalloc.net\u002F) out of the box.  To use either one of them,\npass `-DALTERNATIVE_MALLOC=name` to the CMake invocation line, using the\nnames provided in the \"Optional dependencies\"  section.\n\nThe `-DUSE_SYSLOG=ON` option can be passed to CMake to also log to the system log\nin addition to the standard output.\n\nIf you're building Lwan for a distribution, it might be wise to use the\n`-DMTUNE_NATIVE=OFF` option, otherwise the generated binary may fail to\nrun on some computers.\n\nTLS support is enabled automatically in the presence of a suitable mbedTLS\ninstallation on Linux systems with headers new enough to support kTLS, but\ncan be disabled by passing `-DENABLE_TLS=NO` to CMake.\n\n### Tests\n\n    ~\u002Flwan\u002Fbuild$ make testsuite\n\nThis will compile the `testrunner` program and execute regression test suite\nin `src\u002Fscripts\u002Ftestsuite.py`.\n\n### Benchmark\n\n    ~\u002Flwan\u002Fbuild$ make benchmark\n\nThis will compile `testrunner` and execute benchmark script\n`src\u002Fscripts\u002Fbenchmark.py`.\n\n### Coverage\n\nLwan can also be built with the Coverage build type by specifying\n`-DCMAKE_BUILD_TYPE=Coverage`.  This enables the `generate-coverage` make\ntarget, which will run `testrunner` to prepare a test coverage report with\n[lcov](http:\u002F\u002Fltp.sourceforge.net\u002Fcoverage\u002Flcov.php).\n\nEvery commit in this repository triggers the generation of this report,\nand results are [publicly available](https:\u002F\u002Fbuildbot.lwan.ws\u002Flcov\u002F).\n\n### CMake options\n\nThese options are available to configure a build.  Even if an option is\nset, it'll be checked if it can be used or not (e.g. third-party libraries\nneed to be installed).\n\n| Option | Default | Description |\n|--------|---------|-------------|\n|`ENABLE_BROTLI`|`ON`|Enables Brotli compression support|\n|`ENABLE_IA32_CRC32`|`ON`|Enable Intel CRC32c instructions for hashing|\n|`ENABLE_TLS`|`OFF`|Enables kTLS (with mbedTLS) support|\n|`ENABLE_ZSTD`|`ON`|Enables Zstd compression support|\n|`MTUNE_NATIVE`|`ON`|Tune the binary for the current architecture|\n|`SANITIZER`|`none`|Select which sanitizer to use (see above)|\n|`USE_ALTERNATIVE_MALLOC`|`OFF`|Select a different `malloc()` implementation; default is to use the one from libc|\n|`USE_SYSLOG`|`OFF`|Log to syslog in addition to standard output|\n\nRunning\n-------\n\nSet up the server by editing the provided `lwan.conf`; the format is\nexplained in details below.\n\n> [!NOTE]\n>\n> Lwan will try to find a configuration file based in the\n> executable name in the current directory; `testrunner.conf` will be used\n> for the `testrunner` binary, `lwan.conf` for the `lwan` binary, and so on.\n\nConfiguration files are loaded from the current directory. If no changes\nare made to this file, running Lwan will serve static files located in\nthe `.\u002Fwwwroot` directory. Lwan will listen on port 8080 on all interfaces.\n\nLwan will detect the number of CPUs, will increase the maximum number of\nopen file descriptors and generally try its best to autodetect reasonable\nsettings for the environment it's running on.  Many of these settings can\nbe tweaked in the configuration file, but it's usually a good idea to not\nmess with them.\n\n> [!TIP]\n>\n>  Optionally, the `lwan` binary can be used for one-shot\n> static file serving without any configuration file.  Run it with `--help`\n> for help on that.\n\nConfiguration File\n------------------\n\n### Format\n\nLwan uses a familiar `key = value` configuration file syntax.  Comments are\nsupported with the `#` character (similar to e.g.  shell scripts, Python,\nand Perl).  Nested sections can be created with curly brackets.  Sections\ncan be empty; in this case, curly brackets are optional.\n\n`some_key_name` is equivalent to `some key name` in configuration files (as\nan implementation detail, code reading configuration options will only be\ngiven the version with underscores).\n\n> [!TIP]\n>\n>  Values can contain environment variables. Use the\n> syntax `${VARIABLE_NAME}`.  Default values can be specified with a colon\n> (e.g.  `${VARIABLE_NAME:foo}`, which evaluates to `${VARIABLE_NAME}` if\n> it's set, or `foo` otherwise).\n\n```\nsound volume = 11 # This one is 1 louder\n\nplaylist metal {\n   files = '''\n\t\u002Fmulti\u002Fline\u002Fstrings\u002Fare\u002Fsupported.mp3\n\t\u002Fanything\u002Finside\u002Fthese\u002Fare\u002Fstored\u002Fverbatim.mp3\n   '''\n}\n\nplaylist chiptune {\n   files = \"\"\"\n\t\u002Fif\u002Fit\u002Fstarts\u002Fwith\u002Fsingle\u002Fquotes\u002Fit\u002Fends\u002Fwith\u002Fsingle\u002Fquotes.mod\n\t\u002Fbut\u002Fit\u002Fcan\u002Fuse\u002Fdouble\u002Fquotes.s3m\n   \"\"\"\n}\n```\n\nSome examples can be found in `lwan.conf` and `techempower.conf`.\n\n#### Constants\n\nConstants can be defined and reused throughout the configuration file by\nspecifying them in a `constants` section anywhere in the configuration\nfile.  A constant will be available only after that section defines a\nparticular constant. Constants can be re-defined.  If a constant isn't\ndefined, its value will be obtained from an environment variable.  If\nit's not defined in either one `constants` section, or in the environment,\nLwan will abort with an appropriate error message.\n\n```\nconstants {\n    user_name = ${USER}\n    home_directory = ${HOME}\n    buffer_size = 1000000\n}\n```\n\nThe same syntax for default values specified above is valid here (e.g.\nspecifying `user_name` to be `${USER:nobody}` will set `${user_name}` to\n`nobody` if `${USER}` isn't set in the environment variable or isn't\nanother constant.)\n\n#### Value types\n\n| Type   | Description |\n|--------|-------------|\n| `str`  | Any kind of free-form text, usually application specific |\n| `int`  | Integer number. Range is application specific |\n| `time` | Time interval.  See table below for units |\n| `bool` | Boolean value. See table below for valid values |\n\n#### Time Intervals\n\nTime fields can be specified using multipliers. Multiple can be specified, they're\njust added together; for instance, \"1M 1w\" specifies \"1 month and 1 week\"\n(37 days).  The following table lists all known multipliers:\n\n| Multiplier | Description |\n|------------|-------------|\n| `s`        | Seconds |\n| `m`        | Minutes |\n| `h`        | Hours |\n| `d`        | Days |\n| `w`        | 7-day Weeks |\n| `M`        | 30-day Months |\n| `y`        | 365-day Years |\n\n> [!NOTE]\n>\n> A number with a multiplier not in this table is ignored; a\n> warning is issued while reading the configuration file.  No spaces must\n> exist between the number and its multiplier.\n\n#### Boolean Values\n\n| True Values | False Values |\n|-------------|--------------|\n| Any integer number different than 0 | 0 |\n| `on` | `off` |\n| `true` | `false` |\n| `yes` | `no` |\n\n### Global Settings\n\nIt's generally a good idea to let Lwan decide the best settings for your\nenvironment.  However, not every environment is the same, and not all uses\ncan be decided automatically, so some configuration options are provided.\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `keep_alive_timeout` | `time`  | `15` | Timeout to keep a connection alive |\n| `quiet` | `bool` | `false` | Set to true to not print any debugging messages. Only effective in release builds. |\n| `expires` | `time` | `1M 1w` | Value of the \"Expires\" header. Default is 1 month and 1 week |\n| `threads` | `int` | `0` | Number of I\u002FO threads. Default (0) is the number of online CPUs |\n| `proxy_protocol` | `bool` | `false` | Enables the [PROXY protocol](https:\u002F\u002Fwww.haproxy.com\u002Fblog\u002Fhaproxy\u002Fproxy-protocol\u002F). 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 |\n| `max_post_data_size` | `int` | `40960` | Sets the maximum number of data size for POST requests, in bytes |\n| `max_put_data_size` | `int` | `40960` | Sets the maximum number of data size for PUT requests, in bytes |\n| `max_file_descriptors` | `int` | `524288` | Maximum number of file descriptors. Needs to be at least 10x `threads` |\n| `request_buffer_size` | `int` | `4096` | Request buffer size length. If larger than the default of `4096`, it'll be dynamically allocated. |\n| `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.|\n| `error_template` | `str` | Default error template | Template for error codes. See variables below. |\n\n#### Variables for `error_template`\n\n| Variable | Type | Description |\n|----------|------|-------------|\n| `short_message` | `str` | Short error message (e.g. `Not found`) |\n| `long_message` | `str` | Long error message (e.g. `The requested resource could not be found on this server`) |\n\n### Straitjacket\n\nLwan can drop its privileges to a user in the system, and limit its\nfilesystem view with a chroot.  While not bulletproof, this provides a\nfirst layer of security in the case there's a bug in Lwan.\n\nIn order to use this feature, declare a `straitjacket` (or `straightjacket`)\nsection, and set some options.  This requires Lwan to be executed as `root`.\n\nAlthough this section can be written anywhere in the file (as long as\nit is a top level declaration), if any directories are open, due to\ne.g.  instantiating the `serve_files` module, Lwan will refuse to\nstart.  (This check is only performed on Linux as a safeguard for\nmalconfiguration.)\n\n> [!TIP]\n>\n>  Declare a Straitjacket right before a `site` section\n> in such a way that configuration files and private data (e.g. TLS keys)\n> are out of reach of the server after initialization has taken place.\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `user` | `str`  | `NULL` | Drop privileges to this user name |\n| `chroot` | `str` | `NULL` | Path to `chroot()` |\n| `drop_capabilities` | `bool` | `true` | Drop all capabilities with capset(2) (under Linux), or pledge(2) (under OpenBSD). |\n\n### Headers\n\nIf there's a need to specify custom headers for each response, one can declare\na `headers` section in the global scope.  The order which this section appears\nisn't important.\n\nFor example, this declaration:\n\n```\nheaders {\n\tServer = Apache\u002F1.0.0 or nginx\u002F1.0.0 (at your option)\n\tSome-Custom-Header = ${WITH_THIS_ENVIRONMENT_VARIABLE}\n}\n```\n\nWill both override the `Server` header (`Server: lwan` won't be sent), and set\n`Some-Custom-Header` with the value obtained from the environment variable\n`$WITH_THIS_ENVIRONMENT_VARIABLE`.\n\nSome headers can't be overridden, as that would cause issues when sending their\nactual values while servicing requests.  These include but is not limited to:\n\n  - `Date`\n  - `Expires`\n  - `WWW-Authenticate`\n  - `Connection`\n  - `Content-Type`\n  - `Transfer-Encoding`\n  - All `Access-Control-Allow-` headers\n\n> [!NOTE]\n>\n> Header names are also case-insensitive (and case-preserving).  Overriding\n> `SeRVeR` will override the `Server` header, but send it the way it was\n> written in the configuration file.\n\n### Listeners\n\nOnly two listeners are supported per Lwan process: the HTTP listener (`listener`\nsection), and the HTTPS listener (`tls_listener` section).  Only one listener\nof each type is allowed.\n\n> [!WARNING]\n>\n> TLS support is experimental.  Although it is stable\n> during initial testing, your mileage may vary. Only TLSv1.2 is supported\n> at this point, but TLSv1.3 is planned.\n\n> [!NOTE]\n>\n> TLS support requires :penguin: Linux with the `tls.ko`\n> module built-in or loaded.  Support for other operating systems may be\n> added in the future.  FreeBSD seems possible, other operating systems\n> do not seem to offer similar feature.  For unsupported operating systems,\n> using a TLS terminator proxy such as [Hitch](https:\u002F\u002Fhitch-tls.org\u002F) is a good\n> option.\n\nFor both `listener` and `tls_listener` sections, the only parameter is the\nthe interface address and port to listen on.  The listener syntax is\n`${ADDRESS}:${PORT}`, where `${ADDRESS}` can either be `*` (binding to all\ninterfaces), an IPv6 address (if surrounded by square brackets), an IPv4\naddress, or a hostname.  For instance, `listener localhost:9876` would\nlisten only in the `lo` interface, port `9876`.\n\nWhile a `listener` section takes no keys, a `tls_listener` section requires\ntwo: `cert` and `key` (each pointing, respectively, to the location on disk\nwhere the TLS certificate and private key files are located) and takes an\noptional boolean `hsts` key, which controls if `Strict-Transport-Security`\nheaders will be sent on HTTPS responses.\n\n> [!TIP]\n>\n>  To generate these keys for testing purposes, the\n> OpenSSL command-line tool can be used like the following:\n> `openssl req -nodes -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 7`\n\n> [!NOTE]\n>\n> It's recommended that a [Straitjacket](#Straitjacket) with a `chroot` option is declared\n> right after a `tls_listener` section, in such a way that the paths to the\n> certificate and key are out of reach from that point on.\n\nIf systemd socket activation is used, `systemd` can be specified as a\nparameter.  (If multiple listeners from systemd are specified,\n`systemd:FileDescriptorName` can be specified, where `FileDescriptorName`\nfollows the [conventions set in the `systemd.socket` documentation](https:\u002F\u002Fwww.freedesktop.org\u002Fsoftware\u002Fsystemd\u002Fman\u002Fsystemd.socket.html).)\n\nExamples:\n\n```\nlistener *:8080\t\t# Listen on all interfaces, port 8080, HTTP\n\ntls_listener *:8081 {\t# Listen on all interfaces, port 8081, HTTPS\n\tcert = \u002Fpath\u002Fto\u002Fcert.pem\n\tkey = \u002Fpath\u002Fto\u002Fkey.pem\n}\n\n# Use named systemd socket activation for HTTP listener\nlistener systemd:my-service-http.socket\n\n# Use named systemd socket activation for HTTPS listener\ntls_listener systemd:my-service-https.socket {\n\t...\n}\n```\n\n### Site\n\nA `site` section groups instances of modules and handlers that will respond to\nrequests to a given URL prefix.\n\n#### Routing URLs Using Modules or Handlers\n\nIn order to route URLs, Lwan matches the largest common prefix from the request\nURI with a set of prefixes specified in the listener section.  How a request to\na particular prefix will be handled depends on which handler or module has been\ndeclared in the listener section.  Handlers and modules are similar internally;\nhandlers are merely functions and hold no state, and modules holds state\n(\"instance\").  Multiple instances of a module can appear in a listener section.\n\nThere is no special syntax to attach a prefix to a handler or module; all the\nconfiguration parser rules apply here.  Use `${NAME} ${PREFIX}` to link the\n`${PREFIX}` prefix path to either a handler named `${NAME}` (if `${NAME}`\nbegins with `&`, as with C's \"address of\" operator), or a module named\n`${NAME}`.  Empty sections can be used here.\n\nEach module will have its specific set of options, and they're listed in the\nnext sections.  In addition to configuration options, a special `authorization`\nsection can be present in the declaration of a module instance.  Handlers do\nnot take any configuration options, but may include the `authorization`\nsection.\n\n> [!TIP]\n>\n>  Executing Lwan with the `--version` command-line\n> argument will show a list of built-in modules and handlers.\n\nThe following is some basic documentation for the modules shipped with Lwan.\n\n#### File Serving\n\nThe `serve_files` module will serve static files, and automatically create\ndirectory indices or serve pre-compressed files.  It'll generally try its\nbest to serve files in the fastest way possible according to some heuristics.\n\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `path`                     | `str`  | `NULL`       | Path to a directory containing files to be served |\n| `index_path`               | `str`  | `index.html` | File name to serve as an index for a directory |\n| `serve_precompressed_path` | `bool` | `true`       | If $FILE.gz exists, is smaller and newer than $FILE, and the client accepts `gzip` encoding, transfer it |\n| `auto_index`               | `bool` | `true`       | Generate a directory list automatically if no `index_path` file present.  Otherwise, yields 404 |\n| `auto_index_readme`        | `bool` | `true`       | Includes the contents of README files as part of the automatically generated directory index |\n| `directory_list_template`  | `str`  | `NULL`       | Path to a Mustache template for the directory list; by default, use an internal template |\n| `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\u002FO threads while file extents are being read from the filesystem. |\n| `cache_for`                | `time` | `5s`         | Time to keep file metadata (size, compressed contents, open file descriptor, etc.) in cache |\n\n> [!NOTE]\n>\n> Files smaller than 16KiB will be compressed in RAM for\n> the duration specified in the `cache_for` setting.  Lwan will always try\n> to compress with deflate, and will optionally compress with Brotli and\n> zstd (if Lwan has been built with proper support).\n>\n> In cases where compression wouldn't be worth the effort (e.g. adding the\n> `Content-Encoding` header would result in a larger response than sending\n> the uncompressed file, usually the case for very small files), Lwan won't\n> spend time compressing a file.\n>\n> For files larger than 16KiB, Lwan will not attempt to compress them.  In\n> future versions, it might do this and send responses using\n> chunked-encoding while the file is being compressed (up to a certain\n> limit, of course), but for now, only precompressed files (see\n> `serve_precompressed_path` setting in the table above) are considered.\n>\n> For all cases, Lwan might try using the gzipped version if that's found in\n> the filesystem and the client requested this encoding.\n\n##### Variables for `directory_list_template`\n\n| Variable | Type | Description |\n|----------|------|-------------|\n| `rel_path` | `str` | Path relative to the root directory real path |\n| `readme`   | `str` | Contents of first readme file found (`readme`, `readme.txt`, `read.me`, `README.TXT`, `README`) |\n| `file_list` | iterator | Iterates on file list |\n| `file_list.zebra_class` | `str` | `odd` for odd items, or `even` or even items |\n| `file_list.icon` | `str` | Path to the icon for the file type |\n| `file_list.name` | `str` | File name (escaped) |\n| `file_list.type` | `str` | File type (directory or regular file) |\n| `file_list.size` | `int` | File size |\n| `file_list.unit` | `str` | Unit for `file_size` |\n\n#### Lua\n\nThe `lua` module will allow requests to be serviced by scripts written in\nthe [Lua](https:\u002F\u002Fwww.lua.org\u002F) programming language.  Although the\nfunctionality provided by this module is quite spartan, it's able to run\nframeworks such as [Sailor](https:\u002F\u002Fgithub.com\u002Flpereira\u002Fsailor-hello-lwan).\n\nScripts can be served from files or embedded in the configuration file, and\nthe results of loading them, the standard Lua modules, and (optionally, if\nusing LuaJIT) optimizing the code will be cached for a while.\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `default_type` | `str` | `text\u002Fplain` | Default MIME-Type for responses |\n| `script_file` | `str` | `NULL` | Path to Lua script|\n| `cache_period` | `time` | `15s` | Time to keep Lua state loaded in memory |\n| `script` | `str` | `NULL` | Inline lua script |\n\n##### Writing request handlers\n\n> [!NOTE]\n>\n> Lua scripts can't use global variables, as they may be not\n> only serviced by different threads, but the state will be available only\n> for the amount of time specified in the `cache_period` configuration\n> option.  This is because each I\u002FO thread in Lwan will create an instance\n> of a Lua VM (i.e.  one `lua_State` struct for every I\u002FO thread), and each\n> Lwan coroutine will spawn a Lua thread (with `lua_newthread()`) per\n> request.\n\nThere's no need to have one instance of the Lua module for each endpoint; a\nsingle script, embedded in the configuration file or otherwise, can service\nmany different endpoints.  Scripts are supposed to implement functions with\nthe following signature: `handle_${METHOD}_${ENDPOINT}(req)`, where\n`${METHOD}` can be a HTTP method (i.e.  `get`, `post`, `head`, etc.), and\n`${ENDPOINT}` is the desired endpoint to be handled by that function.  A generic\n`handle(req)` function will be called if the specific version doesn't exist.\n\n> [!TIP]\n>\n>  Use the `root` endpoint for a catchall. For example,\n> the handler function `handle_get_root()` will be called if no other handler\n> could be found for that request.  If no catchall is specified, the server\n> will return a `404 Not Found` error.\n\nThe `req` parameter points to a metatable that contains methods to obtain\ninformation from the request, or to set the response, as seen below:\n\n   - `req:query_param(param)` returns the query parameter (from the query string) with the key `param`, or `nil` if not found\n   - `req:post_param(param)` returns the post parameter (only for `${POST}` handlers) with the key `param`, or `nil` if not found\n   - `req:set_response(str)` sets the response to the string `str`\n   - `req:say(str)` sends a response chunk (using chunked encoding in HTTP)\n   - `req:send_event(event, str)` sends an event (using server-sent events)\n   - `req:cookie(param)` returns the cookie named `param`, or `nil` is not found\n   - `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()`\n   - `req:header(name)` obtains the header from the request with the given name or `nil` if not found\n   - `req:sleep(ms)` pauses the current handler for the specified amount of milliseconds\n   - `req:ws_upgrade()` returns `1` if the connection could be upgraded to a WebSocket; `0` otherwise\n   - `req:ws_write_text(str)` sends `str` through the WebSocket-upgraded connection as text frame\n   - `req:ws_write_binary(str)` sends `str` through the WebSocket-upgraded connection as binary frame\n   - `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\n   - `req:ws_read()` returns a string with the contents of the last WebSocket frame, or a number indicating an status (ENOTCONN\u002F107 on Linux if it has been disconnected; EAGAIN\u002F11 on Linux if nothing was available; ENOMSG\u002F42 on Linux otherwise).  The return value here might change in the future for something more Lua-like.\n   - `req:remote_address()` returns a string with the remote IP address.\n   - `req:path()` returns a string with the request path.\n   - `req:query_string()` returns a string with the query string (empty string if no query string present).\n   - `req:body()` returns the request body (POST\u002FPUT requests).\n   - `req:request_id()` returns a string containing the request ID.\n   - `req:request_date()` returns the date as it'll be written in the `Date` response header.\n   - `req:is_https()` returns `true` if this request is serviced through HTTPS, `false` otherwise.\n   - `req:host()` returns the value of the `Host` header if present, otherwise `nil`.\n   - `req:http_version()` returns `HTTP\u002F1.0` or `HTTP\u002F1.1` depending on the request version.\n   - `req:http_method()` returns a string, in uppercase, with the HTTP method (e.g. `\"GET\"`).\n   - `req:http_headers()` returns a table with all headers and their values.\n\nHandler functions may return either `nil` (in which case, a `200 OK` response\nis generated), or a number matching an HTTP status code.  Attempting to return\nan invalid HTTP status code or anything other than a number or `nil` will result\nin a `500 Internal Server Error` response being thrown.\n\n##### Logging\n\nIn addition to the metamethods in the `req` parameter, one can also log messages\nwith different logging levels by calling methods from `Lwan.log`:\n\n   - `Lwan.log:warning(str)`\n   - `Lwan.log:info(str)`\n   - `Lwan.log:error(str)`\n   - `Lwan.log:critical(str)` (Will also abort Lwan! Use with caution)\n   - `Lwan.log:debug(str)` (Only available in debug builds; no-op otherwise)\n\n> [!NOTE]\n>\n> If Lwan is built with syslog support, these messages will also be sent to the\n> system log, otherwise they'll be printed to the standard error.\n\n\n#### Rewrite\n\nThe `rewrite` module will match\n[patterns](https:\u002F\u002Fman.openbsd.org\u002Fpatterns.7) in URLs and give the option\nto either redirect to another URL, or rewrite the request in a way that Lwan\nwill handle the request as if it were made in that way originally.\n\n> [!NOTE]\n>\n> Forked from Lua 5.3.1, the regular expresion\n> engine may not be as feature-packed as most general-purpose engines, but\n> has been chosen specifically because it is a [deterministic finite\n> automaton](https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FDeterministic_finite_automaton)\n> in an attempt to make some kinds of [denial of service\n> attacks](https:\u002F\u002Fowasp.org\u002Fwww-community\u002Fattacks\u002FRegular_expression_Denial_of_Service_-_ReDoS)\n> impossible.\n\nThe new URL can be specified using a simple text substitution syntax, or use Lua scripts.\n\n> [!TIP]\n>\n>  Lua scripts will contain the same metamethods\n> available in the `req` metatable provided by the Lua module, so it can be\n> quite powerful.\n\nEach instance of the rewrite module will require a `pattern` and the action\nto execute when such pattern is matched.  Patterns are evaluated in the\norder they appear in the configuration file, and are specified using nested\nsections in the configuration file.  For instance, consider the following\nexample, where two patterns are specified:\n\n```\nrewrite \u002Fsome\u002Fbase\u002Fendpoint {\n    pattern posts\u002F(%d+) {\n        # Matches \u002Fsome\u002Fbase\u002Fendpointposts\u002F2600 and \u002Fsome\u002Fbase\u002Fendpoint\u002Fposts\u002F2600\n        rewrite_as = \u002Fcms\u002Fview-post?id=%1\n    }\n    pattern imgur\u002F(%a+)\u002F(%g+) {\n        # Matches \u002Fsome\u002Fbase\u002Fendpointimgur\u002Fgif\u002FmpT94Ld and \u002Fsome\u002Fbase\u002Fendpoint\u002Fimgur\u002Fgif\u002FmpT94Ld\n        redirect_to = https:\u002F\u002Fi.imgur.com\u002F%2.%1\n    }\n}\n```\n\nThis example defines two patterns, one providing a nicer URL that's hidden\nfrom the user, and another providing a different way to obtain a direct link\nto an image hosted on a popular image hosting service (i.e.  requesting\n`\u002Fsome\u002Fbase\u002Fendpoint\u002Fimgur\u002Fmp4\u002F4kOZNYX` will redirect directly to a resource\nin the Imgur service).\n\nThe value of `rewrite_as` or `redirect_to` can be Lua scripts as well; in\nwhich case, the option `expand_with_lua` must be set to `true`, and, instead\nof using the simple text substitution syntax as the example above, a\nfunction named `handle_rewrite(req, captures)` has to be defined instead.\nThe `req` parameter is documented in the Lua module section; the `captures`\nparameter is a table containing all the captures, in order (i.e. ``captures[2]``\nis equivalent to ``%2`` in the simple text substitition syntax).  This function\nreturns the new URL to redirect to.\n\nThis module has no options by itself.  Options are specified in each and\nevery pattern.\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `rewrite_as` | `str` | `NULL` | Rewrite the URL following this pattern |\n| `redirect_to` | `str` | `NULL` | Redirect to a new URL following this pattern |\n| `expand_with_lua` | `bool` | `false` | Use Lua scripts to redirect to or rewrite a request |\n\n`redirect_to` and `rewrite_as` options are mutually exclusive, and one of\nthem must be specified at least.\n\nIt's also possible to specify conditions to trigger a rewrite.  To specify one,\nopen a `condition` block, specify the condition type, and then the parameters\nfor that condition to be evaluated.  Multiple conditions can be set per rewrite\nrule as long as there's one condition per type:\n\n|Condition          |Can use subst. syntax|Section required|Parameters|Description|\n|-------------------|---------------------|----------------|----------|-----------|\n|`cookie`           | Yes | Yes | A single `key` = `value`| Checks if request has cookie `key` has value `value` |\n|`query`            | Yes | Yes | A single `key` = `value`| Checks if request has query variable `key` has value `value` |\n|`post`             | Yes | Yes | A single `key` = `value`| Checks if request has post data `key` has value `value` |\n|`header`           | Yes | Yes | A single `key` = `value`| Checks if request header `key` has value `value` |\n|`environment`      | Yes | Yes | A single `key` = `value`| Checks if environment variable `key` has value `value` |\n|`stat`             | Yes | Yes | `path`, `is_dir`, `is_file` | Checks if `path` exists in the filesystem, and optionally checks if `is_dir` or `is_file` |\n|`encoding`         | No  | Yes | `deflate`, `gzip`, `brotli`, `zstd`, `none` | Checks if client accepts responses in a determined encoding (e.g. `deflate = yes` for Deflate encoding) |\n|`proxied`          | No  | No  | Boolean | Checks if request has been proxied through PROXY protocol |\n|`http_1.0`         | No  | No  | Boolean | Checks if request is made with a HTTP\u002F1.0 client |\n|`is_https`         | No  | No  | Boolean | Checks if request is made through HTTPS |\n|`has_query_string` | No  | No  | Boolean | Checks if request has a query string (even if empty) |\n|`method`           | No  | No  | Method name | Checks if HTTP method is the one specified |\n|`lua`              | No  | No  | String | Runs Lua function `matches(req)` inside String and checks if it returns `true` or `false` |\n|`backref`          | No  | Yes | A single `backref index` = `value` | Checks if the backref number matches the provided value |\n\n*Can use subst. syntax* refers to the ability to reference the matched\npattern using the same substitution syntax used for the `rewrite as` or\n`redirect to` actions.  For instance, `condition cookie { some-cookie-name =\nfoo-%1-bar }` will substitute `%1` with the first match from the pattern\nthis condition is related to.\n\n> [!NOTE]\n>\n> Conditions that do not require a section have to be written\n> as a key; for instance, `condition has_query_string = yes`.\n\nFor example, if one wants to send `site-dark-mode.css` if there is a\n`style` cookie with the value `dark`, and send `site-light-mode.css`\notherwise, one can write:\n\n```\npattern site.css {\n   rewrite as = \u002Fsite-dark-mode.css\n   condition cookie { style = dark }\n}\npattern site.css {\n   rewrite as = \u002Fsite-light-mode.css\n}\n```\n\nAnother example: if one wants to send pre-compressed files\nif they do exist in the filesystem and the user requested them:\n\n```\npattern (%g+) {\n   condition encoding { brotli = yes }\n   condition stat { path = %1.brotli }\n   rewrite as = %1.brotli\n}\npattern (%g+) {\n   condition encoding { gzip = yes }\n   condition stat { path = %1.gzip }\n   rewrite as = %1.gzip\n}\npattern (%g+) {\n   condition encoding { zstd = yes }\n   condition stat { path = %1.zstd }\n   rewrite as = %1.zstd\n}\npattern (%g+) {\n   condition encoding { deflate = yes }\n   condition stat { path = %1.deflate }\n   rewrite as = %1.deflate\n}\n```\n\n> [!NOTE]\n>\n> In general, this is not necessary, as the file serving\n> module will do this automatically and pick the smallest file available for\n> the requested encoding, but this shows it's possible to have a similar\n> feature by configuration alone.\n\n#### Redirect\n\nThe `redirect` module will, as it says in the tin, generate a `301\nMoved permanently` (by default; the code can be changed, see below)\nresponse, according to the options specified in its configuration.\nGenerally, the `rewrite` module should be used instead as it packs more\nfeatures; however, this module serves also as an example of how to\nwrite Lwan modules (less than 100 lines of code).\n\nIf the `to` option is not specified, it always generates a `500\nInternal Server Error` response.  Specifying an invalid HTTP code, or a\ncode that Lwan doesn't know about (see `enum lwan_http_status`), will\nproduce a `301 Moved Permanently` response.\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `to` | `str` | `NULL` | The location to redirect to |\n| `code` | `int` | `301` | The HTTP code to perform a redirect |\n\n#### Response\n\nThe `response` module will generate an artificial response of any HTTP code.\nIn addition to also serving as an example of how to write a Lwan module,\nit can be used to carve out voids from other modules (e.g. generating a\n`405 Not Allowed` response for files in `\u002F.git`, if `\u002F` is served with\nthe `serve_files` module).\n\nIf the supplied `code` falls outside the response codes known by Lwan,\na `404 Not Found` error will be sent instead.\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `code` | `int` | `999` | A HTTP response code |\n\n#### FastCGI\n\nThe `fastcgi` module proxies requests between the HTTP client connecting to\nLwan and a [FastCGI](https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FFastCGI) server\naccessible by Lwan.  This is useful, for instance, to serve pages from a\nscripting language such as PHP.\n\n> [!NOTE]\n>\n> This is a preliminary version of this module, and\n> as such, it's not well optimized, some features are missing, and\n> some values provided to the environment are hardcoded.\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `address` | `str` |  | Address to connect to. Can be a file path (for Unix Domain Sockets), IPv4 address (`aaa.bbb.ccc.ddd:port`), or IPv6 address (`[...]:port`). |\n| `script_path` | `str` |  | Location where the CGI scripts are located. |\n| `default_index` | `str` | `index.php` | Default script to execute if unspecified in the request URI. |\n\n### Authorization Section\n\nAuthorization sections can be declared in any module instance or handler,\nand provides a way to authorize the fulfillment of that request through\nthe standard HTTP authorization mechanism.  In order to require authorization\nto access a certain module instance or handler, declare an `authorization`\nsection with a `basic` parameter, and set one of its options.\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `realm` | `str` | `Lwan` | Realm for authorization. This is usually shown in the user\u002Fpassword UI in browsers |\n| `password_file` | `str` | `NULL` | Path for a file containing username and passwords (in clear text).  The file format is the same as the configuration file format used by Lwan |\n\n> [!WARNING]\n>\n> Not only passwords are stored in clear text in a file\n> that should be accessible by the server, they'll be kept in memory for a few\n> seconds.  Avoid using this feature if possible.\n\nHacking\n-------\n\nPlease read this section (and follow it) if you're planning on contributing\nto Lwan.  There's nothing unexpected here; this mostly follows the rules and\nexpectations of many other FOSS projects, but every one expects things a\nlittle bit different from one another.\n\n### Coding Style\n\nLwan tries to follow a consistent coding style throughout the project.  If you're\nconsidering contributing a patch to the project, please respect this style by trying\nto match the style of the surrounding code.  In general:\n\n - `global_variables_are_named_like_this`, even though they tend to be rare and should be marked as `static` (with rare exceptions)\n - Local variables are usually shorter, e.g. `local_var`, `i`, `conn`\n - Struct names are often as short as they're descriptive.  `typedef` for structs are rarely used in Lwan\n - Header files should use `#pragma once` instead of the usual include guard hackery\n - Functions that are used between .c files but are not APIs to be exposed to liblwan should have their prototype added to `lwan-private.h`\n - Functions should be short and sweet.  Exceptions may apply\n - Public functions should be prefixed with `lwan_`\n - Public types should be prefixed with `lwan_`\n - Private functions must be static, and can be named without the `lwan_` prefix\n - Code is indented with 4 spaces; don't use tabs\n - There's a suggested line break at column 80, but it's not enforced\n - `\u002F* Old C-style comments are preferred *\u002F`\n - `clang-format` can be used to format the source code in an acceptable way; a `.clang-format` file is provided\n\n### Tests\n\nIf modifying well-tested areas of the code (e.g. the event loop, HTTP parser,\netc.), please add a new integration test and make sure that, before you send a\npull request, all tests (including the new ones you've sent) are working.\nTests can be added by modifying `src\u002Fscripts\u002Ftestsuite.py`, and executed by\neither invoking that script directly from the source root, or executing the\n`testsuite` build target.\n\nSome tests will only work on Linux, and won't be executed on other platforms.\n\n### Fuzz-testing\n\nLwan is automatically fuzz-tested by\n[OSS-Fuzz](https:\u002F\u002Fgithub.com\u002Fgoogle\u002Foss-fuzz\u002F).  To fuzz-test locally,\nthough, one can [follow the instructions to test\nlocally](https:\u002F\u002Fgithub.com\u002Fgoogle\u002Foss-fuzz\u002Fblob\u002Fmaster\u002Fdocs\u002Fnew_project_guide.md#testing-locally).\n\nCurrently, there are fuzzing drivers for the request parsing code, the\nconfiguration file parser, the template parser, and the Lua string pattern\nmatching library used in the rewrite module.\n\nAdding new fuzzers is trivial:\n\n- Fuzzers are implemented in C++ and the sources are placed in\n  `src\u002Fbin\u002Ffuzz`.\n- Fuzzers should be named `${FUZZER_NAME}_fuzzer.cc`.  Look at the OSS-Fuzz\n  documentation and other fuzzers on information about how to write these.\n- These files are not compiled by the Lwan build system, but rather by the\n  build scripts used by OSS-Fuzz.  To test your fuzzer, please follow the\n  instructions to test locally, which will build the fuzzer in the\n  environment they'll be executed in.\n- A fuzzing corpus has to be provided in `src\u002Ffuzz\u002Fcorpus`.  Files have to\n  be named `corpus-${FUZZER_NAME}-${UNIQUE_ID}`.\n\n### Exporting APIs\n\nThe shared object version of `liblwan` on ELF targets (e.g. Linux) will use\na symbol filter script to hide symbols that are considered private to the\nlibrary.  Please edit `src\u002Flib\u002Fliblwan.sym` to add new symbols that should\nbe exported to `liblwan.so`.\n\n### Using Git and Pull Requests\n\nLwan tries to maintain a source history that's as flat as possible, devoid of\nmerge commits.  This means that pull requests should be rebased on top of the\ncurrent master before they can be merged; sometimes this can be done\nautomatically by the GitHub interface, sometimes they need some manual work to\nfix conflicts.  It is appreciated if the contributor fixes these conflicts when\nasked.\n\nIt is advisable to push your changes to your fork on a branch-per-pull request,\nrather than pushing to the `master` branch; the reason is explained below.\n\nPlease ensure that Git is configured properly with your name (it doesn't really\nmatter if it is your legal name or a nickname, but it should be enough to credit\nyou) and a valid email address.  There's no need to add `Signed-off-by` lines,\neven though it's fine to send commits with them.\n\nIf a change is requested in a pull request, you have two choices:\n\n - *Reply asking for clarification.*  Maybe the intentions were not clear enough,\nand whoever asked for changes didn't fully understand what you were trying to\nachieve\n - *Fix the issue.*  When fixing issues found in pull requests, *please* use\n[interactive rebases](https:\u002F\u002Fgit-scm.com\u002Fbook\u002Fen\u002Fv2\u002FGit-Tools-Rewriting-History) to\nsquash or fixup commits; don't add your fixes on top of your tree.  Do not create\nanother pull request just to accomodate the changes. After rewriting\nthe history locally, force-push to your PR branch; the PR will update automatically\nwith your changes.  Rewriting the history of development branches is fine, and\nforce-pushing them is normal and expected\n\nIt is not enforced, but it is recommended to create smaller commits. How\ncommits are split in Lwan is pretty much arbitrary, so please take a look at\nthe commit history to get an idea on how the division should be made.  Git\noffers a plethora of commands to achieve this result: the already mentioned\ninteractive rebase, the `-p` option to `git add`, and `git commit --amend`\nare good examples.\n\nCommit messages should have one line of summary (~72 chars), followed by an\nempty line, followed by paragraphs of 80-char lines explaining the change.  The\nparagraphs explaining the changes are usually not necessary if the summary\nis good enough.  Try to [write good commit messages](https:\u002F\u002Fchris.beams.io\u002Fposts\u002Fgit-commit\u002F).\n\n### Licensing\n\nLwan is licensed under the GNU General Public License, version 2, or (at your option),\nany later version.  Therefore:\n\n - Code must be either LGPLv2.1, GPLv2, a permissive \"copyfree\" license that is compatible\nwith GPLv2 (e.g. MIT, BSD 3-clause), or public domain code (e.g. CC0)\n - Although the program can be distributed and used as if it were licensed as GPLv3,\nits code must be compatible with GPLv2 as well; no new code can be licensed under versions\nof GPL newer than 2\n - Likewise, code licensed under licenses compatible with GPLv3 but\nincompatible with GPLv2 (e.g.  Apache 2) are not suitable for inclusion in\nLwan\n - Even if the license does not specify that credit should be given (e.g. CC0-licensed code),\nplease give credit to the original author for that piece of code\n - Contrary to popular belief, it is possible to use a GPL'd piece of code on a server without\nhaving to share the code for your application.  It is only when the binary of that server is\nshared that source must be available to whoever has that binary.  Merely accessing a Lwan\nserver through HTTP does not qualify as having access to the binary program that's running\non the server\n - When in doubt, don't take legal advice from a README file: please consult\na lawyer that understands free software licensing\n\n### AI and LLM use\n\nThe Lwan project will not accept any work partially or completely generated by AI tools;\nthis includes code, documentation, commit messages, bug reports, issues.\n\nPortability\n-----------\n\nWhile Lwan was written originally for Linux, it has been ported to BSD\nsystems as well.  The build system will detect the supported features\nand build support library functions as appropriate.\n\nFor instance, [epoll](https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FEpoll) has been\nimplemented on top of [kqueue](https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FKqueue), and\nLinux-only syscalls and GNU extensions have been implemented for the\nsupported systems.  [This blog post](https:\u002F\u002Ftia.mat.br\u002Fposts\u002F2018\u002F06\u002F28\u002Finclude_next_and_portability.html)\nexplains the details and how `#include_next` is used.\n\nPerformance\n-----------\n\nIt can achieve good performance, yielding about **320000 requests\u002Fsecond**\non a Core i7 laptop for requests without disk access, and without pipelining.\n\nWhen disk I\u002FO is required, for files up to 16KiB, it yields about\n**290000 requests\u002Fsecond**; for larger files, this drops to **185000\nrequests\u002Fsecond**, which isn't too shabby either.\n\nThese results, of course, with keep-alive connections, and with weighttp\nrunning on the same machine (and thus using resources that could be used\nfor the webserver itself).\n\nWithout keep-alive, these numbers drop around 6-fold.\n\nIRC Channel\n-----------\n\nThere is an IRC channel (`#lwan`) on [Libera](https:\u002F\u002Flibera.chat). A\nstandard IRC client can be used.\n\nLwan in the wild\n----------------\n\nHere's a non-definitive list of third-party stuff that uses Lwan and have\nbeen seen in the wild.  *If you see mentions of Lwan in the media or\nacademia, however small it might be, please contact the author! It'll make\nher day!*\n\n* [This project uses Cython and Lwan](https:\u002F\u002Fwww.erp5.com\u002FNXD-Blog.Multicore.Python.HTTP.Server) to make it possible to write handlers in Python.\n* [An experimental version of Node.js using Lwan](https:\u002F\u002Fgithub.com\u002Fraadad\u002Fnode-lwan) as its HTTP server is maintained by [@raadad](https:\u002F\u002Fgithub.com\u002Fraadad).\n* The beginnings of a C++11 [web framework](https:\u002F\u002Fgithub.com\u002Fvileda\u002Fwfpp) based on Lwan written by [@vileda](https:\u002F\u002Fgithub.com\u002Fvileda).\n* A more complete C++14 [web framework](https:\u002F\u002Fgithub.com\u002Fmatt-42\u002Fsilicon) by [@matt-42](https:\u002F\u002Fgithub.com\u002Fmatt-42) offers Lwan as one of its backends.\n* A [word ladder sample program](https:\u002F\u002Fgithub.com\u002Fsjnam\u002Flwan-sgb-ladders) by [@sjnam](https:\u002F\u002Fgithub.com\u002Fsjnam). [Demo](http:\u002F\u002Ftbcoe.ddns.net\u002Fsgb\u002Fladders?start=chaos&goal=order).\n* A [Shodan search](https:\u002F\u002Fwww.shodan.io\u002Fsearch?query=server%3A+lwan) listing some brave souls that expose Lwan to the public internet.\n* This [write-up shows the use of Lwan on a Capture the Flag competition](https:\u002F\u002Fmedium.com\u002Ffeedzaitech\u002Fpixels-camp-ctf-challenge-qualifiers-writeup-ac661f4af96a).\n\nSome other distribution channels were made available as well:\n\n* Container images are available from the [GitHub Container Registry](https:\u002F\u002Fghcr.io\u002Flpereira\u002Flwan).  [More information below](#container-images).\n* A `Dockerfile` is maintained by [@jaxgeller](https:\u002F\u002Fgithub.com\u002Fjaxgeller), and is [available from the Docker registry](https:\u002F\u002Fhub.docker.com\u002Fr\u002Fjaxgeller\u002Flwan\u002F).\n* A buildpack for Heroku is maintained by [@bherrera](https:\u002F\u002Fgithub.com\u002Fbherrera), and is [available from its repo](https:\u002F\u002Fgithub.com\u002Fbherrera\u002Fheroku-buildpack-lwan).\n* Lwan is also available as a package in [Biicode](http:\u002F\u002Fdocs.biicode.com\u002Fc++\u002Fexamples\u002Flwan.html).\n* It's also available in some GNU\u002FLinux distributions:\n    * [Arch Linux](https:\u002F\u002Faur.archlinux.org\u002Fpackages\u002Flwan-git\u002F)\n    * [Ubuntu](https:\u002F\u002Flaunchpad.net\u002Flwan-unofficial)\n    * [Alpine Linux](https:\u002F\u002Fpkgs.alpinelinux.org\u002Fpackage\u002Fedge\u002Ftesting\u002Fx86_64\u002Flwan)\n    * [NixOS](https:\u002F\u002Fnixos.org\u002Fnixos\u002Fpackages.html#lwan)\n* It's also available as a package for the [Nanos unikernel](https:\u002F\u002Fgithub.com\u002Fnanovms\u002Fnanos).\n\nLwan has been also used as a benchmark:\n\n* [Raphael Javaux's master thesis](https:\u002F\u002Fgithub.com\u002FRaphaelJ\u002Fmaster-thesis) cites Lwan in chapter 5 (\"Performance Analysis\").\n* Lwan is used as a benchmark by the [PyParallel](http:\u002F\u002Fpyparallel.org\u002F) [author](https:\u002F\u002Fwww.reddit.com\u002Fr\u002Fprogramming\u002Fcomments\u002F3jhv80\u002Fpyparallel_an_experimental_proofofconcept_fork_of\u002Fcur4tut).\n* [Kong](https:\u002F\u002Fgetkong.org\u002Fabout\u002Fbenchmark\u002F) uses Lwan as the [backend API](https:\u002F\u002Fgist.github.com\u002Fmontanaflynn\u002F01376991f0a3ad07059c) in its benchmark.\n* [TechEmpower Framework benchmarks](https:\u002F\u002Fwww.techempower.com\u002Fbenchmarks\u002F#section=data-r10&hw=peak&test=json) feature Lwan since round 10.\n* [KrakenD](http:\u002F\u002Fwww.krakend.io) used Lwan for the REST API in all official [benchmarks](http:\u002F\u002Fwww.krakend.io\u002Fdocs\u002Fbenchmarks\u002Faws\u002F)\n* [Effective System Call Aggregation (ESCA)](https:\u002F\u002Fgithub.com\u002Feecheng87\u002FESCA) project uses Lwan as one of the benchmarks; they claim that Lwan throughput improved by about 30% with their system call batching approach.\n\nMentions in academic journals:\n\n* [A dynamic predictive race detector for C\u002FC++ programs (in English, published 2017)](https:\u002F\u002Flink.springer.com\u002Farticle\u002F10.1007\u002Fs11227-017-1996-8) uses Lwan as a \"real world example\".\n* [High-precision Data Race Detection Method for Large Scale Programs (in Chinese, published 2021)](http:\u002F\u002Fwww.jos.org.cn\u002Fjos\u002Farticle\u002Fabstract\u002F6260) also uses Lwan as one of the case studies.\n* [AGE: Automatic Performance Evaluation of API Gateways (in English, published 2023)](https:\u002F\u002Fwww.computer.org\u002Fcsdl\u002Fproceedings-article\u002Fiscc\u002F2023\u002F10218286\u002F1PYLvz6ihBm) mentions Lwan as part of its usage in the KrakenD benchmarks.\n* [Canary: Practical Static Detection of Inter-thread Value-Flow Bugs](https:\u002F\u002Frainoftime.github.io\u002Ffiles\u002FPLDI21Canary.pdf) used Lwan as one of the pieces of software that have been analyzed.\n* [Uma análise comparativa de ferramentas de análise estática para deteção de erros de memória](https:\u002F\u002Farxiv.org\u002Fpdf\u002F1807.08015.pdf) used Lwan as one of the evaluation softwares.\n\nMentions in magazines:\n\n* [Linux-Magazin (Germany) mentions Lwan in their December\u002F2021 issue](https:\u002F\u002Fwww.linux-magazin.de\u002Fausgaben\u002F2021\u002F12\u002Ftooltipps\u002F)\n* [Raspberry Pi Geek (Germany) mentions Lwan in their October\u002FNovember 2022 issue](https:\u002F\u002Fwww.discountmags.com\u002Fmagazine\u002Fraspberry-pi-geek-october-6-2022-digital\u002Fin-this-issue\u002F17)\n* [LinuxUser (Germany) mentions Lwan in their October 2022 issue](https:\u002F\u002Fwww.linux-community.de\u002Fausgaben\u002Flinuxuser\u002F2022\u002F10\u002Faktuelle-software-im-kurztest-30\u002F)\n\nMentions in books:\n\n* [The Ascetic Programmer](https:\u002F\u002Fasceticprogrammer.info\u002Fbook) has a paragraph about Lwan.\n\nSome talks mentioning Lwan:\n\n* [Talk about Lwan](https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=cttY9FdCzUE) at Polyconf16, given by [@lpereira](https:\u002F\u002Fgithub.com\u002Flpereira).\n* This [talk about Iron](https:\u002F\u002Fmichaelsproul.github.io\u002Firon-talk\u002F), a framework for Rust, mentions Lwan as an *insane C thing*.\n* [University seminar presentation](https:\u002F\u002Fgithub.com\u002Fcu-data-engineering-s15\u002Fsyllabus\u002Fblob\u002Fmaster\u002Fstudent_lectures\u002FLWAN.pdf) about Lwan.\n* This [presentation about Sailor web framework](http:\u002F\u002Fwww.slideshare.net\u002FEtieneDalcol\u002Fweb-development-with-lua-bulgaria-web-summit) mentions Lwan.\n* [Performance and Scale @ Istio Service Mesh](https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=G4F5aRFEXnU), presented at KubeCon Europe 2018, mentions (at the 7:30 mark) that Lwan is used on the server side for testing due to its performance and robustness.\n* [A multi-core Python HTTP server (much) faster than Go (spoiler: Cython)](https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=mZ9cXOH6NYk) presented at PyConFR 2018 by J.-P. Smets mentions [Nexedi's work](https:\u002F\u002Fwww.nexedi.com\u002FNXD-Blog.Multicore.Python.HTTP.Server) on using Lwan as a backend for Python services with Cython.\n\nNot really third-party, but alas:\n\n* The [author's blog](http:\u002F\u002Ftia.mat.br).\n* The [project's webpage](http:\u002F\u002Flwan.ws).\n\nContainer Images\n----------------\n\nLwan container images are available at\n[ghcr.io\u002Flpereira\u002Flwan](https:\u002F\u002Fghcr.io\u002Flpereira\u002Flwan).  Container runtimes\nlike [Docker](https:\u002F\u002Fdocker.io) or [Podman](https:\u002F\u002Fpodman.io) may be used\nto build and run Lwan in a container.\n\n### Pull lwan images from GHCR\nContainer images are tagged with release version numbers, so a specific version of Lwan can be pulled.\n\n    # latest version\n    docker pull ghcr.io\u002Flpereira\u002Flwan:latest\n    # pull a specific version\n    docker pull ghcr.io\u002Flpereira\u002Flwan:v0.3\n\n### Build images locally\nClone the repository and use `Containerfile` (Dockerfile) to build Lwan with all optional dependencies enabled.\n\n    podman build -t lwan .\n\n### Run your image\nThe image expects to find static content at `\u002Fwwwroot`, so a volume containing your content can be mounted.\n\n    docker run --rm -p 8080:8080 -v .\u002Fwww:\u002Fwwwroot lwan\n\nTo bring your own `lwan.conf`, simply mount it at `\u002Flwan.conf`.\n\n    podman run --rm -p 8080:8080 -v .\u002Flwan.conf:\u002Flwan.conf lwan\n\n### Run image with socket activation on a Linux host with Podman\n\nPodman supports [socket activation of containers](https:\u002F\u002Fgithub.com\u002Fcontainers\u002Fpodman\u002Fblob\u002Fmain\u002Fdocs\u002Ftutorials\u002Fsocket_activation.md#socket-activation-of-containers).\nThis example shows how to run lwan with socket activation and Podman on a Linux host.\n\nRequirements: Podman version 4.5.0 or higher.\n\n1. Create user _test_\n   ```\n   sudo useradd test\n   ```\n2. Start a login shell for the user _test_\n   ```\n   sudo machinectl shell test@\n   ```\n3. Clone the lwan git repository to _~\u002Flwan_\n4. Build the image\n   ```\n   podman build -t lwan ~\u002Flwan\n   ```\n5. Create directories\n   ```\n   mkdir -p ~\u002F.config\u002Fcontainers\u002Fsystemd\n   mkdir -p ~\u002F.config\u002Fsystemd\u002Fuser\n   ```\n6. Create the file _~\u002Flwan.conf_ with the contents\n   ```\n   listener systemd:my.socket\n   site {\n       serve_files \u002F {\n               path = \u002Fweb\n       }\n   }\n   ```\n7. Create the file _~\u002F.config\u002Fsystemd\u002Fuser\u002Fmy.socket_ with the contents\n   ```\n   [Socket]\n   ListenStream=8080\n   ```\n8. Create the file _~\u002F.config\u002Fcontainers\u002Fsystemd\u002Fmy.container_ with the contents\n   ```\n   [Unit]\n   After=my.socket\n   Requires=my.socket\n\n   [Container]\n   Network=none\n   Image=localhost\u002Flwan\n   Volume=\u002Fhome\u002Ftest\u002Flwan.conf:\u002Flwan.conf:Z\n   Volume=\u002Fhome\u002Ftest\u002Fweb:\u002Fweb:Z\n   ```\n   The option `:Z` is needed on SELinux systems.\n   As __lwan__ only needs to communicate over the socket-activated socket, it's possible to use `Network=none`. See the article [How to limit container privilege with socket activation](https:\u002F\u002Fwww.redhat.com\u002Fsysadmin\u002Fsocket-activation-podman).\n9. Create the web directory and an example text file\n   ```\n   mkdir ~\u002Fweb\n   echo hello > ~\u002Fweb\u002Ffile.txt\n   ```\n10. Reload systemd configuration\n    ```\n    systemctl --user daemon-reload\n    ```\n11. Start the socket\n    ```\n    systemctl --user start my.socket\n    ```\n12. Download the example text file from the lwan web server\n    ```\n    $ curl localhost:8080\u002Ffile.txt\n    hello\n    ```\n\nLwan quotes\n-----------\n\nThese are some of the quotes found in the wild about Lwan.  They're presented\nin no particular order.  Contributions are appreciated:\n\n> \"Lwan is like a classic, according to the definition given by Italian\n> -- writer Italo Calvino: you can read it again and again\" [Antonio\n> Piccolboni](https:\u002F\u002Fasceticprogrammer.info\u002Fbook)\n\n> \"I read lwan's source code. Especially, the part of using coroutine was\n> very impressive and it was more interesting than a good novel.  Thank you\n> for that.\" --\n> [@patagonia](https:\u002F\u002Ftwitter.com\u002Fhakman314\u002Fstatus\u002F996617563470680064)\n\n> \"For the server side, we're using Lwan, which can handle 100k+ reqs\u002Fs.\n> It's supposed to be super robust and it's working well for us.\" --\n> [@fawadkhaliq](https:\u002F\u002Ftwitter.com\u002Ffawadkhaliq)\n\n> \"Insane C thing\" -- [Michael\n> Sproul](https:\u002F\u002Fmichaelsproul.github.io\u002Firon-talk\u002F)\n\n> \"The best performer is LWAN, a newcomer\" -- [InfoQ](https:\u002F\u002Fwww.infoq.com\u002Fnews\u002F2015\u002F04\u002Fweb-frameworks-benchmark-2015\u002F)\n\n> \"I've never had a chance to thank you for Lwan.  It inspired me a lot to\n> develop [Zewo](https:\u002F\u002Fgithub.com\u002FZewo\u002FZero)\" --\n> [@paulofariarl](https:\u002F\u002Ftwitter.com\u002Fpaulofariarl\u002Fstatus\u002F707926806373003265)\n\n> \"Let me say that lwan is a thing of beauty.  I got sucked into reading the\n> source code for pure entertainment, it's so good.  *high five*\" --\n> [@kwilczynski](https:\u002F\u002Ftwitter.com\u002Fkwilczynski\u002Fstatus\u002F692881117003644929)\n\n> \"mad science\" -- [jwz](https:\u002F\u002Fjwz.org\u002Fb\u002FyjFZ)\n\n> \"Nice work with Lwan! I haven't looked _that_ carefully yet but so far I\n> like what I saw.  You definitely have the right ideas.\" --\n> [@thinkingfish](https:\u002F\u002Ftwitter.com\u002Fthinkingfish\u002Fstatus\u002F521574267612196864)\n\n> \"Lwan is a work of art. Every time I read through it, I am almost always\n> awe-struck.\" --\n> [@neurodrone](https:\u002F\u002Ftwitter.com\u002Fneurodrone\u002Fstatus\u002F359296080283840513)\n\n> \"For Round 10, Lwan has taken the crown\" --\n> [TechEmpower](https:\u002F\u002Fwww.techempower.com\u002Fblog\u002F2015\u002F04\u002F21\u002Fframework-benchmarks-round-10\u002F)\n\n> \"Jeez this is amazing. Just end to end, rock solid engineering. (...) But that sells this work short.\"\n> [kjeetgill](https:\u002F\u002Fnews.ycombinator.com\u002Fitem?id=17548983)\n\n> \"I am only a spare time C coder myself and was surprised that I can follow the code. Nice!\"\n> [cntlzw](https:\u002F\u002Fnews.ycombinator.com\u002Fitem?id=17550319)\n\n> \"Impressive all and all, even more for being written in (grokkable!) C. Nice work.\"\n> [tpaschalis](https:\u002F\u002Fnews.ycombinator.com\u002Fitem?id=17550961)\n\n> \"LWAN was a complete failure\" [dermetfan](http:\u002F\u002Fdermetfan.net\u002Fposts\u002Fzig-with-c-web-servers.html)\n","Lwan 是一个实验性的、可扩展的高性能HTTP服务器。它使用C语言编写，具备高并发处理能力和低资源消耗的特点，支持多种压缩算法（如Brotli、ZSTD）以及TLS加密。Lwan 适用于需要高效处理大量请求的Web服务场景，例如静态文件托管、API网关等。此外，通过集成Lua脚本引擎，Lwan 还能为开发者提供灵活的应用开发能力。项目遵循GPLv2许可证发布，拥有活跃的社区支持和详细的构建指南。",2,"2026-06-11 03:07:13","top_language"]