From 1a6b171b184381fcda3eab5bcad0bac1a25e8a61 Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Fri, 1 May 2020 11:15:40 +0300 Subject: initial commit --- ewww.md | 149 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 ewww.md diff --git a/ewww.md b/ewww.md new file mode 100644 index 0000000..9214753 --- /dev/null +++ b/ewww.md @@ -0,0 +1,149 @@ +# Introduction + +This is a web server for static sites. It aims to be simple code, +simple to configure, simple to keep running, and fast. + +## Use cases + +* I have files in a directory, and a domain name pointing at the host. + I want to serve the files using HTTPS. I want the TLS certificate to + come from Let's Encrypt, and get renewed automatically. + +* Same, but I have multiple domain names and each should serve from + different directories and potentially have their own certificates. + +* Same, but some of the domain names are aliases for each other, and + web clients should be redirected to the main one. + + +# Requirements + +These are main, highlevel requirements. Detailed requirements are +expressed as _scenarios_ in the acceptance criteria chapter. + +* Fast, at least 100 requests per second over localhost, using HTTPS, + on my Thinkpad T480 laptop. A self-signed certificate is OK. +* Fast, time from starting server to having served first HTTPS request + should be at most 100 ms. +* Serves only HTTPS, except what Let's Encrypt needs to be served over + plain HTTP. + +I don't need flexibility, and I don't want to configure anything +that's not essential for this. Hardcoded assumptions are A-OK, if my +life as someone running the program is easier. + +At this point, I don't need support for `If-Modified-Since` or `ETag`. +or generating directory listings. I don't even care about MIME types +for now. Those will probably become important once I start using this +software for real, but for now I am trying to keep requirements +minimal. + + +# Architecture + +This is a thin layer on top of the Rust warp crate. It does minimal +processing for each request, and does not cache anything. + +At startup, the server is provided with a directory and it reads all +configuration files in that directory. Each configuration file looks +like this: + +~~~yaml +webroot: /srv/http/example.com +hosts: + - example.com + - www.example.com +ports: + http: 80, 8080, 8888 + https: 443, 4433 +tls-cert: /etc/letsencrypt/live/certname/fullchain.pem +tls-key: /etc/letsencrypt/live/certname/privkey.pem +~~~ + +The hosts are aliases; the first host on the list is the main one, the +others automatically redirect to it. + +The server is started as `root`, reads in the TLS private key and +cert, binds to the ports, then drops privileges to `nobody`. The +configuration specifies for each port if plain HTTP or HTTPS is +expected. + +The server automatically listens on both port 80 (http) and 443 +(https) so that it can serve the Let's Encrypt files. It only serves +the `/.well-known/` path prefix in the webroot on port 80. Everything +else gets redirected to 443. I don't think I need to serve other +ports, but it's a handy feature to have for testing, so it shall be +supported at least for testing. + +There is no "reload configuration". The server needs to be restarted. +This is good enough for me, but may not be good enough for more +serious use on sites with much traffic. Restarting should be fast. + +Only the GET and HEAD methods are supported for HTTP: this is a server +for static content only. Every other method returns an error. + + +# Acceptance criteria + +## Smoke test + +~~~scenario +given a self-signed certificate as snakeoil.pem, using key snakeoil.key +and a running server using config file smoke.yaml + +when I request GET http://example.com/ +then I am redirected to https://example.com/ + +when I request GET http://example.com/.well-known/foo +then I get status code 404 + +when I create webroot/foo +and I request GET http://example.com/.well-known/foo +then I get status code 200 +and content-type is application/octet-stream + +when I request HEAD http://example.com/.well-known/foo +then I get status code 200 + +when I request GET https://example.com/ +then I get status code 200 + +when I request HEAD https://example.com/ +then I get status code 200 + +when I request GET https://www.example.com/ +then I get redirected to https://example.com/ +~~~ + +The following config file does not specify port numbers. The test +scaffolding adds randomly chosen port numbers so that the test can run +without being root. + +~~~{#smoke.yaml .file .yaml .numberLines} +webroot: webroot +hosts: + - example.com + - www.example.com +tlscert: snakeoil.pem +tlskey: snakeoil.key +~~~ + +## Performance test + +~~~scenario +given a self-signed certificate as snakeoil.pem, using key snakeoil.key +and a running server using config file smoke.yaml +and 1000 files in webroot +when I request files under https://example.com in random order 100000 times +then I can do at least 100 requests per second +~~~ + +# Unhappy scenarios + +* not GET or HEAD + + +--- +title: Web server for static sites +bindings: webserver.yaml +... -- cgit v1.2.1