userver - micro HTTP server and gawk extension
userver is a minimal HTTP server where additional options are added at compile-time. Use-cases are e.g.
- static-files-only server,
- small home HTTP server,
- gawk extension.
The HTTP protocol is very limited, just what is needed to serve requests with some support for Gopher protocol (I plan to make that optional.) and the level of HTTP implementation compares well to Gopher protocol: The client requests a file and the server sends it. userver
- is implemented as non-forking, non-threading process doing
non-blocking I/O.
- applies some chosen resource limits: E.g. it can handle only 15
parallel connections (no heavy loads expected or supported) but
that can be easily changed. Others (like path lengths) are more
difficult but I think they are good enough.
Protocol support may be enhanced in future releases.
Optional Features
Compiling userver.c without any options gives the core server that can handle only static files. This is what I would use to run it on the Internet from home assuming that the software is secure enough (statically linked, locked in a chroot(7) jail, with apparmor(7) on it, behind a firewall, if possible on a minimal Linux).
Depending on the particular use-case, additional features can be added at compile-time. See the manpage to understand userver's functionality and what the options do (and what not).
- CGI script (
-DWITH_CGI
). Add capability to run CGI scripts to the server. - gawk extension (
-DAS_EXTENSION
). At some point I asked myself, if it is possible to use the protocol handling part from userver to do the HTTP stuff and let a gawk script handle the request. It was surprisingly easy. See server-example.awk to get an impression how it works. - Directory listings (
-DWITH_DIR_LISTING
). This enables the server to create simple directory listings if a request points to a directory and the configured default file is not there. - Request regexp matching (
-DWITH_REGEXP
). Do you know tftpd-hpa? It also doesn't have any features beyond basic file transfer (it's TFTP) but it implements matching requests against regular expressions to allow some control of requests. - File uploads (
-DWITH_UPLOADS
). When I browse my own files I often would like to add or correct things. But from the browser client. Not by changing to a terminal, logging in to the server and editing the file there. So I added support for PUT requests giving upload capabilities equivalent to "tftp-on-tcp". - Identity checks (
-DWITH_LOCAL_IDENTITY
). This makes userver check the uid of the connection user and allow very simple access and permission checks. This feature is only implemented as IPv4 and incomplete without IPv6. - Root permission on startup (
-DWITH_START_AS_ROOT
). userver was made with the idea that is is started from a normal user account, accepting that it cannot bind to a privileged TCP port. Fine for me, I don't mind add:1080
to the URL, but again, it all depends. This option adds the ability to change the user id before entering the I/O loop. - Run as root (
WITH_NO_UID_CHECK
). This disables the user id check and makes it possible to run userver with uid 0.
Downloads
There are Debian archives
and the source tgz. The Debian archives install a default userver in /usr/bin and the gawk extension, source and makefile userver.make in /usr/lib/userver. I understand that distributing the source is quite unusual but compiling your own version is a central point.
In case you never compiled before, you need (in Debian terms)
$ sudo apt-get install gcc binutils make
Add ctags is you like. Options can be added in the userver.make and
$ make
creates a new userver and userver.so.
Run
$ userver -V userver 1.0.0-beta1 -DWITH_DIR_LISTING -DWITH_LOCAL_IDENTITY -DWITH_REGEX_MAP -DWITH_START_AS_ROOT -DWITH_UPLOADS
to check the activated options in the pre-compiled binary.
Compiling
There is only userver.c to compile and that is simple to do:
$ gcc -o userver -DWITH_DIR_LISTING -DVERSION=\"1.2.3\" userver.c
The version number is optional and undefined
if not set. Verify
that you made the right combination:
$ ./userver -V userver 1.2.3 -DWITH_DIR_LISTING
-V
lists all compiled options. You can add the -Wall
option to
gcc and it should return two types of warnings:
- There are functions defined but not used and I have to correct that
but it does not hurt.
- There may be warnings about "truncated writing" in snprintf
calls. They are difficult to address (with something else that
ever-growing buffers) and my current stance is, that I don't care.
The output is truncated (which I don't see as a big issue in this
particular situation) but it will not overflow (which would be
important). The included makefile userver.make sets the
-Wno-format-truncation
option to suppress these messages.
Instead of calling the compiler directly you can also set your desired
options in userver.make and run make -f userver.make userver
.
userver.make can also produce the gawk extension. If you prefer to create it from the command line you would run
$ gcc -o userver.so your-options-here -DAS_EXTENSION userver.c -shared
Code Maturity
This is a beta release. I use userver at home for small servers running on Raspberry Pis for this and that. I expect to find bugs (even stupid ones) and will fix then and add new features as I find them. I would not use this release for prime-time on Internet.
Wishlist
I already have some more options on my "things to add" list:
- More HTTP support, like If-Modified-Since, Keep-alive.
- IPv6
- SSL
- Make Gopher or HTTP protocol a choice.
- Make some code documentation to allow reviews by others.
Code Reviews
userver comes in a single source file and #ifdef
's are scattered
all over it, which may make it somewhat difficult to read. The script
spp pre-processes the file based on -D
options and prints
the result, removing unselected options. E.g.
spp -DWITH_DIR_LISTING userver.c
prints the base code plus the source required for WITH_DIR_LISTING
.
There are two interesting options.
- -c
- produce source that can be compiled without giving any
-D
options to gcc. (The output is similar than to without -c but all#ifdef
's are removed.) - -s
- print only the source of the given options. Helpful to see how a
particular option is implemented.
The script is not perfect and adapted to my style of source formatting, which is really not main-stream.
Background
userver started as HTTP server for static files from a simple question: Can I can make a web server with a code basis that I trust enough to use it as public server from home? Central aspects would be
- Only minimal HTTP/1.0 support required, just what is needed to
transfer files. E.g. no authentication, no SSL, no ... you name
it.
- Only static files. No CGI.
A first version was ready with approx. 1500 lines of code (30 kB) and then the usual thing happened. Features were added to pursue another goal: What would I want from a server for private, home-only (not public) use? Functionality is all about the use-case. Sometimes static files are enough and sometime CGI is needed. But I also don't want to have different code bases. I noticed that I can add all kind of features to userver and that's fine as long as there is a compile-time option for it. I can have the core static-files-only version at any time I like.
Why is this important, why not simply use an off-the-shelf product? They are often very "feature rich". And you have to take care about dependencies and their interactions, more features mean more possibilities for misconfiguration and more lines of code often come with more bugs. There are long-running projects like apache with less bugs but they are simply too large for my taste.
Then, as far as I know, they can't be used as gawk extension. And finally, not to forget, there's the fun to code.