Published:

Jarle Aase

Monthly update, January 2025

bookmark 7 min read

These monthly updates may be a bit technical. They are written for my future self (to remember how I spent the month—and to motivate me to do at least something remotely interesting), for friends and colleagues (past and future) to give them an idea of what I work on, and of course, for potential clients of my C++ freelance business and fellow software developers.

The banner shows NextApp being compiled on my aging MacBook Pro.

Projects

January was a productive month.

NextApp

NextApp is an upcoming GTD/productivity application for desktop and mobile.

I made a series of small incremental improvements to make the app simpler and faster to work with.

I added a crucial feature to store updates locally before sending them to the server. This ensures they can be retried if the app crashes, gets suspended, or the server is temporarily unavailable (such as during an upgrade or migration). Implementing this was an interesting challenge. The request had to be serialized and stored in sqlite, then deserialized and routed to the correct RPC function. On the server side, I added replay protection to prevent duplicate updates when a client sends a request but fails to receive the response.

I also changed the initial handshake between the client and the server. In the December version, the client would connect and then send a series of requests to replicate any missing data in its local database tables. Now, it gets a "publish ID" (an incremental number for each update received from the server), and if it matches the client’s last seen "publish ID," it skips the sync phase and simply starts processing incoming events from the server. This saves a second or two when the app connects or reconnects, and it also saves the server from handling a series of unnecessary SQL database requests.

Finally, I worked on making the client reconnect automatically if it was unable to connect or if it was disconnected. Additionally, the app now tracks network status and hibernation status. If the app is suspended (Android) or wakes up from sleep (desktop), it will reconnect to the server as soon as the network becomes available. This works well now.

Yahat and OpenMetrics

Yahat-cpp – Yet Another HTTP API Thing – is a trivial HTTP server for simple REST APIs and other HTTP interfaces in C++ projects.

Late last year, I added three OpenMetrics metrics to Yahat. If enabled, Yahat adds a "/metrics" route to the embedded HTTP server. From there, it is simple and intuitive to add metrics to a C++ application. In January, I added the remaining metric types, completing the Metrics API.

The API provides scoped types to simplify working with gauges and timers.

Here is an example of a timer in a Summary metric, updating the metric with the duration of processing a request inside a code block:

Example

 1
 2 } else if (req.type == Request::Type::POST && req.target == "/chat/message") {
 3 
 4>>>     auto metric = req_duration_metric_->scoped();   <<<
 5
 6        if (auto message = json->at("message").as_string(); !message.empty()) {
 7            if (!user_name.empty()) {
 8                chat_mgr_.sendMessage(user_name, message);
 9                return {200, "OK"};
10            } else {
11                return {400, "User not found"};
12            }
13        } else {
14            return {400, "Empty message"};
15        }
16    }

The Metrics class owns the metric, and a pointer allows fast access to update it. The public methods are thread-safe. To initialize the variable we use above, ...

 1
 2    yahat::Metrics::labels_t labels{{"api", "chat"}};
 3    std::vector<double> quantiles = {0.5, 0.9, 0.99};
 4
 5    req_duration_metric_ = chatMgr.server().metrics()->AddSummary(
 6            "yahat_chat_messages",
 7            "Duration distribution for handling chat messages",
 8            "sec",
 9            labels,
10            quantiles);
11        

The complete Metrics API is here

When used as an integral part of Yahat, all we have to do is define the metrics we need, update them when appropriate, and Yahat will automatically handle the "/metrics" endpoint and return the correct OpenMetrics-formatted data upon a GET request.

Chat Server

I implemented a simple single-room chat server as an example for Yahat. I refactored the Server-Sent Events (SSE) code to make it usable. I used the ChatServer as an example on how to use SSE in a web application. The chat server was actually something I have wanted for a while. I frequently set up VMs and reinstall machines locally. A secure HTTP chat server that does not leak information or phone home saves me a lot of time and effort when I need passwords or configuration snippets between local machines. The web-app for this server use very little js, and no js libraries or external dependencies.

Yahat Chat

Multi Platform

Until January, I had only tested Yahat on Linux. That's basically the only platform I need it for, as all my servers at the moment are designed to be run in Linux containers. They can also run locally on a Linux machine, but I don't see any reasons to make servers for macOS or Windows. From my perspective those platforms are primarily for end users using UI applications.

