Hydrogen
The http-server written by a solo dev that's leaving web frameworks in the dust.
About
Hydrogen is my own implementation of an http-server written in C/C++. I am writing it because I do not need a web framework to develop performant web applications. Web frameworks might be great for other professional developers and that's fine with me, they are using the tools that they prefer. I get it, everyone on the team speaks the same language (in some sense) and it's great for collaboration.
I am a solo developer who enjoys developing my own tools because I get to familiarize myself with the entire stack starting from the system level. I do not intend to write a fully compliant implementation of the HTTP protocol, I am just implementing what my applications need. If you would like to use this software, you are on your own, don't ask me to add features unless you are willing to pay for my services. This is more honest than what AI companies do, have you pay for mediocre code at scales you are not willing to understand with no guarantee that the prices will not raise and that the quality if any will not worsen. Good luck with that.
So if you enjoyed the acidity of my previous remarks and you are still here you will find out that the purpose of this page is to share the lessons learned from attempting to implement an http-server just from the specs RFC9110 and RFC9112 and the Linux man pages. Unlike the so-called builders, prompt engineers, or whatever they fancy to call themselves these days, I do not use AI to generate code or for any other "assistance". The code that you see is mostly the result of experimentation and borrowing ideas from other open-source projects such as the xserver. For example, I got the idea of adding routes to the server by loading shared-objects by studying the xinput driver loading of the xserver. This is of course not unique to the xserver, there are epic open-source games such as Quake-II that use dynamic loading to enable modders to modify the game with ease. There are probably other ways to achieve the same flexibility but I did not bother to look at how other open-source projects that implement the http protocol do it. (Why would I want to spoil the fun of figuring it all out by myself?)
Motivation
Part of the motivation for this project came from the boot.dev course From TCP to HTTP in which the Primeagen teaches you how to build an http-parser with Golang basically from scratch. After watching the first 45 minutes of the video I decided that I would complete watching it after doing the hard work myself and then learn by comparing their solution to mine despite that I am using a different programming language and that I am embarking on the broader effort of actually building the server.
Compiling Hydrogen
All that you need to compile the hydrogen source code into a binary is the GNU Compiler Collection (GCC) along some standard libraries that should be present in most Linux distributions. I am assuming that you are familiar with the Unix way of building projects from source.
You can build hydrogen with GNU Make by executing it from the top level of the codebase:
make
Probably all of this sounds too good to be true but that's the case. Hydrogen is not opinionated about how you structure the code that makes up your webpage. All that hydrogen requires is that you register the modules that you will use to implement resource routing and put those modules (the .so files) in the modules directory. You are encouraged to read the favicon.cpp source file to see how straightforward it is to register a module. You have the option of compiling the modules along hydrogen or separately it's entirely up to you.
Running Hydrogen
The server does not require special permissions because it serves requests on non-privileged port 8080 and so you can start the server from the console in the usual way:
./hydrogen.binDebugging Hydrogen
You can debug hydrogen with gdb in the usual way from the command line:
gdb ./hydrogen.binIt is worth mentioning at this point that hydrogen spawns child processes when it gets http requests and when the response is complete it reaps the child process. It also gets its status and checks if the child process was terminated by signal (usually from the Linux kernel if there's something really bad going on) Nevertheless, looking at the console log helps a lot to know if the child process was able to complete its task successfully.
If you are reading this it's probably because there's a problem with the request and so what you want to do is to follow the child process instead, you can do that with the debugger this way:
set follow-fork-mode child
You would be issuing that command from the gdb session. My recommendation is to check the documenation via if you have never done this before. It helps to know that the child process only calls a function and that all that you need to do is to step through, eventually one of the functions in the modules that you registered would be called. Here's how you can get help from the debugger:
help set follow-fork-mode
Again, you would be issuing the helper command from the gdb session itself. The debugging session should lead you to the problem and if you have identified the problem you can contrive a solution (believe in yourself).