Using Caddy As A Embedded HTTP Server
A bit of historical background here. Someone mentioned to me that basic rate limiting should be delegated to the reverse proxy server, while I thought pacing should be done in the actual application itself, rather than returning 429 and
Retry-After
. While I still don’t know the anwser to that question, I wrote this article as a by-product of my research into reverse proxy servers. Now we move on to the main topic, which is Caddy.
Caddy is a web server written in Go, and it can be compiled as a Go program.
Here’s how you build Caddy from a Go file:
mkdir caddy-build
cd caddy-build
go mod init caddy
wget2 https://github.com/caddyserver/caddy/raw/master/cmd/caddy/main.go
go mod tidy # it will fetch a bunch of cloud.google.com packages i don't need
go build
Since Caddy can be built from a Go file, I wondered if it can be embedded in another Go program. After a bit of browsing Caddy’s source code, starting from main.go
, I found out how.
First, we will need a Caddyfile
, and use caddy adapt -c Caddyfile
to turn it into JSON. Then, caddy.Load([]byte(configJSON, true)
starts the server!
Next, we need to find a suitable host for our server application.
In the Caddy module list, we find two modules fitting our requirement.
We will use FastCGI since it is simple and designed to be used with a reverse proxy like Caddy.
Finally, we define our own FastCGI handler. Go has the module "net/http/fcgi"
in its standard library, so we will use that.
That’s it! Now we have an HTTP server with custom server logic powered by Caddy!
Thoughts
It’s surprising for me to know that Caddy’s config format is actually in JSON.
Caddy is really simple to be embedded. We can also run it as a standalone process with caddy run -c Caddyfile
.
We can also use UNIX Domain Socket for FastCGI communication to and from Caddy, which, to my experience, is faster than loopback TCP on Linux.