Intro ¶
I’ve been recently working on a simple static website1. I quickly noticed previewing it by browsing index.html
is far from perfect.
I needed a local server to host it. Ubuntu comes with busybox httpd by default so I decided to give it a go.
The server can host files form the current dir (-f
makes it run in foreground rather than as a daemon):
busybox httpd -f -p 8090
It worked great until I got tired with hitting F5
to reload the page. I needed hot-reloading.
Hot-reloading ¶
After some trial and error I have come up with a solution.
Once the page has loaded it should make an async request which would block in the background until a file system change is detected. The request’s call-back would simply call location.reload()
which would repeat the whole process. The only option to implement server-side scripts in busybox httpd
is CGI. We can use a CGI script to monitor the website dir for changes.
File changes detection (server-side) ¶
The below script blocks until a file change is detected2:
#!/bin/sh
watch -d -t -g -n 1 "ls -lR --full-time ../ | sha1sum"
echo "Content-Type: text/html"
echo ""
echo "OK"
The above script generates a SHA1 checksum of the file list in the parent dir (recursively) every second (the -n
switch). When the checksum changes (i.e. there was a file change) it exits (the -g
switch) and sends a success response to the client.
busybox httpd
detects CGI scripts the cgi-bin
dir located in the www root dir so I saved the above script at cgi-bin/reload.sh
Awaiting the change (client-side) ¶
The below script reloads the page when the /cgi-bin/reload.sh
call returns:
<script type="text/javascript">
fetch("/cgi-bin/reload.sh").then(() => location.reload());
</script>
Only run hot-reloading in dev ¶
Obviously we don’t want hot-reloading to be present in production. It would be great if we could only inject the client-side script in the development scenario. For that I used another CGI script. busybox httpd
can load the home page from /cgi-bin/index.cgi
given there is no index.html
in the www root and the script has 700
permissions. I renamed my index.html
to index.htm
and then made a /cgi-bin/index.cgi
script that reads the contents of index.htm
, injects the script, and returns the output to the client.
The index.cgi
script:
#!/bin/bash
echo "Content-Type: text/html"
echo ""
index=$(cat ../index.htm)
inject='
<script type="text/javascript">
fetch("/cgi-bin/reload.sh").then(() => location.reload());
</script>'
echo -e "${index/<\/title>/"</title>\n $inject"}"
And that’s it - a nice low-ceremony hack for hot-reloading without additional dependencies.