This website has been ported from Rocket to Axum, and it's been a joy.

Notable steps during the refactoring

Some MIME types needed to be handled manually. For instance, serving an SVG should have the type image/svg+xml and browsers may not render them correctly in places such as background images otherwise.

The configuration mechanism built into Rocket, it differentiates between development and deployment environment with differing security levels and other differences in the profile, is not builtin. To avoid breakage, the new application still reads the old ROCKET_* environment variables.

The Response handling changed significantly. Rocket provides a high degree of flexibility at the definition site. Your handles can have any result type, bounded by a trait, and Rocket tries lets you get away even with rather ambiguous semantics such as returning a borrowed str. Here, axum is a little stricter. Its IntoResponse type makes sure that some definition of at least a status code is present and bodies are built around streams of Bytes much more explicitly. You get less surprise copies of data but this necessitated a bit of implementation rework. For instance, the internal representation of the pre-rendered in-memory cache now also utilized Bytes directly.

On a related note, from a performance perspective it would be appropriate to have AsyncRead here. The network queue provides buffers which are to be filled with fragments of the headers and response bodies. Techniques such as pre-registered IO-uring buffers will not work when the handler returns some new allocation anyways. And having the user dynamically and freely allocate from such a precious resource is a huge DOS risk.

The old binary still compiled perfectly, but a dependency update revealed that version resolution does not respect the Rust Version. I've gone ahead and bumped to the current stable, as well as gone through all dependencies. Surprisingly, conflicts were rather rare and most interfaces quite stable even across versions that, by SemVer, did not need to be. Updating also accordingly allowed bumping to Edition 2021.

New changes

The server now provides metrics, available behind an API secured with an OAuth Bearer token. None of these metrics store or utilize any personally identifiable information. Different requests are not correlated. No cookie banner will be introduced. Utilizing the metered crate will give me an idea of the hit-count of articles, and server resource usage such as in-flight requests, memory, CPU.

Maybe I'll stream that to a prometheus but it just looks stupidly complicated. But there's serde_prometheus for its idiosyncratic Text format and maybe it can be deployed in a neat little sandbox. Appropriately, the license for that crate is WTFPL.

The project pages are now generated from Markdown, same as articles, with their own little pages coming shortly. I've also started actually writing down something for most of the projects started in the last 4 years or so. And it'll get a redesign as some of the web-based projects should shine by including their showcases inline. Nothing's finalized yet to be shown.

For tracing, I'm not looking forward to integrating tracing. It's a bit infant and I understand the use case they're aiming for but it's not ready for async in the shape it should be. Globals and macros everywhere and you're going mad if trying to talk directly to their main subscriber trait. (The name confusion in the jump 0.1 -> 0.2 will also be quite something, I'm going to avoid that by waiting).

Final thoughts

The change from Rocket to Axum has had surprisingly little impact on any of the user-facing interfaces. This allowed me to clean up some architecture along the way. With some of the remaining code duplication, I've been tempted to move part of my pre-rendering process into WASM modules to be shipped to the page with the content. This would turn the whole thing into much more of a CMS than it is now. There's going to be interesting times for WebAssembly ahead.

Published on
Last updated