This is amazing work!
What appeals to me is the Lua integration. I'd love for a Lua application server to become widely available. The http servers out there now don't really fit the bill for (Xavante, Luvit for example).
> Small disk footprint: x86-64 executable has 110KiB (~52KiB if packed with UPX)
> No copies between kernel and userland for files larger than 16KiB
Looks perfect for embedded stuff (and generally impressive).
It needs a separate TLS terminator, though.
TLS is hard given the current architecture, but I've been investigating kTLS, so maybe it'll happen someday. I would appreciate help on this front, though.
I don't know how portable it is yet though
I'd like to use BearSSL, which is a nice, clean implementation of TLS, makes no memory allocation (so it fits Lwan architecture very well)... but only for the initial connection handshake; I'd then hand over TLS to the Linux kernel with kTLS (https://netdevconf.org/1.2/papers/ktls.pdf). However, there's not much beyond some tests in that front.
For static files, I ended up using the aio syscalls in Linux as they work quite well for reading files on a supporting filesystem (mainly ext4, xfs). I also couldn't figure out how to guarantee that sendfile doesn't block, but didn't look into it too deeply.
Very nice project, seems very mature.
I don't think there's a way to guarantee that sendfile() won't block on Linux. On FreeBSD, you can specify a "go as much as you can without blocking, and then return" flag; Linux doesn't have a flags parameter for sendfile() (maybe it needs a sendfile2() syscall with that?).
You can maybe use readahead() and keep the file open, but that may also block as it does its work behind the scenes. A thread just for this kind of blocking calls would be nice. But even then, this doesn't seem sufficient; so you might want to use multiplex sendfile() calls in bursts.
Also, yeah, Linux aio syscalls are interesting, but they're quite tricky to use and not very portable. I'll check out your project, thanks for linking to it -- should be useful reading material while I learn Rust.
Which ever way you do it, it's so easy to end up blocking without realizing, strace is great here to see how much time you actually spend in the kernel.
How much the aio calls block is highly filesystem-dependent. I was hoping to use btrfs for my project, but when testing aio support, I saw my demo was spending around 80% in io_submit and less than 10% in epoll_wait, where as with ext4 it was more than the opposite.
Interesting stuff, and doing it in Rust was a pleasant experience I must say, although if you are trying hard to avoid copying and allocating, the borrow checker can get a bit naggy. Worth it though IMO, and should improve in the future.
Registered I/O (RIO) or I/O Completion Ports (IOCP) in Windows seems to be better in this regard, but I don't have a lot of experience in that platform. Trent Nelson, from the PyParallel project, has a few talks about it; it's quite neat.
Am I just blind or is Lwan missing here:
The reason struct typedefs are avoided is because I prefer to be explicit (the "Zen of Python" isn't just for Python!). It's also common practice in most of the projects I work on, and it's not much of an issue for me as far as a little bit of typing goes: I'm a fast typist.
The Linux style suggests avoiding it: https://www.kernel.org/doc/html/v4.10/process/coding-style.h...
However, as I said before, this is an open source project; if you have an itch, you can scratch it. PRs are open. :)
EDIT: touché! i agree!
A long time ago I benchmarked it, and Lwan came out on top, performance-wise. However, performance isn't the only thing that matters in a piece of software like this.
H2O is a great project; in fact, the authors were tightly involved in the definition of HTTP/2.0, and it is one of the first servers to support this version of the protocol. It is well written, and easy to grok the code. Some of its byproducts include a nice (and reusable) HTTP parser library, and clever tool named qrintf (that pre-parses the format string passed to printf()-family functions and generates optimal code for that).
Also, there's a company behind it -- Lwan is just a toy I work on the weekends when I feel like it.
My favorite kinds of projects are ones that end in a (relatively short) punchline, but required tons of time investment.