Skip to content

killerbat00/bhm.sh

Repository files navigation

This repository contains the code powering my personal website, https://bhm.sh.

The website is deployed as a single-file binary (written in Nim!) managed by systemd, proxied by NGINX, and using Let's Encrypt for SSL certificate management.

To serve the website, I wrote a webserver in Nim using asynchttpserver. The webserver core is responsible for identifying request destinations, assembling the correct content, and responding to the request (optionally gzipped!). For page requests, the webserver dynamically generates the resulting HTML using a simplistic token-based templating approach for HTML content.

The core of the webserver looks like this:

bhm.sh/main.nim

Lines 186 to 225 in a9b05d8

proc serve*(settings: Settings) =
echo genMsg(settings)
let
htmlContentHeader = @[("Content-Type", "text/html"), ("Content-Language", "en-US")]
server = newAsyncHttpServer()
proc handleRequest(req: Request): Future[void] {.async.} =
var res: HttpResponse
var data = TemplateData(pageTitle: sample(titles), chyron: sample(chyrons), quote: sample(quotes), htmlContentHeader: htmlContentHeader)
try:
when not defined(release):
printReqInfo(settings, req)
let route = toSeq(req.url.path.split("/"))[1 .. ^1] #always starts with `/`; discard first item
if (route.len < 1) or (route[0] == "") or (route[0] == "index"):
res = index(settings, req, data, route)
elif (route[0] in settings.files):
res = sendTemplatedFile(settings, req, route, data)
elif (req.url.path[1 .. ^1] in settings.files):
res = sendStaticFile(settings, req)
else:
res = sendTemplatedFile(settings, req, @["404"], data)
except:
logException(settings)
res = (code: Http500, content: "", headers: @[])
if res.code == Http200 and req.headers.hasKey("Accept-Encoding") and req.headers["Accept-Encoding"].contains("gzip"):
res.headers.add(("Content-Encoding", "gzip"))
let content = compress(res.content, BestSpeed)
#let uc = uncompress(content)
await req.respond(res.code, content, res.headers.newHttpHeaders)
else:
await req.respond(res.code, res.content, res.headers.newHttpHeaders)
asyncCheck server.serve(settings.port, handleRequest, settings.address, -1, settings.domain)

All of the website's content and resources are compiled directly into the resulting binary so no files are read or written during normal operation (except for the log files, and stdout; this isn't really a pro, con, or anything other than a fun fact ☺️).

Feel free to build your own with any inspiration (or imitation) sparked by the code in this repository. It's lots of fun! One of my favorite parts of building a new personal website in this style has been adding my own touch, not only in code, but in fun dynamic elements like the page title and chyron (try refreshing the page a few times to see what I mean!).

This whole concept of a single-file binary website (and much of the structure and layout!) was inspired by Jes' version (https://j3s.sh/thought/my-website-is-one-binary.html) which was the first time I saw something similar.