Lwan Web Server


Lwan is a high-performance & scalable web server.

View it on GitHub

Requests per second vs. number of concurrent connections?

performance-chart
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)
  • Small disk footprint: x86-64 executable has 110KiB (~52KiB if packed with UPX)
  • Minimal memory allocations & copies
  • Minimal system calls
  • Hand-written HTTP request parser
  • Static file serving uses the most efficient way according to file size
  • 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
  • Efficient, Guava-inspired loading cache. Used for:
    • Directory listing
    • File information (size, last modified date, MIME type, etc)
    • Compressed file contents
    • Compiled Lua scripts
  • Request rewriting support based on Lua pattern matching syntax
    • Lua scripts can be executed to control the rewriting behavior as well
  • Diminute codebase 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
  • Supports rebimboca da parafuseta
  • No-nonsense configuration file syntax
  • Supports a subset of HTTP/1.0 and HTTP/1.1
    • Support for keep-alive connections
    • Support for pipelined requests
  • 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 Sierra
    • Test suite written in Python tests the server as a black box
    • Test suite is executed with both Undefined Behavior and Address sanitizers
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

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

API example

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.

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
We're not security experts, by any means. Having said that, here are some facts:
  • Lwan has a 0.0 defect density 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.
  • It has been also fuzzed with Sulley, libFuzzer, and afl. One bug has been found with libFuzzer and it has been fixed.
  • All tests are performed automatically, with both Address Sanitizer and Undefined Behavior Sanitizer; coverage is still on the low side, though.

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?

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 as JSON serialization, database queries and templating.
For Round 10, Lwan has taken the crown. But we expect the other top contenders won't leave this a settled matter. TechEmpower Blog

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.

Four 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.

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.
Brought to you by @lafp. Powered by Lwan.