catflap icon indicating copy to clipboard operation
catflap copied to clipboard

🐈πŸšͺ Creates listening sockets and passes their FDs to a child process. You should probably use systemfd instead!

Catflap

Crate release version Crate license: MIT Crate download count Build status (Travis)

This is a small CLI tool for unix-likes that creates a TCP socket at the address you tell it to, then passes its FD index to a child process using an environment variable. The child (or any descendants) can then bind the socket.

The idea is for tools that reload servers, for instance cargo watch:

$ catflap cargo watch
[Catflap listening at 127.0.0.1:5000]
[Running 'cargo run']
   Compiling sample-server v0.1.0 (file:///home/code/rust/test)
    Finished dev [unoptimized + debuginfo] target(s) in 0.71 secs
     Running `target/debug/sample-server`
Binding to socket FD 3
Serving requests...

[[ Some file is changed so the server is reloaded ]]

[Running 'cargo run']
   Compiling sample-server v0.1.0 (file:///home/code/rust/test)
    Finished dev [unoptimized + debuginfo] target(s) in 0.84 secs
     Running `target/debug/sample-server`
Binding to socket FD 3
Serving requests...

[[ etc ]]

Servers that bind to ports might encounter EADDRINUSE and similar errors, as they attempt to listen on the same address but before the OS has freed them. Additionally, because the socket is always bound, requests simply wait for the program to answer them instead of failing when the server is restarting, leading to a better development experience.

Often, process supervisors implement this functionality, for example systemd, lithos, or the Flask dev server. Catflap is a single-purpose tool that does this and only this, so it can be used without all the configuration or dependence on a particular framework, and it can thus be plugged into your development workspace at very little cost.

Install

You should probably use systemfd instead! It does the same thing but follows systemd semantics closely.

The usual way:

$ cargo install catflap

Or, to upgrade:

$ cargo install --force catflap

Usage

$ catflap [options] [--] <command> [args...]

$ catflap -e LISTEN_FDS -- <command> [args...]
$ catflap -h 0.0.0.0 [--] <command> [args...]
$ catflap -p 8000 [--] <command> [args...]
Option Default Description
-e, --env LISTEN_FD Environment variable that will hold the socket's FD.
-h, --host 127.0.0.1 IP address (IPv4 or IPv6, no domain names) to bind the socket to.
-p, --port 5000 Port to bind the socket to.

Command specifics

The <command> is executed directly, without passing through a shell, so shellisms cannot be used directly. Additionally, you'll want to use -- to separate catflap options from program options:

$ catflap 'foo && bar'
# Will error because 'foo && bar' doesn't exist in PATH

$ catflap sh -c 'foo && bar'
# Will error because '-c' is not a catflap option

$ catflap -- sh -c 'foo && bar'
# Will work!

Port zero

If you specify port zero, the system will pick an unused high port at random. Catflap prints the socket's actual address right before it execs the given command, so you can find the right port to connect to.

$ catflap -p 0 cargo watch
[Catflap listening at 127.0.0.1:55917]

Example servers

These can be built and run directly in the respective folder. Then simply: $ curl -i http://localhost:5000.

  • Hyper only.
  • Using Iron.
  • Express on Node.js.

Etc

Licensed under MIT. Made by FΓ©lix Saparelli.

The name is both because it's a small door that you install so that you don't have to constantly open and close and open and close a bigger door for your furry companion, and as a play on the netcat tool.