Lwan is a high-performance & scalable web server.

View it on GitHub

Requests per second vs. number of concurrent connections? Hello, World! (C) 100B file Hello, World! (LuaJIT) 32KiB file

Lwan was until recently just a personal research effort that focused mostly on building a solid infrastructure for a lightweight and speedy web server.

With its low disk and memory footprints, it's suitable to be used from embedded devices to robust servers. Both static and dynamic content can be served, as it can also be used as a library. Dynamic content can be generated by code written in either C or Lua.

Its simple architecture and tiny source code guarantees the entire code base can be easily grokked.

Connections are handled individually by coroutines, which are transparently and efficiently juggled by a per-CPU cooperative scheduler, giving the illusion of blocking I/O to handlers.

Resource management is also greatly simplified with coroutines: whenever a client connection is closed, memory is automatically reclaimed, files are automatically closed, references are automatically decremented. This provides more predictability than most garbage collectors, helping keep latencies low while being almost as easy to use.

Lwan is a work of art. Every time I read through it, I am almost always awe-struck. @neurodrone

What is deadlier than bullet points?

Low memory footprint (~500KiB for 10k idle connections)

(~500KiB for 10k idle connections) Small disk footprint : x86-64 executable has 110KiB (~52KiB if packed with UPX)

: x86-64 executable has 110KiB (~52KiB if packed with UPX) Minimal memory allocations & copies

& copies Minimal system calls

Hand-written HTTP request parser

Static file serving uses the most efficient way according to file size No copies between kernel and userland for files larger than 16KiB Smaller files are sent using scatter-gather I/O Header overhead is considered before considering deflate compression Precompressed files are sent if the client asks for gzip compression

Mostly wait-free multi-threaded design One thread accepts connections, one I/O thread per logical CPU handles them Coroutines makes asynchronous I/O a breeze in C Supports Linux , FreeBSD and macOS Purpose-built I/O loop

multi-threaded design Efficient, Guava-inspired loading cache . Used for: Directory listing File information (size, last modified date, MIME type, etc) Compressed file contents Compiled Lua scripts

. Used for: Request rewriting support based on Lua pattern matching syntax Lua scripts can be executed to control the rewriting behavior as well

pattern matching syntax Diminute codebase with roughly 10000 lines of C99 code

with roughly 10000 lines of C99 code Efficient Mustache templating engine Used internally for directory listing & error messages (can be loaded from a file) Available for user-built handlers

Easy to use API to create web applications or extend the web server Example: Freegeoip reimplementation, which performs better than the Go version and is roughly the same amount of lines of code. The live version has been up for years, and serves millions of requests per day, with a RSS of roughtly 5MiB. Handlers can be written in C and Lua

to create web applications or extend the web server Supports rebimboca da parafuseta

No-nonsense configuration file syntax

file syntax Supports a subset of HTTP/1.0 and HTTP/1.1 Support for keep-alive connections Support for pipelined requests

WebSockets

PROXY protocol support Useful for TLS terminators such as Hitch

systemd socket activation

IPv6 ready

Buildbots checks every commit Code is statically analyzed by Clang Static Analyzer Debug & Release builds Various platforms: Arch Linux , always up-to-date FreeBSD 10 and 11, courtesy of @koobs macOS High Sierra Test suite written in Python tests the server as a black box Test suite is executed with both Undefined Behavior and Address sanitizers Request parser continuously fuzzed with oss-fuzz



Nice work with Lwan! I haven't looked that carefully yet but so far I like what I saw. You definitely have the right ideas. @thinkingfish

How to build, setup, and run

API example

The README.md file in the repository will list build dependencies, commands, and set up information.

Lwan isn't just a simple static file server: it can be used as a library to build web services on top of it. In fact, the static file server isn't a special case: it just uses the same APIs that are available when Lwan is used as a library.

The Hello, World! handler, shown below, was used to generate the above chart. While the API is simple, Lwan isn't a framework, so not everything is built-in; some features will actually require changes to the internals, as they're very tightly-coupled.

#include "lwan.h" LWAN_HANDLER(hello_world) { static const char message[] = "Hello, World!"; response->mime_type = "text/plain"; lwan_strbuf_set_static(response->buffer, message, sizeof(message) - 1); return HTTP_OK; } int main(void) { const struct lwan_url_map default_map[] = { { .prefix = "/", .handler = LWAN_HANDLER_REF(hello_world) }, { .prefix = NULL } }; struct lwan l; lwan_init(&l); lwan_set_url_map(&l, default_map); lwan_main_loop(&l); lwan_shutdown(&l); return 0; }

