# Introduction Ewww 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 am 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 ~~~ ## Using POST, PUT, or DELETE fails ~~~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 POST https://example.com/ then I get status code 405 and allow is "GET HEAD" when I request PUT https://example.com/ then I get status code 405 and allow is "GET HEAD" when I request DELETE https://example.com/ then I get status code 405 and allow is "GET HEAD" ~~~ --- title: "Ewww — a Web server for static sites" author: Lars Wirzenius bindings: ewww.yaml ...