But as part of the upgrade of Yahat, I added GitHub Actions workflows for Linux, macOS and Windows, and fixed the problems that appeared. So now I can even run the chat-server on my MacBook Pro or Windows laptop.

Restc-cpp

Restc-cpp: The magic that takes the pain out of accessing JSON API's from C++

This is a 9-year-old project that makes it simple to send HTTP/REST requests to API servers. It can serialize a JSON object directly to/from a C++ class or struct and contains a connection pool, among other features. The only real drawback now is that it uses ASIO stackful coroutines instead of C++20 coroutines. I should probably do something about that…!

In January, I received a GitHub issue reporting that compilation failed with Boost 1.87. The ASIO developers had changed their APIs again. This is frustrating for people like me, who use ASIO in multiple projects and must spend hours or days refactoring perfectly working code just because some class or function names were changed.

For example, boost::asio::io_service was renamed to boost::asio::io_context. In most of my projects, I simply update the code to use whatever the latest version of Boost requires. However, restc-cpp still supports older Linux distributions with older Boost versions. To maintain compatibility, I created a compatibility layer in a header file so the library compiles with both old and new Boost versions.

I also fixed a few other open tickets while I was at it.

Shinysocks (and macOS Universal builds)

Shinysocks: A small, ultra-fast SOCKS proxy server that just works.

This is another old utility I wrote 10 years ago, when I worked as a senior C++ contractor at VmWare in Bulgaria. Cubicles has never been my thing, so whenever I had to work on something hard, I did it from my home office. However, to access the corporate network I had to use VPN. The VPN client only worked on Windows. I was the maintainer for the event infrastructure and database driver in vCenter. This required me to do the development on Linux. So I wrote a very simple SOCKS proxy server that runs on pretty much anything, including Windows, and routed my work related HTTP sessions and ssh sessions trough a VM with Windows, where Shinysocks was running.

Shinysocks is getting a steady (low frequency) stream of stars on Github. I don't know who is using it or for what, but I am keeping it up to date.

In January, I got an email from Github, about a failed Github Action build of the project. I have one that is triggered automatically at the start of each month, to catch compilation errors with newer versions of compilers or boost. This was again version 1.87 of boost.asio, that had changed it's API.

Since Shinysocks is a utility, and not a library, I took the simple path and just changed the code to use the new, fashionable asio names for their classes and methods.

I also decided to use the opportunity now to look closer at how to build universal binaries for macOS. The reason for this is that the Github runners now use arm64 runners, making the binaries produced useless for my old Intel based MacBook Pro. Apple has announced a Universal format, where I can make one binary that runs on both x64 and arm64 architectures. At some point, soon, I have to set up the CI pipeline for the NextApp UI app for all platforms, including macOS. So I figured Shinysocks, with it's very simple build, and only one dependency (you guessed it, boost!), was the perfect project try it out.

It was not easy. I asked ChatGPT, Github Copilot and DeepSeek for help, and they all found creative ways to waste my time.

Github Copilot was just terrible. I started with that, from the assumption that Microsoft/Github would have trained it to help people make Github workflows. For example:

None of the workflows worked. I switched to ChatGPT which appeared more competent, and tried a series of workflows using different variations of package managers, manual compilation of boost using b2, or brew. I even tried DeepSeek, which also did not know how to do this.

After having wasted around 12 hours over 2 days, I asked for help in r/cpp_questions. I did not get any answers from anyone who had experience with Github Actions and universal builds.

As a last resort i read the Apple documentation on Universal binaries and designed the workflow myself. After almost 80 failed runs, the green icon finally appeared on my workflow.

...

And some companies are already starting to replace developers with AI. Good luck with that! I used ChatGPT to help me with the algorithms for the Yahat Metrics. It knows OpenMetrics in and out, so it saved me for quite some time. However, the code was not good. For example, it suggested to use a std::vector as a ring-buffer, adding to the end and removing from the front. For metrics with a fixed size of internal data, it again suggested a vector, even if this was already a template method, and an array would be faster and more memory efficient.

When I made the Github Action workflow for Yahat, ChatGPT wanted to use an old version of zlib that had a CVE on it with a remote code execution vulnerability! Confronted with this, ChatGPT suggested a newer version, that also had issues. Why don't these models just default to the latest version of any library?

Anyway, January was a good month. My dog also appeared a little better, and very happy when I left the computers and invited him to a walk or made pizza for us.