Lua is also available to write handlers, but support is still pretty rough and not everything available to C handlers is available to Lua scripts.

However, it's enough to run some frameworks, such as Sailor. To whet your appetite, an improvement of the program above could be easily written in Lua like so:

function handle_get_hello(req) local name = req:query_param[[name]] if name then req:set_response("Hello, " .. name .. "!") else req:set_response("Hello, World!") end end

Lwan infers what method and endpoint by the function name, so there's no need to specify them with something akin to an lwan_url_map_t array. In this case, issuing a GET /hello will be sufficient to run this handler.

An in-depth view of the C API can be seen in this article, but your best bet would be looking throughout the "samples" or "tests" directories.

One of the samples shipped with Lwan is the "clock" sample, which renders the current time as a never-ending animated GIF file, streamed using chunked-encoding. The following Dali Clock (other styles available) shows the current time in UTC, and is rendered completely in the server-side; no JavaScript is required in the client-side to display it:

FAQ

Is it secure?

How is it possible to write a “hand-crafted HTTP parser” in 2014, and not have at least an H2 for security? @gnat

Lwan has a 0.0 defect density as reported by Coverity. No top CWE 25 defects were found by this tool.

as reported by Coverity. No top CWE 25 defects were found by this tool. Every commit is checked with Clang Static Analyzer. As far as we can tell, only false positives remain in the code; feel free to check for yourself.

It produces no warnings when executed on Valgrind Memcheck (with debug builds: release builds disable some things that Memcheck relies on to keep sanity when coroutines switches stacks.)

No warnings are produced while building Lwan, even though options to enable extra warnings are always passed.

are produced while building Lwan, even though options to enable extra warnings are always passed. HTTP request parser, configuration file parser, and pattern matcher are continually fuzzed by oss-fuzz (thanks!). Some bugs have been found and were promptly fixed.

All tests are performed automatically, with both Address Sanitizer and Undefined Behavior Sanitizer; coverage is still on the low side, though.

We're not security experts, by any means. Having said that, here are some facts:

So, to answer the question: we have no idea. We were dead serious when we said we're not security experts. But care is being taken to at least find the most mundane sources of vulnerabilities and fix them before they hit the source tree.

There's nothing stopping you from exploiting Lwan, however. We would specially appreciate a public writeup about the bug; we'll even pay you a drink if we ever meet in person.

It's interesting how some of the tricks you used for performance also made the code less "risky". For example all the parsing is just setting pointers, no allocation of new buffers, so there isn't much opportunity for memory corruption, and coro_defer makes use-after-frees pretty much impossible. immerse

A web server is rarely the bottleneck. Why not just use Nginx or Apache?

If you're asking this question, then you probably should. These web servers are well known, stable, compatible with almost everything under the sun. People depend on them. Breaking their behavior will most likely make some system administrators very upset. On the other hand, few (if any) people use this server, so it is possible to innovate without guilt.

Where are the benchmarks?

For Round 10, Lwan has taken the crown. TechEmpower Blog

Lwan is in the 10th round of TechEmpower's web framework benchmarks. This independent work tests a large number of frameworks and platforms against a set of tests common to web applications, such asand

Could you share some details on how exactly Lwan is this fast?

A good place to start are these blog posts . The code is fairly small as well, so lots of things can be inferred from reading it. Also, there's a rather lengthy post that explains the life of a HTTP request from Lwan's viewpoint, which gives lots of insights.

What's the license?

GNU GPLv2+ , plus a few other bits and pieces licensed under other permissive stuff. Refer to each file for their respective credits and legalese. It might move to LGPLv2+ (with a static linking exception) in the future, though.

What does Lwan mean?

Nobody knows. Tell us when you find out! Someone suggested a recursive bacronym , "Lwan Will Annihilate Node.js", which is funny because this is very unlikely to ever happen.

Eight years is a long time for a project this small!

This isn't a question, but, yes, it is a long time. Keep in mind that this has been written exclusively on the spare time of a single developer, who works full-time on a real job. Also consider that this was exclusively a research project: the goal was not to write a web server, but to learn while finding novel ways to implement certain things.

Is there a stable release?

There's just one release: the current. This might or might not change in the future.

Is anyone using this server?

Please refer to the GitHub repository for a list of Lwan servers spotted in the wild.

Does it support HTTP2, or at least TLS?

Not yet. As for TLS, Lwan supports the PROXY protocol, so a TLS terminator proxy such as Hitch can be used.

This web page looks like an advertisement for snake oil.

Lwan. Brought to you by @lafp . Powered by

That's not a question. And, yes, it's on purpose. It's a parody of the web page for a similarly-named web server. In fact, there are a few easter eggs in this web page.